use waf_core::{
Bytes, Config, Decision, GraphqlConfig, Normalized,
RequestContext, WafMode, WafModule,
};
use waf_detection::graphql::GraphqlModule;
fn config() -> Config {
let mut c = Config::default();
c.waf.mode = WafMode::Blocking;
c.waf.paranoia_level = 3;
c.modules.graphql = GraphqlConfig { enabled: true, block_introspection: true, ..Default::default() };
c
}
fn module() -> GraphqlModule {
let mut m = GraphqlModule::new();
m.init(&config());
m
}
fn get(path: &str, query_value: &str) -> RequestContext {
let mut ctx = RequestContext {
client_ip: "127.0.0.1".parse().unwrap(),
request_id: "t".to_string(),
timestamp: std::time::SystemTime::now(),
method: "GET".to_string(),
path: path.to_string(),
raw_path: path.to_string(),
query: None,
http_version: "HTTP/1.1".to_string(),
headers: vec![],
cookies: vec![],
body: Bytes::new(),
normalized: Normalized::default(),
score: 0,
score_contributions: vec![],
};
ctx.normalized.path = path.to_string();
ctx.normalized.query_params = vec![("query".to_string(), query_value.to_string())];
ctx
}
#[test]
fn get_json_envelope_introspection_is_blocked() {
let ctx = get("/graphql", r#"{"query":"query IntrospectionQuery {__schema {queryType {name}}}"}"#);
assert!(
matches!(module().inspect(&ctx), Decision::Block { rule_id, .. } if rule_id == "graphql-introspection"),
"envelope introspection must be blocked"
);
}
#[test]
fn get_raw_document_introspection_still_blocked() {
let ctx = get("/graphql", "{__schema{types{name}}}");
assert!(matches!(module().inspect(&ctx), Decision::Block { .. }));
}
#[test]
fn get_json_envelope_deep_query_is_rejected() {
let deep = format!("query{}{{id}}{}", "{a".repeat(20), "}".repeat(21));
let ctx = get("/graphql", &format!("{{\"query\":\"{deep}\"}}"));
assert!(matches!(module().inspect(&ctx), Decision::Reject { status: 400, .. }));
}
#[test]
fn get_benign_envelope_is_allowed() {
let ctx = get("/graphql", r#"{"query":"query{user{id name}}"}"#);
assert!(matches!(module().inspect(&ctx), Decision::Allow));
}
#[test]
fn get_benign_raw_document_is_allowed() {
let ctx = get("/graphql", "{user{id name}}");
assert!(matches!(module().inspect(&ctx), Decision::Allow));
}
#[test]
fn envelope_on_non_graphql_path_is_ignored() {
let ctx = get("/api/search", r#"{"query":"query{__schema{name}}"}"#);
assert!(matches!(module().inspect(&ctx), Decision::Allow));
}