@@ -10,7 +10,7 @@ use libddwaf::object::{Keyed, WafMap, WafObject};
1010use libddwaf:: { Context as WafContext , Handle , RunError , RunOutput , RunResult , waf_map} ;
1111use mime:: Mime ;
1212use multipart:: server:: Multipart ;
13- use tracing:: { debug, warn} ;
13+ use tracing:: { debug, info , warn} ;
1414
1515use crate :: appsec:: processor:: response:: ExpectedResponseFormat ;
1616use 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