Gateway keys are selected by environment, live keys never appear outside production and test keys never in production, no literal fallback exists next to an environment read, and a rotation-capable source or a documented rotation practice is in place. No AI engine is involved: the verdict is reproducible from the source alone.
Inputs
Code, configuration and documentation files of the checkout (JavaScript, TypeScript, Python, PHP, .env, YAML, TOML, JSON, Terraform, INI, Markdown), tests included, that reference a gateway or a gateway key variable. Code is read as tokens (a key in a comment never counts; strings do); configuration and documentation as text. The environment of a file is read from its path and name (prod/production/live versus dev/test/staging/sandbox/local/example/preview; the last environment word wins). Environment reads of gateway key variables are found as expressions: process.env.X, import.meta.env.X, settings.X, os.environ['X'], os.environ.get('X', default), os.getenv, env('X', default), getenv, $_ENV['X'], config('services.<gateway>.secret', default). Paths listed in excludes (test, spec and tmp files, __tests__, __mocks__, fixtures, test, tests and audit-scripts folders) are never analysed. Nothing of the checkout is executed.
Decision rule
Not applicable when no gateway is referenced, or when a gateway is referenced but no gateway key variable, environment read or key literal is present (nothing to scope or rotate). FAIL (HIGH, with the line) when a live key (sk_live_, rk_live_, rzp_live_, pdl_live_, a Mercado Pago APP_USR token…) appears in a file scoped to a non-production environment, when a test or sandbox key appears in a file scoped to production, or when a literal of 12 characters or more follows an environment read of a gateway key variable through ||, ??, or, ?: or the default argument of the accessor (placeholders such as xxxx, changeme, example, ${…} never count; a fixture-looking value such as sk_test_ is LOW, informative). A live key in a configuration file scoped to no environment is MEDIUM. MEDIUM (blocks in this Extended suite) when gateway key variables are read and neither an environment switch exists in the code (NODE_ENV, APP_ENV, SST_STAGE, stage, a production flag, app()->environment()) nor per-environment configuration files each hold the variables, and when no rotation-capable source or rotation signal exists anywhere (a secret store — Secrets Manager, SSM, Vault, Doppler, 1Password, Infisical, Key Vault, SST secrets, Kubernetes secret references —, a rotation schedule, a key version or dated key id, rotation documentation). PASS otherwise; the summary names the scoping evidence and the rotation evidence. Not judged by this check (declared): whether the rotation actually happens and how often, a key assigned from a constant defined elsewhere, a live key literal assigned directly to a variable (secrets-scan and gateway-secrets-out-of-repo report it), publishable keys, and which environment a deployment really loads at runtime.
Type
deterministic
1.1.0: deterministic: the rule runs over the static analysis kit (tokens, function units, calls, imports, routes) with no AI engine; exact decisionRule and languages published; not-applicable with the reason when the checkout gives nothing to evaluate. Environment of a file from its path (last environment word wins); environment reads found as expressions (process.env.X, os.environ.get('X', default), env('X', default), $_ENV['X'], config('services.<gateway>.secret', default)) with a literal fallback through ||, ??, or, ?: or the default argument = HIGH; live key in a file scoped to no environment = MEDIUM; scoping = an environment switch in code OR per-environment files; rotation = a secret store (Secrets Manager, SSM, Vault, Doppler, SST, k8s secretRef) or key-rotation words or schedule. Not judged: whether rotation happens, keys from constants, direct literals (secrets-scan), the runtime environment. (1.0.2: never analyses temporary files (*.tmp.*; spec.excludes follows AUDITOR_ANALYSIS_EXCLUDES). (1.0.1: never analyses test, spec, fixture and mock paths nor the auditor's own scripts (spec.excludes = AUDITOR_ANALYSIS_EXCLUDES); a fixture-looking secret (sk_test_, example, dummy) in real code is LOW, informative; every model call stays under the engine prompt cap (spec.promptMaxChars = AUDITOR_AI_PROMPT_MAX_CHARS): assess mode sends the candidate set in parts and aggregates the verdicts (a HIGH in any part is a FAIL, an unsatisfied part is a FAIL).))
const LIVE_KEY = /\b(?:sk|rk)_live_[0-9A-Za-z]{8,}|rzp_live_|pdl_live_|\bAPP[_-]USR-\d{6,}/; // in a dev/test/staging/sandbox/local file -> FAIL; in a file scoped to no environment -> MEDIUM
const TEST_KEY = /\b(?:sk|rk)_test_[0-9A-Za-z]{8,}|rzp_test_|pdl_sdbx_|sk_sbox_/; // in a prod/production/live file -> FAIL
// an environment read of a gateway key variable is an EXPRESSION of the static analysis kit (process.env.X, os.environ.get('X', default), env('X', default), $_ENV['X'], config('services.stripe.secret', default))
// a string literal after it through || ?? or ?: or as the default argument -> FAIL (key-fallback-literal)
// no environment switch in code AND no per-environment files holding the variables -> MEDIUM; no secret store / rotation signal -> MEDIUM
The full script is disclosed on request in a read-only viewer (never published on GitHub); the attestation binds to this exact hash.