Refactor code structure for improved readability and maintainability; optimize performance in key functions.
This commit is contained in:
@@ -1,33 +1,32 @@
|
||||
# Scanner Drift API Reference
|
||||
# Scanner Drift API Reference
|
||||
|
||||
**Module:** Scanner
|
||||
**Version:** 1.0
|
||||
**Base Path:** `/api/scanner`
|
||||
**Base Path:** `/api/v1`
|
||||
**Last Updated:** 2025-12-22
|
||||
|
||||
---
|
||||
|
||||
## 1. Overview
|
||||
|
||||
The Scanner Drift API provides endpoints for computing and retrieving reachability drift analysis between scans. Drift detection identifies when code changes create new paths to vulnerable sinks or mitigate existing risks.
|
||||
The Scanner Drift API computes and retrieves reachability drift between scans. Drift detection identifies when code changes introduce new paths to sensitive sinks or remove existing paths.
|
||||
|
||||
---
|
||||
|
||||
## 2. Authentication & Authorization
|
||||
## 2. Authentication and Authorization
|
||||
|
||||
### Required Scopes
|
||||
|
||||
| Endpoint | Scope |
|
||||
|----------|-------|
|
||||
| Read drift results | `scanner:read` |
|
||||
| Compute reachability | `scanner:write` |
|
||||
| Admin operations | `scanner:admin` |
|
||||
|---|---|
|
||||
| Read drift results | `scanner.scans.read` |
|
||||
| Compute reachability | `scanner.scans.write` |
|
||||
|
||||
### Headers
|
||||
|
||||
```http
|
||||
Authorization: Bearer <access_token>
|
||||
X-Tenant-Id: <tenant_uuid>
|
||||
X-Tenant-Id: <tenant_uuid> # optional fallback for rate limiting
|
||||
```
|
||||
|
||||
---
|
||||
@@ -36,68 +35,32 @@ X-Tenant-Id: <tenant_uuid>
|
||||
|
||||
### 3.1 GET /scans/{scanId}/drift
|
||||
|
||||
Retrieves drift analysis results comparing the specified scan against its base scan.
|
||||
Returns drift results for the scan. If `baseScanId` is provided, drift is computed and stored. If omitted, the most recent stored drift result is returned.
|
||||
|
||||
**Parameters:**
|
||||
**Parameters**
|
||||
|
||||
| Name | In | Type | Required | Description |
|
||||
|------|-----|------|----------|-------------|
|
||||
| scanId | path | string | Yes | Head scan identifier |
|
||||
| baseScanId | query | string | No | Base scan ID (defaults to previous scan) |
|
||||
| language | query | string | No | Filter by language (dotnet, node, java, etc.) |
|
||||
|---|---|---|---|---|
|
||||
| scanId | path | string | yes | Head scan identifier |
|
||||
| baseScanId | query | string | no | Base scan identifier |
|
||||
| language | query | string | no | Language (default: `dotnet`) |
|
||||
| includeFullPath | query | boolean | no | Include full path nodes in compressed paths |
|
||||
|
||||
**Response: 200 OK**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"baseScanId": "abc123",
|
||||
"headScanId": "def456",
|
||||
"baseScanId": "base123",
|
||||
"headScanId": "head456",
|
||||
"language": "dotnet",
|
||||
"detectedAt": "2025-12-22T10:30:00Z",
|
||||
"newlyReachableCount": 3,
|
||||
"newlyUnreachableCount": 1,
|
||||
"totalDriftCount": 4,
|
||||
"hasMaterialDrift": true,
|
||||
"resultDigest": "sha256:a1b2c3d4..."
|
||||
}
|
||||
```
|
||||
|
||||
**Response: 404 Not Found**
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "DRIFT_NOT_FOUND",
|
||||
"message": "No drift analysis found for scan def456"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.2 GET /drift/{driftId}/sinks
|
||||
|
||||
Retrieves individual drifted sinks with pagination.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
| Name | In | Type | Required | Description |
|
||||
|------|-----|------|----------|-------------|
|
||||
| driftId | path | uuid | Yes | Drift result identifier |
|
||||
| direction | query | string | No | Filter: `became_reachable` or `became_unreachable` |
|
||||
| sinkCategory | query | string | No | Filter by sink category |
|
||||
| offset | query | int | No | Pagination offset (default: 0) |
|
||||
| limit | query | int | No | Page size (default: 100, max: 1000) |
|
||||
|
||||
**Response: 200 OK**
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [
|
||||
"newlyReachable": [
|
||||
{
|
||||
"id": "660e8400-e29b-41d4-a716-446655440001",
|
||||
"sinkNodeId": "MyApp.Services.DbService.ExecuteQuery(string)",
|
||||
"symbol": "DbService.ExecuteQuery",
|
||||
"sinkCategory": "sql_raw",
|
||||
"sinkCategory": "SQL_RAW",
|
||||
"direction": "became_reachable",
|
||||
"cause": {
|
||||
"kind": "guard_removed",
|
||||
@@ -112,13 +75,19 @@ Retrieves individual drifted sinks with pagination.
|
||||
"nodeId": "MyApp.Controllers.UserController.GetUser(int)",
|
||||
"symbol": "UserController.GetUser",
|
||||
"file": "src/Controllers/UserController.cs",
|
||||
"line": 15
|
||||
"line": 15,
|
||||
"package": "app",
|
||||
"isChanged": false,
|
||||
"changeKind": null
|
||||
},
|
||||
"sink": {
|
||||
"nodeId": "MyApp.Services.DbService.ExecuteQuery(string)",
|
||||
"symbol": "DbService.ExecuteQuery",
|
||||
"file": "src/Services/DbService.cs",
|
||||
"line": 88
|
||||
"line": 88,
|
||||
"package": "app",
|
||||
"isChanged": false,
|
||||
"changeKind": null
|
||||
},
|
||||
"intermediateCount": 3,
|
||||
"keyNodes": [
|
||||
@@ -127,25 +96,90 @@ Retrieves individual drifted sinks with pagination.
|
||||
"symbol": "AuthMiddleware.Validate",
|
||||
"file": "src/Middleware/AuthMiddleware.cs",
|
||||
"line": 42,
|
||||
"package": "app",
|
||||
"isChanged": true,
|
||||
"changeKind": "guard_changed"
|
||||
}
|
||||
]
|
||||
},
|
||||
"associatedVulns": [
|
||||
{
|
||||
"cveId": "CVE-2024-12345",
|
||||
"epss": 0.85,
|
||||
"cvss": 9.8,
|
||||
"vexStatus": "affected",
|
||||
"packagePurl": "pkg:nuget/Dapper@2.0.123"
|
||||
}
|
||||
]
|
||||
"associatedVulns": []
|
||||
}
|
||||
],
|
||||
"totalCount": 3,
|
||||
"newlyUnreachable": [],
|
||||
"resultDigest": "sha256:a1b2c3d4...",
|
||||
"totalDriftCount": 1,
|
||||
"hasMaterialDrift": true
|
||||
}
|
||||
```
|
||||
|
||||
**Response: 404 Not Found**
|
||||
|
||||
Returned if the scan or drift result is missing or if call graph snapshots are not available.
|
||||
|
||||
---
|
||||
|
||||
### 3.2 GET /drift/{driftId}/sinks
|
||||
|
||||
Returns drifted sinks for a drift result.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | In | Type | Required | Description |
|
||||
|---|---|---|---|---|
|
||||
| driftId | path | uuid | yes | Drift result identifier |
|
||||
| direction | query | string | no | `became_reachable` or `became_unreachable` |
|
||||
| offset | query | integer | no | Offset (default: 0) |
|
||||
| limit | query | integer | no | Page size (default: 100, max: 500) |
|
||||
|
||||
**Response: 200 OK**
|
||||
|
||||
```json
|
||||
{
|
||||
"driftId": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"direction": "became_reachable",
|
||||
"offset": 0,
|
||||
"limit": 100
|
||||
"limit": 100,
|
||||
"count": 1,
|
||||
"sinks": [
|
||||
{
|
||||
"id": "660e8400-e29b-41d4-a716-446655440001",
|
||||
"sinkNodeId": "MyApp.Services.DbService.ExecuteQuery(string)",
|
||||
"symbol": "DbService.ExecuteQuery",
|
||||
"sinkCategory": "SQL_RAW",
|
||||
"direction": "became_reachable",
|
||||
"cause": {
|
||||
"kind": "guard_removed",
|
||||
"description": "Guard condition removed in AuthMiddleware.Validate",
|
||||
"changedSymbol": "AuthMiddleware.Validate",
|
||||
"changedFile": "src/Middleware/AuthMiddleware.cs",
|
||||
"changedLine": 42,
|
||||
"codeChangeId": "770e8400-e29b-41d4-a716-446655440002"
|
||||
},
|
||||
"path": {
|
||||
"entrypoint": {
|
||||
"nodeId": "MyApp.Controllers.UserController.GetUser(int)",
|
||||
"symbol": "UserController.GetUser",
|
||||
"file": "src/Controllers/UserController.cs",
|
||||
"line": 15,
|
||||
"package": "app",
|
||||
"isChanged": false,
|
||||
"changeKind": null
|
||||
},
|
||||
"sink": {
|
||||
"nodeId": "MyApp.Services.DbService.ExecuteQuery(string)",
|
||||
"symbol": "DbService.ExecuteQuery",
|
||||
"file": "src/Services/DbService.cs",
|
||||
"line": 88,
|
||||
"package": "app",
|
||||
"isChanged": false,
|
||||
"changeKind": null
|
||||
},
|
||||
"intermediateCount": 3,
|
||||
"keyNodes": []
|
||||
},
|
||||
"associatedVulns": []
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -153,21 +187,21 @@ Retrieves individual drifted sinks with pagination.
|
||||
|
||||
### 3.3 POST /scans/{scanId}/compute-reachability
|
||||
|
||||
Triggers reachability computation for a scan. Idempotent - returns cached result if already computed.
|
||||
Triggers reachability computation for a scan.
|
||||
|
||||
**Parameters:**
|
||||
**Parameters**
|
||||
|
||||
| Name | In | Type | Required | Description |
|
||||
|------|-----|------|----------|-------------|
|
||||
| scanId | path | string | Yes | Scan identifier |
|
||||
|---|---|---|---|---|
|
||||
| scanId | path | string | yes | Scan identifier |
|
||||
|
||||
**Request Body:**
|
||||
**Request Body**
|
||||
|
||||
```json
|
||||
{
|
||||
"languages": ["dotnet", "node"],
|
||||
"baseScanId": "abc123",
|
||||
"forceRecompute": false
|
||||
"forceRecompute": false,
|
||||
"entrypoints": ["MyApp.Controllers.UserController.GetUser"],
|
||||
"targets": ["pkg:nuget/Dapper@2.0.123"]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -175,37 +209,29 @@ Triggers reachability computation for a scan. Idempotent - returns cached result
|
||||
|
||||
```json
|
||||
{
|
||||
"jobId": "880e8400-e29b-41d4-a716-446655440003",
|
||||
"status": "queued",
|
||||
"estimatedCompletionSeconds": 30
|
||||
"jobId": "reachability_head456",
|
||||
"status": "scheduled",
|
||||
"estimatedDuration": null
|
||||
}
|
||||
```
|
||||
|
||||
**Response: 200 OK** (cached result)
|
||||
**Response: 409 Conflict**
|
||||
|
||||
```json
|
||||
{
|
||||
"jobId": "880e8400-e29b-41d4-a716-446655440003",
|
||||
"status": "completed",
|
||||
"driftResultId": "550e8400-e29b-41d4-a716-446655440000"
|
||||
}
|
||||
```
|
||||
Returned when computation is already in progress for the scan.
|
||||
|
||||
---
|
||||
|
||||
### 3.4 GET /scans/{scanId}/reachability/components
|
||||
|
||||
Lists components with their reachability status.
|
||||
Lists components with reachability status.
|
||||
|
||||
**Parameters:**
|
||||
**Parameters**
|
||||
|
||||
| Name | In | Type | Required | Description |
|
||||
|------|-----|------|----------|-------------|
|
||||
| scanId | path | string | Yes | Scan identifier |
|
||||
| language | query | string | No | Filter by language |
|
||||
| reachable | query | bool | No | Filter by reachability |
|
||||
| offset | query | int | No | Pagination offset |
|
||||
| limit | query | int | No | Page size |
|
||||
|---|---|---|---|---|
|
||||
| scanId | path | string | yes | Scan identifier |
|
||||
| purl | query | string | no | Filter by PURL |
|
||||
| status | query | string | no | Filter by status |
|
||||
|
||||
**Response: 200 OK**
|
||||
|
||||
@@ -214,17 +240,13 @@ Lists components with their reachability status.
|
||||
"items": [
|
||||
{
|
||||
"purl": "pkg:nuget/Newtonsoft.Json@13.0.1",
|
||||
"language": "dotnet",
|
||||
"reachableSinkCount": 2,
|
||||
"unreachableSinkCount": 5,
|
||||
"totalSinkCount": 7,
|
||||
"highestSeveritySink": "unsafe_deser",
|
||||
"reachabilityGate": 5
|
||||
"status": "reachable",
|
||||
"confidence": 0.92,
|
||||
"latticeState": "confirmed",
|
||||
"why": ["entrypoint:UserController.GetUser"]
|
||||
}
|
||||
],
|
||||
"totalCount": 42,
|
||||
"offset": 0,
|
||||
"limit": 100
|
||||
"total": 1
|
||||
}
|
||||
```
|
||||
|
||||
@@ -232,17 +254,15 @@ Lists components with their reachability status.
|
||||
|
||||
### 3.5 GET /scans/{scanId}/reachability/findings
|
||||
|
||||
Lists reachable vulnerable sinks with CVE associations.
|
||||
Lists reachability findings for CVEs.
|
||||
|
||||
**Parameters:**
|
||||
**Parameters**
|
||||
|
||||
| Name | In | Type | Required | Description |
|
||||
|------|-----|------|----------|-------------|
|
||||
| scanId | path | string | Yes | Scan identifier |
|
||||
| minCvss | query | float | No | Minimum CVSS score |
|
||||
| kevOnly | query | bool | No | Only KEV vulnerabilities |
|
||||
| offset | query | int | No | Pagination offset |
|
||||
| limit | query | int | No | Page size |
|
||||
|---|---|---|---|---|
|
||||
| scanId | path | string | yes | Scan identifier |
|
||||
| cve | query | string | no | Filter by CVE |
|
||||
| status | query | string | no | Filter by status |
|
||||
|
||||
**Response: 200 OK**
|
||||
|
||||
@@ -250,25 +270,16 @@ Lists reachable vulnerable sinks with CVE associations.
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"sinkNodeId": "MyApp.Services.CryptoService.Encrypt(string)",
|
||||
"symbol": "CryptoService.Encrypt",
|
||||
"sinkCategory": "crypto_weak",
|
||||
"isReachable": true,
|
||||
"shortestPathLength": 4,
|
||||
"vulnerabilities": [
|
||||
{
|
||||
"cveId": "CVE-2024-54321",
|
||||
"cvss": 7.5,
|
||||
"epss": 0.42,
|
||||
"isKev": false,
|
||||
"vexStatus": "affected"
|
||||
}
|
||||
]
|
||||
"cveId": "CVE-2024-12345",
|
||||
"purl": "pkg:nuget/Dapper@2.0.123",
|
||||
"status": "reachable",
|
||||
"confidence": 0.81,
|
||||
"latticeState": "likely",
|
||||
"severity": "critical",
|
||||
"affectedVersions": "< 2.0.200"
|
||||
}
|
||||
],
|
||||
"totalCount": 15,
|
||||
"offset": 0,
|
||||
"limit": 100
|
||||
"total": 1
|
||||
}
|
||||
```
|
||||
|
||||
@@ -276,139 +287,123 @@ Lists reachable vulnerable sinks with CVE associations.
|
||||
|
||||
### 3.6 GET /scans/{scanId}/reachability/explain
|
||||
|
||||
Explains why a specific sink is reachable or unreachable.
|
||||
Explains reachability for a CVE and PURL.
|
||||
|
||||
**Parameters:**
|
||||
**Parameters**
|
||||
|
||||
| Name | In | Type | Required | Description |
|
||||
|------|-----|------|----------|-------------|
|
||||
| scanId | path | string | Yes | Scan identifier |
|
||||
| sinkNodeId | query | string | Yes | Sink node identifier |
|
||||
| includeFullPath | query | bool | No | Include full path (default: false) |
|
||||
|---|---|---|---|---|
|
||||
| scanId | path | string | yes | Scan identifier |
|
||||
| cve | query | string | yes | CVE identifier |
|
||||
| purl | query | string | yes | Package URL |
|
||||
|
||||
**Response: 200 OK**
|
||||
|
||||
```json
|
||||
{
|
||||
"sinkNodeId": "MyApp.Services.DbService.ExecuteQuery(string)",
|
||||
"isReachable": true,
|
||||
"reachabilityGate": 6,
|
||||
"confidence": "confirmed",
|
||||
"explanation": "Sink is reachable from 2 HTTP entrypoints via direct call paths",
|
||||
"entrypoints": [
|
||||
{
|
||||
"nodeId": "MyApp.Controllers.UserController.GetUser(int)",
|
||||
"entrypointType": "http_handler",
|
||||
"pathLength": 4
|
||||
},
|
||||
{
|
||||
"nodeId": "MyApp.Controllers.AdminController.Query(string)",
|
||||
"entrypointType": "http_handler",
|
||||
"pathLength": 2
|
||||
}
|
||||
"cveId": "CVE-2024-12345",
|
||||
"purl": "pkg:nuget/Dapper@2.0.123",
|
||||
"status": "reachable",
|
||||
"confidence": 0.81,
|
||||
"latticeState": "likely",
|
||||
"pathWitness": ["entrypoint:UserController.GetUser", "sink:Dapper.Query"],
|
||||
"why": [
|
||||
{ "code": "call_graph", "description": "Path exists from HTTP entrypoint", "impact": 0.6 }
|
||||
],
|
||||
"shortestPath": {
|
||||
"entrypoint": {...},
|
||||
"sink": {...},
|
||||
"intermediateCount": 1,
|
||||
"keyNodes": [...]
|
||||
"evidence": {
|
||||
"staticAnalysis": {
|
||||
"callgraphDigest": "sha256:...",
|
||||
"pathLength": 4,
|
||||
"edgeTypes": ["direct", "virtual"]
|
||||
},
|
||||
"runtimeEvidence": {
|
||||
"observed": false,
|
||||
"hitCount": 0,
|
||||
"lastObserved": null
|
||||
},
|
||||
"policyEvaluation": {
|
||||
"policyDigest": "sha256:...",
|
||||
"verdict": "block",
|
||||
"verdictReason": "delta_reachable > 0"
|
||||
}
|
||||
},
|
||||
"fullPath": ["node1", "node2", "node3", "sink"]
|
||||
"spineId": "spine:sha256:..."
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Request/Response Models
|
||||
## 4. Request and Response Models
|
||||
|
||||
### 4.1 DriftDirection
|
||||
Key models (JSON names shown):
|
||||
- `ReachabilityDriftResult`: `id`, `baseScanId`, `headScanId`, `language`, `detectedAt`, `newlyReachable`, `newlyUnreachable`, `resultDigest`, `totalDriftCount`, `hasMaterialDrift`.
|
||||
- `DriftedSink`: `id`, `sinkNodeId`, `symbol`, `sinkCategory`, `direction`, `cause`, `path`, `associatedVulns`.
|
||||
- `DriftCause`: `kind`, `description`, `changedSymbol`, `changedFile`, `changedLine`, `codeChangeId`.
|
||||
- `CompressedPath`: `entrypoint`, `sink`, `intermediateCount`, `keyNodes`, `fullPath` (optional).
|
||||
- `PathNode`: `nodeId`, `symbol`, `file`, `line`, `package`, `isChanged`, `changeKind`.
|
||||
- `ComputeReachabilityRequestDto`: `forceRecompute`, `entrypoints`, `targets`.
|
||||
- `ComputeReachabilityResponseDto`: `jobId`, `status`, `estimatedDuration`.
|
||||
|
||||
```typescript
|
||||
enum DriftDirection {
|
||||
became_reachable = "became_reachable",
|
||||
became_unreachable = "became_unreachable"
|
||||
}
|
||||
---
|
||||
|
||||
## 5. Enumerations
|
||||
|
||||
### DriftDirection
|
||||
```text
|
||||
became_reachable
|
||||
became_unreachable
|
||||
```
|
||||
|
||||
### 4.2 DriftCauseKind
|
||||
|
||||
```typescript
|
||||
enum DriftCauseKind {
|
||||
guard_removed = "guard_removed",
|
||||
guard_added = "guard_added",
|
||||
new_public_route = "new_public_route",
|
||||
visibility_escalated = "visibility_escalated",
|
||||
dependency_upgraded = "dependency_upgraded",
|
||||
symbol_removed = "symbol_removed",
|
||||
unknown = "unknown"
|
||||
}
|
||||
### DriftCauseKind
|
||||
```text
|
||||
guard_removed
|
||||
guard_added
|
||||
new_public_route
|
||||
visibility_escalated
|
||||
dependency_upgraded
|
||||
symbol_removed
|
||||
unknown
|
||||
```
|
||||
|
||||
### 4.3 SinkCategory
|
||||
|
||||
```typescript
|
||||
enum SinkCategory {
|
||||
cmd_exec = "cmd_exec",
|
||||
unsafe_deser = "unsafe_deser",
|
||||
sql_raw = "sql_raw",
|
||||
ssrf = "ssrf",
|
||||
file_write = "file_write",
|
||||
path_traversal = "path_traversal",
|
||||
template_injection = "template_injection",
|
||||
crypto_weak = "crypto_weak",
|
||||
authz_bypass = "authz_bypass",
|
||||
ldap_injection = "ldap_injection",
|
||||
xpath_injection = "xpath_injection",
|
||||
xxe_injection = "xxe_injection",
|
||||
code_injection = "code_injection",
|
||||
log_injection = "log_injection",
|
||||
reflection = "reflection",
|
||||
open_redirect = "open_redirect"
|
||||
}
|
||||
### CodeChangeKind
|
||||
```text
|
||||
added
|
||||
removed
|
||||
signature_changed
|
||||
guard_changed
|
||||
dependency_changed
|
||||
visibility_changed
|
||||
```
|
||||
|
||||
### 4.4 CodeChangeKind
|
||||
|
||||
```typescript
|
||||
enum CodeChangeKind {
|
||||
added = "added",
|
||||
removed = "removed",
|
||||
signature_changed = "signature_changed",
|
||||
guard_changed = "guard_changed",
|
||||
dependency_changed = "dependency_changed",
|
||||
visibility_changed = "visibility_changed"
|
||||
}
|
||||
### SinkCategory
|
||||
```text
|
||||
CMD_EXEC
|
||||
UNSAFE_DESER
|
||||
SQL_RAW
|
||||
SSRF
|
||||
FILE_WRITE
|
||||
PATH_TRAVERSAL
|
||||
TEMPLATE_INJECTION
|
||||
CRYPTO_WEAK
|
||||
AUTHZ_BYPASS
|
||||
LDAP_INJECTION
|
||||
XPATH_INJECTION
|
||||
XXE
|
||||
CODE_INJECTION
|
||||
LOG_INJECTION
|
||||
REFLECTION
|
||||
OPEN_REDIRECT
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Error Codes
|
||||
## 6. Errors
|
||||
|
||||
| Code | HTTP Status | Description |
|
||||
|------|-------------|-------------|
|
||||
| `SCAN_NOT_FOUND` | 404 | Scan ID does not exist |
|
||||
| `DRIFT_NOT_FOUND` | 404 | No drift analysis for this scan |
|
||||
| `GRAPH_NOT_EXTRACTED` | 400 | Call graph not yet extracted |
|
||||
| `LANGUAGE_NOT_SUPPORTED` | 400 | Language not supported for reachability |
|
||||
| `COMPUTATION_IN_PROGRESS` | 409 | Reachability computation already running |
|
||||
| `COMPUTATION_FAILED` | 500 | Reachability computation failed |
|
||||
| `INVALID_SINK_ID` | 400 | Sink node ID not found in graph |
|
||||
|
||||
---
|
||||
|
||||
## 6. Rate Limiting
|
||||
|
||||
| Endpoint | Rate Limit |
|
||||
|----------|------------|
|
||||
| GET endpoints | 100/min |
|
||||
| POST compute | 10/min |
|
||||
|
||||
Rate limit headers:
|
||||
```http
|
||||
X-RateLimit-Limit: 100
|
||||
X-RateLimit-Remaining: 95
|
||||
X-RateLimit-Reset: 1703242800
|
||||
```
|
||||
Endpoints return Problem Details (RFC 7807) for errors. Common cases:
|
||||
- 400: invalid scan identifier, invalid direction, missing query parameters.
|
||||
- 404: scan not found, call graph snapshot missing, drift result not found.
|
||||
- 409: reachability computation already in progress.
|
||||
- 500: unexpected server error.
|
||||
|
||||
---
|
||||
|
||||
@@ -418,109 +413,32 @@ X-RateLimit-Reset: 1703242800
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
'https://api.stellaops.example/api/scanner/scans/def456/drift?language=dotnet' \
|
||||
-H 'Authorization: Bearer <token>' \
|
||||
-H 'X-Tenant-Id: <tenant_id>'
|
||||
'https://scanner.example/api/v1/scans/head456/drift?baseScanId=base123&language=dotnet' \
|
||||
-H 'Authorization: Bearer <token>'
|
||||
```
|
||||
|
||||
### 7.2 cURL - Compute Reachability
|
||||
### 7.2 cURL - List Drifted Sinks
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
'https://scanner.example/api/v1/drift/550e8400-e29b-41d4-a716-446655440000/sinks?direction=became_reachable&offset=0&limit=100' \
|
||||
-H 'Authorization: Bearer <token>'
|
||||
```
|
||||
|
||||
### 7.3 cURL - Compute Reachability
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
'https://api.stellaops.example/api/scanner/scans/def456/compute-reachability' \
|
||||
'https://scanner.example/api/v1/scans/head456/compute-reachability' \
|
||||
-H 'Authorization: Bearer <token>' \
|
||||
-H 'X-Tenant-Id: <tenant_id>' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"languages": ["dotnet"],
|
||||
"baseScanId": "abc123"
|
||||
}'
|
||||
```
|
||||
|
||||
### 7.3 C# SDK
|
||||
|
||||
```csharp
|
||||
var client = new ScannerClient(options);
|
||||
|
||||
// Get drift results
|
||||
var drift = await client.GetDriftAsync("def456", language: "dotnet");
|
||||
Console.WriteLine($"Newly reachable: {drift.NewlyReachableCount}");
|
||||
|
||||
// Get drifted sinks
|
||||
var sinks = await client.GetDriftedSinksAsync(drift.Id,
|
||||
direction: DriftDirection.BecameReachable);
|
||||
|
||||
foreach (var sink in sinks.Items)
|
||||
{
|
||||
Console.WriteLine($"{sink.Symbol}: {sink.Cause.Description}");
|
||||
}
|
||||
```
|
||||
|
||||
### 7.4 TypeScript SDK
|
||||
|
||||
```typescript
|
||||
import { ScannerClient } from '@stellaops/sdk';
|
||||
|
||||
const client = new ScannerClient({ baseUrl, token });
|
||||
|
||||
// Get drift results
|
||||
const drift = await client.getDrift('def456', { language: 'dotnet' });
|
||||
console.log(`Newly reachable: ${drift.newlyReachableCount}`);
|
||||
|
||||
// Explain a sink
|
||||
const explanation = await client.explainReachability('def456', {
|
||||
sinkNodeId: 'MyApp.Services.DbService.ExecuteQuery(string)',
|
||||
includeFullPath: true
|
||||
});
|
||||
|
||||
console.log(explanation.explanation);
|
||||
-d '{"forceRecompute": false}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Webhooks
|
||||
## 8. References
|
||||
|
||||
### 8.1 drift.computed
|
||||
|
||||
Fired when drift analysis completes.
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "drift.computed",
|
||||
"timestamp": "2025-12-22T10:30:00Z",
|
||||
"data": {
|
||||
"driftResultId": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"scanId": "def456",
|
||||
"baseScanId": "abc123",
|
||||
"newlyReachableCount": 3,
|
||||
"newlyUnreachableCount": 1,
|
||||
"hasMaterialDrift": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 8.2 drift.kev_reachable
|
||||
|
||||
Fired when a KEV becomes reachable.
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "drift.kev_reachable",
|
||||
"timestamp": "2025-12-22T10:30:00Z",
|
||||
"severity": "critical",
|
||||
"data": {
|
||||
"driftResultId": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"scanId": "def456",
|
||||
"kevCveId": "CVE-2024-12345",
|
||||
"sinkNodeId": "..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. References
|
||||
|
||||
- **Architecture:** `docs/modules/scanner/reachability-drift.md`
|
||||
- **Operations:** `docs/operations/reachability-drift-guide.md`
|
||||
- **Source:** `src/Scanner/StellaOps.Scanner.WebService/Endpoints/ReachabilityDriftEndpoints.cs`
|
||||
- `docs/modules/scanner/reachability-drift.md`
|
||||
- `docs/operations/reachability-drift-guide.md`
|
||||
- `src/Scanner/StellaOps.Scanner.WebService/Endpoints/ReachabilityDriftEndpoints.cs`
|
||||
|
||||
Reference in New Issue
Block a user