5
5
import static com .datadog .iast .taint .Ranges .mergeRanges ;
6
6
import static com .datadog .iast .taint .Tainteds .canBeTainted ;
7
7
import static com .datadog .iast .taint .Tainteds .getTainted ;
8
+ import static datadog .trace .api .telemetry .LogCollector .SEND_TELEMETRY ;
8
9
9
10
import com .datadog .iast .model .Range ;
10
11
import com .datadog .iast .taint .Ranges ;
20
21
import java .util .Iterator ;
21
22
import java .util .LinkedList ;
22
23
import java .util .Locale ;
24
+ import java .util .Map ;
25
+ import java .util .function .Function ;
23
26
import java .util .regex .Matcher ;
24
27
import java .util .regex .Pattern ;
28
+ import java .util .stream .Collectors ;
29
+ import java .util .stream .Stream ;
25
30
import javax .annotation .Nonnull ;
26
31
import javax .annotation .Nullable ;
27
32
@@ -31,6 +36,10 @@ public class StringModuleImpl implements StringModule {
31
36
private static final Pattern FORMAT_PATTERN =
32
37
Pattern .compile ("%(?<index>\\ d+\\ $)?([-#+ 0,(\\ <]*)?(\\ d+)?(\\ .\\ d+)?([tT])?([a-zA-Z%])" );
33
38
39
+ /** Escaped format patterns * */
40
+ private static final Map <String , String > ESCAPED_PATTERNS =
41
+ Stream .of ("%%" , "%n" ).collect (Collectors .toMap (Function .identity (), String ::format ));
42
+
34
43
private static final Ranged END = Ranged .build (Integer .MAX_VALUE , 0 );
35
44
36
45
private static final int NULL_STR_LENGTH = "null" .length ();
@@ -442,21 +451,32 @@ public void onStringFormat(
442
451
final String placeholder = matcher .group ();
443
452
final Object parameter ;
444
453
final String formattedValue ;
445
- final String index = matcher .group ("index" );
446
- if (index != null ) {
447
- // indexes are 1-based
448
- final int parsedIndex = Integer .parseInt (index .substring (0 , index .length () - 1 )) - 1 ;
449
- // remove the index before the formatting without increment the current state
450
- parameter = parameters [parsedIndex ];
451
- formattedValue = String .format (locale , placeholder .replace (index , "" ), parameter );
454
+ final TaintedObject taintedObject ;
455
+ final String escaped = ESCAPED_PATTERNS .get (placeholder );
456
+ if (escaped != null ) {
457
+ parameter = placeholder ;
458
+ formattedValue = escaped ;
459
+ taintedObject = null ;
452
460
} else {
453
- parameter = parameters [paramIndex ++];
454
- formattedValue = String .format (locale , placeholder , parameter );
461
+ final String index = matcher .group ("index" );
462
+ if (index != null ) {
463
+ // indexes are 1-based
464
+ final int parsedIndex = Integer .parseInt (index .substring (0 , index .length () - 1 )) - 1 ;
465
+ // remove the index before the formatting without increment the current state
466
+ parameter = parameters [parsedIndex ];
467
+ formattedValue = String .format (locale , placeholder .replace (index , "" ), parameter );
468
+ } else {
469
+ if (!checkParameterBounds (format , parameters , paramIndex )) {
470
+ return ; // return without tainting the string in case of error
471
+ }
472
+ parameter = parameters [paramIndex ++];
473
+ formattedValue = String .format (locale , placeholder , parameter );
474
+ }
475
+ taintedObject = to .get (parameter );
455
476
}
456
477
final Ranged placeholderPos = Ranged .build (matcher .start (), placeholder .length ());
457
478
final Range placeholderRange =
458
479
addFormatTaintedRanges (placeholderPos , offset , formatRanges , finalRanges );
459
- final TaintedObject taintedObject = to .get (parameter );
460
480
final Range [] paramRanges = taintedObject == null ? null : taintedObject .getRanges ();
461
481
final int shift = placeholderPos .getStart () + offset ;
462
482
addParameterTaintedRanges (
@@ -473,6 +493,20 @@ public void onStringFormat(
473
493
}
474
494
}
475
495
496
+ private static boolean checkParameterBounds (
497
+ final String format , final Object [] parameters , int paramIndex ) {
498
+ if (paramIndex < parameters .length ) {
499
+ return true ;
500
+ }
501
+ LOG .debug (
502
+ SEND_TELEMETRY ,
503
+ "Error handling string format pattern {} with args {} at index {}" ,
504
+ format ,
505
+ parameters .length ,
506
+ paramIndex );
507
+ return false ;
508
+ }
509
+
476
510
@ Override
477
511
public void onStringFormat (
478
512
@ Nonnull final Iterable <String > literals ,
0 commit comments