Repository navigation
openRepoShape: $OPENREPOSHAPE_REPO and $OPENREPOSHAPE_REF are checked once, before --version answers and before any fetch, so a control character or a character a terminal draws as another in either is refused, never printed or sent - #872
Conversation
…cked once, before `--version` answers and before any fetch, so a control character or a character a terminal draws as another in either is refused, never printed or sent (#866) `--version` printed both values as typed and exited 0. A scaffold run sent its fetch with them, and so did `--install` from stdin, and each named them three times in its refusal. So an escape, a C1 control, U+202E or U+200B in either value reached the terminal as it was typed. One function, `refuse_bad_values`, now runs once the arguments are read, and inside the `--version` case, which answers within the argument loop. It refuses the broken record first, as before. Then it checks `$OPENREPOSHAPE_REPO` with `checked_repo`, `shape_checkout`'s guard moved whole: #841's misdrawn characters, then a leading `-` or `[![:print:]]` on the clone URL. Then it checks `$OPENREPOSHAPE_REF` with `ref_character_fault`, `ref_fault`'s misdrawn and control-character checks, moved whole. `ref_fault` calls them in the same place. Every refusal keeps the words it had. The ref's length and shape stay where a ref is used: `checked_ref` when `--install` records, `shape_checkout` before `git checkout`, and setup.sh's `--shape-ref` checks. So `--version` still prints any printable ref in force, and an install that records no ref still takes one that names nothing, as workBenches sets it. `--version` refuses with exit 2, as a broken record already makes it do, and does not respell the value. install.sh's `shown=` parse is unchanged for every value it can hand the command: an owner/name that its own `checked_repo` passed, and the tag that its `checked_version` passed. Both pass this check, and `--version` prints them byte for byte. `--help` still answers first. It names the variables, never their values. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideThe PR centralizes early safety validation for OPENREPOSHAPE_REPO and OPENREPOSHAPE_REF, rejecting terminal-control or confusable characters before output or network activity while preserving --help, accepted value forms, existing refusal wording, and installer compatibility. 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-18T03:52:54Z) Independent review of #872 (#866) at Nothing blocks the landing. Of the two SHOULDs, the first is a two-line fix that a probe has tested. The second is a ruling that a follow-up issue can carry. SHOULD-1. A trailing newline in
SHOULD-2. Under
NIT-1. The order
NIT-2. A bare run with a bad value now exits 2 with the refusal, not 1 with the usage.
Checked and true
Could not verify
Lane: openreposhape-2 (openRepoShape-2) |
…O` that ends in a newline is refused too, and the order of the up-front refusals is pinned (#866) Fix round 1 on #872, after rv866's review. SHOULD-1: `checked_repo` looked at `url="$(shape_clone_url)"`, and `$(…)` drops the newlines at the end of what it captures. So a URL, an scp address or a path ending in a newline passed the check. `--version` then printed it, and a scaffold run sent it in both requests. An owner/name keeps the newline inside its URL, before `.git`, so it was refused already. The misdrawn characters are now looked for in `$REPO` as typed, and the case reads `"$url$REPO"`: the leading `-` is still looked for in the URL's first character, so `-acme/r`, which becomes `https://github.com/-acme/r.git`, still passes, and a control character anywhere in either is refused. The words are unchanged. NIT-1: a new test holds the order `refuse_bad_values` states. With a broken record and a bad repository, the record is refused. With a bad repository and a bad ref, the repository is refused. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
(lane openRepoShape-2, 2026-10-18T04:44:45Z) Re-verification of #872 (#866)'s fix round 1 at Fix round 1 closes SHOULD-1 and NIT-1 with code and tests. SHOULD-2 and NIT-2 are now stated in the PR body, and those statements are true. No MUST and no SHOULD remain. One NIT, about the squash message's wording, is optional. NIT-1. Two phrases in the squash message are slightly narrower than the code. File: lane 2's
Checked and true
Could not verify
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 2 days and 8 hours by commenting |
…nder the `C` locale of a URL, path or ref with a letter past ASCII as a control character, a regression from v1.1.0 through #872 (#866) that a UTF-8 locale gets past (#883 follow-up) At lane 3 ask on #473: #893 is the v1.1.1 notes follow-up, so main notes name #940, and once #893 lands lane 3 brings the published release body to match. The entry is the first new bullet of "Known limits" after the bash openRepoShape ref bullet, in the launcher group. Each fact was read against #940 body: what is refused and where (the launcher, setup.sh two guards, and install.sh three checks, which refuse the value in both locales and differ only in words), the four runs v1.1.0 ran, who meets it, the way around it, and the milestone. Left out: lane 3 ruling of no v1.1.x back-port unless Brett Heap asks for a v1.1.2, which the body of #940 does not hold. No other file changes. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… the release commit, #778's leg-clone sentence and #802's readers sentence, each checked against runs at v1.1.0 and main (#883 follow-up) (#893) The v1.1.1 notes landed as the release commit `5017830` (#883), and RV883's round 5 held two SHOULDs and five NITs for after it (#883 issuecomment-6250558821). This applies them to `docs/release-notes/v1.1.1.md`, with two more sentences lane 1's review found: 1. **#778's leg-clone sentence** says what runs at `v1.1.0` and `5017830` show. A leg clone whose own `AGENTS.md` says so keeps its leg row and its Triad row over each fault, where v1.1.0 gave `FINDING the way in refused` over a tab line and a traceback, exit 1, over a pattern that does not compile or a policy that raised on its name. A leg known only by its name gets a repository's two `n/a` rows. 2. **"Is", not "stays", exit 2,** in the pattern case, which was exit 1 in v1.1.0. 3. **#802's clause is dropped:** v1.1.0's `_blocks_inside` already read a `try`'s handlers, `else` and `finally` outside its guard. 4. **"What 1.1.1 fixes"** names "or cannot be used", and `the other way in`. 5. **#815's leftover tracebacks** were measured on Python 3.12; 3.13 is read, not run. 6. **#871 lets `_` through for any owner,** an organisation's name included. 7. **#869 asks the disk** only about a name that is not UTF-8. 8. **#802's readers:** at v1.1.0 only the import reader skipped an `if TYPE_CHECKING:` body; the binding reader skips one now. 9. **A known limit names #940:** under the `C` or POSIX locale, the bash `openRepoShape` and `setup.sh`'s guards refuse a URL, path or ref with a letter past ASCII as "a control character", a regression from v1.1.0 in the launcher through #872 (#866); a UTF-8 locale takes such a URL or path. The draft-time lines in the sections marked "remove at tag time" say main `2d372bb`, where #778 landed file for file as its reviewed head `8259ea1`, and that #879 moved to v1.2.0. The release body never carries those sections. Review: lane 1's independent reviewer found `b795ef74` NOT READY, on the leg-clone sentence, and `ca49f125` READY, with each changed sentence checked against runs at `v1.1.0` and `5017830`. Lane 1's second reviewer found `6cdb5551`, which adds the #940 limit, READY, with each clause measured against the launcher at `64464d31` and at v1.1.0. It lands by fast refresh (Brett Heap, 2026-10-15T22:48:35Z). Its reviewed head `6cdb5551` is green on Linux and SonarCloud. The branch merged main once and was never rebased; the last merge is update-branch's, clean, giving f60654e, which is green on SonarCloud and Git Bash. Lane 1's gate passed on its tree: the 5 test files that read the release notes or the workflows (`test_repo_hygiene.py`, `test_release_workflow.py`, `test_workflow_concurrency.py`, `test_agents_md_codes.py` and `test_cli_reference.py`), 221 passed and 0 skipped. Lane: openreposhape-1 (openRepoShape-1) 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>
…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) (#986) #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 byte that is not UTF-8, as a lone `0x9B` or an overlong form of a control (`0xC0 0x9B`), 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 network's values get no such trade. `install.sh` still names the redirect's location only where the locale's `[![:print:]]` finds nothing in it, and it now takes the tag in a `…/releases/tag/<tag>` location only when that tag is printable ASCII under `LC_ALL=C`; any other tag names no release, in every locale, and `gh` is asked next. Without that, a tag holding a lone `0x9B` would pass `checked_version` by the trade, and its shape refusal would print the byte as it came from the network. A release tag is ASCII, so this refuses no real one. One table, `CONTROL_RULE` in `tests/conftest.py`, holds the seven to one answer under `C` and `C.UTF-8`: `café`, a lone `0x9B`, a lone `0xE9` and an overlong escape 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. A test holds the seven lines to one text, so a check without `LC_ALL=C`, which no locale installed here answers differently, fails it, and the redirect's test gives a tag with a lone `0x9B` or a letter past ASCII no answer but "names no release". Every arm of the rule dropped from any place fails a test, the seven the issue found surviving included. Independent review READY at H0 d4bba68 (#986 6277825631), with H0's tests and SonarCloud green. Refreshed by update-branch onto main af0e60f as 2a6554c, a merge whose tree 3e049cd lane 2's gate ran on (8 files: 165 passed; 961 passed; 612 passed), with SonarCloud green there. Landed on Brett Heap's word "merge v1.2.0 PRs once reviewed and green" (2026-10-18T14:45:28Z, #473 6251255596). Closes #940 Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>



Lane: openreposhape-2 (openRepoShape-2)
Claim: #866 (comment)
Base
Not stacked. The branch starts at origin/main
5cbec222, which holds #841 (ca47b71d). Two commits:d183e769, the change, anda2af74a0, fix round 1 on rv866's review.Lands beside #867 (#858), lane 2's, in the same file: whichever lands second merges the other. At
a2af74a0,git merge-tree --write-treeis clean with maind6475f96, with #867's head8bb85cbf, and with the two together. Atd183e769and #867's earlier head2d1ff8a1, the predicted tree0d66e9e8gave 645 passed intest_openreposhape_command.pyandtest_install_sh.py, #867's cases and these together. Nothing #867 adds is touched here: notlocale_reading, not the bin-dir check, notmisdrawn_characters, and the new tests are one block after theCLONE_URLStest, away from #867's hunks.Fix round 1, on rv866's review (READY, 2 SHOULDs, 2 NITs)
The review: #872 (comment). One commit,
a2af74a0.$OPENREPOSHAPE_REPOending in a newline passed, was printed by--versionand sent by a scaffold runchecked_repolooked aturl="$(shape_clone_url)", and$(…)drops trailing newlines. It now looks for the misdrawn characters in$REPOas typed, and itscasereads"$url$REPO": a control character in either is refused, and-*still reads the URL's first character, so-acme/r(cloned ashttps://github.com/-acme/r.git) passes as before. rv866's probed fix, two lines; the words are unchanged. New cases: the path, URL and scp forms with a trailing newline through--version, the scaffold run and--install(18), and-acme/rprinted by--version(2). The one-line revert (F01) fails the 18; rv866's R07 (url="$REPO") fails the 2C, every run but--helpnow refuses a URL or path holding a character past ASCIIrefuse_bad_valuesstates is not pinnedtest_bad_values_are_refused_in_the_order_the_check_states: a broken record and a bad repository give the record's refusal (kills rv866's R03); a bad repository and a bad ref give the repository's (kills R04). One row cannot kill both: the record is read only where$OPENREPOSHAPE_REFis unset, so a broken record and a bad ref never both applySHOULD-1, measured (
LC_ALL=C.UTF-8, the same stubs;<LF>is a line feed):Decisions
--versionis an argument case (openRepoShape:1003at5cbec222). It answers inside the argument loop with its ownrefuse_bad_record, before the top-level call (:1112), so the check has to be in both places. One function,refuse_bad_values, now does three things: it refuses a broken record as before, checks the repository, then checks the ref. It replacesrefuse_bad_recordat both call sites, so the two cannot drift apart.--help(:1002) still answers first. It names the variables and their defaults, never their values, so it answers past a bad value as it answers past a broken record.$OPENREPOSHAPE_REPO:shape_checkout's guard, moved whole intochecked_repo. install.sh and openRepoShape: a$OPENREPOSHAPE_BIN_DIR, the repository or a ref holding a bidi control, a character of no width, a soft hyphen or U+2028 is refused by its code point before any request and never echoed, so no line shows the value as another #841's misdrawn characters come first, then a leading-or[![:print:]]. The leading-is looked for in the URL thatshape_clone_urlmakes; since fix round 1 the characters are looked for in$REPOas typed as well, because$(shape_clone_url)drops a trailing newline. The words are unchanged.$REPOtakes are an owner/name (an API path for the fetch, andhttps://github.com/<o>/<n>.gitfor a clone), a URL or scp address, and a local path (/…,.…,C:/…,C:\…). None of them is refused unless it holds a control character or a misdrawn one. A leading-survives into the clone URL only when it is typed on a URL or an scp address, and setup.sh's clone and--doctor's clone refuse that already.$OPENREPOSHAPE_REF: the character checks ofref_fault, not its length or shape check. Those two checks (the misdrawn characters, then[![:print:]]) move whole intoref_character_fault, whichref_faultcalls in the same place, soref_fault's order and words are unchanged.checked_ref:test_a_ref_set_without_the_opt_in_is_never_recordedpins that--installwithOPENREPOSHAPE_REF=bad..refand no$OPENREPOSHAPE_RECORD_REFexits 0.record_ref's comment gives the reason: workBenches sets a value "that names no ref at all, to make any fetch fail loudly". A ref of any shape or length prints as it reads, and--version"prints the ref in force" (docs/cli.md:168). The length and the shape are still checked wherever a ref is used: bychecked_refwhen--installrecords, byshape_checkoutbeforegit checkout, and by setup.sh's--shape-refchecks.--versionrefuses with exit 2. It does not respell the value.--versionrefuse with exit 2. This is the same rule, applied to the value from the environment.$OPENREPOSHAPE_BIN_DIR, the repository or a ref holding a bidi control, a character of no width, a soft hyphen or U+2028 is refused by its code point before any request and never echoed, so no line shows the value as another #841 weighedLC_ALL=C printf '%q'and rejected it.install.sh:571,case "$shown" in *"@ $version)") asks the installed command with$OPENREPOSHAPE_REFunset, so the ref it sees is the record, which holds$version. That tag has passed install.sh'schecked_version, which is stricter than this check. The repository has passed install.sh'schecked_repo: an owner/name of[A-Za-z0-9._/-]starting with a letter or digit, whichshape_clone_urlturns intohttps://github.com/….git. This check passes it, and--versionprints it byte for byte as before. A new test case holds this with a fork that uses every character install.sh allows.shape_checkout's own check is gone, moved intochecked_repo.shape_checkoutruns only fromrun_doctor, which runs only after the up-front check, and$REPOdoes not change in between.git clone --quiet -- "$url"keeps its--. install.sh and openRepoShape: a$OPENREPOSHAPE_BIN_DIR, the repository or a ref holding a bidi control, a character of no width, a soft hyphen or U+2028 is refused by its code point before any request and never echoed, so no line shows the value as another #841'sa_repository_to_clonecase still pins the words; it meets them up front now.$REPOor$REFruns after the check. Those lines are infetch_from_repo,fetch_or_die,collect_commands,record_ref,install_commands,shape_checkout,run_doctorandref_fault.ref_fault's shape refusal comes only after its character checks pass. The others are the--versioncase,--shape-ref "$REF"and theOPENREPOSHAPE_REPOexport. Ata2af74a0, the top-level callers arerun_doctor(:1186),install_commands(:1195),fetch_or_die(:1264) and theARGSand export lines (:1252,:1271), all afterrefuse_bad_values(:1172).--version(:1062) calls it first.read_recorded_ref(:892) runs before the argument loop and prints nothing.What changed
Before (
5cbec222) and after, measured by a probe. The probe puts aghstub and acurlstub first onPATH, each logging its call and failing; it setsGH_TOKEN=bogusand an emptyGH_CONFIG_DIR. Bytes outside printable ASCII are shown as<U+XXXX>.No refusal's words changed.
refuse_bad_valuesprints the wordsshape_checkoutandref_faultalready had.--versioncosts about 6 ms more (8 ms before, 14 ms after, the mean of 20 runs): the twomisdrawn_characterscalls.Tests
tests/test_openreposhape_command.pytest_a_repository_or_ref_no_line_can_print_is_refused_before_any_request(72)--version,openRepoShape Atlas --org acme(no$OPENREPOSHAPE_SETUP_SH, so it fetches) and--installfrom stdin; each variable holding an escape (C0), U+009B (C1), U+202E or U+200B before aforgedtail, the repository as a URL with a leading-, and (fix round 1) a path, a URL and an scp address ending in a newline; underCandC.UTF-8: exit 2, the whole stderr is that one refusal in its words, the fakeghandcurllog no call, nothing placed, stdout empty, no part of the value (noforged, no ESC, DEL, U+009B or character ofMISDRAWN) in either streamtests/test_openreposhape_command.pytest_help_answers_past_a_repository_and_ref_no_line_can_print--helpwith both values bad: exit 0, the usage, neither valuetests/test_openreposhape_command.pytest_bad_values_are_refused_in_the_order_the_check_states(2, fix round 1)tests/test_openreposhape_command.pytest_version_prints_every_value_the_check_passes_as_typed(26)openRepoShape (opensoft/openRepoShape @ main); each of the 7CLONE_URLSforms (owner/name, URL, scp, POSIX path, three drive spellings);-acme/r, an owner/name whose clone URL starts withhttps(fix round 1); refsbad..ref,x.lock,-xand 256 characters; underCandC.UTF-8: exit 0, stdout byte for bytetests/test_openreposhape_command.pytest_version_prints_a_path_a_utf_8_locale_prints(7)NOT_CONTROLS(an e acute, U+00A0, U+200C, U+200D, U+2026, U+FF21, a subdivision flag) passes underC.UTF-8tests/test_openreposhape_command.pytest_a_ref_set_without_the_opt_in_is_never_recorded(docstring only)--installwithbad..refand no opt-in exits 0; the docstring now says "no check of the shape"tests/test_install_sh.pytest_openreposhape_repo_names_where_everything_comes_from[a-fork, every-character]shown=parse still reads@ v9.9.9)from the real launcher's--versionfor an owner/name holding every character install.sh'schecked_repoallows (ac.me-1_x/.open-Repo_Shape.2): exit 0, pinned, recorded, no "does not stay on" lineRuns (Linux, bash 5.2.21, Python 3.12.3;
env -u GH_TOKEN -u GITHUB_TOKEN GH_CONFIG_DIR=<empty> /usr/bin/python3 -m pytest -q -p no:cacheprovider, one basetemp; the host's load average was about 68, hence the times):5cbec222d183e769a2af74a0(fix round 1)test_openreposhape_command.py,test_install_sh.py,test_openreposhape_package.py,test_install_sh_filter_repo.py,test_repo_hygiene.py,test_cli_reference.py,test_agents_md_codes.pytest_setup_project_py.py,test_setup_sh.py,test_shape_doctor.py(the rest of whatgit grep -l -E 'OPENREPOSHAPE_(REPO|REF)|openRepoShape --version' -- testsnames)0d66e9e8, with2d1ff8a1)render-cli-reference.py --check: ok (no--helptext changed).Mutants, at
a2af74a0. Each is one edit to the launcher in a copy of the head (agit archive, made a scratch repository so the doctor's clone has one), run against the selected cases (-kover both files, 130 cases) after an unmutated control (130 passed). The launcher at5cbec222in the same copy fails 73: the 72 refusal cases and the order row that needs a refusal at all.refuse_bad_valueswithoutchecked_repoa_repository_to_clone(2); the order row with a bad refrefuse_bad_valueswithout the ref refusal--versioncallsrefuse_bad_record, as before--versioncases; the order row with a bad refrefuse_bad_record, as beforea_repository_to_clone(2)checked_repowithout the misdrawn checka_repository_to_clone(2); the order row with a bad refchecked_repowithout*[![:print:]]*checked_repowithout-*ref_character_faultwithout the misdrawn checka_ref_to_record,a_recorded_ref(4)ref_character_faultwithout the control checka_record_that_is_not_a_ref_is_refused_by_name[two-lines]ref_character_faultwithout itsreturn 0(the control words follow the misdrawn ones)C(the whole-stderr assertion)ref_faultwithout itsreturn 0after a character fault (the shape refusal follows)a_recorded_ref(2),two-linesref_faultwithout its character checksa_recorded_ref(2),two-linesrefuse_bad_valuesbefore the argument loop (--helpno longer first)test_help_answers_past_…;a_record_that_is_not_a_ref_is_refused_by_name(5, its--helpassertion)case "$url$REPO"reverted tocase "$url"misdrawn_characters "$REPO"reverted to"$url"$REPOor$REPObetween ASCII, and$(…)drops only newlines, none of which is inMISDRAWN, so the two name the same characterschecked_repo'surl="$REPO"-acme/rprinted by--version, both localesM11 and M12 are killed only through the record: an
$OPENREPOSHAPE_REFnow meets the up-front check beforechecked_ref, so the record is the one way intoref_fault's character branch. rv866's R06 (the check reads${OPENREPOSHAPE_REF:-main}rather than$REF) is equivalent and was not run again: a ref from the record has passed all ofref_fault, and a broken record leaves$REFatmain. The runner, its logs and both kill tables (d183e769's anda2af74a0's) are in this PR's attachments, undermutants/; the probes underprobes/.Not changed
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 (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)'s ground. install.sh and openRepoShape: a$OPENREPOSHAPE_BIN_DIR, the repository or a ref holding a bidi control, a character of no width, a soft hyphen or U+2028 is refused by its code point before any request and never echoed, so no line shows the value as another #841's checks on the repository and the ref match bytes alone (misdrawn_charactersunderLC_ALL=C), so in a multibyte locale that is not UTF-8 (GB18030, GBK, Shift_JIS) they can refuse a value the locale reads as text.locale_readingis not used here.rv866's SHOULD-2, ruled out of scope for this PR: under
C, a URL or path holding a character past ASCII is refused by every run but--help.[![:print:]]underCmatches every byte past ASCII, soOPENREPOSHAPE_REPO=/srv/café/openRepoShape.gitis refused, in the words "carries a control character". That isshape_checkout's and setup.sh's rule (setup.sh:207), which already refused it wherever the value is cloned; it now holds for every run. Measured here (each launcher run from inside its own checkout, the stubs first onPATH):LC_ALL=C5cbec222a2af74a0--versionopenRepoShape (/srv/café/openRepoShape.git @ main)--installfrom a file--doctor <dir>beside a checkoutNOT A SHAPE ROOT (exit 2)--preflightwith$OPENREPOSHAPE_SETUP_SHin a checkoutUnder
C.UTF-8all four behave ata2af74a0as at5cbec222, and a test holds the--versioncase for each ofNOT_CONTROLS. rv866's probe of 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 locale-free rule (*[[:cntrl:]]*or the C1 bytes, underLC_ALL=C) passes café underCand still refuses every test value, at the cost of passing a lone raw0x9Bbyte. The place for it is a follow-up issue coveringchecked_repo,ref_character_faultand setup.sh's two guards (setup.sh:207,:232), so the four move together. Lane 2 files nothing without Brett Heap's word.Runs that never read the value refuse it too:
--doctorbeside a checkout,--installfrom a file, a scaffold run with$OPENREPOSHAPE_SETUP_SHin a checkout, and a bare run, as each already refuses a broken record.rv866's NIT-2, a bare run's exit code. With a bad value, a bare run now exits 2 with the refusal, where it printed the usage and exited 1 (measured:
5cbec222exit 1, 69 lines of usage;a2af74a0exit 2, the repository's refusal, nothing on stdout).--help,docs/cli.md:132,README.md:276,docs/handbook.html:858andAGENTS.md:67still say a bare run exits 1. A broken record (One-line install:curl -fsSL …/releases/latest/download/install.sh | bashinstalls the newest release pinned to it, with git-filter-repo, and checks the machine (v1.0.1) #564) is already an exception none of them names, and the next real run would refuse anyway, so no doc changes here.The words: the repository's refusal says
OPENREPOSHAPE_REPOwith no$, the ref's$OPENREPOSHAPE_REF(shape_checkout's andref_fault's words, setup.sh's and the package's too). Kept as they are.The package's
openRepoShape --version(src/openreposhape/cli.py:385) prints U+202E in$OPENREPOSHAPE_REPOas typed, exit 0 (measured from a copy outside any checkout at5cbec222:openRepoShape 0.0.0 (acme/rE2 80 AEx @ main)). Itsclone_urlandref_fault(src/openreposhape/standard.py:72,:178,:198) checkCONTROL_RE(C0, DEL, C1) and not install.sh and openRepoShape: a$OPENREPOSHAPE_BIN_DIR, the repository or a ref holding a bidi control, a character of no width, a soft hyphen or U+2028 is refused by its code point before any request and never echoed, so no line shows the value as another #841's characters, so an escape is refused there. A candidate issue, not filed.install.sh: unchanged; its
checked_repoandchecked_versionalready run before it uses either value.--help,docs/cli.md, README, the handbook, AGENTS.md: unchanged. "--versionprints the ref in force" stays true for every value the check passes.Not run: bash 3.2 (macOS), Git Bash on Windows (the new refusal and UTF-8 cases skip there as install.sh and openRepoShape: a
$OPENREPOSHAPE_BIN_DIR, the repository or a ref holding a bidi control, a character of no width, a soft hyphen or U+2028 is refused by its code point before any request and never echoed, so no line shows the value as another #841's do), any real terminal emulator. CI is Linux-only since ci: run the test suite on Linux only #597.Closes #866
🤖 Generated with Claude Code
Summary by Sourcery
Validate repository and ref environment values before any output or operation, refusing terminal-unsafe and clone-invalid input without altering accepted values.
New Features:
Bug Fixes:
Enhancements:
Tests: