359 lines
8.8 KiB
Markdown
359 lines
8.8 KiB
Markdown
# Unknowns API Reference
|
||
|
||
**Sprint:** SPRINT_3600_0002_0001
|
||
**Task:** UNK-RANK-011 - Update unknowns API documentation
|
||
|
||
## Overview
|
||
|
||
The Unknowns API provides access to items that could not be fully classified due to missing evidence, ambiguous data, or incomplete intelligence. Unknowns are ranked by blast radius, exploit pressure, and containment signals.
|
||
|
||
## Base URL
|
||
|
||
```
|
||
/api/v1/unknowns
|
||
```
|
||
|
||
## Authentication
|
||
|
||
All endpoints require Bearer token authentication:
|
||
|
||
```http
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
Required scope: `scanner:unknowns:read`
|
||
|
||
## Endpoints
|
||
|
||
### List Unknowns
|
||
|
||
```http
|
||
GET /api/v1/unknowns
|
||
```
|
||
|
||
Returns paginated list of unknowns, optionally sorted by score.
|
||
|
||
#### Query Parameters
|
||
|
||
| Parameter | Type | Default | Description |
|
||
|-----------|------|---------|-------------|
|
||
| `sort` | string | `score` | Sort field: `score`, `created_at`, `blast_dependents` |
|
||
| `order` | string | `desc` | Sort order: `asc`, `desc` |
|
||
| `page` | int | 1 | Page number (1-indexed) |
|
||
| `pageSize` | int | 50 | Items per page (max 200) |
|
||
| `artifact` | string | - | Filter by artifact digest |
|
||
| `reason` | string | - | Filter by reason code |
|
||
| `minScore` | float | - | Minimum score threshold (0-1) |
|
||
| `maxScore` | float | - | Maximum score threshold (0-1) |
|
||
| `kev` | bool | - | Filter by KEV status |
|
||
| `seccomp` | string | - | Filter by seccomp state: `enforced`, `permissive`, `unknown` |
|
||
|
||
#### Response
|
||
|
||
```json
|
||
{
|
||
"items": [
|
||
{
|
||
"id": "unk-12345678-abcd-1234-5678-abcdef123456",
|
||
"artifactDigest": "sha256:abc123...",
|
||
"artifactPurl": "pkg:oci/myapp@sha256:abc123",
|
||
"reasonCode": "Reachability",
|
||
"reasonCodeShort": "U-RCH",
|
||
"remediationHint": "Run reachability analysis",
|
||
"detailedHint": "Execute call-graph analysis to determine if vulnerable code paths are reachable from application entrypoints.",
|
||
"automationCommand": "stella analyze --reachability",
|
||
"evidenceRefs": [
|
||
{
|
||
"type": "reachability",
|
||
"uri": "proofs/unknowns/unk-12345678/evidence.json",
|
||
"digest": "sha256:0a1b2c..."
|
||
}
|
||
],
|
||
"reasons": ["missing_vex", "ambiguous_indirect_call"],
|
||
"blastRadius": {
|
||
"dependents": 15,
|
||
"netFacing": true,
|
||
"privilege": "user"
|
||
},
|
||
"evidenceScarcity": 0.7,
|
||
"exploitPressure": {
|
||
"epss": 0.45,
|
||
"kev": false
|
||
},
|
||
"containment": {
|
||
"seccomp": "enforced",
|
||
"fs": "ro"
|
||
},
|
||
"score": 0.62,
|
||
"proofRef": "proofs/unknowns/unk-12345678/tree.json",
|
||
"createdAt": "2025-01-15T10:30:00Z",
|
||
"updatedAt": "2025-01-15T10:30:00Z"
|
||
}
|
||
],
|
||
"pagination": {
|
||
"page": 1,
|
||
"pageSize": 50,
|
||
"totalItems": 142,
|
||
"totalPages": 3
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Example
|
||
|
||
```bash
|
||
# Get top 10 highest-scored unknowns
|
||
curl -H "Authorization: Bearer $TOKEN" \
|
||
"https://scanner.example.com/api/v1/unknowns?sort=score&order=desc&pageSize=10"
|
||
|
||
# Filter by KEV and minimum score
|
||
curl -H "Authorization: Bearer $TOKEN" \
|
||
"https://scanner.example.com/api/v1/unknowns?kev=true&minScore=0.5"
|
||
|
||
# Filter by artifact
|
||
curl -H "Authorization: Bearer $TOKEN" \
|
||
"https://scanner.example.com/api/v1/unknowns?artifact=sha256:abc123"
|
||
```
|
||
|
||
### Get Unknown by ID
|
||
|
||
```http
|
||
GET /api/v1/unknowns/{id}
|
||
```
|
||
|
||
Returns detailed information about a specific unknown.
|
||
|
||
#### Response
|
||
|
||
```json
|
||
{
|
||
"id": "unk-12345678-abcd-1234-5678-abcdef123456",
|
||
"artifactDigest": "sha256:abc123...",
|
||
"artifactPurl": "pkg:oci/myapp@sha256:abc123",
|
||
"reasonCode": "Reachability",
|
||
"reasonCodeShort": "U-RCH",
|
||
"remediationHint": "Run reachability analysis",
|
||
"detailedHint": "Execute call-graph analysis to determine if vulnerable code paths are reachable from application entrypoints.",
|
||
"automationCommand": "stella analyze --reachability",
|
||
"evidenceRefs": [
|
||
{
|
||
"type": "reachability",
|
||
"uri": "proofs/unknowns/unk-12345678/evidence.json",
|
||
"digest": "sha256:0a1b2c..."
|
||
}
|
||
],
|
||
"reasons": ["missing_vex", "ambiguous_indirect_call"],
|
||
"reasonDetails": [
|
||
{
|
||
"code": "missing_vex",
|
||
"message": "No VEX statement found for CVE-2024-1234",
|
||
"component": "pkg:npm/lodash@4.17.20"
|
||
},
|
||
{
|
||
"code": "ambiguous_indirect_call",
|
||
"message": "Indirect call target could not be resolved",
|
||
"location": "src/utils.js:42"
|
||
}
|
||
],
|
||
"blastRadius": {
|
||
"dependents": 15,
|
||
"netFacing": true,
|
||
"privilege": "user"
|
||
},
|
||
"evidenceScarcity": 0.7,
|
||
"exploitPressure": {
|
||
"epss": 0.45,
|
||
"kev": false
|
||
},
|
||
"containment": {
|
||
"seccomp": "enforced",
|
||
"fs": "ro"
|
||
},
|
||
"score": 0.62,
|
||
"scoreBreakdown": {
|
||
"blastComponent": 0.35,
|
||
"scarcityComponent": 0.21,
|
||
"pressureComponent": 0.26,
|
||
"containmentDeduction": -0.20
|
||
},
|
||
"proofRef": "proofs/unknowns/unk-12345678/tree.json",
|
||
"createdAt": "2025-01-15T10:30:00Z",
|
||
"updatedAt": "2025-01-15T10:30:00Z"
|
||
}
|
||
```
|
||
|
||
### Get Unknown Proof
|
||
|
||
```http
|
||
GET /api/v1/unknowns/{id}/proof
|
||
```
|
||
|
||
Returns the proof tree explaining the ranking decision.
|
||
|
||
#### Response
|
||
|
||
```json
|
||
{
|
||
"version": "1.0",
|
||
"unknownId": "unk-12345678-abcd-1234-5678-abcdef123456",
|
||
"nodes": [
|
||
{
|
||
"kind": "input",
|
||
"hash": "sha256:abc...",
|
||
"data": {
|
||
"reasons": ["missing_vex"],
|
||
"evidenceScarcity": 0.7
|
||
}
|
||
},
|
||
{
|
||
"kind": "delta",
|
||
"hash": "sha256:def...",
|
||
"factor": "blast_radius",
|
||
"contribution": 0.35
|
||
},
|
||
{
|
||
"kind": "delta",
|
||
"hash": "sha256:ghi...",
|
||
"factor": "containment_seccomp",
|
||
"contribution": -0.10
|
||
},
|
||
{
|
||
"kind": "score",
|
||
"hash": "sha256:jkl...",
|
||
"finalScore": 0.62
|
||
}
|
||
],
|
||
"rootHash": "sha256:mno..."
|
||
}
|
||
```
|
||
|
||
### Batch Get Unknowns
|
||
|
||
```http
|
||
POST /api/v1/unknowns/batch
|
||
```
|
||
|
||
Get multiple unknowns by ID in a single request.
|
||
|
||
#### Request Body
|
||
|
||
```json
|
||
{
|
||
"ids": [
|
||
"unk-12345678-abcd-1234-5678-abcdef123456",
|
||
"unk-87654321-dcba-4321-8765-654321fedcba"
|
||
]
|
||
}
|
||
```
|
||
|
||
#### Response
|
||
|
||
Same format as list response with matching items.
|
||
|
||
### Get Unknowns Summary
|
||
|
||
```http
|
||
GET /api/v1/unknowns/summary
|
||
```
|
||
|
||
Returns aggregate statistics about unknowns.
|
||
|
||
#### Query Parameters
|
||
|
||
| Parameter | Type | Description |
|
||
|-----------|------|-------------|
|
||
| `artifact` | string | Filter by artifact digest |
|
||
|
||
#### Response
|
||
|
||
```json
|
||
{
|
||
"totalCount": 142,
|
||
"byReason": {
|
||
"missing_vex": 45,
|
||
"ambiguous_indirect_call": 32,
|
||
"incomplete_sbom": 28,
|
||
"unknown_platform": 15,
|
||
"other": 22
|
||
},
|
||
"byScoreBucket": {
|
||
"critical": 12, // score >= 0.8
|
||
"high": 35, // 0.6 <= score < 0.8
|
||
"medium": 48, // 0.4 <= score < 0.6
|
||
"low": 47 // score < 0.4
|
||
},
|
||
"byContainment": {
|
||
"enforced": 45,
|
||
"permissive": 32,
|
||
"unknown": 65
|
||
},
|
||
"kevCount": 8,
|
||
"avgScore": 0.52
|
||
}
|
||
```
|
||
|
||
## Reason Codes
|
||
|
||
| Code | Short Code | Description |
|
||
|------|------------|-------------|
|
||
| `Reachability` | `U-RCH` | Call path analysis is indeterminate. |
|
||
| `Identity` | `U-ID` | Ambiguous package identity or missing digest. |
|
||
| `Provenance` | `U-PROV` | Cannot map binary artifact to source repository. |
|
||
| `VexConflict` | `U-VEX` | VEX statements conflict or applicability data is missing. |
|
||
| `FeedGap` | `U-FEED` | Required advisory/feed coverage missing or stale. |
|
||
| `ConfigUnknown` | `U-CONFIG` | Runtime configuration or feature flags not observable. |
|
||
| `AnalyzerLimit` | `U-ANALYZER` | Language or framework not supported by analyzer. |
|
||
|
||
## Score Calculation
|
||
|
||
The unknown score is calculated as:
|
||
|
||
```
|
||
score = 0.60 × blast + 0.30 × scarcity + 0.30 × pressure + containment_deduction
|
||
```
|
||
|
||
Where:
|
||
- `blast` = normalized blast radius (0-1)
|
||
- `scarcity` = evidence scarcity factor (0-1)
|
||
- `pressure` = exploit pressure (EPSS + KEV factor)
|
||
- `containment_deduction` = -0.10 for enforced seccomp, -0.10 for read-only FS
|
||
|
||
### Blast Radius Normalization
|
||
|
||
```
|
||
dependents_normalized = min(dependents / 50, 1.0)
|
||
net_factor = 0.5 if net_facing else 0.0
|
||
priv_factor = 0.5 if privilege == "root" else 0.0
|
||
blast = min((dependents_normalized + net_factor + priv_factor) / 2, 1.0)
|
||
```
|
||
|
||
### Exploit Pressure
|
||
|
||
```
|
||
epss_normalized = epss ?? 0.35 // Default if unknown
|
||
kev_factor = 0.30 if kev else 0.0
|
||
pressure = min(epss_normalized + kev_factor, 1.0)
|
||
```
|
||
|
||
## Error Responses
|
||
|
||
| Status | Code | Description |
|
||
|--------|------|-------------|
|
||
| 400 | `INVALID_PARAMETER` | Invalid query parameter |
|
||
| 401 | `UNAUTHORIZED` | Missing or invalid token |
|
||
| 403 | `FORBIDDEN` | Insufficient permissions |
|
||
| 404 | `NOT_FOUND` | Unknown not found |
|
||
| 429 | `RATE_LIMITED` | Too many requests |
|
||
|
||
## Rate Limits
|
||
|
||
- List: 100 requests/minute
|
||
- Get by ID: 300 requests/minute
|
||
- Summary: 60 requests/minute
|
||
|
||
## Related Documentation
|
||
|
||
- [Unknowns Ranking Technical Reference](../product-advisories/14-Dec-2025%20-%20Triage%20and%20Unknowns%20Technical%20Reference.md)
|
||
- [Scanner Architecture](../modules/scanner/architecture.md)
|
||
- [Proof Bundle Format](../api/proof-bundle-format.md)
|