Skip to content

chore(deps): bump typescript from 5.9.3 to 6.0.3 #344

chore(deps): bump typescript from 5.9.3 to 6.0.3

chore(deps): bump typescript from 5.9.3 to 6.0.3 #344

Workflow file for this run

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"