# How to Customize VulnClaw's Report Generation Template: 4 Proven Methods

> Customize VulnClaw report generation templates using 4 proven methods. Edit source templates, load external files, or extend the rendering context for personalized penetration-test reports.

- Repository: [Unclecheng/VulnClaw](https://github.com/Unclecheng-li/VulnClaw)
- Tags: how-to-guide
- Published: 2026-06-30

---

**VulnClaw uses Jinja2 templates defined as string literals in [`vulnclaw/report/generator.py`](https://github.com/Unclecheng-li/VulnClaw/blob/main/vulnclaw/report/generator.py) to render penetration-test reports, allowing customization by editing the source template, loading external files, or extending the rendering context.**

VulnClaw generates security assessment reports using embedded Jinja2 templates that transform scan data into formatted Markdown or HTML output. If you need to modify report branding, add custom fields, or completely restructure the output layout, you must interact with the template system located in the core generator module. This guide shows you how to customize VulnClaw report generation templates using four distinct approaches based on the actual source code implementation.

## Where Report Templates Live in VulnClaw

VulnClaw maintains two primary report templates as Python string constants inside [`vulnclaw/report/generator.py`](https://github.com/Unclecheng-li/VulnClaw/blob/main/vulnclaw/report/generator.py). The **`REPORT_TEMPLATE`** variable (starting at line 16) handles standard per-target reports, while **`CYCLE_REPORT_TEMPLATE`** (starting at line 374) generates persistent weekly cycle reports.

When the CLI or Web API triggers report generation, the `generate_report()` or `generate_persistent_cycle_report()` function loads the appropriate template, injects a **context dictionary** containing variables like `target`, `verified_count`, and `recommendations`, and calls `jinja2.Template.render()`. The resulting string is written as Markdown (`*.md`) or wrapped in a minimal HTML wrapper (lines 410-416) when `report_format="html"`.

## How to Customize VulnClaw Report Templates

### Method 1: Edit the Inline Template String

The fastest way to customize VulnClaw report generation templates is modifying the string literal directly in [`vulnclaw/report/generator.py`](https://github.com/Unclecheng-li/VulnClaw/blob/main/vulnclaw/report/generator.py). For example, to add a report author field after the project overview:

```python

# In vulnclaw/report/generator.py

REPORT_TEMPLATE = """\

# 渗透测试报告

## 1. 项目概述

| 项目 | 详情 |
|------|------|
| **测试目标** | {{ target }} |
| **报告作者** | {{ author }} |
| **测试时间** | {{ started_at }} |
...
"""

```

You must then extend the context dictionary in `generate_report()` (around line 68) to supply the new variable:

```python
context = {
    "target": session.target or "unknown",
    "author": "Security Team <security@example.com>",
    "started_at": session.started_at,
    # ... other variables

}

```

### Method 2: Load an External Template File

For maintainability, load a custom template file at runtime without modifying the source constants. Create a file named [`custom_report_template.md`](https://github.com/Unclecheng-li/VulnClaw/blob/main/custom_report_template.md) and patch `generate_report()` (around line 300) to check for an environment variable:

```python
import os
from jinja2 import Template

template_path = os.getenv("VULNCLAW_REPORT_TEMPLATE")
if template_path and os.path.isfile(template_path):
    with open(template_path, "r", encoding="utf-8") as f:
        template_src = f.read()
else:
    template_src = REPORT_TEMPLATE

template = Template(template_src)
report_content = template.render(**context)

```

Run VulnClaw with your custom template:

```bash
export VULNCLAW_REPORT_TEMPLATE=/path/to/custom_report_template.md
vulnclaw report generate --target 10.0.0.1

```

### Method 3: Extend the Rendering Context

To expose new data fields from the API to the template, extend the `ReportGenerateRequest` schema in [`vulnclaw/web/schemas.py`](https://github.com/Unclecheng-li/VulnClaw/blob/main/vulnclaw/web/schemas.py) (line 168):

```python
class ReportGenerateRequest(BaseModel):
    target: str
    format: Literal["markdown", "html"] = "markdown"
    custom_note: Optional[str] = None  # New field

```

Pass this value through the web handler in [`vulnclaw/web/app.py`](https://github.com/Unclecheng-li/VulnClaw/blob/main/vulnclaw/web/app.py) (line 215):

```python
async def report_target(request: ReportGenerateRequest):
    report_path = generate_report(
        session, 
        report_format=request.format,
        custom_note=request.custom_note
    )
    # ...

```

Update `generate_report()` to accept and inject the parameter:

```python
def generate_report(
    session: SessionState,
    output_path: Optional[str] = None,
    llm_attack_summary: str = "",
    report_format: str = "markdown",
    target_state_context: Optional[dict[str, Any]] = None,
    custom_note: Optional[str] = None,  # New parameter

) -> Path:
    context = {
        # ... existing variables

        "custom_note": custom_note or "",
    }
    # ...

```

Your template can now access `{{ custom_note }}` to display user-defined content.

### Method 4: Change the Output Format

VulnClaw supports both Markdown and HTML output without template modifications. The `report_format` parameter controls whether the generator produces raw Markdown or wraps content in HTML. Set the format via CLI:

```bash
vulnclaw report generate --target 10.0.0.1 --format html

```

Or specify `"html"` in the `ReportGenerateRequest` payload when using the Web API.

## Verifying Your Template Changes

Test your modifications using the VulnClaw CLI:

```bash
vulnclaw report generate --target 10.0.0.1 --format markdown

```

For Web UI verification, navigate to the **Risk Results** page and click the "Generate Report" button (handled in [`frontend/src/pages/RiskResultsPage.tsx`](https://github.com/Unclecheng-li/VulnClaw/blob/main/frontend/src/pages/RiskResultsPage.tsx) at line 179). The frontend calls `generateTargetReport(target, reportFormat)`, and your customized template will render immediately after a page refresh.

## Summary

- **Template Location**: VulnClaw stores Jinja2 templates in [`vulnclaw/report/generator.py`](https://github.com/Unclecheng-li/VulnClaw/blob/main/vulnclaw/report/generator.py) as `REPORT_TEMPLATE` and `CYCLE_REPORT_TEMPLATE`.
- **Inline Editing**: Modify string literals directly and update the context dictionary in `generate_report()` to inject new variables.
- **External Files**: Use the `VULNCLAW_REPORT_TEMPLATE` environment variable to load template files without touching source code.
- **Context Extension**: Add fields to `ReportGenerateRequest` in [`vulnclaw/web/schemas.py`](https://github.com/Unclecheng-li/VulnClaw/blob/main/vulnclaw/web/schemas.py) and thread them through [`vulnclaw/web/app.py`](https://github.com/Unclecheng-li/VulnClaw/blob/main/vulnclaw/web/app.py) to expose API data to templates.
- **Format Options**: Set `report_format` to `"markdown"` or `"html"` to control output wrapping behavior.

## Frequently Asked Questions

### Where are VulnClaw report templates stored?

VulnClaw report templates are stored as string literals inside [`vulnclaw/report/generator.py`](https://github.com/Unclecheng-li/VulnClaw/blob/main/vulnclaw/report/generator.py). The standard template is defined in the `REPORT_TEMPLATE` variable starting at line 16, and the persistent cycle report template is `CYCLE_REPORT_TEMPLATE` starting at line 374.

### Can I use an external file instead of editing the source code?

Yes. Create a custom template file and set the `VULNCLAW_REPORT_TEMPLATE` environment variable to its absolute path. Modify `generate_report()` in [`vulnclaw/report/generator.py`](https://github.com/Unclecheng-li/VulnClaw/blob/main/vulnclaw/report/generator.py) (around line 300) to check for this variable and load the file content using `open()` with UTF-8 encoding before falling back to the default template.

### How do I add custom variables to my VulnClaw report template?

First, add the field to `ReportGenerateRequest` in [`vulnclaw/web/schemas.py`](https://github.com/Unclecheng-li/VulnClaw/blob/main/vulnclaw/web/schemas.py) (line 168). Then pass the value from the request handler in [`vulnclaw/web/app.py`](https://github.com/Unclecheng-li/VulnClaw/blob/main/vulnclaw/web/app.py) (line 215) into the `generate_report()` function. Finally, update the `generate_report()` signature to accept the parameter and include it in the context dictionary passed to `template.render()`.

### Does VulnClaw support HTML report generation?

Yes. VulnClaw supports both Markdown and HTML output formats. Set `report_format="html"` in your API request or use the `--format html` CLI flag. When HTML is selected, the generator wraps the rendered Markdown content in a minimal HTML wrapper (implemented between lines 410-416 in [`vulnclaw/report/generator.py`](https://github.com/Unclecheng-li/VulnClaw/blob/main/vulnclaw/report/generator.py)).