Triage Air-Gap Workflows
This guide explains how to triage vulnerability findings in fully air-gapped environments using Stella Ops. The workflow moves signed evidence bundles across the air gap, captures triage decisions offline, and generates replay tokens so every decision stays auditable. It is written for security reviewers and operators working on sealed networks.
Overview
Triage in an air-gapped estate follows a round trip: a connected machine exports a signed evidence bundle, the bundle is transferred across the air gap, a reviewer makes decisions offline, and the signed decisions are synced back. Three workflows are documented below — bundle-based triage, batch triage for high volume, and an evidence-first review mode — followed by configuration, bundle/decision formats, replay tokens, and troubleshooting.
Workflow 1: Offline Triage with Evidence Bundles
Step 1: Export Evidence Bundle (Connected Machine)
# Export triage bundle for specific findings
stellaops triage export \
--scan-id scan-12345678 \
--findings CVE-2024-1234,CVE-2024-5678 \
--include-evidence \
--include-graph \
--output triage-bundle.stella.bundle.tgz
# Export entire scan for offline review
stellaops triage export \
--scan-id scan-12345678 \
--all-findings \
--output full-triage-bundle.stella.bundle.tgz
Step 2: Bundle Contents
The .stella.bundle.tgz archive contains:
triage-bundle.stella.bundle.tgz/
├── manifest.json # Signed bundle manifest
├── findings/
│ ├── index.json # Finding list with IDs
│ ├── CVE-2024-1234.json # Finding details
│ └── CVE-2024-5678.json
├── evidence/
│ ├── reachability/ # Reachability proofs
│ ├── callstack/ # Call stack snippets
│ ├── vex/ # VEX/CSAF statements
│ └── provenance/ # Provenance data
├── graph/
│ ├── nodes.ndjson # Dependency graph nodes
│ └── edges.ndjson # Graph edges
├── feeds/
│ └── snapshot.json # Feed snapshot metadata
└── signature.dsse # DSSE envelope
Step 3: Transfer to Air-Gapped Environment
Transfer using approved methods:
- USB media (security scanned)
- Optical media
- Data diode
Step 4: Import and Verify
On the air-gapped machine:
# Verify bundle integrity
stellaops triage verify-bundle \
--input triage-bundle.stella.bundle.tgz \
--public-key /path/to/signing-key.pub
# Import for offline triage
stellaops triage import \
--input triage-bundle.stella.bundle.tgz \
--workspace /opt/stellaops/triage
Step 5: Perform Offline Triage
# List findings in bundle
stellaops triage list \
--workspace /opt/stellaops/triage
# View finding with evidence
stellaops triage show CVE-2024-1234 \
--workspace /opt/stellaops/triage \
--show-evidence
# Make triage decision
stellaops triage decide CVE-2024-1234 \
--workspace /opt/stellaops/triage \
--status not_affected \
--justification "Code path is unreachable due to config gating" \
--reviewer "security-team"
Step 6: Export Decisions
# Export decisions for sync back
stellaops triage export-decisions \
--workspace /opt/stellaops/triage \
--output decisions-2025-01-15.json \
--sign
Step 7: Sync Decisions (Connected Machine)
# Import and apply decisions
stellaops triage import-decisions \
--input decisions-2025-01-15.json \
--verify \
--apply
Workflow 2: Batch Offline Triage
For high-volume environments.
Step 1: Export Batch Bundle
# Export all untriaged findings
stellaops triage export-batch \
--query "status=untriaged AND priority>=0.7" \
--limit 100 \
--output batch-triage-2025-01-15.stella.bundle.tgz
Step 2: Offline Batch Processing
# Interactive batch triage
stellaops triage batch \
--workspace /opt/stellaops/triage \
--input batch-triage-2025-01-15.stella.bundle.tgz
# Keyboard shortcuts enabled:
# j/k - Next/Previous finding
# a - Accept (affected)
# n - Not affected
# w - Will not fix
# f - False positive
# u - Undo last decision
# q - Quit (saves progress)
Step 3: Export and Sync
# Export batch decisions
stellaops triage export-decisions \
--workspace /opt/stellaops/triage \
--format json \
--sign \
--output batch-decisions.json
Workflow 3: Evidence-First Offline Review
Step 1: Pre-compute Evidence
On connected machine:
# Generate evidence for all high-priority findings
stellaops evidence generate \
--scan-id scan-12345678 \
--priority-min 0.7 \
--output-dir ./evidence-pack
# Include:
# - Reachability analysis
# - Call stack traces
# - VEX lookups
# - Dependency graph snippets
Step 2: Package with Findings
stellaops triage package \
--scan-id scan-12345678 \
--evidence-dir ./evidence-pack \
--output evidence-triage.stella.bundle.tgz
Step 3: Offline Review with Evidence
# Evidence-first view
stellaops triage show CVE-2024-1234 \
--workspace /opt/stellaops/triage \
--evidence-first
# Output:
# ═══════════════════════════════════════════
# CVE-2024-1234 · lodash@4.17.20
# ═══════════════════════════════════════════
#
# EVIDENCE SUMMARY
# ────────────────
# Reachability: EXECUTED (tier 2/3)
# └─ main.js:42 → utils.js:15 → lodash/merge
#
# Call Stack:
# 1. main.js:42 handleRequest()
# 2. utils.js:15 mergeConfig()
# 3. lodash:merge <vulnerable>
#
# VEX Status: No statement found
# EPSS: 0.45 (Medium)
# KEV: No
#
# ─────────────────────────────────────────────
# Press [a]ffected, [n]ot affected, [s]kip...
Configuration
Environment Variables
| Variable | Description | Default |
|---|---|---|
STELLAOPS_OFFLINE | Enable offline mode | false |
STELLAOPS_TRIAGE_WORKSPACE | Triage workspace path | ~/.stellaops/triage |
STELLAOPS_BUNDLE_VERIFY | Verify bundle signatures | true |
STELLAOPS_DECISION_SIGN | Sign exported decisions | true |
Config File
# ~/.stellaops/triage.yaml
offline:
enabled: true
workspace: /opt/stellaops/triage
bundle_verify: true
decisions:
require_justification: true
sign_exports: true
keyboard:
enabled: true
vim_mode: true
Bundle Format Specification
manifest.json
{
"version": "1.0",
"type": "triage-bundle",
"created_at": "2025-01-15T10:00:00Z",
"scan_id": "scan-12345678",
"finding_count": 25,
"feed_snapshot": "sha256:abc123...",
"graph_revision": "sha256:def456...",
"signatures": {
"manifest": "sha256:ghi789...",
"dsse_envelope": "signature.dsse"
}
}
Decision Format
{
"finding_id": "finding-12345678",
"vuln_key": "CVE-2024-1234:pkg:npm/lodash@4.17.20",
"status": "not_affected",
"justification": "Code path gated by feature flag",
"reviewer": "security-team",
"decided_at": "2025-01-15T14:30:00Z",
"replay_token": "rt_abc123...",
"evidence_refs": [
"evidence/reachability/CVE-2024-1234.json"
]
}
Replay Tokens
Each decision generates a replay token for audit trail:
# View replay token
stellaops triage show-token rt_abc123...
# Output:
# Replay Token: rt_abc123...
# ─────────────────────────────
# Finding: CVE-2024-1234
# Decision: not_affected
# Evidence Hash: sha256:xyz789...
# Feed Snapshot: sha256:abc123...
# Decided: 2025-01-15T14:30:00Z
# Reviewer: security-team
Verify Token
stellaops triage verify-token rt_abc123... \
--public-key /path/to/key.pub
# ✓ Token signature valid
# ✓ Evidence hash matches
# ✓ Feed snapshot verified
Troubleshooting
Error: Bundle signature invalid
Error: Bundle signature verification failed
Solution: Ensure the correct public key is used:
stellaops triage verify-bundle \
--input bundle.tgz \
--public-key /path/to/correct-key.pub \
--verbose
Error: Evidence not found
Error: Evidence for CVE-2024-1234 not included in bundle
Solution: Re-export with evidence:
stellaops triage export \
--scan-id scan-12345678 \
--findings CVE-2024-1234 \
--include-evidence \
--output bundle.tgz
Error: Decision sync conflict
Error: Finding CVE-2024-1234 has newer decision on server
Solution: Review and resolve:
stellaops triage import-decisions \
--input decisions.json \
--conflict-mode review
# Options: keep-local, keep-server, newest, review
