pkg/triage¶
The package has two layers:
- the current observation and identity APIs, used for new execution paths;
- compatibility APIs such as
CrashType,HashStack, and the locked oracle, whose values remain stable for historical evidence.
The package surfaces evidence for human triage. It does not confirm vulnerabilities or automatically determine severity, affected versions, or disclosure readiness.
Observation types¶
ExecKind¶
| Constant | Value | Meaning |
|---|---|---|
KindClean | clean | target ran, exited zero, no supported fatal evidence |
KindDiagnostic | diagnostic | target survived after diagnostic, leak, or non-fatal evidence |
KindCrash | crash | process death plus evidence naming the fault |
KindSuspect | suspect | process death without supported evidence naming the fault |
KindAmbiguous | ambiguous | target ran and exited non-zero, but the cause is not established |
KindIncomplete | incomplete | timeout, loader failure, failure to start, or missing execution |
KindInterceptedFault | intercepted-fault | a declared harness intercepted a real target fault and survived |
KindUnknown | unknown | no admissible process outcome is available |
ArtifactAdmission¶
ArtifactKind describes structure: live execution, single-process log, campaign log, differential excerpt, multi-process artifact, aggregate rows, transcript, or unclassified. AdmissionStatus is an explicit adjudication: admitted, excluded, or unadjudicated.
The zero value is unadjudicated and therefore fails closed.
func AdmitLiveExecution() ArtifactAdmission
func BankedLog(kind ArtifactKind, status AdmissionStatus, reason string) ArtifactAdmission
func (a ArtifactAdmission) Admitted() bool
BankedLog does not inspect bytes or infer admission from kind.
TextEvidence¶
type TextEvidence struct {
SanitizerVerdict bool
SanitizerClass string
SanitizerDiagnostics int
LeakVerdict bool
AllowlistedAssertion bool
AssertionForm string
LoaderDiagnostic bool
ResourceExhaustion bool
ResourceKind string
}
This type says what output contains. It never establishes whether a process started, died, timed out, or survived.
The extractor distinguishes fatal sanitizer verdicts, recover-mode diagnostics, leaks, anchored assertion forms, loader diagnostics, and resource exhaustion. Generic prose matches are deliberately insufficient.
ProcessFacts¶
type ProcessFacts struct {
Known bool
CommandStarted bool
TargetRan bool
LoaderFailure bool
TimedOut bool
HasExitCode bool
ExitCode int
Signal syscall.Signal
InterceptedFault bool
}
InterceptedFault is a harness capability, not a text inference. Production replay obtains it from a manifest bound to the harness binary hash; rebuilding the binary invalidates a stale declaration.
Observation¶
type Observation struct {
Admission ArtifactAdmission
Text TextEvidence
Process ProcessFacts
Kind ExecKind
Reason string
}
func Observe(a ArtifactAdmission, t TextEvidence, p ProcessFacts) Observation
func (o Observation) NeedsHumanTriage() bool
Observe is the authoritative combination rule. NeedsHumanTriage() is true for crash, suspect, and intercepted fault. Its name is contractual: it does not mean reportable or confirmed.
Replay APIs¶
func ObserveReplay(
admission ArtifactAdmission,
harness string,
input []byte,
timeout time.Duration,
extraEnv []string,
) (Observation, string, error)
func ObserveReplayDeclared(
admission ArtifactAdmission,
caps HarnessCapabilities,
harness string,
input []byte,
timeout time.Duration,
extraEnv []string,
) (Observation, string, error)
func ObserveReplayWithManifest(
admission ArtifactAdmission,
harness string,
input []byte,
timeout time.Duration,
extraEnv []string,
) (Observation, string, string, error)
These functions execute the target and collect process facts. The manifest-aware path validates any intercepted-fault capability against the binary.
Execute and ExecutionOutcome remain compatibility wrappers for paths that have not moved to the full observation surface. New callers should preserve admission and the complete Observation.
Crash identity¶
CrashIdentity¶
type CrashIdentity struct {
Exact string
ExactNamespace string
Stable string
Site Frame
StableFrames []Frame
TotalFrames int
TargetFrames int
Degraded bool
Basis string
}
func Identify(trace string) CrashIdentity
The two current identities answer different questions:
| Field | Contract |
|---|---|
Exact | build-local dedup key using every parsed frame's observed function/raw location/module |
ExactNamespace | formula identifier; consumers reject unknown namespaces |
Stable | cross-build key using normalized target functions and repo-relative paths without lines |
Site | best target attribution after skipping runtime, sanitizer, harness, and compiler-library frames |
Degraded | true when no target frame supports attribution |
Stable is the literal UNATTRIBUTED when no target frame survives. Never use Stable to merge within-build crash buckets.
Legacy HashStack¶
This is the frozen historical five-frame hash. It remains comparable to locked oracle manifests and banked reports. It is not the current campaign-dedup key and can merge traces that first differ below frame five.
Compatibility crash types¶
type CrashType string
func ClassifyCrash(output string) CrashType
func ClassifyCrashWithFilename(output, filename string) CrashType
CrashType is a taxonomy extracted from output. It predates the observation model and cannot represent process start failure, timeout facts, recover-mode diagnostics, admission, or intercepted faults. Do not branch on it when process facts are available.
Crash¶
Crash carries the observed type, source attribution, input, target metadata, and identity fields. The important distinction is:
ExactID: current build-local bucket and report filename identity;StableID: cross-build observation;StackHash: legacy compatibility value.
Triager.AddCrash deduplicates on Exact identity. It does not establish that two inputs share a source-level root cause.
Deduplication¶
func DeduplicateDir(
crashDir, harness string,
timeout time.Duration,
extraEnv []string,
delete bool,
outputDir string,
recursive bool,
) (*DedupResult, error)
Full mode replays candidate inputs and groups observations by Exact identity. Timeouts and incomplete executions are counted but never bucketed as crashes. Intercepted faults are supported when the harness manifest establishes that capability.
Fast mode groups by content fingerprint without executing the harness. It is storage cleanup, not a root-cause or execution equivalence check.
Both CLI paths default to dry-run. Destructive callers must review the proposed actions.
Minimization¶
func MinimizeCrashDir(
crashDir, harness, outputDir string,
timeout time.Duration,
extraEnv []string,
) ([]MinimizeResult, *MinimizeSummary, error)
A successful minimization preserves the relevant Exact identity. A smaller input that merely crashes somewhere else is not a reproduction of the original observation.
Reports¶
type Report struct {
Crash *Crash
CVSSScore float64
Severity string
CWE CWE
Description string
AffectedVersions string
CVSSVector string
ReproducerPath string
ReportDate string
}
func GenerateReport(crash *Crash) *Report
func (r *Report) String() string
func (r *Report) WriteToFile(path string) error
GenerateReport creates an internal investigation report. Defaults are deliberately fail-closed:
- severity is
UNRATED; - affected versions are unknown;
- no CVSS score or vector is printed;
- location is explicit about unattributed or unavailable evidence.
Only an operator-supplied CVSSVector can add a score. The deprecated internal per-type score helper is not used to populate reports.
CWE hints¶
CWEForType maps a text-derived crash class to a taxonomy hint. Source review sometimes requires a different classification. A heap-buffer-overflow banner, for example, does not by itself distinguish the submitted primitive or prove the correct CWE.
SARIF¶
SARIF level is taxonomy-based, not CVSS-based: memory-safety classes are error, crash-only classes are warning, and resource exhaustion is note. SARIF transport does not turn an observation into a confirmed vulnerability.
Minimal usage¶
obs, output, err := triage.ObserveReplay(
triage.AdmitLiveExecution(),
"./crucible-libfuzzer-model",
input,
30*time.Second,
nil,
)
if err != nil || obs.Kind == triage.KindIncomplete {
// No verdict: preserve the reason and repair the execution.
}
if obs.NeedsHumanTriage() {
id := triage.Identify(output)
if id.Degraded {
// Preserve UNATTRIBUTED rather than inventing a target site.
}
// Bank raw output, replay, read target source, and adjudicate manually.
}