Skip to content

fix(api): guard dynamic action dispatch against prototype property lookup - #1117

Merged
collinsezedike merged 1 commit into
drydocs:mainfrom
EngrAlbaniy001:fix/tx-action-validation
Oct 20, 2026
Merged

collinsezedike merged 1 commit into
drydocs:mainfrom
EngrAlbaniy001:fix/tx-action-validation

Conversation

@EngrAlbaniy001

Copy link
Copy Markdown

Summary

  • Guarded the dynamic action route (api/v1/tx/[action].ts) against Object.prototype property lookup by replacing the in operator check with Object.prototype.hasOwnProperty.call(ACTIONS, value).
  • Disallowed prototype property names such as constructor, toString, valueOf, and __proto__ from validating as valid transaction actions, ensuring they cleanly return a 404 with { error: "Unknown action" }.
  • Added unit test coverage in api/__tests__/handlers.test.ts verifying that calls to /api/v1/tx/constructor, /api/v1/tx/toString, and unknown actions return 404.

Why

The dynamic route handler in api/v1/tx/[action].ts checked whether an action was valid using value in ACTIONS. Because in traverses the prototype chain of standard JavaScript objects, inherited properties like constructor, toString, valueOf, hasOwnProperty, and __proto__ passed the validation check.

When action=constructor, invoking ACTIONS[action](req.body) executed Object(req.body), returning the caller's parsed JSON payload. The route then executed res.status(result.status).json(result.body), allowing an unauthenticated caller to arbitrarily set the HTTP status code and response payload from a public endpoint. Inherited method names like toString returned non-RouteResult values lacking a numeric .status, throwing an unhandled exception and causing a 500 error instead of a clean 404.

Implementation

  • Modified isAction in api/v1/tx/[action].ts to check Object.prototype.hasOwnProperty.call(ACTIONS, value) instead of value in ACTIONS. This ensures only declared own properties of ACTIONS (deposit, withdraw, submit, add-trustline) pass validation.
  • Added a describe("POST /api/v1/tx/[action] action validation") test suite in api/__tests__/handlers.test.ts:
    • Tested action="constructor" returns 404 { error: "Unknown action" } and ignores status/body payload injection.
    • Tested action="toString" returns 404 { error: "Unknown action" } instead of throwing 500.
    • Tested unknown action names return 404.
  • Verified other dynamic segment routes (api/v1/vaults/[vaultId].ts, api/v1/positions/[publicKey].ts, api/v1/admin/[resource].ts, and api/v1/keepers/[action].ts): confirmed they already use Set.has lookup, key validation, or strict string equality and are not vulnerable to prototype property traversal.

Testing

  • pnpm --filter @meridian/api test __tests__/handlers.test.ts (60/60 tests passing, including 3 new test cases)
  • pnpm run typecheck:api (tsc --noEmit -p api/tsconfig.json exits with code 0)
  • pnpm run lint (turbo run lint exits with code 0)
  • npx prettier --check api/v1/tx/[action].ts api/__tests__/handlers.test.ts (Prettier code style check passes)

Scope / Risk

  • Scope: Narrow and localized to api/v1/tx/[action].ts and corresponding tests in api/__tests__/handlers.test.ts.
  • Risk: Very low. Legitimate declared actions (deposit, withdraw, submit, add-trustline) continue to function unchanged, while non-declared prototype keys that previously faulted or returned attacker-controlled bodies now consistently return 404.

Issue

Closes #1049

@vercel

vercel Bot commented Oct 18, 2026

Copy link
Copy Markdown

@EngrAlbaniy001 is attempting to deploy a commit to the Collins' projects Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave

drips-wave Bot commented Oct 18, 2026

Copy link
Copy Markdown

@EngrAlbaniy001 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Use Object.prototype.hasOwnProperty.call in isAction so constructor, toString, valueOf, and __proto__ cannot pass validation.

Add tests asserting constructor, toString, and unknown actions return 404.
@collinsezedike
collinsezedike force-pushed the fix/tx-action-validation branch from 3262e99 to 2907c09 Compare October 20, 2026 14:39
@collinsezedike
collinsezedike merged commit fa1d32e into drydocs:main Oct 20, 2026
10 of 11 checks passed
@collinsezedike

Copy link
Copy Markdown
Collaborator

@EngrAlbaniy001 thank you again for getting this over the line. If you are enjoying working on Meridian, starring the repository helps more contributors find the project and is much appreciated.

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.

[Bug] POST /api/v1/tx/constructor lets the caller choose the status code and response body

2 participants