Single-commit clean baseline after security scrub of niche-tells, project codenames, internal jargon, and contributor-email leaks. Contents: - 100 Rust crates (_primitives/_rust/) - 37 agent manifests (_manifests/) + generated specs (_generated/) - 67 user-invocable skills (skills/) - 33 hooks (hooks/) - Composition blocks (_blocks/) - Documentation (docs/, README.md) - TS adapter packages (_ts_packages/) - Assembler (_assembler/) - Roles (_roles/) - Templates (_templates/) - Forgejo CI (.forgejo/) Author: Denis Parfionovich <info@greendragon.info> License: see LICENSE.
167 lines
5.9 KiB
Rust
167 lines
5.9 KiB
Rust
//! Scanner integration tests.
|
|
//!
|
|
//! Tests use a synthetic in-test matrix written to a temp file. We do NOT
|
|
//! echo any real SSoT pattern. Test fixtures use clearly-test-only tokens
|
|
//! (TESTONLY_* prefixes) that the SSoT matrix does not list.
|
|
//!
|
|
//! Where we cross-check the real SSoT matrix, we reference rules by `id`
|
|
//! only — never by pattern source.
|
|
|
|
use kei_leak_matrix::{scan_file, scan_string, Matrix, Scope, Severity};
|
|
use std::io::Write;
|
|
use tempfile::NamedTempFile;
|
|
|
|
/// Build a tiny test matrix on disk and return the loaded Matrix.
|
|
/// Patterns below are TEST-ONLY tokens, intentionally distinct from any
|
|
/// SSoT pattern, so this fixture cannot accidentally leak IP-bearing regex.
|
|
fn build_test_matrix() -> (NamedTempFile, Matrix) {
|
|
let toml = r#"
|
|
[[rule]]
|
|
id = "test-block-alpha"
|
|
pattern = "TESTONLY_BLOCK_ALPHA"
|
|
category = "secret"
|
|
severity = "block"
|
|
scope = ["all-writes", "public-mirror"]
|
|
rationale = "test-only block rule"
|
|
added = "2026-04-26"
|
|
|
|
[[rule]]
|
|
id = "test-warn-beta"
|
|
pattern = "TESTONLY_WARN_BETA"
|
|
category = "secret"
|
|
severity = "warn"
|
|
scope = ["public-mirror"]
|
|
rationale = "test-only warn rule"
|
|
added = "2026-04-26"
|
|
|
|
[[rule]]
|
|
id = "test-sub-gamma"
|
|
pattern = "TESTONLY_SUB_GAMMA"
|
|
substitute_with = "REDACTED"
|
|
category = "personal"
|
|
severity = "substitute"
|
|
scope = ["public-mirror"]
|
|
rationale = "test-only substitute rule"
|
|
added = "2026-04-26"
|
|
|
|
[[rule]]
|
|
id = "test-scoped-only-commit"
|
|
pattern = "TESTONLY_COMMIT_ONLY"
|
|
category = "secret"
|
|
severity = "block"
|
|
scope = ["commit-msg"]
|
|
rationale = "test-only commit-msg rule"
|
|
added = "2026-04-26"
|
|
"#;
|
|
let mut f = NamedTempFile::new().expect("tmp file");
|
|
f.write_all(toml.as_bytes()).expect("write");
|
|
f.flush().expect("flush");
|
|
let m = Matrix::load(f.path()).expect("matrix loads");
|
|
(f, m)
|
|
}
|
|
|
|
#[test]
|
|
fn loads_and_compiles_all_test_rules() {
|
|
let (_f, m) = build_test_matrix();
|
|
assert_eq!(m.rules.len(), 4);
|
|
let ids: Vec<&str> = m.rules.iter().map(|r| r.id.as_str()).collect();
|
|
assert!(ids.contains(&"test-block-alpha"));
|
|
assert!(ids.contains(&"test-warn-beta"));
|
|
assert!(ids.contains(&"test-sub-gamma"));
|
|
assert!(ids.contains(&"test-scoped-only-commit"));
|
|
}
|
|
|
|
#[test]
|
|
fn scan_string_finds_block_violation_by_id() {
|
|
let (_f, m) = build_test_matrix();
|
|
// Synthetic content that triggers the block rule.
|
|
let content = "line one\nfoo TESTONLY_BLOCK_ALPHA bar\nline three\n";
|
|
let v = scan_string(&m, content, Scope::AllWrites, None, "syn");
|
|
assert_eq!(v.len(), 1, "exactly one violation");
|
|
assert_eq!(v[0].rule_id, "test-block-alpha");
|
|
assert_eq!(v[0].line, 2);
|
|
assert_eq!(v[0].severity, "block");
|
|
}
|
|
|
|
#[test]
|
|
fn scan_string_redacts_match_to_12_chars_plus_ellipsis() {
|
|
let (_f, m) = build_test_matrix();
|
|
let content = "TESTONLY_BLOCK_ALPHA";
|
|
let v = scan_string(&m, content, Scope::AllWrites, None, "syn");
|
|
assert_eq!(v.len(), 1);
|
|
// Exactly 12 chars + ellipsis when source longer than 12.
|
|
let chars: Vec<char> = v[0].matched_redacted.chars().collect();
|
|
assert_eq!(chars.len(), 13, "12 + ellipsis");
|
|
assert_eq!(chars.last(), Some(&'…'));
|
|
}
|
|
|
|
#[test]
|
|
fn scope_filter_excludes_non_matching_scope() {
|
|
let (_f, m) = build_test_matrix();
|
|
// test-scoped-only-commit only applies to commit-msg.
|
|
let content = "x TESTONLY_COMMIT_ONLY y";
|
|
let v = scan_string(&m, content, Scope::PublicMirror, None, "syn");
|
|
assert_eq!(v.len(), 0, "commit-msg-only rule must not fire on public-mirror");
|
|
let v2 = scan_string(&m, content, Scope::CommitMsg, None, "syn");
|
|
assert_eq!(v2.len(), 1);
|
|
assert_eq!(v2[0].rule_id, "test-scoped-only-commit");
|
|
}
|
|
|
|
#[test]
|
|
fn all_writes_scope_matches_every_request() {
|
|
let (_f, m) = build_test_matrix();
|
|
// test-block-alpha has scope all-writes — matches both PublicMirror and AllWrites.
|
|
let content = "x TESTONLY_BLOCK_ALPHA y";
|
|
let v_pm = scan_string(&m, content, Scope::PublicMirror, None, "syn");
|
|
let v_aw = scan_string(&m, content, Scope::AllWrites, None, "syn");
|
|
assert_eq!(v_pm.len(), 1);
|
|
assert_eq!(v_aw.len(), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn severity_filter_narrows_to_block_only() {
|
|
let (_f, m) = build_test_matrix();
|
|
let content = "TESTONLY_BLOCK_ALPHA TESTONLY_WARN_BETA TESTONLY_SUB_GAMMA";
|
|
let v_block = scan_string(&m, content, Scope::PublicMirror, Some(Severity::Block), "syn");
|
|
assert_eq!(v_block.len(), 1);
|
|
assert_eq!(v_block[0].rule_id, "test-block-alpha");
|
|
}
|
|
|
|
#[test]
|
|
fn severity_filter_narrows_to_warn_only() {
|
|
let (_f, m) = build_test_matrix();
|
|
let content = "TESTONLY_BLOCK_ALPHA TESTONLY_WARN_BETA TESTONLY_SUB_GAMMA";
|
|
let v_warn = scan_string(&m, content, Scope::PublicMirror, Some(Severity::Warn), "syn");
|
|
assert_eq!(v_warn.len(), 1);
|
|
assert_eq!(v_warn[0].rule_id, "test-warn-beta");
|
|
}
|
|
|
|
#[test]
|
|
fn scan_file_reads_path_and_finds_violations() {
|
|
let (_f, m) = build_test_matrix();
|
|
let mut tf = NamedTempFile::new().unwrap();
|
|
writeln!(tf, "first line").unwrap();
|
|
writeln!(tf, "TESTONLY_BLOCK_ALPHA on second").unwrap();
|
|
tf.flush().unwrap();
|
|
let v = scan_file(&m, tf.path(), Scope::AllWrites, None).expect("scan ok");
|
|
assert_eq!(v.len(), 1);
|
|
assert_eq!(v[0].rule_id, "test-block-alpha");
|
|
assert_eq!(v[0].line, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn ssot_matrix_has_eleven_secret_rules() {
|
|
// Cross-check the real SSoT matrix: count secret-category rules.
|
|
// We never echo any pattern; we count by category alone.
|
|
let path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
|
.join("../../../security/leak-matrix.toml");
|
|
if !path.exists() {
|
|
// Skip if the SSoT file isn't reachable from test harness path.
|
|
eprintln!("ssot path not present, skipping: {}", path.display());
|
|
return;
|
|
}
|
|
let m = Matrix::load(&path).expect("ssot matrix loads");
|
|
let secret_count = m.rules.iter()
|
|
.filter(|r| r.category == kei_leak_matrix::Category::Secret).count();
|
|
assert_eq!(secret_count, 11, "SSoT must contain 11 secret rules");
|
|
}
|