-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Validate CLAUDE_CLI_VERSION and remove shell interpolation from the build scripts #1117
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 6 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
ae771e2
Validate CLAUDE_CLI_VERSION and avoid shell interpolation in download…
qing-ant 118bf7c
Validate the version argument to update_cli_version.py
qing-ant cd91b25
Fix two union-attr errors in download_cli.py retry loop
qing-ant 6764d44
Fail CI when the CLI install pipeline fails
qing-ant 7d23db2
Lint and typecheck scripts/ in CI
qing-ant d4a6529
Exit cleanly on an invalid CLAUDE_CLI_VERSION
qing-ant a6a912a
Match the installer's version grammar, and stop retrying its rejections
qing-ant 0b68217
Simplify the version validator and its install-path tests
qing-ant 87cc5d6
Stop guessing at installer argument rejections; tighten dist-tag matc…
qing-ant 35ed7ed
Fail fast on the install failures a retry cannot fix
qing-ant 54fd51c
Accept comment-based-help installers; simplify the validator and inst…
qing-ant 1676543
Fold the two install-path test harnesses into one
qing-ant a8c73e9
Cut the comment bloat, and share the install scaffolding across platf…
qing-ant File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| r"""Shared validation for Claude Code CLI version strings. | ||
|
|
||
| Two scripts constrain the same value: update_cli_version.py writes it into | ||
| src/claude_agent_sdk/_cli_version.py, and download_cli.py (reached from | ||
| build_wheel.py) reads it back out and hands it to an installer. A second copy | ||
| of the rule would let the writer emit a value the reader rejects, so the | ||
| pattern and its validation helper live here once. | ||
|
|
||
| A version must start with an alphanumeric character (so flag-shaped values | ||
| like "--help" are rejected) and may then contain only characters that appear | ||
| in real CLI versions, including dev builds such as | ||
| "2.1.146-dev.20260519.t105443.shaece3dab". | ||
|
|
||
| This allowlist is a security boundary, not just input hygiene: | ||
|
|
||
| * update_cli_version.update_cli_version() writes the version into a Python | ||
| string literal in a real source file, so it must never admit a double | ||
| quote, a backslash, or a newline. | ||
| * download_cli.download_cli() hands the version to an installer. Neither of | ||
| its paths interpolates it into a command string -- Unix passes it as its | ||
| own argv element, Windows passes it in the environment -- so for that | ||
| caller the allowlist is defense in depth rather than the only barrier. | ||
|
|
||
| Widening it requires re-reading tests/test_download_cli.py::TestGetCliVersion | ||
| and tests/test_update_cli_version.py. | ||
|
|
||
| Deliberately unanchored, and matched with fullmatch() rather than match(): | ||
| with "^...$" a swap to match() would silently accept a trailing newline | ||
| ("1.0.0\n"); unanchored, the same swap accepts obvious prefixes like | ||
| "1.0.0; id" and fails immediately in tests. | ||
| """ | ||
|
|
||
| import re | ||
|
|
||
| VERSION_PATTERN = re.compile(r"[0-9A-Za-z][0-9A-Za-z.+-]*") | ||
|
|
||
|
|
||
| def validate_version(version: str, *, source: str, allow_latest: bool) -> str: | ||
| """Return ``version`` unchanged if it is a usable CLI version. | ||
|
|
||
| Args: | ||
| version: The candidate version string. | ||
| source: Name of where the value came from, used in the error message | ||
| (e.g. "CLAUDE_CLI_VERSION"). | ||
| allow_latest: Whether the "latest" sentinel is acceptable. It is for a | ||
| download, which resolves it at install time; it is not for a value | ||
| pinned into _cli_version.py, which must name one concrete build. | ||
|
|
||
| Raises: | ||
| ValueError: If ``version`` is neither an allowed "latest" nor a | ||
| fullmatch of VERSION_PATTERN. | ||
| """ | ||
| # "latest" fullmatches VERSION_PATTERN, so it has to be ruled out by name | ||
| # rather than by the pattern when it is not allowed. | ||
| if version == "latest": | ||
| if allow_latest: | ||
| return version | ||
| raise ValueError( | ||
| f"Invalid {source}: 'latest' is not a concrete version. " | ||
| f"Expected a version matching {VERSION_PATTERN.pattern}" | ||
| ) | ||
|
|
||
| if not VERSION_PATTERN.fullmatch(version): | ||
| expected = "'latest' or a version" if allow_latest else "a concrete version" | ||
| raise ValueError( | ||
| f"Invalid {source}: {version!r}. " | ||
| f"Expected {expected} matching {VERSION_PATTERN.pattern}" | ||
| ) | ||
| return version |
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
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
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.