use crate::tests::{parse_ts, parse_tsx};
#[test]
fn sink_captures_arg_idents_for_bare_identifier() {
let info = parse_ts("const userInput = getInput();\neval(userInput);");
let sink = info
.security_sinks
.iter()
.find(|s| s.callee_path == "eval")
.expect("eval sink captured");
assert!(sink.arg_idents.iter().any(|n| n == "userInput"));
}
#[test]
fn sink_captures_arg_idents_through_member_and_concat() {
let info = parse_ts("el.innerHTML = \"<b>\" + data.value;");
let sink = info
.security_sinks
.iter()
.find(|s| s.callee_path == "el.innerHTML")
.expect("innerHTML sink captured");
assert!(sink.arg_idents.iter().any(|n| n == "data"));
assert!(!sink.arg_idents.iter().any(|n| n == "value"));
}
#[test]
fn sink_captures_arg_idents_in_call_argument() {
let info = parse_ts("db.query(buildSql(userId));");
let sink = info
.security_sinks
.iter()
.find(|s| s.callee_path == "db.query")
.expect("query sink captured");
assert!(sink.arg_idents.iter().any(|n| n == "buildSql"));
assert!(sink.arg_idents.iter().any(|n| n == "userId"));
}
#[test]
fn sink_captures_arg_idents_in_tagged_template() {
let info = parse_ts("const q = sql`SELECT * FROM t WHERE id = ${id}`;");
let sink = info
.security_sinks
.iter()
.find(|s| s.callee_path == "sql")
.expect("tagged-template sink captured");
assert!(sink.arg_idents.iter().any(|n| n == "id"));
}
#[test]
fn sink_captures_arg_idents_in_jsx_attr() {
let info = parse_tsx("const C = () => <div dangerouslySetInnerHTML={markup} />;");
let sink = info
.security_sinks
.iter()
.find(|s| s.callee_path == "dangerouslySetInnerHTML")
.expect("jsx-attr sink captured");
assert!(sink.arg_idents.iter().any(|n| n == "markup"));
}
#[test]
fn direct_binding_records_object_path_as_source() {
let info = parse_ts("const id = req.query.id;");
let binding = info
.tainted_bindings
.iter()
.find(|b| b.local == "id")
.expect("tainted binding for id");
assert_eq!(binding.source_path, "req.query");
}
#[test]
fn destructure_binding_records_full_init_path_as_source() {
let info = parse_ts("const { id, name } = req.body;");
for local in ["id", "name"] {
let binding = info
.tainted_bindings
.iter()
.find(|b| b.local == local)
.unwrap_or_else(|| panic!("tainted binding for {local}"));
assert_eq!(binding.source_path, "req.body");
}
}
#[test]
fn await_init_unwraps_to_member_object_path() {
let info = parse_ts("async function h() { const body = await ctx.req.json(); }");
assert!(info.tainted_bindings.iter().all(|b| b.local != "body"));
}
#[test]
fn literal_init_records_no_source_binding() {
let info = parse_ts("const x = 1;\nconst y = \"hello\";");
assert!(info.tainted_bindings.is_empty());
}