@@ -13,6 +13,7 @@ private import semmle.code.java.frameworks.spring.SpringHttp
1313private import semmle.code.java.Maps
1414private import semmle.code.java.dataflow.internal.ContainerFlow
1515private import semmle.code.java.frameworks.jackson.JacksonSerializability
16+ private import semmle.code.java.StringFormat
1617
1718/**
1819 * Holds if taint can flow from `src` to `sink` in zero or more
@@ -124,6 +125,8 @@ private predicate localAdditionalTaintExprStep(Expr src, Expr sink) {
124125 stringBuilderStep ( src , sink )
125126 or
126127 serializationStep ( src , sink )
128+ or
129+ formatStep ( src , sink )
127130}
128131
129132/**
@@ -387,6 +390,11 @@ private predicate taintPreservingQualifierToMethod(Method m) {
387390 stringlist .getTypeArgument ( 0 ) instanceof TypeString
388391 )
389392 )
393+ or
394+ m instanceof StringFormatMethod
395+ or
396+ m .getDeclaringType ( ) instanceof TypeFormatter and
397+ m .hasName ( "out" )
390398}
391399
392400private class StringReplaceMethod extends Method {
@@ -446,7 +454,9 @@ private predicate argToMethodStep(Expr tracked, MethodAccess sink) {
446454 */
447455private predicate taintPreservingArgumentToMethod ( Method method ) {
448456 method .getDeclaringType ( ) instanceof TypeString and
449- ( method .hasName ( "format" ) or method .hasName ( "formatted" ) or method .hasName ( "join" ) )
457+ method .hasName ( "join" )
458+ or
459+ method instanceof StringFormatMethod
450460}
451461
452462/**
@@ -625,6 +635,21 @@ private predicate argToQualifierStep(Expr tracked, Expr sink) {
625635 tracked = ma .getArgument ( i ) and
626636 sink = ma .getQualifier ( )
627637 )
638+ or
639+ exists ( Method m , MethodAccess ma |
640+ taintPreservingArgumentToQualifier ( m ) and
641+ ma .getMethod ( ) = m and
642+ tracked = ma .getAnArgument ( ) and
643+ sink = ma .getQualifier ( )
644+ )
645+ }
646+
647+ /**
648+ * Holds if `method` is a method that transfers taint from any of its arguments to its qualifier.
649+ */
650+ private predicate taintPreservingArgumentToQualifier ( Method method ) {
651+ method instanceof StringFormatMethod and
652+ not method .getDeclaringType ( ) instanceof TypeString
628653}
629654
630655/**
@@ -722,6 +747,56 @@ class ObjectOutputStreamVar extends LocalVariableDecl {
722747 }
723748}
724749
750+ /** Flow through string formatting. */
751+ private predicate formatStep ( Expr tracked , Expr sink ) {
752+ exists ( FormatterVar v , VariableAssign def |
753+ def = v .getADef ( ) and
754+ exists ( MethodAccess ma , RValue use |
755+ ma .getAnArgument ( ) = tracked and
756+ ma = v .getAFormatMethodAccess ( ) and
757+ use = ma .getQualifier ( ) and
758+ defUsePair ( def , use )
759+ ) and
760+ exists ( RValue output , ClassInstanceExpr cie |
761+ cie = def .getSource ( ) and
762+ output = cie .getArgument ( 0 ) and
763+ adjacentUseUse ( output , sink ) and
764+ exists ( RefType t | output .getType ( ) .( RefType ) .getASourceSupertype * ( ) = t |
765+ t .hasQualifiedName ( "java.io" , "OutputStream" ) or
766+ t .hasQualifiedName ( "java.lang" , "Appendable" )
767+ )
768+ )
769+ )
770+ }
771+
772+ /**
773+ * A local variable that is assigned a `Formatter`.
774+ * Writing tainted data to such a formatter causes the underlying
775+ * `OutputStream` or `Appenable` to be tainted.
776+ */
777+ private class FormatterVar extends LocalVariableDecl {
778+ FormatterVar ( ) {
779+ exists ( ClassInstanceExpr cie | cie = this .getAnAssignedValue ( ) |
780+ cie .getType ( ) instanceof TypeFormatter
781+ )
782+ }
783+
784+ VariableAssign getADef ( ) {
785+ result .getSource ( ) .( ClassInstanceExpr ) .getType ( ) instanceof TypeFormatter and
786+ result .getDestVar ( ) = this
787+ }
788+
789+ MethodAccess getAFormatMethodAccess ( ) {
790+ result .getQualifier ( ) = getAnAccess ( ) and
791+ result .getMethod ( ) .hasName ( "format" )
792+ }
793+ }
794+
795+ /** The class `java.util.Formatter`. */
796+ private class TypeFormatter extends Class {
797+ TypeFormatter ( ) { this .hasQualifiedName ( "java.util" , "Formatter" ) }
798+ }
799+
725800private import StringBuilderVarModule
726801
727802module StringBuilderVarModule {
0 commit comments