Files
git.stella-ops.org/docs/api/unknowns-api.md
master 8bbfe4d2d2 feat(rate-limiting): Implement core rate limiting functionality with configuration, decision-making, metrics, middleware, and service registration
- Add RateLimitConfig for configuration management with YAML binding support.
- Introduce RateLimitDecision to encapsulate the result of rate limit checks.
- Implement RateLimitMetrics for OpenTelemetry metrics tracking.
- Create RateLimitMiddleware for enforcing rate limits on incoming requests.
- Develop RateLimitService to orchestrate instance and environment rate limit checks.
- Add RateLimitServiceCollectionExtensions for dependency injection registration.
2025-12-17 18:02:37 +02:00

7.6 KiB
Raw Blame History

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:

Authorization: Bearer <token>

Required scope: scanner:unknowns:read

Endpoints

List Unknowns

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

{
  "items": [
    {
      "id": "unk-12345678-abcd-1234-5678-abcdef123456",
      "artifactDigest": "sha256:abc123...",
      "artifactPurl": "pkg:oci/myapp@sha256:abc123",
      "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

# 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

GET /api/v1/unknowns/{id}

Returns detailed information about a specific unknown.

Response

{
  "id": "unk-12345678-abcd-1234-5678-abcdef123456",
  "artifactDigest": "sha256:abc123...",
  "artifactPurl": "pkg:oci/myapp@sha256:abc123",
  "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

GET /api/v1/unknowns/{id}/proof

Returns the proof tree explaining the ranking decision.

Response

{
  "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

POST /api/v1/unknowns/batch

Get multiple unknowns by ID in a single request.

Request Body

{
  "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

GET /api/v1/unknowns/summary

Returns aggregate statistics about unknowns.

Query Parameters

Parameter Type Description
artifact string Filter by artifact digest

Response

{
  "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 Description
missing_vex No VEX statement for vulnerability
ambiguous_indirect_call Indirect call target unresolved
incomplete_sbom SBOM missing component data
unknown_platform Platform not recognized
missing_advisory No advisory data for CVE
conflicting_evidence Multiple conflicting data sources
stale_data Data exceeds freshness threshold

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