Skip to content

Commit cd8e2cf

Browse files
committed
Update to latest commit from DataDog/libddwaf-rust#5
1 parent 1d0b374 commit cd8e2cf

File tree

5 files changed

+83
-54
lines changed

5 files changed

+83
-54
lines changed

bottlecap/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bottlecap/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ datadog-trace-obfuscation = { git = "https://github.com/DataDog/libdatadog", rev
6464
dogstatsd = { git = "https://github.com/DataDog/serverless-components", rev = "c3d8ed4f90591c6958921145d485463860307f78", default-features = false }
6565
datadog-trace-agent = { git = "https://github.com/DataDog/serverless-components", rev = "c3d8ed4f90591c6958921145d485463860307f78" }
6666
datadog-fips = { git = "https://github.com/DataDog/serverless-components", rev = "c3d8ed4f90591c6958921145d485463860307f78", default-features = false }
67-
libddwaf = { version = "1.25.1", git = "https://github.com/DataDog/libddwaf-rust", rev = "1353c5cf1c2ff6b8474b950bd96bccd5d89cf4f3", default-features = false, features = ["default"] }
67+
libddwaf = { version = "1.25.1", git = "https://github.com/DataDog/libddwaf-rust", rev = "b23b2d7e6abd6340b30dc8031482a6326bda57cd", default-features = false, features = ["default"] }
6868

6969
[dev-dependencies]
7070
figment = { version = "0.10", default-features = false, features = ["yaml", "env", "test"] }

bottlecap/src/appsec/payload/mod.rs

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,17 @@ use aws_lambda_events::{
55
lambda_function_urls, s3, sns, sqs,
66
};
77
use bytes::{Buf, Bytes};
8-
use libddwaf::object::{WAFArray, WAFMap, WAFObject};
8+
use libddwaf::object::{WafArray, WafMap, WafObject};
99
use tracing::warn;
1010

1111
mod request;
1212

13+
1314
trait IsValid {
1415
fn is_valid(map: &serde_json::Map<String, serde_json::Value>) -> bool;
1516
}
1617

17-
struct HTTPRequestData {
18+
struct HttpRequestData {
1819
source_ip: Option<String>,
1920
route: Option<String>,
2021
client_ip: Option<String>,
@@ -23,16 +24,16 @@ struct HTTPRequestData {
2324
cookies: Option<HashMap<String, Vec<String>>>,
2425
query: Option<HashMap<String, Vec<String>>>,
2526
path_params: Option<HashMap<String, String>>,
26-
body: Option<WAFObject>,
27-
response_body: Option<WAFObject>,
27+
body: Option<WafObject>,
28+
response_body: Option<WafObject>,
2829
response_status: Option<u32>,
2930
}
3031

31-
trait ToWAFMap {
32-
fn to_waf_map(self) -> WAFMap;
32+
trait ToWafMap {
33+
fn to_waf_map(self) -> WafMap;
3334
}
34-
impl ToWAFMap for HTTPRequestData {
35-
fn to_waf_map(self) -> WAFMap {
35+
impl ToWafMap for HttpRequestData {
36+
fn to_waf_map(self) -> WafMap {
3637
let count = [
3738
self.client_ip.is_some(),
3839
self.raw_uri.is_some(),
@@ -47,7 +48,7 @@ impl ToWAFMap for HTTPRequestData {
4748
.into_iter()
4849
.filter(|b| *b)
4950
.count();
50-
let mut map = WAFMap::new(count as u64);
51+
let mut map = WafMap::new(count as u64);
5152
let mut i = 0;
5253

5354
if let Some(client_ip) = self.client_ip {
@@ -92,12 +93,12 @@ impl ToWAFMap for HTTPRequestData {
9293
map
9394
}
9495
}
95-
impl ToWAFMap for HashMap<String, Vec<String>> {
96-
fn to_waf_map(self) -> WAFMap {
97-
let mut map = WAFMap::new(self.len() as u64);
96+
impl ToWafMap for HashMap<String, Vec<String>> {
97+
fn to_waf_map(self) -> WafMap {
98+
let mut map = WafMap::new(self.len() as u64);
9899

99100
for (i, (k, v)) in self.into_iter().enumerate() {
100-
let mut arr = WAFArray::new(v.len() as u64);
101+
let mut arr = WafArray::new(v.len() as u64);
101102
for (j, v) in v.into_iter().enumerate() {
102103
arr[j] = v.as_str().into();
103104
}
@@ -108,9 +109,9 @@ impl ToWAFMap for HashMap<String, Vec<String>> {
108109
map
109110
}
110111
}
111-
impl ToWAFMap for HashMap<String, String> {
112-
fn to_waf_map(self) -> WAFMap {
113-
let mut map = WAFMap::new(self.len() as u64);
112+
impl ToWafMap for HashMap<String, String> {
113+
fn to_waf_map(self) -> WafMap {
114+
let mut map = WafMap::new(self.len() as u64);
114115

115116
for (i, (k, v)) in self.into_iter().enumerate() {
116117
map[i] = (k.as_str(), v.as_str()).into();
@@ -120,11 +121,23 @@ impl ToWAFMap for HashMap<String, String> {
120121
}
121122
}
122123

123-
trait Extractor<'d>: serde::de::Deserialize<'d> + IsValid {
124-
fn extract(self) -> HTTPRequestData;
124+
enum RequestType {
125+
APIGatewayV1, // Or Kong
126+
APIGatewayV2Http,
127+
APIGatewayV2Websocket,
128+
APIGatewayLambdaAuthorizerToken,
129+
APIGatewayLambdaAuthorizerRequest,
130+
Alb,
131+
LambdaFunctionUrl,
132+
}
133+
134+
135+
trait Extractor {
136+
const TYPE: RequestType;
137+
fn extract(self) -> HttpRequestData;
125138
}
126139

127-
pub(super) fn extract_request_address_data(body: &Bytes) -> Option<WAFMap> {
140+
pub(super) fn extract_request_address_data(body: &Bytes) -> Option<WafMap> {
128141
let reader = body.clone().reader();
129142
let data: serde_json::Map<String, serde_json::Value> = match serde_json::from_reader(reader) {
130143
Ok(data) => data,
@@ -185,7 +198,7 @@ pub(super) fn extract_request_address_data(body: &Bytes) -> Option<WAFMap> {
185198
None
186199
}
187200

188-
pub(super) fn extract_response_address_data(body: &Bytes) -> Option<WAFMap> {
201+
pub(super) fn extract_response_address_data(body: &Bytes) -> Option<WafMap> {
189202
let reader = body.clone().reader();
190203
let data: serde_json::Map<String, serde_json::Value> = match serde_json::from_reader(reader) {
191204
Ok(data) => data,

bottlecap/src/appsec/payload/request.rs

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::{Extractor, HTTPRequestData, IsValid};
1+
use super::{Extractor, HttpRequestData, IsValid, RequestType};
22

33
use std::collections::hash_map::Entry;
44
use std::collections::HashMap;
@@ -9,7 +9,7 @@ use aws_lambda_events::{
99
lambda_function_urls, s3, sns, sqs,
1010
};
1111
use bytes::Buf;
12-
use libddwaf::object::{WAFObject, WAFString};
12+
use libddwaf::object::{WafObject, WafString};
1313
use tracing::debug;
1414

1515
/// Kong API Gateway events are a subset of [`apigw::ApiGatewayProxyRequest`].
@@ -49,8 +49,10 @@ impl IsValid for apigw::ApiGatewayProxyRequest {
4949
&& !apigw::ApiGatewayCustomAuthorizerRequestTypeRequest::is_valid(map)
5050
}
5151
}
52-
impl Extractor<'_> for apigw::ApiGatewayProxyRequest {
53-
fn extract(self) -> HTTPRequestData {
52+
impl Extractor for apigw::ApiGatewayProxyRequest {
53+
const TYPE: RequestType = RequestType::APIGatewayV1;
54+
55+
fn extract(self) -> HttpRequestData {
5456
let (headers, cookies) = filter_headers(self.multi_value_headers);
5557

5658
// Headers are normalized to lowercase by [`filter_headers`].
@@ -61,7 +63,7 @@ impl Extractor<'_> for apigw::ApiGatewayProxyRequest {
6163
.flatten()
6264
});
6365

64-
HTTPRequestData {
66+
HttpRequestData {
6567
source_ip: self.request_context.identity.source_ip.clone(),
6668
route: self.resource,
6769
client_ip: self.request_context.identity.source_ip, // API Gateway exposes the Client IP as the Source IP
@@ -102,8 +104,10 @@ impl IsValid for apigw::ApiGatewayV2httpRequest {
102104
}
103105
}
104106
}
105-
impl Extractor<'_> for apigw::ApiGatewayV2httpRequest {
106-
fn extract(self) -> HTTPRequestData {
107+
impl Extractor for apigw::ApiGatewayV2httpRequest {
108+
const TYPE: RequestType = RequestType::APIGatewayV2Http;
109+
110+
fn extract(self) -> HttpRequestData {
107111
let (headers, cookies) = filter_headers(self.headers);
108112

109113
let content_type = headers["content-type"].first().map(String::as_str);
@@ -113,7 +117,7 @@ impl Extractor<'_> for apigw::ApiGatewayV2httpRequest {
113117
.flatten()
114118
});
115119

116-
HTTPRequestData {
120+
HttpRequestData {
117121
source_ip: self.request_context.http.source_ip.clone(),
118122
route: self.route_key,
119123
client_ip: self.request_context.http.source_ip, // API Gateway exposes the Client IP as the Source IP
@@ -135,8 +139,10 @@ impl IsValid for KongAPIGatewayEvent {
135139
&& matches!(map.get("resource"), Some(serde_json::Value::String(_)))
136140
}
137141
}
138-
impl Extractor<'_> for KongAPIGatewayEvent {
139-
fn extract(self) -> HTTPRequestData {
142+
impl Extractor for KongAPIGatewayEvent {
143+
const TYPE: RequestType = RequestType::APIGatewayV1;
144+
145+
fn extract(self) -> HttpRequestData {
140146
self.0.extract()
141147
}
142148
}
@@ -153,8 +159,10 @@ impl IsValid for apigw::ApiGatewayWebsocketProxyRequest {
153159
}
154160
}
155161
}
156-
impl Extractor<'_> for apigw::ApiGatewayWebsocketProxyRequest {
157-
fn extract(self) -> HTTPRequestData {
162+
impl Extractor for apigw::ApiGatewayWebsocketProxyRequest {
163+
const TYPE: RequestType = RequestType::APIGatewayV2Websocket;
164+
165+
fn extract(self) -> HttpRequestData {
158166
let (headers, cookies) = filter_headers(self.multi_value_headers);
159167

160168
let content_type = headers["content-type"].first().map(String::as_str);
@@ -164,7 +172,7 @@ impl Extractor<'_> for apigw::ApiGatewayWebsocketProxyRequest {
164172
.flatten()
165173
});
166174

167-
HTTPRequestData {
175+
HttpRequestData {
168176
source_ip: self.request_context.identity.source_ip.clone(),
169177
route: self.resource,
170178
client_ip: self.request_context.identity.source_ip, // API Gateway exposes the Client IP as the Source IP
@@ -194,9 +202,11 @@ impl IsValid for apigw::ApiGatewayCustomAuthorizerRequest {
194202
}
195203
}
196204
}
197-
impl Extractor<'_> for apigw::ApiGatewayCustomAuthorizerRequest {
198-
fn extract(self) -> HTTPRequestData {
199-
HTTPRequestData {
205+
impl Extractor for apigw::ApiGatewayCustomAuthorizerRequest {
206+
const TYPE: RequestType = RequestType::APIGatewayLambdaAuthorizerToken;
207+
208+
fn extract(self) -> HttpRequestData {
209+
HttpRequestData {
200210
source_ip: None,
201211
route: None,
202212
client_ip: None,
@@ -238,13 +248,15 @@ impl IsValid for apigw::ApiGatewayCustomAuthorizerRequestTypeRequest {
238248
}
239249
}
240250
}
241-
impl Extractor<'_> for apigw::ApiGatewayCustomAuthorizerRequestTypeRequest {
242-
fn extract(self) -> HTTPRequestData {
251+
impl Extractor for apigw::ApiGatewayCustomAuthorizerRequestTypeRequest {
252+
const TYPE: RequestType = RequestType::APIGatewayLambdaAuthorizerRequest;
253+
254+
fn extract(self) -> HttpRequestData {
243255
let source_ip = self.request_context.identity.and_then(|i| i.source_ip);
244256

245257
let (headers, cookies) = filter_headers(self.headers);
246258

247-
HTTPRequestData {
259+
HttpRequestData {
248260
source_ip: source_ip.clone(),
249261
route: self.resource,
250262
client_ip: source_ip,
@@ -272,8 +284,10 @@ impl IsValid for alb::AlbTargetGroupRequest {
272284
}
273285
}
274286
}
275-
impl Extractor<'_> for alb::AlbTargetGroupRequest {
276-
fn extract(self) -> HTTPRequestData {
287+
impl Extractor for alb::AlbTargetGroupRequest {
288+
const TYPE: RequestType = RequestType::Alb;
289+
290+
fn extract(self) -> HttpRequestData {
277291
// Based on configuration, ALB provides headers EITHER in multi-value form OR in single-value form, never both.
278292
let (headers, cookies) = filter_headers(if self.multi_value_headers.is_empty() {
279293
self.headers
@@ -294,7 +308,7 @@ impl Extractor<'_> for alb::AlbTargetGroupRequest {
294308
.flatten()
295309
});
296310

297-
HTTPRequestData {
311+
HttpRequestData {
298312
source_ip: None,
299313
route: None,
300314
client_ip: None,
@@ -375,8 +389,10 @@ impl IsValid for lambda_function_urls::LambdaFunctionUrlRequest {
375389
}
376390
}
377391
}
378-
impl Extractor<'_> for lambda_function_urls::LambdaFunctionUrlRequest {
379-
fn extract(self) -> HTTPRequestData {
392+
impl Extractor for lambda_function_urls::LambdaFunctionUrlRequest {
393+
const TYPE: RequestType = RequestType::LambdaFunctionUrl;
394+
395+
fn extract(self) -> HttpRequestData {
380396
let (headers, cookies) = filter_headers(self.headers);
381397

382398
let content_type = headers["content-type"].first().map(String::as_str);
@@ -386,7 +402,7 @@ impl Extractor<'_> for lambda_function_urls::LambdaFunctionUrlRequest {
386402
.flatten()
387403
});
388404

389-
HTTPRequestData {
405+
HttpRequestData {
390406
source_ip: self.request_context.http.source_ip.clone(),
391407
route: None,
392408
client_ip: self.request_context.http.source_ip,
@@ -458,7 +474,7 @@ fn parse_body(
458474
body: impl AsRef<[u8]>,
459475
is_base64_encoded: bool,
460476
content_type: Option<&str>,
461-
) -> Result<Option<WAFObject>, Box<dyn std::error::Error>> {
477+
) -> Result<Option<WafObject>, Box<dyn std::error::Error>> {
462478
let body = body.as_ref();
463479
let reader: Box<dyn Read> = if is_base64_encoded {
464480
Box::new(base64::read::DecoderReader::new(
@@ -481,7 +497,7 @@ fn parse_body(
481497
(mime::APPLICATION, mime::WWW_FORM_URLENCODED) => todo!(),
482498
(mime::APPLICATION | mime::TEXT, mime::XML) => todo!(),
483499
(mime::MULTIPART, mime::FORM_DATA) => todo!(),
484-
(mime::TEXT, mime::PLAIN) => Some(WAFString::new(body).into()),
500+
(mime::TEXT, mime::PLAIN) => Some(WafString::new(body).into()),
485501
_ => {
486502
debug!("appsec: unsupported content type: {mime_type}");
487503
None

bottlecap/src/appsec/processor.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::appsec::{is_enabled, is_standalone, payload};
44
use crate::config::Config;
55

66
use bytes::Bytes;
7-
use libddwaf::object::{WAFMap, WAFOwned};
7+
use libddwaf::object::{WafMap, WafOwned};
88
use libddwaf::{Builder, Config as WAFConfig, Context, Handle};
99
use tracing::{debug, info, warn};
1010
/// The App & API Protection processor.
@@ -13,7 +13,7 @@ use tracing::{debug, info, warn};
1313
/// the request payload, and evaluate in-app WAF rules against that data.
1414
pub struct Processor {
1515
handle: Handle,
16-
diagnostics: WAFOwned<WAFMap>,
16+
diagnostics: WafOwned<WafMap>,
1717
waf_timeout: Duration,
1818
}
1919

@@ -42,7 +42,7 @@ impl Processor {
4242
};
4343

4444
let rules = Self::get_rules(config)?;
45-
let mut diagnostics = WAFOwned::<WAFMap>::default();
45+
let mut diagnostics = WafOwned::<WafMap>::default();
4646
if !builder.add_or_update_config("rules", &rules, Some(&mut diagnostics)) {
4747
return Err("Failed to add ruleset to the WAF builder".into());
4848
}
@@ -93,7 +93,7 @@ impl Processor {
9393

9494
/// Parses the App & API Protection ruleset from the provided [Config], falling back to the
9595
/// default built-in ruleset if the [Config] has [None].
96-
fn get_rules(config: &Config) -> Result<WAFMap, Box<dyn std::error::Error>> {
96+
fn get_rules(config: &Config) -> Result<WafMap, Box<dyn std::error::Error>> {
9797
// Default on recommended rules
9898
match &config.appsec_rules {
9999
None => {
@@ -117,7 +117,7 @@ pub struct ProcessorContext {
117117
}
118118
impl ProcessorContext {
119119
/// Evaluates the in-app WAF rules against the provided address data.
120-
fn run(&mut self, address_data: WAFMap) {
120+
fn run(&mut self, address_data: WafMap) {
121121
let result = match self
122122
.waf_context
123123
.run(Some(address_data), None, self.waf_timeout)

0 commit comments

Comments
 (0)