Security review: Remote sub-tasks hear remote state changes between turns
The
security-reviewnode's proof. See the-loop'sreference/security.md.
Security review (gate)
Mechanism: the security-review skill (Claude Code's
/security-review). A fresh-context sub-agent reviewed the code diff against the threat model inrequirements.md§ Security considerations anddesign.md§ Security design. The review ran after both self-review rounds and alongside the critic round.Outcome: pass. The skill found no vulnerabilities at its reporting bar (confidence 8 or more out of 10).
- The critic round raised one security finding, critic #1: a push token could reach exported traces through the SDK's span recording of a registration error. It is fixed with
no_exportandExportGuardand tested. - Self-review rounds 1 and 2 raised no security findings beyond critic #1's class. Round 1's #8 (refusal timing) is a won't-fix with its reason in requirements.md abuse case 2.
- The critic round raised one security finding, critic #1: a push token could reach exported traces through the SDK's span recording of a registration error. It is fixed with
Findings:
Source Finding Disposition security-review skill none — critic round 1, #1 (high) A push token in a remote's error text could be exported in an SDK client span (abuse case 7) fixed: spans started during registration are never exported. Test: test_a_failed_registration_exports_no_span_and_logs_no_tokenself-review round 1, #8 (low) Refusal latency differs between an unknown id and a wrong token won't-fix: status and body are identical. Task ids are not secret, and the 256-bit token is what protects a stand-in. Recorded in abuse case 2. implementation (found by T8) The SDK's DEBUG Request bodylog on a receiving server holds registered push tokensfixed: DropRequestBodiesfilter. Tests:test_unverifiable_pushes_…,test_the_sdk_request_body_debug_record_is_droppedChecklist cross-check (
reference/security.md):# Item Result 1 Trust boundaries enforced as designed pass: limits middleware, MAX_PUSH_BYTES, the token before Temporal, the validator (digest, id, mode, closed), andGetTaskas the only state source2 Untrusted inputs validated at ingress pass: size, header, and protobuf parse into StreamResponse; the path id only selects a workflow handle3 Untrusted content cannot steer privileged behaviour pass: the push body is never applied; remote text reaches the parent's model only as a sub-task note 4 No secrets in code, logs or fixtures pass: the token is generated and used inside one activity; history holds only its SHA-256 digest; the log and trace paths are covered above 5 AuthZ fails closed pass: every verification failure is 401and changes nothing; a failed registration falls back to follow6 Least privilege pass: outbound calls go only to registered agents, with the harness's own participant id 7 Every abuse case has a passing negative test pass: tests/security/test_remote_push.pyplus the unit tests named intesting-plan.mdT88 New dependencies justified n/a: none added Risk tier: 4.
- The change adds a new authenticated inbound network surface.
- It touches a sensitive path:
tests/contract/snapshots/settings.schema.jsonmatches**/*schema*.
Human sign-off: required at tier 4. It is requested on PR #28 together with the reviewer briefing, and is pending.