security(daemon): trust gate + worker isolation + DES-028 pipeline design#161
security(daemon): trust gate + worker isolation + DES-028 pipeline design#161claude-puntlabs merged 2 commits intomainfrom
Conversation
…line design 9 security findings from djb review, all verified CLOSED: Trust gate (CRITICAL/HIGH): - Require PGP verification for x-bit execution. Proton E2E headers are SMTP-injectable — no longer sufficient for execute decisions. - Bind PGP KeyID to contact.GPGKeyID. Prevents impersonation via wrong key. HasSuffix match for long/short key ID compatibility. Worker isolation (MEDIUM): - HOME set to ephemeral temp dir via os.MkdirTemp. No access to ~/.ssh, ~/.gnupg, ~/.punt-labs. Cleaned up on worker exit. - PATH restricted to claude binary dir + /usr/bin + /usr/local/bin. - Concurrency semaphore: max 2 workers, non-blocking acquire. Contract hardening (HIGH/LOW): - Adversarial robustness instructions in system prompt. - Email subject removed from success_criteria — fixed text only. Subject in inputs.trigger (audit metadata, not instructions). - escapeYAMLValue strips NUL bytes, caps at 500 chars. - Mission ID validated at Create, not just spawn. - All temp dirs 0700 (owner-only). Design (DES-028 pipeline orchestrator): - Missions as typed commands with GPG-signed YAML definitions. - Typed args (no string interpolation) prevent shell injection. - Planner interface (LLM + Rule implementations). - Persisted pipeline state with crash recovery. - try/else error handling with fixed-text replies. Refs beadle-88g
There was a problem hiding this comment.
Pull request overview
This PR hardens beadle-daemon against spoofed/hostile email triggers by requiring PGP verification for x-bit execution, isolating worker subprocess environments, and documenting the DES-028 pipeline/orchestrator design.
Changes:
- Enforce a PGP-based trust gate for x-bit execution (Proton trust headers alone are rejected) and bind signing KeyID to the configured contact
gpg_key_id. - Isolate worker subprocess execution (ephemeral
HOME, restrictedPATH, isolatedTMPDIR) and introduce a concurrency limit for worker spawns. - Harden mission contracts/prompts (subject moved to
inputs.trigger, adversarial instructions added, temp dirs 0700, NUL stripping + length cap) and expand tests + design docs.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/testserver/imap.go | Adds raw RFC822 seeding helper to support signature/trust-header test cases. |
| internal/testserver/fixture.go | Exposes AddRawMessage via fixture API for integration tests. |
| internal/daemon/templates_test.go | Asserts new adversarial-robustness system-prompt content. |
| internal/daemon/templates.go | Tightens temp dir perms (0700) and adds SECURITY section to system prompt. |
| internal/daemon/spawner_test.go | Updates mission ID validation test to use exported helper. |
| internal/daemon/spawner.go | Adds ValidMissionID helper; isolates worker env (HOME/PATH/TMPDIR) and resolves claude via LookPath. |
| internal/daemon/mission_test.go | Updates contract expectations (trigger metadata) and tests new escaping behaviors. |
| internal/daemon/mission.go | Moves subject out of success_criteria, adds inputs.trigger, adds NUL stripping/length cap, validates mission IDs. |
| internal/daemon/handler_test.go | Adds PGP verification/key-binding tests using ephemeral GPG keys; adds Proton-header-only rejection test. |
| internal/daemon/handler.go | Enforces PGP verification gate before mission creation; adds worker concurrency semaphore. |
| docs/orchestrator-design.md | Updates design to include transport trust gate and DES-028 typed-command pipeline architecture. |
| cmd/beadle-daemon/main.go | Updates handler construction to pass max-worker setting (defaulted). |
| DESIGN.md | Adds DES-028 design summary. |
| CHANGELOG.md | Documents new security hardening and DES-028 design addition. |
| .beads/issues.jsonl | Updates beadle-88g status/priority metadata. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…-aware truncation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit fa30f41. Configure here.
| }() | ||
| default: | ||
| h.logger.Warn("worker capacity full, skipping", "from", addr, "id", msg.ID) | ||
| } |
There was a problem hiding this comment.
Non-blocking semaphore permanently drops authorized messages
High Severity
When workerSem is full, the default branch silently drops the message without creating a mission. These messages are never retried because the poller only calls OnNewMail when the unseen count increases beyond the previously recorded lastSeen (which is updated unconditionally every poll cycle). A skipped message stays unread but the poller won't re-trigger for it, and even if new mail arrives, ListMessages fetches only the last newCount unread UIDs — the older skipped message may never appear in the fetch window. The previous code always created missions for all authorized messages; moving mission creation inside the semaphore gate turns a concurrency limit into permanent data loss.
Reviewed by Cursor Bugbot for commit fa30f41. Configure here.


Summary
9 security findings from dedicated djb review, all verified CLOSED:
Trust gate (Critical/High):
Worker isolation (Medium):
Contract hardening (High/Low):
Design (DES-028):
Test plan
make checkpassesNote
High Risk
High risk because it changes the security boundary for autonomous mission execution (trust verification + key binding) and modifies worker spawning isolation and concurrency limits, which can affect whether missions run and how they access the host environment.
Overview
Tightens daemon execution security for x-bit email triggers. The daemon now rejects mission creation/execution unless the triggering message is PGP-signed and
pgp.Verifysucceeds, and (when configured) the signingKeyIDmatches the contact’s registeredGPGKeyID.Hardens worker spawning. Worker subprocesses run with an isolated temp
HOME/TMPDIR, a restrictedPATH, explicitclaudebinary resolution, mission ID validation moved earlier, and a default concurrency semaphore (max 2 workers, configurable) to cap parallel execution.Reduces prompt/contract injection surface. Mission contracts record email provenance under
inputs.trigger(notsuccess_criteria), remove attacker-controlled subject text from criteria, cap/clean YAML-escaped values, and tighten temp dir permissions; the worker system prompt adds explicit adversarial-content instructions.Docs/tests updated. Adds DES-028 pipeline orchestrator design writeup, expands daemon tests with real ephemeral GPG-signed RFC822 fixtures (including key match/mismatch), and extends the IMAP test server to inject raw RFC822 messages.
Reviewed by Cursor Bugbot for commit fa30f41. Bugbot is set up for automated code reviews on this repo. Configure here.