Extension Playbooks
This guide provides practical implementation playbooks for extending VulnParse-Pin in its current architecture.
Current extension seams are explicit but code-driven:
- Parser extensions are registered through
PARSER_SPECS. - Pass extensions are wired in bootstrap into
PassRunner. - Enrichment extensions are integrated in the enrichment pipeline and supporting enrichment modules.
Playbook 1: Add a New Parser
Goal (Parser extension)
Add support for a new scanner format while preserving schema detection behavior and normalization contracts.
Where to implement (Parser extension)
- Create parser module under
src/vulnparse_pin/parsers/. - Implement
BaseParsersubclass withparse()anddetect_file(). - Register parser in
src/vulnparse_pin/parsers/__init__.pyviaPARSER_SPECS.
Minimal parser skeleton
from pathlib import Path
from typing import List, Tuple
from vulnparse_pin.core.classes.dataclass import ScanResult
from vulnparse_pin.parsers.base_parser import BaseParser
class CustomXMLParser(BaseParser):
@classmethod
def detect_file(cls, filepath: str | Path):
# Preferred: return (confidence, evidence)
# confidence >= 0.50 means matched
# evidence: list[tuple[str, str]]
return 0.85, [("root_tag", "custom_scan"), ("extension", ".xml")]
def parse(self) -> ScanResult:
# Build normalized ScanResult with assets/findings populated
raise NotImplementedError
Register parser
Add to PARSER_SPECS with a stable name, scanner label, format hints, and tie-break priority.
ParserSpec(
name="custom-xml",
parser_cls=CustomXMLParser,
formats=("xml",),
scanner="custom",
priority=15,
stability="stable",
deprecated=False,
)
Lifecycle metadata recommendations:
- Production parser paths should set
stability="stable". - In-progress parser paths should set
stability="experimental". - Sunset parser paths should set
deprecated=Trueand providedeprecation_notice.
Detection contract notes
SchemaDetectorfirst does lightweight format sniffing.- Candidate parsers then run
detect_file(). - Winner selection uses confidence, then priority, then parser name.
- Legacy boolean
detect_file()returns still work, but confidence tuple is preferred.
Parser extension checklist
- Parser returns valid
ScanResultwith stableasset_idmapping. - Findings include required identity and vulnerability fields.
- Fallback chains are deterministic for title/description/port/protocol extraction.
- Regression sample is added under
tests/regression_testing/. - Parser tests updated (
tests/test_parser_smoke.pyplus targeted fallback tests). - Parser lifecycle metadata is set intentionally and documented if user-visible.
Playbook 2: Add a New Pass
Goal (Pass extension)
Add a new derived-analysis pass while preserving append-only derived context and pass pipeline determinism.
Where to implement (Pass extension)
- Create pass module under
src/vulnparse_pin/core/passes/<PassName>/. - Implement
Passprotocol:name,version, andrun(ctx, scan). - Return
DerivedPassResultwithPassMetaand structured data. - Wire pass into bootstrap pass list in
src/vulnparse_pin/app/bootstrap.py.
Minimal pass skeleton
from datetime import datetime, timezone
from vulnparse_pin.core.classes.pass_classes import DerivedPassResult, Pass, PassMeta
class ExposurePass(Pass):
name = "Exposure"
version = "1.0"
def run(self, ctx, scan):
data = {"status": "ok"}
return DerivedPassResult(
meta=PassMeta(
name=self.name,
version=self.version,
created_at_utc=datetime.now(timezone.utc).isoformat(),
notes="Exposure analysis",
),
data=data,
)
Wiring in pipeline
Add your pass to bootstrap pass order, respecting dependencies (for example, TopN depends on scoring output).
Pass extension checklist
- Pass output key (
Name@Version) is unique. - Output shape is deterministic and testable.
- Pass behavior is documented if user-visible.
- Contract tests are added or updated in
tests/test_pass_contracts.py. - If output impacts reports/exporters, update report/export tests.
Playbook 3: Add a New Enrichment Source
Goal
Add a new intelligence source (or enrich signal) without breaking existing KEV/EPSS/Exploit/NVD flow.
Where to implement
Current enrichment flow is orchestrated in src/vulnparse_pin/app/enrichment.py and helper modules under src/vulnparse_pin/utils/.
Common integration points:
- Source loader function (pattern similar to
load_kev/load_epss). - Per-finding enrichment transform.
- Enrichment pipeline integration and stats reporting.
- Optional feed cache spec and cache policy integration.
Suggested implementation pattern
- Add a loader function with online/offline support and bounded logging.
- Normalize keys once for lookup efficiency.
- Apply enrichment in batch loops with clear fallback behavior.
- Update enrichment stats and status fields.
- Keep behavior compatible with
--log-leveland large-workload patterns.
Enrichment extension checklist
- New source can be enabled/disabled explicitly.
- Offline path behavior is defined and tested.
- Source failures degrade gracefully (warn and continue where safe).
- Output fields are documented and do not silently collide with existing keys.
- Tests cover source hit, source miss, malformed input, and offline fallback.
Testing Matrix for Extension Work
Use this minimum matrix for extension PRs:
- Parser contract and smoke tests for parser changes.
- Pass contract tests for pass changes.
- Enrichment-specific tests plus runmanifest checks for enrichment changes.
- At least one representative end-to-end run.
Recommended commands:
pytest tests/test_parser_smoke.py -v
pytest tests/test_pass_contracts.py -v
pytest tests/test_runmanifest.py -v
pytest tests/ -v
Documentation and Changelog Requirements
When extension behavior is user-visible:
- Update
documentation/docs/Usage.mdas needed. - Update migration/troubleshooting docs if semantics changed.
- Add changelog notes in
CHANGELOG.md.
When extension behavior is architecture-impacting:
- Add or update an ADR using ADR Workflow.
- Complete Architecture Review Checklist in the PR description.