Skip to content

fix(deps): update follow-redirects to 1.16.1 - #35

Merged
1395490241lhj-code merged 2 commits into
mainfrom
fix/follow-redirects-audit
Oct 21, 2026
Merged

1395490241lhj-code merged 2 commits into
mainfrom
fix/follow-redirects-audit

Conversation

@1395490241lhj-code

@1395490241lhj-code 1395490241lhj-code commented Oct 21, 2026 •

Copy link
Copy Markdown
Owner

Summary

The deploy.yml CI gate is currently blocking PRs against main, including #34. This PR has two independent commits, one for each blocker:

Commit Scope Purpose
1dc789e fix(deps): patch follow-redirects security advisories package-lock.json +3/−3 Clears two high-severity advisories that fail npm audit --omit=dev --audit-level=high
f533d6d test(node22): avoid startup log deserialization failure test/ai-conversation-lifecycle.test.mjs +15/−0 Works around a Node 22 test-runner bug that crashes the lifecycle test file in CI

Neither commit changes production code, package.json, the CI workflow, the Node 22/24 matrix, test concurrency or any test assertion.

1. follow-redirects 1.16.1 security update (1dc789e)

follow-redirects@1.16.0 is flagged by:

The upstream fix is 1.16.1.

  • Lockfile change: only the version, resolved and integrity fields of node_modules/follow-redirects change. The integrity value matches the npm registry's 1.16.1 dist. A field-by-field compare of all 122 lockfile entries shows this is the only one that changed.
  • Why package.json is untouched: follow-redirects is transitive only. Its single dependent is the production dependency axios@1.20.0, which declares ^1.16.0, so 1.16.1 is in range.
  • Unchanged: overrides (there are none) and the audit gate. No advisories are ignored.
  • Upstream change: between 1.16.0 and 1.16.1 only two request-time paths change, array-form header normalization and escapeRegex. Load-time behaviour is the same.
  • Runtime impact: /api/ai-conversation and /api/ai-chat use the openai SDK, which has no lockfile dependencies and does not go through follow-redirects. axios is used by the SSRF guard and media fetch, both with maxRedirects: 0 and manual hop validation, and by the vision OCR POST with default redirect handling.

2. Node 22 test-runner compatibility (f533d6d)

On this PR, the test (22) job failed twice (original run and one re-run). Both times the error was in test/ai-conversation-lifecycle.test.mjs:

failureType: 'uncaughtException'
error: 'Unable to deserialize cloned data due to invalid or unsupported version.'

The file has two tests. The first reported ok; the runner crashed before it could read the second test's result. No assertion failed. Node 24 passed every step.

Root cause. The Node v22.23.3 parent runner (FileTest#processRawBuffer) finishes one v8 frame and then reads the next 4 bytes as a length, without checking for the frame header first. This is nodejs/node#64061. If those bytes are test stdout whose 3rd byte is ≥ 0x80, the length comes out negative and the runner deserializes garbage. nodejs/node#64706 fixed this with >>> 0, and the fix is in v24 and v26. It has not been backported to v22.x; see nodejs/node#65934. The trigger here is the server.js startup line, 🍳 Kitchen Manager 全栈服务已启动,端口 0, which begins with the bytes f0 9f 8d b3. Only the lifecycle tests really listen() and print it. Whether it crashes depends on that line landing in the same pipe read as the preceding frame, which explains the intermittent CI history.

Change. Inside startRealServer only, console.log is wrapped so that a line starting with 🍳 Kitchen Manager gets a [server] prefix, which is ASCII. All other output passes through unchanged. The wrapper is installed before require(server.js), because the line is printed later from the listen callback. afterEach restores the original console.log, before the server-close await.

Local evidence. These were run on Node v26.12.0. Node 22 is not installed locally, so this is not a real Node 22 run.

  • npm test: 1999/1999 pass, same test count as before. The lifecycle file passed 5 runs in a row.

  • Captured the lifecycle file's raw child-v8 stdout before and after the change:

    • before: one text segment directly after a frame would yield a negative length on v22, namely the emoji line;
    • after: none would;
    • stdout is 11 lines in both cases, and only the two startup lines gained the prefix. stderr is identical.
  • Replayed both captures through a line-for-line transcription of the v22.23.3 parseMessage/#processRawBuffer:

    Model Before After
    v22, whole stream in one read reproduces the exact CI error parses all 20 events
    v22, 5000 random reads coalescing 1–4 consecutive writes 3081 failures 0 failures
    v24.21.0 model 0 failures 0 failures
  • A temporary failure-injection copy of the file, since deleted, confirmed that console.log is restored after a failing test and after a failure before listen.

Residual risk.

  • Stdout bytes that mimic a full frame header can still break the runner, even on v24 (nodejs/node#66164). Current output contains none.
  • The workaround can be removed once v22.x ships #64706.

Test plan

  • Local npm test on Node v26.12.0: 1999/1999
  • npm audit --omit=dev --audit-level=high and npm audit: 0 vulnerabilities (before: exit 1 with the two advisories)
  • validate:recipe-packs / validate:recipe-pack-data pass
  • Loopback smoke with the real axios 1.20.0 and follow-redirects 1.16.1:
    • same-origin redirect is followed
    • maxRedirects: 0 does not follow
    • Authorization is stripped on a cross-host redirect
  • CI test (22): full suite, recipe validation and production audit on real Node 22
  • CI test (24): full suite, recipe validation and production audit

Notes

🤖 Generated with Claude Code

1395490241lhj-code and others added 2 commits October 20, 2026 20:56
Update the transitive follow-redirects dependency (via axios@1.20.0,
range ^1.16.0) from 1.16.0 to 1.16.1 in package-lock.json only, to
clear the high-severity advisories GHSA-q3jh-wrc6-jgq8 and
GHSA-8r9p-f939-6h3c reported by the production npm audit gate.

Only the version, resolved and integrity fields of that one entry
change. package.json, overrides and every other locked package are
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Node 22 test runner (v22.23.3) reads the 4 bytes after a consumed
v8 frame as the next frame length without re-checking for the frame
header (nodejs/node#64061). The fix, nodejs/node#64706, is in v24+ but
has not been backported to v22.x (nodejs/node#65934).

server.js prints its startup line starting with an emoji. When that
stdout line lands in the same pipe read right after a frame, Node 22
computes a negative length and fails the whole lifecycle test file with
"Unable to deserialize cloned data due to invalid or unsupported
version", even though every subtest passed.

In the real-HTTP lifecycle tests only, prefix that one startup line
with "[server] " so it starts with ASCII. Every other console.log call
passes through unchanged, and afterEach restores the original
console.log. No production code, assertion, CI matrix or concurrency
setting changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@1395490241lhj-code
1395490241lhj-code marked this pull request as ready for review October 21, 2026 01:47
@1395490241lhj-code
1395490241lhj-code merged commit 1a3d3b9 into main Oct 21, 2026
4 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.

1 participant