Skip to content

Commit 62395e8

Browse files
author
Jonathan D.A. Jewell
committed
Auto-commit: Sync changes [2026-02-21]
1 parent e0d8925 commit 62395e8

8 files changed

Lines changed: 1220 additions & 2 deletions

File tree

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,3 +587,8 @@ If you use panic-attack in your research, please cite:
587587
---
588588

589589
**Status**: Active development | **Version**: 0.2.0 | **MSRV**: 1.85.0
590+
591+
592+
## Architecture
593+
594+
See [TOPOLOGY.md](TOPOLOGY.md) for a visual architecture map and completion dashboard.

TOPOLOGY.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<!-- SPDX-License-Identifier: PMPL-1.0-or-later -->
2+
<!-- TOPOLOGY.md — Project architecture map and completion dashboard -->
3+
<!-- Last updated: 2026-02-19 -->
4+
5+
# panic-attack — Project Topology
6+
7+
## System Architecture
8+
9+
```
10+
┌─────────────────────────────────────────┐
11+
│ SECURITY TESTER │
12+
│ (CLI, TUI, GUI, CI Hook) │
13+
└───────────────────┬─────────────────────┘
14+
│ Command / Spec
15+
16+
┌─────────────────────────────────────────┐
17+
│ PANIC-ATTACK CORE │
18+
│ (Orchestration, Reports, Wiring) │
19+
└──────────┬───────────────────┬──────────┘
20+
│ │
21+
▼ ▼
22+
┌───────────────────────┐ ┌────────────────────────────────┐
23+
│ ANALYSIIS LAYER │ │ ATTACK LAYER │
24+
│ - Assail (Static) │ │ - Multi-Axis Stress (CPU, Mem) │
25+
│ - Language Patterns │ │ - Ambush (Ambient Stressors) │
26+
│ - Weak Point Scoring │ │ - Amuck (Mutations) │
27+
└──────────┬────────────┘ └──────────┬─────────────────────┘
28+
│ │
29+
└────────────┬─────────────┘
30+
31+
┌─────────────────────────────────────────┐
32+
│ SIGNATURE ENGINE │
33+
│ (Datalog-inspired bug detection) │
34+
└───────────────────┬─────────────────────┘
35+
36+
37+
┌─────────────────────────────────────────┐
38+
│ TARGET PROGRAM │
39+
│ (Rust, C/C++, Go, Python, etc.) │
40+
└─────────────────────────────────────────┘
41+
42+
┌─────────────────────────────────────────┐
43+
│ REPO INFRASTRUCTURE │
44+
│ Justfile / Cargo .machine_readable/ │
45+
│ VerisimDB Data PanLL Integration │
46+
└─────────────────────────────────────────┘
47+
```
48+
49+
## Completion Dashboard
50+
51+
```
52+
COMPONENT STATUS NOTES
53+
───────────────────────────────── ────────────────── ─────────────────────────────────
54+
CORE CAPABILITIES
55+
Assail Static Analysis ██████████ 100% 5 languages supported
56+
Multi-Axis Stress Testing ██████████ 100% 6 axes (CPU, Mem, Disk, etc)
57+
Ambush / Amuck / Abduct ██████████ 100% Advanced workflows stable
58+
Signature Detection Engine ████████░░ 80% Datalog rules expanding
59+
60+
REPORTING & UI
61+
JSON/YAML/Nickel Reports ██████████ 100% Audit-grade exports stable
62+
TUI / GUI Dashboard ████████░░ 80% Report browsing verified
63+
VerisimDB Diff Viewer ██████████ 100% Latest report comparison active
64+
A2ML Bundle Import/Export ██████████ 100% Schema-versioned verified
65+
66+
REPO INFRASTRUCTURE
67+
Justfile Automation ██████████ 100% Standard build/lint/test
68+
.machine_readable/ ██████████ 100% STATE tracking active
69+
Test Suite (Unit/Integ) ██████████ 100% High coverage (306+ tests)
70+
71+
─────────────────────────────────────────────────────────────────────────────
72+
OVERALL: █████████░ ~95% v0.2.0 Stable Development
73+
```
74+
75+
## Key Dependencies
76+
77+
```
78+
Assail (Static) ───► Attack Strategy ───► Target Binary ───► Crash Report
79+
│ │ │ │
80+
▼ ▼ ▼ ▼
81+
Pattern Lib ──────► Multi-Axis ────────► Signature Engine ──► Verdict
82+
```
83+
84+
## Update Protocol
85+
86+
This file is maintained by both humans and AI agents. When updating:
87+
88+
1. **After completing a component**: Change its bar and percentage
89+
2. **After adding a component**: Add a new row in the appropriate section
90+
3. **After architectural changes**: Update the ASCII diagram
91+
4. **Date**: Update the `Last updated` comment at the top of this file
92+
93+
Progress bars use: `` (filled) and `` (empty), 10 characters wide.
94+
Percentages: 0%, 10%, 20%, ... 100% (in 10% increments).

src/lib.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
// SPDX-License-Identifier: PMPL-1.0-or-later
22

3-
//! panic-attack: Universal stress testing and logic-based bug signature detection
3+
//! Panic-Attacker — Universal Stress Testing & Bug Signature Detection.
44
//!
5-
//! Library interface for integration tests and external consumers.
5+
//! This crate provides the core engine for "Security Ambush" operations.
6+
//! It combines traditional stress testing (chaos engineering) with
7+
//! logic-based inference to identify subtle race conditions and
8+
//! state-corruption bugs.
9+
//!
10+
//! ENGINE PILLARS:
11+
//! 1. **Ambush**: Orchestrates high-concurrency attack patterns.
12+
//! 2. **Kanren**: Employs relational programming (microKanren) to infer
13+
//! logical contradictions from system logs.
14+
//! 3. **Signatures**: A database of known bug patterns (e.g. "Double Free",
15+
//! "UAF", "Logic Contradiction") matched against execution traces.
616
717
pub mod a2ml;
818
pub mod abduct;

src/report/sarif.rs

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
// SPDX-License-Identifier: PMPL-1.0-or-later
2+
3+
//! SARIF 2.1.0 output for GitHub Security tab integration
4+
//!
5+
//! Converts AssailReport weak points into OASIS SARIF format.
6+
//! See: https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html
7+
8+
use crate::types::{AssailReport, Severity, WeakPointCategory};
9+
use anyhow::Result;
10+
use serde::Serialize;
11+
12+
const SARIF_SCHEMA: &str = "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json";
13+
const SARIF_VERSION: &str = "2.1.0";
14+
15+
/// Top-level SARIF log
16+
#[derive(Debug, Serialize)]
17+
#[serde(rename_all = "camelCase")]
18+
pub struct SarifLog {
19+
#[serde(rename = "$schema")]
20+
pub schema: String,
21+
pub version: String,
22+
pub runs: Vec<SarifRun>,
23+
}
24+
25+
/// A single SARIF run (one tool execution)
26+
#[derive(Debug, Serialize)]
27+
#[serde(rename_all = "camelCase")]
28+
pub struct SarifRun {
29+
pub tool: SarifTool,
30+
pub results: Vec<SarifResult>,
31+
}
32+
33+
/// Tool descriptor
34+
#[derive(Debug, Serialize)]
35+
#[serde(rename_all = "camelCase")]
36+
pub struct SarifTool {
37+
pub driver: SarifToolComponent,
38+
}
39+
40+
/// Tool component with rules
41+
#[derive(Debug, Serialize)]
42+
#[serde(rename_all = "camelCase")]
43+
pub struct SarifToolComponent {
44+
pub name: String,
45+
pub version: String,
46+
pub information_uri: String,
47+
pub rules: Vec<SarifRule>,
48+
}
49+
50+
/// Rule descriptor
51+
#[derive(Debug, Serialize)]
52+
#[serde(rename_all = "camelCase")]
53+
pub struct SarifRule {
54+
pub id: String,
55+
pub name: String,
56+
pub short_description: SarifMessage,
57+
pub default_configuration: SarifConfiguration,
58+
}
59+
60+
/// Configuration with level
61+
#[derive(Debug, Serialize)]
62+
#[serde(rename_all = "camelCase")]
63+
pub struct SarifConfiguration {
64+
pub level: String,
65+
}
66+
67+
/// A single finding
68+
#[derive(Debug, Serialize)]
69+
#[serde(rename_all = "camelCase")]
70+
pub struct SarifResult {
71+
pub rule_id: String,
72+
pub level: String,
73+
pub message: SarifMessage,
74+
pub locations: Vec<SarifLocation>,
75+
}
76+
77+
/// Message with text
78+
#[derive(Debug, Serialize)]
79+
#[serde(rename_all = "camelCase")]
80+
pub struct SarifMessage {
81+
pub text: String,
82+
}
83+
84+
/// Physical location
85+
#[derive(Debug, Serialize)]
86+
#[serde(rename_all = "camelCase")]
87+
pub struct SarifLocation {
88+
pub physical_location: SarifPhysicalLocation,
89+
}
90+
91+
/// Physical location with artifact
92+
#[derive(Debug, Serialize)]
93+
#[serde(rename_all = "camelCase")]
94+
pub struct SarifPhysicalLocation {
95+
pub artifact_location: SarifArtifactLocation,
96+
#[serde(skip_serializing_if = "Option::is_none")]
97+
pub region: Option<SarifRegion>,
98+
}
99+
100+
/// Artifact URI
101+
#[derive(Debug, Serialize)]
102+
#[serde(rename_all = "camelCase")]
103+
pub struct SarifArtifactLocation {
104+
pub uri: String,
105+
}
106+
107+
/// Region (line number)
108+
#[derive(Debug, Serialize)]
109+
#[serde(rename_all = "camelCase")]
110+
pub struct SarifRegion {
111+
pub start_line: u32,
112+
}
113+
114+
/// Map WeakPointCategory to a stable rule ID
115+
fn rule_id(category: &WeakPointCategory) -> &'static str {
116+
match category {
117+
WeakPointCategory::UncheckedAllocation => "PA001",
118+
WeakPointCategory::UnboundedLoop => "PA002",
119+
WeakPointCategory::BlockingIO => "PA003",
120+
WeakPointCategory::UnsafeCode => "PA004",
121+
WeakPointCategory::PanicPath => "PA005",
122+
WeakPointCategory::RaceCondition => "PA006",
123+
WeakPointCategory::DeadlockPotential => "PA007",
124+
WeakPointCategory::ResourceLeak => "PA008",
125+
WeakPointCategory::CommandInjection => "PA009",
126+
WeakPointCategory::UnsafeDeserialization => "PA010",
127+
WeakPointCategory::DynamicCodeExecution => "PA011",
128+
WeakPointCategory::UnsafeFFI => "PA012",
129+
WeakPointCategory::AtomExhaustion => "PA013",
130+
WeakPointCategory::InsecureProtocol => "PA014",
131+
WeakPointCategory::ExcessivePermissions => "PA015",
132+
WeakPointCategory::PathTraversal => "PA016",
133+
WeakPointCategory::HardcodedSecret => "PA017",
134+
WeakPointCategory::UncheckedError => "PA018",
135+
WeakPointCategory::InfiniteRecursion => "PA019",
136+
WeakPointCategory::UnsafeTypeCoercion => "PA020",
137+
}
138+
}
139+
140+
/// Map WeakPointCategory to a human-readable name
141+
fn rule_name(category: &WeakPointCategory) -> &'static str {
142+
match category {
143+
WeakPointCategory::UncheckedAllocation => "unchecked-allocation",
144+
WeakPointCategory::UnboundedLoop => "unbounded-loop",
145+
WeakPointCategory::BlockingIO => "blocking-io",
146+
WeakPointCategory::UnsafeCode => "unsafe-code",
147+
WeakPointCategory::PanicPath => "panic-path",
148+
WeakPointCategory::RaceCondition => "race-condition",
149+
WeakPointCategory::DeadlockPotential => "deadlock-potential",
150+
WeakPointCategory::ResourceLeak => "resource-leak",
151+
WeakPointCategory::CommandInjection => "command-injection",
152+
WeakPointCategory::UnsafeDeserialization => "unsafe-deserialization",
153+
WeakPointCategory::DynamicCodeExecution => "dynamic-code-execution",
154+
WeakPointCategory::UnsafeFFI => "unsafe-ffi",
155+
WeakPointCategory::AtomExhaustion => "atom-exhaustion",
156+
WeakPointCategory::InsecureProtocol => "insecure-protocol",
157+
WeakPointCategory::ExcessivePermissions => "excessive-permissions",
158+
WeakPointCategory::PathTraversal => "path-traversal",
159+
WeakPointCategory::HardcodedSecret => "hardcoded-secret",
160+
WeakPointCategory::UncheckedError => "unchecked-error",
161+
WeakPointCategory::InfiniteRecursion => "infinite-recursion",
162+
WeakPointCategory::UnsafeTypeCoercion => "unsafe-type-coercion",
163+
}
164+
}
165+
166+
/// Map Severity to SARIF level
167+
fn sarif_level(severity: &Severity) -> &'static str {
168+
match severity {
169+
Severity::Critical => "error",
170+
Severity::High => "error",
171+
Severity::Medium => "warning",
172+
Severity::Low => "note",
173+
}
174+
}
175+
176+
/// Parse a location string like "src/main.rs:42" into (path, optional line)
177+
fn parse_location(loc: &str) -> (&str, Option<u32>) {
178+
if let Some(colon_pos) = loc.rfind(':') {
179+
let (path, rest) = loc.split_at(colon_pos);
180+
if let Ok(line) = rest[1..].parse::<u32>() {
181+
return (path, Some(line));
182+
}
183+
}
184+
(loc, None)
185+
}
186+
187+
/// Convert an AssailReport to SARIF JSON
188+
pub fn to_sarif(report: &AssailReport) -> Result<SarifLog> {
189+
// Collect unique rules
190+
let mut seen_categories = std::collections::HashSet::new();
191+
let mut rules = Vec::new();
192+
193+
for wp in &report.weak_points {
194+
if seen_categories.insert(wp.category) {
195+
rules.push(SarifRule {
196+
id: rule_id(&wp.category).to_string(),
197+
name: rule_name(&wp.category).to_string(),
198+
short_description: SarifMessage {
199+
text: format!("{:?}", wp.category),
200+
},
201+
default_configuration: SarifConfiguration {
202+
level: sarif_level(&wp.severity).to_string(),
203+
},
204+
});
205+
}
206+
}
207+
208+
// Convert weak points to results
209+
let results: Vec<SarifResult> = report
210+
.weak_points
211+
.iter()
212+
.map(|wp| {
213+
let loc_str = wp.location.as_deref().unwrap_or("unknown");
214+
let (path, line) = parse_location(loc_str);
215+
216+
SarifResult {
217+
rule_id: rule_id(&wp.category).to_string(),
218+
level: sarif_level(&wp.severity).to_string(),
219+
message: SarifMessage {
220+
text: wp.description.clone(),
221+
},
222+
locations: vec![SarifLocation {
223+
physical_location: SarifPhysicalLocation {
224+
artifact_location: SarifArtifactLocation {
225+
uri: path.to_string(),
226+
},
227+
region: line.map(|l| SarifRegion { start_line: l }),
228+
},
229+
}],
230+
}
231+
})
232+
.collect();
233+
234+
Ok(SarifLog {
235+
schema: SARIF_SCHEMA.to_string(),
236+
version: SARIF_VERSION.to_string(),
237+
runs: vec![SarifRun {
238+
tool: SarifTool {
239+
driver: SarifToolComponent {
240+
name: "panic-attack".to_string(),
241+
version: env!("CARGO_PKG_VERSION").to_string(),
242+
information_uri: "https://github.com/hyperpolymath/panic-attacker".to_string(),
243+
rules,
244+
},
245+
},
246+
results,
247+
}],
248+
})
249+
}
250+
251+
/// Serialize a SARIF log to JSON string
252+
pub fn to_sarif_json(report: &AssailReport) -> Result<String> {
253+
let log = to_sarif(report)?;
254+
let json = serde_json::to_string_pretty(&log)?;
255+
Ok(json)
256+
}

0 commit comments

Comments
 (0)