T09 · Insecure Skill Coding Practices
- Location
lib/shopify-provider.mjs:27- Finding
Shopify Client Credentials Can Be Sent to an Attacker-Controlled Host
- Content
View full analysis
{ const storeDomain = readString(connection.storeDomain) const clientId = readString(connection.clientId) const apiKey = readString(connection.apiKey) const apiVersion = readString(connection.apiVersion) if (!storeDomain || !clientId || !apiKey) { return undefined } return { storeDomain, clientId, apiKey, apiVersion, } } const getShopifyAccessToken = async (connection, signal) => { const response = await fetch(`https://${connection.storeDomain}/admin/oauth/access_token`, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", }, body: new URLSearchParams({ grant_type: "client_credentials", client_id: connection.clientId, client_secret: connection.apiKey, }).toString(), signal, }) ``` ```js validateConnection(connection) { return normalizeConnection(connection) ? { ok: true } : { ok: false, reason: "Set SHOPIFY_STORE_DOMAIN, SHOPIFY_CLIENT_ID, and configure this skill's apiKey so OpenClaw can inject SHOPIFY_CLIENT_SECRET.", } }, ``` ### Technical Analysis The Skill declares that `SHOPIFY_STORE_DOMAIN` should identify a Shopify `*.myshopify.com` store. However, `normalizeConnection()` and `validateConnection()` only require the value to be a non-empty string. They do not verify that it is a Shopify-controlled hostname. `getShopifyAccessToken()` interpolates this unvalidated value directly into the token endpoint and sends both `SHOPIFY_CLIENT_ID` and `SHOPIFY_CLIENT_SECRET` in the request body. HTTPS protects the request in transit, but it does not ensure that the destination is Shopify. If configuration is m ...[truncated 1641 chars]- Remediation
View remediation
{ const hostname = readString(value)?.toLowerCase() if ( !hostname || hostname.includes("/") || hostname.includes("@") || hostname.includes(":") || !/^[a-z0-9][a-z0-9-]*\.myshopify\.com$/u.test(hostname) ) { return undefined } return hostname } ``` For stronger control, validate the normalized hostname against the exact store domain configured by a trusted administrator. ]]>
