chore(deps): bump typescript from 5.9.3 to 6.0.3 #344
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 | |
| on: | |
| pull_request: | |
| branches: [main] | |
| push: | |
| branches: [main] | |
| schedule: | |
| - cron: '0 0 * * 1' # Weekly on Monday midnight UTC | |
| workflow_dispatch: | |
| inputs: | |
| severity: | |
| description: 'Minimum severity to report' | |
| required: false | |
| default: 'high' | |
| type: choice | |
| options: [high, medium] | |
| skip_dast: | |
| description: 'Skip DAST scan' | |
| required: false | |
| default: false | |
| type: boolean | |
| skip_codeql: | |
| description: 'Skip CodeQL scan' | |
| required: false | |
| default: false | |
| type: boolean | |
| concurrency: | |
| group: security-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: read-all | |
| jobs: | |
| changes: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 2 | |
| outputs: | |
| security_relevant: ${{ steps.filter.outputs.security_relevant }} | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 | |
| id: filter | |
| with: | |
| filters: | | |
| security_relevant: | |
| - '**/*.ts' | |
| - '**/*.svelte' | |
| - '**/*.js' | |
| - 'package*.json' | |
| - 'Dockerfile' | |
| codeql: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| needs: [changes] | |
| # Always run on PRs (Code Scanning Security ruleset requires CodeQL results). | |
| # On push/schedule, only run when security-relevant files changed. | |
| if: | | |
| !inputs.skip_codeql && | |
| (github.event_name == 'pull_request' || needs.changes.outputs.security_relevant == 'true' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') | |
| permissions: | |
| security-events: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - name: Initialize CodeQL | |
| uses: github/codeql-action/init@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17 | |
| with: | |
| languages: javascript-typescript | |
| config-file: .github/codeql/codeql-config.yml | |
| queries: security-extended | |
| - name: Autobuild | |
| uses: github/codeql-action/autobuild@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17 | |
| - name: Perform CodeQL analysis | |
| uses: github/codeql-action/analyze@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17 | |
| with: | |
| category: '/language:javascript-typescript' | |
| secret-scan: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| needs: [changes] | |
| if: needs.changes.outputs.security_relevant == 'true' || github.event_name == 'schedule' | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| fetch-depth: 0 | |
| - name: Install Gitleaks | |
| run: | | |
| GITLEAKS_VERSION=$(curl -s https://api.github.com/repos/gitleaks/gitleaks/releases/latest | grep '"tag_name"' | sed 's/.*"v\(.*\)".*/\1/') | |
| curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" | tar -xz -C /usr/local/bin gitleaks | |
| - name: Run Gitleaks | |
| run: gitleaks detect --source . --config .gitleaks.toml --verbose | |
| dependency-scan: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| needs: [changes] | |
| if: needs.changes.outputs.security_relevant == 'true' || github.event_name == 'schedule' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| node-version: 24 | |
| cache: npm | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Audit production dependencies | |
| run: npm audit --omit=dev --audit-level=high | |
| snyk: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| needs: [changes] | |
| if: | | |
| needs.changes.outputs.security_relevant == 'true' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' | |
| permissions: | |
| security-events: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| node-version: 24 | |
| cache: npm | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Run Snyk test | |
| uses: snyk/actions/node@9adf32b1121593767fc3c057af55b55db032dc04 # v1.0.0 | |
| continue-on-error: true | |
| env: | |
| SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} | |
| with: | |
| args: --severity-threshold=high --sarif-file-output=snyk-results.sarif | |
| - name: Upload Snyk SARIF | |
| if: always() && hashFiles('snyk-results.sarif') != '' | |
| uses: github/codeql-action/upload-sarif@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17 | |
| with: | |
| sarif_file: snyk-results.sarif | |
| category: snyk | |
| build-image: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| needs: [changes] | |
| if: needs.changes.outputs.security_relevant == 'true' || github.event_name == 'schedule' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 | |
| - name: Build Docker image | |
| uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2 | |
| with: | |
| context: . | |
| push: false | |
| tags: scrolly:security-scan | |
| outputs: type=docker,dest=/tmp/scrolly-image.tar | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| - name: Upload image artifact | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: docker-image | |
| path: /tmp/scrolly-image.tar | |
| retention-days: 1 | |
| container-scan: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| needs: [build-image] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| sparse-checkout: .trivyignore | |
| sparse-checkout-cone-mode: false | |
| - name: Download image artifact | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 | |
| with: | |
| name: docker-image | |
| path: /tmp | |
| - name: Load Docker image | |
| run: docker load -i /tmp/scrolly-image.tar | |
| - name: Install Trivy | |
| run: | | |
| wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null | |
| echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb generic main" | sudo tee /etc/apt/sources.list.d/trivy.list | |
| sudo apt-get update -qq | |
| sudo apt-get install -y -qq trivy | |
| - name: Run Trivy | |
| run: trivy image --severity HIGH,CRITICAL --ignore-unfixed --ignorefile .trivyignore --exit-code 1 scrolly:security-scan | |
| semgrep: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| needs: [changes] | |
| if: needs.changes.outputs.security_relevant == 'true' || github.event_name == 'schedule' | |
| permissions: | |
| security-events: write | |
| container: | |
| image: semgrep/semgrep:1.112.0 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - name: Run Semgrep | |
| run: semgrep scan --config=auto --sarif --output=semgrep-results.sarif . | |
| env: | |
| SEMGREP_RULES: auto | |
| - name: Upload Semgrep SARIF | |
| if: always() | |
| uses: github/codeql-action/upload-sarif@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17 | |
| with: | |
| sarif_file: semgrep-results.sarif | |
| category: semgrep | |
| dast: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| needs: [build-image] | |
| if: | | |
| !inputs.skip_dast && | |
| github.event.pull_request.head.repo.fork != true | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - name: Download image artifact | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 | |
| with: | |
| name: docker-image | |
| path: /tmp | |
| - name: Load Docker image | |
| run: docker load -i /tmp/scrolly-image.tar | |
| - name: Start application | |
| run: | | |
| docker run -d --name scrolly-dast \ | |
| -p 3000:3000 \ | |
| -e SESSION_SECRET=test-secret-for-dast-scanning-only-not-real \ | |
| -e PUBLIC_APP_URL=http://localhost:3000 \ | |
| scrolly:security-scan | |
| - name: Wait for health check | |
| run: | | |
| for i in $(seq 1 30); do | |
| if curl -sf http://localhost:3000/api/health; then | |
| echo "App is healthy" | |
| exit 0 | |
| fi | |
| echo "Waiting for app... ($i/30)" | |
| sleep 2 | |
| done | |
| echo "App failed to start" | |
| docker logs scrolly-dast | |
| exit 1 | |
| - name: Run OWASP ZAP baseline scan | |
| uses: zaproxy/action-baseline@7c4deb10e6261301961c86d65d54a516394f9aed # v0.14.0 | |
| with: | |
| target: http://localhost:3000 | |
| rules_file_name: .zap/rules.tsv | |
| allow_issue_writing: false | |
| - name: Stop application | |
| if: always() | |
| run: docker stop scrolly-dast || true | |
| security-status: | |
| runs-on: ubuntu-latest | |
| if: always() | |
| needs: [codeql, secret-scan, dependency-scan, snyk, container-scan, semgrep, dast] | |
| steps: | |
| - name: Check security status | |
| run: | | |
| declare -A jobs=( | |
| [codeql]="${{ needs.codeql.result }}" | |
| [secret-scan]="${{ needs.secret-scan.result }}" | |
| [dependency-scan]="${{ needs.dependency-scan.result }}" | |
| [snyk]="${{ needs.snyk.result }}" | |
| [container-scan]="${{ needs.container-scan.result }}" | |
| [semgrep]="${{ needs.semgrep.result }}" | |
| [dast]="${{ needs.dast.result }}" | |
| ) | |
| failed=false | |
| for job in "${!jobs[@]}"; do | |
| result="${jobs[$job]}" | |
| case "$result" in | |
| success) echo " $job: passed" ;; | |
| skipped) echo " $job: skipped" ;; | |
| failure) echo " $job: FAILED"; failed=true ;; | |
| cancelled) echo " $job: cancelled"; failed=true ;; | |
| *) echo " $job: unknown ($result)"; failed=true ;; | |
| esac | |
| done | |
| if $failed; then | |
| echo "Security scan failed" | |
| exit 1 | |
| fi | |
| echo "All security scans passed" |