Back to skill

Security audit

Content Engine

Security checks for vulnerabilities and agentic risk

Overview

This content-generation skill has a legitimate purpose, but its API-key handling can expose a user's OpenRouter key to the wrong provider and its paid network actions need careful review.

Review before installing. Prefer a dedicated OFOX_API_KEY and do not rely on OPENROUTER_API_KEY with this version; rotate an OpenRouter key if you already ran generation with only that key configured. Keep API keys out of project-level .env files where possible, verify OFOX_BASE_URL and ARK_BASE_URL are not overridden, and use --no-real-video or avoid --no-confirm unless you explicitly accept paid Seedance video calls.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

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

OpenRouter API Key Is Transmitted to the Ofox API Endpoint

Content
View full analysis
str | None: """env > .env 文件三源。允许 OPENROUTER_API_KEY 作为兼容备选。""" 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 ``` The selected credential does not cause the endpoint to switch to the corresponding provider. Unless explicitly overridden, the client continues to use the Ofox endpoint: ```python self.base_url = (base_url or os.environ.get("OFOX_BASE_URL") or OFOX_BASE_URL).rstrip("/") self.model = model or os.environ.get("OFOX_LLM_MODEL") or DEFAULT_MODEL ``` The selected key is then transmitted as a bearer credential to that endpoint: ```python req = urllib.request.Request( url, data=body, headers={ "Content-Type": "application/json; charset=utf-8", "Authorization": f"Bearer {self.api_key}", }, method="POST", ) with urllib.request.urlopen(req, timeout=self.timeout) as resp: data = json.loads(resp.read()) ``` The mod ...[truncated 2786 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
92% confidence
Finding

The request destination is influenced by configuration via base_url/OFOX_BASE_URL, and the code sends both the full prompt and a bearer token to that endpoint without validating or allowlisting the host. If an attacker can control environment variables or caller-supplied base_url, they can redirect traffic to an attacker-controlled server and exfiltrate credentials and sensitive prompt content.

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
91% confidence
Finding

submit_task allows the destination host to be overridden via base_url or the ARK_BASE_URL environment variable, then sends an authenticated request with the Bearer API key to that URL. If an attacker can influence configuration or runtime environment, they can redirect requests to an attacker-controlled endpoint and exfiltrate the Ark credential; in a shared agent/skill environment this is more dangerous because prompts and generated content may already cross trust boundaries.

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
91% confidence
Finding

query_task repeats the same pattern: it builds the request URL from base_url / ARK_BASE_URL and includes the Bearer token in the Authorization header. A malicious environment override can turn polling into a credential-leak SSRF-style sink, exposing secrets and sending task metadata to an attacker-controlled server.

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 remote API and writes the response directly to disk without validating the URL's scheme, host, size, or content type. If the upstream service is compromised, misconfigured, or pointed at a malicious endpoint, this can cause SSRF-like access to internal resources or uncontrolled download of untrusted content into the local filesystem.

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
98% confidence
Finding

The skill performs outbound network access, discovers credentials from multiple .env and config locations, and reads local filesystem paths without declaring these behaviors up front. Hidden credential discovery and external transmission materially increase the risk of unintended secret exposure and data exfiltration in an agent setting.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill performs outbound network access, discovers credentials from multiple .env and config locations, and reads local filesystem paths without declaring these behaviors up front. Hidden credential discovery and external transmission materially increase the risk of unintended secret exposure and data exfiltration in an agent setting.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill performs outbound network access, discovers credentials from multiple .env and config locations, and reads local filesystem paths without declaring these behaviors up front. Hidden credential discovery and external transmission materially increase the risk of unintended secret exposure and data exfiltration in an agent setting.

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 102)May include surrounding context.

md
1. Locate the skill root (where `SKILL.md` lives)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
87% confidence
Finding

--no-confirm disables the only described pause before submitting sequential external video-generation jobs, making it easier for an agent or malicious prompt chain to trigger cost-incurring network actions without meaningful user approval. In combination with broad triggers and available API keys, this becomes a strong tool-parameter abuse vector.

Content

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

md
- Submit shots sequentially to Volcengine Ark Seedance 2.0 (async task + polling, ~1-3 min per shot)
- Failed shots don't block other shots; `partial-video.md` records which failed + their prompts for manual re-run
- Successful shots are stitched with `ffmpeg concat` into `final-video.mp4`
- Flags: `--no-real-video` (prompt-only, skip API) / `--async` (submit only, return task_ids) / `--no-confirm` (skip 3s countdown)

### Step 8: validator (built-in since v0.2.1)
- **Hard errors** → auto-retry the relevant step (max 1 attempt): taboo word hits / empty file / tags < 5 / image too small (suspected gen failure)

Credential Access

High
Category
Privilege Escalation
Confidence
91% confidence
Finding

The skill instructs storing API credentials in a persistent plaintext .env under the user's home config and is designed to read such files automatically. In an agent environment with file access, encouraging broad plaintext secret storage increases the blast radius of any prompt injection, overbroad read, or accidental disclosure.

Content

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

bash
# v1 deconstruct: TikHub
mkdir -p ~/.config/content-engine
echo 'TIKHUB_API_TOKEN=your_tikhub_token' >> ~/.config/content-engine/.env

# v2 generate: Ofox (LLM text + Nano Banana images)
echo 'OFOX_API_KEY=ofox-your_key' >> ~/.config/content-engine/.env

Credential Access

High
Category
Privilege Escalation
Confidence
91% confidence
Finding

This finding similarly promotes persistent plaintext storage of OFOX_API_KEY in a readable config file that the skill later auto-discovers. Because this key enables LLM/image-generation calls, compromise can lead to unauthorized spend and exposure of uploaded brand or product data.

Content

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

md
echo 'TIKHUB_API_TOKEN=your_tikhub_token' >> ~/.config/content-engine/.env

# v2 generate: Ofox (LLM text + Nano Banana images)
echo 'OFOX_API_KEY=ofox-your_key' >> ~/.config/content-engine/.env

# v0.3.0+ video generation: Volcengine Ark (Seedance 2.0)
echo 'ARK_API_KEY=your_ark_key' >> ~/.config/content-engine/.env

Credential Access

High
Category
Privilege Escalation
Confidence
91% confidence
Finding

Persisting ARK_API_KEY in a plaintext config file creates the same credential-exposure problem, with the added risk of cost-incurring video-generation abuse. Since the skill defaults to real video generation in some paths, exposed keys could be used for unauthorized workloads.

Content

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

echo 'OFOX_API_KEY=ofox-your_key' >> ~/.config/content-engine/.env

v0.3.0+ video generation: Volcengine Ark (Seedance 2.0)

echo 'ARK_API_KEY=your_ark_key' >> ~/.config/content-engine/.env

text

| Token | Sign up | Use | Required? |

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/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 函数解析,避免硬编码
"""

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 · tests/test_seedance.py (reported line 101)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
75% confidence
Finding

Automatically reading a token from the current working directory .env can cause ambient credential capture from unrelated projects or directories, especially in agentic environments where the runtime CWD may vary. This broadens the trust boundary and can lead to accidental use of the wrong secret or unintended transmission of a local credential to the configured remote API.

Content

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

python
# Token 自动查找路径(按优先级,第一个找到即用)
_TOKEN_SEARCH_PATHS = [
    lambda: Path.cwd() / ".env",                # 1. 当前工作目录
    lambda: _xdg_config_dir() / ".env",         # 2. XDG 标准位置
    lambda: _skill_root_dir() / ".env",         # 3. Skill 根目录
]

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 37)May include surrounding context.

python
# Token 自动查找路径(按优先级,第一个找到即用)
_TOKEN_SEARCH_PATHS = [
    lambda: Path.cwd() / ".env",                # 1. 当前工作目录
    lambda: _xdg_config_dir() / ".env",         # 2. XDG 标准位置
    lambda: _skill_root_dir() / ".env",         # 3. Skill 根目录
]

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 46)May include surrounding context.

python
# Token 自动查找路径(按优先级,第一个找到即用)
_TOKEN_SEARCH_PATHS = [
    lambda: Path.cwd() / ".env",                # 1. 当前工作目录
    lambda: _xdg_config_dir() / ".env",         # 2. XDG 标准位置
    lambda: _skill_root_dir() / ".env",         # 3. Skill 根目录
]

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 47)May include surrounding context.

python
# Token 自动查找路径(按优先级,第一个找到即用)
_TOKEN_SEARCH_PATHS = [
    lambda: Path.cwd() / ".env",                # 1. 当前工作目录
    lambda: _xdg_config_dir() / ".env",         # 2. XDG 标准位置
    lambda: _skill_root_dir() / ".env",         # 3. Skill 根目录
]

Static analysis

No suspicious patterns detected.