Skip to content

fix: enforce route access rules consistently and add CI and npm releases - #8

Merged
Michael McQuade (giraffesyo) merged 1 commit into
masterfrom
ci/harden-release-npm
Oct 1, 2026
Merged

Michael McQuade (giraffesyo) merged 1 commit into
masterfrom
ci/harden-release-npm

Conversation

@giraffesyo

Copy link
Copy Markdown
Member

Summary

Fixes several access-control and routing bugs found in an audit, clears the dependency advisories, and adds CI plus a release-please pipeline that publishes to npm.

Bug fixes

Access control

  • Roles and validation were skipped on lowercase method exports. export const post was mounted, but roles = { POST: [...] } and validation were matched case-sensitively and not applied. They now apply regardless of case.
  • A handler that threw a falsy value (throw undefined, Promise.reject()) called next(undefined), which Express treats as success, so the request continued to the next handler. A thrown 'route'/'router' had the same effect. These are now passed on as real errors.
  • roles: { GET: 'org:admin' } (a string instead of an array) was matched by substring. This now throws at startup.
  • guestAccess together with ensureAdmin silently dropped the admin check. This now throws at startup, as do non-boolean values for either export.
  • Roles set for a method with no exported handler protected nothing. This now throws at startup; for validation it warns.
  • defaultErrorHandler returned the error message and stack trace in production. It now only does so when NODE_ENV is not production.

Routing

  • Param routes shadowed static routes that start with a digit or - (/users/:id was mounted before /users/2fa). Static segments now always sort ahead of param segments.
  • /indexes.ts was mounted as /es, and the extension regex had a precedence bug.
  • .d.ts files were picked up as routes.
  • A relative routesPath was resolved against the library's own folder, and glob characters in the path were treated as a pattern.
  • The file count was off by one, the summary line ignored logMounts, non-function exports now give a clear startup error, and the error handlers check headersSent.

Behavior change to be aware of

The startup checks are stricter. A consumer with one of the misconfigurations above will now fail to boot with a descriptive error instead of running with the rule silently not applied.

Dependencies and packaging

  • Lockfile refresh clears all known advisories (pnpm audit is clean); ajv moves to 8.20.
  • TypeScript 4.9 → 5.9, since the refreshed @types/node no longer parses under 4.9.
  • The published tarball contains only the library build (no compiled tests or test routes).
  • license is now Apache-2.0 to match LICENSE; version is set to 0.2.2 to match the last published version.

CI and release

  • ci.yml: typecheck, test, build and pack check on Node 22/24/26, plus dependency review on pull requests.
  • release.yml: release-please on master, then npm publish through OIDC trusted publishing in an npm environment. No npm token, no dependency cache in the publish job.
  • pr-title.yml, codeql.yml, dependabot.yml.
  • Actions are pinned to commit SHAs, default permissions are read-only, persist-credentials: false, and installs use --ignore-scripts.

After merging

  1. release-please opens a chore: release 0.2.3 pull request.
  2. The package does not exist on npmjs yet, so the first publish needs a one-time manual npm publish from the release tag, followed by:
    npm trust github @parallelworks/fsrouter --repo parallelworks/fsrouter --file release.yml --env npm
  3. Packages are no longer published to GitHub Packages.

Testing

  • 25 tests pass, 15 of them new; 14 fail against the previous source.
  • The packed tarball was installed into a scratch Express app and exercised over HTTP (auth, roles, validation, error paths).
  • actionlint passes. The workflows run for the first time on this pull request.

Access control
- apply roles and validation to method exports regardless of case
- pass a real error to next when a handler throws a falsy value, 'route' or 'router'
- reject roles that are not an array of role names, roles without a handler,
  non-boolean guestAccess/ensureAdmin, and guestAccess combined with ensureAdmin
- do not return error messages and stack traces from defaultErrorHandler in production

Routing
- mount static routes before param routes for every segment
- fix route paths for files that start with index, and the extension match
- ignore declaration files, resolve relative routesPath, and do not treat
  the path to the routes as a glob pattern

Packaging and CI
- refresh the lockfile to clear known advisories, move to TypeScript 5.9
- publish only the library build, set the license to Apache-2.0
- add CI, CodeQL, dependency review, PR title check and dependabot
- add release-please, publishing to npm with trusted publishing
@github-advanced-security

Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

@giraffesyo
Michael McQuade (giraffesyo) merged commit 63df332 into master Oct 1, 2026
8 checks passed
@github-actions github-actions Bot mentioned this pull request Oct 1, 2026
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.

2 participants