fix(policy): read policy.json only from the caller's own working tree
loadPolicy() read .llm-security/policy.json from whatever root it was
given, and every scanner passes the SCANNED TARGET: scan-orchestrator
(policyRoot = resolve(args.target)), entropy-scanner (thresholds and
suppression patterns), signature-scanner (sig.custom_rules_path and
enabled_families), trigger-scanner (phrase lists) and ast-taint-scanner
(enabled, python_path). A foreign/cloned target could raise its own
entropy thresholds, disable SIG families, supply its own SIG ruleset or
name the interpreter the AST scanner spawns — configuring the scan of
itself. Same defect class as S3b's .llm-security-ignore fix.
Chosen: move isOwnWorkingTree() to scanners/lib/own-working-tree.mjs (one
copy, reused by the orchestrator's ignore-file check) and make
loadPolicy() refuse an EXPLICIT root that is not the caller's own tree —
defaults plus one stderr line, same form as S3b — because one rule in one
function covers every scanner and a future call site cannot forget it.
The IMPLICIT root (CLAUDE_PROJECT_ROOT/cwd, what every hook uses) is the
caller's own project by construction and is read as before.
entropy-scanner's calibration.policy_source no longer reports an ignored
file as its source.
New tests/scanners/policy-scope.test.mjs was red on 0d37f5a (foreign
target: entropy finding silenced, custom SIG rule loaded, findings differ
from the same tree without policy.json, no stderr line) and is green now;
its own-tree scenario (known-positive) is green before and after. The 15
existing policy tests that placed own-tree fixtures under os.tmpdir() now
use tests/helpers/own-tree.mjs (fixture under $HOME, cwd set to it).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0d37f5a628
commit
6d0f3c31fc
12 changed files with 364 additions and 50 deletions
|
|
@ -4,20 +4,27 @@ import { describe, it, beforeEach, afterEach } from 'node:test';
|
|||
import assert from 'node:assert/strict';
|
||||
import { writeFileSync, mkdirSync, rmSync, existsSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { homedir } from 'node:os';
|
||||
import { loadPolicy, getPolicyValue, getDefaultPolicy, _resetCacheForTest } from '../../scanners/lib/policy-loader.mjs';
|
||||
|
||||
const TEST_ROOT = join(tmpdir(), `llm-security-policy-test-${Date.now()}`);
|
||||
// S3c: an explicit root is read only when it is the caller's own working
|
||||
// tree (under cwd, never under os.tmpdir()) — see tests/helpers/own-tree.mjs.
|
||||
const TEST_ROOT = join(homedir(), `.llm-security-policy-test-${Date.now()}`);
|
||||
const POLICY_DIR = join(TEST_ROOT, '.llm-security');
|
||||
const POLICY_FILE = join(POLICY_DIR, 'policy.json');
|
||||
|
||||
describe('policy-loader', () => {
|
||||
let prevCwd;
|
||||
|
||||
beforeEach(() => {
|
||||
_resetCacheForTest();
|
||||
mkdirSync(POLICY_DIR, { recursive: true });
|
||||
prevCwd = process.cwd();
|
||||
process.chdir(TEST_ROOT);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.chdir(prevCwd);
|
||||
_resetCacheForTest();
|
||||
try { rmSync(TEST_ROOT, { recursive: true }); } catch {}
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue