@@ -520,6 +520,26 @@ static int chained_is_matched(modsec_rec *msr, const msre_rule *next_rule) {
520520}
521521
522522#ifdef WITH_JSON_LOGGING
523+ /**
524+ * Write detailed information about performance metrics into a JSON generator
525+ */
526+ static void format_performance_variables_json (modsec_rec * msr , yajl_gen g ) {
527+ yajl_string (g , "stopwatch" );
528+ yajl_gen_map_open (g );
529+
530+ yajl_kv_int (g , "p1" , msr -> time_phase1 );
531+ yajl_kv_int (g , "p2" , msr -> time_phase2 );
532+ yajl_kv_int (g , "p3" , msr -> time_phase3 );
533+ yajl_kv_int (g , "p4" , msr -> time_phase4 );
534+ yajl_kv_int (g , "p5" , msr -> time_phase5 );
535+ yajl_kv_int (g , "sr" , msr -> time_storage_read );
536+ yajl_kv_int (g , "sw" , msr -> time_storage_write );
537+ yajl_kv_int (g , "l" , msr -> time_logging );
538+ yajl_kv_int (g , "gc" , msr -> time_gc );
539+
540+ yajl_gen_map_close (g );
541+ }
542+
523543/**
524544 * Write detailed information about a rule and its actionset into a JSON generator
525545 */
@@ -558,6 +578,9 @@ static void write_rule_json(modsec_rec *msr, const msre_rule *rule, yajl_gen g)
558578 yajl_kv_int (g , "phase" , rule -> actionset -> phase );
559579 }
560580 yajl_kv_bool (g , "is_chained" , rule -> actionset -> is_chained );
581+ if (rule -> actionset -> is_chained && (rule -> chain_starter == NULL )) {
582+ yajl_kv_bool (g , "chain_starter" , 1 );
583+ }
561584 yajl_gen_map_close (g );
562585
563586 yajl_string (g , "operator" );
@@ -602,6 +625,7 @@ void sec_audit_logger(modsec_rec *msr) {
602625 int arg_min , arg_max , sanitize_matched ;
603626#ifdef WITH_JSON_LOGGING
604627 yajl_gen g ;
628+ int been_opened = 0 ; // helper flag for conditionally opening maps
605629#endif
606630
607631#ifndef WITH_JSON_LOGGING
@@ -704,7 +728,7 @@ void sec_audit_logger(modsec_rec *msr) {
704728 g = yajl_gen_alloc (NULL );
705729
706730 /**
707- * don't pretty print JSON by default
731+ * don't pretty print JSON
708732 * this is harder to eyeball but much easier to parse programmatically
709733 */
710734 yajl_gen_config (g , yajl_gen_beautify , 0 );
@@ -771,16 +795,14 @@ void sec_audit_logger(modsec_rec *msr) {
771795 for (i = 0 ; i < arr -> nelts ; i ++ ) {
772796 sanitized_partial = 0 ;
773797 sanitize_matched = 0 ;
774- #ifndef WITH_JSON_LOGGING
775798 text = apr_psprintf (msr -> mp , "%s: %s\n" , te [i ].key , te [i ].val );
776- #else
799+ #ifdef WITH_JSON_LOGGING
777800 // write the key no matter what
778801 // since sanitization only occurs on the value
779802 yajl_string (g , te [i ].key );
780803#endif
781804 if (apr_table_get (msr -> request_headers_to_sanitize , te [i ].key ) != NULL ) {
782805 buf = apr_psprintf (msr -> mp , "%s" ,text + strlen (te [i ].key )+ 2 );
783-
784806 for ( k = 0 ; k < tarr_pattern -> nelts ; k ++ ) {
785807 if (strncmp (telts_pattern [k ].key ,te [i ].key ,strlen (te [i ].key )) == 0 ) {
786808 mparm = (msc_parm * )telts_pattern [k ].val ;
@@ -818,7 +840,8 @@ void sec_audit_logger(modsec_rec *msr) {
818840#ifndef WITH_JSON_LOGGING
819841 memset (text + strlen (te [i ].key ) + 2 , '*' , strlen (te [i ].val ));
820842#else
821- yajl_string (g , "****" ); // fix this later
843+ memset (buf , '*' , strlen (buf )); // strlen also includes the appended newline on the header
844+ yajl_string (g , buf );
822845#endif
823846 }
824847 }
@@ -827,7 +850,9 @@ void sec_audit_logger(modsec_rec *msr) {
827850#else
828851 // we diverge from the original logic a bit because we always print the key
829852 // at this no point sanitization had occured, so we just print the value
830- yajl_string (g , te [i ].val );
853+ else {
854+ yajl_string (g , te [i ].val );
855+ }
831856#endif
832857 }
833858#ifdef WITH_JSON_LOGGING
@@ -1025,7 +1050,6 @@ void sec_audit_logger(modsec_rec *msr) {
10251050 sec_auditlog_write (msr , text , strlen (text ));
10261051 sec_auditlog_write (msr , buffer , strlen (buffer ));
10271052#else
1028- // this is a key instead 'request', doesn't need an array or map since it's one value
10291053 yajl_kv_string (g , "fake_body" , buffer );
10301054#endif
10311055 }
@@ -1055,7 +1079,8 @@ void sec_audit_logger(modsec_rec *msr) {
10551079 msr -> status_line );
10561080#else
10571081 yajl_kv_string (g , "protocol" , msr -> response_protocol );
1058- yajl_kv_string (g , "status" , msr -> status_line );
1082+ // as an integer, response status is easier to parse than status_line
1083+ yajl_kv_int (g , "status" , (int )msr -> response_status );
10591084#endif
10601085 } else {
10611086#ifndef WITH_JSON_LOGGING
@@ -1084,9 +1109,8 @@ void sec_audit_logger(modsec_rec *msr) {
10841109 for (i = 0 ; i < arr -> nelts ; i ++ ) {
10851110 sanitized_partial = 0 ;
10861111 sanitize_matched = 0 ;
1087- #ifndef WITH_JSON_LOGGING
10881112 text = apr_psprintf (msr -> mp , "%s: %s\n" , te [i ].key , te [i ].val );
1089- #else
1113+ #ifdef WITH_JSON_LOGGING
10901114 // write the key no matter what
10911115 // since sanitization only occurs on the value
10921116 yajl_string (g , te [i ].key );
@@ -1131,7 +1155,8 @@ void sec_audit_logger(modsec_rec *msr) {
11311155#ifndef WITH_JSON_LOGGING
11321156 memset (text + strlen (te [i ].key ) + 2 , '*' , strlen (te [i ].val ));
11331157#else
1134- yajl_string (g , "****" ); // fix this later
1158+ memset (buf , '*' , strlen (buf ));
1159+ yajl_string (g , buf );
11351160#endif
11361161 }
11371162 }
@@ -1140,7 +1165,9 @@ void sec_audit_logger(modsec_rec *msr) {
11401165#else
11411166 // we diverge from the original logic a bit because we always print the key
11421167 // at this point no sanitization had occured, so we just print the value
1143- yajl_string (g , te [i ].val );
1168+ else {
1169+ yajl_string (g , te [i ].val );
1170+ }
11441171#endif
11451172 }
11461173#ifdef WITH_JSON_LOGGING
@@ -1169,8 +1196,8 @@ void sec_audit_logger(modsec_rec *msr) {
11691196#ifdef WITH_JSON_LOGGING
11701197 yajl_gen_map_close (g ); // response top-level key is finished
11711198
1172- yajl_string (g , "data " );
1173- yajl_gen_map_open (g ); // data top-level key
1199+ yajl_string (g , "audit_data " );
1200+ yajl_gen_map_open (g ); // audit_data top-level key
11741201#endif
11751202
11761203 /* AUDITLOG_PART_TRAILER */
@@ -1184,8 +1211,12 @@ void sec_audit_logger(modsec_rec *msr) {
11841211
11851212 /* Messages */
11861213#ifdef WITH_JSON_LOGGING
1187- yajl_string (g , "messages" );
1188- yajl_gen_array_open (g );
1214+ been_opened = 0 ;
1215+ if (msr -> alerts -> nelts > 0 ) {
1216+ yajl_string (g , "messages" );
1217+ yajl_gen_array_open (g );
1218+ been_opened = 1 ;
1219+ }
11891220#endif
11901221 for (i = 0 ; i < msr -> alerts -> nelts ; i ++ ) {
11911222#ifndef WITH_JSON_LOGGING
@@ -1196,13 +1227,19 @@ void sec_audit_logger(modsec_rec *msr) {
11961227#endif
11971228 }
11981229#ifdef WITH_JSON_LOGGING
1199- yajl_gen_array_close (g );
1230+ if (been_opened == 1 ) {
1231+ yajl_gen_array_close (g );
1232+ }
12001233#endif
12011234
12021235 /* Apache error messages */
12031236#ifdef WITH_JSON_LOGGING
1204- yajl_string (g , "error_messages" );
1205- yajl_gen_array_open (g );
1237+ been_opened = 0 ;
1238+ if (msr -> error_messages -> nelts > 0 ) {
1239+ yajl_string (g , "error_messages" );
1240+ yajl_gen_array_open (g );
1241+ been_opened = 1 ;
1242+ }
12061243#endif
12071244 for (i = 0 ; i < msr -> error_messages -> nelts ; i ++ ) {
12081245 error_message_t * em = (((error_message_t * * )msr -> error_messages -> elts )[i ]);
@@ -1215,7 +1252,9 @@ void sec_audit_logger(modsec_rec *msr) {
12151252#endif
12161253 }
12171254#ifdef WITH_JSON_LOGGING
1218- yajl_gen_array_close (g );
1255+ if (been_opened == 1 ) {
1256+ yajl_gen_array_close (g );
1257+ }
12191258#endif
12201259
12211260 /* Action */
@@ -1228,6 +1267,7 @@ void sec_audit_logger(modsec_rec *msr) {
12281267 yajl_gen_map_open (g );
12291268 yajl_kv_bool (g , "intercepted" , 1 );
12301269 yajl_kv_int (g , "phase" , msr -> intercept_phase );
1270+ yajl_kv_string (g , "message" , msr -> intercept_message );
12311271 yajl_gen_map_close (g );
12321272#endif
12331273 }
@@ -1242,27 +1282,25 @@ void sec_audit_logger(modsec_rec *msr) {
12421282#endif
12431283 }
12441284
1285+ #ifndef WITH_JSON_LOGGING
12451286 /* Stopwatch; left in for compatibility reasons */
12461287 text = apr_psprintf (msr -> mp , "Stopwatch: %" APR_TIME_T_FMT " %" APR_TIME_T_FMT " (- - -)\n" ,
12471288 msr -> request_time , (now - msr -> request_time ));
1248- #ifndef WITH_JSON_LOGGING
12491289 sec_auditlog_write (msr , text , strlen (text ));
1250- #else
1251- yajl_kv_string (g , "stopwatch" , text );
12521290#endif
12531291
12541292 /* Stopwatch2 */
1293+ #ifndef WITH_JSON_LOGGING
12551294 {
12561295 char * perf_all = format_all_performance_variables (msr , msr -> mp );
12571296
12581297 text = apr_psprintf (msr -> mp , "Stopwatch2: %" APR_TIME_T_FMT " %" APR_TIME_T_FMT
12591298 "; %s\n" , msr -> request_time , (now - msr -> request_time ), perf_all );
1260- #ifndef WITH_JSON_LOGGING
12611299 sec_auditlog_write (msr , text , strlen (text ));
1300+ }
12621301#else
1263- yajl_kv_string ( g , "stopwatch2" , text );
1302+ format_performance_variables_json ( msr , g );
12641303#endif
1265- }
12661304
12671305 /* Our response body does not contain chunks */
12681306 /* ENH Only write this when the output was chunked. */
@@ -1293,8 +1331,7 @@ void sec_audit_logger(modsec_rec *msr) {
12931331 }
12941332
12951333#ifdef WITH_JSON_LOGGING
1296- yajl_string (g , "sanitized" );
1297- yajl_gen_map_open (g ); // open a separate map for sanitized values
1334+ been_opened = 0 ;
12981335#endif
12991336
13001337 /* Sanitised arguments */
@@ -1310,6 +1347,12 @@ void sec_audit_logger(modsec_rec *msr) {
13101347 text = apr_psprintf (msr -> mp , "Sanitised-Args: " );
13111348 sec_auditlog_write (msr , text , strlen (text ));
13121349#else
1350+ if (been_opened == 0 ) {
1351+ yajl_string (g , "sanitized" );
1352+ yajl_gen_map_open (g );
1353+ been_opened = 1 ;
1354+ }
1355+
13131356 yajl_string (g , "args" );
13141357 yajl_gen_array_open (g );
13151358#endif
@@ -1346,6 +1389,12 @@ void sec_audit_logger(modsec_rec *msr) {
13461389 text = apr_psprintf (msr -> mp , "Sanitised-Request-Headers: " );
13471390 sec_auditlog_write (msr , text , strlen (text ));
13481391#else
1392+ if (been_opened == 0 ) {
1393+ yajl_string (g , "sanitized" );
1394+ yajl_gen_map_open (g );
1395+ been_opened = 1 ;
1396+ }
1397+
13491398 yajl_string (g , "request_headers" );
13501399 yajl_gen_array_open (g );
13511400#endif
@@ -1380,6 +1429,12 @@ void sec_audit_logger(modsec_rec *msr) {
13801429 text = apr_psprintf (msr -> mp , "Sanitised-Response-Headers: " );
13811430 sec_auditlog_write (msr , text , strlen (text ));
13821431#else
1432+ if (been_opened == 0 ) {
1433+ yajl_string (g , "sanitized" );
1434+ yajl_gen_map_open (g );
1435+ been_opened = 1 ;
1436+ }
1437+
13831438 yajl_string (g , "response_headers" );
13841439 yajl_gen_array_open (g );
13851440#endif
@@ -1402,7 +1457,9 @@ void sec_audit_logger(modsec_rec *msr) {
14021457 }
14031458
14041459#ifdef WITH_JSON_LOGGING
1405- yajl_gen_map_close (g ); // sanitized args map is finished
1460+ if (been_opened == 1 ) {
1461+ yajl_gen_map_close (g ); // sanitized args map is finished
1462+ }
14061463#endif
14071464
14081465 /* Web application info. */
@@ -1493,7 +1550,7 @@ void sec_audit_logger(modsec_rec *msr) {
14931550 }
14941551
14951552#ifdef WITH_JSON_LOGGING
1496- yajl_gen_map_close (g ); // data top-level key is finished
1553+ yajl_gen_map_close (g ); // audit_data top-level key is finished
14971554#endif
14981555
14991556 /* AUDITLOG_PART_UPLOADS */
@@ -1552,7 +1609,6 @@ void sec_audit_logger(modsec_rec *msr) {
15521609 yajl_gen_array_open (g ); // matched_rules top-level key
15531610#endif
15541611
1555-
15561612 /* Matched Rules */
15571613
15581614 for (i = 0 ; i < msr -> matched_rules -> nelts ; i ++ ) {
@@ -1608,7 +1664,7 @@ void sec_audit_logger(modsec_rec *msr) {
16081664 }
16091665 }
16101666#ifdef WITH_JSON_LOGGING
1611- yajl_gen_array_close (g ); // matched_rules top-level key is finished
1667+ yajl_gen_array_close (g ); // matched_rules top-level key is finished
16121668#endif
16131669
16141670 }
0 commit comments