Back to skill

Security audit

shopify-admin-pro

Security checks for vulnerabilities and agentic risk

Overview

This is a powerful but coherent Shopify admin skill that clearly discloses its store access, credential handling, write confirmations, and audit logging.

Install only for a Shopify store you administer. Start with read-only Shopify scopes, add write scopes only for specific tasks, use a development store for first writes, and read previews carefully before confirming refunds, cancellations, theme publishing, policy changes, or bulk edits.

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

Tainted flow: 'req' from os.environ.get (line 145, 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 79)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 145, 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 147)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 137, 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 394)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)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding

The skill describes substantial capabilities including network access to Shopify and Google Cloud Storage, local file reads/writes, environment/secret access, and execution of a Python helper, but it does not declare an explicit tool/permission scope. That creates a governance gap: the runtime may grant broader powers than reviewers or users can easily verify, increasing the chance of unintended data access or destructive admin actions.

Content

No source excerpt is available for this finding.

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 3)May include surrounding context.

md
---
name: shopify-admin-pro
description: "Guarded Shopify Admin for OpenClaw: everything shopify-audit-pro does, plus acting as the store admin. Safety first: nothing is written without a preview and your explicit confirmation, delete/refund/cancel/publish and other high-risk actions need a second exact confirmation, a failed write is never blindly retried, 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 admin power: audits and reports, plus products, inventory, orders, fulfillment, refunds, discounts, customers, themes and bulk edits via the Admin GraphQL API. Only the scopes you grant are usable."
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 76)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 scopes by tier (`references/safety-and-scopes.md`), release a version, install it on the store, then

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 85)May include surrounding context.

md
2. **Start read-only.** Add `write_*` scopes only when a task needs them: release a new app version and approve
   it on the store. Prefer a separate app per tier so a read-only task cannot write even if something goes wrong.
3. 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=...`.
4. Verify: `shopify scopes` 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 102)May include surrounding context.

python
2. **Start read-only.** Add `write_*` scopes only when a task needs them: release a new app version and approve
   it on the store. Prefer a separate app per tier so a read-only task cannot write even if something goes wrong.
3. 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=...`.
4. Verify: `shopify scopes` 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 · references/graphql-cookbook.md (reported line 14)May include surrounding context.

md
not exercised against real rows.
- Part 2 (writes) has NOT been executed against a live store. Its shapes were checked against the live schema of
  API 2026-10 by introspection: every mutation name, argument, input field and payload field used below exists
  (this caught two stale shapes, now fixed). That proves the shapes are valid, not that a write behaves as
  expected. The `mutate` guards were tested against a stubbed network only. Introspect the input type for your
  API version, preview, get approval, then confirm.

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 · references/safety-and-scopes.md (reported line 18)May include surrounding context.

md
| 5 Storefront content | `write_themes write_content write_online_store_navigation write_online_store_pages` | redirects, menus, pages, theme edits |
| Extras | `write_discounts`, `write_customers`, `read_all_orders` (history over 60 days, needs Shopify approval) | add per task |

The write scope names above were checked against Shopify's access-scope documentation. They have NOT been granted
to a live app yet: add them in the Dev Dashboard when you configure the app, and confirm with `shopify scopes`.
Billing and subscription mutations belong to app developers; a store-operations token should never need them.

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 73)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",

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 292)May include surrounding context.

python
elif c == ")":
            pd -= 1
        elif bd == 1 and pd == 0 and s.startswith("...", j):
            die("Fragment spreads at the mutation root are not allowed; write the mutation fields directly.")
        elif bd == 1 and pd == 0 and (c.isalpha() or c == "_") and not (s[j - 1].isalnum() or s[j - 1] in "_@."):
            k = j
            while k < len(s) and (s[k].isalnum() or s[k] == "_"):

Static analysis

No suspicious patterns detected.