Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/nightly-soak.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Nightly Soak Test

on:
schedule:
- cron: '0 2 * * *'
workflow_dispatch:

jobs:
soak:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version: '1.22'

- name: Run exploratory harness
run: |
ITERATIONS=100 CONCURRENCY=50 MAX_PAYLOAD=65536 DEE_PORT=9188 ./scripts/exploratory-test.sh

- name: Upload results
uses: actions/upload-artifact@v4
if: always()
with:
name: soak-results
path: test-results/
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/bin/
/test-results/
34 changes: 34 additions & 0 deletions BUG_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Bug Report Template

## Severity
- [ ] CRITICAL: Security property violation, crash, or data loss
- [ ] HIGH: Functional failure under normal conditions
- [ ] MEDIUM: Edge case failure, performance degradation
- [ ] LOW: Cosmetic issue, logging noise

## Category
- [ ] Protocol: Encryption/decryption incorrect
- [ ] Concurrency: Race condition, deadlock
- [ ] Resource: Memory leak, file descriptor leak
- [ ] Input: Malformed payload handling
- [ ] Performance: Timeout, latency spike
- [ ] Logging: Secret material in logs

## Reproduction
Exact command to reproduce:
```
[paste command]
```

Expected behavior:

Actual behavior:

## Root Cause Analysis
[To be filled after investigation]

## Fix Verification
- [ ] Fix implemented
- [ ] Regression test added
- [ ] make verify passes
- [ ] Exploratory test passes
21 changes: 21 additions & 0 deletions IMPROVEMENT_PROTOCOL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Zero-Bug Improvement Protocol

## Weekly Cycle
1. Run exploratory harness with 2x previous load
2. Classify any failures using BUG_TEMPLATE.md
3. Fix CRITICAL/HIGH immediately, schedule others
4. Add regression test for each fixed bug
5. Update fuzz corpus with new inputs

## Monthly Cycle
1. Review all TODO/FIXME in codebase
2. Run mutation testing (if available for Go)
3. Update threat model with new attack vectors
4. Review and rotate any test keys/vectors

## Release Gate
Before any release:
- Exploratory harness: ITERATIONS=500 CONCURRENCY=100
- No failures in 3 consecutive runs
- All regression tests pass
- Security audit: no new vulnerabilities in dependencies
164 changes: 164 additions & 0 deletions scripts/exploratory-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
#!/bin/bash
# Deadend-Lab Exploratory Test Harness
# Usage: ITERATIONS=100 CONCURRENCY=50 MAX_PAYLOAD=131072 DEE_PORT=9188 ./scripts/exploratory-test.sh
set -euo pipefail

ITERATIONS="${ITERATIONS:-50}"
CONCURRENCY="${CONCURRENCY:-25}"
MAX_PAYLOAD="${MAX_PAYLOAD:-65536}"
DEE_PORT="${DEE_PORT:-9188}"
TIMEOUT_SEC="${TIMEOUT_SEC:-30}"
RESULTS_DIR="${RESULTS_DIR:-./test-results/$(date +%Y%m%d-%H%M%S)}"

mkdir -p "$RESULTS_DIR"
LOG_FILE="$RESULTS_DIR/test-run.log"
FAILURES_FILE="$RESULTS_DIR/failures.json"
METRICS_FILE="$RESULTS_DIR/metrics.json"

FORBIDDEN_PATTERNS='(k_enc|k_mac|session_key|private_key|secret_key|BEGIN.*PRIVATE KEY|AKIA[0-9A-Z]{16}|ghp_[A-Za-z0-9]{36}|xox[baprs]-|AIzaSy)'
SUSPICIOUS_HEX='[0-9a-fA-F]{128,}'

cleanup() {
echo "Cleaning up..."
DEE_PORT="$DEE_PORT" docker compose down -t 10 2>/dev/null || true
echo "Results in: $RESULTS_DIR"
}
trap cleanup EXIT

log() {
echo "[$(date '+%Y-%m-%dT%H:%M:%S')] $*" | tee -a "$LOG_FILE"
}

log "=== EXPLORATORY TEST HARNESS ==="
log "Configuration: ITER=$ITERATIONS CONC=$CONCURRENCY MAX_PAYLOAD=$MAX_PAYLOAD PORT=$DEE_PORT"

log "[1/8] Building Docker image..."
docker build -t deadend-lab:exploratory . >> "$LOG_FILE" 2>&1

log "[2/8] Starting services..."
DEE_PORT="$DEE_PORT" docker compose down 2>/dev/null || true
DEE_PORT="$DEE_PORT" docker compose up -d >> "$LOG_FILE" 2>&1

log "[3/8] Health check..."
for i in $(seq 1 30); do
if curl -fsS "http://localhost:${DEE_PORT}/health" >/dev/null 2>&1; then
log "Health check: PASS"
break
fi
sleep 1
done

if ! curl -fsS "http://localhost:${DEE_PORT}/health" >/dev/null 2>&1; then
log "BLOCKER: Health check failed"
exit 1
fi

pound() {
local mode="$1"
local n="$2"
for _ in $(seq 1 "$n"); do
curl -fsS -X POST "http://localhost:${DEE_PORT}/scenario/${mode}" --max-time "$TIMEOUT_SEC" >/dev/null 2>&1 || true
done
}

concurrent_load_test() {
local mode="$1"
local concurrency="$2"
local failed=0
for _ in $(seq 1 "$concurrency"); do
(curl -fsS -X POST "http://localhost:${DEE_PORT}/scenario/${mode}" --max-time "$TIMEOUT_SEC" >/dev/null 2>&1) || ((failed++)) || true
done
wait 2>/dev/null || true
return 0
}

TOTAL_REQUESTS=0
FAILED_REQUESTS=0
FAILURES=()

log "[4/8] Starting exploratory test iterations..."

for iter in $(seq 1 "$ITERATIONS"); do
log "=== ITERATION $iter/$ITERATIONS ==="

mode=$([ $((RANDOM % 2)) -eq 0 ] && echo "safe" || echo "naive")
if curl -fsS -X POST "http://localhost:${DEE_PORT}/scenario/${mode}" --max-time "$TIMEOUT_SEC" >/dev/null 2>&1; then
: $((TOTAL_REQUESTS++))
else
: $((TOTAL_REQUESTS++))
: $((FAILED_REQUESTS++))
FAILURES+=("scenario_${mode}_iter_${iter}")
fi

if [ $((iter % 10)) -eq 0 ]; then
log "Concurrent load test..."
pound safe "$CONCURRENCY"
pound naive "$CONCURRENCY"
fi

if [ $((iter % 25)) -eq 0 ]; then
log "Container churn: restart"
DEE_PORT="$DEE_PORT" docker compose restart >> "$LOG_FILE" 2>&1
sleep 2
if ! curl -fsS "http://localhost:${DEE_PORT}/health" >/dev/null 2>&1; then
log "BLOCKER: Service did not recover after restart"
exit 1
fi
fi

DEE_PORT="$DEE_PORT" docker compose logs --no-color > "$RESULTS_DIR/compose.log" 2>&1 || true
if grep -Ei "$FORBIDDEN_PATTERNS" "$RESULTS_DIR/compose.log" 2>/dev/null; then
log "BLOCKER: Forbidden pattern in logs"
exit 1
fi
if grep -E "$SUSPICIOUS_HEX" "$RESULTS_DIR/compose.log" 2>/dev/null; then
log "WARNING: Suspicious long hex in logs"
fi
done

log "[5/8] Attack demos..."
if ! go run ./cmd/attacks/nonce-reuse 2>> "$LOG_FILE" | grep -q 'Recovered plaintext == expected: true'; then
FAILURES+=("attack_nonce_reuse")
fi
if ! go run ./cmd/attacks/replay 2>> "$LOG_FILE" | grep -q 'Replay accepted: true'; then
FAILURES+=("attack_replay")
fi

log "[6/8] Generating metrics..."

if command -v jq >/dev/null 2>&1; then
echo "{\"test_run_id\":\"$(basename "$RESULTS_DIR")\",\"timestamp\":\"$(date -Iseconds 2>/dev/null || date '+%Y-%m-%dT%H:%M:%S')\",\"configuration\":{\"iterations\":$ITERATIONS,\"concurrency\":$CONCURRENCY,\"max_payload\":$MAX_PAYLOAD,\"port\":$DEE_PORT},\"summary\":{\"total_requests\":$TOTAL_REQUESTS,\"failed_requests\":$FAILED_REQUESTS},\"failures_count\":${#FAILURES[@]},\"artifacts_location\":\"$RESULTS_DIR\"}" | jq . > "$METRICS_FILE"
else
echo "total_requests=$TOTAL_REQUESTS failed_requests=$FAILED_REQUESTS failures=${#FAILURES[@]}" > "$METRICS_FILE"
fi

log "[7/8] Writing failure report..."

if [ ${#FAILURES[@]} -gt 0 ]; then
if command -v jq >/dev/null 2>&1; then
printf '%s\n' "${FAILURES[@]}" | jq -R . | jq -s . > "$FAILURES_FILE"
else
printf '%s\n' "${FAILURES[@]}" > "${FAILURES_FILE%.json}.txt"
fi
else
echo "[]" > "$FAILURES_FILE" 2>/dev/null || true
fi

log "[8/8] Test run complete"

echo ""
echo "=== TEST SUMMARY ==="
echo "Results directory: $RESULTS_DIR"
echo "Total requests: $TOTAL_REQUESTS"
echo "Failed requests: $FAILED_REQUESTS"
echo "Failure count: ${#FAILURES[@]}"
echo ""

if [ ${#FAILURES[@]} -gt 0 ]; then
echo "FAILURES:"
for f in "${FAILURES[@]}"; do echo "$f"; done
exit 1
else
echo "ALL TESTS PASSED"
exit 0
fi
1 change: 1 addition & 0 deletions testdata/seeds/current.seed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
seed: 0
2 changes: 1 addition & 1 deletion tests/policy/commits_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const (
allowedAuthorName = "Thor Thor"
)

// allowedCommit is true if author+committer match maintainer or Dependabot (GitHub's dependency bot).
// allowedCommit is true if author+committer match maintainer or allowed automation.
func allowedCommit(an, ae, cn, ce string) bool {
if an == allowedAuthorName && ae == allowedAuthorEmail && cn == allowedAuthorName && ce == allowedAuthorEmail {
return true
Expand Down
9 changes: 9 additions & 0 deletions tests/property/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Property-Based Tests

This directory contains property-based tests using Go's testing/quick or gofuzz.

Properties under test:
- Nonce uniqueness: forall counters, nonces are unique
- Ciphertext indistinguishability: forall messages, ciphertexts are random-looking
- Decryption correctness: forall (key, nonce, plaintext), decrypt(encrypt(pt)) == pt
- Counter monotonicity: forall sequences, counters never decrease
5 changes: 5 additions & 0 deletions tests/regression/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Regression Tests

Each file corresponds to a fixed bug. Tests must fail before fix, pass after fix.

Naming: Issue-{number}_{short-desc}.go
Loading