Skip to content

[ENHANCEMENT] Replace string-substitution HTML templates with Jinja2 #51

@advaitpatel

Description

@advaitpatel

Overview

The HTML report generation in `docker_scanner.py` and `report_generator.py` uses a hand-rolled `{{VARIABLE}}` string substitution system. This works today but has real limitations that are already visible in the codebase:

  1. No loops — vulnerability tables are pre-built as a single string concatenation, not a template loop
  2. No conditionals — empty sections (e.g., no CRITICAL findings) require Python-side string manipulation
  3. Escaping must be handled manually — XSS-prone if any path is missed
  4. Not composable — can't include sub-templates or extend a base layout
  5. Fragile — a variable name typo silently leaves `{{VAR}}` in the output (see acceptance criteria in [TEST] Add unit tests for report_generator.py — JSON, CSV, PDF, HTML formats #43)

Jinja2 is already the standard templating engine in the Python ecosystem (Flask, Ansible, Cookiecutter all use it). It solves all the above.

Proposed Change

1. Add Jinja2 to `requirements.txt`

Jinja2>=3.1.0

2. Move the HTML template to `templates/report.html.j2`

<!DOCTYPE html>
<html>
<head><title>DockSec Security Report — {{ image_name }}</title></head>
<body>
  <h1>Security Report</h1>
  <p>Scan date: {{ scan_date }}</p>
  <p>Image: {{ image_name }}</p>

  <h2>Severity Summary</h2>
  <ul>
    {% for level in ["CRITICAL", "HIGH", "MEDIUM", "LOW"] %}
    <li>{{ level }}: {{ severity_counts[level] }}</li>
    {% endfor %}
  </ul>

  <h2>Vulnerabilities</h2>
  <table>
    <tr><th>ID</th><th>Severity</th><th>Package</th><th>Title</th></tr>
    {% for vuln in vulnerabilities[:50] %}
    <tr class="severity-{{ vuln.Severity | lower }}">
      <td><a href="{{ vuln.PrimaryURL | e }}">{{ vuln.VulnerabilityID | e }}</a></td>
      <td>{{ vuln.Severity | e }}</td>
      <td>{{ vuln.PkgName | e }}</td>
      <td>{{ vuln.Title | e }}</td>
    </tr>
    {% endfor %}
  </table>

  {% if not vulnerabilities %}
  <p class="success">No vulnerabilities found!</p>
  {% endif %}
</body>
</html>

3. Update `report_generator.py`

from jinja2 import Environment, FileSystemLoader, select_autoescape

def generate_html_report(self, ...):
    env = Environment(
        loader=FileSystemLoader(TEMPLATES_DIR),
        autoescape=select_autoescape(["html"])  # Auto-escaping enabled by default
    )
    template = env.get_template("report.html.j2")
    html = template.render(
        image_name=image_name,
        scan_date=scan_date,
        vulnerabilities=vulnerabilities,
        severity_counts=severity_counts,
    )
    ...

Benefits

Current With Jinja2
Manual `{{VAR}}` substitution Auto-escaping (XSS-safe by default)
Vulnerability table built in Python Template loop in HTML file
Typos leave `{{VAR}}` visible `UndefinedError` at render time
One monolithic Python string Separate, editable `.j2` template files

Files to Modify

File Action
`requirements.txt` Add `Jinja2>=3.1.0`
`report_generator.py` Use `jinja2.Environment`
`docker_scanner.py` Remove inline HTML template string
`config.py` Remove `HTML_TEMPLATE` constant
`templates/report.html.j2` Create Jinja2 template
`tests/test_report_generator.py` Update/add HTML tests

Acceptance Criteria

  • HTML reports render identically to current output
  • Auto-escaping enabled (Jinja2 `select_autoescape`)
  • Template file lives in `templates/` directory
  • Report generation tests pass
  • No raw `{{VAR}}` strings remain in Python source

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestpythonPull requests that update python code

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions