fix(m7-floor): canonicalAction +PowerShell — escape-привязка специфична (P-2)
Task 1.2b Фазы 1 М7 (КРИТ). canonicalAction получил ветку PowerShell:
`powershell:${normalizeCommand(command)}`. Без неё PS уходил в write-fallback,
пустой путь резолвился в cwd → один escape-грант разблокировал ЛЮБУЮ PS-команду
в окне (тест специфичности был зелёным ложно: a===b==='write:<cwd>').
Сквозной фикс: тот же canonicalAction зовут пол (Task 1.4), стена (enforce-supreme-gate)
и консьюмер. Bash/Write/mcp-ветки не задеты. 118 GREEN (escape-grant + 4 потребителя).
This commit is contained in:
@@ -26,6 +26,10 @@ export function canonicalAction(toolName, toolInput, { normalizeImpl = pathNorma
|
||||
const name = String(toolName || '');
|
||||
const input = toolInput || {};
|
||||
if (name === 'Bash') return `bash:${normalizeCommand(input.command || '')}`;
|
||||
// M7 Task 1.2b (P-2): PowerShell несёт команду в input.command (как Bash), не путь записи.
|
||||
// Без этой ветки PS уходил в write-fallback → пустой путь резолвился в cwd → один escape-грант
|
||||
// разблокировал ЛЮБУЮ PS-команду в окне. Специфичная канон-строка — escape привязан к точной команде.
|
||||
if (name === 'PowerShell') return `powershell:${normalizeCommand(input.command || '')}`;
|
||||
if (name.startsWith('mcp__')) {
|
||||
let args; try { args = JSON.stringify(input); } catch { args = String(input); }
|
||||
return `mcp:${name}:${normalizeCommand(args)}`;
|
||||
|
||||
@@ -58,3 +58,21 @@ describe('escape-grant findOpenGrant (M6 FIX-2)', () => {
|
||||
expect(findOpenGrant('bash:x', [g], [{ action: 'bash:x', ts: g.ts }], now)).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
// M7 Task 1.2b (P-2, КРИТ): canonicalAction обязан иметь ветку PowerShell. Без неё все PS-команды
|
||||
// схлопываются в 'write:' (input.command не в PATH_FIELDS) → один escape-грант разблокирует ЛЮБУЮ
|
||||
// PS-команду в окне, а тест специфичности проходил бы зелёным ложно (a===b==='write:').
|
||||
describe('escape-grant canonicalAction — PowerShell (P-2)', () => {
|
||||
it('PowerShell специфичен (разные команды → разные ключи)', () => {
|
||||
const a = canonicalAction('PowerShell', { command: 'Remove-Item -Recurse -Force C:\\x' });
|
||||
const b = canonicalAction('PowerShell', { command: 'Invoke-WebRequest https://e.rf' });
|
||||
expect(a).toMatch(/^powershell:/);
|
||||
expect(a).not.toBe(b); // НЕ оба 'write:' (баг до 1.2b даёт a===b==='write:')
|
||||
expect(a).not.toBe('write:');
|
||||
});
|
||||
it('PowerShell нормализует пробелы (тот же ключ при whitespace-дрейфе)', () => {
|
||||
const a = canonicalAction('PowerShell', { command: 'Remove-Item -Recurse -Force C:\\x' });
|
||||
const b = canonicalAction('PowerShell', { command: 'Remove-Item -Recurse -Force C:\\x' });
|
||||
expect(a).toBe(b);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user