Back to skill

Security audit

内容引擎

Security checks for vulnerabilities and agentic risk

Overview

The skill is broadly aligned with its content-generation purpose, but it handles paid API credentials and persistent graph updates too loosely for automatic installation.

Review this skill before installing. Use dedicated low-limit API keys, avoid OPENROUTER_API_KEY fallback unless the endpoint is explicitly correct, do not set custom BASE_URL values except to trusted HTTPS provider hosts, restrict permissions on ~/.config/content-engine/.env, and require manual review of graph write-backs and any --no-confirm video-generation runs.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (7)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/content_engine/llm.py:53
Finding

OpenRouter credentials can be transmitted to the Ofox API

Content
View full analysis
str | None: """env > .env file sources. OPENROUTER_API_KEY is accepted as a fallback.""" for var in ("OFOX_API_KEY", "OPENROUTER_API_KEY"): if v := os.environ.get(var): return v.strip() for path_fn in _TOKEN_SEARCH_PATHS: try: path = path_fn() except Exception: continue if not path.exists() or not path.is_file(): continue try: for line in path.read_text(encoding="utf-8").splitlines(): line = line.strip() for var in ("OFOX_API_KEY", "OPENROUTER_API_KEY"): if line.startswith(f"{var}="): val = line.split("=", 1)[1].strip() return val.strip('"').strip("'") except (OSError, UnicodeDecodeError): continue return None ``` ```python self.api_key = api_key or _load_ofox_key() if not self.api_key: raise OfoxError(_help_message()) self.base_url = ( base_url or os.environ.get("OFOX_BASE_URL") or OFOX_BASE_URL ).rstrip("/") ``` ```python req = urllib.request.Request( url, data=body, headers={ "Content-Type": "application/json; charset=utf-8", "Authorization": f"Bearer {self.api_key}", }, method="POST", ) ``` ### Technical Analysis The credential loader treats `OPENROUTER_API_KEY` as interchangeable with `OFOX_API_KEY`. However, selecting an OpenRouter credential does not select an OpenRouter endpoint. Unless separately overridden, the client continues using the default Ofox origin, `https://api.ofox.ai`. Consequently, a credential issued by OpenRouter is placed in an HTTP `Authorization` header and disclosed to Ofox. Credential compatibility cannot safely b ...[truncated 1125 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/content_engine/client.py:86
Finding

Bearer credentials can be redirected to arbitrary configured origins

Content
View full analysis
Remediation
View remediation

T01 · Skill Instruction Hijacking

Error
Location
scripts/content_engine/prompts.py:100
Finding

Untrusted XHS content can influence prompts and poison persistent graph state

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/extract_xhs.py:215
Finding

API-controlled media URLs can trigger server-side request forgery

Content
View full analysis
max_bytes: f.close() dest.unlink(missing_ok=True) raise DownloadTooLargeError( f"Video exceeded {max_bytes // (1024*1024)} MB limit " f"(got {written // (1024*1024)} MB)" ) f.write(chunk) ``` Image download: ```python with urllib.request.urlopen(url, timeout=timeout) as resp, dest.open("wb") as f: while True: chunk = resp.read(64 * 1024) if not chunk: break written += len(chunk) if written > max_bytes: f.close() dest.unlink(missing_ok=True) raise RuntimeError( f"exceeded {max_bytes // (1024*1024)} MB" ...[truncated 1921 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:535
Finding

Installation instructions store multiple API secrets without enforcing restrictive permissions

Content
View full analysis
> ~/.config/content-engine/.env # v2 generation: Ofox echo 'OFOX_API_KEY=ofox-your-key' >> ~/.config/content-engine/.env # video generation: Ark echo 'ARK_API_KEY=your_ark_key' >> ~/.config/content-engine/.env ``` ### Technical Analysis The documented setup stores three paid-service credentials together in a plaintext `.env` file. Neither the directory nor the file is created with explicit restrictive permissions. Their final permissions therefore depend on the user's current `umask`. On systems with permissive defaults or shared accounts, the file may be readable by other local users or processes. Using `>>` also creates duplicate entries on repeated setup. Since the loaders return the first matching entry, stale or revoked values may unexpectedly remain effective. The file location is legitimate for the declared functionality, but the instructions do not apply minimum protections appropriate for secrets. ### Attack Path 1. The user follows the documented setup commands on a shared or permissively configured system. 2. The `.env` file is created with group-readable or world-readable permissions. 3. Another local user or compromised process reads `~/.config/content-engine/.env`. 4. The attacker obtains TikHub, Ofox, and Ark credentials from one file. 5. The attacker uses those keys within their provider-side permissions and spending limits. ### Impact Assessment The exposed keys may authorize paid API calls and disclose account-associated usage. Compromise of the single file exposes all three services simultaneously. The flaw does not grant privileges beyond the affected user's file access and the permissions assigned to the keys, but it can cause unauthori ...[truncated 26 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
SKILL.md:580
Finding

Optional Feishu installation executes an unpinned package from the npm registry

Content
View full analysis
Remediation
View remediation
install ``` 2. Remove `-y` so users can review the package and version before execution. 3. Where possible, provide an integrity-verified installation mechanism or lockfile. 4. Document the expected npm publisher and package metadata that users should verify. 5. Recommend testing the plugin in a restricted environment before granting access to production credentials or documents. 6. Review and update the pinned version deliberately rather than tracking the mutable latest release. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/content_engine/seedance.py:244
Finding

Seedance video responses are buffered entirely without a size limit

Content
View full analysis
Path: """Download a video file locally.""" out_path = Path(out_path) out_path.parent.mkdir(parents=True, exist_ok=True) try: req = urllib.request.Request( url, headers={"User-Agent": "curl/8.7.1"} ) with urllib.request.urlopen(req, timeout=timeout) as resp: out_path.write_bytes(resp.read()) except urllib.error.HTTPError as e: raise SeedanceError( f"HTTP {e.code} downloading video from {url[:80]}" ) from e except urllib.error.URLError as e: raise SeedanceError( f"Network error downloading video: {e.reason}" ) from e return out_path ``` ### Technical Analysis `resp.read()` reads the complete response into memory before `write_bytes()` writes it to disk. No maximum response size is enforced, and no `Content-Length` or media type validation is performed. This implementation differs from the XHS video downloader, which streams fixed-size chunks and aborts after a configured byte limit. A malformed, unexpectedly large, or endless Seedance/CDN response can consume substantial memory. Disk exhaustion is also possible once the buffered response is written. The configured timeout does not reliably bound response size because a server can continue delivering data before the timeout condition is reached. ### Attack Path 1. Ark returns a malicious, compromised, or erroneous `video_url`. 2. The client opens the URL after task completion. 3. The remote server returns an extremely large response body. 4. `resp.read()` attempts to allocate memory for the entire body. 5. The process is terminated by memory exhaustion or becomes una ...[truncated 413 chars]
Remediation
View remediation
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (107)

Tainted flow: 'req' from os.environ.get (line 69, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
91% confidence
Finding

The request destination is indirectly controllable via environment-derived base_url, and the function sends both the bearer token and full prompt to that endpoint. If an attacker can influence OFOX_BASE_URL or related configuration, this becomes an SSRF/exfiltration path that can leak credentials and sensitive brand/product prompts to an arbitrary server.

Content

Scanner excerpt · scripts/content_engine/nano_banana.py (reported line 80)May include surrounding context.

python
)

    try:
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            data = json.loads(resp.read())
    except urllib.error.HTTPError as e:
        err_body = e.read().decode("utf-8", errors="replace")[:500]

Tainted flow: 'req' from os.environ.get (line 186, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
95% confidence
Finding

The request target is derived from a caller-controlled or environment-controlled base_url (including ARK_BASE_URL), then used directly in urllib.request.urlopen while sending the Authorization bearer token. If an attacker can influence environment variables or pass base_url, they can redirect requests to an attacker-controlled host and exfiltrate the API key and request contents, which is a real SSRF/credential-leak risk.

Content

Scanner excerpt · scripts/content_engine/seedance.py (reported line 156)May include surrounding context.

python
)

    try:
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            data = json.loads(resp.read())
    except urllib.error.HTTPError as e:
        body = e.read().decode("utf-8", errors="replace")[:500]

Tainted flow: 'req' from os.environ.get (line 186, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
95% confidence
Finding

This status-query request also uses a caller/environment-controlled base_url and includes the bearer token in the Authorization header. An attacker who can set ARK_BASE_URL or supply base_url can cause authenticated polling requests to go to an arbitrary server, leaking credentials and task metadata.

Content

Scanner excerpt · scripts/content_engine/seedance.py (reported line 193)May include surrounding context.

python
)

    try:
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            return json.loads(resp.read())
    except urllib.error.HTTPError as e:
        body = e.read().decode("utf-8", errors="replace")[:500]

Tainted flow: 'req' from os.environ.get (line 186, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

download_video fetches an arbitrary URL returned by the upstream service without validating scheme, host, or IP range. If that URL is compromised or attacker-controlled, this can be abused for SSRF-like internal network access or for downloading unexpected content to disk, which is more concerning in an agent context that may run in privileged environments.

Content

Scanner excerpt · scripts/content_engine/seedance.py (reported line 254)May include surrounding context.

python
out_path.parent.mkdir(parents=True, exist_ok=True)
    try:
        req = urllib.request.Request(url, headers={"User-Agent": "curl/8.7.1"})
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            out_path.write_bytes(resp.read())
    except urllib.error.HTTPError as e:
        raise SeedanceError(f"HTTP {e.code} downloading video from {url[:80]}") from e

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared description presents a higher-level product with two modes: structured XHS analysis and AI-assisted content generation with a shared knowledge graph. The supplied code chunk, however, only exposes package metadata/documentation for a lower-level extraction toolkit centered on XHS scraping/parsing, link resolution, media download, ffmpeg frame extraction, and preflight checks. Some of this may support the declared v1 analysis workflow, so it is not wholly unrelated, but the actual behavior shown is materially narrower in some areas and broader in others. In particular, undeclared media download/frame extraction and preflight/token/network validation are present, while the described v2 generation and graph-sharing functionality are absent from the code chunk. Therefore this is a description-behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description presents a higher-level 'content engine' focused on two user-facing modes: structured Xiaohongshu爆款拆解 and downstream content generation using Ofox/Nano Banana, plus shared graph/knowledge features. The supplied code chunk does not implement either analysis-card generation, generation pipelines, or graph logic. Instead, it is a low-level TikHub client for authenticated retrieval of Xiaohongshu note info and comments via external API calls. While fetching note data could be a supporting component for the declared拆解 workflow, this chunk's actual behavior is materially narrower and different from the declared purpose, and it relies on an undeclared paid third-party service (TikHub). Therefore this code chunk does not accurately represent the declared functionality on its own.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The supplied code chunk does not implement or invoke the declared content-engine functionality. Instead, it is a narrow unit test for a utility function that guesses image extensions from URLs. This is materially different from the declared primary purpose of decomposing Xiaohongshu爆款内容, generating branded content assets via LLM/image tools, and maintaining a shared knowledge graph. While such a helper could be a minor supporting detail inside a larger media pipeline, this specific code chunk itself only exercises pure-function test logic and shows none of the declared product behavior, triggers, or resource usage. Therefore the description does not accurately represent what this code chunk actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The supplied code does not implement the declared content-engine functionality. It merely tests a helper function resolve_xhs_link that extracts a Xiaohongshu note ID from various URL/text forms and raises errors when no link is present. While link resolution could be a supporting utility for the declared '拆解' mode, this chunk by itself is far narrower than the declared purpose and shows none of the main behaviors: structured content analysis, generation, graph sharing, or broader platform workflow. Therefore the description does not accurately represent what this code chunk actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

This code chunk does not match the declared end-user functionality. The description presents a feature-rich content engine for Xiaohongshu analysis and generation, but the actual code is only a test suite for helper behavior in an Ofox LLM client module. While Ofox is mentioned in the description, this chunk only verifies API key discovery, client construction, and config paths; these are supporting infrastructure details rather than the declared primary behavior. Therefore, the supplied code chunk materially differs from the declared purpose.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description presents a fairly high-level product capability: an end-to-end Xiaohongshu content engine with analysis, generation, and knowledge-graph features. The actual code chunk does not implement those behaviors. Instead, it is a narrow test module validating low-level parsing utilities for notes and comments. While such parsers could be supporting infrastructure for a larger Xiaohongshu tool, this chunk’s actual behavior is materially different from the declared primary purpose and does not demonstrate the advertised decomposition, generation, graph, or evaluation functionality. There are no suspicious undeclared external accesses or triggers here; the mismatch is mainly that the supplied code is only parser tests, not the described content engine.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 104)May include surrounding context.

md
1. 找到 skill 根目录(`SKILL.md` 所在目录)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
91% confidence
Finding

The --no-confirm parameter suppresses a safety gate before expensive external API operations and media generation. In a tool-using agent, such flags are dangerous because they let natural-language prompts bypass human review for side-effectful actions, potentially causing unapproved spend, uploads, or long-running jobs.

Content

Scanner excerpt · SKILL.md (reported line 440)May include surrounding context.

md
- 顺序提交到火山方舟 Seedance 2.0(异步任务 + 轮询,单 shot 通常 1-3 分钟)
- 失败的 shot 不阻断其他 shot,只在 `partial-video.md` 里写明哪几个失败 + 失败 shot 的 prompt 方便手补
- 成功的 shot 用 ffmpeg concat 拼成 `final-video.mp4`
- 开关:`--no-real-video`(仅出 prompt 不调 API)/ `--async`(仅提交不等结果)/ `--no-confirm`(跳过 3 秒倒数)

### Step 8:质检 validator(v0.2.1 起内置)
- **硬错** → 自动重跑相应步骤(最多 1 次):禁忌词命中 / 文件为空 / tags < 5 / 图片过小(疑似生成失败)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 537)May include surrounding context.

md
设计原则:
- UA 像 curl(Cloudflare 拦截 Python-urllib,但拒绝未知自定义 UA)
- 不强 raise,让调用者决定怎么处理失败
- token 多源解析:env > XDG config > 项目 .env
- 所有路径通过 helper 函数解析,避免硬编码
"""

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 540)May include surrounding context.

md
设计原则:
- UA 像 curl(Cloudflare 拦截 Python-urllib,但拒绝未知自定义 UA)
- 不强 raise,让调用者决定怎么处理失败
- token 多源解析:env > XDG config > 项目 .env
- 所有路径通过 helper 函数解析,避免硬编码
"""

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 543)May include surrounding context.

md
设计原则:
- UA 像 curl(Cloudflare 拦截 Python-urllib,但拒绝未知自定义 UA)
- 不强 raise,让调用者决定怎么处理失败
- token 多源解析:env > XDG config > 项目 .env
- 所有路径通过 helper 函数解析,避免硬编码
"""

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/content_engine/client.py (reported line 6)May include surrounding context.

python
设计原则:
- UA 像 curl(Cloudflare 拦截 Python-urllib,但拒绝未知自定义 UA)
- 不强 raise,让调用者决定怎么处理失败
- token 多源解析:env > XDG config > 项目 .env
- 所有路径通过 helper 函数解析,避免硬编码
"""

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/content_engine/client.py (reported line 52)May include surrounding context.

python
设计原则:
- UA 像 curl(Cloudflare 拦截 Python-urllib,但拒绝未知自定义 UA)
- 不强 raise,让调用者决定怎么处理失败
- token 多源解析:env > XDG config > 项目 .env
- 所有路径通过 helper 函数解析,避免硬编码
"""

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/content_engine/client.py (reported line 102)May include surrounding context.

python
设计原则:
- UA 像 curl(Cloudflare 拦截 Python-urllib,但拒绝未知自定义 UA)
- 不强 raise,让调用者决定怎么处理失败
- token 多源解析:env > XDG config > 项目 .env
- 所有路径通过 helper 函数解析,避免硬编码
"""

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/content_engine/llm.py (reported line 8)May include surrounding context.

python
设计原则:
- UA 像 curl(Cloudflare 拦截 Python-urllib,但拒绝未知自定义 UA)
- 不强 raise,让调用者决定怎么处理失败
- token 多源解析:env > XDG config > 项目 .env
- 所有路径通过 helper 函数解析,避免硬编码
"""

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/content_engine/llm.py (reported line 53)May include surrounding context.

python
设计原则:
- UA 像 curl(Cloudflare 拦截 Python-urllib,但拒绝未知自定义 UA)
- 不强 raise,让调用者决定怎么处理失败
- token 多源解析:env > XDG config > 项目 .env
- 所有路径通过 helper 函数解析,避免硬编码
"""

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/content_engine/preflight.py (reported line 134)May include surrounding context.

python
设计原则:
- UA 像 curl(Cloudflare 拦截 Python-urllib,但拒绝未知自定义 UA)
- 不强 raise,让调用者决定怎么处理失败
- token 多源解析:env > XDG config > 项目 .env
- 所有路径通过 helper 函数解析,避免硬编码
"""

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/content_engine/preflight.py (reported line 178)May include surrounding context.

python
设计原则:
- UA 像 curl(Cloudflare 拦截 Python-urllib,但拒绝未知自定义 UA)
- 不强 raise,让调用者决定怎么处理失败
- token 多源解析:env > XDG config > 项目 .env
- 所有路径通过 helper 函数解析,避免硬编码
"""

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/content_engine/preflight.py (reported line 205)May include surrounding context.

python
设计原则:
- UA 像 curl(Cloudflare 拦截 Python-urllib,但拒绝未知自定义 UA)
- 不强 raise,让调用者决定怎么处理失败
- token 多源解析:env > XDG config > 项目 .env
- 所有路径通过 helper 函数解析,避免硬编码
"""

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/content_engine/seedance.py (reported line 9)May include surrounding context.

python
设计原则:
- UA 像 curl(Cloudflare 拦截 Python-urllib,但拒绝未知自定义 UA)
- 不强 raise,让调用者决定怎么处理失败
- token 多源解析:env > XDG config > 项目 .env
- 所有路径通过 helper 函数解析,避免硬编码
"""

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/content_engine/seedance.py (reported line 63)May include surrounding context.

python
设计原则:
- UA 像 curl(Cloudflare 拦截 Python-urllib,但拒绝未知自定义 UA)
- 不强 raise,让调用者决定怎么处理失败
- token 多源解析:env > XDG config > 项目 .env
- 所有路径通过 helper 函数解析,避免硬编码
"""

Static analysis

No suspicious patterns detected.