trapline 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +585 -0
- data/config.ru.example +21 -0
- data/exe/trapline +8 -0
- data/lib/generators/trapline/install_generator.rb +34 -0
- data/lib/generators/trapline/templates/initializer.rb +23 -0
- data/lib/tasks/trapline.rake +19 -0
- data/lib/trapline/cli.rb +212 -0
- data/lib/trapline/configuration.rb +19 -0
- data/lib/trapline/cve/client.rb +203 -0
- data/lib/trapline/cve/record.rb +160 -0
- data/lib/trapline/engine.rb +243 -0
- data/lib/trapline/generator.rb +236 -0
- data/lib/trapline/languages.rb +65 -0
- data/lib/trapline/railtie.rb +26 -0
- data/lib/trapline/rule.rb +165 -0
- data/lib/trapline/runtime.rb +148 -0
- data/lib/trapline/store.rb +113 -0
- data/lib/trapline/suite.rb +44 -0
- data/lib/trapline/templates/cwe/cwe-22.yml +167 -0
- data/lib/trapline/templates/cwe/cwe-327.yml +124 -0
- data/lib/trapline/templates/cwe/cwe-502.yml +123 -0
- data/lib/trapline/templates/cwe/cwe-601.yml +124 -0
- data/lib/trapline/templates/cwe/cwe-611.yml +87 -0
- data/lib/trapline/templates/cwe/cwe-78.yml +166 -0
- data/lib/trapline/templates/cwe/cwe-79.yml +164 -0
- data/lib/trapline/templates/cwe/cwe-798.yml +125 -0
- data/lib/trapline/templates/cwe/cwe-89.yml +189 -0
- data/lib/trapline/templates/cwe/cwe-917.yml +46 -0
- data/lib/trapline/templates/cwe/cwe-918.yml +166 -0
- data/lib/trapline/templates/cwe/cwe-94.yml +131 -0
- data/lib/trapline/templates.rb +261 -0
- data/lib/trapline/validator.rb +359 -0
- data/lib/trapline/version.rb +5 -0
- data/lib/trapline/web/app.rb +234 -0
- data/lib/trapline/web/assets/THIRD_PARTY_LICENSES.txt +214 -0
- data/lib/trapline/web/assets/app.css +399 -0
- data/lib/trapline/web/assets/app.js +804 -0
- data/lib/trapline/web/assets/codemirror.css +344 -0
- data/lib/trapline/web/assets/codemirror.js +13263 -0
- data/lib/trapline/web/assets/index.html +165 -0
- data/lib/trapline/web/assets/jetbrains-mono-latin-400-normal.woff2 +0 -0
- data/lib/trapline/web/assets/jetbrains-mono-latin-600-normal.woff2 +0 -0
- data/lib/trapline/web/assets/public-sans-latin-400-normal.woff2 +0 -0
- data/lib/trapline/web/assets/public-sans-latin-500-normal.woff2 +0 -0
- data/lib/trapline/web/assets/public-sans-latin-600-normal.woff2 +0 -0
- data/lib/trapline/web/assets/public-sans-latin-700-normal.woff2 +0 -0
- data/lib/trapline/web/server.rb +108 -0
- data/lib/trapline.rb +70 -0
- metadata +136 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
cwe: CWE-79
|
|
2
|
+
name: "Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')"
|
|
3
|
+
title: Cross-site scripting (XSS)
|
|
4
|
+
owasp: ["A03:2021 - Injection"]
|
|
5
|
+
keywords: ["cross-site scripting", "xss", "inject arbitrary web script", "html injection"]
|
|
6
|
+
message: >-
|
|
7
|
+
Untrusted input is written to an HTML response without escaping, which can
|
|
8
|
+
lead to cross-site scripting. Escape the value or use the framework's
|
|
9
|
+
auto-escaping templates.
|
|
10
|
+
languages:
|
|
11
|
+
ruby:
|
|
12
|
+
rule: |
|
|
13
|
+
mode: taint
|
|
14
|
+
pattern-sources:
|
|
15
|
+
- pattern: params
|
|
16
|
+
- pattern: cookies
|
|
17
|
+
pattern-sanitizers:
|
|
18
|
+
- pattern: ERB::Util.html_escape(...)
|
|
19
|
+
- pattern: CGI.escapeHTML(...)
|
|
20
|
+
- pattern: sanitize(...)
|
|
21
|
+
- pattern: $H.sanitize(...)
|
|
22
|
+
- pattern: h(...)
|
|
23
|
+
pattern-sinks:
|
|
24
|
+
- patterns:
|
|
25
|
+
- pattern-either:
|
|
26
|
+
- pattern: $X.html_safe
|
|
27
|
+
- pattern: raw($X)
|
|
28
|
+
- pattern: $H.raw($X)
|
|
29
|
+
- pattern: "render(..., inline: $X, ...)"
|
|
30
|
+
- focus-metavariable: $X
|
|
31
|
+
test: |
|
|
32
|
+
class SearchController < ApplicationController
|
|
33
|
+
def show
|
|
34
|
+
# ruleid: __RULE_ID__
|
|
35
|
+
render html: "<h1>#{params[:q]}</h1>".html_safe
|
|
36
|
+
# ruleid: __RULE_ID__
|
|
37
|
+
render inline: "<p>#{params[:q]}</p>"
|
|
38
|
+
# ok: __RULE_ID__
|
|
39
|
+
render html: "<h1>#{ERB::Util.html_escape(params[:q])}</h1>".html_safe
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
python:
|
|
43
|
+
rule: |
|
|
44
|
+
mode: taint
|
|
45
|
+
pattern-sources:
|
|
46
|
+
- pattern: flask.request.$ANY
|
|
47
|
+
pattern-sanitizers:
|
|
48
|
+
- pattern: markupsafe.escape(...)
|
|
49
|
+
- pattern: flask.escape(...)
|
|
50
|
+
- pattern: html.escape(...)
|
|
51
|
+
pattern-sinks:
|
|
52
|
+
- pattern: flask.make_response(...)
|
|
53
|
+
- pattern: flask.Response(...)
|
|
54
|
+
- pattern: markupsafe.Markup(...)
|
|
55
|
+
- pattern: flask.render_template_string(...)
|
|
56
|
+
test: |
|
|
57
|
+
from flask import request, make_response
|
|
58
|
+
from markupsafe import escape
|
|
59
|
+
|
|
60
|
+
def hello():
|
|
61
|
+
name = request.args.get("name")
|
|
62
|
+
# ruleid: __RULE_ID__
|
|
63
|
+
return make_response("<h1>Hello " + name + "</h1>")
|
|
64
|
+
|
|
65
|
+
def hello_safe():
|
|
66
|
+
name = request.args.get("name")
|
|
67
|
+
# ok: __RULE_ID__
|
|
68
|
+
return make_response("<h1>Hello " + escape(name) + "</h1>")
|
|
69
|
+
javascript:
|
|
70
|
+
rule: |
|
|
71
|
+
mode: taint
|
|
72
|
+
pattern-sources:
|
|
73
|
+
- pattern: $REQ.query
|
|
74
|
+
- pattern: $REQ.body
|
|
75
|
+
- pattern: $REQ.params
|
|
76
|
+
- pattern: location.hash
|
|
77
|
+
- pattern: location.search
|
|
78
|
+
pattern-sanitizers:
|
|
79
|
+
- pattern: escapeHtml(...)
|
|
80
|
+
- pattern: $L.escape(...)
|
|
81
|
+
- pattern: DOMPurify.sanitize(...)
|
|
82
|
+
- pattern: encodeURIComponent(...)
|
|
83
|
+
pattern-sinks:
|
|
84
|
+
- pattern: $RES.send(...)
|
|
85
|
+
- pattern: $RES.write(...)
|
|
86
|
+
- pattern: document.write(...)
|
|
87
|
+
- patterns:
|
|
88
|
+
- pattern-either:
|
|
89
|
+
- pattern: $EL.innerHTML = $X
|
|
90
|
+
- pattern: $EL.outerHTML = $X
|
|
91
|
+
- pattern: $EL.insertAdjacentHTML($POS, $X)
|
|
92
|
+
- focus-metavariable: $X
|
|
93
|
+
test: |
|
|
94
|
+
app.get("/hello", (req, res) => {
|
|
95
|
+
// ruleid: __RULE_ID__
|
|
96
|
+
res.send("<h1>Hello " + req.query.name + "</h1>");
|
|
97
|
+
// ok: __RULE_ID__
|
|
98
|
+
res.send("<h1>Hello " + escapeHtml(req.query.name) + "</h1>");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
function render() {
|
|
102
|
+
// ruleid: __RULE_ID__
|
|
103
|
+
document.getElementById("out").innerHTML = location.hash.slice(1);
|
|
104
|
+
// ok: __RULE_ID__
|
|
105
|
+
document.getElementById("out").textContent = location.hash.slice(1);
|
|
106
|
+
}
|
|
107
|
+
java:
|
|
108
|
+
rule: |
|
|
109
|
+
mode: taint
|
|
110
|
+
pattern-sources:
|
|
111
|
+
- pattern: (javax.servlet.http.HttpServletRequest $REQ).$GET(...)
|
|
112
|
+
- pattern: (jakarta.servlet.http.HttpServletRequest $REQ).$GET(...)
|
|
113
|
+
pattern-sanitizers:
|
|
114
|
+
- pattern: Encode.$FOR(...)
|
|
115
|
+
- pattern: StringEscapeUtils.escapeHtml4(...)
|
|
116
|
+
- pattern: HtmlUtils.htmlEscape(...)
|
|
117
|
+
pattern-sinks:
|
|
118
|
+
- pattern: $RESP.getWriter().$WRITE(...)
|
|
119
|
+
- pattern: (java.io.PrintWriter $W).$WRITE(...)
|
|
120
|
+
test: |
|
|
121
|
+
import javax.servlet.http.HttpServletRequest;
|
|
122
|
+
import javax.servlet.http.HttpServletResponse;
|
|
123
|
+
import org.owasp.encoder.Encode;
|
|
124
|
+
|
|
125
|
+
public class HelloServlet {
|
|
126
|
+
public void doGet(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
|
127
|
+
String name = request.getParameter("name");
|
|
128
|
+
// ruleid: __RULE_ID__
|
|
129
|
+
response.getWriter().write("<h1>Hello " + name + "</h1>");
|
|
130
|
+
// ok: __RULE_ID__
|
|
131
|
+
response.getWriter().write("<h1>Hello " + Encode.forHtml(name) + "</h1>");
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
go:
|
|
135
|
+
rule: |
|
|
136
|
+
mode: taint
|
|
137
|
+
pattern-sources:
|
|
138
|
+
- pattern: '($R : *http.Request).URL.Query().Get(...)'
|
|
139
|
+
- pattern: '($R : *http.Request).FormValue(...)'
|
|
140
|
+
pattern-sanitizers:
|
|
141
|
+
- pattern: html.EscapeString(...)
|
|
142
|
+
- pattern: template.HTMLEscapeString(...)
|
|
143
|
+
pattern-sinks:
|
|
144
|
+
- pattern: template.HTML(...)
|
|
145
|
+
- pattern: '($W : http.ResponseWriter).Write(...)'
|
|
146
|
+
- pattern: 'fmt.Fprintf(($W : http.ResponseWriter), ...)'
|
|
147
|
+
- pattern: 'fmt.Fprint(($W : http.ResponseWriter), ...)'
|
|
148
|
+
- pattern: 'io.WriteString(($W : http.ResponseWriter), ...)'
|
|
149
|
+
test: |
|
|
150
|
+
package main
|
|
151
|
+
|
|
152
|
+
import (
|
|
153
|
+
"fmt"
|
|
154
|
+
"html"
|
|
155
|
+
"net/http"
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
func hello(w http.ResponseWriter, r *http.Request) {
|
|
159
|
+
name := r.URL.Query().Get("name")
|
|
160
|
+
// ruleid: __RULE_ID__
|
|
161
|
+
fmt.Fprintf(w, "<h1>Hello %s</h1>", name)
|
|
162
|
+
// ok: __RULE_ID__
|
|
163
|
+
fmt.Fprintf(w, "<h1>Hello %s</h1>", html.EscapeString(name))
|
|
164
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
cwe: CWE-798
|
|
2
|
+
name: "Use of Hard-coded Credentials"
|
|
3
|
+
title: Hard-coded credentials
|
|
4
|
+
owasp: ["A07:2021 - Identification and Authentication Failures"]
|
|
5
|
+
keywords: ["hard-coded", "hardcoded", "hard coded", "default password", "default credentials", "embedded credentials"]
|
|
6
|
+
message: >-
|
|
7
|
+
A credential appears to be hard-coded in the source. Load secrets from the
|
|
8
|
+
environment or a secrets manager.
|
|
9
|
+
languages:
|
|
10
|
+
ruby:
|
|
11
|
+
rule: |
|
|
12
|
+
patterns:
|
|
13
|
+
- pattern-either:
|
|
14
|
+
- pattern: $KEY = "$VALUE"
|
|
15
|
+
- pattern: '{..., $KEY: "$VALUE", ...}'
|
|
16
|
+
- pattern: '$FN(..., $KEY: "$VALUE", ...)'
|
|
17
|
+
- metavariable-regex:
|
|
18
|
+
metavariable: $KEY
|
|
19
|
+
regex: (?i).*(passw(or)?d|secret|api_?key|access_?key|auth_?token|private_?key)
|
|
20
|
+
- metavariable-regex:
|
|
21
|
+
metavariable: $VALUE
|
|
22
|
+
regex: .{6,}
|
|
23
|
+
test: |
|
|
24
|
+
# ruleid: __RULE_ID__
|
|
25
|
+
password = "SuperSecret123"
|
|
26
|
+
# ruleid: __RULE_ID__
|
|
27
|
+
client = Client.new(api_key: "sk_live_1234567890")
|
|
28
|
+
# ok: __RULE_ID__
|
|
29
|
+
password = ENV.fetch("DB_PASSWORD")
|
|
30
|
+
# ok: __RULE_ID__
|
|
31
|
+
password = ""
|
|
32
|
+
python:
|
|
33
|
+
rule: |
|
|
34
|
+
patterns:
|
|
35
|
+
- pattern-either:
|
|
36
|
+
- pattern: $KEY = "$VALUE"
|
|
37
|
+
- pattern: $FN(..., $KEY="$VALUE", ...)
|
|
38
|
+
- metavariable-regex:
|
|
39
|
+
metavariable: $KEY
|
|
40
|
+
regex: (?i).*(passw(or)?d|secret|api_?key|access_?key|auth_?token|private_?key)
|
|
41
|
+
- metavariable-regex:
|
|
42
|
+
metavariable: $VALUE
|
|
43
|
+
regex: .{6,}
|
|
44
|
+
test: |
|
|
45
|
+
import os
|
|
46
|
+
|
|
47
|
+
# ruleid: __RULE_ID__
|
|
48
|
+
DB_PASSWORD = "SuperSecret123"
|
|
49
|
+
# ruleid: __RULE_ID__
|
|
50
|
+
client = Client(api_key="sk_live_1234567890")
|
|
51
|
+
# ok: __RULE_ID__
|
|
52
|
+
DB_PASSWORD = os.environ["DB_PASSWORD"]
|
|
53
|
+
javascript:
|
|
54
|
+
rule: |
|
|
55
|
+
patterns:
|
|
56
|
+
- pattern-either:
|
|
57
|
+
- pattern: $KEY = "$VALUE"
|
|
58
|
+
- pattern: '{..., $KEY: "$VALUE", ...}'
|
|
59
|
+
- metavariable-regex:
|
|
60
|
+
metavariable: $KEY
|
|
61
|
+
regex: (?i).*(passw(or)?d|secret|api_?key|access_?key|auth_?token|private_?key)
|
|
62
|
+
- metavariable-regex:
|
|
63
|
+
metavariable: $VALUE
|
|
64
|
+
regex: .{6,}
|
|
65
|
+
test: |
|
|
66
|
+
// ruleid: __RULE_ID__
|
|
67
|
+
const password = "SuperSecret123";
|
|
68
|
+
// ruleid: __RULE_ID__
|
|
69
|
+
const client = new Client({ apiKey: "sk_live_1234567890" });
|
|
70
|
+
// ok: __RULE_ID__
|
|
71
|
+
const token = process.env.API_TOKEN;
|
|
72
|
+
java:
|
|
73
|
+
rule: |
|
|
74
|
+
patterns:
|
|
75
|
+
- pattern-either:
|
|
76
|
+
- pattern: $TYPE $KEY = "$VALUE";
|
|
77
|
+
- pattern: $KEY = "$VALUE";
|
|
78
|
+
- metavariable-regex:
|
|
79
|
+
metavariable: $KEY
|
|
80
|
+
regex: (?i).*(passw(or)?d|secret|api_?key|access_?key|auth_?token|private_?key)
|
|
81
|
+
- metavariable-regex:
|
|
82
|
+
metavariable: $VALUE
|
|
83
|
+
regex: .{6,}
|
|
84
|
+
test: |
|
|
85
|
+
public class Config {
|
|
86
|
+
// ruleid: __RULE_ID__
|
|
87
|
+
private static final String DB_PASSWORD = "SuperSecret123";
|
|
88
|
+
// ok: __RULE_ID__
|
|
89
|
+
private static final String DB_HOST = "db.internal";
|
|
90
|
+
|
|
91
|
+
public String password() {
|
|
92
|
+
// ok: __RULE_ID__
|
|
93
|
+
String password = System.getenv("DB_PASSWORD");
|
|
94
|
+
return password;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
go:
|
|
98
|
+
rule: |
|
|
99
|
+
patterns:
|
|
100
|
+
- pattern-either:
|
|
101
|
+
- pattern: $KEY := "$VALUE"
|
|
102
|
+
- pattern: $KEY = "$VALUE"
|
|
103
|
+
- pattern: const $KEY = "$VALUE"
|
|
104
|
+
- pattern: var $KEY = "$VALUE"
|
|
105
|
+
- metavariable-regex:
|
|
106
|
+
metavariable: $KEY
|
|
107
|
+
regex: (?i).*(passw(or)?d|secret|api_?key|access_?key|auth_?token|private_?key)
|
|
108
|
+
- metavariable-regex:
|
|
109
|
+
metavariable: $VALUE
|
|
110
|
+
regex: .{6,}
|
|
111
|
+
test: |
|
|
112
|
+
package main
|
|
113
|
+
|
|
114
|
+
import "os"
|
|
115
|
+
|
|
116
|
+
// ruleid: __RULE_ID__
|
|
117
|
+
const apiKey = "sk_live_1234567890"
|
|
118
|
+
|
|
119
|
+
func connect() {
|
|
120
|
+
// ruleid: __RULE_ID__
|
|
121
|
+
password := "SuperSecret123"
|
|
122
|
+
// ok: __RULE_ID__
|
|
123
|
+
token := os.Getenv("API_TOKEN")
|
|
124
|
+
_, _ = password, token
|
|
125
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
cwe: CWE-89
|
|
2
|
+
name: "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')"
|
|
3
|
+
title: SQL injection
|
|
4
|
+
owasp: ["A03:2021 - Injection"]
|
|
5
|
+
keywords: ["sql injection", "sqli", "sql command"]
|
|
6
|
+
message: >-
|
|
7
|
+
Untrusted input flows into a SQL query built from strings. Use parameterized
|
|
8
|
+
queries / bind parameters instead of string concatenation or interpolation.
|
|
9
|
+
languages:
|
|
10
|
+
ruby:
|
|
11
|
+
rule: |
|
|
12
|
+
mode: taint
|
|
13
|
+
pattern-sources:
|
|
14
|
+
- pattern: params
|
|
15
|
+
- pattern: cookies
|
|
16
|
+
pattern-sanitizers:
|
|
17
|
+
- pattern: $X.to_i
|
|
18
|
+
- pattern: $M.sanitize_sql(...)
|
|
19
|
+
- pattern: $M.connection.quote(...)
|
|
20
|
+
pattern-sinks:
|
|
21
|
+
- patterns:
|
|
22
|
+
- pattern-either:
|
|
23
|
+
- pattern: $M.where($Q, ...)
|
|
24
|
+
- pattern: $M.order($Q, ...)
|
|
25
|
+
- pattern: $M.group($Q, ...)
|
|
26
|
+
- pattern: $M.having($Q, ...)
|
|
27
|
+
- pattern: $M.find_by_sql($Q, ...)
|
|
28
|
+
- pattern: $M.select($Q, ...)
|
|
29
|
+
- pattern: $M.pluck($Q, ...)
|
|
30
|
+
- pattern: $M.exists?($Q, ...)
|
|
31
|
+
- pattern: $M.execute($Q, ...)
|
|
32
|
+
- pattern: $M.exec_query($Q, ...)
|
|
33
|
+
- focus-metavariable: $Q
|
|
34
|
+
- pattern-not: "{...}"
|
|
35
|
+
test: |
|
|
36
|
+
class UsersController < ApplicationController
|
|
37
|
+
def index
|
|
38
|
+
# ruleid: __RULE_ID__
|
|
39
|
+
@users = User.where("name = '#{params[:name]}'")
|
|
40
|
+
# ruleid: __RULE_ID__
|
|
41
|
+
@users = User.order(params[:sort])
|
|
42
|
+
# ok: __RULE_ID__
|
|
43
|
+
@users = User.where(name: params[:name])
|
|
44
|
+
# ok: __RULE_ID__
|
|
45
|
+
@users = User.where("name = ?", params[:name])
|
|
46
|
+
# ok: __RULE_ID__
|
|
47
|
+
@users = User.where("id = #{params[:id].to_i}")
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
python:
|
|
51
|
+
rule: |
|
|
52
|
+
mode: taint
|
|
53
|
+
pattern-sources:
|
|
54
|
+
- pattern: flask.request.$ANY
|
|
55
|
+
- patterns:
|
|
56
|
+
- pattern-inside: |
|
|
57
|
+
def $VIEW(request, ...):
|
|
58
|
+
...
|
|
59
|
+
- pattern: request.$ANY
|
|
60
|
+
pattern-sanitizers:
|
|
61
|
+
- pattern: int(...)
|
|
62
|
+
pattern-sinks:
|
|
63
|
+
- patterns:
|
|
64
|
+
- pattern-either:
|
|
65
|
+
- pattern: $CUR.execute($Q, ...)
|
|
66
|
+
- pattern: $CUR.executemany($Q, ...)
|
|
67
|
+
- pattern: $M.objects.raw($Q, ...)
|
|
68
|
+
- pattern: sqlalchemy.text($Q, ...)
|
|
69
|
+
- focus-metavariable: $Q
|
|
70
|
+
test: |
|
|
71
|
+
from flask import request
|
|
72
|
+
|
|
73
|
+
def search(cursor):
|
|
74
|
+
name = request.args.get("name")
|
|
75
|
+
# ruleid: __RULE_ID__
|
|
76
|
+
cursor.execute("SELECT * FROM users WHERE name = '%s'" % name)
|
|
77
|
+
# ruleid: __RULE_ID__
|
|
78
|
+
cursor.execute(f"SELECT * FROM users WHERE name = '{name}'")
|
|
79
|
+
# ok: __RULE_ID__
|
|
80
|
+
cursor.execute("SELECT * FROM users WHERE name = %s", (name,))
|
|
81
|
+
javascript:
|
|
82
|
+
rule: |
|
|
83
|
+
mode: taint
|
|
84
|
+
pattern-sources:
|
|
85
|
+
- pattern: $REQ.query
|
|
86
|
+
- pattern: $REQ.body
|
|
87
|
+
- pattern: $REQ.params
|
|
88
|
+
pattern-sanitizers:
|
|
89
|
+
- pattern: parseInt(...)
|
|
90
|
+
- pattern: Number(...)
|
|
91
|
+
pattern-sinks:
|
|
92
|
+
- patterns:
|
|
93
|
+
- pattern-either:
|
|
94
|
+
- pattern: $DB.query($Q, ...)
|
|
95
|
+
- pattern: $DB.execute($Q, ...)
|
|
96
|
+
- pattern: $DB.raw($Q, ...)
|
|
97
|
+
- pattern: $DB.$RAW`...`
|
|
98
|
+
- focus-metavariable: $Q
|
|
99
|
+
test: |
|
|
100
|
+
app.get("/users", (req, res) => {
|
|
101
|
+
// ruleid: __RULE_ID__
|
|
102
|
+
db.query("SELECT * FROM users WHERE name = '" + req.query.name + "'");
|
|
103
|
+
// ruleid: __RULE_ID__
|
|
104
|
+
db.query(`SELECT * FROM users WHERE id = ${req.params.id}`);
|
|
105
|
+
// ok: __RULE_ID__
|
|
106
|
+
db.query("SELECT * FROM users WHERE name = ?", [req.query.name]);
|
|
107
|
+
// ok: __RULE_ID__
|
|
108
|
+
db.query("SELECT * FROM users WHERE id = " + parseInt(req.query.id));
|
|
109
|
+
});
|
|
110
|
+
java:
|
|
111
|
+
rule: |
|
|
112
|
+
mode: taint
|
|
113
|
+
pattern-sources:
|
|
114
|
+
- pattern: (javax.servlet.http.HttpServletRequest $REQ).$GET(...)
|
|
115
|
+
- pattern: (jakarta.servlet.http.HttpServletRequest $REQ).$GET(...)
|
|
116
|
+
- patterns:
|
|
117
|
+
- pattern-inside: |
|
|
118
|
+
$RET $METHOD(..., @RequestParam $T $P, ...) { ... }
|
|
119
|
+
- pattern: $P
|
|
120
|
+
pattern-sanitizers:
|
|
121
|
+
- pattern: Integer.parseInt(...)
|
|
122
|
+
- pattern: Long.parseLong(...)
|
|
123
|
+
pattern-sinks:
|
|
124
|
+
- patterns:
|
|
125
|
+
- pattern-either:
|
|
126
|
+
- pattern: $S.executeQuery($Q)
|
|
127
|
+
- pattern: $S.executeUpdate($Q)
|
|
128
|
+
- pattern: $S.execute($Q)
|
|
129
|
+
- pattern: $S.addBatch($Q)
|
|
130
|
+
- pattern: $C.prepareStatement($Q, ...)
|
|
131
|
+
- pattern: $EM.createQuery($Q, ...)
|
|
132
|
+
- pattern: $EM.createNativeQuery($Q, ...)
|
|
133
|
+
- pattern: $JDBC.queryForObject($Q, ...)
|
|
134
|
+
- pattern: $JDBC.queryForList($Q, ...)
|
|
135
|
+
- focus-metavariable: $Q
|
|
136
|
+
test: |
|
|
137
|
+
import javax.servlet.http.HttpServletRequest;
|
|
138
|
+
import java.sql.Connection;
|
|
139
|
+
import java.sql.PreparedStatement;
|
|
140
|
+
import java.sql.Statement;
|
|
141
|
+
|
|
142
|
+
public class UserDao {
|
|
143
|
+
public void find(HttpServletRequest request, Connection conn) throws Exception {
|
|
144
|
+
String name = request.getParameter("name");
|
|
145
|
+
Statement st = conn.createStatement();
|
|
146
|
+
// ruleid: __RULE_ID__
|
|
147
|
+
st.executeQuery("SELECT * FROM users WHERE name = '" + name + "'");
|
|
148
|
+
// ok: __RULE_ID__
|
|
149
|
+
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE name = ?");
|
|
150
|
+
ps.setString(1, name);
|
|
151
|
+
ps.executeQuery();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
go:
|
|
155
|
+
rule: |
|
|
156
|
+
mode: taint
|
|
157
|
+
pattern-sources:
|
|
158
|
+
- pattern: '($R : *http.Request).URL.Query().Get(...)'
|
|
159
|
+
- pattern: '($R : *http.Request).FormValue(...)'
|
|
160
|
+
- pattern: '($R : *http.Request).PostFormValue(...)'
|
|
161
|
+
pattern-sanitizers:
|
|
162
|
+
- pattern: strconv.Atoi(...)
|
|
163
|
+
pattern-sinks:
|
|
164
|
+
- patterns:
|
|
165
|
+
- pattern-either:
|
|
166
|
+
- pattern: $DB.Query($Q, ...)
|
|
167
|
+
- pattern: $DB.QueryRow($Q, ...)
|
|
168
|
+
- pattern: $DB.Exec($Q, ...)
|
|
169
|
+
- pattern: $DB.QueryContext($CTX, $Q, ...)
|
|
170
|
+
- pattern: $DB.ExecContext($CTX, $Q, ...)
|
|
171
|
+
- focus-metavariable: $Q
|
|
172
|
+
test: |
|
|
173
|
+
package main
|
|
174
|
+
|
|
175
|
+
import (
|
|
176
|
+
"database/sql"
|
|
177
|
+
"fmt"
|
|
178
|
+
"net/http"
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
func handler(db *sql.DB, r *http.Request) {
|
|
182
|
+
name := r.URL.Query().Get("name")
|
|
183
|
+
// ruleid: __RULE_ID__
|
|
184
|
+
db.Query(fmt.Sprintf("SELECT * FROM users WHERE name = '%s'", name))
|
|
185
|
+
// ruleid: __RULE_ID__
|
|
186
|
+
db.Exec("DELETE FROM users WHERE name = '" + name + "'")
|
|
187
|
+
// ok: __RULE_ID__
|
|
188
|
+
db.Query("SELECT * FROM users WHERE name = $1", name)
|
|
189
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
cwe: CWE-917
|
|
2
|
+
name: "Improper Neutralization of Special Elements used in an Expression Language Statement ('Expression Language Injection')"
|
|
3
|
+
title: Expression language / JNDI lookup injection
|
|
4
|
+
owasp: ["A03:2021 - Injection"]
|
|
5
|
+
keywords: ["expression language", "jndi", "lookup", "log4j", "log4shell", "message lookup", "el injection", "logging untrusted", "log messages"]
|
|
6
|
+
message: >-
|
|
7
|
+
Untrusted input is logged with Log4j 2. On vulnerable versions (< 2.17.1)
|
|
8
|
+
message lookups such as ${jndi:...} can lead to remote code execution.
|
|
9
|
+
Upgrade Log4j and avoid logging raw request data.
|
|
10
|
+
languages:
|
|
11
|
+
java:
|
|
12
|
+
rule: |
|
|
13
|
+
mode: taint
|
|
14
|
+
pattern-sources:
|
|
15
|
+
- pattern: (javax.servlet.http.HttpServletRequest $REQ).$GET(...)
|
|
16
|
+
- pattern: (jakarta.servlet.http.HttpServletRequest $REQ).$GET(...)
|
|
17
|
+
- patterns:
|
|
18
|
+
- pattern-inside: |
|
|
19
|
+
$RET $METHOD(..., @RequestHeader $T $P, ...) { ... }
|
|
20
|
+
- pattern: $P
|
|
21
|
+
- patterns:
|
|
22
|
+
- pattern-inside: |
|
|
23
|
+
$RET $METHOD(..., @RequestParam $T $P, ...) { ... }
|
|
24
|
+
- pattern: $P
|
|
25
|
+
pattern-sinks:
|
|
26
|
+
- patterns:
|
|
27
|
+
- pattern: (org.apache.logging.log4j.Logger $LOG).$LEVEL(...)
|
|
28
|
+
- metavariable-regex:
|
|
29
|
+
metavariable: $LEVEL
|
|
30
|
+
regex: ^(trace|debug|info|warn|error|fatal|log|printf)$
|
|
31
|
+
test: |
|
|
32
|
+
import javax.servlet.http.HttpServletRequest;
|
|
33
|
+
import org.apache.logging.log4j.LogManager;
|
|
34
|
+
import org.apache.logging.log4j.Logger;
|
|
35
|
+
|
|
36
|
+
public class LoginServlet {
|
|
37
|
+
private static final Logger logger = LogManager.getLogger(LoginServlet.class);
|
|
38
|
+
|
|
39
|
+
public void doPost(HttpServletRequest request) {
|
|
40
|
+
String agent = request.getHeader("User-Agent");
|
|
41
|
+
// ruleid: __RULE_ID__
|
|
42
|
+
logger.info("Login attempt from " + agent);
|
|
43
|
+
// ok: __RULE_ID__
|
|
44
|
+
logger.info("Login attempt");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
cwe: CWE-918
|
|
2
|
+
name: "Server-Side Request Forgery (SSRF)"
|
|
3
|
+
title: Server-side request forgery (SSRF)
|
|
4
|
+
owasp: ["A10:2021 - Server-Side Request Forgery (SSRF)"]
|
|
5
|
+
keywords: ["server-side request forgery", "ssrf", "internal network", "arbitrary url", "arbitrary requests"]
|
|
6
|
+
message: >-
|
|
7
|
+
An outbound HTTP request is made to a URL controlled by the user (SSRF).
|
|
8
|
+
Validate the destination against an allowlist of hosts and block internal
|
|
9
|
+
addresses.
|
|
10
|
+
languages:
|
|
11
|
+
ruby:
|
|
12
|
+
rule: |
|
|
13
|
+
mode: taint
|
|
14
|
+
pattern-sources:
|
|
15
|
+
- pattern: params
|
|
16
|
+
- pattern: cookies
|
|
17
|
+
pattern-sinks:
|
|
18
|
+
- patterns:
|
|
19
|
+
- pattern-either:
|
|
20
|
+
- pattern: Net::HTTP.get($U, ...)
|
|
21
|
+
- pattern: Net::HTTP.get_response($U, ...)
|
|
22
|
+
- pattern: Net::HTTP.start($U, ...)
|
|
23
|
+
- pattern: Net::HTTP.new($U, ...)
|
|
24
|
+
- pattern: URI.open($U, ...)
|
|
25
|
+
- pattern: open($U, ...)
|
|
26
|
+
- pattern: HTTParty.$VERB($U, ...)
|
|
27
|
+
- pattern: Faraday.$VERB($U, ...)
|
|
28
|
+
- pattern: Faraday.new($U, ...)
|
|
29
|
+
- pattern: RestClient.$VERB($U, ...)
|
|
30
|
+
- pattern: HTTP.$VERB($U, ...)
|
|
31
|
+
- focus-metavariable: $U
|
|
32
|
+
test: |
|
|
33
|
+
require "net/http"
|
|
34
|
+
|
|
35
|
+
class PreviewController < ApplicationController
|
|
36
|
+
def show
|
|
37
|
+
# ruleid: __RULE_ID__
|
|
38
|
+
body = Net::HTTP.get(URI(params[:url]))
|
|
39
|
+
# ruleid: __RULE_ID__
|
|
40
|
+
resp = HTTParty.get(params[:url])
|
|
41
|
+
# ok: __RULE_ID__
|
|
42
|
+
resp = HTTParty.get("https://api.example.com/items", query: { q: params[:q] })
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
python:
|
|
46
|
+
rule: |
|
|
47
|
+
mode: taint
|
|
48
|
+
pattern-sources:
|
|
49
|
+
- pattern: flask.request.$ANY
|
|
50
|
+
- patterns:
|
|
51
|
+
- pattern-inside: |
|
|
52
|
+
def $VIEW(request, ...):
|
|
53
|
+
...
|
|
54
|
+
- pattern: request.$ANY
|
|
55
|
+
pattern-sinks:
|
|
56
|
+
- patterns:
|
|
57
|
+
- pattern-either:
|
|
58
|
+
- pattern: requests.$VERB($U, ...)
|
|
59
|
+
- pattern: requests.request($M, $U, ...)
|
|
60
|
+
- pattern: httpx.$VERB($U, ...)
|
|
61
|
+
- pattern: urllib.request.urlopen($U, ...)
|
|
62
|
+
- pattern: urllib.request.Request($U, ...)
|
|
63
|
+
- focus-metavariable: $U
|
|
64
|
+
test: |
|
|
65
|
+
import requests
|
|
66
|
+
from flask import request
|
|
67
|
+
|
|
68
|
+
def preview():
|
|
69
|
+
url = request.args.get("url")
|
|
70
|
+
# ruleid: __RULE_ID__
|
|
71
|
+
r = requests.get(url, timeout=5)
|
|
72
|
+
# ok: __RULE_ID__
|
|
73
|
+
r = requests.get("https://api.example.com/items", params={"q": url}, timeout=5)
|
|
74
|
+
javascript:
|
|
75
|
+
rule: |
|
|
76
|
+
mode: taint
|
|
77
|
+
pattern-sources:
|
|
78
|
+
- pattern: $REQ.query
|
|
79
|
+
- pattern: $REQ.body
|
|
80
|
+
- pattern: $REQ.params
|
|
81
|
+
pattern-sinks:
|
|
82
|
+
- patterns:
|
|
83
|
+
- pattern-either:
|
|
84
|
+
- pattern: axios($U, ...)
|
|
85
|
+
- pattern: axios.$VERB($U, ...)
|
|
86
|
+
- pattern: fetch($U, ...)
|
|
87
|
+
- pattern: got($U, ...)
|
|
88
|
+
- pattern: got.$VERB($U, ...)
|
|
89
|
+
- pattern: http.get($U, ...)
|
|
90
|
+
- pattern: https.get($U, ...)
|
|
91
|
+
- pattern: http.request($U, ...)
|
|
92
|
+
- pattern: https.request($U, ...)
|
|
93
|
+
- focus-metavariable: $U
|
|
94
|
+
test: |
|
|
95
|
+
const axios = require("axios");
|
|
96
|
+
|
|
97
|
+
app.get("/preview", async (req, res) => {
|
|
98
|
+
// ruleid: __RULE_ID__
|
|
99
|
+
const r = await axios.get(req.query.url);
|
|
100
|
+
// ok: __RULE_ID__
|
|
101
|
+
const s = await axios.get("https://api.example.com/items", { params: { q: req.query.q } });
|
|
102
|
+
res.send(r.data + s.data);
|
|
103
|
+
});
|
|
104
|
+
java:
|
|
105
|
+
rule: |
|
|
106
|
+
mode: taint
|
|
107
|
+
pattern-sources:
|
|
108
|
+
- pattern: (javax.servlet.http.HttpServletRequest $REQ).$GET(...)
|
|
109
|
+
- pattern: (jakarta.servlet.http.HttpServletRequest $REQ).$GET(...)
|
|
110
|
+
- patterns:
|
|
111
|
+
- pattern-inside: |
|
|
112
|
+
$RET $METHOD(..., @RequestParam $T $P, ...) { ... }
|
|
113
|
+
- pattern: $P
|
|
114
|
+
pattern-sinks:
|
|
115
|
+
- pattern: new URL(...)
|
|
116
|
+
- pattern: URI.create(...)
|
|
117
|
+
- pattern: new URI(...)
|
|
118
|
+
- patterns:
|
|
119
|
+
- pattern-either:
|
|
120
|
+
- pattern: (RestTemplate $R).$METHOD($U, ...)
|
|
121
|
+
- pattern: (WebClient $W).get().uri($U, ...)
|
|
122
|
+
- focus-metavariable: $U
|
|
123
|
+
test: |
|
|
124
|
+
import javax.servlet.http.HttpServletRequest;
|
|
125
|
+
import java.net.URL;
|
|
126
|
+
import java.io.InputStream;
|
|
127
|
+
|
|
128
|
+
public class PreviewServlet {
|
|
129
|
+
public void doGet(HttpServletRequest request) throws Exception {
|
|
130
|
+
String target = request.getParameter("url");
|
|
131
|
+
// ruleid: __RULE_ID__
|
|
132
|
+
InputStream in = new URL(target).openStream();
|
|
133
|
+
// ok: __RULE_ID__
|
|
134
|
+
InputStream fixed = new URL("https://api.example.com/items").openStream();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
go:
|
|
138
|
+
rule: |
|
|
139
|
+
mode: taint
|
|
140
|
+
pattern-sources:
|
|
141
|
+
- pattern: '($R : *http.Request).URL.Query().Get(...)'
|
|
142
|
+
- pattern: '($R : *http.Request).FormValue(...)'
|
|
143
|
+
pattern-sinks:
|
|
144
|
+
- patterns:
|
|
145
|
+
- pattern-either:
|
|
146
|
+
- pattern: http.Get($U)
|
|
147
|
+
- pattern: http.Head($U)
|
|
148
|
+
- pattern: http.Post($U, ...)
|
|
149
|
+
- pattern: http.PostForm($U, ...)
|
|
150
|
+
- pattern: http.NewRequest($M, $U, ...)
|
|
151
|
+
- pattern: http.NewRequestWithContext($CTX, $M, $U, ...)
|
|
152
|
+
- focus-metavariable: $U
|
|
153
|
+
test: |
|
|
154
|
+
package main
|
|
155
|
+
|
|
156
|
+
import "net/http"
|
|
157
|
+
|
|
158
|
+
func preview(w http.ResponseWriter, r *http.Request) {
|
|
159
|
+
target := r.URL.Query().Get("url")
|
|
160
|
+
// ruleid: __RULE_ID__
|
|
161
|
+
resp, _ := http.Get(target)
|
|
162
|
+
// ok: __RULE_ID__
|
|
163
|
+
fixed, _ := http.Get("https://api.example.com/items?q=" + "static")
|
|
164
|
+
_ = resp
|
|
165
|
+
_ = fixed
|
|
166
|
+
}
|