Skip to content

Latest commit

 

History

History
251 lines (209 loc) · 6.23 KB

File metadata and controls

251 lines (209 loc) · 6.23 KB

Examples

Real-world usage examples for Terminal.

Reminder: Terminal is a command filter, not a sandbox. All examples below should be combined with OS-level hardening. See Security for chroot, containers, and unprivileged users.

AI Agent Integration

Filter AI agent commands before execution with a strict safeguard configuration:

import Terminal from '@neabyte/terminal'

Terminal.initialize({
  workspaces: ['/home/ai-agent/projects'],
  commands: {
    // Only allow specific safe commands. Never allow interpreter wildcards.
    allow: [
      'git status',
      'git log *',
      'git diff *',
      'git branch *',
      'ls *',
      'cat *',
      'head *',
      'tail *',
      'wc *',
      'echo *',
      'npm run build',
      'npm run test',
      'npm run lint',
      'npm install',
      'npm ci'
    ],
    // Explicitly block dangerous commands and all interpreters
    deny: [
      'rm *',
      'sudo *',
      'chmod *',
      'mv * /',
      'cp * /',
      'sh *',
      'bash *',
      'curl *',
      'wget *',
      'nc *',
      'python3 *',
      'node *',
      'deno *'
    ],
    maxArgs: 10,
    strictArgs: true, // Blocks ; | & ` $ ( ) { } [ ] < > and ../
    noShell: true // Direct execution only
  },
  env: {
    allow: ['NODE_ENV', 'PATH'],
    deny: ['HOME', 'SSH_*', 'AWS_*', 'TOKEN*', 'SECRET*', 'LD_PRELOAD', 'LD_LIBRARY_PATH']
  },
  timeout: 30000,
  uid: 1000, // Drop privileges at spawn time
  gid: 1000
})

export async function safeExecute(command: string): Promise<string> {
  try {
    const result = await Terminal.execute(command, {
      cwd: '/home/ai-agent/projects',
      timeout: 15000
    })
    if (result.exitCode !== 0) {
      return `Error (exit ${result.exitCode}): ${result.stderr}`
    }
    return result.stdout
  } catch (error) {
    return `Blocked: ${(error as Error).message}`
  }
}

Why this config is safer:

  • No interpreter wildcards - python3 *, node *, deno * are denied. An LLM that can run arbitrary scripts can bypass any command filter.
  • Specific commands only - git status is allowed, but git * is not. This prevents git aliases or hooks from being abused.
  • strictArgs + noShell - Blocks shell injection (echo; rm -rf /), command chaining (&&, ||), and path traversal (../).
  • Privilege dropping - uid/gid runs the child as an unprivileged user, even if the parent has elevated permissions.

Do NOT allow node *.js or python3 *.py unless you control every file in the workspace. See Interpreter Usage.

CI/CD Pipeline

Restrict build scripts:

import Terminal from '@neabyte/terminal'

Terminal.initialize({
  workspaces: ['/workspace'],
  commands: {
    allow: ['npm run *', 'npm install', 'npm ci', 'echo *', 'ls *'],
    deny: ['npm publish', 'npm unpublish', 'rm *', 'git push *', 'git reset *'],
    maxArgs: 10,
    strictArgs: true,
    noShell: true
  },
  timeout: 300000
})

Read-Only Command Wrapper

For safely running read-only commands:

import Terminal from '@neabyte/terminal'

Terminal.initialize({
  workspaces: ['/readonly-data'],
  commands: {
    allow: ['echo *', 'cat *', 'ls *', 'wc *', 'head *', 'tail *'],
    deny: ['rm *', 'mv *', 'cp *', '>', '>>'],
    maxArgs: 5,
    strictArgs: true,
    noShell: true
  },
  timeout: 10000
})

This is not a sandbox. cat /etc/passwd is allowed because Terminal only checks the command string. Use filesystem permissions (chmod) to enforce read-only access.

File Watcher with Safe Commands

Run commands on file changes:

import Terminal from '@neabyte/terminal'

Terminal.initialize({
  workspaces: ['/project'],
  commands: {
    allow: ['npm run build', 'npm run test', 'echo *'],
    deny: ['rm *', 'sudo *'],
    maxArgs: 5,
    strictArgs: true,
    noShell: true
  }
})

async function onFileChange(filepath: string): Promise<void> {
  if (filepath.endsWith('.ts')) {
    const result = await Terminal.execute('npm run build', {
      cwd: '/project',
      timeout: 60000
    })
    console.log(result.exitCode === 0 ? 'Build successful' : 'Build failed')
  }
}

Process Monitoring Dashboard

Track multiple background tasks:

import Terminal from '@neabyte/terminal'

async function runMonitoredTasks(): Promise<void> {
  const tasks = [
    { name: 'Database', cmd: 'docker start postgres' },
    { name: 'Cache', cmd: 'docker start redis' },
    { name: 'Server', cmd: 'node server.js' }
  ]

  for (const task of tasks) {
    const { id } = await Terminal.execute(task.cmd, {
      cwd: '/project',
      background: true
    })
    console.log(`Started ${task.name}: ${id}`)
  }

  setInterval(() => {
    const processes = Terminal.getList()
    console.log('\n--- Active Processes ---')
    for (const proc of processes) {
      console.log(`${proc.id}: ${proc.command} (${proc.running ? 'running' : 'stopped'})`)
    }
  }, 5000)
}

Privilege Dropping

Run commands as an unprivileged user:

import Terminal from '@neabyte/terminal'

Terminal.initialize({
  workspaces: ['/workspace'],
  commands: {
    allow: ['git *', 'npm *', 'echo *'],
    deny: ['rm *', 'sudo *'],
    maxArgs: 10,
    strictArgs: true,
    noShell: true,
    killSignal: 'SIGTERM'
  },
  env: {
    allow: ['NODE_ENV', 'PATH'],
    deny: ['HOME', 'SSH_*', 'AWS_*']
  },
  timeout: 30000,
  uid: 1000,
  gid: 1000
})

const result = await Terminal.execute('git status', {
  cwd: '/workspace'
})

Note: uid/gid require the parent process to have permission to change to the target identity. Combine with OS-level unprivileged users for real isolation.

Windows Hardening

Prevent console window pop-ups and argument injection on Windows:

import Terminal from '@neabyte/terminal'

Terminal.initialize({
  workspaces: ['C:\\workspace'],
  commands: {
    allow: ['echo *', 'dir *'],
    deny: ['del *', 'format *'],
    maxArgs: 5,
    strictArgs: true,
    noShell: true,
    windowsHide: true,
    windowsVerbatimArguments: true
  },
  timeout: 10000
})

See Also