orca/src/shared/managed-agent-command-token.ts

66 lines
1.6 KiB
TypeScript

const QUOTES = new Set(['"', "'"])
type ExtractExecutableTokenOptions = {
platform?: NodeJS.Platform
}
export function extractExecutableToken(
command: string | null | undefined,
options: ExtractExecutableTokenOptions = {}
): string | null {
const input = command?.trim()
if (!input) {
return null
}
const backslashEscapes = (options.platform ?? process.platform) !== 'win32'
let index = 0
let quote: string | null = null
let token = ''
while (index < input.length) {
const char = input[index]
if (quote) {
if (char === quote) {
quote = null
} else if (char === '\\' && quote === '"' && backslashEscapes && index + 1 < input.length) {
index += 1
token += input[index]
} else {
token += char
}
} else if (QUOTES.has(char)) {
quote = char
} else if (/\s/.test(char)) {
break
} else if (char === '\\' && backslashEscapes && index + 1 < input.length) {
index += 1
token += input[index]
} else {
token += char
}
index += 1
}
return token.length > 0 ? token : null
}
export function hasPathSeparatorToken(token: string): boolean {
return token.includes('/') || token.includes('\\')
}
export function isSafeExecutableBasename(token: string): boolean {
return /^[A-Za-z0-9._+-]+$/.test(token)
}
export function isSafeOverrideExecutableToken(token: string): boolean {
if (token.includes('\0')) {
return false
}
if (!hasPathSeparatorToken(token)) {
return isSafeExecutableBasename(token)
}
return (
!token.includes('..') &&
!/[|&;<>(){}[\]$`"'*!?]/.test(token) &&
/^[A-Za-z0-9._+\-/:\\~ ]+$/.test(token)
)
}