Security Main Branch #69
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Security Main Branch | |
| on: | |
| push: | |
| branches: [main] | |
| schedule: | |
| # Run daily at 02:00 UTC | |
| - cron: '0 2 * * *' | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| jobs: | |
| # Detect changes (skip for scheduled runs) | |
| changes: | |
| runs-on: ubuntu-latest | |
| if: github.event_name != 'schedule' | |
| permissions: | |
| pull-requests: read | |
| outputs: | |
| security: ${{ steps.filter.outputs.security }} | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 | |
| id: filter | |
| with: | |
| filters: | | |
| security: | |
| - 'package.json' | |
| - 'package-lock.json' | |
| - 'src/**' | |
| - '.github/workflows/**' | |
| # NPM audit (production only) | |
| npm-audit: | |
| needs: changes | |
| if: | | |
| always() && | |
| (needs.changes.result == 'skipped' || needs.changes.outputs.security == 'true') | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - name: Create package-lock.json if missing | |
| run: | | |
| if [ ! -f package-lock.json ]; then | |
| echo "package-lock.json not found, running npm install to create it" | |
| npm install --package-lock-only --ignore-scripts | |
| fi | |
| - name: Setup Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| node-version: 22 | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm ci --omit=dev --ignore-scripts | |
| - name: Run npm audit | |
| run: npm audit --production --audit-level=moderate | |
| continue-on-error: false | |
| # Generate SBOM | |
| sbom: | |
| needs: changes | |
| if: | | |
| always() && | |
| (needs.changes.result == 'skipped' || needs.changes.outputs.security == 'true') | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - name: Create package-lock.json if missing | |
| run: | | |
| if [ ! -f package-lock.json ]; then | |
| echo "package-lock.json not found, running npm install to create it" | |
| npm install --package-lock-only --ignore-scripts | |
| fi | |
| - name: Setup Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| node-version: 22 | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Generate SBOM (CycloneDX) | |
| run: npx @cyclonedx/cyclonedx-npm --output-file sbom.json | |
| - name: Upload SBOM | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: sbom-main | |
| path: sbom.json | |
| retention-days: 90 | |
| # CodeQL Analysis | |
| codeql: | |
| needs: changes | |
| if: | | |
| always() && | |
| (needs.changes.result == 'skipped' || needs.changes.outputs.security == 'true') | |
| runs-on: ubuntu-latest | |
| permissions: | |
| security-events: write | |
| contents: read | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - name: Initialize CodeQL | |
| uses: github/codeql-action/init@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9 | |
| with: | |
| languages: javascript-typescript | |
| queries: security-and-quality | |
| - name: Autobuild | |
| uses: github/codeql-action/autobuild@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9 | |
| - name: Perform CodeQL Analysis | |
| uses: github/codeql-action/analyze@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9 | |
| with: | |
| category: "/language:javascript-typescript" | |
| # OpenSSF Scorecard | |
| scorecard: | |
| needs: changes | |
| if: | | |
| always() && | |
| (needs.changes.result == 'skipped' || needs.changes.outputs.security == 'true') | |
| runs-on: ubuntu-latest | |
| permissions: | |
| security-events: write | |
| id-token: write | |
| contents: read | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| persist-credentials: false | |
| - name: Run OpenSSF Scorecard | |
| uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 | |
| with: | |
| results_file: results.sarif | |
| results_format: sarif | |
| publish_results: true | |
| - name: Upload SARIF to GitHub Security | |
| uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9 | |
| with: | |
| sarif_file: results.sarif | |
| # License compliance | |
| license-check: | |
| needs: changes | |
| if: | | |
| always() && | |
| (needs.changes.result == 'skipped' || needs.changes.outputs.security == 'true') | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - name: Create package-lock.json if missing | |
| run: | | |
| if [ ! -f package-lock.json ]; then | |
| echo "package-lock.json not found, running npm install to create it" | |
| npm install --package-lock-only --ignore-scripts | |
| fi | |
| - name: Setup Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| node-version: 22 | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Check licenses | |
| run: npx license-checker --summary | |
| # Summary | |
| security-summary: | |
| needs: [npm-audit, sbom, codeql, scorecard, license-check] | |
| runs-on: ubuntu-latest | |
| if: always() | |
| steps: | |
| - name: Security Summary | |
| run: | | |
| { | |
| echo "## Security Main Branch Summary" | |
| echo "" | |
| echo "- NPM Audit: ${{ needs.npm-audit.result }}" | |
| echo "- SBOM Generation: ${{ needs.sbom.result }}" | |
| echo "- CodeQL Analysis: ${{ needs.codeql.result }}" | |
| echo "- OpenSSF Scorecard: ${{ needs.scorecard.result }}" | |
| echo "- License Check: ${{ needs.license-check.result }}" | |
| } >> "$GITHUB_STEP_SUMMARY" |