Entry-Point Static Analysis

This guide captures the static half of the Stella Ops entry-point detection pipeline: how the scanner turns image metadata and filesystem contents into a resolved binary, an explainable execution chain, and a confidence score — without executing customer code. Audience: engineers working on or integrating with the EntryTrace static reducer. For the running-container path, see entrypoint-dynamic-analysis.md; for the full subsystem map, see the documentation index.

0) Implementation snapshot — Sprint 130.A (2025-11-02)

The StellaOps.Scanner.EntryTrace stack (analyzer + worker + surfaces) currently provides:

Open follow-ups tracked for this wave:

Sections §4–§7 below capture the long-term reduction design; features not yet implemented are explicitly noted in the task board.

Probing the analyzer today

  1. Load the image config
    using var stream = File.OpenRead("config.json");
    var config = OciImageConfigLoader.Load(stream);
    
  2. Create a layered filesystem from extracted layer directories or tar archives:
    var fs = LayeredRootFileSystem.FromArchives(layers);
    
  3. Build the image context (normalises env, PATH, user, working dir):
    var imageCtx = EntryTraceImageContextFactory.Create(
        config, fs, new EntryTraceAnalyzerOptions(), imageDigest, scanId);
    
  4. Resolve the entry trace:
    var analyzer = serviceProvider.GetRequiredService<IEntryTraceAnalyzer>();
    var graph = await analyzer.ResolveAsync(imageCtx.Entrypoint, imageCtx.Context, cancellationToken);
    
  5. Inspect resultsgraph.Terminals lists classified candidates (path, runtime, confidence, evidence), graph.Nodes/Edges capture the explainable chain, and graph.Diagnostics highlight unresolved steps. Emit metrics/telemetry via EntryTraceMetrics.
  6. Serialize if needed – pass the graph through EntryTraceNdjsonWriter.Serialize to obtain deterministic NDJSON lines; the helper already computes capability summaries.

For ad-hoc investigation, snapshotting EntryTraceResult keeps graph and NDJSON aligned. Avoid ad-hoc JSON writers to maintain ordering guarantees.

Probing through Scanner.Worker

EntryTrace runs automatically inside the worker when these metadata keys exist on the lease:

KeyPurpose
ScanMetadataKeys.ImageConfigPath (default scanner.analyzers.entrytrace.configMetadataKey)Absolute path to the OCI config.json.
ScanMetadataKeys.LayerDirectories or ScanMetadataKeys.LayerArchivesSemicolon-delimited list of extracted layer folders or tar archives.
ScanMetadataKeys.RuntimeProcRoot (optional)Path to a captured /proc tree for runtime reconciliation (air-gapped runs can mount a snapshot).

Worker output lands in context.Analysis (EntryTraceGraph, EntryTraceNdjson) and is persisted via IEntryTraceResultStore. Ensure Surface Validation prerequisites pass before dispatching the analyzer.

Probing via WebService & CLI

Both surfaces consume the persisted result; rerunning the worker updates the stored document atomically.

NDJSON reference

EntryTraceNdjsonWriter.Serialize emits newline-delimited JSON in the following order so AOC consumers can stream without buffering:

Every line ends with a newline and is emitted in deterministic order (IDs ascending, keys lexicographically sorted) so downstream tooling can hash or diff outputs reproducibly.

1) Loading OCI images

1.1 Supported inputs

1.2 Normalised model

sealed class OciImage {
  public required string Os;
  public required string Arch;
  public required string[] Entrypoint;
  public required string[] Cmd;
  public required string[] Shell;      // Windows / powershell overrides
  public required string WorkingDir;
  public required string[] Env;
  public required string[] ExposedPorts;
  public required LabelMap Labels;
  public required LayerRef[] Layers;   // ordered, compressed blobs
}

Compose the runtime argv as Entrypoint ++ Cmd, honouring shell-form vs exec-form (see §2.3).

2) Overlay virtual filesystem

2.1 Whiteouts

2.2 Lazy extraction

2.3 Shell-form composition

3) Low-level primitives

3.1 PATH resolution

3.2 Shebang handling

3.3 Binary probes

4) Wrapper catalogue

Collapse known wrappers before analysing the target command so the terminal reflects the real runtime binary. Sprint 130.A ships the extended catalogue from SCANNER-ENTRYTRACE-18-508, covering init/user-switch/environment/supervisor wrappers as well as package and language launchers such as bundle exec, docker-php-entrypoint, npm exec, yarn node, pipenv run, and poetry run.

Rules:

5) ShellFlow integration

When the resolved command is a shell script, invoke the ShellFlow analyser to locate the eventual exec target. Key capabilities:

The analyser returns one or more candidate commands along with reasons, which feed into the reduction engine.

6) Reduction algorithm

  1. Compose argv ENTRYPOINT ++ CMD.
  2. Collapse wrappers; append ReductionEdge entries documenting each step.
  3. Resolve argv0 to an absolute file and classify (ELF/PE/script).
  4. If script → run ShellFlow; replace current command with highest-confidence exec target while preserving alternates as evidence.
  5. Attempt to resolve application artefacts for VM hosts (JARs, DLLs, JS entry, Python module, etc.).
  6. Emit EntryTraceResult with candidate terminals ranked by confidence.

7) Confidence scoring

Use a simple logistic model with feature contributions captured for the evidence trail. Example features:

IdSignalWeight
f1Entrypoint already an executable (ELF/PE)+0.18
f2Observed chain ends in non-wrapper binary+0.22
f3VM host + resolvable artefact+0.20
f4Exposed ports align with runtime+0.06
f5Shebang interpreter matches runtime family+0.05
f6Language artefact validation succeeded+0.15
f8Multi-branch script unresolved ($@ taint)−0.20
f9Target missing execute bit−0.25
f10Shell with no exec−0.18

Persist per-feature evidence strings so UI/CLI users can see why the scanner picked a given entry point.

8) Outputs

Return a populated EntryTraceResult:

Static and dynamic reducers share this shape, enabling downstream modules to remain agnostic of the detection mode.

9) ProcGraph replay (runtime parity)

Static resolution must be reconciled with live observations when a workload is running under the Stella Ops runtime agent:

  1. Read /proc/1/{cmdline,exe} and walk descendants via /proc/*/stat to construct the initial exec chain (ascending PID order).
  2. Collapse known wrappers (tini, dumb-init, gosu, su-exec, s6-supervise, runsv, supervisord) and privilege switches, mirroring the static wrapper catalogue.
  3. Materialise a ProcGraph object that records each transition and the resolved executable path (via /proc/<pid>/exe symlinks).
  4. Compare ProcGraph.Terminal with EntryTraceResult.Terminals[0], emitting confidence=high when they match and downgrade when divergence occurs.
  5. Persist the merged view so the CLI/UI can highlight static vs runtime discrepancies and feed drift detection in Zastava.

This replay is optional offline, but required when runtime evidence is available so policy decisions can lean on High-confidence matches.

10) Service & CLI surfaces