Files
xdrop/.github/workflows/security-audits.yml
T

202 lines
5.7 KiB
YAML

name: Security Audits
on:
schedule:
- cron: '43 2 * * 1'
workflow_dispatch:
permissions:
contents: read
issues: write
jobs:
bun-audit:
name: bun audit
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Check out repository
uses: actions/checkout@v6
- name: Set up Bun
uses: oven-sh/setup-bun@v2
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Run bun audit
shell: bash
run: |
set -o pipefail
bun audit --audit-level=high --json | tee bun-audit.json
- name: Upload bun audit report
if: always()
uses: actions/upload-artifact@v7
with:
name: bun-audit-report
path: bun-audit.json
if-no-files-found: ignore
govulncheck:
name: govulncheck
runs-on: ubuntu-latest
timeout-minutes: 15
defaults:
run:
working-directory: apps/api
steps:
- name: Check out repository
uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: apps/api/go.mod
cache-dependency-path: apps/api/go.sum
- name: Install govulncheck
run: go install golang.org/x/vuln/cmd/govulncheck@latest
- name: Run govulncheck
shell: bash
run: |
set -o pipefail
govulncheck ./... | tee ../../govulncheck.txt
- name: Upload govulncheck report
if: always()
uses: actions/upload-artifact@v7
with:
name: govulncheck-report
path: govulncheck.txt
if-no-files-found: ignore
report-status:
name: Report security audit status
runs-on: ubuntu-latest
timeout-minutes: 10
needs:
- bun-audit
- govulncheck
if: always()
steps:
- name: Create or update failure issue
if: ${{ needs.bun-audit.result != 'success' || needs.govulncheck.result != 'success' }}
uses: actions/github-script@v8
env:
BUN_AUDIT_RESULT: ${{ needs.bun-audit.result }}
GOVULNCHECK_RESULT: ${{ needs.govulncheck.result }}
ISSUE_LABEL: security-audit
ISSUE_TITLE: Security audits failing
with:
script: |
const owner = context.repo.owner
const repo = context.repo.repo
const label = process.env.ISSUE_LABEL
const title = process.env.ISSUE_TITLE
const runUrl = `${context.serverUrl}/${owner}/${repo}/actions/runs/${context.runId}`
const body = [
'One or more scheduled security audit jobs failed.',
'',
`- Workflow run: ${runUrl}`,
`- bun audit: ${process.env.BUN_AUDIT_RESULT}`,
`- govulncheck: ${process.env.GOVULNCHECK_RESULT}`,
`- Trigger: ${context.eventName}`,
`- Commit: ${context.sha}`,
].join('\n')
try {
await github.rest.issues.getLabel({ owner, repo, name: label })
} catch (error) {
if (error.status !== 404) {
throw error
}
await github.rest.issues.createLabel({
owner,
repo,
name: label,
color: 'b60205',
description: 'Failures from the scheduled security audits workflow',
})
}
const { data: issues } = await github.rest.issues.listForRepo({
owner,
repo,
state: 'open',
labels: label,
per_page: 100,
})
const existing = issues.find((issue) => issue.title === title)
if (existing) {
await github.rest.issues.createComment({
owner,
repo,
issue_number: existing.number,
body,
})
core.info(`Updated existing issue #${existing.number}.`)
return
}
const { data: created } = await github.rest.issues.create({
owner,
repo,
title,
body,
labels: [label],
})
core.info(`Created issue #${created.number}.`)
- name: Close resolved failure issue
if: ${{ needs.bun-audit.result == 'success' && needs.govulncheck.result == 'success' }}
uses: actions/github-script@v8
env:
ISSUE_LABEL: security-audit
ISSUE_TITLE: Security audits failing
with:
script: |
const owner = context.repo.owner
const repo = context.repo.repo
const label = process.env.ISSUE_LABEL
const title = process.env.ISSUE_TITLE
const { data: issues } = await github.rest.issues.listForRepo({
owner,
repo,
state: 'open',
labels: label,
per_page: 100,
})
const existing = issues.find((issue) => issue.title === title)
if (!existing) {
core.info('No open security audit failure issue to close.')
return
}
await github.rest.issues.createComment({
owner,
repo,
issue_number: existing.number,
body: `Security audits recovered in ${context.serverUrl}/${owner}/${repo}/actions/runs/${context.runId}. Closing this alert.`,
})
await github.rest.issues.update({
owner,
repo,
issue_number: existing.number,
state: 'closed',
})
core.info(`Closed issue #${existing.number}.`)