Skip to content

feat: add EU Cyber Resilience Act compliance features#407

Open
eins78 wants to merge 5 commits intomainfrom
claude/cyber-resilience-act-planning-01Li39jTr4TLKPSkeRzMNDTZ
Open

feat: add EU Cyber Resilience Act compliance features#407
eins78 wants to merge 5 commits intomainfrom
claude/cyber-resilience-act-planning-01Li39jTr4TLKPSkeRzMNDTZ

Conversation

@eins78
Copy link
Copy Markdown
Owner

@eins78 eins78 commented Nov 19, 2025

Implement supply chain security measures for CRA compliance:

  • SBOM generation in CycloneDX and SPDX formats using Syft
  • Vulnerability scanning with Grype
  • GitHub artifact attestations for SLSA L2 provenance
  • SBOM attestations for build transparency
  • Cosign keyless signing for GHCR and Docker Hub images
  • SOURCE_DATE_EPOCH support for reproducible builds

The SBOM and attestation features provide the required documentation
for EU CRA compliance (enforcement December 2027).

Implement supply chain security measures for CRA compliance:
- SBOM generation in CycloneDX and SPDX formats using Syft
- Vulnerability scanning with Grype
- GitHub artifact attestations for SLSA L2 provenance
- SBOM attestations for build transparency
- Cosign keyless signing for GHCR and Docker Hub images
- SOURCE_DATE_EPOCH support for reproducible builds

The SBOM and attestation features provide the required documentation
for EU CRA compliance (enforcement December 2027).
@github-actions
Copy link
Copy Markdown

github-actions bot commented Nov 19, 2025

⚠️ Duplicate Dependencies (threshold: 1)

📦 Package 📋 Versions
@types/node
2 versions
@types/node@16.18.126
  • root@
    • @types/node@22.19.1

globals
2 versions
  • root@
    • @eslint/compat@1.4.1
      • eslint@9.39.1
        • @eslint/eslintrc@3.3.1
          • globals@14.0.0

  • root@
    • eslint-plugin-playwright@2.3.0
      • globals@16.5.0

wait-on
2 versions
  • root@
    • start-server-and-test@2.1.2
      • wait-on@8.0.5

  • root@
    • wait-on@9.0.3

@cucumber/gherkin
2 versions
@cucumber/gherkin@31.0.0
@cucumber/gherkin@32.2.0
@cucumber/messages
2 versions
@cucumber/messages@26.0.1
@cucumber/messages@27.2.0
uuid
2 versions
uuid@10.0.0
uuid@11.0.5
eslint-visitor-keys
2 versions
  • root@
    • @eslint/compat@1.4.1
      • eslint@9.39.1
        • @eslint-community/eslint-utils@4.9.0
          • eslint-visitor-keys@3.4.3

  • root@
    • @eslint/compat@1.4.1
      • eslint@9.39.1
        • @eslint/eslintrc@3.3.1
          • espree@10.4.0
            • eslint-visitor-keys@4.2.1

debug
2 versions
debug@2.6.9
  • root@
    • @eslint/compat@1.4.1
      • eslint@9.39.1
        • @eslint/config-array@0.21.1
          • debug@4.4.3

minimatch
3 versions
minimatch@10.1.1
  • root@
    • @eslint/compat@1.4.1
      • eslint@9.39.1
        • @eslint/config-array@0.21.1
          • minimatch@3.1.2

  • root@
    • @typescript-eslint/eslint-plugin@8.47.0
      • @typescript-eslint/parser@8.47.0
        • @typescript-eslint/typescript-estree@8.47.0
          • minimatch@9.0.5

ignore
2 versions
  • root@
    • @eslint/compat@1.4.1
      • eslint@9.39.1
        • @eslint/eslintrc@3.3.1
          • ignore@5.3.2

  • root@
    • @typescript-eslint/eslint-plugin@8.47.0
      • ignore@7.0.5

strip-json-comments
2 versions
  • root@
    • @eslint/compat@1.4.1
      • eslint@9.39.1
        • @eslint/eslintrc@3.3.1
          • strip-json-comments@3.1.1

  • root@
    • knip@5.69.1
      • strip-json-comments@5.0.2

string-width
2 versions
string-width@4.2.3
string-width@5.1.2
strip-ansi
2 versions
strip-ansi@6.0.1
strip-ansi@7.1.2
wrap-ansi
2 versions
wrap-ansi@7.0.0
wrap-ansi@8.1.0
picomatch
2 versions
  • root@
    • @typescript-eslint/eslint-plugin@8.47.0
      • ...
        • fast-glob@3.3.3
          • micromatch@4.0.8
            • picomatch@2.3.1

  • root@
    • knip@5.69.1
      • picomatch@4.0.3

resolve
2 versions
resolve@1.22.10
resolve@1.22.11
@types/send
2 versions
@types/send@0.17.6
@types/send@1.2.1
mime-types
2 versions
  • root@
    • start-server-and-test@2.1.2
      • wait-on@8.0.5
        • axios@1.13.2
          • form-data@4.0.5
            • mime-types@2.1.35

mime-types@3.0.1
ansi-styles
2 versions
  • root@
    • @eslint/compat@1.4.1
      • eslint@9.39.1
        • chalk@4.1.2
          • ansi-styles@4.3.0

  • root@
    • npm-run-all2@8.0.4
      • ansi-styles@6.2.3

safe-buffer
2 versions
safe-buffer@5.1.2
safe-buffer@5.2.1
on-finished
2 versions
on-finished@2.3.0
on-finished@2.4.1
brace-expansion
2 versions
  • root@
    • @eslint/compat@1.4.1
      • eslint@9.39.1
        • @eslint/config-array@0.21.1
          • minimatch@3.1.2
            • brace-expansion@1.1.12

  • root@
    • @typescript-eslint/eslint-plugin@8.47.0
      • @typescript-eslint/parser@8.47.0
        • @typescript-eslint/typescript-estree@8.47.0
          • minimatch@9.0.5
            • brace-expansion@2.0.2

supports-color
2 versions
  • root@
    • @eslint/compat@1.4.1
      • eslint@9.39.1
        • @eslint/config-array@0.21.1
          • debug@4.4.3
            • supports-color@5.5.0

  • root@
    • @eslint/compat@1.4.1
      • eslint@9.39.1
        • chalk@4.1.2
          • supports-color@7.2.0

glob-parent
2 versions
  • root@
    • @typescript-eslint/eslint-plugin@8.47.0
      • @typescript-eslint/parser@8.47.0
        • @typescript-eslint/typescript-estree@8.47.0
          • fast-glob@3.3.3
            • glob-parent@5.1.2

  • root@
    • @eslint/compat@1.4.1
      • eslint@9.39.1
        • glob-parent@6.0.2

fsevents
2 versions
fsevents@2.3.2
fsevents@2.3.3
cookie-signature
2 versions
cookie-signature@1.0.6
cookie-signature@1.2.2
which
2 versions
  • root@
    • @eslint/compat@1.4.1
      • eslint@9.39.1
        • cross-spawn@7.0.6
          • which@2.0.2

  • root@
    • npm-run-all2@8.0.4
      • which@5.0.0

ms
2 versions
ms@2.0.0
  • root@
    • @eslint/compat@1.4.1
      • eslint@9.39.1
        • @eslint/config-array@0.21.1
          • debug@4.4.3
            • ms@2.1.3

signal-exit
2 versions
  • root@
    • start-server-and-test@2.1.2
      • execa@5.1.1
        • signal-exit@3.0.7

signal-exit@4.1.0
mime-db
2 versions
  • root@
    • start-server-and-test@2.1.2
      • ...
        • form-data@4.0.5
          • mime-types@2.1.35
            • mime-db@1.52.0

mime-db@1.54.0
emoji-regex
2 versions
emoji-regex@8.0.0
emoji-regex@9.2.2
ansi-regex
2 versions
ansi-regex@5.0.1
ansi-regex@6.2.2
has-flag
2 versions
  • root@
    • @eslint/compat@1.4.1
      • ...
        • debug@4.4.3
          • supports-color@5.5.0
            • has-flag@3.0.0

  • root@
    • @eslint/compat@1.4.1
      • eslint@9.39.1
        • chalk@4.1.2
          • supports-color@7.2.0
            • has-flag@4.0.0

isexe
2 versions
  • root@
    • @eslint/compat@1.4.1
      • eslint@9.39.1
        • cross-spawn@7.0.6
          • which@2.0.2
            • isexe@2.0.0

  • root@
    • npm-run-all2@8.0.4
      • which@5.0.0
        • isexe@3.1.1

💡 To find out what depends on a specific package, run: pnpm -r why example-package

@claude
Copy link
Copy Markdown

claude bot commented Nov 19, 2025

PR Review: EU Cyber Resilience Act Compliance Features

CI Status: ❌ CI failures detected (waited ~1.5 min)

  • Failed: docker-build
  • Passed: test, e2e-tests (chromium/firefox/webkit), docker-e2e, dependency-diff

CI Failure Analysis

The docker-build workflow failed. Based on the workflow changes, the most likely cause is the SOURCE_DATE_EPOCH build argument on line 134:

build-args: |
  SOURCE_DATE_EPOCH=${{ github.event.repository.pushed_at }}

Issue: github.event.repository.pushed_at may be empty or undefined for pull request events. This would pass an empty value to the Docker build, potentially causing issues.

Suggested fix:

build-args: |
  SOURCE_DATE_EPOCH=${{ github.event.repository.pushed_at || github.event.pull_request.head.sha }}

Or use a more reliable timestamp:

build-args: |
  SOURCE_DATE_EPOCH=${{ github.run_id }}

Code Review

Strengths

  1. Comprehensive CRA compliance approach - Good coverage of SBOM generation (both CycloneDX and SPDX), vulnerability scanning, attestations, and signing.

  2. Correct conditional execution - All new steps properly use if: github.event_name != 'pull_request' to skip on PRs, avoiding permission issues and unnecessary work.

  3. Well-structured workflow sections - Clear separation between SBOM generation and supply chain security with helpful comments.

  4. Dockerfile improvements - Adding SOURCE_DATE_EPOCH support for reproducible builds is a good practice.

Issues to Address

  1. Missing Docker Hub attestation (lines 206-210)

    You sign the Docker Hub image with Cosign but don't attest build provenance or SBOM for Docker Hub - only for GHCR. If you need attestations for Docker Hub too, add:

    - name: Attest build provenance (Docker Hub)
      uses: actions/attest-build-provenance@v2
      if: github.event_name != 'pull_request'
      with:
        subject-name: docker.io/${{ github.repository }}
        subject-digest: ${{ steps.build.outputs.digest }}
        push-to-registry: true
  2. Missing GAR (Google Artifact Registry) signing

    You sign GHCR and Docker Hub images but not GAR images. Consider adding:

    - name: Sign container image with Cosign (GAR)
      if: github.event_name != 'pull_request'
      run: |
        cosign sign --yes \
          ${{ secrets.GAR_LOCATION }}-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/${{ secrets.GAR_REPOSITORY }}/hello-world-web@${{ steps.build.outputs.digest }}
  3. Action versions should use SHA pinning (security best practice per CLAUDE.md)

    The new actions use version tags (@v0, @v2, @v3, @v6) instead of SHA pinning:

    • anchore/sbom-action@v0
    • anchore/scan-action@v6
    • actions/attest-build-provenance@v2
    • actions/attest-sbom@v2
    • sigstore/cosign-installer@v3
    • softprops/action-gh-release@v2

    Consider pinning to specific commit SHAs for supply chain security.

  4. Vulnerability scan doesn't upload results

    The scan-action generates SARIF output but it's not uploaded to GitHub Security. Add:

    - name: Upload vulnerability scan results
      uses: github/codeql-action/upload-sarif@v3
      if: github.event_name != 'pull_request'
      with:
        sarif_file: results.sarif

Minor Suggestions

  1. Dockerfile comment accuracy - The comment mentions "Uses lockfile for deterministic dependency installation" but this was already in place; the PR only adds SOURCE_DATE_EPOCH support.

  2. Consider adding artifact retention - The SBOM files are uploaded as artifacts but will be deleted after the default retention period. Consider explicit retention for compliance audits.


Summary

This is a well-intentioned PR that adds important EU CRA compliance features. However, the CI failure needs to be resolved first. After fixing the SOURCE_DATE_EPOCH issue, I recommend:

  1. Priority: Fix the CI failure (likely the SOURCE_DATE_EPOCH build arg)
  2. Should have: Add missing GAR image signing for consistency
  3. Nice to have: Pin action versions to SHAs, upload vulnerability scan results

Once CI passes, this will be a solid foundation for CRA compliance.

🤖 Generated with Claude Code

Document the supply chain security features implemented for CRA compliance:
- SBOM generation with Syft (CycloneDX and SPDX)
- Vulnerability scanning with Grype
- GitHub artifact attestations (SLSA L2)
- Cosign keyless signing
- Verification commands and architecture overview
The pushed_at field returns an ISO 8601 string but SOURCE_DATE_EPOCH
requires a Unix timestamp (integer). Convert using date command.
@claude
Copy link
Copy Markdown

claude bot commented Nov 19, 2025

Code Review: EU Cyber Resilience Act Compliance Features

All CI checks passed (waited <1 min)

Summary

This PR adds supply chain security features for EU CRA compliance including SBOM generation, vulnerability scanning, attestations, and container signing. The implementation is well-structured and follows security best practices.

Positives

  1. Comprehensive SBOM support: Generating both CycloneDX and SPDX formats covers different compliance requirements
  2. Conditional execution: Using if: github.event_name != 'pull_request' avoids unnecessary work on PRs
  3. Keyless signing: Cosign with OIDC tokens eliminates key management overhead
  4. Documentation: The eu-cra-compliance.md is thorough with verification examples
  5. Reproducible builds: SOURCE_DATE_EPOCH support is a nice addition

Suggestions

Workflow improvements:

  1. Pin action versions - anchore/sbom-action@v0 should use a specific SHA for supply chain security. Same applies to anchore/scan-action@v6 and sigstore/cosign-installer@v3

  2. Docker Hub signing may fail - The digest from docker/build-push-action is the manifest list digest. When pushing to multiple registries, the digest may differ. Consider adding continue-on-error: true or verifying Docker Hub push works correctly

  3. SARIF upload missing - The vulnerability scan generates SARIF output but does not upload it to GitHub Security tab using github/codeql-action/upload-sarif@v3

Minor observations:

  1. SOURCE_DATE_EPOCH calculation - Using pushed_at from the event may not be available for all event types. Consider falling back to commit timestamp with git log -1 --format=%ct

  2. GAR attestations - The architecture diagram mentions GAR (Google Artifact Registry) but attestations/signing only target GHCR and Docker Hub

Security Considerations

  • The contents: write permission is needed for release assets but is a broader permission - this is acceptable given the use case
  • The attestations: write permission is correctly scoped

Documentation

The documentation is excellent and provides clear verification instructions. One minor note: line numbers in the "Workflow File Reference" section will drift as the file changes - consider removing them or using anchors.


Verdict: This is a solid implementation of CRA compliance features. The suggestions above are improvements but not blockers. Ready to merge once the action pinning is addressed for better supply chain security (which is ironic for a supply chain security PR!).

@claude
Copy link
Copy Markdown

claude bot commented Nov 19, 2025

PR Review: EU Cyber Resilience Act Compliance Features

All CI checks passed (waited ~2 min)

Summary

This PR implements comprehensive supply chain security measures for EU CRA compliance, including SBOM generation, vulnerability scanning, artifact attestations, and container image signing. The implementation is well-structured and follows security best practices.

Positive Aspects

  1. Excellent documentation - The docs/eu-cra-compliance.md is thorough, includes verification commands, and explains both what provenance proves and what it doesn't.

  2. Multi-format SBOM support - Generating both CycloneDX (security-focused) and SPDX (ISO standard) formats provides flexibility for different compliance scenarios.

  3. Proper conditional execution - Using if: github.event_name != 'pull_request' prevents security tools from running on PRs while still testing the build itself.

  4. Reproducible build support - The SOURCE_DATE_EPOCH implementation enables deterministic builds.

Suggestions and Issues

1. Permission Scope Concern

The contents: write permission is needed for softprops/action-gh-release, but this is a broad permission. Consider documenting this explicitly in the workflow comments to clarify why write access is needed.

2. Date Parsing Potential Issue

The pushed_at timestamp format may vary. Consider adding error handling or using a more reliable timestamp source like github.event.head_commit.timestamp for push events.

3. Missing Error Handling for Cosign

The Cosign signing steps dont have error handling. If Docker Hub doesnt have the image (e.g., if earlier push failed), the workflow will fail. Consider adding continue-on-error: true or a proper if condition.

4. Vulnerability Scanning Configuration

The fail-build: false setting is reasonable for initial implementation, but consider documenting a timeline for when to enable fail-build: true once the baseline is clean.

5. Version Pinning

The Anchore actions use @v0 and @v6 which may introduce breaking changes. Consider pinning to specific versions or SHA for production stability.

Security Considerations

  • Uses keyless signing (no secrets to manage)
  • Attestations pushed to registry for verification
  • SLSA L2 provenance claims are appropriate
  • Consider adding SARIF upload to GitHub Security tab for vulnerability visibility

Minor Nits

  1. Line references in documentation may become stale as the file evolves
  2. The architecture diagram is helpful - consider adding it to the main README

Verdict

Approve - This is a well-implemented feature that adds significant value for EU CRA compliance. The suggestions above are improvements rather than blockers. The code is clean, well-documented, and follows security best practices.


Generated with Claude Code

@claude
Copy link
Copy Markdown

claude bot commented Nov 19, 2025

Code Review: EU CRA Compliance Features

All CI checks passed (waited ~1.5 min)

Overall Assessment

This is a well-structured PR that adds comprehensive supply chain security features for EU Cyber Resilience Act compliance. The implementation follows security best practices and uses established tools from the Sigstore ecosystem.

Strengths

  1. Complete SBOM Coverage: Generates both CycloneDX and SPDX formats
  2. Good Conditional Logic: All new steps correctly skip PR builds
  3. Proper Permission Scoping: Permissions are correctly expanded only as needed
  4. Excellent Documentation: The eu-cra-compliance.md is thorough
  5. Reproducible Build Support: Using SOURCE_DATE_EPOCH is a good practice

Suggestions for Improvement

  1. Pin Action Versions with SHA - The PR uses mutable tags like @v0, @v2. For supply chain security, consider SHA pinning.

  2. SOURCE_DATE_EPOCH Calculation - Consider using git log -1 --format=%ct for the commit timestamp instead of github.event.repository.pushed_at

  3. Docker Hub Signing May Fail - The signing step references docker.io but push is only to GHCR. This step may fail if Docker Hub push is not configured.

  4. SARIF Upload Missing - The vulnerability scan produces SARIF output but does not upload it to GitHub Security tab.

Minor Notes

  • Line references in eu-cra-compliance.md may drift as the file evolves
  • Grype output file not explicitly specified

Verdict

Good to merge with the considerations above. The PR adds valuable compliance features with solid implementation. The suggestions are improvements rather than blockers.

- Use git commit timestamp for SOURCE_DATE_EPOCH instead of pushed_at
- Add SARIF upload to GitHub Security tab for vulnerability scan results
- Add continue-on-error for Docker Hub signing when not configured
- Add security-events permission for SARIF upload
- Update documentation to use stable section references instead of line numbers
@claude
Copy link
Copy Markdown

claude bot commented Nov 19, 2025

Code Review: EU Cyber Resilience Act Compliance Features

All CI checks passed (waited ~2 min)

Summary

This PR adds comprehensive supply chain security features for EU CRA compliance. The implementation is well-structured and follows current best practices.

Positive Aspects

  1. Comprehensive SBOM coverage - Generates both CycloneDX (security-focused) and SPDX (ISO standard) formats
  2. Multi-layered security - Combines SBOM generation, vulnerability scanning, attestations, and signing
  3. Excellent documentation - The eu-cra-compliance.md provides clear verification instructions and context
  4. Sensible defaults - SBOM/attestation steps only run on non-PR builds to avoid noise
  5. Graceful degradation - Docker Hub signing uses continue-on-error for unconfigured setups
  6. Reproducible builds - SOURCE_DATE_EPOCH support is a nice touch for build reproducibility

Suggestions

Security Considerations

  1. Fail-build on critical vulnerabilities (.github/workflows/docker-image-publish.yml:166)

    fail-build: false  # Currently disabled

    Consider enabling this or at least setting a threshold. Currently all vulnerabilities are logged but won't block releases. For CRA compliance, you may want to fail on critical/high vulnerabilities before the December 2027 deadline.

  2. SARIF upload condition (line 173)

    if: github.event_name != 'pull_request' && always()

    The always() means it runs even when the scan step fails. Verify this is intentional - it might upload incomplete or missing SARIF files.

  3. Docker Hub signing secret check (line 219)

    if: github.event_name != 'pull_request' && secrets.DOCKERHUB_USERNAME != ''

    Note: This condition may not work as expected. In GitHub Actions, secrets.* in if conditions are always truthy (they evaluate to ***). Consider using a different approach like checking a context variable or making it a separate job that only runs when Docker Hub is configured.

Minor Improvements

  1. Pin action versions for security - Most actions are pinned (e.g., @v0, @v2, @v3), which is good, but consider using SHA-pinned versions for supply chain security:

    # Example: More secure
    uses: anchore/sbom-action@<SHA>
  2. SBOM upload condition - The softprops/action-gh-release step only runs for tags. Consider also uploading SBOMs as workflow artifacts for non-tag builds on main branch to ensure they're always available.

  3. Dockerfile comment length - The header comment in the Dockerfile is informative but somewhat long. Consider moving detailed compliance information to the documentation and keeping the Dockerfile comment brief.

Documentation Quality

The docs/eu-cra-compliance.md is excellent:

  • Clear timeline and penalty information
  • Well-structured verification commands
  • Good architecture diagram
  • Appropriate caveats about what attestations do/don't prove

Overall

This is a well-implemented feature that significantly improves the project's supply chain security posture. The code quality is high and the documentation is thorough. The suggestions above are mostly refinements rather than critical issues.

Recommendation: Approve with minor suggestions. Consider enabling fail-build: true for vulnerability scanning before the CRA enforcement deadline.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants