fix(ms-ai-architect): G6 Layer B — unicode/carrier-scan kjører pre-write via temp-fil (lukker ingestion-brief §8-akseptansegap, TDD) [skip-docs]
This commit is contained in:
parent
b5c44e6c6e
commit
b227c278eb
2 changed files with 77 additions and 4 deletions
|
|
@ -23,7 +23,9 @@
|
|||
// pattern as scripts/kb-eval/score-skill.mjs.
|
||||
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname, resolve, basename } from 'node:path';
|
||||
import { dirname, resolve, basename, join } from 'node:path';
|
||||
import { mkdtempSync, writeFileSync, rmSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
|
|
@ -80,7 +82,9 @@ const ENCODED_MIN_ENTROPY = 4.0;
|
|||
* driven with a single-file discovery so no charset is re-derived here.
|
||||
*
|
||||
* @param {string} content — the candidate file content
|
||||
* @param {{path?: string}} [opts] — path is required for unicode detection (the scanner reads it)
|
||||
* @param {{path?: string}} [opts] — optional on-disk path; when absent the unicode scan runs
|
||||
* PRE-WRITE against a temp file materialized from `content` (the ingestion gate), so unicode
|
||||
* coverage no longer depends on the file already existing on disk
|
||||
* @returns {Promise<Array<{class: string, subtype?: string, severity: string, line: number, evidence: string}>>}
|
||||
*/
|
||||
export async function detectAdversarial(content, opts = {}) {
|
||||
|
|
@ -108,8 +112,22 @@ export async function detectAdversarial(content, opts = {}) {
|
|||
}
|
||||
|
||||
// --- unicode (disk-based scanner; needs a real path) ---
|
||||
if (opts.path) {
|
||||
const discovery = { files: [{ absPath: resolve(opts.path), relPath: basename(opts.path) }] };
|
||||
// The llm-security unicode scanner reads a file off disk. When a caller passes content
|
||||
// WITHOUT a path — the pre-write ingestion gate: scan the COMPOSED artefact before it is
|
||||
// ever written — materialize a temp file so the SAME scanner runs pre-write. This closes
|
||||
// the ingestion-brief §8 acceptance test (zero-width/bidi rejected BEFORE write); the old
|
||||
// `if (opts.path)` guard skipped unicode entirely on the content path, leaving pre-write
|
||||
// carriers undetected. Fail-closed posture is unchanged — a scanner error still propagates
|
||||
// as a throw → the caller BLOCKs.
|
||||
let scanPath = opts.path;
|
||||
let tmpDir = null;
|
||||
try {
|
||||
if (!scanPath) {
|
||||
tmpDir = mkdtempSync(join(tmpdir(), 'kb-adv-'));
|
||||
scanPath = join(tmpDir, 'candidate.md');
|
||||
writeFileSync(scanPath, String(content ?? ''), 'utf8');
|
||||
}
|
||||
const discovery = { files: [{ absPath: resolve(scanPath), relPath: basename(scanPath) }] };
|
||||
const res = await unicodeScan('.', discovery);
|
||||
for (const f of res.findings || []) {
|
||||
findings.push({
|
||||
|
|
@ -120,6 +138,8 @@ export async function detectAdversarial(content, opts = {}) {
|
|||
evidence: f.evidence || f.title || 'unicode anomaly',
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
if (tmpDir) rmSync(tmpDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
return findings;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue