Back to skill

Security audit

shopify-audit-pro

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed read-only Shopify auditing helper that handles sensitive store data but does not show hidden exfiltration or store-changing behavior.

Install only if you are comfortable granting read access to Shopify store, order, customer, inventory, theme, and reporting data. Use a Shopify app with only read_* scopes, keep secrets in the masked store or a mode-600 file, prefer --redact-pii, and treat bulk JSONL exports and audit logs as sensitive local files.

Vulnerability Patterns
  • 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
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (13)

Tainted flow: 'req' from os.environ.get (line 132, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/shopify_api.py (reported line 67)May include surrounding context.

python
"Accept": "application/json",
                 "User-Agent": USER_AGENT})
    try:
        with urllib.request.urlopen(req, timeout=30) as r:
            tok = json.load(r).get("access_token", "")
    except urllib.error.HTTPError as e:
        hint = (" The app and the store must belong to the same Shopify organization, "

Tainted flow: 'req' from os.environ.get (line 132, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/shopify_api.py (reported line 134)May include surrounding context.

python
for attempt in range(retries):
        req = urllib.request.Request(url, data=body, headers=headers, method="POST")
        try:
            with urllib.request.urlopen(req, timeout=60) as r:
                payload = json.load(r)
        except urllib.error.HTTPError as e:
            if e.code == 429:

Tainted flow: 'url' from os.environ.get (line 124, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/shopify_api.py (reported line 293)May include surrounding context.

python
die(f"{out} already exists; choose a new --out path (nothing downloaded).")
    fd = os.open(out, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
    total = 0
    with urllib.request.urlopen(urllib.request.Request(url, headers={"User-Agent": USER_AGENT}),
                                timeout=300) as r, os.fdopen(fd, "wb") as f:
        while True:
            chunk = r.read(1 << 20)

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The file-level documentation states that every command refuses mutations/subscriptions, but the code later exempts bulk-query and sends a mutation anyway. This kind of security-description mismatch is dangerous because users, reviewers, or orchestrators may grant broader trust to the skill than its actual behavior warrants.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill advertises significant capabilities via its described helper and commands (network access to Shopify and Google Cloud Storage, reading secrets, reading/writing local files, and likely shell/Python execution) but does not declare explicit tool scope such as permissions or allowed-tools. That mismatch weakens platform-level enforcement and reviewability, so a caller may grant broader execution than the skill contract visibly communicates.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
77% confidence
Finding

The skill description states that a token is used for API access and that an audit log records every call, indicating credentialed session use and persistent local state. Even though the text claims the token is not logged and the log is PII-free, any persisted session artifacts or call metadata on disk can become sensitive if host isolation or file permissions are weak.

Content

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

md
---
name: shopify-audit-pro
description: "Read-only Shopify store audits and reporting for OpenClaw. Safety first: it cannot write to your store (mutations are refused in code and the app holds read_* scopes only), your token goes only to your own *.myshopify.com store (no relay, no third-party plugin), secrets are never typed in chat, and a PII-free audit log records every call. Then the audit power: pre-opening store audits, catalog, shipping, theme, policy and discount checks, orders, customers, inventory, bulk exports and sales/ops briefs via the Admin GraphQL API. Need to make changes too? Use shopify-admin-pro."
metadata:
  {
    "openclaw":

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

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

md
## Setup (once)

1. Since 2026-01-01 Shopify no longer lets stores create legacy custom apps. Create the app in the
   **Dev Dashboard** (dev.shopify.com) **under the same Shopify organization that owns the store**.
   The client-credentials grant fails across organizations, so use the store's own org, not a separate
   partner org. Choose read-only scopes (`references/safety-and-scopes.md`), release a version, install it on

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

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

md
the store, then copy the Client ID and Client Secret from the app's Settings.
   Legacy apps created before 2026 still work: use their static `SHOPIFY_ACCESS_TOKEN`.
2. Store the secrets with the host's masked entry (the `secrets` tool), or, on a node host, create
   `~/.openclaw/secrets/shopify.env` yourself with hidden input and `chmod 600` it. The agent must
   never create that file with the secret value and must never ask for the secret in chat.
   Lines: `SHOPIFY_STORE_DOMAIN=my-store.myshopify.com`, `SHOPIFY_CLIENT_ID=...`, `SHOPIFY_CLIENT_SECRET=...`.
3. Verify: `shopify scopes` (should list only `read_*` handles) and `shopify versions`.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/shopify_api.py (reported line 90)May include surrounding context.

python
the store, then copy the Client ID and Client Secret from the app's Settings.
   Legacy apps created before 2026 still work: use their static `SHOPIFY_ACCESS_TOKEN`.
2. Store the secrets with the host's masked entry (the `secrets` tool), or, on a node host, create
   `~/.openclaw/secrets/shopify.env` yourself with hidden input and `chmod 600` it. The agent must
   never create that file with the secret value and must never ask for the secret in chat.
   Lines: `SHOPIFY_STORE_DOMAIN=my-store.myshopify.com`, `SHOPIFY_CLIENT_ID=...`, `SHOPIFY_CLIENT_SECRET=...`.
3. Verify: `shopify scopes` (should list only `read_*` handles) and `shopify versions`.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/shopify_api.py (reported line 6)May include surrounding context.

python
Talks ONLY to https://<your-store>.myshopify.com (domain is regex-validated),
plus one Shopify-hosted download URL for `bulk-query` results (host-checked).
It cannot write to a store: every command refuses GraphQL mutations/subscriptions,
and the recommended app only holds read_* scopes.

Env (or ~/.openclaw/secrets/shopify.env, mode 600):

Dynamic Request Target

Medium
Category
Server-Side Request Forgery
Confidence
60% confidence
Finding

Request target host is built from a dynamic or untrusted value. If the host is attacker-influenced, this enables SSRF to arbitrary internal or metadata endpoints.

Content

Scanner excerpt · scripts/shopify_api.py (reported line 61)May include surrounding context.

python
"client_id": client_id,
        "client_secret": client_secret,
    }).encode()
    req = urllib.request.Request(
        f"https://{domain}/admin/oauth/access_token", data=body, method="POST",
        headers={"Content-Type": "application/x-www-form-urlencoded",
                 "Accept": "application/json",

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill claims it cannot write because it refuses mutations, yet bulk-query deliberately invokes the GraphQL mutation bulkOperationRunQuery. Even if Shopify treats this as starting a read/export job rather than mutating store business data, the implementation contradicts the advertised safety boundary and may mislead operators or automated policy systems that rely on the no-mutation guarantee.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

This code file performs a safety-relevant file write by downloading Shopify bulk export data to disk, which may contain sensitive business or customer data. Although the implementation documents file handling details, the user-facing CLI help does not explicitly warn that bulk exports will be persisted locally and may contain sensitive data.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.