Skip to content

deps: update lshpack to v2.3.5 - #31315

Open
github-actions[bot] wants to merge 2 commits into
mainfrom
deps/update-lshpack
Open

deps: update lshpack to v2.3.5#31315
github-actions[bot] wants to merge 2 commits into
mainfrom
deps/update-lshpack

Conversation

@github-actions

@github-actions github-actions Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Updates lshpack to version v2.3.5

Compare: litespeedtech/ls-hpack@8905c02...cf0f70d

Auto-updated by this workflow


no test proof · iteration 27 · Platform-specific test-only change; deferring to CI.

@robobun

robobun commented May 24, 2026

Copy link
Copy Markdown
Collaborator
Updated 10:52 PM PT - Aug 8th, 2026

@robobun, your commit d8e7f43 has 1 failures in Build #90779 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 31315

That installs a local version of the PR into your bun-31315 executable, so you can run:

bun-31315 --bun

@coderabbitai

coderabbitai Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The PR retargets the LSHPACK_COMMIT constant to a new upstream commit SHA and updates the process.versions test to expect the new lshpack commit hash.

Changes

Dependency Update

Layer / File(s) Summary
Update lshpack commit pin and test
scripts/build/deps/lshpack.ts, test/js/node/process/process.test.js
Retarget LSHPACK_COMMIT to a new commit SHA; update the process.versions test's lshpack expected hash to match.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the lshpack dependency update to version v2.3.5.
Description check ✅ Passed The description explains the dependency update and includes verification status, although it uses a different heading level and omits the template heading.

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

Comment thread scripts/build/deps/lshpack.ts
@Jarred-Sumner

Copy link
Copy Markdown
Collaborator

@robobun fix process.test.js in this branch

@robobun

robobun commented May 26, 2026

Copy link
Copy Markdown
Collaborator

✅ Fixed at d8e7f43process.test.js pins lshpack: "cf0f70dd10b352194c97448eb5d00b4aa484f531" matching LSHPACK_COMMIT, verified on a fresh build. process.versions is green on every lane it runs on.

Twelfth re-apply. The weekly update-lshpack cron force-pushes this branch with only the dep file, so any test commit here is deleted every Sunday; this cannot converge from the branch side.

#29295 is the permanent fix (the test derives its expected hashes from scripts/build/deps/*.ts). It has now rotted into merge conflict six times, each because another dep bump on main (boringssl, mimalloc three times, tinycc, lolhtml) collided with the hardcoded block it deletes — which is itself the demonstration of why it should land. Rebased again (45977f9), verified passing with all current hashes derived correctly, mergeable.

The open review thread about handle_push_promise_frame missing is_malformed_field_name is a non-blocking semantic note on the dep bump itself, separate from this test fix.

@github-actions
github-actions Bot force-pushed the deps/update-lshpack branch from c098aeb to 08ba1cb Compare May 31, 2026 06:26
Comment thread scripts/build/deps/lshpack.ts
@github-actions
github-actions Bot force-pushed the deps/update-lshpack branch from ca8280f to 64dd4b3 Compare June 7, 2026 06:27
Comment thread scripts/build/deps/lshpack.ts
Comment thread scripts/build/deps/lshpack.ts
Comment thread scripts/build/deps/lshpack.ts
@github-actions
github-actions Bot force-pushed the deps/update-lshpack branch from 72d5979 to a2df3f1 Compare June 21, 2026 06:32
Comment thread scripts/build/deps/lshpack.ts
@github-actions
github-actions Bot force-pushed the deps/update-lshpack branch from 056cc44 to 3eb7950 Compare June 28, 2026 06:26
Comment thread scripts/build/deps/lshpack.ts
@github-actions
github-actions Bot force-pushed the deps/update-lshpack branch from 3dfe472 to 3757eb1 Compare July 5, 2026 06:15
@robobun

robobun commented Jul 5, 2026

Copy link
Copy Markdown
Collaborator

Re-applied the test hash for the seventh time (59cabbc) after this Sunday's update-lshpack run force-pushed the branch again. process.versions passes on a fresh build.

The loop is the add-paths + fixed-branch behavior in .github/workflows/update-lshpack.yml: every cron run rebuilds the branch from main with only scripts/build/deps/lshpack.ts and force-pushes, deleting any companion test commit. So this PR is only mergeable in the window between a re-apply and the next Sunday.

Two ways out, either is fine:

  • Merge this PR now (HEAD 59cabbc, both files in sync).
  • Merge test: derive process.versions expectations from scripts/build/deps #29295 first, which makes the test derive its expected hashes from scripts/build/deps/*.ts. After that no dep bump ever touches this test and every update-*.yml PR stops arriving red. I just rebased it onto current main (it had drifted into conflict when boringssl was bumped) and re-verified it: passes on a fresh build, and still fails correctly when a pin and the binary disagree.

Comment thread scripts/build/deps/lshpack.ts
@github-actions
github-actions Bot force-pushed the deps/update-lshpack branch from 59cabbc to 6bacbd2 Compare July 12, 2026 05:58
Comment thread scripts/build/deps/lshpack.ts
import type { Dependency, DirectBuild } from "../source.ts";

const LSHPACK_COMMIT = "8905c024b6d052f083a3d11d0a169b3c2735c8a1";
const LSHPACK_COMMIT = "cf0f70dd10b352194c97448eb5d00b4aa484f531";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 The earlier (resolved) comment on v2.3.5's removed trailing-whitespace strip is now stale for the two paths it named — both have since gained full tchar validation — but a fourth decode loop was missed: handle_push_promise_frame (src/runtime/api/bun/h2_frame_parser.rs:3678-3712) still surfaces header.name/header.value straight to JS with no is_malformed_field_name/is_malformed_field_value check between self.decode() at :3679 and create_utf8_for_js at :3697. After this bump a PUSH_PROMISE literal name like "cookie \t\r\n" or " " (previously trimmed/rejected by lshpack itself) reaches the node:http2 client's 'push' event verbatim. Client-only and requires a non-compliant server sending PUSH_PROMISE, so not blocking — but it's the one residual gap in the class fix; mirroring decode_header_block's guard (h2_frame_parser.rs:3828-3836) after :3692 would close it.

Extended reasoning...

What changed and why the earlier comment is stale

The resolved 2026-05-24 inline comment on this PR flagged that lshpack v2.3.5 (cf0f70dd) deletes from lshpack_dec_decode() the block while(len > 0 && isspace(*(name + len - 1))) --len; if (len == 0) return LSHPACK_ERR_BAD_DATA;, so literal header names with trailing whitespace now decode verbatim instead of being trimmed/rejected. Upstream's own new test test_hdec_trailing_whitespace_name asserts a literal name of cookie \t\v\f\r\n is returned with name_len == 12 and rc == 0 — validation is now the caller's job.

That comment named two Bun call sites (dispatch.zig:546, h2_frame_parser.zig:1918-1965). Both are now covered by full RFC 9110 tchar validation — the class fix landed:

  • src/http/h2_client/dispatch.rs:621is_malformed_response_field (dispatch.rs:737-762) rejects any byte outside the lowercase-tchar set
  • src/runtime/api/bun/h2_frame_parser.rs:3829decode_header_block calls is_malformed_field_name(header.name) || is_malformed_field_value(header.value) (defined at :627-656)
  • src/runtime/api/bun/h2/connection.rs:910 calls the same helper

Grep confirms is_malformed_field_name is called at exactly those sites — and not in handle_push_promise_frame.

The residual gap

handle_push_promise_frame at h2_frame_parser.rs:3678-3712 is the fourth HPACK decode loop, and it was missed by the class fix:

while off < payload.len() {
    let header = match self.decode(&payload[off..]) {   // :3679
        Ok(h) => h,
        Err(_) => { /* GOAWAY COMPRESSION_ERROR */ return Ok(end); }
    };
    off += header.next;                                  // :3692
    let js_name = match get_http2_common_string(...) {
        Some(cached) => cached,
        None => create_utf8_for_js(&global_object, header.name)?,   // :3697
    };
    headers.push(&global_object, js_name)?;
    headers.push(&global_object, create_utf8_for_js(&global_object, header.value)?)?;  // :3703
    ...
}

Between self.decode() and create_utf8_for_js there is no call to is_malformed_field_name / is_malformed_field_value, no pseudo-header check, no connection-specific-field check, and no maxHeaderListSize/maxHeaderListPairs accounting. Everything decoded is pushed straight into the JS headers array and dispatched via onStreamPush. Contrast with the sibling loop at :3828-3836, which gates on is_malformed_field_name(header.name) || is_malformed_field_value(header.value) || (header.name.first() == Some(&b':') && !is_valid_..._pseudo_header(...)) before surfacing anything.

The only remaining upstream guard is lshpack.rs:67's name_len == 0 → EmptyHeaderName, which catches truly-empty names but not whitespace-bearing ones (name_len > 0 after v2.3.5).

Interaction with this PR (step-by-step)

  1. A server sends a PUSH_PROMISE frame whose HPACK block contains a literal-header-field with a non-Huffman name string of 63 6f 6f 6b 69 65 20 09 0d 0a ("cookie \t\r\n", 10 bytes).
  2. Before this bump (8905c024): lshpack_dec_decode decodes len = 10, the trailing-whitespace loop strips the last four bytes, len = 6, name is "cookie". get_http2_common_string matches the well-known index, and JS sees the canonical key.
  3. After this bump (cf0f70dd): the loop is gone, len stays 10, header.name = b"cookie \t\r\n". well_know is 0 (no static-table hit for a 10-byte name), so control falls through to create_utf8_for_js(&global_object, header.name) at :3697 and JS receives a header keyed "cookie \t\r\n".
  4. All-whitespace case: a literal name of 20 20 20 (" "). Before, lshpack stripped to len = 0return LSHPACK_ERR_BAD_DATAErr(_) at :3681 → COMPRESSION_ERROR GOAWAY. After, name_len = 3 → passes lshpack.rs:67 → surfaces to JS as a header keyed " ".

So the LSHPACK_COMMIT bump at line 13 observably widens what a node:http2 client's 'push' event sees, on this one path — it is not purely pre-existing.

Why nothing else catches it

is_malformed_field_name (:627-656) is a lowercase-tchar allowlist and would reject space/tab/CR/LF — it just isn't wired into this loop. The path is client-only (is_server sends GOAWAY at :3648-3656), and the frame-type dispatch calls handle_push_promise_frame unconditionally for type 0x05 with no enable_push gate, so it's reachable from any server the client connects to.

Impact

Low. Requires a deliberately non-compliant server sending PUSH_PROMISE (a largely-deprecated feature) with a crafted literal header name. Worst outcome is an odd header key surfaced to JS — no crash, no memory issue, no security boundary crossed. The missing maxHeaderListSize/maxHeaderListPairs accounting on this path is fully pre-existing and independent of this bump. Not worth blocking a routine dep bump; flagged so the one-line guard can be added to complete the class fix.

Suggested fix

Mirror decode_header_block's guard — after h2_frame_parser.rs:3692:

if is_malformed_field_name(header.name) || is_malformed_field_value(header.value) {
    // set a malformed flag and continue decoding (to keep HPACK dynamic-table state in sync),
    // then RST/GOAWAY PROTOCOL_ERROR after the loop instead of dispatching onStreamPush
}

@github-actions
github-actions Bot force-pushed the deps/update-lshpack branch from 162ae3a to c4c22bf Compare July 19, 2026 05:57
Comment thread scripts/build/deps/lshpack.ts
@github-actions
github-actions Bot force-pushed the deps/update-lshpack branch from 1f9e6a7 to ee439df Compare July 26, 2026 05:58
Comment thread scripts/build/deps/lshpack.ts
@github-actions
github-actions Bot force-pushed the deps/update-lshpack branch from d324c10 to 88e1ac3 Compare August 2, 2026 05:57
Comment thread scripts/build/deps/lshpack.ts
@github-actions
github-actions Bot force-pushed the deps/update-lshpack branch from a5a4c26 to a39bf77 Compare August 9, 2026 05:19
Comment thread scripts/build/deps/lshpack.ts
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