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):
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.
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 ¶
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 ¶
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 ¶
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).