Skip to content

Require isolated hash-locked policy parser installation - #240

Draft
rmcdaniel wants to merge 25 commits into
mainfrom
fix/237-isolated-preflight
Draft

rmcdaniel wants to merge 25 commits into
mainfrom
fix/237-isolated-preflight

Conversation

@rmcdaniel

@rmcdaniel rmcdaniel commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

The candidate workflow must not be able to authorize its own CI policy result. This draft separates protected-source policy validation from the candidate checkout and binds the eventual required status to an independent issuer.

The implementation uses the protected policy source, isolated hash-locked parser dependencies, bounded YAML/alias traversal and exact reviewed preflight actions. It verifies effective shell, working directory and Composer network-call guards, and refuses skipped or ignored preflight execution. Caller-supplied preflight containers, services, strategy and startup-hook environments are outside the reviewed shape. The ordinary caller result-gate job is not a general execution sandbox or an independent authority boundary.

Exact current source: d4fe8e08d9e7dfdd2c1b24e477e59573385c30f0, whole tree 0398da440f63bf176211ae3766b9d6ae49fd2ac6, incorporating qualified main ce4a7ca and the final preflight-context component. The protected workflow remains SHA-256 642d204ea939ca5eba7ef51be9e4ae56a35177413caabe849315086d3d9d2d31; reader, check, issuer, parser lock and policy thresholds retain their reviewed bytes.

Validation: the exact combined source passes 157 focused controls on CPython 3.13.16, pip 26.2.2 and the hash-locked native PyYAML 6.0.2 wheel: 155 policy/reader/input/issuer/Composer cases plus both real dependency-installer controls. The installer ignores working-directory pip shadows and rejects a tampered wheel hash. The initial local runner attempts failed because a non-executable temporary mount could not load the native extension and the installer test requires a disposable venv; those were setup failures, not source passes. The final disposable venv run passes without weakening assertions. Fresh GitHub checks qualify this head independently.

A pinned main-caller inventory finds no new environment/default incompatibility from the final component. The existing Server legacy five-step/unlocked bootstrap fails both the preceding and new checker, so its migration remains a separate adoption prerequisite. Other callers retain their actual reviewed results.

Not activated. Organization required status context plus integration ID does not bind a workflow identity. The independent issuer App/environment, live allow/deny and fork, stale, malformed, wrong-issuer, ambiguous-write and required-rule controls remain necessary before activation. Structural source tests do not substitute for those live controls. No account setting, required rule or repository enforcement is changed by this PR.

The earlier combined source passed its hosted source controls but its API check failed after protected main changed. That original remains failed; passing results are not transferred across heads. Related: #237.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Prepared issuer follow-through is pushed separately at
f0b3e47f1e9b12c69817743fd38b55ae3e6e0f66, whole
da2a5c3f22e51665387806ec498387c7b9a3228e, on
fix/237-check-issuer.
This PR's admitted head remains 1d9db9d7; no current pass qualifies the new source.

The component runs the real validator on bounded, inert inputs, binds the
original event head, creates one incomplete Check Run, verifies its name/head
and expected App, then rechecks protected main and the complete PR identity
immediately before completing it. Read and checks-only credentials remain
separate and stdin-only. The two clients share the total API deadline.
No signing key is accepted and no arbitrary repository/endpoint is exposed.

All 98 local tests pass: 27 issuer/caller cases plus the existing 71 reader,
input and policy controls. Wrong App (including the Actions App), stale head,
changed base, forbidden workflow, malformed caller context, timeout and failed
completion refuse. The first 92-case batch had one YAML-fixture failure: a
scalar trigger was refused before the intended forbidden-trigger assertion.
The corrected explicit trigger mapping reaches that assertion. No runtime
guard or expectation is weakened. All responses are synthetic; no App token or
live check was used. Normal CI/Sonar test selections include the new cases.

This does not enable a workflow or policy exception. Protected bootstrap,
immutable source selection, actual App/Environment acceptance and required
issuer binding remain pending. The current policy still rejects all
pull_request_target execution. Account setup was requested once; merge and
Server publication holds remain.

@rmcdaniel rmcdaniel left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Independent maintainer source review of exact ed2334a72976044b0d7aa37f6235de35cae71e13 (tree 4bd8fb6ba11f68748523fd1622bb76d6ff40a915), in progress. One concrete hash-boundary finding is below.

I reran 47 existing exact-source constructed controls for input capture, bounded read transport and caller context. All pass. The temporary source snapshot was retired; no dependency installation, live App request, Check Run write, credential/settings change or protected-source edit was made. This is not Security-owner acknowledgment or live enforcement qualification. Review of the remaining boundaries and required live controls continues.

policy["workflow_trust"]["pull_request_target_exceptions"] == [PROTECTED_POLICY_EXCEPTION]
and target_name == PROTECTED_POLICY_EXCEPTION["target"]
and Path(workflow_path).parts[-3:] == Path(PROTECTED_POLICY_EXCEPTION["path"]).parts
and hashlib.sha256(source.encode("utf-8")).hexdigest() == PROTECTED_POLICY_EXCEPTION["sha256"]

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

[P2] Preserve raw workflow bytes before checking the exception hash

validate_local_action_references reads files with Path.read_text, which normalizes CRLF and lone CR to LF. This helper therefore hashes normalized text rather than the collected Git blob bytes.

A constructed control using the exact helper and protected workflow shows the effect: reviewed raw SHA-256 642d204ea939ca5eba7ef51be9e4ae56a35177413caabe849315086d3d9d2d31; after changing LF to CRLF, raw SHA-256 54b9cf6a3b3da2ac1337cfeab6976d53dd5815f8e3ccb84bacf3d82bb9178673; the helper still returns True after the current read path. No repository bytes or App state were changed.

This control changes line endings only; it does not demonstrate a semantic or executable workflow change. It does contradict the documented exact-byte/content-hash exception. Preserve original bytes through hashing (or preserve newlines during decoding) and add CRLF/lone-CR file controls before relying on that guarantee.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Prepared and pushed in exact combined source 619c0d595aed0fe9214ed6b1aa0155216bb3bc59, whole de9d1564ea7b7f6e683343a7c36ae91e35733644. Both file-level newline mutations fail before the byte-read correction; the unchanged LF and ordinary workflow controls pass. The alias-work counterfactual also fails before its correction. All 110 focused controls now pass. The initial cycle fixture expected acceptance of a value the existing BaseLoader already refuses; its corrected refusal control preserves the parser and is not claimed as a second product defect. Protected workflow SHA-256 remains unchanged. Fresh whole-source qualification and the named follow-up review remain pending; no App/Environment or live enforcement claim.

@rmcdaniel rmcdaniel left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Additional immutable ed2334a7 source review: the API transport has byte/count/time bounds, but the YAML graph walk has a separate availability risk. A bounded constructed control is described below.

With the exact source and the hash-verified locked PyYAML 6.0.2 wheel's pure-Python BaseLoader, all 79 existing input, reader and issuer tests pass locally. This is not a native ABI/install qualification; the existing hosted amd64/arm64 wheel receipts cover that separate scope. The temporary source/parser snapshot was retired. No live App/API write or credential/settings/source change was made.

candidate = Path(temporary) / "data"
write_inputs(files, candidate)
try:
actions = check(policy, candidate / ".github/workflows", "github-control-plane")

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Residual availability risk: raw byte limits do not bound YAML graph traversal

This call reaches _workflow_action_references, whose recursive visitor follows aliases repeatedly without a visited-node or work budget. A 434-byte, 14-level acyclic alias fixture parsed by the locked PyYAML BaseLoader exceeded 10,000 visits before an instrumented stop; a 206-byte, four-level control took 98. Increasing the alias depth multiplies work while remaining far below the file/transfer bounds. No long-running/hanging probe was performed.

The 120-second API deadline is checked around requests, not while this visitor runs. The outer 10-minute job timeout is the current final time limit. This is resource amplification, not a demonstrated credential or executable-code bypass.

Before enabling the untrusted-input trigger, bound logical YAML work (including other recursive walks), or explicitly assess and retain the outer-timeout risk. A small alias fan-out control belongs with the constructed tests; raw size and JSON-depth controls do not cover it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Prepared and pushed in exact combined source 619c0d595aed0fe9214ed6b1aa0155216bb3bc59, whole de9d1564ea7b7f6e683343a7c36ae91e35733644. Both file-level newline mutations fail before the byte-read correction; the unchanged LF and ordinary workflow controls pass. The alias-work counterfactual also fails before its correction. All 110 focused controls now pass. The initial cycle fixture expected acceptance of a value the existing BaseLoader already refuses; its corrected refusal control preserves the parser and is not claimed as a second product defect. Protected workflow SHA-256 remains unchanged. Fresh whole-source qualification and the named follow-up review remain pending; no App/Environment or live enforcement claim.

@rmcdaniel rmcdaniel left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Completed independent maintainer source review of exact ed2334a72976044b0d7aa37f6235de35cae71e13, tree 4bd8fb6ba11f68748523fd1622bb76d6ff40a915. This review does not qualify a later combined revision or attribute Security-owner approval.

The exact-byte finding remains a source defect in this snapshot. The YAML graph observation is a separate availability risk: byte/JSON transport bounds do not bound recursive alias work, whose current final limit is the outer job timeout. Neither control demonstrated an executable workflow change or credential bypass.

I traced the data-only reader and issuer boundaries: fixed public pilot and repository ID; exact event/base/head identity; canonical regular-file inputs and nonexecutable temporary materialization outside the import path; fixed HTTPS metadata endpoints with explicit digest-checked roots; separate read and Checks-write client roles; protected workflow/source markers; App/name/head/check-ID verification; and main/PR rechecks immediately before completion. The App key is supplied to the pinned token action, not the reader/issuer program. The token action manifest at 64ab729a... recognizes the requested repository and Checks-write scope and uses Node 24. Actual credential scope and denial remain platform properties to prove.

Verification: all 79 existing input/reader/issuer controls passed in an exact-source temporary snapshot using the hash-verified locked PyYAML wheel's pure-Python BaseLoader; this is not a native ABI claim. Existing run 39907499738 checks out c6b4625f..., whose tree independently matches 4bd8fb6b...; it records 193 shared tests, both native wheel jobs, and a read-only API report bound to this head and policy SHA-256 550d11be0ae511644f2b05b4927d214e19ca0fbabe899e20c1d80ab8fface5f7. Both lock hashes match the official PyPI CPython 3.13 glibc amd64/arm64 wheels. No live App/check-write probe was performed. All temporary review material is retired.

Live adoption still needs these distinct controls:

  • Establish the actual pilot-only App installation, effective Checks-write/Metadata-read token scope and protected Environment credential path. Pair negative fork/same-repository/unprotected-context denial with a genuine protected positive control; missing credentials everywhere cannot prove denial. Caller environment markers and constructed App responses are insufficient.
  • Verify the credentialed token action's effective trust selection and failure behavior. The read-only API exercise does not prove the App create/revoke path or its credential denial.
  • Qualify the actual required-check rule with the expected App ID and exact Protected workflow policy name, preserving existing protections and strictness. A same-name Actions/wrong-App check must not satisfy it; safe correct-App/current-head success must. The ordinary policy audit checks context names/strictness, not this new App binding.
  • Exercise stale head/main/policy, malformed input, interrupted/ambiguous completion and same-head rerun behavior. In particular, validation precedes issuer.begin; an early refusal emits no new App check and does not revoke an existing success on that same SHA. Whether prior success can remain qualifying after trusted-policy/base changes needs explicit design/live qualification. This is an unqualified residual, not a demonstrated bypass.

Bootstrap order should keep source review/qualification, protected-main bootstrap, account/Environment preparation, live issuer controls and actual ruleset-enforcement qualification distinguishable. A bootstrap merge is a separately reviewed trust-root action; candidate CI cannot authorize it. Require the final combined source's fresh checks and a named follow-up review, preserving the workflow bytes/hash and existing CI/Sonar module entries. This gate validates workflow inputs, not arbitrary replacements of its own policy, reader, issuer or bootstrap code. Those trust-root changes retain independent review.

@rmcdaniel rmcdaniel left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Additional bootstrap-contract pass on immutable ed2334a72976044b0d7aa37f6235de35cae71e13.

[P2] Bind the preflight working directory as well as its command text.

The job-shape check accepts jobs.action-policy.defaults.run.working-directory; verify_workflow_source also drops workflow-level defaults before calling that helper. Both the locked install and validator commands use relative .central-action-policy/... paths. Setting either default to candidate-controlled therefore makes them select candidate-controlled/.central-action-policy/..., which the candidate can supply, while the reviewed checkout still places protected source at $GITHUB_WORKSPACE/.central-action-policy.

Constructed control through the exact committed validate_local_action_references(policy, directory, "server"), using the existing preflight fixture and locked bootstrap/install: the baseline, added job default, and added workflow default all pass with the same three approved external actions. No candidate command, install, live workflow, or credential operation was executed. Parser: hash-verified PyYAML 6.0.2 wheel Python modules; temporary snapshot removed.

GitHub documents that these defaults apply to run steps (default working-directory semantics); checkout documents its workspace-relative path.

Please bind the effective directory for both commands, or reject both levels of directory override, and add failing-before controls for each. This is a protected-source/preflight contract defect, not evidence of App credential exposure or a live enforcement bypass. The prior issuer/reader findings and required live controls remain unchanged.

@rmcdaniel rmcdaniel left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Named follow-up review: 619c0d595aed0fe9214ed6b1aa0155216bb3bc59, tree de9d1564ea7b7f6e683343a7c36ae91e35733644.

The raw-byte correction resolves the reviewed line-ending defect. Independent failing-before controls on ed2334a7 produce CRLF, lone-CR and alias-work failures; the corrected source passes 126 focused tests, including the Composer contract module. The reader, caller, issuer and protected workflow are byte-identical to ed2334a7; protected workflow SHA-256 remains 642d204ea939ca5eba7ef51be9e4ae56a35177413caabe849315086d3d9d2d31. The CI/Sonar integration retains both protected and metrics modules.

Two follow-up findings:

  1. [P2] Bound secret-context traversal too. The corrected action-reference walker visits shared containers once, but _string_values still recursively expands them while the full workflow scanner checks secret references. A 637-byte candidate YAML graph with fourteen binary alias levels passes the corrected action-reference walk, then exceeds 10,000 calls in this remaining traversal. An instrumented budget stopped at 10,001; no long-running or memory-exhaustion probe was performed. The availability/work-amplification risk therefore remains in the full entry point. Preserve the parser's existing recursive-node refusal and cover the full scan, rather than only _workflow_action_references.

  2. [P2] Apply effective-directory reasoning to Composer helper inspection. _composer_step_source always inspects root / name; _composer_job_sinks does not supply the actual directory. Through the exact full validator, a harmless root scripts/request.sh and a network-request helper at alternate/scripts/request.sh are accepted without a bootstrap/verifier when the same scripts/request.sh command uses step, job-default or workflow-default working-directory: alternate. A cd alternate inside the run script is also accepted. Moving that network request into the inspected root helper correctly refuses it. No helper was executed. Bind or conservatively refuse directory contexts that the source proof cannot resolve. GitHub's default-directory semantics apply to these run steps.

The original preflight-directory finding in review 5627276128 remains present. The separate privileged-dispatch _step_invokes proof already rejects shell/directory overrides at step, job and workflow levels. The exact-byte protected workflow also prevents such candidate changes to that workflow.

Verification: local CPython 3.12.3 with hash-verified PyYAML 6.0.2 Python modules, not native ABI qualification. Existing CI 39941988083 passes 203 shared tests and two wheel controls on each native runner. All three jobs checked out 48df8d44cbc3f8578c66e463637a60cf188e800a, whose tree equals this review's tree. All nine head checks pass; direct Sonar gate is OK with zero open/confirmed PR issues. Temporary snapshots/dependencies/raw are retired.

This is an independent maintainer source review, not Security-owner acknowledgment or live enforcement approval. App setup, Environment denial, real issuer binding and the other live controls in review 5627156061 remain pending. Further corrections need fresh qualification and a separately identified review.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Implementation component for integration and independent review: 9165fc644850420721f0b779db67d5d75c0dba53, based directly on reviewed 619c0d595aed0fe9214ed6b1aa0155216bb3bc59. Component diff.

  • Preflight validates each command's effective working directory, including workflow/job defaults and step overrides. Known workspace-root controls remain valid and the most-specific setting wins. Locked bootstrap and command paths are unchanged.
  • Composer's captured shell-helper proof refuses non-root/unknown directory contexts and directory-changing helper source. Explicit non-shell programs preserve their existing behavior, including the current Rust documentation caller. A following shell helper is still inspected; a shell invocation cannot hide behind a non-shell file extension.
  • One iterative traversal visits shared YAML containers once for both action references and secret detection. Recursive-YAML parser refusal, ordinary scalar order, distinct action references and secret refusal remain covered.

Validation: git diff --check; 139 focused tests pass across qualification policy, Composer contract and the three protected-policy modules. Four selected rejection controls fail against 619c0d5 before the correction, with 33 failure entries, including the bounded alias budget. Local runtime was CPython 3.12.3 with hash-verified PyYAML 6.0.2 Python modules; this is not native wheel qualification.

Only the policy implementation and two test modules changed. Reader, caller, issuer, protected workflow, policy JSON, parser lock, schema and CI/Sonar lists are byte-identical to the base; protected workflow SHA-256 remains 642d204ea939ca5eba7ef51be9e4ae56a35177413caabe849315086d3d9d2d31.

This component needs independent review and fresh combined-source qualification in this PR. App setup and actual Environment/issuer enforcement remain pending; no activation follows from these source tests.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Additional pre-existing preflight execution-context finding, carried through the scoped cwd/graph component: inherited custom shells are still accepted.

A source-only control using the exact committed _verify_action_policy_preflight and existing locked Server fixture accepts both job-level and workflow-level defaults.run.shell: bash -c true {0}. The locked install and validator command strings remain unchanged, and their working directory is the workspace root. No shell, candidate command or live workflow was executed.

GitHub documents that the shell template receives the temporary run-script path at {0}. With this example, Bash's command is true and the script path is an argument, so those reviewed commands need not run. The current preflight shape does not bind inherited shell semantics; the separate privileged-dispatch _step_invokes proof already refuses shell overrides at every scope.

This is another preflight contract defect, not a demonstrated App credential or live issuer bypass. The independently reviewed cwd/graph component remains unchanged. Bind the effective shell to reviewed semantics, or refuse unreviewed inherited templates, with failing-before controls and fresh combined qualification. Implementation/design disposition remains with this PR's owner.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Focused shell-binding follow-up: 17627125d9ae4d2749a44c674f8145be34ec2347, directly after the independently reviewed component 9165fc6 / equivalent integrated tree 7a5b404. Follow-up diff.

The install, validator, result-gate step and Composer relative-helper proof bind the effective shell using the most-specific run setting. Ordinary runner defaults and reviewed built-in bash/sh invocations remain valid. Custom/dynamic invocations refuse, including templates that discard the reviewed script or change directory before running it. Safe step/job overrides retain their precedence. Locked command paths and the separate privileged-dispatch contract remain unchanged.

The actionable S5778 finding AaFohD0R0y4ru6qMfsqs is also corrected: the shared-secret source mutation is evaluated before assertRaises, leaving only the intended scan inside its body. No rule/profile suppression.

Validation: 146 focused tests pass across policy, Composer and all three protected-policy modules. Three selected shell controls fail against the reviewed predecessor with six failure entries; all pass after correction. Local CPython 3.12.3 / hash-verified PyYAML 6.0.2 Python modules, not native ABI qualification. git diff --check passes. The same nine protected/config files remain byte-identical, including workflow SHA-256 642d204ea939ca5eba7ef51be9e4ae56a35177413caabe849315086d3d9d2d31.

Independent log readback of 7a5 run 39945932877 confirms 216 retained tests passed, then the read-only client refused because protected main moved. That is not a current-source qualification pass. This follow-up needs independent integration review and fresh checks together with current main. No live App/Environment/issuer result or activation is claimed.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Focused result-gate refusal component: 9f69f23951f75f98b410d04d21ec8a43ca61af71, directly after shell follow-up 1762712.

The reviewed result-test step must preserve failure. The guard uses the existing _step_invokes default/false semantics: absent or explicit false is accepted, while true and dynamic values are refused. The regression covers both ordinary false forms and true/dynamic forms, including a dynamically expressed false value that cannot be accepted by source identity alone.

One focused control produces four failure entries against 1762712 before this guard. All 147 focused controls pass afterward across policy, Composer and the three protected-policy modules. git diff --check passes. Local CPython 3.12.3 / hash-verified PyYAML 6.0.2 Python modules; final native/whole-source qualification remains with integration.

This adds one guard and one regression in two files. Protected workflow, reader/check/issuer, policy thresholds and exception scope remain untouched. Integrate the shell and this result-gate component together with qualified main, then freshly qualify that exact combined source. No live bypass, App readiness or activation is claimed.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Closed execution-context component: 60cdf46deb41ca78586486f7c3dab568758ead6a, after the reviewed cwd/graph/shell/result-gate components.

Bounded AST-only controls confirmed that exact f82554fb accepted unreviewed job container/services/strategy, inherited startup bindings, and conditional suppression on the first three action steps. Three control methods produce 31 failure entries before and pass afterward. No candidate startup code/commands were executed and no candidate environment was applied to the host.

The component closes the dedicated preflight job/default/action-step shapes. Its caller-supplied environment is a closed reviewed set, reusing the existing allowed-environment check. Ordinary CI, Cargo/Rust, Python buffering/UTF-8 and color diagnostics remain explicit. Only literal empty BASH_ENV, ENV, LD_PRELOAD and NODE_OPTIONS clears are accepted as startup controls. Unknown declarations, including GIT_CONFIG_* overrides, refuse by membership in this shape rather than by a blacklist. Unsafe declarations still refuse when shadowed by another scope. Unrelated test jobs keep their existing behavior.

Action metadata, the known GitHub-host condition, explicit false/default failure handling, locked bootstrap and command paths remain supported. 155 focused controls pass across policy, Composer and the three protected-policy modules; git diff --check passes. Local CPython 3.12.3 / hash-verified PyYAML Python modules, with no native ABI claim. Temporary qualification dependencies/raw are retired.

Only the policy implementation and its focused test module changed. The same nine protected/config files are byte-identical, including protected workflow SHA-256 642d204ea939ca5eba7ef51be9e4ae56a35177413caabe849315086d3d9d2d31.

The earlier f825 qualification remains real for that snapshot: all nine checks, 224 retained controls and two wheel tests on each native runner, with read-only base 987209e. It did not check these new contexts. This component needs independent review and fresh whole-source qualification with current qualified main. App/environment/issuer live enforcement and activation remain outside this component.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Public caller-context inventory for the 60c follow-up, read at 12:46–12:47 UTC on 2026-10-23. Each main ref was resolved first, then the selected workflow was fetched by immutable commit and parsed with PyYAML BaseLoader as data. These are source observations, not new runtime or enforcement results. The policy target selection is the exact f825 snapshot; comparison is f825 versus component 60cdf46deb41ca78586486f7c3dab568758ead6a.

Public target Pinned main / selected workflow Preflight comparison
ai fbdc2c98b7b7d3cb1f06b6831fd10278b9b4e380 / .github/workflows/ci.yml Pass in both
cli 535cb6e9b949aadf53c8b0313963577cccfec43c / .github/workflows/build.yml Pass in both
documentation 6929717489e4cc1bc411a46634b4d42d9ae184cc / .github/workflows/qualification.yml Pass in both
github-control-plane ce4a7ca220e2118cbfd051b25f7dd092074a7235 / .github/workflows/ci.yml N/A: this target has no caller-preflight requirement
sample-app 40be451c020c6a0e8c9561585bcd96d38a352a75 / .github/workflows/ci.yml Pass in both
sdk-php cb1d11e57767e20e1fdb8081024f67057ff66714 / .github/workflows/ci.yml Pass in both
sdk-python 38b5a639262bebcf3fd0e67c6ec42ab16bc3a966 / .github/workflows/ci.yml Pass in both
sdk-rust d19b7bdc4956001ecd8db1cb3f6267224af02a70 / .github/workflows/ci.yml Pass in both
server b57e3c97b594755f2728701b760480d0087c2f55 / .github/workflows/phpunit-feature.yml Same refusal in both: missing locked bootstrap
waterline 0147ca40ad0e93ea57eb5675171d83c92efd1cff / .github/workflows/php.yml Pass in both
workflow ae6bf6c8a7c4b9597ee4eecb15ecd786e78b0430 / .github/workflows/php.yml Pass in both

Across all ten public caller preflights above, workflow/job env and defaults are absent, and every preflight step has no declared env, if, shell or working-directory. Their job keys are name, runs-on, steps, timeout-minutes, with the already accepted optional GitHub-host condition. There is no inherited CARGO_INCREMENTAL or nonempty NODE_OPTIONS memory flag in this selected caller set, so this inventory finds no new allowlist incompatibility. This does not inventory every unrelated workflow or attest the runner's ambient environment. Future legitimate diagnostic additions still require an explicit bounded contract decision.

The Server caller is an existing bootstrap compatibility gap, not a 60c regression. Its five-step preflight still runs python -m pip install PyYAML==6.0.2 at line 45 without the required locked-policy bootstrap. Both exact verifiers refuse it. No Server workflow was edited or candidate command executed.

The ordinary result gate remains a narrower structural assertion. In separate bounded AST-only fixtures, adding gate-local env: {BASH_ENV: candidate-startup.sh}, a gate container, or job-level continue-on-error: true is accepted by 60c. No startup file, shell, image or caller command was executed. Gate-local execution context can affect its ordinary CI result; these checks do not prove arbitrary caller job execution. The pinned public callers currently declare no gate-level environment or container. The independently issued App check remains the intended authority boundary, with App setup, Environment denial, issuer enforcement and live qualification still pending. This inventory grants no activation and makes no App-bypass claim.

No additional implementation was made for this inventory. The 155 focused controls and f825's earlier passing gates retain their exact snapshot scope.

@sonarqubecloud

Copy link
Copy Markdown

@rmcdaniel

Copy link
Copy Markdown
Member Author

Independent combined-source qualification is complete for d4fe8e08d9e7dfdd2c1b24e477e59573385c30f0 / tree 0398da440f63bf176211ae3766b9d6ae49fd2ac6, incorporating 60cdf46d and qualified main. All nine GitHub checks pass.

A fresh disposable CPython 3.13.16 / pip 26.2.2 environment with the hash-locked native PyYAML 6.0.2 wheel passes 155 focused controls plus both actual dependency-installer cases (157 total). The protected workflow remains SHA-256 642d204ea939ca5eba7ef51be9e4ae56a35177413caabe849315086d3d9d2d31. Earlier local non-executable-mount / missing-venv setup failures remain failed setup, not source qualification. Every task container and memory-only dependency environment is removed.

The pinned caller inventory confirms no new environment/default incompatibility in this component. Server's existing unlocked five-step bootstrap fails both versions and still needs its own adoption migration. Ordinary caller gate execution is outside the structural preflight proof.

This remains a draft, unactivated source result. Independent issuer App/environment and actual live allow/deny, wrong-issuer, fork, stale/malformed, ambiguous-write and required-rule qualification remain the activation criteria. No settings or repository enforcement are changed, and there is no substitute based on a spoofable caller check context.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Reader/issuer/bootstrap follow-up review of exact d4fe8e08d9e7dfdd2c1b24e477e59573385c30f0, tree 0398da440f63bf176211ae3766b9d6ae49fd2ac6.

Scope is the central input reader, read client, issuer and prepared workflow. I authored the separate policy component 60cdf46d, so this is a non-author review of these components rather than an independent whole-PR review of my own policy changes. It does not represent the Security owner's acknowledgment.

  • The input reader binds open/public PR and repository IDs, exact base/head/tree/blob metadata, regular-file modes, canonical paths and bounded UTF-8 bytes. It writes candidate inputs as mode-0600 data outside the import path. The reviewed checker imports its policy and validator from the protected checkout; candidate helpers are inspected as data.
  • The reader and App issuer use separate credentials. The issuer runs validation itself, checks the returned App/name/head/check identity, and rechecks protected main and the original PR immediately before completing that check. The candidate cannot supply a ready-made passing report. The reviewed endpoints are fixed to the public pilot for writes, with bounded transport, response parsing and shared API deadline.
  • Bootstrap order is consistent with that separation: immutable protected-base checkout without persisted credentials, isolated interpreter, protected trust bootstrap and hash-locked parser, workflow/checkout SHA checks, then the pinned App-token action with pilot-only Checks write. The pinned official action exposes the requested permission input and runs on Node 24. The signing key belongs to that action's protected Environment; it is absent from reader/issuer program inputs.
  • The only pull_request_target exception is target github-control-plane, path .github/workflows/protected-policy.yml, SHA-256 642d204ea939ca5eba7ef51be9e4ae56a35177413caabe849315086d3d9d2d31. I recomputed that exact raw-byte hash. This exception does not review arbitrary future changes to central policy, reader, issuer or local bootstrap code; those remain root-of-trust review work.

Fresh independent verification on CPython 3.12.3: all 27 input-reader controls pass, zero skips. Exact reader blob 00dddf208e2b586ff8dedd001faa920e783786a9, test blob 5a9966face8e966a4507f77a1107010e89943155. I loaded those committed modules in memory with the ordinary unittest loader, prohibiting network and subprocess entry points during the controls. Temporary materialization controls cleaned up their files. I also read the check/issuer controls and verified all nine hosted checks for this exact head succeeded; the reported 157-control native qualification remains its owner's evidence rather than a new local rerun.

Required live/integration controls remain:

  1. Establish and verify the App installation, least-privilege token and Environment access denial for candidate/fork/unapproved execution. Environment marker strings in the issuer are defense in depth; source tests cannot prove secret denial or installation scope.
  2. Bind the required context to the actual independent App and retain an up-to-date-branch requirement. The Checks API binds a result to a commit, while strict required checks require an up-to-date branch. The issuer's pre-write base recheck does not invalidate an already published check when the base later advances. Live acceptance should cover base advancement after a completed result, as well as stale heads and wrong issuers. This is an integration requirement, not a demonstrated live bypass.
  3. Exercise fork behavior, malformed/oversized inputs, candidate nonexecution, a safe success, and ambiguous writes. A local timeout/refusal after a PATCH does not by itself establish the remote Check Run's state; inspect the actual remote outcome before claiming that control passed. Define the intended maintainer/admin bypass and root-change review scope explicitly.

No additional concrete source defect found in these reviewed components. The App/account setup and live acceptance remain pending. No protected file, setting, credential or enforcement state changed during this review.

This branch has not been deployed

No deployments
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.

1 participant