A FastAPI security dependency (Security(...), an API-key scheme such as APIKeyHeader/APIKeyQuery/APIKeyCookie, or an OAuth2 bearer scheme) injects a credential that is then compared against a hard-coded string literal.
Why AI tools produce this: AI coding tools produce this regularly, typically when prompted for a shortcut or a quick fix.
Why this matters
The accepted key/token is baked into the source, so anyone who reads the code (or a leaked repo) can authenticate, and the secret cannot be rotated without a redeploy (CWE-798). This is a common AI-generated shortcut: a single inline literal replaces a real key store.
Compare against a secret loaded from the environment or a secret manager with a constant-time check instead, e.g. secrets.compare_digest(api_key, os.environ["API_KEY"]), and issue/verify per-client keys rather than one shared literal.
import osimport secretsfrom typing import Annotatedfrom fastapi import Depends, FastAPI, HTTPException, Security, statusfrom fastapi.security import APIKeyHeaderapp = FastAPI()api_key_header = APIKeyHeader(name="X-API-Key")def get_api_key(api_key: str = Security(api_key_header)): # Compared against a secret from the environment. # ok: auth.py.fastapi.hardcoded-api-key if secrets.compare_digest(api_key, os.environ["API_KEY"]): return api_key raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)def get_api_key_lookup( api_key: Annotated[str, Security(api_key_header)],): # Verified against a key store — no literal. # ok: auth.py.fastapi.hardcoded-api-key if is_valid_api_key(api_key): return api_key raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)def get_role(role: str = Depends(current_role)): # Generic Depends value compared to a literal — an authorization check, # NOT a credential. Must not fire (why the anchor is Security, not Depends). # ok: auth.py.fastapi.hardcoded-api-key if role == "admin": return role raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
Suppressing this rule
If a finding is a genuine false positive, scope the suppression to the exact line and leave a reason, never disable the rule project-wide. Disable directives are line-scoped by design.