Check Catalog

The complete native check set, generated directly from checks.json — the source of truth shared by the web app and CLI native engine. The catalog currently holds 59 checks across nine agent domains, weighted to 100 points.

How this catalog is built

Every check below is read straight from checks.json by scripts/generate-checks-docs.py. Nothing here is hand-written or aspirational — if a check is not in checks.json, it does not run, and it is not listed here. For planned checks that are not yet implemented, see the roadmap section of the examination spec.

Orchestration is additive

The CLI can also run the separately scored ALIGNMENT analyzer and optional Gitleaks, OSV-Scanner, Zizmor, OpenSSF Scorecard, and actionlint feeders. Their rules are not part of this catalog and never alter native points. See the CLI Scanner and Reading Reports pages.

Check states

Every check resolves to exactly one of four states when a scan runs:

passApplicable, evaluated, clean.
failApplicable, evaluated, one or more findings.
not_applicablePrecondition unmet (e.g. no Dockerfile), with the reason stated.
skippedCould not be evaluated (file budget exhausted, fetch error), with the reason stated.

Agent index

AgentDomainWeightChecks
SENTINELCode Analysis1411
GATEKEEPERAccess Control126
VAULTData Security138
CONDUITNetwork & API115
WATCHTOWERApplication Config118
LIBRARIANDependencies126
SHIELDClient Security116
AUDITORLogging & Monitoring84
ARCHITECTInfrastructure85
Total10059

SENTINEL11 checks

S-01

Hardcoded credential assignment

critical CWE-798

A secret-looking variable is assigned a long literal string directly in source.

Risk

Anyone who can read the repository — including every fork, every CI log, and every future clone — holds a working credential. Git history keeps it even after the line is deleted.

Remediation

Move the value into an environment variable or a secrets manager, rotate the exposed credential immediately, and purge it from git history with `git filter-repo`.

Suggested fix
- API_KEY = "sk_live_9f2b1c8e4a7d0553"
+ import os
+ API_KEY = os.environ["API_KEY"]   # set in your secrets manager / .env (gitignored)
S-02

AWS access key ID in source

critical CWE-798

A literal matching the AWS access key ID format (AKIA/ASIA + 16 chars) appears in the repository.

Risk

AWS key IDs are harvested from public GitHub within minutes by automated scrapers. Paired with a secret key this grants direct access to your cloud account.

Remediation

Deactivate the key in IAM right now, then re-issue via IAM roles or AWS SSO instead of long-lived keys.

Suggested fix
aws iam update-access-key --access-key-id AKIA... --status Inactive
aws iam delete-access-key --access-key-id AKIA...
S-03

Private key material committed

critical CWE-321

A PEM private key block is embedded in a tracked file.

Risk

A leaked private key lets an attacker impersonate your service, decrypt intercepted traffic, or sign artifacts as you.

Remediation

Revoke and reissue the key pair, then load keys at runtime from a mounted secret or KMS — never from the repo.

S-04

SQL built by string concatenation

critical CWE-89

A SQL statement is assembled with f-strings, `+`, `%`, or `.format()` rather than bound parameters.

Risk

Any user-controlled value reaching this query can rewrite it — reading other tenants' rows, dumping the user table, or dropping it.

Remediation

Use parameter binding for every dynamic value. Only table/column names may be interpolated, and only from a fixed allow-list.

Suggested fix
- cursor.execute(f"SELECT * FROM users WHERE email = '{email}'")
+ cursor.execute("SELECT * FROM users WHERE email = %s", (email,))
S-05

Shell execution with interpolated input

critical CWE-78

An OS command is run through a shell with a dynamically built string, or with `shell=True`.

Risk

A `;`, backtick, or `$()` in any interpolated value becomes arbitrary code execution on the host running the process.

Remediation

Pass arguments as a list with `shell=False` (the default) and validate any value that reaches the argv.

Suggested fix
- subprocess.run(f"git clone {repo_url}", shell=True)
+ subprocess.run(["git", "clone", repo_url], shell=False, check=True)
S-06

Dynamic code evaluation

high CWE-95

`eval`, `exec`, `new Function`, or `setTimeout(string)` is called on a non-literal value.

Risk

If the evaluated string is influenced by input, request data, or config fetched at runtime, it is remote code execution.

Remediation

Replace with an explicit parser (`JSON.parse`, `ast.literal_eval`) or a dispatch table keyed by known-safe names.

S-06P

Python exec() on a dynamic value

high CWE-95

Python's builtin `exec()` is called on a non-literal value.

Risk

`exec` compiles and runs whatever string it is handed. If that string is influenced by input or remote config, it is remote code execution.

Remediation

Use `ast.literal_eval` for data, or a dispatch dict keyed by known-safe names for behaviour.

S-07

Unsafe deserialization

high CWE-502

Untrusted bytes are loaded through pickle, `yaml.load` without a safe loader, or Java/PHP native deserialization.

Risk

These formats can instantiate arbitrary classes on load — a crafted payload runs code before your first line of validation.

Remediation

Use `yaml.safe_load`, JSON, or a schema-validated format. Never unpickle data that crossed a trust boundary.

Suggested fix
- config = yaml.load(untrusted_bytes)
+ config = yaml.safe_load(untrusted_bytes)
S-08

Non-cryptographic randomness for security values

medium CWE-338

`Math.random()` or `random.random()` is used near a token, password, nonce, or ID.

Risk

These generators are predictable from a handful of outputs, so an attacker can forecast reset tokens or session identifiers.

Remediation

Use `crypto.randomUUID()` / `crypto.randomBytes()` in Node, `secrets.token_urlsafe()` in Python.

Suggested fix
- const token = Math.random().toString(36).slice(2);
+ const token = crypto.randomUUID();
S-09

Path traversal in file access

high CWE-22

A filesystem path is built from request/user input without normalisation.

Risk

`../../etc/passwd` style input reads or overwrites files outside the intended directory.

Remediation

Resolve the path and assert it stays within a base directory before opening it.

Suggested fix
base = Path("/srv/uploads").resolve()
target = (base / user_path).resolve()
if not target.is_relative_to(base):
    raise PermissionError("path escapes upload root")
S-10

Server-side request forgery risk

high CWE-918

An outbound HTTP request targets a URL taken from request input.

Risk

An attacker can point the request at internal services or the cloud metadata endpoint (169.254.169.254) and read credentials.

Remediation

Validate the destination against an allow-list of hosts and block link-local, loopback, and private ranges.

GATEKEEPER6 checks

G-01

Signature verification disabled

critical CWE-347

JWT or token verification is switched off, or the `none` algorithm is accepted.

Risk

Anyone can mint a token claiming to be any user, including an administrator. Authentication is effectively absent.

Remediation

Always verify with an explicit algorithm allow-list (`algorithms=["RS256"]`) and never accept `none`.

Suggested fix
- jwt.decode(token, options={"verify_signature": False})
+ jwt.decode(token, PUBLIC_KEY, algorithms=["RS256"], audience=API_AUDIENCE)
G-02

Weak password length policy

high CWE-521

Password validation accepts fewer than 8 characters.

Risk

Short passwords fall to offline cracking in seconds regardless of how well you hash them.

Remediation

Require at least 12 characters and screen against a breached-password list (e.g. Have I Been Pwned range API).

G-03

Endpoint opted out of authentication

medium CWE-306

A route is explicitly marked as public via `AllowAny`, `@csrf_exempt`, `authenticate: false`, or similar.

Risk

Each opt-out is an unauthenticated entry point. They accumulate silently and are rarely re-reviewed.

Remediation

Default to deny. Keep an audited list of intentionally public routes and assert it in a test.

G-04

Default or hardcoded admin credentials

critical CWE-1392

An admin/root username is paired with a literal password in source or config.

Risk

Default credentials are the single most reliable way into a self-hosted deployment; scanners try them first.

Remediation

Generate the initial admin password at install time, force rotation on first login, and never ship a fallback.

G-05

Session cookie missing security flags

high CWE-1004

A cookie is set with `httpOnly` or `secure` explicitly false, or a session cookie config omits both.

Risk

Without `httpOnly` any XSS reads the session; without `secure` it leaks over plain HTTP on a hostile network.

Remediation

Set `httpOnly: true`, `secure: true`, and `sameSite: 'lax'` (or `'strict'`) on every session cookie.

Suggested fix
res.cookie('sid', token, {
  httpOnly: true,
  secure: true,
  sameSite: 'lax',
  maxAge: 1000 * 60 * 60 * 8
});
G-06

Authorization decided on the client

medium CWE-602

A role or admin flag is read from local/session storage or a decoded token without server verification.

Risk

Anything the browser stores, the user edits. Client-side role checks are UI hints, not access control.

Remediation

Re-check the caller's role server-side on every privileged request; treat client state as untrusted display data.

VAULT8 checks

V-01

Broken hash used for passwords

high CWE-916

MD5 or SHA-1 is applied to a password or credential value.

Risk

Commodity GPUs test billions of MD5/SHA-1 candidates per second; a stolen table is cracked, not merely exposed.

Remediation

Use Argon2id (preferred), bcrypt, or scrypt with tuned work factors, and re-hash on next successful login.

Suggested fix
- digest = hashlib.md5(password.encode()).hexdigest()
+ from argon2 import PasswordHasher
+ digest = PasswordHasher().hash(password)
V-02

Weak cipher mode or static IV

high CWE-327

ECB mode, DES/RC4, or a hardcoded initialisation vector is used for encryption.

Risk

ECB leaks structure in ciphertext and a reused IV in CBC/CTR lets an attacker recover plaintext across messages.

Remediation

Use AES-GCM or ChaCha20-Poly1305 with a fresh random nonce per message.

V-03

Sensitive value written to stdout

medium CWE-532

A print/console statement includes a password, token, SSN, or card field.

Risk

Container stdout is shipped to log aggregators, retained for months, and readable by anyone with dashboard access.

Remediation

Remove the statement or mask the value; add a redaction filter in the logging pipeline as a backstop.

V-04

Environment file committed to the repository

critical CWE-538

A real `.env` file (not `.env.example`) is tracked in git.

Risk

Env files are where production credentials live. Committed once, they stay in history and in every clone forever.

Remediation

`git rm --cached .env`, add it to `.gitignore`, rotate every value it contained, then scrub history.

Suggested fix
git rm --cached .env
echo '.env' >> .gitignore
git commit -m 'chore: stop tracking .env'
# then rotate every credential it held
V-05

Key or certificate file committed

critical CWE-312

A `.pem`, `.key`, `.p12`, `.pfx`, `.keystore`, or SSH private key file is tracked.

Risk

Committed key material must be treated as compromised the moment it is pushed.

Remediation

Revoke and reissue, then deliver keys through a secret mount or KMS at deploy time.

V-06

Database dump or datastore committed

medium CWE-538

A `.sqlite`, `.db`, or `.sql` dump file is tracked in the repository.

Risk

Development databases routinely contain copies of real user records, and often password hashes.

Remediation

Remove the file, gitignore the pattern, and seed local databases from a fixtures script instead.

V-07

.gitignore does not cover secret files

medium CWE-1230

The repository has a `.gitignore` but it does not exclude `.env` or key material.

Risk

Without the ignore rule, the next `git add .` commits whatever credentials happen to be on disk.

Remediation

Add the standard secret patterns to `.gitignore` before they get committed.

Suggested fix
# secrets
.env
.env.*
!.env.example
*.pem
*.key
*.p12
credentials.json
V-08

Bearer or provider token literal

critical CWE-798

A recognisable provider token prefix (GitHub, Slack, Stripe, Google, OpenAI, Anthropic) appears as a literal.

Risk

These are directly usable credentials with a known issuer, so exploitation needs no guesswork at all.

Remediation

Revoke at the provider immediately, then load from the environment at runtime.

CONDUIT5 checks

C-01

Wildcard CORS origin

high CWE-942

`Access-Control-Allow-Origin` is set to `*`, or the CORS middleware allows all origins.

Risk

Any website can call your API from a visitor's browser. On a cookie-authenticated API this is cross-origin data theft.

Remediation

Enumerate trusted origins explicitly and reject everything else.

Suggested fix
- app.use(cors({ origin: '*' }))
+ app.use(cors({ origin: ['https://app.example.com'], credentials: true }))
C-02

Wildcard CORS combined with credentials

critical CWE-942

Credentialed CORS is enabled alongside a permissive origin policy.

Risk

Attacker-controlled pages can issue authenticated requests as the logged-in victim and read the responses.

Remediation

Never combine `credentials: true` with a reflected or wildcard origin — pin to an explicit host list.

C-03

TLS certificate validation disabled

high CWE-295

An HTTP client is configured with `verify=False`, `rejectUnauthorized: false`, or `InsecureSkipVerify`.

Risk

TLS without certificate validation stops any active attacker on the path from being detected — HTTPS becomes decoration.

Remediation

Keep validation on. For internal CAs, install the CA bundle rather than disabling the check.

Suggested fix
- requests.get(url, verify=False)
+ requests.get(url, verify="/etc/ssl/certs/internal-ca.pem")
C-04

Cleartext HTTP endpoint

medium CWE-319

A non-local `http://` URL is used for an API or asset.

Risk

Requests and any tokens they carry are readable and modifiable by anyone on the network path.

Remediation

Switch to `https://` and add HSTS so downgrades are refused.

C-05

Service bound to all interfaces

medium CWE-1327

A server listens on `0.0.0.0` outside of a container entrypoint.

Risk

Services intended for localhost become reachable from the network, and from the internet on a misconfigured host.

Remediation

Bind to `127.0.0.1` and place a reverse proxy in front of anything that must be public.

WATCHTOWER8 checks

W-01

Debug mode enabled

high CWE-489

`DEBUG = True`, `app.run(debug=True)`, or an equivalent development flag is set in committed config.

Risk

Debug handlers expose stack traces, settings, and — in Flask/Django — an interactive console that executes code.

Remediation

Drive the flag from the environment and default it to off.

Suggested fix
- DEBUG = True
+ DEBUG = os.environ.get("DEBUG", "").lower() == "true"   # off unless explicitly enabled
W-02

Container runs as root

high CWE-250

A Dockerfile never drops privileges — no `USER` instruction, or it explicitly sets `USER root`.

Risk

A process escape or a mounted host path gives the attacker root on the node instead of an unprivileged account.

Remediation

Create a non-root user and switch to it before the entrypoint.

Suggested fix
RUN adduser --system --uid 10001 appuser
USER appuser
CMD ["node", "server.js"]
W-03

Unpinned base image tag

medium CWE-1104

A Dockerfile uses `:latest` or omits the tag entirely.

Risk

Builds are not reproducible and a compromised or breaking upstream image ships straight to production.

Remediation

Pin to a digest: `FROM node:20.11-alpine@sha256:...`.

W-04

Privileged container or host namespace

high CWE-250

A compose or Kubernetes manifest requests `privileged: true`, `hostNetwork`, `hostPID`, or docker socket access.

Risk

A privileged container is functionally root on the host; the docker socket is a full container escape.

Remediation

Drop the privilege, add only the specific capabilities needed, and never mount `/var/run/docker.sock`.

W-05

Plaintext secret in CI workflow

high CWE-798

A CI workflow sets a token or password to a literal value instead of referencing the secrets store.

Risk

Workflow files are public on public repos, and the value is echoed into build logs on failure.

Remediation

Reference `${{ secrets.NAME }}` (GitHub) or the equivalent masked variable in your CI provider.

Suggested fix
- env:
-   NPM_TOKEN: npm_9f2b1c8e4a7d0553aa11
+ env:
+   NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
W-06

No security disclosure policy

medium CWE-1059

The repository has no `SECURITY.md`.

Risk

Researchers who find a flaw have no private channel, so issues get filed publicly — or sold.

Remediation

Add `SECURITY.md` with a contact address, supported versions, and expected response time.

Suggested fix
# Security Policy

## Reporting a Vulnerability
Email security@example.com. We acknowledge within 2 business days
and aim to ship a fix within 30 days.

## Supported Versions
| Version | Supported |
|---------|-----------|
| 2.x     | yes       |
| < 2.0   | no        |
W-07

No license file

low

The repository does not declare a license.

Risk

Without a license the code is all-rights-reserved by default, which blocks legitimate reuse and complicates audits.

Remediation

Add a `LICENSE` file, or state the proprietary terms explicitly if the code is closed.

W-08

Security headers not configured

medium CWE-693

No Content-Security-Policy, HSTS, or X-Frame-Options configuration found anywhere in the repository.

Risk

Missing CSP removes the main mitigation for XSS; missing frame protection allows clickjacking of authenticated views.

Remediation

Add a security-headers middleware (helmet, django-csp, secure.py) or set them at the edge/CDN.

Suggested fix
import helmet from 'helmet';
app.use(helmet({
  contentSecurityPolicy: { directives: { defaultSrc: ["'self'"] } },
  hsts: { maxAge: 31536000, includeSubDomains: true }
}));

LIBRARIAN6 checks

L-01

Dependency lockfile missing

medium CWE-1104

A manifest declares dependencies but no lockfile pins the resolved versions.

Risk

Every install can pull different transitive code. A hijacked patch release lands in production without a diff.

Remediation

Commit the lockfile your package manager produces and install with `npm ci` / `pip install -r requirements.txt --require-hashes`.

L-02

Known-vulnerable dependency version

high CWE-1395

A manifest pins a package version with a published CVE.

Risk

Public advisories come with public exploits; these are the first things scanned for on an exposed service.

Remediation

Upgrade to the patched release and enable automated dependency updates so this does not recur.

L-03

Dependency sourced from a URL or git ref

medium CWE-829

A dependency is installed from a git URL or tarball rather than a registry release.

Risk

A moving branch reference means the code can change under you with no version bump and no audit trail.

Remediation

Publish an internal registry package, or at minimum pin to an immutable commit SHA.

L-04

No automated dependency updates

low CWE-1104

No Dependabot or Renovate configuration is present.

Risk

Patch lag is the dominant cause of exploited dependency CVEs; manual upgrades slip.

Remediation

Add `.github/dependabot.yml` or a Renovate config so upgrade PRs open automatically.

Suggested fix
version: 2
updates:
  - package-ecosystem: npm
    directory: "/"
    schedule: { interval: weekly }
    open-pull-requests-limit: 10
L-05

Remote script piped to a shell

high CWE-494

A build or CI step pipes `curl`/`wget` output directly into `bash` or `sh`.

Risk

The remote server decides what code runs on your builder, and can serve different content to you than to reviewers.

Remediation

Download to a file, verify a pinned checksum or signature, then execute.

Suggested fix
curl -fsSL -o install.sh https://example.com/install.sh
echo "<known-sha256>  install.sh" | sha256sum -c -
sh install.sh
L-06

Unpinned third-party GitHub Action

medium CWE-829

A workflow references a third-party action by branch or floating tag instead of a commit SHA.

Risk

The action author — or anyone who compromises their account — can retroactively change what runs with your repo token.

Remediation

Pin third-party actions to a full commit SHA and let Dependabot bump them.

Suggested fix
- uses: some-org/deploy-action@main
+ uses: some-org/deploy-action@a1b2c3d4e5f60718293a4b5c6d7e8f9012345678  # v3.1.0

SHIELD6 checks

F-01

Auth token stored in web storage

high CWE-922

A JWT, session, or auth token is written to `localStorage` or `sessionStorage`.

Risk

Web storage is readable by any script on the page, so a single XSS or a compromised npm package exfiltrates every session.

Remediation

Keep the session in an `HttpOnly; Secure; SameSite` cookie so script cannot read it.

Suggested fix
- localStorage.setItem('token', res.data.token);
+ // server sets: Set-Cookie: sid=...; HttpOnly; Secure; SameSite=Lax
+ // client sends credentials automatically:
+ fetch('/api/me', { credentials: 'include' });
F-02

Unsanitised HTML injection sink

high CWE-79

`dangerouslySetInnerHTML`, `v-html`, `[innerHTML]`, or a direct `innerHTML =` assignment is used.

Risk

If any part of that string is user-controlled it becomes stored XSS — session theft, keylogging, or account takeover.

Remediation

Render as text, or sanitise with DOMPurify immediately before insertion.

Suggested fix
- <div dangerouslySetInnerHTML={{ __html: comment.body }} />
+ import DOMPurify from 'dompurify';
+ <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(comment.body) }} />
F-03

document.write or legacy DOM sink

medium CWE-79

`document.write` is used, which parses its argument as HTML.

Risk

It is an XSS sink, blocks the parser, and is ignored entirely in async script contexts.

Remediation

Build nodes with `createElement`/`textContent` and append them.

F-04

postMessage without origin validation

medium CWE-346

A `message` listener does not check `event.origin`, or `postMessage` targets `*`.

Risk

Any framing or opened window can send messages your handler trusts, or read messages you broadcast.

Remediation

Compare `event.origin` against an exact expected origin and pass a specific target origin when sending.

Suggested fix
window.addEventListener('message', (e) => {
  if (e.origin !== 'https://trusted.example.com') return;
  handle(e.data);
});
F-05

target="_blank" without rel protection

low CWE-1022

An anchor opens a new tab without `rel="noopener"`.

Risk

In older browsers the opened page can redirect the original tab via `window.opener` — a credible phishing pivot.

Remediation

Add `rel="noopener noreferrer"` to every `target="_blank"` link.

F-06

Secret embedded in client-side code

critical CWE-798

A credential literal appears in a file that ships to the browser.

Risk

Anything in the bundle is public — view-source is all the attacker needs, regardless of build-time obfuscation.

Remediation

Proxy the call through your backend and keep the credential server-side.

AUDITOR4 checks

A-01

Credentials passed to the logger

high CWE-532

A structured log call includes a password, key, or secret field.

Risk

Log aggregators have far broader access than production databases, and retain data long after rotation.

Remediation

Redact sensitive keys in a log processor and pass identifiers, not credentials.

Suggested fix
- logger.info("login attempt", { email, password });
+ logger.info("login attempt", { email, hasPassword: Boolean(password) });
A-02

Stack trace returned to the client

medium CWE-209

An error handler sends `err.stack`, `traceback`, or the raw exception in the HTTP response.

Risk

Traces reveal file paths, framework versions, and query structure — the reconnaissance step of a real attack.

Remediation

Return a generic message plus a correlation ID, and log the detail server-side.

Suggested fix
- res.status(500).json({ error: err.stack });
+ const ref = crypto.randomUUID();
+ logger.error({ ref, err });
+ res.status(500).json({ error: 'Internal error', ref });
A-03

Debug output left in source

low CWE-489

`debugger` statements or `console.debug`/`console.trace` calls remain in non-test source.

Risk

`debugger` halts execution in any browser with devtools open, and verbose debug calls bury real signals.

Remediation

Route through a level-aware logger and enforce `no-console` / `no-debugger` in lint.

A-04

No continuous integration pipeline

medium

No CI configuration was found in the repository.

Risk

Without automated checks, security linting and dependency audits depend on whoever remembers to run them.

Remediation

Add a CI workflow that runs tests, a linter, and a dependency audit on every pull request.

Suggested fix
name: ci
on: [push, pull_request]
jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm test
      - run: npm audit --audit-level=high

ARCHITECT5 checks

R-01

Hardcoded IP address

medium CWE-1327

A routable IPv4 literal is embedded in source or configuration.

Risk

Infrastructure changes silently break the deployment, and the address discloses internal topology.

Remediation

Resolve endpoints through DNS or service discovery and supply them as configuration.

R-02

Security group open to the internet

critical CWE-284

An infrastructure definition allows ingress from `0.0.0.0/0`.

Risk

Databases and admin ports exposed this way are found by internet-wide scanners within hours of going live.

Remediation

Restrict ingress to known CIDRs or a bastion/VPN security group; expose only 443 publicly.

Suggested fix
  ingress {
    from_port   = 5432
    to_port     = 5432
-   cidr_blocks = ["0.0.0.0/0"]
+   security_groups = [aws_security_group.app.id]
  }
R-03

Publicly readable object storage

high CWE-732

A bucket or blob container is configured with a public-read ACL.

Risk

Public buckets are the most common source of large-scale data exposure; they are indexed and enumerated continuously.

Remediation

Block public access at the account level and serve objects through signed URLs or a CDN origin identity.

R-04

No automated tests

medium

No test directory or test files were found in the repository.

Risk

Security fixes regress silently when nothing verifies the behaviour they depend on.

Remediation

Add a test suite and wire it into CI, starting with the authentication and authorization paths.

R-05

Terraform state committed

critical CWE-538

A `.tfstate` file is tracked in the repository.

Risk

Terraform state stores resource attributes in plaintext, routinely including database passwords and generated keys.

Remediation

Move state to an encrypted remote backend (S3 + DynamoDB lock, Terraform Cloud) and gitignore `*.tfstate*`.

Suggested fix
terraform {
  backend "s3" {
    bucket         = "tf-state-prod"
    key            = "app/terraform.tfstate"
    encrypt        = true
    dynamodb_table = "tf-locks"
  }
}
Want the scoring math?

See Severity & Scoring for how per-check deductions, hit caps, and agent weights combine into the final 0–100 score.