T09 · Insecure Skill Coding Practices
- Location
scripts/compress_content.py:17- Finding
Indirect Prompt Injection Through Untrusted Scraped Content
- Content
View full analysis
Vulnerability Details
File Location:
scripts/compress_content.py:17-35
Related Locations:SKILL.md:101-103,SKILL.md:115-117,references/prompts.md:5,references/dynamic_sites_whitelist.json:20-25
Vulnerability Type: Untrusted scraped content is passed to an LLM without prompt-injection defenses
Risk Level: MediumVulnerable Code and Configuration
scripts/compress_content.py:17-35:python def compress_html(html_content: str, max_length: int = 3000) -> str: """Clean HTML content, extract plain text, and limit length.""" # Remove script and style tags along with their contents content = re.sub(r'<script[^>]*>.*?</script>', ' ', html_content, flags=re.DOTALL | re.IGNORECASE) content = re.sub(r'<style[^>]*>.*?</style>', ' ', content, flags=re.DOTALL | re.IGNORECASE) # Remove all HTML tags content = re.sub(r'<[^>]+>', ' ', content) # Replace common HTML entities entities = { ' ': ' ', '<': '<', '>': '>', '&': '&', '"': '"', ''': "'", ''': "'" } for entity, char in entities.items(): content = content.replace(entity, char) # Compress whitespace (multiple spaces, newlines, tabs to single space) content = re.sub(r'\s+', ' ', content).strip()SKILL.md:101-103:markdown 4. **Content Cleaning**: Before feeding scraped text to LLM, invoke the cleaning script: ```bash python scripts/compress_content.py --max-length 3000 < raw_text.txt > cleaned_text.txttext `SKILL.md:115-117`: ```markdown 1. **Context Preparation**: Use the cleaned text from Step 0 as core context. If user provided private PDFs/patents, also process via `scripts/compress_content.py` and merge. 2. The Skill invokes internal LLM, strictly following the **Step 1 Prompt** template in `references/prompts.md`.references/prompts.md:5:markdown **Input**: Cleaned text from initial search results.`references/dynamic_s ...[truncated 2990 chars]
- Remediation
View remediation
Remediation Suggestions
-
Establish an explicit trust boundary
- Place retrieved material inside uniquely delimited blocks.
- State in the highest-priority prompt that text within those blocks is untrusted evidence, not an instruction source.
- Prohibit following commands, role changes, tool requests, or output-format changes found inside source material.
-
Separate instructions from evidence structurally
- Prefer structured model input fields for trusted task instructions and untrusted source documents.
- Preserve each source's URL and identity rather than merging multiple documents into an undifferentiated text block.
-
Detect and quarantine instruction-like content
- Flag phrases that attempt to override prior instructions, impersonate system messages, request tool use, or direct the model to conceal information.
- Treat detection as a warning rather than relying on pattern matching as the sole defense.
- Exclude or separately quote suspicious passages while retaining them in the evidence package for review.
-
Validate generated output
- Enforce the required report schema after generation.
- Verify that factual claims map to supplied source identifiers.
- Reject unsupported citations, missing attribution labels, and unexpected instructions or tool requests.
- Require human confirmation before consequential follow-up actions based on untrusted content.
-
Restrict dynamic-site matching
- Replace broad patterns such as
docs.*.comwith exact trusted hosts or a reviewed allowlist. - Match normalized hostnames at registrable-domain boundaries.
- Reject deceptive suffixes, embedded credentials, unexpected redirects, and attacker-controlled subdomains unless explicitly approved.
- Replace broad patterns such as
-
Apply defense in depth
- Keep scraper and analysis tools least-privileged.
- Do not expose credentials or unrestricted execution tools to a model processing untrusted webpage content.
- Record source ...[truncated 63 chars]
-
