mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-08-03 05:15:24 +00:00
Some checks failed
CI / semgrep (push) Has been cancelled
extractBashCommands recorded the wrapper (sudo, npx, rtk, and friends) instead of the command it delegates to, so any agent that prefixes its shell calls collapsed its whole bash breakdown into one meaningless bucket and the optimize detectors lost the actual tool. Skip a known set of command wrappers when a real command follows, and interleave that skip with the existing VAR=value env-assignment skip so forms like 'sudo NODE_ENV=prod node x' resolve to the real tool. A wrapper followed by a flag or a quoted token is kept as-is so we never emit a garbage key. Closes #657
64 lines
1.7 KiB
TypeScript
64 lines
1.7 KiB
TypeScript
import { basename } from 'path'
|
|
import stripAnsi from 'strip-ansi'
|
|
|
|
function stripQuotedStrings(command: string): string {
|
|
return command.replace(/"[^"]*"|'[^']*'/g, match => ' '.repeat(match.length))
|
|
}
|
|
|
|
const COMMAND_PREFIXES = new Set([
|
|
'sudo', 'doas',
|
|
'npx', 'bunx',
|
|
'time',
|
|
'nice', 'nohup', 'stdbuf',
|
|
'rtk',
|
|
])
|
|
|
|
export function extractBashCommands(rawCommand: string): string[] {
|
|
if (!rawCommand || !rawCommand.trim()) return []
|
|
|
|
const command = stripAnsi(rawCommand)
|
|
const stripped = stripQuotedStrings(command)
|
|
|
|
const separatorRegex = /\s*(?:&&|;|\|)\s*/g
|
|
const separators: Array<{ start: number; end: number }> = []
|
|
let match: RegExpExecArray | null
|
|
|
|
while ((match = separatorRegex.exec(stripped)) !== null) {
|
|
separators.push({ start: match.index, end: match.index + match[0].length })
|
|
}
|
|
|
|
const ranges: Array<[number, number]> = []
|
|
let cursor = 0
|
|
for (const sep of separators) {
|
|
ranges.push([cursor, sep.start])
|
|
cursor = sep.end
|
|
}
|
|
ranges.push([cursor, command.length])
|
|
|
|
const commands: string[] = []
|
|
for (const [start, end] of ranges) {
|
|
const segment = command.slice(start, end).trim()
|
|
if (!segment) continue
|
|
|
|
const tokens = segment.split(/\s+/)
|
|
let i = 0
|
|
while (i < tokens.length) {
|
|
if (/^\w+=/.test(tokens[i]!)) { i++; continue }
|
|
const next = tokens[i + 1]
|
|
if (
|
|
next !== undefined &&
|
|
COMMAND_PREFIXES.has(basename(tokens[i]!)) &&
|
|
!next.startsWith('-') &&
|
|
!/["']/.test(next)
|
|
) { i++; continue }
|
|
break
|
|
}
|
|
const base = i < tokens.length ? basename(tokens[i]!) : ''
|
|
|
|
if (base && base !== 'cd' && base !== 'true' && base !== 'false') {
|
|
commands.push(base)
|
|
}
|
|
}
|
|
|
|
return commands
|
|
}
|