narvy-cli 1.0.0__py3-none-any.whl
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.
- narvy/__init__.py +3 -0
- narvy/android/__init__.py +0 -0
- narvy/android/source_analyzer.py +268 -0
- narvy/android/split_bundle.py +475 -0
- narvy/android_rule_context.py +196 -0
- narvy/apk_memory_preflight.py +248 -0
- narvy/auth.py +40 -0
- narvy/ci_templates/bitbucket.yml +25 -0
- narvy/ci_templates/github.yml +60 -0
- narvy/ci_templates/gitlab.yml +32 -0
- narvy/cloud/__init__.py +0 -0
- narvy/cloud/aws_scan.py +1188 -0
- narvy/comment_filter.py +134 -0
- narvy/crypto_taint_lite.py +82 -0
- narvy/decompiler.py +337 -0
- narvy/doctor.py +244 -0
- narvy/host/__init__.py +0 -0
- narvy/host/audit.py +157 -0
- narvy/host/host_knowledge.py +982 -0
- narvy/host/lynis_bootstrap.py +400 -0
- narvy/host/lynis_parser.py +358 -0
- narvy/host/narvy_checks.py +789 -0
- narvy/host/report.py +69 -0
- narvy/host/ssh_exec.py +219 -0
- narvy/ios/__init__.py +0 -0
- narvy/ios/binary_analyzer.py +1201 -0
- narvy/ios/plist_checks.py +249 -0
- narvy/ios/source_analyzer.py +301 -0
- narvy/ios/third_party_filter.py +242 -0
- narvy/ios/trust_all_context.py +66 -0
- narvy/main.py +1983 -0
- narvy/native_hardening.py +503 -0
- narvy/reporter.py +151 -0
- narvy/rule_engine.py +57 -0
- narvy/rules/android/config.yml +77 -0
- narvy/rules/android/crypto.yml +34 -0
- narvy/rules/android/secrets.yml +161 -0
- narvy/rules/android/storage.yml +24 -0
- narvy/rules/android/webview.yml +23 -0
- narvy/rules/android.yml +219 -0
- narvy/rules/ios/objc/crypto.yml +56 -0
- narvy/rules/ios/objc/network.yml +67 -0
- narvy/rules/ios/objc/secrets.yml +79 -0
- narvy/rules/ios/objc/storage.yml +45 -0
- narvy/rules/ios/objc/webview.yml +45 -0
- narvy/rules/ios_swift.yml +327 -0
- narvy/rules/web/go.yml +2837 -0
- narvy/rules/web/java.yml +1576 -0
- narvy/rules/web/javascript.yml +3683 -0
- narvy/rules/web/kotlin.yml +413 -0
- narvy/rules/web/local/csharp_narvy/config.yml +61 -0
- narvy/rules/web/local/csharp_narvy/crypto.yml +64 -0
- narvy/rules/web/local/csharp_narvy/deserialization.yml +59 -0
- narvy/rules/web/local/csharp_narvy/injection.yml +122 -0
- narvy/rules/web/local/csharp_narvy/xxe.yml +48 -0
- narvy/rules/web/local/java_narvy/auth_jwt.yml +134 -0
- narvy/rules/web/local/java_narvy/deserialization.yml +108 -0
- narvy/rules/web/local/java_narvy/mybatis.yml +39 -0
- narvy/rules/web/local/java_narvy/snakeyaml.yml +34 -0
- narvy/rules/web/local/java_narvy/spring_authz.yml +32 -0
- narvy/rules/web/local/java_narvy/spring_config.yml +67 -0
- narvy/rules/web/local/java_narvy/spring_hardening.yml +291 -0
- narvy/rules/web/local/java_narvy/sqli.yml +235 -0
- narvy/rules/web/local/java_narvy/xxe.yml +212 -0
- narvy/rules/web/php.yml +1644 -0
- narvy/rules/web/python.yml +3967 -0
- narvy/rules/web/ruby.yml +703 -0
- narvy/rules/web/rust.yml +258 -0
- narvy/rules/web/secrets.yml +1420 -0
- narvy/rules/web/secrets_supplement.yml +383 -0
- narvy/sca/__init__.py +1 -0
- narvy/sca/android_deps.py +349 -0
- narvy/sca/ios_deps.py +578 -0
- narvy/sca/osv_client.py +617 -0
- narvy/sca/web_deps.py +955 -0
- narvy/scope_config.py +195 -0
- narvy/semgrep_engine.py +219 -0
- narvy/stack_protector_evidence.py +96 -0
- narvy/third_party_filter.py +211 -0
- narvy/uploader.py +92 -0
- narvy/weak_prng_context.py +270 -0
- narvy/web/__init__.py +0 -0
- narvy/web/nuclei_binary.py +105 -0
- narvy/web/scan_blocklist.py +96 -0
- narvy/web/scanner.py +842 -0
- narvy/web/source_analyzer.py +714 -0
- narvy/web/ssrf_guard.py +374 -0
- narvy_cli-1.0.0.dist-info/METADATA +165 -0
- narvy_cli-1.0.0.dist-info/RECORD +93 -0
- narvy_cli-1.0.0.dist-info/WHEEL +5 -0
- narvy_cli-1.0.0.dist-info/entry_points.txt +2 -0
- narvy_cli-1.0.0.dist-info/licenses/LICENSE +202 -0
- narvy_cli-1.0.0.dist-info/top_level.txt +1 -0
narvy/rules/web/ruby.yml
ADDED
|
@@ -0,0 +1,703 @@
|
|
|
1
|
+
# Ruby and Rails web-source security rules.
|
|
2
|
+
# Covers command/SQL injection, CSRF, mass assignment, TLS, weak hashing,
|
|
3
|
+
# unsafe deserialization, send dispatch, unescaped output and open redirects.
|
|
4
|
+
|
|
5
|
+
rules:
|
|
6
|
+
|
|
7
|
+
# OS command injection - system / exec / spawn / popen / Open3 family
|
|
8
|
+
- id: narvy.ruby.command-injection.command-injection-system.command-injection-system
|
|
9
|
+
message: >
|
|
10
|
+
Request data flows into a single-string shell invocation
|
|
11
|
+
(system/exec/spawn/IO.popen/Open3). Ruby hands a one-argument command
|
|
12
|
+
string to /bin/sh, so any shell metacharacter in the request lets an
|
|
13
|
+
attacker run arbitrary commands (CWE-78). Remediation: pass the program
|
|
14
|
+
and each argument as separate positional arguments
|
|
15
|
+
(system("cmd", arg1, arg2)) so no shell is spawned, or escape untrusted
|
|
16
|
+
fragments with Shellwords.escape and validate against an allow-list.
|
|
17
|
+
severity: ERROR
|
|
18
|
+
languages: [ruby]
|
|
19
|
+
metadata:
|
|
20
|
+
category: security
|
|
21
|
+
cwe:
|
|
22
|
+
- "CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')"
|
|
23
|
+
owasp:
|
|
24
|
+
- "A03:2021 - Injection"
|
|
25
|
+
subcategory: [vuln]
|
|
26
|
+
likelihood: HIGH
|
|
27
|
+
impact: HIGH
|
|
28
|
+
confidence: MEDIUM
|
|
29
|
+
source: narvy-local
|
|
30
|
+
mode: taint
|
|
31
|
+
pattern-sources:
|
|
32
|
+
- patterns:
|
|
33
|
+
- pattern-either:
|
|
34
|
+
- pattern: params[...]
|
|
35
|
+
- pattern: params.$M
|
|
36
|
+
- pattern: params.fetch(...)
|
|
37
|
+
- pattern: request.params[...]
|
|
38
|
+
- pattern: request.query_parameters[...]
|
|
39
|
+
- pattern: request.request_parameters[...]
|
|
40
|
+
- pattern: request.GET[...]
|
|
41
|
+
- pattern: request.POST[...]
|
|
42
|
+
- pattern: cookies[...]
|
|
43
|
+
- pattern: request.env[...]
|
|
44
|
+
- pattern: request.headers[...]
|
|
45
|
+
pattern-sanitizers:
|
|
46
|
+
- pattern: Shellwords.escape(...)
|
|
47
|
+
- pattern: Shellwords.split(...)
|
|
48
|
+
- pattern: Integer(...)
|
|
49
|
+
- pattern: Float(...)
|
|
50
|
+
pattern-sinks:
|
|
51
|
+
- patterns:
|
|
52
|
+
- pattern-either:
|
|
53
|
+
- pattern: system($CMD)
|
|
54
|
+
- pattern: Kernel.system($CMD)
|
|
55
|
+
- pattern: exec($CMD)
|
|
56
|
+
- pattern: Kernel.exec($CMD)
|
|
57
|
+
- pattern: spawn($CMD)
|
|
58
|
+
- pattern: Process.spawn($CMD)
|
|
59
|
+
- pattern: IO.popen($CMD)
|
|
60
|
+
- pattern: IO.popen($CMD, ...)
|
|
61
|
+
- pattern: Open3.capture2($CMD)
|
|
62
|
+
- pattern: Open3.capture2e($CMD)
|
|
63
|
+
- pattern: Open3.capture3($CMD)
|
|
64
|
+
- pattern: Open3.popen2($CMD)
|
|
65
|
+
- pattern: Open3.popen2e($CMD)
|
|
66
|
+
- pattern: Open3.popen3($CMD)
|
|
67
|
+
- patterns:
|
|
68
|
+
# Focus taint on the program slot: literal program with tainted args is the safe form.
|
|
69
|
+
- pattern-either:
|
|
70
|
+
- pattern: system($CMD, ...)
|
|
71
|
+
- pattern: Kernel.system($CMD, ...)
|
|
72
|
+
- pattern: exec($CMD, ...)
|
|
73
|
+
- pattern: spawn($CMD, ...)
|
|
74
|
+
- pattern: Process.spawn($CMD, ...)
|
|
75
|
+
- focus-metavariable: $CMD
|
|
76
|
+
|
|
77
|
+
# OS command injection - backtick execution
|
|
78
|
+
- id: narvy.ruby.command-injection.command-injection-backtick.command-injection-backtick
|
|
79
|
+
message: >
|
|
80
|
+
Request data is interpolated into a backtick command string
|
|
81
|
+
(`...#{input}...`). Backticks execute their contents in a subshell, so an
|
|
82
|
+
attacker-controlled fragment yields arbitrary command execution (CWE-78).
|
|
83
|
+
Remediation: do not build backtick commands from request data; use an
|
|
84
|
+
argument-vector API such as system("cmd", arg) or IO.popen(["cmd", arg]),
|
|
85
|
+
and escape any remaining untrusted value with Shellwords.escape.
|
|
86
|
+
severity: ERROR
|
|
87
|
+
languages: [ruby]
|
|
88
|
+
metadata:
|
|
89
|
+
category: security
|
|
90
|
+
cwe:
|
|
91
|
+
- "CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')"
|
|
92
|
+
owasp:
|
|
93
|
+
- "A03:2021 - Injection"
|
|
94
|
+
subcategory: [vuln]
|
|
95
|
+
likelihood: HIGH
|
|
96
|
+
impact: HIGH
|
|
97
|
+
confidence: MEDIUM
|
|
98
|
+
source: narvy-local
|
|
99
|
+
mode: taint
|
|
100
|
+
pattern-sources:
|
|
101
|
+
- patterns:
|
|
102
|
+
- pattern-either:
|
|
103
|
+
- pattern: params[...]
|
|
104
|
+
- pattern: params.$M
|
|
105
|
+
- pattern: request.params[...]
|
|
106
|
+
- pattern: cookies[...]
|
|
107
|
+
- pattern: request.env[...]
|
|
108
|
+
- pattern: request.headers[...]
|
|
109
|
+
pattern-sanitizers:
|
|
110
|
+
- pattern: Shellwords.escape(...)
|
|
111
|
+
pattern-sinks:
|
|
112
|
+
- pattern: "`...#{$X}...`"
|
|
113
|
+
|
|
114
|
+
# OS command injection - %x[] execution
|
|
115
|
+
- id: narvy.ruby.command-injection.command-injection-percent-x.command-injection-percent-x
|
|
116
|
+
message: >
|
|
117
|
+
Request data is interpolated into a %x{} / %x[] literal. %x is Ruby's
|
|
118
|
+
alternate spelling for backtick command execution and runs its contents
|
|
119
|
+
in a subshell, so untrusted input produces arbitrary command execution
|
|
120
|
+
(CWE-78). Remediation: build the command with an argument vector
|
|
121
|
+
(system("cmd", arg)) instead of a %x literal, and escape any untrusted
|
|
122
|
+
fragment with Shellwords.escape.
|
|
123
|
+
severity: ERROR
|
|
124
|
+
languages: [ruby]
|
|
125
|
+
metadata:
|
|
126
|
+
category: security
|
|
127
|
+
cwe:
|
|
128
|
+
- "CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')"
|
|
129
|
+
owasp:
|
|
130
|
+
- "A03:2021 - Injection"
|
|
131
|
+
subcategory: [vuln]
|
|
132
|
+
likelihood: HIGH
|
|
133
|
+
impact: HIGH
|
|
134
|
+
confidence: MEDIUM
|
|
135
|
+
source: narvy-local
|
|
136
|
+
mode: taint
|
|
137
|
+
pattern-sources:
|
|
138
|
+
- patterns:
|
|
139
|
+
- pattern-either:
|
|
140
|
+
- pattern: params[...]
|
|
141
|
+
- pattern: params.$M
|
|
142
|
+
- pattern: request.params[...]
|
|
143
|
+
- pattern: cookies[...]
|
|
144
|
+
- pattern: request.env[...]
|
|
145
|
+
- pattern: request.headers[...]
|
|
146
|
+
pattern-sanitizers:
|
|
147
|
+
- pattern: Shellwords.escape(...)
|
|
148
|
+
pattern-sinks:
|
|
149
|
+
- pattern: "%x[...#{$X}...]"
|
|
150
|
+
|
|
151
|
+
# SQL injection - ActiveRecord where() / find_by_sql / execute with a
|
|
152
|
+
# non-parameterised string
|
|
153
|
+
- id: narvy.ruby.sql-injection.sql-injection-where-string.sql-injection-where-string
|
|
154
|
+
message: >
|
|
155
|
+
Request data flows into the SQL-string argument of an ActiveRecord query
|
|
156
|
+
(where / find_by_sql / connection.execute / exec_query). Interpolating or
|
|
157
|
+
concatenating untrusted input into a SQL string allows an attacker to
|
|
158
|
+
alter the query and read or modify arbitrary data (CWE-89). Remediation:
|
|
159
|
+
use bound parameters - where("col = ?", value) or the hash form
|
|
160
|
+
where(col: value) - so the value can never be parsed as SQL.
|
|
161
|
+
severity: ERROR
|
|
162
|
+
languages: [ruby]
|
|
163
|
+
metadata:
|
|
164
|
+
category: security
|
|
165
|
+
cwe:
|
|
166
|
+
- "CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')"
|
|
167
|
+
owasp:
|
|
168
|
+
- "A03:2021 - Injection"
|
|
169
|
+
subcategory: [vuln]
|
|
170
|
+
likelihood: HIGH
|
|
171
|
+
impact: HIGH
|
|
172
|
+
confidence: HIGH
|
|
173
|
+
source: narvy-local
|
|
174
|
+
mode: taint
|
|
175
|
+
pattern-sources:
|
|
176
|
+
- patterns:
|
|
177
|
+
- pattern-either:
|
|
178
|
+
- pattern: params[...]
|
|
179
|
+
- pattern: params.$M
|
|
180
|
+
- pattern: request.params[...]
|
|
181
|
+
- pattern: cookies[...]
|
|
182
|
+
pattern-sanitizers:
|
|
183
|
+
- pattern: ActiveRecord::Base.sanitize_sql(...)
|
|
184
|
+
- pattern: ActiveRecord::Base.sanitize_sql_array(...)
|
|
185
|
+
- pattern: ActiveRecord::Base.connection.quote(...)
|
|
186
|
+
- pattern: $M.sanitize_sql(...)
|
|
187
|
+
- pattern: quote(...)
|
|
188
|
+
- pattern: Integer(...)
|
|
189
|
+
pattern-sinks:
|
|
190
|
+
# Focus taint on the SQL-string slot only: bound-parameter args are the safe form.
|
|
191
|
+
- patterns:
|
|
192
|
+
- pattern-either:
|
|
193
|
+
- pattern: $M.where($SQL)
|
|
194
|
+
- pattern: $M.where($SQL, ...)
|
|
195
|
+
- pattern: $M.rewhere($SQL)
|
|
196
|
+
- pattern: $M.having($SQL)
|
|
197
|
+
- pattern: $M.find_by_sql($SQL)
|
|
198
|
+
- pattern: $M.find_by_sql($SQL, ...)
|
|
199
|
+
- focus-metavariable: $SQL
|
|
200
|
+
- patterns:
|
|
201
|
+
- pattern-either:
|
|
202
|
+
- pattern: $C.execute($SQL)
|
|
203
|
+
- pattern: $C.exec_query($SQL)
|
|
204
|
+
- pattern: $C.select_all($SQL)
|
|
205
|
+
- pattern: $C.select_values($SQL)
|
|
206
|
+
- focus-metavariable: $SQL
|
|
207
|
+
|
|
208
|
+
# SQL injection - explicit tainted-string interpolation reaching where()
|
|
209
|
+
- id: narvy.ruby.sql-injection.sql-injection-string-interpolation.sql-injection-string-interpolation
|
|
210
|
+
message: >
|
|
211
|
+
A local variable holding request data is interpolated directly into a SQL
|
|
212
|
+
string that is handed to ActiveRecord. String interpolation of untrusted
|
|
213
|
+
input into a query is the canonical SQL-injection sink (CWE-89).
|
|
214
|
+
Remediation: replace the interpolation with a bound placeholder -
|
|
215
|
+
where("name = ?", value) - or use the hash condition form.
|
|
216
|
+
severity: ERROR
|
|
217
|
+
languages: [ruby]
|
|
218
|
+
metadata:
|
|
219
|
+
category: security
|
|
220
|
+
cwe:
|
|
221
|
+
- "CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')"
|
|
222
|
+
owasp:
|
|
223
|
+
- "A03:2021 - Injection"
|
|
224
|
+
subcategory: [vuln]
|
|
225
|
+
likelihood: HIGH
|
|
226
|
+
impact: HIGH
|
|
227
|
+
confidence: HIGH
|
|
228
|
+
source: narvy-local
|
|
229
|
+
patterns:
|
|
230
|
+
- pattern-either:
|
|
231
|
+
- pattern: |
|
|
232
|
+
$V = params[...]
|
|
233
|
+
...
|
|
234
|
+
$M.where("...#{$V}...")
|
|
235
|
+
- pattern: |
|
|
236
|
+
$V = params.$K
|
|
237
|
+
...
|
|
238
|
+
$M.where("...#{$V}...")
|
|
239
|
+
- pattern: |
|
|
240
|
+
$V = params[...]
|
|
241
|
+
...
|
|
242
|
+
$M.find_by_sql("...#{$V}...")
|
|
243
|
+
- pattern: |
|
|
244
|
+
$V = params[...]
|
|
245
|
+
...
|
|
246
|
+
$C.execute("...#{$V}...")
|
|
247
|
+
|
|
248
|
+
# Unsafe YAML deserialization
|
|
249
|
+
- id: narvy.ruby.yaml-load-unsafe.yaml-load-unsafe.yaml-load-unsafe
|
|
250
|
+
message: >
|
|
251
|
+
Request data reaches YAML.load / Psych.load. The full YAML loader
|
|
252
|
+
instantiates arbitrary Ruby objects from tags such as
|
|
253
|
+
!ruby/object:..., which is a remote-code-execution primitive when the
|
|
254
|
+
document is attacker-controlled (CWE-502). Remediation: use
|
|
255
|
+
YAML.safe_load (or Psych.safe_load) with an explicit permitted-classes
|
|
256
|
+
allow-list.
|
|
257
|
+
severity: ERROR
|
|
258
|
+
languages: [ruby]
|
|
259
|
+
metadata:
|
|
260
|
+
category: security
|
|
261
|
+
cwe:
|
|
262
|
+
- "CWE-502: Deserialization of Untrusted Data"
|
|
263
|
+
owasp:
|
|
264
|
+
- "A08:2021 - Software and Data Integrity Failures"
|
|
265
|
+
subcategory: [vuln]
|
|
266
|
+
likelihood: HIGH
|
|
267
|
+
impact: HIGH
|
|
268
|
+
confidence: HIGH
|
|
269
|
+
source: narvy-local
|
|
270
|
+
mode: taint
|
|
271
|
+
pattern-sources:
|
|
272
|
+
- patterns:
|
|
273
|
+
- pattern-either:
|
|
274
|
+
- pattern: params[...]
|
|
275
|
+
- pattern: params.$M
|
|
276
|
+
- pattern: request.body.read
|
|
277
|
+
- pattern: request.raw_post
|
|
278
|
+
- pattern: cookies[...]
|
|
279
|
+
pattern-sanitizers:
|
|
280
|
+
- pattern: YAML.safe_load(...)
|
|
281
|
+
- pattern: Psych.safe_load(...)
|
|
282
|
+
pattern-sinks:
|
|
283
|
+
- patterns:
|
|
284
|
+
- pattern-either:
|
|
285
|
+
- pattern: YAML.load($D)
|
|
286
|
+
- pattern: YAML.load($D, ...)
|
|
287
|
+
- pattern: YAML::load($D)
|
|
288
|
+
- pattern: Psych.load($D)
|
|
289
|
+
- pattern: Psych.load($D, ...)
|
|
290
|
+
- pattern: YAML.load_stream($D)
|
|
291
|
+
- pattern: YAML.parse($D)
|
|
292
|
+
|
|
293
|
+
# Reflected method dispatch via send / public_send with a request-derived name
|
|
294
|
+
- id: narvy.ruby.unsafe-send-direct-param.unsafe-send-direct-param.unsafe-send-direct-param
|
|
295
|
+
message: >
|
|
296
|
+
A method name taken from request data is passed to
|
|
297
|
+
send / public_send / __send__. This lets a caller invoke any reachable
|
|
298
|
+
method on the receiver - including destructive or privileged ones - which
|
|
299
|
+
is a code-execution / broken-access-control primitive (CWE-94).
|
|
300
|
+
Remediation: map the request value through an explicit allow-list of
|
|
301
|
+
permitted method names before dispatching, and never call the private
|
|
302
|
+
send/__send__ form with external input.
|
|
303
|
+
severity: ERROR
|
|
304
|
+
languages: [ruby]
|
|
305
|
+
metadata:
|
|
306
|
+
category: security
|
|
307
|
+
cwe:
|
|
308
|
+
- "CWE-94: Improper Control of Generation of Code ('Code Injection')"
|
|
309
|
+
owasp:
|
|
310
|
+
- "A03:2021 - Injection"
|
|
311
|
+
subcategory: [vuln]
|
|
312
|
+
likelihood: HIGH
|
|
313
|
+
impact: HIGH
|
|
314
|
+
confidence: MEDIUM
|
|
315
|
+
source: narvy-local
|
|
316
|
+
mode: taint
|
|
317
|
+
pattern-sources:
|
|
318
|
+
- patterns:
|
|
319
|
+
- pattern-either:
|
|
320
|
+
- pattern: params[...]
|
|
321
|
+
- pattern: params.$M
|
|
322
|
+
- pattern: request.params[...]
|
|
323
|
+
pattern-sinks:
|
|
324
|
+
- patterns:
|
|
325
|
+
- pattern-either:
|
|
326
|
+
- pattern: send($NAME)
|
|
327
|
+
- pattern: send($NAME, ...)
|
|
328
|
+
- pattern: public_send($NAME)
|
|
329
|
+
- pattern: public_send($NAME, ...)
|
|
330
|
+
- pattern: __send__($NAME)
|
|
331
|
+
- pattern: __send__($NAME, ...)
|
|
332
|
+
- pattern: $O.send($NAME)
|
|
333
|
+
- pattern: $O.send($NAME, ...)
|
|
334
|
+
- pattern: $O.public_send($NAME)
|
|
335
|
+
- pattern: $O.public_send($NAME, ...)
|
|
336
|
+
- pattern: $O.__send__($NAME)
|
|
337
|
+
- pattern: $O.__send__($NAME, ...)
|
|
338
|
+
- focus-metavariable: $NAME
|
|
339
|
+
|
|
340
|
+
# Unescaped view output - raw() / html_safe on request data (XSS)
|
|
341
|
+
- id: narvy.ruby.erb-raw-output.erb-raw-output.erb-raw-output
|
|
342
|
+
message: >
|
|
343
|
+
Request data is marked HTML-safe via raw() or .html_safe and rendered
|
|
344
|
+
without escaping. This disables Rails' automatic output encoding and
|
|
345
|
+
reflects attacker input into the page, producing cross-site scripting
|
|
346
|
+
(CWE-79). Remediation: render the value normally so Rails escapes it, or
|
|
347
|
+
sanitize with the sanitize() helper / Rails::Html::Sanitizer; never call
|
|
348
|
+
.html_safe on a string that contains untrusted data.
|
|
349
|
+
severity: ERROR
|
|
350
|
+
languages: [ruby]
|
|
351
|
+
metadata:
|
|
352
|
+
category: security
|
|
353
|
+
cwe:
|
|
354
|
+
- "CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')"
|
|
355
|
+
owasp:
|
|
356
|
+
- "A03:2021 - Injection"
|
|
357
|
+
subcategory: [vuln]
|
|
358
|
+
likelihood: HIGH
|
|
359
|
+
impact: MEDIUM
|
|
360
|
+
confidence: MEDIUM
|
|
361
|
+
source: narvy-local
|
|
362
|
+
mode: taint
|
|
363
|
+
pattern-sources:
|
|
364
|
+
- patterns:
|
|
365
|
+
- pattern-either:
|
|
366
|
+
- pattern: params[...]
|
|
367
|
+
- pattern: params.$M
|
|
368
|
+
- pattern: request.params[...]
|
|
369
|
+
pattern-sanitizers:
|
|
370
|
+
- pattern: sanitize(...)
|
|
371
|
+
- pattern: ERB::Util.html_escape(...)
|
|
372
|
+
- pattern: CGI.escapeHTML(...)
|
|
373
|
+
- pattern: h(...)
|
|
374
|
+
pattern-sinks:
|
|
375
|
+
- patterns:
|
|
376
|
+
- pattern-either:
|
|
377
|
+
- pattern: raw($X)
|
|
378
|
+
- pattern: $X.html_safe
|
|
379
|
+
- pattern: content_tag($T, $X.html_safe)
|
|
380
|
+
- pattern: content_tag($T, raw($X))
|
|
381
|
+
|
|
382
|
+
# Open redirect - redirect_to with a request-derived target
|
|
383
|
+
- id: narvy.ruby.open-redirect.open-redirect.open-redirect
|
|
384
|
+
message: >
|
|
385
|
+
redirect_to receives a request-controlled URL. An attacker can point the
|
|
386
|
+
redirect at an external site to build a convincing phishing link or, on
|
|
387
|
+
older Rails, bypass host checks (CWE-601). Remediation: redirect to a
|
|
388
|
+
fixed internal path, look the target up in a server-side allow-list, or
|
|
389
|
+
pass only a relative path and set allow_other_host: false.
|
|
390
|
+
severity: WARNING
|
|
391
|
+
languages: [ruby]
|
|
392
|
+
metadata:
|
|
393
|
+
category: security
|
|
394
|
+
cwe:
|
|
395
|
+
- "CWE-601: URL Redirection to Untrusted Site ('Open Redirect')"
|
|
396
|
+
owasp:
|
|
397
|
+
- "A01:2021 - Broken Access Control"
|
|
398
|
+
subcategory: [audit]
|
|
399
|
+
likelihood: MEDIUM
|
|
400
|
+
impact: MEDIUM
|
|
401
|
+
confidence: MEDIUM
|
|
402
|
+
source: narvy-local
|
|
403
|
+
mode: taint
|
|
404
|
+
pattern-sources:
|
|
405
|
+
- patterns:
|
|
406
|
+
- pattern-either:
|
|
407
|
+
- pattern: params[...]
|
|
408
|
+
- pattern: params.$M
|
|
409
|
+
- pattern: request.params[...]
|
|
410
|
+
pattern-sinks:
|
|
411
|
+
- patterns:
|
|
412
|
+
- pattern-either:
|
|
413
|
+
- pattern: redirect_to($URL)
|
|
414
|
+
- pattern: redirect_to($URL, ...)
|
|
415
|
+
- pattern: "redirect_back(fallback_location: $URL)"
|
|
416
|
+
- focus-metavariable: $URL
|
|
417
|
+
|
|
418
|
+
# Weak hash - MD5
|
|
419
|
+
- id: narvy.ruby.weak-hash.weak-hash-md5-digest.weak-hash-md5-digest
|
|
420
|
+
message: >
|
|
421
|
+
MD5 is used to compute a digest. MD5 offers no collision resistance and is
|
|
422
|
+
fast to brute-force, so it is unsuitable for signatures, integrity tokens,
|
|
423
|
+
or password/credential storage (CWE-328). Remediation: use SHA-256 or
|
|
424
|
+
SHA-3 for integrity, an HMAC with a strong hash for authentication, and a
|
|
425
|
+
dedicated password KDF (bcrypt, scrypt, Argon2) for credentials.
|
|
426
|
+
severity: WARNING
|
|
427
|
+
languages: [ruby]
|
|
428
|
+
metadata:
|
|
429
|
+
category: security
|
|
430
|
+
cwe:
|
|
431
|
+
- "CWE-328: Use of Weak Hash"
|
|
432
|
+
owasp:
|
|
433
|
+
- "A02:2021 - Cryptographic Failures"
|
|
434
|
+
subcategory: [vuln]
|
|
435
|
+
likelihood: LOW
|
|
436
|
+
impact: HIGH
|
|
437
|
+
confidence: MEDIUM
|
|
438
|
+
source: narvy-local
|
|
439
|
+
pattern-either:
|
|
440
|
+
- pattern: Digest::MD5
|
|
441
|
+
- pattern: OpenSSL::Digest::MD5
|
|
442
|
+
- pattern: OpenSSL::Digest.new("MD5")
|
|
443
|
+
- pattern: OpenSSL::Digest.new('MD5')
|
|
444
|
+
- pattern: OpenSSL::Digest.digest("MD5", ...)
|
|
445
|
+
- pattern: OpenSSL::HMAC.hexdigest("MD5", ...)
|
|
446
|
+
- pattern: OpenSSL::HMAC.digest("MD5", ...)
|
|
447
|
+
- pattern: OpenSSL::HMAC.hexdigest("md5", ...)
|
|
448
|
+
|
|
449
|
+
# Weak hash - SHA-1
|
|
450
|
+
- id: narvy.ruby.weak-hash.weak-hash-sha1-digest.weak-hash-sha1-digest
|
|
451
|
+
message: >
|
|
452
|
+
SHA-1 is used to compute a digest. SHA-1 has a practical, publicly
|
|
453
|
+
demonstrated collision and must not be relied on for signatures,
|
|
454
|
+
certificates, integrity tokens, or credential storage (CWE-328).
|
|
455
|
+
Remediation: migrate to SHA-256 or SHA-3; for message authentication use
|
|
456
|
+
an HMAC over a strong hash, and for passwords use bcrypt/scrypt/Argon2.
|
|
457
|
+
severity: WARNING
|
|
458
|
+
languages: [ruby]
|
|
459
|
+
metadata:
|
|
460
|
+
category: security
|
|
461
|
+
cwe:
|
|
462
|
+
- "CWE-328: Use of Weak Hash"
|
|
463
|
+
owasp:
|
|
464
|
+
- "A02:2021 - Cryptographic Failures"
|
|
465
|
+
subcategory: [vuln]
|
|
466
|
+
likelihood: LOW
|
|
467
|
+
impact: MEDIUM
|
|
468
|
+
confidence: MEDIUM
|
|
469
|
+
source: narvy-local
|
|
470
|
+
pattern-either:
|
|
471
|
+
- pattern: Digest::SHA1
|
|
472
|
+
- pattern: OpenSSL::Digest::SHA1
|
|
473
|
+
- pattern: OpenSSL::Digest.new("SHA1")
|
|
474
|
+
- pattern: OpenSSL::Digest.new('SHA1')
|
|
475
|
+
- pattern: OpenSSL::Digest.digest("SHA1", ...)
|
|
476
|
+
- pattern: OpenSSL::HMAC.hexdigest("SHA1", ...)
|
|
477
|
+
- pattern: OpenSSL::HMAC.digest("SHA1", ...)
|
|
478
|
+
- pattern: OpenSSL::HMAC.hexdigest("sha1", ...)
|
|
479
|
+
- pattern: OpenSSL::HMAC.digest("sha1", ...)
|
|
480
|
+
|
|
481
|
+
# CSRF - controller with no forgery protection
|
|
482
|
+
- id: narvy.ruby.csrf.csrf-protection-not-enabled.csrf-protection-not-enabled
|
|
483
|
+
message: >
|
|
484
|
+
This controller subclasses ActionController::Base but never calls
|
|
485
|
+
protect_from_forgery, so state-changing requests are accepted without a
|
|
486
|
+
verified CSRF token unless protection is enabled app-wide (CWE-352).
|
|
487
|
+
Remediation: add protect_from_forgery with: :exception (the Rails
|
|
488
|
+
default), or confirm forgery protection is configured globally for this
|
|
489
|
+
application.
|
|
490
|
+
severity: WARNING
|
|
491
|
+
languages: [ruby]
|
|
492
|
+
metadata:
|
|
493
|
+
category: security
|
|
494
|
+
cwe:
|
|
495
|
+
- "CWE-352: Cross-Site Request Forgery (CSRF)"
|
|
496
|
+
owasp:
|
|
497
|
+
- "A01:2021 - Broken Access Control"
|
|
498
|
+
subcategory: [audit]
|
|
499
|
+
likelihood: LOW
|
|
500
|
+
impact: MEDIUM
|
|
501
|
+
confidence: LOW
|
|
502
|
+
source: narvy-local
|
|
503
|
+
patterns:
|
|
504
|
+
- pattern: |
|
|
505
|
+
class $K < ActionController::Base
|
|
506
|
+
...
|
|
507
|
+
end
|
|
508
|
+
- pattern-not: |
|
|
509
|
+
class $K < ActionController::Base
|
|
510
|
+
...
|
|
511
|
+
protect_from_forgery
|
|
512
|
+
...
|
|
513
|
+
end
|
|
514
|
+
- pattern-not: |
|
|
515
|
+
class $K < ActionController::Base
|
|
516
|
+
...
|
|
517
|
+
protect_from_forgery(...)
|
|
518
|
+
...
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
# CSRF - token verification skipped
|
|
522
|
+
- id: narvy.ruby.csrf.csrf-verification-skipped.csrf-verification-skipped
|
|
523
|
+
message: >
|
|
524
|
+
CSRF token verification is turned off for this controller via
|
|
525
|
+
skip_before_action/skip_before_filter :verify_authenticity_token. This is
|
|
526
|
+
only safe for endpoints that do not rely on cookie/session authentication
|
|
527
|
+
(for example token-authenticated API or webhook actions). If any skipped
|
|
528
|
+
action performs a state change while trusting the session cookie, it is
|
|
529
|
+
exploitable via CSRF (CWE-352). Remediation: scope the skip to specific
|
|
530
|
+
actions and confirm none of them authenticate via the session.
|
|
531
|
+
severity: WARNING
|
|
532
|
+
languages: [ruby]
|
|
533
|
+
metadata:
|
|
534
|
+
category: security
|
|
535
|
+
cwe:
|
|
536
|
+
- "CWE-352: Cross-Site Request Forgery (CSRF)"
|
|
537
|
+
owasp:
|
|
538
|
+
- "A01:2021 - Broken Access Control"
|
|
539
|
+
subcategory: [audit]
|
|
540
|
+
likelihood: LOW
|
|
541
|
+
impact: MEDIUM
|
|
542
|
+
confidence: LOW
|
|
543
|
+
source: narvy-local
|
|
544
|
+
pattern-either:
|
|
545
|
+
- pattern: skip_before_action :verify_authenticity_token
|
|
546
|
+
- pattern: skip_before_action :verify_authenticity_token, ...
|
|
547
|
+
- pattern: skip_before_action(:verify_authenticity_token, ...)
|
|
548
|
+
- pattern: skip_before_filter :verify_authenticity_token
|
|
549
|
+
- pattern: skip_before_filter :verify_authenticity_token, ...
|
|
550
|
+
- pattern: skip_before_filter(:verify_authenticity_token, ...)
|
|
551
|
+
- pattern: skip_forgery_protection
|
|
552
|
+
- pattern: skip_forgery_protection(...)
|
|
553
|
+
|
|
554
|
+
# CSRF - protection disabled at application/environment config level
|
|
555
|
+
- id: narvy.ruby.csrf.csrf-protection-disabled-globally.csrf-protection-disabled-globally
|
|
556
|
+
message: >
|
|
557
|
+
Application-level forgery protection is switched off
|
|
558
|
+
(allow_forgery_protection = false or default_protect_from_forgery =
|
|
559
|
+
false), which removes CSRF defenses from every controller in the app
|
|
560
|
+
(CWE-352). This is only defensible for a pure token-authenticated API
|
|
561
|
+
with no session cookies. Remediation: leave allow_forgery_protection at
|
|
562
|
+
its default (true) for any app that uses cookie/session authentication,
|
|
563
|
+
and never disable it in production.
|
|
564
|
+
severity: ERROR
|
|
565
|
+
languages: [ruby]
|
|
566
|
+
metadata:
|
|
567
|
+
category: security
|
|
568
|
+
cwe:
|
|
569
|
+
- "CWE-352: Cross-Site Request Forgery (CSRF)"
|
|
570
|
+
owasp:
|
|
571
|
+
- "A01:2021 - Broken Access Control"
|
|
572
|
+
subcategory: [vuln]
|
|
573
|
+
likelihood: LOW
|
|
574
|
+
impact: HIGH
|
|
575
|
+
confidence: MEDIUM
|
|
576
|
+
source: narvy-local
|
|
577
|
+
pattern-either:
|
|
578
|
+
- pattern: $X.action_controller.allow_forgery_protection = false
|
|
579
|
+
- pattern: config.action_controller.allow_forgery_protection = false
|
|
580
|
+
- pattern: $X.action_controller.default_protect_from_forgery = false
|
|
581
|
+
- pattern: config.action_controller.default_protect_from_forgery = false
|
|
582
|
+
|
|
583
|
+
# Mass assignment - raw params slice into a model
|
|
584
|
+
- id: narvy.ruby.mass-assignment.mass-assignment-unpermitted-params.mass-assignment-unpermitted-params
|
|
585
|
+
message: >
|
|
586
|
+
A model is built or updated from a raw params slice with no .permit(...)
|
|
587
|
+
filtering in between. An attacker can supply extra fields
|
|
588
|
+
(role, admin, verified, ...) that map straight onto model attributes,
|
|
589
|
+
escalating privilege or tampering with records (CWE-915). Remediation:
|
|
590
|
+
pass request attributes through strong parameters -
|
|
591
|
+
params.require(:model).permit(:field1, :field2) - before .new / .create /
|
|
592
|
+
.update.
|
|
593
|
+
severity: WARNING
|
|
594
|
+
languages: [ruby]
|
|
595
|
+
metadata:
|
|
596
|
+
category: security
|
|
597
|
+
cwe:
|
|
598
|
+
- "CWE-915: Improperly Controlled Modification of Dynamically-Determined Object Attributes"
|
|
599
|
+
owasp:
|
|
600
|
+
- "A08:2021 - Software and Data Integrity Failures"
|
|
601
|
+
subcategory: [audit]
|
|
602
|
+
likelihood: LOW
|
|
603
|
+
impact: HIGH
|
|
604
|
+
confidence: LOW
|
|
605
|
+
source: narvy-local
|
|
606
|
+
pattern-either:
|
|
607
|
+
- pattern: $M.new(params[$K])
|
|
608
|
+
- pattern: $M.create(params[$K])
|
|
609
|
+
- pattern: $M.create!(params[$K])
|
|
610
|
+
- pattern: $R.update(params[$K])
|
|
611
|
+
- pattern: $R.update!(params[$K])
|
|
612
|
+
- pattern: $R.update_attributes(params[$K])
|
|
613
|
+
- pattern: $R.update_attributes!(params[$K])
|
|
614
|
+
- pattern: $R.assign_attributes(params[$K])
|
|
615
|
+
- pattern: $M.new(params.to_unsafe_h)
|
|
616
|
+
- pattern: $M.new(params[$K].to_unsafe_h)
|
|
617
|
+
- pattern: $M.create(params.to_unsafe_h)
|
|
618
|
+
|
|
619
|
+
# Mass assignment - permit! / attr_accessible :all
|
|
620
|
+
- id: narvy.ruby.mass-assignment.mass-assignment-permit-bang.mass-assignment-permit-bang
|
|
621
|
+
message: >
|
|
622
|
+
permit! whitelists every key in the parameter hash instead of an explicit
|
|
623
|
+
field list, and the legacy attr_accessible :all has the same effect. Both
|
|
624
|
+
fully defeat strong-parameter filtering and re-open mass assignment
|
|
625
|
+
(CWE-915). Remediation: replace permit! with an explicit
|
|
626
|
+
permit(:field1, :field2) enumerating only the attributes the action is
|
|
627
|
+
allowed to set.
|
|
628
|
+
severity: ERROR
|
|
629
|
+
languages: [ruby]
|
|
630
|
+
metadata:
|
|
631
|
+
category: security
|
|
632
|
+
cwe:
|
|
633
|
+
- "CWE-915: Improperly Controlled Modification of Dynamically-Determined Object Attributes"
|
|
634
|
+
owasp:
|
|
635
|
+
- "A08:2021 - Software and Data Integrity Failures"
|
|
636
|
+
subcategory: [vuln]
|
|
637
|
+
likelihood: MEDIUM
|
|
638
|
+
impact: HIGH
|
|
639
|
+
confidence: HIGH
|
|
640
|
+
source: narvy-local
|
|
641
|
+
pattern-either:
|
|
642
|
+
- pattern: params.permit!
|
|
643
|
+
- pattern: params[$K].permit!
|
|
644
|
+
- pattern: params.require($K).permit!
|
|
645
|
+
- pattern: params.fetch($K).permit!
|
|
646
|
+
- pattern: attr_accessible :all
|
|
647
|
+
|
|
648
|
+
# TLS - certificate verification explicitly disabled (SSLContext)
|
|
649
|
+
- id: narvy.ruby.ssl-verify-mode-none.ssl-verify-mode-none.ssl-verify-mode-none
|
|
650
|
+
message: >
|
|
651
|
+
TLS peer verification is explicitly set to OpenSSL::SSL::VERIFY_NONE. The
|
|
652
|
+
client will accept any certificate, so an active network attacker can
|
|
653
|
+
present a forged certificate and transparently intercept the connection
|
|
654
|
+
(CWE-295). Remediation: use OpenSSL::SSL::VERIFY_PEER (the default). If a
|
|
655
|
+
private CA is required, supply it through ca_file / ca_path / cert_store
|
|
656
|
+
rather than disabling verification.
|
|
657
|
+
severity: ERROR
|
|
658
|
+
languages: [ruby]
|
|
659
|
+
metadata:
|
|
660
|
+
category: security
|
|
661
|
+
cwe:
|
|
662
|
+
- "CWE-295: Improper Certificate Validation"
|
|
663
|
+
owasp:
|
|
664
|
+
- "A07:2021 - Identification and Authentication Failures"
|
|
665
|
+
subcategory: [vuln]
|
|
666
|
+
likelihood: MEDIUM
|
|
667
|
+
impact: HIGH
|
|
668
|
+
confidence: MEDIUM
|
|
669
|
+
source: narvy-local
|
|
670
|
+
pattern-either:
|
|
671
|
+
- pattern: $X.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
672
|
+
- pattern: $H[$K] = OpenSSL::SSL::VERIFY_NONE
|
|
673
|
+
- pattern: "OpenSSL::SSL::SSLContext.new(verify_mode: OpenSSL::SSL::VERIFY_NONE)"
|
|
674
|
+
|
|
675
|
+
# TLS - Net::HTTP with SSL but verification disabled
|
|
676
|
+
- id: narvy.ruby.net-http-ssl-verify-disabled.net-http-ssl-verify-disabled.net-http-ssl-verify-disabled
|
|
677
|
+
message: >
|
|
678
|
+
A Net::HTTP client uses TLS (use_ssl) but disables certificate
|
|
679
|
+
verification with verify_mode = OpenSSL::SSL::VERIFY_NONE. Any
|
|
680
|
+
certificate, including an attacker's, is accepted, defeating the point of
|
|
681
|
+
TLS (CWE-295). Remediation: remove the verify_mode override so Net::HTTP
|
|
682
|
+
validates against the system trust store, or provide a specific ca_file
|
|
683
|
+
for a private CA.
|
|
684
|
+
severity: ERROR
|
|
685
|
+
languages: [ruby]
|
|
686
|
+
metadata:
|
|
687
|
+
category: security
|
|
688
|
+
cwe:
|
|
689
|
+
- "CWE-295: Improper Certificate Validation"
|
|
690
|
+
owasp:
|
|
691
|
+
- "A07:2021 - Identification and Authentication Failures"
|
|
692
|
+
subcategory: [vuln]
|
|
693
|
+
likelihood: MEDIUM
|
|
694
|
+
impact: HIGH
|
|
695
|
+
confidence: HIGH
|
|
696
|
+
source: narvy-local
|
|
697
|
+
pattern-either:
|
|
698
|
+
- pattern: "Net::HTTP.start(..., verify_mode: OpenSSL::SSL::VERIFY_NONE, ...)"
|
|
699
|
+
- patterns:
|
|
700
|
+
- pattern-inside: |
|
|
701
|
+
$H = Net::HTTP.new(...)
|
|
702
|
+
...
|
|
703
|
+
- pattern: $H.verify_mode = OpenSSL::SSL::VERIFY_NONE
|