| paths |
|
|---|
- All Domain models are immutable (
readonlyproperties). - State changes return new instances (copy-on-write): follow
withReviewerValidation()/withElevatedSeverity()pattern onVulnerability. AuditContextis the only intentionally mutable object — it is the pipeline accumulator. All other Domain models are immutable.AuditReportis created exactly once viaAuditReport::fromContext()after the pipeline finishes. It captures onlyvalidatedVulnerabilities().- Vulnerability
idis deterministic:VULN-{sha1(sha1(type)+sha1(filePath)+sha1(lineStart))[0..7]}(no microtime, no title). Each field is hashed individually before joining — mirroringChunkContextKeyDeriver::derive()— so a digit shifting across thefilePath/lineStartboundary (e.g.src/Foo1+23vssrc/Foo+123) can't collide. Do not change this scheme without preserving that per-field-hash-then-join property. - Vulnerability
fingerprint/attackerFingerprint(baseline/diff/trend identity) isSSA-{sha1(sha1(type).sha1(filePath).sha1(title))[0..11]}— each field is hashed individually before joining so a delimiter shift across a field boundary (e.g.titlestarting with the same bytes that endfilePath) cannot collide two different findings onto the same fingerprint. Do not revert to hashing a delimiter-joined string of the raw fields. - Adding a
ProjectFileTypecase requires mapping it inProjectFileType::archetype()— thematchis exhaustive, so an unmapped case throws at runtime, andSurfaceArchetypeTestfails first.SurfaceArchetypeis the framework-neutral shape core logic switches on; keepHTTP_ENTRYPOINTmeaning a route-guarded surface, sinceisControllerLike()delegates to it and it decides which files reach the access-control and form-binding maps. - Adding a
VulnerabilityTypecase requires updatingcategory(),owaspReference(),owaspReferenceUrl(), andcwe()— nothing else changes. - Adding a
VulnerabilitySeveritycase requires updatingscore(),label(),isExploitable(), and theriskLevelEnum()thresholds inAuditReport(riskLevel()derives its string from that enum). RiskLevelis the ordered aggregate-risk scale (safe…critical) used by theaudit.fail_onCI gate;RiskLevel::isAtLeast()is the comparison.