Safeguard
Developer Tools•

How do you publish a scan report as a GitHub Actions job summary?

Append Markdown to the GITHUB_STEP_SUMMARY file and GitHub shows it on the run page. A table template, safety cautions and what to leave out of the report.

Safeguard Security Team
4 min read

Write Markdown to the file named by the GITHUB_STEP_SUMMARY environment variable. GitHub groups the steps' summaries into one job summary shown on the workflow run page. It is the simplest way to publish a readable scan report with no extra service, chat message or ticket.

What a job summary is

GitHub's workflow commands reference describes it this way: you can set custom Markdown for each job, shown on the summary page of a run, to display content such as test result summaries so people do not need to dig through logs. It supports GitHub flavored Markdown, and each step has its own summary file that is combined when the job finishes.

- name: Write summary
  run: |
    echo "## Dependency scan" >> "$GITHUB_STEP_SUMMARY"
    echo "" >> "$GITHUB_STEP_SUMMARY"
    echo "| Severity | Count |" >> "$GITHUB_STEP_SUMMARY"
    echo "|---|---|" >> "$GITHUB_STEP_SUMMARY"
    echo "| High | $HIGH |" >> "$GITHUB_STEP_SUMMARY"
    echo "| Medium | $MEDIUM |" >> "$GITHUB_STEP_SUMMARY"

On Windows runners the reference shows the PowerShell form that appends to $env:GITHUB_STEP_SUMMARY.

What belongs in a good summary

SectionWhy
Scope lineWhich repository, branch and commit, and when. A report without scope is hard to trust
Counts by severityQuick read for a reviewer
New versus existingPeople act on new findings first
Top items with fixed versionsThe part someone will actually do
Coverage noteWhat was not scanned, and why
Link to full resultsDetail lives elsewhere

For the last two rows see scan coverage statements and reporting zero findings without overclaiming.

Keep it safe

A summary is a place where untrusted text can sneak in. A package description or pull request title that contains Markdown or links will render as such. Do not paste raw advisory or issue text into the summary. Use fixed strings, counts and identifiers you validated. Never print secrets or tokens: workflow logs can mask registered secrets, but a summary is rendered content, and masking is not a safe design to rely on.

The summary is also visible to anyone who can read the run, which for a public repository means the whole internet. Keep internal detail, customer names and unfixed vulnerability specifics out of public repositories.

Summary, artifact, ticket or chat?

  • Job summary: best for the person who opened the run.
  • Artifact: best for full machine readable output, such as an SBOM or SARIF file.
  • Ticket: best for work someone must do.
  • Chat: best for a short pointer to the run.

A common pattern is the summary for the human, an artifact for the detail and a one line chat message linking to the run. Retention matters too: see keeping SBOM and scan reports beyond artifact retention.

Try it yourself

Click through it at your own pace, or open it full screen.

Look at the inventory columns and decide which three belong in your summary table.

Safeguard produces SBOMs and dependency scans you can feed into reports like this. Try the free scan at gold.safeguard.sh/scan.

Quick check

Can the summary replace an audit record?

Not on its own. Keep a copy of the scan output as an artifact or in your evidence store, since run pages are not a durable archive.

Does each step get its own summary?

Yes. The file differs per step, and GitHub joins them into one job summary when the job ends.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.