Request input selects Admin API shop context
Detects request-derived shop values passed into unauthenticated.admin(...), allowing callers to select another shop's Admin API context.
| Detail | Value |
|---|---|
| Check ID | REQUEST_CONTROLLED_ADMIN_CONTEXT |
| Severity | High |
| How it runs | Deterministic scan and agent review |
Anchor to Vulnerable exampleVulnerable example
app/routes/app.sync.tsx
import type { ActionFunctionArgs } from "react-router";
import { authenticate, unauthenticated } from "../shopify.server";
export const action = async ({ request }: ActionFunctionArgs) => {
await authenticate.admin(request);
const formData = await request.formData();
const requestedShop = String(formData.get("shop"));
// Authenticating and then handing a request value to
// unauthenticated.admin lets the caller pick which shop's Admin
// API context they get.
const { admin } = await unauthenticated.admin(requestedShop);
const response = await admin.graphql(
`{ products(first: 10) { nodes { id } } }`,
);
const { data } = await response.json();
return { products: data.products.nodes };
};
Anchor to How to fixHow to fix
Use the Admin API context returned by authenticate.admin(request). Never pass form, JSON, query, or route input into unauthenticated.admin(...).
For more detail, see Online access tokens.
app/routes/app.sync.tsx
import type { ActionFunctionArgs } from "react-router";
import { authenticate } from "../shopify.server";
export const action = async ({ request }: ActionFunctionArgs) => {
// The Admin context comes back bound to the authenticated
// session's shop, so no request value can redirect it.
const { admin } = await authenticate.admin(request);
const response = await admin.graphql(
`{ products(first: 10) { nodes { id } } }`,
);
const { data } = await response.json();
return { products: data.products.nodes };
};
shopify app security check, the command that runs this check- All app security checks
Was this page helpful?