Skip to content

T100 follow-on: the trust store's review findings (plan 034) - #86

Draft
brettheap wants to merge 14 commits into
mainfrom
build/034-p3-t100-followon
Draft

brettheap wants to merge 14 commits into
mainfrom
build/034-p3-t100-followon

Conversation

@brettheap

@brettheap brettheap commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

The T100 follow-on, plan 034. The holder ran an adversarial review of openDox-code#82 (T100, landed as 38d3350e). It found 19 issues (0 high, 9 medium, 10 low) and 2 older ones outside the diff. This PR acts on every finding except A14, whose ruling asks for no change, and on N1 and N2. It lands before T087.

Rulings (openxFactory#656 comment 5982436447; comments 5983805990 and 5984069416 for A2's extension; comments 5985046107 and 5985490378 for the review of this PR; 5985553609 for "a real file"; 5986391296, 5988088910 and 5988818366 for Copilot's third, fourth and fifth reviews; 5988369111 for lane openXfactory-3's D7 review):

  • A2, Brett Heap, "Refuse in-repo programs": a binding's argv may not name a file inside the served repository. It is refused by name where trust is recorded and where it is checked, with the remedy "install the broker outside the repository".
  • A2 extended, Brett Heap, 2026-10-04, comment 5983805990, "Refuse inline scripts (Recommended)". (1) A binding whose program is a shell or an interpreter given an inline script is refused by name: sh/bash/zsh/dash and the like with -c, python with -c, node with -e/-p, perl/ruby with -e, and so on. The program must be a real file outside the served repository, and the remedy is to name the program itself (for example ["pass","show","key"]) or a script kept outside the repository. (2) Every broker starts with its working directory outside the served repository, as defence in depth.
  • A2 launchers, the holder, comment 5984069416, implementing 5983805990:
    1. The common launchers are unwrapped to the program they start, and the inline-script and in-repository rules apply to that program. The launchers are env (with -S and NAME=value), nice, nohup, timeout, stdbuf, setsid, chrt, ionice and taskset, with their flags. ["/usr/bin/env","python3","-c",…] is an inline script.
    2. PWD and OLDPWD are dropped from a broker's environment.
    3. Further wrappers of the same class are treated the same way (busybox sh -c, xargs sh -c).
    4. A general program that runs code from its own arguments (awk, find -exec) is the recorded accepted limit.
  • The review of T100 follow-on: the trust store's review findings (plan 034) #86, the holder, comment 5985046107, "FIX ALL FIVE": C1 to C5 of the adversarial review of this PR at 96ae8816, all in this push. See the section of its own below.
  • The T094 addendum, the holder, comment 5985490378, an addendum to 5985046107 from lane 4's check of openxFactory's GovernedBindingTrust against this PR at 96ae8816: D2 and D3, in this push. See the same section.
  • F16.1 as T007 batch P amends it (openxFactory#1230), the holder's cases for the rulings above and for 5985553609: each case is one test in tests/test_model_binding_trust.py (section 9 below).
  • 5985553609, the holder: "a real file" is what runs. A program that cannot be found is not refused at trust; it fails before spawn as broker_unreachable.
  • Copilot's third review, the holder, comment 5986391296: five fixes, option (a) for the add race, and the writer's three choices accepted (see "Copilot's third review" and "Accepted limits" below).
  • Copilot's fourth review, the holder, comment 5988088910: -m through linked package initializers and __main__, and through the effective PYTHONPATH, fixed; a repeated working directory refused fail-closed; one accepted limit (see "Accepted limits").
  • Copilot's fifth review, the holder, comment 5988818366: a dotted -m's parent modules, an unknown interpreter option's value, and env's assignments of any name, fixed; every xargs and any assignment given to sudo refused fail-closed as unreadable; one accepted limit, the interpreter's own default import path (see "Copilot's fifth review" and "Accepted limits"). Its convergence rule: after this push T100 follow-on: the trust store's review findings (plan 034) #86 goes to READY-CANDIDATE, and the holder rules any further Copilot round at READY.
  • Lane openXfactory-3 D7 early findings N1/N2, holder ruled fix now (from that lane's read-only review of d3587910): N1, julia -e/--eval/-E, Rscript -e and R -e are inline scripts; N2, deno's leading global options are read before its subcommand (deno --quiet eval, deno -q eval), and an unknown one is refused as unreadable, fail-closed.
  • Lane openXfactory-3 D7 F1, the holder, comment 5988369111 (from that lane's independent read-only review of e03d1a56, which found round 3 sound): raku -e is an inline script, fixed now (raku is perl6's current name and is read as perl is); the interpreters the table does not hold are an accepted limit, named (see "Accepted limits").
  • A14, Brett Heap, "Served repo only, limit": no code change. A later T007 batch records the limit.
  • A8, the holder: follow F16.1's ratified text. A state directory that is itself a symbolic link trusts nothing.

The claim is openxFactory#656 comment 5982447319.

Each finding, its fix and its test

Every case named here is in tests/test_model_binding_trust.py unless another file is named. Each fails at 38d3350e (see Evidence), except the A9 cases, which pin behavior that was already right. Each fix has at least one mutant, and every mutant is killed.

# Fix Test (red at 38d3350e)
A1 trust_remedy prints a command only where trust_can_repair(reason), that is for never trusted, changed, another record form, or no verdict. (Since Copilot's r4179077004, "not covered", a policy's invalid answer, is not on that list.) Any other reason (the store, the platform, a host's own refusal, a failing policy) prints REMEDY_NOT_BY_TRUST and the cause. turn_message_for answers the new UNTRUSTABLE_TURN_MESSAGE for such a binding, and the approval answers APPROVED_UNTRUSTABLE_NOTICE. test_A1_an_unsupported_platform_is_told_no_trust_command, test_A1_a_hosts_own_refusal_is_told_no_trust_command, test_A1_an_unusable_store_is_told_no_trust_command, test_A1_a_policys_answer_for_another_binding_is_told_no_trust_command. The converse, that every repairable reason still prints a command which works when run as printed: test_A1_each_reason_trust_repairs_prints_the_command_that_repairs_it
A2 broker_refusal(binding, root=) is refused in recorded_for, _judged (so verdict_for under any policy), intake_verdict_for, and require_admitted (the gate beneath, for a verdict given before the rule). It answers REASON_INLINE_SCRIPT (remedy REMEDY_INLINE_SCRIPT) for an inline script, and REASON_IN_REPOSITORY (remedy REMEDY_IN_REPOSITORY) for a command naming a file inside the repository. Every broker runs in BROKER_WORKING_DIRECTORY, the file system's root, and is judged from there. A command that cannot be read to the program it runs answers REASON_UNREADABLE_COMMAND, with the inline-script remedy. What counts is listed after this table. test_A2_a_broker_inside_the_repository_is_refused_by_name (21 ways of naming a file), test_A2_a_trusted_broker_edited_in_the_repository_never_runs (the review's CANARY case), test_A2_add_refuses_a_broker_inside_the_repository_and_writes_nothing, test_A2_a_shell_or_interpreter_given_an_inline_script_is_refused (46 commands), test_A2_an_inline_script_trusted_before_the_rule_never_runs (the ruling's ["/bin/sh","-c","exec ./tools/broker.py"]), test_A2_an_interpreter_behind_a_name_of_its_own_is_refused, test_A2_every_broker_starts_outside_the_served_repository, test_A2_a_launcher_is_unwrapped_to_the_program_it_starts (22 commands), test_A2_what_a_launcher_is_given_is_judged_too (7), test_A2_a_command_that_cannot_be_read_is_refused_by_name (15), test_A2_a_cleared_search_path_is_the_default_one, test_A2_a_broker_never_inherits_a_working_directory_variable. Controls: test_A2_a_broker_outside_the_repository_is_trusted_as_before, test_A2_a_program_given_a_file_is_not_an_inline_script (22 commands), test_A2_launchers_within_the_depth_are_unwrapped_whole, test_A2_a_general_program_running_its_arguments_is_the_accepted_limit (awk, sed, find -exec)
A3 declared_model_port_factory passes over a binding the catalog cannot list, as it passes over a pending one, with [model-provider] model binding … is passed over: … on stderr. list says passes over it: …. The gate beneath still refuses such a binding. test_A3_the_start_passes_over_a_binding_the_catalog_cannot_list: the harness on PATH, or another binding added, is then what the start declares
A4 BrokeredProviderPort._now_unavailable prints one fixed line, [model-provider] model binding <id> is unavailable: <fixed diagnostic>, through the notice seam. It prints once, as the port turns unavailable, for a resolver refusal or a mint refusal. test_A4_a_trusted_bindings_refusal_is_printed_once_by_name[env, broker]
A5 The intake surface's offered also requires doxbench_trust.intake_admissible(). That reads the seam once and registers nothing. It answers no under openDox's own trust, registered or not, and no for a policy without intake_verdict. The reason given is INTAKE_NOT_ADMISSIBLE. It is also not offered for a declared broker that the A2 rules refuse; that reason is INTAKE_BROKER_REFUSED (Copilot, r4179077029). test_A5_the_intake_is_offered_only_where_a_policy_could_admit_it[none-registered, strict-default, no-intake-verdict] ([admits] is the control), test_A5_an_intake_broker_the_rules_refuse_is_not_offered. tests/test_capability_honesty.py's host-gate case now registers a host trust policy that answers intake_verdict.
A6 set-credential refuses with refusal_message(..., bindings=<the document it read>). test_A6_set_credential_prints_the_command_for_the_document_it_read
A7 _store_refused (another user's link, owner or mode) is kept for the tree checks; a wrong kind of thing in a place of the tree that is no link (C5) names what it is, with RECOVER_MOVE_ASIDE. The new _store_unusable(path, cause, recovery) covers everything else: a torn copy, another kind, an older schema or an unknown entry (RECOVER_MOVE_ASIDE); a newer schema (RECOVER_NEWER); a lock or store this user cannot open (_store_cannot_open); and a copy that cannot be created (RECOVER_PERMISSIONS). test_A7_a_store_refusal_names_its_cause_and_recovery[torn-json, newer-schema, older-schema, another-kind, state-dir-0500, state-dir-0500-once-locked], test_C5_a_wrong_kind_in_the_stores_place_names_what_it_is[directory, fifo, socket]
A8 _refuse_foreign_links refuses the state directory itself as a link, whoever owns it. The refusal is _store_refused_state_link, whose remedy is to set OPENDOX_STATE_DIR to the directory itself. A link above it is judged as before. test_A8_a_state_directory_that_is_a_link_trusts_nothing, with this user's own link to a 0700 directory. The existing directory-link plant now uses a 0700 target, and the dangling state-directory case now expects this refusal.
A9 Tests only. They pin each rule a reviewer mutant survived. G1 test_A9_an_ancestor_others_could_write_trusts_nothing_unless_sticky; G2 test_A9_a_link_above_the_store_in_a_directory_others_could_write; test_A9_a_link_above_the_store_owned_by_another_user_trusts_nothing; test_A9_the_lock_and_the_store_are_opened_without_waiting; test_A9_a_refused_intake_body_is_drained_unread; test_A9_a_directory_made_for_the_store_is_judged_once_made; test_A9_and_A17_the_store_and_its_directory_are_synced (file fsync); kind and schema_version via A7's another-kind, older-schema and newer-schema
A10 shown() on every path that list, add, edit, remove and trust print. test_A10_a_repositorys_path_is_printed_escaped
A11 edit reads whether the existing form was trusted. If the write then fails (BindingRefused or OSError), it puts that trust back and refuses by name (_cannot_write). The trust goes back only while the store still holds the form this edit recorded, compared and written under the store's lock (MachineTrust.restore, restored_for; Copilot, r4179076901). Both settings documents are written atomically (write_settings_document; r4179076956). add and set-credential also wrap OSError. Where the earlier form was not trusted, or the binding is new, a failed write withdraws the trust it recorded (C2); where taking it back fails, the refusal says so and how to recover. test_A11_an_edit_whose_write_fails_keeps_the_trusted_form_trusted, test_A11_a_failed_edit_never_overwrites_a_trust_recorded_meanwhile, test_A11_a_write_that_fails_part_way_leaves_the_document_as_it_was, and C2's four (below)
A12 type(...) is TrustVerdict in _held_to, require_admitted and the provider's catalog(). test_A12_a_verdict_subclass_cannot_admit_another_binding, which covers the seam, the gate and the provider
A13 policy() is one operation under the seam's lock. recorded_for asks it inside its refusal net. test_A13_a_host_torn_down_while_the_default_registers_records_anyway, test_A13_a_seam_that_fails_is_refused_by_name
A14 Ruled: no change. none
A15 The root conftest.py has a session fixture and a per-case autouse fixture. Each gives a scratch OPENDOX_STATE_DIR under pytest's own basetemp, and the trust seam is emptied after each case. test_A15_a_case_has_a_scratch_state_directory_of_its_own (it checks the session's setting too, through a module fixture), then test_A15_the_seam_is_emptied_after_every_case
A16 DIGEST_SCHEME_FIELDS is pinned to BINDING_FIELDS. A held digest under another prefix reads REASON_RECORD_FORM, never "changed", and trust repairs it. test_A16_a_digest_of_another_scheme_reads_as_another_record_form, test_A16_the_digest_scheme_names_every_field_of_the_record
A17 _sync_directory(state) after os.replace. A failure there leaves the recorded trust recorded. test_A9_and_A17_the_store_and_its_directory_are_synced
A18 _make_private_directories judges its starting directory by fstat on the descriptor before the first mkdir, as #69's bundle does. test_A18_the_directory_the_store_is_made_in_is_judged_first
A19 registered_verdict_for asks MachineTrust() without registering it where nothing is registered. test_A19_an_approval_reads_this_machines_store_where_nothing_registered
N1 doxbench_binding.linked_component refuses a link at the document, and at any directory of its default relative path, in BindingStore and DeclarationStore, on read and on write. This also covers a path that is exactly the relative path (r4179076919). --bindings is made absolute, not resolved (r4179076934). test_N1_add_never_writes_through_a_link_a_clone_carries[document, its-directory], test_N1_each_stores_write_refuses_a_link_by_itself, test_N1_the_declarations_document_is_never_written_through_a_link, test_N1_a_relative_path_that_is_the_default_is_judged_whole, test_N1_bindings_named_on_the_command_line_are_never_written_through_a_link[document, its-directory]
N2 doxbench_binding.read_settings_document refuses by name a document this user cannot read, one that is not UTF-8, one nested too deeply, and one whose values its constructor rejects (C3). Both stores use it, and both refuse by name a directory on the way that this user cannot search (r4179241603). Every path in these refusals, and in N1's, is shown escaped (shown_path; r4179076973, r4179076986). test_N2_an_unreadable_bindings_document_is_refused_by_name[no-permission, not-utf-8, nested, unconstructable], test_N2_an_unreadable_declarations_document_is_refused_by_name[…], test_N2_every_refusal_of_a_document_prints_its_path_escaped, test_N2_a_bindings_document_behind_an_unsearchable_directory_is_refused, test_N2_a_declarations_document_behind_an_unsearchable_directory_is_refused, test_N2_a_missing_settings_document_still_declares_nothing

What counts as naming a file inside the repository (A2). A broker command is judged as it runs (r4179241532, r4179366288): from BROKER_WORKING_DIRECTORY, the file system's root, on the search path it inherits, with its launchers unwrapped (below). Every member is judged with its placeholders filled, by every file it could name (_candidates, C4): the member itself or, for an option, its value after = and, for a single-dash option, the rest after its letter (-I<dir>); and, in either, every absolute path it holds (PERL5OPT=-I<dir>). The program itself is never read as an option, and no member after -- is one (r4179076944).

  • A path with a separator, absolute or relative, is joined to the working directory the broker has there, and a relative one to the served root as well, fail-closed, as the repository's author wrote it (F16.1 as batch P amends it: ["python3", "tools/broker.py"]). It is judged whether or not a file is there yet, since a pull could add one. A URL is not joined to the root.
  • The first word is found on the search path, as the child finds it (_which): a relative entry is read from the broker's directory, and a file that cannot run is passed over.
  • Any other bare word counts where it names an existing file in that directory or at the served root; the program's own bare name only as the search path finds it.
  • A module named by -m, read by Python's own option rules (-m X, -mX, -BmX; a value letter -W/-X or -c ends a cluster; r4180041213), under the start directory and every entry of the broker's effective PYTHONPATH (the inherited entries after the environment's filter, or what a launcher assigns or clears; r4180717772): the whole dotted name as a package directory and with every import suffix (importlib.machinery.all_suffixes(): sources, sourceless bytecode, extension modules; r4179241555, r4180041203), every prefix of the dotted name as a module file with every suffix (a parent that is a plain module runs before the import finds it is no package; r4181006328), every package's __init__ on the way and the final package's __main__ (r4180717725).
  • Every name the system looks up on the way to a path is judged (_traversed; r4179241566). Links are expanded one path component at a time, as the kernel expands them, so every name a chain passes through is judged, a link inside the repository anywhere in it included (r4180041184): a link inside the repository is refused wherever it points, and a link outside that points in is refused too. A path whose links pass the kernel's own bound (40), or loop, is refused as unreadable. A path holding a NUL is judged as written, never failing (r4179077018).

Launchers (5984069416, and C1 of 5985046107). env, nice, nohup, timeout, stdbuf, setsid, chrt, ionice, taskset, time, xargs, busybox, flock, sudo and doas are unwrapped, nested launchers included, at most 32 deep. Each is read by its own getopt grammar (_LAUNCHERS, _launcher_options): long options by GNU getopt_long's unambiguous-prefix rule, in both --opt=value and --opt value forms, and short clusters getopt-style (-iS…, -vC/dir, -0u NAME), then its operands (timeout's duration, taskset's mask, flock's file). The command it starts is then judged as a command of its own, in the context the launchers leave it.

  • env -C/--chdir and sudo -D move the working directory. env PATH=… sets the search path, and env -i, env -, --ignore-environment and -u PATH leave the platform default.
  • The launcher's own program is judged, and so is each value and operand of its: env -C DIR, every directory of env NAME=VALUE (an assignment as GNU env reads one: any member holding a = after its options, whatever its name, A-B=x included; r4181006390), xargs -a FILE in every spelling, time -o FILE, flock's file. A file a launcher writes inside the repository is refused too, an accepted strictness (C4).
  • env -S STRING is split as a shell would, only where it holds no backslash, $ or # (which env reads as its own escapes, expansions and comments), and read again as env's arguments. A string it would not split so, or whose quotes do not close, is refused as an inline script (REASON_INLINE_SCRIPT), as F16.1 names it.
  • Refused FAIL-CLOSED as unreadable (REASON_UNREADABLE_COMMAND, with the inline-script remedy): an option the launcher does not have, or has by more than one name (env --d, env --i); a flag given a value; a missing value; an option after which what runs cannot be judged from its words (env --argv0, sudo --chroot, sudo -i); a launcher given a second working directory (a repeated env -C/--chdir, counting one an env -S string gives, or a repeated sudo -D; r4180717752); any assignment given to sudo (sudo A=b, PATH= included), which sudo's policy honours or not (5988818366); every xargs, whose command is built from its input (openDox's request on stdin, or an argument file's contents), asked only AFTER the in-repository and inline-script judgments so those keep their names (xargs -a <root>/args is in the repository, xargs -n 1 python3 -c an inline script; r4181006365); launchers left past the depth (r4179366319); and a path whose links pass the kernel's bound (r4180041184).
  • A broker's environment is doxbench_provider.broker_environment: the harness allowlist without PWD and OLDPWD, whatever that allowlist comes to hold, and, given the served root, without any path inside it (F16.1 as batch P amends it; r4180041233). A CLOSED list of path-list variables (PATH_LIST_VARIABLES: PATH, PYTHONPATH, NODE_PATH, LD_LIBRARY_PATH, PERL5LIB, PERLLIB, RUBYLIB, CLASSPATH, GEM_PATH, MANPATH) loses each entry inside it; any other variable is never edited, and is dropped whole where any os.pathsep-separated part of it names a path inside, so a HOME or a TMPDIR is kept as it is or not at all.

What counts as an inline script (A2 extended). The command is asked with its launchers unwrapped. Every member of the command as written is asked too, not the program alone, so a wrapper not in the launcher table (busybox sh -c, xargs sh -c, sudo bash -c, an unknown wrapper) hides none. A member's name is the name it is called by and the name of the file it resolves to, with any version suffix dropped (python3.12 is python). Its options are read by its own grammar (_INTERPRETERS; r4179241583, r4179241614): a short cluster letter by letter, so an attached script (python -cprint(1)) is read; a letter or a long option that takes a value skips it (-W ignore, --require mod); and the scan stops at the script or module operand (python /opt/broker.py -c profile is not refused), at --, and at python -m, php -f and pwsh -File. A long option the table does not know, given without =, is read both ways, fail-closed: as a flag, and as taking the next member as its value, the scan going on (node --v8-pool-size 1 -e … is refused; r4181006345, 5988818366); a member read as a value is still judged as a path.

  • Shells (sh, bash, zsh, dash, ksh, csh and their kin): -c in a cluster, with +o/-o taking a value. fish: -c, -C, --command, --init-command.
  • python/pypy/jython: -c. perl, and raku, perl6's current name (D7 F1): -e, -E. ruby, lua, luajit, osascript: -e. php: -r, -R, -B, -E.
  • node/bun: -e, -p, --eval, --print (also as --eval=…). deno eval, after deno's leading global options (-q/--quiet, --unstable…, -L/--log-level); an unknown leading option is refused as unreadable (N2).
  • julia: -e, --eval, -E, --print. Rscript: -e. R: -e, with -f/--file and --args ending its options (N1).
  • pwsh -Command/-c/-EncodedCommand/-CommandWithArgs, in any case.
  • flock FILE -c, su -c, runuser -c/--command.
  • The accepted limit (5984069416, item 4): a general program that runs code from its own arguments, such as awk 'PROGRAM', sed or find -exec, is not judged; nor is an interpreter the table does not hold, such as guile -c or -e, elixir -e, erl -eval, escript, groovy -e, scala -e, clojure -e, gjs -c and swipl -g (D7 F1, 5988369111). The limit is stated in REASON_INLINE_SCRIPT's and inline_script's documentation.

Behavior changes an operator sees

  • A binding the catalog cannot list no longer blocks the console. It is passed over, with a stderr line, as a pending one is (A3).
  • OPENDOX_STATE_DIR set to a symbolic link is refused. Point it at the directory itself (A8).
  • A bindings or declarations document that is a link, or sits under a linked ideation/ or ideation/dashboard/, is refused. This includes a --bindings the operator names (N1).
  • The console intake is not offered unless a host registers a trust policy that answers intake_verdict (A5).
  • A broker program inside the served repository is refused (A2), judged from where the broker runs, so is a shell or an interpreter given an inline script (A2 extended), and so is a command that cannot be read to the program it runs: an option a launcher does not have, an ambiguous one, an env -S string with a backslash, $ or #, env --argv0, sudo --chroot, sudo -i (C1).
  • A failed add or edit leaves no trust for a form no document declares (C2), unless the document now declares exactly that form (r4180041219).
  • A broker's environment carries no path inside the served root: such entries of PATH and the other listed path lists are dropped, and any other variable naming one is dropped whole (F16.1 as batch P amends it; r4180041233).
  • The first write of a settings document publishes it whole with a hard link; one made by another process meanwhile refuses this write, and is never overwritten (r4180041167).
  • Under a host policy whose record() answers nothing, add, edit and trust take that policy's verdict as the trust, and write nothing to this machine's store (5986391296).
  • A broker starts in the file system's root, not in the directory the console was started from (A2 extended), and a relative path in its command is read from there.
  • add, edit, set-credential and the console intake write their documents atomically: a new one through a hard link, an existing one by an atomic replace. A document this user cannot write is still refused, never replaced.
  • The rail's UNTRUSTED_BINDING_REMEDY now reads "and, where trusting it can help, …". The JS twin is changed on its one line, so census A is unchanged.

Evidence

All runs used the CI install command (pip install --only-binary :all: -c constraints-cpython312-linux.txt -e ".[runtime,test]") and a short TMPDIR.

  • Full suite on the final tree: 4474 passed, 177 skipped at fe56c0c4. T100, 16.3a: a served repository's bindings are trusted per machine (plan 034) #82 landed with 3945 passed and 177 skipped; 96ae8816 had 4089 passed; d3587910 4236; e03d1a56 4439; d4877eb8 (round 4 alone) 4450; c00a8fd7 4453.
  • Red at c00a8fd7 (the previous head), for Copilot's fifth review and 5988818366: the final test file run against c00a8fd7's source: 12 failed, 486 passed. Every round-5 case for a behavior change fails there (12: both parent-module cases, the odd-named env assignment before the program, node --v8-pool-size 1 -e, node --no-warnings /opt/x.js -e, the three xargs and the three sudo cases, and test_R5_xargs_is_refused_after_the_other_two_judgments). The cases that pass there are controls (a program outside after env's odd-named assignments, a node flag before a file, a flock option before a program) or pin what the old rules already caught (dotted-package-main-linked-into-the-repository, value-of-an-assignment-whose-name-is-no-identifier, member-an-unknown-option-may-take, node-flag-then-e, pwsh-flag-then-command, pwsh-a-word-then-command).
  • Red at e03d1a56, for Copilot's fourth review (5988088910) and D7 F1 (5988369111) (reported at c00a8fd7): 11 failed, 466 passed. Every case for a behavior change fails there (10 for round 4, and raku-e); its controls pass (one working directory per launcher, raku-given-a-file, and dotted-module-linked-at-its-last-name).
  • Red at d3587910, for Copilot's third review, 5986391296 and N1/N2 (reported at e03d1a56): 30 failed, 433 passed; every behavior-change case of that round fails there, its controls pass.
  • Red at 96ae8816, for Copilot's second review, C1 to C5, D2 and D3, and F16.1 as batch P amends it (reported at d3587910): 125 failed, 301 passed; all 44 batch P cases fail there. The new cases that passed there are controls, or spellings the old rules already caught, which pin them.
  • Red at 38d3350e, 3e4958ab and 46ac0a0f, for the earlier rounds: 156, 82 and 26 failed, as reported for those heads.
  • Mutants: 270 mutants, all killed on the final source (fe56c0c4), at least one for every finding, every Copilot comment, every ruling and every batch P rule, one per launcher, and one per interpreter-table entry. The harness (mutate_t100f.py, kept outside the repository) first runs the cases each mutant targets, then the whole file if those all pass. Mutants that became equivalent were dropped, each named here: an option judged whole, and a URL judged as a path (no option's text and no URL is joined to the root unless its first name is one the root holds); .. dropped from the root join (the root always holds ..); the digits grammar (removed: a digit is read past like any letter that is no option); and Path.is_file for the presence check (equivalent on Python 3.12, where it raises the same error). One round-4 survivor, "a dotted module is judged at its top level alone" (the walk over every package initializer judges each prefix already), is killed by a new control, dotted-module-linked-at-its-last-name: the packages lie outside and the last name's own file is a link into the repository. Two round-5 survivors were fixed, not dropped: "a sourceless module is not judged" had become equivalent because the whole name's module files were listed twice (the walk over every prefix now lists them once, e74eb9fb, with two more mutants: a parent's module file not judged, and the whole name's own not judged), and "PowerShell reads no operand before its command" was pinned only by a case the both-ways reading of an unknown option now catches as well (pwsh-a-word-then-command pins it, fe56c0c4).

Copilot's first review (5407887563, at 3e4958ab)

All 10 findings are taken, each with a test that fails at 3e4958ab and a mutant. The table above names each one by its comment id:

  • r4179076901: the rollback race.
  • r4179076919: the equal-length path.
  • r4179076934: --bindings resolved.
  • r4179076944: a program named like an option, and --.
  • r4179076956: atomic writes.
  • r4179076973 and r4179076986: escaped paths.
  • r4179077004: "not covered" prints no command.
  • r4179077018: NUL.
  • r4179077029: the intake for a refused broker.

r4179077004 reverses an expectation two #82 cases held: a host answering for another binding or another root used to get the trust command. It now gets none, because trust asks the same host and is refused.

Copilot's second review (5408265138, at 96ae8816)

All 8 findings are taken, each with a test that fails at 96ae8816 and a mutant:

  • r4179241532 and r4179366288: the broker's execution context (its directory, /, and its search path, as the launchers change them) is where a command is judged.
  • r4179241555: a dotted -m name, judged whole.
  • r4179241566: a link inside the repository pointing outside.
  • r4179241583: an attached inline script (python -cprint(1)).
  • r4179241614: an interpreter's options end at its script.
  • r4179241603: the stores' preflight refuses by name.
  • r4179366319: launchers past the depth, under REASON_UNREADABLE_COMMAND.

Copilot's third review (5409093070, at d3587910; ruling 5986391296)

All 6 findings are taken as the holder ruled, each with a test that fails at d3587910 and a mutant:

  • r4180041167: a new document is written whole beside its place and published with a hard link (os.link), which never replaces one made meanwhile; an edit keeps the atomic replace. test_R3_a_new_document_is_never_seen_in_part, test_R3_a_document_made_meanwhile_is_never_overwritten (both documents).
  • r4180041184: links are expanded one component at a time, bounded at the kernel's 40, past which (or in a loop) the command is unreadable. [a-link-inside-in-the-middle-of-a-chain, a-relative-chain-through-the-repository, a-chain-through-the-repository-as-the-program], test_R3_links_past_the_bound_are_unreadable, and the 40-link control.
  • r4180041203: every import suffix and the package directory. [sourceless-bytecode-module, extension-module].
  • r4180041213: -m by Python's option rules. [module-attached-to-its-option, module-in-an-option-cluster], and the -Wm…/-Xm… controls.
  • r4180041233: the closed path-list list, every other variable whole. test_R3_a_variable_off_the_path_list_is_kept_whole_or_dropped_whole.
  • r4180041219, option (a): an undoing is skipped where the document now declares exactly the form recorded. test_R3_an_add_racing_one_of_the_same_form_keeps_its_trust, test_R3_an_edit_racing_one_of_the_same_form_keeps_its_trust.

record()'s truthiness (5986391296). openDox withdraws only what a policy recorded. A policy's record() reports whether it recorded anything by what it answers (doxbench_trust.recording_for, TrustRecording):

  • a falsy answer (None, as openxFactory's GovernedBindingTrust.record() is ruled to answer) recorded nothing: the policy's verdict is the trust, and a failed document write says truthfully that nothing changed, with nothing withdrawn (test_R3_a_host_that_recorded_nothing_has_nothing_withdrawn[add, edit], test_R3_a_host_that_records_nothing_is_asked_its_verdict);
  • a truthy answer is a record: openDox's own store withdraws its own, and a host's record, which openDox cannot withdraw, is refused as REASON_NO_WITHDRAWAL with the recovery (test_C2_a_host_policy_cannot_be_asked_to_withdraw_and_says_so). No new hook is added.

Copilot's fourth review (5410001269, at e03d1a56; ruling 5988088910)

All 3 findings are taken as the holder ruled, each with a test that fails at e03d1a56 and a mutant:

  • r4180717725: a dotted -m judges every package __init__ on the way and the final package's __main__, every suffix, as resolved. [package-main-linked-into-the-repository, package-init-linked-into-the-repository].
  • r4180717752: a second working directory for one launcher is refused as unreadable, fail-closed, not modelled. [env-chdir-twice, env-chdir-twice-long, env-chdir-again-in-a-split-string, sudo-chdir-twice]; control test_R4_one_working_directory_per_launcher_is_judged.
  • r4180717772: -m is judged under the effective PYTHONPATH. [module-on-an-assigned-pythonpath, module-on-a-relative-pythonpath-entry, module-on-the-inherited-pythonpath], test_R4_the_effective_pythonpath_is_what_the_broker_has.

Copilot's fifth review (5410336597, at c00a8fd7; ruling 5988818366)

Each of the 5 findings was reproduced at c00a8fd7 with a harmless marker payload before the fix, and is taken as the holder ruled, with a test that fails at c00a8fd7 and a mutant:

  • r4181006277: -m found through the interpreter's own default import path (site-packages, a .pth file, an editable install) linking into the repository. Accepted limit, the class 5988088910 named (see "Accepted limits"); recorded in in_repository_argv's and _python_roots's documentation.
  • r4181006328: a dotted -m judges every prefix of its name as a module file, every suffix, under every root. [parent-module-on-an-assigned-pythonpath, parent-module-in-the-start-directory], and [dotted-package-main-linked-into-the-repository], which keeps the round-4 survivor killed.
  • r4181006345: an unknown long option given without = is read as a flag and as taking the next member as its value. [node-unknown-value-option-then-e, node-flag-then-e, pwsh-flag-then-command, node-flag-a-file-then-e] (the last is the ruled strictness); controls [node-flag-then-a-file, node-flag-a-file-and-its-own-options, flock-unknown-option-then-a-program]; and [member-an-unknown-option-may-take], a value still judged as a path.
  • r4181006365: every xargs is refused as unreadable, after the in-repository and inline-script judgments. [xargs-building-its-command, xargs-and-a-program-outside, xargs-abbreviated-option] and test_R5_xargs_is_refused_after_the_other_two_judgments; the C4 xargs -a cases and batch P's xargs and xargs-argument-file keep their names.
  • r4181006390: env's assignments as GNU env reads them. [program-past-an-assignment-whose-name-is-no-identifier, value-of-an-assignment-whose-name-is-no-identifier]; the C1 controls env A-B=x and env 1A=x A.B=y before a program outside.
  • The writer's sibling, sudo A=b broker (admitted while sudo broker was refused): any assignment given to sudo is refused as unreadable, PATH= included. [sudo-assignment, sudo-path-assignment, sudo-option-then-assignment].

Lane openXfactory-3's D7 review (at e03d1a56; ruling 5988369111)

An independent read-only review by lane openXfactory-3. It found round 3 sound: Copilot's third-review fixes (V1 to V6) and N1/N2 verified, and deno's leading options fail-closed. Its one finding, F1 (low), is taken as the holder ruled:

  • raku -e was admitted while perl6 -e was refused (only because the version suffix strips perl6 to perl). raku is now read as perl is. [raku-e]; control [raku-given-a-file] (["raku", "/opt/x.raku"]); mutant "raku is not read".
  • The rest of F1, the interpreters the table does not hold, is an accepted limit, named in the code (see below).

Accepted limits (recorded on the holder's rulings)

  • A general program that runs code from its own arguments (awk, sed, find -exec) is not judged as an inline script (5984069416, item 4).
  • An interpreter the inline-script table does not hold, such as guile -c or -e, elixir -e, erl -eval, escript, groovy -e, scala -e, clojure -e, gjs -c and swipl -g, is not judged as an inline script. They are named in REASON_INLINE_SCRIPT's and inline_script's documentation beside awk, sed and find (lane openXfactory-3 D7 F1, 5988369111).
  • chroot, and any other wrapper not in the launcher table, is not unwrapped: its arguments are judged as members and the inline-script scan reads every member, but paths it reads inside a new root are not. The same class as the awk and find limit; chroot needs privilege besides (5986391296).
  • Two acts of the same form racing (r4180041219): one's undoing can still run before the other's write lands. It fails closed: the declared binding reads untrusted, and trusting it again recovers. A release-1 limit; no revision token and no cross-step lock (5986391296, (a)).
  • A script outside the repository that imports by name from a directory outside it holding links into the repository (r4180717772): that directory's contents are not enumerated, the same class as the awk and find limit (5988088910). A -m module is judged under the effective PYTHONPATH.
  • A module -m finds on the interpreter's own default import path (site-packages, a .pth file, an editable install) that links into the repository (r4181006277): the same class (5988818366). Refusing every -m not found under the judged roots would refuse ordinary python3 -m <installed broker> brokers.
  • An unknown interpreter option read as taking a value refuses a little more than it must: node --no-warnings /opt/x.js -e …, and a flock option before a command whose own arguments hold -c, are refused, an accepted strictness (5988818366).
  • An output path into the repository a launcher or an option names (time -o, -o<root>/out) is refused too, an accepted strictness (5985046107, C4).

The review of #86 (C1 to C5; ruling 5985046107, "FIX ALL FIVE"), and D2 and D3 (5985490378)

# Fix Test (red at 96ae8816)
C1 Each launcher is read by its own getopt grammar: long options by unambiguous prefix in both value forms, short clusters getopt-style. An ambiguous or unknown option, a flag given a value or a missing value is refused, fail-closed, as REASON_UNREADABLE_COMMAND. env -S is split only where it holds no backslash, $ or #; otherwise it is refused as an inline script. test_A2_a_shell_or_interpreter_given_an_inline_script_is_refused[env-split-string-abbreviated, env-split-string-abbreviated-next, …] (--split=, --spl, -iS), test_A2_a_broker_inside_the_repository_is_refused_by_name[relative-after-env-chdir-abbreviated, relative-after-env-chdir-in-a-cluster] (--chd=, -vC/dir), test_A2_a_launcher_is_unwrapped_to_the_program_it_starts[timeout-abbreviated, stdbuf-abbreviated], test_A2_a_command_that_cannot_be_read_is_refused_by_name[ambiguous-d, ambiguous-i, split-string-escape, split-string-variable, split-string-comment, unknown-long-option, unknown-short-option, …], test_C1_a_split_string_env_would_expand_is_refused (${VAR}). Controls: test_C1_a_launchers_own_options_are_read_as_its_getopt_reads_them
C2 A failed add or edit takes back the trust it recorded: the earlier form trusted again where it was, the new form's trust withdrawn where it was not (MachineTrust.restore with no earlier form, restored_for, cli_model_binding._undone). Where that fails, the refusal says so and how to recover (RECOVER_FAILED_UNDOING); a host's policy cannot be asked to withdraw (REASON_NO_WITHDRAWAL). "Nothing in it changed" is said of the document, whose write is atomic. test_C2_an_add_whose_write_fails_withdraws_the_trust_it_recorded[the-system, the-store], test_C2_an_edit_of_an_untrusted_binding_whose_write_fails_trusts_nothing, test_C2_an_undoing_that_fails_says_so_and_how_to_recover[untrusted, trusted], test_C2_a_host_policy_cannot_be_asked_to_withdraw_and_says_so
C3 read_settings_document refuses a constructor's ValueError as "is not readable YAML", after UnicodeDecodeError, which keeps its own words. test_N2_an_unreadable_bindings_document_is_refused_by_name[unconstructable] (the store, the console's start, list), test_N2_an_unreadable_declarations_document_is_refused_by_name[unconstructable] (the store, pending_binding_ids)
C4 xargs -a/--arg-file in every spelling, and every other launcher value and operand, are judged; so is every file a member could name, an option's attached value and every absolute path it holds included. A file a launcher writes inside the repository is refused too, an accepted strictness. test_C4_every_file_a_launcher_or_an_option_names_is_judged (11: xargs -a five ways, -I<dir>, -wI<dir>, PERL5OPT=-I<dir> two ways, time -o, flock's file)
C5 A directory, a FIFO or a socket in the store's place names what it is, with the move-aside recovery; "another user could change" stays for a link, an owner and a mode. test_C5_a_wrong_kind_in_the_stores_place_names_what_it_is[directory, fifo, socket]
D2 (5985490378) REMEDY_NOT_BY_TRUST's last sentence is policy-neutral: "Then list its bindings again: it is shown trusted, or with the command that trusts it". Under a host whose approval trusts the binding, list shows it trusted and prints no command. test_D2_the_remedy_where_trust_cannot_help_holds_under_every_policy
D3 (5985490378) The console intake's hand-off, refused by the host's own registered policy (basis host, as for a pending binding), answers its own FIXED sentence INTAKE_HOST_NOT_ADMITTED: "the host's trust policy does not admit this hand-off; model-binding list shows why" (intake_refusal_reason). Every other basis keeps INTAKE_BROKER_UNTRUSTED unchanged. FIXED_DIAGNOSTICS is the broker's closed set and holds neither sentence, so it is unchanged. Two #82 cases whose host refused (a host with no intake_verdict, and one answering for another root) now expect the host's sentence. test_D3_the_intakes_refusal_names_the_policy_that_refused[default, host], test_D3_each_basis_has_its_sentence

F16.1 as T007 batch P amends it (openxFactory#1230)

Batch P writes F16.1's cases for the rulings above: its named test file asserts, one test per case, that each command is refused by name by add, edit, trust and set-credential, and before any spawn, with no marker file written. tests/test_model_binding_trust.py, section 9:

  • test_F16_1_batch_p_each_command_is_refused_by_name_everywhere (44 cases): an in-repository program or script, absolute and relative to the root; an inline script, through each launcher and a nested chain and env -S; an alias as named and as it resolves; a launcher's own assignment and working-directory option; and group G (perl -I<root>/lib, xargs -a <root>/args, env --chd=<root>, env -S 'sh\_-c\_id', --config=<root>/conf, -o<root>/out, env --i, env --d, env --no-such-option, env -iS "python3 -c '…'", timeout --sig KILL 5 python3 -c, stdbuf --out=L python3 -c, env -S '$BROKER'). For each: add writes nothing, edit leaves the document as it was, trust records nothing, set-credential reads no credential, each naming its reason and remedy; with a trust recorded as before the rule, it reads untrusted, the catalog lists it available: false, a turn is refused by name, the gate refuses a verdict that admits it, and no marker file exists.
  • test_F16_1_batch_p_an_outside_broker_runs_with_nothing_inside_the_root: an outside broker, trusted, runs in / (opendox started at the served root), with no PWD, OLDPWD, variable or path-list entry inside the root, though PATH, PYTHONPATH, NODE_PATH and scalar variables carry them, directly and through aliases both ways.
  • test_F16_1_batch_p_a_trust_recorded_outside_admits_nothing_once_it_leads_in[program-link, script-link, path-entry]: a trust recorded by trust while the paths resolved outside admits nothing once a link or a PATH entry leads inside. The PATH alias is the program's case only, as batch P's head 051f9945 has it.
  • test_F16_1_batch_p_a_programs_bare_name_is_found_where_it_runs, test_F16_1_batch_p_a_relative_word_is_judged_from_the_root_where_it_names_a_place_there and test_F16_1_batch_p_an_environment_value_is_judged_entry_by_entry: the controls for judging a relative path from the root as well, and for the environment's reading.

F16.1 run against this head, by the T089 runner (run-one.sh, env -i, a fresh openDox-code tree, a fresh venv, pip install ".[test]"), with the block extracted from openxFactory main ba6bb870, batch P landed (#1230, head 051f9945), lines 3604-3647 and 3666-3667 (as written fe22ec4873d1, as run 619b32a955e2; batch P changes no command): rc 0 at fe56c0c4: 24 passed (16.6), dialect and model declared; a raw key is refused in a field and in the URL, no model configured: the catalog offers nothing, 83 passed (tests/test_chat_model_configuration.py), 498 passed (tests/test_model_binding_trust.py), and no failed or error in the log.

For the holder

  • A program that cannot be found (not on PATH, no such file) is not refused at trust. It fails before spawn as broker_unreachable, as before, and as the holder's 5985553609 ("a real file" is what runs) keeps it.
  • PWD and OLDPWD never reached a broker before this change either: the harness allowlist (PATH, HOME, LANG, LC_ALL, TMPDIR) excludes them. The ruling's item 2 is made explicit, so it holds whatever that allowlist comes to hold.
  • C1's unreadable env -S string is refused as an inline script, REASON_INLINE_SCRIPT, as F16.1 as batch P amends it names it. The other unreadable commands (a launcher option its getopt would read otherwise, launchers past the depth) answer REASON_UNREADABLE_COMMAND, with the inline-script remedy.
  • Every xargs is now refused as unreadable, which tightens what 5985046107 C4 left admissible. No F16.1 case changes: batch P's xargs (inline) and xargs-argument-file (in the repository) keep their names, and the full F16.1 block passes at this head.
  • Beyond C1's letter, three options are refused as unreadable, fail-closed, because what runs after them cannot be judged from the command's words: env --argv0 (a program told another name), sudo --chroot (a new root for every path) and sudo -i (the target user's home as working directory). Accepted (5986391296).

#84 (T104) landed first (main 32943cbf). This PR merged main at a41cc4f8 (a merge commit, clean: tests/test_capability_honesty.py and the web boundary census auto-merged, census A needs no re-derivation), and every gate above ran on the merged tree.

Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Arc: neutral-product-standalone-operability

🤖 Generated with Claude Code

The holder's adversarial review of openDox-code#82 (T100, landed as
38d3350) found 19 issues and 2 older ones. This change acts on A1 to A19
except A14, and on N1 and N2. The rulings are on openxFactory#656 comment
5982436447: A2 "Refuse in-repo programs" and A14 "Served repo only,
limit" (Brett Heap), and A8 to follow F16.1's ratified text (the holder).

- A1: a `trust` command is printed only where `trust` can repair the
  refusal (TRUST_REPAIRS, trust_can_repair). Elsewhere the store's,
  platform's or host's own reason is printed, with REMEDY_NOT_BY_TRUST.
  This adds UNTRUSTABLE_TURN_MESSAGE and APPROVED_UNTRUSTABLE_NOTICE.
- A2: a broker command naming a file inside the served repository is
  refused by name (in_repository_program, REASON_IN_REPOSITORY,
  REMEDY_IN_REPOSITORY). It is refused where trust is recorded
  (recorded_for) and wherever it is judged (_judged, intake_verdict_for,
  require_admitted).
- A3: the start passes over a binding the model catalog cannot list, as
  it passes over a pending one, and says so by name. `list` mirrors it.
- A4: the brokered port prints one fixed [model-provider] line, naming the
  binding and its fixed diagnostic, as it turns unavailable.
- A5: the console intake is offered only where the registered trust
  policy could admit its hand-off (intake_admissible). Otherwise the
  reason is INTAKE_NOT_ADMISSIBLE.
- A6: set-credential's refusal prints the command for the document it
  read.
- A7: only a link, an owner or a mode is blamed on another user. A torn,
  newer or foreign store, and a directory this user cannot write, each
  name their cause and a recovery.
- A8: a state directory that is itself a symbolic link trusts nothing.
- A9: cases pin the store rules the reviewer's mutants showed unpinned.
- A10: printed paths are shown escaped.
- A11: a failed edit re-records the trusted form and is refused by name.
- A12: a verdict is a TrustVerdict exactly, in the seam, the gate and the
  provider's catalog.
- A13: policy() is one locked operation, asked inside recorded_for's
  refusal net.
- A15: the root conftest gives the session, and each case, a scratch
  OPENDOX_STATE_DIR, and empties the trust seam after each case.
- A16: the digest scheme is versioned (DIGEST_SCHEME_FIELDS), and a
  digest of another scheme reads REASON_RECORD_FORM.
- A17: the state directory is fsynced after the replace.
- A18: _make_private_directories judges its starting directory by
  descriptor.
- A19: an approval with nothing registered asks openDox's default
  without registering it.
- N1: the bindings and declarations documents are neither read nor
  written through a symbolic link a clone could carry.
- N2: an unreadable settings document (permissions, not UTF-8, nested
  too deeply) is refused by name.

The JS twin of UNTRUSTED_BINDING_REMEDY moves with it, on its one line,
so census A is unchanged.

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 4, 2026 19:38
@sourcery-ai

sourcery-ai Bot commented Oct 4, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR implements the trust-store review plan by hardening trust decisions and filesystem boundaries, refusing repository-controlled or unsafe inputs, making CLI and console remediation accurate, and adding isolated regression and mutation-tested coverage for the acted-on findings.

Sequence diagram for repository-controlled broker refusal

sequenceDiagram
    participant CLI
    participant Trust as doxbench_trust
    participant Policy
    participant Store as MachineTrust
    participant Broker

    CLI->>Trust: in_repository_program(binding, root=)
    Trust-->>CLI: REASON_IN_REPOSITORY
    CLI->>Trust: recorded_for(binding, root=root)
    Trust->>Trust: in_repository_program(binding, root=root)
    Trust-->>CLI: TrustNotRecorded
    CLI-->>Broker: no execution

    participant Factory as declared_model_port_factory
    Factory->>Trust: unservable_because(binding)
    Factory->>Trust: in_repository_program(binding, root=root)
    Trust-->>Factory: untrusted verdict
    Factory-->>CLI: pass over binding
Loading

Sequence diagram for broker refusal becoming a single unavailable notice

sequenceDiagram
    participant User
    participant Port as BrokeredProviderPort
    participant Broker
    participant Notice

    User->>Port: resolve_credential_reference(binding, trust=...)
    Port->>Broker: broker request
    Broker-->>Port: BrokerRefused
    Port->>Port: _now_unavailable(diagnostic)
    Port->>Notice: _notice(fixed diagnostic)
    Port-->>User: refusal

    User->>Port: next request
    Port-->>User: unavailable without duplicate notice
Loading

Flow diagram for isolated trust-store test cases

flowchart TD
    Session["pytest session"] --> Base["Session scratch state directory"]
    Base --> Case["Per-case OPENDOX_STATE_DIR"]
    Case --> Test["Test reads or writes trust"]
    Test --> Cleanup["unregister() after case"]
    Cleanup --> Case
Loading

File-Level Changes

Change Details Files
Tighten trust verdicts and operator remediation so only genuinely repairable failures offer a trust command.
  • Classify store, platform, host-policy, in-repository, and unservable failures separately.
  • Add fixed user-facing messages for untrustable turns and approvals, and update the JS remedy twin.
  • Preserve trusted state when binding edits fail and identify the document used by credential operations.
src/opendox/doxbench_trust.py
src/opendox/cli_model_binding.py
src/opendox/serve_workbench.py
src/opendox/web/views/doxbench-chat.js
src/opendox/doxbench_provider.py
Prevent repository-controlled programs and linked or unreadable settings files from being trusted, executed, or written through.
  • Reject broker argv members that resolve to files under the served repository across trust recording, verdict checks, intake, and admission.
  • Reject bindings and declarations documents reached through symbolic links or unreadable as UTF-8/YAML.
  • Pass through unservable bindings at console startup while retaining a refusal gate beneath the factory.
src/opendox/doxbench_trust.py
src/opendox/doxbench_binding.py
src/opendox/doxbench_install.py
src/opendox/doxbench_intake.py
Harden trust-store integrity, filesystem handling, synchronization, and policy-seam concurrency.
  • Distinguish malformed, incompatible, newer, inaccessible, and permission-failure store conditions with targeted recovery guidance.
  • Refuse a state directory that is itself a symlink and validate directory security before creation.
  • Use exact TrustVerdict checks, atomic seam access, digest scheme identification, nonblocking opens, and directory fsync after replacement.
src/opendox/doxbench_trust.py
Make provider availability and intake capability reporting honest and deterministic.
  • Emit one fixed diagnostic when a trusted provider becomes unavailable.
  • Offer model intake only when the registered host policy can admit it.
  • Use isolated per-session/per-case trust state and clear the trust seam between tests.
src/opendox/doxbench_provider.py
src/opendox/serve_workbench.py
conftest.py
tests/test_capability_honesty.py
Expand regression and mutation coverage for all acted-on review findings.
  • Add focused tests for A1–A19, N1, and N2, excluding ruled-out A14.
  • Pin path escaping, filesystem link and permission rules, digest compatibility, failure recovery, and operator-visible messages.
  • Make test harness behavior independent of the host PATH and machine trust store.
tests/test_model_binding_trust.py
tests/test_capability_honesty.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Broker and settings-path protections remain bypassable, and failed-edit rollback can overwrite concurrent trust updates.

Review effort: Balanced
Findings: 4 High severity · 6 Medium severity

Open (10)
What changed in this PR

This T100 follow-on hardens openDox’s per-machine model-binding trust and improves console behavior when bindings are unusable.

Changes:

  • Strengthens broker-path, verdict and settings-file checks.
  • Improves intake gating, startup fallback and refusal diagnostics.
  • Adds adversarial regression coverage and isolates test trust state.
File Description
tests/​test_model_binding_trust.py Adds trust and filesystem regression cases.
tests/​test_capability_honesty.py Registers host trust for intake tests.
src/​opendox/​web/​views/​doxbench-chat.js Clarifies trust-remedy wording.
src/​opendox/​serve_workbench.py Updates intake gating and approval notices.
src/​opendox/​doxbench_trust.py Hardens trust checks and store handling.
src/​opendox/​doxbench_provider.py Reports provider unavailability once.
src/​opendox/​doxbench_intake.py Applies shared settings-document checks.
src/​opendox/​doxbench_install.py Skips bindings the catalog cannot list.
src/​opendox/​doxbench_binding.py Adds link checks and read refusals.
src/​opendox/​cli_model_binding.py Improves diagnostics and failed-edit handling.
conftest.py Isolates test state and trust policies.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/opendox/cli_model_binding.py Outdated
Comment thread src/opendox/doxbench_binding.py Outdated
Comment thread src/opendox/doxbench_binding.py
Comment thread src/opendox/doxbench_trust.py Outdated
Comment thread src/opendox/cli_model_binding.py
Comment thread src/opendox/doxbench_binding.py
Comment thread src/opendox/doxbench_binding.py Outdated
Comment thread src/opendox/doxbench_trust.py Outdated
Comment thread src/opendox/doxbench_trust.py Outdated
Comment thread src/opendox/serve_workbench.py Outdated
…plan 034)

This commit acts on Copilot's first review of openDox-code#86 (review
5407887563, 10 findings at 3e4958a). It also covers A2 as extended by
Brett Heap on openxFactory#656 comment 5983805990, "Refuse inline scripts
(Recommended)".

A2 extended:
- A broker command that gives a shell or an interpreter an inline script
  is refused by name, both where trust is recorded and wherever it is
  judged, with REASON_INLINE_SCRIPT and REMEDY_INLINE_SCRIPT. Examples
  are sh/bash/zsh/dash -c, python -c, node -e/-p/--eval, perl/ruby -e,
  php -r, env -S, pwsh -Command, deno eval, and an awk or sed program.
- Wrappers (env, timeout, busybox) hide none of these. A program is
  judged by the name it is called by and by the file it resolves to.
- Every broker now starts with its working directory outside the served
  repository (doxbench_provider.BROKER_WORKING_DIRECTORY).

Copilot round 1:
- r4179076901: a failed edit puts its earlier trust back only while the
  store still holds the form that edit recorded, compared and written
  under the store's lock (MachineTrust.restore, restored_for).
- r4179076956: both settings documents are written atomically
  (doxbench_binding.write_settings_document).
- r4179076919: a path that is exactly the default relative path has its
  directories judged for links.
- r4179076934: --bindings is made absolute, not resolved, so a link is
  refused rather than followed.
- r4179076944: the program is never read as an option, and a member
  after "--" is never one.
- r4179076973 and r4179076986: every path in a document's refusal is
  shown escaped.
- r4179077004: REASON_NOT_COVERED, a policy's invalid answer, no longer
  prints a trust command.
- r4179077018: a broker path holding a NUL is judged without failing.
- r4179077029: the console intake is not offered for a broker the rules
  refuse (INTAKE_BROKER_REFUSED).

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 4, 2026 20:29

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/opendox/doxbench_provider.py
Comment thread src/opendox/doxbench_trust.py Outdated
Comment thread src/opendox/doxbench_trust.py Outdated
Comment thread src/opendox/doxbench_trust.py Outdated
Comment thread src/opendox/doxbench_trust.py Outdated
Comment thread src/opendox/doxbench_binding.py Outdated
Comment thread src/opendox/doxbench_trust.py Outdated
…an 034)

This implements the holder's ruling on openxFactory#656 comment 5984069416,
which implements Brett Heap's 5983805990 ("Refuse inline scripts").

1. The common launchers are unwrapped to the program they start, each read
   by its own grammar (doxbench_trust._LAUNCHERS, _unwrapped): env (with
   -S, NAME=value and -C), nice, nohup, timeout, stdbuf, setsid, chrt,
   ionice and taskset, and wrappers of the same class: time, xargs,
   busybox, flock, sudo and doas. The in-repository rule judges the
   launcher's own program, the values that could name a file (env -C, and
   each directory of an assigned search path), and the command it starts,
   whose program is found as PATH finds it. The inline-script rule asks the
   unwrapped command as well as every member as written, so
   ["/usr/bin/env","python3","-c",...], env -S "python3 -c ...",
   busybox sh -c and xargs sh -c are refused. An env -S string that does
   not split is refused too. flock -c, su -c and runuser -c are inline
   scripts.
2. A broker's environment is doxbench_provider.broker_environment: the
   harness allowlist without PWD and OLDPWD, whatever that allowlist comes
   to hold.
3. The accepted limit (item 4) is stated where the rule is documented: a
   general program that runs code from its own arguments (awk, sed,
   find -exec) is not judged. The awk and sed rule the previous commit
   carried is removed accordingly.

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 4, 2026 21:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/opendox/doxbench_trust.py Outdated
Comment thread src/opendox/doxbench_trust.py Outdated
brettheap and others added 3 commits October 4, 2026 23:04
… (plan 034)

Copilot's second review of openDox-code#86 (review 5408265138, at
96ae881), all 8 threads, and the adversarial review of #86 at 96ae881,
under the holder's ruling on openxFactory#656 comment 5985046107 ("FIX ALL
FIVE").

Copilot, round 2:
1. A broker command is judged as it runs (r4179241532, r4179366288): from
   BROKER_WORKING_DIRECTORY (now doxbench_trust's, which the provider
   aliases), on the search path the broker inherits (_Context). A relative
   path is joined to that directory only, a relative PATH entry is read
   from it (_which replaces shutil.which), and the launchers change both as
   they do at run time: env -C/--chdir and sudo -D move the directory;
   env PATH=... sets the search path; env -i, env -, --ignore-environment
   and -u PATH leave the platform default.
2. Every name on the way to a path is judged, each in its resolved
   directory, and the end resolved (_traversed; r4179241566): a link inside
   the repository is refused wherever it points.
3. A module after -m is judged by its whole dotted name, every package on
   the way included (r4179241555).
4. Interpreters' options are read by each one's grammar (_Interpreter,
   _INTERPRETERS): a short cluster letter by letter, so an attached script
   (python -cprint(1)) is read (r4179241583), and only until the script or
   module operand, counting options that take a value, so
   python /opt/broker.py -c profile is not refused (r4179241614).
5. Launchers left past the depth are refused, under the new
   REASON_UNREADABLE_COMMAND (remedy REMEDY_INLINE_SCRIPT), as is an env -S
   string that does not split (r4179366319).
6. Both stores' preflight (the link and presence checks) refuses by name a
   directory this user cannot search (r4179241603), and presence reads only
   ENOENT and ENOTDIR as absence (document_present).

The review of #86 (ruling 5985046107):
C1. Each launcher is read by its own getopt grammar (_launcher,
    _launcher_options): long options by GNU getopt_long's unambiguous
    prefix, in both value forms, and short clusters getopt-style (-iS...,
    -vC/dir, -0u NAME). An ambiguous or unknown option, a flag given a
    value or a missing value is refused, fail-closed, under
    REASON_UNREADABLE_COMMAND, and so are env --argv0, sudo --chroot and
    sudo -i, after which what runs cannot be judged from its words. env -S
    is split only where its string holds no backslash, '$' or '#'.
C2. A failed add or edit takes back the trust it recorded: the earlier form
    trusted again where it was, the new form's trust withdrawn where it
    was not (MachineTrust.restore with no earlier form, restored_for,
    cli_model_binding._undone). Where that fails, the refusal says so and
    how to recover; a host's policy cannot be asked to withdraw
    (REASON_NO_WITHDRAWAL).
C3. read_settings_document refuses a constructor's ValueError ("is not
    readable YAML"), after UnicodeDecodeError, which keeps its words.
C4. Every value and operand of a launcher is judged (xargs -a in every
    spelling, time -o, flock's file), and every member by every file it
    could name (_candidates): an option's value, attached or after '=',
    and every absolute path it holds (-I<dir>, PERL5OPT=-I<dir>). A file a
    launcher writes inside the repository is refused too, an accepted
    strictness.
C5. A directory, FIFO or socket in the store's place is refused for what it
    is, with the move-aside recovery, not blamed on another user.

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…intake refusal (plan 034)

The holder's ruling on openxFactory#656 comment 5985490378, an addendum to
5985046107, from lane 4's T094 check of openxFactory's GovernedBindingTrust
against this PR at 96ae881.

D2. REMEDY_NOT_BY_TRUST's last sentence is policy-neutral: "Then list its
    bindings again: it is shown trusted, or with the command that trusts
    it". Under a host whose approval trusts the binding, list shows it
    trusted and prints no command.
D3. The console intake's hand-off refused by the host's own registered
    policy (basis host, as for a pending binding) answers its own FIXED
    sentence, INTAKE_HOST_NOT_ADMITTED: "the host's trust policy does not
    admit this hand-off; model-binding list shows why"
    (doxbench_trust.intake_refusal_reason). Every other basis keeps
    INTAKE_BROKER_UNTRUSTED unchanged. FIXED_DIAGNOSTICS is the broker's
    closed set and holds neither sentence, so it is unchanged.

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
openxFactory#1230 (T007 batch P) writes F16.1's cases for the holder's
rulings on openxFactory#656 (5982436447 item 2, 5983805990, 5984069416,
5985046107 and 5985553609): its named test file asserts, one test per
case, that each command is refused by name by add, edit, trust and
set-credential, and before any spawn, with no marker file.

1. An env -S string env would not split as a shell does (a backslash, a
   '$', a '#', or a quote left open) is refused as an inline script
   (REASON_INLINE_SCRIPT), as F16.1 names it. REASON_UNREADABLE_COMMAND
   stays for launcher options that cannot be read and launchers past the
   depth.
2. A relative path whose first name the served root holds (or `.`, `..`)
   is judged from the root as well, fail-closed, as the repository's author
   wrote it (["python3", "tools/broker.py"]), and so is a bare argument
   that names a file there; the program's own bare name is still found on
   the search path, as the child finds it. A word whose first name the root
   does not hold (a URL, the rest of an option cluster) is judged where the
   broker runs alone. A name on the way counts only inside the root, so a
   path that passes through the root and leaves it is not refused.
3. A broker's environment carries no path inside the served root:
   broker_environment(base, root=) drops each path-list entry that names
   one, and a variable whose whole value does
   (doxbench_trust.names_a_path_inside), beside PWD and OLDPWD.
4. tests/test_model_binding_trust.py, section 9: every case of the amended
   F16.1 (44 commands through all four commands and the spawn), the
   outside broker's working directory and environment, the bare
   program-name control, the root join's controls, and the
   environment's path-list reading, and a trust recorded while the
   binding's paths resolved outside, which admits nothing once a link or a
   PATH entry leads inside.

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 5, 2026 00:50

Copilot AI left a comment

Copy link
Copy Markdown

Comment thread src/opendox/doxbench_binding.py
Comment thread src/opendox/doxbench_trust.py Outdated
Comment thread src/opendox/doxbench_trust.py Outdated
Comment thread src/opendox/doxbench_trust.py Outdated
Comment thread src/opendox/doxbench_trust.py
Comment thread src/opendox/doxbench_provider.py
…6391296 (plan 034)

Copilot's third review of openDox-code#86 (review 5409093070, at
d358791), all 6 threads, as the holder ruled on openxFactory#656
comment 5986391296.

1. A new settings document is written whole beside its place and
   published with a hard link (write_settings_document), so no reader sees
   part of one and one made meanwhile is never overwritten: that write is
   refused (File exists). An edit keeps its atomic replace (r4180041167).
2. Links are expanded one path component at a time, as the kernel expands
   them (_traversed), so a link inside the repository anywhere in a chain
   is judged (outside -> repository -> outside). A path whose links pass
   the kernel's own bound (_LINK_HOPS, 40), or loop, is refused as
   unreadable (REASON_UNREADABLE_COMMAND) (r4180041184).
3. -m is judged with every import suffix (importlib.machinery's: source,
   bytecode, extension modules) and as a package directory (r4180041203),
   and read by Python's own option rules: -m X, -mX, -BmX, a value letter
   (-W, -X) or -c ending the cluster (_module_operands; r4180041213).
4. A closed list of path-list variables (PATH, PYTHONPATH, NODE_PATH,
   LD_LIBRARY_PATH, PERL5LIB, PERLLIB, RUBYLIB, CLASSPATH, GEM_PATH,
   MANPATH; doxbench_provider.PATH_LIST_VARIABLES) loses each entry inside
   the served repository; any other variable is never edited, and is
   dropped whole where any part of it names a path inside (r4180041233).
5. A failed add or edit skips its undoing where the document now declares
   exactly the form recorded (ruling (a); r4180041219). The residual race
   is the accepted release-1 limit: it fails closed.
6. openDox withdraws only what a policy recorded: a policy's record()
   that answers something falsy recorded nothing, its verdict() is asked
   instead (doxbench_trust.recording_for, TrustRecording), and a failed
   write under it says truthfully that nothing changed. A truthy answer is
   a record; under a host policy, withdrawing it is REASON_NO_WITHDRAWAL.

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
brettheap and others added 2 commits October 5, 2026 03:07
#84 landed first, as planned; this brings main at 32943cb into #86 before
its final gates. The merge is clean: tests/test_capability_honesty.py and
the web boundary census auto-merged, and census A needs no re-derivation.

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ng options (plan 034)

Lane openXfactory-3 D7 early findings N1 and N2, from its read-only review
of #86 at d358791; the holder ruled both fix now, in round 3's push.

N1. julia (-e/--eval, -E/--print), Rscript (-e) and R (-e) join the
    interpreter table (_INTERPRETERS), each read by its own option
    grammar: julia's value options skipped (an attached one too:
    -Ccore-avx2), R's -f/--file taking its script with R's own options
    still read after it (fail-closed), and --args ending them.
N2. deno's leading global options are read before its subcommand
    (_deno_subcommand): flags (-q/--quiet, --unstable..., -h, -V) and
    value options (-L/--log-level) are skipped, so deno --quiet eval and
    deno -q eval are inline scripts. A leading option deno's global
    grammar does not hold is refused as unreadable, fail-closed
    (REASON_UNREADABLE_COMMAND), and inline_script names it.

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 5, 2026 04:10

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/opendox/doxbench_trust.py Outdated
Comment thread src/opendox/doxbench_trust.py
Comment thread src/opendox/doxbench_trust.py
brettheap and others added 2 commits October 5, 2026 04:35
…88088910 (plan 034)

Copilot's fourth review of openDox-code#86 (review 5410001269, at
e03d1a5), all 3 threads, as the holder ruled on openxFactory#656
comment 5988088910.

1. A dotted -m judges each package's initializer on the way and the final
   package's __main__, with every import suffix, links followed
   (_module_paths; r4180717725).
2. A launcher given a second working directory is refused as unreadable,
   fail-closed, rather than modelled: a repeated env -C/--chdir, counting
   one an env -S string gives, and a repeated sudo -D/--chdir
   (r4180717752). One per launcher keeps its judgment, nested launchers
   each with their own included.
3. A -m module is judged under every entry of the broker's effective
   PYTHONPATH as well as its start directory (_python_roots;
   r4180717772): the inherited entries after the broker environment's
   filter, or what a launcher assigns (env PYTHONPATH=..., a relative entry
   read from the start directory) or clears (env -i, env -, -u
   PYTHONPATH). THE ACCEPTED LIMIT: a script outside the repository that
   imports by name from a directory outside it holding links into it.

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…5988369111 (plan 034)

Lane openXfactory-3's D7 review of openDox-code#86 at e03d1a5, finding
F1, as the holder ruled on openxFactory#656 comment 5988369111.

1. raku, perl6's current name, is read as perl is (_INTERPRETERS), so
   raku -e is refused as an inline script as perl6 -e already was; a raku
   given a file is not.
2. THE ACCEPTED LIMIT, named beside awk, sed and find in
   REASON_INLINE_SCRIPT's and inline_script's docstrings: an interpreter
   the table does not hold, such as guile -c or -e, elixir -e, erl -eval,
   escript, groovy -e, scala -e, clojure -e, gjs -c and swipl -g.

Also a control the round-4 mutants asked for: a dotted -m whose packages
lie outside and whose last name is a link into the repository is refused
(the whole dotted name is judged, not its top level alone).

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 5, 2026 05:25

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/opendox/doxbench_trust.py
Comment thread src/opendox/doxbench_trust.py
Comment thread src/opendox/doxbench_trust.py
Comment thread src/opendox/doxbench_trust.py
Comment thread src/opendox/doxbench_trust.py Outdated
brettheap and others added 3 commits October 5, 2026 05:58
…8818366 (plan 034)

Copilot's fifth review of openDox-code#86 (review 5410336597, at
c00a8fd), all 5 threads, and the writer's sibling finding, as the holder
ruled on openxFactory#656 comment 5988818366.

1. A dotted -m judges every prefix of its name as a module file with
   every import suffix, under every root, since a parent that is a plain
   module runs before the import finds it is no package (_module_paths;
   r4181006328).
2. A long option the interpreter table does not know, given without =,
   is read both ways, fail-closed: as a flag, and as taking the next
   member as its value, the scan going on (r4181006345, node
   --v8-pool-size 1 -e). The strictness is accepted: node --no-warnings
   /opt/x.js -e is refused. A member read as a value is still judged as
   a path.
3. Every xargs is refused as unreadable, after the in-repository and
   inline-script judgments, so each keeps its name: xargs -a <root>/args
   is in the repository, xargs -n 1 python3 -c an inline script
   (_builds_its_command; r4181006365).
4. env's assignments are read as GNU env reads them: any member holding
   a = after its options and before the command, whatever its name, its
   value judged as a path (_is_assignment; r4181006390).
5. The sibling: any VAR=value given to sudo is refused as unreadable,
   PATH= included, fail-closed.
6. THE ACCEPTED LIMIT (r4181006277), recorded in in_repository_argv's
   docstring with 5988088910's: a module -m finds on the interpreter's
   own default import path (site-packages, a .pth file, an editable
   install) linking into the repository.

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…n 034)

The whole dotted name's own module file is the last prefix's, so the walk
over every prefix (r4181006328; the holder's ruling, openxFactory#656
comment 5988818366) judges it, and it is no longer listed twice. The
round-5 mutants found the duplicate: "a sourceless module is not judged"
had become equivalent. It is now anchored on the walk, and two more
mutants pin it: a parent's module file not judged, and the whole name's
own not judged.

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…n 034)

The round-5 rule for an option the interpreter table does not know
(r4181006345; the holder's ruling, openxFactory#656 comment 5988818366)
reads pwsh -ExecutionPolicy Bypass -Command both ways, so that case no
longer pinned pwsh's operands=None, and the round-5 mutants found it. A
word before -Command, which pwsh reads past, now pins it: refused,
fail-closed, as before.

Arc: neutral-product-standalone-operability
Lane: openxfactory-4 (openXfactory-4-openDox_extraction)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Oct 5, 2026

Copy link
Copy Markdown

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Targeted checks confirmed an execution-validation bypass and quadratic memory allocation before trust approval.

Review effort: Balanced
Findings: 1 High severity

Open (1)
Resolved since last review (5)

Comment on lines +1161 to +1163
found += [package + suffix for suffix in _MODULE_SUFFIXES]
found += [os.path.join(package, "__init__" + suffix)
for suffix in _MODULE_SUFFIXES]
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.

2 participants