T02 · Agent Memory Poisoning
- Location
scripts/zenquote.sh:111- Finding
Persistent Agent Prompt and JSON Injection Through Unvalidated Identifiers
- Content
View full analysis
"$cron_config" << EOF { "name": "${job_name}", "enabled": true, "schedule": { "kind": "cron", "expr": "0 9 * * *", "tz": "Asia/Taipei" }, "payload": { "kind": "agentTurn", "message": "Send daily inspirational ${type} from ZenQuotes API to telegram:${chat_id} using the zenquote skill. Use ${message_cmd} command.", "model": "opencode-go/kimi-k2.5" }, "delivery": { "mode": "announce", "channel": "telegram", "to": "${chat_id}", "accountId": "${account_id}" } } EOF echo "{\"setup\": true, \"type\": \"${type}\", \"message\": \"Daily ${type} scheduled for 9:00 AM\", \"chat_id\": \"${chat_id}\"}" } ``` ### Technical Analysis The `chat_id` and `account_id` parameters are accepted without format validation or JSON escaping. They are interpolated directly into a persistent cron configuration using a shell heredoc. The `chat_id` is used in two security-sensitive contexts: 1. Inside the natural-language `agentTurn` message that will be interpreted during future scheduled executions. 2. Inside the JSON `delivery.to` property. The `account_id` is similarly inserted directly into `delivery.account ...[truncated 2177 chars]- Remediation
View remediation
"$cron_config" ``` 4. Do not place user-controlled routing identifiers inside a natural-language agent prompt. Keep routing data exclusively in structured, validated delivery fields. 5. Write the configuration to a temporary file first, validate it with `jq empty`, assign restrictive permissions, and then move it atomically into place. 6. Add tests containing quotes, backslashes, newlines, JSON fragments, and prompt-like text to confirm that input cannot alter the generated JSON or scheduled instructions. ]]>
