Initial commit. monorepo deps are copied in a dirty way to the vendor folder

This commit is contained in:
Florian Frank committed 2026-09-18 16:58:29 +02:00
commit 06593ac488
53 files changed
+7692

No files matched your search

+90
View File
@@ -0,0 +1,90 @@
"""Renders the SAST/SCA report template.
The template and its stylesheet are the design; this module is only what turns them into a
document. It takes a context that is already shaped for the page -- every number formatted, every
bar already a percentage -- so that the template places strings and never computes.
Nothing here reads an artifact. Filling a context from a scan is :mod:`context`, which works from
what :mod:`sources` collected; :func:`sample.sample_context` builds one by hand, which is what the
template tests render.
"""
from __future__ import annotations
from pathlib import Path
from typing import Any
from jinja2 import Environment, FileSystemLoader, select_autoescape
TEMPLATE_DIR = Path(__file__).resolve().parent / "template"
TEMPLATE_NAME = "report.html.j2"
STYLESHEET = TEMPLATE_DIR / "report.css"
# What the document calls itself, in the PDF's own metadata rather than only on its first page.
TITLE = "SAST/SCA Report"
AUTHOR = "CipherCell"
# The shape :func:`render_html` expects. Written down because the template guards on it: a key that
# is absent leaves its section out, which is how a scan that ran half the pipeline produces a
# shorter report rather than one full of empty tables.
CONTEXT = {
"cover": "scan_id, footer_date, logo, title, identity[], severity_ledger{title, rows[]}",
"scan": "title, overview[], stages[{title, rows[], table?}]",
"project": "title, overview[], languages{title, rows[]}",
"summary": "title, overview[], distributions[{title, rows[], legend}]",
"critical": "title, table{columns[], rows[]}",
"sections": "[{title, new_page?, groups[{title, note?, cards[], table?}]}]",
}
def render_html(context: dict[str, Any]) -> str:
"""Render the report as an HTML document.
Args:
context: The page-ready context; see :data:`CONTEXT`.
Returns:
The rendered HTML.
"""
environment = Environment(
loader=FileSystemLoader(TEMPLATE_DIR),
autoescape=select_autoescape(default_for_string=True, default=True),
trim_blocks=True,
lstrip_blocks=True,
)
return environment.get_template(TEMPLATE_NAME).render(**context)
def render_pdf(context: dict[str, Any]) -> bytes:
"""Render the report as a PDF.
Args:
context: The page-ready context; see :data:`CONTEXT`.
Returns:
The PDF.
"""
# WeasyPrint pulls in native cairo and pango bindings. Imported here so that importing this
# module -- to render the HTML, or merely to read CONTEXT -- does not pay for them.
from weasyprint import CSS, HTML
from weasyprint.text.fonts import FontConfiguration
# Without this the @font-face rules are ignored and Space Grotesk is silently replaced by
# whatever the host happens to have, which is how the design sample came to be set in Carlito.
fonts = FontConfiguration()
# Relative URLs in the template and the stylesheet -- the mark, the font files -- resolve
# against the template directory.
document = HTML(string=render_html(context), base_url=str(TEMPLATE_DIR)).render(
stylesheets=[CSS(filename=str(STYLESHEET), font_config=fonts)],
font_config=fonts,
)
document.metadata.title = f"{TITLE} - {context.get('cover', {}).get('subject', '')}".strip(" -")
document.metadata.authors = [AUTHOR]
return bytes(document.write_pdf())