Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message
Domain Models
vinceamstoutz/symfony-security-auditor
cursor
domain-model
immutability
audit-context
vulnerability
fingerprinting
enums
risk-levels

Domain Model Rules

Install
php artisan boost:add-skill vinceamstoutz/symfony-security-auditor

Save this content to: .cursor/rules/domain-models.md

# Domain Model Rules

- All Domain models are **immutable** (`readonly` properties).
- State changes return new instances (copy-on-write): follow `withReviewerValidation()` / `withElevatedSeverity()` pattern on `Vulnerability`.
- `AuditContext` is the **only** intentionally mutable object — it is the pipeline accumulator. All other Domain models are immutable.
- `AuditReport` is created exactly once via `AuditReport::fromContext()` after the pipeline finishes. It captures only `validatedVulnerabilities()`.
- Vulnerability `id` is deterministic: `VULN-{sha1(sha1(type)+sha1(filePath)+sha1(lineStart))[0..7]}` (no microtime, no title). Each field is hashed individually before joining — mirroring `ChunkContextKeyDeriver::derive()` — so a digit shifting across the `filePath`/`lineStart` boundary (e.g. `src/Foo1`+`23` vs `src/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) is `SSA-{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. `title` starting with the same bytes that end `filePath`) cannot collide two different findings onto the same fingerprint. Do not revert to hashing a delimiter-joined string of the raw fields.
- Adding a `ProjectFileType` case requires mapping it in `ProjectFileType::archetype()` — the `match` is exhaustive, so an unmapped case throws at runtime, and `SurfaceArchetypeTest` fails first. `SurfaceArchetype` is the framework-neutral shape core logic switches on; keep `HTTP_ENTRYPOINT` meaning a _route-guarded_ surface, since `isControllerLike()` delegates to it and it decides which files reach the access-control and form-binding maps.
- Adding a `VulnerabilityType` case requires updating `category()`, `owaspReference()`, `owaspReferenceUrl()`, and `cwe()` — nothing else changes.
- Adding a `VulnerabilitySeverity` case requires updating `score()`, `label()`, `isExploitable()`, and the `riskLevelEnum()` thresholds in `AuditReport` (`riskLevel()` derives its string from that enum).
- `RiskLevel` is the ordered aggregate-risk scale (`safe` … `critical`) used by the `audit.fail_on` CI gate; `RiskLevel::isAtLeast()` is the comparison.

Domain Model Rules

  • All Domain models are immutable (readonly properties).
  • State changes return new instances (copy-on-write): follow withReviewerValidation() / withElevatedSeverity() pattern on Vulnerability.
  • AuditContext is the only intentionally mutable object — it is the pipeline accumulator. All other Domain models are immutable.
  • AuditReport is created exactly once via AuditReport::fromContext() after the pipeline finishes. It captures only validatedVulnerabilities().
  • Vulnerability id is deterministic: VULN-{sha1(sha1(type)+sha1(filePath)+sha1(lineStart))[0..7]} (no microtime, no title). Each field is hashed individually before joining — mirroring ChunkContextKeyDeriver::derive() — so a digit shifting across the filePath/lineStart boundary (e.g. src/Foo1+23 vs src/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) is SSA-{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. title starting with the same bytes that end filePath) cannot collide two different findings onto the same fingerprint. Do not revert to hashing a delimiter-joined string of the raw fields.
  • Adding a ProjectFileType case requires mapping it in ProjectFileType::archetype() — the match is exhaustive, so an unmapped case throws at runtime, and SurfaceArchetypeTest fails first. SurfaceArchetype is the framework-neutral shape core logic switches on; keep HTTP_ENTRYPOINT meaning a route-guarded surface, since isControllerLike() delegates to it and it decides which files reach the access-control and form-binding maps.
  • Adding a VulnerabilityType case requires updating category(), owaspReference(), owaspReferenceUrl(), and cwe() — nothing else changes.
  • Adding a VulnerabilitySeverity case requires updating score(), label(), isExploitable(), and the riskLevelEnum() thresholds in AuditReport (riskLevel() derives its string from that enum).
  • RiskLevel is the ordered aggregate-risk scale (safe … critical) used by the audit.fail_on CI gate; RiskLevel::isAtLeast() is the comparison.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle