Skip to content

Fix hoek prototype pollution in examples via @hapi/joi and @hapi/boom - #14

Open
ch-asimakopoulos wants to merge 1 commit into
security/lib-hoekfrom
security/examples-hoek
Open

Fix hoek prototype pollution in examples via @hapi/joi and @hapi/boom#14
ch-asimakopoulos wants to merge 1 commit into
security/lib-hoekfrom
security/examples-hoek

Conversation

@ch-asimakopoulos

Copy link
Copy Markdown

Chain 4 of 4. Base: security/lib-hoek (#12). Resolves alert #2 (hoek — prototype pollution via the clone function).

Dependency Before After hoek
boom ^7.3.0 @hapi/boom ^10.0.1 6.1.2 → @hapi/hoek 11.0.7 ✓
joi ^13.6.0 @hapi/joi ^15.1.1 5.0.4 → @hapi/hoek 8.5.1 ✓

Require paths updated in the four api actions that use them: create.js, delete.js, info.js, update.js.

Why this is chained, not independent

examples defines joi schemas that are consumed by lib's build.js/validate.js. After chain 3 moved lib to @hapi/joi 15, leaving examples on joi 13 would hand a joi 13 schema object to @hapi/joi 15's validator — a cross-version mismatch. The two have to move together.

Verification — end to end, not just audit

  • examples/package-lock.json now reports zero advisories at every severity. That also clears the five highs I'd flagged as out-of-scope back in the koa ReDoS PR (Fix critical ReDoS in koa (examples) #1) — they were all reached through joi 13 and boom 7.
  • /api/v1/discovery returns correct schema-derived output, exercising describe().children over @hapi/joi schemas defined here and read by lib.
  • Full client.js demo passes against the chain 3 lib: create → list → update → list → delete → list, all expected payloads.
  • Lockfile regenerated at lockfileVersion 3 to match master. An earlier pass used --lockfile-version 1 out of habit from the other two projects, which would have silently reverted the v3 upgrade that arrived with the qs bump.

⚠️ Pre-existing bug found while testing — not introduced here, not fixed here

Validation failures and Boom.notFound() both return HTTP 500 instead of 422/404.

Cause: Boom sets its status on err.output.statusCode and leaves err.status/err.statusCode undefined, while Koa reads err.status/err.statusCode to build the response. examples/server.js installs no error-handling middleware to bridge them, so every Boom error surfaces as a 500.

boom@7          err.status = undefined  err.statusCode = undefined  err.output.statusCode = 404
@hapi/boom@10   err.status = undefined  err.statusCode = undefined  err.output.statusCode = 404

Identical shape, so this migration is neutral with respect to the bug.

Reproduced at 10c5d9d — Sokratis Vidros' last commit, 2021-05-29 — on the original joi 13 / joi 14 / boom 7 / koa 2.5.2 dependency set:

-- missing required param --  Internal Server Error -> HTTP 500
-- wrong type --              Internal Server Error -> HTTP 500
-- not found (Boom.notFound)  Internal Server Error -> HTTP 500

server log:
  Error: child "movie_id" fails because ["movie_id" is required]. child "title" fails ...
  Error: child "movie_id" fails because ["movie_id" must be a number]
  Error: Not Found

The Boom errors are thrown correctly with their joi messages intact — only the HTTP status is lost. The bug predates all of today's work and the 2022 node-fetch bump.

The fix would be a couple of lines of Koa middleware in examples/server.js translating err.isBoomctx.status = err.output.statusCode + ctx.body = err.output.payload. I left it out to keep this PR a pure dependency change — say the word and I'll add it here or as a separate PR.

🤖 Generated with Claude Code

Chain 4 of 4. Resolves alert #2 (hoek, prototype pollution via the clone
function) in examples/package-lock.json.

  boom ^7.3.0  -> @hapi/boom ^10.0.1  (hoek 6.1.2 -> @hapi/hoek 11.0.7)
  joi  ^13.6.0 -> @hapi/joi  ^15.1.1  (hoek 5.0.4 -> @hapi/hoek 8.5.1)

Require paths updated in the four api actions that use them: create.js,
delete.js, info.js and update.js.

This is not optional cleanup. examples defines joi schemas that are
consumed by lib's build.js and validate.js, so after chain 3 moved lib to
@hapi/joi 15, leaving examples on joi 13 would hand a joi 13 schema object
to @hapi/joi 15's validator - a cross-version mismatch. The two have to
move together, which is why this is chained onto chain 3 rather than
targeting master.

examples/package-lock.json now reports zero advisories at every severity.
That also clears the five highs flagged as out of scope back in the koa
ReDoS PR, since they were all reached through joi 13 and boom 7.

Verified end to end against the chain 3 lib, not just by audit:

  - /api/v1/discovery returns the correct schema-derived output, which
    exercises describe().children over @hapi/joi schemas defined here and
    read by lib.
  - The full client.js demo passes: create, list, update, list, delete,
    list all return the expected payloads.

The lockfile is regenerated at lockfileVersion 3 to match what is on
master. An earlier pass used --lockfile-version 1 out of habit from the
other two projects, which would have silently reverted the v3 upgrade that
came in with the qs bump.

Pre-existing issue found while testing, not introduced here and not fixed
here: validation failures and Boom.notFound both return HTTP 500 rather
than 422/404.

Boom sets its status on err.output.statusCode and leaves err.status and
err.statusCode undefined, while Koa reads err.status/err.statusCode to
build the response. examples/server.js installs no error-handling
middleware to bridge the two, so every Boom error surfaces as a 500. Both
boom 7 and @hapi/boom 10 have that same shape, so this migration is
neutral with respect to the bug.

Reproduced at 10c5d9d, Sokratis Vidros' last commit from 2021-05-29, on
the original joi 13 / joi 14 / boom 7 / koa 2.5.2 dependency set: the same
three requests return 500 there too, with the server log showing the Boom
errors thrown correctly and their joi messages intact. The bug predates
all of today's work and the 2022 node-fetch bump.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ch-asimakopoulos
ch-asimakopoulos marked this pull request as ready for review July 31, 2026 14:54
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