Back to skill

Security audit

Creem Store Agent

Security checks for vulnerabilities and agentic risk

Overview

This skill is clearly intended to monitor a Creem store, but it can automatically make live billing and subscription changes with weak authorization and validation controls.

Review this carefully before installing, especially on a production Creem account. Use test-mode credentials first, disable or remove automatic execution unless you have a strong approval policy, restrict the Creem API key, limit Telegram operators, and add validation for LLM outputs and webhook body sizes before relying on it for live billing workflows.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
src/llm-analyzer.ts:31
Finding
Webhook-Derived Text Can Manipulate Autonomous Financial Decisions Through Prompt Injection<![CDATA[ ## Vulnerability Details **File Location**: `src/index-helpers.ts:62-64`, `src/llm-analyzer.ts:31-59`, `src/index.ts:61-66` **Vulnerability Type**: Indirect prompt injection leading to unauthorized autonomous actions **Risk Level**: High ### Vulnerable Code `src/index-helpers.ts:62-64` extracts the cancellation reason directly from the webhook: ```ts const cancelReason = (obj.cancel_reason as string) ?? (obj.cancelReason as string) ?? "not provided"; ``` `src/llm-analyzer.ts:31-59` interpolates that untrusted value directly into the model prompt: ```ts export function buildChurnPrompt(ctx: ChurnContext): string { return `You are a SaaS retention analyst. A customer is about to churn. Analyze and recommend ONE action. Customer: ${ctx.customerEmail} Plan: ${ctx.productName} ($${ctx.price}/mo) Tenure: ${ctx.tenureMonths} months Total Revenue: $${ctx.totalRevenue} Cancel Reason: ${ctx.cancelReason || "not provided"} Available actions (pick exactly one): - CREATE_DISCOUNT: Create a retention discount. Params: { percentage: 10-50, durationMonths: 1-6 } - SUGGEST_PAUSE: Pause subscription instead of cancel. Params: {} - NO_ACTION: Let the customer go. Params: {} Rules: - High-value customers (>$500 total or >6 months): prefer CREATE_DISCOUNT with 20-40% - Low-tenure (<2 months) or low-value: prefer NO_ACTION - Medium cases: consider SUGGEST_PAUSE - Include confidence (0-1) in your assessment Respond in JSON only: {"action": "CREATE_DISCOUNT|SUGGEST_PAUSE|NO_ACTION", "reason": "one sentence", "confidence": 0.0-1.0, "params": {...}}`; } ``` `src/index.ts:61-66` treats model confidence as authorization for execution: ```ts // Auto-execute if confidence is high enough if (shouldAutoExecute(decision, AUTO_EXECUTE_THRESHOLD)) { const result = await executeAction(decision, churnCtx, creem as any); const resultMsg = formatActionResult(result, churnCtx); await bot.sendMessage(`🤖 Auto-executed (confidence ${Math.round(decision.confidence * 100)} ...[truncated 2345 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat all webhook and customer fields as untrusted data, including cancellation reasons, product names, and customer identifiers. 2. Place untrusted fields in a clearly delimited data section and explicitly state that their contents must never be interpreted as instructions. 3. Prefer structured model input or tool schemas rather than concatenating external text into a policy prompt. 4. Do not use model-generated confidence as an authorization mechanism. 5. Require explicit human approval for every state-changing financial action, or restrict automatic execution to deterministic, locally evaluated rules. 6. Derive action type and permitted parameters from server-side policy after the model response. 7. Add adversarial tests containing cancellation reasons such as instruction overrides, fake JSON responses, and requests for high-confidence actions. 8. Record an audit event containing the source webhook ID, chosen policy, approver, and executed Creem operation. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
src/action-executor.ts:26
Finding
Unvalidated LLM Parameters Are Passed Directly to the Creem Discount API<![CDATA[ ## Vulnerability Details **File Location**: `src/llm-analyzer.ts:5-28`, `src/action-executor.ts:26-40` **Vulnerability Type**: Missing validation of security-sensitive action parameters **Risk Level**: High ### Vulnerable Code `src/llm-analyzer.ts:5-28` validates the general response shape but does not validate the contents of `params`: ```ts export function parseLLMResponse(raw: string): LLMDecision | null { try { let jsonStr = raw; const codeBlockMatch = raw.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/); if (codeBlockMatch) { jsonStr = codeBlockMatch[1].trim(); } const parsed = JSON.parse(jsonStr); if ( !parsed.action || !VALID_ACTIONS.includes(parsed.action) || typeof parsed.reason !== "string" || typeof parsed.confidence !== "number" || typeof parsed.params !== "object" ) { return null; } return { action: parsed.action, reason: parsed.reason, confidence: Math.max(0, Math.min(1, parsed.confidence)), params: parsed.params ?? {}, }; } catch { return null; } } ``` `src/action-executor.ts:26-40` passes the model-provided values to Creem without enforcing the documented limits: ```ts case "CREATE_DISCOUNT": { const percentage = decision.params.percentage ?? 20; const months = decision.params.durationMonths ?? 3; const discount = await creem.discounts.create({ name: `Retention ${percentage}% off`, type: "percentage", percentage, duration: "repeating", durationInMonths: months, appliesToProducts: [ctx.productId], }); return { success: true, action: "CREATE_DISCOUNT", details: `Created ${percentage}% discount for ${months} months (code: ${discount.code})`, }; } ``` ### Technical Analysis The prompt states that discounts should use percentages from 10 to 50 and durations from 1 to 6 months. These are only natural-language instructions and are not security controls. The parser verifies t ...[truncated 1581 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate model responses with a strict runtime schema. 2. For `CREATE_DISCOUNT`, require: - `percentage` to be a finite integer within the approved range. - `durationMonths` to be a finite integer within the approved range. 3. Reject invalid parameters rather than silently accepting or forwarding them. 4. Revalidate parameters inside `executeAction()`, even if validation was already performed by the parser. The executor should be the final security boundary. 5. Maintain server-side policy constants rather than relying on ranges written only in the prompt. 6. Require approval for unusually costly actions and display the exact validated values to the approver. 7. Add tests for negative values, zero, strings, `null`, fractions, excessively large numbers, missing values, and values outside policy. 8. Consider using fixed retention offers selected from an allowlist instead of allowing the model to generate arbitrary numbers. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
src/index.ts:69
Finding
Telegram Callbacks and Commands Do Not Verify the Authorized Chat or User<![CDATA[ ## Vulnerability Details **File Location**: `src/telegram.ts:49-64`, `src/index.ts:69-116`, `demo/server.ts:76-114` **Vulnerability Type**: Missing authorization for inbound Telegram actions **Risk Level**: High ### Vulnerable Code `src/telegram.ts:49-64` pins outbound messages to a configured chat but forwards all inbound updates without authorization checks: ```ts interface RawTelegramBot { sendMessage(chatId: string, text: string, options?: Record<string, unknown>): Promise<unknown>; on(event: string, handler: (...args: unknown[]) => void): void; onText(pattern: RegExp, handler: (msg: { chat: { id: number } }) => void): void; } export function createTelegramBot(rawBot: RawTelegramBot, chatId: string): TelegramBot { return { async sendMessage(text, options) { await rawBot.sendMessage(chatId, text, options); }, onCallbackQuery(handler) { rawBot.on("callback_query", handler as (...args: unknown[]) => void); }, onText(pattern, handler) { rawBot.onText(pattern, handler); }, }; } ``` `src/index.ts:69-101` executes actions based only on callback data and a matching pending subscription: ```ts async function handleTelegramAction(data: string): Promise<void> { const [action, subscriptionId] = data.split(":"); if (!action || !subscriptionId) return; if (action === "skip") { pendingDecisions.delete(subscriptionId); await bot.sendMessage(`⏭️ Skipped action for ${subscriptionId}`); return; } if (action === "apply" || action === "pause") { const entry = pendingDecisions.get(subscriptionId); if (!entry) { await bot.sendMessage(`❌ No pending decision for ${subscriptionId}`); return; } const overrideDecision = action === "apply" ? entry.decision : { ...entry.decision, action: "SUGGEST_PAUSE" as const }; const result = await executeAction(overrideDecision, entry.context, creem as any); const resultMsg = formatActionResult(result, entry.con ...[truncated 2305 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Include complete Telegram sender and chat metadata in the callback interface. 2. Reject every callback unless `query.message.chat.id` exactly matches the configured authorized chat ID. 3. Maintain an allowlist of authorized Telegram user IDs and verify `query.from.id`. 4. Apply the same checks to `/creem-status`, `/creem-report`, and all future commands. 5. Bind each pending decision to: - Authorized chat ID - Authorized user or role - Original message ID - Random one-time nonce - Expiration time 6. Consume the pending decision atomically before invoking the Creem API to prevent duplicate approvals. 7. Call Telegram's callback acknowledgement API and report unauthorized attempts without exposing subscription details. 8. Add tests for callbacks from unauthorized chats, unauthorized users, expired buttons, replayed callbacks, and mismatched message IDs. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/webhook-handler.ts:13
Finding
Unbounded Webhook Body Buffering and Deduplication Storage Allow Memory Exhaustion<![CDATA[ ## Vulnerability Details **File Location**: `src/webhook-handler.ts:13-19`, `src/webhook-handler.ts:36-64` **Vulnerability Type**: Unbounded memory allocation in an externally reachable webhook **Risk Level**: Medium ### Vulnerable Code `src/webhook-handler.ts:13-19` reads the complete body into memory without a size limit: ```ts async function readBody(req: IncomingMessage): Promise<string> { const chunks: Buffer[] = []; for await (const chunk of req) { chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk); } return Buffer.concat(chunks).toString("utf-8"); } ``` `src/webhook-handler.ts:36-64` reads the body before checking the signature and stores unique event IDs indefinitely: ```ts export function createWebhookHandler(config: WebhookHandlerConfig): (req: IncomingMessage, res: ServerResponse) => Promise<boolean> { const { secret, onEvent } = config; const processedEvents = new Set<string>(); return async (req: IncomingMessage, res: ServerResponse): Promise<boolean> => { const body = await readBody(req); const signature = (req.headers["creem-signature"] as string) ?? ""; if (!signature) { return jsonResponse(res, 401, { error: "Missing signature" }); } if (!verifySignature(body, signature, secret)) { return jsonResponse(res, 401, { error: "Invalid signature" }); } let payload: CreemWebhookPayload; try { payload = JSON.parse(body); } catch { return jsonResponse(res, 400, { error: "Invalid JSON" }); } if (payload.id && processedEvents.has(payload.id)) { return jsonResponse(res, 200, { status: "ok", deduplicated: true }); } if (payload.id) { processedEvents.add(payload.id); } ``` ### Technical Analysis The handler stores every incoming chunk and concatenates the complete body before checking whether a signature is present or valid. Consequently, an unauthenticated request can force large memory allocations before receivi ...[truncated 1658 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce a strict webhook body limit appropriate for Creem events, such as 256 KB or lower. 2. Track accumulated bytes while streaming and immediately: - Stop reading the request. - Destroy or drain the stream safely. - Return HTTP `413 Payload Too Large`. 3. Configure equivalent limits at the reverse proxy and OpenClaw gateway layers. 4. Apply request timeouts, header timeouts, connection limits, and rate limiting. 5. Replace the unbounded `Set` with a fixed-size TTL or LRU cache. 6. Expire deduplication records after the maximum expected webhook retry interval. 7. Validate event IDs for type and maximum length before storage. 8. Add tests for oversized bodies, slow streams, concurrent requests, excessive unique IDs, and eviction behavior. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • 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 (87)

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding
This mismatch is more security-significant because it references state-changing actions such as creating discounts and pausing subscriptions while the public description frames the skill mainly as monitoring and recommendations. Undisclosed write actions against billing/subscription systems can surprise operators and, if triggered improperly, cause financial loss or customer-impacting changes.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
This mismatch is more security-significant because it references state-changing actions such as creating discounts and pausing subscriptions while the public description frames the skill mainly as monitoring and recommendations. Undisclosed write actions against billing/subscription systems can surprise operators and, if triggered improperly, cause financial loss or customer-impacting changes.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding
This mismatch is more security-significant because it references state-changing actions such as creating discounts and pausing subscriptions while the public description frames the skill mainly as monitoring and recommendations. Undisclosed write actions against billing/subscription systems can surprise operators and, if triggered improperly, cause financial loss or customer-impacting changes.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
This mismatch is more security-significant because it references state-changing actions such as creating discounts and pausing subscriptions while the public description frames the skill mainly as monitoring and recommendations. Undisclosed write actions against billing/subscription systems can surprise operators and, if triggered improperly, cause financial loss or customer-impacting changes.

Tp4

High
Category
MCP Tool Poisoning
Confidence
87% confidence
Finding
This mismatch is more security-significant because it references state-changing actions such as creating discounts and pausing subscriptions while the public description frames the skill mainly as monitoring and recommendations. Undisclosed write actions against billing/subscription systems can surprise operators and, if triggered improperly, cause financial loss or customer-impacting changes.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
This mismatch is more security-significant because it references state-changing actions such as creating discounts and pausing subscriptions while the public description frames the skill mainly as monitoring and recommendations. Undisclosed write actions against billing/subscription systems can surprise operators and, if triggered improperly, cause financial loss or customer-impacting changes.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
This mismatch is more security-significant because it references state-changing actions such as creating discounts and pausing subscriptions while the public description frames the skill mainly as monitoring and recommendations. Undisclosed write actions against billing/subscription systems can surprise operators and, if triggered improperly, cause financial loss or customer-impacting changes.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
This mismatch is more security-significant because it references state-changing actions such as creating discounts and pausing subscriptions while the public description frames the skill mainly as monitoring and recommendations. Undisclosed write actions against billing/subscription systems can surprise operators and, if triggered improperly, cause financial loss or customer-impacting changes.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
This mismatch is more security-significant because it references state-changing actions such as creating discounts and pausing subscriptions while the public description frames the skill mainly as monitoring and recommendations. Undisclosed write actions against billing/subscription systems can surprise operators and, if triggered improperly, cause financial loss or customer-impacting changes.

Credential Access

High
Category
Privilege Escalation
Content
import { readFileSync } from "node:fs";
import { createServer } from "node:http";

// Load .env (no dotenv dependency needed)
try {
  const env = readFileSync(new URL("../.env", import.meta.url), "utf-8");
  for (const line of env.split("\n")) {
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
import { readFileSync } from "node:fs";
import { createServer } from "node:http";

// Load .env (no dotenv dependency needed)
try {
  const env = readFileSync(new URL("../.env", import.meta.url), "utf-8");
  for (const line of env.split("\n")) {
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
// Load .env (no dotenv dependency needed)
try {
  const env = readFileSync(new URL("../.env", import.meta.url), "utf-8");
  for (const line of env.split("\n")) {
    const trimmed = line.trim();
    if (!trimmed || trimmed.startsWith("#")) continue;
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
// Load .env (no dotenv dependency needed)
try {
  const env = readFileSync(new URL("../.env", import.meta.url), "utf-8");
  for (const line of env.split("\n")) {
    const trimmed = line.trim();
    if (!trimmed || trimmed.startsWith("#")) continue;
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Known Vulnerable Dependency: form-data==4.0.5 — 1 advisory(ies): CVE-2026-12143 (form-data: CRLF injection in form-data via unescaped multipart field names and f)

High
Category
Supply Chain
Confidence
91% confidence
Finding
form-data 4.0.5 is present as a transitive dependency of @cypress/request and is flagged for CRLF injection in multipart field names. In a lockfile this is a real supply-chain risk because if application code or a dependency builds multipart requests from attacker-controlled field names, it can corrupt request bodies or headers; however, exploitability depends on actual runtime usage and untrusted input reachability.

Known Vulnerable Dependency: brace-expansion==5.0.4 — 5 advisory(ies): CVE-2026-13149 (brace-expansion: DoS via exponential-time expansion of consecutive non-expanding); CVE-2026-33750 (brace-expansion: Zero-step sequence causes process hang and memory exhaustion); CVE-2026-45149 (brace-expansion: Large numeric range defeats documented `max` DoS protection) +2 more

High
Category
Supply Chain
Confidence
90% confidence
Finding
brace-expansion 5.0.4 is a real vulnerable package with multiple algorithmic complexity and memory exhaustion advisories. In this lockfile it is a transitive dev/build dependency, so exploitation would usually require feeding attacker-controlled glob/brace patterns into tooling rather than exploiting the deployed skill directly.

Known Vulnerable Dependency: fast-uri==3.1.0 — 7 advisory(ies): CVE-2026-13676 (fast-uri vulnerable to host confusion via failed IDN canonicalization); CVE-2026-18446 (fast-uri vulnerable to host confusion via backslash authority introducer); CVE-2026-75975 (fast-uri vulnerable to server-side request forgery via malformed IPv6 normalizat) +4 more

High
Category
Supply Chain
Confidence
89% confidence
Finding
fast-uri 3.1.0 is present and the advisories include SSRF and host confusion issues, making this a substantive vulnerable dependency. Because this skill monitors a store and may interact with remote APIs or webhooks, any unsafe URI parsing in reachable server or client code could increase the chance of request misrouting or SSRF-like behavior if attacker-controlled URLs are processed.

Known Vulnerable Dependency: form-data==2.5.5 — 1 advisory(ies): CVE-2026-12143 (form-data: CRLF injection in form-data via unescaped multipart field names and f)

High
Category
Supply Chain
Confidence
91% confidence
Finding
form-data 2.5.5 is another real instance of the same CRLF injection issue, but this copy is marked dev-only through @types/request-related tooling and is less likely to affect production behavior. The vulnerable package still increases supply-chain exposure in development and CI if multipart fields can be influenced by untrusted input.

Known Vulnerable Dependency: brace-expansion==2.0.2 — 4 advisory(ies): CVE-2026-13149 (brace-expansion: DoS via exponential-time expansion of consecutive non-expanding); CVE-2026-33750 (brace-expansion: Zero-step sequence causes process hang and memory exhaustion); CVE-2026-14257 (brace-expansion: DoS via unbounded expansion length causing an out-of-memory pro) +1 more

High
Category
Supply Chain
Confidence
90% confidence
Finding
brace-expansion 2.0.2 is also a true vulnerable dependency with DoS-style expansion issues. As with the other brace-expansion finding, its placement under dev tooling makes it unlikely to be exploitable through normal skill operation, though it can still affect CI or local developer workflows that process attacker-supplied patterns.

Known Vulnerable Dependency: hono==4.12.8 — 16 advisory(ies): CVE-2026-56762 (Hono missing validation of cookie name on write path in setCookie()); CVE-2026-47676 (Hono: app.mount() strips mount prefix using undecoded path, causing incorrect ro); CVE-2026-47675 (Hono: Cookie helper does not sanitize sameSite and priority, allowing Set-Cookie) +13 more

High
Category
Supply Chain
Confidence
87% confidence
Finding
hono 4.12.8 is a real vulnerable dependency with numerous advisories, including cookie handling and routing issues. This is more relevant than many dev-only findings because it is pulled by the MCP SDK, and the skill description suggests a networked agent that may expose server endpoints or callbacks, so flawed HTTP handling could be reachable in practice.

Known Vulnerable Dependency: ip-address==10.1.0 — 2 advisory(ies): CVE-2026-69192 (ip-address: Address4 decodes leading-zero octets as decimal while resolvers deco); CVE-2026-42338 (ip-address has XSS in Address6 HTML-emitting methods)

High
Category
Supply Chain
Confidence
82% confidence
Finding
ip-address 10.1.0 is a real vulnerable dependency, with findings around ambiguous IPv4 parsing and XSS in HTML-emitting methods. In this skill context, impact is limited unless the library is used for security decisions on IP allow/deny logic or its HTML output is rendered somewhere; the lockfile alone does not show such usage.

Intent-Code Divergence

High
Confidence
98% confidence
Finding
The action name "SUGGEST_PAUSE" implies a non-destructive recommendation, but the implementation directly calls `creem.subscriptions.pause(...)` and changes the customer’s subscription state. This mismatch is dangerous because an LLM, operator, or downstream workflow may treat the action as advisory while it actually performs a real account modification, increasing the chance of unintended service disruption.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The README explicitly promotes autonomous execution of billing-impacting actions such as creating discounts and pausing subscriptions, but it does not prominently warn that these actions can modify live customer accounts and revenue state. In an agent skill context, normalizing unsupervised financial/account changes increases the chance of accidental or overbroad execution, especially if operators enable it in production without understanding the consequences.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
### Autonomous Actions

- **Auto-execute** when AI confidence ≥ 80%
- **Manual approval** via Telegram inline buttons when confidence is lower
- Actions: Apply retention discount, pause subscription, or skip
Confidence
97% confidence
Finding
Advertising 'Auto-execute' based solely on an AI confidence threshold means the system may take real business actions without human verification. In this skill, those actions affect discounts and subscription lifecycle, so LLM misclassification, prompt/input anomalies, or incomplete customer context could directly alter revenue and customer access.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
3. **Deduplication** — event IDs tracked in memory to prevent duplicate processing
4. **Classification** — cancellations go through AI analysis; everything else gets a formatted alert
5. **AI Analysis** — Claude Haiku evaluates customer value, tenure, and cancel reason
6. **Action** — high-confidence decisions auto-execute; others await Telegram approval
7. **Execution** — creates retention discounts or pauses subscriptions via Creem SDK

### LLM Decision Logic
Confidence
96% confidence
Finding
The workflow description confirms that high-confidence AI decisions auto-execute and then call Creem SDK methods to create discounts or pause subscriptions. This is dangerous because the README presents autonomous operational control over customer billing state as a standard behavior, increasing the risk of unintended account changes if the model is wrong or inputs are manipulated.

Lp3

Medium
Category
MCP Least Privilege
Confidence
83% confidence
Finding
The skill declares required environment variables and clearly implies networked behavior (webhooks, Telegram, external AI API), but it does not declare an explicit tool scope such as permissions or allowed-tools. That makes the skill's effective capabilities less transparent to reviewers and users, increasing the risk of over-privileged execution or unexpected external communication.

Static analysis

Detected: suspicious.env_credential_access, suspicious.exposed_secret_literal, suspicious.potential_exfiltration

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
demo/demo-script.ts:14

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
demo/server.ts:58

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
src/index.ts:29

Sensitive-looking file read is paired with a network send.

Warn
Code
suspicious.potential_exfiltration
Location
demo/demo-script.ts:6