Skip to content

Adopt workflow execution protections across the portfolio #202

Open
11 of 12 issues completed
Open

Description

@devantler

🤖 Generated by the Agentic Engineer

Objective

Adopt GitHub Actions workflow execution protections across every active portfolio repository so only approved actors and events can start each workflow, with stricter policy for deployment and manual-entry workflows.

Context

GitHub made workflow execution protections generally available on 2026-09-17. Policies can restrict actors and events, target specific workflow paths, expose enforcement insights, and be managed through REST at repository, organization, or enterprise scope.

Current verified evidence:

  • The monorepo has no pull_request_target workflow.
  • Its three workflow_dispatch entry points are audit-docs.yaml, publish-pages.yaml, and todos.yaml.
  • The monorepo repository-level Actions policy inventory is empty.
  • Organization-level policy inventory is currently QUERY-UNKNOWN because the routine credential lacks admin:org; no absence is inferred.
  • GitHub will enforce its default public-repository pull_request_target restriction on 2026-11-02 for affected repositories.

References:

Desired state

Every workflow is covered by a deliberate layered policy. Broad CI remains usable by the contributors and automation that need it; deployment, publication, workflow_dispatch, and repository_dispatch workflows are limited to explicitly approved identities and events. pull_request_target is prohibited by default and permitted only for a reviewed workflow-path exception with a documented threat model.

Acceptance criteria

  • Inventory every active, non-archived portfolio repository, workflow path, trigger event, required caller, and current policy inherited from repository/organization/enterprise scope.
  • Resolve every required actor to its live GitHub actor type and ID, including trusted humans, repository roles, Dependabot, Renovate, suite-owned Apps/bots, and reusable-workflow callers.
  • Classify workflows by exposure: ordinary CI, required reusable workflow, scheduled maintenance, manual/repository dispatch, publication, deployment, and privileged administration.
  • Define layered actor/event policies rather than one account-wide allowlist.
  • Prohibit pull_request_target by default; document and independently review any workflow-path exception before enforcement.
  • Apply workflow-path targeting to deployment, publication, privileged administration, and manual-entry workflows.
  • Preserve required pull_request, push, merge_group, schedule, workflow_call, workflow_dispatch, and repository_dispatch events only where the inventory proves they are needed.
  • Use Evaluate mode and policy insights where the plan supports it; otherwise stage enforcement through a representative low-risk repository before portfolio rollout.
  • Exercise positive controls for every required actor/event pair and safe negative controls through policy insights or a dedicated test repository.
  • Verify no required checks, releases, deployments, dependency updates, or scheduled workflows are stranded.
  • Manage policies through the reviewed declarative path when supported, otherwise use the versioned REST API and record the remaining GitOps gap under Adopt supported GitHub rulesets and track remaining provider gaps #69.
  • Read back policy source, enforcement mode, target repositories, workflow paths, actor IDs, and events after every mutation.
  • Add drift coverage so deletion, broadening, or actor/event omission is detected.
  • Document staged rollback and the 2026-11-02 pull_request_target enforcement deadline.

Dependencies

  • Parent epic: Adopt supported GitHub rulesets and track remaining provider gaps #69
  • Requires organization-administration access to inventory and manage inherited policies.
  • Final actor allowlists and enforcement activation require maintainer approval.
  • Provider support may lag the REST surface; that must not be misreported as declarative enforcement.

Notes

Do not infer trust from a provider name or repository write access. GitHub features may be exempt for built-in processes, but custom workflows triggered by their identities still require the relevant actor to be allowed.

Blocker: the maintainer approves the world-at-ruin deploy-starter activation by marking #623 ready and merging it | authority | last-verified 2026-10-17: the Data Product Controller trial passed and its outcome is recorded (comment 6229852065); the next activation is prepared with clean actor evidence and waits on that approval | asked pr 2026-10-17

Activity

  1. moved this to 📥 Backlog in 🌊 Project Boardon Sep 19, 2026
  2. self-assigned this
    on Sep 19, 2026
  3. added a commit that references this issue on Sep 19, 2026
  4. devantler commented on Sep 19, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    First inventory, 2026-09-19 ~17:30Z

    Ran the read-only inventory from #203 across all 23 active repositories. It exited cleanly, with no unreadable repositories or workflows.

    What Count
    Workflows 142
    Can be started by hand or from another repository (workflow_dispatch / repository_dispatch) 85
    Publish, release or deploy (by name; confirm each by reading it) 42, of which 26 can also be started by hand
    Reusable (workflow_call) 18
    Scheduled 26
    Plain CI only 28
    Use pull_request_target or workflow_run 0
    Repositories with any execution policy today 0 of 23

    Findings that shape the policy design:

    • No workflow uses a privileged trigger. A code search agrees: the only pull_request_target match is an if: condition in actions/dependency-review.yaml, not a trigger. So the "prohibit pull_request_target by default" rule can start in enforce mode without breaking anything, and it needs no exceptions today.
    • Manual entry is the big surface. 60% of workflows accept workflow_dispatch, including 26 release or deploy workflows. Restricting who can dispatch those is the most valuable first rule.
    • Nothing is protected yet. Every repository reports zero policies, so each rule we add is purely additive.

    Next slice: resolve the live actor identities each workflow needs (people, Dependabot, Renovate, our Apps). Then draft layered policies in Evaluate mode. Organization-scope policies still need an admin:org read, which this run cannot do.

  5. devantler commented on Sep 20, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    Live default-branch inventory — 2026-09-20

    Ran the repository's fail-closed scripts/workflow-execution-inventory.sh --org devantler-tech against the live organization. Its repository-list completeness check passed, every default-branch workflow document parsed, and the run exited successfully.

    • 23 active organization repositories yielded default-branch workflows. Archived repositories were excluded by the inventory.
    • Every observed repository-level workflow-execution policy count was 0.
    • No workflow was classified privileged-trigger: no default-branch workflow currently uses pull_request_target or workflow_run.
    • Manual-entry workflows are widespread, including deployment, recovery, publication, maintenance, and test entry points, so a single organization-wide actor/event allowlist would be too coarse.

    This closes no acceptance criterion by itself. Repository-level zero does not prove that organization- or enterprise-level inherited policy is absent, and the routine credential still cannot read that scope. The inherited-policy join therefore remains QUERY-UNKNOWN; no policy mutation or enforcement recommendation was made from this partial view.

  6. devantler commented on Sep 21, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    Enforcement path — lead for the policy-design slice (2026-09-21)

    This repo manages org settings through deploy/, which Crossplane's upjet GitHub provider reconciles. It is built on the Terraform integrations/github provider. So whatever policies this issue designs have to be expressible through that provider, or they will drift outside GitOps and get no revert-on-drift.

    A search of the provider's public registry docs today turned up only the older Actions resources: repository/organization Actions permissions, workflow token permissions, and runner groups. No resource for the workflow execution protections GA'd on 2026-09-17 surfaced. A search is weak evidence, so treat this as a lead: the next run should confirm it against the provider's resource index before designing anything.

    If it holds, the design slice splits in two:

    1. Record the layered actor/event policy as reviewed desired state in this repo, in the shape the REST API takes, so it is ready when a GitOps path exists.
    2. Track provider support as the enforcement blocker, not a one-off REST apply, which would sit outside GitOps and outside the drift check.
  7. 12 remaining items

  8. devantler commented on Sep 25, 2026

    @devantler
    ContributorAuthor

    🤖 Generated with Claude Code in an interactive session with the maintainer.

    The go-template deploy-starter trial is live

    Next measurement due: 2026-10-02. On that date, read go-template's runs of those two workflows for the window and record whether the policy should stay on, be widened to the next repository, or be reverted.

  9. devantler commented on Oct 2, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    go-template trial measurement (window 2026-09-25 10:53Z → 2026-10-02 12:00Z)

    • release.yaml: 14 runs in the window, all success. They were started by botantler-1[bot] (13 runs, all push) and devantler (1 run, push). No run was blocked.
    • cd.yaml: not exercised. It starts only on a v* tag push (or a manual run). No tag was cut in the window: the newest release is still v1.5.4 from 2026-09-22, before the trial. The success signal for this workflow is therefore unmeasured, not passed.
    • Whole repository: none of the 149 workflow runs in the window ended in anything other than success or skipped. Nothing was stranded.
    • Policy insights: not read. The routine credential still lacks the organization-admin read, so this stays unknown rather than clean.

    Recommendation: keep the go-template policy on and extend the trial until the next v* tag runs cd.yaml. That run is the missing positive control for the deploy-starter path. Nothing measured points to a rollback, and release.yaml is proven for both identities that use it. Widening to a second repository needs the maintainer's approval (see Dependencies), so that call stays open here. Next measurement: the first go-template cd.yaml run after a new tag, read the same way.

  10. devantler commented on Oct 3, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    The missing deployment positive control has now occurred: go-template CD run 37048903707 completed successfully on 2026-10-02 at exact commit 29e1052dc6620c6342e054850cfb2c51e12d3bb7. Its actual GoReleaser job built and published v1.5.5; this was an executed tag-triggered deployment, not an interface check or a skipped job.

    Today's policy reconciliation at reviewed 88b17960d56da9328e7f92bae02c9509cfd2c2be read all ten declared policies back IN-SYNC, including the go-template starter policy. Together with the already recorded release controls, this completes the previously missing positive-path observation.

    Keep the existing pilot active. Policy insights remain UNKNOWN to the routine credential; the direct organization-policy read is rejected for missing administration scope. No clean negative-control verdict or broader rollout is inferred. A second repository still needs a separately reviewed, explicitly approved activation and native actor/event controls. This issue remains open.

  11. devantler commented on Oct 7, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer (interactive Claude Code session)

    The data-product-controller deploy-starter policy is live. The maintainer approved it in an interactive session on 2026-10-07; #452 merged as c252e112, and the reconciler run on main (run 37674569373) updated the live policy and reported the go-template one already in sync.

    Two of the nine prepared deploy-starter policies are now active. Nothing waits on the maintainer right now, so the blocker line and label are removed: the next step is to prepare the next activation as a draft, which then needs its own approval like this one did.

  12. devantler commented on Oct 8, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    The Data Product Controller trial began with the verified policy update in apply run 37674569373 on 2026-10-07 at 19:34 UTC, after approved activation #452 merged. The existing rollout procedure requires one week of observation and a recorded outcome before activating another policy.

    Next measurement: 2026-10-14 at 19:34 UTC. The complete current run census contains eight successful postactivation runs, but none exercises the targeted release workflow. Its latest successful release preceded activation and does not satisfy that control. Observe the first native release after activation, then measure the full week before proposing the next activation with refreshed actor and event evidence. No additional policy was activated.

  13. devantler commented on Oct 8, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    Delivered child #539 in merged PR #544: drift comparison now retains property source and explicitly declared name protection, validates the full selector shape before any writes, and checks targeting again after writes.

    Current-head CI and Codex review passed. The automatic live reconciliation at merged commit e748d78e81dea89471cf1474108cddd65499029d read all ten declarations as IN-SYNC and revoked its App token. v7.6.1 contains the repair.

    This closes the reconciler defect. #202 remains open for its existing staged activation, actor/event controls and rollout acceptance; no additional policy was activated by this change.

  14. devantler commented on Oct 17, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    Data Product Controller trial outcome: passed. The trial ran from 2026-10-07 19:34 UTC to 2026-10-14 19:34 UTC. I measured it on 2026-10-17, which also covers the days after it ended.

    • Targeted workflow: release.yaml ran 45 times between 2026-10-08 and 2026-10-16. All 45 succeeded. 23 were started by devantler and 22 by botantler-1[bot], and both identities are on the allowlist.
    • Native releases after activation: 16 releases shipped, from v2.6.0 (2026-10-08) to v5.4.1 (2026-10-16). Their tag-triggered cd.yaml runs succeeded, apart from one run cancelled on a proof tag the maintainer pushed himself.
    • Nothing failed to start: the repository had no startup_failure run of any workflow in the window.
    • Declarations: the daily apply run on 2026-10-16 (38857840624) read all ten declarations as IN-SYNC.
    • Still unknown: policy insights and negative controls, because the routine credential cannot read them. No clean negative-control verdict is inferred from this trial.

    Next activation: world-at-ruin, whose evidence is the strongest of the remaining eight:

    • workflow-execution-actors.sh exited 0 for 2026-09-17 to today with no UNKNOWN row. cd.yaml was started only by devantler (26203420) and botantler-1[bot] (185060876), with 83 tag pushes in 30 days.
    • The dotnet-template candidate is not usable for a trial: neither publish.yaml nor release.yaml has ever run.

    Gap found: world-at-ruin also has server-cd.yaml, a second deploy workflow that the prepared policy does not target. Covering it is a separate, reviewed change.

    The activation pull request follows as a draft for the maintainer's approval.

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

Metadata

Metadata

Assignees

Labels

Fields

No fields configured for Security.

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions