Back to skill

Security audit

Openclaw Shopify Manager

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its Shopify-connector purpose, but it handles powerful Shopify credentials and default permissions in ways users should review before installing.

Review this before installing on a real Shopify store. Start with read-only scopes, remove read_orders unless needed, protect the runtime directory and .env with owner-only permissions, validate the shop domain before OAuth exchange, add webhook size/rate limits, and require explicit confirmation for every product or content write.

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 (5)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/shopify-connector.mjs:189
Finding

Unvalidated Shop Host Can Receive Shopify OAuth Credentials

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/shopify-connector.mjs:79
Finding

Shopify Secrets and Tokens Are Written Without Restrictive File Permissions

Content
View full analysis
value !== undefined && value !== null) .map(([key, value]) => `${key}=${String(value)}`) ]; fs.writeFileSync(file, lines.join('\n') + '\n'); } function updateEnvFile(file, patch) { const current = parseEnvFile(file); writeEnvFile(file, { ...current, ...patch }); } ``` The setup helper has the same issue when initially creating `.env`: ```js function writeEnvFile(file, values) { const lines = [ '# OpenClaw Shopify Manager runtime secrets', ...Object.entries(values).map(([key, value]) => `${key}=${value ?? ''}`) ]; fs.writeFileSync(file, lines.join('\n') + '\n'); } ``` ### Technical Analysis The `.env` file stores `SHOPIFY_API_SECRET` and, after OAuth, `SHOPIFY_ACCESS_TOKEN`. Both creation paths call `fs.writeFileSync()` without an explicit `0600` mode. Initial permissions therefore depend on the process umask. With a common `022` umask, a newly created file can be readable by other local users. The generated `.gitignore` reduces accidental source-control disclosure but does not enforce operating-system access controls. Existing files are also not checked or corrected before secrets are read or rewritten. ### Attack Path 1. A user runs guided setup, creating the runtime `.env`. 2. The host's umask permits group or world read access. 3. OAuth completes and the offline Shopify token is written to the same file. 4. Another local account or compromised process reads the file ...[truncated 397 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/shopify-connector.mjs:349
Finding

Unauthenticated Webhook Requests Are Buffered Without a Size Limit

Content
View full analysis
{ const chunks = []; req.on('data', (chunk) => chunks.push(chunk)); req.on('end', () => resolve(Buffer.concat(chunks))); req.on('error', reject); }); } ``` The public webhook handler authenticates the request only after the full body has been buffered: ```js if (req.method === 'POST' && url.pathname.startsWith(webhookPath)) { const rawBody = await readBody(req); const receivedHmac = req.headers['x-shopify-hmac-sha256']; if (!webhookHmacIsValid(rawBody, config.apiSecret, receivedHmac)) { appendLog(webhookLogPath, JSON.stringify({ at: new Date().toISOString(), event: 'invalid-hmac', path: url.pathname })); return send(res, 401, 'Invalid webhook HMAC'); } ``` ### Technical Analysis Every POST request whose path starts with `/shopify/webhooks` is read entirely into memory before HMAC verification. There is no maximum byte count, content-length validation, streaming limit, request timeout, or concurrency limit. Because the documented deployment model exposes this route through public HTTPS ingress, an attacker does not need a valid Shopify HMAC to consume memory. Multiple concurrent oversized requests can increase memory use until the Node.js process becomes unresponsive or terminates. ### Attack Path 1. The operator exposes the connector through Tailscale Funnel or another public reverse proxy. 2. An unauthenticated attacker opens one or more POST requests to `/shopify/webhooks`. 3. The attacker streams large request bodies or keeps many bodies in progress concurrently. 4. The connector retains all received chunks in memory. 5. Memory exhaustion or event-loop/resource pressure causes connector failure. 6. If systemd is enabled with `Restart=always`, repea ...[truncated 337 chars]
Remediation
View remediation
{ const chunks = []; let size = 0; req.on('data', (chunk) => { size += chunk.length; if (size > maxBytes) { reject(Object.assign(new Error('Request body too large'), { status: 413 })); req.destroy(); return; } chunks.push(chunk); }); req.on('end', () => resolve(Buffer.concat(chunks, size))); req.on('error', reject); }); } ``` ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/setup-runtime.mjs:124
Finding

Default OAuth Scopes Exceed the Connector's Implemented Requirements

Content
View full analysis
x.trim()).filter(Boolean); } ``` The example configuration establishes the same broad defaults: ```json "scopes": [ "read_products", "write_products", "read_content", "write_content", "read_orders" ], ``` ### Technical Analysis The setup process requests product and content write scopes by default, even though the Skill describes a read-first posture. It also requests `read_orders`, although the connector implements no order-related command. Consequently, a user who accepts the defaults grants permissions beyond the minimum needed for initial setup and read verification. Write scopes support declared optional mutation functionality, but they should be requested only when that functionality is explicitly enabled. `read_orders` is not justified by any reviewed implementation path. ### Attack Path 1. A user runs guided setup and accepts the default scope list. 2. The generated OAuth URL requests product writes, content writes, and order reads. 3. The user installs the application, issuing an offline token with all requested scopes. 4. The token is later exposed through local compromise, backup disclosure, or another application flaw. 5. The attacker uses permissions that were unnecessary for the user's initial task, including store mutations or order-data access. ### Impact Assessment The unnecessary `read_orders` scope expands exposure to commercially and potentially personally sensitive order information. Default write scopes permit product and content mo ...[truncated 136 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
scripts/shopify-connector.mjs:168
Finding

OAuth State Is Neither Expired Nor Consumed After Successful Use

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (44)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill description centers on Shopify/OpenClaw integration, but the detected behavior includes Tailscale inspection and callback exposure support that are not clearly reflected as primary capabilities in the declaration. This mismatch can mislead operators about what the skill will do on the host and network, making it easier for unexpected networking behavior or environment discovery to occur without informed approval.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 30)May include surrounding context.

md
2. Use `scripts/setup-runtime.mjs guided-setup` to create the runtime directory, config files, `.env`, logs/state folders, and optional systemd unit template.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 35)May include surrounding context.

md
2. Use `scripts/setup-runtime.mjs guided-setup` to create the runtime directory, config files, `.env`, logs/state folders, and optional systemd unit template.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 50)May include surrounding context.

md
2. Use `scripts/setup-runtime.mjs guided-setup` to create the runtime directory, config files, `.env`, logs/state folders, and optional systemd unit template.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 88)May include surrounding context.

md
2. Use `scripts/setup-runtime.mjs guided-setup` to create the runtime directory, config files, `.env`, logs/state folders, and optional systemd unit template.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 34)May include surrounding context.

md
6. Use `scripts/shopify-connector.mjs` for auth URL generation, callback handling, webhook validation, and Shopify API calls.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 54)May include surrounding context.

md
6. Use `scripts/shopify-connector.mjs` for auth URL generation, callback handling, webhook validation, and Shopify API calls.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 90)May include surrounding context.

md
6. Use `scripts/shopify-connector.mjs` for auth URL generation, callback handling, webhook validation, and Shopify API calls.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · assets/shopify-connector.service.txt (reported line 11)May include surrounding context.

text
WorkingDirectory=%h/oc/shopify-runtime
Environment=SHOPIFY_RUNTIME_ROOT=%h/oc/shopify-runtime
Environment=SHOPIFY_CONFIG_PATH=%h/oc/shopify-runtime/config.json
Environment=SHOPIFY_ENV_PATH=%h/oc/shopify-runtime/.env
ExecStart=/usr/bin/env node %h/oc/shopify-runtime/shopify-connector.mjs run-server
Restart=always
RestartSec=3

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/install-host-runtime.sh (reported line 13)May include surrounding context.

sh
WorkingDirectory=%h/oc/shopify-runtime
Environment=SHOPIFY_RUNTIME_ROOT=%h/oc/shopify-runtime
Environment=SHOPIFY_CONFIG_PATH=%h/oc/shopify-runtime/config.json
Environment=SHOPIFY_ENV_PATH=%h/oc/shopify-runtime/.env
ExecStart=/usr/bin/env node %h/oc/shopify-runtime/shopify-connector.mjs run-server
Restart=always
RestartSec=3

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/setup-runtime.mjs (reported line 251)May include surrounding context.

js
WorkingDirectory=%h/oc/shopify-runtime
Environment=SHOPIFY_RUNTIME_ROOT=%h/oc/shopify-runtime
Environment=SHOPIFY_CONFIG_PATH=%h/oc/shopify-runtime/config.json
Environment=SHOPIFY_ENV_PATH=%h/oc/shopify-runtime/.env
ExecStart=/usr/bin/env node %h/oc/shopify-runtime/shopify-connector.mjs run-server
Restart=always
RestartSec=3

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/setup-runtime.mjs (reported line 252)May include surrounding context.

js
WorkingDirectory=%h/oc/shopify-runtime
Environment=SHOPIFY_RUNTIME_ROOT=%h/oc/shopify-runtime
Environment=SHOPIFY_CONFIG_PATH=%h/oc/shopify-runtime/config.json
Environment=SHOPIFY_ENV_PATH=%h/oc/shopify-runtime/.env
ExecStart=/usr/bin/env node %h/oc/shopify-runtime/shopify-connector.mjs run-server
Restart=always
RestartSec=3

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/setup-runtime.mjs (reported line 333)May include surrounding context.

js
WorkingDirectory=%h/oc/shopify-runtime
Environment=SHOPIFY_RUNTIME_ROOT=%h/oc/shopify-runtime
Environment=SHOPIFY_CONFIG_PATH=%h/oc/shopify-runtime/config.json
Environment=SHOPIFY_ENV_PATH=%h/oc/shopify-runtime/.env
ExecStart=/usr/bin/env node %h/oc/shopify-runtime/shopify-connector.mjs run-server
Restart=always
RestartSec=3

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/setup-runtime.mjs (reported line 340)May include surrounding context.

js
WorkingDirectory=%h/oc/shopify-runtime
Environment=SHOPIFY_RUNTIME_ROOT=%h/oc/shopify-runtime
Environment=SHOPIFY_CONFIG_PATH=%h/oc/shopify-runtime/config.json
Environment=SHOPIFY_ENV_PATH=%h/oc/shopify-runtime/.env
ExecStart=/usr/bin/env node %h/oc/shopify-runtime/shopify-connector.mjs run-server
Restart=always
RestartSec=3

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/setup-runtime.mjs (reported line 360)May include surrounding context.

js
WorkingDirectory=%h/oc/shopify-runtime
Environment=SHOPIFY_RUNTIME_ROOT=%h/oc/shopify-runtime
Environment=SHOPIFY_CONFIG_PATH=%h/oc/shopify-runtime/config.json
Environment=SHOPIFY_ENV_PATH=%h/oc/shopify-runtime/.env
ExecStart=/usr/bin/env node %h/oc/shopify-runtime/shopify-connector.mjs run-server
Restart=always
RestartSec=3

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/shopify-connector.mjs (reported line 128)May include surrounding context.

js
WorkingDirectory=%h/oc/shopify-runtime
Environment=SHOPIFY_RUNTIME_ROOT=%h/oc/shopify-runtime
Environment=SHOPIFY_CONFIG_PATH=%h/oc/shopify-runtime/config.json
Environment=SHOPIFY_ENV_PATH=%h/oc/shopify-runtime/.env
ExecStart=/usr/bin/env node %h/oc/shopify-runtime/shopify-connector.mjs run-server
Restart=always
RestartSec=3

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/security-and-behavior.md (reported line 8)May include surrounding context.

md
## What it does

- generates Shopify OAuth install URLs
- exchanges OAuth callback codes for Shopify offline access tokens
- validates Shopify callback and webhook HMAC signatures
- runs a small local HTTP server for `/healthz`, OAuth callback handling, and webhook receipt
- performs explicit Shopify Admin GraphQL reads and user-confirmed writes

Self-Modification

High
Category
Rogue Agent
Confidence
90% confidence
Finding

Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Content

Scanner excerpt · references/security-and-behavior.md (reported line 21)May include surrounding context.

md
- no browser automation
- no credential exfiltration or forwarding to third-party services
- no background tunneling binary, SOCKS proxy, VPN client, or reverse shell
- no self-update or self-modifying behavior

## Default exposure model

Self-Modification

High
Category
Rogue Agent
Confidence
90% confidence
Finding

Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Content

Scanner excerpt · references/security-and-behavior.md (reported line 21)May include surrounding context.

md
- no browser automation
- no credential exfiltration or forwarding to third-party services
- no background tunneling binary, SOCKS proxy, VPN client, or reverse shell
- no self-update or self-modifying behavior

## Default exposure model

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · scripts/setup-runtime.mjs (reported line 245)May include surrounding context.

js
async function getSecret(flag, question, fallback = '') {
    if (args[flag] !== undefined) return args[flag];
    if (!prompter) return fallback;
    return prompter.askSecret(question, fallback);
  }

  const tailscaleHostname = await getValue('tailscale-hostname', 'Tailscale/custom hostname for public HTTPS (omit https://, optional)', '');

Credential Access

High
Category
Privilege Escalation
Confidence
84% confidence
Finding

This script stores Shopify client credentials and later OAuth access tokens in a plaintext .env file under a predictable path in the user's home directory. While local secret storage is expected for this skill, plaintext storage without explicit permission hardening increases the risk of credential disclosure to other local users, backup systems, or accidentally permissive filesystems.

Content

Scanner excerpt · scripts/setup-runtime.mjs (reported line 273)May include surrounding context.

js
ensureDir(path.join(runtimeRoot, 'logs'));

  const configPath = path.join(runtimeRoot, 'config.json');
  const envPath = path.join(runtimeRoot, '.env');
  const connectorPath = path.join(runtimeRoot, 'shopify-connector.mjs');
  const servicePath = path.join(runtimeRoot, 'shopify-connector.service');

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/setup-runtime.mjs (reported line 193)May include surrounding context.

js
const stateDir = path.resolve(args['state-dir'] || process.env.SHOPIFY_STATE_DIR || path.join(runtimeRoot, 'state'));
const logsDir = path.resolve(args['logs-dir'] || process.env.SHOPIFY_LOGS_DIR || path.join(runtimeRoot, 'logs'));
const configPath = path.resolve(args.config || process.env.SHOPIFY_CONFIG_PATH || path.join(runtimeRoot, 'config.json'));
const envPath = path.resolve(args.env || process.env.SHOPIFY_ENV_PATH || path.join(runtimeRoot, '.env'));
const oauthStatePath = path.join(stateDir, 'oauth-state.json');
const installLogPath = path.join(logsDir, 'shopify-install.log');
const webhookLogPath = path.join(logsDir, 'shopify-webhooks.ndjson');

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/setup-runtime.mjs (reported line 213)May include surrounding context.

js
const stateDir = path.resolve(args['state-dir'] || process.env.SHOPIFY_STATE_DIR || path.join(runtimeRoot, 'state'));
const logsDir = path.resolve(args['logs-dir'] || process.env.SHOPIFY_LOGS_DIR || path.join(runtimeRoot, 'logs'));
const configPath = path.resolve(args.config || process.env.SHOPIFY_CONFIG_PATH || path.join(runtimeRoot, 'config.json'));
const envPath = path.resolve(args.env || process.env.SHOPIFY_ENV_PATH || path.join(runtimeRoot, '.env'));
const oauthStatePath = path.join(stateDir, 'oauth-state.json');
const installLogPath = path.join(logsDir, 'shopify-install.log');
const webhookLogPath = path.join(logsDir, 'shopify-webhooks.ndjson');

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/setup-runtime.mjs (reported line 264)May include surrounding context.

js
const stateDir = path.resolve(args['state-dir'] || process.env.SHOPIFY_STATE_DIR || path.join(runtimeRoot, 'state'));
const logsDir = path.resolve(args['logs-dir'] || process.env.SHOPIFY_LOGS_DIR || path.join(runtimeRoot, 'logs'));
const configPath = path.resolve(args.config || process.env.SHOPIFY_CONFIG_PATH || path.join(runtimeRoot, 'config.json'));
const envPath = path.resolve(args.env || process.env.SHOPIFY_ENV_PATH || path.join(runtimeRoot, '.env'));
const oauthStatePath = path.join(stateDir, 'oauth-state.json');
const installLogPath = path.join(logsDir, 'shopify-install.log');
const webhookLogPath = path.join(logsDir, 'shopify-webhooks.ndjson');

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/setup-runtime.mjs (reported line 333)May include surrounding context.

js
const stateDir = path.resolve(args['state-dir'] || process.env.SHOPIFY_STATE_DIR || path.join(runtimeRoot, 'state'));
const logsDir = path.resolve(args['logs-dir'] || process.env.SHOPIFY_LOGS_DIR || path.join(runtimeRoot, 'logs'));
const configPath = path.resolve(args.config || process.env.SHOPIFY_CONFIG_PATH || path.join(runtimeRoot, 'config.json'));
const envPath = path.resolve(args.env || process.env.SHOPIFY_ENV_PATH || path.join(runtimeRoot, '.env'));
const oauthStatePath = path.join(stateDir, 'oauth-state.json');
const installLogPath = path.join(logsDir, 'shopify-install.log');
const webhookLogPath = path.join(logsDir, 'shopify-webhooks.ndjson');

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access, suspicious.exposed_secret_literal (+1 more)

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/setup-runtime.mjs:150

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/shopify-connector.mjs:28

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/setup-runtime.mjs:291

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/shopify-connector.mjs:108

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

Warn
Code
suspicious.potential_exfiltration
Location
scripts/shopify-connector.mjs:377