feat: add per-project bash command allowlist system

Implement hierarchical command security with project and org-level configs:

WHAT'S NEW:
- Project-level YAML config (.autocoder/allowed_commands.yaml)
- Organization-level config (~/.autocoder/config.yaml)
- Pattern matching (exact, wildcards, local scripts)
- Hardcoded blocklist (sudo, dd, shutdown - never allowed)
- Org blocklist (terraform, kubectl - configurable)
- Helpful error messages with config hints
- Comprehensive documentation and examples

ARCHITECTURE:
- Hierarchical resolution: Hardcoded → Org Block → Org Allow → Global → Project
- YAML validation with 50 command limit per project
- Pattern matching: exact ("swift"), wildcards ("swift*"), scripts ("./build.sh")
- Secure by default: all examples commented out

TESTING:
- 136 unit tests (pattern matching, YAML, hierarchy, validation)
- 9 integration tests (real security hook flows)
- All tests passing, 100% backward compatible

DOCUMENTATION:
- examples/README.md - comprehensive guide with use cases
- examples/project_allowed_commands.yaml - template (all commented)
- examples/org_config.yaml - org config template (all commented)
- PHASE3_SPEC.md - mid-session approval spec (future enhancement)
- Updated CLAUDE.md with security model documentation

USE CASES:
- iOS projects: Add Swift toolchain (xcodebuild, swift*, etc.)
- Rust projects: Add cargo, rustc, clippy
- Enterprise: Block aws, kubectl, terraform org-wide
- Custom scripts: Allow ./scripts/build.sh

PHASES:
 Phase 1: Project YAML + blocklist (implemented)
 Phase 2: Org config + hierarchy (implemented)
📋 Phase 3: Mid-session approval (spec ready, not implemented)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Marian Paul
2026-01-22 12:16:16 +01:00
parent 29c6b252a9
commit a9a0fcd865
11 changed files with 3789 additions and 8 deletions

531
examples/README.md Normal file
View File

@@ -0,0 +1,531 @@
# AutoCoder Security Configuration Examples
This directory contains example configuration files for controlling which bash commands the autonomous coding agent can execute.
## Table of Contents
- [Quick Start](#quick-start)
- [Project-Level Configuration](#project-level-configuration)
- [Organization-Level Configuration](#organization-level-configuration)
- [Command Hierarchy](#command-hierarchy)
- [Pattern Matching](#pattern-matching)
- [Common Use Cases](#common-use-cases)
- [Security Best Practices](#security-best-practices)
---
## Quick Start
### For a Single Project (Most Common)
When you create a new project with AutoCoder, it automatically creates:
```
my-project/
.autocoder/
allowed_commands.yaml ← Automatically created from template
```
**Edit this file** to add project-specific commands (Swift tools, Rust compiler, etc.).
### For All Projects (Organization-Wide)
If you want commands available across **all projects**, manually create:
```bash
# Copy the example to your home directory
cp examples/org_config.yaml ~/.autocoder/config.yaml
# Edit it to add org-wide commands
nano ~/.autocoder/config.yaml
```
---
## Project-Level Configuration
**File:** `{project_dir}/.autocoder/allowed_commands.yaml`
**Purpose:** Define commands needed for THIS specific project.
**Example** (iOS project):
```yaml
version: 1
commands:
- name: swift
description: Swift compiler
- name: xcodebuild
description: Xcode build system
- name: swift*
description: All Swift tools (swiftc, swiftlint, swiftformat)
- name: ./scripts/build.sh
description: Project build script
```
**When to use:**
- ✅ Project uses a specific language toolchain (Swift, Rust, Go)
- ✅ Project has custom build scripts
- ✅ Temporary tools needed during development
**Limits:**
- Maximum 50 commands per project
- Cannot override org-level blocked commands
- Cannot allow hardcoded blocklist commands (sudo, dd, etc.)
**See:** `examples/project_allowed_commands.yaml` for full example with Rust, Python, iOS, etc.
---
## Organization-Level Configuration
**File:** `~/.autocoder/config.yaml`
**Purpose:** Define commands and policies for ALL projects.
**Example** (startup team):
```yaml
version: 1
# Available to all projects
allowed_commands:
- name: jq
description: JSON processor
- name: python3
description: Python interpreter
# Blocked across all projects (cannot be overridden)
blocked_commands:
- aws
- kubectl
- terraform
```
**When to use:**
- ✅ Multiple projects need the same tools (jq, python3, etc.)
- ✅ Enforce organization-wide security policies
- ✅ Block dangerous commands across all projects
**See:** `examples/org_config.yaml` for full example with enterprise/startup configurations.
---
## Command Hierarchy
When the agent tries to run a command, the system checks in this order:
```
┌─────────────────────────────────────────────────────┐
│ 1. HARDCODED BLOCKLIST (highest priority) │
│ sudo, dd, shutdown, reboot, chown, etc. │
│ ❌ NEVER allowed, even with user approval │
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 2. ORG BLOCKLIST (~/.autocoder/config.yaml) │
│ Commands you block organization-wide │
│ ❌ Projects CANNOT override these │
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 3. ORG ALLOWLIST (~/.autocoder/config.yaml) │
│ Commands available to all projects │
│ ✅ Automatically available │
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 4. GLOBAL ALLOWLIST (security.py) │
│ Default commands: npm, git, curl, ls, cat, etc. │
│ ✅ Always available │
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 5. PROJECT ALLOWLIST (.autocoder/allowed_commands) │
│ Project-specific commands │
│ ✅ Available only to this project │
└─────────────────────────────────────────────────────┘
```
**Key Rules:**
- If a command is BLOCKED at any level above, it cannot be allowed below
- If a command is ALLOWED at any level, it's available (unless blocked above)
- Blocklist always wins over allowlist
---
## Pattern Matching
You can use patterns to match multiple commands:
### Exact Match
```yaml
- name: swift
description: Swift compiler only
```
Matches: `swift`
Does NOT match: `swiftc`, `swiftlint`
### Prefix Wildcard
```yaml
- name: swift*
description: All Swift tools
```
Matches: `swift`, `swiftc`, `swiftlint`, `swiftformat`
Does NOT match: `npm`, `rustc`
### Local Scripts
```yaml
- name: ./scripts/build.sh
description: Build script
```
Matches:
- `./scripts/build.sh`
- `scripts/build.sh`
- `/full/path/to/scripts/build.sh`
- Running `build.sh` from any directory (matched by filename)
---
## Common Use Cases
### iOS Development
**Project config** (`.autocoder/allowed_commands.yaml`):
```yaml
version: 1
commands:
- name: swift*
description: All Swift tools
- name: xcodebuild
description: Xcode build system
- name: xcrun
description: Xcode tools runner
- name: simctl
description: iOS Simulator control
```
### Rust CLI Project
**Project config**:
```yaml
version: 1
commands:
- name: cargo
description: Rust package manager
- name: rustc
description: Rust compiler
- name: rustfmt
description: Rust formatter
- name: clippy
description: Rust linter
- name: ./target/debug/my-cli
description: Debug build
- name: ./target/release/my-cli
description: Release build
```
### API Testing Project
**Project config**:
```yaml
version: 1
commands:
- name: jq
description: JSON processor
- name: httpie
description: HTTP client
- name: ./scripts/test-api.sh
description: API test runner
```
### Enterprise Organization (Restrictive)
**Org config** (`~/.autocoder/config.yaml`):
```yaml
version: 1
allowed_commands:
- name: jq
description: JSON processor
blocked_commands:
- aws # No cloud access
- gcloud
- az
- kubectl # No k8s access
- terraform # No infrastructure changes
- psql # No production DB access
- mysql
```
### Startup Team (Permissive)
**Org config** (`~/.autocoder/config.yaml`):
```yaml
version: 1
allowed_commands:
- name: python3
description: Python interpreter
- name: jq
description: JSON processor
- name: pytest
description: Python tests
blocked_commands: [] # Rely on hardcoded blocklist only
```
---
## Security Best Practices
### ✅ DO
1. **Start restrictive, add as needed**
- Begin with default commands only
- Add project-specific tools when required
- Review the agent's blocked command errors to understand what's needed
2. **Use org-level config for shared tools**
- If 3+ projects need `jq`, add it to org config
- Reduces duplication across project configs
3. **Block dangerous commands at org level**
- Prevent accidental production deployments (`kubectl`, `terraform`)
- Block cloud CLIs if appropriate (`aws`, `gcloud`, `az`)
4. **Use descriptive command names**
- Good: `description: "Swift compiler for iOS builds"`
- Bad: `description: "Compiler"`
5. **Prefer patterns for tool families**
- `swift*` instead of listing `swift`, `swiftc`, `swiftlint` separately
- Automatically includes future tools (e.g., new Swift utilities)
### ❌ DON'T
1. **Don't add commands "just in case"**
- Only add when the agent actually needs them
- Empty config is fine - defaults are usually enough
2. **Don't try to allow blocklisted commands**
- Commands like `sudo`, `dd`, `shutdown` can NEVER be allowed
- The system will reject these in validation
3. **Don't use org config for project-specific tools**
- Bad: Adding `xcodebuild` to org config when only one project uses it
- Good: Add `xcodebuild` to that project's config
4. **Don't exceed the 50 command limit per project**
- If you need more, you're probably being too specific
- Use wildcards instead: `npm-*` covers many npm tools
5. **Don't ignore validation errors**
- If your YAML is rejected, fix the structure
- Common issues: missing `version`, malformed lists, over 50 commands
---
## Default Allowed Commands
These commands are **always available** to all projects:
**File Operations:**
- `ls`, `cat`, `head`, `tail`, `wc`, `grep`, `cp`, `mkdir`, `mv`, `rm`, `touch`
**Shell:**
- `pwd`, `echo`, `sh`, `bash`, `sleep`
**Version Control:**
- `git`
**Process Management:**
- `ps`, `lsof`, `kill`, `pkill` (dev processes only: node, npm, vite)
**Network:**
- `curl`
**Node.js:**
- `npm`, `npx`, `pnpm`, `node`
**Docker:**
- `docker`
**Special:**
- `chmod` (only `+x` mode for making scripts executable)
---
## Hardcoded Blocklist
These commands are **NEVER allowed**, even with user approval:
**Disk Operations:**
- `dd`, `mkfs`, `fdisk`, `parted`
**System Control:**
- `shutdown`, `reboot`, `poweroff`, `halt`, `init`
**Privilege Escalation:**
- `sudo`, `su`, `doas`
**System Services:**
- `systemctl`, `service`, `launchctl`
**Network Security:**
- `iptables`, `ufw`
**Ownership Changes:**
- `chown`, `chgrp`
**Dangerous Commands** (Phase 3 will add approval):
- `aws`, `gcloud`, `az`, `kubectl`, `docker-compose`
---
## Troubleshooting
### Error: "Command 'X' is not allowed"
**Solution:** Add the command to your project config:
```yaml
# In .autocoder/allowed_commands.yaml
commands:
- name: X
description: What this command does
```
### Error: "Command 'X' is blocked at organization level"
**Cause:** The command is in the org blocklist or hardcoded blocklist.
**Solution:**
- If in org blocklist: Edit `~/.autocoder/config.yaml` to remove it
- If in hardcoded blocklist: Cannot be allowed (by design)
### Error: "Could not parse YAML config"
**Cause:** YAML syntax error.
**Solution:** Check for:
- Missing colons after keys
- Incorrect indentation (use 2 spaces, not tabs)
- Missing quotes around special characters
### Config not taking effect
**Solution:**
1. Restart the agent (changes are loaded on startup)
2. Verify file location:
- Project: `{project}/.autocoder/allowed_commands.yaml`
- Org: `~/.autocoder/config.yaml` (must be manually created)
3. Check YAML is valid (run through a YAML validator)
---
## Testing
### Running the Tests
AutoCoder has comprehensive tests for the security system:
**Unit Tests** (136 tests - fast):
```bash
source venv/bin/activate
python test_security.py
```
Tests:
- Pattern matching (exact, wildcards, scripts)
- YAML loading and validation
- Blocklist enforcement
- Project and org config hierarchy
- All existing security validations
**Integration Tests** (9 tests - uses real security hooks):
```bash
source venv/bin/activate
python test_security_integration.py
```
Tests:
- Blocked commands are rejected (sudo, shutdown, etc.)
- Default commands work (ls, git, npm, etc.)
- Non-allowed commands are blocked (wget, python, etc.)
- Project config allows commands (swift, xcodebuild, etc.)
- Pattern matching works (swift* matches swiftlint)
- Org blocklist cannot be overridden
- Org allowlist is inherited by projects
- Invalid YAML is safely ignored
- 50 command limit is enforced
### Manual Testing
To manually test the security system:
**1. Create a test project:**
```bash
python start.py
# Choose "Create new project"
# Name it "security-test"
```
**2. Edit the project config:**
```bash
# Navigate to the project directory
cd path/to/security-test
# Edit the config
nano .autocoder/allowed_commands.yaml
```
**3. Add a test command (e.g., Swift):**
```yaml
version: 1
commands:
- name: swift
description: Swift compiler
```
**4. Run the agent and observe:**
- Try a blocked command: `"Run sudo apt install nginx"` → Should be blocked
- Try an allowed command: `"Run ls -la"` → Should work
- Try your config command: `"Run swift --version"` → Should work
- Try a non-allowed command: `"Run wget https://example.com"` → Should be blocked
**5. Check the agent output:**
The agent will show security hook messages like:
```
Command 'sudo' is blocked at organization level and cannot be approved.
```
Or:
```
Command 'wget' is not allowed.
To allow this command:
1. Add to .autocoder/allowed_commands.yaml for this project, OR
2. Request mid-session approval (the agent can ask)
```
---
## Files Reference
- **`examples/project_allowed_commands.yaml`** - Full project config template
- **`examples/org_config.yaml`** - Full org config template
- **`security.py`** - Implementation and hardcoded blocklist
- **`test_security.py`** - Unit tests (136 tests)
- **`test_security_integration.py`** - Integration tests (9 tests)
- **`CLAUDE.md`** - Full system documentation
---
## Questions?
See the main documentation in `CLAUDE.md` for architecture details and implementation specifics.

172
examples/org_config.yaml Normal file
View File

@@ -0,0 +1,172 @@
# Organization-Level AutoCoder Configuration
# ============================================
# Location: ~/.autocoder/config.yaml
#
# IMPORTANT: This file is OPTIONAL and must be manually created by you.
# It does NOT exist by default.
#
# Org-level config applies to ALL projects and provides:
# 1. Organization-wide allowed commands (available to all projects)
# 2. Organization-wide blocked commands (cannot be overridden by projects)
# 3. Global settings (approval timeout, etc.)
#
# Use this to:
# - Add commands that ALL your projects need (jq, python3, etc.)
# - Block dangerous commands across ALL projects (aws, kubectl, etc.)
# - Enforce organization-wide security policies
version: 1
# ==========================================
# Organization-Wide Allowed Commands
# ==========================================
# These commands become available to ALL projects automatically.
# Projects don't need to add them to their own .autocoder/allowed_commands.yaml
#
# By default, this is empty. Uncomment and add commands as needed.
allowed_commands: []
# Common development utilities
# - name: jq
# description: JSON processor for API responses
# - name: python3
# description: Python 3 interpreter
# - name: pip3
# description: Python package installer
# - name: pytest
# description: Python testing framework
# - name: black
# description: Python code formatter
# Database CLIs (if safe in your environment)
# - name: psql
# description: PostgreSQL client
# - name: mysql
# description: MySQL client
# ==========================================
# Organization-Wide Blocked Commands
# ==========================================
# Commands listed here are BLOCKED across ALL projects.
# Projects CANNOT override these blocks - this is the final word.
#
# Use this to enforce security policies, such as:
# - Preventing accidental production deployments
# - Blocking cloud CLI tools to avoid infrastructure changes
# - Preventing access to production databases
#
# By default, this is empty. Uncomment commands you want to block.
blocked_commands: []
# Block cloud CLIs to prevent accidental production changes
# - aws
# - gcloud
# - az
# Block container orchestration to prevent production deployments
# - kubectl
# - docker-compose
# Block infrastructure-as-code tools
# - terraform
# - pulumi
# Block database CLIs to prevent production data access
# - psql
# - mysql
# - mongosh
# Block other potentially dangerous tools
# - ansible
# - chef
# - puppet
# ==========================================
# Global Settings (Phase 3 feature)
# ==========================================
# These settings control approval behavior when agents request
# commands that aren't in the allowlist.
# How long to wait for user approval before denying a command request
approval_timeout_minutes: 5
# ==========================================
# Command Hierarchy (for reference)
# ==========================================
# When the agent tries to run a bash command, the system checks in this order:
#
# 1. Hardcoded Blocklist (in security.py) - HIGHEST PRIORITY
# Commands like: sudo, dd, shutdown, reboot, etc.
# These can NEVER be allowed, even with user approval.
#
# 2. Org Blocked Commands (this file)
# Commands you specify in "blocked_commands:" above.
# Projects cannot override these.
#
# 3. Org Allowed Commands (this file)
# Commands you specify in "allowed_commands:" above.
# Available to all projects automatically.
#
# 4. Global Allowed Commands (in security.py)
# Default commands: npm, git, curl, ls, cat, etc.
# Always available to all projects.
#
# 5. Project Allowed Commands (.autocoder/allowed_commands.yaml)
# Project-specific commands defined in each project.
# LOWEST PRIORITY (can't override blocks above).
#
# If a command is in BOTH allowed and blocked lists, BLOCKED wins.
# ==========================================
# Example Configurations by Organization Type
# ==========================================
# Startup / Small Team (permissive):
# allowed_commands:
# - name: python3
# - name: jq
# blocked_commands: [] # Empty - rely on hardcoded blocklist only
# Enterprise / Regulated (restrictive):
# allowed_commands: [] # Empty - projects must explicitly request each tool
# blocked_commands:
# - aws
# - gcloud
# - az
# - kubectl
# - terraform
# - psql
# - mysql
# - mongosh
# Development Team (balanced):
# allowed_commands:
# - name: jq
# - name: python3
# - name: pytest
# blocked_commands:
# - aws # Block production access
# - kubectl # Block deployments
# - terraform
# ==========================================
# To Create This File
# ==========================================
# 1. Copy this example to: ~/.autocoder/config.yaml
# 2. Uncomment and customize the sections you need
# 3. Leave empty lists if you don't need org-level controls
#
# To learn more, see: examples/README.md

View File

@@ -0,0 +1,139 @@
# Project-Specific Allowed Commands
# ==================================
# Location: {project_dir}/.autocoder/allowed_commands.yaml
#
# This file defines bash commands that the autonomous coding agent can use
# for THIS SPECIFIC PROJECT, beyond the default allowed commands.
#
# When you create a new project, AutoCoder automatically creates this file
# in your project's .autocoder/ directory. You can customize it for your
# project's specific needs (iOS, Rust, Python, etc.).
version: 1
# Uncomment the commands you need for your specific project.
# By default, this file has NO commands enabled - you must explicitly add them.
commands: []
# ==========================================
# iOS Development Example
# ==========================================
# Uncomment these if building an iOS app:
# - name: xcodebuild
# description: Xcode build system for compiling iOS apps
# - name: swift
# description: Swift compiler and REPL
# - name: swiftc
# description: Swift compiler command-line interface
# - name: xcrun
# description: Run Xcode developer tools
# - name: simctl
# description: iOS Simulator control tool
# Pattern matching with wildcard
# This matches: swift, swiftc, swiftformat, swiftlint, etc.
# - name: swift*
# description: All Swift development tools
# ==========================================
# Rust Development Example
# ==========================================
# Uncomment these if building a Rust project:
# - name: cargo
# description: Rust package manager and build tool
# - name: rustc
# description: Rust compiler
# - name: rustfmt
# description: Rust code formatter
# - name: clippy
# description: Rust linter
# ==========================================
# Python Development Example
# ==========================================
# Uncomment these if building a Python project:
# - name: python3
# description: Python 3 interpreter
# - name: pip3
# description: Python package installer
# - name: pytest
# description: Python testing framework
# ==========================================
# Database Tools Example
# ==========================================
# Uncomment these if you need database access:
# - name: psql
# description: PostgreSQL command-line client
# - name: sqlite3
# description: SQLite database CLI
# ==========================================
# Project-Specific Scripts
# ==========================================
# Local scripts are matched by filename, so these work from any directory
# Uncomment and customize for your project:
# - name: ./scripts/build.sh
# description: Project build script
# - name: ./scripts/test.sh
# description: Run all project tests
# - name: ./scripts/deploy-staging.sh
# description: Deploy to staging environment
# ==========================================
# Notes and Best Practices
# ==========================================
#
# Pattern Matching:
# - Exact: "swift" matches only "swift"
# - Wildcard: "swift*" matches "swift", "swiftc", "swiftlint", etc.
# - Scripts: "./scripts/build.sh" matches the script by name
#
# Limits:
# - Maximum 50 commands per project
# - Commands in the blocklist (sudo, dd, shutdown, etc.) can NEVER be allowed
# - Org-level blocked commands (see ~/.autocoder/config.yaml) cannot be overridden
#
# Default Allowed Commands (always available):
# File operations: ls, cat, head, tail, wc, grep, cp, mkdir, mv, rm, touch
# Shell: pwd, echo, sh, bash, sleep
# Version control: git
# Process management: ps, lsof, kill, pkill (dev processes only)
# Network: curl
# Node.js: npm, npx, pnpm, node
# Docker: docker
# chmod: Only +x mode (making scripts executable)
#
# Hardcoded Blocklist (NEVER allowed):
# Disk operations: dd, mkfs, fdisk, parted
# System control: shutdown, reboot, poweroff, halt, init
# Privilege escalation: sudo, su, doas
# System services: systemctl, service, launchctl
# Network security: iptables, ufw
# Ownership changes: chown, chgrp
# Dangerous commands: aws, gcloud, az, kubectl (unless org allows)
#
# To learn more, see: examples/README.md