Harden release workflows against tag injection and credential leakage#45
Merged
Harden release workflows against tag injection and credential leakage#45
Conversation
Made-with: Cursor
cotti
reviewed
Mar 30, 2026
Contributor
cotti
left a comment
There was a problem hiding this comment.
Since the job gives write, we can tight the workflow-level perms a bit more. Otherwise looks good!
Comment on lines
+8
to
+9
| permissions: | ||
| contents: write | ||
| contents: read |
Contributor
There was a problem hiding this comment.
Suggested change
| permissions: | |
| contents: write | |
| contents: read | |
| permissions: {} |
cotti
requested changes
Mar 30, 2026
The job already declares contents: write, so the workflow-level contents: read was redundant. Empty permissions at workflow level ensures no implicit grants. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Member
Author
|
Thanks @cotti! Good catch — applied the suggestion. Setting |
cotti
approved these changes
Apr 7, 2026
cotti
pushed a commit
that referenced
this pull request
Apr 7, 2026
…#45) * Harden release workflows against tag injection and credential leakage Made-with: Cursor * Set workflow-level permissions to {} for least-privilege The job already declares contents: write, so the workflow-level contents: read was redundant. Empty permissions at workflow level ensures no implicit grants. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
cotti
pushed a commit
that referenced
this pull request
Apr 7, 2026
…#45) * Harden release workflows against tag injection and credential leakage Made-with: Cursor * Set workflow-level permissions to {} for least-privilege The job already declares contents: write, so the workflow-level contents: read was redundant. Empty permissions at workflow level ensures no implicit grants. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
cotti
added a commit
that referenced
this pull request
Apr 7, 2026
* Add S3 upload support for changelogs * Add README * Use js-yaml for path checking. * Add pr-number parameter and tighten workflow-level permissions * Fix IFS, add --no-follow-symlinks * Adjust call * Use docs-builder to upload to S3 * Update .gitignore * Update README * Harden release workflows against tag injection and credential leakage (#45) * Harden release workflows against tag injection and credential leakage Made-with: Cursor * Set workflow-level permissions to {} for least-privilege The job already declares contents: write, so the workflow-level contents: read was redundant. Empty permissions at workflow level ensures no implicit grants. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> * Adjust call * Adjust inputs and concurrency for upload workflow * Adjust concurrency group * Adjust docs, improve push target pattern * Use the push SHA instead of the merge commit SHA * Update README * Add config input * Set cancel-in-progress * Introduce config input and additional documentation * Update README --------- Co-authored-by: Martijn Laarman <Mpdreamz@gmail.com> Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
cotti
added a commit
that referenced
this pull request
Apr 22, 2026
* Describe skip label availability and add skip-specific comment * Let docs-builder know it's running on CI to set landing-page-path (#54) * Let docs-builder know it's running on CI to set landing-page-path * Set CI again. * Add agentic workflow infrastructure and docs-check workflow (#56) * Add agentic workflow infrastructure and docs-check workflow Introduces gh-aw agentic workflow support alongside the existing composite actions. Workflow .md sources live in workflows/ as a library; a compile script copies them into .github/workflows/ for the gh-aw compiler and produces .lock.yml files that consumer repos reference via workflow_call. First workflow: docs-check — analyzes PRs/commits for documentation impact using the Elastic Docs MCP server and posts structured findings as comments. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Simplify tooling: use gh extension instead of Go binary Replace go install with gh extension install for the gh-aw compiler. Drop actionlint binary download — the repo already has it via pre-commit. Remove bin/ dependency entirely. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix CI failures and address review feedback - Move `roles` under `on:` (latest gh-aw schema change) - Exclude .lock.yml from pre-commit trailing-whitespace and end-of-file-fixer - Replace custom lint/release Makefile targets with `pre-commit run --all` - Remove release target (release-drafter handles releases) - Recompile lock file with latest gh-aw Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix CI: recompile with latest gh-aw, exclude lock files from actionlint - Update gh-aw extension (v0.63.0 → v0.65.5) and recompile lock file - Exclude .lock.yml from actionlint pre-commit hook (generated files reference secrets injected by the gh-aw runtime) - Fix .gitattributes missing trailing newline Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Rename workflows/ to agentic-workflows/ Update all references in compile script, Makefile, CI, README, DEVELOPING.md, and workflow docs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add /create-agentic-workflow Claude skill Interactive skill that scaffolds the three required files for a new gh-aw agentic workflow: source .md, example.yml trigger, and README. Guides users through pattern selection, trigger config, and prompt structure. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Teach skill about fragment selection and creation Add Step 3 that reads available fragments, explains what each provides and when to include it, documents import rules, and guides users on when to create new fragments vs. reuse existing ones. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Harden release workflows against tag injection and credential leakage (#45) * Harden release workflows against tag injection and credential leakage Made-with: Cursor * Set workflow-level permissions to {} for least-privilege The job already declares contents: write, so the workflow-level contents: read was redundant. Empty permissions at workflow level ensures no implicit grants. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> * Remove post-skipped-comment * Update aw lock --------- Co-authored-by: Fabrizio Ferri-Benedetti <fabri.ferribenedetti@elastic.co> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Martijn Laarman <Mpdreamz@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Security audit of the release-related workflows (
create-major-tag.yml,required-labels.yml) revealed several gaps compared to the recently hardeneddocs-buildanddocs-deployworkflows. This PR brings them up to the same security standard.Changes
create-major-tag.yml— 4 fixes1. Strict semver tag validation (HIGH)
The workflow previously extracted a "major version" from
GITHUB_REFviaawkwith no format validation. Sincerelease: publishedfires for any release (including manually-created ones), an attacker with release-create permission could publish a release with an arbitrary tag name. The extracted value would then be force-pushed as a tag, potentially overwriting legitimate major version tags likev1that downstream consumers depend on.Now rejects any tag not matching
^[0-9]+\.[0-9]+\.[0-9]+$before performing any git operations.2.
persist-credentials: falseon checkout (HIGH)The checkout step was storing
contents: write-scoped credentials in.git/config, making them available to all subsequentrun:steps. If any step were compromised or the workflow extended carelessly, those credentials could push arbitrary code to any branch. Now uses explicit token-based remote URL (set and immediately cleared) only in the step that needs it — matching the pattern used inchangelog/submit.3. Least-privilege permissions (MEDIUM)
contents: writewas set at the workflow level, meaning any future job added to this file would silently inherit write access. Moved tocontents: readat workflow level withcontents: writegranted only to the job that needs it.4. Concurrency guard (MEDIUM)
Two releases published in quick succession (e.g.
2.0.0and2.1.0) would both extractMAJOR_VERSION=2and race ongit push -ffor thev2tag. Added a serializing concurrency group withcancel-in-progress: falseso tag updates are applied in order.required-labels.yml— 2 fixes5. Hardened
pull_request_targetcheckout (MEDIUM)This workflow runs on
pull_request_target(base-repo context with elevated trust). While the default checkout is the base branch (safe today), the lack of explicit constraints made it a footgun — a future edit addingref: ${{ github.event.pull_request.head.sha }}would immediately become a critical code-execution vulnerability from fork PRs.Now uses
sparse-checkoutlimited to the single file it needs (.github/release-drafter.yml) andpersist-credentials: falseto eliminate credential exposure.6. Delimiter-based
GITHUB_OUTPUT(LOW-MEDIUM)The
yqoutput was written toGITHUB_OUTPUTusing inlinekey=valueformat. If the parsed YAML ever produced output containing newlines, it could inject additional output variables. Now uses a random hex delimiter boundary, which is the recommended pattern for multi-line or untrusted output values.Test plan
required-labelscheck should still pass and correctly extract labels fromrelease-drafter.yml99.0.0) →create-major-tagshould createv99foo-bar) is rejected by the validation stepMade with Cursor