Skip to main content

Bug Reproduction Guide

A good bug report includes a minimal reproduction that any contributor can run. This guide explains how to write one.

Principles

  1. Deterministic — use --now to fix the evaluation timestamp. Never rely on wall-clock time.
  2. Minimal — include only the fields and assets needed to trigger the bug. Remove everything else.
  3. Sanitized — replace real bucket names, ARNs, account IDs, and tags with placeholders. Use --sanitize or manually substitute.
  4. Self-contained — the reproduction should run with only the Stave binary and the files you provide. No cloud credentials, no external dependencies.
  5. No network — Stave is offline. Your repro should work in an air-gapped environment.

Quick Template

Use the bug reproduction template as a starting point. It provides:

  • A minimal sanitized observation file
  • A Go test harness that asserts exit code and JSON output
  • Version printing for debugging
  • Automatic cleanup

Step-by-Step

1. Identify the minimum input

Start with your full observation file and strip fields until the bug disappears. Then add back the last field you removed — that's your minimal reproduction.

2. Sanitize sensitive data

Replace real values with placeholders:

Real valueReplacement
Bucket namesSANITIZED_01, test-bucket
Account IDs123456789012
ARNsarn:aws:s3:::SANITIZED_01
TagsRemove or use generic values
PoliciesSimplify to minimum triggering structure

Or use Stave's built-in sanitization:

stave apply --controls ./controls --observations ./obs --sanitize > sanitized-output.json

3. Write the observation file

Create a minimal obs.v0.1 observation:

{
"schema_version": "obs.v0.1",
"captured_at": "2026-01-01T00:00:00Z",
"assets": [{
"id": "res:aws:s3:bucket:SANITIZED_01",
"type": "storage_bucket",
"vendor": "aws",
"properties": {
"storage": {
"visibility": { "public_read": true }
}
}
}]
}

For duration bugs, include at least two snapshots with different timestamps.

4. Write the reproduction command

STAVE_BIN="${STAVE_BIN:-stave}"
NOW="2026-01-01T00:00:00Z"

$STAVE_BIN apply \
--controls controls/s3 \
--observations ./repro-observations/ \
--now "$NOW"

echo "Exit code: $?"

Use || rc=$? instead of set -e when expecting non-zero exit codes:

rc=0
$STAVE_BIN apply --controls ./controls --observations ./obs --now "$NOW" || rc=$?
echo "Exit code: $rc"

5. Document expectations

In your bug report, state clearly:

  • Expected behavior — what should happen (exit code, output structure, finding count)
  • Actual behavior — what actually happens
  • Stave version — output of stave --version
  • OS and architectureuname -a or equivalent

What to Include in the Issue

## Bug Report

**Stave version:** v1.2.3
**OS:** Linux x86_64 / macOS arm64

### Steps to reproduce

1. Save the attached observation file as `obs/snapshot.json`
2. Run: `stave apply --controls controls/s3 --observations obs/ --now 2026-01-01T00:00:00Z`
3. Observe exit code X

### Expected
Exit code 3 with finding for CTL.S3.PUBLIC.001

### Actual
Exit code 0 with no findings

### Minimal observation
(attach sanitized JSON)

Common Pitfalls

PitfallFix
Non-deterministic outputAlways use --now
set -e with expected non-zero exitUse || rc=$? pattern
Missing second snapshot for duration bugsInclude 2+ snapshot files
Real infrastructure data in reportSanitize all identifiers