Skip to content

security: fix SSRF on WebChannel, broken API key, add prompt-injection guardrails - #456

Closed
chrismaddern wants to merge 2 commits into
Panniantong:mainfrom
chrismaddern:security/prompt-injection-ssrf-fixes
Closed

security: fix SSRF on WebChannel, broken API key, add prompt-injection guardrails#456
chrismaddern wants to merge 2 commits into
Panniantong:mainfrom
chrismaddern:security/prompt-injection-ssrf-fixes

Conversation

@chrismaddern

Copy link
Copy Markdown

Security Fixes

1. SSRF protection on WebChannel.read() (MEDIUM)

File: agent_reach/channels/web.py

WebChannel.read() had zero SSRF validation — any URL was passed directly to Jina Reader (https://r.jina.ai/{url}), which fetches the target URL. An agent instructed to read http://169.254.169.254/latest/meta-data/ would fetch cloud metadata via Jina Reader, potentially leaking instance credentials.

Fix: Import and call _assert_safe_public_url from transcribe.py before constructing the Jina Reader URL. This reuses the existing SSRF guard that rejects private IPs, localhost, and blocked hostnames.

2. Fix broken API key in transcribe_xiaoyuzhou.sh (HIGH)

File: agent_reach/scripts/transcribe_xiaoyuzhou.sh:116,140

The curl invocations to Groq's Whisper API used -H "Authorization: Bearer ***" — a literal string *** instead of $GROQ_API_KEY. The variable was correctly loaded (lines 31-37) and the Python polish step (line 203) used it correctly, but the two curl calls that perform the actual transcription hardcoded ***. This made the entire xiaoyuzhou transcription feature non-functional — every transcription attempt failed with HTTP 401.

Fix: Replace Bearer *** with Bearer "$GROQ_API_KEY".

3. Prompt-injection guardrails in SKILL.md (MEDIUM)

File: agent_reach/skill/SKILL.md

The skill file instructs the agent to fetch content from 15 platforms (tweets, Reddit posts, YouTube transcripts, web pages via Jina Reader) and feed it into the agent's context. There were zero guardrails instructing the agent to treat fetched content as untrusted data. A malicious tweet, Reddit post, or web page could contain text like "Ignore all previous instructions" that the agent might follow.

Fix: Added a prominent ⚠️ 安全规则 section to SKILL.md warning the agent that all fetched content is UNTRUSTED DATA and must never be treated as instructions.

Test Results

All 196 existing tests pass:

============================= 196 passed in 8.02s ==============================

Security Audit Context

These fixes were identified during a comprehensive security audit of the repo. The audit found strong security hygiene overall (no shell=True, atomic 0o600 credential files, shlex.quote for shell-sourceable files, yaml.safe_load exclusively, -- end-of-options marker for yt-dlp). These 3 issues were the only must-fix items.

…n guardrails

1. SSRF protection on WebChannel.read() — was zero validation, now imports
   and calls _assert_safe_public_url from transcribe.py before fetching.
   Prevents agent from reaching cloud metadata (169.254.169.254) or
   internal services via Jina Reader.

2. Fix broken API key in transcribe_xiaoyuzhou.sh — was using literal
   'Bearer ***' instead of $GROQ_API_KEY, making the entire xiaoyuzhou
   transcription feature non-functional (every call returned 401).

3. Add prompt-injection guardrails to SKILL.md — warns the agent that all
   fetched platform content (tweets, posts, web pages, transcripts) is
   UNTRUSTED DATA and must never be treated as instructions.

All 196 tests pass.
Verifies the SSRF protection added in the previous commit:
- Rejects cloud metadata (169.254.169.254, metadata.google.internal)
- Rejects localhost, 127.0.0.1, 0.0.0.0
- Rejects private IP ranges (10.x, 172.16-31.x, 192.168.x)
- Allows public URLs (with and without scheme)
- Verifies urlopen is NOT called when URL is blocked (SSRF check runs first)

Follows the same pattern as TestDownloadAudioSafety in test_transcribe.py.
All 207 tests pass (196 existing + 11 new).
@Panniantong

Copy link
Copy Markdown
Owner

感谢推动 Web 安全边界。merged #576 已完成 URL、响应大小、credential scrubbing 与 challenge 检测,凭据部分由 #530#577 覆盖;本旧大包不再合并。

@Panniantong Panniantong closed this Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants