Skip to content

ci: security hardening for GitHub Actions workflows#993

Merged
TomerFi merged 10 commits into
devfrom
ci/security-hardening
May 15, 2026
Merged

ci: security hardening for GitHub Actions workflows#993
TomerFi merged 10 commits into
devfrom
ci/security-hardening

Conversation

@TomerFi
Copy link
Copy Markdown
Owner

@TomerFi TomerFi commented May 15, 2026

Summary

  • Pin all 34 GitHub Actions to SHA digests with version comments (supply-chain mitigation)
  • Add permissions: blocks following least-privilege principle across all 4 workflows
  • Move SSH signing key to $RUNNER_TEMP/ and remove unnecessary cleanup step
  • Add concurrency: blocks to cancel overlapping workflow runs
  • Add persist-credentials: false to checkout steps (remove saved token from disk)
  • Add hadolint Dockerfile linting to pr/stage/release workflows
  • Require build-docker as dependency for e2e-smoke-test (skip smoke test if hadolint/Docker build fails)
  • Fix permissions typo (content -> contents in pr.yml)

Files Changed

  • .github/workflows/pr.yml
  • .github/workflows/release.yml
  • .github/workflows/stage.yml
  • .github/workflows/pages.yml
  • Dockerfile
  • .hadolint.yaml (new)

Changes

130 insertions, 23 deletions

Summary by CodeRabbit

  • Chores
    • CI/CD: Added concurrency control to workflows to reduce overlapping runs and improve reliability.
    • CI/CD: Pinned third‑party workflow actions to specific revisions for more stable, reproducible runs.
    • CI/CD: Hardened build steps (reduced credential persistence and adjusted signing key handling) for safer releases.
    • Docker: Streamlined image build with leaner package installs and pip upgrade for more efficient builds.
    • Linting: Added/updated Hadolint configuration and container linting step to enforce image quality.

Review Change Stack

TomerFi added 7 commits May 10, 2026 13:05
- Replace all @tag references with full 40-character SHA commits
- Add version comment to each pinned action for easy reference
- Covers all 31 action references across 4 workflow files
- Actions affected: checkout, setup-python, cache, codecov, docker/*, github-script, peaceiris/actions-gh-pages, EnricoMi/publish-unit-test-result-action

Signed-off-by: Tomer Figenblat <tomer@figenblat.com>
Signed-off-by: Tomer Figenblat <tomer@figenblat.com>
…anup

Signed-off-by: Tomer Figenblat <tomer@figenblat.com>
Signed-off-by: Tomer Figenblat <tomer@figenblat.com>
Signed-off-by: Tomer Figenblat <tomer@figenblat.com>
Signed-off-by: Tomer Figenblat <tomer@figenblat.com>
…dependency

Signed-off-by: Tomer Figenblat <tomer@figenblat.com>
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented May 15, 2026

Reviewer's Guide

Security-hardens all GitHub Actions workflows by pinning actions to SHAs, adding least-privilege permissions and concurrency controls, tightening checkout/token and SSH key handling, and introducing hadolint-based Dockerfile linting that gates Docker builds and dependent jobs.

File-Level Changes

Change Details Files
Pin all GitHub Actions to specific commit SHAs for supply-chain hardening.
  • Replace version-tagged uses: references (e.g., @v6, @v4.0.0) with pinned commit SHAs while retaining version comments
  • Ensure all core actions across PR, stage, release, and pages workflows are now SHA-pinned, including checkout, setup-python, cache, github-script, docker actions, Codecov, and gh-pages
.github/workflows/pr.yml
.github/workflows/stage.yml
.github/workflows/release.yml
.github/workflows/pages.yml
Introduce least-privilege permissions and stricter GITHUB_TOKEN handling across workflows.
  • Add explicit permissions blocks per job to scope access to contents, packages, pages, and pull-requests as needed
  • Remove an overly broad permissions block from build-docker and rely on defaults where appropriate
  • Set persist-credentials: false on all checkout steps so the GITHUB_TOKEN is not stored in the local git config
.github/workflows/pr.yml
.github/workflows/stage.yml
.github/workflows/release.yml
.github/workflows/pages.yml
Add concurrency controls to prevent overlapping workflow runs on the same ref/PR.
  • Add top-level concurrency blocks to all workflows with grouping by workflow name and ref (and PR number for pr.yml)
  • Configure cancel-in-progress true for PR and stage workflows to auto-cancel superseded runs and false for release to let release jobs finish
  • Ensure pages workflow also serializes by ref to avoid conflicting docs deployments
.github/workflows/pr.yml
.github/workflows/stage.yml
.github/workflows/release.yml
.github/workflows/pages.yml
Harden SSH signing key handling for releases.
  • Move SSH signing key storage from ~/.ssh/signing_key to $RUNNER_TEMP/signing_key
  • Stop creating and chmod-ing ~/.ssh and instead rely on $RUNNER_TEMP with restrictive file permissions
  • Remove the explicit cleanup step since files in $RUNNER_TEMP are ephemeral
.github/workflows/release.yml
Introduce hadolint-based Dockerfile linting and gate Docker builds/tests on it.
  • Add hadolint/hadolint-action steps to PR, stage, and release workflows to lint the Dockerfile before building
  • Configure hadolint to use a project-local .hadolint.yaml to ignore selected rules (DL3008, DL3013, DL3059)
  • Make e2e-smoke-test depend on build-docker so smoke tests only run if Docker build (and hadolint) succeed
.github/workflows/pr.yml
.github/workflows/stage.yml
.github/workflows/release.yml
.hadolint.yaml
Minor Dockerfile hardening and efficiency improvements to satisfy linting and reduce attack surface.
  • Install build-essential using apt-get with --no-install-recommends to reduce unnecessary packages
  • Use pip install --no-cache-dir for both upgrading pip and installing requirements to avoid caching wheels and reduce image size
Dockerfile
Miscellaneous workflow fixes and cleanups.
  • Fix a permissions typo by using contents instead of content in pr.yml (if present in surrounding context)
  • Standardize workflow structure by adding/adjusting environment, needs, and step configurations (e.g., needs: build-docker for e2e-smoke-test)
.github/workflows/pr.yml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@auto-me-bot auto-me-bot Bot added the status: needs review Pull request needs a review label May 15, 2026
@snyk-io
Copy link
Copy Markdown

snyk-io Bot commented May 15, 2026

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 15, 2026

Warning

Rate limit exceeded

@TomerFi has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 28 minutes and 21 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7fd69e96-8d16-4290-a85a-60da9dbec366

📥 Commits

Reviewing files that changed from the base of the PR and between 143eaf0 and e8b5266.

📒 Files selected for processing (1)
  • .github/workflows/pr.yml
📝 Walkthrough

Walkthrough

Adds workflow-level concurrency across GitHub Actions, pins third-party actions to specific commit SHAs, updates release SSH signing key handling, and tweaks Dockerfile + Hadolint config for leaner builds and adjusted lint rules.

Changes

Workflow Security and Docker Optimization

Layer / File(s) Summary
Pages, PR, and Stage workflow security hardening
.github/workflows/pages.yml, .github/workflows/pr.yml, .github/workflows/stage.yml
Pages/PR/Stage workflows add top-level concurrency groups and pin checkout, setup-python, cache, hadolint, QEMU/Buildx, docker build/push, test-reporting, and codecov actions to specific commit SHAs; PR workflow adds needs: build-docker and some checkouts set persist-credentials: false.
Release workflow hardening with SSH signing key updates
.github/workflows/release.yml
Release workflow adds concurrency, sets job permissions: contents: write, pins many third-party actions to commit SHAs, changes SSH signing key setup to use $RUNNER_TEMP/signing_key with updated chmod/git signing config, and removes the SSH key cleanup step.
Docker build optimization and Hadolint configuration
Dockerfile, .hadolint.yaml
Dockerfile installs build-essential with --no-install-recommends, upgrades pip with --no-cache-dir, and installs requirements with --no-cache-dir while preserving extension flags. .hadolint.yaml disables DL3008, DL3013, and DL3059.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

size: m

Suggested reviewers

  • dmatik
  • YogevBokobza

Poem

🐰 I pinned the actions, one by one,
Concurrency set so runs don't run and run,
SSH keys tuck'd in temp to rest,
Docker trimmed to do its best,
A hop, a push — CI sleeps well tonight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'ci: security hardening for GitHub Actions workflows' directly and accurately summarizes the main objective of the PR, which focuses on security hardening across GitHub Actions workflows through SHA pinning, permission blocks, and other security measures.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/security-hardening

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The concurrency.group expressions use ${{workflow}} which is not a valid context; consider switching to ${{ github.workflow }} (and similar github.* contexts) so concurrency grouping works as expected.
  • In pr.yml, the build-docker and e2e-smoke-test jobs still rely on default token permissions; consider adding explicit least-privilege permissions: blocks (e.g., contents: read) to align them with the hardening applied elsewhere.
  • The hadolint step and pinned action SHAs are repeated across multiple workflows; consider extracting a reusable workflow or composite action to centralize these definitions and make future updates less error-prone.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `concurrency.group` expressions use `${{workflow}}` which is not a valid context; consider switching to `${{ github.workflow }}` (and similar `github.*` contexts) so concurrency grouping works as expected.
- In `pr.yml`, the `build-docker` and `e2e-smoke-test` jobs still rely on default token permissions; consider adding explicit least-privilege `permissions:` blocks (e.g., `contents: read`) to align them with the hardening applied elsewhere.
- The hadolint step and pinned action SHAs are repeated across multiple workflows; consider extracting a reusable workflow or composite action to centralize these definitions and make future updates less error-prone.

## Individual Comments

### Comment 1
<location path=".github/workflows/release.yml" line_range="15-17" />
<code_context>
   release:
     types: [published]

+concurrency:
+  group: ${{workflow}}-${{github.ref}}
+  cancel-in-progress: true
</code_context>
<issue_to_address>
**issue (bug_risk):** Use the correct GitHub context for the concurrency group expression.

`workflow` without a context (as in `group: ${{workflow}}-${{github.ref}}`) is an invalid GitHub Actions expression and will cause the workflow to fail at runtime. Please change this to use the correct context, e.g. `group: ${{ github.workflow }}-${{ github.ref }}`, and update the same pattern in the other workflows for consistency.
</issue_to_address>

### Comment 2
<location path=".github/workflows/pages.yml" line_range="16-17" />
<code_context>
   deploy-pages:
     runs-on: ubuntu-latest
     environment: github-pages
+    permissions:
+      pages: write
     if: ${{ github.ref != 'refs/tags/early-access' }}
     name: Build documentation site and deploy to GH-Pages
</code_context>
<issue_to_address>
**issue (bug_risk):** Job permissions here likely break checkout and gh-pages publishing.

With job-level permissions restricted to `pages: write`, the default `GITHUB_TOKEN` no longer has `contents` scope. `actions/checkout` requires `contents: read` and `peaceiris/actions-gh-pages` typically needs `contents: write` to push to `gh-pages`. Update this block to grant `contents: write` (and drop `pages: write` unless you’re explicitly calling the Pages deployment APIs), for example:

```yaml
deploy-pages:
  permissions:
    contents: write
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread .github/workflows/release.yml
Comment thread .github/workflows/pages.yml Outdated
@auto-me-bot auto-me-bot Bot added status: review started Pull review in progress and removed status: needs review Pull request needs a review labels May 15, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/pages.yml:
- Around line 8-10: The concurrency group uses an invalid context variable
`${{workflow}}`; update the value in the concurrency block (the "group" key) to
use the correct GitHub Actions context `${{ github.workflow }}` so the group
becomes `${{ github.workflow }}-${{ github.ref }}` ensuring the concurrency
grouping is valid.
- Around line 16-18: The workflow currently sets permissions: pages: write which
lacks the contents scope needed by actions/checkout and
peaceiris/actions-gh-pages when using GITHUB_TOKEN; update the permissions block
to include contents: write (in addition to pages: write) so third-party actions
can read and push repository contents; ensure the permissions stanza explicitly
contains both "contents: write" and "pages: write" so actions/checkout and
peaceiris/actions-gh-pages succeed.

In @.github/workflows/release.yml:
- Around line 15-17: The concurrency.group expression uses an invalid context
token `${{workflow}}`; replace it with the correct GitHub context `${{
github.workflow }}` so the group reads `${{ github.workflow }}-${{ github.ref
}}` (update the concurrency.group line and any similar occurrences) to use the
proper github.workflow context and avoid runtime evaluation errors.
- Around line 24-26: The workflow currently grants the unnecessary GitHub
Packages write scope via the permissions block (packages: write); remove the
packages: write entry and leave only contents: write so the GITHUB_TOKEN cannot
write packages/GHCR. In other words, update the permissions stanza (which
contains permissions: contents: write and packages: write) to drop the packages:
write line; keep contents: write and continue using docker/login-action with
DockerHub credentials and the separate RELEASE_PAT for creating releases.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 971d4dc1-c1f0-4756-aaf4-196a818966a3

📥 Commits

Reviewing files that changed from the base of the PR and between 6d4cb7f and 6650ec6.

📒 Files selected for processing (6)
  • .github/workflows/pages.yml
  • .github/workflows/pr.yml
  • .github/workflows/release.yml
  • .github/workflows/stage.yml
  • .hadolint.yaml
  • Dockerfile

Comment thread .github/workflows/pages.yml
Comment thread .github/workflows/pages.yml
Comment thread .github/workflows/release.yml
Comment thread .github/workflows/release.yml Outdated
Signed-off-by: Tomer Figenblat <tomer@figenblat.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 15, 2026

Test Results

69 tests   69 ✅  1s ⏱️
 1 suites   0 💤
 1 files     0 ❌

Results for commit e8b5266.

♻️ This comment has been updated with latest results.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 15, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.07%. Comparing base (6d4cb7f) to head (e8b5266).

Additional details and impacted files
@@           Coverage Diff           @@
##              dev     #993   +/-   ##
=======================================
  Coverage   96.07%   96.07%           
=======================================
  Files           1        1           
  Lines         331      331           
=======================================
  Hits          318      318           
  Misses         13       13           
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…ges write scope

Signed-off-by: Tomer Figenblat <tomer@figenblat.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/pr.yml (1)

17-24: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add contents: read to build-app job permissions.

build-app sets job-level permissions but omits contents: read; this breaks actions/checkout because checkout requires repository read access. When a job defines permissions, any scope not explicitly specified defaults to none, preventing checkout from authenticating the clone operation.

Suggested fix
   build-app:
     runs-on: ubuntu-latest
     name: Build project
     permissions:
+      contents: read
       checks: write
       pull-requests: write
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/pr.yml around lines 17 - 24, The job-level permissions
block for the build-app job currently sets checks and pull-requests to write but
omits contents, which prevents actions/checkout from working; update the
permissions block referenced under the build-app job (the permissions: checks,
pull-requests entries) to include contents: read so
actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd can authenticate and
clone the repo.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In @.github/workflows/pr.yml:
- Around line 17-24: The job-level permissions block for the build-app job
currently sets checks and pull-requests to write but omits contents, which
prevents actions/checkout from working; update the permissions block referenced
under the build-app job (the permissions: checks, pull-requests entries) to
include contents: read so
actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd can authenticate and
clone the repo.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7a2bc529-e254-4da2-a845-5cd048d11050

📥 Commits

Reviewing files that changed from the base of the PR and between 6650ec6 and 143eaf0.

📒 Files selected for processing (4)
  • .github/workflows/pages.yml
  • .github/workflows/pr.yml
  • .github/workflows/release.yml
  • .github/workflows/stage.yml
🚧 Files skipped from review as they are similar to previous changes (3)
  • .github/workflows/stage.yml
  • .github/workflows/pages.yml
  • .github/workflows/release.yml

Signed-off-by: Tomer Figenblat <tomer@figenblat.com>
@TomerFi
Copy link
Copy Markdown
Owner Author

TomerFi commented May 15, 2026

@coderabbitai Added contents: read to build-app permissions in pr.yml - actions/checkout can now authenticate in that job. See commit e8b5266.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 15, 2026

Tip

For best results, initiate chat on the files or code changes.

@TomerFi: Confirmed — with contents: read on the build-app job, actions/checkout can now authenticate properly while still keeping the job scoped to the minimum necessary permissions. Thanks for the fix!


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

@TomerFi TomerFi merged commit 3debc11 into dev May 15, 2026
12 checks passed
@TomerFi TomerFi deleted the ci/security-hardening branch May 15, 2026 21:37
@auto-me-bot auto-me-bot Bot added status: merged Pull request merged and removed status: review started Pull review in progress labels May 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: merged Pull request merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant