feat(llm-security)!: v8 Phase 3 step 1 - remove the deprecated mode env-vars
BREAKING CHANGE: the four LLM_SECURITY_* configuration env-vars deprecated in v7.3.0 are removed. .llm-security/policy.json is now the only source: LLM_SECURITY_INJECTION_MODE -> injection.mode LLM_SECURITY_TRIFECTA_MODE -> trifecta.mode LLM_SECURITY_ESCALATION_WINDOW -> trifecta.escalation_window LLM_SECURITY_AUDIT_LOG -> audit.log_path LLM_SECURITY_DEPRECATION_QUIET -> dies with the mechanism it silenced Setting a removed var is now inert - it does not warn, and it does not configure. Env-vars with no policy equivalent (PRECOMPACT_MODE, PRECOMPACT_MAX_BYTES, UPDATE_CHECK, MCP_CACHE_FILE, IDE_ROOTS) are unaffected. getPolicyValueWithEnvWarn and its one-shot stderr warning are deleted from policy-loader.mjs, along with the module-scoped warned-var Set. The four call sites collapse to getPolicyValue. getPolicyValue's JSDoc claimed "environment variables ALWAYS take precedence" - it never read env itself, so that line described the shim, and it is corrected rather than deleted. User-facing hook strings that advertised a removed var as the escape hatch now name the policy key instead: the inject-scan block reason, its warn-mode note, both escalation-window advisories, and the trifecta block message. A blocked user following the old text would have set a var that does nothing. Tests. tests/lib/v8-env-removal.test.mjs is the regression gate and was written failing first (8 of 12 red before the change). It pins the NEGATIVE - setting a removed var does not alter the outcome - because that is the half that rots silently: a re-introduced process.env read would leave every migrated positive test green, since those configure through policy.json and never set the var at all. One assertion walks hooks/scripts and scanners for `process.env.<removed>` so the re-introduction is caught structurally, not only behaviourally. The 44 env-driven test occurrences (18 inject-scan, 13+4 session-guard, 9 audit-trail) migrate to a throwaway .llm-security/policy.json via a new runHookWithPolicy helper in hook-helper.mjs; audit-trail runs in-process, so it supplies the same policy through CLAUDE_PROJECT_ROOT. The D3 mechanism tests in policy-loader.test.mjs are deleted with the mechanism. Suite 2039 tests, 2037 pass (+12 gate, -7 D3 mechanism). The 2 failures are the known parallel-load timing flakes (pre-compact-scan size-cap, pre-install-supply-chain F-3); both green when run isolated. Remaining in Phase 3: posture-scanner TRIFECTA_MODE heuristic, riskScoreV1 removal, ghost-var cleanup, docs + migration note. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BB4vXvwvtW4dxbPRd6vsez
This commit is contained in:
parent
7336250c60
commit
b6af9b46df
10 changed files with 430 additions and 299 deletions
|
|
@ -2,6 +2,9 @@
|
|||
// Spawns a hook as a child process and feeds it JSON via stdin.
|
||||
|
||||
import { execFile } from 'node:child_process';
|
||||
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { tmpdir } from 'node:os';
|
||||
|
||||
/**
|
||||
* Run a hook script by spawning `node <scriptPath>` and piping `input` to stdin.
|
||||
|
|
@ -40,3 +43,31 @@ export function runHookWithEnv(scriptPath, input, envOverrides) {
|
|||
child.stdin.end(typeof input === 'string' ? input : JSON.stringify(input));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a hook script against a throwaway project root carrying a
|
||||
* `.llm-security/policy.json`.
|
||||
*
|
||||
* v8.0.0 replaced the `LLM_SECURITY_*` mode env-vars with policy.json keys, so
|
||||
* a test that wants non-default hook behaviour has to give the hook a project
|
||||
* root to read. The temp root is removed even when the hook throws.
|
||||
*
|
||||
* @param {string} scriptPath - Absolute path to the hook .mjs file
|
||||
* @param {object|string} input - JSON payload (object will be stringified)
|
||||
* @param {object} policy - Written verbatim to `.llm-security/policy.json`
|
||||
* @param {Record<string, string>} [envOverrides] - Extra env vars to set
|
||||
* @returns {Promise<{ code: number, stdout: string, stderr: string }>}
|
||||
*/
|
||||
export async function runHookWithPolicy(scriptPath, input, policy, envOverrides = {}) {
|
||||
const root = mkdtempSync(join(tmpdir(), 'llmsec-policy-'));
|
||||
try {
|
||||
mkdirSync(join(root, '.llm-security'), { recursive: true });
|
||||
writeFileSync(join(root, '.llm-security', 'policy.json'), JSON.stringify(policy));
|
||||
return await runHookWithEnv(scriptPath, input, {
|
||||
CLAUDE_PROJECT_ROOT: root,
|
||||
...envOverrides,
|
||||
});
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue