use graph_d::{Graph, GraphError};
use serde_json::json;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::Write as FmtWrite;
const SENSITIVE_VALUES: &[&str] = &[
"super_secret_password_123",
"sk-api-key-12345678",
"4111111111111111", "123-45-6789", "john.doe@secret.com",
"Bearer eyJhbGciOiJIUzI1NiIs", "AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCY", ];
const PROPERTY_KEYS: &[&str] = &[
"password",
"api_key",
"credit_card",
"ssn",
"email",
"token",
"aws_access_key",
"aws_secret_key",
];
fn contains_sensitive_data(text: &str) -> Option<&'static str> {
SENSITIVE_VALUES
.iter()
.find(|&&sensitive| text.contains(sensitive))
.copied()
}
fn create_sensitive_node(graph: &mut Graph) -> u64 {
let mut props = HashMap::new();
for (key, value) in PROPERTY_KEYS.iter().zip(SENSITIVE_VALUES.iter()) {
props.insert(key.to_string(), json!(value));
}
graph
.create_node(props)
.expect("Failed to create test node")
}
#[test]
fn test_graph_error_display_no_leakage() {
let errors = vec![
GraphError::Storage("Storage error with context".into()),
GraphError::NotFound("Node 123 not found".into()),
GraphError::Invalid("Invalid operation".into()),
GraphError::Serialization("JSON parse error".into()),
GraphError::Transaction("Transaction failed".into()),
GraphError::Concurrency("Lock conflict".into()),
GraphError::Io("File not found".into()),
GraphError::Memory("Pool exhausted".into()),
GraphError::Query("Query syntax error".into()),
GraphError::Index("Index lookup failed".into()),
GraphError::Config("Invalid config".into()),
GraphError::Network("Connection refused".into()),
];
for error in errors {
let display = format!("{error}");
let debug = format!("{error:?}");
if let Some(leaked) = contains_sensitive_data(&display) {
panic!("S3 VIOLATION: GraphError Display contains sensitive value: {leaked}");
}
if let Some(leaked) = contains_sensitive_data(&debug) {
panic!("S3 VIOLATION: GraphError Debug contains sensitive value: {leaked}");
}
}
}
#[test]
fn test_node_not_found_error_no_value_leakage() {
let mut graph = Graph::new().expect("Failed to create graph");
let _node_id = create_sensitive_node(&mut graph);
let result = graph.get_node(99999);
let result_str = format!("{result:?}");
if let Some(leaked) = contains_sensitive_data(&result_str) {
panic!("S3 VIOLATION: get_node result contains sensitive value: {leaked}");
}
}
#[test]
fn test_relationship_creation_error_no_value_leakage() {
let mut graph = Graph::new().expect("Failed to create graph");
let node1 = create_sensitive_node(&mut graph);
let mut rel_props = HashMap::new();
rel_props.insert(
"secret_token".to_string(),
json!("Bearer eyJhbGciOiJIUzI1NiIs"),
);
rel_props.insert(
"connection_string".to_string(),
json!("postgres://user:super_secret_password_123@host/db"),
);
let result = graph.create_relationship(node1, 99999, "SECRET_REL".to_string(), rel_props);
if let Err(e) = result {
let error_display = format!("{e}");
let error_debug = format!("{e:?}");
if let Some(leaked) = contains_sensitive_data(&error_display) {
panic!(
"S3 VIOLATION: create_relationship error Display contains sensitive value: {leaked}"
);
}
if let Some(leaked) = contains_sensitive_data(&error_debug) {
panic!(
"S3 VIOLATION: create_relationship error Debug contains sensitive value: {leaked}"
);
}
}
}
#[test]
fn test_relationship_not_found_no_value_leakage() {
let graph = Graph::new().expect("Failed to create graph");
let result = graph.get_relationship(99999);
let result_str = format!("{result:?}");
if let Some(leaked) = contains_sensitive_data(&result_str) {
panic!("S3 VIOLATION: get_relationship result contains sensitive value: {leaked}");
}
}
#[test]
fn test_transaction_error_no_value_leakage() {
let mut graph = Graph::new().expect("Failed to create graph");
let _node_id = create_sensitive_node(&mut graph);
let result = graph.create_relationship(9999, 9998, "INVALID".to_string(), HashMap::new());
if let Err(e) = result {
let error_display = format!("{e}");
let error_debug = format!("{e:?}");
if let Some(leaked) = contains_sensitive_data(&error_display) {
panic!("S3 VIOLATION: Transaction error Display contains sensitive value: {leaked}");
}
if let Some(leaked) = contains_sensitive_data(&error_debug) {
panic!("S3 VIOLATION: Transaction error Debug contains sensitive value: {leaked}");
}
}
}
#[test]
fn test_serialization_error_no_value_leakage() {
let error = GraphError::Serialization("Invalid JSON in property 'password'".into());
let display = format!("{error}");
let debug = format!("{error:?}");
assert!(
!display.contains("super_secret"),
"S3 VIOLATION: Serialization error contains value"
);
assert!(
!debug.contains("super_secret"),
"S3 VIOLATION: Serialization error debug contains value"
);
}
#[test]
fn test_query_result_no_value_leakage_on_error() {
use graph_d::gql::Gql;
let graph = RefCell::new(Graph::new().expect("Failed to create graph"));
let gql = Gql::new(&graph);
let result = gql.execute("INVALID QUERY SYNTAX");
if let Err(e) = result {
let error_str = format!("{e:?}");
if let Some(leaked) = contains_sensitive_data(&error_str) {
panic!("S3 VIOLATION: GQL error contains sensitive value: {leaked}");
}
}
}
#[test]
fn test_bulk_operation_errors_no_leakage() {
let mut graph = Graph::new().expect("Failed to create graph");
for i in 0..10 {
let mut props = HashMap::new();
props.insert(
"password".to_string(),
json!(format!("secret_{}_super_secret_password_123", i)),
);
props.insert(
"api_key".to_string(),
json!(format!("key_{}_sk-api-key-12345678", i)),
);
graph.create_node(props).expect("Failed to create node");
}
let mut all_output = String::new();
for id in 9990..10000 {
let result = graph.get_node(id);
writeln!(all_output, "{result:?}").unwrap();
}
if let Some(leaked) = contains_sensitive_data(&all_output) {
panic!("S3 VIOLATION: Bulk operation output contains sensitive value: {leaked}");
}
}
#[test]
fn test_property_keys_allowed_in_errors() {
let error = GraphError::NotFound("Property 'password' not found on node 123".into());
let display = format!("{error}");
assert!(
display.contains("password"),
"Property keys should be allowed in error messages"
);
assert!(
!display.contains("super_secret_password_123"),
"Property values must never appear in error messages"
);
}
#[test]
fn test_index_error_no_value_leakage() {
let error = GraphError::Index("Index lookup failed for property 'email'".into());
let display = format!("{error}");
if let Some(leaked) = contains_sensitive_data(&display) {
panic!("S3 VIOLATION: Index error contains sensitive value: {leaked}");
}
}
#[test]
fn test_concurrent_error_no_value_leakage() {
let error = GraphError::Concurrency("Lock conflict on node 123 property 'api_key'".into());
let display = format!("{error}");
if let Some(leaked) = contains_sensitive_data(&display) {
panic!("S3 VIOLATION: Concurrency error contains sensitive value: {leaked}");
}
}
#[test]
fn test_gql_error_variants_no_leakage() {
use graph_d::gql::GqlError;
let errors: Vec<GqlError> = vec![
GqlError::LexError {
message: "Unexpected character".into(),
position: 10,
},
GqlError::ParseError {
message: "Expected identifier".into(),
token: Some("123".into()),
position: 5,
},
GqlError::SemanticError {
message: "Variable 'x' not defined".into(),
},
GqlError::ExecutionError {
message: "Node not found".into(),
},
GqlError::TypeError {
expected: "String".into(),
found: "Integer".into(),
},
GqlError::VariableNotFound {
name: "myVar".into(),
},
GqlError::LabelNotFound {
name: "Person".into(),
},
GqlError::PropertyNotFound {
name: "email".into(), },
];
for error in errors {
let debug = format!("{error:?}");
if let Some(leaked) = contains_sensitive_data(&debug) {
panic!("S3 VIOLATION: GqlError Debug contains sensitive value: {leaked}");
}
}
}
#[test]
fn test_error_chain_no_leakage() {
let inner = GraphError::NotFound("Property 'password' missing".into());
let outer = GraphError::Transaction(format!("Operation failed: {inner}"));
let display = format!("{outer}");
let debug = format!("{outer:?}");
if let Some(leaked) = contains_sensitive_data(&display) {
panic!("S3 VIOLATION: Chained error Display contains sensitive value: {leaked}");
}
if let Some(leaked) = contains_sensitive_data(&debug) {
panic!("S3 VIOLATION: Chained error Debug contains sensitive value: {leaked}");
}
}
#[test]
fn test_comprehensive_error_scan() {
let mut graph = Graph::new().expect("Failed to create graph");
let mut props = HashMap::new();
for (key, value) in PROPERTY_KEYS.iter().zip(SENSITIVE_VALUES.iter()) {
props.insert(key.to_string(), json!(value));
}
let node_id = graph
.create_node(props.clone())
.expect("Failed to create node");
let node2 = graph.create_node(HashMap::new()).expect("create");
let rel_id = graph
.create_relationship(node_id, node2, "HAS_DATA".to_string(), props)
.expect("create rel");
let mut all_outputs = String::new();
writeln!(all_outputs, "{:?}", graph.get_node(node_id)).unwrap();
writeln!(all_outputs, "{:?}", graph.get_relationship(rel_id)).unwrap();
writeln!(all_outputs, "{:?}", graph.get_node(99999)).unwrap();
writeln!(all_outputs, "{:?}", graph.get_relationship(99999)).unwrap();
let node_result = graph.get_node(node_id).expect("should work");
if let Some(node) = node_result {
let node_debug = format!("{node:?}");
if let Some(leaked) = contains_sensitive_data(&node_debug) {
panic!(
"S3 VIOLATION: Node Debug representation leaks sensitive value: {leaked}\n\
Consider implementing a custom Debug that omits property values."
);
}
}
let rel_result = graph.get_relationship(rel_id).expect("should work");
if let Some(rel) = rel_result {
let rel_debug = format!("{rel:?}");
if let Some(leaked) = contains_sensitive_data(&rel_debug) {
panic!(
"S3 VIOLATION: Relationship Debug representation leaks sensitive value: {leaked}\n\
Consider implementing a custom Debug that omits property values."
);
}
}
}
#[test]
fn test_node_debug_shows_keys_not_values() {
let mut graph = Graph::new().expect("Failed to create graph");
let mut props = HashMap::new();
props.insert("password".to_string(), json!("super_secret_password_123"));
props.insert("name".to_string(), json!("John Doe"));
let node_id = graph.create_node(props).expect("create");
let node = graph.get_node(node_id).expect("get").expect("exists");
let debug_str = format!("{node:?}");
if debug_str.contains("super_secret_password_123") {
eprintln!("DESIGN NOTE: Node Debug currently shows property values.");
eprintln!("For production use, consider implementing custom Debug that redacts values.");
}
}
#[test]
fn test_relationship_debug_shows_keys_not_values() {
let mut graph = Graph::new().expect("Failed to create graph");
let node1 = graph.create_node(HashMap::new()).expect("create");
let node2 = graph.create_node(HashMap::new()).expect("create");
let mut props = HashMap::new();
props.insert("token".to_string(), json!("sk-api-key-12345678"));
let rel_id = graph
.create_relationship(node1, node2, "HAS".to_string(), props)
.expect("create");
let rel = graph
.get_relationship(rel_id)
.expect("get")
.expect("exists");
let debug_str = format!("{rel:?}");
if debug_str.contains("sk-api-key-12345678") {
eprintln!("DESIGN NOTE: Relationship Debug currently shows property values.");
eprintln!("For production use, consider implementing custom Debug that redacts values.");
}
}
#[test]
fn test_gql_query_error_no_value_leakage() {
use graph_d::gql::Gql;
let mut graph = Graph::new().expect("Failed to create graph");
let mut props = HashMap::new();
props.insert("secret".to_string(), json!("super_secret_password_123"));
graph.create_node(props).expect("create");
let graph = RefCell::new(graph);
let gql = Gql::new(&graph);
let queries = [
"MATCH (n) WHERE n.nonexistent = 'value' RETURN n",
"MATCH (n)-[r:NONEXISTENT]->(m) RETURN r",
"INVALID SYNTAX HERE",
];
for query in queries {
let result = gql.execute(query);
let result_str = format!("{result:?}");
if let Some(leaked) = contains_sensitive_data(&result_str) {
panic!("S3 VIOLATION: GQL query '{query}' result contains sensitive value: {leaked}");
}
}
}
#[test]
fn test_memory_error_no_value_leakage() {
let error = GraphError::Memory(
"Pool exhausted after processing node with properties [password, email]".into(),
);
let display = format!("{error}");
assert!(display.contains("password"));
assert!(display.contains("email"));
if let Some(leaked) = contains_sensitive_data(&display) {
panic!("S3 VIOLATION: Memory error contains sensitive value: {leaked}");
}
}
#[test]
fn test_storage_error_no_value_leakage() {
let error =
GraphError::Storage("Failed to persist node 123 properties [api_key, token]".into());
let display = format!("{error}");
assert!(display.contains("api_key"));
assert!(display.contains("token"));
if let Some(leaked) = contains_sensitive_data(&display) {
panic!("S3 VIOLATION: Storage error contains sensitive value: {leaked}");
}
}