Add a summary report set from Markdown

from-markdown takes a hand-written summary such as SUMMARY.md and sets it
in the full report's dress: the same cover, header, footers, table of
contents, headings and grids. The shared macros move to macros.html.j2 so
both templates draw from them, and layout.render_pdf takes the template and
document title to use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
alexanderandClaude Opus 5.5 committed 2026-09-25 12:22:45 +02:00
1 parent 0ace2e1803
commit 864e2ff824
10 files changed
+573 -92

No files matched your search

+27
View File
@@ -85,6 +85,32 @@ by the one naming convention in `cc_common.config.env_var_name`, so it stays wha
calls it. A `.env` beside the `pyproject.toml` is read too. `--config` is resolved relative to the
working directory.
## The summary report
A shorter document, written by hand in Markdown (like `SUMMARY.md` beside the scan's artifacts) and
set in the dress of the full report: the same cover, header, footers, table of contents, headings
and tables.
```
report_generator from-markdown --markdown SUMMARY.md [--path <file-or-directory>]
[--project <name>] [--manufacturer <name>] [--scan_id <uuid>]
```
How the Markdown is read (`report_generator/summary.py`):
- The first `#` heading is the title. Written as `<title>: <subject>`, the subject is the project
unless `--project` is given.
- What comes before the first section is the introduction, and goes on the cover.
- Every further `#` or `##` heading is a section: it starts a new page, is listed in the table of
contents and keeps the anchor GitHub gives it, so links such as `[…](#1-static-analysis-sast)`
work in the PDF. `###` and below become the report's subheadings.
- A paragraph that is only bold text (`**Remaining findings**`) is set as a subheading.
- Tables are drawn as the report's grids. A number in a column headed *Severity*, *CVSS* or *Score*
carries its severity colour, and a row starting with `**Total**` is set as a total.
The scan id is optional here; without it the cover leaves it out and the footer names the project.
The file is written as `summary_<scan-id>.pdf`, or `summary_<project>.pdf` without one.
## How it is put together
Four steps, each its own module, so that the one that will change is the one that is separate:
@@ -95,6 +121,7 @@ Four steps, each its own module, so that the one that will change is the one tha
| `report_generator/rendering.py` | Decides which stored file feeds which part of the report, and parses it. |
| `report_generator/context.py` | Maps the parsed artifacts onto the page, and writes `<xxx>` where they say nothing. |
| `report_generator/layout.py` | Draws `template/report.html.j2` through WeasyPrint. |
| `report_generator/summary.py` | Sets a Markdown summary in `template/summary.html.j2`, drawn the same way. |
No parsing of its own. SARIF and the vulnerability report are read by the summarisers in
`cipherverifier.core.services`, which are written to be free of Django and of any store; the CVSS