diff --git a/examples/node9.config.json.example b/examples/node9.config.json.example index ba40d0e..e161b60 100644 --- a/examples/node9.config.json.example +++ b/examples/node9.config.json.example @@ -11,7 +11,7 @@ "policy": { "sandboxPaths": ["/tmp/**", "**/sandbox/**", "**/test-results/**"], - "dangerousWords": ["mkfs", "shred"], + "dangerousWords": ["rm", "drop", "mkfs", "shred"], "ignoredTools": [ "list_*", @@ -64,7 +64,7 @@ { "field": "command", "op": "notMatches", - "value": "(&&|\\|\\||;\\s*\\S|\\$\\(|`)", + "value": "(&&|\\|\\||;|\\$\\(|`)", "flags": "i" } ], @@ -124,7 +124,7 @@ "reason": "Command contains $() or backtick substitution — can run arbitrary subcommands" }, { - "name": "review-secrets-write", + "name": "flag-secrets-access", "tool": "*", "conditions": [ { @@ -145,7 +145,7 @@ ], "conditionMode": "any", "verdict": "review", - "reason": "Writing to secrets or credentials file" + "reason": "Accessing a secrets or credentials file (read or write)" } ], diff --git a/src/__tests__/advanced_policy.test.ts b/src/__tests__/advanced_policy.test.ts index f44a5af..0fd9733 100644 --- a/src/__tests__/advanced_policy.test.ts +++ b/src/__tests__/advanced_policy.test.ts @@ -110,7 +110,7 @@ describe('allow-readonly-bash — chained command guard', () => { { field: 'command', op: 'notMatches', - value: '(&&|\\|\\||;\\s*\\S)', + value: '(&&|\\|\\||;)', flags: 'i', }, ], @@ -180,7 +180,7 @@ describe('allow-readonly-bash — command substitution bypass', () => { { field: 'command', op: 'notMatches', - value: '(&&|\\|\\||;\\s*\\S|\\$\\(|`)', + value: '(&&|\\|\\||;|\\$\\(|`)', flags: 'i', }, ], @@ -290,15 +290,15 @@ describe('allow-install-devtools — global install guard', () => { }); }); -// ── review-secrets-write — multi-field matching ─────────────────────────────── +// ── flag-secrets-access — multi-field matching ──────────────────────────────── -describe('review-secrets-write — multi-field matching', () => { +describe('flag-secrets-access — multi-field matching', () => { const secretsRule = { policy: { dangerousWords: [], smartRules: [ { - name: 'review-secrets-write', + name: 'flag-secrets-access', tool: '*', conditions: [ { @@ -322,7 +322,7 @@ describe('review-secrets-write — multi-field matching', () => { ], conditionMode: 'any', verdict: 'review', - reason: 'Writing to secrets or credentials file', + reason: 'Accessing a secrets or credentials file (read or write)', }, ], }, @@ -353,10 +353,16 @@ describe('review-secrets-write — multi-field matching', () => { expect(r.decision).not.toBe('review'); }); - it('does NOT flag a file whose name merely contains .env as a substring', async () => { + it('does NOT flag a file whose basename does not start with .env (e.g. notmy.env.bak)', async () => { + // Regex anchors on (^|[/\\]) + .env, so "notmy.env.bak" does NOT match — basename starts with 'n' const r = await evaluatePolicy('write', { file_path: '/project/notmy.env.bak' }); expect(r.decision).not.toBe('review'); }); + + it('flags a file named .env.bak (actual dotfile backup of .env)', async () => { + const r = await evaluatePolicy('write', { file_path: '/project/.env.bak' }); + expect(r.decision).toBe('review'); + }); }); // ── version mismatch handling ─────────────────────────────────────────────────