Skip to content

fix(security): polynomial ReDoS in comboAgentMiddleware regex → main (CodeQL #612/#613) - #3983

Merged
diegosouzapw merged 1 commit into
mainfrom
fix/main-comboagent-redos
Jun 16, 2026
Merged

diegosouzapw merged 1 commit into
mainfrom
fix/main-comboagent-redos

Conversation

@diegosouzapw

Copy link
Copy Markdown
Owner

Hotfix to main of the ReDoS fix already merged to release/v3.8.27 (#3982), so CodeQL js/polynomial-redos alerts #612/#613 close on the next main scan instead of waiting for the v3.8.27 ship. Code + regression test only (the [3.8.27] CHANGELOG bullet stays on release/v3.8.27; the file is identical on both branches so the v3.8.27 release merge is conflict-free). 107 related comboAgent tests green.

…ex on main (CodeQL js/polynomial-redos #612/#613)

Brings the release/v3.8.27 fix (#3982) to main so CodeQL alerts #612/#613 close
on the next scan. Code + regression test only; the [3.8.27] CHANGELOG bullet lives
on release/v3.8.27 and reaches main when v3.8.27 ships (identical file → no merge
conflict). Detection pattern drops the unbounded surrounding newline run; global
strip pattern bounds it ({0,16}). Behavior unchanged (107 related tests green).
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@diegosouzapw
diegosouzapw merged commit 7509a32 into main Jun 16, 2026
4 checks passed
@diegosouzapw
diegosouzapw deleted the fix/main-comboagent-redos branch June 16, 2026 11:46

@gemini-code-assist gemini-code-assist Bot left a comment

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.

Code Review

This pull request addresses a polynomial ReDoS vulnerability by removing unbounded newline runs from the CACHE_TAG_PATTERN regex and bounding them to {0,16} in CACHE_TAG_PATTERN_GLOBAL. It also adds regression tests to verify linear performance and correct tag stripping. Feedback on the changes highlights a potential bug in the global stripping pattern: consuming up to 16 newlines on both sides of the tag can cause adjacent text (such as chat history lines) to merge, corrupting the structure. A suggestion is provided to only consume newlines on the trailing side of the tag and to update the regression tests to assert that paragraph breaks are preserved.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +54 to +55
const CACHE_TAG_PATTERN_GLOBAL =
/(?:\\n|\n|\r){0,16}<omniModel>([^<]+)<\/omniModel>(?:\\n|\n|\r){0,16}/g;

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.

high

⚠️ Potential Chat History Corruption / Word Merging Bug

By matching and stripping up to 16 newlines both before and after the <omniModel> tag, any text preceding and succeeding the tag will be merged together.

For example, in an inlined chat history (like the Open WebUI follow-up requests mentioned in the comments), you might have:

Assistant: Hello!
<omniModel>gpt-4</omniModel>

User: How are you?

With the current regex, the \n before and \n\n after the tag are both consumed and replaced with "", resulting in:

Assistant: Hello!User: How are you?

This merges the assistant's message with the user's message, corrupting the chat history structure sent to the provider.

Suggested Fix

To avoid merging words/lines while still cleaning up the tag and its surrounding blank lines, we should only consume the newline run on one side (e.g., the trailing side). Any leading newlines before the tag will either act as the single natural separator between the preceding and succeeding text, or (if the tag was at the very end of the message) will be cleanly stripped by the subsequent .trimEnd() call.

Suggested change
const CACHE_TAG_PATTERN_GLOBAL =
/(?:\\n|\n|\r){0,16}<omniModel>([^<]+)<\/omniModel>(?:\\n|\n|\r){0,16}/g;
const CACHE_TAG_PATTERN_GLOBAL =
/<omniModel>([^<]+)<\/omniModel>(?:\\n|\n|\r){0,16}/g;

Comment on lines +93 to +100
test("stripModelTags removes the newline run wrapping a tag (no blank line left)", () => {
const out = String(
stripModelTags([{ role: "user", content: "before\n\n<omniModel>a/b</omniModel>\n\nafter" }])[0]
.content
);
assert.ok(!out.includes("<omniModel>"), "tag removed");
assert.ok(!out.includes("\n\n\n"), "no triple newline left from stripping");
});

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.

medium

🧪 Update Regression Test to Prevent Word Merging

Let's update the regression test to assert that the paragraph break is preserved and words are not merged when stripping the tag.

  test("stripModelTags removes the newline run wrapping a tag (no blank line left)", () => {
    const out = String(
      stripModelTags([{ role: "user", content: "before\n\n<omniModel>a/b</omniModel>\n\nafter" }])[0]
        .content
    );
    assert.ok(!out.includes("<omniModel>"), "tag removed");
    assert.ok(!out.includes("\n\n\n"), "no triple newline left from stripping");
    assert.equal(out, "before\n\nafter", "should preserve the paragraph break and not merge words");
  });

@github-actions

Copy link
Copy Markdown
Contributor

CI Coverage Report

  • Coverage job: success
  • PR test policy: failure

Coverage artifact was not available for this run.

PR Test Policy

This PR changes production code in src/, open-sse/, electron/, or bin/ without accompanying automated tests.

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)
B Maintainability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
…iegosouzapw#3983)

Brings the release/v3.8.27 fix (diegosouzapw#3982) to main so CodeQL alerts diegosouzapw#612/diegosouzapw#613 close
on the next scan. Code + regression test only; the [3.8.27] CHANGELOG bullet lives
on release/v3.8.27 and reaches main when v3.8.27 ships (identical file → no merge
conflict). Detection pattern drops the unbounded surrounding newline run; global
strip pattern bounds it ({0,16}). Behavior unchanged (107 related tests green).
Poid-ZA pushed a commit to Poid-ZA/OmniRoute that referenced this pull request Aug 5, 2026
…iegosouzapw#3983)

Brings the release/v3.8.27 fix (diegosouzapw#3982) to main so CodeQL alerts diegosouzapw#612/diegosouzapw#613 close
on the next scan. Code + regression test only; the [3.8.27] CHANGELOG bullet lives
on release/v3.8.27 and reaches main when v3.8.27 ships (identical file → no merge
conflict). Detection pattern drops the unbounded surrounding newline run; global
strip pattern bounds it ({0,16}). Behavior unchanged (107 related tests green).
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…iegosouzapw#3983)

Brings the release/v3.8.27 fix (diegosouzapw#3982) to main so CodeQL alerts diegosouzapw#612/diegosouzapw#613 close
on the next scan. Code + regression test only; the [3.8.27] CHANGELOG bullet lives
on release/v3.8.27 and reaches main when v3.8.27 ships (identical file → no merge
conflict). Detection pattern drops the unbounded surrounding newline run; global
strip pattern bounds it ({0,16}). Behavior unchanged (107 related tests green).
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