---
title: App security checks
description: >-
  Every security check that `shopify app security check` runs against an app,
  with its severity and a worked example of the fix.
source_url:
  html: 'https://shopify.dev/docs/apps/build/security/app-security-checks'
  md: 'https://shopify.dev/docs/apps/build/security/app-security-checks.md'
---

# App security checks

[`shopify app security check`](https://shopify.dev/docs/api/shopify-cli/app/app-security-check) checks your app for the security issues listed under [Checks](#checks). Each issue has a corresponding page in this section, keyed to the stable ID reported in the findings, such as `COMMITTED_SECRET`.

### How it works

The command checks your app in two ways:

* **Deterministic scan.** Fixed code inspections that the command runs locally, for questions with an exact answer, such as whether an API version is end-of-life.
* **Agent review.** Checks that can't be determined by code inspection alone, such as whether a database query is scoped to a single shop, which the command hands to your coding agent as instructions to investigate.

Some checks use both.

### Check your app with your coding agent (recommended)

The recommended way to check your app is through your coding agent with Shopify AI Toolkit installed.

1. Install [Shopify AI Toolkit](https://shopify.dev/docs/apps/build/ai-toolkit) for your coding agent.

2. In your app directory, ask your agent to check your app:

   ## Example prompt

   /shopify Check my app for security vulnerabilities

3. Review the results with your agent. It runs the deterministic scan, investigates the checks that can't be determined by code inspection alone, records its findings, and walks you through the combined results.

4. Fix the issues it reports. Each finding names a check ID, so you can reference the details on each [check](#checks), including how to fix it.

5. Check your app again on every release.

### Check your app with the CLI command

Use this option if your coding agent doesn't have the AI Toolkit.

1. In your app directory, run [`shopify app security check`](https://shopify.dev/docs/api/shopify-cli/app/app-security-check). The command runs the deterministic scan on its own, with no agent involved, and prints its findings. It then offers to copy the agent review instructions to your clipboard or print them. In a non-interactive terminal, pass `--print-instructions` to print them.
2. Give the instructions to your coding agent. Your agent follows them to investigate your code, record its findings with [`shopify app security record`](https://shopify.dev/docs/api/shopify-cli/app/app-security-record), and show the combined results with [`shopify app security review`](https://shopify.dev/docs/api/shopify-cli/app/app-security-review).
3. Fix the issues that the findings report, referencing the details on each [check](#checks).
4. Run the check again on every release.

Without a coding agent, the deterministic scan is all you get. You don't run `shopify app security record` yourself: it only accepts the findings document that your agent writes.

Results are stored under `.shopify/app-security/`, in a separate folder for each app configuration. To delete the results, run [`shopify app security clean`](https://shopify.dev/docs/api/shopify-cli/app/app-security-clean).

### What gets scanned

The command scans the files in your app directory, which is the folder that contains your selected `shopify.app*.toml` file, or `--path` when you pass `--without-app-config`.

Files that Git ignores are skipped, and files that Git tracks are always scanned. Use `--no-git-ignore` to scan ignored files too.

Use the `--exclude` flag of [`app security check`](https://shopify.dev/docs/api/shopify-cli/app/app-security-check) to skip extra paths. Each value is a glob relative to the working directory. Repeat the flag to add globs. An exclusion can't remove the selected app configuration file. For example, to skip generated code anywhere in the app and everything under `test/fixtures`:

## Terminal

```terminal
shopify app security check --exclude '**/generated' --exclude 'test/fixtures/**'
```

Some deterministic checks support only apps built with the [React Router app template](https://github.com/Shopify/shopify-app-template-react-router). For apps built with other frameworks, those checks are left to agent review.

### Severity

Each check carries a severity, and the tables under [Checks](#checks) group the checks by it. [`app security check`](https://shopify.dev/docs/api/shopify-cli/app/app-security-check) explains what each severity asks of you.

### Examples

The pages linked in the tables under [Checks](#checks) show how to fix each issue, and most of them also show an example of code that fails the check. Examples are written against the [React Router app template](https://github.com/Shopify/shopify-app-template-react-router), so the imports and route conventions match a freshly generated app, and a failing example is the pattern the check actually reports.

A check with `Applies to` set is meant for apps with that capability, such as a theme app extension or an app proxy. The deterministic scan skips a check whose capability is absent, and the agent review instructions tell your coding agent not to apply a check to a capability the app doesn't have.

[CLI command - shopify app security check\
\
](https://shopify.dev/docs/api/shopify-cli/app/app-security-check)

[Run these checks against your app, then record agent findings and review the combined results.](https://shopify.dev/docs/api/shopify-cli/app/app-security-check)

***

## Checks

Each check below links to its own page, with an explanation of the issue and how to fix it.

### High severity

| Check | ID |
| - | - |
| [Active upload or privileged preview may execute untrusted content](https://shopify.dev/docs/apps/build/security/app-security-checks/active-uploads-and-privileged-previews) | `ACTIVE_UPLOADS_AND_PRIVILEGED_PREVIEWS` |
| [App proxy Liquid injection risk](https://shopify.dev/docs/apps/build/security/app-security-checks/app-proxy-liquid-injection) | `APP_PROXY_LIQUID_INJECTION` |
| [App proxy request trusted without signature verification](https://shopify.dev/docs/apps/build/security/app-security-checks/app-proxy-unverified-signature) | `APP_PROXY_UNVERIFIED_SIGNATURE` |
| [Authorization check may be missing](https://shopify.dev/docs/apps/build/security/app-security-checks/missing-authorization-check) | `MISSING_AUTHORIZATION_CHECK` |
| [Committed secret or environment file](https://shopify.dev/docs/apps/build/security/app-security-checks/committed-secret) | `COMMITTED_SECRET` |
| [Config write trusts shop from request input](https://shopify.dev/docs/apps/build/security/app-security-checks/unscoped-shop-config-write) | `UNSCOPED_SHOP_CONFIG_WRITE` |
| [Configured callback URL is not HTTPS](https://shopify.dev/docs/apps/build/security/app-security-checks/insecure-webhook-url) | `INSECURE_WEBHOOK_URL` |
| [Credential reaches a log sink](https://shopify.dev/docs/apps/build/security/app-security-checks/credential-log-leakage) | `CREDENTIAL_LOG_LEAKAGE` |
| [Credential reaches the client browser](https://shopify.dev/docs/apps/build/security/app-security-checks/credential-browser-leakage) | `CREDENTIAL_BROWSER_LEAKAGE` |
| [Cross-site scripting risk](https://shopify.dev/docs/apps/build/security/app-security-checks/cross-site-scripting) | `CROSS_SITE_SCRIPTING` |
| [Database query may not be scoped by shop](https://shopify.dev/docs/apps/build/security/app-security-checks/missing-tenant-isolation) | `MISSING_TENANT_ISOLATION` |
| [Embedded app frame-ancestors uses a wildcard](https://shopify.dev/docs/apps/build/security/app-security-checks/static-frame-ancestors) | `STATIC_FRAME_ANCESTORS` |
| [Metafield write using offline token context](https://shopify.dev/docs/apps/build/security/app-security-checks/metafield-offline-token) | `METAFIELD_OFFLINE_TOKEN` |
| [OAuth scope may be over-requested](https://shopify.dev/docs/apps/build/security/app-security-checks/scope-over-request) | `SCOPE_OVER_REQUEST` |
| [Query scoped by shop value taken from request input](https://shopify.dev/docs/apps/build/security/app-security-checks/request-derived-shop-scope) | `REQUEST_DERIVED_SHOP_SCOPE` |
| [Request input selects Admin API shop context](https://shopify.dev/docs/apps/build/security/app-security-checks/request-controlled-admin-context) | `REQUEST_CONTROLLED_ADMIN_CONTEXT` |
| [Route handler lacks recognized auth verification](https://shopify.dev/docs/apps/build/security/app-security-checks/unauthenticated-endpoint) | `UNAUTHENTICATED_ENDPOINT` |
| [ScriptTag URL may be request controlled](https://shopify.dev/docs/apps/build/security/app-security-checks/script-tag-url-injection) | `SCRIPT_TAG_URL_INJECTION` |
| [Server-side request forgery risk](https://shopify.dev/docs/apps/build/security/app-security-checks/ssrf-request-forgery) | `SSRF_REQUEST_FORGERY` |
| [Session lifecycle or replay control may be missing](https://shopify.dev/docs/apps/build/security/app-security-checks/session-lifecycle-and-replay) | `SESSION_LIFECYCLE_AND_REPLAY` |
| [Shop domain validation regex is not anchored](https://shopify.dev/docs/apps/build/security/app-security-checks/weak-shop-validation) | `WEAK_SHOP_VALIDATION` |
| [SQL injection risk](https://shopify.dev/docs/apps/build/security/app-security-checks/sql-injection) | `SQL_INJECTION` |
| [Text setting may be rendered as unsafe HTML](https://shopify.dev/docs/apps/build/security/app-security-checks/text-setting-html-smuggling) | `TEXT_SETTING_HTML_SMUGGLING` |
| [Theme extension cross-site scripting risk](https://shopify.dev/docs/apps/build/security/app-security-checks/theme-extension-xss) | `THEME_EXTENSION_XSS` |
| [Unsafe HTML assignment](https://shopify.dev/docs/apps/build/security/app-security-checks/unsafe-innerhtml) | `UNSAFE_INNERHTML` |

### Medium severity

| Check | ID |
| - | - |
| [Cross-site request forgery protection may be missing](https://shopify.dev/docs/apps/build/security/app-security-checks/csrf-missing-protection) | `CSRF_MISSING_PROTECTION` |
| [Data access may exceed the current request](https://shopify.dev/docs/apps/build/security/app-security-checks/overbroad-data-access) | `OVERBROAD_DATA_ACCESS` |
| [Deprecated ScriptTag capability](https://shopify.dev/docs/apps/build/security/app-security-checks/deprecated-script-tag-scope) | `DEPRECATED_SCRIPT_TAG_SCOPE` |
| [Embedded app missing frame-ancestors CSP directive](https://shopify.dev/docs/apps/build/security/app-security-checks/missing-embedded-csp) | `MISSING_EMBEDDED_CSP` |
| [Expiring offline access tokens explicitly disabled](https://shopify.dev/docs/apps/build/security/app-security-checks/expiring-offline-token) | `EXPIRING_OFFLINE_TOKEN` |
| [Missing mandatory GDPR compliance webhooks](https://shopify.dev/docs/apps/build/security/app-security-checks/missing-compliance-webhooks) | `MISSING_COMPLIANCE_WEBHOOKS` |
| [Open redirect in auth callback](https://shopify.dev/docs/apps/build/security/app-security-checks/open-redirect) | `OPEN_REDIRECT` |
| [Unsafe Liquid metafield or setting output](https://shopify.dev/docs/apps/build/security/app-security-checks/liquid-unsafe-render) | `LIQUID_UNSAFE_RENDER` |
| [Vulnerable dependency may be reachable in app code](https://shopify.dev/docs/apps/build/security/app-security-checks/dependency-reachability) | `DEPENDENCY_REACHABILITY` |

### Low severity

| Check | ID |
| - | - |
| [End-of-life API version](https://shopify.dev/docs/apps/build/security/app-security-checks/eol-api-version) | `EOL_API_VERSION` |
| [Repository-level dependency management configuration not detected](https://shopify.dev/docs/apps/build/security/app-security-checks/missing-dependency-security-automation) | `MISSING_DEPENDENCY_SECURITY_AUTOMATION` |

***
