Skip to main content

Security and quality reporting across GitHub organisations

Project description

🔐 GitHub Security Report

Linux Foundation Source Code License

Security and quality reporting (not scanning) across GitHub organisations. Aggregates existing signals — CodeQL, OpenSSF Scorecard, zizmor, Dependabot, and secret scanning — and ranks the worst offenders so remediation effort goes where it is needed.

🗒️ Published reports

https://lfreleng-actions.github.io/github-security-report-action/

What it does

For each in-scope repository, every signal is classified into one of four states and rendered worst-first:

  • Offenders — enabled with open findings (a ranked table row).
  • Clean — enabled with zero findings (a count beneath the table).
  • Not enabled — supported but switched off (a nag list prompting you to enable it).
  • Unknown — indeterminate (insufficient permission), footnoted separately.

The single GitHub code-scanning feed is partitioned by tool.name into CodeQL, Scorecard, and zizmor; Scorecard prefers the external aggregate score and falls back to code-scanning findings. See docs/BRIEF.md and docs/phase0-findings.md for the full design and the API research it is built on.

Two further sections report configuration posture and freshness as plain tables (org mode):

  • Dependabot — three tables: repositories with vulnerability alerts not enabled, repositories with security updates not enabled, and ecosystems with no update cooldown configured (mandatory; any value passes).
  • Releases / Tagging — repositories overdue a release or tag, ranked by a hidden compound staleness score. Repositories younger than release_min_age_days (default 28; 0 includes all) and those in releases_exclude are omitted.

Operating modes

Mode Token Scope Output
org fine-grained PAT (single org) or classic PAT (multiple orgs) one or more organisations GitHub Pages + Slack + terminal
repo GITHUB_TOKEN the current repository only job summary + outputs + optional PR gate

scope: auto resolves to org mode when configuration is supplied, otherwise repo mode for the detected repository. The ephemeral GITHUB_TOKEN cannot read org-wide security data, so org mode requires a PAT — see Token permissions for the exact scopes.

Token permissions

Repo mode needs nothing beyond the workflow's ephemeral GITHUB_TOKEN. Org mode needs a Personal Access Token; choose one of the two options below depending on how many organisations the report covers.

All required access is read-only. The tool degrades any read it is not permitted to make to an "unknown" status rather than reporting a repository as clean, so an under-scoped token surfaces as unknowns in the report instead of silently wrong results — start minimal and widen if you see unknowns.

Single organisation — fine-grained PAT

A fine-grained PAT is bound to one resource owner, so it works for a report covering a single organisation. Create it with Resource owner set to the organisation and Repository access set to All repositories, then grant:

Repository permissions (all Read-only):

Permission Used for
Metadata Mandatory baseline; listing organisation repositories
Contents .github/dependabot.yml, latest release, and tag dates
Dependabot alerts Open Dependabot vulnerability alerts
Code scanning alerts CodeQL / Scorecard / zizmor findings
Secret scanning alerts Open secret-scanning alerts
Administration Dependabot enablement + security-updates status, and effective branch rules

Organization permissions (Read-only):

Permission Used for
Administration Organisation rulesets (detect tools enabled via a required workflow)

A fine-grained token cannot span organisations. For a report covering more than one org, use a classic PAT (below).

Multiple organisations — classic PAT

A classic PAT is authorised across every organisation its creator can access (subject to SSO authorisation), so a single token can report on multiple organisations. Grant these scopes:

Scope Used for
repo Repository data, including private repositories
security_events Code scanning, secret scanning, and Dependabot alerts (org-bulk and per-repo)
read:org Listing organisation repositories and reading organisation rulesets

For organisations that enforce SSO, the PAT must be SSO-authorised for each target organisation, or the org-level endpoints return 403 (reported as unknown). Store the token as a secret (e.g. SECURITY_REPORT_PAT) and reference it by env-var name via token_env; never embed it in the config.

Usage

Org mode (scheduled report)

- name: "Security report"
  id: report
  uses: lfreleng-actions/github-security-report-action@v0.1.0
  with:
    scope: "org"
    config: "${{ secrets.GSR_CONFIG || vars.GSR_CONFIG }}"
    token: "${{ secrets.SECURITY_REPORT_PAT }}"
    output_dir: "site"
    pages_url: "https://lfreleng-actions.github.io/github-security-report-action/"

A ready-to-use scheduled workflow lives in .github/workflows/reporting.yaml: it runs daily at 09:00 UTC, publishes to GitHub Pages every day, and posts a Slack digest only on the configured report_day (default Tuesday).

Repo mode (PR gate)

- name: "Security report"
  uses: lfreleng-actions/github-security-report-action@v0.1.0
  with:
    scope: "repo"
    token: "${{ github.token }}"
    fail_threshold: "high"  # fail the job on any open high/critical finding
  # requires: permissions: { security-events: read }

Configuration

Configuration is JSON, supplied as a plain vars. entry or base64-encoded in a secrets. entry (base64 only to stop JSON braces tripping GitHub's log redaction — it is encoding, not encryption). Tokens are referenced by environment-variable name, never embedded.

{
  "slack": { "channel": "releng-scm", "report_day": "tuesday" },
  "report": {
    "top_n": 10,
    "top_n_report": 10,
    "top_n_cli": 10,
    "top_n_slack": 10,
    "include_archived": false,
    "include_test": false,
    "release_min_age_days": 28
  },
  "organizations": [
    {
      "name": "lfreleng-actions",
      "token_env": "GITHUB_TOKEN",
      "exclude": ["actions-template"],
      "releases_exclude": ["internal-only-repo"]
    }
  ]
}

report_day accepts a single weekday, a list of weekdays, "never", or "always".

top_n controls how many offenders are shown per signal. It is the shared default for all three outputs; set any of top_n_report (GitHub Pages), top_n_cli (terminal), or top_n_slack (Slack digest) to override an individual output. Each can also be set at the CLI with --top-n, --top-n-report, --top-n-cli, and --top-n-slack.

report.release_min_age_days (default 28, 0 = include all) and the per-org releases_exclude tune the Releases / Tagging section; they can be overridden locally with --release-min-age-days and the repeatable --releases-exclude.

The per-org exclude list removes repositories from analysis entirely; they are reported as excluded (distinct from "not enabled"), so an intentional exclusion is visible rather than silently dropped.

slack.channel is optional. The action's slack_channel input (wired to the SLACK_CHANNEL_ID variable in reporting.yaml) overrides it, so the channel can live as an org/repo variable rather than in the config JSON. It must be the channel ID (C0…), not the name.

Config file location

For local use you can drop the same JSON at a conventional per-user path and run with no flags — it is picked up automatically when no --config, --config-data, or --org is given (instead of erroring):

$XDG_CONFIG_HOME/github-security-report/config.json
# or, when XDG_CONFIG_HOME is unset:
~/.config/github-security-report/config.json

An explicit --config, --config-data, or --org always takes precedence, and the action itself never reads this path (it is supplied configuration directly). Secrets stay out of the file: reference the token by environment-variable name via token_env (e.g. SECURITY_REPORT_PAT, exported in your shell or sourced from a secrets file) — the channel ID is the only Slack value the file holds, and the Slack bot token is consumed by the workflow, not the CLI.

Inputs

Name Required Default Description
scope No auto auto, org, or repo
config No JSON config (raw or base64)
org No Single organisation (shorthand for org mode)
repo No detected owner/name for repo mode
token No ${{ github.token }} PAT (org mode) or GITHUB_TOKEN (repo mode)
token_env No GITHUB_TOKEN Env var name the tool reads the token from
output_dir No Directory for Pages output (org mode)
pages_url No Published Pages URL (used in the Slack link)
slack_channel No Slack channel ID; overrides the config slack.channel (e.g. the SLACK_CHANNEL_ID variable)
top_n No 10 Offenders per signal across all outputs (shared default)
top_n_report No Offenders per signal in the GitHub Pages output (overrides top_n)
top_n_cli No Offenders per signal in the terminal output (overrides top_n)
top_n_slack No Offenders per signal in the Slack digest (overrides top_n)
fail_threshold No none none/low/medium/high/critical/any (repo mode)
force_notify No false Post to Slack regardless of report_day
tool_version No 0.1.0 Published PyPI version (ignored on pull requests)
use_local_source No false Run from the checked-out source instead of PyPI (for testing unreleased code from any event)

Outputs

Name Description
should_notify Whether today is a Slack notification day
slack_payload Prebuilt Slack chat.postMessage payload (JSON)
failed Whether the repo-mode fail threshold was met

Running locally

The tool is published to PyPI and runs with uvx. Inside a Git checkout with a GITHUB_TOKEN exported, it auto-detects the repository (preferring the upstream remote, then origin) and prints a Rich table report:

export GITHUB_TOKEN=ghp_your_token
uvx github-security-report report

# Or org mode locally with a PAT:
uvx github-security-report report --org lfreleng-actions

Development

uv sync --extra dev
uv run pytest
uv run ruff check src/ tests/

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

github_security_report-0.1.0.tar.gz (53.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

github_security_report-0.1.0-py3-none-any.whl (65.8 kB view details)

Uploaded Python 3

File details

Details for the file github_security_report-0.1.0.tar.gz.

File metadata

  • Download URL: github_security_report-0.1.0.tar.gz
  • Upload date:
  • Size: 53.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for github_security_report-0.1.0.tar.gz
Algorithm Hash digest
SHA256 afb60847eb4b9781a737caab55fb1370a4e16b4d09a2e897799b99b872c5306a
MD5 b8ee51465de256a887bf6e92393d0758
BLAKE2b-256 ff640e54781cc67c374afeb3ac5675c442173dc363c7de9b520286d5db14bc9d

See more details on using hashes here.

File details

Details for the file github_security_report-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for github_security_report-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 92024e41300e2c478227675738eb0c5b121ed1f4366a48c7936e7695eea77490
MD5 d5c46e3e0ecc1804f8b100faf1e7b242
BLAKE2b-256 022cc68eee2cff0eacddb5d94dea09538a3bb75ff52c2a0ef03901cd3c704e1c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page