How to Customize VulnClaw's Report Generation Template: 4 Proven Methods
VulnClaw uses Jinja2 templates defined as string literals in 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. 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. For example, to add a report author field after the project overview:
# 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:
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 and patch generate_report() (around line 300) to check for an environment variable:
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:
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 (line 168):
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 (line 215):
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:
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:
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:
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 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.pyasREPORT_TEMPLATEandCYCLE_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_TEMPLATEenvironment variable to load template files without touching source code. - Context Extension: Add fields to
ReportGenerateRequestinvulnclaw/web/schemas.pyand thread them throughvulnclaw/web/app.pyto expose API data to templates. - Format Options: Set
report_formatto"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. 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 (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 (line 168). Then pass the value from the request handler in 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).
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →