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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions