| import { getFeatureValue_CACHED_MAY_BE_STALE } from 'src/services/analytics/growthbook.js' |
| import { splitCommand_DEPRECATED } from '../../utils/bash/commands.js' |
| import { SandboxManager } from '../../utils/sandbox/sandbox-adapter.js' |
| import { getSettings_DEPRECATED } from '../../utils/settings/settings.js' |
| import { |
| BINARY_HIJACK_VARS, |
| bashPermissionRule, |
| matchWildcardPattern, |
| stripAllLeadingEnvVars, |
| stripSafeWrappers, |
| } from './bashPermissions.js' |
|
|
| type SandboxInput = { |
| command?: string |
| dangerouslyDisableSandbox?: boolean |
| } |
|
|
| |
| |
| |
| function containsExcludedCommand(command: string): boolean { |
| |
| if (process.env.USER_TYPE === 'ant') { |
| const disabledCommands = getFeatureValue_CACHED_MAY_BE_STALE<{ |
| commands: string[] |
| substrings: string[] |
| }>('tengu_sandbox_disabled_commands', { commands: [], substrings: [] }) |
|
|
| |
| for (const substring of disabledCommands.substrings) { |
| if (command.includes(substring)) { |
| return true |
| } |
| } |
|
|
| |
| try { |
| const commandParts = splitCommand_DEPRECATED(command) |
| for (const part of commandParts) { |
| const baseCommand = part.trim().split(' ')[0] |
| if (baseCommand && disabledCommands.commands.includes(baseCommand)) { |
| return true |
| } |
| } |
| } catch { |
| |
| |
| |
| } |
| } |
|
|
| |
| const settings = getSettings_DEPRECATED() |
| const userExcludedCommands = settings.sandbox?.excludedCommands ?? [] |
|
|
| if (userExcludedCommands.length === 0) { |
| return false |
| } |
|
|
| |
| |
| |
| |
| let subcommands: string[] |
| try { |
| subcommands = splitCommand_DEPRECATED(command) |
| } catch { |
| subcommands = [command] |
| } |
|
|
| for (const subcommand of subcommands) { |
| const trimmed = subcommand.trim() |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| const candidates = [trimmed] |
| const seen = new Set(candidates) |
| let startIdx = 0 |
| while (startIdx < candidates.length) { |
| const endIdx = candidates.length |
| for (let i = startIdx; i < endIdx; i++) { |
| const cmd = candidates[i]! |
| const envStripped = stripAllLeadingEnvVars(cmd, BINARY_HIJACK_VARS) |
| if (!seen.has(envStripped)) { |
| candidates.push(envStripped) |
| seen.add(envStripped) |
| } |
| const wrapperStripped = stripSafeWrappers(cmd) |
| if (!seen.has(wrapperStripped)) { |
| candidates.push(wrapperStripped) |
| seen.add(wrapperStripped) |
| } |
| } |
| startIdx = endIdx |
| } |
|
|
| for (const pattern of userExcludedCommands) { |
| const rule = bashPermissionRule(pattern) |
| for (const cand of candidates) { |
| switch (rule.type) { |
| case 'prefix': |
| if (cand === rule.prefix || cand.startsWith(rule.prefix + ' ')) { |
| return true |
| } |
| break |
| case 'exact': |
| if (cand === rule.command) { |
| return true |
| } |
| break |
| case 'wildcard': |
| if (matchWildcardPattern(rule.pattern, cand)) { |
| return true |
| } |
| break |
| } |
| } |
| } |
| } |
|
|
| return false |
| } |
|
|
| export function shouldUseSandbox(input: Partial<SandboxInput>): boolean { |
| if (!SandboxManager.isSandboxingEnabled()) { |
| return false |
| } |
|
|
| |
| if ( |
| input.dangerouslyDisableSandbox && |
| SandboxManager.areUnsandboxedCommandsAllowed() |
| ) { |
| return false |
| } |
|
|
| if (!input.command) { |
| return false |
| } |
|
|
| |
| if (containsExcludedCommand(input.command)) { |
| return false |
| } |
|
|
| return true |
| } |
|
|