CVE-to-Symbol Mapping

Last updated: 2026-09-28 (Concelier-era sources replaced by the Vulnerabilities hub path, PCD-163). Owner: Scanner Guild with the Vulnerabilities hub.

Audience: Scanner and Vulnerabilities contributors implementing or consuming CVE-to-symbol resolution.

This guide describes how Stella Ops maps CVE identifiers to the specific binary symbols and functions that drive reachability slices — the foundation for deciding whether a vulnerable function is actually called.

History. Until 2026-09-11 the upstream symbols came from Concelier’s AffectedSymbol projection, with Concelier offline bundles (schema v1/v2) and a curated Top-N overlay for air-gapped sites. Concelier is frozen under src/__Obsoleted/Concelier/, and those providers, exporters and formats went with it. The pre-2026-09-28 text of this guide, including those formats, is kept at docs-archive/modules/graph/reachability/guides/cve-symbol-mapping.md.


1. Overview

To determine whether a vulnerability is reachable, Stella Ops resolves:

The mapping is used by SliceExtractor to target the right symbols and by downstream VEX decisions.

Scanner resolves sinks through one ICveSymbolMappingService, a decorator chain whose order is the precedence ladder: explicit store rows (PostgresCveSymbolMappingRepository) win over upstream advisory symbols, which win over patch-diff surfaces. Each bridge joins the chain only when the host registers its source service (src/Scanner/__Libraries/StellaOps.Scanner.Reachability/ServiceCollectionExtensions.cs:46-83).


2. Data Sources

2.1 Patch Diff Surfaces

Highest-fidelity source: compute method-level diffs between vulnerable and fixed versions.

Implementation: StellaOps.Scanner.VulnSurfaces. VulnSurfaceCveSymbolMappingService joins the chain when the host registers the surface read service IVulnSurfaceService (ServiceCollectionExtensions.cs:72-79). Scanner.Worker registers it only when VulnSurfaces:FeedTrigger:Enabled is set (VulnSurfacesServiceCollectionExtensions.cs:94-128, Scanner.Worker/Program.cs:256). The request source is the Vulnerabilities hub’s vuln.facts stream: VulnSurfaceHubFeedConsumer (Scanner.Worker/Processing/VulnSurfaces/) reads it over the hub’s owner API, keeps its checkpoint in Scanner’s own database, and submits one computation per OSV affected fact that declares a fix and starts at a concrete version, in a configured ecosystem (nuget, maven, npm, pypi, gem, composer). The same flag also requires Scanner:Worker:Reachability:Enabled with an absolute HubAdvisoryBaseUrl, because the consumer uses the reachability lane’s hub client. Surfaces are keyed by the purl namespace and name and the range’s introduced version, and the scan-time lookup matches that version exactly.

2.2 Upstream Advisory Symbols (Vulnerabilities hub)

The hub serves upstream symbols at GET /api/vulnerabilities/v1/advisories/{vulnerabilityId}/symbols (src/Vulnerabilities/StellaOps.Vulnerabilities.WebService/Program.cs:451, policy VulnerabilitiesPolicies.AdvisoryRead, scope advisory:read). It decodes the OSV documents behind live facts with OsvAffectedSymbolReader (src/Vulnerabilities/__Libraries/StellaOps.Vulnerabilities.Contracts/Corpus/OsvAffectedSymbolReader.cs:14) and answers AdvisorySymbolsResponse (.../Corpus/AdvisorySymbolsResponse.cs:6).

Scanner.Worker reads it through HubHttpAffectedSymbolProvider, registered as IAdvisoryAffectedSymbolProvider (src/Scanner/StellaOps.Scanner.Worker/Program.cs:844-845) against Scanner:Worker:Reachability:HubAdvisoryBaseUrl. Its presence adds AffectedSymbolCveSymbolMappingService to the chain (ServiceCollectionExtensions.cs:62-70). Bounds, errors and tests: advisory affected symbols.

2.3 Offline: the Compact Vulnerability Database

For air-gapped environments, stella vuln-db export writes a compact offline database that carries the upstream symbols as reachability sinks (ReachabilitySinkProjector over OsvAffectedSymbolReader, src/Cli/StellaOps.Cli/Commands/VulnDb/CompactVulnDbExporter.cs:230). stella sbom check reads it through CompactDbCveSymbolMappingService (src/Cli/StellaOps.Cli/Commands/Sbom/ReachabilityAssessment.cs:53), so offline verdicts use the same mapping contract as the server.

Legal boundary: upstream advisory symbols are the only symbols in the compact database. Stella-DERIVED patch-diff surfaces (vuln_surfaces) use a separate approved-subset export policy/format only for PyPI, RubyGems, NuGet, and Packagist. npm and Maven Central derived surfaces are not supported for kit export by design — they are redistribution-licensing excluded (npm security-data ToS clause; Sonatype Central commercial/scanner-use restriction) and the operator has decided not to pursue the registry permission that was their only path (see §5.3 “Unsupported ecosystems: npm + Maven Central”). Internal-only use of npm/Maven surfaces is unaffected (docs/legal/decisions/patch-diff-surface-airgap-export-counsel-thread.md, reply).


3. Service Contracts

3.1 CVE -> Upstream Symbols (hub)

The live contract is the hub’s AdvisorySymbolsResponse (src/Vulnerabilities/__Libraries/StellaOps.Vulnerabilities.Contracts/Corpus/AdvisorySymbolsResponse.cs:6), consumed in Scanner through IAdvisoryAffectedSymbolProvider (src/Scanner/__Libraries/StellaOps.Scanner.Reachability/Services/AdvisoryAffectedSymbolContracts.cs:10).

3.2 CVE + PURL -> Affected Symbols

src/Scanner/__Libraries/StellaOps.Scanner.VulnSurfaces/Services/IVulnSurfaceService.cs:

public interface IVulnSurfaceService
{
    Task<VulnSurfaceResult> GetAffectedSymbolsAsync(
        string cveId,
        string purl,
        CancellationToken cancellationToken = default);

    Task<bool> HasSurfaceAsync(
        string cveId,
        CancellationToken cancellationToken = default);
}

public sealed record VulnSurfaceResult
{
    public required string CveId { get; init; }
    public required string Purl { get; init; }
    public required ImmutableArray<AffectedSymbol> Symbols { get; init; }
    public required string Source { get; init; } // "surface" | "package-symbols" | "heuristic"
    public required double Confidence { get; init; }
}

public sealed record AffectedSymbol
{
    public required string SymbolId { get; init; }
    public string? MethodKey { get; init; }
    public string? DisplayName { get; init; }
    public string? ChangeType { get; init; }
    public double Confidence { get; init; }
}

4. Availability

An unavailable hub leaves upstream symbols unavailable, and reachability stays unknown for those CVEs; it does not fall back to a stale cache (advisory affected symbols). The compact database is a file the operator refreshes by re-export.


5. Offline and Export Formats

5.1 Compact vulnerability database

The current offline carrier for upstream symbols (§2.3). Export: stella vuln-db export.

5.2 Concelier offline bundle schema v1/v2 (withdrawn)

Withdrawn with Concelier; no live code reads or writes it. The format description is kept in the archived guide §5.1-§5.2.

5.3 Derived Patch-Diff Surface Export v1 (approved subset)

Produced by StellaOps.Scanner.VulnSurfaces.Export from computed VulnSurface rows only after the 2026-06-12 counsel gates pass. This is a separate artifact from schema v2 advisory-symbol bundles: it does not write derived rows into the curated/upstream symbol store. The approved-subset loader verifies canonical signed bytes, trusted row-level DSSE signatures, row payload digests, registry/provenance policy, and the approved ecosystem boundary before exposing accepted rows as a read-only IVulnSurfaceService for the existing mapping read path.

{
  "schemaVersion": "stellaops.vuln-surface-export/v1",
  "rows": [
    {
      "cveId": "CVE-2026-0004",
      "advisoryId": "GHSA-demo",
      "ecosystem": "pypi",
      "registry": "pypi.org",
      "exactDownloadEndpoint": "https://files.pythonhosted.org/packages/demo-1.0.0.tar.gz",
      "purl": "pkg:pypi/demo@1.0.0",
      "packageName": "demo",
      "vulnerableVersion": "1.0.0",
      "fixedVersion": "1.0.1",
      "packageLicenseExpression": "MIT",
      "licenseClassification": "oss",
      "tosSnapshotId": "tos-2026-06-12",
      "tosSnapshotDate": "2026-06-12",
      "userAgent": "StellaOps/1.0 security-surface-export contact=security@example.invalid",
      "contact": "security@example.invalid",
      "rateLimitPolicy": "official-api-backoff",
      "retryAfterPolicy": "honor-retry-after",
      "auditLogReferences": ["audit:surface:20260612"],
      "computationDigest": "sha256:...",
      "sinks": [
        {
          "methodKey": "demo::Parser.parse(value)",
          "declaringType": "Parser",
          "methodName": "parse",
          "canonicalSignature": "parse(value)",
          "changeType": "modified",
          "vulnHash": "sha256:...",
          "fixedHash": "sha256:...",
          "parameterCount": 1,
          "returnType": "string"
        }
      ]
    }
  ]
}

Policy and format rules:

NOTICE OBLIGATION ACTIONED (OK-11, 2026-08-25). Counsel’s three §5 blocks — the derived-surface statement, BUSL/EULA "Third-Party Data" carve-out, and non-affiliation sentence — now appear verbatim in the customer-reaching third-party-licenses/DATA-NOTICES.md. tools/scripts/validate/check_patch_diff_data_notice.py extracts those blocks from the canonical counsel reply and fails if the root notice, release-bundle copy, offline-kit staging call, release-bundle staging call, manifest checksum surface, or either wholesale runtime-assets copy drifts. Its --self-test removes each carrier/gate link in turn and proves the detector goes red. The offline-kit builder and release-bundle stager also validate their staged notice before producing a customer artifact. No packaging pipeline emits a derived-surface bundle yet; these checks are the mandatory first-ship precondition, not evidence of a live distribution.

Unsupported ecosystems: npm + Maven Central (not supported by design)

npm and Maven Central derived patch-diff surfaces are NOT exported into offline kits, and this is a permanent product decision — not a temporary block. The supported export set is exactly the four counsel-approved ecosystems above: PyPI, RubyGems, NuGet, Packagist. Everything else fail-closes.

Why the two are excluded:

If the operator ever reverses this decision and obtains the npm/GitHub confirmation and/or Sonatype arrangement, re-enabling an ecosystem is a single, auditable change: record the written permission under docs/legal/decisions/, then add the ecosystem to VulnSurfaceExportPolicy.ApprovedEcosystems and remove its dedicated block branch. Until that recorded permission exists, npm and Maven Central stay unsupported.

5.4 Curated Top-N bundle v1 (withdrawn)

The curated overlay (CuratedAffectedSymbolProvider, LadderAffectedSymbolProvider) lived in StellaOps.Concelier.Core/Signals and was withdrawn with Concelier. The curated rung of the ladder is now the explicit store (PostgresCveSymbolMappingRepository, §1). The format is kept in the archived guide §5.4.


6. Fallback Behavior

When no surface or advisory mapping is available, the surface service returns an empty symbol list with low confidence and Source = "heuristic". Callers may inject an IPackageSymbolProvider (src/Scanner/__Libraries/StellaOps.Scanner.VulnSurfaces/Services/IPackageSymbolProvider.cs) to supply public-symbol fallbacks.



Created: 2025-12-22. See Sprint 3810 for implementation details.