Neuer Stand Assessor #1

Merged
bika merged 33 commits from AI-integration into experimental 2026-09-25 09:34:44 +00:00
2 changed files with 46 additions and 8 deletions
Showing only changes of commit 78ea9a616d - Show all commits

View File

@ -577,12 +577,23 @@ function observationFact(snapshot: EnvironmentSnapshot | undefined, property: st
return observation ? factText(displayValue(observation)) : UNKNOWN_FACT;
}
/**
* Parst einen Faktwert, ohne eine Null zu erfinden: leere Zeichenketten,
* ausdrueckliches null und Wahrheitswerte bleiben unbekannt statt zu 0 zu
* werden. `Number('')`, `Number(null)` und `Number(false)` ergeben sonst
* jeweils die endliche 0 und faelschten den em-dash zu einer "0".
*/
function finiteFactNumber(value: unknown): number | undefined {
const numeric = typeof value === 'number' ? value
: typeof value === 'string' && value.trim() !== '' ? Number(value)
: NaN;
return Number.isFinite(numeric) ? numeric : undefined;
}
function optionalObservationNumber(snapshot: EnvironmentSnapshot | undefined, property: string, label?: string, sectionKey?: string): number | undefined {
const observation = observationFor(snapshot, property, label, sectionKey);
if (!observation) return undefined;
const value = observation.normalizedValue ?? observation.originalValue;
const numeric = typeof value === 'number' ? value : Number(value);
return Number.isFinite(numeric) ? numeric : undefined;
return finiteFactNumber(observation.normalizedValue ?? observation.originalValue);
}
function numberFact(value: number | undefined): string {
@ -887,9 +898,7 @@ function dashboardFieldValue(record: FindingRecord, pattern: RegExp): string | u
function dashboardObservationNumber(snapshot: EnvironmentSnapshot | undefined, property: string): number | undefined {
const observation = snapshot?.observations.find(item => item.property === property && item.state === 'observed');
const value = observation?.normalizedValue ?? observation?.originalValue;
const number = typeof value === 'number' ? value : Number(value);
return Number.isFinite(number) ? number : undefined;
return finiteFactNumber(observation?.normalizedValue ?? observation?.originalValue);
}
function dashboardEnvironmentTableNumber(snapshot: EnvironmentSnapshot | undefined, sectionKey: string, header: RegExp): number | undefined {
@ -899,8 +908,8 @@ function dashboardEnvironmentTableNumber(snapshot: EnvironmentSnapshot | undefin
const column = table.headers.findIndex(value => header.test(value.trim()));
if (column < 0) return [];
return table.rows.flatMap(row => {
const numeric = Number(row[column]);
return Number.isFinite(numeric) ? [numeric] : [];
const numeric = finiteFactNumber(row[column]);
return numeric === undefined ? [] : [numeric];
});
})) ?? [];
return values.length ? values.reduce((sum, value) => sum + value, 0) : undefined;

View File

@ -94,6 +94,35 @@ describe('client info environment detail', () => {
expect(fact(dashboardEnvironmentDetail(snapshot).inventory, 'Trusts')).toBe('—');
});
it('does not infer a zero for present numeric facts that are empty, null, or boolean', () => {
const blank = {
version: 1, projectId: 'project-1', scanRunIds: ['run-1'], generatedAt: '2026-08-25T00:00:00.000Z',
coverage: { observed: 4, explicitlyAbsent: 0, empty: 0, restricted: 0, incomplete: 0 },
sections: [
section('sectionUserInformation', 'User information', [{
name: 'Account analysis',
headers: ['Nb User Accounts', 'Nb Enabled', 'Nb Disabled'],
rows: [['', '', '']]
}])
],
observations: [
observation('benchmark.score', 'Benchmark score', '' as unknown as number),
observation('purple_knight.ad.ioes_found', 'IOEs found', null as unknown as number),
observation('purple_knight.ad.security_posture_score', 'security posture score', false)
]
} satisfies EnvironmentSnapshot;
const detail = dashboardEnvironmentDetail(blank);
// A present observation whose value is an empty string, explicit null, or
// boolean must stay an em dash: Number('')/Number(null)/Number(false) are all 0.
expect(fact(detail.toolContext, 'CIS-CAT score')).toBe('—');
expect(fact(detail.toolContext, 'Purple Knight IOEs')).toBe('—');
expect(fact(detail.toolContext, 'Purple Knight posture')).toBe('—');
// A table column whose cells are all blank must not sum to a fabricated 0.
expect(fact(detail.inventory, 'User accounts')).toBe('—');
});
it('reads the password policy and PKI counts from their own report tables', () => {
const detail = dashboardEnvironmentDetail(snapshot);