This hardening effort was directly triggered by the recent Trivy supply chain compromise, which impacted some repositories in my workplace and prompted a full review of my own repository controls.

If you want the incident and response details, these two links are worth reading first:

This post is an updated, end-to-end view of the hardening work I applied to this repository.

The aim was not to chase perfect security. It was to put in practical controls that are easy to maintain and that reduce real risk in day-to-day development.

Why Repository Hardening Matters

For a static site, most of the risk is in the repository and pipeline layers:

  • Dependency updates
  • CI workflows and action references
  • Build tool downloads
  • Merge controls and review gates
  • Secret leakage in commits

If those layers are weak, a clean application codebase still has exposure.

Hardening Measures Implemented

1. Automated Dependency Maintenance

Dependabot is configured for Hugo modules, GitHub Actions, and npm. This keeps dependency drift visible and turns updates into reviewable pull requests instead of surprise breakages.

2. Workflow Action Version Pinning and Updates

Workflows use pinned action versions rather than floating branch references. On top of that, Dependabot tracks Actions updates so the pinning stays current.

3. Reproducible Build Behaviour in CI

One important change was removing runtime dependency mutation in CI.

The build pipeline no longer runs hugo mod get -u during normal build execution. Instead it verifies and tidies module state, then fails if dependency files change unexpectedly.

This keeps builds predictable and avoids silent dependency drift.

4. Toolchain Version Alignment

Go and Hugo versions were aligned across workflows and the Amplify build. This reduces “works in one pipeline but not another” behaviour and makes failures easier to reason about.

5. Build Tool Integrity Checks in Amplify

The Amplify pipeline verifies checksums for downloaded build tools before extraction and execution.

That includes Dart Sass, Go, and Hugo release artifacts, with explicit fail-fast behaviour when verification does not match.

6. Dependency Verification Workflow Scope Fix

Dependency verification now runs on the branch patterns that matter for normal development, rather than old one-off hardening branches.

This keeps the guardrail active where merges actually happen.

7. Secret Scanning in CI

Gitleaks runs in CI for pushes and pull requests to main. This adds a baseline control for accidentally committed tokens, keys, and other sensitive strings.

8. Local Secret Scanning Before Commit

A pre-commit hook was added with Gitleaks so developers can catch most secret leaks before pushing code.

This is one of the cheapest controls to add and it saves cleanup time later.

9. SAST with Semgrep (Instead of CodeQL)

SAST means Static Application Security Testing. It analyses source code, configuration, and workflow files without executing the application, to catch potential security issues early in the development lifecycle. Popular SAST tools in current use include Semgrep, CodeQL, SonarQube, Snyk Code, and Checkmarx.

CodeQL is GitHub’s semantic code analysis engine. It converts code into a queryable database and runs security queries over that data model to identify potential vulnerabilities and insecure coding patterns. It is especially useful for code scanning workflows when repository and organisation permissions allow Code Scanning uploads.

Originally, the plan was to use CodeQL. In practice, organisation-level permissions overrode repository-level feature settings for my account context, so CodeQL upload features were unavailable for this repo setup.

I documented that permissions issue in detail here: When Your Workplace Controls Your Personal GitHub Repos: Understanding GitHub Org Policies.

Rather than leaving a gap, I switched to Semgrep in GitHub Actions. That kept static analysis coverage in place without relying on unavailable permissions.

Semgrep is a rule-based static analysis tool that scans source code and configuration files for insecure patterns. It is a strong alternative here because it is straightforward to run in CI, works well with pull request gating, and does not depend on GitHub Code Scanning upload permissions to be effective.

The key point: if your first tool is blocked by policy, keep the security objective and choose a workable alternative.

10. Dependency Review on Pull Requests

A dedicated dependency review workflow now checks dependency changes during PRs and fails on high severity findings.

11. OSSF Scorecard Monitoring

OSSF means the Open Source Security Foundation, a cross-industry initiative focused on improving open source software security practices.

A scheduled Scorecard workflow was added to track repository-level security posture over time.

Scorecard checks signals such as branch protection, workflow hardening, and dependency update hygiene, so it is useful as an ongoing posture check rather than a one-off audit.

This helps catch policy or workflow regressions early.

12. Governance Baseline

The repository includes:

  • SECURITY.md for responsible disclosure flow
  • CODEOWNERS for security-sensitive review ownership
  • Branch protection guidance for required reviews and checks

These controls are simple, but they establish clear accountability.

How These Controls Work Together

  ---
config:
  theme: dark
---
graph TD
    A[Developer changes code] --> B[Pre-commit secret scan]
    B --> C[Push or PR to main]
    C --> D[Dependency verification]
    C --> E[Secret scanning in CI]
    C --> F[Semgrep SAST]
    C --> G[Dependency review]
    D --> H{All required checks pass?}
    E --> H
    F --> H
    G --> H
    H -- Yes --> I[Merge allowed]
    H -- No --> J[Merge blocked]

This is not about any single control being perfect. It is about layered checks at different points in the development flow.

CodeQL to Semgrep: Practical Decision Flow

  ---
config:
  theme: dark
---
flowchart TD
    A[Goal: add SAST in CI] --> B[Try CodeQL]
    B --> C{Repository context permits Code Scanning uploads?}
    C -- Yes --> D[Use CodeQL]
    C -- No --> E[Switch to Semgrep workflow]
    E --> F[Keep SAST gate active in PR checks]

The practical lesson here is straightforward: define the security outcome first, then pick the implementation path that is viable in your permission model.

What I Would Recommend Next

If you are doing similar hardening on a personal or small-team repo, this sequence works well:

  1. Start with dependency hygiene and workflow pinning.
  2. Make CI builds deterministic.
  3. Add secret scanning and SAST.
  4. Add PR-time dependency review.
  5. Add scheduled posture checks like Scorecard.
  6. Enforce branch protection and code owner approvals.

You do not need to do everything in one day. Small, steady changes still improve your security posture significantly.


This is part of my repository security series. The goal is practical hardening that fits normal development workflows.