stitch

package
v0.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 2, 2026 License: GPL-2.0, GPL-2.0-only Imports: 6 Imported by: 0

Documentation

Overview

Package stitch produces the merged findings + callgraph that `crypto-finder scan --scan-dependencies --export-callgraph` would emit, from inputs that have already been computed per-component.

The intended caller is a service that has previously scanned each component independently (no --scan-dependencies) and stored the resulting findings.json + callgraph.json. When asked "what crypto is reachable from artifact X?", the caller fetches X's outputs and X's transitive deps' outputs from its storage, hands them to Merge, and receives bytes byte-equivalent to what crypto-finder would have produced in one --scan-dependencies run.

Why this lives in crypto-finder (not the calling service):

  • The InterimReport / call-graph-export schemas are crypto-finder's public contract. Operations on that contract — finding_id hashing, dependency_info stamping, schema-5.x's "self-contained chains" concat rule — belong with the contract owner. A reimplementation in a downstream service would drift the moment the schema bumps.

  • The pure stitch is reusable: catalog services, CI plugins, IDE extensions can all import this package to assemble cross-component reachability without re-running crypto-finder.

What this package does NOT do:

  • It does not read from any storage. Inputs are []byte; the caller is responsible for fetching them (e.g., from a DB).
  • It does not run any scanner, resolver, or callgraph builder. The dependency tree must be resolved by the caller before invocation.
  • It does not gzip. Inputs and outputs are plain JSON; storage compression is a caller concern.

Correctness properties Merge depends on (validated empirically against `crypto-finder scan --scan-dependencies` output on real Maven artifacts):

  1. Schema 5.x callgraph exports contain no top-level functions[] / edges[]. The graph is materialized as finding_graphs (one per cryptographic asset) with self-contained call_chains. Zero chains span multiple components — every chain lives entirely in one module, tagged by dependency_info on every frame.

  2. findings.json is a flat union: target findings (source=direct) interleaved with dep findings (source=dependency + dependency_info). finding_id is sha256(path:start_line:first_rule_id)[:8], with path prefixed by "module@version/" when the asset belongs to a dep.

Both properties make Merge a structural concat + decorate pass — no type resolution, no edge synthesis, no scanner invocation.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Dep

type Dep struct {
	Module    string
	Version   string
	Findings  []byte
	Callgraph []byte
}

Dep is one transitive dependency's contribution to the merge.

Module is the crypto-finder-internal module identifier — for Maven this is "<group>:<artifact>" (e.g. "commons-codec:commons-codec"); for other ecosystems whatever crypto-finder's dependency scanner would have emitted. The exact string is propagated into every CryptographicAsset.DependencyInfo.Module and every chain frame's dependency_info.module in the merged output, so the caller controls what downstream consumers see.

Findings is the raw findings.json bytes (uncompressed) produced by running `crypto-finder scan` against this dep's source, without --scan-dependencies. Required.

Callgraph is the raw callgraph.json bytes produced by running `crypto-finder scan --export-callgraph` against this dep's source. Optional: a nil/empty Callgraph means the dep contributes findings only and its frames are absent from the merged finding_graphs. Callers that require complete reachability should refuse to call Merge when any dep is missing a callgraph rather than silently producing a thin graph.

type Policy

type Policy struct {
	// PruneToRootModule truncates every chain at the first frame whose
	// function name is in the source fragment's root module. The fragment
	// supplies its root via scan_metadata.root_module; fragments that
	// omit it are passed through unchanged. After truncation, chains that
	// are now byte-equal to a sibling are deduplicated.
	PruneToRootModule bool

	// MaxChainsPerFinding caps the number of chains kept for any one
	// finding_graph. 0 disables the cap. crypto-finder's exporter uses
	// 128; mirror that for output-shape parity.
	MaxChainsPerFinding int

	// RebuildEntryPointIndex regenerates entry_point_index from the
	// pruned + deduped chains, replacing whatever the source fragments
	// supplied. This is required to match crypto-finder's L2 baseline
	// shape, where entry_point_index is always a derivative of the
	// final finding_graphs and never carries entries for frames that
	// were pruned away.
	RebuildEntryPointIndex bool
}

Policy controls how Merge prunes the merged callgraph. The zero value is "passthrough" — every chain from every fragment is copied verbatim, matching what a naive concat would produce.

Direction-B callers should set PruneToRootModule=true so the merged callgraph mirrors what `crypto-finder scan --scan-dependencies` would emit, where the internal Tracer is constrained by userPackages={root_module} and walks back only until it reaches the component's package boundary. Without that prune, L1's standalone fragments (built with userPackages=nil, walking to graph roots) carry chains many times longer and more numerous than L2 keeps.

type Result

type Result struct {
	Findings  []byte
	Callgraph []byte
	Summary   Summary
}

Result is what Merge returns.

Findings and Callgraph are raw JSON bytes ready to write to disk, gzip, or push to an HTTP response. The callgraph carries the same schema_version as the highest-version input fragment; Merge does not transcode between schema versions, so all inputs must share a schema.

func Merge

func Merge(targetFindings, targetCallgraph []byte, deps []Dep) (*Result, error)

Merge produces the cross-component findings + callgraph from one target and zero or more dep contributions, applying the default policy (passthrough — no pruning). Use MergeWithPolicy to control pruning.

func MergeWithPolicy

func MergeWithPolicy(targetFindings, targetCallgraph []byte, deps []Dep, policy Policy) (*Result, error)

MergeWithPolicy is the policy-aware form of Merge. See Policy for the supported knobs.

targetFindings is required; the merge fails if it's nil or empty. targetCallgraph is optional — Merge still produces a Callgraph result when only deps contributed graphs, but the typical caller will invariant-check that the target has its own graph before invoking.

Ordering: deps are processed in slice order. The merged finding_graphs and entry_point_index preserve that order with the target's contribution first, then each dep's contribution in the order received. Callers wanting byte-stable output should sort deps before calling.

type Summary

type Summary struct {
	SchemaVersion         string
	FindingCount          int
	EntryPointCount       int
	ReachableFindingCount int
}

Summary holds the few counts a typical caller wants to denormalize next to the stored callgraph for fast listings (matching the columns crypto-mining-service writes to reachability_paths today).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL