Files
yvgude--lean-ctx/rust/tests/sensitivity_floor.rs
T
wehub-resource-sync 26382a7ac6
CI / Clippy (push) Failing after 15m13s
CI / Test (ubuntu-latest) (push) Failing after 16m1s
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Build (no embeddings / no ORT) (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Cookbook (Node) (push) Has been cancelled
CI / Pi Extension (Node) (push) Has been cancelled
CI / Rust SDK (lean-ctx-client) (push) Has been cancelled
CI / Embed SDK (lean-ctx-sdk) (push) Has been cancelled
CI / Python SDK (leanctx) (push) Has been cancelled
CI / Hermes Plugin (Python) (push) Has been cancelled
CI / SDK Conformance Matrix (push) Has been cancelled
CI / Coverage (push) Has been cancelled
CI / cargo-deny (push) Has been cancelled
CI / Adversarial Safety (push) Has been cancelled
CI / Benchmarks (push) Has been cancelled
CI / Output-Quality Gate (eval A/B) (push) Has been cancelled
CI / Documentation (push) Has been cancelled
CI / CI Green (push) Has been cancelled
JetBrains Plugin / Actionlint (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (rust) (push) Has been cancelled
JetBrains Plugin / Validation (push) Has been cancelled
JetBrains Plugin / Build (push) Has been cancelled
JetBrains Plugin / Test (push) Has been cancelled
Security Check / Security Scan (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:35:30 +08:00

130 lines
4.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Integration tests for the per-item sensitivity model + policy floor (#212).
//!
//! Exercises the public `core::sensitivity` API end-to-end: classification of
//! real secret/PII vectors, the floor × action matrix, and level ordering.
//! All deterministic — no env, no config files, no global state.
use lean_ctx::core::sensitivity::{
Enforced, FloorAction, SensitivityConfig, SensitivityLevel, classify, classify_content,
enforce_text,
};
use std::path::Path;
fn cfg(enabled: bool, floor: SensitivityLevel, action: FloorAction) -> SensitivityConfig {
SensitivityConfig {
enabled,
policy_floor: floor,
action,
}
}
#[test]
fn disabled_is_a_full_noop() {
let cfg = SensitivityConfig::default(); // enabled == false
let secret = "aws AKIAIOSFODNN7EXAMPLE here".to_string();
assert_eq!(
enforce_text(secret.clone(), None, &cfg),
Enforced::Pass(secret)
);
}
#[test]
fn secret_redacted_keeps_surrounding_text() {
let c = cfg(true, SensitivityLevel::Secret, FloorAction::Redact);
match enforce_text("before AKIAIOSFODNN7EXAMPLE after".to_string(), None, &c) {
Enforced::Redacted { text, level } => {
assert_eq!(level, SensitivityLevel::Secret);
assert!(text.contains("before") && text.contains("after"));
assert!(!text.contains("AKIAIOSFODNN7EXAMPLE"));
}
other => panic!("expected Redacted, got {other:?}"),
}
}
#[test]
fn secret_dropped_when_action_is_drop() {
let c = cfg(true, SensitivityLevel::Secret, FloorAction::Drop);
match enforce_text(
"token ghp_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".to_string(),
None,
&c,
) {
Enforced::Dropped { level, notice } => {
assert_eq!(level, SensitivityLevel::Secret);
assert!(notice.contains("withheld"));
}
other => panic!("expected Dropped, got {other:?}"),
}
}
#[test]
fn confidential_pii_blocked_at_confidential_floor() {
let c = cfg(true, SensitivityLevel::Confidential, FloorAction::Redact);
// 4111 1111 1111 1111 is the canonical Luhn-valid Visa test number.
match enforce_text(
"charge card 4111 1111 1111 1111 today".to_string(),
None,
&c,
) {
Enforced::Redacted { text, level } => {
assert_eq!(level, SensitivityLevel::Confidential);
assert!(text.contains("[REDACTED:card]"));
assert!(text.contains("charge") && text.contains("today"));
}
other => panic!("expected Redacted, got {other:?}"),
}
}
#[test]
fn confidential_passes_when_floor_is_secret() {
// A card number is Confidential, which is BELOW a Secret floor → untouched.
let c = cfg(true, SensitivityLevel::Secret, FloorAction::Drop);
let text = "card 4111 1111 1111 1111".to_string();
assert_eq!(enforce_text(text.clone(), None, &c), Enforced::Pass(text));
}
#[test]
fn benign_text_always_passes_when_enabled() {
let c = cfg(true, SensitivityLevel::Confidential, FloorAction::Drop);
let text = "deployment finished in 12 seconds, 0 errors".to_string();
assert_eq!(enforce_text(text.clone(), None, &c), Enforced::Pass(text));
}
#[test]
fn path_hint_raises_level_even_for_benign_content() {
let c = cfg(true, SensitivityLevel::Secret, FloorAction::Drop);
// Benign body but a secret-like path → Secret → dropped.
let out = enforce_text(
"PORT=8080\n".to_string(),
Some(Path::new("/srv/app/.env")),
&c,
);
assert!(matches!(out, Enforced::Dropped { .. }));
}
#[test]
fn classification_vectors_are_precise() {
assert_eq!(
classify_content("nothing sensitive here, just text"),
SensitivityLevel::Public
);
// Non-Luhn 16-digit number must NOT be flagged.
assert_eq!(
classify_content("invoice 1234567890123456"),
SensitivityLevel::Public
);
// Valid IBAN (mod-97) → Confidential.
assert_eq!(
classify_content("iban DE89370400440532013000"),
SensitivityLevel::Confidential
);
// Secret beats path-public.
assert_eq!(
classify(
Some(Path::new("src/main.rs")),
"key = ghp_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
),
SensitivityLevel::Secret
);
}