Repository navigation
openRepoShape, setup.sh and install.sh: a URL, path or ref with a letter past ASCII is no control character under C either, as #760's byte rule decides in all seven checks that used [![:print:]], and every control character is still refused - #986
Conversation
…a letter past ASCII is no control character under `C` either, as #760's byte rule now decides in all seven checks that held one to `[![:print:]]`, and every control character is still refused in both locales (#940) #872 checked `$OPENREPOSHAPE_REPO` and `$OPENREPOSHAPE_REF` before every run but `--help`, with `[![:print:]]`, which under `C` and POSIX matches every byte past ASCII: a mirror at `/srv/café/openRepoShape.git`, or a ref `café`, was refused as "carrying a control character" by `--version`, a scaffold run, `--install` and `--preflight`, where `C.UTF-8` took it. `setup.sh`'s two guards and `install.sh`'s `checked_repo`, `checked_version` and option check held the same class. All seven now ask #760's rule, as `install_commands` holds the directory to it: under `LC_ALL=C`, in a subshell, `[[:cntrl:]]` or U+0080 to U+009F's two bytes, so one value gets one answer in every locale, and the launcher, `setup.sh`, `install.sh` and the package agree. The leading `-` arms stay, `misdrawn_characters` stays first, and no refusal's words change. A lone `0x9B` byte that is not UTF-8 now passes in both locales, the trade #867 took for the directory, which the comment at each place states. `setup.sh`, which has no copy of `misdrawn_characters`, also refuses U+2028 and U+2029 by their bytes, as it refused both in every locale before. The redirect's location in `install.sh` keeps `[![:print:]]`: it is the network's, and that class prints no lone C1 byte from it. One table, `CONTROL_RULE` in `tests/conftest.py`, holds the seven to one answer under `C` and `C.UTF-8`: `café`, a lone `0x9B` and a lone `0xE9` taken; an escape, U+009B, DEL, a tab, U+0085, U+0080, U+009F, a newline at the end and a lone `0xE9` then U+009B refused; U+202E, U+200B, U+2028 and U+2029 refused by code point. The three test files read it, and a repository or ref with `café` goes through every launcher run in both locales. Every arm of the rule dropped from any place fails a test, the seven the issue found surviving included. Closes #940 Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ter past ASCII is no control character under `C` either, as #760's byte rule decides in all seven checks that used `[![:print:]]`, and every control character is still refused (#940) #872 checked `$OPENREPOSHAPE_REPO` and `$OPENREPOSHAPE_REF` before every run but `--help`, with `[![:print:]]`, which under `C` and POSIX matches every byte past ASCII: a mirror at `/srv/café/openRepoShape.git`, or a ref `café`, was refused as "carrying a control character" by `--version`, a scaffold run, `--install` and `--preflight`, where `C.UTF-8` took it. `setup.sh`'s two guards and `install.sh`'s `checked_repo`, `checked_version` and option check held the same class. All seven now ask #760's rule, as `install_commands` holds the directory to it: under `LC_ALL=C`, in a subshell, `[[:cntrl:]]` or U+0080 to U+009F's two bytes, so one value gets one answer in every locale, and the launcher, `setup.sh`, `install.sh` and the package agree. The leading `-` arms stay, `misdrawn_characters` stays first, and no refusal's words change. A lone `0x9B` byte that is not UTF-8 now passes in both locales, the trade #867 took for the directory, which the comment at each place states. `setup.sh`, which has no copy of `misdrawn_characters`, also refuses U+2028 and U+2029 by their bytes, as it refused both in every locale before. The redirect's location in `install.sh` keeps `[![:print:]]`: it is the network's, and that class prints no lone C1 byte from it. One table, `CONTROL_RULE` in `tests/conftest.py`, holds the seven to one answer under `C` and `C.UTF-8`: `café`, a lone `0x9B` and a lone `0xE9` taken; an escape, U+009B, DEL, a tab, U+0085, U+0080, U+009F, a newline at the end and a lone `0xE9` then U+009B refused; U+202E, U+200B, U+2028 and U+2029 refused by code point. The three test files read it, and a repository or ref with `café` goes through every launcher run in both locales. Every arm of the rule dropped from any place fails a test, the seven the issue found surviving included. Closes #940 Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideThe PR standardizes seven shell-side validation checks on #760’s locale-independent byte rule so non-ASCII letters and selected non-control bytes are accepted consistently under C and UTF-8 locales, while control characters remain rejected; setup.sh retains its separator-specific safeguards, and extensive shared tests validate behavior, propagation, output safety, and the intentional network-redirect exception. Sequence diagram for validating a non-ASCII repository or refsequenceDiagram
participant User
participant Launcher as openRepoShape
participant Script as setup.sh_or_install.sh
participant Git
User->>Launcher: Provide repository, ref, version or option
Launcher->>Launcher: misdrawn_characters
Launcher->>Launcher: LC_ALL=C byte-rule check
alt control character
Launcher-->>User: Refuse with control-character message
else non-control value
Launcher->>Script: Pass value to downstream check
Script->>Script: LC_ALL=C byte-rule check
alt setup separator U+2028 or U+2029
Script-->>User: Refuse with control-character message
else accepted by byte rule
Script->>Git: Continue clone or checkout validation
Git-->>User: Operation result
end
end
Flow diagram for locale-independent input validationflowchart TD
A[Input value] --> B[misdrawn_characters]
B -->|visual hazard| C[Reject with code-point message]
B -->|safe| D[LC_ALL=C byte-rule check]
D -->|control byte| E[Reject as control character]
D -->|non-control byte or UTF-8 letter| F[Continue to existing validation]
F --> G[Accept or apply existing shape or owner/name rules]
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
(lane openRepoShape-2, 2026-10-19T14:14:44Z) Independent review of #986 (#940) at The PR does what #940 asks. All seven places now use one byte rule and give one answer under SHOULD-1.
NIT-1. Each rule line (
NIT-2. The seven "no
NIT-3.
Checked and true
Could not verify
Lane 2's decision: SHOULD-1 and the three NITs go back to the writer for one fix round before #986 lands. This reviewer then re-checks the new head, so the landing head has its own READY. Lane: openreposhape-2 (openRepoShape-2) |
…leases/tag/ tag is taken only in printable ASCII, the same in every locale, so a lone 0x9B from the network is never printed (SHOULD-1); the seven rule comments name an overlong form of a control beside the lone 0x9B, and CONTROL_RULE gains that row (NIT-1); a test holds the seven rule lines to one spelling (NIT-2); #874's test calls run_shim_under (NIT-3) Tested by w940: the eight files, 1738 passed; the new tag rows fail against 9ab5874's install.sh; NIT-2's eight mutants and three on the tag check are killed. Lane: openreposhape-2 (openRepoShape-2)
|
(lane openRepoShape-2, 2026-10-19T21:33:31Z) Independent review of #986 (#940) at This re-checks the PR after its fix commit. Fixed since
NIT-1 (new). The landing text does not yet describe the fix.
Checked and true
Could not verify
Lane 2's note on the new NIT: the reviewer read the 2026-10-18 copy of the squash message. The writer's updated one already says the tag in a Lane: openreposhape-2 (openRepoShape-2) |
|
|
Sorry @brettheap, you've used your own review budget of 250,000 diff characters for the last 7 days. You can request another review in 3 days and 21 hours by commenting |
…lict, so #1025 is tested against main as it stands, with #983's change to validate-family.py and #1026's fix of main's two red rows (#936) Main took twelve squashes since e076e3e: #1026, #958, #986, #1002, #1016, #1011, #1021, #1022, #973, #975, #1015 and #983. Only #983 touches validate-family.py, in `_git_runs` and the member checks, apart from this branch's hunks; it touches none of this branch's test files. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>



Lane: openreposhape-2 (openRepoShape-2)
Claim: #940 (comment)
Milestone v1.2.0. Brett Heap's v1.2.0 merge word, "merge v1.2.0 PRs once reviewed and green" (2026-10-18T14:45:28Z, relayed on #473 issuecomment-6251255596), lands it once an independent review is READY at the landing head and
testsand SonarCloud are green. It stays a DRAFT until then. Cut ata4de0aae, withorigin/mainmerged in atebbd9fa4and at2a78ae1a(a4c39592); nothing stacked. Fix round 1 (d4bba685b) answers rv986's review of9ab5874b(READY, with SHOULD-1 and NIT-1 to NIT-3): see Fix round 1, below.What changed
openRepoShape,setup.sh,install.sh,tests/conftest.pyand the three test files.#872 (v1.1.1) checked
$OPENREPOSHAPE_REPOand$OPENREPOSHAPE_REFbefore every run but--help, with[![:print:]]. That class is the locale's: underCand POSIX it matches every byte past ASCII, so a mirror at/srv/café/openRepoShape.git, or a refcafé, was refused as "carrying a control character" by--version, a scaffold run,--installand--preflight, whereC.UTF-8took it and v1.1.0 ran it.setup.sh's two guards andinstall.sh's three checks held the same class.All seven now ask #760's rule, the one
install_commandsholds$OPENREPOSHAPE_BIN_DIRto, withoutlocale_reading: underLC_ALL=C, in a subshell,[[:cntrl:]]or U+0080 to U+009F's two bytes. Eachcasebecame the same three lines:checked_repo(with its leading-arm, over$url$REPOas before) andref_character_fault;setup.sh: the guard on$SHAPE_REPO(with its leading-arm) and the one on--shape-ref, each with one more arm, U+2028 and U+2029 by their bytes (Decisions, 3);install.sh:checked_repo,checked_versionand the option check inmain.misdrawn_charactersstays first where it was. No refusal's words changed, and "carries a control character" is now true of what each names:The comments that said the repository and the ref are held to
[![:print:]]say what holds them now (openRepoShapeininstall_commands, abovechecked_repo, aboveref_faultand aboveref_character_fault;install.shabovechecked_repo,checked_version, inlatest_from_redirectand inmain), each place's comment states the trade (a byte that is not UTF-8, as a lone0x9Bor an overlong form of a control), and the docstring oftest_version_prints_a_path_a_utf_8_locale_printssays the path is no fault in either locale (the test now runs under both).install.sh'slatest_from_redirecttakes the tag in a…/releases/tag/<tag>location only when it is printable ASCII, asked underLC_ALL=Cin a subshell so every locale answers alike; any other tag names no release ("it redirects to a location that names no release"), andghis asked next (Decisions, 4).Measured, before (
a4de0aae) and afterC.UTF-8gave the same answers before and after in every cell; "before" namesCandC.UTF-8where they differed. "taken" means the check passed the value on:--versionprinted it,setup.sh's guard went on togit clone, and otherwise the next check answered as it did underC.UTF-8(the ref's shape refusal,install.sh's owner/name refusal or "unknown option"). "ctrl" is the refusal in the words above;U+XXXXismisdrawn_characters's refusal by code point. Each value ends a repository (/srv/<x>, oracme/r<x>forinstall.sh), a ref (v1<x>) or an option (-x<x>); a lone byte is that byte and no UTF-8.C0 9B)¹¹ From rv986's probe of
9ab5874binC,C.UTF-8andPOSIX, which foundC0 80,C1 BF,E0 80 9B,E0 82 9BandF0 80 82 9Btaken the same way in all seven places, and each refused at the base.CONTROL_RULE'san-overlong-escaperow pinsC0 9Bin every place.A real bare repository at a
cafépath, and a refcaféwith an ASCII repository (stubghandcurlthat exit 97, so a fetch fails as offline):CC.UTF-8, and after in both--versionAtlas --org acmesetup.sh, then exit 2 "could not fetch setup.sh" (both values)--installfrom a file--doctor <dir>beside a checkout--doctor <dir>, no checkout--preflight,$OPENREPOSHAPE_SETUP_SHin a checkoutsetup-project.py's shape refusal--preflight, asetup.shoutside any checkoutsetup.sh's shape refusalThe package, from
ebbd9fa4(after #947),python3 -m openreposhape --versionover the same values: the repository's answers are the launcher's in every row and both locales (taken:café, the three lone bytes, U+00A0, U+0378, U+FFFE; control: the nine; code point: U+202E, U+200B, U+2028, U+2029). Its ref gets the same control answer in every row; where the control check takes a ref, the package's--versiongoes on to the ref's shape, which the launcher's--versiondoes not ask (#866), so there it is refused by shape (Not changed).Decisions
$OPENREPOSHAPE_BIN_DIRholding:or a newline is refused byinstall.shonly after its fetch, and one holding a carriage return or an escape is placed and echoed as typed, so both refuse every control character before they fetch or print anything #760's bytes, in all seven places, underLC_ALL=Cin a subshell, so each answers one value one way in every locale. Eachifis the three lines thecaseit replaces was. The one answer still holds after standard.py: the package'sopenRepoShaperefuses a repository or ref holding U+202E, U+200B or another character the launcher refuses, by code point, before--versionor anygitcall names it, and refusesa.lock/b,a/.b,a//b,a/b/anda.before it clones, in the launcher's words (#939, #941) #947 (5f62ea48, now on main), which made the package refuse install.sh and openRepoShape: a$OPENREPOSHAPE_BIN_DIRholding a bidi override, U+200B, U+2028 or U+FEFF passes #804's control-character check and is echoed as typed, so the PATH note can show a directory as another #837's characters by code point before itsCONTROL_RE, as the launcher does: probed fromebbd9fa4, the package's--versiongives a repository the launcher's answer in every row of the table and every row above, in both locales, and gives a ref the same control answer (Measured).tests/test_openreposhape_package.pynow runs this table's rows (throughUNPRINTABLE_VALUES) against the package too.0x9Bor an overlong form of a control (C0 9B, an escape in two bytes): the trade is taken, as install.sh and openRepoShape: a$OPENREPOSHAPE_BIN_DIRthe bytes refuse is read again in the locale's own charset, so a GB18030, GBK or Shift_JIS name that holds no control character and nothing drawn as another is placed, and where nothing can read it the bytes decide as before #867 took it for the directory. Both are the same class: bytes no UTF-8 decoder reads as a control. Each passes in both locales now, where[![:print:]]refused it in both (rv986 measured the overlong forms in all seven places); the package's--versionalready passes both (measured:/srv/a,0x9B,bprints asa?b, exit 0, underCandC.UTF-8; rv986:/srv/a,C0 9B,bprints asa??b, exit 0). A terminal that reads UTF-8 draws it as a character it cannot read, and only one set to an 8-bit charset acts on it. Where it passes, the line after it prints it:--version,setup.sh's clone line, and the shape or owner/name refusals that name a value. The comment above each place says so, asinstall_commands's does (openRepoShape:633-635after this change).setup.shkeeps refusing U+2028 and U+2029, by their three bytes (\342\200, then\250or\251), one arm beyond install.sh and openRepoShape: a$OPENREPOSHAPE_BIN_DIRholding:or a newline is refused byinstall.shonly after its fetch, and one holding a carriage return or an escape is placed and echoed as typed, so both refuse every control character before they fetch or print anything #760's rule in both guards. This is lane 2's ruling: keepingsetup.sh's existing refusal of the two separators is not install.sh and openRepoShape: a$OPENREPOSHAPE_BIN_DIRholding a bidi override, U+200B, U+2028 or U+FEFF passes #804's control-character check and is echoed as typed, so the PATH note can show a directory as another #837's candidate. Probed at the base,setup.shrefused both in both locales:C's[![:print:]]with every byte past ASCII, andC.UTF-8's because glibc counts both among its controls. The bare rule would take both in both locales, and the clone line and the--shape-refrefusal would then print a line separator they never printed. The launcher,install.shand the package refuse both first by code point (install.sh and openRepoShape: a$OPENREPOSHAPE_BIN_DIRholding a bidi override, U+200B, U+2028 or U+FEFF passes #804's control-character check and is echoed as typed, so the PATH note can show a directory as another #837), so they need no arm; the arm keepssetup.shas it was, in the same words, and the four still refuse both.install.sh's redirect location keeps the locale's[![:print:]]for naming it: the fault names it only to say more, and that class matches a lone0x9Band every overlong form inC,C.UTF-8andPOSIX(measured), so no such byte from the network is printed; the byte rule's trade would print one. The cost: underCa location with a letter past ASCII goes unnamed ("it redirects to a location that names no release"). The tag in a…/releases/tag/<tag>location is taken only when it is printable ASCII, underLC_ALL=C(fix round 1, rv986's SHOULD-1): at9ab5874bthe arm took any tag,checked_versionpassed a lone0x9Bin it by the trade, and the shape refusal printed the byte raw. Now such a tag names no release, in every locale, andghis asked next. A release tag is ASCII by the release scheme, so this refuses no real one, and a tag with a letter past ASCII gets one answer in every locale, where a UTF-8 locale's[:print:]would have passed it on. The tagghanswers is unchanged:ghdecodes the JSON with Go'sencoding/json, which reads a byte that is not UTF-8 as U+FFFD, so a lone0x9Bcannot come from it, and a C1 control comes as its two bytes, which the rule refuses (read from Go's documentation, not measured: no realghruns here). The comments inlatest_from_redirectand abovechecked_versionsay so, and the redirect test pins both forms.caféunder--doctorwith no checkout is not tested here. Whetherchecked_refrefuses it before or after the clone is openRepoShape:--doctor'sshape_checkoutrunsgit clonebeforechecked_ref, so a ref it refuses (a.lock/b,a//b, one over 255 characters) costs a whole clone first, and where the clone fails the ref's refusal is never reached #942's (shape_checkout), which lane 2's w942 changes in the same file; this PR's ref test of--doctorruns it beside a checkout, where the ref names nothing to check out.Tests
One table,
CONTROL_RULEintests/conftest.py, appended afterREF_PARTS's helpers, read by all three files, as #874'sREF_PARTSis. Each row is how a value ends and what the checks say of it: taken (café, a lone0x9B, a lone0xE9, an overlong escapeC0 9B), a control character (an escape, U+009B, DEL, a tab, U+0085, U+0080, U+009F, a newline at the end, and a lone0xE9then U+009B), or a code point (U+202E, U+200B, U+2028, U+2029).the_rules_answerreads a run's answer andassert_a_copy_answers_as_the_rule_doesholds it to the row. A lone byte is a surrogate thatsurrogateescapehands a child as the byte, andrun_cmd,run_bare_setupandNet.runread output that way where a test asks.tests/conftest.pyCONTROL_RULE,SEPARATORS,A_LETTER_PAST_ASCII,REPO_WITH_A_LEADING_DASH,the_rules_answer,assert_a_copy_answers_as_the_rule_doestests/test_openreposhape_command.pytest_version_answers_a_repository_or_ref_as_the_control_rule_does(new)CandC.UTF-8: taken printed as typed, exit 0; the rest refused in the launcher's words, none of it echoedtest_a_letter_past_ascii_goes_through_every_run_in_either_locale(new)caféin each form, in both locales: a scaffold run fetchessetup.shonce with it,--installfrom a file places the command,--doctorhands a fakegitthe URL (repository) or runs beside a checkout (ref),--preflighthandssetup.shthe repository or--shape-reftest_a_repository_or_ref_no_line_can_print_is_refused_before_any_request(rows now from the table)-, the three trailing-newline forms, through--version, a scaffold run and--installfrom stdin: exit 2, the one refusal, no request.tests/test_openreposhape_package.pyruns the same rows through the package's--version(70 cases, was 34)test_version_prints_a_path_a_utf_8_locale_prints(now both locales)NOT_CONTROLSpaths printed underCtootests/test_setup_sh.pytest_the_repository_guard_answers_as_the_launchers_rule_does(new)-, both locales: taken goes togit cloneand the clone line names it; the rest exit 2 with the guard's words, no clone, nothing echoed. The file's first C1 and leading--casestest_the_shape_ref_guard_answers_as_the_launchers_rule_does(new)--shape-ref v1<x>: taken meets the shape refusal inC.UTF-8's words; the rest exit 2 with the guard's words. The file's first C1 case for the reftests/test_install_sh.pytest_the_installer_answers_each_value_as_the_launchers_rule_does(new)$OPENREPOSHAPE_REPO, the version and an option, both locales: exit 2, nothing placed, no request, in each check's words (taken: the owner/name refusal, the shape refusal, "unknown option").checked_repo's first control-character test, and the first C1 case of all threetest_the_redirect_names_no_location_holding_a_control_character(new)0x9B, the lone0xE9, the overlong escape), is never named; a tag holding a lone0x9B, or a letter past ASCII, is not taken and names no release; in either localetest_the_installer_refuses_a_value_drawn_as_another_unechoed(the_newest_tagnow fromgh)gh, since the redirect's tag is no longer passed on (Decisions, 4)tests/test_openreposhape_command.pytest_the_seven_control_checks_carry_one_rule_as_it_is(new)if (LC_ALL=C; case …lines are one text in each file (2, 2 and 3), apart from the value, the leading-arm andsetup.sh's separators arm, astest_install_sh_carries_the_launchers_misdrawn_check_as_it_isholdsmisdrawn_characterstests/test_setup_sh.pytest_the_shim_takes_a_shape_ref_as_git_does_before_any_clone(#874's)run_shim_under, now(tmp_path, repo, *args, locale=""), in place of the two blocks it shared with it; its assertions are unchangedHelpers:
run_cmd,run_bare_setupandNet.runtakeerrors;cloned_fortakesenv; the fakecurlwrites headers withsurrogateescape, so a lone byte reaches the installer.Mutants (
S/w940/mutants.py, andS/w940/fix1-out/mutants2.pyfor fix round 1, one at a time in a scratch copy of the tree, the targeted tests of the file each mutant is in):[[:cntrl:]]arm-arm[![:print:]]in the localeLC_ALL=Cchecked_reporef_character_faultsetup.sh$SHAPE_REPOsetup.sh--shape-refinstall.shchecked_repoinstall.shchecked_versioninstall.shoption checkThe numbers are the failing cases. The seven the issue found surviving (
setup.shguard 1 without the C1 or the-arm, guard 2 without the C1 arm,install.sh'schecked_repowithout either arm,checked_versionand the option check without the C1 arm) are killed. The redirect's location moved to the byte rule is killed by 6 cases (the lone0x9B, the lone0xE9and the overlong escape, both locales).² By
test_the_seven_control_checks_carry_one_rule_as_it_is, the case for that file (fix round 1, NIT-2). The behaviour tests cannot kill these: no locale installed here answers a row differently withoutLC_ALL=C(Not changed).Fix round 1's other mutants, all killed:
\236LC_ALL=Ccafétag,C.UTF-8)9ab5874bdid)And
9ab5874b'sinstall.shagainst this round's tests (-k "names_no_location or drawn_as_another_unechoed"): 4 failed, the two tag rows in both locales, withREFUSED: the newest release's tag is 'v1<0x9B>31mforged', …where the test asks for "names no release"; 32 passed.Not changed
setup.shand install.sh and openRepoShape: a$OPENREPOSHAPE_BIN_DIRholding a bidi override, U+200B, U+2028 or U+FEFF passes #804's control-character check and is echoed as typed, so the PATH note can show a directory as another #837's characters. It has no copy ofmisdrawn_characters, and Brett Heap has not ruled on that candidate. So U+202E, U+200B and the rest passsetup.shin every locale now; underC.UTF-8they passed before too, and underCthe over-broad class refused them as control characters (U+2028 and U+2029 are Decisions, 3). The launcher refuses each first when it is the way in.README.md:347-348'scurl … setup.sh | bash -s --is the way in without it.[![:print:]]refused, each measured in every place: U+0378 (unassigned), U+FFFE (a noncharacter), and a lone0x9B,0x85or0xE9byte. The issue's table has them; none is a control character. The overlong forms of a control (C0 9Band the rest, rv986's NIT-1) are taken in every locale, and refused at the base in every locale: Decisions, 2.0xC2 0x80to0xC2 0x9Fas hanzi; the rule refuses a value holding one as a C1 control.install_commandsaskslocale_readingfor the directory (install.sh and openRepoShape: the control-character check matches bytes, so a GBK or GB18030 name holding0xC2and a byte from0x80to0x9Fis refused as a control, and a lone0x9B, which an 8-bit terminal acts on, is placed #858); these checks do not, as the issue says (setup.shhas no copy of it).src/openreposhape/: itsCONTROL_REis this rule over the decoded text, and since standard.py: the package'sopenRepoShaperefuses a repository or ref holding U+202E, U+200B or another character the launcher refuses, by code point, before--versionor anygitcall names it, and refusesa.lock/b,a/.b,a//b,a/b/anda.before it clones, in the launcher's words (#939, #941) #947 itsmisdrawn_charactersruns before it. The package's--versionasks a ref its shape as well, where the launcher's asks a ref only its characters (openRepoShape:--versionand two fetch refusals print$OPENREPOSHAPE_REPOand$OPENREPOSHAPE_REFwith no check, so an escape or a bidi override in either reaches the terminal as typed, and a scaffold run sends its fetch with them #866), so a ref the control check takes (v1café) prints from the launcher's--versionand is refused by shape from the package's, in both locales, before and after this PR.LC_ALL=Csubshells are held by their text, not by their answers (fix round 1, NIT-2). Each place withLC_ALL=C;dropped and its arms kept passes every behaviour test: on this host, bash 5.2.21 on glibc with onlyC,C.utf8andPOSIXinstalled, they answer every row alike in both locales: glibc's[[:cntrl:]]inC.UTF-8adds only U+2028 and U+2029, which the launcher andinstall.shrefuse first by code point andsetup.shrefuses by its arm, and an invalid byte makes bash match byte by byte. The subshell is there for what no installed locale here can show, an ISO 8859 locale reading0x9Balone as a control, asinstall_commands's comment has it (install.sh and openRepoShape: the control-character check matches bytes, so a GBK or GB18030 name holding0xC2and a byte from0x80to0x9Fis refused as a control, and a lone0x9B, which an 8-bit terminal acts on, is placed #858).git grepofdocs/,README.mdandAGENTS.mdforprint:]and "control character" finds no sentence about the repository or the ref;docs/release-notes/v1.1.1.md:1082describes the release as it shipped.Overlaps
openRepoShaperefuses a repository or ref holding U+202E, U+200B or another character the launcher refuses, by code point, before--versionor anygitcall names it, and refusesa.lock/b,a/.b,a//b,a/b/anda.before it clones, in the launcher's words (#939, #941) #947 and setup-project.py and validate-repository-naming.py: step (4) reads and names what step (5) does,--link-remedyreads a link holding=, strips its words and notes an unused one, and the Windows hand clone passes-c core.autocrlf=false(#907, #926) #949 (lane 3's) edittests/conftest.pytoo. standard.py: the package'sopenRepoShaperefuses a repository or ref holding U+202E, U+200B or another character the launcher refuses, by code point, before--versionor anygitcall names it, and refusesa.lock/b,a/.b,a//b,a/b/anda.before it clones, in the launcher's words (#939, #941) #947 has landed (5f62ea48,REF_PARTS's rows at lines 2433-2450) and is in this branch through the merge ofebbd9fa4, which took no hand resolution. setup-project.py and validate-repository-naming.py: step (4) reads and names what step (5) does,--link-remedyreads a link holding=, strips its words and notes an unused one, and the Windows hand clone passes-c core.autocrlf=false(#907, #926) #949's branch,l3-907-926-setup-step4-linksatced3d7c2, the one lane-3 branch still open that touches any file here, changesSTEP_FOUR_WORDS(2245-2335); this PR only appends a section afterREF_PARTS's helpers, at the end of the file.git merge-tree --write-treeof this head withorigin/main(ebbd9fa4), with openRepoShape:--doctor'sshape_checkoutrunsgit clonebeforechecked_ref, so a ref it refuses (a.lock/b,a//b, one over 255 characters) costs a whole clone first, and where the clone fails the ref's refusal is never reached #942's headd5d2152cand withced3d7c2is clean for each (exit 0).--doctor'sshape_checkoutrunsgit clonebeforechecked_ref, so a ref it refuses (a.lock/b,a//b, one over 255 characters) costs a whole clone first, and where the clone fails the ref's refusal is never reached #942 (lane 2's w942) changesopenRepoShape'sshape_checkoutandtest_a_ref_the_doctor_would_check_out_is_validated, and adds tests beside it; this PR touches neither, and changeschecked_repoandref_character_fault, which openRepoShape:--doctor'sshape_checkoutrunsgit clonebeforechecked_ref, so a ref it refuses (a.lock/b,a//b, one over 255 characters) costs a whole clone first, and where the clone fails the ref's refusal is never reached #942's body names as openRepoShape, setup.sh and install.sh: under theClocale a$OPENREPOSHAPE_REPOURL or path, or a ref, with a letter past ASCII is refused as "a control character", so--version,--installand--preflightrefuse what v1.1.0 ran #940's. Whichever lands second merges the other.Fix round 1
rv986's review of
9ab5874bwas READY with one SHOULD and three NITs;d4bba685banswers all four, on the merge of2a78ae1a(a4c39592).install.sh's redirect tag):latest_from_redirecttakes the tag only when it is printable ASCII underLC_ALL=C, else "it redirects to a location that names no release" andghis asked; the comments there and abovechecked_versionsay so. The redirect test gains a tag with a lone0x9B(rv986's repro) and a tag withcafé, andthe_newest_tagnow comes fromgh. Decisions, 4.0x9Bor an overlong form of a control",CONTROL_RULEgainsan-overlong-escape(a,C0 9B,b, taken), and the redirect test takes the table's bytes that are not UTF-8 as rows. Decisions, 2.LC_ALL=Csurvivors):test_the_seven_control_checks_carry_one_rule_as_it_isholds the seven lines to one text; it kills the sevenLC_ALL=Cmutants and a range-end mutant (Mutants).EMAILset): the eight files (the three test files,tests/test_openreposhape_package.py, the trio andtests/test_install_sh_filter_repo.py), 1738 passed in 305.51s; this round's targeted tests, 377 passed.tests/test_setup_sh.py):run_shim_under(tmp_path, repo, *args, locale="")moved above setup.sh, install.sh and the openRepoShape command: each keeps its own copy of the old ref rule (*..* | *.lock), so a ref git refuses part by part (a.lock/b,a/.b,a//b) clones the standard before it fails #874's test, which calls it; the callers pass--preflightthemselves.Closes #940
🤖 Generated with Claude Code
Summary by Sourcery
Use a consistent locale-independent control-character policy across repository, ref, version, option, and release-redirect validation while preserving safe handling of actual controls and line separators.
Bug Fixes:
Enhancements:
Tests: