T01 · Skill Instruction Hijacking
- Location
scripts/trends/store.py:105- Finding
Untrusted Trend Content Is Injected into the System Prompt Without Instruction Isolation
- Content
View full analysis
Vulnerability Details
File Location:
scripts/pipeline/runner.py:220-228,scripts/trends/store.py:105-139,164-166,203-225,scripts/pipeline/orchestrator.py:142-149,scripts/pipeline/injector.py:129-137
Vulnerability Type: Indirect prompt injection through externally sourced trend content
Risk Level: MediumComplete Code Snippets
python # scripts/pipeline/runner.py:220-232 @register("trends.ingest") def _task_trends_ingest(payload: Dict[str, Any], ctx: Dict[str, Any]) -> Dict[str, Any]: """把抓回来的原文离线提炼成要点入库(原文不进上下文)""" from scripts.trends.sources import SourceRouter from scripts.trends.store import TrendStore source = payload.get("source", "") result = TrendStore(ctx["data_dir"]).ingest( payload.get("raw", ""), payload.get("topic", ""), source) # 抓到了算成功,一条都提炼不出来算这个源挂了(影响下次排程) if source: SourceRouter(ctx["data_dir"]).mark(source, bool(result.get("added"))) return resultpython # scripts/trends/store.py:105-142 def digest(raw: str, topic: str = "", max_points: int = 4, point_len: int = 22) -> List[str]: """把抓取回来的原始文本提炼成数条短要点 打分:含主题词 +2;含热度词 +1;长度适中 +1;与已选要点重复的直接丢弃。 """ text = strip_html(raw) if not text: return [] candidates = [s.strip() for s in re.split(r"[。!?!?\n;;]", text) if 8 <= len(s.strip()) <= 80] scored: List[tuple] = [] for s in candidates: score = 0.0 if topic and topic in s: score += 2 score += sum(1 for w in HEAT_WORDS if w in s) if 12 <= len(s) <= 45: score += 1 # 纯链接/纯数字/广告导航直接判负 if re.match(r"^https?://", s) or s.count("/") > 3: score -= 2 if any(w in s for w in NOISE_WORDS): score -= 3 scored.append((score, s)) scored.sort(key=lambda kv: -kv[0]) points: List[str] = [] for _score, s in scored: if s and len(s) > point_len: s = s[:point_len].rstrip(",,、 ") if not s: ...[truncated 5407 chars]- Remediation
View remediation
Remediation Suggestions
- Do not concatenate externally sourced trend text into the System Prompt. Pass it through a lower-trust data or user-content channel supported by the hosting framework.
- Wrap every trend item in an explicit structured representation containing fields such as
source,topic, andquoted_content. - Add a non-user-controlled instruction outside the quoted content stating that trend entries are untrusted reference data and that instructions contained within them must never be followed.
- Preserve provenance through ingestion and rendering so the model-facing layer can enforce trust-aware handling.
- Reject imperative or model-directed phrases during ingestion, including attempts to reference system messages, override instructions, request secrets, invoke tools, or redirect the conversation.
- Use strict allowlisted schemas for trend points instead of accepting arbitrary natural-language sentences as privileged context.
- Add regression tests with indirect prompt-injection payloads embedded in HTML, titles, trend sentences, and repeated high-scoring topic text.
- Consider requiring explicit user approval before newly ingested trend content is made available to the prompt assembly layer.
