fix(sql_safety): 修复 CTE 兄弟片段边界误判
- CTE 内 DELETE/UPDATE 离开当前片段后停止查找 WHERE/LIMIT - 补充 sibling CTE WHERE/LIMIT 不应降低风险等级的回归测试
This commit is contained in:
parent
99e7997620
commit
81a6dce5ac
|
|
@ -163,13 +163,24 @@ fn has_unfiltered_destructive_write(sql: &str) -> bool {
|
|||
return false;
|
||||
}
|
||||
|
||||
!statement[index + 1..]
|
||||
.iter()
|
||||
.any(|boundary| boundary.depth == token.depth && matches!(boundary.text.as_str(), "WHERE" | "LIMIT"))
|
||||
!has_same_fragment_boundary(&statement, index, token.depth)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn has_same_fragment_boundary(statement: &[SqlToken], destructive_write_index: usize, depth: usize) -> bool {
|
||||
for boundary in &statement[destructive_write_index + 1..] {
|
||||
if boundary.depth < depth {
|
||||
break;
|
||||
}
|
||||
if boundary.depth == depth && matches!(boundary.text.as_str(), "WHERE" | "LIMIT") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn executable_tokens(sql: &str) -> Vec<String> {
|
||||
executable_statements(sql).into_iter().flatten().collect()
|
||||
}
|
||||
|
|
@ -443,6 +454,26 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cte_destructive_writes_do_not_use_sibling_cte_boundaries() {
|
||||
assert_eq!(
|
||||
risk_for(
|
||||
"WITH deleted AS (DELETE FROM users RETURNING id), scoped AS (SELECT id FROM audit WHERE id = 1) SELECT * FROM scoped",
|
||||
RiskContext::new("dev")
|
||||
)
|
||||
.risk_level,
|
||||
RiskLevel::Critical
|
||||
);
|
||||
assert_eq!(
|
||||
risk_for(
|
||||
"WITH updated AS (UPDATE users SET active = false RETURNING id), scoped AS (SELECT id FROM audit LIMIT 1) SELECT * FROM scoped",
|
||||
RiskContext::new("dev")
|
||||
)
|
||||
.risk_level,
|
||||
RiskLevel::Critical
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn postgresql_dollar_quotes_do_not_contribute_tokens() {
|
||||
assert_eq!(classify_sql("SELECT $$ DELETE FROM users $$"), OperationClass::Read);
|
||||
|
|
|
|||
Loading…
Reference in New Issue