Conversation
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>
Reviewer's GuideThis 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 refusalsequenceDiagram
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
Sequence diagram for broker refusal becoming a single unavailable noticesequenceDiagram
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
Flow diagram for isolated trust-store test casesflowchart 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
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
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
Open (10)
Rollback can overwrite a concurrent trust update · New Equal-length relative paths bypass linked-directory checks · New Explicit bindings paths bypass symlink protection · New Dash-prefixed executables bypass broker validation · New Non-atomic writes can corrupt documents after I/O failure · New Refusal messages allow terminal injection through paths · New Unreadable-document refusal paths allow terminal injection · New Invalid host responses incorrectly suggest recording trust · New NUL-containing broker paths crash trust evaluation · New Impossible brokers are incorrectly offered for intake · New
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.
…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>
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Verified broker-validation bypasses and execution-path mismatches leave security and functionality issues unresolved.
Review effort: Balanced
Findings: 7
Open (10)
Path validation and child execution use inconsistent resolution · New Wrapped delegated executables bypass command validation · New Dotted module names can execute code inside the checkout · New Repository symlinks to external targets bypass trust validation · New Attached Python scripts bypass inline-script prohibition · New Explicit bindings paths bypass symlink protection Rollback can overwrite a concurrent trust update Preflight permission errors bypass store refusal handling · New Interpreter scan wrongly rejects arguments after script filename · New Non-atomic writes can corrupt documents after I/O failure
Resolved since last review (7)
Dash-prefixed executables bypass broker validation Equal-length relative paths bypass linked-directory checks Impossible brokers are incorrectly offered for intake NUL-containing broker paths crash trust evaluation Invalid host responses incorrectly suggest recording trust Unreadable-document refusal paths allow terminal injection Refusal messages allow terminal injection through paths
…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>
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Verified broker-command validation bypasses still allow mutable repository programs to execute.
Review effort: Balanced
Findings: 6
Open (8)
Path resolution ignores the broker execution context · New Launcher depth limit allows partially unwrapped commands · New Attached Python scripts bypass inline-script prohibition Repository symlinks to external targets bypass trust validation Dotted module names can execute code inside the checkout Path validation and child execution use inconsistent resolution Interpreter scan wrongly rejects arguments after script filename Preflight permission errors bypass store refusal handling
Resolved since last review (11)
Wrapped delegated executables bypass command validation Dash-prefixed executables bypass broker validation Explicit bindings paths bypass symlink protection Equal-length relative paths bypass linked-directory checks Rollback can overwrite a concurrent trust update Impossible brokers are incorrectly offered for intake NUL-containing broker paths crash trust evaluation Invalid host responses incorrectly suggest recording trust Unreadable-document refusal paths allow terminal injection Refusal messages allow terminal injection through paths Non-atomic writes can corrupt documents after I/O failure
… (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>
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Verified broker-admission bypasses and trust rollback and document-publication failures remain unresolved.
Review effort: Balanced
Findings: 5
Open (6)
New trust documents are written non-atomically · New Intermediate symlink targets bypass repository name checks · New Sourceless .pyc module candidates are not checked · New Module option parsing misses attached and clustered operands · New Duplicate-add rollback can revoke an identical trust record · New Scalar environment paths are incorrectly split as path lists · New
Resolved since last review (8)
Launcher depth limit allows partially unwrapped commands Path resolution ignores the broker execution context Attached Python scripts bypass inline-script prohibition Repository symlinks to external targets bypass trust validation Dotted module names can execute code inside the checkout Path validation and child execution use inconsistent resolution Interpreter scan wrongly rejects arguments after script filename Preflight permission errors bypass store refusal handling
…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>
#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>
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
The custom broker-command classifier has verified admission bypasses and needs final human security review.
Review effort: Balanced
Findings: 3
Open (3)
Resolved since last review (6)
Duplicate-add rollback can revoke an identical trust record Module option parsing misses attached and clustered operands Sourceless .pyc module candidates are not checked Intermediate symlink targets bypass repository name checks New trust documents are written non-atomically Scalar environment paths are incorrectly split as path lists
…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>
…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>
|
| found += [package + suffix for suffix in _MODULE_SUFFIXES] | ||
| found += [os.path.join(package, "__init__" + suffix) | ||
| for suffix in _MODULE_SUFFIXES] |





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):
sh/bash/zsh/dashand the like with-c,pythonwith-c,nodewith-e/-p,perl/rubywith-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.env(with-SandNAME=value),nice,nohup,timeout,stdbuf,setsid,chrt,ioniceandtaskset, with their flags.["/usr/bin/env","python3","-c",…]is an inline script.PWDandOLDPWDare dropped from a broker's environment.busybox sh -c,xargs sh -c).awk,find -exec) is the recorded accepted limit.96ae8816, all in this push. See the section of its own below.GovernedBindingTrustagainst this PR at96ae8816: D2 and D3, in this push. See the same section.tests/test_model_binding_trust.py(section 9 below).broker_unreachable.-mthrough linked package initializers and__main__, and through the effectivePYTHONPATH, fixed; a repeated working directory refused fail-closed; one accepted limit (see "Accepted limits").-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.d3587910): N1,julia -e/--eval/-E,Rscript -eandR -eare 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.e03d1a56, which found round 3 sound):raku -eis an inline script, fixed now (rakuis 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").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.pyunless another file is named. Each fails at38d3350e(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.38d3350e)trust_remedyprints a command only wheretrust_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) printsREMEDY_NOT_BY_TRUSTand the cause.turn_message_foranswers the newUNTRUSTABLE_TURN_MESSAGEfor such a binding, and the approval answersAPPROVED_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_itbroker_refusal(binding, root=)is refused inrecorded_for,_judged(soverdict_forunder any policy),intake_verdict_for, andrequire_admitted(the gate beneath, for a verdict given before the rule). It answersREASON_INLINE_SCRIPT(remedyREMEDY_INLINE_SCRIPT) for an inline script, andREASON_IN_REPOSITORY(remedyREMEDY_IN_REPOSITORY) for a command naming a file inside the repository. Every broker runs inBROKER_WORKING_DIRECTORY, the file system's root, and is judged from there. A command that cannot be read to the program it runs answersREASON_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)declared_model_port_factorypasses over a binding the catalog cannot list, as it passes over a pending one, with[model-provider] model binding … is passed over: …on stderr.listsayspasses 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 declaresBrokeredProviderPort._now_unavailableprints 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]offeredalso requiresdoxbench_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 withoutintake_verdict. The reason given isINTAKE_NOT_ADMISSIBLE. It is also not offered for a declared broker that the A2 rules refuse; that reason isINTAKE_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 answersintake_verdict.set-credentialrefuses withrefusal_message(..., bindings=<the document it read>).test_A6_set_credential_prints_the_command_for_the_document_it_read_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, withRECOVER_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]_refuse_foreign_linksrefuses the state directory itself as a link, whoever owns it. The refusal is_store_refused_state_link, whose remedy is to setOPENDOX_STATE_DIRto 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 existingdirectory-linkplant now uses a 0700 target, and the danglingstate-directorycase now expects this refusal.test_A9_an_ancestor_others_could_write_trusts_nothing_unless_sticky; G2test_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'sanother-kind,older-schemaandnewer-schemashown()on every path thatlist,add,edit,removeandtrustprint.test_A10_a_repositorys_path_is_printed_escapededitreads whether the existing form was trusted. If the write then fails (BindingRefusedorOSError), 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).addandset-credentialalso wrapOSError. 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)type(...) is TrustVerdictin_held_to,require_admittedand the provider'scatalog().test_A12_a_verdict_subclass_cannot_admit_another_binding, which covers the seam, the gate and the providerpolicy()is one operation under the seam's lock.recorded_forasks 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_nameconftest.pyhas a session fixture and a per-case autouse fixture. Each gives a scratchOPENDOX_STATE_DIRunder 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), thentest_A15_the_seam_is_emptied_after_every_caseDIGEST_SCHEME_FIELDSis pinned toBINDING_FIELDS. A held digest under another prefix readsREASON_RECORD_FORM, never "changed", andtrustrepairs it.test_A16_a_digest_of_another_scheme_reads_as_another_record_form,test_A16_the_digest_scheme_names_every_field_of_the_record_sync_directory(state)afteros.replace. A failure there leaves the recorded trust recorded.test_A9_and_A17_the_store_and_its_directory_are_synced_make_private_directoriesjudges its starting directory byfstaton the descriptor before the firstmkdir, as #69's bundle does.test_A18_the_directory_the_store_is_made_in_is_judged_firstregistered_verdict_forasksMachineTrust()without registering it where nothing is registered.test_A19_an_approval_reads_this_machines_store_where_nothing_registereddoxbench_binding.linked_componentrefuses a link at the document, and at any directory of its default relative path, inBindingStoreandDeclarationStore, on read and on write. This also covers a path that is exactly the relative path (r4179076919).--bindingsis 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]doxbench_binding.read_settings_documentrefuses 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_nothingWhat 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).["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._which): a relative entry is read from the broker's directory, and a file that cannot run is passed over.-m, read by Python's own option rules (-m X,-mX,-BmX; a value letter-W/-Xor-cends a cluster; r4180041213), under the start directory and every entry of the broker's effectivePYTHONPATH(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)._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,sudoanddoasare 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=valueand--opt valueforms, 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/--chdirandsudo -Dmove the working directory.env PATH=…sets the search path, andenv -i,env -,--ignore-environmentand-u PATHleave the platform default.env -C DIR, every directory ofenv NAME=VALUE(an assignment as GNU env reads one: any member holding a=after its options, whatever its name,A-B=xincluded; r4181006390),xargs -a FILEin 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 STRINGis 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 asenv'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.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 repeatedenv -C/--chdir, counting one anenv -Sstring gives, or a repeatedsudo -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>/argsis in the repository,xargs -n 1 python3 -can inline script; r4181006365); launchers left past the depth (r4179366319); and a path whose links pass the kernel's bound (r4180041184).doxbench_provider.broker_environment: the harness allowlist withoutPWDandOLDPWD, 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 anyos.pathsep-separated part of it names a path inside, so aHOMEor aTMPDIRis 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.12ispython). 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 profileis not refused), at--, and atpython -m,php -fand 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.sh,bash,zsh,dash,ksh,cshand their kin):-cin a cluster, with+o/-otaking a value.fish:-c,-C,--command,--init-command.python/pypy/jython:-c.perl, andraku, 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/--fileand--argsending its options (N1).pwsh -Command/-c/-EncodedCommand/-CommandWithArgs, in any case.flock FILE -c,su -c,runuser -c/--command.awk 'PROGRAM',sedorfind -exec, is not judged; nor is an interpreter the table does not hold, such asguile -cor-e,elixir -e,erl -eval,escript,groovy -e,scala -e,clojure -e,gjs -candswipl -g(D7 F1, 5988369111). The limit is stated inREASON_INLINE_SCRIPT's andinline_script's documentation.Behavior changes an operator sees
OPENDOX_STATE_DIRset to a symbolic link is refused. Point it at the directory itself (A8).ideation/orideation/dashboard/, is refused. This includes a--bindingsthe operator names (N1).intake_verdict(A5).env -Sstring with a backslash,$or#,env --argv0,sudo --chroot,sudo -i(C1).addoreditleaves no trust for a form no document declares (C2), unless the document now declares exactly that form (r4180041219).PATHand the other listed path lists are dropped, and any other variable naming one is dropped whole (F16.1 as batch P amends it; r4180041233).record()answers nothing,add,editandtrusttake that policy'sverdictas the trust, and write nothing to this machine's store (5986391296).add,edit,set-credentialand 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.UNTRUSTED_BINDING_REMEDYnow 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 shortTMPDIR.fe56c0c4. T100, 16.3a: a served repository's bindings are trusted per machine (plan 034) #82 landed with 3945 passed and 177 skipped;96ae8816had 4089 passed;d35879104236;e03d1a564439;d4877eb8(round 4 alone) 4450;c00a8fd74453.c00a8fd7(the previous head), for Copilot's fifth review and 5988818366: the final test file run againstc00a8fd7'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, andtest_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).e03d1a56, for Copilot's fourth review (5988088910) and D7 F1 (5988369111) (reported atc00a8fd7): 11 failed, 466 passed. Every case for a behavior change fails there (10 for round 4, andraku-e); its controls pass (one working directory per launcher,raku-given-a-file, anddotted-module-linked-at-its-last-name).d3587910, for Copilot's third review, 5986391296 and N1/N2 (reported ate03d1a56): 30 failed, 433 passed; every behavior-change case of that round fails there, its controls pass.96ae8816, for Copilot's second review, C1 to C5, D2 and D3, and F16.1 as batch P amends it (reported atd3587910): 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.38d3350e,3e4958aband46ac0a0f, for the earlier rounds: 156, 82 and 26 failed, as reported for those heads.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); andPath.is_filefor 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-commandpins it,fe56c0c4).Copilot's first review (5407887563, at
3e4958ab)All 10 findings are taken, each with a test that fails at
3e4958aband a mutant. The table above names each one by its comment id:--bindingsresolved.--.r4179077004 reverses an expectation two #82 cases held: a host answering for another binding or another root used to get the
trustcommand. It now gets none, becausetrustasks 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
96ae8816and a mutant:/, and its search path, as the launchers change them) is where a command is judged.-mname, judged whole.python -cprint(1)).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
d3587910and a mutant: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).[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.[sourceless-bytecode-module, extension-module].-mby Python's option rules.[module-attached-to-its-option, module-in-an-option-cluster], and the-Wm…/-Xm…controls.test_R3_a_variable_off_the_path_list_is_kept_whole_or_dropped_whole.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'srecord()reports whether it recorded anything by what it answers (doxbench_trust.recording_for,TrustRecording):GovernedBindingTrust.record()is ruled to answer) recorded nothing: the policy'sverdictis 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);REASON_NO_WITHDRAWALwith 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
e03d1a56and a mutant:-mjudges 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].[env-chdir-twice, env-chdir-twice-long, env-chdir-again-in-a-split-string, sudo-chdir-twice]; controltest_R4_one_working_directory_per_launcher_is_judged.-mis judged under the effectivePYTHONPATH.[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
c00a8fd7with a harmless marker payload before the fix, and is taken as the holder ruled, with a test that fails atc00a8fd7and a mutant:-mfound through the interpreter's own default import path (site-packages, a.pthfile, an editable install) linking into the repository. Accepted limit, the class 5988088910 named (see "Accepted limits"); recorded inin_repository_argv's and_python_roots's documentation.-mjudges 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.=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.[xargs-building-its-command, xargs-and-a-program-outside, xargs-abbreviated-option]andtest_R5_xargs_is_refused_after_the_other_two_judgments; the C4xargs -acases and batch P'sxargsandxargs-argument-filekeep their names.[program-past-an-assignment-whose-name-is-no-identifier, value-of-an-assignment-whose-name-is-no-identifier]; the C1 controlsenv A-B=xandenv 1A=x A.B=ybefore a program outside.sudo A=b broker(admitted whilesudo brokerwas 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 -ewas admitted whileperl6 -ewas refused (only because the version suffix stripsperl6toperl).rakuis now read as perl is.[raku-e]; control[raku-given-a-file](["raku", "/opt/x.raku"]); mutant "raku is not read".Accepted limits (recorded on the holder's rulings)
awk,sed,find -exec) is not judged as an inline script (5984069416, item 4).guile -cor-e,elixir -e,erl -eval,escript,groovy -e,scala -e,clojure -e,gjs -candswipl -g, is not judged as an inline script. They are named inREASON_INLINE_SCRIPT's andinline_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;chrootneeds privilege besides (5986391296).-mmodule is judged under the effectivePYTHONPATH.-mfinds on the interpreter's own default import path (site-packages, a.pthfile, an editable install) that links into the repository (r4181006277): the same class (5988818366). Refusing every-mnot found under the judged roots would refuse ordinarypython3 -m <installed broker>brokers.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).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)
96ae8816)REASON_UNREADABLE_COMMAND.env -Sis 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_themaddoredittakes back the trust it recorded: the earlier form trusted again where it was, the new form's trust withdrawn where it was not (MachineTrust.restorewith 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_soread_settings_documentrefuses a constructor'sValueErroras "is not readable YAML", afterUnicodeDecodeError, 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)xargs -a/--arg-filein 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 -afive ways,-I<dir>,-wI<dir>,PERL5OPT=-I<dir>two ways,time -o,flock's file)test_C5_a_wrong_kind_in_the_stores_place_names_what_it_is[directory, fifo, socket]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,listshows it trusted and prints no command.test_D2_the_remedy_where_trust_cannot_help_holds_under_every_policyINTAKE_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 keepsINTAKE_BROKER_UNTRUSTEDunchanged.FIXED_DIAGNOSTICSis the broker's closed set and holds neither sentence, so it is unchanged. Two #82 cases whose host refused (a host with nointake_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_sentenceF16.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,trustandset-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 andenv -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:addwrites nothing,editleaves the document as it was,trustrecords nothing,set-credentialreads no credential, each naming its reason and remedy; with a trust recorded as before the rule, it reads untrusted, the catalog lists itavailable: 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 noPWD,OLDPWD, variable or path-list entry inside the root, thoughPATH,PYTHONPATH,NODE_PATHand 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 bytrustwhile the paths resolved outside admits nothing once a link or aPATHentry leads inside. ThePATHalias is the program's case only, as batch P's head051f9945has 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_thereandtest_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 freshopenDox-codetree, a fresh venv,pip install ".[test]"), with the block extracted from openxFactory mainba6bb870, batch P landed (#1230, head051f9945), lines 3604-3647 and 3666-3667 (as writtenfe22ec4873d1, as run619b32a955e2; batch P changes no command): rc 0 atfe56c0c4: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 nofailedorerrorin the log.For the holder
PATH, no such file) is not refused at trust. It fails before spawn asbroker_unreachable, as before, and as the holder's 5985553609 ("a real file" is what runs) keeps it.PWDandOLDPWDnever 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.env -Sstring 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) answerREASON_UNREADABLE_COMMAND, with the inline-script remedy.xargs(inline) andxargs-argument-file(in the repository) keep their names, and the full F16.1 block passes at this head.env --argv0(a program told another name),sudo --chroot(a new root for every path) andsudo -i(the target user's home as working directory). Accepted (5986391296).#84 (T104) landed first (main
32943cbf). This PR merged main ata41cc4f8(a merge commit, clean:tests/test_capability_honesty.pyand 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