T09 · Insecure Skill Coding Practices
Warning
- Location
- smart-keepalive.py:2188
- Finding
- Untrusted feed content is inserted directly into an Agent prompt<![CDATA[ ## Vulnerability Details **File Location**: `smart-keepalive.py:2188-2219` **Related Source Ingestion**: `smart-keepalive.py:526-558` **Vulnerability Type**: Indirect prompt injection through externally controlled feed content **Risk Level**: Medium ### Vulnerable Code ```python def generate_message( openclaw_bin: str, locale: str, skill_dir: Path, agent_id: str ) -> tuple[str, str]: brief, theme_tag = build_keepalive_brief(with_links=True, skill_dir=skill_dir) weather_city = resolve_weather_city(skill_dir) if weather_append_enabled(): weather_line = fetch_nmc_weather_daily(weather_city, 1, with_links=True) if weather_line.strip(): brief = f"{brief}\n{weather_line}" now = datetime.now() style = os.getenv("KEEPALIVE_STYLE_GUIDE", "").strip() or "(无)" tpl = load_prompt_file(skill_dir, "rewrite-main.md") if not tpl.strip(): tpl = FALLBACK_REWRITE_PROMPT prompt = fill_prompt_template( tpl, { "LOCALE": locale, "BRIEF": brief, "HOUR": str(now.hour), "MINUTE": str(now.minute), "LOCAL_TIME": now.strftime("%Y-%m-%d %H:%M:%S"), "STYLE_GUIDE": style, "THEME_HINT": theme_section_label(locale, theme_tag), "WEATHER_CITY": weather_city, }, ) code, out = run_agent_command( openclaw_bin=openclaw_bin, agent_id=agent_id, prompt=prompt, timeout_sec=45, ) ``` The content placed in `brief` originates from external RSS and HTTP responses: ```python req = urllib.request.Request( safe_url, headers={ "User-Agent": ( "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " "AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15" ), }, ) with urllib.request.urlopen(req, timeout=12) as resp: data = resp.read() root = ET.fromstring(data) items = root.findall(".//item") if not i ...[truncated 2556 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Represent source records as structured objects containing separate `title`, `url`, and `source` fields instead of concatenating them into free-form prompt text. 2. Serialize those records as JSON inside a clearly delimited data block. 3. Add an explicit high-priority instruction stating that all text inside the source-data block is untrusted content and that directives found there must never be followed. 4. Validate Agent output against a strict schema before sending it: - Require the fixed report heading. - Limit the number and length of entries. - Reject unexpected sections or instructions. - Ensure each visible title exactly matches a parsed source title. - Ensure every URL exactly matches a URL from the corresponding parsed record. 5. Build the final Markdown links in Python from validated source records rather than allowing the Agent to create arbitrary links. 6. Remove control characters and impose conservative byte and character limits on titles and URLs. 7. Run the rewrite Agent without external tools, sensitive session context, or filesystem/network permissions where the runtime supports such isolation. 8. If validation fails, use the deterministic Python formatter instead of transmitting the unvalidated Agent response. ]]>
