Skip to content

Commit 53c3604

Browse files
authored
Merge pull request github#5329 from tamasvajk/feature/csv-taint-step
Java: migrate taint steps to CSV
2 parents 46bae88 + d02fba8 commit 53c3604

File tree

2 files changed

+87
-202
lines changed

2 files changed

+87
-202
lines changed

java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,91 @@ private predicate sourceModelCsv(string row) {
182182

183183
private predicate sinkModelCsv(string row) { none() }
184184

185-
private predicate summaryModelCsv(string row) { none() }
185+
private predicate summaryModelCsv(string row) {
186+
row =
187+
[
188+
// qualifier to arg
189+
"java.io;InputStream;true;read;(byte[]);;Argument[-1];Argument[0];taint",
190+
"java.io;InputStream;true;read;(byte[],int,int);;Argument[-1];Argument[0];taint",
191+
"java.io;ByteArrayOutputStream;false;writeTo;;;Argument[-1];Argument[0];taint",
192+
"java.io;Reader;true;read;;;Argument[-1];Argument[0];taint",
193+
// qualifier to return
194+
"java.io;ByteArrayOutputStream;false;toByteArray;;;Argument[-1];ReturnValue;taint",
195+
"java.io;ByteArrayOutputStream;false;toString;;;Argument[-1];ReturnValue;taint",
196+
"java.util;StringTokenizer;false;nextElement;();;Argument[-1];ReturnValue;taint",
197+
"java.util;StringTokenizer;false;nextToken;;;Argument[-1];ReturnValue;taint",
198+
"javax.xml.transform.sax;SAXSource;false;getInputSource;;;Argument[-1];ReturnValue;taint",
199+
"javax.xml.transform.stream;StreamSource;false;getInputStream;;;Argument[-1];ReturnValue;taint",
200+
"java.nio;ByteBuffer;false;get;;;Argument[-1];ReturnValue;taint",
201+
"java.net;URI;false;toURL;;;Argument[-1];ReturnValue;taint",
202+
"java.io;File;false;toURI;;;Argument[-1];ReturnValue;taint",
203+
"java.io;File;false;toPath;;;Argument[-1];ReturnValue;taint",
204+
"java.nio.file;Path;false;toFile;;;Argument[-1];ReturnValue;taint",
205+
"java.io;Reader;true;readLine;;;Argument[-1];ReturnValue;taint",
206+
"java.io;Reader;true;read;();;Argument[-1];ReturnValue;taint",
207+
// arg to return
208+
"java.util;Base64$Encoder;false;encode;(byte[]);;Argument[0];ReturnValue;taint",
209+
"java.util;Base64$Encoder;false;encode;(ByteBuffer);;Argument[0];ReturnValue;taint",
210+
"java.util;Base64$Encoder;false;encodeToString;(byte[]);;Argument[0];ReturnValue;taint",
211+
"java.util;Base64$Encoder;false;wrap;(OutputStream);;Argument[0];ReturnValue;taint",
212+
"java.util;Base64$Decoder;false;decode;(byte[]);;Argument[0];ReturnValue;taint",
213+
"java.util;Base64$Decoder;false;decode;(ByteBuffer);;Argument[0];ReturnValue;taint",
214+
"java.util;Base64$Decoder;false;decode;(String);;Argument[0];ReturnValue;taint",
215+
"java.util;Base64$Decoder;false;wrap;(InputStream);;Argument[0];ReturnValue;taint",
216+
"org.apache.commons.codec;Encoder;true;encode;;;Argument[0];ReturnValue;taint",
217+
"org.apache.commons.codec;Decoder;true;decode;;;Argument[0];ReturnValue;taint",
218+
"org.apache.commons.io;IOUtils;false;buffer;;;Argument[0];ReturnValue;taint",
219+
"org.apache.commons.io;IOUtils;false;readLines;;;Argument[0];ReturnValue;taint",
220+
"org.apache.commons.io;IOUtils;false;readFully;(InputStream,int);;Argument[0];ReturnValue;taint",
221+
"org.apache.commons.io;IOUtils;false;toBufferedInputStream;;;Argument[0];ReturnValue;taint",
222+
"org.apache.commons.io;IOUtils;false;toBufferedReader;;;Argument[0];ReturnValue;taint",
223+
"org.apache.commons.io;IOUtils;false;toByteArray;;;Argument[0];ReturnValue;taint",
224+
"org.apache.commons.io;IOUtils;false;toCharArray;;;Argument[0];ReturnValue;taint",
225+
"org.apache.commons.io;IOUtils;false;toInputStream;;;Argument[0];ReturnValue;taint",
226+
"org.apache.commons.io;IOUtils;false;toString;;;Argument[0];ReturnValue;taint",
227+
"java.net;URLDecoder;false;decode;;;Argument[0];ReturnValue;taint",
228+
"java.net;URI;false;create;;;Argument[0];ReturnValue;taint",
229+
"javax.xml.transform.sax;SAXSource;false;sourceToInputSource;;;Argument[0];ReturnValue;taint",
230+
// arg to arg
231+
"java.lang;System;false;arraycopy;;;Argument[0];Argument[2];taint",
232+
"org.apache.commons.io;IOUtils;false;copy;;;Argument[0];Argument[1];taint",
233+
"org.apache.commons.io;IOUtils;false;copyLarge;;;Argument[0];Argument[1];taint",
234+
"org.apache.commons.io;IOUtils;false;read;;;Argument[0];Argument[1];taint",
235+
"org.apache.commons.io;IOUtils;false;readFully;(InputStream,byte[]);;Argument[0];Argument[1];taint",
236+
"org.apache.commons.io;IOUtils;false;readFully;(InputStream,byte[],int,int);;Argument[0];Argument[1];taint",
237+
"org.apache.commons.io;IOUtils;false;readFully;(InputStream,ByteBuffer);;Argument[0];Argument[1];taint",
238+
"org.apache.commons.io;IOUtils;false;readFully;(ReadableByteChannel,ByteBuffer);;Argument[0];Argument[1];taint",
239+
"org.apache.commons.io;IOUtils;false;readFully;(Reader,char[]);;Argument[0];Argument[1];taint",
240+
"org.apache.commons.io;IOUtils;false;readFully;(Reader,char[],int,int);;Argument[0];Argument[1];taint",
241+
"org.apache.commons.io;IOUtils;false;write;;;Argument[0];Argument[1];taint",
242+
"org.apache.commons.io;IOUtils;false;writeChunked;;;Argument[0];Argument[1];taint",
243+
"org.apache.commons.io;IOUtils;false;writeLines;;;Argument[0];Argument[2];taint",
244+
"org.apache.commons.io;IOUtils;false;writeLines;;;Argument[1];Argument[2];taint",
245+
// constructor flow
246+
"java.io;File;false;File;;;Argument[0];Argument[-1];taint",
247+
"java.io;File;false;File;;;Argument[1];Argument[-1];taint",
248+
"java.net;URI;false;URI;(String);;Argument[0];Argument[-1];taint",
249+
"javax.xml.transform.stream;StreamSource;false;StreamSource;;;Argument[0];Argument[-1];taint",
250+
"javax.xml.transform.sax;SAXSource;false;SAXSource;(InputSource);;Argument[0];Argument[-1];taint",
251+
"javax.xml.transform.sax;SAXSource;false;SAXSource;(XMLReader,InputSource);;Argument[1];Argument[-1];taint",
252+
"org.xml.sax;InputSource;false;InputSource;;;Argument[0];Argument[-1];taint",
253+
"javax.servlet.http;Cookie;false;Cookie;;;Argument[0];Argument[-1];taint",
254+
"javax.servlet.http;Cookie;false;Cookie;;;Argument[1];Argument[-1];taint",
255+
"java.util.zip;ZipInputStream;false;ZipInputStream;;;Argument[0];Argument[-1];taint",
256+
"java.util.zip;GZIPInputStream;false;GZIPInputStream;;;Argument[0];Argument[-1];taint",
257+
"java.util;StringTokenizer;false;StringTokenizer;;;Argument[0];Argument[-1];taint",
258+
"java.beans;XMLDecoder;false;XMLDecoder;;;Argument[0];Argument[-1];taint",
259+
"com.esotericsoftware.kryo.io;Input;false;Input;;;Argument[0];Argument[-1];taint",
260+
"java.io;BufferedInputStream;false;BufferedInputStream;;;Argument[0];Argument[-1];taint",
261+
"java.io;DataInputStream;false;DataInputStream;;;Argument[0];Argument[-1];taint",
262+
"java.io;ByteArrayInputStream;false;ByteArrayInputStream;;;Argument[0];Argument[-1];taint",
263+
"java.io;ObjectInputStream;false;ObjectInputStream;;;Argument[0];Argument[-1];taint",
264+
"java.io;StringReader;false;StringReader;;;Argument[0];Argument[-1];taint",
265+
"java.io;CharArrayReader;false;CharArrayReader;;;Argument[0];Argument[-1];taint",
266+
"java.io;BufferedReader;false;BufferedReader;;;Argument[0];Argument[-1];taint",
267+
"java.io;InputStreamReader;false;InputStreamReader;;;Argument[0];Argument[-1];taint"
268+
]
269+
}
186270

187271
/**
188272
* A unit class for adding additional source model rows.

java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll

Lines changed: 2 additions & 201 deletions
Original file line numberDiff line numberDiff line change
@@ -166,60 +166,6 @@ private predicate inputStreamWrapper(Constructor c, int argi) {
166166
/** An object construction that preserves the data flow status of any of its arguments. */
167167
private predicate constructorStep(Expr tracked, ConstructorCall sink) {
168168
exists(int argi | sink.getArgument(argi) = tracked |
169-
exists(string s | sink.getConstructedType().getQualifiedName() = s |
170-
// some readers preserve the content of streams
171-
s = "java.io.InputStreamReader" and argi = 0
172-
or
173-
s = "java.io.BufferedReader" and argi = 0
174-
or
175-
s = "java.io.CharArrayReader" and argi = 0
176-
or
177-
s = "java.io.StringReader" and argi = 0
178-
or
179-
// data preserved through streams
180-
s = "java.io.ObjectInputStream" and argi = 0
181-
or
182-
s = "java.io.ByteArrayInputStream" and argi = 0
183-
or
184-
s = "java.io.DataInputStream" and argi = 0
185-
or
186-
s = "java.io.BufferedInputStream" and argi = 0
187-
or
188-
s = "com.esotericsoftware.kryo.io.Input" and argi = 0
189-
or
190-
s = "java.beans.XMLDecoder" and argi = 0
191-
or
192-
// a tokenizer preserves the content of a string
193-
s = "java.util.StringTokenizer" and argi = 0
194-
or
195-
// unzipping the stream preserves content
196-
s = "java.util.zip.ZipInputStream" and argi = 0
197-
or
198-
s = "java.util.zip.GZIPInputStream" and argi = 0
199-
or
200-
// a cookie with tainted ingredients is tainted
201-
s = "javax.servlet.http.Cookie" and argi = 0
202-
or
203-
s = "javax.servlet.http.Cookie" and argi = 1
204-
or
205-
// various xml stream source constructors.
206-
s = "org.xml.sax.InputSource" and argi = 0
207-
or
208-
s = "javax.xml.transform.sax.SAXSource" and argi = 0 and sink.getNumArgument() = 1
209-
or
210-
s = "javax.xml.transform.sax.SAXSource" and argi = 1 and sink.getNumArgument() = 2
211-
or
212-
s = "javax.xml.transform.stream.StreamSource" and argi = 0
213-
or
214-
//a URI constructed from a tainted string is tainted.
215-
s = "java.net.URI" and argi = 0 and sink.getNumArgument() = 1
216-
or
217-
//a File constructed from a tainted string is tainted.
218-
s = "java.io.File" and argi = 0
219-
or
220-
s = "java.io.File" and argi = 1
221-
)
222-
or
223169
// wrappers constructed by extension
224170
exists(Constructor c, Parameter p, SuperConstructorInvocationStmt sup |
225171
c = sink.getConstructor() and
@@ -267,32 +213,12 @@ private int argToParam(Call call, int arg) {
267213
/** Access to a method that passes taint from qualifier to argument. */
268214
private predicate qualifierToArgumentStep(Expr tracked, Expr sink) {
269215
exists(MethodAccess ma, int arg |
270-
taintPreservingQualifierToArgument(ma.getMethod(), argToParam(ma, arg)) and
216+
ma.getMethod().(TaintPreservingCallable).transfersTaint(-1, argToParam(ma, arg)) and
271217
tracked = ma.getQualifier() and
272218
sink = ma.getArgument(arg)
273219
)
274220
}
275221

276-
/** Methods that passes tainted data from qualifier to argument. */
277-
private predicate taintPreservingQualifierToArgument(Method m, int arg) {
278-
m.getDeclaringType().hasQualifiedName("java.io", "ByteArrayOutputStream") and
279-
m.hasName("writeTo") and
280-
arg = 0
281-
or
282-
exists(Method read |
283-
m.overrides*(read) and
284-
read.getDeclaringType().hasQualifiedName("java.io", "InputStream") and
285-
read.hasName("read") and
286-
arg = 0
287-
)
288-
or
289-
m.getDeclaringType().getASupertype*().hasQualifiedName("java.io", "Reader") and
290-
m.hasName("read") and
291-
arg = 0
292-
or
293-
m.(TaintPreservingCallable).transfersTaint(-1, arg)
294-
}
295-
296222
/** Access to a method that passes taint from the qualifier. */
297223
private predicate qualifierToMethodStep(Expr tracked, MethodAccess sink) {
298224
(taintPreservingQualifierToMethod(sink.getMethod()) or unsafeEscape(sink)) and
@@ -305,50 +231,16 @@ private predicate qualifierToMethodStep(Expr tracked, MethodAccess sink) {
305231
private predicate taintPreservingQualifierToMethod(Method m) {
306232
m instanceof CloneMethod
307233
or
308-
m.getDeclaringType().getASupertype*().hasQualifiedName("java.io", "Reader") and
309-
(
310-
m.getName() = "read" and m.getNumberOfParameters() = 0
311-
or
312-
m.getName() = "readLine"
313-
)
314-
or
315234
m.getDeclaringType().getQualifiedName().matches("%StringWriter") and
316235
(
317236
m.getName() = "getBuffer"
318237
or
319238
m.getName() = "toString"
320239
)
321240
or
322-
m.getDeclaringType().hasQualifiedName("java.util", "StringTokenizer") and
323-
m.getName().matches("next%")
324-
or
325-
m.getDeclaringType().hasQualifiedName("java.io", "ByteArrayOutputStream") and
326-
(m.getName() = "toByteArray" or m.getName() = "toString")
327-
or
328241
m.getDeclaringType().hasQualifiedName("java.io", "ObjectInputStream") and
329242
m.getName().matches("read%")
330243
or
331-
m.getDeclaringType().hasQualifiedName("javax.xml.transform.sax", "SAXSource") and
332-
m.hasName("getInputSource")
333-
or
334-
m.getDeclaringType().hasQualifiedName("javax.xml.transform.stream", "StreamSource") and
335-
m.hasName("getInputStream")
336-
or
337-
m.getDeclaringType().hasQualifiedName("java.nio", "ByteBuffer") and
338-
m.hasName("get")
339-
or
340-
m.getDeclaringType() instanceof TypeFile and
341-
m.hasName("toPath")
342-
or
343-
m.getDeclaringType() instanceof TypePath and
344-
m.hasName("toFile")
345-
or
346-
m.getDeclaringType() instanceof TypeFile and
347-
m.hasName("toURI")
348-
or
349-
m.getDeclaringType() instanceof TypeUri and
350-
m.hasName("toURL")
351-
or
352244
m instanceof GetterMethod and
353245
m.getDeclaringType().getASubtype*() instanceof SpringUntrustedDataType and
354246
not m.getDeclaringType() instanceof TypeObject
@@ -421,69 +313,13 @@ private predicate argToMethodStep(Expr tracked, MethodAccess sink) {
421313
* `arg`th argument is tainted.
422314
*/
423315
private predicate taintPreservingArgumentToMethod(Method method, int arg) {
424-
(
425-
method.getDeclaringType().hasQualifiedName("java.util", "Base64$Encoder") or
426-
method.getDeclaringType().hasQualifiedName("java.util", "Base64$Decoder") or
427-
method
428-
.getDeclaringType()
429-
.getASupertype*()
430-
.hasQualifiedName("org.apache.commons.codec", "Encoder") or
431-
method
432-
.getDeclaringType()
433-
.getASupertype*()
434-
.hasQualifiedName("org.apache.commons.codec", "Decoder")
435-
) and
436-
(
437-
method.getName() = "encode" and arg = 0 and method.getNumberOfParameters() = 1
438-
or
439-
method.getName() = "decode" and arg = 0 and method.getNumberOfParameters() = 1
440-
or
441-
method.getName() = "encodeToString" and arg = 0
442-
or
443-
method.getName() = "wrap" and arg = 0
444-
)
445-
or
446316
method.getDeclaringType().hasQualifiedName("org.apache.commons.codec.binary", "Base64") and
447317
(
448318
method.getName() = "decodeBase64" and arg = 0
449319
or
450320
method.getName().matches("encodeBase64%") and arg = 0
451321
)
452322
or
453-
method.getDeclaringType().hasQualifiedName("org.apache.commons.io", "IOUtils") and
454-
(
455-
method.getName() = "buffer" and arg = 0
456-
or
457-
method.getName() = "readLines" and arg = 0
458-
or
459-
method.getName() = "readFully" and arg = 0 and method.getParameterType(1).hasName("int")
460-
or
461-
method.getName() = "toBufferedInputStream" and arg = 0
462-
or
463-
method.getName() = "toBufferedReader" and arg = 0
464-
or
465-
method.getName() = "toByteArray" and arg = 0
466-
or
467-
method.getName() = "toCharArray" and arg = 0
468-
or
469-
method.getName() = "toInputStream" and arg = 0
470-
or
471-
method.getName() = "toString" and arg = 0
472-
)
473-
or
474-
method.getDeclaringType().hasQualifiedName("java.net", "URLDecoder") and
475-
method.hasName("decode") and
476-
arg = 0
477-
or
478-
// A URI created from a tainted string is still tainted.
479-
method.getDeclaringType() instanceof TypeUri and
480-
method.hasName("create") and
481-
arg = 0
482-
or
483-
method.getDeclaringType().hasQualifiedName("javax.xml.transform.sax", "SAXSource") and
484-
method.hasName("sourceToInputSource") and
485-
arg = 0
486-
or
487323
method.(TaintPreservingCallable).returnsTaintFrom(arg)
488324
}
489325

@@ -493,48 +329,13 @@ private predicate taintPreservingArgumentToMethod(Method method, int arg) {
493329
*/
494330
private predicate argToArgStep(Expr tracked, Expr sink) {
495331
exists(MethodAccess ma, Method method, int input, int output |
496-
taintPreservingArgToArg(method, argToParam(ma, input), argToParam(ma, output)) and
332+
method.(TaintPreservingCallable).transfersTaint(argToParam(ma, input), argToParam(ma, output)) and
497333
ma.getMethod() = method and
498334
ma.getArgument(input) = tracked and
499335
ma.getArgument(output) = sink
500336
)
501337
}
502338

503-
/**
504-
* Holds if `method` is a library method that writes tainted data to the
505-
* `output`th argument if the `input`th argument is tainted.
506-
*/
507-
private predicate taintPreservingArgToArg(Method method, int input, int output) {
508-
method.getDeclaringType().hasQualifiedName("org.apache.commons.io", "IOUtils") and
509-
(
510-
method.hasName("copy") and input = 0 and output = 1
511-
or
512-
method.hasName("copyLarge") and input = 0 and output = 1
513-
or
514-
method.hasName("read") and input = 0 and output = 1
515-
or
516-
method.hasName("readFully") and
517-
input = 0 and
518-
output = 1 and
519-
not method.getParameterType(1).hasName("int")
520-
or
521-
method.hasName("write") and input = 0 and output = 1
522-
or
523-
method.hasName("writeChunked") and input = 0 and output = 1
524-
or
525-
method.hasName("writeLines") and input = 0 and output = 2
526-
or
527-
method.hasName("writeLines") and input = 1 and output = 2
528-
)
529-
or
530-
method.getDeclaringType().hasQualifiedName("java.lang", "System") and
531-
method.hasName("arraycopy") and
532-
input = 0 and
533-
output = 2
534-
or
535-
method.(TaintPreservingCallable).transfersTaint(input, output)
536-
}
537-
538339
/**
539340
* Holds if `tracked` is the argument of a method that transfers taint
540341
* from the argument to the qualifier and `sink` is the qualifier.

0 commit comments

Comments
 (0)