Skip to content

Commit c58a8ce

Browse files
authored
fix(aap): lower severity of several warnings (#852)
These produce excessive log spam in customer accounts, for conditions they may not be able to generally address (either because the cause is outside of their control, or because they decided the trade-off was acceptable). The alternative would be allowing granuar configuration of log level limits for each module, which would be a significantly more involved change. This also reverts to a behavior that is closer to that of the Go version, which was silently ignoring these conditions for the most part. Fixes #840
1 parent daf633d commit c58a8ce

1 file changed

Lines changed: 49 additions & 21 deletions

File tree

bottlecap/src/appsec/processor/context.rs

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use libddwaf::object::{Keyed, WafMap, WafObject};
1010
use libddwaf::{Context as WafContext, Handle, RunError, RunOutput, RunResult, waf_map};
1111
use mime::Mime;
1212
use multipart::server::Multipart;
13-
use tracing::{debug, warn};
13+
use tracing::{debug, info, warn};
1414

1515
use crate::appsec::processor::response::ExpectedResponseFormat;
1616
use crate::appsec::processor::{InvocationPayload, Processor};
@@ -169,9 +169,16 @@ impl Context {
169169
}
170170

171171
if self.waf_timeout.is_zero() {
172-
warn!(
173-
"aap: WAF execution time budget for this request is exhausted, skipping WAF ruleset evaluation (consider tweaking DD_APPSEC_WAF_TIMEOUT)"
172+
// Logging as INFO and not as WARN as users may find this an acceptable trade-off in
173+
// situations where extending the WAF timeout is not acceptable. Issuing a WARN here
174+
// would lead to excessive log spam for these customers, who then can't do anything
175+
// about it.
176+
info!(
177+
"aap: WAF execution time budget for this request is exhausted, skipping WAF ruleset evaluation entirely (consider tweaking DD_APPSEC_WAF_TIMEOUT)"
174178
);
179+
// This still counts as a WAF timeout even though we did not actually call it... The
180+
// budget would have been 0 here.
181+
self.waf_timed_out_occurrences += 1;
175182
return;
176183
}
177184

@@ -427,7 +434,11 @@ impl Context {
427434
duration, self.waf_timeout, self.waf_duration
428435
);
429436
if result.timeout() {
430-
warn!(
437+
// Logging as INFO and not as WARN as users may find this an acceptable trade-off in
438+
// situations where extending the WAF timeout is not acceptable. Issuing a WARN here
439+
// would lead to excessive log spam for these customers, who then can't do anything
440+
// about it.
441+
info!(
431442
"aap: time out reached while evaluating the WAF ruleset; detections may be incomplete. Consider tuning DD_APPSEC_WAF_TIMEOUT"
432443
);
433444
self.waf_timed_out_occurrences += 1;
@@ -648,7 +659,9 @@ fn to_address_data(payload: &dyn InvocationPayload) -> Option<WafMap> {
648659
Ok(value) => {
649660
addresses.push(($name, value).into());
650661
},
651-
Err(e) => warn!("aap: failed to parse body: {e}"),
662+
// Logging these as INFO as the user is often unable to do anything about these issues, and hence
663+
// WARN is excessive.
664+
Err(e) => info!("aap: unable to parse body, it will not be analyzed for security activity: {e}"),
652665
}
653666
}
654667
};
@@ -708,18 +721,12 @@ fn to_address_data(payload: &dyn InvocationPayload) -> Option<WafMap> {
708721
Some(result)
709722
}
710723

711-
fn try_parse_body(
712-
body: impl Read,
713-
content_type: &str,
714-
) -> Result<WafObject, Box<dyn std::error::Error>> {
724+
fn try_parse_body(body: impl Read, content_type: &str) -> Result<WafObject, BodyParseError> {
715725
let mime_type: Mime = content_type.parse()?;
716726
try_parse_body_with_mime(body, mime_type)
717727
}
718728

719-
fn try_parse_body_with_mime(
720-
body: impl Read,
721-
mime_type: Mime,
722-
) -> Result<WafObject, Box<dyn std::error::Error>> {
729+
fn try_parse_body_with_mime(body: impl Read, mime_type: Mime) -> Result<WafObject, BodyParseError> {
723730
match (mime_type.type_(), mime_type.subtype(), mime_type.suffix()) {
724731
(typ, sub, suff)
725732
if ((typ == mime::TEXT || typ == mime::APPLICATION)
@@ -739,10 +746,7 @@ fn try_parse_body_with_mime(
739746
}
740747
(mime::MULTIPART, mime::FORM_DATA, None) => {
741748
let Some(boundary) = mime_type.get_param("boundary") else {
742-
return Err(format!(
743-
"aap: cannot parse {mime_type} body: missing boundary parameter"
744-
)
745-
.into());
749+
return Err(BodyParseError::MissingBoundary(mime_type));
746750
};
747751
let mut multipart = Multipart::with_body(body, boundary.as_str());
748752
let mut items = Vec::new();
@@ -756,7 +760,9 @@ fn try_parse_body_with_mime(
756760
{
757761
Ok(value) => value,
758762
Err(e) => {
759-
warn!(
763+
// Logging as INFO as this is often not directly actionnable by the
764+
// customer and can lead to excessive log spam if sent as WARN.
765+
info!(
760766
"aap: failed to parse multipart body entry {name}: {e}",
761767
name = entry.headers.name
762768
);
@@ -783,9 +789,31 @@ fn try_parse_body_with_mime(
783789
let body = read_to_string(body)?;
784790
Ok(body.as_str().into())
785791
}
786-
_ => Err(
787-
format!("aap: unsupported MIME type, the body will not be parsed: {mime_type}").into(),
788-
),
792+
_ => Err(BodyParseError::UnsupportedMimeType(mime_type)),
793+
}
794+
}
795+
796+
#[derive(Debug, thiserror::Error)]
797+
enum BodyParseError {
798+
#[error("failed to parse content type: {0}")]
799+
ContentTypeParseError(#[from] mime::FromStrError),
800+
#[error("cannot parse {0} body: missing boundary parameter")]
801+
MissingBoundary(Mime),
802+
#[error("unsupported MIME type: {0}")]
803+
UnsupportedMimeType(Mime),
804+
#[error("failed to read body: {0}")]
805+
IOError(#[from] std::io::Error),
806+
#[error("failed to parse body: {0}")]
807+
SerdeError(Box<dyn std::error::Error>),
808+
}
809+
impl From<serde_json::Error> for BodyParseError {
810+
fn from(e: serde_json::Error) -> Self {
811+
Self::SerdeError(Box::new(e))
812+
}
813+
}
814+
impl From<serde_html_form::de::Error> for BodyParseError {
815+
fn from(e: serde_html_form::de::Error) -> Self {
816+
Self::SerdeError(Box::new(e))
789817
}
790818
}
791819

0 commit comments

Comments
 (0)