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
| Section | Why |
|---|---|
| Scope line | Which repository, branch and commit, and when. A report without scope is hard to trust |
| Counts by severity | Quick read for a reviewer |
| New versus existing | People act on new findings first |
| Top items with fixed versions | The part someone will actually do |
| Coverage note | What was not scanned, and why |
| Link to full results | Detail 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.