// lib/parsers/bash-normalize.mjs // Bash-evasion normalization, lifted from hooks/scripts/pre-bash-executor.mjs. // // Source: ../../hooks/scripts/pre-bash-executor.mjs (lines 22-45) — verbatim // extraction so the runtime hook and the test suite share one implementation. // The hook still inlines a copy because it cannot import from outside the // plugin distribution at this time; both copies must stay in sync. /** * Strip bash evasion techniques: empty quotes, ${} expansion, backslash splitting. * Used to canonicalize a command before running denylist regex over it. */ export function normalizeBashExpansion(cmd) { if (typeof cmd !== 'string' || cmd === '') return ''; let result = cmd .replace(/''/g, '') .replace(/""/g, '') .replace(/\$\{(\w)\}/g, '$1') .replace(/\$\{[^}]*\}/g, '') .replace(/`\s*`/g, ''); let prev; do { prev = result; result = result.replace(/(\w)\\(\w)/g, '$1$2'); } while (result !== prev); return result; } /** * Strip ANSI escape codes and collapse whitespace. */ export function normalizeCommand(cmd) { if (typeof cmd !== 'string') return ''; return cmd .replace(/\x1B\[[0-9;]*m/g, '') .replace(/\s+/g, ' ') .trim(); } /** * Full canonicalization pipeline used by hooks before pattern matching. */ export function canonicalize(cmd) { return normalizeCommand(normalizeBashExpansion(cmd)); }