Report Generation Architecture#
The Report Generation Architecture in opnDossier is a multi-format export system that transforms OPNsense firewall configuration files into documentation and analysis reports. The convert command provides neutral configuration documentation, while the audit command supports two distinct report modes: Blue Team (defensive audit) and Red Team (attack surface analysis), each tailored to different operational perspectives. Unlike conventional template-based documentation generators, opnDossier employs programmatic generation via MarkdownBuilder, which uses direct method calls to the github.com/nao1215/markdown library for type-safe, compile-time validated markdown generation.
The architecture follows a three-tier design pattern separating concerns between formatting utilities (internal/converter/formatters/), report structure builders (internal/converter/builder/), and orchestration components (Generator interfaces). This separation enables the system to export to multiple formats (markdown, JSON, YAML, HTML, plaintext) while maintaining a single source of truth for configuration parsing and transformation. All report generation modules share common interfaces (ReportBuilder, Generator), the device-agnostic CommonDevice model, and the shared analysis package (internal/analysis/) for detection logic, statistics computation, and rule comparison.
The system is designed for offline-first, operator-focused workflows where exported files must be valid and parseable by standard tools. Smart file naming with overwrite protection ensures safe operation, with the -f flag available to force overwrites when needed.
Architecture Overview#
Design Philosophy#
The Report Generation Architecture embodies several key design principles:
- Programmatic over Template-Based: Uses direct Go method calls instead of text templates for markdown generation, providing type safety, compile-time validation, and enhanced maintainability
- Separation of Concerns: Clear boundaries between formatting logic, report structure, and orchestration
- Multi-Format Support: Single data processing pipeline serving multiple output formats
- Mode-Based Reporting: Two distinct audit modes (Blue/Red) with unified base generation and mode-specific augmentation
Data Processing Pipeline#
The system follows a four-stage data processing pipeline:
- XML Parsing: OPNsense config.xml files are parsed using Go's built-in
encoding/xmlpackage - Data Conversion: Platform-specific XML DTOs are transformed into the device-agnostic CommonDevice model
- Report Generation: MarkdownBuilder programmatically generates markdown through method calls
- Output Rendering: Formatted output is rendered to terminal or exported to files
Three-Tier Architecture#
The converter package implements a layered architecture:
internal/
├── analysis/ # Shared analysis logic
│ ├── detect.go # Detection functions (dead rules, unused interfaces, security/performance/consistency issues)
│ ├── statistics.go # Statistics computation (ComputeStatistics, scoring)
│ ├── rules.go # Rule comparison (RulesEquivalent)
│ └── helpers.go # Shared helper functions
└── converter/
├── formatters/ # Tier 1: Pure formatting functions
├── builder/ # Tier 2: Report structure and assembly
└── hybrid_generator.go # Tier 3: Multi-format orchestration
- Detection logic:
DetectDeadRules(),DetectUnusedInterfaces(),DetectSecurityIssues(),DetectPerformanceIssues(),DetectConsistency() - Statistics computation:
ComputeStatistics(),ComputeSecurityScore(),ComputeConfigComplexity() - Rule comparison:
RulesEquivalent()for duplicate detection - All exported functions include nil guards for defensive API safety
- Used by both converter (for JSON/YAML export) and processor (for runtime analysis)
- Uses typed enum constants (e.g.,
common.RuleTypeBlock,common.SeverityCritical) for type-safe comparisons
- Pure functions with no dependencies on report structure
- Boolean formatting, timestamp conversion, markdown escaping
- Security risk assessment and data transformations
- Reusable across all report types and formats
- Structure-aware report construction
- Section assembly and table generation
- Depends on Tier 1 formatters for display details
- Implements ReportBuilder interface, which composes SectionBuilder, TableWriter, and ReportComposer sub-interfaces
- Multi-format routing via FormatRegistry dispatch
- Streaming and string-based output management
- Implements Generator and StreamingGenerator interfaces
- Uses
handlerForFormat()registry lookups instead of switch statements
Core Components#
MarkdownBuilder#
The MarkdownBuilder is the primary report generation engine. It provides programmatic markdown generation through method-based composition. MarkdownBuilder implements all three sub-interfaces (SectionBuilder, TableWriter, ReportComposer) that compose the ReportBuilder interface, providing backward compatibility through interface composition.
The implementation is organized across domain-specific files in internal/converter/builder/:
builder.go: Core MarkdownBuilder struct, ReportBuilder composite interface (SectionBuilder + TableWriter + ReportComposer), and primary sections (system, network, security)builder_vpn.go: VPN-related methods (IPsec, OpenVPN, VLAN, static routes, HA/CARP configuration)builder_services.go: Services-related methods (DHCP, DNS, SNMP, NTP, load balancer monitors)
Structure:
type MarkdownBuilder struct {
config *common.CommonDevice
logger *logging.Logger
generated time.Time
toolVersion string
}
Key Characteristics:
- Minimal mutable state: Configuration set once at construction, read-only thereafter
- Stateless section generation: Each section operates independently
- Efficient string building: Uses
bytes.Bufferto reduce memory allocations - Type-safe generation: Compile-time validation via method calls
Main Methods:
- Generates neutral configuration documentation
- Includes system, network, security, and services sections
- Creates table of contents with navigation links
- Extended report with additional detail
- Adds VPN (IPsec, OpenVPN), high availability, and IDS sections
- Includes all Standard report content
Section Builders:
- BuildSystemSection(): System configuration
- BuildNetworkSection(): Network interfaces
- BuildSecuritySection(): Firewall rules and NAT
- BuildServicesSection(): DHCP, DNS, SNMP, NTP (located in
builder_services.go) - BuildIPsecSection(), BuildOpenVPNSection(), BuildHASection(): VPN and HA configuration (located in
builder_vpn.go) - BuildAuditSection(): Compliance audit findings (returns empty string when
ComplianceResultsis nil). Partitions findings by Type field: security findings rendered in "Security Findings" table (Severity, Component, Title, Recommendation columns), inventory findings (Type: "inventory") rendered in separate "Configuration Notes" table (Component, Title, Details columns)
Table Generation:
All table methods accept a *markdown.Markdown instance and return it for method chaining:
- WriteFirewallRulesTable(): Firewall rules with source/dest/action
- WriteInterfaceTable(): Interface configuration
- WriteOutboundNATTable() / WriteInboundNATTable(): NAT rules
- WriteDHCPSummaryTable(), WriteDHCPStaticLeasesTable(): DHCP configuration (located in
builder_services.go)
Programmatic Generation Approach#
The MarkdownBuilder uses the github.com/nao1215/markdown library (v0.10.0) for fluent API-based markdown construction:
md := markdown.NewMarkdown(&buf).
H1("OPNsense Configuration Summary").
H2("System Information").
PlainTextf("%s: %s", markdown.Bold("Hostname"), sys.Hostname).LF().
PlainTextf("%s: %s", markdown.Bold("Domain"), sys.Domain).LF()
Benefits over Template-Based Generation:
- Type Safety: Compile-time validation of markdown structure
- Performance: No template parsing overhead, direct code execution
- Composability: Methods can be chained and conditionally called
- Maintainability: Pure Go code, no template language complexity
- Testability: Direct unit testing of generation methods
Formatting Utilities (formatters/)#
The formatters package provides pure, stateless functions for data display and transformation:
formatters.go: Basic formatting
FormatBool(): Converts booleans to checkmark/x-markFormatBytes(): Human-readable byte sizesFormatTimestamp(): ISO 8601 timestamp formattingFormatInterfacesAsLinks(): Markdown links for interfaces
security.go: Security assessment
CalculateSecurityScore(): 0-100 security score calculationAssessRiskLevel(): Severity to risk level mapping (emoji + text)AssessServiceRisk(): Service-specific security risk evaluationGroupServicesByStatus(): Running/stopped service grouping
transformers.go: Data transformations
FilterSystemTunables(): Sysctl filtering- Data aggregation and grouping utilities
utils.go: String utilities
EscapeTableContent(): Markdown special character escaping- String manipulation helpers
Generator Interfaces#
The system defines two complementary interfaces for report generation:
Generator Interface (String-based):
type Generator interface {
Generate(ctx context.Context, cfg *common.CommonDevice, opts Options) (string, error)
}
StreamingGenerator Interface (Memory-efficient):
type StreamingGenerator interface {
Generator
GenerateToWriter(ctx context.Context, w io.Writer, cfg *common.CommonDevice, opts Options) error
}
The HybridGenerator implements both interfaces and coordinates multi-format output:
type HybridGenerator struct {
builder reportGenerator // Narrowed interface (internal field)
logger *logging.Logger
}
Interface Narrowing: Internally, HybridGenerator uses a narrower reportGenerator interface (composed of auditBuilder and ReportComposer) that exposes only the methods it directly calls: SetIncludeTunables, SetFailuresOnly, BuildAuditSection, BuildStandardReport, and BuildComprehensiveReport. The public API (SetBuilder/GetBuilder) continues to work with the full ReportBuilder interface for backward compatibility, with GetBuilder using a two-value type assertion to recover the full interface. This refactoring implements the Interface Segregation Principle (closed issue #323) with no breaking changes to existing code.
CommonDevice Model#
The CommonDevice struct in pkg/model/ provides a platform-agnostic device representation:
type CommonDevice struct {
DeviceType DeviceType `json:"device_type" yaml:"device_type"`
Version string `json:"version,omitempty" yaml:"version,omitempty"`
System System `json:"system" yaml:"system,omitempty"`
Interfaces []Interface `json:"interfaces,omitempty" yaml:"interfaces,omitempty"`
FirewallRules []FirewallRule `json:"firewallRules,omitempty" yaml:"firewallRules,omitempty"`
NAT NATConfig `json:"nat" yaml:"nat,omitempty"`
VPN VPN `json:"vpn" yaml:"vpn,omitempty"`
Routing Routing `json:"routing" yaml:"routing,omitempty"`
ComplianceResults *ComplianceResults `json:"complianceResults,omitempty" yaml:"complianceResults,omitempty"`
}
This model serves as the single source of truth shared by:
- All report generators (Blue and Red audit modes, plus convert command for neutral documentation)
- All output formats (markdown, JSON, YAML, HTML, plaintext)
- All formatters and transformation functions
The ComplianceResults field stores audit findings populated by handleAuditMode() before enrichment, enabling automatic serialization in JSON/YAML formats and programmatic rendering in markdown through BuildAuditSection().
ComplianceControl Model:
The ComplianceControl struct in pkg/model/enrichment.go includes a Status field populated during audit result mapping:
type ComplianceControl struct {
ID string `json:"id,omitempty" yaml:"id,omitempty"`
Status string `json:"status" yaml:"status"` // "PASS" or "FAIL"
Title string `json:"title,omitempty" yaml:"title,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Category string `json:"category,omitempty" yaml:"category,omitempty"`
Severity string `json:"severity,omitempty" yaml:"severity,omitempty"`
}
The Status field uses two constants defined in pkg/model/enrichment.go:
ControlStatusPass = "PASS"- Indicates a compliant controlControlStatusFail = "FAIL"- Indicates a non-compliant control
This field is populated by the mapControls() function in cmd/audit_handler.go, which derives status from the plugin's Compliance map during audit result mapping. The self-describing status enables both structured JSON/YAML exports and markdown table rendering without re-deriving compliance state.
Type Safety with Typed Enums (PR #452): The CommonDevice model uses typed string enums for compile-time safety and to eliminate magic string literals. Key typed fields include:
FirewallRule.Type: UsesFirewallRuleTypeenum (RuleTypePass,RuleTypeBlock,RuleTypeReject)FirewallRule.Direction: UsesFirewallDirectionenum (DirectionIn,DirectionOut,DirectionAny)FirewallRule.IPProtocol: UsesIPProtocolenum (IPProtocolInet,IPProtocolInet6)NATConfig.OutboundMode: UsesNATOutboundModeenum (OutboundAutomatic,OutboundHybrid,OutboundAdvanced,OutboundDisabled)LAGG.Protocol: UsesLAGGProtocolenum (LAGGProtocolLACP,LAGGProtocolFailover,LAGGProtocolLoadBalance,LAGGProtocolRoundRobin)VirtualIP.Mode: UsesVIPModeenum (VIPModeCarp,VIPModeIPAlias,VIPModeProxyARP)
These typed enums provide compile-time validation of field values and prevent string typos. When JSON/YAML serialization is required, the enums automatically convert to their string representation. When aggregating statistics or using map keys, explicit casts to string are used (e.g., stats.RulesByType[string(rule.Type)]++).
Two Audit Modes#
The audit command provides two report modes with distinct purposes and audiences, orchestrated by the ModeController in internal/audit/mode_controller.go. Neutral configuration documentation is handled by the convert command, which does not run compliance checks or security analysis.
Architecture Pattern#
All modes share a unified generation process:
- Audit Analysis: ModeController runs compliance checks and generates an
audit.Report - Data Mapping:
handleAuditMode()maps the audit results todevice.ComplianceResultsviamapAuditReportToComplianceResults() - Standard Pipeline: Report generation is delegated to
generateWithProgrammaticGenerator(), which flows through the standard HybridGenerator pipeline
This design eliminates format-specific code in cmd/audit_handler.go. All rendering logic lives in internal/converter/builder/, ensuring consistent audit section output across all formats (markdown, JSON, YAML).
Finding Data Structure#
Unified Type System (PR #391): As of PR #391, opnDossier uses a canonical Finding struct and Severity type defined in internal/analysis/, eliminating previous duplication across packages. The type hierarchy is:
analysis.Finding: Canonical finding struct ininternal/analysis/finding.gocompliance.Finding: Type alias toanalysis.Findingprocessor.Finding: Type alias toanalysis.Findingaudit.Finding: Embedsanalysis.Findingand adds audit-specific fields (AttackSurface,ExploitNotes,Control)
All modules now use the same underlying structure, ensuring consistency across audit, compliance, and processor packages.
Finding Structure:
The analysis.Finding struct contains the following key fields:
Type: Category of the finding (e.g., "compliance", "security", "inventory"). Used for classification and rendering partitioning in BuildAuditSection(). Findings withType: "inventory"are rendered in a separate "Configuration Notes" section rather than the main "Security Findings" table.Severity: Severity level of the finding usinganalysis.Severitytype ("critical", "high", "medium", "low", "info"). This field is displayed in report tables (except for inventory findings) and used for severity breakdowns.Title: Short title of the findingDescription: Detailed description of the issueRecommendation: Actionable remediation guidanceComponent: Configuration component involvedReference: Additional documentation linksReferences: Related standard or control identifiersTags: Classification labelsMetadata: Arbitrary key-value pairs for additional context
Severity Constants:
Severity levels are defined in internal/analysis/finding.go as constants:
analysis.SeverityCritical- Critical findings requiring immediate attentionanalysis.SeverityHigh- High-severity findings to address soonanalysis.SeverityMedium- Medium-severity findings worth investigatinganalysis.SeverityLow- Low-severity findings for general improvementanalysis.SeverityInfo- Informational findings with no immediate action required
All compliance plugins derive Severity from control metadata using the deriveSeverityFromControl() function, which resolves severity from the plugin's control definitions based on finding references. This ensures consistent severity classification across all plugins.
Blue Team Mode#
Purpose: Defensive security audit with compliance checks
Additional Content:
- Compliance checks via plugin system (STIG, SANS, Firewall frameworks)
- Security findings with severity ratings
- Compliance analysis against frameworks
- Actionable remediation recommendations
- Structured configuration tables for analysis
- Per-plugin severity breakdown showing critical/high/medium/low finding counts per compliance plugin
- Unified controls table with Status column (PASS/FAIL) when control data is available
Unified Controls Table:
When plugin Control data is available, Blue Team reports render a unified controls table that includes both compliant and non-compliant controls with a Status column showing PASS or FAIL. This approach provides complete compliance reporting by showing all evaluated controls, not just failures. The --failures-only flag filters this table to show only non-compliant controls (Status=FAIL). When Control data is unavailable, the system falls back to the legacy findings-only table.
Per-Plugin Severity Breakdown:
Blue Team reports include granular severity statistics for each compliance plugin through the computePerPluginSummary() function. This function:
- Calculates severity counts (critical/high/medium/low/info) for each plugin's findings using the shared
countSeverities()helper - Uses
analysis.Severityconstants for consistent severity classification - Stores results in per-plugin
ComplianceResultentries via thePluginFindingsmap - Enables
appendAuditFindings()to render detailed severity breakdowns per plugin in the output report, including informational findings count ("Informational: N")
The severity counting logic is DRY (Don't Repeat Yourself) via the countSeverities() helper function, which tallies findings by severity level for both aggregate summaries and per-plugin breakdowns. This ensures consistency across all severity statistics in the report, with all severity values represented using the canonical analysis.Severity type.
Metadata: report_type: "blue_team"
Example Findings:
- Insecure SNMP configurations (SNMPv1/v2c without community string protection)
- Allow-all firewall rules without source/destination restrictions
- Expired or expiring certificates
- Missing or weak authentication on services
Usage:
opndossier audit config.xml --mode blue
# Show only failing controls in plugin results tables
opndossier audit config.xml --mode blue --failures-only
Red Team Mode#
Purpose: Attacker-focused reconnaissance and attack surface enumeration
Additional Content:
- WAN-exposed services identification
- Weak NAT rules discovery
- Admin portal discovery
- Attack surface mapping
- Enumeration data (hostnames, static DHCP leases, service ports)
- Optional blackhat commentary (enabled via
--audit-blackhat)
Metadata: report_type: "red_team", blackhat_mode: bool
Finding Structure: Includes AttackSurface and ExploitNotes fields specific to offensive security
Usage:
opndossier audit config.xml --mode red
opndossier audit config.xml --mode red --audit-blackhat # with commentary
Multi-Format Export#
Supported Formats#
The system exports to five output formats through a unified architecture:
- Markdown - Human-readable documentation (default)
- JSON - Machine-readable structured data
- YAML - Configuration management friendly
- HTML - Web browser viewing
- Plain Text - Terminal/email friendly
FormatRegistry Architecture#
The FormatRegistry serves as the single source of truth for supported output formats, replacing scattered constant definitions and switch statements across the codebase. All format-related operations—validation, alias resolution, file extension mapping, and generation dispatch—route through DefaultRegistry.
Key Components:
- FormatHandler Interface: Defines
Generate(),GenerateToWriter(),FileExtension(), andAliases()methods that encapsulate format-specific logic - DefaultRegistry: Package-level singleton pre-populated with handlers for all five formats (markdown, json, yaml, text, html)
- Alias Resolution:
DefaultRegistry.Canonical()maps short aliases (md,yml,txt,htm) to canonical format names - Handler Dispatch:
HybridGenerator.Generate()andGenerateToWriter()usehandlerForFormat()for registry-based dispatch instead of switch statements
Adding New Formats:
To register a new output format, implement the FormatHandler interface and register it in newDefaultRegistry(). All validation, shell completion, file extension handling, and generation routing will automatically support the new format with no changes to caller code.
Related Documentation: See AGENTS.md §5.9b for detailed implementation guidance on the FormatRegistry pattern.
Format-Specific Processing#
Markdown Generation:
- Uses MarkdownBuilder directly: Calls
BuildStandardReport()orBuildComprehensiveReport() - Automatically appends
BuildAuditSection()whenComplianceResultsis present viaHybridGenerator.generateMarkdown() - Programmatic method-based generation via nao1215/markdown library
- Primary format with full feature support
- Handler:
markdownHandlerwith aliases["md"]and extension.md
JSON/YAML Export:
- Serializes CommonDevice directly via
prepareForExport() - Delegates to
analysis.ComputeStatistics()andanalysis.ComputeAnalysis()for computed fields ComplianceResultsfield serialized automatically via struct tags - no special handling needed- Supports redaction mode for sensitive fields
- Dead rule findings include structured
Kindfield ("unreachable"or"duplicate") for classification - Handlers:
jsonHandler(no aliases,.json) andyamlHandler(alias["yml"],.yaml)
HTML Export:
- Generates markdown first via MarkdownBuilder
- Converts to HTML using goldmark library via
RenderMarkdownToHTML() - Preserves tables, headers, and formatting
- Handler:
htmlHandlerwith aliases["htm"]and extension.html
Plain Text Export:
- Generates markdown first via MarkdownBuilder
- Strips markdown formatting via
StripMarkdownFormatting()for terminal output - Maintains structure and readability
- Handler:
textHandlerwith aliases["txt"]and extension.txt
CLI Usage Examples#
# Export to JSON for scripting
opndossier convert config.xml --format json -o config.json
# Export to YAML for configuration management
opndossier convert config.xml --format yaml -o config.yaml
# Export to Markdown for documentation
opndossier convert config.xml --format markdown -o config.md
# Export to HTML for web viewing
opndossier convert config.xml --format html -o config.html
# Export to plain text for terminal/email
opndossier convert config.xml --format text -o config.txt
# Use format aliases (md, yml, txt, htm)
opndossier convert config.xml --format md -o config.md
# Export with redaction for sensitive data
opndossier convert config.xml --format json --redact -o config.json
Processor Integration#
The processor package supports all five output formats through integration with the converter's FormatRegistry:
- Format Resolution:
processor.Transform()usesDefaultRegistry.Canonical()to resolve format aliases before dispatching - Text Format: Generates markdown then calls
converter.StripMarkdownFormatting()(exported function) - HTML Format: Generates markdown then calls
converter.RenderMarkdownToHTML()(exported function) - JSON/YAML/Markdown: Native processor implementations via
ToJSON(),toYAML(), andtoMarkdown() - Alias Support: All standard aliases (
md,yml,txt,htm) work consistently across converter and processor code paths
This unified approach ensures format aliases and validation rules remain consistent between the converter and processor packages, with the FormatRegistry as the single source of truth.
Redaction Support#
The redaction system replaces sensitive fields with [REDACTED] placeholders:
- Pre-shared keys and passwords
- SNMP community strings
- API keys and tokens
- Certificate private keys (
Certificate.PrivateKeyin theCertificatesslice) - Certificate authority private keys (
CertificateAuthority.PrivateKeyin theCAsslice) - User password hashes
Implementation Details:
The redaction logic in internal/processor/report.go uses deep-copy behavior via redactedCopyUnsafe() to ensure the original CommonDevice structure remains unchanged during JSON/YAML serialization. Redaction is conditional—only entries with non-empty sensitive values are redacted. Helper functions hasCertPrivateKeys() and hasCAPrivateKeys() gate the redaction logic to avoid unnecessary processing when no sensitive data is present. Certificate and CA slices are deep-copied via make + copy before mutation to prevent unintended side effects.
See AGENTS.md §5.25 for expanded redaction implementation details.
Available in JSON and YAML formats via --redact flag.
Module Organization#
Directory Structure#
internal/
├── analysis/ # Shared analysis logic
│ ├── detect.go # Dead rule, unused interface, and issue detection
│ ├── statistics.go # Statistics computation and scoring
│ ├── rules.go # Rule comparison (RulesEquivalent)
│ ├── helpers.go # Shared helper functions (FindInterface, FindDHCPScope)
│ ├── detect_test.go # Detection tests
│ ├── statistics_test.go # Statistics computation tests
│ ├── rules_test.go # Rule comparison tests
│ └── helpers_test.go # Helper function tests
└── converter/
├── builder/ # Report construction layer
│ ├── builder.go # MarkdownBuilder, ReportBuilder interface, core sections
│ ├── builder_vpn.go # VPN-related builder methods (IPsec, OpenVPN, HA/CARP)
│ ├── builder_services.go # Services-related builder methods (DHCP, DNS, SNMP, NTP)
│ ├── writer.go # Streaming output support (SectionWriter)
│ ├── helpers.go # Helper methods wrapping formatters
│ └── errors.go # Builder-specific errors
├── formatters/ # Shared formatting utilities
│ ├── formatters.go # Basic formatting (bool, bytes, timestamp)
│ ├── security.go # Security scoring and risk assessment
│ ├── transformers.go # Data filtering and aggregation
│ └── utils.go # String escaping and manipulation
├── registry.go # FormatRegistry and FormatHandler interface
├── registry_test.go # Registry unit tests
├── hybrid_generator.go # Multi-format orchestration
├── options.go # Configuration options struct
├── enrichment.go # Export preparation and redaction
├── adapter.go # Legacy compatibility adapter
├── compat.go # Deprecated type aliases
├── errors.go # Package-level errors
├── markdown.go # Markdown converter (legacy)
├── json.go # JSON converter
├── yaml.go # YAML converter
├── html.go # HTML converter
└── plaintext.go # Plain text converter
Dependency Relationships#
One-Way Dependencies:
- Builder uses formatters for display details
- Formatters have no knowledge of builder
- Enables formatter reuse across packages
- Converter delegates to
analysis.ComputeStatistics()andanalysis.ComputeAnalysis()for export enrichment - Analysis package has no knowledge of converter specifics
- Enables shared detection logic between converter and processor
- Processor delegates to analysis package for detection and statistics
- Processor adds runtime-specific checks and context-aware reporting
- Shared logic eliminates code duplication
Composition:
HybridGenerator contains reportGenerator
- Generator delegates markdown generation to builder
- Handles format routing and output management
- Allows swapping builder implementations
- Internal field uses narrowed
reportGeneratorinterface (Interface Segregation Principle) - Public API maintains full
ReportBuildercompatibility via type assertions
Shared vs. Report-Specific Code#
Shared Common Code:
- CommonDevice model: Platform-agnostic device representation
- Analysis package: Detection logic, statistics computation, rule comparison
- Formatter functions: Pure display utilities
- ReportBuilder interface: Composite report generation contract (SectionBuilder + TableWriter + ReportComposer)
- Generator interface: Multi-format API contract
Report-Specific Code:
- Section builders: Structure-aware markdown assembly
- Table generators: Component-specific table construction
- Report orchestrators: Document assembly and TOC generation
Streaming Support#
SectionWriter Interface#
The SectionWriter interface enables memory-efficient streaming output:
type SectionWriter interface {
WriteStandardReport(ctx context.Context, w io.Writer, data *common.CommonDevice) error
WriteComprehensiveReport(ctx context.Context, w io.Writer, data *common.CommonDevice) error
WriteSystemSection(ctx context.Context, w io.Writer, data *common.CommonDevice) error
WriteNetworkSection(ctx context.Context, w io.Writer, data *common.CommonDevice) error
WriteSecuritySection(ctx context.Context, w io.Writer, data *common.CommonDevice) error
WriteServicesSection(ctx context.Context, w io.Writer, data *common.CommonDevice) error
WriteAuditSection(w io.Writer, data *common.CommonDevice) error
}
Benefits#
Memory Efficiency:
- Writes sections incrementally to output stream
- Avoids loading entire report into memory
- Suitable for large configurations
Streaming Integration:
- Compatible with
io.Writerinterface - Can pipe directly to files, network sockets, or HTTP responses
- Supports cancellation via context
Usage Pattern#
builder := builder.NewMarkdownBuilder(device, logger)
file, _ := os.Create("report.md")
defer file.Close()
err := builder.WriteStandardReport(ctx, file, device)
// Streaming audit section when compliance data is present
if device.ComplianceResults != nil {
io.WriteString(file, "\n\n")
err = builder.WriteAuditSection(file, device)
}
Pro-Level Generators#
Build Tag Architecture#
Report generators can be conditionally included using Go build tags:
//go:build pro
package reports
// Pro-level report generator
Build Variants#
Standard Builds:
- Core report types (Blue and Red audit modes)
- All essential functionality
- Default build without special flags
Pro Builds:
- Additional enterprise features
- Advanced compliance frameworks
- Built with
-tags=pro
Custom Builds:
- Specific report combinations
- Feature subset selection
- Custom build tag combinations
Future Modular Design#
As the system evolves, each report type may be extracted to its own package:
internal/converter/<report-type>/
├── generator.go # Main generation logic
├── calculations.go # Report-specific calculations
├── transformers.go # Data transformation functions
├── constants.go # Report-specific constants
└── <report-type>_test.go
What Remains Shared:
common.CommonDevicemodel- String helpers (markdown escaping, formatting)
- Table builders (generic markdown table construction)
- Common interfaces (ReportBuilder, Generator)
Usage Examples#
Basic Report Generation#
# Generate standard markdown report
opndossier convert config.xml
# Generate comprehensive report with all sections
opndossier convert config.xml --comprehensive
# Export to specific file
opndossier convert config.xml -o firewall-config.md
Mode-Based Reports#
# Blue team mode (defensive audit) - default
opndossier audit config.xml
# Blue team mode with specific plugins
opndossier audit config.xml --plugins stig,sans
# Show only failing controls in blue mode
opndossier audit config.xml --failures-only
# Red team mode (attack surface analysis)
opndossier audit config.xml --mode red --audit-blackhat
Multi-Format Export#
# JSON export for automation
opndossier convert config.xml --format json -o config.json
# YAML export with redaction
opndossier convert config.xml --format yaml --redact -o config.yaml
# HTML for web viewing
opndossier convert config.xml --format html -o config.html
Programmatic Usage#
import (
"context"
"github.com/EvilBit-Labs/opnDossier/internal/converter"
"github.com/EvilBit-Labs/opnDossier/internal/converter/builder"
common "github.com/EvilBit-Labs/opnDossier/pkg/model"
)
// Create builder
builder := builder.NewMarkdownBuilder(device, logger)
// Generate standard report
report, err := builder.BuildStandardReport(device)
// Generate comprehensive report
comprehensiveReport, err := builder.BuildComprehensiveReport(device)
// Use generator for multi-format support
gen := converter.NewHybridGenerator(logger)
opts := converter.Options{
Format: "markdown",
Comprehensive: true,
}
output, err := gen.Generate(ctx, device, opts)
// Example: Working with typed enums
rule := common.FirewallRule{
Type: common.RuleTypePass, // Typed constant, not "pass"
Direction: common.DirectionIn, // Typed constant, not "in"
IPProtocol: common.IPProtocolInet, // Typed constant, not "inet"
// ...
}
// Example: Filtering rules by type with typed enum
passRules := builder.FilterRulesByType(device.FirewallRules, common.RuleTypePass)
Related Topics#
- CLI Architecture: Command-line interface and flag handling
- Parser Architecture: XML parsing and CommonDevice transformation
- Audit System: Compliance checking and security analysis
- Plugin System: Extensible compliance framework integration
Relevant Code Files#
| File Path | Description | Key Components |
|---|---|---|
internal/converter/builder/builder.go | Core report builder | MarkdownBuilder, ReportBuilder composite interface (SectionBuilder + TableWriter + ReportComposer), core section builders, table generators |
internal/converter/builder/builder_vpn.go | VPN builder methods | BuildIPsecSection, BuildOpenVPNSection, BuildHASection, writeIPsecSection, writeOpenVPNSection, writeVLANSection, writeStaticRoutesSection, writeHASection, VPN table builders with consistent markdown escaping |
internal/converter/builder/builder_services.go | Services builder methods | BuildServicesSection, writeServicesSection, WriteDHCPSummaryTable, WriteDHCPStaticLeasesTable, BuildDHCPSummaryTableSet, BuildDHCPStaticLeasesTableSet |
internal/converter/builder/writer.go | Streaming output | SectionWriter interface, streaming report methods |
internal/converter/builder/helpers.go | Helper methods | Formatter method wrappers, EscapePipeForMarkdown(), TruncateString(), FilterRulesByType() with typed enum parameter, convenience functions |
internal/converter/formatters/formatters.go | Basic formatting | Boolean, timestamp, byte formatting functions |
internal/converter/formatters/security.go | Security utilities | Risk assessment, security scoring, service risk evaluation |
internal/converter/formatters/transformers.go | Data transformation | Filtering, grouping, aggregation functions |
internal/converter/formatters/utils.go | String utilities | Markdown escaping, string manipulation |
internal/converter/hybrid_generator.go | Multi-format orchestration | Generator interface, StreamingGenerator, HybridGenerator, handlerForFormat() registry lookup |
internal/converter/registry.go | Format dispatch | FormatRegistry, FormatHandler interface, DefaultRegistry singleton, format handlers for markdown/json/yaml/text/html |
internal/converter/registry_test.go | Registry tests | 76 test cases covering Get, Canonical, Register, ValidFormats, Extensions, handler dispatch |
internal/converter/options.go | Configuration | Options struct for report generation |
internal/converter/enrichment.go | Data enrichment | Export preparation, redaction, delegates to analysis package for statistics and analysis |
internal/analysis/detect.go | Detection logic | Dead rule detection, unused interface detection, security/performance/consistency issue detection with typed enum constants |
internal/analysis/statistics.go | Statistics computation | ComputeStatistics, ComputeSecurityScore, ComputeConfigComplexity; aggregates enum values via string casts |
internal/analysis/rules.go | Rule comparison | RulesEquivalent for duplicate detection, includes Disabled field comparison |
internal/analysis/helpers.go | Helper functions | FindInterface, FindDHCPScope, IndexedRule type |
internal/analysis/finding.go | Canonical finding types | Finding struct, Severity type, severity constants and validation helpers |
internal/audit/mode_controller.go | Mode orchestration | ModeController, mode-specific report generation, per-plugin severity breakdown |
internal/audit/plugin.go | Plugin registry | PluginRegistry, compliance checks, severity derivation from control metadata |
cmd/audit_handler.go | CLI integration | handleAuditMode(), mapAuditReportToComplianceResults(), audit report to CommonDevice mapping |
internal/compliance/interfaces.go | Compliance types | Plugin interface, Control struct, Finding type alias to analysis.Finding |
internal/processor/processor.go | Processor logic | Process flow, Transform with FormatRegistry alias resolution, text/html format support |
internal/processor/processor_test.go | Processor tests | 12 new tests for text/html formats and alias resolution (md/yml/txt/htm) |
pkg/model/device.go | Data model | CommonDevice, platform-agnostic device representation, ComplianceResults field, typed enum constants (PR #452) |
pkg/model/enrichment.go | Compliance models | ComplianceResults, ComplianceFinding, PluginComplianceResult, ComplianceControl, ComplianceResultSummary |
docs/development/architecture.md | Architecture docs | Comprehensive design documentation |
internal/converter/markdown_bench_test.go | Performance tests | Benchmark tests for MarkdownBuilder |
See Also#
- Architecture Documentation - Complete system design and principles
- API Reference - MarkdownBuilder API documentation
- Contributing Guide - Development guidelines
- System Architecture Overview - High-level system design