Back to skill

Security audit

News Skill

Security checks for vulnerabilities and agentic risk

Overview

This paid news skill should go to Review because it embeds a payment API key, sends user and query data to external services, and has broken billing/loading behavior.

Review carefully before installing. The skill's core idea is not inherently malicious, but it exposes a payment API key, attempts automatic per-call billing, sends user and query data to external services, and is currently broken at load time. The publisher should remove and rotate the exposed key, fail closed on billing errors, define clearer activation and consent behavior, and accurately disclose the external news provider path.

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
handler.py:11
Finding

Hard-Coded SkillPay API Credential

Content
View full analysis

Vulnerability Details

File Location: handler.py:11 and SKILL.md:21
Vulnerability Type: Hard-coded secret exposure
Risk Level: High

Vulnerable Code

handler.py:11:

python
SKILLPAY_API_KEY = "sk_93c5ff38cc3e6112623d361fffcc5d1eb1b5844eac9c40043b57c0e08f91430e"

SKILL.md:21:

markdown
- API Key: sk_93c5ff38cc3e6112623d361fffcc5d1eb1b5844eac9c40043b57c0e08f91430e

The credential is subsequently transmitted in two locations in the same request:

python
payload = {
    "api_key": SKILLPAY_API_KEY,
    "user_id": user_id,
    "amount": PRICE_USDT,
    "skill_id": SKILL_ID, "currency": "USDT",
    "description": "News summary query"
}
headers = {"Content-Type": "application/json", "X-API-Key": SKILLPAY_API_KEY}

Technical Analysis

A live-looking SkillPay API credential is embedded directly in both the source code and user-facing documentation. Anyone who can download, inspect, clone, or otherwise access the project can recover the credential without authentication.

Hard-coded credentials cannot be protected through ordinary file permissions once a package is distributed. The same credential is also placed in both the JSON request body and the X-API-Key header, unnecessarily increasing its exposure to request logging, debugging systems, proxies, and application telemetry.

Attack Path

  1. An attacker obtains or inspects the project package.
  2. The attacker reads SKILL.md or handler.py and extracts the SkillPay API key.
  3. The attacker constructs requests to the SkillPay billing API using the exposed credential.
  4. If the credential remains active and has sufficient permissions, the attacker acts under the Skill's API identity.
  5. The attacker may generate unauthorized billing operations or consume API resources until the key is revoked.

Impact Assessment

The exposed credential may permit unauthorized access to operations available to ...[truncated 457 chars]

Remediation
View remediation

Remediation Suggestions

  1. Revoke the exposed API key immediately and issue a replacement.

  2. Remove the credential from both handler.py and SKILL.md.

  3. Load the replacement credential from an environment variable or managed secret store:

    python
    import os
    
    SKILLPAY_API_KEY = os.environ["SKILLPAY_API_KEY"]
    
  4. Validate at startup that the secret is present, but never print its value.

  5. Transmit the credential only through the authentication mechanism required by SkillPay; do not duplicate it in the request body unless the verified API specification explicitly requires this.

  6. Configure log redaction for authentication headers and sensitive request fields.

  7. Restrict the replacement key to the minimum required operations and apply billing, rate, and source restrictions where supported.

  8. Review repository history and distributed package versions for prior exposure.

  9. Add automated secret scanning to the development and release process.

T09 · Insecure Skill Coding Practices

Error
Location
handler.py:16
Finding

Billing Enforcement Fails Open on Exceptions

Content
View full analysis

Vulnerability Details

File Location: handler.py:16-34 and handler.py:65-75
Vulnerability Type: Fail-open payment authorization and undefined billing identifier
Risk Level: High

Vulnerable Code

python
def charge_user(user_id: str) -> dict:
    """Charge user via SkillPay"""
    try:
        payload = {
            "api_key": SKILLPAY_API_KEY,
            "user_id": user_id,
            "amount": PRICE_USDT,
            "skill_id": SKILL_ID, "currency": "USDT",
            "description": "News summary query"
        }
            headers = {"Content-Type": "application/json", "X-API-Key": SKILLPAY_API_KEY}
        response = requests.post(f"{SKILLPAY_API_URL}/charge", json=payload, headers=headers, timeout=10)
        if response.status_code == 200:
            return {"success": True, "data": response.json()}
        return {"success": False, "error": response.text}
    except Exception as e:
        return {"success": True, "demo": True, "error": str(e)}

The result is trusted by the handler:

python
charge_result = charge_user(user_id)

if not charge_result.get("success") and not charge_result.get("demo"):
    return {
        "payment_required": True,
        "amount": PRICE_USDT,
        "skill_id": SKILL_ID, "payment_url": charge_result.get("payment_url", "https://skillpay.me")
    }

news = search_news(topic)
news["payment_status"] = "free_demo" if charge_result.get("demo") else "paid"
return news

Technical Analysis

The exception handler marks every billing exception as a successful demo:

python
{"success": True, "demo": True, "error": str(e)}

The main handler consequently continues to provide the paid operation whenever billing raises an exception. This violates the fail-closed principle required for payment and authorization controls.

In addition, SKILL_ID is referenced but never defined in the reviewed project. On ...[truncated 1752 chars]

Remediation
View remediation

Remediation Suggestions

  1. Define SKILL_ID using trusted configuration and validate it during startup.

  2. Treat billing exceptions as payment failures rather than successful demo access:

    python
    except requests.RequestException:
        return {"success": False, "error": "Billing service unavailable"}
    except Exception:
        return {"success": False, "error": "Billing operation failed"}
    
  3. Permit demo mode only through an explicit, trusted deployment setting—not as a consequence of an exception.

  4. Require a verified successful charge result before executing search_news.

  5. Validate the billing response schema and transaction status instead of relying only on HTTP status code 200.

  6. Use idempotency keys to prevent inconsistent or duplicate charging when requests are retried.

  7. Return generic client-facing errors and log detailed exceptions only in protected server-side telemetry.

  8. Add tests covering undefined configuration, timeout, connection failure, non-200 responses, malformed JSON, and rejected transactions.

  9. Ensure all payment-control failures deny the paid operation by default.

other

Warning
Location
handler.py:27
Finding

Syntax Error Prevents Skill Loading and Execution

Content
View full analysis

Vulnerability Details

File Location: handler.py:27
Vulnerability Type: Application denial of service caused by invalid indentation
Risk Level: Medium

Vulnerable Code

python
def charge_user(user_id: str) -> dict:
    """Charge user via SkillPay"""
    try:
        payload = {
            "api_key": SKILLPAY_API_KEY,
            "user_id": user_id,
            "amount": PRICE_USDT,
            "skill_id": SKILL_ID, "currency": "USDT",
            "description": "News summary query"
        }
            headers = {"Content-Type": "application/json", "X-API-Key": SKILLPAY_API_KEY}
        response = requests.post(f"{SKILLPAY_API_URL}/charge", json=payload, headers=headers, timeout=10)

Technical Analysis

The headers assignment has an additional indentation level even though no new block was opened. Python rejects the module with an IndentationError during parsing.

This error occurs before any handler logic is executed. Consequently, the module cannot be imported, the command-line entry point cannot run, and none of the intended billing or news-search functionality is available.

This is primarily an availability and release-quality defect rather than a privilege-escalation issue. It nevertheless creates a complete denial of service for this Skill.

Attack Path

No attacker-controlled input is required:

  1. The runtime attempts to import or execute handler.py.
  2. The Python parser reaches line 27.
  3. The unexpected indentation triggers an IndentationError.
  4. Module initialization terminates.
  5. Every request relying on this handler fails before processing.

If an attacker can cause deployment of this reviewed version, normal service initialization is sufficient to trigger the denial of service.

Impact Assessment

The defect does not grant additional privileges or expose host resources. Its scope is total loss of availability for the Skill: all users ...[truncated 279 chars]

Remediation
View remediation

Remediation Suggestions

  1. Align the headers assignment with the payload and response assignments:

    python
    headers = {
        "Content-Type": "application/json",
        "X-API-Key": SKILLPAY_API_KEY,
    }
    
  2. Run python -m py_compile handler.py before packaging or deployment.

  3. Add a continuous-integration check that imports every executable module.

  4. Apply a Python formatter and linter to prevent indentation defects.

  5. Add a smoke test that invokes handle with a representative request.

  6. Correct the undefined SKILL_ID and fail-open exception handling before redeployment; fixing only the indentation would leave the payment bypass active.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (8)

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

A live-looking API key/payment credential is embedded directly in the skill document, which exposes it to anyone who can view, copy, or reuse the skill. In this context, the key is unrelated to user-facing documentation and could enable unauthorized API usage, billing abuse, quota exhaustion, or downstream compromise of linked services.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

A live API key is hardcoded directly in the source and then used in outbound requests. Embedded credentials are highly dangerous because anyone with code access can extract and abuse the key for unauthorized charges, impersonation, or depletion of the associated account.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The skill advertises news lookup via external services but does not clearly warn users that their queries may be transmitted to third-party providers. This creates a privacy and transparency issue because users may unknowingly send sensitive topics, interests, or identifiers outside the platform boundary.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The listed invocation examples such as "Latest news about AI" and "Tech news today" are generic natural-language requests that overlap with ordinary conversation. The file does not define any narrower activation constraints, explicit trigger syntax, or exclusion conditions, which increases the risk of unintended invocation.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The payment helper treats any exception during billing as a successful 'demo' result, which causes the main handler to continue delivering the service without a valid charge. This creates a fail-open billing bypass and also hides operational errors, making abuse and revenue loss likely if the payment endpoint is unreachable or deliberately triggered to fail.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The skill charges users and sends their topic queries to external services without any visible consent, notice, or disclosure in the code path. In a skill context, silent billing and undisclosed third-party transmission can violate user expectations, platform policy, and privacy requirements even if the network calls themselves are intentional.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
89% confidence
Finding

The code transmits user_id, billing details, and an API key to an external billing service. External transmission is expected for payment processing, but in this skill it becomes security-relevant because sensitive data leaves the local context and is sent alongside an embedded secret, with no visible minimization or disclosure.

Content

Scanner excerpt · handler.py (reported line 27)May include surrounding context.

python
"description": "News summary query"
        }
            headers = {"Content-Type": "application/json", "X-API-Key": SKILLPAY_API_KEY}
        response = requests.post(f"{SKILLPAY_API_URL}/charge", json=payload, headers=headers, timeout=10)
        if response.status_code == 200:
            return {"success": True, "data": response.json()}
        return {"success": False, "error": response.text}

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
80% confidence
Finding

The docstring at L035 says "Get latest news using Jina RSS", while the implementation at L037-L039 constructs a Google News RSS search URL and sends it through the r.jina.ai proxy. This is not merely omitted detail; the inline comment and code indicate a different upstream source than the docstring suggests.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
handler.py:12

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
SKILL.md:24