Back to skill

Security audit

Skill Dropshipping Fulfillment

Security checks for vulnerabilities and agentic risk

Overview

This skill performs plausible dropshipping fulfillment work, but it has under-disclosed live commerce actions, credential handling, and customer-data logging that users should review before installing.

Install only after reviewing the scripts and using test or least-privilege WooCommerce/CJ credentials. Treat live runs as capable of placing supplier orders, changing WooCommerce order state, adding order notes, changing product SKUs, storing CJ tokens, and writing customer data to local logs. Prefer fixing URL validation, dry-run side effects, PII redaction, live confirmation, and dependency updates before production use.

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
scripts/fulfill.js:115
Finding
Unvalidated API origins can expose credentials, access tokens, and customer data<![CDATA[ ## Vulnerability Details **File Location**: - `scripts/fulfill.js:79-80` - `scripts/fulfill.js:115-147` - `scripts/rebuild-mapping.js:37-40` - `scripts/rebuild-mapping.js:90-114` - `scripts/cj-api.js:15-34` - `scripts/cj-api.js:43-61` - `scripts/woo-api.js:14-19` **Vulnerability Type**: Unvalidated security-sensitive endpoint configuration **Risk Level**: High ### Vulnerable Code From `scripts/fulfill.js`: ```js function wooCfg() { const cfg = JSON.parse(fs.readFileSync(WOO_API_PATH, 'utf8')); const base = cfg.url.replace(/\/$/, '') + '/wp-json/wc/v3'; const auth = { username: cfg.consumerKey, password: cfg.consumerSecret }; return { base, auth }; } ``` ```js async function cjEnsureToken() { const cfg = cjCfg(); const now = Date.now(); const exp = Number(cfg.tokenExpiry || 0); if (cfg.accessToken && exp && now < exp - 10 * 60 * 1000) return cfg.accessToken; console.log(' 🔑 Refreshing CJ access token...'); const baseUrl = (cfg.baseUrl || 'https://developers.cjdropshipping.com/api2.0/v1').replace(/\/$/, ''); const res = await axios.post(`${baseUrl}/authentication/getAccessToken`, { apiKey: cfg.apiKey }, { headers: { 'Content-Type': 'application/json' }, timeout: 30000, }); if (!res.data?.result) throw new Error(`CJ token refresh failed: ${JSON.stringify(res.data).slice(0, 200)}`); const token = res.data.data.accessToken; cfg.accessToken = token; cfg.tokenExpiry = now + 14 * 24 * 3600 * 1000; fs.writeFileSync(CJ_API_PATH, JSON.stringify(cfg, null, 2)); console.log(' ✅ CJ token refreshed'); return token; } function cjBaseUrl() { return (cjCfg().baseUrl || 'https://developers.cjdropshipping.com/api2.0/v1').replace(/\/$/, ''); } async function cjHeaders() { return { 'CJ-Access-Token': await cjEnsureToken(), 'Content-Type': 'application/json' }; } async function createCjOrder(orderData) { const res = await axios.post(`${cjBaseUrl()}/shopping/order/createOrder`, orderData, { headers: await cjHeader ...[truncated 3182 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse every configured endpoint with `new URL()` before use. 2. Require the `https:` protocol for both WooCommerce and CJ endpoints. Permit insecure HTTP only through a clearly named, explicit development-only override. 3. Allowlist the documented CJ API hostname. If alternate CJ endpoints are necessary, require an explicit administrator-controlled allowlist rather than trusting arbitrary configuration. 4. Reject URLs containing embedded usernames or passwords. 5. Validate that the final request destination remains approved when redirects occur, or disable redirects for authenticated requests. 6. Separate endpoint configuration from credential files and protect both with restrictive filesystem permissions. 7. Validate configuration before reading or transmitting credentials and fail closed with a non-sensitive error. 8. Use narrowly scoped WooCommerce API credentials limited to the order and product operations actually required. 9. Add automated tests covering hostile hosts, HTTP URLs, malformed URLs, embedded credentials, and redirects to unapproved origins. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/fulfill.js:238
Finding
Dry-run mode performs unnecessary CJ authentication and exposes the API key to avoidable network risk<![CDATA[ ## Vulnerability Details **File Location**: - `scripts/fulfill.js:238-243` - `scripts/fulfill.js:362-368` **Vulnerability Type**: Excessive network access and credential use in preview mode **Risk Level**: Medium ### Vulnerable Code ```js async function run() { console.log(`\n⚡ CJ Fulfillment Engine — ${DRY_RUN ? 'DRY RUN' : 'LIVE'}\n`); // Ensure CJ token is valid before processing await cjEnsureToken(); ``` The dry-run branch occurs only after authentication and order mapping: ```js if (DRY_RUN) { console.log(`\n 🔍 DRY RUN — would submit payload:`); console.log(' ' + JSON.stringify(payload, null, 2).split('\n').join('\n ')); submitted++; continue; } ``` ### Technical Analysis The program calls `cjEnsureToken()` unconditionally at the beginning of execution. If the cached access token is missing, expired, or close to expiry, that function sends the CJ API key to the configured CJ endpoint and rewrites the credential file with a new token. Dry-run mode does not create a CJ order. Consequently, CJ authentication is not necessary to preview the WooCommerce-to-CJ payload using the existing local selection map. This network access and credential transmission exceed the minimum privileges needed for the documented preview operation. The behavior is especially risky in combination with the unvalidated `baseUrl` configuration. A user may reasonably expect `--dry-run` to avoid external side effects, but the current implementation can transmit a secret and modify `cj-api.json`. ### Attack Path 1. The cached CJ access token is absent, expired, or within the ten-minute refresh window. 2. An attacker or compromised configuration changes `cj-api.json.baseUrl` to a hostile endpoint, or network traffic is sent through an unintended insecure endpoint. 3. The operator invokes `node scripts/fulfill.js --dry-run`, expecting a non-submitting preview. 4. Before reaching the dry-run branch, `cjEnsureToken()` posts the CJ API key to the configure ...[truncated 684 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not call `cjEnsureToken()` when `DRY_RUN` is true. 2. Structure the execution flow so authentication occurs immediately before the first live CJ request. 3. Define dry-run semantics explicitly: no supplier writes, no token refresh, and no credential-file modification. 4. If remote validation is desired, expose it as a separate explicit option such as `--validate-cj-access`. 5. Add tests that intercept network calls and verify that dry-run mode makes no requests to CJ. 6. Combine this change with strict CJ endpoint validation so that any necessary live authentication is limited to approved HTTPS destinations. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/fulfill.js:288
Finding
Customer personally identifiable information is printed and retained in plaintext logs<![CDATA[ ## Vulnerability Details **File Location**: - `scripts/fulfill.js:288-289` - `scripts/fulfill.js:350-365` - `scripts/fulfill.js:380-389` - `scripts/fulfill.js:35-44` **Vulnerability Type**: Excessive plaintext logging of sensitive customer information **Risk Level**: Medium ### Vulnerable Code Customer identity and address information are printed during normal operation: ```js console.log(` Customer: ${mapped.shippingName} <${mapped.shippingEmail}>`); console.log(` Address: ${mapped.shippingAddress}, ${mapped.shippingCity}, ${mapped.shippingCountry} ${mapped.shippingZip}`); ``` The payload contains complete fulfillment details and is printed in dry-run mode: ```js const payload = { orderNumber: mapped.orderNumber, shippingZip: mapped.shippingZip, shippingCountry: mapped.shippingCountry, shippingCountryCode: mapped.shippingCountry, shippingProvince: mapped.shippingProvince, shippingCity: mapped.shippingCity, shippingAddress: mapped.shippingAddress, shippingPhone: mapped.shippingPhone, shippingCustomerName: mapped.shippingName, shippingEmail: mapped.shippingEmail, products: mapped.matched.map(p => ({ vid: p.vid, quantity: p.quantity })), }; if (DRY_RUN) { console.log(`\n 🔍 DRY RUN — would submit payload:`); console.log(' ' + JSON.stringify(payload, null, 2).split('\n').join('\n ')); submitted++; continue; } ``` The full payload is retained when CJ rejects an order: ```js } else { const errDetail = JSON.stringify(result).slice(0, 300); console.error(` ❌ CJ rejected: ${errDetail}`); appendLog({ orderId: mapped.orderId, status: 'failed', error: errDetail }); appendRejectionLog({ type: 'cj_api_rejection', orderId: mapped.orderId, reason: 'cj_api_returned_failure', evaluated: { payload, cjResponse: errDetail }, }); failed++; } ``` The rejection log is written without an explicit restrictive mode: ```js function appendRejectionLog(entry) { const existing = readJson(REJECTION_LOG_PATH) ...[truncated 2745 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove customer names, email addresses, phone numbers, and full addresses from default console output. 2. Log only a non-sensitive order identifier and processing status. 3. If diagnostic output is necessary, require an explicit option such as `--debug-sensitive-data` and display a clear warning. 4. Redact sensitive values in dry-run output, for example: - Mask email local parts. - Retain only the final few phone digits. - Omit street-address lines. - Show only country and a partially masked postal code. 5. Do not store the complete fulfillment payload in rejection logs. Retain only the order ID, CJ error code, timestamp, matched-item count, and a sanitized error summary. 6. Create logs with mode `0600`, and verify existing files are not group- or world-readable. 7. Replace read-modify-write JSON arrays with a controlled append-only format or protected logging service. 8. Add age-based retention and secure deletion appropriate to the store's privacy obligations. 9. Document all locally retained fields and ensure that backups and centralized logs follow the same access and retention controls. ]]>
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
  • 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
99% confidence
Finding
A description-behavior mismatch is a serious security and safety issue here because the skill is presented as order-fulfillment automation, but the analysis indicates the underlying code performs different actions such as SKU backfilling and rebuilding local mappings instead of the declared order workflow. When a skill that touches commerce systems behaves differently than advertised, operators may authorize it under false assumptions, leading to unintended data modification, bad inventory mappings, or incorrect downstream fulfillment decisions.

Ae1

High
Category
analysis-evasion
Content
tch, it's logged to the rejection log and the order is skipped. Fix by running `rebuild-mapping.js` or adding the entry manually.
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Known Vulnerable Dependency: axios==1.13.6 — 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
The lockfile pins axios 1.13.6, and the finding cites multiple published advisories including SSRF-related proxy bypass and prototype-pollution-assisted MITM/credential theft classes. In this skill, axios is the primary HTTP client used to fetch WooCommerce orders and submit them to CJ Dropshipping, so a vulnerable client library can directly affect authenticated outbound API traffic and any environment-based proxy behavior.

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
80% confidence
Finding
form-data 4.0.5 is reported vulnerable to CRLF injection through unescaped multipart field names/filenames. If this skill or future extensions construct multipart requests using untrusted order, product, or supplier-derived values, an attacker may be able to tamper with request structure or smuggle unintended headers/content to downstream services.

Known Vulnerable Dependency: axios==1.13.6 — 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
94% confidence
Finding
The package explicitly depends on axios 1.13.6, which the finding indicates is affected by multiple published advisories, including SSRF and prototype-pollution-related issues. This skill integrates with external WooCommerce and CJ Dropshipping endpoints, so a vulnerable HTTP client can materially increase risk of request manipulation, credential exposure, or abuse of network trust boundaries depending on how requests are constructed elsewhere in the skill.

Credential Access

High
Category
Privilege Escalation
Content
const exp = Number(cfg.tokenExpiry || 0);
  if (cfg.accessToken && exp && now < exp - 10 * 60 * 1000) return cfg.accessToken;

  console.log('  🔑 Refreshing CJ access token...');
  const baseUrl = (cfg.baseUrl || 'https://developers.cjdropshipping.com/api2.0/v1').replace(/\/$/, '');
  const res = await axios.post(`${baseUrl}/authentication/getAccessToken`, { apiKey: cfg.apiKey }, {
    headers: { 'Content-Type': 'application/json' }, timeout: 30000,
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
83% confidence
Finding
The skill declares it requires Node but does not define an explicit tool/permission scope, while the documented behavior clearly relies on environment variables and file-based credentials. In an automation that can place external fulfillment orders and update WooCommerce state, missing scope declarations weakens operator understanding and runtime guardrails, increasing the chance of unintended access or execution with broader privileges than expected.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill documentation does not prominently warn that running it may update live WooCommerce order statuses and place real CJ fulfillment orders, despite describing production-affecting actions. In this context, lack of an explicit warning makes accidental execution more dangerous because users may run the default command against live credentials without realizing it will create external orders and alter store records.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
This code persists the refreshed CJ access token and expiry back into ./cj-api.json, which is a sensitive file write involving credentials. While the file header mentions the JSON fields, there is no user-facing confirmation, warning, or runtime disclosure that authentication material will be stored on disk.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
The createOrder function transmits shipping address, phone, customer name, and email to an external CJ API endpoint. The code contains no confirmation prompt, user-facing log, or warning comment indicating that personal data will be sent to a third-party service.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The code posts order data to CJ Dropshipping, and the payload later includes name, address, phone, and email for the customer. While the header comment says orders are submitted to CJ, it does not explicitly warn that customer personal data will be transmitted to a third-party service, and there is no confirmation prompt before live submission.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The manifest describes a fulfillment skill that fetches processing orders, submits them to CJ Dropshipping, updates WooCommerce order status, and logs results. This script instead rebuilds product-to-supplier mappings and backfills product/variation SKUs in WooCommerce, which is catalog maintenance behavior rather than order fulfillment.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
The stated skill scope is order fulfillment and order-status updates, but these lines perform PUT requests to mutate WooCommerce product and variation SKUs. That is a materially different write capability than pushing orders to CJ and marking fulfillment progress.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The helper reads `consumerKey` and `consumerSecret` from `./woo-api.json` and uses them for authenticated API access, but there is no user-facing log, prompt, or warning beyond a brief developer comment. This is a sensitive-credential access path, and the file does not disclose that secret material will be read and used for network authentication.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The functions perform authenticated HTTP requests to retrieve and modify WooCommerce orders and notes, which can transmit business and customer-related data and make remote state changes. The file contains no confirmation prompt, user-facing logging, or warning text explaining these network and modification actions.

Known Vulnerable Dependency: follow-redirects==1.15.11 — 1 advisory(ies): CVE-2026-40895 (follow-redirects leaks Custom Authentication Headers to Cross-Domain Redirect Ta)

Low
Category
Supply Chain
Confidence
87% confidence
Finding
follow-redirects 1.15.11 is flagged for leaking custom authentication headers across cross-domain redirects. Because this skill likely makes authenticated requests to supplier and store APIs, a malicious or compromised upstream that triggers redirects could expose API keys or bearer tokens to an unintended domain.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"license": "ISC",
  "type": "commonjs",
  "dependencies": {
    "axios": "^1.13.6"
  }
}
Confidence
98% confidence
Finding
The dependency is specified with a caret range, which allows automatic installation of newer minor/patch releases instead of a fully pinned version. In an automation skill that submits orders to external APIs, this weakens supply-chain reproducibility and can introduce unexpected or malicious upstream changes without code review.

Static analysis

Detected: suspicious.env_credential_access, suspicious.exposed_secret_literal

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/cj-api.js:8

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/fulfill.js:18

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/rebuild-mapping.js:19

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/woo-api.js:8

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/fulfill.js:80

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/rebuild-mapping.js:40