Call-Paths And Build-Stage SBOM Inputs
This contract defines the Scanner inputs used when a runtime image is too slim to produce useful SBOM or call-path evidence by itself.
Metadata Contract
Scanner Worker reads these metadata keys before dependency reachability runs:
| Key | Purpose |
|---|---|
scanner.source.path | Explicit source checkout/root for call-graph extraction. |
scanner.build_stage.rootfs.path | Materialized build-stage filesystem root that still has manifests, lockfiles, project files, or installed dependency trees. |
scanner.attached_sbom.path | CI-attached SBOM file to copy into the scan workspace instead of generating a build-stage SBOM. |
scanner.attached_sbom.format | Attached SBOM format. Values containing spdx map to SPDX; all other values map to CycloneDX. |
scanner.rootfs.path | Runtime filesystem root. Used to detect the empty static-runtime case. |
The stage publishes these downstream metadata keys:
| Key | Purpose |
|---|---|
sbom.path / sbom.format | Canonical SBOM location consumed by dependency reachability. |
reachability.callgraph.path | richgraph-v1 call graph materialized from the source/build-stage call graph snapshot. |
reachability.witness.path | Deterministic entrypoint-to-sink witness JSON. |
reachability.witness.digest | SHA-256 digest of the witness JSON artifact. |
reachability.input.source | attached-sbom or build-stage. |
scanner.runtime.sbom.status | empty-runtime-image when the runtime root lacks supported manifests/installed npm tree and build-stage SBOM was used. |
scanner.runtime.sbom.empty_reason | Human-readable reason for the empty runtime SBOM fallback. |
Supported Inputs
- Attached SBOM: Scanner copies the supplied SBOM into the scan workspace and does not regenerate it from the build-stage root.
- JS/TS build-stage root: Scanner reads
package.jsonpluspackage-lock.jsonor concretedependenciesentries and emits a CycloneDX 1.5 SBOM with npm purls. It also runs the in-process JavaScript source call-graph extractor. - .NET source/build-stage root: Scanner detects
.slnor.csprojroots and dispatches the existing Roslyn call-graph extractor. NuGet build-stage SBOM generation readsPackageReferenceentries from the first project file. The deterministic offline acceptance fixture is a local.slnsnapshot with no package restore or external feed dependency.
Witness Shape
reachability.witness.path points to a deterministic JSON document with:
schema=stellaops.scanner.reachability-witness@v1,scanId,graphDigest,resultDigest,richGraphHash, andlanguage.releaseEvidence.component, aligned to the Release Evidence component shape:componentId,componentName,digest,tag,semVer,sourceRef, andsbomDigestwhen Scanner metadata can supply them. If no release component ID is provided, Scanner derives a deterministic local ID from the component identity and scan ID.- Top-level and per-path
policyGateEvidenceshaped for PolicyGateEvidence consumers:subject.scanId, optionalsubject.purl,subject.symbolId,subject.artifactDigest, andevidence.graphHash,hasRuntimeEvidence,hasExecutionEvidence, andexecutionEvidenceDigest. A concrete static path addslatticeState=SRandpathLength.confidenceis present only when every adjacent node pair identifies exactly one graph edge with a finite confidence in[0,1]; its value is the arithmetic mean of those edge confidences. No path, missing confidence, or an ambiguous parallel edge leaves those derived fields absent. Scanner does not emitriskScoreoruncertaintyTierfrom this stage because it has not run the owning risk or uncertainty computation; in particular, no-path evidence is not relabelled asSUor given a synthetic zero.
This shape is policy-gate-consumable Scanner evidence. Sprint 019 (SPRINT_20260526_019_Scanner_reachability_live_forcing_function) wires the cross-module path live: the Scanner Worker’s ReachabilityPublishStageExecutor now (opt-in) posts the union graph to the ReachGraph WebService and uploads a canonical DependencyReachabilityReport JSON as a release-evidence packet, which the Release Orchestrator’s ReachabilityGateEvaluator reads back at promotion time. See:
- docs/modules/release-orchestrator/gates/reachability-gate.md
- docs/modules/reach-graph/guides/policy-gate-integration.md
Coverage Limits
- Runtime-only static images such as nginx SPA images cannot provide full package or source call-path evidence when manifests and source maps are absent. Scanner records that state and uses build-stage or attached SBOM evidence.
- .NET extraction currently covers deterministic source/build-stage solution and project inputs that Roslyn can load offline. It detects method declarations, direct/virtual/delegate invocations, ASP.NET-style method attributes,
Main, hosted-service entrypoints, gRPC service methods, and registered .NET sinks. Sprint 018 RCH-018-001 adds two framework-registration shapes:- Minimal API
Map*calls (MapGet,MapPost,MapPut,MapDelete,MapPatch,MapMethods,MapFallback) whose handler argument is a method-group reference promote the referenced method to anHttpHandlerentrypoint. services.AddHostedService<T>()promotesT.StartAsyncandT.ExecuteAsynctoBackgroundJobentrypoints when the type argument is a concreteINamedTypeSymbol.
- Minimal API
- JS/TS call-path extraction currently covers deterministic source/build-stage patterns: named functions, named Express/Fastify-style route handlers, simple
require/importaliases, direct calls, and known sink calls such aschild_process.exec. Sprint 018 RCH-018-002 adds two further shapes:- Module Federation
exposes: { './X': './x.js' }blocks inwebpack.config.js/vite.config.js/module-federation.config.js/rspack.config.jspromote the exported top-level functions of the referenced file toHttpHandlerentrypoints (remote-callable). *.bundle.js(or.mjs/.cjs) with a sibling*.mapsource map emits abundled-source:<original>node persources[]entry. This is a provenance node only – the extractor does NOT synthesize edges from bundled artifacts.
- Module Federation
- Minified bundles WITHOUT a sibling source map remain a documented gap: the extractor adds zero nodes for them, and any reachability claim must come from a different signal (build-stage source, attached SBOM, or runtime evidence).
- Reflection-heavy DI registration (
Activator.CreateInstance,Type.GetType, genericAddTransient(typeof(IFoo), runtimeType)), reflection-like JavaScript metaprogramming, generated route tables, and.NETminimal-API generated route builders that pass anonymous lambdas instead of method-group references are follow-up enrichment areas. They must not be reported as proven reachable unless a deterministic extractor or runtime signal produced the path. - Python source extraction (Sprint 020 TIER-B-001) detects top-level
defandasync defplus Flask / FastAPI / Django REST decorator routes (@app.route,@app.get,@router.post,@api_view,@click.command,@app.taskCelery, etc.). Two further shapes from Sprint 020:asyncio.run(<funcName>)andasyncio.run(<funcName>())at module scope promote the referenced top-level function to aBackgroundJobentrypoint. Only bareword identifier arguments are captured; lambdas, attribute access, andgetattr(...)are skipped.- Reflection / dynamic-dispatch shapes (
getattr(module, name)(),globals()[name](),importlibindirection) are an explicit gap: the referenced function is NOT auto-promoted.
- Go source extraction (Sprint 020 TIER-B-002) is the static fallback used when the
stella-callgraph-goSSA tool is not present (CI lane). Three new supported shapes from Sprint 020:- HTTP framework registration:
http.HandleFunc("/x", Handler),mux.HandleFunc(...),engine.GET/POST/PUT/DELETE/PATCH("/x", H)with an identifier handler argument promotes the referenced function to anHttpHandlerentrypoint. init()functions are promoted toScheduledJobentrypoints (Go runsinit()at package load beforemain). Previously these were silently dropped.- Goroutine spawns
go FuncName(...)are materialised asDelegate-kind edges from a synthesised<package>.<goroutine-spawn>pseudo-node to the spawned function. The pseudo-node is NOT an entrypoint – spawning a goroutine never proves user-facing reachability; the edge just preserves transitive flow. Documented gaps: method-value handler arguments (router.GET("/x", h.Handle)), inline closure handlers, method-call goroutines (go obj.Method(...)), and reflection dispatch (reflect.ValueOf(fn).Call(...)) are all skipped.
- HTTP framework registration:
- Java bytecode extraction (Sprint 020 TIER-B-003) covers Spring (
@RestController,@GetMapping/@PostMapping/etc.,@Scheduled,@KafkaListener,@RabbitListener,@JmsListener,@EventListener,@QueryMapping/@MutationMapping,@GrpcService), JAX-RS (@Path,@GET/@POST/etc., bothjavaxandjakarta), Micronaut (@Controller,@Get/@Post/etc.,@Scheduled), GraphQL annotations,static main([Ljava/lang/String;)V, classes extendingHttpServlet(doGet/doPost/etc.). New from Sprint 020:- JSR-250 lifecycle hooks
@PostConstructand@PreDestroy(bothjavax.annotationandjakarta.annotation) are promoted toBackgroundJobentrypoints. The container auto-fires these at bean init / shutdown. Documented gap: CDI@Injectsetters / constructors are NOT auto-promoted – DI callsites are not user-facing entrypoints and the classifier returns null for@Injectmethods.
- JSR-250 lifecycle hooks
- This Scanner path emits local witness and graph artifacts. Release Orchestrator policy-gate consumption remains a separate integration boundary.
