The Supply Chain Security Imperative

The specter of supply chain attacks looms large in software development. A stark reminder came in late 2025 when maintainer accounts for popular npm packages like chalk and debug were compromised. Malicious versions were published and remained live for approximately two hours. For any CI/CD pipeline running an installation during that window, the consequences could have been severe. This incident, and others like it, cemented the prevailing wisdom: pin all external dependencies, including GitHub Actions, to immutable commit SHAs. This is excellent advice for third-party actions, and it's a practice I rigorously follow for actions used in projects like CVE Lite CLI.

The Case for Not Pinning Your Own Actions

However, I deliberately do not pin my own GitHub Actions to specific commit SHAs. This isn't an oversight; it's a conscious decision based on a different risk calculus. When a contributor suggested pinning actions/checkout to a SHA in a README example, it highlighted the common understanding of security best practices. While correct for external dependencies, the dynamic changes when you control both the dependency and the consuming workflow.

Consider the alternative: using a branch or tag. If you push a fix or an update to your own action, and your workflows reference the main branch or a stable tag, those changes are immediately available. This allows for rapid iteration and bug fixing. If a critical bug is discovered in your action, you can push a fix and have it live across all consuming workflows within minutes, assuming they are configured to pull from the main branch. This agility is invaluable.

The risk of a malicious commit being introduced into your own repository is significantly lower than that of a third-party dependency. You control the codebase, the commit process, and the review. While not impossible, a malicious commit would likely require a compromised maintainer account or a sophisticated internal attack. In such scenarios, pinning to a SHA might offer only marginal protection, as the attacker could potentially push malicious code to multiple SHAs or branches.

Furthermore, pinning to SHAs introduces a significant maintenance burden. Every time you update your own action, you must remember to update the SHA reference in every workflow that uses it. This can be particularly cumbersome for larger projects or organizations with many repositories. This manual process is error-prone and can lead to developers running outdated versions of actions, potentially missing out on new features or security patches that aren't critical enough to warrant an immediate SHA update but are still beneficial.

The argument for pinning is strong when you cannot fully trust the source or the integrity of the entire supply chain. When it's your own action, you are the source, and you manage the integrity. This control allows for a different approach to risk management.

The Reviewer's Catch and the Nuance of Trust

The PR that suggested pinning actions/checkout to a SHA was a good catch, reinforcing the standard security posture. It demonstrates a healthy skepticism towards dependencies. But this skepticism must be nuanced. Trust isn't binary; it's contextual. I trust my own team and my own code review processes to a degree that allows for this flexibility. It's akin to trusting your own kitchen to prepare food safely, versus eating at a restaurant where you don't know the kitchen's practices. You'd likely be more cautious at the restaurant.

The primary benefit of pinning to a SHA is immutability. It guarantees that the exact code executed today is the exact code that will be executed tomorrow, regardless of any subsequent changes. This is crucial for reproducibility and security when dealing with external, opaque dependencies. For internal actions, the reproducibility is managed by your version control system and your team's development practices. The immediate availability of fixes and updates often outweighs the marginal security gain of SHA pinning for self-managed actions.

What isn't often discussed is the trade-off between absolute security and development velocity. For critical production systems, strict SHA pinning for all dependencies, including internal ones, might be the correct choice. This ensures that no unexpected changes, however well-intentioned, can alter the deployed artifact. However, for many development workflows, internal tools, and less critical pipelines, the ability to quickly deploy fixes and improvements to self-managed actions provides a more tangible benefit.

Alternative Strategies for Internal Actions

If you're concerned about the security of your own actions but want to retain agility, consider these strategies:

  • Strict Branch Protection Rules: Enforce mandatory code reviews and status checks on all commits to the main branch of your action repository. This acts as a powerful gatekeeper against malicious or erroneous changes.
  • Tagging Strategy: Use semantic versioning tags for your actions (e.g., v1.0.0, v1.1.0). While not as immutable as SHAs, tags provide a stable reference point. You can then choose to pin to specific tags in your workflows, offering a balance between stability and ease of update. When a bug is found, you tag a new version.
  • Regular Audits: Periodically review your internal actions, their dependencies, and their commit history. Treat them with the same scrutiny you would a third-party dependency, even if you aren't pinning to SHAs.
  • Automated Dependency Scanning: Ensure that your internal actions also benefit from automated security scanning tools that can detect vulnerabilities in their own dependencies or code.

Ultimately, the decision to pin or not to pin your own GitHub Actions to a SHA is a judgment call based on your team's risk tolerance, development practices, and the criticality of the workflows involved. For many, the agility and ease of maintenance offered by referencing the main branch or a stable tag can be a more practical approach, provided robust internal review processes are in place.