Skip to content

fix(policy): handle unmapped HTTP methods in methodToAction (HEAD, OPTIONS)#3158

Merged
PierreBrisorgueil merged 5 commits intomasterfrom
worktree-httpmethodsfix
Feb 23, 2026
Merged

fix(policy): handle unmapped HTTP methods in methodToAction (HEAD, OPTIONS)#3158
PierreBrisorgueil merged 5 commits intomasterfrom
worktree-httpmethodsfix

Conversation

@PierreBrisorgueil
Copy link
Contributor

@PierreBrisorgueil PierreBrisorgueil commented Feb 23, 2026

Summary

  • Add `head: 'read'` and `options: 'read'` explicit mappings to `methodToAction` so HEAD and OPTIONS requests resolve to a valid CASL action instead of `undefined`
  • Add an early guard in `isAllowed` (before `defineAbilityFor`) that returns 405 Method Not Allowed for any method not present in the map, preventing incorrect authorization decisions and unnecessary CASL evaluation
  • Add 3 unit tests covering `isAllowed` for HEAD, OPTIONS, and an unknown method (PROPFIND)
  • Fix pre-existing Zod v4 migration issue: `z.string({ error: '...' })` does not override `invalid_type` messages — align schema and test with actual Zod v4 output

Closes #3137

Test plan

  • `npm run lint` passes
  • `npm test` passes (new tests: `isAllowed should call next() for HEAD`, `isAllowed should call next() for OPTIONS`, `isAllowed should deny unknown HTTP methods with 405`)

Summary by CodeRabbit

  • New Features

    • Added support for HTTP HEAD and OPTIONS to improve API compatibility.
  • Improvements

    • Unsupported HTTP methods now return 405 Method Not Allowed.
    • Validation error messages updated to reflect current framework behavior.
  • Documentation

    • Added guidance about updated validation messages.
  • Tests

    • Added unit and integration tests covering HEAD/OPTIONS behavior and updated validation errors.

Copilot AI review requested due to automatic review settings February 23, 2026 07:44
@coderabbitai
Copy link

coderabbitai bot commented Feb 23, 2026

No actionable comments were generated in the recent review. 🎉


ℹ️ Recent review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 32523c1 and ea37cef.

📒 Files selected for processing (3)
  • ERRORS.md
  • modules/core/tests/core.unit.tests.js
  • modules/tasks/tests/tasks.integration.tests.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • ERRORS.md
  • modules/core/tests/core.unit.tests.js

📝 Walkthrough

Walkthrough

Mapped HEAD and OPTIONS to 'read' in the policy middleware; isAllowed now returns 405 for unmapped HTTP methods. Removed custom Zod string error option from the Task schema and updated docs/tests to reflect Zod v4's default "Invalid input: expected string, received number." message.

Changes

Cohort / File(s) Summary
Policy middleware & tests
lib/middlewares/policy.js, modules/core/tests/core.unit.tests.js
Added head and options'read' to methodToAction. isAllowed now returns 405 for unmapped HTTP methods; unit tests added/updated to assert HEAD/OPTIONS pass and unknown methods return 405.
Task schema & integration test
modules/tasks/models/tasks.schema.js, modules/tasks/tests/tasks.integration.tests.js
Removed custom error option from title (z.string({ error: ... })z.string().trim().min(1)). Updated integration test expected validation message to Zod v4 default ("Invalid input: expected string, received number.").
Documentation
ERRORS.md
Added entry documenting that Zod v4 ignores the custom error option for invalid_type (e.g., z.string({ error: 'msg' })) and updated guidance accordingly.

Sequence Diagram(s)

mermaid
sequenceDiagram
rect rgba(220,220,255,0.5)
Client->>Server: HTTP request (method, route)
end
rect rgba(200,255,200,0.5)
Server->>PolicyMiddleware: isAllowed(req)
PolicyMiddleware->>PolicyMiddleware: map method -> action (get/post/put/patch/delete/head/options)
PolicyMiddleware->>Ability: ability.can(action, subject)
Ability-->>PolicyMiddleware: allowed/denied
end
rect rgba(255,220,220,0.5)
alt action undefined (unmapped)
PolicyMiddleware-->>Client: 405 Method Not Allowed
else allowed
PolicyMiddleware->>Next: next()
else denied
PolicyMiddleware-->>Client: 403 Forbidden
end

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I nibbled code by moonlight bright,

HEAD and OPTIONS now take flight,
Unknown knocks are met with "405" clear,
Zod speaks true — its default we hear,
Hop, test, and ship — the meadow's near!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding explicit mappings for HEAD and OPTIONS HTTP methods to fix how unmapped methods are handled in the policy middleware.
Description check ✅ Passed The description covers the main changes and includes a test plan, but is missing some required template sections like Risk level, Validation checklist, and Guardrails check.
Linked Issues check ✅ Passed The PR successfully implements all objectives from issue #3137: adds explicit head/options mappings, includes 405 error guard before CASL evaluation, and covers with unit tests.
Out of Scope Changes check ✅ Passed Changes to Zod schema and error messages in tasks schema/tests address a pre-existing v4 migration issue mentioned in commit summary; this is a necessary fix related to test accuracy rather than an out-of-scope change.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch worktree-httpmethodsfix

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Feb 23, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.86%. Comparing base (3dc239b) to head (ea37cef).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #3158   +/-   ##
=======================================
  Coverage   89.85%   89.86%           
=======================================
  Files          52       52           
  Lines        1163     1164    +1     
  Branches      233      234    +1     
=======================================
+ Hits         1045     1046    +1     
  Misses        107      107           
  Partials       11       11           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the authorization middleware to correctly handle HTTP methods that were previously unmapped (HEAD, OPTIONS), and adds unit tests to prevent regressions in method-to-action resolution.

Changes:

  • Extend methodToAction to map HEAD and OPTIONS to the read CASL action.
  • Add an early-deny guard in isAllowed for methods that don’t map to a CASL action.
  • Add unit tests covering isAllowed behavior for HEAD, OPTIONS, and an unknown method (PROPFIND).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
lib/middlewares/policy.js Adds HEAD/OPTIONS mappings and rejects unmapped methods to avoid ability.can(undefined, ...).
modules/core/tests/core.unit.tests.js Adds unit coverage for isAllowed with HEAD, OPTIONS, and an unmapped method.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
lib/middlewares/policy.js (1)

35-47: Missing @returns in JSDoc for async function.

Per coding guidelines, async functions must document the resolved value with @returns. The isAllowed JSDoc should include the return type.

Suggested JSDoc update
 /**
  * `@desc` MiddleWare to check if user is allowed
  * `@param` {Object} req - Express request object
  * `@param` {Object} res - Express response object
  * `@param` {Function} next - Express next middleware function
+ * `@returns` {Promise<void>} Calls next() or sends 403 response
  */
 const isAllowed = async (req, res, next) => {

As per coding guidelines: "Every new or modified function must have a JSDoc header: one-line description, @param for each argument, @returns for any non-void return value (always include @returns for async functions to document the resolved value)"

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/middlewares/policy.js` around lines 35 - 47, The JSDoc for the async
middleware is missing a `@returns` entry: update the JSDoc above the isAllowed
function to include a `@returns` describing the resolved value (e.g.,
Promise<void> or Promise<express.Response|void> as appropriate) so
callers/readers know what the async function resolves to; keep the existing
`@param` tags for req, res, next and reference the isAllowed middleware and its
behavior (it awaits defineAbilityFor, uses methodToAction and responses.error)
in the brief description.
ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 251183b and 2682f45.

📒 Files selected for processing (5)
  • ERRORS.md
  • lib/middlewares/policy.js
  • modules/core/tests/core.unit.tests.js
  • modules/tasks/models/tasks.schema.js
  • modules/tasks/tests/tasks.integration.tests.js
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@lib/middlewares/policy.js`:
- Around line 35-47: The JSDoc for the async middleware is missing a `@returns`
entry: update the JSDoc above the isAllowed function to include a `@returns`
describing the resolved value (e.g., Promise<void> or
Promise<express.Response|void> as appropriate) so callers/readers know what the
async function resolves to; keep the existing `@param` tags for req, res, next and
reference the isAllowed middleware and its behavior (it awaits defineAbilityFor,
uses methodToAction and responses.error) in the brief description.

Copilot AI review requested due to automatic review settings February 23, 2026 07:51
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Add explicit `head: 'read'` and `options: 'read'` mappings so HEAD and
OPTIONS requests are routed to the correct CASL action instead of
resolving to `undefined`. Add a guard in `isAllowed` that returns 403
for any unmapped method to prevent incorrect authorization decisions.

Closes #3137
In Zod v4, passing `error: 'string'` to `z.string()` does not override
the `invalid_type` issue message — the actual output is
'Expected string, received number'. Remove the no-op `error` option from
the Task schema and update the integration test expectation to match.

Add a note to ERRORS.md to prevent the same mistake in future migrations.
- Return 405 Method Not Allowed (instead of 403) for unmapped HTTP methods
  with a descriptive message to distinguish from permission denials
- Move the action lookup and early-return guard before defineAbilityFor
  to avoid unnecessary CASL import and rules evaluation for unsupported methods
- Update unit test expectation from 403 to 405 accordingly
Local node_modules had Zod v3 (3.25.76) masking the real v4 behavior.
Zod v4 (^4.3.6 from package.json) produces
'Invalid input: expected string, received number' — not the v3 message
'Expected string, received number'. Update the test expectation and the
ERRORS.md entry to match the actual Zod v4 output.
Copilot AI review requested due to automatic review settings February 23, 2026 08:10
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Following Copilot review: add assertions on message and description fields
in the 405 response to catch regressions if the error message format changes.
@PierreBrisorgueil PierreBrisorgueil merged commit 0792497 into master Feb 23, 2026
5 checks passed
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.

fix(policy): handle unmapped HTTP methods in methodToAction (HEAD, OPTIONS)

2 participants