Refactor code structure for improved readability and maintainability
This commit is contained in:
264
docs/modules/concelier/api/observations.md
Normal file
264
docs/modules/concelier/api/observations.md
Normal file
@@ -0,0 +1,264 @@
|
||||
# Observations API (v1)
|
||||
|
||||
Status: stable; aligns with LNM v1 (frozen 2025-11-17).
|
||||
|
||||
## Intent
|
||||
- Provide raw observation retrieval for graph overlays, audit trails, and detailed provenance inspection.
|
||||
- Observations are the immutable evidence units that feed into linkset aggregation.
|
||||
- Each observation represents a single upstream source's statement about an advisory at a point in time.
|
||||
|
||||
## Endpoints
|
||||
|
||||
### Query Observations
|
||||
- Method: `GET`
|
||||
- Path: `/advisories/observations`
|
||||
|
||||
### Get Observations for Advisory
|
||||
- Method: `GET`
|
||||
- Path: `/concelier/observations`
|
||||
|
||||
## Headers
|
||||
| Header | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `X-Stella-Tenant` | Yes | Tenant identifier for multi-tenant isolation. |
|
||||
| `X-Stella-Request-Id` | No | Optional correlation ID for distributed tracing. |
|
||||
|
||||
## Query Parameters
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `advisoryKey` | string | Filter by advisory key (CVE, GHSA, etc.). |
|
||||
| `purl` | string[] | Filter by Package URLs (repeatable). |
|
||||
| `source` | string | Filter by upstream source. |
|
||||
| `format` | string | Filter by content format (OSV, GHSA, etc.). |
|
||||
| `limit` | integer | Maximum observations to return (default: 50, max: 200). |
|
||||
| `cursor` | string | Opaque cursor for pagination. |
|
||||
|
||||
## Response (200)
|
||||
```json
|
||||
{
|
||||
"observations": [
|
||||
{
|
||||
"id": "obs:nvd:CVE-2024-1234:2025-11-20T10:30:00Z",
|
||||
"tenant": "acme",
|
||||
"advisoryKey": "CVE-2024-1234",
|
||||
"aliases": ["CVE-2024-1234", "GHSA-abcd-efgh-ijkl"],
|
||||
"source": "nvd",
|
||||
"format": "OSV",
|
||||
"purls": ["pkg:npm/vulnerable-package@1.0.0"],
|
||||
"cpes": ["cpe:2.3:a:example:vulnerable-package:1.0.0:*:*:*:*:node.js:*:*"],
|
||||
"severity": {
|
||||
"type": "CVSS_V3",
|
||||
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
|
||||
"baseScore": 9.8,
|
||||
"label": "critical"
|
||||
},
|
||||
"summary": "Remote code execution vulnerability in vulnerable-package",
|
||||
"publishedAt": "2024-06-15T12:00:00Z",
|
||||
"modifiedAt": "2024-06-20T08:00:00Z",
|
||||
"observedAt": "2025-11-20T10:30:00Z",
|
||||
"provenance": {
|
||||
"connectorId": "nvd-osv-connector",
|
||||
"retrievedAt": "2025-11-20T10:30:00Z",
|
||||
"contentHash": "sha256:a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456",
|
||||
"signaturePresent": false,
|
||||
"signatureVerified": false
|
||||
},
|
||||
"raw": {
|
||||
"id": "CVE-2024-1234",
|
||||
"modified": "2024-06-20T08:00:00Z",
|
||||
"published": "2024-06-15T12:00:00Z",
|
||||
"aliases": ["CVE-2024-1234"],
|
||||
"summary": "Remote code execution vulnerability in vulnerable-package",
|
||||
"details": "A critical vulnerability exists in vulnerable-package versions prior to 2.0.0...",
|
||||
"severity": [
|
||||
{
|
||||
"type": "CVSS_V3",
|
||||
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"
|
||||
}
|
||||
],
|
||||
"affected": [
|
||||
{
|
||||
"package": {
|
||||
"ecosystem": "npm",
|
||||
"name": "vulnerable-package"
|
||||
},
|
||||
"ranges": [
|
||||
{
|
||||
"type": "SEMVER",
|
||||
"events": [
|
||||
{"introduced": "0"},
|
||||
{"fixed": "2.0.0"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"type": "ADVISORY",
|
||||
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-1234"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "obs:github:GHSA-abcd-efgh-ijkl:2025-11-18T14:00:00Z",
|
||||
"tenant": "acme",
|
||||
"advisoryKey": "CVE-2024-1234",
|
||||
"aliases": ["CVE-2024-1234", "GHSA-abcd-efgh-ijkl"],
|
||||
"source": "github",
|
||||
"format": "GHSA",
|
||||
"purls": ["pkg:npm/vulnerable-package@1.0.0"],
|
||||
"cpes": [],
|
||||
"severity": {
|
||||
"type": "CVSS_V3",
|
||||
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
|
||||
"baseScore": 9.8,
|
||||
"label": "critical"
|
||||
},
|
||||
"summary": "Critical RCE in vulnerable-package",
|
||||
"publishedAt": "2024-06-15T14:00:00Z",
|
||||
"modifiedAt": "2024-06-18T10:00:00Z",
|
||||
"observedAt": "2025-11-18T14:00:00Z",
|
||||
"provenance": {
|
||||
"connectorId": "github-advisory-connector",
|
||||
"retrievedAt": "2025-11-18T14:00:00Z",
|
||||
"contentHash": "sha256:b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456ab",
|
||||
"signaturePresent": true,
|
||||
"signatureVerified": true
|
||||
},
|
||||
"raw": {
|
||||
"id": "GHSA-abcd-efgh-ijkl",
|
||||
"aliases": ["CVE-2024-1234"],
|
||||
"summary": "Critical RCE in vulnerable-package",
|
||||
"severity": [{"type": "CVSS_V3", "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"}],
|
||||
"database_specific": {
|
||||
"github_reviewed": true,
|
||||
"github_reviewed_at": "2024-06-15T14:00:00Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"linkset": {
|
||||
"aliases": ["CVE-2024-1234", "GHSA-abcd-efgh-ijkl"],
|
||||
"purls": ["pkg:npm/vulnerable-package@1.0.0"],
|
||||
"cpes": ["cpe:2.3:a:example:vulnerable-package:1.0.0:*:*:*:*:node.js:*:*"],
|
||||
"references": [
|
||||
{"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-1234", "type": "ADVISORY"},
|
||||
{"url": "https://github.com/advisories/GHSA-abcd-efgh-ijkl", "type": "ADVISORY"}
|
||||
],
|
||||
"scopes": ["npm"],
|
||||
"relationships": [],
|
||||
"confidence": 0.95,
|
||||
"conflicts": []
|
||||
},
|
||||
"nextCursor": null,
|
||||
"hasMore": false,
|
||||
"freshness": null
|
||||
}
|
||||
```
|
||||
|
||||
## Response with Conflicts
|
||||
When observations from different sources disagree:
|
||||
```json
|
||||
{
|
||||
"observations": [
|
||||
{
|
||||
"id": "obs:nvd:CVE-2024-5678:2025-11-20T10:30:00Z",
|
||||
"advisoryKey": "CVE-2024-5678",
|
||||
"source": "nvd",
|
||||
"severity": {
|
||||
"type": "CVSS_V3",
|
||||
"baseScore": 9.8,
|
||||
"label": "critical"
|
||||
},
|
||||
"observedAt": "2025-11-20T10:30:00Z"
|
||||
},
|
||||
{
|
||||
"id": "obs:vendor:CVE-2024-5678:2025-11-22T08:00:00Z",
|
||||
"advisoryKey": "CVE-2024-5678",
|
||||
"source": "vendor-security",
|
||||
"severity": {
|
||||
"type": "CVSS_V3",
|
||||
"baseScore": 7.5,
|
||||
"label": "high"
|
||||
},
|
||||
"observedAt": "2025-11-22T08:00:00Z"
|
||||
}
|
||||
],
|
||||
"linkset": {
|
||||
"aliases": ["CVE-2024-5678"],
|
||||
"purls": ["pkg:npm/another-package@3.0.0"],
|
||||
"cpes": [],
|
||||
"confidence": 0.72,
|
||||
"conflicts": [
|
||||
{
|
||||
"field": "severity",
|
||||
"code": "severity-mismatch",
|
||||
"observedValues": [
|
||||
{"source": "nvd", "value": "critical", "observedAt": "2025-11-20T10:30:00Z"},
|
||||
{"source": "vendor-security", "value": "high", "observedAt": "2025-11-22T08:00:00Z"}
|
||||
],
|
||||
"reason": "Sources disagree on severity classification: nvd reports critical (9.8), vendor-security reports high (7.5)"
|
||||
}
|
||||
]
|
||||
},
|
||||
"nextCursor": null,
|
||||
"hasMore": false
|
||||
}
|
||||
```
|
||||
|
||||
## Errors
|
||||
| Status | Code | Description |
|
||||
|--------|------|-------------|
|
||||
| 400 | `ERR_VALIDATION_FAILED` | Invalid query parameters. |
|
||||
| 400 | `ERR_INVALID_CURSOR` | Malformed or expired cursor. |
|
||||
| 401 | `ERR_UNAUTHORIZED` | Missing or invalid authentication. |
|
||||
| 403 | `ERR_FORBIDDEN` | Tenant access denied. |
|
||||
| 404 | `ERR_RESOURCE_NOT_FOUND` | No observations found for advisory. |
|
||||
|
||||
### Error Response Example
|
||||
```json
|
||||
{
|
||||
"type": "https://stellaops.io/errors/validation-failed",
|
||||
"title": "Validation Failed",
|
||||
"status": 400,
|
||||
"detail": "The 'advisoryKey' parameter is required when 'source' is specified.",
|
||||
"instance": "/advisories/observations",
|
||||
"traceId": "trace-id-xyz789",
|
||||
"error": {
|
||||
"code": "ERR_VALIDATION_FAILED",
|
||||
"message": "Missing required parameter.",
|
||||
"target": "advisoryKey",
|
||||
"innerErrors": [
|
||||
{
|
||||
"field": "advisoryKey",
|
||||
"code": "REQUIRED_WHEN",
|
||||
"message": "advisoryKey is required when source is specified"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Observation Lifecycle
|
||||
1. **Ingested**: Raw advisory data retrieved from upstream source.
|
||||
2. **Validated**: Schema validated against content format (OSV, GHSA, etc.).
|
||||
3. **Stored**: Immutable observation record created with provenance.
|
||||
4. **Linked**: Observation contributes to linkset aggregation.
|
||||
|
||||
## Determinism & Ordering
|
||||
- Observations are ordered by `observedAt desc`, then `source asc`, then `id asc`.
|
||||
- The same query with identical parameters returns identical results.
|
||||
- Cursor-based pagination ensures stable iteration even as new data arrives.
|
||||
|
||||
## Notes
|
||||
- Observations are immutable; updates from upstream create new observation records.
|
||||
- The `raw` field contains the unmodified upstream content.
|
||||
- Provenance includes connector identity and content hashes for audit.
|
||||
- Multiple observations may exist for the same advisory from different sources.
|
||||
- Conflicts are detected and surfaced in the aggregate linkset, not resolved.
|
||||
|
||||
## Changelog
|
||||
- 2025-12-06: Added curated examples with conflict scenarios (CONCELIER-WEB-OAS-62-001).
|
||||
- 2025-11-17: LNM v1 specification frozen.
|
||||
Reference in New Issue
Block a user