Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,4 @@ docs/让*
tests/run_*.sh
tests/launch_*.py
*.launch.log
uv.lock
48 changes: 48 additions & 0 deletions docs/superpowers/SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Security Considerations for Superpowers Adapter

## Execution Model

The Superpowers adapter runs Claude Code with candidate skills that control agent behavior. A malicious skill can:

- Execute arbitrary shell commands
- Read/write files in the project directory
- Access environment variables (including API keys)
- Make network requests

## Current Mitigations

1. **Scrubbed environment**: Only essential vars passed (HOME, PATH, TERM, LANG, ANTHROPIC_API_KEY)
2. **Isolated HOME**: Each scenario gets its own HOME directory
3. **No `--dangerously-skip-permissions` by default**: Permission prompts required unless explicitly bypassed
4. **Project directory isolation**: Each scenario gets its own project directory
Comment on lines +14 to +17

## Known Limitations

- **API key exposure**: ANTHROPIC_API_KEY is passed to the subprocess
- **No OS-level isolation**: Without Docker/bubblewrap, candidate code runs with user privileges
- **SKILLOPT_UNSAFE bypass**: When enabled, full filesystem access

## Recommendations

### For Local Testing (trusted candidates)
```bash
SKILLOPT_UNSAFE=1 python -m skillopt_sleep.adapters.superpowers --candidate my_skill.md
```

### For Untrusted Candidates (future work)

Docker isolation (not yet implemented):
```bash
# Build sandbox image
docker build -t skillopt-sandbox .

# Run with isolation
python -m skillopt_sleep.adapters.superpowers --candidate untrusted.md --sandbox docker
```

## Follow-up Work

- [ ] Docker sandbox implementation
- [ ] Bubblewrap (bwrap) support for Linux
- [ ] Network isolation option
- [ ] API key injection via Docker secrets
71 changes: 71 additions & 0 deletions scripts/smoke_superpowers.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/bin/bash
# Smoke test for Superpowers adapter integration.
# Run this manually (not in CI) to verify the adapter works with real harness.
#
# Prerequisites:
# - Harness installed and authenticated
# - Same model/settings for baseline and candidate runs
#
# Usage:
# SKILLOPT_UNSAFE=1 ./scripts/smoke_superpowers.sh [candidate_skill_path]
#
# Output: writes results + raw output to smoke_results/ for PR evidence.
# Fails on any runner error (no silent swallowing).
Comment on lines +9 to +13

set -euo pipefail

SKILL="${1:-}"
OUTDIR="smoke_results/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$OUTDIR"

echo "Smoke test: Superpowers adapter"
echo "Output: $OUTDIR"
echo "SKILLOPT_UNSAFE=${SKILLOPT_UNSAFE:-0}"
echo ""

run_scenario() {
local name="$1"
local candidate="${2:-}"
local outfile="$OUTDIR/${name}.json"

echo "=== $name ==="

local args=(
--skill verification-before-completion
--scenario test-passes-verify
--json
)
if [[ -n "$candidate" ]]; then
args+=(--candidate "$candidate")
fi

# No || true - fail if runner errors
python -m skillopt_sleep.adapters.superpowers "${args[@]}" > "$outfile"

# Extract and preserve raw output
python -c "
import json, sys
data = json.load(open('$outfile'))
for s in data.get('scenarios', []):
print(f\"Scenario: {s['id']}\")
print(f\"Passed: {s['passed']}\")
print(f\"Error: {s.get('error', 'none')}\")
# Raw output preserved in JSON, print preview
out = s.get('output', '')
if out:
print(f\"Output preview ({len(out)} chars):\")
print(out[:500])
print()
"
}

# Baseline run (stock skill)
run_scenario "baseline"

# Candidate run if provided
if [[ -n "$SKILL" ]]; then
run_scenario "candidate" "$SKILL"
fi

echo "Results saved to $OUTDIR"
echo "Include these files in your PR as evidence of smoke test."
Comment on lines +70 to +71
1 change: 1 addition & 0 deletions skillopt_sleep/adapters/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""SkillOpt adapters for external skill frameworks."""
Loading