T09 · Insecure Skill Coding Practices
Warning
- Location
- examples/inbox-triage.lobster:6
- Finding
- Untrusted Email Content Can Influence External Routing Actions Through Indirect Prompt Injection## Vulnerability Details **File Location**: `examples/inbox-triage.lobster`, lines 6-32 **Vulnerability Type**: Indirect prompt injection and insufficient validation of security-sensitive LLM output **Risk Level**: Medium ### Vulnerable Code ```yaml - id: fetch command: gog.gmail.search --query 'newer_than:1d' --max 20 - id: classify command: >- openclaw.invoke --tool llm-task --action json --args-json '{"prompt":"Classify each inbox item as business, personal, or later. Return one JSON object per item with route and summary.","thinking":"low","schema":{"type":"object","properties":{"items":{"type":"array"}},"required":["items"],"additionalProperties":false}}' stdin: $fetch.stdout - id: post_business command: slack-route --bucket business stdin: $classify.stdout condition: $classify.json.items[0].route == "business" - id: wait_for_business_reply command: echo '{"status":"waiting","reason":"slack_reply"}' condition: $classify.json.items[0].route == "business" - id: notify_personal command: >- openclaw.invoke --tool message --action send --args-json '{"provider":"telegram","to":"owner-thread","content":"Personal inbox item needs attention."}' condition: $classify.json.items[0].route == "personal" - id: stash_for_eod command: summary-append --bucket eod stdin: $classify.stdout condition: $classify.json.items[0].route == "later" ``` ### Technical Analysis The workflow retrieves externally controlled email content and passes the complete result directly to an LLM through `stdin`. Email senders can therefore place prompt-like instructions in message subjects or bodies that attempt to override the classification request. The declared response schema only requires `items` to be an array. It does not define the structure of each array element or constrain `route` to the permitted values `business`, `personal`, and `later`. The workflow then trusts `$classif ...[truncated 1923 chars]
- Remediation
- ## Remediation Suggestions - Treat all fetched email fields as untrusted data and clearly delimit them from system instructions supplied to the model. - Define a strict item schema with required properties, bounded string lengths, and an enum such as `["business", "personal", "later"]`. - Reject malformed output, unknown routes, empty arrays, and responses containing unexpected properties before evaluating conditions. - Associate each classification result with an immutable message identifier and process each item independently rather than relying only on `items[0]`. - Forward only the minimum fields required for the selected message. Do not pass the complete classifier response to external routing commands. - Require explicit owner approval before transmitting mailbox-derived content to Slack or another external destination. - Apply destination allowlists and least-privilege credentials to all messaging integrations. - Add tests containing adversarial email text to verify that embedded instructions cannot alter workflow policy.
