Back to skill

Security audit

Generate News Article

Security checks for vulnerabilities and agentic risk

Overview

The skill is understandable as a news-article generator, but its bundled script has unsafe and under-disclosed credential, path, and input-handling behavior that needs review before installation.

Review before installing. Do not run this skill as-is with privileged workspace access. The embedded SerpAPI key should be removed and rotated, the script should use a user-supplied secret only, output paths should be configurable and workspace-local, inputs should be passed to Python as data rather than generated code, and image downloads should be restricted and size-limited.

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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate.sh:38
Finding
Hardcoded SerpAPI Credential Embedded in Source Code<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate.sh`, line 38 **Vulnerability Type**: Hardcoded secret / credential exposure **Risk Level**: High ### Vulnerable Code ```bash SERPAPI_API_KEY="<redacted exposed API key>" python3 "$SERPAPI_SCRIPT" google "$KEYWORD" --num "$NUM_RESULTS" > "$TEMP_JSON" 2>&1 ``` The source contains a complete SerpAPI API key in place of the redacted value above. ### Technical Analysis A live-looking SerpAPI credential is embedded directly in a distributed shell script. Anyone who can read the skill package, a source repository containing it, a backup, or an installed copy can recover the credential without needing access to the intended secret-management system. The inline assignment also overrides any `SERPAPI_API_KEY` value configured by the user for that command. This conflicts with the documented configuration model and prevents operators from controlling which credential is used. Removing the credential from the current version is insufficient if it has already been published or committed, because it may remain available in package archives, caches, logs, or version-control history. ### Attack Path 1. An attacker downloads or otherwise obtains a copy of the skill package. 2. The attacker opens `scripts/generate.sh`. 3. The attacker extracts the hardcoded value assigned to `SERPAPI_API_KEY`. 4. The attacker submits requests to SerpAPI using the exposed credential. 5. Requests consume the associated account's quota and may cause financial or operational impact until the credential is revoked. ### Impact Assessment The attacker gains the ability to authenticate to SerpAPI with the exposed account credential. The scope is limited to the permissions and quota assigned to that key, but may include: - Unauthorized consumption of paid API quota. - Exhaustion of quota needed by legitimate workflows. - Charges against the credential owner's account. - Access to any SerpAPI capabilities authorized for the ...[truncated 184 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Revoke and rotate the exposed API key immediately. Treat it as compromised. 2. Remove the key from the script and read it exclusively from the environment: ```bash if [ -z "${SERPAPI_API_KEY:-}" ]; then echo "Error: SERPAPI_API_KEY is not configured" >&2 exit 1 fi SERPAPI_API_KEY="$SERPAPI_API_KEY" \ python3 "$SERPAPI_SCRIPT" google "$KEYWORD" --num "$NUM_RESULTS" \ > "$TEMP_JSON" 2>&1 ``` 3. Prefer a platform secret store or credential manager over plaintext configuration files. 4. Purge the exposed value from version-control history, release archives, package registries, build artifacts, and logs where feasible. 5. Add automated secret scanning to commits and release pipelines. 6. Restrict the replacement key's permissions and quota to the minimum required by this skill. 7. Monitor the affected account for unauthorized historical usage. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate.sh:62
Finding
Arbitrary Python Code Execution Through Heredoc Argument Interpolation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate.sh`, lines 6–7 and 62–68 **Vulnerability Type**: Code injection **Risk Level**: High ### Vulnerable Code ```bash # Default parameters KEYWORD="${1:-AI助手}" NUM_RESULTS="${2:-5}" ``` ```bash # Process JSON with Python python3 << PYTHON_SCRIPT import json import os import re import sys import urllib.request from urllib.parse import urlparse # Read from environment keyword = "$KEYWORD" output_dir = "$OUTPUT_DIR" assets_dir = "$ASSETS_DIR" num_articles = int("$NUM_RESULTS") ``` ### Technical Analysis `KEYWORD` and `NUM_RESULTS` originate from command-line arguments and are expanded by the shell directly into an unquoted heredoc containing Python source code. Shell quoting at the point where the arguments are initially assigned does not make the later interpolation safe. An argument containing quotation marks, line breaks, or Python syntax can terminate the intended Python string or expression and introduce additional statements. The resulting generated source is then executed by `python3`. Both inputs are affected: - `KEYWORD` is inserted inside a Python string literal without Python escaping. - `NUM_RESULTS` is inserted inside an executable Python expression without validation or quoting. This is source-code generation from untrusted data rather than safe data transfer between the shell and Python. ### Attack Path 1. An attacker obtains the ability to invoke `generate.sh` with a crafted keyword or result-count argument. This may occur directly or through an agent that passes user-supplied skill parameters. 2. The crafted argument includes syntax that closes the intended Python string or expression and introduces an additional Python statement. 3. Bash expands the argument while constructing the heredoc. 4. `python3` parses the expanded heredoc as source code. 5. The injected statement executes with the same user identity, environment, filesystem access, and network access as the skil ...[truncated 813 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Pass user-controlled values as data rather than interpolating them into Python source. For example: ```bash if ! [[ "$NUM_RESULTS" =~ ^[0-9]+$ ]] || [ "$NUM_RESULTS" -lt 1 ] || [ "$NUM_RESULTS" -gt 100 ]; then echo "Error: result count must be an integer from 1 to 100" >&2 exit 1 fi KEYWORD="$KEYWORD" \ NUM_RESULTS="$NUM_RESULTS" \ OUTPUT_DIR="$OUTPUT_DIR" \ ASSETS_DIR="$ASSETS_DIR" \ TEMP_JSON="$TEMP_JSON" \ python3 <<'PYTHON_SCRIPT' import json import os keyword = os.environ["KEYWORD"] output_dir = os.environ["OUTPUT_DIR"] assets_dir = os.environ["ASSETS_DIR"] num_articles = int(os.environ["NUM_RESULTS"]) temp_json = os.environ["TEMP_JSON"] with open(temp_json, "r", encoding="utf-8") as f: data = json.load(f) PYTHON_SCRIPT ``` Key hardening measures are: 1. Quote the heredoc delimiter so that shell expansion is disabled. 2. Transfer values through environment variables, command-line arguments, or standard input. 3. Validate `NUM_RESULTS` as a bounded positive integer before invoking Python. 4. Continue treating `KEYWORD` as arbitrary text; do not attempt to secure source interpolation with incomplete character blacklists. 5. Add regression tests using quotation marks, backslashes, command substitutions, newlines, and Unicode in the keyword. 6. Run the skill with least privilege to reduce the consequences of future injection flaws. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate.sh:91
Finding
Unrestricted Retrieval of Search-Result-Controlled Image URLs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate.sh`, lines 91–109 **Vulnerability Type**: Unrestricted server-side URL retrieval / SSRF and resource exhaustion **Risk Level**: Medium ### Vulnerable Code ```python # Download thumbnail or favicon if available cover_image = '' image_url = thumbnail if thumbnail else favicon if image_url: try: # Extract filename from URL parsed_url = urlparse(image_url) filename = os.path.basename(parsed_url.path) if not filename or filename == '': filename = f'{safe_title[:30]}_cover.png' # Download image image_path = os.path.join(assets_dir, filename) urllib.request.urlretrieve(image_url, image_path) cover_image = f'./assets/{filename}' print(f'📥 Downloaded image: {filename}', file=sys.stderr) except Exception as e: print(f'⚠️ Failed to download image: {e}', file=sys.stderr) ``` ### Technical Analysis The script retrieves `thumbnail` or `favicon` URLs obtained from external search-result data using `urllib.request.urlretrieve`. It performs no validation of: - URL scheme. - Destination host or resolved IP address. - Redirect destinations. - Loopback, link-local, or private network ranges. - Response size. - Connection and read duration. - MIME type or decoded image format. Search-result data is external and cannot be treated as trusted merely because it was returned by SerpAPI. A malicious or compromised indexed page may influence the thumbnail or favicon URL associated with a result. The absence of size limits also permits a remote server to return a very large or unending response. The content is saved with a filename inferred from the URL without verifying that it is an image. ### Attack Path 1. An attacker controls or poisons a web page that can appear in the relevant Google search results. 2. The associated result supplies or causes the search provider to return an attacker-selected thumbnail ...[truncated 1387 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Replace `urlretrieve` with a controlled downloader that applies all of the following safeguards: 1. Permit only `https` URLs. 2. Resolve the hostname and reject loopback, private, link-local, multicast, reserved, and unspecified IP ranges. 3. Repeat destination validation after every redirect and limit the redirect count. 4. Set explicit connection and read timeouts. 5. Stream the response in bounded chunks and abort when a conservative maximum image size is exceeded. 6. Require an approved image MIME type, while not relying on MIME type alone. 7. Decode and validate the downloaded data with an image library before retaining it. 8. Generate a local random filename rather than trusting a URL-derived filename. 9. Write to a securely created temporary file and atomically move it into the assets directory only after validation. 10. Consider disabling external image downloads by default or routing them through a restricted image proxy. 11. Run the downloader in a network sandbox that cannot reach local, private, or metadata services. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (8)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The documented purpose is simple article generation, but the behavior described by the finding includes use of undeclared credentials, access to fixed absolute paths outside the skill directory, and invocation of an external helper from another location. Those hidden behaviors create supply-chain and data exposure risk because the skill can depend on uncontrolled code and touch filesystem locations beyond the expected workspace.

Missing User Warnings

High
Confidence
98% confidence
Finding
The script silently uses an embedded credential without any disclosure to the user, so operators may unknowingly run code that consumes someone else's API account or exfiltrates privileged access material through source sharing and execution. The article-generation purpose does not justify undisclosed bundled credentials; in fact, distributing a working secret inside an automation skill makes misuse easier because users are encouraged to execute it as-is.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The README is written as if the skill operates in Chinese and examples use Chinese output/input terms, but there is no indication that users may choose another language or that the locale restriction is intentional and justified. This can violate language/locale policy when a skill implicitly forces a specific language without user choice.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill advertises behavior that requires file reads/writes and network access, but it does not declare any tool scope or permissions. This weakens reviewability and containment because an operator cannot clearly see or restrict the capabilities the skill expects before execution.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The primary descriptive instruction text is presented in Chinese, which can impose a language requirement on users without consent or alternative locale support. The policy requires avoiding forced language or locale constraints unless the user is offered a choice or the restriction is clearly justified.

Context-Inappropriate Capability

Medium
Confidence
99% confidence
Finding
The script hardcodes a live SerpAPI credential directly in source and uses it at runtime, which exposes the secret to anyone with file access, version control history, logs, backups, or downstream redistribution of the skill. In this context the secret is not merely configuration metadata: it enables third-party API consumption under the owner's account, leading to quota theft, billing abuse, and possible broader account compromise if reused elsewhere.

Missing User Warnings

Low
Confidence
89% confidence
Finding
The markdown states that thumbnails are automatically downloaded into an `assets/` directory, and elsewhere the README explains that article files are generated under an output directory. While these behaviors are part of the skill's function, the documentation does not clearly warn users up front that running the skill will create multiple files and perform network downloads into the local workspace.

Missing User Warnings

Low
Confidence
90% confidence
Finding
This markdown file describes that the skill generates independent Markdown files and automatically downloads thumbnails, which affects user data and workspace contents. Although the behavior is described functionally, there is no clear caution or warning to the user about filesystem changes or downloaded external content.

Static analysis

No suspicious patterns detected.