(command: string, settings: SandboxSettings = {})
| 568 | } |
| 569 | |
| 570 | export function getSandboxMutationBlockReason(command: string, settings: SandboxSettings = {}): string | null { |
| 571 | const trimmed = command.trim(); |
| 572 | if (!trimmed) return null; |
| 573 | |
| 574 | if (/\bgit\s+/.test(trimmed) && !/\bgit\s+(status|diff|log|show|rev-parse|grep|ls-files)\b/.test(trimmed)) { |
| 575 | return [ |
| 576 | "Sandbox mode blocks git commands that mutate repository state because Shuru guest-side workspace changes do not persist back to the host.", |
| 577 | "Disable sandbox mode to run persistent git mutations on the real workspace.", |
| 578 | ].join(" "); |
| 579 | } |
| 580 | |
| 581 | const blockedPatterns: Array<{ pattern: RegExp; reason: string }> = [ |
| 582 | { |
| 583 | pattern: |
| 584 | /\b(?:prettier\s+--write|eslint\b.*--fix|biome\s+check\b.*--write|ruff\s+check\b.*--fix|gofmt\s+-w|rustfmt\b|clang-format\b.*-i)\b/, |
| 585 | reason: |
| 586 | "Shell-driven formatters that rewrite files are blocked in sandbox mode because those file changes would not persist back to the host workspace.", |
| 587 | }, |
| 588 | ]; |
| 589 | |
| 590 | if (!settings.allowEphemeralInstall) { |
| 591 | const installPatterns: Array<{ pattern: RegExp; reason: string }> = [ |
| 592 | { |
| 593 | pattern: /\b(?:npm|pnpm|yarn|bun)\s+(?:add|install|remove|unlink|update|upgrade)\b/, |
| 594 | reason: |
| 595 | "Package-manager installs are blocked in sandbox mode because workspace changes like lockfile updates would stay inside the Shuru guest overlay.", |
| 596 | }, |
| 597 | { |
| 598 | pattern: /\b(?:pip|pip3)\s+install\b|\bpoetry\s+add\b|\buv\s+add\b|\bcargo\s+add\b/, |
| 599 | reason: |
| 600 | "Dependency install commands are blocked in sandbox mode because resulting workspace changes would not persist back to the host.", |
| 601 | }, |
| 602 | ]; |
| 603 | const installMatch = installPatterns.find(({ pattern }) => pattern.test(trimmed)); |
| 604 | if (installMatch) { |
| 605 | return `${installMatch.reason} Use read_file/edit_file/write_file for durable edits, or disable sandbox mode for host-persistent shell changes.`; |
| 606 | } |
| 607 | } |
| 608 | |
| 609 | const matched = blockedPatterns.find(({ pattern }) => pattern.test(trimmed)); |
| 610 | if (!matched) return null; |
| 611 | return `${matched.reason} Use read_file/edit_file/write_file for durable edits, or disable sandbox mode for host-persistent shell changes.`; |
| 612 | } |
no outgoing calls
no test coverage detected