llm-security/tests/lib/commons-loader.test.mjs
Kjell Tore Guttormsen 69cad7c973 feat(llm-security): v8 Phase 5 step 2 - commons-loader.mjs [skip-docs]
Thin, sync-read JSON artifact loader for the future vendored
llm-security-commons subtree, modeled on signature-scanner.mjs's
loadRules()/loadCustomRules() pair: process-cached, graceful-empty
fallback on any read/parse error, and policy-extensible via a
`commons.root` policy value (mirrors sig.custom_rules_path).

Unit-tested now against a local fixture — Phase 4 (commons repo
creation, gated on the operator creating the Forgejo remote) hasn't
run yet, so the default `shared/` vendor path doesn't exist in this
checkout. That "not vendored yet" case is itself asserted: the loader
must degrade to the caller's fallback, not crash.

Not wired to any consumer yet (that's Phase 5 step 4, table-by-table
behind the golden gate). No CLI/hook/scanner-visible behaviour exists
to document. Golden baseline unchanged; full suite 2063/2063 (2053 +
10 new).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QAYkRaBXT6tmWXTQAi1ZBg
2026-08-09 14:06:37 +02:00

116 lines
5.7 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// commons-loader.test.mjs — Tests for the vendored-commons JSON artifact loader
//
// v8 Phase 5 step 2: llm-security-commons doesn't exist yet (Phase 4 is
// blocked on the operator creating the Forgejo repo), so these tests drive
// the loader against a local fixture directory instead of a real vendored
// subtree — including the "commons isn't vendored yet" case, which must
// degrade gracefully rather than crash.
import { describe, it, beforeEach, afterEach } from 'node:test';
import assert from 'node:assert/strict';
import { writeFileSync, mkdirSync, rmSync } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import { loadArtifact, _resetCacheForTest } from '../../scanners/lib/commons-loader.mjs';
import { _resetCacheForTest as _resetPolicyCacheForTest } from '../../scanners/lib/policy-loader.mjs';
const TEST_ROOT = join(tmpdir(), `llm-security-commons-loader-test-${Date.now()}`);
const FIXTURE_COMMONS_ROOT = join(TEST_ROOT, 'fixture-commons');
const POLICY_DIR = join(TEST_ROOT, '.llm-security');
const POLICY_FILE = join(POLICY_DIR, 'policy.json');
function writeArtifact(relPath, data) {
const filePath = join(FIXTURE_COMMONS_ROOT, `${relPath}.json`);
mkdirSync(join(filePath, '..'), { recursive: true });
writeFileSync(filePath, JSON.stringify(data));
}
describe('commons-loader', () => {
beforeEach(() => {
_resetCacheForTest();
_resetPolicyCacheForTest();
mkdirSync(FIXTURE_COMMONS_ROOT, { recursive: true });
});
afterEach(() => {
_resetCacheForTest();
_resetPolicyCacheForTest();
try { rmSync(TEST_ROOT, { recursive: true }); } catch {}
});
it('loads and parses a valid artifact from an explicit commonsRoot', () => {
writeArtifact('lexicon/injection-lexicon', { version: '0.1.0', terms: ['ignore previous instructions'] });
const data = loadArtifact('lexicon/injection-lexicon', { commonsRoot: FIXTURE_COMMONS_ROOT, fallback: null });
assert.deepEqual(data, { version: '0.1.0', terms: ['ignore previous instructions'] });
});
it('returns the caller fallback when the artifact file is missing', () => {
const data = loadArtifact('lexicon/injection-lexicon', { commonsRoot: FIXTURE_COMMONS_ROOT, fallback: [] });
assert.deepEqual(data, []);
});
it('returns the caller fallback when the artifact JSON is invalid', () => {
const filePath = join(FIXTURE_COMMONS_ROOT, 'mapping', 'owasp-map.json');
mkdirSync(join(filePath, '..'), { recursive: true });
writeFileSync(filePath, 'not valid json!!!');
const data = loadArtifact('mapping/owasp-map', { commonsRoot: FIXTURE_COMMONS_ROOT, fallback: {} });
assert.deepEqual(data, {});
});
it('degrades gracefully when commons has not been vendored yet (no override, no policy)', () => {
// Phase 4 hasn't run in this repo checkout: the default `shared/` vendor
// path does not exist. The loader must not throw.
const data = loadArtifact('lexicon/injection-lexicon', { fallback: 'EMPTY' });
assert.equal(data, 'EMPTY');
});
it('caches a loaded artifact per resolved path', () => {
writeArtifact('calibration/calibration', { entropyFloor: 4.5 });
const first = loadArtifact('calibration/calibration', { commonsRoot: FIXTURE_COMMONS_ROOT });
writeArtifact('calibration/calibration', { entropyFloor: 9.9 }); // mutate after first read
const second = loadArtifact('calibration/calibration', { commonsRoot: FIXTURE_COMMONS_ROOT });
assert.equal(first, second); // same reference (cached)
assert.equal(second.entropyFloor, 4.5); // original value, not the mutation
});
it('caches a failed load too, so a fixed-but-unread file still returns the cached fallback', () => {
const first = loadArtifact('signatures/malware-signatures', { commonsRoot: FIXTURE_COMMONS_ROOT, fallback: [] });
writeArtifact('signatures/malware-signatures', { rules: [{ id: 'late-arrival' }] });
const second = loadArtifact('signatures/malware-signatures', { commonsRoot: FIXTURE_COMMONS_ROOT, fallback: [] });
assert.deepEqual(first, []);
assert.deepEqual(second, []);
});
it('resolves commonsRoot from the commons.root policy value when no explicit override is given', () => {
mkdirSync(POLICY_DIR, { recursive: true });
writeFileSync(POLICY_FILE, JSON.stringify({ commons: { root: FIXTURE_COMMONS_ROOT } }));
writeArtifact('codepoints/carriers', { zeroWidth: [''] });
const data = loadArtifact('codepoints/carriers', { targetPath: TEST_ROOT, fallback: null });
assert.deepEqual(data, { zeroWidth: [''] });
});
it('resolves a relative commons.root policy value against targetPath', () => {
mkdirSync(POLICY_DIR, { recursive: true });
writeFileSync(POLICY_FILE, JSON.stringify({ commons: { root: './fixture-commons' } }));
writeArtifact('lexicon/injection-lexicon', { version: '0.1.0', terms: [] });
const data = loadArtifact('lexicon/injection-lexicon', { targetPath: TEST_ROOT, fallback: null });
assert.deepEqual(data, { version: '0.1.0', terms: [] });
});
it('an explicit commonsRoot option takes precedence over the commons.root policy value', () => {
mkdirSync(POLICY_DIR, { recursive: true });
writeFileSync(POLICY_FILE, JSON.stringify({ commons: { root: join(TEST_ROOT, 'does-not-exist') } }));
writeArtifact('lexicon/injection-lexicon', { version: '0.1.0', terms: ['override-wins'] });
const data = loadArtifact('lexicon/injection-lexicon', {
targetPath: TEST_ROOT,
commonsRoot: FIXTURE_COMMONS_ROOT,
fallback: null,
});
assert.deepEqual(data, { version: '0.1.0', terms: ['override-wins'] });
});
it('defaults the fallback to null when the caller passes none', () => {
const data = loadArtifact('lexicon/injection-lexicon', { commonsRoot: FIXTURE_COMMONS_ROOT });
assert.equal(data, null);
});
});