feat(ms-ai-architect): Enhet A2 — Layer B baseline-adjudikering: innholds-basert allowlist (75 funn adjudikert, TDD) + 4 korpus-defekter fikset (2 homoglyph-ord, 2 U+00AD-filer); korpus 389/389 exit 0 [skip-docs]
This commit is contained in:
parent
135f34eb9b
commit
af6c31c4c1
11 changed files with 1048 additions and 15 deletions
|
|
@ -125,21 +125,51 @@ export function disposition(finding, tier) {
|
|||
return 'block';
|
||||
}
|
||||
|
||||
const RANK = { block: 2, warn: 1, clean: 0 };
|
||||
const RANK = { block: 2, warn: 1, clean: 0, allow: 0 };
|
||||
|
||||
/**
|
||||
* Does an adjudicated allowlist entry cover this finding? (Enhet A2, brief §8.)
|
||||
* CONTENT-based, not line-number-based: the entry pins the exact TRIMMED content of
|
||||
* the adjudicated line, so a line that merely moves stays green while a line whose
|
||||
* content changes RESURFACES as WARN/BLOCK (fail-safe — re-adjudication required).
|
||||
* All four axes must match: class, evidence, provenance tier, trimmed line content.
|
||||
* (Path scoping happens in the caller — scanPaths — which filters entries per file.)
|
||||
* @param {{class: string, evidence: string}} finding
|
||||
* @param {string} tier — the finding's computed provenance tier
|
||||
* @param {string} trimmedLine — trimmed content of the finding's line
|
||||
* @param {Array<object>} allow — allowlist entries scoped to this file
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isAllowlisted(finding, tier, trimmedLine, allow) {
|
||||
return (allow ?? []).some(
|
||||
(e) =>
|
||||
e.class === finding.class &&
|
||||
e.evidence === finding.evidence &&
|
||||
e.tier === tier &&
|
||||
typeof e.match === 'string' &&
|
||||
e.match === trimmedLine,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Classify a batch of raw findings against the file content. Pure + sync.
|
||||
* @param {string} content — the full candidate file content (for code-fence + Source tiering)
|
||||
* @param {Array<object>} rawFindings — [{class, subtype?, severity, line, evidence}]
|
||||
* @param {{sourceUrl?: string}} [opts] — sourceUrl overrides the in-file **Source:** header
|
||||
* @param {{sourceUrl?: string, allow?: Array<object>}} [opts] — sourceUrl overrides the in-file
|
||||
* **Source:** header; allow = adjudicated allowlist entries ALREADY scoped to this file
|
||||
* @returns {{disposition: 'block'|'warn'|'clean', findings: Array<object>}}
|
||||
*/
|
||||
export function classifyFindings(content, rawFindings, opts = {}) {
|
||||
const ranges = findFencedCodeRanges(content);
|
||||
const lines = String(content ?? '').split('\n');
|
||||
const sourceUrl = opts.sourceUrl ?? parseSourceHeader(content) ?? null;
|
||||
const findings = (rawFindings ?? []).map((f) => {
|
||||
const tier = provenanceTier({ line: f.line, ranges, sourceUrl });
|
||||
return { ...f, tier, disposition: disposition(f, tier) };
|
||||
const trimmedLine = (lines[f.line - 1] ?? '').trim();
|
||||
const disp = isAllowlisted(f, tier, trimmedLine, opts.allow)
|
||||
? 'allow'
|
||||
: disposition(f, tier);
|
||||
return { ...f, tier, disposition: disp };
|
||||
});
|
||||
let worst = 'clean';
|
||||
for (const f of findings) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue