Skip to content

CI: bound and pin reusable NuGet deployment - #8

Merged
dansiegel merged 4 commits into
masterfrom
fix/7-harden-nuget-deploy
Aug 20, 2026
Merged

dansiegel merged 4 commits into
masterfrom
fix/7-harden-nuget-deploy

Conversation

@dansiegel

@dansiegel dansiegel commented Aug 20, 2026 •

Copy link
Copy Markdown
Member

Closes #7

Summary

  • cap the reusable Windows NuGet deploy job at 10 minutes, based on 53 observed executions (P95 114 seconds, maximum 158 seconds)
  • pin artifact download, repository-owned signing, Azure login, NuGet publication, fixture checkout, and fixture upload actions to reviewed full commit SHAs
  • add an opt-in, credential-free dry-run job that downloads and discovers NuGet artifacts, validates ZIP and .nuspec structure, and records size and SHA-256 without reading deployment secrets or containing sign/publish steps
  • add a PR-triggered ubuntu-slim caller that creates a minimal NuGet artifact and invokes the same-commit reusable workflow with dry-run: true
  • make the fail-closed policy authoritative with complete-file hashes for the reusable deploy, dry-run caller, policy workflow, and signing composite, plus exact per-file/per-job action+SHA sequences
  • add 47 hostile mutation tests covering credential access, action substitution, YAML-shape bypasses, permission elevation, disabled/skipped steps and jobs, production guard semantics, runners, and timeouts
  • record the sample window, failures, percentile method, runner decision, immutable revisions, dry-run boundary, and merge-method constraint

Runner and cost decision

The real deploy job stays on hosted windows-latest because its optional signing path uses PowerShell and Windows signing tooling. The 10-minute cap is about 5.3× P95 and 3.8× the observed maximum, preserving bounded network/signing headroom without a blanket 60-minute allowance.

The policy and dry-run jobs use hosted ubuntu-slim with three-minute caps. The corrected literal-head proof consumed 9 seconds to create/upload the fixture and 11 seconds to download/inspect it. No self-hosted runner or production credential/feed was used, and the Windows deployment job was skipped before runner allocation.

GitHub does not support conditionally required workflow_call secrets. apiKey is therefore optional during reusable-call validation, while an early guard in the mutually exclusive real-deploy job rejects a missing API key before artifact download, signing, or publication. Normal signing and publication inputs remain unchanged.

Validation

  • node --test scripts/validate-deploy-workflow-policy.test.mjs — 47/47 passed on Node 24.16.0
  • node scripts/validate-deploy-workflow-policy.mjs — passed
  • Actionlint on all three applicable workflows — passed
  • unique-key YAML parse of the deploy, dry-run, policy, and signing files — passed
  • Node syntax, package regeneration/ZIP inspection, and git diff --check — passed
  • independent pre-push hostile review — GO; all original and adjacent bypass probes rejected with no remaining P1/P2
  • exact PR head/base: b88a8796ede661dcd98604bf8786d12254dda373 / f5810c7f33d929cd73dc6b7c295f159603528d1d
  • exact-head Workflow policy run 32420756203 — passed on GitHub-hosted ubuntu-slim in 7 seconds
  • PR-triggered NuGet dry-run 32420756479 — passed; fixture 9 seconds, artifact inspection 7 seconds, production Windows job skipped
  • literal branch-head NuGet dry-run 32420834893 — passed at b88a8796ede661dcd98604bf8786d12254dda373; fixture 9 seconds, artifact inspection 11 seconds, production Windows job skipped
  • the hosted inspector verified AppPortal.WorkflowDryRun.0.0.0.nupkg as 870 bytes with SHA-256 743418eb87a1b54cd5e35c8ae6e80454bd13bb0a2fe1ba483113aa75257861cf

Draft status and external settings

The credential-free exact-head reusable-workflow exit criterion is exercised and green. This PR remains draft intentionally and has no requested reviewers.

master currently has no branch protection. Requiring the policy and dry-run checks is therefore a repository-owner ruleset/branch-protection task; repository-local policy cannot guarantee its own future execution.

Merge method: the signing action is pinned to intermediate branch commit 0836b48f03b9309fe1bcadda6109d9e761e5aaef. Merge this PR with a normal merge commit. Do not squash or rebase-merge unless that pin is first moved to an already-merged durable commit.

@dansiegel
dansiegel marked this pull request as ready for review August 20, 2026 21:49
@dansiegel
dansiegel merged commit 964306e into master Aug 20, 2026
7 checks passed
@dansiegel
dansiegel deleted the fix/7-harden-nuget-deploy branch August 20, 2026 21:50

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6

P1 Badge Execute the policy from a trusted revision

On pull_request runs, this checkout loads the PR's version of the workflow, validator, and tests, so a PR that weakens deploy-nuget.yml can also replace these commands with a no-op while preserving the required job name and receiving a green check. The full-file hashes therefore provide no enforcement because the code verifying them is controlled by the same change; run the policy implementation from a protected base revision or an external immutable workflow while passing it the proposed files to inspect.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

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.

Harden reusable NuGet deployment timeout and action trust boundaries

1 participant