Cosmo Docs
API Reference

Command Security

How Cosmo classifies, sandboxes, and gates shell commands before execution.

Cosmo's AI engine can run shell commands on the user's system (e.g., git status, npm install, ls). The command policy system ensures every command is classified by risk, explained in plain English, and gated by user permission before it executes.

Architecture

The command policy sits between the LLM's tool call and the actual execFile call:

LLM emits tool_call
  → CommandPolicy.evaluateCommand(command, args, workspaceDir)
    → returns CommandVerdict { risk, explanation, blocked }
  → if blocked: return error to LLM (no prompt shown)
  → if not blocked: show confirmation UI with risk + explanation
    → user allows or denies
  → execFile (only if allowed)

Execution uses Node.js execFile with arguments passed as an array — never interpolated into a shell string — preventing command injection.

Risk Levels

Every command receives one of four risk classifications:

LevelColorMeaningUser Prompt?
safeGreenRead-only, cannot modify stateYes
moderateYellowWrites within the workspace directoryYes
dangerousRedSystem-wide impact or writes outside workspaceYes
blockedNever allowedNo — instant error

Safe Commands

Read-only commands that cannot modify the system:

  • File inspection: ls, cat, head, tail, wc, tree, file, stat
  • Search: grep, rg, find (without -exec/-delete), ag
  • Git (read): git status, git log, git diff, git branch, git blame
  • GitHub CLI (read): gh pr list, gh issue view, gh run list
  • Package managers (read): npm list, npm outdated, npm audit
  • System info: pwd, whoami, date, uname, which, env
  • Version checks: any command with --version or -v

Moderate Commands

Commands that modify files within the workspace directory or have limited scope:

  • Git (write): git add, git commit, git push, git checkout
  • Package managers: npm install, npm run, pnpm build
  • File operations (in workspace): mkdir, touch, cp, mv, rm
  • Script execution: node script.js, python script.py

Dangerous Commands

Commands with system-wide impact or that target paths outside the workspace:

  • Privilege escalation: sudo, su
  • Permissions: chmod, chown
  • Global installs: npm install -g, pip install --user
  • Force operations: git push --force
  • System package managers: brew install
  • Any file-modifying command targeting a path outside the workspace directory

Blocked Commands

These are never executed — the LLM receives an error explaining why:

  • Destructive system commands: mkfs, fdisk, dd, shutdown, reboot, halt
  • Protected paths: operations targeting /usr, /etc, /System, /var, /bin, /sbin, ~/.ssh, ~/.gnupg, ~/.aws
  • Recursive delete on critical paths: rm -rf /, rm -rf ~

Directory Sandboxing

Commands that modify files (rm, cp, mv, mkdir, touch, ln, rsync) have their path arguments resolved against the workspace directory:

  1. Relative paths are resolved from workspaceDir
  2. ~ is expanded to the user's home directory
  3. Each resolved path is checked:
    • Inside workspace directory → risk stays moderate
    • Outside workspace directory → escalated to dangerous
    • Targeting a protected system path → blocked

Plain-English Explanations

Every command gets a human-readable explanation generated from static templates:

CommandExplanation
ls -laList files in the current folder
git push origin mainPush commits to the remote repository
rm -rf node_modulesDelete folder and all its contents: node_modules
npm install lodashInstall package: lodash
curl https://api.example.comFetch data from: https://api.example.com
git push --forceForce-push changes to the remote (overwrites remote history)

Unknown commands fall back to: "Run command: {command} {args}".

Denial Behavior

When a user denies a command, the engine stops immediately. It does not:

  • Feed the denial back to the LLM for retry
  • Continue executing other pending tool calls
  • Attempt an alternative command

This is intentional — a denial is a hard stop, not a suggestion to try something else.

Types

import { evaluateCommand, type CommandRisk, type CommandVerdict } from '@cosmohq/core'

type CommandRisk = 'safe' | 'moderate' | 'dangerous' | 'blocked'

interface CommandVerdict {
  risk: CommandRisk
  explanation: string
  blocked: boolean
  blockReason?: string
}

// Evaluate a command before execution
const verdict = evaluateCommand('rm', ['-rf', 'node_modules'], '/path/to/workspace')
// → { risk: 'moderate', explanation: 'Delete folder...', blocked: false }

Confirmation Interface

Shells (CLI, Desktop) receive a ToolConfirmationRequest and render it appropriately:

interface ToolConfirmationRequest {
  call: ToolCall
  risk: CommandRisk
  explanation: string
}

type ToolConfirmFn = (request: ToolConfirmationRequest) => Promise<boolean>

The CLI renders a color-coded terminal prompt. The Desktop app renders an inline confirmation card with Allow/Deny buttons.