Fleet-wide linter for the iSimplifyMe Next.js/SST repo fleet. It encodes the
recurring failure modes that have each cost a production incident — the
⚡ RULE entries in the team's memory — plus baseline security checks, as
automated checks that fail in CI instead of in production.
Two ways to use it:
- Fleet scan — audit every repo at once, producing a health/security matrix.
- Single-repo check — run in one repo's CI via the bundled GitHub Action.
Each check declares whether it applies to every repo or only to Next.js repos
(detected by a next dependency).
| Check | Scope | Severity | Catches |
|---|---|---|---|
secret-scan |
all | security | AWS / GitHub / Stripe / Cloudflare / Slack credentials committed to source |
sst-secret-fallback |
all (sst) | security | app secrets in sst.config.ts read as process.env.X || "" / ?? "" or hardcoded, instead of sst.Secret (allowlisted: infra vars AWS_* / CLOUDFLARE_* / CF_ZONE_* / GITHUB_*, public-by-design NEXT_PUBLIC_* / *_PUBLISHABLE_KEY / *_URL, Secrets Manager references *_SECRET_ID / *_SECRET_ARN / *_SECRET_NAME, and arn:aws:… / SSH-public-key values) |
ahpra-schema-guard |
listed repos | security | new Review / AggregateRating schema on AU medical sites beyond the pinned per-repo baseline (AHPRA National Law s133) |
next-cve |
next | security | Next.js versions below 16.2.6 — the May 2026 advisory batch floor (CVE-2026-45109 middleware/proxy bypass; includes CVE-2026-44578 SSRF, patched 16.2.5) |
authed-cache-leak |
next | security | gated pages that can render static/shared-cacheable behind a cookie-blind CDN (the VB 8/11 authed-page cache leak): generateStaticParams / revalidate > 0 / force-static inside a gated tree (security — the generator wins even over force-dynamic), or middleware-gated pages with no dynamic proof (warning). Session-reading trees and no-store-stamping middlewares are recognized as safe shapes; exempt extra public segments via .fleetcheckrc.json { "authed-cache-leak": { "publicRoutes": [...] } } |
opennext-version-pin |
next | error | Next 16+ deployed via sst.aws.Nextjs without an openNextVersion pin ≥ 4.0.2 (the /_next/image 500 incident) |
stale-aws-creds |
all | warning | deploy workflows whose recent runs failed on AWS credential errors |
worktrees-gitignore |
all | warning | .gitignore missing .worktrees/ |
edge-runtime-og |
next | error | files that import next/og and declare the edge runtime |
public-dir-collision |
next | error | a public/<dir> that collides with an app route (s3.routes 403) |
dynamic-params |
next | warning | dynamicParams = false — verify generateStaticParams is exhaustive |
jsonld-script |
next | warning | JSON-LD rendered through next/script instead of a plain <script> |
aeo-requirements |
next | info | per-repo summary of pages missing h1 / AtomicAnswer / FAQ schema |
blog-schema-spokes |
next | warning | BlogPosting/Article schema on a non-blog route |
blog-url-year |
next | warning | year tokens in blog route URLs |
eca-template-residue |
next | warning | lorem ipsum / placeholder phone numbers left in shipped copy |
Severity order: security > error > warning > info.
npm install # install dependencies
# Single repo (used by the GitHub Action):
npm run dev -- check [path] [--fail-on <severity>] [--json]
# Whole fleet:
npm run dev -- scan --root <dir> [--repo <name>] [--out <dir>] [--fail-on <sev>]
# Re-render the matrix from a previous scan:
npm run dev -- report [--out <dir>]scan reads fleet.config.json, resolves each repo at <root>/<name>, runs
all applicable checks, and writes to reports/:
scan.json— full machine-readable result<repo>.json— per-repo resultfleet-health-matrix.md— the human-readable matrix
fix --safe (build phase 8) opens worktree-based PRs for the safe mechanical
class only: a same-major Next.js CVE patch bump and the .worktrees/ gitignore
line. It never merges and never deploys.
Add to any repo's CI:
- uses: actions/checkout@v4
- uses: iSimplifyMe/fleetcheck@<full-commit-sha> # pin to a SHA, not a branch
with:
fail-on: error # security|error fail the build; warnings do notThe action checks the current repo and exits non-zero at or above fail-on.
Pin the action to a full commit SHA, not @main or a tag: branch and tag
refs are mutable, so an unpinned reference runs whatever the ref points to at
build time (supply-chain exposure). Bump the SHA deliberately when upgrading.
{
"repos": [
{ "name": "<local-dir-name>", "slug": "<org>/<repo>", "skip": false }
]
}name is the working-tree directory under --root; slug is the GitHub
org/repo (they differ for several repos). slug powers stale-aws-creds
and the fix PR flow. An optional path overrides the working-tree
directory when it differs from name (relative paths resolve under
--root, e.g. anitapatelmd → anita-patel-md).
A repo entry may carry a settings object, keyed by check id:
{
"name": "signature-dentistry",
"slug": "iSimplifyMe/signature-dentistry",
"settings": { "ahpra-schema-guard": { "baseline": 1 } }
}A repo can also ship its own settings in a .fleetcheckrc.json at its root
(same shape, { "<check-id>": { … } }). The repo-local file wins over the
fleet config entry — it is what the single-repo GitHub Action mode reads,
since that mode never sees fleet.config.json.
ahpra-schema-guard.baseline pins the approved count of existing
Review/AggregateRating schema occurrences; anything above it fails.
Suppressions are value/shape-based, never path-based — a real credential in a test file still fires. What is suppressed (calibrated on the 2026-07-16 fleet baseline's six false positives):
-
fixture tokens whose delimited segments say so (
xoxb-test-token; segments: test/fake/dummy/example/sample/placeholder/redacted). AWS, Stripe, Cloudflare, and GitHub key bodies contain no delimiters, so they can never be value-suppressed; -
private-key
-----BEGIN…-----markers that provably hold no key: the END marker on the same line with < 64 chars between (single-line test fixtures), or the marker immediately followed by a backtick or ellipsis (doc prose); -
an explicit escape for anything else:
// fleetcheck-ignore-next-line: documented example token, rotated 2026-05-01 const example = "cfut_…";The reason after the directive is required — a bare
fleetcheck-ignore-next-lineis inert and the finding still fires.
scan/check exit 2 when any finding is at or above --fail-on
(check defaults to error). Set FLEETCHECK_NO_NETWORK=1 to skip checks
that call the network (stale-aws-creds).
Two memory rules were deliberately left unimplemented — static analysis produces too many false positives to be useful:
await-after-response on Lambda handlersdisplay: flexon multi-child<div>insidenext/ogImageResponse
dependency-audit (full npm audit integration beyond the Next.js CVE) is a
planned enhancement.
fleetcheck scans each repo's working tree as-is, on whatever branch it is
checked out on — results can differ from the deployed default branch. The first
fleet scan over-reported next-cve because several repos were on stale feature
branches. Scan clean default-branch checkouts for a deployed-state audit; a
--ref flag is a planned enhancement.
A check is a module exporting a Check: { id, title, severity, appliesTo, run }. run(repo) returns Finding[]. Add the module under
src/checks/universal/ or src/checks/next/, register it in
src/checks/index.ts, and add a fixture-backed test under test/.
npm test # vitest
npm run build # tsc -> dist/