Back to skill

Security audit

Revenue Dashboard

Security checks for vulnerabilities and agentic risk

Overview

This Stripe revenue skill mostly does what it says, but it handles sensitive Stripe credentials and financial data in ways users should review carefully before installing.

Review this skill before installing. Use a restricted read-only Stripe key if possible, store it with tight permissions, avoid running it on shared or heavily monitored machines, and narrow when the skill is invoked so ordinary revenue discussions do not automatically access Stripe data.

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

Warning
Location
scripts/revenue.py:105
Finding
Stripe Secret Key Exposed in Process Command-Line Arguments in Revenue Reporting<![CDATA[ ## Vulnerability Details **File Location**: `scripts/revenue.py`, lines 105-112 **Vulnerability Type**: Stripe credential disclosure through process arguments **Risk Level**: Medium ### Vulnerable Code ```python headers = ["-u", f"{stripe_key}:", "-H", "Stripe-Version: 2025-01-27.acacia"] if acct_id: headers.extend(["-H", f"Stripe-Account: {acct_id}"]) all_data = [] url = f"https://api.stripe.com/v1/{endpoint}" while url: result = subprocess.run( ["curl", "-s", "-g", url] + headers, capture_output=True, text=True ) ``` ### Technical Analysis The code passes the Stripe secret key to `curl` using the `-u` argument. Although `subprocess.run` uses an argument list rather than a shell and therefore does not introduce shell injection here, the secret becomes part of the child process's command-line argument vector. Depending on operating-system process visibility, container configuration, monitoring software, and local account permissions, another process may observe this argument through process inspection facilities such as `/proc/<pid>/cmdline`, `ps`, audit logs, endpoint monitoring, or process telemetry. The credential remains exposed for the lifetime of each `curl` process. Pagination and multi-account reporting can create multiple opportunities to observe it. The key is a Stripe secret rather than a restricted ephemeral token. Its effective authority depends on how the key was provisioned. ### Attack Path 1. An attacker first obtains local code execution or process-inspection access on the host, under an account permitted to inspect the reporting process. 2. The attacker monitors process creation or repeatedly queries available process metadata. 3. A user or scheduled workflow invokes `scripts/revenue.py`. 4. The script starts `curl` with `-u sk_...:` in its process argument vector. 5. The attacker captures the argument vector and extracts the Stripe secret key. 6. The attacker sends authenticated requests to Strip ...[truncated 900 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the external `curl` process with a vetted in-process Stripe SDK or HTTPS client. 2. Supply authentication through an HTTP `Authorization` header managed in process memory, not through command-line arguments. 3. Use a restricted Stripe key granting only the read operations required for revenue reporting. 4. Verify that `~/.config/stripe/api_key` is a regular file owned by the expected user and has restrictive permissions such as mode `0600`. 5. Avoid logging request headers, authorization values, environment dumps, or exception objects that could contain the key. 6. Rotate the existing Stripe key if the script has run on a shared or monitored host where process arguments may have been retained. 7. If retaining `curl` temporarily is unavoidable, provide credentials through a protected mechanism that does not expose them in the argument vector; an in-process client remains the preferred correction. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/mrr.py:34
Finding
Stripe Secret Key Exposed in Process Command-Line Arguments in MRR Reporting<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mrr.py`, lines 34-41 **Vulnerability Type**: Stripe credential disclosure through process arguments **Risk Level**: Medium ### Vulnerable Code ```python headers = ["-u", f"{stripe_key}:", "-H", "Stripe-Version: 2025-01-27.acacia"] if acct_id: headers.extend(["-H", f"Stripe-Account: {acct_id}"]) all_data = [] url = f"https://api.stripe.com/v1/{endpoint}" while url: result = subprocess.run( ["curl", "-s", "-g", url] + headers, capture_output=True, text=True ) ``` ### Technical Analysis The MRR reporting script embeds the Stripe secret key in the command-line arguments passed to `curl`. The use of a Python argument array avoids shell interpretation, but it does not prevent credential disclosure through the child process's argument vector. Local users, host administrators, monitoring agents, audit facilities, or compromised processes with sufficient process-inspection permissions may be able to capture the key while `curl` is running. Repeated subscription pagination can increase the observation window by creating additional authenticated `curl` processes. ### Attack Path 1. An attacker gains process-inspection access on the machine or access to monitoring data that records process command lines. 2. The attacker waits for or detects execution of `scripts/mrr.py`. 3. The script launches `curl` with the Stripe key included after the `-u` option. 4. The attacker reads the command-line arguments and recovers the key. 5. The attacker authenticates directly to the Stripe API with the stolen credential. 6. The attacker accesses or changes Stripe resources according to the credential's assigned permissions. The attacker must already have suitable local visibility or access to process telemetry; this flaw is a credential-exposure channel rather than a standalone remote compromise. ### Impact Assessment The exposed credential may authorize access to subscription, customer, p ...[truncated 326 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use the official Stripe Python SDK or a vetted in-process HTTPS library instead of spawning `curl`. 2. Keep the API key in process memory and place it only in the HTTPS authorization header. 3. Provision a restricted, read-only Stripe key containing only the permissions required to list subscriptions and related pricing information. 4. Enforce restrictive ownership and mode `0600` on `~/.config/stripe/api_key`. 5. Ensure application logs and error handlers never record authorization headers or secret-bearing request objects. 6. Rotate the current key if process command lines may have been captured by other users, monitoring tools, or audit infrastructure. 7. Add a regression test or static-analysis rule that rejects secrets interpolated into subprocess arguments. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (11)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The code is narrowly focused on pulling active Stripe subscriptions and calculating MRR/ARR and subscription counts across multiple accounts, with optional plan-level breakdowns. That partially matches the declared description around Stripe metrics and MRR/ARR. However, the declared purpose substantially overstates the skill: there is no automation/scheduling for daily reports, no goal tracking, no anomaly detection or alerting, no comparison of time periods, no churn analysis, and no general revenue or sales performance answering capability. The accessed resource (Stripe) is consistent, but the implemented behavior is materially narrower than the declared description.

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill documents direct shell execution of local Python scripts but does not declare any explicit tool scope or allowed-tools boundary. That creates unnecessary ambiguity about what execution capabilities the skill needs, making it easier for an agent or operator to invoke shell access more broadly than intended.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The trigger language is extremely broad and includes common business terms like revenue, growth rate, churn, and sales numbers. That can cause the skill to activate in many unrelated conversations, increasing the chance of unnecessary access to financial data or execution of local scripts in contexts where the user did not intend to invoke this capability.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The documentation tells users to store a Stripe secret key in a local file without any warning about secret sensitivity, file permissions, rotation, or safer secret-management options. Because Stripe secret keys grant access to sensitive financial account data and potentially operational actions, poor handling materially raises the risk of credential theft or accidental exposure.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This code performs outbound API requests to Stripe using account credentials and retrieves subscription data, but the script provides no user-facing disclosure before making the network call. Aside from the module docstring stating its purpose, there is no runtime warning, confirmation, or explicit notice about external data access/transmission.

External Transmission

Medium
Category
Data Exfiltration
Content
headers.extend(["-H", f"Stripe-Account: {acct_id}"])

    all_data = []
    url = f"https://api.stripe.com/v1/{endpoint}"

    while url:
        result = subprocess.run(
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
headers.extend(["-H", f"Stripe-Account: {acct_id}"])

    all_data = []
    url = f"https://api.stripe.com/v1/{endpoint}"

    while url:
        result = subprocess.run(
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
headers.extend(["-H", f"Stripe-Account: {acct_id}"])

    all_data = []
    url = f"https://api.stripe.com/v1/{endpoint}"

    while url:
        result = subprocess.run(
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
headers.extend(["-H", f"Stripe-Account: {acct_id}"])

    all_data = []
    url = f"https://api.stripe.com/v1/{endpoint}"

    while url:
        result = subprocess.run(
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
url = f"https://api.stripe.com/v1/{endpoint}"

    while url:
        result = subprocess.run(
            ["curl", "-s", "-g", url] + headers,
            capture_output=True, text=True
        )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
url = f"https://api.stripe.com/v1/{endpoint}"

    while url:
        result = subprocess.run(
            ["curl", "-s", "-g", url] + headers,
            capture_output=True, text=True
        )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Static analysis

No suspicious patterns detected.