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.latticeState,uncertaintyTier,graphHash,riskScore,confidence,hasRuntimeEvidence,pathLength,hasExecutionEvidence, andexecutionEvidenceDigest.
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.
