|
| 1 | +/** Provides configurations to be used in queries related to regex injection. */ |
| 2 | + |
1 | 3 | import java |
2 | 4 | import semmle.code.java.dataflow.FlowSources |
3 | 5 | import semmle.code.java.dataflow.TaintTracking |
4 | | -import semmle.code.java.regex.RegexFlowConfigs |
5 | | - |
6 | | -/** The Java class `java.util.regex.Pattern`. */ |
7 | | -private class RegexPattern extends RefType { |
8 | | - RegexPattern() { this.hasQualifiedName("java.util.regex", "Pattern") } |
9 | | -} |
10 | | - |
11 | | -/** The Java class `java.util.regex.Matcher`. */ |
12 | | -private class RegexMatcher extends RefType { |
13 | | - RegexMatcher() { this.hasQualifiedName("java.util.regex", "Matcher") } |
14 | | -} |
15 | | - |
16 | | -/** The Java class `org.apache.commons.lang3.RegExUtils`. */ |
17 | | -private class ApacheRegExUtils extends RefType { |
18 | | - ApacheRegExUtils() { this.hasQualifiedName("java.util.regex", "Matcher") } |
19 | | -} |
20 | | - |
21 | | -// TODO: Look for above in pre-existing regex libraries again. |
22 | | -// TODO: look into further: Pattern.matcher, .pattern() and .toString() as taint steps, .split and .splitAsStream |
23 | | -/** |
24 | | - * A data flow sink for untrusted user input used to construct regular expressions. |
25 | | - */ |
26 | | -class RegexSink extends DataFlow::ExprNode { |
27 | | - RegexSink() { |
28 | | - exists(MethodAccess ma, Method m | m = ma.getMethod() | |
29 | | - ma.getArgument(0) = this.asExpr() and |
30 | | - ( |
31 | | - m.getDeclaringType() instanceof TypeString and |
32 | | - m.hasName(["matches", "split", "replaceFirst", "replaceAll"]) |
33 | | - or |
34 | | - m.getDeclaringType() instanceof RegexPattern and |
35 | | - m.hasName(["compile", "matches"]) |
36 | | - ) |
37 | | - or |
38 | | - m.getDeclaringType() instanceof ApacheRegExUtils and |
39 | | - ( |
40 | | - ma.getArgument(1) = this.asExpr() and |
41 | | - m.getParameterType(1) instanceof TypeString and // only does String here because other option is Pattern, but that's already handled by `java.util.regex.Pattern` above |
42 | | - m.hasName([ |
43 | | - "removeAll", "removeFirst", "removePattern", "replaceAll", "replaceFirst", |
44 | | - "replacePattern" |
45 | | - ]) |
46 | | - ) |
47 | | - ) |
48 | | - } |
49 | | -} |
50 | | - |
51 | | -// ! keep and rename to RegexInjectionSanitizer IF makes sense to have two sanitizers extending it?; |
52 | | -// ! else, ask Tony/others about if stylistically better to keep it (see default example in LogInjection.qll, etc.) |
53 | | -// ! maybe make abstract classes for source and sink as well (if you do this, mention it in PR description as an attempt to be similar to the other languages' implementations) |
54 | | -abstract class Sanitizer extends DataFlow::ExprNode { } |
55 | | - |
56 | | -/** |
57 | | - * A call to a function whose name suggests that it escapes regular |
58 | | - * expression meta-characters. |
59 | | - */ |
60 | | -// ! rename as DefaultRegexInjectionSanitizer? |
61 | | -class RegExpSanitizationCall extends Sanitizer { |
62 | | - RegExpSanitizationCall() { |
63 | | - exists(string calleeName, string sanitize, string regexp | |
64 | | - calleeName = this.asExpr().(Call).getCallee().getName() and |
65 | | - // ! add test case for sanitize? I think current tests only check escape |
66 | | - sanitize = "(?:escape|saniti[sz]e)" and // TODO: confirm this is sufficient |
67 | | - regexp = "regexp?" // TODO: confirm this is sufficient |
68 | | - | |
69 | | - calleeName |
70 | | - .regexpMatch("(?i)(" + sanitize + ".*" + regexp + ".*)" + "|(" + regexp + ".*" + sanitize + |
71 | | - ".*)") // TODO: confirm this is sufficient |
72 | | - ) |
73 | | - or |
74 | | - // adds Pattern.quote() as a sanitizer |
75 | | - // see https://rules.sonarsource.com/java/RSPEC-2631 and https://sensei.securecodewarrior.com/recipes/scw:java:regex-injection |
76 | | - exists(MethodAccess ma, Method m | m = ma.getMethod() | |
77 | | - m.getDeclaringType() instanceof RegexPattern and |
78 | | - ( |
79 | | - ma.getArgument(0) = this.asExpr() and |
80 | | - m.hasName("quote") |
81 | | - ) |
82 | | - ) |
83 | | - } |
84 | | -} |
| 6 | +import semmle.code.java.security.RegexInjection |
85 | 7 |
|
86 | 8 | /** |
87 | 9 | * A taint-tracking configuration for untrusted user input used to construct regular expressions. |
88 | 10 | */ |
89 | 11 | class RegexInjectionConfiguration extends TaintTracking::Configuration { |
90 | | - RegexInjectionConfiguration() { this = "RegexInjectionConfiguration" } |
| 12 | + RegexInjectionConfiguration() { this = "RegexInjection" } |
91 | 13 |
|
92 | 14 | override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } |
93 | 15 |
|
94 | | - override predicate isSink(DataFlow::Node sink) { sink instanceof RegexSink } |
| 16 | + override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } |
95 | 17 |
|
96 | | - // ! testing below RegexFlowSink from RegexFlowConfigs.qll |
97 | | - // ! extra results from jfinal with this... look into further... |
98 | | - // override predicate isSink(DataFlow::Node sink) { sink instanceof RegexFlowSink } |
99 | 18 | override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer } |
100 | 19 | } |
0 commit comments