Skip to content

Research: fault-injection tests for CLI failure visibility and SARIF artifact integrity (bounded pilot) #428

Open

Description

@PhysShell

Classification

Problem

A static analyzer can be semantically correct on valid inputs yet produce a dangerously misleading outcome when its surrounding pipeline fails:

  • facts are absent, truncated, unreadable or partially written;
  • a child process terminates unexpectedly;
  • output serialization/write fails, or a previous report survives a failed run;
  • the launcher loses the failure status and presents a clean run / empty SARIF as if the analysis completed.

The safety property is failure visibility and artifact integrity, not ownership-analysis soundness. 0 findings after successful analysis and 0 findings because the analysis never completed must not be indistinguishable.

#261 already tested Rust CLI catchable panic and hard-death controls behind an opt-in fault-injection feature. Do not redo those tests. This issue probes external I/O failures and end-to-end output handling, especially at the extractor → facts → analyzer → report boundaries.

Scope: small read-only-to-product pilot

First inventory the actual current production invocation, supported formats and exit/status contracts on Linux/Windows from the repository and tests. Do not assume that every entry point writes SARIF to a file (some may render to stdout), or that an illustrative syscall number maps to the desired artifact.

Select 4–6 reproducible, independently controllable scenarios from:

  1. Input read/open failure: valid input becomes inaccessible or unreadable after preflight; check stage-specific error classification.
  2. Facts handoff failure: simulated missing/truncated/unreadable facts after extraction; verify no downstream clean result, and no silent fallback.
  3. Report destination failure: cannot create/open the chosen output path (permissions or deliberately invalid path, using the platform's actual supported output mode).
  4. Write failure after partial progress: injected ENOSPC/EIO or a controlled failing writer targeted to the report, not an arbitrary fifth write; verify no successfully published truncated report.
  5. Interrupted/short output: if realistically inducible on a supported path, verify the result is either retried correctly or reported as failure.
  6. Stale artifact hazard: leave a valid report from an earlier run, then fail the next run at the producer; no stale artifact should be presented/consumed as the current result without explicit provenance.

Include two controls: a genuinely clean successful analysis and a successful analysis with real findings, so the harness cannot count every nonzero exit as an expected failure.

For errors already covered by contract tests, reuse those fixtures instead of adding copies.

Mechanism: prefer the narrowest deterministic hook

  • First try existing test seams, temporary directories, permission controls and injected stream/writer failures where applicable.
  • On Linux, strace --inject can be evaluated for a single experiment. Prove the intercepted syscall, process, file descriptor and artifact being targeted; -f --inject=write:...:when=5 by itself is not sufficient evidence that the SARIF write was faulted.
  • Keep Windows as a contract-level validation target using portable test seams. Do not turn Linux ptrace into a required Windows/CI dependency.
  • Preserve production semantics; any new fault hook must be test-only, opt-in and disabled in normal builds/runs. No broad interception framework or Murphy vendoring.
  • Pin the scenario and any injection plan/seed, and make it rerunnable without network access. Minimize a complex failure to the first causally necessary failing operation if needed, borrowing Murphy's replay/reduction idea.

Oracle and evidence

For each scenario record:

Field Required evidence
Baseline exact command, entry point, OS, version/commit, expected stdout/stderr/exit and artifacts
Injection stage, mechanism, targeted file/FD/call, injected fault, proof it fired
Observation actual exit/status, stdout/stderr, artifact paths/sizes/hashes, temp-file leftovers
Verdict Handled / Failure-masked / Wrong-artifact / Injection-not-triggered / Unsupported / Infra-error
Reproduction standalone rerun command with cleanup and explicit comparison to baseline

Do not classify a scenario as handled merely because the process exited nonzero. Check that the launcher propagates an actionable failure and downstream consumers cannot misinterpret produced or pre-existing artifacts as successful current results.

Distinguish injection-not-triggered from pass. An unsupported injection must not be scored as coverage. Do not bake in a universal numeric exit code; follow existing platform/entry-point contracts (#261/#262) unless a separately approved change is necessary.

Deliverables / acceptance

Stop / promotion gate

P2 research only. Stop after the bounded pilot. Promote to a permanent regression test or open a narrowly scoped fix issue only if a new, reproducible failure-masking or artifact-integrity bug is found, or existing critical failure contracts demonstrably lack coverage. If all useful paths are already protected, preserve the evidence and STOP, rather than developing a reusable fault-injection platform.

Explicit non-goals

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions