Refactor code structure for improved readability and maintainability
This commit is contained in:
281
docs/modules/concelier/api/conflicts.md
Normal file
281
docs/modules/concelier/api/conflicts.md
Normal file
@@ -0,0 +1,281 @@
|
||||
# Linkset Conflicts API Reference (v1)
|
||||
|
||||
Status: stable; aligns with LNM v1 (frozen 2025-11-17).
|
||||
|
||||
## Intent
|
||||
- Document conflict detection and representation in the Link-Not-Merge system.
|
||||
- Conflicts are surfaced but never automatically resolved; consumers implement their own resolution strategy.
|
||||
- This reference describes conflict types, detection logic, and how conflicts appear in API responses.
|
||||
|
||||
## Philosophy: Link-Not-Merge
|
||||
The Concelier LNM (Link-Not-Merge) approach differs from traditional advisory aggregation:
|
||||
- **Link**: Observations from multiple sources are linked together via shared identifiers (CVE, GHSA, PURL, CPE).
|
||||
- **Not Merge**: Conflicting data is preserved with full provenance rather than collapsed into a single "truth".
|
||||
- **Surface, Don't Resolve**: Conflicts are clearly marked for downstream consumers to handle according to their own policies.
|
||||
|
||||
## Conflict Types
|
||||
|
||||
### severity-mismatch
|
||||
Sources disagree on severity rating.
|
||||
```json
|
||||
{
|
||||
"field": "severity",
|
||||
"reason": "severity-mismatch",
|
||||
"observedValue": "critical",
|
||||
"observedAt": "2025-11-18T08:00:00Z",
|
||||
"evidenceHash": "sha256:f6e5d4c3b2a1098765432109876543210fedcba0987654321fedcba098765432"
|
||||
}
|
||||
```
|
||||
|
||||
**Detection**: Triggered when severity labels (critical, high, medium, low) or CVSS scores differ by more than 1.0 points between observations.
|
||||
|
||||
**Common causes**:
|
||||
- Different CVSS versions (v2 vs v3 vs v3.1)
|
||||
- Vendor-specific severity assessments
|
||||
- Time lag between source updates
|
||||
|
||||
### version-range-conflict
|
||||
Sources disagree on affected version ranges.
|
||||
```json
|
||||
{
|
||||
"field": "affected.ranges",
|
||||
"reason": "version-range-conflict",
|
||||
"observedValue": "{\"fixed\": \"2.0.0\"}",
|
||||
"observedAt": "2025-11-19T12:00:00Z",
|
||||
"evidenceHash": "sha256:a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456"
|
||||
}
|
||||
```
|
||||
|
||||
**Detection**: Triggered when version range events differ between observations for the same package.
|
||||
|
||||
**Common causes**:
|
||||
- Backports creating different fix points per distribution
|
||||
- Vendor patches not reflected in upstream
|
||||
- Different ecosystem-specific versioning
|
||||
|
||||
### status-conflict
|
||||
Sources disagree on vulnerability status.
|
||||
```json
|
||||
{
|
||||
"field": "status",
|
||||
"reason": "status-conflict",
|
||||
"observedValue": "not_affected",
|
||||
"observedAt": "2025-11-20T09:00:00Z",
|
||||
"evidenceHash": "sha256:b2c3d4e5f6a789012345678901234567890abcdef1234567890abcdef1234567b"
|
||||
}
|
||||
```
|
||||
|
||||
**Detection**: Triggered when status values (affected, not_affected, under_investigation, fixed) differ.
|
||||
|
||||
**Common causes**:
|
||||
- VEX statements from vendors
|
||||
- Incomplete upstream analysis
|
||||
- Context-specific applicability (e.g., platform-dependent)
|
||||
|
||||
### cpe-mismatch
|
||||
Sources disagree on CPE identifiers.
|
||||
```json
|
||||
{
|
||||
"field": "cpe",
|
||||
"reason": "cpe-mismatch",
|
||||
"observedValue": "cpe:2.3:a:example:lib:*:*:*:*:*:*:*:*",
|
||||
"observedAt": "2025-11-18T16:00:00Z",
|
||||
"evidenceHash": "sha256:c3d4e5f6a789012345678901234567890abcdef1234567890abcdef123456789c"
|
||||
}
|
||||
```
|
||||
|
||||
**Detection**: Triggered when CPE identifiers for the same advisory don't match.
|
||||
|
||||
**Common causes**:
|
||||
- Different CPE dictionary versions
|
||||
- Vendor vs product naming variations
|
||||
- Platform-specific CPE assignments
|
||||
|
||||
### reference-conflict
|
||||
Sources provide conflicting reference information.
|
||||
```json
|
||||
{
|
||||
"field": "references",
|
||||
"reason": "reference-conflict",
|
||||
"observedValue": "https://example.com/advisory/different",
|
||||
"observedAt": "2025-11-21T10:00:00Z",
|
||||
"evidenceHash": "sha256:d4e5f6a789012345678901234567890abcdef1234567890abcdef123456789def"
|
||||
}
|
||||
```
|
||||
|
||||
## Conflict in Linkset Response
|
||||
|
||||
Full linkset with multiple conflicts:
|
||||
```json
|
||||
{
|
||||
"advisoryId": "CVE-2024-9999",
|
||||
"source": "aggregated",
|
||||
"purl": ["pkg:maven/org.example/library@1.5.0"],
|
||||
"cpe": [
|
||||
"cpe:2.3:a:example:library:1.5.0:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:example_inc:lib:1.5.0:*:*:*:*:java:*:*"
|
||||
],
|
||||
"summary": "Deserialization vulnerability in example library",
|
||||
"publishedAt": "2024-09-01T00:00:00Z",
|
||||
"modifiedAt": "2024-09-15T00:00:00Z",
|
||||
"severity": "high",
|
||||
"status": "affected",
|
||||
"provenance": {
|
||||
"ingestedAt": "2025-11-20T10:30:00Z",
|
||||
"connectorId": "multi-source-aggregator",
|
||||
"evidenceHash": "sha256:aggregated-evidence-hash"
|
||||
},
|
||||
"conflicts": [
|
||||
{
|
||||
"field": "severity",
|
||||
"reason": "severity-mismatch",
|
||||
"observedValue": "critical",
|
||||
"observedAt": "2025-11-15T10:00:00Z",
|
||||
"evidenceHash": "sha256:nvd-observation-hash"
|
||||
},
|
||||
{
|
||||
"field": "severity",
|
||||
"reason": "severity-mismatch",
|
||||
"observedValue": "medium",
|
||||
"observedAt": "2025-11-18T14:00:00Z",
|
||||
"evidenceHash": "sha256:vendor-observation-hash"
|
||||
},
|
||||
{
|
||||
"field": "cpe",
|
||||
"reason": "cpe-mismatch",
|
||||
"observedValue": "cpe:2.3:a:example_inc:lib:1.5.0:*:*:*:*:java:*:*",
|
||||
"observedAt": "2025-11-17T08:00:00Z",
|
||||
"evidenceHash": "sha256:github-observation-hash"
|
||||
},
|
||||
{
|
||||
"field": "affected.ranges",
|
||||
"reason": "version-range-conflict",
|
||||
"observedValue": "{\"type\": \"SEMVER\", \"events\": [{\"introduced\": \"1.0.0\"}, {\"fixed\": \"1.5.1\"}]}",
|
||||
"observedAt": "2025-11-19T12:00:00Z",
|
||||
"evidenceHash": "sha256:distro-observation-hash"
|
||||
}
|
||||
],
|
||||
"timeline": [
|
||||
{"event": "first-observed", "at": "2025-11-15T10:00:00Z", "evidenceHash": "sha256:nvd-observation-hash"},
|
||||
{"event": "conflict-detected", "at": "2025-11-17T08:00:00Z", "evidenceHash": "sha256:github-observation-hash"},
|
||||
{"event": "conflict-detected", "at": "2025-11-18T14:00:00Z", "evidenceHash": "sha256:vendor-observation-hash"},
|
||||
{"event": "conflict-detected", "at": "2025-11-19T12:00:00Z", "evidenceHash": "sha256:distro-observation-hash"}
|
||||
],
|
||||
"normalized": {
|
||||
"aliases": ["CVE-2024-9999", "GHSA-xxxx-yyyy-zzzz"],
|
||||
"severities": [
|
||||
{"source": "nvd", "type": "CVSS_V3", "score": 9.8, "label": "critical"},
|
||||
{"source": "github", "type": "CVSS_V3", "score": 7.5, "label": "high"},
|
||||
{"source": "vendor", "type": "CVSS_V3", "score": 5.3, "label": "medium"}
|
||||
],
|
||||
"ranges": [
|
||||
{"source": "nvd", "type": "SEMVER", "introduced": "0", "fixed": "1.6.0"},
|
||||
{"source": "distro", "type": "SEMVER", "introduced": "1.0.0", "fixed": "1.5.1"}
|
||||
]
|
||||
},
|
||||
"cached": false,
|
||||
"observations": [
|
||||
"obs:nvd:CVE-2024-9999:2025-11-15",
|
||||
"obs:github:GHSA-xxxx-yyyy-zzzz:2025-11-17",
|
||||
"obs:vendor:CVE-2024-9999:2025-11-18",
|
||||
"obs:distro:CVE-2024-9999:2025-11-19"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Querying for Conflicts
|
||||
|
||||
### List Only Linksets with Conflicts
|
||||
```bash
|
||||
GET /v1/lnm/linksets?includeConflicts=true&hasConflicts=true
|
||||
X-Stella-Tenant: acme
|
||||
```
|
||||
|
||||
### Filter by Conflict Type
|
||||
```bash
|
||||
POST /v1/lnm/linksets/search
|
||||
X-Stella-Tenant: acme
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"conflictTypes": ["severity-mismatch", "version-range-conflict"],
|
||||
"includeConflicts": true,
|
||||
"pageSize": 50
|
||||
}
|
||||
```
|
||||
|
||||
### Advisory Summary with Conflicts
|
||||
```bash
|
||||
GET /advisories/summary?conflicts_only=true
|
||||
X-Stella-Tenant: acme
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"meta": {
|
||||
"tenant": "acme",
|
||||
"count": 3,
|
||||
"sort": "advisory"
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"advisoryKey": "CVE-2024-9999",
|
||||
"aliases": ["CVE-2024-9999", "GHSA-xxxx-yyyy-zzzz"],
|
||||
"source": "aggregated",
|
||||
"confidence": 0.65,
|
||||
"conflicts": [
|
||||
{"field": "severity", "reason": "severity-mismatch", "sourceIds": ["nvd", "vendor", "github"]}
|
||||
],
|
||||
"counts": {
|
||||
"observations": 4,
|
||||
"conflictFields": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Conflict Resolution Strategies
|
||||
|
||||
Concelier does not resolve conflicts, but here are common strategies consumers implement:
|
||||
|
||||
### Source Priority
|
||||
Prioritize sources by trust level:
|
||||
```
|
||||
nvd > vendor > github > community
|
||||
```
|
||||
|
||||
### Most Recent
|
||||
Use the most recently observed value:
|
||||
```
|
||||
Sort by observedAt desc, take first
|
||||
```
|
||||
|
||||
### Most Conservative
|
||||
For severity, use the highest rating:
|
||||
```
|
||||
critical > high > medium > low
|
||||
```
|
||||
|
||||
### Voting/Consensus
|
||||
Use the value with most agreement:
|
||||
```
|
||||
Count occurrences, take majority
|
||||
```
|
||||
|
||||
## Conflict Confidence Impact
|
||||
The `confidence` field in linksets reflects conflict presence:
|
||||
- **No conflicts**: confidence ≥ 0.9
|
||||
- **Minor conflicts** (1-2 fields): confidence 0.7-0.9
|
||||
- **Major conflicts** (3+ fields or severity): confidence < 0.7
|
||||
|
||||
## Notes
|
||||
- Conflicts are preserved indefinitely; they are only removed if all observations align.
|
||||
- Evidence hashes allow consumers to trace conflicts back to specific observations.
|
||||
- The `timeline` array shows when conflicts were first detected.
|
||||
- Multiple conflicts on the same field from different sources create multiple entries.
|
||||
|
||||
## Changelog
|
||||
- 2025-12-06: Initial conflict reference documentation (CONCELIER-WEB-OAS-62-001).
|
||||
- 2025-11-17: LNM v1 conflict model frozen.
|
||||
Reference in New Issue
Block a user