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.
Files changed (93) hide show
  1. narvy/__init__.py +3 -0
  2. narvy/android/__init__.py +0 -0
  3. narvy/android/source_analyzer.py +268 -0
  4. narvy/android/split_bundle.py +475 -0
  5. narvy/android_rule_context.py +196 -0
  6. narvy/apk_memory_preflight.py +248 -0
  7. narvy/auth.py +40 -0
  8. narvy/ci_templates/bitbucket.yml +25 -0
  9. narvy/ci_templates/github.yml +60 -0
  10. narvy/ci_templates/gitlab.yml +32 -0
  11. narvy/cloud/__init__.py +0 -0
  12. narvy/cloud/aws_scan.py +1188 -0
  13. narvy/comment_filter.py +134 -0
  14. narvy/crypto_taint_lite.py +82 -0
  15. narvy/decompiler.py +337 -0
  16. narvy/doctor.py +244 -0
  17. narvy/host/__init__.py +0 -0
  18. narvy/host/audit.py +157 -0
  19. narvy/host/host_knowledge.py +982 -0
  20. narvy/host/lynis_bootstrap.py +400 -0
  21. narvy/host/lynis_parser.py +358 -0
  22. narvy/host/narvy_checks.py +789 -0
  23. narvy/host/report.py +69 -0
  24. narvy/host/ssh_exec.py +219 -0
  25. narvy/ios/__init__.py +0 -0
  26. narvy/ios/binary_analyzer.py +1201 -0
  27. narvy/ios/plist_checks.py +249 -0
  28. narvy/ios/source_analyzer.py +301 -0
  29. narvy/ios/third_party_filter.py +242 -0
  30. narvy/ios/trust_all_context.py +66 -0
  31. narvy/main.py +1983 -0
  32. narvy/native_hardening.py +503 -0
  33. narvy/reporter.py +151 -0
  34. narvy/rule_engine.py +57 -0
  35. narvy/rules/android/config.yml +77 -0
  36. narvy/rules/android/crypto.yml +34 -0
  37. narvy/rules/android/secrets.yml +161 -0
  38. narvy/rules/android/storage.yml +24 -0
  39. narvy/rules/android/webview.yml +23 -0
  40. narvy/rules/android.yml +219 -0
  41. narvy/rules/ios/objc/crypto.yml +56 -0
  42. narvy/rules/ios/objc/network.yml +67 -0
  43. narvy/rules/ios/objc/secrets.yml +79 -0
  44. narvy/rules/ios/objc/storage.yml +45 -0
  45. narvy/rules/ios/objc/webview.yml +45 -0
  46. narvy/rules/ios_swift.yml +327 -0
  47. narvy/rules/web/go.yml +2837 -0
  48. narvy/rules/web/java.yml +1576 -0
  49. narvy/rules/web/javascript.yml +3683 -0
  50. narvy/rules/web/kotlin.yml +413 -0
  51. narvy/rules/web/local/csharp_narvy/config.yml +61 -0
  52. narvy/rules/web/local/csharp_narvy/crypto.yml +64 -0
  53. narvy/rules/web/local/csharp_narvy/deserialization.yml +59 -0
  54. narvy/rules/web/local/csharp_narvy/injection.yml +122 -0
  55. narvy/rules/web/local/csharp_narvy/xxe.yml +48 -0
  56. narvy/rules/web/local/java_narvy/auth_jwt.yml +134 -0
  57. narvy/rules/web/local/java_narvy/deserialization.yml +108 -0
  58. narvy/rules/web/local/java_narvy/mybatis.yml +39 -0
  59. narvy/rules/web/local/java_narvy/snakeyaml.yml +34 -0
  60. narvy/rules/web/local/java_narvy/spring_authz.yml +32 -0
  61. narvy/rules/web/local/java_narvy/spring_config.yml +67 -0
  62. narvy/rules/web/local/java_narvy/spring_hardening.yml +291 -0
  63. narvy/rules/web/local/java_narvy/sqli.yml +235 -0
  64. narvy/rules/web/local/java_narvy/xxe.yml +212 -0
  65. narvy/rules/web/php.yml +1644 -0
  66. narvy/rules/web/python.yml +3967 -0
  67. narvy/rules/web/ruby.yml +703 -0
  68. narvy/rules/web/rust.yml +258 -0
  69. narvy/rules/web/secrets.yml +1420 -0
  70. narvy/rules/web/secrets_supplement.yml +383 -0
  71. narvy/sca/__init__.py +1 -0
  72. narvy/sca/android_deps.py +349 -0
  73. narvy/sca/ios_deps.py +578 -0
  74. narvy/sca/osv_client.py +617 -0
  75. narvy/sca/web_deps.py +955 -0
  76. narvy/scope_config.py +195 -0
  77. narvy/semgrep_engine.py +219 -0
  78. narvy/stack_protector_evidence.py +96 -0
  79. narvy/third_party_filter.py +211 -0
  80. narvy/uploader.py +92 -0
  81. narvy/weak_prng_context.py +270 -0
  82. narvy/web/__init__.py +0 -0
  83. narvy/web/nuclei_binary.py +105 -0
  84. narvy/web/scan_blocklist.py +96 -0
  85. narvy/web/scanner.py +842 -0
  86. narvy/web/source_analyzer.py +714 -0
  87. narvy/web/ssrf_guard.py +374 -0
  88. narvy_cli-1.0.0.dist-info/METADATA +165 -0
  89. narvy_cli-1.0.0.dist-info/RECORD +93 -0
  90. narvy_cli-1.0.0.dist-info/WHEEL +5 -0
  91. narvy_cli-1.0.0.dist-info/entry_points.txt +2 -0
  92. narvy_cli-1.0.0.dist-info/licenses/LICENSE +202 -0
  93. narvy_cli-1.0.0.dist-info/top_level.txt +1 -0
@@ -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