Files
portal/tools/enforce-powershell-gate.test.mjs
T
Дмитрий cb32aa9907 feat(gate): re-scope router-gate — allow local dev, keep prod+discipline blocks
composer/npm moved from hard-blacklist to whitelist; git dev-allow (commit/add/branch/switch/checkout/stash/worktree) + push main-guard in shared shell-content-rules; read-only GitHub (get_*/actions_get/actions_list) in mcp-classifier. Prod-safety (deploy/prod-DB/secrets/workflow-triggers/MCP-write), discipline hooks, and main push/merge stay blocked. Spec+plan in docs/superpowers. tools regression 1991 GREEN.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 09:32:39 +03:00

85 lines
2.9 KiB
JavaScript

import { describe, it, expect } from 'vitest';
import { tokenizePowerShell, matchPsHardBlacklist } from './enforce-powershell-gate.mjs';
describe('tokenizePowerShell', () => {
it('splits on ; and | into segments', () => {
const segs = tokenizePowerShell('Get-Content a | Select-String x ; Get-Item b');
expect(segs.map((s) => s.cmd)).toEqual(['get-content', 'select-string', 'get-item']);
});
});
describe('matchPsHardBlacklist — keep', () => {
it.each([
'Remove-Item x',
'ri x',
'del x',
'Move-Item a b',
'Copy-Item a b',
'Set-Content x "y"',
'Add-Content x "y"',
'Out-File -FilePath x',
'cmd > out.txt',
'Invoke-Expression $x',
'iex $x',
'Start-Process notepad',
'[System.IO.File]::Delete("x")',
'Stop-Process -Name node',
'Set-ExecutionPolicy Bypass',
'icacls x /grant y',
])('blocks %s', (cmd) => {
expect(matchPsHardBlacklist(cmd)).toBeTruthy();
});
});
describe('matchPsHardBlacklist — v4.1 G10', () => {
it.each([
'$env:PATH = "x"',
'$env:ROUTER_LLM_KEY="leak"',
'[System.Environment]::SetEnvironmentVariable("X","Y")',
'Set-Item -Path Env:FOO -Value bar',
'New-PSDrive -Name X -PSProvider FileSystem -Root C:\\',
'Get-AzVM',
'New-AzResourceGroup x',
'Get-AWSCredential',
'gcloud auth login',
])('blocks %s', (cmd) => {
expect(matchPsHardBlacklist(cmd)).toBeTruthy();
});
});
describe('matchPsHardBlacklist — allows benign', () => {
it.each(['Get-ChildItem', 'Get-Content app/x.php', 'Select-String x file', 'git status'])('allows %s', (cmd) => {
expect(matchPsHardBlacklist(cmd)).toBe(null);
});
});
import { classifyPowerShellCommand } from './enforce-powershell-gate.mjs';
describe('classifyPowerShellCommand', () => {
const now = 4_000_000;
it('allows whitelisted reading cmdlet', () => {
expect(classifyPowerShellCommand('Get-ChildItem -Path app', {}).result).toBe('allow');
});
it('allows alias gci', () => {
expect(classifyPowerShellCommand('gci', {}).result).toBe('allow');
});
it('blocks hard-blacklisted Remove-Item', () => {
expect(classifyPowerShellCommand('Remove-Item x', {}).result).toBe('block');
});
it('blocks G10 $env set', () => {
expect(classifyPowerShellCommand('$env:PATH="x"', {}).result).toBe('block');
});
it('blocks reading a protected path', () => {
expect(classifyPowerShellCommand('Get-Content ~/.claude/settings.json', {}).result).toBe('block');
});
it('routes git through shared classifier (commit dev-allowed 2026-06-02 re-scope)', () => {
expect(classifyPowerShellCommand('git commit -m "x"', { approvedGitOps: [], now }).result).toBe('allow');
});
it('allows readonly git through PowerShell', () => {
expect(classifyPowerShellCommand('git status', {}).result).toBe('allow');
});
it('default-denies unknown cmdlet', () => {
expect(classifyPowerShellCommand('Frobnicate-Thing', {}).result).toBe('block');
});
});