Repository navigation
Make language detection linear on adversarial input - #1
Merged
Merged
Conversation
Several detection patterns started with `^\s*` under re.M. `\s` matches newlines, so from every line start the engine scanned all the whitespace that followed before failing: 20KB of blank lines took ~40s of CPU, and the 200KB the API accepts would take roughly an hour. Detection runs on anonymous input whenever `language` is omitted (the web UI default) and on /v1/detect, so one request could pin the single production worker. Leading indentation is now `[^\S\n]*`, the Python `def` header no longer rescans the rest of the line once per `)`, and the Java generic `new` pattern bounds its `.*`. Detection results are unchanged on a 4,750-file corpus; the worst adversarial 200KB input now takes ~0.2s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UigC7feaY8NkAy9oay3GDW
This was referenced Nov 2, 2026
ethanzhoucool
marked this pull request as ready for review
November 3, 2026 17:06
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Language detection could be made to burn minutes of CPU with a request made of blank lines. Several of the patterns in
SIGNATURESstart with^\s*underre.M.\salso matches\n, so from every line start the engine scans all of the whitespace that follows before it gives up. That makes the cost quadratic in the number of blank lines.Detection runs on anonymous input whenever
languageis omitted, which is the web UI's default ("auto"), and onPOST /v1/detect, which has no size limit at all. The Procfile runs a single uvicorn worker, so one such request ties up the whole service.Measured locally on Python 3.13 before this change:
detect_language"\n" * 20_000" \t\r\n" * 5_000"def a(" + ")->" * 7_000"new A<>" * 6_000It's quadratic, so the 200 KB that
/v1/explainaccepts works out to roughly an hour for the blank-lines case.After this change, each of those inputs at the full 200 KB takes between 0.05 and 0.19 s.
Changes
[^\S\n]*(any whitespace except a newline) instead of\s*. It's factored into one_INDENTprefix so the rule lives in one place.defheader pattern\(.*\)\s*(->.*)?:re-scanned the rest of the line once for every). It is now\(.*\)[^)\n]*:, which only scans up to the next), so the total work stays linear. It still matches every line the old one matched (plain,->-annotated and multi-paren return types)..*to.{0,200}, so an attempt can't rescan a whole long line.SIGNATURESexplains both banned shapes, so a later pattern doesn't bring them back.Testing
pytest: 152 passed, 3 skipped (Python 3.13).pytest tests/test_explain.pyon Python 3.11: 42 passed.ruff check explain_my_code tests: clean.test_detection_stays_fast_on_adversarial_input(4 cases, each must finish in under 0.5 s). Against the old patterns all 4 fail, and the run takes 40 s. With this change they take milliseconds.test_detection_still_reads_indented_and_crlf_lineschecks that indented and CRLF lines are still recognised.detect_languageon 4,750 files (150 CPython stdlib modules, this repo,web/static/samples.js, and JS/TS/Java/C++/SQL files from other local checkouts). Language, confidence and runner-up were identical for every file.Notes
/v1/detectand/v1/analyzestill have no request-size validator, unlike/v1/explain. I left that out of this PR to keep it to one theme.Generated by Claude Code