fix(app-scan): exclude OpenSSL FIPS builds (FP) + per-asset app re-scan button

- OpenSSL FIPS provider builds (e.g. Veeam's 'OpenSSL v3.0.0 FIPS') were
  matched against OpenSSL CVEs, but the advisories explicitly carve the FIPS
  modules OUT (vulnerable code is outside the FIPS boundary) and they carry a
  separate 4-part build version — pure false positive (CVE-2025-15467). Exclude
  via negative lookahead 'openssl(?!.*fips)'. Existing FP self-heals: next scan
  no longer detects it -> auto-resolve marks it patched (now audit-logged).
- Add an 'App CVE re-scan' action button on asset rows (Wazuh- or Intune-backed
  assets) that hits the existing POST /vulnerabilities/app-cve-scan?asset_id=,
  analogous to the Wazuh rescan button.
This commit is contained in:
2026-07-10 13:33:25 +02:00
parent ebea13f740
commit c3b9bb6cbf
2 changed files with 34 additions and 2 deletions
+6 -1
View File
@@ -71,7 +71,12 @@ _REGISTRY: List[tuple] = [
_cpe(r"(?<!\w)putty", "putty:putty"),
_cpe(r"winscp", "winscp:winscp"),
_cpe(r"wireshark", "wireshark:wireshark"),
_cpe(r"openssl", "openssl:openssl"),
# Exclude the FIPS provider/module builds (e.g. Veeam ships "OpenSSL v3.0.0
# FIPS"): OpenSSL advisories explicitly carve the FIPS modules OUT of most
# CVEs (the vulnerable code is outside the FIPS boundary), and they carry a
# separate 4-part build version that doesn't map to NVD's ranges anyway →
# matching them is a false positive (tester: CVE-2025-15467).
_cpe(r"openssl(?!.*fips)", "openssl:openssl"),
_cpe(r"openvpn", "openvpn:openvpn"),
_cpe(r"node\.?js", "nodejs:node.js"),
_cpe(r"(?<!\w)python(?!.*launcher)", "python:python"),
+28 -1
View File
@@ -4,7 +4,7 @@ import { useEffect, useState } from 'react';
import api from '../../lib/api';
import { Asset, UserInfo, Group } from '../../types';
import Link from 'next/link';
import { PencilSquareIcon, TrashIcon, ArrowPathIcon, UserGroupIcon, ChevronDownIcon } from '@heroicons/react/24/outline';
import { PencilSquareIcon, TrashIcon, ArrowPathIcon, UserGroupIcon, ChevronDownIcon, MagnifyingGlassIcon } from '@heroicons/react/24/outline';
import { UserCircleIcon } from '@heroicons/react/24/solid';
export default function AssetsPage() {
@@ -14,6 +14,7 @@ export default function AssetsPage() {
const [loading, setLoading] = useState(true);
const [rescanLoading, setRescanLoading] = useState<number | null>(null);
const [nessusRescanLoading, setNessusRescanLoading] = useState<number | null>(null);
const [appScanLoading, setAppScanLoading] = useState<number | null>(null);
const [isModalOpen, setIsModalOpen] = useState(false);
const [searchText, setSearchText] = useState('');
const [showInactive, setShowInactive] = useState(false);
@@ -223,6 +224,20 @@ export default function AssetsPage() {
}
};
const handleAppRescan = async (asset: Asset) => {
setAppScanLoading(asset.id);
try {
const res = await api.post(`/api/v1/vulnerabilities/app-cve-scan?asset_id=${asset.id}`);
const d = res.data || {};
alert(`App CVE re-scan done for ${asset.hostname}: ${d.findings ?? 0} findings (${d.new ?? 0} new, ${d.resolved ?? 0} auto-resolved).`);
fetchAssets();
} catch (error: any) {
alert(error.response?.data?.detail || 'App re-scan failed.');
} finally {
setAppScanLoading(null);
}
};
const handleNessusRescan = async (asset: Asset) => {
if (!asset.ip_address) {
alert('Asset has no IP address — Nessus cannot target it.');
@@ -717,6 +732,18 @@ export default function AssetsPage() {
}
</button>
)}
{(asset.wazuh_agent_id || asset.intune_device_id) && (
<button
onClick={() => handleAppRescan(asset)}
disabled={appScanLoading === asset.id}
title="App CVE re-scan — match this asset's installed software to CVEs (curated + cvelistV5)"
className={`${appScanLoading === asset.id ? 'text-gray-300' : 'text-emerald-600 hover:text-emerald-800'}`}
>
{appScanLoading === asset.id
? <ArrowPathIcon className="h-5 w-5 animate-spin" />
: <MagnifyingGlassIcon className="h-5 w-5" />}
</button>
)}
{(asset.wazuh_agent_id || asset.nessus_host_uuid) && (
<button
onClick={() => handleCoverageGap(asset)}