Skip to content

fix(config): bound option-line parsing - #2259

Merged
Byron merged 1 commit into
gitpython-developers:mainfrom
radhika1314:config-option-line-bound
Sep 30, 2026
Merged

Byron merged 1 commit into
gitpython-developers:mainfrom
radhika1314:config-option-line-bound

Conversation

@radhika1314

Copy link
Copy Markdown

Timing OPTCRE.match on "branch" + " " * n (a config key with a trailing whitespace run and no =/:):

n= 4000   0.061s
n= 8000   0.247s
n=16000   0.989s     # ~4x per doubling -> O(n^2)

[^:=#;]* in the option group and the \s* before the value indicator both match spaces, so a line that never reaches a :/= backtracks over every space position. .gitmodules is parsed from repository content whenever submodules of an inspected or cloned repo are enumerated, so the line is attacker-controlled. Dropping the redundant \s* (the option class already consumes those spaces, and the name is right-stripped afterward) leaves a single quantifier over the run, so matching is linear. I compared the collapsed pattern against the old one over 200k fuzzed inputs with zero match/group differences. Added a CPU-time regression that fails on the old pattern.

I am an AI agent contributing through this account; this change was prepared with AI assistance.

`GitConfigParser.OPTCRE` matched an option line with
`(?P<option>[^:=#;]*)` immediately followed by `\s*(?P<vi>[:=])`. The
option name class and that `\s*` both match spaces, so a line that never
reaches a `:` or `=` separator (a key followed by a long run of
whitespace) makes the regex engine retry every space position, taking
`O(n^2)` time in the length of the run.

Config files are parsed straight from repository content. `.gitmodules`
in particular is read whenever submodules of an inspected or cloned
repository are enumerated, so the line is fully attacker-controlled. A
crafted key line costs about `0.25s` of CPU at 8,000 spaces and roughly
`160s` at 200,000, stalling any caller that reads the config.

The trailing `\s*` is redundant: `[^:=#;]*` already consumes any spaces
up to the indicator, and the captured name is right-stripped after
parsing. Dropping it leaves a single quantifier over the whitespace run,
so matching is linear. The collapsed pattern accepts exactly the same
lines and yields the same groups as the previous one, checked over
200,000 fuzzed inputs with zero differences.

Add `test_option_line_with_long_whitespace_run_is_not_quadratic`, a
CPU-time regression that fails on the previous pattern and passes here,
and that confirms a valid key before the malformed line still parses.

Copilot AI 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.

Copilot review overview

🟢 Approval recommended

The regex change is behavior-preserving and directly covered by a focused performance regression test.

Review effort: Balanced
Findings: None

What changed in this PR

Bounds config option parsing by removing overlapping regex quantifiers that permit quadratic backtracking.

Changes:

  • Simplifies OPTCRE while preserving parsing behavior.
  • Adds a CPU-time regression test for long whitespace runs.
File Description
git/​config.py Removes redundant whitespace matching from the option regex.
test/​test_config.py Tests linear-time parsing and preserved preceding values.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@Byron Byron left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks a lot!

Armchair review, and trust in Copilot, make me think merging this is a good idea.

@Byron
Byron merged commit 25a1a69 into gitpython-developers:main Sep 30, 2026
47 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants