name: Security on: push: branches: [main] pull_request: branches: [main] schedule: # Run weekly to catch new vulnerabilities - cron: "0 8 * * 1" workflow_dispatch: # Allow manual triggering concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: gitleaks: name: Scan for Secrets runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 # Full history to ensure base/head SHAs are available for PR scans - name: Install Gitleaks run: | set -euo pipefail GITLEAKS_VERSION="8.24.3" GITLEAKS_BASE_URL="https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}" RUNNER_ARCH="$(uname -m)" case "${RUNNER_ARCH}" in x86_64|amd64) GITLEAKS_ARCH="x64" ;; aarch64|arm64) GITLEAKS_ARCH="arm64" ;; *) echo "Unsupported runner architecture: ${RUNNER_ARCH}" >&2 exit 1 ;; esac ARCHIVE_NAME="gitleaks_${GITLEAKS_VERSION}_linux_${GITLEAKS_ARCH}.tar.gz" # Download gitleaks archive using the release filename so checksum verification works curl -sSfL "${GITLEAKS_BASE_URL}/${ARCHIVE_NAME}" -o "${ARCHIVE_NAME}" # Download the combined checksums file curl -sSfL "${GITLEAKS_BASE_URL}/gitleaks_${GITLEAKS_VERSION}_checksums.txt" -o gitleaks_checksums.txt # Verify checksum (sha256sum -c expects the filename in the checksums file to match the local file) grep "${ARCHIVE_NAME}" gitleaks_checksums.txt | sha256sum -c - # Extract the verified binary to a user-writable directory and add it to PATH INSTALL_DIR="${RUNNER_TEMP}/gitleaks-bin" mkdir -p "${INSTALL_DIR}" tar -xzf "${ARCHIVE_NAME}" -C "${INSTALL_DIR}" gitleaks echo "${INSTALL_DIR}" >> "${GITHUB_PATH}" # Clean up rm -f "${ARCHIVE_NAME}" gitleaks_checksums.txt - name: Run Gitleaks run: | if [ "${{ github.event_name }}" = "pull_request" ]; then # Ensure the base and head SHAs exist locally when checkout uses the PR merge ref git fetch origin ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }} --depth=1 gitleaks detect --source . --verbose --log-opts "${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}" else gitleaks detect --source . --verbose --log-opts "-n 10" fi dependency-review: name: Dependency Review runs-on: ubuntu-latest if: github.event_name == 'pull_request' steps: - name: Checkout code uses: actions/checkout@v4 - name: Dependency Review uses: actions/dependency-review-action@v4 with: fail-on-severity: moderate npm-audit: name: NPM Audit runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup pnpm uses: pnpm/action-setup@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: 20 cache: pnpm - name: Install dependencies run: pnpm install --frozen-lockfile - name: Run npm audit run: pnpm audit --audit-level=moderate continue-on-error: true # Don't fail build on vulnerabilities in deps - name: Run pnpm audit (strict) run: pnpm audit --prod --audit-level=high continue-on-error: true # npm's legacy audit endpoint returns 410 Gone — non-blocking until pnpm upgrades to bulk advisory API