1919
2020permissions :
2121 contents : write
22+ id-token : write
23+ attestations : write
2224
2325jobs :
2426 # ── Step 1: Lint (clang-format + cppcheck) ───────────────────
@@ -343,6 +345,8 @@ jobs:
343345 runs-on : ubuntu-latest
344346 permissions :
345347 contents : write
348+ id-token : write
349+ attestations : write
346350 steps :
347351 - uses : actions/checkout@v4
348352
@@ -356,6 +360,72 @@ jobs:
356360 - name : Generate checksums
357361 run : sha256sum *.tar.gz *.zip > checksums.txt
358362
363+ # ── Artifact attestations (SLSA provenance) ──────────────
364+ # Cryptographically proves each binary was built by this
365+ # GitHub Actions workflow from this repo. Verifiable with:
366+ # gh attestation verify <file> --repo DeusData/codebase-memory-mcp
367+ - name : Attest build provenance (tar.gz)
368+ uses : actions/attest-build-provenance@v2
369+ with :
370+ subject-path : ' *.tar.gz'
371+
372+ - name : Attest build provenance (zip)
373+ uses : actions/attest-build-provenance@v2
374+ with :
375+ subject-path : ' *.zip'
376+
377+ - name : Attest build provenance (checksums)
378+ uses : actions/attest-build-provenance@v2
379+ with :
380+ subject-path : ' checksums.txt'
381+
382+ # ── SBOM generation ──────────────────────────────────────
383+ # Software Bill of Materials listing all vendored dependencies.
384+ - name : Generate SBOM
385+ run : |
386+ cat > sbom.json << 'SBOMEOF'
387+ {
388+ "bomFormat": "CycloneDX",
389+ "specVersion": "1.4",
390+ "version": 1,
391+ "metadata": {
392+ "component": {
393+ "type": "application",
394+ "name": "codebase-memory-mcp",
395+ "version": "${{ inputs.version }}"
396+ }
397+ },
398+ "components": [
399+ {"type": "library", "name": "sqlite3", "version": "3.49.1", "description": "Vendored SQLite amalgamation"},
400+ {"type": "library", "name": "yyjson", "version": "0.10.0", "description": "Fast JSON parser"},
401+ {"type": "library", "name": "mongoose", "version": "7.16", "description": "Embedded HTTP server"},
402+ {"type": "library", "name": "mimalloc", "version": "2.1.7", "description": "Memory allocator"},
403+ {"type": "library", "name": "xxhash", "version": "0.8.2", "description": "Fast hash function"},
404+ {"type": "library", "name": "tre", "version": "0.8.0", "description": "POSIX regex (Windows)"},
405+ {"type": "library", "name": "tree-sitter", "version": "0.24.4", "description": "AST parser runtime (64 grammars)"}
406+ ]
407+ }
408+ SBOMEOF
409+
410+ - name : Attest SBOM
411+ uses : actions/attest-sbom@v2
412+ with :
413+ subject-path : ' *.tar.gz'
414+ sbom-path : ' sbom.json'
415+
416+ # ── Sigstore cosign signing ──────────────────────────────
417+ # Keyless signing via GitHub OIDC identity. Verifiable with:
418+ # cosign verify-blob --bundle <file>.bundle <file>
419+ - name : Install cosign
420+ uses : sigstore/cosign-installer@v3
421+
422+ - name : Sign release artifacts with cosign
423+ run : |
424+ for f in *.tar.gz *.zip checksums.txt; do
425+ cosign sign-blob --yes --bundle "${f}.bundle" "$f"
426+ done
427+
428+ # ── Create release ───────────────────────────────────────
359429 - name : Delete existing release
360430 if : ${{ inputs.replace }}
361431 env :
@@ -377,23 +447,34 @@ jobs:
377447 *.tar.gz
378448 *.zip
379449 checksums.txt
450+ sbom.json
451+ *.bundle
380452 body : ${{ inputs.release_notes || '' }}
381453 generate_release_notes : ${{ inputs.release_notes == '' }}
382454
383- # ── Step 6: VirusTotal scan all release binaries ─────────────
384- virustotal :
455+ # ── Step 6: Post-release security verification ───────────────
456+ # Scans binaries with VirusTotal, runs OpenSSF Scorecard,
457+ # and appends all results to the release notes.
458+ verify :
385459 needs : [release]
386460 runs-on : ubuntu-latest
387461 permissions :
388462 contents : write
463+ security-events : write
464+ id-token : write
389465 steps :
390- - name : Download release assets
466+ - uses : actions/checkout@v4
467+ with :
468+ persist-credentials : false
469+
470+ # ── VirusTotal scan ──────────────────────────────────────
471+ - name : Download release binaries
391472 env :
392473 GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
393474 VERSION : ${{ inputs.version }}
394475 run : |
395476 mkdir -p assets
396- gh release download "$VERSION" --dir assets --repo "$GITHUB_REPOSITORY"
477+ gh release download "$VERSION" --dir assets --repo "$GITHUB_REPOSITORY" --pattern '*.tar.gz' --pattern '*.zip'
397478 ls -la assets/
398479
399480 - name : Scan all binaries with VirusTotal
@@ -405,34 +486,75 @@ jobs:
405486 assets/*.tar.gz
406487 assets/*.zip
407488
408- - name : Parse scan results and check for detections
489+ # ── OpenSSF Scorecard ────────────────────────────────────
490+ - name : Run OpenSSF Scorecard
491+ uses : ossf/scorecard-action@v2
492+ id : scorecard
493+ with :
494+ results_file : scorecard.sarif
495+ results_format : sarif
496+ publish_results : true
497+
498+ - name : Upload Scorecard SARIF
499+ uses : github/codeql-action/upload-sarif@v3
500+ with :
501+ sarif_file : scorecard.sarif
502+
503+ - name : Extract Scorecard score
504+ id : score
505+ run : |
506+ # Extract overall score from SARIF (properties.score)
507+ SCORE=$(python3 -c "
508+ import json, sys
509+ with open('scorecard.sarif') as f:
510+ d = json.load(f)
511+ props = d.get('runs', [{}])[0].get('tool', {}).get('driver', {}).get('properties', {})
512+ print(props.get('score', 'N/A'))
513+ " 2>/dev/null || echo "N/A")
514+ echo "score=$SCORE" >> "$GITHUB_OUTPUT"
515+ echo "OpenSSF Scorecard: $SCORE/10"
516+
517+ # ── Append all results to release notes ──────────────────
518+ - name : Append security verification to release notes
409519 env :
410520 VT_ANALYSIS : ${{ steps.virustotal.outputs.analysis }}
521+ SCORECARD_SCORE : ${{ steps.score.outputs.score }}
411522 GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
412523 VERSION : ${{ inputs.version }}
413524 run : |
414- echo "=== VirusTotal Scan Results ==="
415- echo "$VT_ANALYSIS"
525+ echo "=== Building security verification report ==="
416526
417- # Build markdown table for release notes
418- VT_REPORT="---\n\n### VirusTotal Scan Results\n\n"
419- VT_REPORT+="All release binaries were scanned by [VirusTotal](https://www.virustotal.com/) (70+ antivirus engines).\n\n"
420- VT_REPORT+="| Binary | Scan |\n|--------|------|\n"
527+ REPORT=$'---\n\n### Security Verification\n\n'
528+ REPORT+=$'All release binaries have been independently verified:\n\n'
421529
422- FAILED=false
530+ # VirusTotal results
531+ REPORT+=$'**VirusTotal** — scanned by 70+ antivirus engines:\n\n'
532+ REPORT+=$'| Binary | Scan |\n|--------|------|\n'
423533 while IFS= read -r line; do
424534 [ -z "$line" ] && continue
425- # Format: filename=analysisURL
426535 FILE=$(echo "$line" | cut -d'=' -f1)
427536 URL=$(echo "$line" | cut -d'=' -f2-)
428537 BASENAME=$(basename "$FILE")
429- VT_REPORT +="| $BASENAME | [View Report]($URL) |\n"
538+ REPORT +="| $BASENAME | [View Report]($URL) |"$'\n'
430539 done <<< "$VT_ANALYSIS"
431540
541+ # OpenSSF Scorecard
542+ REPORT+=$'\n**OpenSSF Scorecard** — repository security health: **'"$SCORECARD_SCORE"$'/10**\n'
543+ REPORT+=$'[View detailed scorecard](https://scorecard.dev/viewer/?uri=github.com/DeusData/codebase-memory-mcp)\n\n'
544+
545+ # Build provenance
546+ REPORT+=$'**Build Provenance (SLSA)** — cryptographic proof each binary was built by GitHub Actions from this repo:\n'
547+ REPORT+=$'```\ngh attestation verify <downloaded-file> --repo DeusData/codebase-memory-mcp\n```\n\n'
548+
549+ # Cosign
550+ REPORT+=$'**Sigstore cosign** — keyless signature verification:\n'
551+ REPORT+=$'```\ncosign verify-blob --bundle <file>.bundle <file>\n```\n\n'
552+
553+ # SBOM
554+ REPORT+=$'**SBOM** — Software Bill of Materials (`sbom.json`) lists all vendored dependencies.\n'
555+
432556 # Append to release notes
433557 EXISTING=$(gh release view "$VERSION" --json body --jq '.body' --repo "$GITHUB_REPOSITORY")
434- UPDATED="${EXISTING}\n\n${VT_REPORT}"
435- echo -e "$UPDATED" | gh release edit "$VERSION" --notes-file - --repo "$GITHUB_REPOSITORY"
558+ printf '%s\n\n%s\n' "$EXISTING" "$REPORT" | gh release edit "$VERSION" --notes-file - --repo "$GITHUB_REPOSITORY"
436559
437- echo ""
438- echo "=== Scan links appended to release notes ==="
560+ echo "=== Security verification appended to release notes ==="
0 commit comments