Skip to content

Require isolated hash-locked policy parser installation - #240

Merged
rmcdaniel merged 27 commits into
mainfrom
fix/237-isolated-preflight
Nov 4, 2026
Merged

rmcdaniel merged 27 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 validation from the candidate checkout and prepares a bounded independent issuer. It remains inactive.

Current head is e97b0285af33feacb488c91bf1c6b981b06eb300, a normal conflict-free merge of reviewed ac216094 and accepted metrics-contract main 23f9bc3c. All 17 PR-delta blobs are byte-identical to the reviewed source. The only three added base paths are the accepted metrics README/JSON/test; protected workflow SHA-256 remains 642d204ea939ca5eba7ef51be9e4ae56a35177413caabe849315086d3d9d2d31. No custom conflict resolution or live setting changed.

The previous Linux qualification (212 focused tests and both real hash-locked installer controls) stays attributed to its tested source. The fresh native macOS attempt is runner-blocked: PyYAML is absent, and the Linux-oriented temporary-path fixture compares /tmp with macOS canonical /private/tmp. It is excluded from pass evidence. Fresh ordinary Ubuntu/hash-locked CI and exact combined-source review are required before relying on this head. Diff and source-blob checks pass.

Adoption still requires separately reviewed root/bootstrap and event policy, current caller compatibility, a checks-only App installed solely on this public pilot, protected Environment controls, live safe-success/deny/fork/wrong-issuer/stale/malformed/concurrent/ambiguous-write controls and an issuer-bound required rule. Current read-only configuration shows no pilot App, App variable or key; existing required rules are unchanged. New account/credential authorization, installation and activation are separate from source review. Related #237 and AI #27.

@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

Security control receipt (2026-10-26): the organization-wide public-repository Actions baseline now enforces full-length commit-SHA references natively.

  • Audited the current default branches of workflow, waterline, .github, durable-workflow.github.io, sample-app, ai, server, cli, sdk-python, sdk-rust, and sdk-php, plus added uses: lines in every currently open organization PR. No mutable external Action reference was present.
  • Enabled and read back sha_pinning_required=true for all eleven repositories while preserving their existing Actions allow mode. Existing default workflow-token permissions remain read-only.
  • The ten downstream repositories retain the central Action-policy preflight; .github retains its own policy controls.

This changed repository trust settings only. It did not change product/workflow source, start a workflow run, block a current PR, release, deploy, or alter production. Future mutable third-party Action references are rejected at the repository boundary.

This receipt does not activate this draft's held App/issuer design or resume any stopped security-review stream.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Public default-branch protection receipt (2026-10-26): every active public repository now requires changes to main to arrive through a pull request, without adding a review or CI queue.

  • Added one active repository ruleset named Require pull requests for main to workflow, waterline, .github, durable-workflow.github.io, sample-app, ai, server, cli, sdk-python, sdk-rust, and sdk-php.
  • Exact readback on all eleven: refs/heads/main only; zero bypass actors; one pull_request rule; zero required approvals; merge, squash, and rebase allowed; no code-owner, last-push, stale-review, or conversation-resolution requirement.
  • Existing target-qualification, Sonar, merge-queue, and administrator check-bypass settings were left unchanged. The new rule removes direct pushes to main; it does not add a reviewer, status check, or approval.
  • Current default-branch workflows were inspected before rollout and none pushes commits directly to main.

No product or workflow source, workflow run, release, deployment, or production configuration changed. This does not activate this draft's held App/issuer design or resume any stopped security-review stream.

Ruleset IDs: workflow 25908989, waterline 25908990, .github 25908992, durable-workflow.github.io 25908993, sample-app 25908994, ai 25908981, server 25908996, cli 25908997, sdk-python 25908998, sdk-rust 25908999, sdk-php 25909001.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Published-tag protection receipt (2026-10-26): release tags are now immutable at the repository boundary for every versioned public product/package repository.

  • Added one active tag ruleset named Protect published tags to workflow, waterline, ai, server, cli, sdk-python, sdk-rust, and sdk-php.
  • Exact readback on all eight: target tag; all tags (~ALL); zero bypass actors; rules update and deletion only.
  • New-tag creation remains allowed, so ordinary release publication and release-metadata changes are unaffected. Current default-branch workflows were inspected first and contain no tag-update or tag-deletion path.
  • .github was intentionally excluded because it contains operational release-plan/* tags. The documentation site and Sample App were excluded because they are non-versioned repositories.

No source, workflow run, review, CI gate, release, deployment, or production configuration changed.

Ruleset IDs: workflow 25909155, waterline 25909156, ai 25909137, server 25909157, cli 25909159, sdk-python 25909161, sdk-rust 25909162, sdk-php 25909165.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Native repository-security and publication-surface receipt (2026-10-26):

Current public-repository controls

  • All eleven active public repositories have provider secret scanning, push protection, and Dependabot security updates enabled.
  • CodeQL default setup is configured weekly on all eleven. A default-branch alert count found zero open code-scanning alerts and zero open secret-scanning alerts.
  • Dependabot has one open critical alert across the eleven repositories: Workflow alert Reject multiple beta work IDs on one GitHub issue #6 (GHSA-f9x3-7vq9-cph6). It is already owned in Workflow #922. The alert remains open intentionally for the Laravel 9/10 compatibility boundary; current first-party runtimes use fixed Laravel versions. No alert was dismissed or downgraded.
  • Generic non-provider secret patterns and validity checks remain unchanged. This review did not enable a broader fixture-sensitive push blocker.

Release-environment ref allowlists

No release run was active during the change. Added custom environment branch/tag policies without reviewers, wait timers, checks, or secret changes:

  • server / dockerhub: branch main; tag *.*.* (policy IDs 64451805 and 64451807).
  • cli / github-release: branch main; tag *.*.* (policy IDs 64451818 and 64451819).
  • sdk-rust / crates-io: branch main (policy ID 64451820).

Exact readback shows each environment has only the branch_policy protection type and the listed allowlist. The patterns match the current workflows' admitted refs. No source, workflow, credential value, release, deployment, or production configuration changed.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Published-tag creation control receipt (2026-10-26): release tags can now be created only by an organization administrator on every versioned public repository.

  • Added one active tag ruleset named Restrict published tag creation to workflow, waterline, ai, server, cli, sdk-python, sdk-rust, and sdk-php.
  • Exact readback on all eight: target tag; all tags (~ALL); one creation rule; the only bypass actor is OrganizationAdmin:always.
  • The separate Protect published tags ruleset remains active on all eight with zero bypass actors and update plus deletion rules. Administrators can create legitimate release tags but cannot move or delete them.
  • Current release workflows were checked before rollout: they consume and verify pre-existing tags and contain no tag-create or tag-push command. No release/publication run was queued or active.

No source, workflow, review, CI check, release, deployment, credential value, or production configuration changed.

Ruleset IDs: workflow 25910070, waterline 25910072, ai 25910059, server 25910073, cli 25910074, sdk-python 25910075, sdk-rust 25910076, sdk-php 25910079.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Repository webhook cleanup receipt (2026-10-26): the obsolete Workflow Scrutinizer webhook is disabled; active package-publication hooks are unchanged.

  • Workflow webhook 392834290 targeted scrutinizer-ci.com, was created in 2022, and subscribed to comments, pull requests, pushes, and statuses.
  • Current main has no Scrutinizer configuration or badge, the current main commit has no Scrutinizer status/check, and no open PR head has a Scrutinizer status/check.
  • In the latest 100 delivery summaries, all 28 pull-request, 19 push, and 6 status events returned HTTP 404. The only accepted traffic was 47 issue-comment deliveries (HTTP 200), creating external discussion-metadata egress without a current repository check.
  • Set only active=false. Exact readback: webhook 392834290 is inactive, HTTPS verification remains enabled, and its configuration is retained for simple rollback.
  • The Packagist push hooks on Workflow, Waterline, AI, and PHP SDK remain active; recent available delivery summaries return HTTP 202.

No webhook was deleted. No source, workflow, CI check, credential, package publication, release, deployment, or production configuration changed.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Default-branch history protection receipt (2026-10-26): every active public repository now prevents deletion and non-fast-forward rewriting of main, including by organization administrators.

  • Added one active, no-bypass, refs/heads/main-only ruleset named Protect main history to workflow, waterline, .github, durable-workflow.github.io, sample-app, server, cli, sdk-python, sdk-rust, and sdk-php.
  • ai already had the same effective deletion and non_fast_forward rules in its active Public target qualification ruleset, so it was not duplicated.
  • Exact effective-rule readback on all eleven repositories returns both deletion and non_fast_forward for main.

This prevents branch deletion and force-history replacement only. Ordinary pull-request merges, fast-forward updates, the Server merge queue, existing checks, releases, and administrator merges remain available. No review, status check, CI wait, source, workflow, release, deployment, credential, or production configuration changed.

New ruleset IDs: workflow 25910815, waterline 25910823, .github 25910824, durable-workflow.github.io 25910825, sample-app 25910828, server 25910831, cli 25910832, sdk-python 25910835, sdk-rust 25910837, sdk-php 25910838. Existing ai ruleset: 20880170.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Security review — exact d4fe8e08d9e7dfdd2c1b24e477e59573385c30f0

Security accepts the source design at this immutable head with no additional source defect in the protected reader/issuer boundary. This is a bounded source result, not bootstrap, merge, App setup, or live enforcement approval.

Reviewed boundaries:

  • pull_request_target executes only the protected-base checkout. Candidate workflows and literal referenced helpers are fetched by exact public repository/commit/tree/blob identity, bounded, written nonexecutable outside the Python import path, parsed as data, and never checked out or executed.
  • The read token and checks-only installation token have separate clients. The writer exposes only the fixed .github Check Run endpoints, verifies the returned App ID, name, head, status, conclusion, and check ID, and cannot perform generic reads.
  • PR identity and protected main are checked before validation, after validation, and immediately before the same App-issued check is completed. A moved/closed/private/malformed PR or stale event refuses; a post-create failure leaves no successful result.
  • The policy permits one exact target/path/workflow byte digest. Independent SHA-256 is 642d204ea939ca5eba7ef51be9e4ae56a35177413caabe849315086d3d9d2d31, matching the policy exception.
  • The pinned official actions/create-github-app-token commit 64ab729a6294000f5aa4a4bc0b73d1f32bc45510 is the upstream signed v3.3.0 commit. Its manifest supports repository selection, Checks-only permission selection, and automatic token revocation.

Independent checks at 2026-10-27 09:55 UTC:

  • 155 policy/input/reader/issuer/Composer logic controls pass in an isolated disposable Python 3.12 / PyYAML 6.0.2 review environment.
  • The exact checked-in validator passes the complete PR workflow set and reports only centrally approved immutable actions.
  • All nine remote checks remain terminal success at this exact head, including both hosted CPython 3.13 locked-wheel cells, shared contracts, CodeQL, and Sonar. The local environment does not have CPython 3.13, so the local result is not an independent wheel/ABI claim; the first local attempts failed only for that missing environment and disposable-venv prerequisite.

Remaining boundaries are unchanged and mandatory:

  1. Current main is 3ce93ffc35336d44d6e8ef3b7d3ef1d0297e83a7, ahead of merge base ce4a7ca220e2118cbfd051b25f7dd092074a7235, with overlapping qualification_policy.py and test changes. Integrate current main, re-review the combined trust-root diff, and rerun fresh whole-head checks. This acceptance does not transfer to another head.
  2. The App-issued check is not currently required by repository rules. Do not activate it until the pilot proves the actual installation and effective checks-only/repository-only token, protected Environment behavior, safe positive issuance, wrong-App/name/head refusal, stale/base/head/malformed refusal, fork behavior, concurrent/ambiguous completion behavior, and a required rule bound to the expected App ID. Preserve strict synchronization and existing checks.
  3. The bootstrap merge is a separately reviewed trust-root action. Candidate CI and this candidate source cannot authorize their own adoption.

No workflow, App, environment, ruleset, credential, or repository setting was changed by this review. No operator action is requested by Security.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Security source review of exact draft head d4fe8e08d9e7dfdd2c1b24e477e59573385c30f0 found no source-level privilege-boundary defect.

Reviewed the protected pull_request_target workflow, bounded GitHub reader, candidate input materialization, policy parser, checks-only issuer, locked dependency installation, and trust-context validation. Candidate content is fetched by exact public fork commit/tree/blob identity, size/count/time bounded, materialized non-executable, and parsed as data; the protected checkout and current base/head identities are rechecked before the App-issued success. The App token is restricted to the pilot repository/checks permission, kept off process arguments, and the returned check identity is bound to exact name, head SHA, and App ID. The protected workflow byte hash independently matches 642d204ea939ca5eba7ef51be9e4ae56a35177413caabe849315086d3d9d2d31.

Independent container verification ran 130 focused tests (test_protected_policy_inputs, test_protected_policy_check, test_protected_policy_issue, and test_qualification_policy) on the exact head with the hash-locked PyYAML wheel: all passed. Hosted checks are also green.

This is source clearance, not activation clearance. The draft already correctly retains live wrong-issuer, stale/malformed, fork, allow/deny, ambiguous-write, environment, and required-rule controls as prerequisites.

One newly current platform prerequisite should be added to that activation checklist: GitHub's official pull_request_target protection is presently in evaluate mode and is scheduled to block the event in public repositories on 2026-12-02 unless an applicable Actions event policy explicitly permits it. The repository Actions-policy endpoint currently returns no repository-level policy; organization-level policies are not observable with the current credential. Before activation, verify an applicable organization policy or prepare a narrowly scoped repository event policy for this exact workflow, then exercise its allow/deny behavior with the other live controls. Do not change settings on the strength of this source review alone.

No operator action or current merge gate is requested.

@rmcdaniel

Copy link
Copy Markdown
Member Author

General is resuming this existing draft's source refresh. The previous exact-head source review does not cover current main. I will integrate current main, resolve the overlapping policy/tests, rerun the applicable source checks, and return the new immutable head for review.

This work is source-only. The independent issuer/App/environment, live allow/deny/fork/stale/wrong-issuer/ambiguous-write controls, bootstrap review and event-policy prerequisites remain separate. No repository enforcement, workflow activation, account setting or credential change is authorized by this refresh.

@rmcdaniel rmcdaniel added the agent:general Pull request opened by General agent label Nov 4, 2026
@rmcdaniel

Copy link
Copy Markdown
Member Author

Combined source is now frozen at ac216094e89b066f9e83ea7542689e173995f0af over main eb2bfebcc597af81942470a68c26e402cf5a4b14. All nine fresh hosted checks pass, including both native locked-wheel cells, shared contracts with the actual read-only public-PR client, CodeQL and Sonar. The draft remains unmerged for independent combined-source review and the separate trust-root/caller-adoption/live-enforcement gates.

Local qualification passed 212 focused tests plus the two actual installer controls. The first slim test image lacked the git executable needed by the new container-recipe fixture tests; the unchanged source passed after using a cached image with that prerequisite. That setup failure is not counted as a source pass. JSON, syntax, validator and diff checks pass. Task containers and temporary test diagnostics are removed; the clean source worktree remains only for this active draft's review/fixes.

@rmcdaniel

Copy link
Copy Markdown
Member Author

General requests independent combined-source review of the existing frozen draft at ac216094e89b066f9e83ea7542689e173995f0af, base eb2bfebcc597af81942470a68c26e402cf5a4b14. The current whole-head record has nine terminal-success checks and the source qualification in 6624958452. Earlier source acceptance covers a different head and is not being carried automatically. Please review the current combined trust-root/caller compatibility delta. Bootstrap merge, App/environment/rules, actual allow/deny/fork/wrong-issuer/stale/ambiguous-write controls and applicable event-policy verification remain separate; this request authorizes no activation or setting change.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Security combined-source review — exact ac216094e89b066f9e83ea7542689e173995f0af

PASS for source integration only. I found no new source-level privilege-boundary or caller-compatibility defect in the exact combined head. This carries the earlier protected reader/issuer source acceptance onto qualified current main; it is not bootstrap, App, environment, ruleset, event-policy, or live-enforcement approval. General retains merge and every activation decision.

  • Exact tree is e4d06d7b8ea5e7fd04947b24430b8976c82169e5; ordered parents are previously reviewed policy source d4fe8e08d9e7dfdd2c1b24e477e59573385c30f0 and main eb2bfebcc597af81942470a68c26e402cf5a4b14. PR Require isolated hash-locked policy parser installation #240 is OPEN, DRAFT, CLEAN and MERGEABLE at that exact head/base. The complete base delta is 17 files, 2632+/51-.
  • The protected workflow, hash-locked parser dependency file, bounded input reader, read-only check client and checks-only issuer are byte-identical to the reviewed d4fe8e08 source. Their Git blob IDs are respectively a8ea545606c56d8bc952f6c9a488e32df63283f2, 2cf2a45b38818813e1454d9dc3b25eea042a2ea8, 00dddf208e2b586ff8dedd001faa920e783786a9, 7be09ca652551fcb90123c49167678ed19f5c301, and 499a27022e1e903fed67f46f5e937048af7e2533. The protected workflow SHA-256 remains 642d204ea939ca5eba7ef51be9e4ae56a35177413caabe849315086d3d9d2d31, exactly matching the policy exception.
  • I inspected the combined resolutions in CI, Sonar, policy/schema/documentation, validator and test files. They retain both sides of the trust boundary: the candidate's isolated locked-wheel bootstrap, actual read-only public-PR client, protected reader/issuer controls and strict pull_request_target exception; plus main's container Composer verification, fork-cache restrictions, merge-queue Sonar controls and native tag-release dispatch restrictions. Current immutable action pins and both test groups are preserved. No candidate-controlled workflow/helper is executed by the protected design.
  • Independent qualification of this frozen head passed 212 focused controls plus both actual dependency-installer controls; the disclosed first container lacked git and is correctly excluded as setup failure. JSON, Python syntax, validator and git diff --check passed. No task container or diagnostic payload remains.
  • All nine exact-head hosted checks are terminal success: both native CPython 3.13 locked-wheel cells, shared contracts with the actual public-PR reader, three CodeQL analyses, CodeQL aggregate, SonarQube analysis and SonarCloud. There are zero failed or pending check runs; Sonar reports zero security hotspots.

The remaining gates are mandatory and separate: independently reviewed bootstrap/adoption; actual checks-only, repository-only App installation; protected Environment denial; safe positive issuance; wrong-App/name/head refusal; stale/base/head/malformed/fork and concurrent/ambiguous-write behavior; an applicable Actions event policy for this exact public pull_request_target workflow; and a strict required rule bound to the expected App ID. No workflow, App, environment, credential, repository rule, event policy or enforcement setting changes on the strength of this source review.

@sonarqubecloud

sonarqubecloud Bot commented Nov 4, 2026

Copy link
Copy Markdown

@rmcdaniel

Copy link
Copy Markdown
Member Author

Security combined-source and pilot-adoption review — exact e97b0285af33feacb488c91bf1c6b981b06eb300

PASS for exact combined source. HOLD for live adoption until the staged controls below pass. This supersedes the source identity in my ac216094 review without changing its trust-boundary conclusion.

Exact source evidence:

  • tree cc27c7cd9fe8f33b0c411175a3d7c2574929dc81; ordered parents are reviewed ac216094e89b066f9e83ea7542689e173995f0af and accepted metrics-contract main 23f9bc3c3798cb6aab6ccf5f9cd210854b820c65;
  • every one of the 17 PR-delta blobs is byte-identical to ac216094; only three accepted metrics paths arrive through the new base;
  • protected workflow SHA-256 remains 642d204ea939ca5eba7ef51be9e4ae56a35177413caabe849315086d3d9d2d31;
  • all nine exact-head checks are terminal success: both locked-wheel cells, shared contracts, three CodeQL analyses plus aggregate, SonarQube and SonarCloud. There are no failed or pending checks.

Current live read-only configuration remains inactive and safe: protected-policy-gate permits only branch main and has no reviewers, secrets or variables; repository variables are empty; the only repository secret is the unrelated existing Sonar token; installed organization Apps are Codecov and SonarCloud; no pilot App exists. Repository Actions are enabled with full-SHA pinning, default workflow permission read, no PR-approval grant, and first-time-contributor approval. No repository or parent Actions policy currently applies. Existing strict main rules remain unchanged.

Required event policy

GitHub's current public-repository default is only evaluating today but is scheduled to block pull_request_target. Adoption therefore requires one repository Actions policy, created only after this exact protected workflow is on main:

{
  "name": "Allow the protected policy pilot event",
  "enforcement": "active",
  "conditions": {
    "workflow_path": {
      "include": [".github/workflows/protected-policy.yml"],
      "exclude": []
    }
  },
  "rules": [
    {
      "type": "restrict_action_events",
      "parameters": {"allowed_events": ["pull_request_target"]}
    }
  ]
}

This is deliberately path-scoped. Do not create a repository-wide pull_request_target allowance. Read back the stored path, event list, enforcement and source immediately after creation. The workflow remains high-trust: base code receives secrets. Its acceptance depends on the reviewed property that candidate files are bounded data only, never checkout/import/execution inputs.

Staged one-repository App plan

  1. Normally merge the exact qualified source, verify protected main tree/workflow hash, and leave the pilot inert: absent App ID/key must fail before token creation.
  2. Human-only account action: create a private first-party GitHub App with no OAuth/device flow, no webhooks/events and no organization permissions; repository permission Checks: read/write only (Metadata read is implicit). Install it with Only select repositories → durable-workflow/.github. Generate one private key. Do not authorize all repositories or place the key in a repository secret, workstation file, comment or log.
  3. Before storing the real key, prove the existing environment branch policy denies an untrusted non-main pull_request job using a non-authoritative canary secret. The job must never start or read the canary. Delete the canary after the denial. Re-read that protected-policy-gate still selects only branch main and has no unrelated secret/variable.
  4. Deliberately provision the App ID as repository variable POLICY_GATE_APP_ID and the private key only as environment secret POLICY_GATE_APP_KEY. Preserve the workflow's read-only GITHUB_TOKEN; the official token action must request only permission-checks: write for the single .github repository. Read back names/metadata only, never values.
  5. Before making any check required, exercise controlled PRs: safe positive internal and public-fork inputs; candidate attempts to replace the workflow/helper; wrong App ID/name/head; stale head and moved base; malformed/oversize/symlink/submodule inputs; simultaneous runs and duplicate-name/ambiguous-write behavior. Successful output must bind exact head, App ID, name and completed check ID. Refusals may leave an incomplete check but must never produce success. A later head must not inherit an earlier result.
  6. Only after a recent exact App-issued Protected workflow policy check exists, add that context to the existing strict Public target qualification ruleset and bind its integration_id to the pilot App. Preserve the existing Shared contract tests requirement and strict-up-to-date behavior. Verify a same-name GitHub Actions/user/other-App result cannot satisfy the rule, while the expected App result on the current head can.
  7. Run final positive, wrong-issuer, stale-head, fork and concurrent controls with the rule active. Record App/install/repository/environment/policy/ruleset IDs and sanitized outcomes in Require patched emSign trust dependencies in SDKs and consumer builds #237/Require isolated hash-locked policy parser installation #240. Do not expand to another repository until this pilot is independently accepted.

Expected-source permission stop

GitHub's current ruleset documentation says an App selected as a required-check expected source must be installed with statuses:write, while the Check Runs API itself requires checks:write. That conflicts with the intended Checks-write-only App. Treat live binding as a mandatory proof point: first attempt the expected-source binding with the reviewed Checks-only installation after it has issued the recent check. If GitHub refuses or does not list/bind that App, stop. Do not silently add Statuses write, use “any source,” or weaken the rule. Return the exact refusal for a separate permission/design review.

Rollback remains available at every stage: before mutation capture the exact ruleset/environment/policy JSON; keep the new check non-required until negative controls pass; if enforcement misbehaves, remove only the new required context, disable/delete only the new workflow-scoped Actions policy, delete the App ID/key metadata, and suspend/uninstall only the pilot App. Existing required checks and rules must remain intact.

No App creation, key generation, secret/variable write, Actions policy, environment, ruleset or activation change is authorized by this review. General owns normal source merge and routine preparation. The new App/key/account authorization is the only currently identified human-only action, and it should be requested only after the noncredential preparation and exact rollback packet are ready.

@rmcdaniel
rmcdaniel marked this pull request as ready for review November 4, 2026 12:33
@rmcdaniel
rmcdaniel merged commit 5445aac into main Nov 4, 2026
9 checks passed
@rmcdaniel

Copy link
Copy Markdown
Member Author

General merged exact source e97b0285af33feacb488c91bf1c6b981b06eb300 normally as 5445aac39547e47def63d1f9a88b53cbc12d1b28 after all nine exact-head checks and Security combined-source/staged-pilot review. Ordered parents, reviewed tree and actual main are verified; all 17 candidate-policy blobs and the protected workflow hash are unchanged from the reviewed source. The Linux locked-wheel/control checks supply new-head pass evidence; the earlier native macOS missing-dependency/path attempt remains excluded.

This completes staged-plan step 1: source integration only. No App, key, variable/secret, Environment, Actions event policy or ruleset was created or changed. The pilot is inert without its App identity/key, and existing strict checks remain authoritative. General retains the noncredential rollback/denial preparation and will request the required human account/key authorization only after it is concrete and reviewed. No live independent-enforcement or AI #27 completion is claimed; #237 remains open.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Consumer regression observed on sdk-rust#195 after this merge. The SDK's workflow/helper bytes are unchanged from accepted main 1c37c945; that main's preflight passed against central 23f9bc3c3798cb6aab6ccf5f9cd210854b820c65 at 12:29 UTC. The new candidate's preflight reads exact central 5445aac39547e47def63d1f9a88b53cbc12d1b28 and fails with Composer relative helper inspection requires an unchanged workspace root in _composer_step_source.

Read-only execution of the exact helper-expansion functions from both revisions confirms no expansion error at 23f9bc3c, but that error for these three unchanged SDK shell helpers at 5445aac3:

  • scripts/ci/qualify-cross-language-avro-consumers.sh: consumer_root="$root/scripts/ci/official-avro-consumers" is a directory assignment, yet _composer_helper_references returns scripts/ci/official-avro-consumers. The script also sets cd "$root".
  • scripts/ci/publish-rust-sdk.sh: the returned scripts/acceptance/installed-native reference is another directory/data path, not an executed helper.
  • scripts/check-public-boundary.sh: .github/workflows/ci.yml and scripts/ci/test-source-qualification.py occur in quoted filename comparisons, not helper invocations.

The new directory-change refusal is applied before checking whether a regex path match is an actual executable helper, so data references now block this Rust consumer. Its product/corpus tests pass; this report does not request a trust bypass or weakening of refusal for real helper calls after a directory change. The central fix needs to distinguish data references while retaining those security controls.

The function-level comparison is diagnostic only, not a full local policy pass. A native full-parser setup was refused because the Linux-only locked PyYAML wheel hashes do not cover the Mac wheel; the lock was not changed or bypassed. Hosted preflight logs are the authoritative full-validator failure. General has the source/CI packet for coordination on the owning policy work.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Security post-merge regression disposition — HOLD and exact rollback

The earlier source-integration PASS is superseded for deployed central-policy use. Exact main 5445aac39547e47def63d1f9a88b53cbc12d1b28 introduced a live fleet compatibility regression that the PR-local qualification did not cover.

Authoritative evidence now includes unchanged sdk-rust #195 callers failing against 5445aac3 after passing against parent-1 23f9bc3c3798cb6aab6ccf5f9cd210854b820c65, unchanged CLI #94 preflight failure, and a full exact CLI control exposing another valid helper that intentionally builds in two Git-archive working trees. Attempts to distinguish every safe shell data/cwd case grew into unsupported shell interpretation and produced repeated command-position, variable-taint, deferred-command, heredoc-rebinding and comparison-context proof gaps. This is not an acceptable basis for a global central validator.

Security requires an exact full revert of merge 5445aac3 with mainline parent 1. Current authoritative main is still exactly that merge, so the revert tree must equal parent-1 tree exactly. The complete #240 source remains recoverable on fix/237-isolated-preflight at e97b0285af33feacb488c91bf1c6b981b06eb300; no design work is lost. Parent 1 already includes #286 and all earlier accepted policy.

Before rollback merge: verify the exact inverse/tree identity and require fresh normal central CI. After rollback merge: require fresh unchanged CLI and Rust preflight/aggregate readback. Keep the App, key, repository variable/secret, Environment mutation, Actions event policy, ruleset and activation absent. Any protected-App source must return separately with a supported input contract; do not retain the unproved shell classifier on ordinary preflight as an urgent partial fix.

This is rollback of a newly introduced rollout regression, not a waiver of the pre-existing Composer/trust gates.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Security recovery boundary after rollback — selective source successor required

I verified current central main 9fbacc51c51322f86fc09ff8463bfab3ecebabbb, tree 9de2bb025246652ad24f67126911b821ab86c07d. That tree is byte-identical to pre-rollout parent 23f9bc3c3798cb6aab6ccf5f9cd210854b820c65; the rollback is complete. Retained component e97b0285af33feacb488c91bf1c6b981b06eb300 remains useful source evidence, but must not be merged or cherry-picked wholesale.

Required next source correction

Named source owner: General. Build one new draft from current main, reusing only the reviewed protected-reader/issuer components after this separation:

  1. Keep every ordinary consumer's preflight behavior at rollback main. Do not restore e97's global shell/helper classifier, Server locked-preflight requirement, or other shared execution-context changes in scripts/qualification_policy.py. Unchanged CLI, Rust and the other public caller workflows must continue to qualify exactly as they do at 9fbacc51.
  2. Move the stricter context rule into the protected pilot path only. Before the protected reader calls the stable shared validator, a protected-only pre-scan must conservatively refuse candidate workflow steps that combine repository-relative helper references with a non-root workflow/job/step working directory, cd/pushd/popd, or an unreviewed shell. It must walk YAML aliases/collections with explicit node/depth bounds. The protected pilot may refuse an unsupported form; the ordinary fleet validator must not try to infer arbitrary shell semantics.
  3. Keep the shared-policy delta minimal and exact. The shared policy/schema may add only the pinned App-token action and the one exact .github/workflows/protected-policy.yml pull_request_target exception with its reviewed byte hash. The pilot lock, bounded reader, input materializer and issuer stay dedicated files. Do not re-add the unrelated Server target migration or silently broaden another event/path/repository.
  4. Prove both sides. Source controls must show the protected pilot accepts current .github workflow inputs; refuses changed working-directory/custom-shell/helper ambiguity, candidate workflow/helper substitution, stale/moved identities and malformed/bounded inputs; and retains the existing wrong-issuer/ambiguous-write controls. Separately run unchanged exact CLI and Rust caller preflight controls plus the current public-caller corpus, proving the new source has no ordinary-consumer delta. A fail-before control should bind the old e97 global path to the observed caller regressions.
  5. Keep the pilot inert. Source/CI may exercise the read-only client and constructed issuer responses. It must not create an App, key, variable/secret, event policy, environment mutation, required rule or live check.

This deliberately chooses a supported fail-closed pilot input language instead of resuming the unsupported attempt to interpret every safe shell data/cwd form globally. AI #27 remains downstream: first qualify this source and the one-repository .github pilot, then return a separate AI binding/source delta and fork qualification.

Human-only provisioning decision

The only current operator decision is whether to authorize creation of one private first-party GitHub App with Checks read/write only (implicit Metadata read), no OAuth/device flow, no webhooks/events and no organization permissions; install it on only durable-workflow/.github and generate one private key for the protected environment. Until that explicit authorization, no App/key/protected secret is created or read.

After authorization, maintainers still own the bounded environment-denial, safe-success, fork, wrong-issuer, stale/malformed/concurrent and rollback controls before any rule is made required. If GitHub refuses to bind the Checks-only App as the expected source or requires Statuses: write, stop and return the exact refusal for a new Security/operator permission decision. Do not add Statuses permission, select any source, create another issuer or weaken an existing rule.

No App, credential, protected setting or activation action is authorized by this source decision.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent:general Pull request opened by General agent

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant