Files
git.stella-ops.org/docs/api/score-replay-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

6.5 KiB

Score Replay API Reference

Sprint: SPRINT_3401_0002_0001
Task: SCORE-REPLAY-014 - Update scanner API docs with replay endpoint

Overview

The Score Replay API enables deterministic re-scoring of scans using historical manifests. This is essential for auditing, compliance verification, and investigating how scores change with updated advisory feeds.

Base URL

/api/v1/score

Authentication

All endpoints require Bearer token authentication:

Authorization: Bearer <token>

Required scope: scanner:replay:read for GET, scanner:replay:write for POST

Endpoints

Replay Score

POST /api/v1/score/replay

Re-scores a scan using the original manifest with an optionally different feed snapshot.

Request Body

{
  "scanId": "scan-12345678-abcd",
  "feedSnapshotHash": "sha256:abc123...",
  "policyVersion": "1.0.0",
  "dryRun": false
}
Field Type Required Description
scanId string Yes Original scan ID to replay
feedSnapshotHash string No Feed snapshot to use (defaults to current)
policyVersion string No Policy version (defaults to original)
dryRun boolean No If true, calculates but doesn't persist

Response

{
  "replayId": "replay-87654321-dcba",
  "originalScanId": "scan-12345678-abcd",
  "status": "completed",
  "feedSnapshotHash": "sha256:abc123...",
  "policyVersion": "1.0.0",
  "originalManifestHash": "sha256:def456...",
  "replayedManifestHash": "sha256:ghi789...",
  "scoreDelta": {
    "originalScore": 7.5,
    "replayedScore": 6.8,
    "delta": -0.7
  },
  "findingsDelta": {
    "added": 2,
    "removed": 5,
    "rescored": 12,
    "unchanged": 45
  },
  "proofBundleRef": "proofs/replays/replay-87654321/bundle.zip",
  "duration": {
    "ms": 1250
  },
  "createdAt": "2025-01-15T10:30:00Z"
}

Example

# Replay with latest feed
curl -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"scanId": "scan-12345678-abcd"}' \
  "https://scanner.example.com/api/v1/score/replay"

# Replay with specific feed snapshot
curl -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "scanId": "scan-12345678-abcd",
    "feedSnapshotHash": "sha256:abc123..."
  }' \
  "https://scanner.example.com/api/v1/score/replay"

# Dry run (preview only)
curl -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "scanId": "scan-12345678-abcd",
    "dryRun": true
  }' \
  "https://scanner.example.com/api/v1/score/replay"

Get Replay History

GET /api/v1/score/replays

Returns history of score replays.

Query Parameters

Parameter Type Default Description
scanId string - Filter by original scan
page int 1 Page number
pageSize int 50 Items per page

Response

{
  "items": [
    {
      "replayId": "replay-87654321-dcba",
      "originalScanId": "scan-12345678-abcd",
      "triggerType": "manual",
      "scoreDelta": -0.7,
      "findingsAdded": 2,
      "findingsRemoved": 5,
      "createdAt": "2025-01-15T10:30:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "pageSize": 50,
    "totalItems": 12,
    "totalPages": 1
  }
}

Get Replay Details

GET /api/v1/score/replays/{replayId}

Returns detailed information about a specific replay.

Get Scan Manifest

GET /api/v1/scans/{scanId}/manifest

Returns the scan manifest containing all input hashes.

Response

{
  "manifestId": "manifest-12345678",
  "scanId": "scan-12345678-abcd",
  "manifestHash": "sha256:def456...",
  "sbomHash": "sha256:aaa111...",
  "rulesHash": "sha256:bbb222...",
  "feedHash": "sha256:ccc333...",
  "policyHash": "sha256:ddd444...",
  "scannerVersion": "1.0.0",
  "createdAt": "2025-01-15T10:00:00Z"
}

Get Proof Bundle

GET /api/v1/scans/{scanId}/proof-bundle

Downloads the proof bundle (ZIP archive) for a scan.

Response

Returns application/zip with the proof bundle containing:

  • manifest.json - Signed scan manifest
  • ledger.json - Proof ledger nodes
  • sbom.json - Input SBOM (hash-verified)
  • findings.json - Scored findings
  • signature.dsse - DSSE envelope

Scheduled Replay

Scans can be automatically replayed when feed snapshots change.

Configuration

# config/scanner.yaml
score_replay:
  enabled: true
  schedule: "0 4 * * *"  # Daily at 4 AM UTC
  max_age_days: 30       # Only replay scans from last 30 days
  notify_on_delta: true  # Send notification if scores change
  delta_threshold: 0.5   # Only notify if delta > threshold

Trigger Types

Type Description
manual User-initiated via API
feed_update Triggered by new feed snapshot
policy_change Triggered by policy version change
scheduled Triggered by scheduled job

Determinism Guarantees

Score replay guarantees deterministic results when:

  1. Same manifest hash - All inputs are identical
  2. Same scanner version - Scoring algorithm unchanged
  3. Same policy version - Policy rules unchanged

Manifest Contents

The manifest captures:

  • SBOM content hash
  • Rules snapshot hash
  • Advisory feed snapshot hash
  • Policy configuration hash
  • Scanner version

Verification

# Verify replay determinism
curl -H "Authorization: Bearer $TOKEN" \
  "https://scanner.example.com/api/v1/scans/{scanId}/manifest" \
  | jq '.manifestHash'

# Compare with replay
curl -H "Authorization: Bearer $TOKEN" \
  "https://scanner.example.com/api/v1/score/replays/{replayId}" \
  | jq '.replayedManifestHash'

Error Responses

Status Code Description
400 INVALID_SCAN_ID Scan ID not found
400 INVALID_FEED_SNAPSHOT Feed snapshot not found
400 MANIFEST_NOT_FOUND Scan manifest missing
401 UNAUTHORIZED Invalid token
403 FORBIDDEN Insufficient permissions
409 REPLAY_IN_PROGRESS Replay already running for scan
429 RATE_LIMITED Too many requests

Rate Limits

  • POST replay: 10 requests/minute
  • GET replays: 100 requests/minute
  • GET manifest: 100 requests/minute