Skip to content

fix(bash) match a function header only at a word boundary - #4552

Open
MaciejZet wants to merge 1 commit into
highlightjs:mainfrom
MaciejZet:fix/bash-function-backtracking
Open

MaciejZet wants to merge 1 commit into
highlightjs:mainfrom
MaciejZet:fix/bash-function-backtracking

Conversation

@MaciejZet

Copy link
Copy Markdown

Refs #4529

Changes

The Bash function rule could start in the middle of a word. On a long word it retried the name from every character while looking for () {, so the cost grew with the square of the input. A 64 KB word took about 2.3 s; with the word-boundary anchor the same input finishes in under a millisecond.

foo() {, my_func () {, and a header after another command still highlight the function name. This is one grammar from #4529, so the issue stays open.

Checklist

  • Added markup tests, or they don't apply here because...
  • I have read and followed our AI-assisted contributions policy (human review, no slop)

A long word was rescanned from every character while the grammar looked for `() {`.
@MaciejZet

Copy link
Copy Markdown
Author

Checked 93be485 locally on macOS using npm ci: all 10 CI build configurations pass on Node 22.23.3, 24.22.0 and 26.12.0. The full Node suite has 1654 passing / 3 pending on each version; browser/CDN suites have 12 passing each (jsdom/tiny-worker). Lint and CommonJS/ESM/Rollup checks also pass.

The exact base passes its matrix too, and an additional 38-case Bash corpus produces identical highlighting before and after the patch.

Could a maintainer approve the pending workflows? Node.js CI, Lint and Create Build Size Report show action_required with no jobs executed.

@PNHD PNHD 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.

Independent check of head 93be485155d67c0fa55e43f2af32863901729580 against base fc3f06392f189354eed922973635ab9e9268b983, with AI assistance.

On Node 24.19.0 I loaded the unmodified ESM core and Bash grammar through a Node vm module linker (17 Git blob hashes verified), then exercised hljs.highlight in debug mode. For a 65,536-character word, base/head took 3627.02/0.51 ms; with trailing (), 4184.26/0.60 ms. The 8/16/32 KiB cases also completed with the fixed grammar. These are single local measurements, not portable timing guarantees.

A 22-input Bash corpus produced identical HTML and relevance on base/head, covering plain/underscored names, whitespace, declarations after commands, comments, quoted text and incomplete headers. Four explicit title assertions passed, including echo hello; my_func() { echo hi; }.

No functional regression found within this scope. Build, Mocha, lint, browser and full suites were not run; this does not independently verify the author's full matrix.

One repository-convention follow-up: AGENTS.md asks touched modes to use scope instead of className and remove deprecated relevance. Could you align the touched FUNCTION mode with that guidance?

@MaciejZet

Copy link
Copy Markdown
Author

I’ve prepared the change locally: FUNCTION now uses scope, and relevance: 0 is removed. The regex is unchanged.

All 10 CI build configurations passed locally on macOS with Node 22/24/26, before and after the change: 1,654 passing / 3 existing pending tests per Node suite, and 12 passing per browser/CDN suite (jsdom/tiny-worker), including the unminified build. Lint, Rollup and CJS/ESM import checks passed too. These are local results; hosted CI still needs approval.

Explicit Bash output was unchanged across 38 fixtures. Removing relevance: 0 gives the mode its default score of 1, so autodetection does change: across 253 inputs, 7 winners changed with all languages and 10 with :common. Winners for the 199 corpus samples were unchanged; existing corpus mismatches and debug-mode Nix failures remained.

For example, function foo() { return 0; } switches from JavaScript to Bash in a restricted language set, and from C# to Bash in :common. The extra point can change ties, including for non-Bash input.

@PNHD

PNHD commented Oct 21, 2026

Copy link
Copy Markdown

Thanks for checking the autodetection impact. I withdraw my suggestion to remove relevance: 0 here. Please retain it to keep the ReDoS fix behaviorally narrow; scoring changes should be discussed separately with the core maintainers. The className to scope change can be considered independently.

@MaciejZet

Copy link
Copy Markdown
Author

I tested a separate local variant of 93be485 that changes FUNCTION.className to scope and keeps relevance: 0. The regex is unchanged.

All 10 CI build configurations passed locally on macOS with Node 22/24/26: 1,654 passing / 3 existing pending tests per Node suite, and 12 passing per browser/CDN suite (jsdom/tiny-worker). Lint, Rollup and import checks passed too.

Across 253 inputs, the full-language and CDN :common builds produced identical autodetection results, scores and HTML to the current PR on all three Node versions. Existing corpus mismatches and Nix debug errors were unchanged.

Could a core maintainer confirm whether keeping relevance: 0 here is an acceptable exception to AGENTS.md, to keep the ReDoS fix narrow? This variant remains local; hosted CI still awaits approval.

This branch has not been deployed

No deployments
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