Skip to main content
To defend against attacks and tampering of GitHub Actions, the official Laravel projects implement a critical security measure: pinning Action dependencies to commit hashes. This page explains the practical approach package developers should adopt.
This page is a companion to Package Development Basics. It assumes you’re familiar with GitHub Actions basics.

Security Risks of GitHub Actions

Dangers of Tag-Based References

Most GitHub Actions are referenced by tags:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
Problems with this approach:
  • Tags are mutable — Tags can be deleted and recreated with the same name
  • Tampering risk — If an action’s repository owner is compromised, malicious code can be injected
  • Supply chain attacks — If a dependency is attacked, your workflow becomes vulnerable

How Official Laravel Projects Respond

laravel/laravel and laravel/framework pin all actions to full commit hashes (SHAs).
# Secure reference method
- uses: actions/checkout@a5ac7e51b41094c7fcab2042361574b0021804ab  # v4

Implementation Strategy

Step 1: Create Dependabot Configuration

Create .github/dependabot.yml in your repository. You can copy Laravel’s file directly:
version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"
    cooldown:
      default-days: 5
    groups:
      github-actions:
        patterns:
          - "*"
This file:
  • Scans automatically — Detects new GitHub Action versions
  • Creates update PRs — Proposes changes when updates are available
  • Controls update method — Unpinned actions update by version; pinned actions update by SHA

Step 2: Pin Existing Actions to SHAs

Convert all action references in your workflows to commit hashes. Use pinact to automate this.

Using the pinact Tool

# Pin all actions to SHAs
pinact run

Manual Approach

If pinact is unavailable, look up commit SHAs on each action’s latest version page and update manually:
# Before
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
  with:
    php-version: ${{ matrix.php }}

# After
- uses: actions/checkout@a5ac7e51b41094c7fcab2042361574b0021804ab  # v4
- uses: shivammathur/setup-php@0a92e4568cab5c87b7175192630661408458a086  # v2
  with:
    php-version: ${{ matrix.php }}

Step 3: Enable Dependabot Configuration

Commit and push .github/dependabot.yml to your repository. Dependabot will automatically begin scanning.

How Dependabot Updates Work

Dependabot applies different update strategies based on your dependabot.yml configuration:

Unpinned Actions

# Before pinning
- uses: actions/checkout@v4
Dependabot’s update: Updates the version range to a new version
# PR created by Dependabot
- uses: actions/checkout@v5
This prioritizes convenience and handles tag movement, but security risks remain.

Pinned Actions

# After pinning
- uses: actions/checkout@a5ac7e51b41094c7fcab2042361574b0021804ab  # v4
Dependabot’s update: Updates the commit hash to the new version’s hash
# PR created by Dependabot
- uses: actions/checkout@f1d3225b54b677ba3e72df8c464cb6cf6dc1aebf  # v4
This is the most secure approach. Even with new versions, SHA references protect against tampering.

Complete Workflow Example

A comprehensive example using multiple actions:
name: Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    strategy:
      fail-fast: false
      matrix:
        php: [8.2, 8.3, 8.4]
        laravel: ["^12.0", "^13.0"]

    steps:
      - uses: actions/checkout@a5ac7e51b41094c7fcab2042361574b0021804ab  # v4
        with:
          fetch-depth: 0

      - name: Setup PHP
        uses: shivammathur/setup-php@0a92e4568cab5c87b7175192630661408458a086  # v2
        with:
          php-version: ${{ matrix.php }}
          extensions: dom, curl, libxml, mbstring, zip, intl, sqlite3
          coverage: none

      - name: Install dependencies
        run: |
          composer require "laravel/framework:${{ matrix.laravel }}" \
                           --no-interaction --no-update
          composer update --prefer-dist --no-interaction

      - name: Run tests
        run: vendor/bin/pest

  lint:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@a5ac7e51b41094c7fcab2042361574b0021804ab  # v4

      - name: Setup PHP
        uses: shivammathur/setup-php@0a92e4568cab5c87b7175192630661408458a086  # v2
        with:
          php-version: "8.4"
          extensions: dom, curl, libxml, mbstring, zip
          coverage: none

      - name: Install dependencies
        run: composer install --prefer-dist --no-interaction

      - name: Run static analysis
        run: vendor/bin/phpstan

Responding to Dependabot Update PRs

Handle Dependabot’s automated update PRs appropriately:

Single Action Updates

Bump shivammathur/setup-php from v1 to v2
For straightforward updates:
  1. Review workflow execution results
  2. Check for breaking changes
  3. Merge and close

Security Update PRs

[SECURITY] Bump actions/checkout to a5ac7e51b41094c7fcab2042361574b0021804ab
Prioritize security fixes and merge immediately.

Grouped Multi-Action Updates

With groups configured in dependabot.yml, multiple actions update in a single PR:
groups:
  github-actions:
    patterns:
      - "*"
Grouping reduces merge overhead.

Benefits and Tradeoffs

Benefits

BenefitDescription
Supply chain protectionReferences specific commit hashes, resistant to action tampering
AuditabilityTrack exactly when and to what version each action was updated
Explicit updatesDependabot requires human review before changes
ReproducibilitySame commit hash = identical environment every run

Tradeoffs

TradeoffSolution
Initial manual setupUse pinact for automation
Update overheadDependabot automates PR creation, minimizing work
ReadabilityInclude version in comments for clarity

Security Audit Checklist

Use this checklist when starting a new package project:
  • Create .github/dependabot.yml
  • Pin all existing actions to SHA
  • Verify with pinact or manually
  • Confirm workflows execute successfully
  • Review Dependabot update PRs weekly
  • Prioritize security updates for immediate merge
  • Pin new actions to SHA when added
  • Monthly: Review all workflow status
  • All action references are SHA-based
  • Dependabot is enabled
  • Last 6 months of Dependabot PRs are merged

Package Development Basics

Learn about Laravel package development with service providers at the core.

Package Version Compatibility

Explore strategies for supporting multiple Laravel versions in your package.
Last modified on June 13, 2026