use fraiseql_core::tenancy::{TenantContext, where_clause_parameterized, where_clause_postgresql};
const MALICIOUS_TENANT_IDS: &[&str] = &[
"'; DROP TABLE users; --",
"1 OR 1=1",
"1; SELECT * FROM secrets",
"tenant' UNION SELECT password FROM admins --",
"admin'--",
"0'; DELETE FROM roles; --",
];
#[test]
fn postgresql_where_clause_never_contains_raw_tenant_id() {
for &tenant_id in MALICIOUS_TENANT_IDS {
let ctx = TenantContext::new(tenant_id);
let sql = ctx.where_clause_postgresql(1);
assert!(
!sql.contains(tenant_id),
"AA1 regression: tenant_id `{tenant_id}` appeared raw in postgresql WHERE clause: `{sql}`"
);
assert_eq!(
sql, "tenant_id = $1",
"AA1 regression: postgresql WHERE clause must be `tenant_id = $1`, got: `{sql}`"
);
}
}
#[test]
fn parameterized_where_clause_never_contains_raw_tenant_id() {
for &tenant_id in MALICIOUS_TENANT_IDS {
let ctx = TenantContext::new(tenant_id);
let sql = ctx.where_clause_parameterized();
assert!(
!sql.contains(tenant_id),
"AA1 regression: tenant_id `{tenant_id}` appeared raw in parameterized WHERE clause: `{sql}`"
);
assert_eq!(
sql, "tenant_id = ?",
"AA1 regression: parameterized WHERE clause must be `tenant_id = ?`, got: `{sql}`"
);
}
}
#[test]
fn module_level_where_clause_postgresql_returns_placeholder() {
let sql = where_clause_postgresql(1);
assert_eq!(sql, "tenant_id = $1");
let sql2 = where_clause_postgresql(5);
assert_eq!(sql2, "tenant_id = $5");
}
#[test]
fn module_level_where_clause_parameterized_returns_question_mark() {
let sql = where_clause_parameterized();
assert_eq!(sql, "tenant_id = ?");
}
#[test]
#[should_panic(expected = "unsafe for SQL interpolation")]
fn where_clause_panics_for_sql_injection_payload() {
let ctx = TenantContext::new("'; DROP TABLE users; --");
let _ = ctx.where_clause(); }
#[test]
#[should_panic(expected = "unsafe for SQL interpolation")]
fn where_clause_panics_for_space_in_tenant_id() {
let ctx = TenantContext::new("tenant id with spaces");
let _ = ctx.where_clause(); }
#[test]
fn where_clause_accepts_safe_tenant_ids() {
let safe_ids = ["acme-corp", "company_123", "tenant.prod", "a1b2c3"];
for &id in &safe_ids {
let ctx = TenantContext::new(id);
let sql = ctx.where_clause();
assert!(
sql.contains(id),
"Safe tenant_id `{id}` should appear in the WHERE clause; got: `{sql}`"
);
}
}