Skip to content

feat: wire up trusted publishing and harden the reusable workflows - #37

Merged
kieronlanning merged 3 commits into
mainfrom
chore/pipeline-hardening
Oct 27, 2026
Merged

kieronlanning merged 3 commits into
mainfrom
chore/pipeline-hardening

Conversation

@kieronlanning

Copy link
Copy Markdown
Contributor

These workflows run in every purview-dev repository, with that repository's token and publishing credentials, so the blast radius of a defect here is the whole organisation.

Trusted Publishing now works. It was a no-op: the toggle made the tool set ApiKey = null and nothing replaced it, because id-token: write appeared nowhere in the repository. The underlying misconception is in the old doc comment - Trusted Publishing does use an API key, the OIDC exchange simply mints a short-lived one.

  • id-token: write on purview-release.yml, and a NuGet/login step immediately before the push, because the key lives an hour and a token exchanges once.
  • A nuget-user input, with a step that fails when trusted-publishing is on and it is missing.
  • PublishNuGetModule always passes the key it finds, and reports a missing one rather than attempting an unauthenticated push.
  • NUGET_APIKEY is blanked while trusted publishing is on: the tool prefers it over NUGET_API_KEY, so a leftover long-lived secret would have shadowed the short-lived key and kept publishing the old way.

Injection hardening. Caller inputs reach shells through env rather than being interpolated into the script text; GITHUB_ENV writes use a random heredoc delimiter, because a value containing a newline could define variables for every later step; workflow-command messages are flattened; set -euo pipefail added where it was missing; publishing credentials scoped to the publishing step rather than exported job-wide.

All 14 third-party action references pinned to commit SHAs, with Dependabot added so pinning does not become staleness. Plus SECURITY.md and CODEOWNERS.

The repository's own guard tests needed updating rather than weakening: Workflow_GuardsEveryOptionalInputItForwards required the literal interpolated form, so it was enforcing the injectable pattern. It now accepts the env-mapped and step-condition forms, and the parity scan follows keys into the forward helper so moving one there does not drop it from validation.

Verified: 530 tests passing, actionlint clean, csharpier clean.

These workflows run in every purview-dev repository, with that repository's
token and publishing credentials, so the blast radius of a defect here is the
whole organisation.

Trusted Publishing now works. It was a no-op: the toggle made the tool set
ApiKey = null and nothing replaced it, because id-token: write appeared nowhere
in the repository. The underlying misconception is in the old doc comment -
Trusted Publishing does use an API key, the OIDC exchange simply mints a
short-lived one.
- id-token: write on purview-release.yml, and a NuGet/login step immediately
  before the push, because the key lives an hour and a token exchanges once.
- A nuget-user input, with a step that fails when trusted-publishing is on and
  it is missing.
- PublishNuGetModule always passes the key it finds, and reports a missing one
  rather than attempting an unauthenticated push.
- NUGET_APIKEY is blanked while trusted publishing is on: the tool prefers it
  over NUGET_API_KEY, so a leftover long-lived secret would have shadowed the
  short-lived key and kept publishing the old way.

Injection hardening. Caller inputs reach shells through env rather than being
interpolated into the script text; GITHUB_ENV writes use a random heredoc
delimiter, because a value containing a newline could define variables for every
later step; workflow-command messages are flattened; set -euo pipefail added
where it was missing; publishing credentials scoped to the publishing step
rather than exported job-wide.

All 14 third-party action references pinned to commit SHAs, with Dependabot
added so pinning does not become staleness. Plus SECURITY.md and CODEOWNERS.

The repository's own guard tests needed updating rather than weakening:
Workflow_GuardsEveryOptionalInputItForwards required the literal interpolated
form, so it was enforcing the injectable pattern. It now accepts the env-mapped
and step-condition forms, and the parity scan follows keys into the forward
helper so moving one there does not drop it from validation.

Verified: 530 tests passing, actionlint clean, csharpier clean.
@kieronlanning
kieronlanning merged commit b043547 into main Oct 27, 2026
1 check passed
@kieronlanning
kieronlanning deleted the chore/pipeline-hardening branch October 27, 2026 10:33
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