diff --git a/AGENTS.md b/AGENTS.md
index 2533592..a22cfa8 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -76,7 +76,7 @@
## Context layer (Phase 13E)
-- Latest explicit user decision 2026-10-01 supersedes the preceding permanent-Context decision: the Work toolbar has an `Engines` button at the former Context-button position. It stays marked blue, including on hover and while the popup is closed (an explicit colour-role exception for this control only). The shared popup lists Core Engine, Context Engine, Report Engine and, since the explicit user decision of 2026-10-05, Compliance Engine with checkboxes. All default to active; Core Engine is always checked and cannot be disabled. User choices for optional engines are stored locally under the shared Core-owned engine-activation contract. Disabling Context removes its UI, stops new context requests and clears the active Context Cluster filter; disabling Report blocks new report generation from the UI; disabling Compliance removes the Finding Reader ISO action and panel, the Result DB `Compliance mapping` batch action and `Export JSON`, the Client Info `Compliance` and `AI Overview` tabs and the Data Hub ISO corpus tile and stops their requests, and re-enabling starts the Finding Reader ISO panel closed. As the only server-side effect of this local preference (explicit user decision 2026-10-08), disabling Compliance also switches the automatic ISO mapping of new uploads off for the whole server; re-enabling does not switch it on again. Already submitted backend work is not cancelled by this local preference; its late UI output is suppressed. Cluster badges still select the exact Work filter, and the Context detail section still starts collapsed. Engine independence and Core invariance remain binding.
+- Latest explicit user decision 2026-10-01 supersedes the preceding permanent-Context decision: the Work toolbar has an `Engines` button at the former Context-button position. It stays marked blue, including on hover and while the popup is closed (an explicit colour-role exception for this control only). The shared popup lists Core Engine, Context Engine, Report Engine, Remediation Engine (see `Remediation Engine (2026-10-08)`) and, since the explicit user decision of 2026-10-05, Compliance Engine with checkboxes, in this order. All default to active; Core Engine is always checked and cannot be disabled. User choices for optional engines are stored locally under the shared Core-owned engine-activation contract. Disabling Context removes its UI, stops new context requests and clears the active Context Cluster filter; disabling Report blocks new report generation from the UI; disabling Compliance removes the Finding Reader ISO action and panel, the Result DB `Compliance mapping` batch action and `Export JSON`, the Client Info `Compliance` and `AI Overview` tabs and the Data Hub ISO corpus tile and stops their requests, and re-enabling starts the Finding Reader ISO panel closed. As the only server-side effect of this local preference (explicit user decision 2026-10-08), disabling Compliance also switches the automatic ISO mapping of new uploads off for the whole server; re-enabling does not switch it on again. Already submitted backend work is not cancelled by this local preference; its late UI output is suppressed. Cluster badges still select the exact Work filter, and the Context detail section still starts collapsed. Engine independence and Core invariance remain binding.
- Explicit user decision 2026-10-01 supersedes the earlier Context on/off, default-off, locally saved switch, hover-button and badge-scroll agreements: Context is permanently active and the global toolbar Context button is removed. Ignore obsolete locally saved on/off preferences. Clicking a Finding Reader Context cluster badge selects that exact `Context Cluster` Work facet value, replacing the previous cluster selection while preserving other filters. The Context detail section still starts collapsed for each finding and opens through its own summary. All read-only, provenance and verdict/score boundaries remain binding; only an explicit cluster-filter selection restricts the Work record set.
diff --git a/docs/IMPLEMENTATION_PLAN.md b/docs/IMPLEMENTATION_PLAN.md
index 9251c87..e6b8e71 100644
--- a/docs/IMPLEMENTATION_PLAN.md
+++ b/docs/IMPLEMENTATION_PLAN.md
@@ -4346,6 +4346,7 @@ Verifiziert 2026-09-09 (Phase 7): Bereichswechsel beschleunigt und Performance-V
Verifiziert 2026-09-08 (Phase 7/13B): GPO-Quellenkorrektur fuer Work und Client Info. Aktuelle Evaluationen aus dem vorhandenen autoritativen Readmodell statt ergebnisabhaengiger Backupgruppierung; Import- und Auswertungszeit getrennt, Source-Scan-Provenienz auch auf GPO-only-Blaettern. 1.014 Tests bestanden, acht optionale PostgreSQL-Tests uebersprungen; Local-only-/Designpruefung und beide Builds erfolgreich. Dummy lesend bestaetigt: eine statt drei GPO-Quellenoptionen, 3.805 aktuelle Zeilen, elf historische Evaluationen unveraendert. Keine Persistenzmutation oder Neuanalyse.
| Datum | Änderung |
+| 2026-10-12 | Integration (Core-Freigabe alle, Governance): Michaels Commit `614ac1b` (Remediation Engine, geschuetzte Importvertraege, Client-Info-Tab `Security`, Technical Report) ist die Basis; die 39 Commits seit `250c213` (AI/ISO/BSI-Compliance-Engine, Defender for IoT) sind darauf rebased (Branch `rebase/michael-backup`, `compliance-mapping` unveraendert). Beide Engines stehen nebeneinander in Engines-Popup (Core, Context, Report, Remediation, Compliance), Hook-Tabelle, Grenztest, Server und Work-Facetten (Work Package an der Stelle von Result Message, ISO/BSI-Facetten weiter direkt vor `Source: Scan`). Client Info nutzt Michaels `ClientInfoTabs` in der vereinbarten Reihenfolge Overview, Security, Microsoft Defender for IoT, Compliance, AI Overview; die drei Engine-/Defender-Tabs sind `lazy` und mounten nur ausgewaehlt (neuer Verhaltenstest), die alte Tab-Navigation und ihre CSS-Regeln entfallen. Finding Reader: `Map ISO controls` steht direkt vor Michaels fester Aktionsgruppe `AI search`/Bookmark/`Results`. Abgeleiteter Forest-Hinweis nach `client-info-facts.ts` portiert. Importgate: zwei additive synthetische Defender-Vertraege (Schwachstellen- und Alert-CSV, je 3 Findings); die Snapshots `pingcastle-html`/`pingcastle-xml` weichen durch die freigegebenen PingCastle-Aenderungen aus 2.19.1 ab (Profil 1.5.0 -> 1.7.0, XML-Domaenenfakten, Vergleichsschluessel) und bleiben bis zur Nutzerpruefung unveraendert, das Gate ist deshalb rot. Sechs weitere Testfehler (Context-Cluster-Reihenfolge, Pathfinder-Mock, Data-Hub-Donut- und DB-Structure-Layouttest) bestehen bereits auf `614ac1b` allein. Produktversion 2.25.0 (Minor: beide Funktionslinien vereint); Versionstests, beide Produktionsbuilds, Local-only- und Designpruefung bestanden. Vor dem ersten Serverstart legt die Remediation Engine das Schema `assessor_remediation` an; vorher Backup des kanonischen Datastores. |
| 2026-10-06 | Phase 7: Data Hub Deviation/Passed now use the same assessed-percentage helper as Client Info; all six donuts state their population basis. 29 focused tests and synthetic Chrome Dark/Light checks passed; absolute counts, other population shares and assessment semantics retained. Client build, design and UI/API version checks passed. Nine out-of-scope report test failures, a report Local-only failure and report server type errors prevent full release verification; attempted 2.16.9 reverted to 2.16.8 pending successful production builds. |
| 2026-10-06 | Phase 7: Client Info Deviations percentage now divides by Passed + Deviations and labels Of assessed checks; zero assessed shows a dash while preserving absolute zero. Permanent UI/click invariance tests in both themes and synthetic Chrome presentation verified; unchanged counts, verdicts, scores and other donuts. Patch 2.16.8: both builds and version contracts passed; full run 1,936 passed/15 skipped with one stale API-version assertion corrected and passed in the 43-test focused rerun; Local-only/design checks passed. |
| 2026-10-06 | Phase 13B: GPO folder read diagnostics retain only safe error names, stages and counts, distinguish empty/denied/unreadable selections, preserve cancellation and wrap complete timeslot messages. 27 focused GPO UI tests and synthetic Chrome Dark/Light checks passed; both builds and version contracts verified for patch 2.16.7. Full run: 1,932 passed, 15 skipped, one general import timeout passed on separate rerun; Local-only/design checks passed. Customer-specific read failure remains unconfirmed; no customer path/data used, Phase 13B remains in progress. |
diff --git a/fixtures/README.md b/fixtures/README.md
index 9fae9f2..1b2454d 100644
--- a/fixtures/README.md
+++ b/fixtures/README.md
@@ -45,3 +45,8 @@ All files below are deliberately small, synthetic, and free of customer or compa
`maester/application-json-binding.synthetic.html` protects the current static declaration in an application/json script. `maester/multi-tenant.synthetic.html` protects separate native tenant paths. Both contain only invented Dummy values.
Compatibility references use synthetic data only, including when a real report reveals a new structure.
+
+## Microsoft Defender for IoT
+
+- Files: `defender-for-iot/cves.synthetic.csv` (vulnerability export) and `defender-for-iot-alerts/alerts.synthetic.csv` (alert export)
+- Basis: the same fictional devices, CVE IDs from the reserved `CVE-2099-*` range, TEST-NET addresses and documentation MACs as the Defender importer tests. Added 2026-10-12 as additive contracts when the protected gate met the existing Defender profiles.
diff --git a/fixtures/defender-for-iot-alerts/alerts.synthetic.csv b/fixtures/defender-for-iot-alerts/alerts.synthetic.csv
new file mode 100644
index 0000000..ed0c895
--- /dev/null
+++ b/fixtures/defender-for-iot-alerts/alerts.synthetic.csv
@@ -0,0 +1,4 @@
+Id,Title,First Detection,Last Detection,Last Activity,Severity,Description,Acknowledged,Additional Information,Source Address,Destination Address
+A-1,Synthetic Unauthorized PLC Programming,11/09/2026 10:05:12,11/09/2026 10:07:11,11/09/2026 10:07:11,Critical,Synthetic programming from 192.0.2.41,False,192.0.2.21 over port CIP (44818),192.0.2.41,192.0.2.21
+A-3,Synthetic Internet Connection,13/09/2026 09:00:00,13/09/2026 09:30:00,13/09/2026 09:30:00,Major,Synthetic outbound connection,False,198.51.100.7 over port HTTPS (443),00-00-5E-00-53-01,
+A-4,Synthetic Unused Protocol,14/09/2026 07:00:00,14/09/2026 07:00:00,14/09/2026 07:05:00,Warning,Synthetic unused protocol,True,,192.0.2.50,192.0.2.51
diff --git a/fixtures/defender-for-iot/cves.synthetic.csv b/fixtures/defender-for-iot/cves.synthetic.csv
new file mode 100644
index 0000000..2013327
--- /dev/null
+++ b/fixtures/defender-for-iot/cves.synthetic.csv
@@ -0,0 +1,4 @@
+Device ID,Device Name,IP Address,Site,Sensor,CVE,CVSS Score,Severity,Component,Status
+DEV-OT-001,PLC-LINE1-01,192.0.2.21,Riverbend Plant,SENS-RB-01,CVE-2099-1001,9.1,Critical,Synthetic component,Open
+DEV-OT-002,HMI-LINE1-01,192.0.2.31,Riverbend Plant,SENS-RB-01,CVE-2099-1002,7.5,High,Synthetic component,Open
+DEV-OT-004,HISTORIAN-01,192.0.2.51,Riverbend Plant,SENS-RB-01,CVE-2099-1005,8.2,High,Synthetic component,Mitigation planned
diff --git a/package-lock.json b/package-lock.json
index 310b0ff..f41524a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "assessor",
- "version": "2.24.2",
+ "version": "2.25.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "assessor",
- "version": "2.24.2",
+ "version": "2.25.0",
"dependencies": {
"@fontsource-variable/inter": "^5.3.0",
"jsdom": "^29.1.1",
diff --git a/package.json b/package.json
index 6d37b69..cdfbcbd 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "assessor",
- "version": "2.24.2",
+ "version": "2.25.0",
"private": true,
"type": "module",
"scripts": {
diff --git a/src/compliance/ui/ComplianceOverview.tsx b/src/compliance/ui/ComplianceOverview.tsx
index efc31bf..d258a3e 100644
--- a/src/compliance/ui/ComplianceOverview.tsx
+++ b/src/compliance/ui/ComplianceOverview.tsx
@@ -28,7 +28,7 @@ export const footprintStep = (findings: number) => findings >= 25 ? 4 : findings
/** Switches the overview row between the frameworks; the framework tiles below stay as they are. */
export function ComplianceFrameworkSwitch({ value, onChange }: { value: ComplianceFramework; onChange(value: ComplianceFramework): void }) {
// The Client Info tab buttons: the approved selection control of this view.
- return
+ return
{FRAMEWORKS.map(([id, label]) => onChange(id)}>{label} )}
;
diff --git a/src/compliance/ui/MappingReviewTile.tsx b/src/compliance/ui/MappingReviewTile.tsx
index 90c0aa9..07cbc1a 100644
--- a/src/compliance/ui/MappingReviewTile.tsx
+++ b/src/compliance/ui/MappingReviewTile.tsx
@@ -75,7 +75,7 @@ function ReviewList({ review, projectId, backendStatus, onChanged }: {
Each rule compares the BSI requirements accepted in its direct BSI mapping with the requirements the BSI
mapping {review.crosswalkVersion} derives from its accepted ISO controls. Agreement shows consistency, not correctness. Nothing here changes a
decision until you accept or reject one.
-
+
{FILTERS.map(option => {
const amount = review.items.filter(item => item.state === option.state).length;
return
{tabs.map(tab => )}
+ hidden={tab.id !== active} data-group-scroll={`client-info-${tab.id}`} tabIndex={0}>{tab.lazy && tab.id !== active ? null : tab.content})}
;
}
diff --git a/src/features/dashboard/DashboardPanel.tsx b/src/features/dashboard/DashboardPanel.tsx
index a9a1b09..02bdb4e 100644
--- a/src/features/dashboard/DashboardPanel.tsx
+++ b/src/features/dashboard/DashboardPanel.tsx
@@ -1147,7 +1147,6 @@ export function dashboardAnalysis(
export function DashboardPanel({ onOpenWork }: DashboardPanelProps) {
const app = useApp();
const { dataset, activeSheet, activeProfile, reviews, setStatusFilter, setFacetFilter, setPathFindingIds, resetFilters, setView } = app;
- const [clientInfoView, setClientInfoView] = useState<'overview' | 'defender' | 'compliance' | 'ai'>('overview');
const hasDefender = useMemo(() => Boolean(dataset && defenderModelForDataset(dataset).available), [dataset]);
const pureDefender = dataset?.profileId === DEFENDER_FOR_IOT_PROFILE_ID;
const environment = useEnvironmentSnapshot(app.targetProjectId, app.activeScanRunIds);
@@ -1161,12 +1160,6 @@ export function DashboardPanel({ onOpenWork }: DashboardPanelProps) {
if (showCompliance && app.targetProjectId) preloadClientComplianceOverview(app.targetProjectId, app.activeAssessmentIds, app.activeScanRunIds);
}, [showCompliance, app.targetProjectId, app.activeAssessmentIds, app.activeScanRunIds]);
- useEffect(() => {
- if (pureDefender && clientInfoView === 'overview') setClientInfoView('defender');
- else if (clientInfoView === 'defender' && !hasDefender) setClientInfoView('overview');
- else if (clientInfoView === 'ai' && !showAiOverview) setClientInfoView(pureDefender ? 'defender' : 'overview');
- else if (clientInfoView === 'compliance' && !showCompliance) setClientInfoView(pureDefender ? 'defender' : 'overview');
- }, [clientInfoView, hasDefender, pureDefender, showAiOverview, showCompliance]);
const gpoInventory = useGpoInventory(app.targetProjectId);
const sourceFilter = app.facetFilters['provenance:source-scan'];
const analysis = useDashboardAnalysis(activeSheet, dataset, activeProfile, reviews, sourceFilter);
@@ -1251,19 +1244,8 @@ export function DashboardPanel({ onOpenWork }: DashboardPanelProps) {
setView('columns');
onOpenWork();
};
- const clientInfoViews: Array
= [
- ...(pureDefender ? [] : [['overview', 'Overview'] as const]),
- ...(hasDefender ? [['defender', 'Microsoft Defender for IoT'] as const] : []),
- ...(showCompliance ? [['compliance', 'Compliance'] as const] : []),
- ...(showAiOverview ? [['ai', 'AI Overview'] as const] : [])
- ];
- const clientInfoTabs = clientInfoViews.length > 1 ?
- {clientInfoViews.map(([id, title]) =>
- setClientInfoView(id)}>{title} )}
- : undefined;
- if (clientInfoView === 'ai' && showAiOverview) return ;
// Compliance Engine tab: one tile per framework; a Results window opens a control or theme in Work.
- if (clientInfoView === 'compliance' && showCompliance) return jump(() => {
const targetSheet = sourceScanNavigationSheet(dataset, activeSheet);
if (targetSheet) {
@@ -1308,12 +1290,10 @@ export function DashboardPanel({ onOpenWork }: DashboardPanelProps) {
context={}/>;
- if (hasDefender && (clientInfoView === 'defender' || pureDefender)) {
- return }/> : null;
+ const defenderContent = hasDefender ? app.recordScopedRiskAcceptance(write) : undefined} />;
- }
+ onScopedRiskAcceptance={app.backendStatus === 'connected' ? write => app.recordScopedRiskAcceptance(write) : undefined} /> : null;
const {
total, statusFacet, severityFacet, sourceFacet, tools, reports,
@@ -1373,8 +1353,7 @@ export function DashboardPanel({ onOpenWork }: DashboardPanelProps) {
: 'all';
return (
-
- {clientInfoTabs}
+
}, { id: 'security', label: 'Security', content:
}]} />
+ dataset={dataset} sheetId={activeSheet.id} toolId={activeSheet.toolId} contextStatus={impactContext.status} {...securityView}/> }]),
+ ...(defenderContent ? [{ id: 'defender', label: 'Microsoft Defender for IoT', content: defenderContent, lazy: true }] : []),
+ ...(complianceContent ? [{ id: 'compliance', label: 'Compliance', content: complianceContent, lazy: true }] : []),
+ ...(showAiOverview ? [{ id: 'ai', label: 'AI Overview', content:
, lazy: true }] : [])]} />
);
}
diff --git a/src/features/dashboard/RiskRegister.tsx b/src/features/dashboard/RiskRegister.tsx
index 149a83a..126525d 100644
--- a/src/features/dashboard/RiskRegister.tsx
+++ b/src/features/dashboard/RiskRegister.tsx
@@ -137,7 +137,7 @@ export function RiskRegister({ dataset, reviews, scopedRiskAcceptances = [], onO
{entry.title} {entry.source}
{entry.state}
Review {displayDate(entry.latest.details.reviewOn)}
- {recordFor(entry) &&
onOpenFinding?.(entry.recordId)}/>} )}
+ {recordFor(entry) && onOpenFinding?.(recordFor(entry)!.id)}/>})}
@@ -146,7 +146,7 @@ export function RiskRegister({ dataset, reviews, scopedRiskAcceptances = [], onO
Risk Information {selected.source}
{onOpenFinding && selected.recordId && onOpenFinding(selected.recordId!)}>Open finding }{selected.state}
- Finding {selected.title} {recordFor(selected) && onOpenFinding?.(selected.recordId)}/>}
+ Finding {selected.title} {recordFor(selected) && onOpenFinding?.(recordFor(selected)!.id)}/>}
{selected.coveredCves && Immutable system scope {selected.device}
{selected.coveredCves.length.toLocaleString('en-US')} covered CVE{selected.coveredCves.length === 1 ? '' : 's'}: {selected.coveredCves.join(', ')}
}
diff --git a/src/features/dashboard/client-info-facts.ts b/src/features/dashboard/client-info-facts.ts
index 17a37ab..6d860eb 100644
--- a/src/features/dashboard/client-info-facts.ts
+++ b/src/features/dashboard/client-info-facts.ts
@@ -73,10 +73,10 @@ export function factText(value: string | undefined): string {
*/
function forestFact(snapshot: EnvironmentSnapshot | undefined): DashboardFact {
const observed = (snapshot?.observations ?? []).filter(item => item.property === 'forest.name' && item.state === 'observed');
- const observation = observed.find(item => !item.attributes.derivation) ?? observed[0];
+ const observation = observed.find(item => !item.attributes?.derivation) ?? observed[0];
if (!observation) return { label: 'Forest', value: UNKNOWN_FACT };
const value = factText(displayValue(observation));
- return observation.attributes.derivation === 'configuration-partition-dn' && value !== UNKNOWN_FACT
+ return observation.attributes?.derivation === 'configuration-partition-dn' && value !== UNKNOWN_FACT
? { label: 'Forest', value, note: 'derived from configuration path' }
: { label: 'Forest', value };
}
diff --git a/src/styles/defender-for-iot.css b/src/styles/defender-for-iot.css
index 0ac3e13..9790ccf 100644
--- a/src/styles/defender-for-iot.css
+++ b/src/styles/defender-for-iot.css
@@ -6,11 +6,11 @@
.defender-metrics article { display: grid; align-content: center; gap: var(--space-2); min-width: 0; min-height: 88px; padding: var(--tile-inset); border: var(--border-hair) solid var(--tile-border); border-radius: var(--tile-radius); background: var(--tile-surface); box-shadow: none; }
.defender-metrics span { color: var(--muted); font-size: var(--type-table-body-size); font-weight: var(--fw-semibold); line-height: 1.3; }
.defender-metrics strong { color: var(--heading); font-size: var(--type-h2-size); font-weight: var(--fw-semibold); font-variant-numeric: tabular-nums; line-height: 1; }
-.defender-tabs, .client-info-tabs { display: flex; flex-wrap: wrap; gap: var(--space-2); }
-.defender-tabs button, .client-info-tabs button { min-height: 36px; padding: var(--space-2) var(--space-4); border: var(--border-hair) solid transparent; border-radius: var(--btn-radius); color: var(--text); background: transparent; font-size: var(--ui-control-size); font-weight: var(--fw-semibold); cursor: pointer; }
-.defender-tabs button:hover, .client-info-tabs button:hover { background: var(--tile-surface-hover); }
-.defender-tabs button:focus-visible, .client-info-tabs button:focus-visible { outline: var(--bw-emphasis) solid var(--focus-ring); outline-offset: calc(-1 * var(--bw-emphasis)); }
-.defender-tabs button[aria-pressed="true"], .client-info-tabs button[aria-pressed="true"] { outline: var(--bw-emphasis) solid var(--marking-color); outline-offset: calc(-1 * var(--bw-emphasis)); background: var(--tile-surface); }
+.defender-tabs { display: flex; flex-wrap: wrap; gap: var(--space-2); }
+.defender-tabs button { min-height: 36px; padding: var(--space-2) var(--space-4); border: var(--border-hair) solid transparent; border-radius: var(--btn-radius); color: var(--text); background: transparent; font-size: var(--ui-control-size); font-weight: var(--fw-semibold); cursor: pointer; }
+.defender-tabs button:hover { background: var(--tile-surface-hover); }
+.defender-tabs button:focus-visible { outline: var(--bw-emphasis) solid var(--focus-ring); outline-offset: calc(-1 * var(--bw-emphasis)); }
+.defender-tabs button[aria-pressed="true"] { outline: var(--bw-emphasis) solid var(--marking-color); outline-offset: calc(-1 * var(--bw-emphasis)); background: var(--tile-surface); }
.defender-view { display: grid; gap: var(--space-4); min-width: 0; padding: var(--tile-inset); border: var(--border-hair) solid var(--tile-border); border-radius: var(--tile-radius); background: var(--tile-surface); }
.defender-view > header { display: flex; align-items: baseline; justify-content: space-between; flex-wrap: wrap; gap: var(--space-2); }
.defender-view > header h2 { margin: 0; color: var(--heading); font-size: var(--type-h2-size); font-weight: var(--fw-semibold); }
diff --git a/tests/app/app-version.test.ts b/tests/app/app-version.test.ts
index d5612c8..a91fa04 100644
--- a/tests/app/app-version.test.ts
+++ b/tests/app/app-version.test.ts
@@ -9,7 +9,7 @@ const components = readFileSync(new URL('../../src/styles/components.css', impor
describe('ASSESSOR version in the brand header', () => {
it('shows the centrally maintained product version and opens About from the brand', () => {
- expect(ASSESSOR_PRODUCT_VERSION).toBe('2.24.2');
+ expect(ASSESSOR_PRODUCT_VERSION).toBe('2.25.0');
expect(app).toContain("'db-structure': 'Data Flow'");
expect(app).toContain("['db-structure', 'Data Flow']");
expect(app).not.toContain("'DB Structure'");
diff --git a/tests/compliance/ai-overview-panel.test.tsx b/tests/compliance/ai-overview-panel.test.tsx
index d93481c..adbfdde 100644
--- a/tests/compliance/ai-overview-panel.test.tsx
+++ b/tests/compliance/ai-overview-panel.test.tsx
@@ -39,9 +39,8 @@ describe('Client Info AI Overview', () => {
it('adds the AI tab beside the Client Info overview', () => {
const dashboard = readFileSync(new URL('../../src/features/dashboard/DashboardPanel.tsx', import.meta.url), 'utf8');
- expect(dashboard).toContain("[['overview', 'Overview'] as const]");
- expect(dashboard).toContain('{clientInfoTabs}');
- expect(dashboard).toContain("...(showAiOverview ? [['ai', 'AI Overview'] as const] : [])");
- expect(dashboard).toContain("clientInfoView === 'ai'");
+ expect(dashboard).toContain("[{ id: 'overview', label: 'Overview', content:");
+ // The AI tab is the last Client Info tab and mounts only while selected.
+ expect(dashboard).toContain("...(showAiOverview ? [{ id: 'ai', label: 'AI Overview', content: , lazy: true }] : [])]} />");
});
});
diff --git a/tests/compliance/client-iso-controls.test.tsx b/tests/compliance/client-iso-controls.test.tsx
index b0e8507..b66d69f 100644
--- a/tests/compliance/client-iso-controls.test.tsx
+++ b/tests/compliance/client-iso-controls.test.tsx
@@ -157,14 +157,17 @@ describe('Client Info ISO controls', () => {
it('is its own Client Info tab, not part of the Overview, and opens Work through the ISO control and theme filters', () => {
const source = readFileSync(join(process.cwd(), 'src/features/dashboard/DashboardPanel.tsx'), 'utf8');
- expect(source).toContain("...(showCompliance ? [['compliance', 'Compliance'] as const] : [])");
- expect(source.indexOf("['compliance', 'Compliance']")).toBeLessThan(source.indexOf("['ai', 'AI Overview']"));
- const tab = source.slice(source.indexOf("if (clientInfoView === 'compliance' && showCompliance) return source.indexOf(id));
+ expect(order.every(index => index > 0)).toBe(true);
+ expect([...order].sort((a, b) => a - b)).toEqual(order);
+ const tab = source.slice(source.indexOf('const complianceContent = showCompliance ? {
} finally { await act(async () => root.unmount()); container.remove(); vi.unstubAllGlobals(); }
});
});
+
+describe('Client Info lazy engine tabs', () => {
+ it('mounts a lazy tab only while it is selected, so hidden engine tabs start no requests', async () => {
+ vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
+ const mounted = vi.fn();
+ function Probe() { mounted(); return Synthetic engine tab
; }
+ const host = document.createElement('div'); document.body.append(host);
+ const root = createRoot(host);
+ try {
+ await act(async () => root.render(Synthetic overview },
+ { id: 'compliance', label: 'Compliance', content: , lazy: true }
+ ]}/>));
+ const tabs = host.querySelectorAll('[role="tab"]');
+ const panels = host.querySelectorAll('[role="tabpanel"]');
+ expect(mounted).not.toHaveBeenCalled();
+ expect(panels[1].childNodes).toHaveLength(0);
+ await act(async () => tabs[1].click());
+ expect(mounted).toHaveBeenCalled();
+ expect(panels[1].textContent).toBe('Synthetic engine tab');
+ await act(async () => tabs[0].click());
+ expect(panels[1].childNodes).toHaveLength(0);
+ } finally { await act(async () => root.unmount()); host.remove(); vi.unstubAllGlobals(); }
+ });
+});
diff --git a/tests/features/defender-dashboard.test.ts b/tests/features/defender-dashboard.test.ts
index 2f59a86..fcf98af 100644
--- a/tests/features/defender-dashboard.test.ts
+++ b/tests/features/defender-dashboard.test.ts
@@ -51,9 +51,9 @@ describe('Defender vulnerability dashboard projection', () => {
expect(component).toContain('className="dashboard-page client-command-center defender-page"');
expect(component).toContain('className="client-overview-header defender-header"');
expect(component).toContain('Source: Microsoft Defender for IoT ');
- expect(dashboard).toContain("[['overview', 'Overview'] as const]");
- expect(dashboard).toContain("[['defender', 'Microsoft Defender for IoT'] as const]");
- expect(dashboard).toContain(' app.recordScopedRiskAcceptance(write) : undefined}');
expect(styles).toContain('padding: var(--panel-inset)');
diff --git a/tests/importers/__snapshots__/import-compatibility.test.ts.snap b/tests/importers/__snapshots__/import-compatibility.test.ts.snap
index b03aeae..0b44860 100644
--- a/tests/importers/__snapshots__/import-compatibility.test.ts.snap
+++ b/tests/importers/__snapshots__/import-compatibility.test.ts.snap
@@ -3098,6 +3098,1753 @@ exports[`protected scanner import contracts (synthetic data only) > 'cis-cat-sum
}
`;
+exports[`protected scanner import contracts (synthetic data only) > 'defender-for-iot-alerts-csv' retains recognition, preview and approved canonical results in browser and backend 1`] = `
+{
+ "approved": {
+ "recordCount": 3,
+ "selected": true,
+ },
+ "definitions": [
+ {
+ "canonicalKey": "defender-for-iot-alerts:synthetic unauthorized plc programming",
+ "category": undefined,
+ "description": "Synthetic programming from 192.0.2.41",
+ "frameworkReferences": [],
+ "identity": {
+ "durable": true,
+ "evidence": [
+ "Werkzeugkennung und gemappte native Regel-ID sind vorhanden.",
+ ],
+ "strategy": "tool-rule-id",
+ "version": 1,
+ },
+ "recommendation": undefined,
+ "title": "Synthetic Unauthorized PLC Programming",
+ },
+ {
+ "canonicalKey": "defender-for-iot-alerts:synthetic internet connection",
+ "category": undefined,
+ "description": "Synthetic outbound connection",
+ "frameworkReferences": [],
+ "identity": {
+ "durable": true,
+ "evidence": [
+ "Werkzeugkennung und gemappte native Regel-ID sind vorhanden.",
+ ],
+ "strategy": "tool-rule-id",
+ "version": 1,
+ },
+ "recommendation": undefined,
+ "title": "Synthetic Internet Connection",
+ },
+ {
+ "canonicalKey": "defender-for-iot-alerts:synthetic unused protocol",
+ "category": undefined,
+ "description": "Synthetic unused protocol",
+ "frameworkReferences": [],
+ "identity": {
+ "durable": true,
+ "evidence": [
+ "Werkzeugkennung und gemappte native Regel-ID sind vorhanden.",
+ ],
+ "strategy": "tool-rule-id",
+ "version": 1,
+ },
+ "recommendation": undefined,
+ "title": "Synthetic Unused Protocol",
+ },
+ ],
+ "evidence": [
+ {
+ "finding": {
+ "canonicalRuleId": "Synthetic Unauthorized PLC Programming",
+ "originalRuleId": "Synthetic Unauthorized PLC Programming",
+ "originalTitle": "Synthetic Unauthorized PLC Programming",
+ "tool": {
+ "id": "defender-for-iot-alerts",
+ "name": "Microsoft Defender for IoT Alerts",
+ },
+ },
+ "kind": "object",
+ "sourceFieldKeys": [
+ "source-observations",
+ ],
+ "sourcePosition": {
+ "cellReference": undefined,
+ "columnIndex": undefined,
+ "elementSelector": undefined,
+ "objectPath": undefined,
+ "rowIndex": 1,
+ },
+ "value": [
+ {
+ "fields": {
+ "Acknowledged": "False",
+ "Additional Information": "192.0.2.21 over port CIP (44818)",
+ "Description": "Synthetic programming from 192.0.2.41",
+ "Destination Address": "192.0.2.21",
+ "First Detection": "11/09/2026 10:05:12",
+ "Id": "A-1",
+ "Last Activity": "11/09/2026 10:07:11",
+ "Last Detection": "11/09/2026 10:07:11",
+ "Severity": "Critical",
+ "Source Address": "192.0.2.41",
+ "Title": "Synthetic Unauthorized PLC Programming",
+ },
+ "source": {
+ "rowIndex": 1,
+ "tableIndex": 0,
+ },
+ },
+ ],
+ },
+ {
+ "finding": {
+ "canonicalRuleId": "Synthetic Internet Connection",
+ "originalRuleId": "Synthetic Internet Connection",
+ "originalTitle": "Synthetic Internet Connection",
+ "tool": {
+ "id": "defender-for-iot-alerts",
+ "name": "Microsoft Defender for IoT Alerts",
+ },
+ },
+ "kind": "object",
+ "sourceFieldKeys": [
+ "source-observations",
+ ],
+ "sourcePosition": {
+ "cellReference": undefined,
+ "columnIndex": undefined,
+ "elementSelector": undefined,
+ "objectPath": undefined,
+ "rowIndex": 2,
+ },
+ "value": [
+ {
+ "fields": {
+ "Acknowledged": "False",
+ "Additional Information": "198.51.100.7 over port HTTPS (443)",
+ "Description": "Synthetic outbound connection",
+ "Destination Address": "",
+ "First Detection": "13/09/2026 09:00:00",
+ "Id": "A-3",
+ "Last Activity": "13/09/2026 09:30:00",
+ "Last Detection": "13/09/2026 09:30:00",
+ "Severity": "Major",
+ "Source Address": "00-00-5E-00-53-01",
+ "Title": "Synthetic Internet Connection",
+ },
+ "source": {
+ "rowIndex": 2,
+ "tableIndex": 0,
+ },
+ },
+ ],
+ },
+ {
+ "finding": {
+ "canonicalRuleId": "Synthetic Unused Protocol",
+ "originalRuleId": "Synthetic Unused Protocol",
+ "originalTitle": "Synthetic Unused Protocol",
+ "tool": {
+ "id": "defender-for-iot-alerts",
+ "name": "Microsoft Defender for IoT Alerts",
+ },
+ },
+ "kind": "object",
+ "sourceFieldKeys": [
+ "source-observations",
+ ],
+ "sourcePosition": {
+ "cellReference": undefined,
+ "columnIndex": undefined,
+ "elementSelector": undefined,
+ "objectPath": undefined,
+ "rowIndex": 3,
+ },
+ "value": [
+ {
+ "fields": {
+ "Acknowledged": "True",
+ "Additional Information": "",
+ "Description": "Synthetic unused protocol",
+ "Destination Address": "192.0.2.51",
+ "First Detection": "14/09/2026 07:00:00",
+ "Id": "A-4",
+ "Last Activity": "14/09/2026 07:05:00",
+ "Last Detection": "14/09/2026 07:00:00",
+ "Severity": "Warning",
+ "Source Address": "192.0.2.50",
+ "Title": "Synthetic Unused Protocol",
+ },
+ "source": {
+ "rowIndex": 3,
+ "tableIndex": 0,
+ },
+ },
+ ],
+ },
+ ],
+ "findings": [
+ {
+ "affectedAsset": undefined,
+ "fieldMappings": [
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "Synthetic Unauthorized PLC Programming",
+ "sourceFieldKeys": [
+ "title",
+ ],
+ "targetRole": "rule-id",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "Critical",
+ "sourceFieldKeys": [
+ "severity",
+ ],
+ "targetRole": "severity",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "Critical",
+ "sourceFieldKeys": [
+ "defender-severity",
+ ],
+ "targetRole": "additional",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "Unacknowledged",
+ "sourceFieldKeys": [
+ "status",
+ ],
+ "targetRole": "status",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "Synthetic programming from 192.0.2.41",
+ "sourceFieldKeys": [
+ "description",
+ ],
+ "targetRole": "description",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "1",
+ "sourceFieldKeys": [
+ "alert-count",
+ ],
+ "targetRole": "additional",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "1",
+ "sourceFieldKeys": [
+ "detail-rows",
+ ],
+ "targetRole": "additional",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "1",
+ "sourceFieldKeys": [
+ "unacknowledged-alerts",
+ ],
+ "targetRole": "additional",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "| Id | Acknowledged | First Detection | Last Detection | Last Activity | Source Address | Destination Address | Description | Additional Information |
+| --- | --- | --- | --- | --- | --- | --- | --- | --- |
+| A-1 | False | 11/09/2026 10:05:12 | 11/09/2026 10:07:11 | 11/09/2026 10:07:11 | 192.0.2.41 | 192.0.2.21 | Synthetic programming from 192.0.2.41 | 192.0.2.21 over port CIP (44818) |",
+ "sourceFieldKeys": [
+ "alerts",
+ ],
+ "targetRole": "additional",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "[{"fields":{"Id":"A-1","Title":"Synthetic Unauthorized PLC Programming","First Detection":"11/09/2026 10:05:12","Last Detection":"11/09/2026 10:07:11","Last Activity":"11/09/2026 10:07:11","Severity":"Critical","Description":"Synthetic programming from 192.0.2.41","Acknowledged":"False","Additional Information":"192.0.2.21 over port CIP (44818)","Source Address":"192.0.2.41","Destination Address":"192.0.2.21"},"source":{"tableIndex":0,"rowIndex":1}}]",
+ "sourceFieldKeys": [
+ "source-observations",
+ ],
+ "targetRole": "evidence",
+ },
+ ],
+ "originalFields": [
+ {
+ "columnIndex": undefined,
+ "key": "title",
+ "label": "Title",
+ "path": undefined,
+ "rawValue": "Synthetic Unauthorized PLC Programming",
+ "value": "Synthetic Unauthorized PLC Programming",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "severity",
+ "label": "Severity",
+ "path": undefined,
+ "rawValue": "Critical",
+ "value": "Critical",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "defender-severity",
+ "label": "Defender Severity",
+ "path": undefined,
+ "rawValue": "Critical",
+ "value": "Critical",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "status",
+ "label": "Status",
+ "path": undefined,
+ "rawValue": "Unacknowledged",
+ "value": "Unacknowledged",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "description",
+ "label": "Description",
+ "path": undefined,
+ "rawValue": "Synthetic programming from 192.0.2.41",
+ "value": "Synthetic programming from 192.0.2.41",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "alert-count",
+ "label": "Alert Count",
+ "path": undefined,
+ "rawValue": "1",
+ "value": 1,
+ },
+ {
+ "columnIndex": undefined,
+ "key": "detail-rows",
+ "label": "Detail Rows",
+ "path": undefined,
+ "rawValue": "1",
+ "value": 1,
+ },
+ {
+ "columnIndex": undefined,
+ "key": "unacknowledged-alerts",
+ "label": "Unacknowledged Alerts",
+ "path": undefined,
+ "rawValue": "1",
+ "value": 1,
+ },
+ {
+ "columnIndex": undefined,
+ "key": "alerts",
+ "label": "Alerts",
+ "path": undefined,
+ "rawValue": "| Id | Acknowledged | First Detection | Last Detection | Last Activity | Source Address | Destination Address | Description | Additional Information |
+| --- | --- | --- | --- | --- | --- | --- | --- | --- |
+| A-1 | False | 11/09/2026 10:05:12 | 11/09/2026 10:07:11 | 11/09/2026 10:07:11 | 192.0.2.41 | 192.0.2.21 | Synthetic programming from 192.0.2.41 | 192.0.2.21 over port CIP (44818) |",
+ "value": "| Id | Acknowledged | First Detection | Last Detection | Last Activity | Source Address | Destination Address | Description | Additional Information |
+| --- | --- | --- | --- | --- | --- | --- | --- | --- |
+| A-1 | False | 11/09/2026 10:05:12 | 11/09/2026 10:07:11 | 11/09/2026 10:07:11 | 192.0.2.41 | 192.0.2.21 | Synthetic programming from 192.0.2.41 | 192.0.2.21 over port CIP (44818) |",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "source-observations",
+ "label": "Source Observations",
+ "path": undefined,
+ "rawValue": "[{"fields":{"Id":"A-1","Title":"Synthetic Unauthorized PLC Programming","First Detection":"11/09/2026 10:05:12","Last Detection":"11/09/2026 10:07:11","Last Activity":"11/09/2026 10:07:11","Severity":"Critical","Description":"Synthetic programming from 192.0.2.41","Acknowledged":"False","Additional Information":"192.0.2.21 over port CIP (44818)","Source Address":"192.0.2.41","Destination Address":"192.0.2.21"},"source":{"tableIndex":0,"rowIndex":1}}]",
+ "value": [
+ {
+ "fields": {
+ "Acknowledged": "False",
+ "Additional Information": "192.0.2.21 over port CIP (44818)",
+ "Description": "Synthetic programming from 192.0.2.41",
+ "Destination Address": "192.0.2.21",
+ "First Detection": "11/09/2026 10:05:12",
+ "Id": "A-1",
+ "Last Activity": "11/09/2026 10:07:11",
+ "Last Detection": "11/09/2026 10:07:11",
+ "Severity": "Critical",
+ "Source Address": "192.0.2.41",
+ "Title": "Synthetic Unauthorized PLC Programming",
+ },
+ "source": {
+ "rowIndex": 1,
+ "tableIndex": 0,
+ },
+ },
+ ],
+ },
+ ],
+ "outcome": {
+ "normalizedValue": "warning",
+ "originalValue": "Unacknowledged",
+ },
+ "score": undefined,
+ "severity": {
+ "normalizedValue": "critical",
+ "originalValue": "Critical",
+ },
+ "sourceIdentity": {
+ "canonicalRuleId": "Synthetic Unauthorized PLC Programming",
+ "originalRuleId": "Synthetic Unauthorized PLC Programming",
+ "originalTitle": "Synthetic Unauthorized PLC Programming",
+ "tool": {
+ "id": "defender-for-iot-alerts",
+ "name": "Microsoft Defender for IoT Alerts",
+ },
+ },
+ "sourcePosition": {
+ "cellReference": undefined,
+ "columnIndex": undefined,
+ "elementSelector": undefined,
+ "objectPath": undefined,
+ "rowIndex": 1,
+ },
+ "status": {
+ "normalizedValue": "open",
+ "originalValue": "Unacknowledged",
+ },
+ "technicalExecutionState": {
+ "normalizedValue": "completed",
+ "originalValue": "Unacknowledged",
+ },
+ },
+ {
+ "affectedAsset": undefined,
+ "fieldMappings": [
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "Synthetic Internet Connection",
+ "sourceFieldKeys": [
+ "title",
+ ],
+ "targetRole": "rule-id",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "High",
+ "sourceFieldKeys": [
+ "severity",
+ ],
+ "targetRole": "severity",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "Major",
+ "sourceFieldKeys": [
+ "defender-severity",
+ ],
+ "targetRole": "additional",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "Unacknowledged",
+ "sourceFieldKeys": [
+ "status",
+ ],
+ "targetRole": "status",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "Synthetic outbound connection",
+ "sourceFieldKeys": [
+ "description",
+ ],
+ "targetRole": "description",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "1",
+ "sourceFieldKeys": [
+ "alert-count",
+ ],
+ "targetRole": "additional",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "1",
+ "sourceFieldKeys": [
+ "detail-rows",
+ ],
+ "targetRole": "additional",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "1",
+ "sourceFieldKeys": [
+ "unacknowledged-alerts",
+ ],
+ "targetRole": "additional",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "| Id | Acknowledged | First Detection | Last Detection | Last Activity | Source Address | Destination Address | Description | Additional Information |
+| --- | --- | --- | --- | --- | --- | --- | --- | --- |
+| A-3 | False | 13/09/2026 09:00:00 | 13/09/2026 09:30:00 | 13/09/2026 09:30:00 | 00-00-5E-00-53-01 | | Synthetic outbound connection | 198.51.100.7 over port HTTPS (443) |",
+ "sourceFieldKeys": [
+ "alerts",
+ ],
+ "targetRole": "additional",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "[{"fields":{"Id":"A-3","Title":"Synthetic Internet Connection","First Detection":"13/09/2026 09:00:00","Last Detection":"13/09/2026 09:30:00","Last Activity":"13/09/2026 09:30:00","Severity":"Major","Description":"Synthetic outbound connection","Acknowledged":"False","Additional Information":"198.51.100.7 over port HTTPS (443)","Source Address":"00-00-5E-00-53-01","Destination Address":""},"source":{"tableIndex":0,"rowIndex":2}}]",
+ "sourceFieldKeys": [
+ "source-observations",
+ ],
+ "targetRole": "evidence",
+ },
+ ],
+ "originalFields": [
+ {
+ "columnIndex": undefined,
+ "key": "title",
+ "label": "Title",
+ "path": undefined,
+ "rawValue": "Synthetic Internet Connection",
+ "value": "Synthetic Internet Connection",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "severity",
+ "label": "Severity",
+ "path": undefined,
+ "rawValue": "High",
+ "value": "High",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "defender-severity",
+ "label": "Defender Severity",
+ "path": undefined,
+ "rawValue": "Major",
+ "value": "Major",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "status",
+ "label": "Status",
+ "path": undefined,
+ "rawValue": "Unacknowledged",
+ "value": "Unacknowledged",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "description",
+ "label": "Description",
+ "path": undefined,
+ "rawValue": "Synthetic outbound connection",
+ "value": "Synthetic outbound connection",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "alert-count",
+ "label": "Alert Count",
+ "path": undefined,
+ "rawValue": "1",
+ "value": 1,
+ },
+ {
+ "columnIndex": undefined,
+ "key": "detail-rows",
+ "label": "Detail Rows",
+ "path": undefined,
+ "rawValue": "1",
+ "value": 1,
+ },
+ {
+ "columnIndex": undefined,
+ "key": "unacknowledged-alerts",
+ "label": "Unacknowledged Alerts",
+ "path": undefined,
+ "rawValue": "1",
+ "value": 1,
+ },
+ {
+ "columnIndex": undefined,
+ "key": "alerts",
+ "label": "Alerts",
+ "path": undefined,
+ "rawValue": "| Id | Acknowledged | First Detection | Last Detection | Last Activity | Source Address | Destination Address | Description | Additional Information |
+| --- | --- | --- | --- | --- | --- | --- | --- | --- |
+| A-3 | False | 13/09/2026 09:00:00 | 13/09/2026 09:30:00 | 13/09/2026 09:30:00 | 00-00-5E-00-53-01 | | Synthetic outbound connection | 198.51.100.7 over port HTTPS (443) |",
+ "value": "| Id | Acknowledged | First Detection | Last Detection | Last Activity | Source Address | Destination Address | Description | Additional Information |
+| --- | --- | --- | --- | --- | --- | --- | --- | --- |
+| A-3 | False | 13/09/2026 09:00:00 | 13/09/2026 09:30:00 | 13/09/2026 09:30:00 | 00-00-5E-00-53-01 | | Synthetic outbound connection | 198.51.100.7 over port HTTPS (443) |",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "source-observations",
+ "label": "Source Observations",
+ "path": undefined,
+ "rawValue": "[{"fields":{"Id":"A-3","Title":"Synthetic Internet Connection","First Detection":"13/09/2026 09:00:00","Last Detection":"13/09/2026 09:30:00","Last Activity":"13/09/2026 09:30:00","Severity":"Major","Description":"Synthetic outbound connection","Acknowledged":"False","Additional Information":"198.51.100.7 over port HTTPS (443)","Source Address":"00-00-5E-00-53-01","Destination Address":""},"source":{"tableIndex":0,"rowIndex":2}}]",
+ "value": [
+ {
+ "fields": {
+ "Acknowledged": "False",
+ "Additional Information": "198.51.100.7 over port HTTPS (443)",
+ "Description": "Synthetic outbound connection",
+ "Destination Address": "",
+ "First Detection": "13/09/2026 09:00:00",
+ "Id": "A-3",
+ "Last Activity": "13/09/2026 09:30:00",
+ "Last Detection": "13/09/2026 09:30:00",
+ "Severity": "Major",
+ "Source Address": "00-00-5E-00-53-01",
+ "Title": "Synthetic Internet Connection",
+ },
+ "source": {
+ "rowIndex": 2,
+ "tableIndex": 0,
+ },
+ },
+ ],
+ },
+ ],
+ "outcome": {
+ "normalizedValue": "warning",
+ "originalValue": "Unacknowledged",
+ },
+ "score": undefined,
+ "severity": {
+ "normalizedValue": "high",
+ "originalValue": "High",
+ },
+ "sourceIdentity": {
+ "canonicalRuleId": "Synthetic Internet Connection",
+ "originalRuleId": "Synthetic Internet Connection",
+ "originalTitle": "Synthetic Internet Connection",
+ "tool": {
+ "id": "defender-for-iot-alerts",
+ "name": "Microsoft Defender for IoT Alerts",
+ },
+ },
+ "sourcePosition": {
+ "cellReference": undefined,
+ "columnIndex": undefined,
+ "elementSelector": undefined,
+ "objectPath": undefined,
+ "rowIndex": 2,
+ },
+ "status": {
+ "normalizedValue": "open",
+ "originalValue": "Unacknowledged",
+ },
+ "technicalExecutionState": {
+ "normalizedValue": "completed",
+ "originalValue": "Unacknowledged",
+ },
+ },
+ {
+ "affectedAsset": undefined,
+ "fieldMappings": [
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "Synthetic Unused Protocol",
+ "sourceFieldKeys": [
+ "title",
+ ],
+ "targetRole": "rule-id",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "Low",
+ "sourceFieldKeys": [
+ "severity",
+ ],
+ "targetRole": "severity",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "Warning",
+ "sourceFieldKeys": [
+ "defender-severity",
+ ],
+ "targetRole": "additional",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "Acknowledged",
+ "sourceFieldKeys": [
+ "status",
+ ],
+ "targetRole": "status",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "Synthetic unused protocol",
+ "sourceFieldKeys": [
+ "description",
+ ],
+ "targetRole": "description",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "1",
+ "sourceFieldKeys": [
+ "alert-count",
+ ],
+ "targetRole": "additional",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "1",
+ "sourceFieldKeys": [
+ "detail-rows",
+ ],
+ "targetRole": "additional",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "0",
+ "sourceFieldKeys": [
+ "unacknowledged-alerts",
+ ],
+ "targetRole": "additional",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "| Id | Acknowledged | First Detection | Last Detection | Last Activity | Source Address | Destination Address | Description | Additional Information |
+| --- | --- | --- | --- | --- | --- | --- | --- | --- |
+| A-4 | True | 14/09/2026 07:00:00 | 14/09/2026 07:00:00 | 14/09/2026 07:05:00 | 192.0.2.50 | 192.0.2.51 | Synthetic unused protocol | |",
+ "sourceFieldKeys": [
+ "alerts",
+ ],
+ "targetRole": "additional",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "[{"fields":{"Id":"A-4","Title":"Synthetic Unused Protocol","First Detection":"14/09/2026 07:00:00","Last Detection":"14/09/2026 07:00:00","Last Activity":"14/09/2026 07:05:00","Severity":"Warning","Description":"Synthetic unused protocol","Acknowledged":"True","Additional Information":"","Source Address":"192.0.2.50","Destination Address":"192.0.2.51"},"source":{"tableIndex":0,"rowIndex":3}}]",
+ "sourceFieldKeys": [
+ "source-observations",
+ ],
+ "targetRole": "evidence",
+ },
+ ],
+ "originalFields": [
+ {
+ "columnIndex": undefined,
+ "key": "title",
+ "label": "Title",
+ "path": undefined,
+ "rawValue": "Synthetic Unused Protocol",
+ "value": "Synthetic Unused Protocol",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "severity",
+ "label": "Severity",
+ "path": undefined,
+ "rawValue": "Low",
+ "value": "Low",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "defender-severity",
+ "label": "Defender Severity",
+ "path": undefined,
+ "rawValue": "Warning",
+ "value": "Warning",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "status",
+ "label": "Status",
+ "path": undefined,
+ "rawValue": "Acknowledged",
+ "value": "Acknowledged",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "description",
+ "label": "Description",
+ "path": undefined,
+ "rawValue": "Synthetic unused protocol",
+ "value": "Synthetic unused protocol",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "alert-count",
+ "label": "Alert Count",
+ "path": undefined,
+ "rawValue": "1",
+ "value": 1,
+ },
+ {
+ "columnIndex": undefined,
+ "key": "detail-rows",
+ "label": "Detail Rows",
+ "path": undefined,
+ "rawValue": "1",
+ "value": 1,
+ },
+ {
+ "columnIndex": undefined,
+ "key": "unacknowledged-alerts",
+ "label": "Unacknowledged Alerts",
+ "path": undefined,
+ "rawValue": "0",
+ "value": 0,
+ },
+ {
+ "columnIndex": undefined,
+ "key": "alerts",
+ "label": "Alerts",
+ "path": undefined,
+ "rawValue": "| Id | Acknowledged | First Detection | Last Detection | Last Activity | Source Address | Destination Address | Description | Additional Information |
+| --- | --- | --- | --- | --- | --- | --- | --- | --- |
+| A-4 | True | 14/09/2026 07:00:00 | 14/09/2026 07:00:00 | 14/09/2026 07:05:00 | 192.0.2.50 | 192.0.2.51 | Synthetic unused protocol | |",
+ "value": "| Id | Acknowledged | First Detection | Last Detection | Last Activity | Source Address | Destination Address | Description | Additional Information |
+| --- | --- | --- | --- | --- | --- | --- | --- | --- |
+| A-4 | True | 14/09/2026 07:00:00 | 14/09/2026 07:00:00 | 14/09/2026 07:05:00 | 192.0.2.50 | 192.0.2.51 | Synthetic unused protocol | |",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "source-observations",
+ "label": "Source Observations",
+ "path": undefined,
+ "rawValue": "[{"fields":{"Id":"A-4","Title":"Synthetic Unused Protocol","First Detection":"14/09/2026 07:00:00","Last Detection":"14/09/2026 07:00:00","Last Activity":"14/09/2026 07:05:00","Severity":"Warning","Description":"Synthetic unused protocol","Acknowledged":"True","Additional Information":"","Source Address":"192.0.2.50","Destination Address":"192.0.2.51"},"source":{"tableIndex":0,"rowIndex":3}}]",
+ "value": [
+ {
+ "fields": {
+ "Acknowledged": "True",
+ "Additional Information": "",
+ "Description": "Synthetic unused protocol",
+ "Destination Address": "192.0.2.51",
+ "First Detection": "14/09/2026 07:00:00",
+ "Id": "A-4",
+ "Last Activity": "14/09/2026 07:05:00",
+ "Last Detection": "14/09/2026 07:00:00",
+ "Severity": "Warning",
+ "Source Address": "192.0.2.50",
+ "Title": "Synthetic Unused Protocol",
+ },
+ "source": {
+ "rowIndex": 3,
+ "tableIndex": 0,
+ },
+ },
+ ],
+ },
+ ],
+ "outcome": {
+ "normalizedValue": "informational",
+ "originalValue": "Acknowledged",
+ },
+ "score": undefined,
+ "severity": {
+ "normalizedValue": "low",
+ "originalValue": "Low",
+ },
+ "sourceIdentity": {
+ "canonicalRuleId": "Synthetic Unused Protocol",
+ "originalRuleId": "Synthetic Unused Protocol",
+ "originalTitle": "Synthetic Unused Protocol",
+ "tool": {
+ "id": "defender-for-iot-alerts",
+ "name": "Microsoft Defender for IoT Alerts",
+ },
+ },
+ "sourcePosition": {
+ "cellReference": undefined,
+ "columnIndex": undefined,
+ "elementSelector": undefined,
+ "objectPath": undefined,
+ "rowIndex": 3,
+ },
+ "status": {
+ "normalizedValue": "open",
+ "originalValue": "Acknowledged",
+ },
+ "technicalExecutionState": {
+ "normalizedValue": "completed",
+ "originalValue": "Acknowledged",
+ },
+ },
+ ],
+ "observations": [],
+ "preview": {
+ "mappingsToConfirm": 0,
+ "recordCount": 3,
+ "selected": true,
+ },
+ "recognition": "defender-for-iot-alerts",
+ "reportSections": [],
+ "scores": [
+ {
+ "assessed": 2,
+ "assessmentId": "assessment-protected-dummy-import",
+ "comparisonKey": undefined,
+ "contentSha256": "c554f60079cf276f21c90b2b9c0b4b3fe6976945fd726b6d1ba1331391e3c184",
+ "deviations": 2,
+ "eligible": 3,
+ "evaluationScopeKey": "["[\\"Synthetic Internet Connection\\",\\"evaluated\\"]","[\\"Synthetic Unauthorized PLC Programming\\",\\"evaluated\\"]","[\\"Synthetic Unused Protocol\\",\\"informational\\"]"]",
+ "importFileIds": [
+ "file-protected-dummy-import-0",
+ ],
+ "issues": [
+ "Evidence present, not scoreable",
+ "Incomplete assessment",
+ "Scan identity or time not verified",
+ "Scanner version unavailable",
+ ],
+ "method": "unavailable",
+ "nativeProperty": undefined,
+ "nativeValue": undefined,
+ "observedAt": undefined,
+ "occurrenceIds": [
+ "occurrence-protected-dummy-import-0-0",
+ "occurrence-protected-dummy-import-0-1",
+ "occurrence-protected-dummy-import-0-2",
+ ],
+ "openOccurrenceIds": [
+ "occurrence-protected-dummy-import-0-0",
+ "occurrence-protected-dummy-import-0-1",
+ ],
+ "passed": 0,
+ "policy": {
+ "defaultWeight": 1,
+ "version": "1.0.0",
+ "weights": {
+ "cis-cat": 1,
+ "group-policy": 1,
+ "maester": 1,
+ "pingcastle": 1,
+ "purple-knight": 1,
+ },
+ },
+ "processingKey": "[null,"defender-for-iot-alerts","1.6.0","1.2.0","1.0.0","1.0.0"]",
+ "resultSignature": "["[\\"Synthetic Internet Connection\\",null,\\"warning\\"]","[\\"Synthetic Unauthorized PLC Programming\\",null,\\"warning\\"]","[\\"Synthetic Unused Protocol\\",null,\\"informational\\"]"]",
+ "runIds": [
+ "run-protected-dummy-import",
+ ],
+ "score": undefined,
+ "toolId": "defender-for-iot-alerts",
+ "toolName": "Microsoft Defender for IoT Alerts",
+ },
+ ],
+ "tables": [
+ {
+ "columnCount": 11,
+ "hidden": undefined,
+ "index": 0,
+ "name": "CSV",
+ "rowCount": 3,
+ "rows": undefined,
+ },
+ ],
+}
+`;
+
+exports[`protected scanner import contracts (synthetic data only) > 'defender-for-iot-vulnerabilities-csv' retains recognition, preview and approved canonical results in browser and backend 1`] = `
+{
+ "approved": {
+ "recordCount": 3,
+ "selected": true,
+ },
+ "definitions": [
+ {
+ "canonicalKey": "defender-for-iot-vulnerabilities:cve-2099-1001",
+ "category": undefined,
+ "description": undefined,
+ "frameworkReferences": [],
+ "identity": {
+ "durable": true,
+ "evidence": [
+ "Werkzeugkennung und gemappte native Regel-ID sind vorhanden.",
+ ],
+ "strategy": "tool-rule-id",
+ "version": 1,
+ },
+ "recommendation": undefined,
+ "title": "CVE-2099-1001",
+ },
+ {
+ "canonicalKey": "defender-for-iot-vulnerabilities:cve-2099-1002",
+ "category": undefined,
+ "description": undefined,
+ "frameworkReferences": [],
+ "identity": {
+ "durable": true,
+ "evidence": [
+ "Werkzeugkennung und gemappte native Regel-ID sind vorhanden.",
+ ],
+ "strategy": "tool-rule-id",
+ "version": 1,
+ },
+ "recommendation": undefined,
+ "title": "CVE-2099-1002",
+ },
+ {
+ "canonicalKey": "defender-for-iot-vulnerabilities:cve-2099-1005",
+ "category": undefined,
+ "description": undefined,
+ "frameworkReferences": [],
+ "identity": {
+ "durable": true,
+ "evidence": [
+ "Werkzeugkennung und gemappte native Regel-ID sind vorhanden.",
+ ],
+ "strategy": "tool-rule-id",
+ "version": 1,
+ },
+ "recommendation": undefined,
+ "title": "CVE-2099-1005",
+ },
+ ],
+ "evidence": [
+ {
+ "finding": {
+ "canonicalRuleId": "CVE-2099-1001",
+ "originalRuleId": "CVE-2099-1001",
+ "originalTitle": "CVE-2099-1001",
+ "tool": {
+ "id": "defender-for-iot-vulnerabilities",
+ "name": "Microsoft Defender for IoT",
+ },
+ },
+ "kind": "object",
+ "sourceFieldKeys": [
+ "source-observations",
+ ],
+ "sourcePosition": {
+ "cellReference": undefined,
+ "columnIndex": undefined,
+ "elementSelector": undefined,
+ "objectPath": undefined,
+ "rowIndex": 1,
+ },
+ "value": [
+ {
+ "additionalSourceFields": {
+ "CVSS Score": "9.1",
+ "Component": "Synthetic component",
+ "Device ID": "DEV-OT-001",
+ "Status": "Open",
+ },
+ "cve": "CVE-2099-1001",
+ "description": "",
+ "ip": "192.0.2.21",
+ "name": "PLC-LINE1-01",
+ "sensor": "SENS-RB-01",
+ "severity": "Critical",
+ "site": "Riverbend Plant",
+ "source": {
+ "rowIndex": 1,
+ "tableIndex": 0,
+ },
+ },
+ ],
+ },
+ {
+ "finding": {
+ "canonicalRuleId": "CVE-2099-1002",
+ "originalRuleId": "CVE-2099-1002",
+ "originalTitle": "CVE-2099-1002",
+ "tool": {
+ "id": "defender-for-iot-vulnerabilities",
+ "name": "Microsoft Defender for IoT",
+ },
+ },
+ "kind": "object",
+ "sourceFieldKeys": [
+ "source-observations",
+ ],
+ "sourcePosition": {
+ "cellReference": undefined,
+ "columnIndex": undefined,
+ "elementSelector": undefined,
+ "objectPath": undefined,
+ "rowIndex": 2,
+ },
+ "value": [
+ {
+ "additionalSourceFields": {
+ "CVSS Score": "7.5",
+ "Component": "Synthetic component",
+ "Device ID": "DEV-OT-002",
+ "Status": "Open",
+ },
+ "cve": "CVE-2099-1002",
+ "description": "",
+ "ip": "192.0.2.31",
+ "name": "HMI-LINE1-01",
+ "sensor": "SENS-RB-01",
+ "severity": "High",
+ "site": "Riverbend Plant",
+ "source": {
+ "rowIndex": 2,
+ "tableIndex": 0,
+ },
+ },
+ ],
+ },
+ {
+ "finding": {
+ "canonicalRuleId": "CVE-2099-1005",
+ "originalRuleId": "CVE-2099-1005",
+ "originalTitle": "CVE-2099-1005",
+ "tool": {
+ "id": "defender-for-iot-vulnerabilities",
+ "name": "Microsoft Defender for IoT",
+ },
+ },
+ "kind": "object",
+ "sourceFieldKeys": [
+ "source-observations",
+ ],
+ "sourcePosition": {
+ "cellReference": undefined,
+ "columnIndex": undefined,
+ "elementSelector": undefined,
+ "objectPath": undefined,
+ "rowIndex": 3,
+ },
+ "value": [
+ {
+ "additionalSourceFields": {
+ "CVSS Score": "8.2",
+ "Component": "Synthetic component",
+ "Device ID": "DEV-OT-004",
+ "Status": "Mitigation planned",
+ },
+ "cve": "CVE-2099-1005",
+ "description": "",
+ "ip": "192.0.2.51",
+ "name": "HISTORIAN-01",
+ "sensor": "SENS-RB-01",
+ "severity": "High",
+ "site": "Riverbend Plant",
+ "source": {
+ "rowIndex": 3,
+ "tableIndex": 0,
+ },
+ },
+ ],
+ },
+ ],
+ "findings": [
+ {
+ "affectedAsset": {
+ "normalizedValue": "1",
+ "originalValue": 1,
+ },
+ "fieldMappings": [
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "CVE-2099-1001",
+ "sourceFieldKeys": [
+ "cve",
+ ],
+ "targetRole": "rule-id",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "1",
+ "sourceFieldKeys": [
+ "affected-device-count",
+ ],
+ "targetRole": "asset",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "Critical",
+ "sourceFieldKeys": [
+ "severity",
+ ],
+ "targetRole": "severity",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "",
+ "sourceFieldKeys": [
+ "description",
+ ],
+ "targetRole": "description",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "1",
+ "sourceFieldKeys": [
+ "observation-count",
+ ],
+ "targetRole": "additional",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "| Name | IP Address | Scanner | Site |
+| --- | --- | --- | --- |
+| PLC-LINE1-01 | 192.0.2.21 | SENS-RB-01 | Riverbend Plant |",
+ "sourceFieldKeys": [
+ "affected-devices",
+ ],
+ "targetRole": "additional",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "[{"name":"PLC-LINE1-01","ip":"192.0.2.21","site":"Riverbend Plant","sensor":"SENS-RB-01","cve":"CVE-2099-1001","severity":"Critical","description":"","additionalSourceFields":{"Device ID":"DEV-OT-001","CVSS Score":"9.1","Component":"Synthetic component","Status":"Open"},"source":{"tableIndex":0,"rowIndex":1}}]",
+ "sourceFieldKeys": [
+ "source-observations",
+ ],
+ "targetRole": "evidence",
+ },
+ ],
+ "originalFields": [
+ {
+ "columnIndex": undefined,
+ "key": "cve",
+ "label": "CVE",
+ "path": undefined,
+ "rawValue": "CVE-2099-1001",
+ "value": "CVE-2099-1001",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "affected-device-count",
+ "label": "Affected Device Count",
+ "path": undefined,
+ "rawValue": "1",
+ "value": 1,
+ },
+ {
+ "columnIndex": undefined,
+ "key": "severity",
+ "label": "Severity",
+ "path": undefined,
+ "rawValue": "Critical",
+ "value": "Critical",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "description",
+ "label": "Description",
+ "path": undefined,
+ "rawValue": "",
+ "value": "",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "observation-count",
+ "label": "Observation Count",
+ "path": undefined,
+ "rawValue": "1",
+ "value": 1,
+ },
+ {
+ "columnIndex": undefined,
+ "key": "affected-devices",
+ "label": "Affected Devices",
+ "path": undefined,
+ "rawValue": "| Name | IP Address | Scanner | Site |
+| --- | --- | --- | --- |
+| PLC-LINE1-01 | 192.0.2.21 | SENS-RB-01 | Riverbend Plant |",
+ "value": "| Name | IP Address | Scanner | Site |
+| --- | --- | --- | --- |
+| PLC-LINE1-01 | 192.0.2.21 | SENS-RB-01 | Riverbend Plant |",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "source-observations",
+ "label": "Source Observations",
+ "path": undefined,
+ "rawValue": "[{"name":"PLC-LINE1-01","ip":"192.0.2.21","site":"Riverbend Plant","sensor":"SENS-RB-01","cve":"CVE-2099-1001","severity":"Critical","description":"","additionalSourceFields":{"Device ID":"DEV-OT-001","CVSS Score":"9.1","Component":"Synthetic component","Status":"Open"},"source":{"tableIndex":0,"rowIndex":1}}]",
+ "value": [
+ {
+ "additionalSourceFields": {
+ "CVSS Score": "9.1",
+ "Component": "Synthetic component",
+ "Device ID": "DEV-OT-001",
+ "Status": "Open",
+ },
+ "cve": "CVE-2099-1001",
+ "description": "",
+ "ip": "192.0.2.21",
+ "name": "PLC-LINE1-01",
+ "sensor": "SENS-RB-01",
+ "severity": "Critical",
+ "site": "Riverbend Plant",
+ "source": {
+ "rowIndex": 1,
+ "tableIndex": 0,
+ },
+ },
+ ],
+ },
+ ],
+ "outcome": {
+ "normalizedValue": "warning",
+ "originalValue": "",
+ },
+ "score": undefined,
+ "severity": {
+ "normalizedValue": "critical",
+ "originalValue": "Critical",
+ },
+ "sourceIdentity": {
+ "canonicalRuleId": "CVE-2099-1001",
+ "originalRuleId": "CVE-2099-1001",
+ "originalTitle": "CVE-2099-1001",
+ "tool": {
+ "id": "defender-for-iot-vulnerabilities",
+ "name": "Microsoft Defender for IoT",
+ },
+ },
+ "sourcePosition": {
+ "cellReference": undefined,
+ "columnIndex": undefined,
+ "elementSelector": undefined,
+ "objectPath": undefined,
+ "rowIndex": 1,
+ },
+ "status": {
+ "normalizedValue": "unknown",
+ "originalValue": "",
+ },
+ "technicalExecutionState": {
+ "normalizedValue": "completed",
+ "originalValue": "",
+ },
+ },
+ {
+ "affectedAsset": {
+ "normalizedValue": "1",
+ "originalValue": 1,
+ },
+ "fieldMappings": [
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "CVE-2099-1002",
+ "sourceFieldKeys": [
+ "cve",
+ ],
+ "targetRole": "rule-id",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "1",
+ "sourceFieldKeys": [
+ "affected-device-count",
+ ],
+ "targetRole": "asset",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "High",
+ "sourceFieldKeys": [
+ "severity",
+ ],
+ "targetRole": "severity",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "",
+ "sourceFieldKeys": [
+ "description",
+ ],
+ "targetRole": "description",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "1",
+ "sourceFieldKeys": [
+ "observation-count",
+ ],
+ "targetRole": "additional",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "| Name | IP Address | Scanner | Site |
+| --- | --- | --- | --- |
+| HMI-LINE1-01 | 192.0.2.31 | SENS-RB-01 | Riverbend Plant |",
+ "sourceFieldKeys": [
+ "affected-devices",
+ ],
+ "targetRole": "additional",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "[{"name":"HMI-LINE1-01","ip":"192.0.2.31","site":"Riverbend Plant","sensor":"SENS-RB-01","cve":"CVE-2099-1002","severity":"High","description":"","additionalSourceFields":{"Device ID":"DEV-OT-002","CVSS Score":"7.5","Component":"Synthetic component","Status":"Open"},"source":{"tableIndex":0,"rowIndex":2}}]",
+ "sourceFieldKeys": [
+ "source-observations",
+ ],
+ "targetRole": "evidence",
+ },
+ ],
+ "originalFields": [
+ {
+ "columnIndex": undefined,
+ "key": "cve",
+ "label": "CVE",
+ "path": undefined,
+ "rawValue": "CVE-2099-1002",
+ "value": "CVE-2099-1002",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "affected-device-count",
+ "label": "Affected Device Count",
+ "path": undefined,
+ "rawValue": "1",
+ "value": 1,
+ },
+ {
+ "columnIndex": undefined,
+ "key": "severity",
+ "label": "Severity",
+ "path": undefined,
+ "rawValue": "High",
+ "value": "High",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "description",
+ "label": "Description",
+ "path": undefined,
+ "rawValue": "",
+ "value": "",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "observation-count",
+ "label": "Observation Count",
+ "path": undefined,
+ "rawValue": "1",
+ "value": 1,
+ },
+ {
+ "columnIndex": undefined,
+ "key": "affected-devices",
+ "label": "Affected Devices",
+ "path": undefined,
+ "rawValue": "| Name | IP Address | Scanner | Site |
+| --- | --- | --- | --- |
+| HMI-LINE1-01 | 192.0.2.31 | SENS-RB-01 | Riverbend Plant |",
+ "value": "| Name | IP Address | Scanner | Site |
+| --- | --- | --- | --- |
+| HMI-LINE1-01 | 192.0.2.31 | SENS-RB-01 | Riverbend Plant |",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "source-observations",
+ "label": "Source Observations",
+ "path": undefined,
+ "rawValue": "[{"name":"HMI-LINE1-01","ip":"192.0.2.31","site":"Riverbend Plant","sensor":"SENS-RB-01","cve":"CVE-2099-1002","severity":"High","description":"","additionalSourceFields":{"Device ID":"DEV-OT-002","CVSS Score":"7.5","Component":"Synthetic component","Status":"Open"},"source":{"tableIndex":0,"rowIndex":2}}]",
+ "value": [
+ {
+ "additionalSourceFields": {
+ "CVSS Score": "7.5",
+ "Component": "Synthetic component",
+ "Device ID": "DEV-OT-002",
+ "Status": "Open",
+ },
+ "cve": "CVE-2099-1002",
+ "description": "",
+ "ip": "192.0.2.31",
+ "name": "HMI-LINE1-01",
+ "sensor": "SENS-RB-01",
+ "severity": "High",
+ "site": "Riverbend Plant",
+ "source": {
+ "rowIndex": 2,
+ "tableIndex": 0,
+ },
+ },
+ ],
+ },
+ ],
+ "outcome": {
+ "normalizedValue": "warning",
+ "originalValue": "",
+ },
+ "score": undefined,
+ "severity": {
+ "normalizedValue": "high",
+ "originalValue": "High",
+ },
+ "sourceIdentity": {
+ "canonicalRuleId": "CVE-2099-1002",
+ "originalRuleId": "CVE-2099-1002",
+ "originalTitle": "CVE-2099-1002",
+ "tool": {
+ "id": "defender-for-iot-vulnerabilities",
+ "name": "Microsoft Defender for IoT",
+ },
+ },
+ "sourcePosition": {
+ "cellReference": undefined,
+ "columnIndex": undefined,
+ "elementSelector": undefined,
+ "objectPath": undefined,
+ "rowIndex": 2,
+ },
+ "status": {
+ "normalizedValue": "unknown",
+ "originalValue": "",
+ },
+ "technicalExecutionState": {
+ "normalizedValue": "completed",
+ "originalValue": "",
+ },
+ },
+ {
+ "affectedAsset": {
+ "normalizedValue": "1",
+ "originalValue": 1,
+ },
+ "fieldMappings": [
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "CVE-2099-1005",
+ "sourceFieldKeys": [
+ "cve",
+ ],
+ "targetRole": "rule-id",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "1",
+ "sourceFieldKeys": [
+ "affected-device-count",
+ ],
+ "targetRole": "asset",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "High",
+ "sourceFieldKeys": [
+ "severity",
+ ],
+ "targetRole": "severity",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "",
+ "sourceFieldKeys": [
+ "description",
+ ],
+ "targetRole": "description",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "1",
+ "sourceFieldKeys": [
+ "observation-count",
+ ],
+ "targetRole": "additional",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "| Name | IP Address | Scanner | Site |
+| --- | --- | --- | --- |
+| HISTORIAN-01 | 192.0.2.51 | SENS-RB-01 | Riverbend Plant |",
+ "sourceFieldKeys": [
+ "affected-devices",
+ ],
+ "targetRole": "additional",
+ },
+ {
+ "decisionSource": "automatic",
+ "normalizedValue": "[{"name":"HISTORIAN-01","ip":"192.0.2.51","site":"Riverbend Plant","sensor":"SENS-RB-01","cve":"CVE-2099-1005","severity":"High","description":"","additionalSourceFields":{"Device ID":"DEV-OT-004","CVSS Score":"8.2","Component":"Synthetic component","Status":"Mitigation planned"},"source":{"tableIndex":0,"rowIndex":3}}]",
+ "sourceFieldKeys": [
+ "source-observations",
+ ],
+ "targetRole": "evidence",
+ },
+ ],
+ "originalFields": [
+ {
+ "columnIndex": undefined,
+ "key": "cve",
+ "label": "CVE",
+ "path": undefined,
+ "rawValue": "CVE-2099-1005",
+ "value": "CVE-2099-1005",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "affected-device-count",
+ "label": "Affected Device Count",
+ "path": undefined,
+ "rawValue": "1",
+ "value": 1,
+ },
+ {
+ "columnIndex": undefined,
+ "key": "severity",
+ "label": "Severity",
+ "path": undefined,
+ "rawValue": "High",
+ "value": "High",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "description",
+ "label": "Description",
+ "path": undefined,
+ "rawValue": "",
+ "value": "",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "observation-count",
+ "label": "Observation Count",
+ "path": undefined,
+ "rawValue": "1",
+ "value": 1,
+ },
+ {
+ "columnIndex": undefined,
+ "key": "affected-devices",
+ "label": "Affected Devices",
+ "path": undefined,
+ "rawValue": "| Name | IP Address | Scanner | Site |
+| --- | --- | --- | --- |
+| HISTORIAN-01 | 192.0.2.51 | SENS-RB-01 | Riverbend Plant |",
+ "value": "| Name | IP Address | Scanner | Site |
+| --- | --- | --- | --- |
+| HISTORIAN-01 | 192.0.2.51 | SENS-RB-01 | Riverbend Plant |",
+ },
+ {
+ "columnIndex": undefined,
+ "key": "source-observations",
+ "label": "Source Observations",
+ "path": undefined,
+ "rawValue": "[{"name":"HISTORIAN-01","ip":"192.0.2.51","site":"Riverbend Plant","sensor":"SENS-RB-01","cve":"CVE-2099-1005","severity":"High","description":"","additionalSourceFields":{"Device ID":"DEV-OT-004","CVSS Score":"8.2","Component":"Synthetic component","Status":"Mitigation planned"},"source":{"tableIndex":0,"rowIndex":3}}]",
+ "value": [
+ {
+ "additionalSourceFields": {
+ "CVSS Score": "8.2",
+ "Component": "Synthetic component",
+ "Device ID": "DEV-OT-004",
+ "Status": "Mitigation planned",
+ },
+ "cve": "CVE-2099-1005",
+ "description": "",
+ "ip": "192.0.2.51",
+ "name": "HISTORIAN-01",
+ "sensor": "SENS-RB-01",
+ "severity": "High",
+ "site": "Riverbend Plant",
+ "source": {
+ "rowIndex": 3,
+ "tableIndex": 0,
+ },
+ },
+ ],
+ },
+ ],
+ "outcome": {
+ "normalizedValue": "warning",
+ "originalValue": "",
+ },
+ "score": undefined,
+ "severity": {
+ "normalizedValue": "high",
+ "originalValue": "High",
+ },
+ "sourceIdentity": {
+ "canonicalRuleId": "CVE-2099-1005",
+ "originalRuleId": "CVE-2099-1005",
+ "originalTitle": "CVE-2099-1005",
+ "tool": {
+ "id": "defender-for-iot-vulnerabilities",
+ "name": "Microsoft Defender for IoT",
+ },
+ },
+ "sourcePosition": {
+ "cellReference": undefined,
+ "columnIndex": undefined,
+ "elementSelector": undefined,
+ "objectPath": undefined,
+ "rowIndex": 3,
+ },
+ "status": {
+ "normalizedValue": "unknown",
+ "originalValue": "",
+ },
+ "technicalExecutionState": {
+ "normalizedValue": "completed",
+ "originalValue": "",
+ },
+ },
+ ],
+ "observations": [],
+ "preview": {
+ "mappingsToConfirm": 0,
+ "recordCount": 3,
+ "selected": true,
+ },
+ "recognition": "defender-for-iot-vulnerabilities",
+ "reportSections": [],
+ "scores": [
+ {
+ "assessed": 3,
+ "assessmentId": "assessment-protected-dummy-import",
+ "comparisonKey": undefined,
+ "contentSha256": "cee1fb7363f1f065a89341b755a27ec9727866d60dc4bc46089561582eda1d8e",
+ "deviations": 3,
+ "eligible": 3,
+ "evaluationScopeKey": "["[\\"CVE-2099-1001\\",\\"evaluated\\"]","[\\"CVE-2099-1002\\",\\"evaluated\\"]","[\\"CVE-2099-1005\\",\\"evaluated\\"]"]",
+ "importFileIds": [
+ "file-protected-dummy-import-0",
+ ],
+ "issues": [
+ "Evidence present, not scoreable",
+ "Scan identity or time not verified",
+ "Scanner version unavailable",
+ ],
+ "method": "unavailable",
+ "nativeProperty": undefined,
+ "nativeValue": undefined,
+ "observedAt": undefined,
+ "occurrenceIds": [
+ "occurrence-protected-dummy-import-0-0",
+ "occurrence-protected-dummy-import-0-1",
+ "occurrence-protected-dummy-import-0-2",
+ ],
+ "openOccurrenceIds": [
+ "occurrence-protected-dummy-import-0-0",
+ "occurrence-protected-dummy-import-0-1",
+ "occurrence-protected-dummy-import-0-2",
+ ],
+ "passed": 0,
+ "policy": {
+ "defaultWeight": 1,
+ "version": "1.0.0",
+ "weights": {
+ "cis-cat": 1,
+ "group-policy": 1,
+ "maester": 1,
+ "pingcastle": 1,
+ "purple-knight": 1,
+ },
+ },
+ "processingKey": "[null,"defender-for-iot-vulnerabilities","1.2.0","1.0.0","1.1.0","1.0.0"]",
+ "resultSignature": "["[\\"CVE-2099-1001\\",\\"1\\",\\"warning\\"]","[\\"CVE-2099-1002\\",\\"1\\",\\"warning\\"]","[\\"CVE-2099-1005\\",\\"1\\",\\"warning\\"]"]",
+ "runIds": [
+ "run-protected-dummy-import",
+ ],
+ "score": undefined,
+ "toolId": "defender-for-iot",
+ "toolName": "Microsoft Defender for IoT",
+ },
+ ],
+ "tables": [
+ {
+ "columnCount": 10,
+ "hidden": undefined,
+ "index": 0,
+ "name": "CSV",
+ "rowCount": 3,
+ "rows": undefined,
+ },
+ ],
+}
+`;
+
exports[`protected scanner import contracts (synthetic data only) > 'generic-csv' retains recognition, preview and approved canonical results in browser and backend 1`] = `
{
"approved": {
diff --git a/tests/importers/import-contracts.json b/tests/importers/import-contracts.json
index db38f20..359f6f8 100644
--- a/tests/importers/import-contracts.json
+++ b/tests/importers/import-contracts.json
@@ -196,5 +196,27 @@
"mediaType": "text/html",
"sha256": "2a080e0facc21626b51b91305e0163382d63e6bc24a8d26d0312300d580afe56",
"variant": "original"
+ },
+ {
+ "id": "defender-for-iot-vulnerabilities-csv",
+ "fixture": "fixtures/defender-for-iot/cves.synthetic.csv",
+ "tool": "defender-for-iot-vulnerabilities",
+ "format": "CSV",
+ "findings": 3,
+ "fileName": "cves.csv",
+ "mediaType": "text/csv",
+ "sha256": "cee1fb7363f1f065a89341b755a27ec9727866d60dc4bc46089561582eda1d8e",
+ "variant": "original"
+ },
+ {
+ "id": "defender-for-iot-alerts-csv",
+ "fixture": "fixtures/defender-for-iot-alerts/alerts.synthetic.csv",
+ "tool": "defender-for-iot-alerts",
+ "format": "CSV",
+ "findings": 3,
+ "fileName": "alerts.csv",
+ "mediaType": "text/csv",
+ "sha256": "c554f60079cf276f21c90b2b9c0b4b3fe6976945fd726b6d1ba1331391e3c184",
+ "variant": "original"
}
]
diff --git a/tests/server/assessor-http-server.test.ts b/tests/server/assessor-http-server.test.ts
index 582106d..6088e4d 100644
--- a/tests/server/assessor-http-server.test.ts
+++ b/tests/server/assessor-http-server.test.ts
@@ -60,7 +60,7 @@ describe('local ASSESSOR HTTP server', () => {
const health = await fetch(`${baseUrl}/api/v1/health`);
expect(await health.json()).toEqual({
- status: 'ok', application: 'ASSESSOR', productVersion: '2.24.2', apiVersion: 1, repositoryRevision: 0, persistence: 'memory'
+ status: 'ok', application: 'ASSESSOR', productVersion: '2.25.0', apiVersion: 1, repositoryRevision: 0, persistence: 'memory'
});
expect(health.headers.get('access-control-allow-origin')).toBeNull();
});