Report Template
Pentest Report Template Guide
Guide for creating Word document (.docx) templates for penetration test reports using python-docx-template (docxtpl) with Jinja2 syntax.
Overview
The report generator fetches data and renders it into Word templates using Jinja2 templating. Templates are stored in S3 and downloaded at generation time.
Template Syntax
python-docx-template uses Jinja2 syntax with special delimiters for Word documents:
- Variables:
{{ variable_name }} - Loops:
{%tr for item in items %}...{%tr endfor %}(table rows) - Conditionals:
{% if condition %}...{% endif %} - Paragraph loops:
{%p for item in items %}...{%p endfor %}
Important Notes
- Use
{%tr ... %}for table row loops (thetrindicates table row) - Use
{%p ... %}for paragraph loops - Use
{% ... %}for inline control structures - Variables with
{{ }}can be placed anywhere in text
Available Template Variables
Report Metadata
{{ organization_name }}- Organization name{{ report_date }}- Report date (Month Year format){{ generated_at }}- Full timestamp{{ report_type }}- Type of report
Attack Surface Totals
Zone-Based Assets:
{{ total_hosts }}- Total number of hosts discovered{{ total_services }}- Total number of services discovered{{ total_web_apps }}- Total number of web applications{{ total_infrastructure }}- Total infrastructure devices
Cloud Assets:
{{ total_s3_buckets }}- AWS S3 buckets{{ total_elbs }}- AWS Elastic Load Balancers{{ total_cloudfront }}- AWS CloudFront distributions{{ total_app_services }}- Azure App Services{{ total_storage_accounts }}- Azure Storage Accounts{{ total_github_repos }}- GitHub repositories{{ total_azure_devops_repos }}- Azure DevOps repositories{{ total_agents }}- Endpoint agents
Findings Summary
{{ critical_count }}- Number of critical findings{{ high_count }}- Number of high findings{{ medium_count }}- Number of medium findings{{ low_count }}- Number of low findings{{ info_count }}- Number of informational findings{{ total_findings }}- Total findings count{{ manual_findings_count }}- Number of manual findings
List/Table Variables
Attack Surface by Zone
{%tr for zone in attack_surface %}
{{ zone.zone_name }}
{{ zone.infrastructure_count }}
{{ zone.hosts_count }}
{{ zone.services_count }}
{{ zone.web_apps_count }}
{%tr endfor %}Fields available:
zone.zone_name- Zone namezone.infrastructure_count- Infrastructure device countzone.hosts_count- Host countzone.services_count- Service countzone.web_apps_count- Web application count
Findings by Zone
This table includes both zone-based findings and cloud asset findings. Cloud assets (S3, CloudFront, ELBs, Azure App Services, Storage Accounts, GitHub repos, Azure DevOps repos, Agents) appear as additional rows with their asset type as the zone_name.
{%tr for zone in findings_by_zone %}
{{ zone.zone_name }}
{{ zone.critical_count }}
{{ zone.high_count }}
{{ zone.medium_count }}
{{ zone.low_count }}
{%tr endfor %}Fields available:
zone.zone_name- Zone name or cloud asset type (e.g., "AWS S3 Buckets", "GitHub Repositories")zone.critical_count- Critical findings countzone.high_count- High findings countzone.medium_count- Medium findings countzone.low_count- Low findings count
Manual Findings by Zone
Same structure as Total Findings - includes both zone-based and cloud asset findings.
{%tr for zone in manual_findings_by_zone %}
{{ zone.zone_name }}
{{ zone.critical_count }}
{{ zone.high_count }}
{{ zone.medium_count }}
{{ zone.low_count }}
{%tr endfor %}Recommendations Summary
{%tr for rec in recommendations %}
{{ rec.recommendation }}
{{ rec.risk }}
{{ rec.percentage }}%
{%tr endfor %}Fields available:
rec.recommendation- Recommendation categoryrec.risk- Risk level in Title Case (Critical, High, Medium, Low)rec.percentage- Percentage of findings this addresses
Finding Details
Findings are deduplicated by vulnerability - multiple instances of the same vulnerability across different assets are grouped together with all affected assets listed.
{%p for finding in critical_findings %}
{{ finding.vulnerability_name }}
Severity: Critical
CVSS Score: {{ finding.base_score }}
Exploitable: {% if finding.vulnerability_exploitable %}Yes{% else %}No{% endif %}
Compromised: {{ finding.compromised }}
Description
{{ finding.vulnerability_summary }}
Remediation
{%p for rem in finding.remediations %}
{{ rem.description }}
{%p endfor %}
Vulnerable Assets
{%p for asset in finding.affected_assets %}
{{ asset }}
{%p endfor %}
{%p endfor %}Available Finding Fields
finding.vulnerability_name- Name of the vulnerabilityfinding.vulnerability_id- Unique vulnerability IDfinding.vulnerability_summary- Description of the vulnerabilityfinding.severity- Numeric severity (0-4)finding.severity_name- Severity name (Critical, High, etc.)finding.base_score- CVSS base scorefinding.overall_score- Overall CVSS scorefinding.vulnerability_exploitable- Boolean - is it exploitablefinding.compromised- Compromise level achievedfinding.affected_assets- List of affected asset stringsfinding.affected_asset_count- Count of affected assetsfinding.remediations- List of remediation objectsfinding.references- List of reference objects
Remediation Fields
rem.name- Remediation namerem.description- Full remediation description (HTML converted to formatted text)rem.category_display- Category display namerem.solution_type_display- Solution type
Note on HTML Content: Remediation descriptions may contain HTML formatting including tables, lists, and code blocks. The generator automatically converts this to formatted text:
- Bold/italic text is preserved
- Lists are converted to bullet points
- Code blocks use monospace font
- Tables are rendered as aligned text with pipe separators and header separators
Reference Fields
ref.reference_type- Type (URL, CVE, etc.)ref.reference_url- URL of the reference
Complete Template Structure Example
Below is the recommended structure for a pentest report template.
Cover Page
Initial Penetration Test Report (External)
{{ report_date }}
Prepared for: {{ organization_name }}
Prepared by: {{ company_name }}
Executive Summary
Background
[Standard background text about penetration testing...]
Objective
[Standard objective text...]
Methodology
[Standard methodology text...]
Results
{{ company_name }} tested from the following perspectives:
- Internet facing
After initial analysis of all external systems and services, it was determined
{{ total_web_apps }} web applications are exposed on the Internet along with
{{ total_hosts }} hosts and {{ total_services }} services.
Numbers Summary
| Exceptions | % w/o Critical or High | Critical | High | Medium |
|------------|------------------------|----------|------|--------|
| 0 | XX% | {{ critical_count }} | {{ high_count }} | {{ medium_count }} |
Recommendations
{%tr for rec in recommendations %}
| {{ rec.recommendation }} | {{ rec.risk }} | {{ rec.percentage }}% |
{%tr endfor %}
Technical Volume - Attack Surface
Attack Surface
| Location | Infrastructure | Hosts | Services | Web Apps |
|----------|---------------|-------|----------|----------|
{%tr for zone in attack_surface %}
| {{ zone.zone_name }} | {{ zone.infrastructure_count }} | {{ zone.hosts_count }} | {{ zone.services_count }} | {{ zone.web_apps_count }} |
{%tr endfor %}
| **Total** | {{ total_infrastructure }} | {{ total_hosts }} | {{ total_services }} | {{ total_web_apps }} |
Technical Volume - Findings Summary
Both tables include zone-based findings and cloud asset findings (S3, CloudFront, ELBs, Azure services, repositories, agents) in the same table.
Total Findings
| Location | Critical | High | Medium | Low |
|----------|----------|------|--------|-----|
{%tr for zone in findings_by_zone %}
| {{ zone.zone_name }} | {{ zone.critical_count }} | {{ zone.high_count }} | {{ zone.medium_count }} | {{ zone.low_count }} |
{%tr endfor %}
Manual Findings
| Location | Critical | High | Medium | Low |
|----------|----------|------|--------|-----|
{%tr for zone in manual_findings_by_zone %}
| {{ zone.zone_name }} | {{ zone.critical_count }} | {{ zone.high_count }} | {{ zone.medium_count }} | {{ zone.low_count }} |
{%tr endfor %}
Finding Details
CRITICAL RISK FINDINGS
{%p for finding in critical_findings %}
────────────────────────────────────────────────────────────
{{ finding.vulnerability_name }}
Severity: Critical
CVSS Score: {{ finding.base_score }}
Exploitable: {% if finding.vulnerability_exploitable %}Yes{% else %}No{% endif %}
Compromised: {{ finding.compromised }}
Description
{{ finding.vulnerability_summary }}
Remediation
{%p for rem in finding.remediations %}
{{ rem.description }}
{%p endfor %}
Vulnerable Assets
{%p for asset in finding.affected_assets %}
{{ asset }}
{%p endfor %}
{%p endfor %}
HIGH RISK FINDINGS
{%p for finding in high_findings %}
[Same structure as critical findings...]
{%p endfor %}
MEDIUM RISK FINDINGS
{%p for finding in medium_findings %}
[Same structure as critical findings...]
{%p endfor %}
Creating the Template in Word
- Create a new Word document or copy an existing report
- Replace static text with Jinja2 variables (e.g., replace "Examworks" with
{{ organization_name }}) - For tables with dynamic rows:
- Create a table with header row
- Add one data row with variables
- Wrap the data row with
{%tr for ... %}and{%tr endfor %}
- For repeated sections:
- Use
{%p for ... %}at the start of a paragraph - Use
{%p endfor %}at the end
- Use
- Save as .docx (not .doc)
- Upload to S3 template bucket
Tips
- Keep formatting in the template - it will be preserved
- Test with sample data first
- Use conditional blocks to hide empty sections:
{% if critical_findings %}
CRITICAL RISK FINDINGS
{%p for finding in critical_findings %}...{%p endfor %}
{% endif %}Environment Variables
The report generator uses these environment variables:
DB_HOST- PostgreSQL host(s), default: localhostDB_NAME- Database name, default: cyberoptixDB_USER- Database user, default: postgresDB_PASSWD- Database passwordBROKER_URL- Redis connection URL, default: redis://localhost:6379/0AWS_ACCESS_KEY_ID- AWS access keyAWS_SECRET_ACCESS_KEY- AWS secretAWS_REGION- AWS region, default: us-east-1COMPANY_NAME- Your company name, default: CyberOptixWORKER_THREADS- Concurrent workers, default: 4
Task Queue Message Format
The Go API sends tasks to the Redis queue with this structure:
{
"id": "task-uuid",
"organization_id": "org-uuid",
"report_id": "report-uuid",
"template_id": "template-uuid",
"template_bucket": "cyberoptix-org-uuid-templates",
"template_key": "pentest-report-template.docx",
"output_bucket": "cyberoptix-org-uuid-reports",
"name": "External Penetration Test Report",
"generated_by": "user-uuid",
"generated_by_name": "John Doe",
"generation_params": "{\"filters\":{\"statuses\":[\"open\"]},\"report_type\":\"pentest\"}"
}Generation Parameters
The generation_params JSON can include:
{
"filters": {
"statuses": ["open"],
"zone_id": "optional-zone-uuid",
"severity": [4, 3, 2],
"manual_only": false
},
"report_type": "pentest",
"custom_data": {
"tester_name": "Avery Rozar",
"engagement_dates": "June 17 - July 22, 2025"
}
}Custom data fields are merged into the template context and can be accessed directly (e.g., {{ tester_name }}).
Next Steps
- Create your Word template using the variables above
- Upload the template to your S3 templates bucket
- Configure template in CyberOptix platform
- Generate test reports to verify formatting
- Iterate on template design as needed
Additional Resources
Updated about 6 hours ago