Skip to content

esm: avoid super-linear data URL MIME regex - #61951

Open
skdas20 wants to merge 1 commit into
nodejs:mainfrom
skdas20:fix-61904-data-url-regex-backtracking
Open

esm: avoid super-linear data URL MIME regex#61951
skdas20 wants to merge 1 commit into
nodejs:mainfrom
skdas20:fix-61904-data-url-regex-backtracking

Conversation

@skdas20

Copy link
Copy Markdown
Contributor

Fixes: #61904

This updates ESM data URL MIME extraction regexes to remove overlapping quantifiers that allow super-linear backtracking.

  • Replaced (?:[^,]*?)(;base64)?, with (?:;[^,]*)?, in:
    • lib/internal/modules/esm/get_format.js
    • lib/internal/modules/esm/load.js

Behavior is preserved for existing valid/invalid data: URL shapes while avoiding pathological backtracking on crafted inputs.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/loaders

@nodejs-github-botnodejs-github-bot added esm Issues and PRs related to the ECMAScript Modules implementation. needs-ci PRs that need a full CI run. labels Feb 23, 2026

@jsumners-nrjsumners-nr left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Where are tests to assert the undesired behavior is resolved by the changes?

@skdas20

Copy link
Copy Markdown
ContributorAuthor

Added regression coverage in test/parallel/test-esm-data-url-format-regex-backtracking.js (commit 740d1320).

The test exercises defaultGetFormat() with a long malformed data: URL (data:a/aaaa...B) that used to trigger super-linear backtracking, and asserts the path now completes within a platform-adjusted bound.

Comment on lines +1 to +19
// Flags: --expose-internals
'use strict';

const common = require('../common');
const assert = require('assert');
const { defaultGetFormat } = require('internal/modules/esm/get_format');

// Regression test for #61904. This malformed data URL path is crafted to
// stress the MIME regex without triggering URL parsing failures.
const longPath = `data:a/${'a'.repeat(2 ** 17)}B`;
const start = process.hrtime.bigint();
const format = defaultGetFormat(new URL(longPath), { parentURL: undefined });
const elapsedMs = Number(process.hrtime.bigint() - start) / 1e6;

assert.strictEqual(format, null);
assert.ok(
elapsedMs < common.platformTimeout(1000),
`Expected format detection to complete quickly, took ${elapsedMs}ms`,
);

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.

This is going to be inherently flaky, could we instead write a benchmark?

@skdas20

Copy link
Copy Markdown
ContributorAuthor

CI has not started yet on this PR (currently no check runs, and needs-ci is present).

Could a collaborator please add the request-ci label to trigger Jenkins?

@JakobJingleheimer

Copy link
Copy Markdown
Member

There are no official approvals on this PR, so CI cannot be started (it will refuse).

@skdas20

Copy link
Copy Markdown
ContributorAuthor

Replaced the timing-based regression test with a benchmark (esm/get-data-protocol-format) parameterized over path length, per @aduh95's suggestion — a backtracking regression will now show up as an ops/sec cliff instead of relying on wall-clock assertions in CI.

Could a collaborator add request-ci to kick off Jenkins?

@avivkelleravivkeller added the request-ci Add this label to start a Jenkins CI on a PR. label Jul 25, 2026
@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Jul 25, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@skdas20
skdas20force-pushed the fix-61904-data-url-regex-backtracking branch from bd04ddd to 3487338CompareJuly 29, 2026 14:17
@skdas20

Copy link
Copy Markdown
ContributorAuthor

Squashed into a single signed commit and force-pushed — the diff is unchanged (regex fix in get_format.js/load.js plus the esm/get-data-protocol-format benchmark).

Doing this pre-emptively since @trivikr flagged the same missing Signed-off-by on #62010 and #62009, and this one is already approved — should be one less thing blocking the commit queue.

@mcollina

Copy link
Copy Markdown
Member

check the commit lint

@codecov

codecovBot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.14%. Comparing base (da5efc4) to head (cf0eb3b).
⚠️ Report is 1468 commits behind head on main.

Additional details and impacted files
@@ Coverage Diff @@## main #61951 +/- ##
==========================================
+ Coverage 88.84% 90.14% +1.30% 
==========================================
Files 674 746 +72 Lines 204957 242818 +37861 Branches 39309 45766 +6457 ==========================================
+ Hits 182087 218891 +36804 - Misses 15088 15420 +332 - Partials 7782 8507 +725 
Files with missing linesCoverage Δ
lib/internal/modules/esm/get_format.js95.20% <100.00%> (+1.10%)⬆️
lib/internal/modules/esm/load.js91.40% <100.00%> (-0.08%)⬇️

... and 520 files with indirect coverage changes

🚀 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.

The MIME regex used for data: URLs could backtrack super-linearly on a
malformed URL lacking a ',' separator. Make the optional parameter
group anchored on ';' so it cannot overlap with the media-type group.
Adds a benchmark (esm/get-data-protocol-format) parameterized over
path length, so a backtracking regression shows up as an ops/sec
cliff instead of a wall-clock assertion in a test, per review.
Fixes: nodejs#61904
Signed-off-by: skdas20 <skdas5405@gmail.com>
@skdas20

Copy link
Copy Markdown
ContributorAuthor

Thanks @mcollinaline-length was failing: one body line was 74 columns. Rewrapped the message so every line is within 72; the diff is unchanged.

@skdas20
skdas20force-pushed the fix-61904-data-url-regex-backtracking branch from 3487338 to cf0eb3bCompareJuly 29, 2026 16:26
@skdas20

Copy link
Copy Markdown
ContributorAuthor

Status summary for whoever picks this up: the PR is approved, and the lint-commit-message failure @mcollina flagged is fixed (one body line was 74 columns; rewrapped to 72). The force-push that fixed it was message-only — the tree is identical to the one that passed CI on 25 July (https://ci.nodejs.org/job/node-test-pull-request/75139/).

Could a collaborator add request-ci so Jenkins can re-run against the current commit? That looks like the only thing left before this can be queued.

@skdas20

Copy link
Copy Markdown
ContributorAuthor

@avivkeller@jsumners-nr — sorry to ping directly, but this has been sitting since 1 Aug and I think it only needs a label.

State: two approvals, GitHub Actions fully green on cf0eb3b7, mergeable_state is blocked solely on the outstanding needs-ci. The last force-push was message-only (rewrapping a 74-column line for lint-commit-message), so the tree is byte-identical to the one that passed Jenkins on 25 July: https://ci.nodejs.org/job/node-test-pull-request/75139/

Could one of you add request-ci? Happy to rebase first if you'd rather it run against a fresher base.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

esmIssues and PRs related to the ECMAScript Modules implementation.needs-ciPRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

non-linear back tracking in the regex used in getDataProtocolModuleFormat

7 participants

@skdas20@nodejs-github-bot@JakobJingleheimer@mcollina@aduh95@avivkeller@jsumners-nr