From dd30132e2de616a59c8d8f09c530f1bbdadc381f Mon Sep 17 00:00:00 2001 From: Martin Vogel Date: Sat, 21 Mar 2026 16:57:17 +0100 Subject: [PATCH] Pin Actions to SHA + enforce via security audit + Dependabot - All GitHub Actions pinned to immutable commit SHAs (prevents tag-poisoning attacks like tj-actions/changed-files incident) - Security audit (Layer 1) now blocks unpinned Actions in CI - Dependabot configured to auto-propose SHA updates weekly - Pre-commit hooks tracked in scripts/hooks/ for contributors - Time-bomb detection + MCP file read audit added to Layer 1 --- .github/dependabot.yml | 6 ++++ .github/workflows/dry-run.yml | 36 ++++++++++----------- .github/workflows/release.yml | 60 +++++++++++++++++------------------ .gitignore | 1 + scripts/security-audit.sh | 27 ++++++++++++++++ 5 files changed, 82 insertions(+), 48 deletions(-) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..5ace4600a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/dry-run.yml b/.github/workflows/dry-run.yml index f96cd10fa..4dc538804 100644 --- a/.github/workflows/dry-run.yml +++ b/.github/workflows/dry-run.yml @@ -25,7 +25,7 @@ jobs: if: ${{ !inputs.skip_lint }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Install build deps run: sudo apt-get update && sudo apt-get install -y zlib1g-dev cmake @@ -37,7 +37,7 @@ jobs: sudo apt-get update sudo apt-get install -y clang-format-20 - - uses: actions/cache@v4 + - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 id: cppcheck-cache with: path: /opt/cppcheck @@ -64,7 +64,7 @@ jobs: if: ${{ !inputs.skip_lint }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: "Layer 1: Static allow-list audit" run: scripts/security-audit.sh @@ -104,7 +104,7 @@ jobs: cxx: c++ runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Install deps (Ubuntu) if: startsWith(matrix.os, 'ubuntu') @@ -118,9 +118,9 @@ jobs: needs: [lint] runs-on: windows-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - uses: msys2/setup-msys2@v2 + - uses: msys2/setup-msys2@4f806de0a5a7294ffabaff804b38a9b435a73bda # v2 with: msystem: CLANG64 path-type: inherit @@ -163,13 +163,13 @@ jobs: cxx: c++ runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Install deps (Ubuntu) if: startsWith(matrix.os, 'ubuntu') run: sudo apt-get update && sudo apt-get install -y zlib1g-dev - - uses: actions/setup-node@v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: "22" @@ -193,7 +193,7 @@ jobs: tar -czf codebase-memory-mcp-ui-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz \ -C build/c codebase-memory-mcp - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: binaries-${{ matrix.goos }}-${{ matrix.goarch }} path: "*.tar.gz" @@ -203,9 +203,9 @@ jobs: needs: [test-unix, test-windows] runs-on: windows-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - uses: msys2/setup-msys2@v2 + - uses: msys2/setup-msys2@4f806de0a5a7294ffabaff804b38a9b435a73bda # v2 with: msystem: CLANG64 path-type: inherit @@ -215,7 +215,7 @@ jobs: make zip - - uses: actions/setup-node@v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: "22" @@ -243,7 +243,7 @@ jobs: cp "$BIN" codebase-memory-mcp-ui.exe zip codebase-memory-mcp-ui-windows-amd64.zip codebase-memory-mcp-ui.exe - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: binaries-windows-amd64 path: "*.zip" @@ -271,9 +271,9 @@ jobs: variant: [standard, ui] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: binaries-${{ matrix.goos }}-${{ matrix.goarch }} @@ -338,9 +338,9 @@ jobs: variant: [standard, ui] runs-on: windows-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - uses: msys2/setup-msys2@v2 + - uses: msys2/setup-msys2@4f806de0a5a7294ffabaff804b38a9b435a73bda # v2 with: msystem: CLANG64 path-type: inherit @@ -348,7 +348,7 @@ jobs: mingw-w64-clang-x86_64-python3 unzip - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: binaries-windows-amd64 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4ada636d5..c835318ef 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,7 +27,7 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Install build deps run: sudo apt-get update && sudo apt-get install -y zlib1g-dev cmake @@ -39,7 +39,7 @@ jobs: sudo apt-get update sudo apt-get install -y clang-format-20 - - uses: actions/cache@v4 + - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 id: cppcheck-cache with: path: /opt/cppcheck @@ -65,7 +65,7 @@ jobs: security-static: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: "Layer 1: Static allow-list audit" run: scripts/security-audit.sh @@ -104,7 +104,7 @@ jobs: cxx: c++ runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Install deps (Ubuntu) if: startsWith(matrix.os, 'ubuntu') @@ -117,9 +117,9 @@ jobs: needs: [lint] runs-on: windows-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - uses: msys2/setup-msys2@v2 + - uses: msys2/setup-msys2@4f806de0a5a7294ffabaff804b38a9b435a73bda # v2 with: msystem: CLANG64 path-type: inherit @@ -161,13 +161,13 @@ jobs: cxx: c++ runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Install deps (Ubuntu) if: startsWith(matrix.os, 'ubuntu') run: sudo apt-get update && sudo apt-get install -y zlib1g-dev - - uses: actions/setup-node@v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: "22" @@ -191,7 +191,7 @@ jobs: tar -czf codebase-memory-mcp-ui-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz \ -C build/c codebase-memory-mcp - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: binaries-${{ matrix.goos }}-${{ matrix.goarch }} path: "*.tar.gz" @@ -200,9 +200,9 @@ jobs: needs: [test-unix, test-windows] runs-on: windows-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - uses: msys2/setup-msys2@v2 + - uses: msys2/setup-msys2@4f806de0a5a7294ffabaff804b38a9b435a73bda # v2 with: msystem: CLANG64 path-type: inherit @@ -212,7 +212,7 @@ jobs: make zip - - uses: actions/setup-node@v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: "22" @@ -240,7 +240,7 @@ jobs: cp "$BIN" codebase-memory-mcp-ui.exe zip codebase-memory-mcp-ui-windows-amd64.zip codebase-memory-mcp-ui.exe - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: binaries-windows-amd64 path: "*.zip" @@ -266,9 +266,9 @@ jobs: variant: [standard, ui] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: binaries-${{ matrix.goos }}-${{ matrix.goarch }} @@ -334,9 +334,9 @@ jobs: variant: [standard, ui] runs-on: windows-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - uses: msys2/setup-msys2@v2 + - uses: msys2/setup-msys2@4f806de0a5a7294ffabaff804b38a9b435a73bda # v2 with: msystem: CLANG64 path-type: inherit @@ -344,7 +344,7 @@ jobs: mingw-w64-clang-x86_64-python3 unzip - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: binaries-windows-amd64 @@ -396,9 +396,9 @@ jobs: id-token: write attestations: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: merge-multiple: true @@ -410,17 +410,17 @@ jobs: # ── Artifact attestations (SLSA provenance) ────────────── - name: Attest build provenance (tar.gz) - uses: actions/attest-build-provenance@v2 + uses: actions/attest-build-provenance@96b4a1ef7235a096b17240c259729fdd70c83d45 # v2 with: subject-path: '*.tar.gz' - name: Attest build provenance (zip) - uses: actions/attest-build-provenance@v2 + uses: actions/attest-build-provenance@96b4a1ef7235a096b17240c259729fdd70c83d45 # v2 with: subject-path: '*.zip' - name: Attest build provenance (checksums) - uses: actions/attest-build-provenance@v2 + uses: actions/attest-build-provenance@96b4a1ef7235a096b17240c259729fdd70c83d45 # v2 with: subject-path: 'checksums.txt' @@ -434,14 +434,14 @@ jobs: python3 -c "import json; d=json.load(open('sbom.json')); json.dump(d,open('sbom.json','w'),indent=2)" - name: Attest SBOM - uses: actions/attest-sbom@v2 + uses: actions/attest-sbom@10926c72720ffc3f7b666661c8e55b1344e2a365 # v2 with: subject-path: '*.tar.gz' sbom-path: 'sbom.json' # ── Sigstore cosign signing ────────────────────────────── - name: Install cosign - uses: sigstore/cosign-installer@v3 + uses: sigstore/cosign-installer@f713795cb21599bc4e5c4b58cbad1da852d7eeb9 # v3 - name: Sign release artifacts with cosign run: | @@ -464,7 +464,7 @@ jobs: git tag -f "$VERSION" git push origin "$VERSION" --force - - uses: softprops/action-gh-release@v2 + - uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2 with: tag_name: ${{ inputs.version }} draft: true @@ -489,7 +489,7 @@ jobs: security-events: write id-token: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 with: persist-credentials: false @@ -504,7 +504,7 @@ jobs: ls -la assets/ - name: Scan all binaries with VirusTotal - uses: crazy-max/ghaction-virustotal@v4 + uses: crazy-max/ghaction-virustotal@d34968c958ae283fe976efed637081b9f9dcf74f # v4 id: virustotal with: vt_api_key: ${{ secrets.VIRUS_TOTAL_SCANNER_API_KEY }} @@ -607,7 +607,7 @@ jobs: # ── OpenSSF Scorecard ──────────────────────────────────── - name: Run OpenSSF Scorecard - uses: ossf/scorecard-action@v2 + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 id: scorecard with: results_file: scorecard.sarif @@ -615,7 +615,7 @@ jobs: publish_results: true - name: Upload Scorecard SARIF - uses: github/codeql-action/upload-sarif@v3 + uses: github/codeql-action/upload-sarif@38697555549f1db7851b81482ff19f1fa5c4fedc # v4 with: sarif_file: scorecard.sarif diff --git a/.gitignore b/.gitignore index e11b2be7c..85ea296ee 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,4 @@ graph-ui/dist/ BENCHMARK_REPORT.md TEST_PLAN.md CHANGELOG.md +.github/workflows/scorecard.yml diff --git a/scripts/security-audit.sh b/scripts/security-audit.sh index 96727cd70..04344d52a 100755 --- a/scripts/security-audit.sh +++ b/scripts/security-audit.sh @@ -253,6 +253,33 @@ if $MCP_READS_OK; then echo "OK: MCP tool handler file reads within expected count." fi +# ── 6. GitHub Actions pinned to SHA ─────────────────────────────── +# Mutable tags (@v4) can be moved by compromised maintainers. +# All Actions must be pinned to full commit SHAs. + +echo "" +echo "--- Scanning for unpinned GitHub Actions ---" + +UNPINNED_FOUND=false +if [ -d "$ROOT/.github/workflows" ]; then + while IFS= read -r file; do + relfile="${file#"$ROOT/"}" + # Only check files tracked by git (skip stale untracked files) + git ls-files --error-unmatch "$file" > /dev/null 2>&1 || continue + while IFS= read -r match; do + [[ -z "$match" ]] && continue + echo "BLOCKED: ${relfile}: ${match}" + echo " -> Pin to SHA: uses: owner/action@ # version" + fail + UNPINNED_FOUND=true + done < <(grep -nE 'uses:.*@v[0-9]' "$file" 2>/dev/null || true) + done < <(find "$ROOT/.github/workflows" -name '*.yml' -type f | sort) +fi + +if ! $UNPINNED_FOUND; then + echo "OK: All GitHub Actions pinned to commit SHAs." +fi + # ── Summary ────────────────────────────────────────────────────── echo ""