diff --git a/packages/mosaic/src/fleet/fleet-documentation.spec.ts b/packages/mosaic/src/fleet/fleet-documentation.spec.ts index addfb1d..bc9fb8a 100644 --- a/packages/mosaic/src/fleet/fleet-documentation.spec.ts +++ b/packages/mosaic/src/fleet/fleet-documentation.spec.ts @@ -131,17 +131,117 @@ function markdownLinkViolations( return violations; } +function commandBasename(token: string): string { + return token.slice(token.lastIndexOf('/') + 1); +} + +function normalizedCommandTokens(segment: string): string[] { + const tokens = segment + .trim() + .replace(/^(?:[$#>]\s*)/, '') + .split(/\s+/) + .filter((token): boolean => token !== ''); + let index = 0; + + while (index < tokens.length) { + const wrapper = commandBasename(tokens[index] ?? ''); + if (wrapper === 'env') { + index += 1; + while (index < tokens.length) { + const token = tokens[index] ?? ''; + if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(token)) { + index += 1; + continue; + } + if (/^(?:-u|--unset|-C|--chdir)$/.test(token)) { + index += 2; + continue; + } + if (/^(?:-u|--unset=|-C|--chdir=)/.test(token)) { + index += 1; + continue; + } + if (/^(?:-S|--split-string)$/.test(token)) { + index += 1; + break; + } + if (token.startsWith('-')) { + index += 1; + continue; + } + break; + } + continue; + } + if (wrapper === 'command') { + index += 1; + while ((tokens[index] ?? '').startsWith('-')) index += 1; + continue; + } + break; + } + + return tokens.slice(index); +} + +function containsPrivilegedCommand(source: string): boolean { + const privilegedCommands = new Set([ + 'sudo', + 'doas', + 'pkexec', + 'systemctl', + 'service', + 'mount', + 'umount', + 'reboot', + 'shutdown', + 'poweroff', + 'halt', + 'chown', + 'chmod', + 'chroot', + 'useradd', + 'usermod', + 'groupadd', + 'visudo', + 'apt', + 'apt-get', + 'apt-cache', + 'dpkg', + 'yum', + 'dnf', + 'rpm', + 'apk', + 'pacman', + 'zypper', + 'emerge', + 'snap', + 'flatpak', + 'brew', + 'nix-env', + ]); + + return source.split(/[\n;&|]+/).some((segment): boolean => { + const tokens = normalizedCommandTokens(segment); + const command = commandBasename(tokens[0] ?? ''); + if (privilegedCommands.has(command)) return true; + const argument = tokens[1] ?? ''; + return ( + command === 'su' && + (/^(?:root|-|--command|-c)$/.test(argument) || argument.startsWith('--command=')) + ); + }); +} + function exampleSafetyViolationKinds(source: string): string[] { const kinds = new Set(); const sensitiveKey = /(?:secret|token|password|credential|MOSAIC_AGENT_COMMAND)/i; const credentialFormat = /(?:\bAKIA[0-9A-Z]{16}\b|\bAIza[0-9A-Za-z_-]{35}\b|\bgh[pousr]_[A-Za-z0-9]{20,}\b|\bgithub_pat_[A-Za-z0-9_]{20,}\b|\bglpat-[A-Za-z0-9_-]{20,}\b|\bnpm_[A-Za-z0-9]{20,}\b|\bsk-ant-(?:api\d{2}-)?[A-Za-z0-9_-]{20,}\b|\bsk-proj-[A-Za-z0-9_-]{20,}\b|\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}\b|\bxox[baprs]-[A-Za-z0-9-]{10,}\b|\bBearer\s+[A-Za-z0-9._~+/=-]{16,}\b|\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b|-----BEGIN [A-Z ]*PRIVATE KEY-----|\b[A-Za-z][A-Za-z0-9+.-]*:\/\/[^\s/:]+:[^\s/@]+@)/; - const privilegedCommand = - /(?:^|[\n;&|])\s*(?:[$#>]\s*)?(?:(?:\/(?:[A-Za-z0-9._+-]+\/)*?)?(?:env|command)\s+(?:(?:-{1,2}[^\s]+|[A-Za-z_][A-Za-z0-9_]*=[^\s]+)\s+)*?)*(?:\/(?:[A-Za-z0-9._+-]+\/)*?)?(?:(?:sudo|doas|pkexec)\s+|su\s+(?:root\b|-|--command\b|-c\b)|(?:systemctl|service|mount|umount|reboot|shutdown|poweroff|halt|chown|chmod|chroot|useradd|usermod|groupadd|visudo|apt|apt-get|apt-cache|dpkg|yum|dnf|rpm|apk|pacman|zypper|emerge|snap|flatpak|brew|nix-env)\s+)/m; if (sensitiveKey.test(source)) kinds.add('sensitive-key'); if (credentialFormat.test(source)) kinds.add('credential-format'); - if (privilegedCommand.test(source)) kinds.add('privileged-command'); + if (containsPrivilegedCommand(source)) kinds.add('privileged-command'); if (/\b(?:Tess|Ultron)\b/.test(source)) kinds.add('identity'); return [...kinds].sort(); } @@ -189,6 +289,17 @@ const UNSAFE_EXAMPLE_FIXTURES = [ expected: 'privileged-command', source: '/usr/bin/env -i /usr/bin/command -- /usr/bin/apt-get install example', }, + { + expected: 'privileged-command', + source: '/usr/bin/env -u FOO /usr/bin/apt-get install example', + }, + { + expected: 'privileged-command', + source: 'env --unset FOO /usr/bin/apt-get install example', + }, + { expected: 'privileged-command', source: 'env -S apt-get install example' }, + { expected: 'privileged-command', source: 'reboot' }, + { expected: 'privileged-command', source: 'su --command=/bin/sh' }, { expected: 'privileged-command', source: 'su root' }, { expected: 'privileged-command', source: '/usr/sbin/chroot /srv/example' }, { expected: 'credential-format', source: ['ghp_', 'a'.repeat(36)].join('') },