Skip to content

fix(ci): scan markdown fences in linear time - #462

Open
luvs01 wants to merge 12 commits into
devfrom
codex/propose-fix-for-regex-denial-of-service-ou1mku
Open

luvs01 wants to merge 12 commits into
devfrom
codex/propose-fix-for-regex-denial-of-service-ou1mku

Conversation

@luvs01

@luvs01 luvs01 commented Sep 7, 2026

Copy link
Copy Markdown
Owner

Motivation

  • A quadratic fenced-code regular expression in the carry-attribution parser made untrusted PR titles/bodies/commit messages a vector for CPU-consuming CI runs and could delay or stall privileged pull_request_target gates.

Description

  • Replaced the backreferenced fenced-code regex with a forward-only scanner implemented as stripFencedCode(text) and made strippedText call it so fence stripping is linear-time on attacker-controlled input.
  • The scanner preserves prior semantics for both closed and unclosed fences and trims fence lines before matching the closing delimiter to avoid false positives.
  • Adjusted exports and tests to exercise referencedCarryNumbers directly and added a regression that feeds many unclosed fence-like lines to validate correctness and bounded runtime.
  • Changes are contained to /.github/scripts/pr-carry-attribution.cjs and its test /.github/scripts/pr-carry-attribution.test.cjs.

Testing

  • Ran node --test .github/scripts/pr-carry-attribution.test.cjs and observed 19 tests passed and 0 failed.
  • Ran node --test .github/scripts/*.test.cjs and the relevant GitHub automation tests (including the new regression) passed.
  • Ran bun test tests/ci-workflows/ci-workflows.test.ts and it failed due to a local Bun runtime/YAML parsing environment mismatch unrelated to this patch.
  • Ran bun run prepush where typecheck and GUI lint steps passed while the full repo test phase surfaced environment-specific/timeout failures that are unrelated to the fenced-code fix.

Codex Task


Devin Review

lidge-jun and others added 8 commits September 6, 2026 01:08
…43-01a07240

[WRONG BRANCH] chore(release): promote verified candidate to 2.43.0
…in-07c0

chore(release): promote validated 2.44.0 to main
Promote frozen dev source cf9f662; no new runtime changes. Candidate CI34061274315 and service34061276621 are the validation references. Publication waits for successful validation and the final main push CI at the exact release SHA.
Promote frozen dev source cf9f662 as 2.45.0.

The repository owner explicitly authorized this main/preview release promotion and admin PR-only merge. This is a release-specific owner decision, not an independent approval or the dev-only maintainer exception.

Frozen candidate full CI34061274315 passed all25jobs after one unchanged-source rerun of Windows5; the initial holder busy assertion remains recorded without a root-cause resolution claim. Service lifecycle34061276621 passed Linux/macOS/Windows. Dev version pre-move3812 is merged. Publication still requires this actual main merge SHA's own successful push CI and Service lifecycle. No local suites were run.
[WRONG BRANCH] chore(release): promote verified 2.46.0 to main
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 7, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-07T07:17:44.061789Z d32b8d6 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

Deterministic PR hygiene checks passed.

@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Repository: luvs01/opencodex/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 0b78c7f3-ac61-47d6-bbf8-38e945033478


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

✅ READY

  • all PR quality gates passed.

Hygiene

Deterministic PR hygiene checks passed.

@github-actions github-actions Bot changed the title fix(ci): scan markdown fences in linear time [WRONG BRANCH] fix(ci): scan markdown fences in linear time Sep 7, 2026
@github-actions
github-actions Bot marked this pull request as draft September 7, 2026 07:10
@github-actions github-actions Bot added the bug Something isn't working label Sep 7, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d32b8d62c9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +92 to +95
while (line[contentEnd - 1] === " " || line[contentEnd - 1] === "\t") {
contentEnd--;
}
if (line.slice(contentStart, contentEnd) !== pendingFence) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Handle CRLF when matching closing fences

When a PR body or commit message uses CRLF line endings, splitting only at \n leaves \r at the end of each line, but this code trims only spaces and tabs; consequently, a closing fence such as ```\r never equals pendingFence. I checked .github/workflows/pr-hygiene.yml lines 156–174, which pass REST commit messages directly into this parser, so fenced carry language in a CRLF-formatted commit can be treated as a real declaration and spuriously block the PR for missing attribution. Normalize line endings or strip the terminal \r before comparing the delimiter.

Useful? React with 👍 / 👎.

@luvs01 luvs01 changed the title [WRONG BRANCH] fix(ci): scan markdown fences in linear time fix(ci): scan markdown fences in linear time Sep 7, 2026
@luvs01
luvs01 changed the base branch from main to Dev September 7, 2026 07:47
@github-actions github-actions Bot changed the title fix(ci): scan markdown fences in linear time [WRONG BRANCH] fix(ci): scan markdown fences in linear time Sep 14, 2026
@luvs01 luvs01 changed the title [WRONG BRANCH] fix(ci): scan markdown fences in linear time fix(ci): scan markdown fences in linear time Sep 16, 2026
@github-actions
github-actions Bot marked this pull request as ready for review September 16, 2026 11:05

@devin-ai-integration devin-ai-integration Bot 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.

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

Devin Review found 2 potential issues.

Devin Review

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Security review is required before merge

This changes repository automation under .github/. Repository policy requires explicit security review before merge.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread .github/scripts/pr-carry-attribution.cjs Outdated
…de a later block

Co-Authored-By: Epinephrine <luvs01@hanmail.net>
@devin-ai-integration

Copy link
Copy Markdown

Fixed in eba3476. stripFencedCode now indexes the lines that are exactly a fence run, then walks the text once: each opener pairs with the nearest later line carrying the same run, and an opener with no such line falls back to ordinary text so a later opener can still pair with its own close — matching the old regex's retry semantics in linear time. Added regressions for mixed-delimiter (~~~ block after unmatched ``` opener) and mixed-length fences, plus a check that carry language outside an unmatched opener is still read. node --test .github/scripts/pr-carry-attribution.test.cjs — 22/22 pass.

devin-ai-integration[bot]

This comment was marked as resolved.

Co-Authored-By: Epinephrine <luvs01@hanmail.net>
@devin-ai-integration

Copy link
Copy Markdown

Fixed in 0c0bccf.

stripFencedCode now indexes pure-fence lines per delimiter by run length and, for each opener, picks the longest close length still ahead that is at most the opener's run — the same outcome the backreference produced by giving back captured delimiters one at a time (greedy run first, then N-13). The earliest line carrying that length ends the block, so a ``` close still closes a ```` opener, and a farther full-length close outranks a nearer shorter one exactly as before. Line boundaries now also follow ^/`$` (CR, LF, U+2028, U+2029; CRLF as one terminator).

Exhausted lengths are retired through a disjoint-set cursor, so the scan stays near-linear — verified by fuzzing ~600k random documents against the original regex with zero output mismatches, plus regressions for the four-opening/three-closing case, the longest-close preference, and CRLF input. node --test .github/scripts/pr-carry-attribution.test.cjs — 25/25 pass.

@devin-ai-integration devin-ai-integration Bot 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.

Devin Review found 1 new potential issue.

Devin Review

Comment on lines +136 to +156
function closeFor(fence, openerLen, after) {
const scan = fenceIndex[fence];
let lo = 0;
let hi = scan.lengths.length;
while (lo < hi) {
const mid = (lo + hi) >> 1;
if (scan.lengths[mid] <= openerLen) lo = mid + 1;
else hi = mid;
}
const pos = lo - 1;
while (pos >= 0) {
const index = aliveAt(scan, pos);
if (index < 0) return -1;
const list = scan.lists[index];
let cursor = scan.cursors[index];
while (cursor < list.length && list[cursor] <= after) cursor++;
scan.cursors[index] = cursor;
if (cursor < list.length) return list[cursor];
scan.parent[index] = aliveAt(scan, index - 1);
}
return -1;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📝 Info: Previous fence semantics are preserved

The scanner selects the longest available closing run no longer than the opener. Exhaustive and randomized comparisons found no drift from the replaced regex.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

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

aardvark bug Something isn't working codex

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants