@@ -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)
558578yajl_kv_int (g ,"phase" ,rule -> actionset -> phase );
559579 }
560580yajl_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+ }
561584yajl_gen_map_close (g );
562585
563586yajl_string (g ,"operator" );
@@ -602,6 +625,7 @@ void sec_audit_logger(modsec_rec *msr) {
602625int arg_min ,arg_max ,sanitize_matched ;
603626#ifdef WITH_JSON_LOGGING
604627yajl_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) {
704728g = 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 */
710734yajl_gen_config (g ,yajl_gen_beautify ,0 );
@@ -771,16 +795,14 @@ void sec_audit_logger(modsec_rec *msr) {
771795for (i = 0 ;i < arr -> nelts ;i ++ ) {
772796sanitized_partial = 0 ;
773797sanitize_matched = 0 ;
774- #ifndef WITH_JSON_LOGGING
775798text = 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
779802yajl_string (g ,te [i ].key );
780803#endif
781804if (apr_table_get (msr -> request_headers_to_sanitize ,te [i ].key )!= NULL ) {
782805buf = apr_psprintf (msr -> mp ,"%s" ,text + strlen (te [i ].key )+ 2 );
783-
784806for (k = 0 ;k < tarr_pattern -> nelts ;k ++ ) {
785807if (strncmp (telts_pattern [k ].key ,te [i ].key ,strlen (te [i ].key ))== 0 ) {
786808mparm = (msc_parm * )telts_pattern [k ].val ;
@@ -818,7 +840,8 @@ void sec_audit_logger(modsec_rec *msr) {
818840#ifndef WITH_JSON_LOGGING
819841memset (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) {
10251050sec_auditlog_write (msr ,text ,strlen (text ));
10261051sec_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
10291053yajl_kv_string (g ,"fake_body" ,buffer );
10301054#endif
10311055 }
@@ -1055,7 +1079,8 @@ void sec_audit_logger(modsec_rec *msr) {
10551079msr -> status_line );
10561080#else
10571081yajl_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) {
10841109for (i = 0 ;i < arr -> nelts ;i ++ ) {
10851110sanitized_partial = 0 ;
10861111sanitize_matched = 0 ;
1087- #ifndef WITH_JSON_LOGGING
10881112text = 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
10921116yajl_string (g ,te [i ].key );
@@ -1131,7 +1155,8 @@ void sec_audit_logger(modsec_rec *msr) {
11311155#ifndef WITH_JSON_LOGGING
11321156memset (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
11701197yajl_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
11901221for (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
12071244for (i = 0 ;i < msr -> error_messages -> nelts ;i ++ ) {
12081245error_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) {
12281267yajl_gen_map_open (g );
12291268yajl_kv_bool (g ,"intercepted" ,1 );
12301269yajl_kv_int (g ,"phase" ,msr -> intercept_phase );
1270+ yajl_kv_string (g ,"message" ,msr -> intercept_message );
12311271yajl_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 */
12461287text = apr_psprintf (msr -> mp ,"Stopwatch: %" APR_TIME_T_FMT " %" APR_TIME_T_FMT " (- - -)\n" ,
12471288msr -> request_time , (now - msr -> request_time ));
1248- #ifndef WITH_JSON_LOGGING
12491289sec_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 {
12561295char * perf_all = format_all_performance_variables (msr ,msr -> mp );
12571296
12581297text = 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
12611299sec_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) {
13101347text = apr_psprintf (msr -> mp ,"Sanitised-Args: " );
13111348sec_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+
13131356yajl_string (g ,"args" );
13141357yajl_gen_array_open (g );
13151358#endif
@@ -1346,6 +1389,12 @@ void sec_audit_logger(modsec_rec *msr) {
13461389text = apr_psprintf (msr -> mp ,"Sanitised-Request-Headers: " );
13471390sec_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+
13491398yajl_string (g ,"request_headers" );
13501399yajl_gen_array_open (g );
13511400#endif
@@ -1380,6 +1429,12 @@ void sec_audit_logger(modsec_rec *msr) {
13801429text = apr_psprintf (msr -> mp ,"Sanitised-Response-Headers: " );
13811430sec_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+
13831438yajl_string (g ,"response_headers" );
13841439yajl_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) {
15521609yajl_gen_array_open (g );// matched_rules top-level key
15531610#endif
15541611
1555-
15561612/* Matched Rules */
15571613
15581614for (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 }