Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "grpc_graphql_gateway"
version = "0.9.1"
version = "0.9.2"
edition = "2021"
authors = ["Protocol Lattice"]
description = "A Rust implementation of gRPC-GraphQL gateway - generates GraphQL execution code from gRPC services"
Expand Down
42 changes: 38 additions & 4 deletions src/waf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,10 @@ pub struct WafConfig {
pub block_ldap: bool,
#[serde(default = "default_true")]
pub block_ssti: bool,
/// Optional custom regex patterns supplied by the user.
/// Each pattern is treated as an independent rule; they are OR‑combined.
#[serde(default)]
pub custom_patterns: Vec<String>,
}

fn default_true() -> bool {
Expand All @@ -293,6 +297,7 @@ impl Default for WafConfig {
block_traversal: true,
block_ldap: true,
block_ssti: true,
custom_patterns: Vec::new(),
}
}
}
Expand Down Expand Up @@ -352,7 +357,7 @@ pub fn validate_json_with_config(value: &serde_json::Value, config: &WafConfig)
if let Some(matched) = check_pattern(value, nosqli_regex()) {
tracing::warn!(match_val = %matched, "NoSQL Injection attempt detected");
return Err(Error::Validation(format!("Potential NoSQL Injection detected: {}", matched)));
}
}
}

if config.block_cmdi {
Expand Down Expand Up @@ -383,6 +388,17 @@ pub fn validate_json_with_config(value: &serde_json::Value, config: &WafConfig)
}
}

// Custom user‑provided patterns (applied to string values only)
if !config.custom_patterns.is_empty() {
// Only check string values; other JSON types are ignored for custom rules.
if let serde_json::Value::String(s) = value {
if let Some(pat) = check_custom(s, &config.custom_patterns) {
tracing::warn!(match_val = %pat, "Custom WAF pattern matched");
return Err(Error::Validation(format!("Custom WAF rule triggered: {}", pat)));
}
}
}

Ok(())
}

Expand Down Expand Up @@ -438,6 +454,19 @@ fn check_keys(value: &serde_json::Value, regex: &Regex) -> Option<String> {
None
}

/// Helper to evaluate user‑provided custom patterns.
/// Returns the first matching pattern string, if any.
fn check_custom(value: &str, patterns: &[String]) -> Option<String> {
for pat in patterns {
if let Ok(re) = Regex::new(pat) {
if re.is_match(value) {
return Some(pat.clone());
}
}
}
None
}

#[async_trait::async_trait]
impl Middleware for WafMiddleware {
async fn call(&self, ctx: &mut Context) -> Result<()> {
Expand All @@ -455,7 +484,6 @@ impl Middleware for WafMiddleware {
tracing::warn!(header = ?name, match_val = value_str, "XSS attempt detected in headers");
return Err(Error::Validation(format!("Potential XSS detected in header: {}", name)));
}
// NoSQLi in headers is rare but possible if headers are logged to NoSQL
if self.config.block_nosqli && nosqli_regex().is_match(value_str) {
tracing::warn!(header = ?name, match_val = value_str, "NoSQL Injection attempt detected in headers");
return Err(Error::Validation(format!("Potential NoSQL Injection detected in header: {}", name)));
Expand All @@ -476,9 +504,15 @@ impl Middleware for WafMiddleware {
tracing::warn!(header = ?name, match_val = value_str, "SSTI attempt detected in headers");
return Err(Error::Validation(format!("Potential SSTI detected in header: {}", name)));
}
// Custom patterns on header values
if !self.config.custom_patterns.is_empty() {
if let Some(pat) = check_custom(value_str, &self.config.custom_patterns) {
tracing::warn!(header = ?name, match_val = pat, "Custom WAF pattern matched in header");
return Err(Error::Validation(format!("Custom WAF rule triggered in header {}: {}", name, pat)));
}
}
}
}

}
Ok(())
}

Expand Down