Skip to content

fix(adopt): two real bugs found in a live adoption (ReDoS + slug-hyphen breakage) - #47

Merged
githubrobbi merged 3 commits into
mainfrom
fix/adopt-redos-edition-regex
Jul 20, 2026
Merged

fix(adopt): two real bugs found in a live adoption (ReDoS + slug-hyphen breakage)#47
githubrobbi merged 3 commits into
mainfrom
fix/adopt-redos-edition-regex

Conversation

@githubrobbi

@githubrobbi githubrobbi commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Two independent bugs in adopt.sh, both found live during an adoption into an existing repo (uffs-products), not from code review:

  1. ReDoS in the edition-bump regex (adopt.sh Phase 5 wiring). (?ms) puts . in DOTALL mode inside a repeated group (?:(?!^\[).*\n)*?. When the trailing literal fails to match - any adoptee already on edition 2024, i.e. not "2015"/"2018"/"2021" - the engine backtracks combinatorially instead of failing fast. Observed: 10+ minutes hung, had to ctrl-C. Fix: drop the s flag, keep (?m) only - matches the sibling regex three lines up (tbl_m) which never had DOTALL and never hung. Isolated repro: still running after 5s before the fix, ~0.1ms after.

  2. Slug-hyphen breaks Rust syntax (adopt.sh step 3, placeholder rename). SLUG is validated as [a-z][a-z0-9-]* - kebab-case project slugs are the norm - but the rename pass substituted the raw hyphenated SLUG verbatim into .rs files too. acmex_version:: became uffs-products_version::: invalid Rust syntax, breaking all 4 copied template tool crates. Cargo.toml/paths are correctly unaffected (Cargo already maps package name foo-bar -> module path foo_bar at compile time) - only .rs file substitution needed the underscore form. Fix: compute SLUG_IDENT/CAP_IDENT/UP_IDENT (hyphens -> underscores) and use those specifically when the destination file is *.rs; every other file type keeps the kebab-case form.

Test plan

  • Bug 1: isolated regex repro (synthetic 400-line [workspace.package] block already on edition 2024) - hangs >5s before, ~0.1ms after.
  • Bug 2: logic-traced substitution for SLUG=uffs-products - confirms use uffs_products_version::VERSION; (was uffs-products_version::VERSION;), Cargo.toml name = "uffs-products-version" unaffected.
  • bash -n adopt.sh - syntax OK.
  • Extracted the PYWIRE heredoc, python3 -m py_compile - syntax OK.
  • lint-fast + lint-pre-push gates passed locally on both commits.
  • No existing CI exercises adopt.sh end-to-end - opening a follow-up issue to add a smoke test (adopt into a fixture repo with a hyphenated slug already on edition 2024) so both regression classes are caught automatically.

A third bug from the same live adoption - a just recipe-name collision between the template's just/analysis.just (audit:) and a pre-existing project just/dev.just also defining audit:, which broke just entirely - is not fixed here; it needs real collision-detection design (adopt.sh's copy step only checks destination file existence, not recipe-name collisions across files). Filed separately as #49.

@githubrobbi githubrobbi changed the title fix(adopt): stop catastrophic backtracking in the edition-bump regex fix(adopt): two real bugs found in a live adoption (ReDoS + slug-hyphen breakage) Jul 20, 2026
githubrobbi and others added 2 commits July 20, 2026 15:41
adopt.sh's Phase 5 wiring step hung indefinitely (observed >10min, ctrl-C
required) on any adoptee whose [workspace.package] edition was already
"2024" - i.e. anything NOT "2015"/"2018"/"2021". The (?ms) flag combo put
`.` in DOTALL mode inside a repeated (?:(?!^[).*\n)*? group; once the
tail literal fails to match, the engine exhausts exponentially many
backtrack paths before giving up. Dropping the DOTALL flag (keep only
(?m), matching the sibling regex three lines up that never had this bug)
makes the same search linear: confirmed via isolated repro, still
running after 5s before the fix, ~0.1ms after.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
adopt.sh's placeholder rename (step 3) substituted the raw SLUG
verbatim everywhere, including inside copied .rs source. SLUG is
validated as [a-z][a-z0-9-]* - kebab-case is the norm for crate/tool
names - but Rust identifiers can't contain '-'. A hyphenated slug (e.g.
"uffs-products") turned `acmex_version::` into `uffs-products_version::`
in every copied tool crate: invalid syntax, silent build breakage.

Cargo.toml/paths are unaffected and correctly stay kebab-case (Cargo
itself maps package name "foo-bar" to module path `foo_bar` at compile
time) - only *.rs files needed the underscore form done here instead.

Reported live: adoption into a repo with a hyphenated slug broke all 4
copied template tool crates (acmex-version and friends) the same way.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@githubrobbi
githubrobbi force-pushed the fix/adopt-redos-edition-regex branch from 6a922c0 to 6646b43 Compare July 20, 2026 22:45
@githubrobbi
githubrobbi added this pull request to the merge queue Jul 20, 2026
Merged via the queue into main with commit d377888 Jul 20, 2026
19 checks passed
@githubrobbi
githubrobbi deleted the fix/adopt-redos-edition-regex branch July 20, 2026 22:55
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