|
11 | 11 | * external/cwe/cwe-020 |
12 | 12 | */ |
13 | 13 |
|
14 | | -import codeql.ruby.DataFlow |
15 | | -import codeql.ruby.StringOps |
16 | | -import codeql.ruby.security.performance.RegExpTreeView::RegExpPatterns as RegExpPatterns |
17 | | - |
18 | | -/** |
19 | | - * A check on a string for whether it contains a given substring, possibly with restrictions on the location of the substring. |
20 | | - */ |
21 | | -class SomeSubstringCheck extends DataFlow::Node { |
22 | | - DataFlow::Node substring; |
23 | | - |
24 | | - SomeSubstringCheck() { |
25 | | - this.(StringOps::StartsWith).getSubstring() = substring or |
26 | | - this.(StringOps::Includes).getSubstring() = substring or |
27 | | - this.(StringOps::EndsWith).getSubstring() = substring |
28 | | - } |
29 | | - |
30 | | - /** |
31 | | - * Gets the substring. |
32 | | - */ |
33 | | - DataFlow::Node getSubstring() { result = substring } |
34 | | -} |
35 | | - |
36 | | -from SomeSubstringCheck check, DataFlow::Node substring, string target, string msg |
37 | | -where |
38 | | - substring = check.getSubstring() and |
39 | | - substring.asExpr().getExpr().getConstantValue().getString() = target and |
40 | | - ( |
41 | | - // target contains a domain on a common TLD, and perhaps some other URL components |
42 | | - target |
43 | | - .regexpMatch("(?i)([a-z]*:?//)?\\.?([a-z0-9-]+\\.)+" + RegExpPatterns::commonTLD() + |
44 | | - "(:[0-9]+)?/?") |
45 | | - or |
46 | | - // target is a HTTP URL to a domain on any TLD |
47 | | - target.regexpMatch("(?i)https?://([a-z0-9-]+\\.)+([a-z]+)(:[0-9]+)?/?") |
48 | | - or |
49 | | - // target is a HTTP URL to a domain on any TLD with path elements, and the check is an includes check |
50 | | - check instanceof StringOps::Includes and |
51 | | - target.regexpMatch("(?i)https?://([a-z0-9-]+\\.)+([a-z]+)(:[0-9]+)?/[a-z0-9/_-]+") |
52 | | - ) and |
53 | | - ( |
54 | | - if check instanceof StringOps::StartsWith |
55 | | - then msg = "may be followed by an arbitrary host name" |
56 | | - else |
57 | | - if check instanceof StringOps::EndsWith |
58 | | - then msg = "may be preceded by an arbitrary host name" |
59 | | - else msg = "can be anywhere in the URL, and arbitrary hosts may come before or after it" |
60 | | - ) and |
61 | | - // whitelist |
62 | | - not ( |
63 | | - // the leading dot in a subdomain sequence makes the suffix-check safe (if it is performed on the host of the url) |
64 | | - check instanceof StringOps::EndsWith and |
65 | | - target.regexpMatch("(?i)\\.([a-z0-9-]+)(\\.[a-z0-9-]+)+") |
66 | | - or |
67 | | - // the trailing port or slash makes the prefix-check safe |
68 | | - check instanceof StringOps::StartsWith and |
69 | | - target.regexpMatch(".*(:[0-9]+|/)") |
70 | | - ) |
71 | | -select check, "'$@' " + msg + ".", substring, target |
| 14 | +import IncompleteUrlSubstringSanitization |
0 commit comments