Back to skill

Security audit

Ai Course Agent

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it advertises, but it handles reusable credentials and paid billing in ways users should review before installing.

Install only if you are comfortable providing Edustem credentials to the configured endpoint and accepting paid SkillPay usage. Before production use, require a stable verified Edustem domain, rotate/remove the embedded SkillPay key, add explicit user confirmation before charges, validate configuration before billing, and separate mocked tests from live billing/API tests.

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)

other

Error
Location
src/edustem-api.ts:79
Finding
Edustem Account Credentials Transmitted to a Hardcoded Ephemeral Tunnel<![CDATA[ ## Vulnerability Details **File Location**: `src/edustem-api.ts:4-5, 79-99`; credential source: `src/config.ts:14-27` **Vulnerability Type**: Sensitive credential disclosure to an insufficiently trusted remote endpoint **Risk Level**: High ### Vulnerable Code ```typescript const API_BASE_URL = "https://6bb95bf119bf.ngrok-free.app/api/v1"; const LESSON_BASE_URL = "https://6bb95bf119bf.ngrok-free.app/ai-lesson"; ``` ```typescript /** * Login to Edustem API and get JWT token */ export async function login( username: string, password: string, ): Promise<string> { try { const form = new FormData(); form.append("username", username); form.append("password", password); const response = await axios.post<LoginResponse>( `${API_BASE_URL}/login/`, form, { headers: form.getHeaders(), }, ); if (response.data.status !== 200) { throw new Error(`Login failed with status ${response.data.status}`); } return response.data.data.token; } catch (error) { throw handleError("Login failed", error); } } ``` The transmitted credentials originate from environment variables: ```typescript export function getEdustemConfig(): EdustemConfig { const username = process.env.EDUSTEM_USERNAME; const password = process.env.EDUSTEM_PASSWORD; if (!username || !password) { throw new Error( "Missing Edustem credentials. Set EDUSTEM_USERNAME and EDUSTEM_PASSWORD environment variables.", ); } return { username, password }; } ``` ### Technical Analysis The Skill reads a reusable Edustem username and password from secret environment variables and submits both to a hardcoded `ngrok-free.app` endpoint. Although HTTPS protects the request in transit, it does not establish that the endpoint operator is Edustem or that the transient tunnel is an authorized credential recipient. An ngrok hostname is generally controlled through a tunnel account and may be temporary, reassigned, comp ...[truncated 1818 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the ngrok endpoint with a stable, documented domain owned and operated by Edustem. 2. Make the API base URL administrator-configurable, but enforce an explicit HTTPS hostname allowlist. 3. Reject IP literals, unapproved tunnel domains, insecure HTTP URLs, and redirects to unapproved hosts. 4. Replace reusable account passwords with scoped, revocable API tokens limited to lesson creation. 5. Obtain explicit user consent that identifies the destination before transmitting authentication material. 6. Consider certificate or public-key pinning where operationally feasible. 7. Prevent Axios from following authentication requests across origins without validation. 8. Rotate the affected Edustem password if it has already been submitted to this endpoint. 9. Document the remote service owner, data-retention policy, and credential-handling policy. 10. Add automated tests that verify secrets are sent only to approved authentication hosts. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
src/skillpay.ts:42
Finding
Production SkillPay API Secret Embedded in Distributed Source Code<![CDATA[ ## Vulnerability Details **File Location**: `src/skillpay.ts:42-55` **Vulnerability Type**: Hardcoded production credential **Risk Level**: High ### Vulnerable Code ```typescript /** * Get billing configuration (hardcoded - DO NOT expose to users!) * * These values belong to the skill author and are used to receive payments. * Users cannot and should not modify these values. */ function getBillingConfig(): BillingConfig { // Hardcoded credentials - payments go to skill author's account const apiKey = 'sk_ee2a96e814192bbe11402f4c624cfa524de6d23babf3baab7b4b306accee9ee5'; const skillId = '476d912d-e597-4be0-a031-6ffe2adf3b13'; return { apiKey, skillId }; } /** * Get headers for billing API */ function getHeaders(): Record<string, string> { const { apiKey } = getBillingConfig(); return { 'X-API-Key': apiKey, 'Content-Type': 'application/json', }; } ``` ### Technical Analysis The package embeds a live-looking SkillPay API key and Skill ID directly in source code. Every user who downloads the package can inspect and extract these values. The comment stating that the secret must not be exposed does not provide protection because JavaScript and TypeScript packages cannot securely conceal credentials distributed to clients. The key is attached as `X-API-Key` to charge, balance, and payment-link requests. If SkillPay authorizes these operations solely through this key, an attacker can invoke the API independently of the Skill and impersonate the author-controlled billing integration. This is a direct violation of secret-management principles: credentials must not be committed to source control or shipped in client-side artifacts. ### Attack Path 1. An attacker downloads the Skill package or accesses its repository. 2. The attacker opens `src/skillpay.ts`. 3. The attacker extracts the hardcoded API key and Skill ID. 4. The attacker sends direct HTTP requests to the SkillPay billing API with the stolen `X-API-Key`. 5. Depen ...[truncated 825 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Revoke and rotate the exposed API key immediately. 2. Remove the secret from source code and repository history. 3. Do not distribute an author-level billing credential in the Skill package. 4. Move billing operations to an author-controlled backend that retains the credential server-side. 5. Authenticate each Skill invocation to that backend with short-lived, user- or installation-bound tokens. 6. Scope the backend credential to only the minimum required billing operations. 7. Add rate limits, replay protection, idempotency keys, audit logging, and anomaly detection. 8. Ensure that charge operations require a server-verified user identity and cannot use arbitrary caller-supplied identifiers. 9. Use a managed secret store and automated secret rotation. 10. Add secret-scanning checks to source-control and release pipelines. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/agent.ts:54
Finding
Users Are Charged Before Configuration Validation and Successful Course Delivery<![CDATA[ ## Vulnerability Details **File Location**: `src/agent.ts:54-130`; production side effects in the documented test path: `src/test.ts:29-44` **Vulnerability Type**: Non-transactional billing and unsafe integration testing **Risk Level**: Medium ### Vulnerable Code ```typescript // Step 0: Billing check - charge user via SkillPay console.log(`[Agent] Checking billing for user: ${userId}...`); const billing = await handleBilling(userId); if (!billing.ok) { console.warn(`[Agent] Billing failed: ${billing.message}`); if (billing.paymentUrl) { return { success: false, message: `${billing.message}\n\n💳 请充值后继续使用:\n${billing.paymentUrl}\n\n充值说明: 1 USDT = 1000 tokens, 最低充值 8 USDT`, }; } return { success: false, message: billing.message, }; } console.log(`[Agent] Billing successful: ${billing.message}`); // Get credentials from config (environment or gateway) let credentials; try { credentials = getEdustemConfig(); } catch (error) { return { success: false, message: `配置错误: ${error instanceof Error ? error.message : "Missing credentials"}`, }; } // Step 1: Gather curriculum content console.log("[Agent] Gathering curriculum content..."); const curriculumData = await gatherCurriculumContent( request.subject, request.content, request.grade, ); // Step 2: Login to Edustem API console.log("[Agent] Logging in to Edustem API..."); const token = await login(credentials.username, credentials.password); console.log("[Agent] Login successful, token received"); // Step 3: Create lesson plan console.log("[Agent] Creating lesson plan..."); const createResponse = await createLessonPlan(token, { subject: curriculumData.subject, year_level: request.grade, teaching_time_minutes: "45", topic: curriculumData.topic, curriculum_text: curriculumData.curriculum_text, elaborations: curriculumData.elaborations, teacher_notes: curriculumData.teacher_notes, concepts: "", subject_specific_instructions: "" ...[truncated 3159 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate request fields, user identity, and Edustem configuration before initiating billing. 2. Use an authorization/reservation model: - reserve the required token before generation; - capture it only after the course is accepted and a valid link is ready; - release the reservation on every failure path. 3. If reservation is unavailable, implement an automatic compensating refund for all post-charge failures. 4. Attach a unique idempotency key to every logical course-generation request. 5. Persist the transaction state so retries cannot charge the same request more than once. 6. Require an authenticated, non-default user identifier for all billing operations. 7. Clearly disclose the charge and require explicit user confirmation before the paid operation. 8. Split unit tests from live integration tests. 9. Mock SkillPay and Edustem in `npm run test`; place live tests behind an explicit command and opt-in environment flag. 10. Add automated tests covering missing credentials, authentication failure, network timeouts, API failures, retries, refunds, and duplicate requests. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (46)

Credential Access

High
Category
Privilege Escalation
Content
- [ ] Implement retry logic with exponential backoff
- [ ] Support more subjects and locales
- [ ] Add structured logging
- [ ] Move credentials to .env file
- [ ] Add input validation and sanitization

## Tech Stack
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The documentation promises NLP detection, external course creation, course-link generation, and billing, but the reported implementation apparently does not provide those features. Security-wise, this kind of mismatch is risky because it undermines auditability and can conceal placeholders, stubs, or substituted logic that behaves differently in production.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The documentation promises NLP detection, external course creation, course-link generation, and billing, but the reported implementation apparently does not provide those features. Security-wise, this kind of mismatch is risky because it undermines auditability and can conceal placeholders, stubs, or substituted logic that behaves differently in production.

Tp4

High
Category
MCP Tool Poisoning
Confidence
90% confidence
Finding
The documentation promises NLP detection, external course creation, course-link generation, and billing, but the reported implementation apparently does not provide those features. Security-wise, this kind of mismatch is risky because it undermines auditability and can conceal placeholders, stubs, or substituted logic that behaves differently in production.

Known Vulnerable Dependency: axios==1.13.5 — 16 advisory(ies): CVE-2026-44494 (axios Vulnerable to Full Man-in-the-Middle via Prototype Pollution Gadget in `co); CVE-2026-44495 (axios Vulnerable to Credential Theft and Response Hijacking via Prototype Pollut); CVE-2025-62718 (Axios has a NO_PROXY Hostname Normalization Bypass that Leads to SSRF) +13 more

High
Category
Supply Chain
Confidence
95% confidence
Finding
axios is a direct production dependency and this skill’s purpose explicitly involves outbound API calls, so HTTP client flaws are relevant. If any of the listed advisories affecting redirects, proxy handling, SSRF, prototype pollution gadgets, or credential leakage are reachable, an attacker could influence requests to internal or unintended hosts, leak secrets, or tamper with responses.

Known Vulnerable Dependency: brace-expansion==1.1.12 — 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
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: flatted==3.3.3 — 2 advisory(ies): CVE-2026-32141 (flatted vulnerable to unbounded recursion DoS in parse() revive phase); CVE-2026-33228 (Prototype Pollution via parse() in NodeJS flatted)

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

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
83% confidence
Finding
form-data is a direct production dependency, and the cited CRLF injection issue can matter if multipart field names or filenames are constructed from untrusted input. In that case, an attacker may be able to alter multipart boundaries or inject additional headers/parts, potentially confusing downstream services or enabling request smuggling-like effects at the application layer.

Known Vulnerable Dependency: js-yaml==4.1.1 — 4 advisory(ies): CVE-2026-84375 (js-yaml: maxTotalMergeKeys does not limit CPU use for empty merge sources); CVE-2026-59869 (js-yaml: YAML merge-key chains can force quadratic CPU consumption); GHSA-5p4m-2wfm-xmqj (JS-YAML: Quadratic CPU consumption in !!omap resolution (3.x and 4.x) — CVE-2026) +1 more

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: minimatch==3.1.2 — 3 advisory(ies): CVE-2026-27904 (minimatch ReDoS: nested *() extglobs generate catastrophically backtracking regu); CVE-2026-26996 (minimatch has a ReDoS via repeated wildcards with non-matching literal in patter); CVE-2026-27903 (minimatch has ReDoS: matchOne() combinatorial backtracking via multiple non-adja)

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: axios==1.13.5 — 16 advisory(ies): CVE-2026-44494 (axios Vulnerable to Full Man-in-the-Middle via Prototype Pollution Gadget in `co); CVE-2026-44495 (axios Vulnerable to Credential Theft and Response Hijacking via Prototype Pollut); CVE-2025-62718 (Axios has a NO_PROXY Hostname Normalization Bypass that Leads to SSRF) +13 more

High
Category
Supply Chain
Confidence
97% confidence
Finding
The skill declares axios with a broad range that can resolve to a version identified by the scanner as vulnerable, including SSRF- and request-handling-related advisories. This is especially relevant because the skill's stated purpose is to make outbound API requests to Edustem, so flaws in HTTP client proxy handling, header processing, or prototype-pollution-related gadgets could materially affect request security and potentially leak credentials or reach unintended hosts.

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
95% confidence
Finding
The flagged form-data version has a CRLF injection advisory, which can be dangerous when multipart field names or filenames incorporate untrusted input. Given this skill likely interacts with an external API and may build multipart requests, an attacker-controlled field component could potentially manipulate request structure or headers, causing request smuggling or injection effects against upstream services.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
The file specifies a mandatory Chinese `teacher_notes` format and presents the user input pattern exclusively in Chinese, including examples, with no indication that users may choose another language. This creates a natural-language locale constraint that is neither optional nor clearly justified as a region-specific skill.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
The README explicitly states that SkillPay billing credentials are hardcoded in the skill. Embedding billing or API credentials in code is a real security weakness because anyone with source access, logs, bundles, or accidental disclosures may recover them and abuse payment functions, impersonate the skill, or redirect billing operations. In this context, the capability is especially sensitive because it controls charging users and transferring value to the author's account.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill declares access to secrets and implicitly requires outbound network access, but it does not declare any explicit tool scope or permission boundaries. That makes the skill harder to audit and increases the chance that a host or user enables broader capabilities than intended, especially since it uses external authentication and billing-related flows.

Vague Triggers

Medium
Confidence
93% confidence
Finding
A broad trigger description without tight activation boundaries can cause the skill to activate on ordinary conversation and send unintended content into its external workflow. In this skill, that risk is amplified because activation may lead to billing and transmission of user-provided educational requests to third-party services.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
The description states that the skill generates courses from natural-language requests in Chinese, which imposes a language constraint. Because the file does not present this as an optional user choice or explain a necessary region-specific limitation, it appears to force a specific language in a way that conflicts with the stated policy.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill does not clearly warn users that their requests will be transmitted to an external API provider. This is a privacy and consent issue, especially since user messages may contain educational, personal, or institutional information and are combined with authenticated third-party account access.

Vague Triggers

Medium
Confidence
89% confidence
Finding
Everyday Chinese phrases as triggers, without constraints or exclusions, increase the chance of accidental invocation. Because this skill can charge tokens and submit data externally, accidental triggering can create unwanted costs and unintended disclosure of user input.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This file contains user-facing messages in Chinese, and the parser only recognizes Chinese-language request patterns and subjects. That effectively forces a specific language/locale without user opt-in, which matches the policy-violation criteria for language or locale restrictions.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
The examples and regex patterns only support Chinese phrasing such as '年级' and a fixed list of Chinese subject names. This is a natural-language policy issue because the skill enforces a single language/locale behavior rather than offering choice or clearly documented limitation.

Context-Inappropriate Capability

Medium
Confidence
87% confidence
Finding
The manifest describes a skill that generates courses from Chinese requests via the Edustem API and uses SkillPay for billing, but it does not mention accessing host environment variables for secrets. Reading process environment for credentials is a sensitive capability that is not clearly justified by the stated purpose as presented in the manifest.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The login function sends raw username and password credentials to a third-party remote API endpoint, and this file provides no user-facing disclosure, consent flow, or indication of how those credentials are sourced or protected. In an agent skill context, this is risky because users may not expect the skill to collect and forward credentials to an externally hosted service, especially one exposed through an ngrok domain, which increases trust and phishing concerns.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
Line L05 says the skill 'Supports natural language input in Chinese,' which imposes a language constraint in natural-language documentation. The file does not indicate that users may choose another language or that the Chinese-only scope is required for a documented region-specific reason.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
* Integration with OpenClaw Main Session
 *
 * This module detects course generation requests from user input
 * and automatically executes the course generation flow.
 */

import { generateCourse, parseCourseRequest } from "./agent";
Confidence
85% confidence
Finding
The module automatically interprets user messages and triggers course generation with billing side effects, without any explicit confirmation, authorization check, or anti-abuse guard in this layer. In this skill context, autonomous execution is more dangerous because the action consumes tokens and may let accidental, spoofed, or prompt-injected text trigger paid operations on behalf of a user.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
src/skillpay.ts:39