- Implemented PathViewerComponent for visualizing reachability call paths. - Added RiskDriftCardComponent to display reachability drift results. - Created corresponding HTML templates and SCSS styles for both components. - Introduced test fixtures for reachability analysis in JSON format. - Enhanced user interaction with collapsible and expandable features in PathViewer. - Included risk trend visualization and summary metrics in RiskDriftCard.
7.6 KiB
Vuln Surface Contract v1
Sprint: SPRINT_3700_0002_0001
Task: SURF-024
Schema: stella.ops/vulnSurface@v1
Overview
A Vulnerability Surface represents the specific methods that changed between a vulnerable and fixed version of a package. This enables precise reachability analysis by identifying the exact "trigger" methods that are dangerous rather than treating the entire package as vulnerable.
Use Cases
- Noise Reduction - Only flag findings where code actually calls vulnerable methods
- Confidence Tiers - "Confirmed reachable" (calls trigger) vs "Potentially reachable" (uses package)
- Remediation Guidance - Show developers exactly which API calls to avoid
- VEX Precision - Automatically generate VEX "not_affected" for unreachable triggers
Data Model
VulnSurface
Root object representing a computed vulnerability surface.
| Field | Type | Required | Description |
|---|---|---|---|
surface_id |
integer | Yes | Database ID |
cve_id |
string | Yes | CVE identifier (e.g., "CVE-2024-12345") |
package_id |
string | Yes | Package identifier in PURL format |
ecosystem |
string | Yes | Package ecosystem: nuget, npm, maven, pypi |
vuln_version |
string | Yes | Vulnerable version analyzed |
fixed_version |
string | Yes | First fixed version used for diff |
sinks |
VulnSurfaceSink[] | Yes | Changed methods (vulnerability triggers) |
trigger_count |
integer | Yes | Number of callers to sink methods |
status |
VulnSurfaceStatus | Yes | Computation status |
confidence |
number | Yes | Confidence score (0.0-1.0) |
computed_at |
string | Yes | ISO 8601 timestamp |
VulnSurfaceSink
A method that changed between vulnerable and fixed versions.
| Field | Type | Required | Description |
|---|---|---|---|
sink_id |
integer | Yes | Database ID |
method_key |
string | Yes | Fully qualified method signature |
method_name |
string | Yes | Simple method name |
declaring_type |
string | Yes | Containing class/module |
namespace |
string | No | Namespace/package |
change_type |
MethodChangeType | Yes | How the method changed |
is_public |
boolean | Yes | Whether method is publicly accessible |
parameter_count |
integer | No | Number of parameters |
return_type |
string | No | Return type |
source_file |
string | No | Source file (from debug symbols) |
start_line |
integer | No | Starting line number |
end_line |
integer | No | Ending line number |
VulnSurfaceTrigger
A call site that invokes a vulnerable sink method.
| Field | Type | Required | Description |
|---|---|---|---|
trigger_id |
integer | Yes | Database ID |
sink_id |
integer | Yes | Reference to sink |
scan_id |
UUID | Yes | Scan where trigger was found |
caller_node_id |
string | Yes | Call graph node ID |
caller_method_key |
string | Yes | FQN of calling method |
caller_file |
string | No | Source file of caller |
caller_line |
integer | No | Line number of call |
reachability_bucket |
string | Yes | Reachability classification |
path_length |
integer | No | Shortest path from entrypoint |
confidence |
number | Yes | Confidence score (0.0-1.0) |
call_type |
string | Yes | Call type: direct, virtual, interface, reflection |
is_conditional |
boolean | Yes | Whether call is behind a condition |
Enums
VulnSurfaceStatus
| Value | Description |
|---|---|
pending |
Surface computation queued |
computing |
Currently being computed |
computed |
Successfully computed |
failed |
Computation failed |
stale |
Needs recomputation (new version available) |
MethodChangeType
| Value | Description |
|---|---|
added |
Method added in fix (not in vulnerable version) |
removed |
Method removed in fix (was in vulnerable version) |
modified |
Method body changed between versions |
unknown |
Change type could not be determined |
Reachability Buckets
| Bucket | Description | Risk Level |
|---|---|---|
entrypoint |
Sink is directly exposed as entrypoint | Critical |
direct |
Reachable from entrypoint with no authentication gates | High |
runtime |
Reachable but behind runtime conditions/auth | Medium |
unknown |
Reachability could not be determined | Medium |
unreachable |
No path from any entrypoint | Low |
Fingerprinting Methods
cecil-il (NuGet/.NET)
Uses Mono.Cecil to compute SHA-256 hash of IL instruction sequence:
IL_0000: ldarg.0
IL_0001: call System.Object::.ctor()
IL_0006: ret
Normalized to remove:
- NOP instructions
- Debug sequence points
- Local variable indices (replaced with placeholders)
babel-ast (npm/Node.js)
Uses Babel to parse JavaScript/TypeScript and compute hash of normalized AST:
function vulnerable(input) {
eval(input); // dangerous!
}
Normalized to remove:
- Comments
- Whitespace
- Variable names (renamed to positional)
asm-bytecode (Maven/Java)
Uses ASM to compute hash of Java bytecode:
ALOAD 0
INVOKESPECIAL java/lang/Object.<init>()V
RETURN
Normalized to remove:
- Line number tables
- Local variable tables
- Stack map frames
python-ast (PyPI)
Uses Python's ast module to compute hash of normalized AST:
def vulnerable(user_input):
exec(user_input) # dangerous!
Normalized to remove:
- Docstrings
- Comments
- Variable names
Database Schema
-- Surfaces table
CREATE TABLE scanner.vuln_surfaces (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL,
cve_id TEXT NOT NULL,
package_ecosystem TEXT NOT NULL,
package_name TEXT NOT NULL,
vuln_version TEXT NOT NULL,
fixed_version TEXT,
fingerprint_method TEXT NOT NULL,
total_methods_vuln INTEGER,
total_methods_fixed INTEGER,
changed_method_count INTEGER,
computed_at TIMESTAMPTZ DEFAULT now(),
UNIQUE (tenant_id, cve_id, package_ecosystem, package_name, vuln_version)
);
-- Sinks table
CREATE TABLE scanner.vuln_surface_sinks (
id UUID PRIMARY KEY,
surface_id UUID REFERENCES scanner.vuln_surfaces(id) ON DELETE CASCADE,
method_key TEXT NOT NULL,
method_name TEXT NOT NULL,
declaring_type TEXT NOT NULL,
change_type TEXT NOT NULL,
UNIQUE (surface_id, method_key)
);
-- Triggers table
CREATE TABLE scanner.vuln_surface_triggers (
id UUID PRIMARY KEY,
sink_id UUID REFERENCES scanner.vuln_surface_sinks(id) ON DELETE CASCADE,
scan_id UUID NOT NULL,
caller_node_id TEXT NOT NULL,
reachability_bucket TEXT NOT NULL,
confidence REAL NOT NULL,
UNIQUE (sink_id, scan_id, caller_node_id)
);
API Endpoints
POST /api/v1/surfaces/compute
Request surface computation for a CVE + package.
Request:
{
"cveId": "CVE-2024-12345",
"ecosystem": "nuget",
"packageName": "Newtonsoft.Json",
"vulnVersion": "13.0.1",
"fixedVersion": "13.0.2"
}
Response:
{
"surfaceId": "uuid",
"status": "pending"
}
GET /api/v1/surfaces/{surfaceId}
Get computed surface with sinks.
GET /api/v1/surfaces/{surfaceId}/triggers?scanId={scanId}
Get triggers for a surface in a specific scan.
Integration Points
- Concelier - Feeds CVE + affected version ranges
- Scanner - Computes surfaces during SBOM analysis
- Call Graph - Provides reachability analysis
- VEX Lens - Uses surfaces for automated VEX decisions
- UI - Displays surface details and trigger paths