Skip to content
Closed
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
15 changes: 10 additions & 5 deletions .github/workflows/actions.lock
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ workflows:
- 'actions/upload-pages-artifact@v5.0.0'
- 'haskell-actions/setup@v2.12.1'
'.github/workflows/chapel-ci.yml':
- 'actions/cache@v6.1.0'
- 'actions/checkout@v7.0.1'
- 'actions/download-artifact@v8.0.2'
- 'actions/upload-artifact@v7.0.2'
- 'dtolnay/rust-toolchain@v1'
- 'actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9'
- 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1'
- 'actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333'
- 'actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9'
- 'dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8'
'.github/workflows/codeql.yml':
- 'actions/checkout@v7.0.1'
- 'github/codeql-action@v4.39.0'
Expand Down Expand Up @@ -114,6 +114,11 @@ dependencies:
commit: 'sha1-368f82528645a54fb793d4d04e342629a3f51346'
owner_id: 44036562
repo_id: 438112499
'actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333':
ref: 'v8.0.2'
commit: 'sha1-9000827ccba6bdab643e8b6fd33ac0654aef8333'
owner_id: 44036562
repo_id: 192626254
'actions/download-artifact@v8.0.2':
ref: 'v8.0.2'
commit: 'sha1-9000827ccba6bdab643e8b6fd33ac0654aef8333'
Expand Down
34 changes: 17 additions & 17 deletions .github/workflows/chapel-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ jobs:
outputs:
relevant: ${{ steps.f.outputs.relevant }}
steps:
- uses: actions/checkout@v7.0.1
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 2
- id: f
Expand Down Expand Up @@ -105,7 +105,7 @@ jobs:
runs-on: ubuntu-22.04
timeout-minutes: 45
steps:
- uses: actions/checkout@v7.0.1
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Chapel ${{ env.CHAPEL_VERSION }}
run: |
set -euo pipefail
Expand All @@ -128,7 +128,7 @@ jobs:
runs-on: ubuntu-22.04
timeout-minutes: 45
steps:
- uses: actions/checkout@v7.0.1
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Chapel ${{ env.CHAPEL_VERSION }}
run: |
set -euo pipefail
Expand All @@ -144,7 +144,7 @@ jobs:
chpl src/MassPanic.chpl src/Protocol.chpl src/Imaging.chpl src/Temporal.chpl -o mass-panic
chpl smoke/two_repo_smoke.chpl src/Protocol.chpl src/Imaging.chpl -o smoke/two_repo_smoke
- name: Upload Chapel artefacts
uses: actions/upload-artifact@v7.0.2
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: chapel-binaries
path: |
Expand All @@ -159,7 +159,7 @@ jobs:
runs-on: ubuntu-22.04
timeout-minutes: 45
steps:
- uses: actions/checkout@v7.0.1
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Chapel ${{ env.CHAPEL_VERSION }}
run: |
set -euo pipefail
Expand All @@ -168,7 +168,7 @@ jobs:
curl -fsSL --retry 3 -o /tmp/chapel.deb "${{ env.CHAPEL_DEB_URL }}"
sudo apt-get install -y /tmp/chapel.deb
- name: Download Chapel artefacts
uses: actions/download-artifact@v8.0.2
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: chapel-binaries
path: chapel/
Expand All @@ -184,7 +184,7 @@ jobs:
runs-on: ubuntu-22.04
timeout-minutes: 45
steps:
- uses: actions/checkout@v7.0.1
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Chapel ${{ env.CHAPEL_VERSION }}
run: |
set -euo pipefail
Expand All @@ -193,7 +193,7 @@ jobs:
curl -fsSL --retry 3 -o /tmp/chapel.deb "${{ env.CHAPEL_DEB_URL }}"
sudo apt-get install -y /tmp/chapel.deb
- name: Download Chapel artefacts
uses: actions/download-artifact@v8.0.2
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: chapel-binaries
path: chapel/
Expand Down Expand Up @@ -221,11 +221,11 @@ jobs:
runs-on: ubuntu-22.04
timeout-minutes: 45
steps:
- uses: actions/checkout@v7.0.1
- uses: dtolnay/rust-toolchain@v1
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # v1
with:
toolchain: stable
- uses: actions/cache@v6.1.0
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.cargo/registry
Expand All @@ -244,11 +244,11 @@ jobs:
runs-on: ubuntu-22.04
timeout-minutes: 45
steps:
- uses: actions/checkout@v7.0.1
- uses: dtolnay/rust-toolchain@v1
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # v1
with:
toolchain: stable
- uses: actions/cache@v6.1.0
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.cargo/registry
Expand All @@ -263,7 +263,7 @@ jobs:
curl -fsSL --retry 3 -o /tmp/chapel.deb "${{ env.CHAPEL_DEB_URL }}"
sudo apt-get install -y /tmp/chapel.deb
- name: Download Chapel artefacts
uses: actions/download-artifact@v8.0.2
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: chapel-binaries
path: chapel/
Expand All @@ -283,15 +283,15 @@ jobs:
env:
CHPL_HOME: /opt/chapel-multilocale
steps:
- uses: actions/checkout@v7.0.1
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

# Cache the entire built-from-source Chapel tree. Key is stable across
# PRs as long as the version, conduit, launcher and cache-gen marker
# don't change. Cold build is ~30-40 min on a 2-core runner; warm
# restore is ~30s.
- name: Cache multilocale Chapel ($CHPL_HOME)
id: chapel-cache
uses: actions/cache@v6.1.0
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ env.CHAPEL_MULTILOCALE_HOME }}
key: ${{ runner.os }}-chapel-multilocale-${{ env.CHAPEL_VERSION }}-gasnet-smp-${{ env.CHAPEL_MULTILOCALE_CACHE_GEN }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
scorecard:
uses: hyperpolymath/standards/.github/workflows/scorecard-reusable.yml@333b06a01db1874ffedb2de1bfabb195640927c7
permissions:
actions: read
actions: read # required by scorecard-reusable.yml to read workflow metadata
contents: read
security-events: write
id-token: write
11 changes: 9 additions & 2 deletions src/aggregate/proven_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,10 @@ impl ReportTier {
impl FromStr for ProvenTestsReport {
type Err = anyhow::Error;

/// Parse a run report from JSON text.
/// Parse a run report from JSON text, requiring schema version 1.
///
/// # Errors
/// Returns JSON deserialisation errors or an error if `schema_version` is not 1.
fn from_str(text: &str) -> Result<Self> {
let report: ProvenTestsReport =
serde_json::from_str(text).context("parsing proven-tests run report")?;
Expand All @@ -134,7 +137,11 @@ impl FromStr for ProvenTestsReport {
}

impl ProvenTestsReport {
/// Parse a run report from a file path.
/// Read a UTF-8 JSON run report from `path` and parse it as schema version 1.
///
/// # Errors
/// Returns file read errors (including invalid UTF-8), JSON deserialisation
/// errors or an error if `schema_version` is not 1.
pub fn from_path(path: &Path) -> Result<Self> {
let text =
std::fs::read_to_string(path).with_context(|| format!("reading {}", path.display()))?;
Expand Down
18 changes: 8 additions & 10 deletions src/assail/analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,18 +263,16 @@ static RE_HARDCODED_SECRET: OnceLock<Regex> = OnceLock::new();
/// Does not handle OCaml `(* *)` or Forth `\` — edge cases for later.
static RE_TODO_COMMENT: OnceLock<Regex> = OnceLock::new();

/// Heuristic: is the `malloc` on `lines[idx]` NULL-checked within a short
/// window of following lines?
/// Heuristically recognise a null check for the allocation on `lines[idx]`.
///
/// An "UncheckedAllocation" finding should only fire when the result is *not*
/// guarded. This recognises the common C idioms (`if (p == NULL)`,
/// `if (!p)`, `if (p == nullptr)`) on the lvalue assigned from `malloc`.
/// Searches the allocation line and up to six following lines for `NULL` or
/// `nullptr` alongside the assigned identifier, or an `if` line containing `!`
/// immediately followed by that identifier. If no identifier can be extracted,
/// any `NULL` or `nullptr` substring in the window suffices. Substring matches
/// do not establish that the allocation is guarded.
///
/// Deliberately conservative to avoid false negatives in a security scanner:
/// a site is treated as checked only on a clear NULL-guard signal that
/// references the allocated variable (or, when no lvalue can be parsed, any
/// NULL/nullptr token in the window). Genuinely-unchecked allocations — the
/// real finding — are still reported.
/// # Panics
/// Panics if the zero-based `idx` is outside `lines`.
fn c_alloc_result_is_null_checked(lines: &[&str], idx: usize) -> bool {
let line = lines[idx];
// Parse the lvalue assigned from malloc: the last identifier left of '='.
Expand Down
3 changes: 2 additions & 1 deletion src/assail/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,8 @@ pub fn apply_suppression(report: &mut AssailReport) {
/// 3. UnsafeFFI finding where the file's [[ffi_kind]] is
/// `BuildSystem` or `TestMock` (audit-accepted by default).
///
/// JIT context handling remains inline in analyzer.rs:1117..1129.
/// JIT context handling remains in the transmute classification block of
/// `Analyzer::analyze_rust` in `analyzer.rs`.
///
/// Fills missing `wp.test_context` for findings with a known file path so
/// downstream audit consumers can render it. Returns the number of
Expand Down
6 changes: 6 additions & 0 deletions src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ const HYPATIA_ENV: &str = "HYPATIA_API_KEY";
const GITBOT_FLEET_ENV: &str = "GITBOT_FLEET_ENDPOINT";
const PANICBOT_DIRECTIVE_PATH: &str = ".machine_readable/bot_directives/panicbot.scm";

/// Print readiness checks using `manifest`, paths relative to the current
/// working directory and the configured integration environment variables.
///
/// # Errors
/// Returns an error after printing all results if any check has error severity.
/// Warnings alone do not cause failure.
pub fn run_self_diagnostics(manifest: &Manifest) -> Result<()> {
println!("panic-attack self-diagnostics");

Expand Down
51 changes: 24 additions & 27 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,34 @@
//! concurrency) and detecting bug signatures using logic programming techniques inspired by
//! Mozart/Oz and Datalog.

mod a2ml;
mod abduct;
mod adjudicate;
mod aggregate;
mod ambush;
mod amuck;
mod assail;
mod assay;
mod assemblyline;
mod attack;
mod attestation;
mod axial;
use panic_attack::a2ml;
use panic_attack::abduct;
use panic_attack::adjudicate;
use panic_attack::aggregate;
use panic_attack::ambush;
use panic_attack::amuck;
use panic_attack::assail;
use panic_attack::assay;
use panic_attack::assemblyline;
use panic_attack::attack;
use panic_attack::attestation;
use panic_attack::axial;
#[cfg(feature = "http")]
mod bridge;
mod campaign;
use panic_attack::bridge;
use panic_attack::campaign;
mod diagnostics;
mod groove;
mod i18n;
mod kanren;
use panic_attack::i18n;
mod kin;
mod mass_panic;
mod notify;
mod panll;
mod query;
mod report;
mod signatures;
mod storage;
mod sweep_tracker;
mod test_context;
mod types;
use panic_attack::mass_panic;
use panic_attack::notify;
use panic_attack::panll;
use panic_attack::query;
use panic_attack::report;
use panic_attack::signatures;
use panic_attack::storage;
use panic_attack::sweep_tracker;
use panic_attack::types;

extern crate walkdir;

Expand All @@ -57,7 +55,6 @@ use anyhow::{anyhow, Context, Result};
use clap::{CommandFactory, Parser, Subcommand};
use clap_complete::{generate, Shell};
use clap_complete_nushell::Nushell;
use panic_attack::{comment_marker, ffi_kind};
use std::collections::HashMap;
use std::fs::{self, File};
use std::io::{self, Read, Write};
Expand Down
9 changes: 6 additions & 3 deletions src/report/gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,11 @@ impl ReportGui {
}

impl App for ReportGui {
/// Render the file loader, navigation and selected report tab.
/// A load-button click or Enter after the path field loses focus attempts
/// to read the specified JSON file, updating the viewer or its status message.
fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut Frame) {
egui::Panel::top("header").show_inside(ui, |ui| {
egui::Panel::top("header").show(ui, |ui| {
ui.horizontal(|ui| {
ui.heading("panic-attack report");
ui.separator();
Expand All @@ -138,7 +141,7 @@ impl App for ReportGui {
});
});

egui::Panel::left("nav").show_inside(ui, |ui| {
egui::Panel::left("nav").show(ui, |ui| {
ui.selectable_value(&mut self.tab, ReportTab::Summary, "Summary");
ui.selectable_value(&mut self.tab, ReportTab::Assail, "Assail");
ui.selectable_value(&mut self.tab, ReportTab::Matrix, "Matrix");
Expand All @@ -159,7 +162,7 @@ impl App for ReportGui {
ui.selectable_value(&mut self.tab, ReportTab::Temporal, temporal_label);
});

egui::CentralPanel::default().show_inside(ui, |ui| match self.tab {
egui::CentralPanel::default().show(ui, |ui| match self.tab {
ReportTab::Summary => self.render_summary(ui),
ReportTab::Assail => self.render_assail(ui),
ReportTab::Matrix => self.render_matrix(ui),
Expand Down
19 changes: 13 additions & 6 deletions src/safe_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,24 @@ pub fn sanitize_filename(filename: &str) -> String {
}
}

#[cfg(test)]
/// Returns true if `c` is a character `sanitize_filename` would keep.
fn is_safe_char(c: char) -> bool {
c.is_ascii_alphanumeric() || SAFE_PUNCT.contains(&c)
}

#[cfg(test)]
/// Returns true if every character in `s` is safe. Used by the
/// idempotence + non-empty proptest invariants.
fn all_chars_safe(s: &str) -> bool {
s.chars().all(is_safe_char)
}

#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;

/// Property-test predicate kept separate from the sanitizer implementation.
fn all_chars_safe(s: &str) -> bool {
s.chars()
.all(|c| c.is_ascii_alphanumeric() || SAFE_PUNCT.contains(&c))
}

// ─────────────────────────────────────────────────────────────────
// has_traversal — concrete cases
// ─────────────────────────────────────────────────────────────────
Expand Down
9 changes: 5 additions & 4 deletions src/scan/cargodeny.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,11 @@ pub fn run_cargo_deny(config: &CargoDenyConfig) -> Result<Vec<WeakPoint>> {
.unwrap_or(WeakPointCategory::Advisory);

let location = diagnostic.location.and_then(|loc| {
loc.file.map(|file| match loc.line {
Some(line) => format!("{}:{}", file, line),
None => file,
})
if let (Some(ref file), Some(line)) = (&loc.file, loc.line) {
Some(format!("{}:{}", file, line))
} else {
loc.file.clone()
}
});

// Parse file and line from location string
Expand Down
Loading