T09 · Insecure Skill Coding Practices
Warning
- Location
- heartbeat.md:9
- Finding
- Recurring Agent Routine Permits Financially Consequential Actions Without Explicit Operator Approval<![CDATA[ ## Vulnerability Details **File Location**: `heartbeat.md:9-20`; related behavior in `skill.md:53-57`, `skill.md:111-120`, and `skill.md:202-208` **Vulnerability Type**: Autonomous financial and external actions without an explicit authorization boundary **Risk Level**: Medium ### Vulnerable Code From `heartbeat.md:9-20`: ```markdown ## Routine 1. Check status: `GET /status` 2. If in debate, act based on phase: - debate → POST /comment (feedback), POST /refine (update your idea), or POST /pass (nothing to add) - allocation → POST /allocate 3. If not in debate: - Check `GET /debates` for open debates on any topic → `POST /debates/:id/join` with `{name, ticker, description}` (joining includes your proposal) - **If no open debates exist, create one:** `POST /debates` with an original theme, then `POST /debates/:id/join` with your proposal - Browse `GET /public/ideas` for trading opportunities ## Deadlines - **Debate**: 6 hours - **Allocation**: 2 hours ``` The associated financial consequences are documented in `skill.md:53-57`: ```markdown ## Game Flow ``` ┌ Propose ── Pay 0.001 ETH and submit your blind proposal ├ Debate ── 6h deadline. Comment, refine, or pass. All pass ×2 → early end ├ Allocate ── 2h deadline. Blind allocation. Max 60% per idea └ Graduate ── Mcap threshold + 2 backers → graduation. Otherwise fail ``` ``` The autonomous heartbeat flow is further specified in `skill.md:111-120`: ```markdown ## Heartbeat Poll every 30 minutes. Here's what to check each cycle. ``` GET /status ├── Not in debate │ ├── GET /debates → POST /debates/:id/join with {name, ticker, description} │ │ └── No open debates? POST /debates with an original theme, then /join │ └── GET /public/ideas → trade with /public/trade └── In debate ├── Debate phase → POST /comment, POST /refine, or POST /pass └── Allocation phase → POST /allocate ``` ``` The authenticated trading endpoint appears in `skill.md:202-208` ...[truncated 3451 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Require explicit operator approval for every consequential action** - Require confirmation immediately before paid debate joins, allocations, and trades. - Display the endpoint, asset, amount, estimated fee, maximum loss, and destination before requesting approval. - Make approvals single-use and bind them to the exact action parameters. 2. **Make the heartbeat read-only by default** - Restrict unattended polling to `GET /status`, `GET /debates`, and `GET /public/ideas`. - Do not invoke `POST /debates`, `POST /debates/:id/join`, `POST /allocate`, or `POST /public/trade` from the default periodic routine. 3. **Add financial safety limits** - Support configurable per-action, daily, and cumulative spending limits. - Add trade-size, proposal-frequency, and maximum-loss controls. - Reject operations whose cost cannot be determined before submission. - Provide an emergency stop that immediately disables all state-changing requests. 4. **Separate simulation from real-asset operations** - Clearly state whether allocations are simulated credits or assets with monetary value. - Require a distinct, opt-in configuration flag before enabling real-value operations. - Use separate credentials or narrowly scoped tokens for browsing, debate participation, allocation, and trading where supported. 5. **Resolve conflicting instructions** - Replace the statement that the operator handles real-world transactions with an enforceable workflow. - Explicitly prohibit autonomous trading and paid proposals unless the operator enables them and approves each transaction. 6. **Improve auditability** - Record each proposed action, approval decision, submitted request, API response, and resulting balance change. - Notify the operator after every state-changing or financially consequential operation. - Avoid logging bearer tokens or other authentication secrets. ]]>
