Centralized Secret Scanning Report #19
This file contains 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: Centralized Secret Scanning Report | |
on: | |
workflow_dispatch: | |
inputs: | |
include_inactive: | |
description: 'Include inactive alerts in report' | |
required: false | |
type: boolean | |
default: false | |
max_workers: | |
description: 'Maximum number of concurrent workers' | |
required: false | |
type: number | |
default: 10 | |
log_level: | |
description: 'Logging level' | |
required: false | |
type: choice | |
options: | |
- INFO | |
- DEBUG | |
- WARNING | |
- ERROR | |
default: 'INFO' | |
alert_threshold: | |
description: 'Number of active alerts to trigger issue creation' | |
required: false | |
type: number | |
default: 10 | |
schedule: | |
- cron: '0 0 * * 1' | |
permissions: | |
security-events: read | |
contents: write | |
actions: write | |
issues: write | |
jobs: | |
generate-report: | |
runs-on: ubuntu-latest | |
steps: | |
- name: Checkout .github repo | |
uses: actions/checkout@v4 | |
with: | |
repository: ${{ github.repository_owner }}/.github | |
ref: main | |
token: ${{ secrets.SECRET_SCANNING_TOKEN }} | |
- name: Set up Python | |
uses: actions/setup-python@v4 | |
with: | |
python-version: '3.11' | |
cache: 'pip' | |
cache-dependency-path: scripts/requirements.txt # Now correct | |
- name: Install dependencies | |
run: | | |
python -m pip install --upgrade pip | |
pip install -r scripts/requirements.txt # Now correct | |
- name: Generate timestamp | |
id: timestamp | |
run: echo "timestamp=$(date +%Y%m%d_%H%M%S)" >> $GITHUB_OUTPUT | |
- name: Generate Secret Report | |
id: generate-report | |
env: | |
GITHUB_TOKEN: ${{ secrets.SECRET_SCANNING_TOKEN }} | |
ORGANIZATION: ${{ github.repository_owner }} | |
REPORT_FILE: "secret_report_${{ steps.timestamp.outputs.timestamp }}.csv" | |
run: | | |
# Create the reports directory (now in the correct location) | |
mkdir -p reports | |
# Construct the command string explicitly (now simpler paths) | |
COMMAND="python scripts/github_secret_scanner.py \ | |
--org $ORGANIZATION \ | |
--token $GITHUB_TOKEN \ | |
--output reports/$REPORT_FILE \ | |
--log-level ${{ inputs.log_level || 'INFO' }} \ | |
--max-workers ${{ inputs.max_workers || 10 }} \ | |
--max-retries 3" | |
# Add the conditional --include-inactive flag | |
if [[ "${{ inputs.include_inactive }}" == "true" ]]; then | |
COMMAND="$COMMAND --include-inactive" | |
fi | |
# Execute the command | |
$COMMAND | |
echo "report_path=reports/$REPORT_FILE" >> $GITHUB_OUTPUT | |
- name: Check for No Repositories | |
id: check-repos | |
if: success() | |
run: | | |
if grep -q "__NO_REPOS__" ${{ steps.generate-report.outputs.report_path }}/../output.txt; then | |
echo "No repositories found in the organization. Exiting." | |
exit 1 | |
fi | |
- name: Process report statistics (inline) | |
id: stats | |
if: success() && steps.check-repos.outcome == 'success' | |
run: | | |
STATS=$(grep "__STATS_START__" ${{ steps.generate-report.outputs.report_path }}/../output.txt | sed 's/__STATS_START__//' | sed 's/__STATS_END__//') | |
echo "total_alerts=$(echo $STATS | cut -d',' -f1 | cut -d'=' -f2)" >> $GITHUB_OUTPUT | |
echo "active_alerts=$(echo $STATS | cut -d',' -f2 | cut -d'=' -f2)" >> $GITHUB_OUTPUT | |
echo "inactive_alerts=$(echo $STATS | cut -d',' -f3 | cut -d'=' -f2)" >> $GITHUB_OUTPUT | |
echo "Total alerts found: $(echo $STATS | cut -d',' -f1 | cut -d'=' -f2)" | |
echo "Active alerts: $(echo $STATS | cut -d',' -f2 | cut -d'=' -f2)" | |
echo "Inactive alerts: $(echo $STATS | cut -d',' -f3 | cut -d'=' -f2)" | |
- name: Create summary issue (using github-script) | |
if: success() && steps.check-repos.outcome == 'success' && steps.stats.outputs.active_alerts > inputs.alert_threshold | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const stats = { | |
total: '${{ steps.stats.outputs.total_alerts }}', | |
active: '${{ steps.stats.outputs.active_alerts }}', | |
inactive: '${{ steps.stats.outputs.inactive_alerts }}', | |
}; | |
const now = new Date(); | |
const formattedDate = now.toLocaleDateString('en-US', { | |
year: 'numeric', | |
month: 'long', | |
day: 'numeric' | |
}); | |
const body = ` | |
# Secret Scanning Report Summary | |
Report generated on: ${now.toISOString()} | |
## Statistics | |
- Total alerts analyzed: ${stats.total} | |
- Active alerts found: ${stats.active} | |
- Inactive alerts found: ${stats.inactive} | |
## Details | |
- Report artifact: [Download report](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}) | |
- Workflow run: [View details](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}) | |
## Configuration | |
- Include inactive alerts: ${{ inputs.include_inactive || 'false' }} | |
- Max workers: ${{ inputs.max_workers || '10' }} | |
- Log level: ${{ inputs.log_level || 'INFO' }} | |
- Alert threshold: ${{ inputs.alert_threshold || '10'}} | |
`; | |
await github.rest.issues.create({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
title: \`📊 Secret Scanning Report - \${formattedDate}\`, | |
body: body, | |
labels: ['secret-scanning', 'report'] | |
}); | |
- name: Commit and Push Report | |
if: success() && steps.check-repos.outcome == 'success' | |
uses: stefanzweifel/git-auto-commit-action@v5 | |
with: | |
commit_message: "Add secret scanning report: ${{ steps.timestamp.outputs.timestamp }}" | |
repository: ./ # Commit to the root of the checked-out repo | |
file_pattern: reports/*.csv | |
commit_user_name: GitHub Actions | |
commit_user_email: [email protected] | |
commit_author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> | |
push_options: '--force' | |
- name: Notify on failure | |
if: failure() | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const body = ` | |
# 🚨 Secret Scanning Report Generation Failed | |
Workflow run failed at ${new Date().toISOString()} | |
## Details | |
- Run ID: \`${context.runId}\` | |
- Trigger: ${context.eventName} | |
- Actor: @${context.actor} | |
## Links | |
- [View run details](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}) | |
- [View workflow file](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/blob/main/.github/workflows/secret-scanning-report.yml) | |
Please check the workflow logs for detailed error information. | |
`; | |
await github.rest.issues.create({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
title: '🚨 Secret Scanning Report Generation Failed', | |
body: body, | |
labels: ['secret-scanning', 'failed'] | |
}); | |
- name: Clean up | |
if: always() | |
run: | | |
echo "No clean up required." | |
concurrency: | |
group: ${{ github.workflow }}-${{ github.ref }} | |
cancel-in-progress: true | |