Documents
security-assurance
security-assurance
Type
External
Status
Published
Created
Feb 27, 2026
Updated
Mar 23, 2026
Updated by
Dosu Bot

Security Assurance Case#

This document provides a structured argument that opnDossier meets its security requirements. It follows the assurance case model described in NIST IR 7608.

1. Security Requirements#

opnDossier is an OPNsense configuration parser, auditor, and reporting tool. Its security requirements are:

  1. SR-1: Must not crash or panic when processing any config.xml input
  2. SR-2: Must not allow XML External Entity (XXE) or entity expansion attacks
  3. SR-3: Must not allow path traversal via CLI arguments or output paths
  4. SR-4: Must not execute arbitrary code based on configuration file contents
  5. SR-5: Must not leak sensitive configuration data (passwords, keys, SNMP communities) in error messages or logs
  6. SR-6: Must not consume unbounded resources (memory, CPU) during parsing or report generation
  7. SR-7: Must handle sensitive data in generated reports with appropriate visibility controls

2. Threat Model#

2.1 Assets#

  • Host system: The machine running opnDossier
  • OPNsense/pfSense configuration: Contains network topology, firewall rules, credentials, VPN keys (including IPsec pre-shared keys), and other sensitive operational data
  • Generated reports: May contain extracts of sensitive configuration data

2.2 Threat Actors#

ActorMotivationCapability
Malicious config authorExploit the parser to gain code execution or cause DoSCan craft arbitrary config.xml content
Insider with report accessExtract sensitive data from generated reportsCan access report output files
Supply chain attackerCompromise a dependency to inject malicious codeCan publish malicious Go module versions

2.3 Attack Vectors#

IDVectorTarget SR
AV-1XXE or entity expansion in crafted config.xmlSR-2, SR-6
AV-2Deeply nested XML elements cause stack overflow or resource exhaustionSR-1, SR-6
AV-3Extremely large config.xml causes memory exhaustionSR-6
AV-4CLI argument with path traversal writes reports to unintended locationsSR-3
AV-5Crafted XML triggers panic in parser or schema mappingSR-1
AV-6Error messages include raw credential values from configSR-5
AV-7Compromised Go module introduces malicious codeSR-4
AV-8Crafted IPsec XML elements exploit pfSense IPsec parser pathsSR-1, SR-2, SR-6

3. Trust Boundaries#

Loading diagram...

All data crossing the trust boundary (config.xml content, CLI arguments, output paths) is treated as untrusted and validated before use.

4. Secure Design Principles (Saltzer and Schroeder)#

PrincipleHow Applied
Economy of mechanismGo with minimal dependencies. Simple parser-schema-report pipeline. No plugin downloads, no scripting engine, no network I/O at runtime.
Fail-safe defaultsBoth OPNsense and pfSense parsers (including IPsec configuration parsing) use shared pkg/parser/xmlutil.go (NewSecureXMLDecoder()) for secure XML decoding: entity expansion disabled, input size limited to 10 MB, charset normalization for UTF-8/ASCII/ISO-8859-1/Windows-1252. Overwrite protection on output files requires explicit --force flag. Offline-first design means no network calls.
Complete mediationEvery XML element is mapped to typed Go structs. Every CLI argument is validated by Cobra. Every output path is checked for overwrite conflicts.
Open designFully open source (Apache-2.0). Security does not depend on obscurity. All security mechanisms are publicly documented.
Separation of privilegeParser, schema, audit, and export are separate packages with distinct responsibilities. Parse errors cannot bypass audit safety checks.
Least privilegeThe tool reads config.xml files and writes reports; it never modifies source configurations, executes commands, or makes network connections. No elevated permissions required.
Least common mechanismNo shared mutable state between report generations. Each invocation operates on its own parsed data. No global caches that could leak information between runs.
Psychological acceptabilityCLI follows standard conventions via Cobra. Error messages are descriptive and actionable. Default behavior is safe (no overwrite, no network, offline-first).

5. Common Weakness Countermeasures#

5.1 CWE/SANS Top 25#

CWEWeaknessCountermeasureStatus
CWE-787Out-of-bounds writeGo is memory-safe; slice/array access is bounds-checked at runtime. No unsafe package usage.Mitigated
CWE-79XSSNot applicable (no web output). Markdown reports are static files.N/A
CWE-89SQL injectionNot applicable (no database).N/A
CWE-416Use after freeGo's garbage collector prevents use-after-free. No manual memory management.Mitigated
CWE-78OS command injectionNo shell invocation or command execution. CLI arguments parsed by Cobra, not passed to shell.Mitigated
CWE-20Improper input validationAll inputs validated: XML parsed by encoding/xml into typed structs, CLI args validated by Cobra, output paths checked for conflicts.Mitigated
CWE-125Out-of-bounds readGo slice access is bounds-checked at runtime. Panics on out-of-bounds access (caught by recovery or test suite).Mitigated
CWE-22Path traversalCLI accepts file paths as arguments. Output paths validated for overwrite protection. No path construction from config.xml contents.Mitigated
CWE-352CSRFNot applicable (no web interface).N/A
CWE-434Unrestricted uploadNot applicable (no file upload).N/A
CWE-476NULL pointer dereferenceGo does not have null pointers in the C sense; nil pointer dereferences cause a recoverable panic. Pointer fields use nil checks or *string patterns with accessor methods.Mitigated
CWE-190Integer overflowGo integer arithmetic wraps silently but opnDossier performs no security-critical arithmetic. Linter (gosec G115) flags unsafe integer conversions.Mitigated
CWE-502Deserialization of untrusted dataConfig.xml is parsed by Go's encoding/xml into strictly typed structs, not arbitrary deserialization.Mitigated
CWE-400Resource exhaustionparser.NewSecureXMLDecoder() wraps input with io.LimitReader (10 MB default). Entity map cleared to prevent expansion. Both OPNsense and pfSense parsers share this hardening.Mitigated
CWE-611XXE (XML External Entity)parser.NewSecureXMLDecoder() sets dec.Entity = map[string]string{}, disabling all entity resolution. Go's encoding/xml does not support DTD processing.Mitigated
CWE-312Cleartext storage of sensitive dataCredentials are redacted via two mechanisms: (1) The sanitize command uses field-pattern matching to redact credentials in configuration files (device-specific field names like pfSense <bcrypt-hash> and <pre-shared-key> require explicit patterns in internal/sanitizer/rules.go). (2) Report serialization (JSON/YAML output via ToJSON/ToYAML) redacts sensitive fields including certificate private keys (Certificate.PrivateKey), CA private keys (CertificateAuthority.PrivateKey), and SNMP community strings. IPsec pre-shared keys are excluded from the common model entirely (json:"-" tag on pfsense.IPsecPhase1.PreSharedKey) and a conversion warning is emitted when a PSK is present, providing defense-in-depth. The sanitizer also covers PSKs at the XML level via the "psk" substring pattern. The serialization redaction is implemented using conditional deep-copy logic that only processes entries with non-empty sensitive values, ensuring both security and performance. This prevents accidental exposure of private keys in exported reports even when users don't explicitly use the sanitize command. Implementation details are documented in AGENTS.md §5.25.Mitigated

5.2 OWASP Top 10 (Where Applicable)#

Most OWASP Top 10 categories target web applications and are not applicable to a CLI configuration parser. The applicable items are:

CategoryApplicabilityCountermeasure
A03: InjectionPartial -- XML parsingGo's encoding/xml maps to typed structs; no dynamic query construction
A04: Insecure DesignApplicableSecure design principles applied throughout (see Section 4)
A06: Vulnerable ComponentsApplicableGrype and Snyk in CI, Dependabot for automated updates, OSSF Scorecard
A09: Security LoggingPartialParse/audit errors logged via charmbracelet/log; security events reported via GitHub Advisories

6. Supply Chain Security#

MeasureImplementation
Dependency auditingGrype and Snyk run in CI; CodeQL for static analysis
Dependency updatesDependabot configured for automated PRs
Pinned toolchainGo version pinned via mise and go.mod
Reproducible buildsgo.sum committed; CGO_ENABLED=0 static builds
Build provenanceSigstore attestations via actions/attest-build-provenance
Artifact signingCosign keyless signing (Sigstore) + GPG signing via GoReleaser
SBOM generationCycloneDX SBOM generated per release via cyclonedx-gomod
CI integrityAll GitHub Actions pinned to SHA hashes
Code reviewRequired on all PRs; automated by CodeRabbit with security-focused checks
License complianceFOSSA scanning for Apache-2.0 compatible dependencies

7. Ongoing Assurance#

This assurance case is maintained as a living document. It is updated when:

  • New features introduce new attack surfaces
  • New threat vectors are identified
  • Dependencies change significantly
  • Security incidents occur

The project maintains continuous assurance through automated CI checks (golangci-lint, CodeQL, Grype, Snyk) that run on every commit.