Skip to content

CodeDiag 0.10 "Reach" - #43

Merged
sabahattink merged 18 commits into
mainfrom
release/0.10
Oct 31, 2026
Merged

sabahattink merged 18 commits into
mainfrom
release/0.10

Conversation

@sabahattink

Copy link
Copy Markdown
Owner

CodeDiag 0.10 "Reach": SvelteKit and React Router/Remix API Health, Next.js middleware judged per route, tRPC perimeters for Hono, Fastify, and Auth.js, one pnpm audit per workspace lock file, and an end to critical password and secret false positives on real apps.

Changes

  • SvelteKit (src/analyzers/sveltekit-*.ts): +server endpoints and form actions; auth from the handle hook (through sequence(), path and route id guards, and helpers) or in the handler. New rules: missing-sveltekit-auth (with a note when the only check sits in a layout load), missing-sveltekit-validation, sveltekit-csrf-disabled, and no-sveltekit-endpoints. trpc-sveltekit handles count as tRPC adapters, and insecure-cookie honors cookies.set() defaults.
  • React Router / Remix (src/analyzers/react-router-*.ts): actions and loaders from app/routes.ts or flat-route files, resource routes, re-exports, and route middleware. New rules: missing-react-router-auth, react-router-loader-auth-only, missing-react-router-validation, and no-react-router-routes.
  • Next.js middleware (src/analyzers/next-middleware.ts): evaluated for an anonymous request to each route path, covering the matcher, pathname and identity conditions, next-auth, Clerk protect(), Auth.js authorized, and returned helpers such as Supabase's updateSession(). This replaces the keyword match that let any auth-flavored middleware protect every route.
  • tRPC perimeters: Hono trpcServer() mounts and Fastify fastifyTRPCPlugin registrations are placed by each framework's own middleware and hook model. Auth.js v5 middleware is judged as Auth.js runs it. Every adapter must be covered.
  • pnpm audits: packages that share a pnpm lock file run one audit at the lock file and keep only the advisories that their own importer reaches.
  • Security precision:
    • plaintext-password-comparison and password-hashing-not-detected now look at the syntax tree.
    • hardcoded-secret skips messages, placeholders, and test fixtures.
    • The Pwned Passwords SHA-1 range API is no longer reported.
    • MOCK_ values count as placeholders.
  • Re-exports (export { auth as middleware } from "./auth") and destructured exports now resolve across files.
  • VS Code extension 0.8.0.

Verification

  • npm run check:

    • unit tests: 417, of which 416 pass and 1 is skipped (the Windows symlink test);
    • CLI and Action tests: 33/33;
    • bundle:check is clean;
    • self-scan scores 99.
  • Real apps, scanned with --no-audit and compared with 0.8.0/0.9 behavior:

    App Score Security API Health / notes
    Remix indie-stack 65 → 87 3 → 78 API Health 100
    Epic Stack 57 → 86 0 → 72 API Health not applicable (remix-flat-routes, documented)
    Lucia SvelteKit example 69 → 83 53 → 100 no auth findings
    sveltejs/realworld 78 — only real validation gaps on form actions
    shadcn/taxonomy (Next.js) unchanged — its middleware matcher excludes /api

    No critical finding is left in any of them.

  • A real pnpm workspace: lodash advisories appear only on the package that depends on lodash, and the audit runs once.

…th.js middleware

- Hono: auth middleware registered before app.use(path, trpcServer())
  for a matching path, including through app.route() and earlier
  arguments of the same use(), protects the procedures; Hono's own
  path matching decides, so use("/") no longer counts.
- Fastify: an auth hook on the context that registers fastifyTRPCPlugin,
  an ancestor, or a fastify-plugin sharing one of them.
- Next.js middleware is run for an anonymous request to the tRPC path:
  pathname comparisons and identity checks in if statements are decided,
  so a guard scoped to /dashboard no longer counts. Auth.js v5 `auth` as
  middleware is judged by its authorized callback (falsy redirects), and
  auth(fn) by fn, where only a Response from authorized stops the request.
- Every adapter must sit behind a perimeter, not just the first kind found.
- Re-exports and destructured exports resolve across files.
The Next.js analyzer counted any middleware mentioning auth, session, or
Clerk as protecting every API route. Middleware (and Next.js 16's proxy)
is now evaluated for an anonymous request to each route path with the
evaluator the tRPC perimeter uses: the matcher, path-scoped guards,
next-auth, Clerk's protect(), Auth.js authorized callbacks, and helpers
returned from the middleware such as Supabase's updateSession().
clerkMiddleware() alone no longer protects routes, matching Clerk's
default of public routes.

runtimeSources() and moduleIndex() move to the shared module index.
pnpm audits the whole workspace lock file from any package, so every
package of a pnpm workspace ran the same audit and reported every other
package's vulnerabilities as its own. Packages under a shared pnpm lock
file now run one audit at the lock file (one prefetch job, one cache
entry) and report only the advisories whose paths start at their
importer (packages__api>lodash). npm and Yarn already audit the
workspace they run in and are unchanged. The on-disk cache keeps the
per-package split.
Adds the react-router framework id for React Router v7 framework mode and
Remix v2. Detection requires a framework-mode package (@react-router/dev,
node, serve, express, ...) next to react-router, a Remix package other than
@remix-run/router, or an app/routes.ts that imports @react-router/dev/routes;
react-router or react-router-dom alone is library mode. It runs after Next.js
and before Fastify, Express, and Hono, because these apps often run on
Express.

Routes come from app/routes.ts (route, index, layout, prefix, plain entries,
and flatRoutes() with rootDirectory and ignoredRouteFiles, read statically)
or the flat file convention under app/routes, with appDirectory taken from
the framework config. A route config that cannot be read statically, such as
remixRoutesOptionAdapter with remix-flat-routes, leaves API Health not
applicable instead of reporting missing routes.

Rules:
- missing-react-router-auth: an action with no auth check of its own (also
  two project calls deep) and no authenticating middleware on its route or a
  parent route. Sign-in, sign-up, and sign-out actions are exempt.
- react-router-loader-auth-only: an action or loader whose only protection is
  a loader's auth check, which does not run before an action and runs in
  parallel with child loaders. Loader findings have medium confidence.
- missing-react-router-validation: an action that reads request.formData()
  or request.json() without a schema parse or value checks.
- no-react-router-routes: the framework is installed but no route modules
  exist.

The realistic fixture is a routes.ts app with a middleware perimeter, Conform
and Zod validation, a session-checked JSON resource route, and a health
route; the defects fixture is a Remix v2 flat-routes app.
SvelteKit's cookies.set() defaults httpOnly and secure to true, so a
session cookie set without those options on a request event's cookies in
a SvelteKit module is not insecure. Explicit false is still reported.
createTRPCHandle() from trpc-sveltekit serves a router from the SvelteKit
handle hook. Files importing it count as adapter files, and a
createContext passed to it that rejects anonymous callers is a perimeter.
The package's client export does not make a file an adapter.
Detects SvelteKit through @sveltejs/kit or a kit svelte.config, ahead of
Express, Fastify, Hono, and tRPC, which a SvelteKit app may also use.

+server handlers and +page.server form actions are the endpoints, named
by their route without groups. New rules:
- missing-sveltekit-auth: a mutating handler or action is neither
  guarded by the handle hook for its path or route id (sequence() parts
  and project imports followed, conditions evaluated per route) nor
  rejects anonymous callers itself or through helpers two calls deep.
  An auth check only in a layout or page load is named in the message,
  since actions run before loads and +server handlers without layouts.
- missing-sveltekit-validation: a body read without a schema parse or a
  400/422 check of the body.
- sveltekit-csrf-disabled: kit.csrf.checkOrigin false or '*' in
  kit.csrf.trustedOrigins.
- no-sveltekit-endpoints: +server files without handlers.

Sign-in flows and handlers that forward the request to another router are
not judged. tRPC procedures resolve $lib imports in SvelteKit apps.
# Conflicts:
#	dist/action.cjs
#	docs/rules.md
#	src/analyzers/trpc-api.ts
#	src/analyzers/trpc-perimeter.ts
#	src/detectors/stack-detector.ts
#	tests/benchmark.test.ts
…eal apps

Scanning the Remix indie-stack, Epic Stack, and Lucia's SvelteKit example
turned up critical findings for ordinary sign-up code:

- plaintext-password-comparison now looks at equality expressions: a
  stored and a supplied password compared as values. typeof and length
  checks, comparisons with constants, and password !== confirmPassword
  are validation.
- password-hashing-not-detected needs a write whose data holds the
  password as it came in (shorthand, a variable, or a property), not any
  create/update call in a file that mentions passwords; helpers such as
  getPasswordHash() count as hashing.
- hardcoded-secret skips "Password is required"-style messages,
  placeholders, and password fixtures in test files; provider tokens in
  tests are still reported.
- SHA-1 for the Pwned Passwords range API is not a weak password hash.
- MOCK_ values are placeholders in committed .env examples.

Security scores: indie-stack 3 -> 78, Epic Stack 0 -> 72, Lucia 53 -> 100,
with no critical findings left in any of them.
@github-actions

github-actions Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

✅ CodeDiag: 95/100 (A+)

no change since the last run · threshold 0

Analyzer Score Findings
Security 100/100 0
Dependencies 84/100 2
Testing 98/100 1
Structure 100/100 0

No findings in the files this pull request changes.

Not listed: 2 findings elsewhere in the project.

Scanned by CodeDiag · updated on every push

@sabahattink
sabahattink merged commit 3cb3fbd into main Oct 31, 2026
7 checks passed
@sabahattink
sabahattink deleted the release/0.10 branch October 31, 2026 04:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant