Skip to content

ci: harden GitHub Actions against supply chain attacks#1722

Open
rodrigopavezi wants to merge 3 commits into
masterfrom
feat/ci-supply-chain-hardening
Open

ci: harden GitHub Actions against supply chain attacks#1722
rodrigopavezi wants to merge 3 commits into
masterfrom
feat/ci-supply-chain-hardening

Conversation

@rodrigopavezi
Copy link
Copy Markdown
Member

Implements all GitHub Actions hardening from RequestNetwork/private-issues#282.

Changes

  • All third-party actions pinned to immutable commit SHAs — including github/codeql-action/upload-sarif which runs with security-events: write
  • Top-level permissions: contents: read added to workflows that lacked it (security workflows already had correct permission blocks)
  • StepSecurity Harden Runner added to every job (egress-policy: audit)
  • SocketDev Firewall Free added to tron-smart-contracts install steps (sfw yarn install --frozen-lockfile)

Next steps after merge

  • Monitor Harden Runner audit logs for ~1 week, then promote egress-policy: auditblock with the actual allowlist

- Pin all third-party actions to immutable commit SHAs
- Add top-level permissions: contents: read to all workflows
- Add StepSecurity Harden Runner (egress-policy: audit) to every job
- Add SocketDev/action (firewall-free) + sfw install wrapper to tron-smart-contracts jobs
- Pin github/codeql-action/upload-sarif to SHA (runs with security-events: write)

Closes RequestNetwork/private-issues#282
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 14, 2026

Greptile Summary

This PR hardens seven GitHub Actions workflows against supply chain attacks by pinning all third-party action references to immutable commit SHAs, adding StepSecurity Harden Runner to every job, introducing top-level permissions: contents: read where missing, and wrapping yarn install calls in the tron workflows with the SocketDev Firewall. The issues: write permission needed by the Echidna nightly-failure issue-creator (flagged in a prior review) is also correctly added here.

  • SHA pinning applied to all first-party actions (actions/checkout, actions/setup-node, actions/cache, actions/upload-artifact, actions/github-script, actions/setup-python, github/codeql-action/upload-sarif) and third-party actions (step-security/harden-runner, kentaro-m/auto-assign-action, SocketDev/action).
  • Three reusable workflow calls in auto-project.yml, pr-comments.yml, and reopen-issue-if-prs-open.yml still reference external repos at a mutable @main ref — flagged in prior review comments and unaddressed in this PR.
  • docker pull trailofbits/echidna:latest in security-echidna.yml uses a floating tag that is inconsistent with the SHA-pinning approach applied to every other external artifact in this PR.

Confidence Score: 4/5

Safe to merge for the changes it makes, but three workflows still call external reusable workflows via mutable @main refs (previously flagged), leaving active supply chain exposure for those paths.

The SHA pinning, Harden Runner additions, and Socket.dev integration are solid improvements. The remaining gap is in pr-comments.yml, auto-project.yml, and reopen-issue-if-prs-open.yml, which all delegate to external repos at a mutable @main ref while passing PATs — a supply chain vector this PR was explicitly designed to close but did not address for reusable workflow references.

pr-comments.yml carries the highest risk: it fires on pull_request_target and forwards GH_PAT_AUTO_COMMENTS to an external repo pinned only to @main. auto-project.yml and reopen-issue-if-prs-open.yml have the same structural problem with their respective PATs.

Important Files Changed

Filename Overview
.github/workflows/auto-project.yml Adds top-level permissions: contents: read; the reusable workflow call still uses a mutable @main ref for an external repo, leaving a supply chain gap flagged in prior review comments.
.github/workflows/auto_assign_pr.yml Adds Harden Runner (SHA-pinned) and pins kentaro-m/auto-assign-action to a commit SHA; permissions block already present with appropriate pull-requests: write.
.github/workflows/pr-comments.yml Adds permissions: contents: read and pull-requests: write; the privileged pull_request_target job still calls RequestNetwork/auto-comments/.github/workflows/pr-auto-comments.yml@main via a mutable ref while passing GH_PAT_AUTO_COMMENTS — the highest-risk unfixed gap in this PR.
.github/workflows/reopen-issue-if-prs-open.yml Adds top-level permissions: contents: read; the reusable workflow call still uses a mutable @main ref for RequestNetwork/.github, passing REOPEN_ISSUES_TOKEN.
.github/workflows/security-echidna.yml Adds Harden Runner, pins all action SHAs, and adds issues: write (fixing the prior P1 comment). The docker pull trailofbits/echidna:latest mutable tag remains unaddressed.
.github/workflows/security-slither.yml Adds Harden Runner and pins all action SHAs including github/codeql-action/upload-sarif; existing permissions block (security-events: write, pull-requests: write) is correct.
.github/workflows/tron-smart-contracts.yml Adds top-level permissions: contents: read, Harden Runner to all three jobs, SHA-pinned actions, and Socket.dev Firewall on each yarn install --frozen-lockfile.

Reviews (3): Last reviewed commit: "chore: re-trigger CI after Performance p..." | Re-trigger Greptile

@github-actions
Copy link
Copy Markdown

✅ Slither Security Analysis

Status: Passed

Findings Summary

Severity Count Status
✅ High 0 Pass
🟡 Medium 2 Review Recommended
🔵 Low 0 Info
ℹ️ Informational 1 Info

⚠️ Please review the findings in the Security tab or download the artifacts.

📄 Full report available in workflow artifacts.
🔍 View detailed findings in the Security tab.

@github-actions
Copy link
Copy Markdown

✅ Echidna Fuzzing Results

Mode: ci (50000 test sequences)
Status: All Properties Passed

Property Test Results

Status Count
✅ Passed 16
❌ Failed 0
Total 16
Pass Rate 100.0%

📄 Full report and corpus available in workflow artifacts.

ℹ️ About Echidna Fuzzing

Echidna is a property-based fuzzer that generates random sequences of transactions
to test invariants (properties that should always hold true).

Properties tested:

  • Fee calculation bounds
  • Access control enforcement
  • Amount constraints
  • No duplicate payments
  • Zero address validation
  • Integer overflow protection

@rodrigopavezi rodrigopavezi self-assigned this May 14, 2026
The nightly failure-alert step calls github.rest.issues.create() to
notify the team when Echidna properties fail. Without issues: write
the call silently returns a 403 and the alert is never created.
@github-actions
Copy link
Copy Markdown

✅ Slither Security Analysis

Status: Passed

Findings Summary

Severity Count Status
✅ High 0 Pass
🟡 Medium 2 Review Recommended
🔵 Low 0 Info
ℹ️ Informational 1 Info

⚠️ Please review the findings in the Security tab or download the artifacts.

📄 Full report available in workflow artifacts.
🔍 View detailed findings in the Security tab.

@github-actions
Copy link
Copy Markdown

✅ Echidna Fuzzing Results

Mode: ci (50000 test sequences)
Status: All Properties Passed

Property Test Results

Status Count
✅ Passed 16
❌ Failed 0
Total 16
Pass Rate 100.0%

📄 Full report and corpus available in workflow artifacts.

ℹ️ About Echidna Fuzzing

Echidna is a property-based fuzzer that generates random sequences of transactions
to test invariants (properties that should always hold true).

Properties tested:

  • Fee calculation bounds
  • Access control enforcement
  • Amount constraints
  • No duplicate payments
  • Zero address validation
  • Integer overflow protection

The previous CircleCI failures on this PR were caused by the
RequestNetwork CircleCI org dropping to the Free plan, which caps
Docker resource classes at large. The repo's .circleci/config.yml
declares xlarge for build/test jobs (deliberate; see #1703), so every
build failed with resource-class-not-in-plan. Org was upgraded to
Performance; this empty commit re-triggers the pipeline. No source
changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

✅ Slither Security Analysis

Status: Passed

Findings Summary

Severity Count Status
✅ High 0 Pass
🟡 Medium 2 Review Recommended
🔵 Low 0 Info
ℹ️ Informational 1 Info

⚠️ Please review the findings in the Security tab or download the artifacts.

📄 Full report available in workflow artifacts.
🔍 View detailed findings in the Security tab.

@github-actions
Copy link
Copy Markdown

✅ Echidna Fuzzing Results

Mode: ci (50000 test sequences)
Status: All Properties Passed

Property Test Results

Status Count
✅ Passed 16
❌ Failed 0
Total 16
Pass Rate 100.0%

📄 Full report and corpus available in workflow artifacts.

ℹ️ About Echidna Fuzzing

Echidna is a property-based fuzzer that generates random sequences of transactions
to test invariants (properties that should always hold true).

Properties tested:

  • Fee calculation bounds
  • Access control enforcement
  • Amount constraints
  • No duplicate payments
  • Zero address validation
  • Integer overflow protection

Copy link
Copy Markdown
Member

@MantisClone MantisClone left a comment

Choose a reason for hiding this comment

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

Approved 👍 pending comment resolution 🚧

Mostly clean; one bug and one scope gap to address before merge.

🔴 Must-fix before merge

github/codeql-action/upload-sarif@5e316336… does not exist in github/codeql-action. Verified three ways: 422 on direct commit lookup, no ref points at it, not in the tag list. The step is if: always() && hashFiles(...) != '' and Slither emits a SARIF every run, so it fails the SARIF upload (and the Slither job) on every contract PR. This is also the exact action #282 called out as highest priority.

Fix: github/codeql-action/upload-sarif@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5.

🟡 Scope gap

security-echidna.yml and security-slither.yml both run yarn install --frozen-lockfile unprotected by Socket Firewall. #282 task 4 said Socket on the install step across CI workflows; the PR only wraps the three Tron install steps. Either add sfw to the two security workflows or call out the rationale in the description.

🔵 Nice-to-have before merge

  • Bump # v2 / # v4 / # v7 comments to exact-version (e.g. # v2.11.1, # v4.4.0) so the next reviewer can verify by eye. The floating major tags have already moved past several of the pinned SHAs.
  • Lift the dedicated fix(ci): add issues: write… commit subject into the PR description so the permission expansion is documented in the body, not just in git log.

📋 UAT

PR is missing a UAT procedure. Suggested shape:

  1. After merge, push any contract change to master to trigger Slither, Echidna, and the three Tron jobs.
  2. Verify each job has Harden Runner as step 1 and (Tron only) sfw yarn install --frozen-lockfile.
  3. Open https://app.stepsecurity.io and confirm the audit-mode log shows an egress list on each run.
  4. Confirm the Slither SARIF upload to the Security tab succeeds (this is the failure mode of the must-fix above).

📌 Follow-ups (separate issues, not blockers)

Fresh-eyes pass surfaced things outside this PR's scope. Happy to file these as private-issues if you want:

  1. Add Slither Static Analysis, Echidna Property-Based Fuzzing, and the three Tron checks to master's required status checks. Currently only CircleCI gates merge.
  2. Enable sha_pinning_required: true at the repo or org level so the pinning policy is regression-proof.
  3. Pin actions/github-script@v6 inside RequestNetwork/auto-comments/.github/workflows/pr-auto-comments.yml. That reusable workflow runs under pull_request_target with GH_PAT_AUTO_COMMENTS, so the supply-chain threat model leaks one hop in.
  4. Pin the three first-party @main reusable workflows (auto-project.yml, pr-comments.yml, reopen-issue-if-prs-open.yml) to SHAs or document accepting the residual risk.
  5. Add .github/CODEOWNERS requiring security-team review on .github/workflows/**.
  6. Add .github/dependabot.yml. The push to this branch surfaced 166 vulns on master (4 critical / 82 high) which is a related theme.
  7. Set dismiss_stale_reviews: true on master branch protection.

- name: Upload SARIF to GitHub Security
if: always() && hashFiles('packages/smart-contracts/reports/security/slither.sarif') != ''
uses: github/codeql-action/upload-sarif@v4
uses: github/codeql-action/upload-sarif@5e316336eb4f107009e477d4bfbfff13d7250fae # v4
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🔴 Must-fix before merge: this SHA does not exist in github/codeql-action. Verified: gh api repos/github/codeql-action/commits/5e316336... returns 422, and no ref points at it. The step runs if: always() && hashFiles(...) != '' and Slither emits a SARIF every run, so this fails the job every time.

Replace with:

uses: github/codeql-action/upload-sarif@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5


- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🟡 Adjacent feedback: the yarn install --frozen-lockfile step a few lines below is unprotected by Socket Firewall. #282 task 4 said Socket on the install step across CI workflows; the PR only wraps the three Tron install steps. Suggest adding the same setup-Socket pattern Tron got, immediately before the install step:

- name: Setup Socket.dev
  uses: SocketDev/action@ba6de6cc0565af1f42295590380973573297e31f # v1.3.2
  with:
    mode: firewall-free

and changing the install line to sfw yarn install --frozen-lockfile.


- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🟡 Same Socket Firewall gap as security-echidna.yml. Same fix.

cache: 'yarn'

- name: Install TronBox globally
run: npm install -g tronbox
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🔵 Minor: this runs before Setup Socket.dev so the tronbox global install isn't gated by sfw. Harden Runner audit still observes its egress. Suggest swapping the two so all dep installs in this job go through Socket.

- name: Comment on PR
if: github.event_name == 'pull_request' && always()
uses: actions/github-script@v7
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🔵 Nit, not blocking: the '${{ steps.parse.outputs.X }}' interpolation pattern below is the github-script script-injection shape that zizmor and actionlint flag. Safe today since the values are jq-extracted integers, but the safer pattern is env: + process.env. Same in security-slither.yml:140.

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.

2 participants