security: fix ReDoS in path-to-regexp and parseRequest crash path
## Summary
Fixes from a security scan of the repo. Scope: `packages/zsa-openapi` (the only package with a runtime HTTP surface).
- **Bump `path-to-regexp` from `^6.2.2` to `^6.3.0`.** `6.2.2` is vulnerable to ReDoS ([GHSA-9wv6-86v2-598j](https://github.com/advisories/GHSA-9wv6-86v2-598j)) and is used at runtime by `createRouteHandlers` to match incoming request paths against user-defined routes. Because `tsup` bundles this dep into the shipped `dist/` (it's declared as a devDependency and thus not externalized), the vulnerable version is currently being published to consumers. Bumping the minimum pins the patched version into the next published bundle.
- **Return `null` instead of `{} as any` in `parseRequest`.** When a request path had a different segment count than a matched wildcard route, `parseRequest` returned `{}`, which is truthy, so the handler proceeded to call `action=undefined` and returned a 500. Now it returns `null`, and the handler returns a proper 404.
Both changes are minimal. Full findings from the scan are in the Notes section below; other items in the scan (Next.js CVEs in `examples/showcase`, lint/ReDoS advisories in dev tooling like `@changesets/cli` and `@manypkg/cli`, etc.) are dev/example-only and not part of this PR.
## Review & Testing Checklist for Human
- [ ] Confirm bumping `path-to-regexp` from `6.2.2` to `6.3.0` is acceptable (same major, only a security fix — no breaking API changes per the [6.3.0 release](https://github.com/pillarjs/path-to-regexp/releases/tag/v6.3.0)).
- [ ] Sanity-check the `parseRequest` behavior change: previously a segment-count mismatch on a wildcard route returned 500; now it returns 404. Verify this matches what you consider correct. The only existing test that exercises this area (`tests/jest/__tests__/openapi.test.tsx`) still passes.
- [ ] Optional: decide whether to also declare `path-to-regexp` as a regular `dependency` (or bundle a pinned copy) rather than a devDependency. Today, tsup bundles it into the dist, so consumers don't need to install it — but this coupling is fragile and upgrades require a rebuild-and-republish.
### Notes
Full security scan findings (only items 1 and 2 are addressed in this PR):
1. **[Fixed] `path-to-regexp@6.2.2` ReDoS** — `packages/zsa-openapi` imports `pathToRegexp` at runtime for route matching in `createRouteHandlers`. v6.2.2 has [GHSA-9wv6-86v2-598j](https://github.com/advisories/GHSA-9wv6-86v2-598j); v6.3.0 fixes it. The lib is bundled into dist, so the fix needs to be released to reach consumers.
2. **[Fixed] `parseRequest` returned truthy `{}` on path segment-count mismatch** — caused a 500 instead of a 404 when a wildcard route matched a path with a different segment depth.
3. **[Report only] `next@14.2.3` in `examples/showcase`** has multiple advisories ([auth bypass](https://github.com/advisories/GHSA-f82v-jwr5-mffw), [cache poisoning](https://github.com/advisories/GHSA-gp8f-8m3g-qvj9), SSRF, etc.). This is a demo/docs app and not published to npm, so it doesn't affect consumers of `zsa` or `zsa-openapi`. Worth a separate bump to latest `14.2.x`.
4. **[Report only] `protect: true` is docs-only.** On `createOpenApiServerActionRouter().get("/posts/{postId}/replies/{replyId}", getReply, { protect: true })`, `protect` is only consumed by `generateOpenApiDocument` to mark the operation as requiring auth in the OpenAPI spec — it does NOT enforce authentication on the actual route handler. Users may reasonably expect it to. Either (a) document this more prominently at the `protect` option's JSDoc, or (b) add enforcement (e.g., 401 when the security scheme is absent from the request). Out of scope for this PR.
5. **[Report only] No CSRF protection on OpenAPI route handlers.** Next.js Server Actions have built-in same-origin CSRF checks; `createRouteHandlers` does not. Consumers who rely on cookies for auth on OpenAPI routes may be vulnerable to CSRF. This is an architectural choice (these are explicitly HTTP API endpoints), but worth documenting.
6. **[Report only] No hardcoded production secrets.** The only credential-looking string in the codebase is the literal `"123"` in `examples/showcase/content/docs/examples/configuring-openapi/actions.ts:47`, used as a placeholder API key in a docs example. Leave as-is but consider making it more obviously non-real (e.g. `"REPLACE_ME"`).
7. **[Report only] No SQL anywhere.** The library doesn't touch databases directly, so there is no SQL injection surface in this codebase.
8. **[Report only] Input validation** is by design delegated to zod (actions are required to declare an `.input(zodSchema)`). Unvalidated input is a consumer concern, not a library concern.
9. **[Report only] No CORS headers** are set in `createRouteHandlers`. This is fine — Next.js defaults to same-origin, which is the safe default. No overly-permissive CORS found.
10. **[Report only] No debug endpoints, no `eval`/`new Function`/`child_process` in library code.** Clean.
11. **[Report only] Prototype pollution — not exploitable.** `createRouteHandlers` spreads `...data`, `...searchParamsJson`, and `...params` into a single input object. Because object spread uses `CreateDataPropertyOrThrow` semantics and `JSON.parse`/`URLSearchParams` don't trigger the `__proto__` setter, an attacker sending `{"__proto__": {...}}` ends up with an own property, not a polluted prototype. Safe.
12. **[Report only] `npm audit`** flags 39 advisories, but after filtering, all high/critical ones are either (a) in `examples/showcase` transitive deps or (b) dev tooling (`@changesets/cli`, `@manypkg/cli`, `@playwright/test`). None affect published consumers of `zsa` / `zsa-openapi` / `zsa-react` / `zsa-react-query` beyond the `path-to-regexp` issue above.
Verified locally: `npm run build`, `npm run typecheck`, and `npm run test:jest` all pass (130/130 tests).
Link to Devin session: https://localhost:3000/sessions/864ca749bf354b099fc9806eef1eb7aa
Requested by: @IdoPesok
合并状态:未合并 3 条评论