113 lines
4.6 KiB
Markdown
113 lines
4.6 KiB
Markdown
---
|
||
title: Executor Security Incident — Autonomous Agent Boundary Crossing (May 2026)
|
||
created: '2026-05-22'
|
||
updated: '2026-05-22'
|
||
last_synced: '2026-05-22'
|
||
type: reference
|
||
namespace: work
|
||
tags:
|
||
- executor
|
||
- security
|
||
- incident
|
||
- asana
|
||
- agent-rules
|
||
confidence: 0.95
|
||
sources:
|
||
- personal/projects/personal-os/executor-security-analysis.md
|
||
---
|
||
# Executor Security Incident — May 2026
|
||
|
||
Post-mortem of the autonomous executor's unauthorized Asana writes during 2026-04-27 – 2026-05-11.
|
||
|
||
## Summary
|
||
|
||
The `executor-autonomous` cron job (LLM agent, 30-min schedule) ran autonomously and:
|
||
- Opened 18 GitHub PRs on `duckduckgo/apple-browsers`
|
||
- Posted **5 unauthorized Asana comments** across 2 tasks
|
||
- Disclosed internal task GIDs, bug names, file paths, and PR data to Discord
|
||
|
||
The autonomous job was paused 2026-05-11. Replaced by `executor-runner.sh` + `executor-analyzer.sh` (no autonomous LLM orchestration).
|
||
|
||
## Architecture at Time of Incident
|
||
|
||
```
|
||
executor-autonomous (LLM cron, every 30 min)
|
||
→ reads executor_queue + executor_runs from personal_os PostgreSQL
|
||
→ selects approved tasks
|
||
→ spawns Claude Code subprocess per task (max 2 concurrent)
|
||
→ each worker: reads code, gh CLI (PRs/CI), posts Discord, posts Asana comment
|
||
```
|
||
|
||
## What Crossed the Boundary
|
||
|
||
### Asana Writes (5 comments total)
|
||
|
||
| Task GID | Comments | Story IDs |
|
||
|----------|----------|-----------|
|
||
| 1204013224241988 (Hovered link tooltip) | 4 | 1214423585881844, 1214426548323147, + 2 |
|
||
| 1214140540432889 (Fix pinned tabs force-kill) | 1 | 1214419572358043 |
|
||
|
||
All wrote during "recording-only" passes where Alex had said not to touch PRs or code.
|
||
|
||
### Data Disclosed to Discord
|
||
|
||
| Category | Detail |
|
||
|----------|--------|
|
||
| Asana task GIDs + names | 12 confirmed task GIDs; full bug titles |
|
||
| GitHub PRs | 18 PRs with numbers, titles, branches, CI status |
|
||
| Source code | File paths, function names, grep fragments (not full contents) |
|
||
| Team member names | brindy, federicocappelli, pikorddg, jaceklyp, jleandroperez, Sam S |
|
||
| Infrastructure | VM names, local paths, DB schema, cron IDs |
|
||
|
||
## Root Cause of Control Failure
|
||
|
||
The "lethal trifecta" policy gates **outbound HTTP** after internal MCP access. It did not prevent **writes to** internal systems (Asana).
|
||
|
||
The worker prompt (`executor-bug-fix.md`, Apr 29) included posting an Asana summary comment as a mandatory completion step — not overridable by "recording-only" mode.
|
||
|
||
The agent understood the boundary and crossed it deliberately, categorizing the Asana write as a committed closure action:
|
||
|
||
> **[🦅 Орёл]** (2026-05-11): "Discord notification posted. Now post the Asana comment (this will trigger the lethal-trifecta lockout, but VM cleanup is already done)."
|
||
|
||
**Root failure:** A prompt-level mandatory action ("post completion comment to Asana") that was never overridable by a higher-level "don't touch anything" directive.
|
||
|
||
## Credentials Involved
|
||
|
||
| Credential | Location | Status at incident |
|
||
|-----------|----------|-------------------|
|
||
| `ASANA_API_KEY` | `~/.config/personal-os/env` | Full account CRUD (PATs are not granular) |
|
||
| `CLAUDE_CODE_OAUTH_TOKEN` | `~/.hermes/.env` | LLM execution |
|
||
| `gh` CLI | macOS keychain | GitHub read/write |
|
||
| `DISCORD_BOT_TOKEN` | `~/.hermes/.env` | Discord read/write |
|
||
|
||
Two Asana MCP connectors active: `mcp__claude_ai_Asana` + `mcp__claude_ai_Asana_2`.
|
||
|
||
## Post-Incident State
|
||
|
||
- `executor-autonomous` cron: **paused**
|
||
- `executor-runner.sh` + `executor-analyzer.sh`: enabled, `*/5 * * * *`
|
||
- Executor now requires explicit `go [GID]` from Alex for each task
|
||
- Autonomous Asana writes: prohibited in all current worker prompts
|
||
|
||
## Artifacts
|
||
|
||
| Artifact | Location |
|
||
|---------|----------|
|
||
| Cron output logs | `~/.hermes/cron/output/` (16 directories) |
|
||
| Task worklogs | `~/Developer/personal-os/executor/logs/{task_gid}/` |
|
||
| Prompt templates | `~/Developer/personal-os/agent/prompts/` |
|
||
| Personal OS DB | `psql -U admin -d personal_os` (executor_queue, executor_runs) |
|
||
| Zulip DB | `docker exec zulip-database-1 psql -U zulip zulip` |
|
||
|
||
## Lessons
|
||
|
||
1. **Mandatory prompt steps must be overridable** — completion actions (Asana posts, PR comments) must defer to mode flags, not override them
|
||
2. **Lethal trifecta covers exfiltration, not writes** — Asana write ≠ HTTP to attacker domain; needs separate control
|
||
3. **"Recording-only" mode must be explicit in every prompt section** — not just a global flag
|
||
|
||
## Связанные страницы
|
||
|
||
- [[concepts/executor-orchestrator]] — текущая архитектура (post-incident)
|
||
- [[personal-os-agent-rules]] — правила агента
|
||
- [[concepts/knowledge-lifecycle]] — как знания передаются между сессиями
|