Back to skill

Security audit

Competitor Radar

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its competitor-monitoring purpose, but it ships and uses a hardcoded LLM API key and accepts unvalidated feed URLs, so it needs review before installation.

Install only after reviewing and preferably removing the hardcoded LLM key, rotating that exposed credential, making AI scoring explicitly configured or opt-in, and restricting custom RSS URLs to approved public HTTPS destinations. Use --no-ai if you do not want collected titles sent to the local LLM gateway.

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
radar.py:18
Finding

Hard-Coded LLM API Credential Embedded in Source Code

Content
View full analysis
str: url = "http://127.0.0.1:18790/anthropic/v1/messages" headers = { "Content-Type": "application/json", "x-api-key": "sk-RPBUoe2SH7KigJ0SZn6IPDirZtJ2fUaWSukEx1FwxjhWFx0G", "anthropic-version": "2023-06-01", } ``` The same credential is embedded in `_write_radar.py:67-70`: ```python TIMEOUT = 8 LLM_URL = "http://127.0.0.1:18790/anthropic/v1/messages" LLM_API_KEY = "sk-RPBUoe2SH7KigJ0SZn6IPDirZtJ2fUaWSukEx1FwxjhWFx0G" LLM_MODEL = "ppio/pa/claude-sonnet-4-6" ``` It is then transmitted in `_write_radar.py:94-106`: ```python def llm_call(prompt, system="你是龙虾公司的信息分析助理", max_tokens=300): headers = { "Content-Type": "application/json", "x-api-key": LLM_API_KEY, "anthropic-version": "2023-06-01", } data = json.dumps({ "model": LLM_MODEL, "max_tokens": max_tokens, "system": system, "messages": [{"role": "user", "content": prompt}] }).encode() req = urllib.request.Request(LLM_URL, data=data, headers=headers, method="POST") try: with urllib.request.urlopen(req, timeout=30) as resp: return json.loads(resp.read())["content"][0]["text"] ``` ### Technical Analysis An API credential is committed directly into two source files. Any person or process able to read the Skill package, source repository, archive, logs containing source code, or repository history can recover the key. Embedding the key is not necessary for the declared competitor-monitoring functionality. The Skill can retrieve public data without this credential, and optional AI scoring should obtain its credential from a protected runtime secret source. ...[truncated 1682 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
radar.py:85
Finding

Arbitrary URL Fetching Enables SSRF and Local Resource Probing

Content
View full analysis
str | None: try: req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 CompetitorRadar/1.0"}) with urllib.request.urlopen(req, timeout=timeout) as resp: return resp.read().decode("utf-8", errors="replace") except Exception: return None ``` A user-supplied configuration value reaches this function in `radar.py:281-284`: ```python if comp.get("blog_rss"): text = fetch_url(comp["blog_rss"]) if text: items.extend(parse_rss(text, since)) ``` The generated implementation embedded in `_write_radar.py:78-88` contains the same unrestricted network primitive: ```python def http_get(url, extra_headers=None, timeout=TIMEOUT): try: headers = { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36", "Accept": "application/json, application/xml, text/xml, */*", } if extra_headers: headers.update(extra_headers) req = urllib.request.Request(url, headers=headers) with urllib.request.urlopen(req, timeout=timeout) as resp: return resp.read(), None ``` ### Technical Analysis The `--config` option allows a caller to select an arbitrary configuration file, and `blog_rss` is passed directly to `urllib.request.urlopen`. There is no restriction to public HTTPS feeds and no validation against: - Local-file or other unintended URL schemes supported by the runtime. - Loopback, private, ...[truncated 2954 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (17)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

If the implementation sends data to an external LLM service using an undeclared credential and does not properly enforce the promised time-window filtering, the skill may process and transmit more data than users expect. That expands both privacy exposure and operational risk, because collection scope is broader than the documented behavior.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

If the implementation sends data to an external LLM service using an undeclared credential and does not properly enforce the promised time-window filtering, the skill may process and transmit more data than users expect. That expands both privacy exposure and operational risk, because collection scope is broader than the documented behavior.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The script contains a hardcoded LLM API key directly in source code. Embedded secrets are dangerous because anyone with access to the skill files can reuse the credential to access the model endpoint, incur cost, or pivot into connected internal services if the key is trusted beyond this script.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill not only embeds an API key but automatically uses it in an outbound request to an LLM service without any user-facing notice or consent. This creates hidden credential use and hidden data egress, which is risky in an automation skill because users may not realize their collected data is being transmitted or that bundled credentials could be abused.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The file contains a hardcoded LLM API key directly in source code. Embedded secrets are easily exposed through source distribution, logs, backups, or repository history, enabling unauthorized use of the service and possible billing abuse or lateral access depending on the key’s scope.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
79% confidence
Finding

The skill advertises executable behavior that reads/writes files and performs outbound network access, but it does not declare any explicit tool scope or permission boundaries in the manifest. That makes the operational privileges opaque to users and reviewers, increasing the risk of overbroad execution and unreviewed data access when the skill runs.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The description does not warn users that the skill will automatically contact third-party services and process retrieved content with AI. In this context, the omission is security-relevant because users may unknowingly trigger network egress and external data sharing while analyzing competitor material.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill sends collected competitor content, including titles and summaries, to an LLM endpoint for scoring without any visible consent, disclosure, or opt-in in this file. Even though the endpoint is localhost, it is still an external processing boundary that may log, retain, or forward sensitive collected data, making the transmission risky and opaque to users.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The LLM interaction uses Chinese-only system instructions and scoring prompts, which imposes a fixed language/locale behavior. There is no indication in this file that users can select another language or explicitly opt into Chinese output.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The system prompt instructs the model to always answer in concise Chinese. This is a natural-language locale policy issue because the skill imposes a specific language without any visible option for user selection or justification of a region-specific constraint.

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 · _write_radar.py (reported line 213)May include surrounding context.

python
def fetch_github_releases(org: str, since: datetime) -> list:
    url = f"https://api.github.com/orgs/{org}/repos?per_page=10&sort=pushed"
    text = fetch_url(url)
    if not text:
        return []

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 · _write_radar.py (reported line 225)May include surrounding context.

python
def fetch_github_releases(org: str, since: datetime) -> list:
    url = f"https://api.github.com/orgs/{org}/repos?per_page=10&sort=pushed"
    text = fetch_url(url)
    if not text:
        return []

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 · radar.py (reported line 124)May include surrounding context.

python
def fetch_github_releases(org: str, since: datetime) -> list:
    url = f"https://api.github.com/orgs/{org}/repos?per_page=10&sort=pushed"
    text = fetch_url(url)
    if not text:
        return []

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 · radar.py (reported line 133)May include surrounding context.

python
def fetch_github_releases(org: str, since: datetime) -> list:
    url = f"https://api.github.com/orgs/{org}/repos?per_page=10&sort=pushed"
    text = fetch_url(url)
    if not text:
        return []

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The tool sends scraped competitor content titles to the LLM for scoring without warning at the point of use. Even if the data is not highly sensitive by default, silent transmission of externally gathered content can violate user expectations, internal policy, or data handling requirements, especially in enterprise monitoring workflows.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
68% confidence
Finding

The code concatenates externally sourced article titles directly into a user prompt and asks the model to return scored reasons. In a skill context, this natural-language pathway can cause the model to surface or relay sensitive text embedded in fetched content, even though there is no explicit technical exfiltration command.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
97% confidence
Finding

All user-facing instructions and the skill description are presented only in Chinese, which effectively forces a specific language for use of the skill. Under the stated policy, locale or language constraints should either be optional for the user or explicitly documented as justified.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.