Add quick button to mark unexpected ports as expected

Allow users to add ports to expected list directly from scan results page
instead of navigating through site config pages. The button appears next
to unexpected ports and updates the site IP configuration via the API.

- Add site_id and site_ip_id to scan result data for linking to config
- Add "Mark Expected" button next to unexpected ports in scan detail view
- Implement markPortExpected() JS function to update site IP settings
This commit is contained in:
2025-11-21 15:40:37 -06:00
parent 3058c69c39
commit 9bd2f67150
2 changed files with 115 additions and 4 deletions

View File

@@ -586,6 +586,19 @@
const screenshotPath = service && service.screenshot_path ? service.screenshot_path : null;
const certificate = service && service.certificates && service.certificates.length > 0 ? service.certificates[0] : null;
// Build status cell with optional "Mark Expected" button
let statusCell;
if (port.expected) {
statusCell = '<span class="badge badge-good">Expected</span>';
} else {
// Show "Unexpected" badge with "Mark Expected" button if site_id and site_ip_id are available
const canMarkExpected = site.site_id && ip.site_ip_id;
statusCell = `<span class="badge badge-warning">Unexpected</span>`;
if (canMarkExpected) {
statusCell += ` <button class="btn btn-sm btn-outline-success ms-1" onclick="markPortExpected(${site.site_id}, ${ip.site_ip_id}, ${port.port}, '${port.protocol}')" title="Add to expected ports"><i class="bi bi-plus-circle"></i></button>`;
}
}
const row = document.createElement('tr');
row.classList.add('scan-row'); // Fix white row bug
row.innerHTML = `
@@ -595,7 +608,7 @@
<td>${service ? service.service_name : '-'}</td>
<td>${service ? service.product || '-' : '-'}</td>
<td class="mono">${service ? service.version || '-' : '-'}</td>
<td>${port.expected ? '<span class="badge badge-good">Expected</span>' : '<span class="badge badge-warning">Unexpected</span>'}</td>
<td>${statusCell}</td>
<td>${screenshotPath ? `<a href="/output/${screenshotPath.replace(/^\/?(?:app\/)?output\/?/, '')}" target="_blank" class="btn btn-sm btn-outline-primary" title="View Screenshot"><i class="bi bi-image"></i></a>` : '-'}</td>
<td>${certificate ? `<button class="btn btn-sm btn-outline-info" onclick='showCertificateModal(${JSON.stringify(certificate).replace(/'/g, "&#39;")})' title="View Certificate"><i class="bi bi-shield-lock"></i></button>` : '-'}</td>
`;
@@ -757,6 +770,74 @@
}
}
// Mark a port as expected in the site config
async function markPortExpected(siteId, ipId, portNumber, protocol) {
try {
// First, get the current IP settings - fetch all IPs with high per_page to find the one we need
const getResponse = await fetch(`/api/sites/${siteId}/ips?per_page=200`);
if (!getResponse.ok) {
throw new Error('Failed to get site IPs');
}
const ipsData = await getResponse.json();
// Find the IP in the site
const ipData = ipsData.ips.find(ip => ip.id === ipId);
if (!ipData) {
throw new Error('IP not found in site');
}
// Get current expected ports
let expectedTcpPorts = ipData.expected_tcp_ports || [];
let expectedUdpPorts = ipData.expected_udp_ports || [];
// Add the new port to the appropriate list
if (protocol.toLowerCase() === 'tcp') {
if (!expectedTcpPorts.includes(portNumber)) {
expectedTcpPorts.push(portNumber);
expectedTcpPorts.sort((a, b) => a - b);
}
} else if (protocol.toLowerCase() === 'udp') {
if (!expectedUdpPorts.includes(portNumber)) {
expectedUdpPorts.push(portNumber);
expectedUdpPorts.sort((a, b) => a - b);
}
}
// Update the IP settings
const updateResponse = await fetch(`/api/sites/${siteId}/ips/${ipId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
expected_tcp_ports: expectedTcpPorts,
expected_udp_ports: expectedUdpPorts
})
});
if (!updateResponse.ok) {
let errorMessage = 'Failed to update IP settings';
try {
const errorData = await updateResponse.json();
errorMessage = errorData.message || errorMessage;
} catch (e) {
// Ignore JSON parse errors
}
throw new Error(errorMessage);
}
// Show success message
showAlert('success', `Port ${portNumber}/${protocol.toUpperCase()} added to expected ports for this IP. Refresh the page to see updated status.`);
// Optionally refresh the scan data to show the change
// Note: The scan data itself won't change, but the user knows it's been updated
} catch (error) {
console.error('Error marking port as expected:', error);
showAlert('danger', `Failed to mark port as expected: ${error.message}`);
}
}
// Find previous scan and show compare button
let previousScanId = null;
let currentConfigId = null;