scryer 0.3.0 → 1.1.1

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +322 -0
  3. data/README.md +546 -43
  4. data/lib/generators/scryer/USAGE +10 -2
  5. data/lib/generators/scryer/templates/scryer_initializer.rb +15 -0
  6. data/lib/scryer/ai_fix_suggester.rb +29 -9
  7. data/lib/scryer/ast.rb +95 -6
  8. data/lib/scryer/authorization_watcher.rb +156 -0
  9. data/lib/scryer/baseline.rb +75 -0
  10. data/lib/scryer/cli.rb +209 -11
  11. data/lib/scryer/dependency_audit.rb +108 -5
  12. data/lib/scryer/finding.rb +6 -0
  13. data/lib/scryer/fix_verifier.rb +82 -0
  14. data/lib/scryer/minitest.rb +48 -0
  15. data/lib/scryer/performance_rules/inefficient_save_loop_rule.rb +32 -0
  16. data/lib/scryer/performance_rules/missing_pagination_rule.rb +1 -0
  17. data/lib/scryer/performance_rules/n_plus_one_query_rule.rb +1 -0
  18. data/lib/scryer/performance_rules/unbounded_table_scan_rule.rb +1 -0
  19. data/lib/scryer/report_renderer.rb +637 -44
  20. data/lib/scryer/rspec.rb +55 -0
  21. data/lib/scryer/rule.rb +22 -2
  22. data/lib/scryer/rules/action_cable_forgery_protection_rule.rb +50 -0
  23. data/lib/scryer/rules/active_storage_inline_disposition_rule.rb +50 -0
  24. data/lib/scryer/rules/active_storage_missing_content_type_validation_rule.rb +79 -0
  25. data/lib/scryer/rules/authentication_bypass_rule.rb +95 -0
  26. data/lib/scryer/rules/command_injection_rule.rb +3 -0
  27. data/lib/scryer/rules/consider_all_requests_local_rule.rb +51 -0
  28. data/lib/scryer/rules/cors_misconfiguration_rule.rb +100 -0
  29. data/lib/scryer/rules/csrf_protection_rule.rb +60 -11
  30. data/lib/scryer/rules/force_ssl_rule.rb +48 -0
  31. data/lib/scryer/rules/graphql_missing_query_limits_rule.rb +106 -0
  32. data/lib/scryer/rules/hardcoded_basic_auth_rule.rb +51 -0
  33. data/lib/scryer/rules/hardcoded_secret_key_base_rule.rb +58 -0
  34. data/lib/scryer/rules/hardcoded_secret_rule.rb +3 -0
  35. data/lib/scryer/rules/host_authorization_disabled_rule.rb +50 -0
  36. data/lib/scryer/rules/idor_rule.rb +165 -0
  37. data/lib/scryer/rules/insecure_cookie_serializer_rule.rb +47 -0
  38. data/lib/scryer/rules/job_raw_params_rule.rb +131 -0
  39. data/lib/scryer/rules/jwt_insecure_rule.rb +123 -0
  40. data/lib/scryer/rules/mass_assignment_rule.rb +34 -10
  41. data/lib/scryer/rules/missing_authorization_rule.rb +103 -0
  42. data/lib/scryer/rules/missing_policy_scope_rule.rb +134 -0
  43. data/lib/scryer/rules/open_redirect_rule.rb +3 -0
  44. data/lib/scryer/rules/path_traversal_rule.rb +110 -0
  45. data/lib/scryer/rules/security_headers_rule.rb +133 -0
  46. data/lib/scryer/rules/sql_injection_rule.rb +3 -0
  47. data/lib/scryer/rules/ssrf_rule.rb +139 -0
  48. data/lib/scryer/rules/unsafe_deserialization_rule.rb +3 -0
  49. data/lib/scryer/rules/verbose_production_log_level_rule.rb +53 -0
  50. data/lib/scryer/rules/weak_crypto_rule.rb +37 -2
  51. data/lib/scryer/rules/weak_session_cookie_rule.rb +51 -0
  52. data/lib/scryer/rules/xss_unsafe_html_rule.rb +41 -0
  53. data/lib/scryer/style_rules/frozen_string_literal_rule.rb +1 -0
  54. data/lib/scryer/version.rb +1 -1
  55. data/lib/scryer.rb +15 -0
  56. data/lib/tasks/scryer.rake +138 -13
  57. metadata +41 -12
@@ -8,7 +8,7 @@ require "fileutils"
8
8
  # ActiveSupport methods (`.presence`/`.blank?`) for the same reason — plain
9
9
  # Ruby nil-or-empty checks only.
10
10
  namespace :scryer do
11
- desc "Run Scryer and write a report. Args are any mix of json/html/csv (which formats " \
11
+ desc "Run Scryer and write a report. Args are any mix of json/html/csv/sarif (which formats " \
12
12
  "to write — default json,html), the token 'nodeps' (skip the dependency audit — OSV.dev " \
13
13
  "vulnerable gems + insecure git/http sources — that otherwise runs by default; use for a " \
14
14
  "fast, fully offline run), plus at most one path (a directory when writing more than one " \
@@ -16,7 +16,9 @@ namespace :scryer do
16
16
  "so pass each token as its own item rather than one comma-joined string. " \
17
17
  "e.g. rails scryer:report, rails 'scryer:report[html]', " \
18
18
  "rails 'scryer:report[json,doc/security.json]', rails 'scryer:report[json,html]', " \
19
- "rails 'scryer:report[html,nodeps]', rails 'scryer:report[csv]'"
19
+ "rails 'scryer:report[html,nodeps]', rails 'scryer:report[csv]', rails 'scryer:report[sarif]'. " \
20
+ "Set SCRYER_BASELINE=PATH (an env var, not a bracket arg — see scryer:save_baseline) to " \
21
+ "report only findings new since that baseline was saved."
20
22
  task :report, [:format] do |_, args|
21
23
  root = defined?(Rails) ? Rails.root.to_s : Dir.pwd
22
24
  dirs = Scryer.configuration.dirs
@@ -38,12 +40,27 @@ namespace :scryer do
38
40
  dependency_findings = []
39
41
  if run_deps
40
42
  puts "Scryer: querying OSV.dev for known-vulnerable gems (needs network)..."
41
- dependency_findings = Scryer::DependencyAudit.insecure_sources(root) + Scryer::DependencyAudit.vulnerable_gems(root)
43
+ dependency_findings = Scryer::DependencyAudit.insecure_sources(root) + Scryer::DependencyAudit.vulnerable_gems(root) +
44
+ Scryer::DependencyAudit.ruby_eol_check(root) + Scryer::DependencyAudit.credentials_exposure_check(root)
45
+ end
46
+
47
+ # Rake bracket args are already stretched thin (format tokens + nodeps +
48
+ # one output path) — an ENV var is the more conventional way Rails rake
49
+ # tasks take an extra config value without overloading positional args
50
+ # further. `rails 'scryer:save_baseline[tmp/scryer_baseline.json]'` writes
51
+ # one; `SCRYER_BASELINE=tmp/scryer_baseline.json rails scryer:report`
52
+ # reads it back. See Scryer::Baseline / the CLI's --baseline flag for
53
+ # what this actually does (fingerprint-based new-vs-fixed diffing, not
54
+ # tied to line number).
55
+ baseline_path = blank_to_nil(ENV["SCRYER_BASELINE"])
56
+ fixed_count = 0
57
+ if baseline_path
58
+ dependency_findings, fixed_count = ScryerTasks.apply_baseline(baseline_path, result, dependency_findings)
42
59
  end
43
60
 
44
61
  if Scryer.configuration.ai_client
45
62
  puts "Scryer: rewriting suggested fixes via the configured AI client..."
46
- Scryer::AiFixSuggester.enhance_result!(result)
63
+ Scryer::AiFixSuggester.enhance_result!(result, root: root)
47
64
  Scryer::AiFixSuggester.enhance_many!(dependency_findings) unless dependency_findings.empty?
48
65
  end
49
66
 
@@ -63,12 +80,14 @@ namespace :scryer do
63
80
  content = case format
64
81
  when "json" then renderer.as_json
65
82
  when "csv" then renderer.as_csv
83
+ when "sarif" then renderer.as_sarif
66
84
  else renderer.as_html
67
85
  end
68
86
  File.write(path, content)
69
87
  end
70
88
 
71
- ScryerTasks.print_summary(result: result, dependency_findings: dependency_findings, ran_deps: run_deps, paths: paths)
89
+ ScryerTasks.print_summary(result: result, dependency_findings: dependency_findings, ran_deps: run_deps, paths: paths,
90
+ renderer: renderer, fixed_count: fixed_count, baseline_path: baseline_path)
72
91
 
73
92
  # Same gate as the `scryer` executable and scryer:audit_dependencies
74
93
  # below: fail the task (and so the CI job running it) on any security or
@@ -81,6 +100,37 @@ namespace :scryer do
81
100
  end
82
101
  end
83
102
 
103
+ desc "Convenience wrapper for CI: runs scryer:report with CI-sensible defaults — JSON + " \
104
+ "SARIF written under tmp/ (SARIF for GitHub Code Scanning / similar dashboards, JSON as " \
105
+ "the machine-readable form other CI steps can parse), dependency audit on, same " \
106
+ "SCRYER_BASELINE support and same fail-the-build-on-findings behavior as scryer:report. " \
107
+ "Equivalent to `rails 'scryer:report[json,sarif]'` under a name that doesn't require " \
108
+ "remembering the bracket-arg syntax. e.g. rails scryer:ci"
109
+ task :ci do
110
+ Rake::Task["scryer:report"].invoke("json", "sarif")
111
+ end
112
+
113
+ desc "Run Scryer and save every finding's fingerprint to PATH, for later comparison via " \
114
+ "SCRYER_BASELINE=PATH rails scryer:report (see that task's SCRYER_BASELINE note). " \
115
+ "Runs the same scan as scryer:report (including the dependency audit) but writes no " \
116
+ "report files. e.g. rails 'scryer:save_baseline[tmp/scryer_baseline.json]'"
117
+ task :save_baseline, [:path] do |_, args|
118
+ path = args[:path] || abort("Scryer: scryer:save_baseline needs a path, " \
119
+ "e.g. rails 'scryer:save_baseline[tmp/scryer_baseline.json]'")
120
+ root = defined?(Rails) ? Rails.root.to_s : Dir.pwd
121
+ dirs = Scryer.configuration.dirs
122
+
123
+ result = Scryer::Scanner.new(root: root, dirs: dirs, skip_rules: Scryer.configuration.skip_rules).call
124
+ puts "Scryer: querying OSV.dev for known-vulnerable gems (needs network)..."
125
+ dependency_findings = Scryer::DependencyAudit.insecure_sources(root) + Scryer::DependencyAudit.vulnerable_gems(root) +
126
+ Scryer::DependencyAudit.ruby_eol_check(root) + Scryer::DependencyAudit.credentials_exposure_check(root)
127
+
128
+ all_findings = (result.security_findings + result.performance_findings + result.style_findings).map(&:to_h) +
129
+ dependency_findings.map(&:to_h)
130
+ Scryer::Baseline.save(path, all_findings)
131
+ puts "Scryer: saved baseline of #{all_findings.size} finding(s) to #{path}."
132
+ end
133
+
84
134
  desc "Check Gemfile.lock for known-vulnerable gem versions (via OSV.dev — needs network) " \
85
135
  "and insecure git/http gem sources (offline). Exits non-zero if anything is found, so " \
86
136
  "this can gate CI the same way `bundle-audit check` does."
@@ -92,20 +142,23 @@ namespace :scryer do
92
142
 
93
143
  puts "Scryer: querying OSV.dev for known vulnerabilities (needs network)..."
94
144
  vulnerable = Scryer::DependencyAudit.vulnerable_gems(root)
145
+ ruby_eol = Scryer::DependencyAudit.ruby_eol_check(root)
146
+ credentials_exposure = Scryer::DependencyAudit.credentials_exposure_check(root)
95
147
 
96
- (insecure + vulnerable).each do |f|
97
- label = f.kind == "insecure_source" ? "[#{f.severity.upcase}] #{f.message}" : "[#{f.severity.upcase}] #{f.gem_name} #{f.installed_version} - #{f.advisory_id}: #{f.title}"
98
- puts label
148
+ (insecure + vulnerable + ruby_eol + credentials_exposure).each do |f|
149
+ puts "[#{f.severity.upcase}] #{ScryerTasks.dependency_label(f)}"
99
150
  puts " fix: #{f.suggested_fix}"
100
151
  end
101
152
 
102
- total = insecure.size + vulnerable.size
103
- puts "\nScryer: #{total} dependency finding(s) (#{insecure.size} insecure source, #{vulnerable.size} vulnerable gem)."
153
+ total = insecure.size + vulnerable.size + ruby_eol.size + credentials_exposure.size
154
+ puts "\nScryer: #{total} dependency finding(s) (#{insecure.size} insecure source, " \
155
+ "#{vulnerable.size} vulnerable gem, #{ruby_eol.size} Ruby EOL, " \
156
+ "#{credentials_exposure.size} credentials exposure)."
104
157
  abort("Scryer: dependency audit failed.") if total.positive?
105
158
  end
106
159
 
107
- VALID_FORMATS = %w[json html csv].freeze
108
- EXTENSION_FOR_FORMAT = { "json" => "json", "html" => "html", "csv" => "csv" }.freeze
160
+ VALID_FORMATS = %w[json html csv sarif].freeze
161
+ EXTENSION_FOR_FORMAT = { "json" => "json", "html" => "html", "csv" => "csv", "sarif" => "sarif" }.freeze
109
162
  NO_DEPS_TOKEN = "nodeps".freeze
110
163
 
111
164
  # tokens is every bracket arg Rake was given, e.g. %w[json doc/security.json] or
@@ -178,11 +231,47 @@ module ScryerTasks
178
231
  nil
179
232
  end
180
233
 
234
+ def dependency_label(f)
235
+ return f.message if f.kind == "insecure_source"
236
+ return "#{f.gem_name} #{f.installed_version} - #{f.advisory_id}: #{f.title}" if f.advisory_id
237
+
238
+ "#{f.gem_name} #{f.installed_version}: #{f.title}"
239
+ end
240
+
241
+ # Same logic as Scryer::CLI#apply_baseline (see that method's comment for
242
+ # why fixed_count has to be computed once against the combined set of
243
+ # every category's current fingerprints, not once per category summed).
244
+ def apply_baseline(path, result, dependency_findings)
245
+ baseline_fingerprints = Scryer::Baseline.load(path)
246
+
247
+ security_hashes = result.security_findings.map(&:to_h)
248
+ performance_hashes = result.performance_findings.map(&:to_h)
249
+ style_hashes = result.style_findings.map(&:to_h)
250
+ dependency_hashes = dependency_findings.map(&:to_h)
251
+
252
+ all_current_fingerprints = Scryer::Baseline.fingerprints(security_hashes + performance_hashes + style_hashes + dependency_hashes)
253
+ fixed_count = (baseline_fingerprints - all_current_fingerprints.to_set).size
254
+
255
+ result.security_findings = filter_new(result.security_findings, security_hashes, baseline_fingerprints)
256
+ result.performance_findings = filter_new(result.performance_findings, performance_hashes, baseline_fingerprints)
257
+ result.style_findings = filter_new(result.style_findings, style_hashes, baseline_fingerprints)
258
+ filtered_deps = filter_new(dependency_findings, dependency_hashes, baseline_fingerprints)
259
+
260
+ [filtered_deps, fixed_count]
261
+ rescue Scryer::Baseline::LoadError => e
262
+ abort("Scryer: #{e.message}")
263
+ end
264
+
265
+ def filter_new(objects, hashes, baseline_fingerprints)
266
+ fingerprints = Scryer::Baseline.fingerprints(hashes)
267
+ objects.each_with_index.reject { |_, i| baseline_fingerprints.include?(fingerprints[i]) }.map(&:first)
268
+ end
269
+
181
270
  # The "one audit command" summary — a single scan's worth of every
182
271
  # category Scryer covers (security, performance, duplicate/smelly code,
183
272
  # dependencies), the same categories usually split across RuboCop +
184
273
  # Brakeman + bundler-audit + Reek, side by side in one box.
185
- def print_summary(result:, dependency_findings:, ran_deps:, paths:)
274
+ def print_summary(result:, dependency_findings:, ran_deps:, paths:, renderer:, fixed_count: 0, baseline_path: nil)
186
275
  # "Code Quality" is the umbrella label for both duplicate-code groups
187
276
  # and rule-based style findings (e.g. frozen_string_literal) — two
188
277
  # different detectors, same broad concern, one row in the box.
@@ -198,14 +287,25 @@ module ScryerTasks
198
287
  ]
199
288
 
200
289
  divider = "─" * 32
290
+ score = renderer.security_score
201
291
  puts ""
202
292
  puts "Scryer Audit — #{result.files_scanned} files scanned"
203
293
  puts divider
204
294
  puts ""
295
+ if baseline_path
296
+ puts "Baseline: #{baseline_path} — showing new findings only (#{fixed_count} fixed since baseline)."
297
+ puts ""
298
+ end
299
+ clean_rate = renderer.rules_clean_rate
300
+ puts "Security Score: #{score["score"]}/100 (#{score["grade"]})"
301
+ puts "Checks: #{clean_rate["clean"]}/#{clean_rate["total"]} rules clean (#{clean_rate["percent"]}%)"
302
+ puts ""
205
303
  rows.each { |label, count| puts summary_row(label, count) }
206
304
  puts divider
207
305
  puts summary_row("Total", total)
208
306
  puts ""
307
+ print_top_priorities(renderer.top_risks)
308
+ print_owasp_coverage(renderer.owasp_coverage)
209
309
  paths.each { |format, path| puts "#{format.upcase} report: #{path}" }
210
310
  end
211
311
 
@@ -213,4 +313,29 @@ module ScryerTasks
213
313
  value = count.nil? ? "skipped (nodeps)" : "#{count} finding#{"s" unless count == 1}"
214
314
  "#{label.ljust(14)}#{value.rjust(20)}"
215
315
  end
316
+
317
+ # The categories above are counted separately, but nothing else ranks
318
+ # across them — this is what actually backs "tells you what to fix
319
+ # first" rather than just splitting findings into four buckets. Same
320
+ # data ReportRenderer#top_risks already sorts for the HTML report.
321
+ def print_top_priorities(risks)
322
+ return if risks.empty?
323
+
324
+ puts "Top priorities:"
325
+ risks.each_with_index do |r, i|
326
+ puts " #{i + 1}. [#{r[:severity]}] #{r[:category]} — #{r[:label]} (#{r[:location]})"
327
+ end
328
+ puts ""
329
+ end
330
+
331
+ # Byproduct of every security rule carrying an owasp_category — see
332
+ # ReportRenderer#owasp_coverage. Scryer's own best-effort tagging, not an
333
+ # OWASP-audited mapping (documented in full in the README).
334
+ def print_owasp_coverage(coverage)
335
+ return if coverage.empty?
336
+
337
+ puts "OWASP Top 10 (2021) coverage:"
338
+ coverage.each { |category, count| puts " #{category}: #{count} finding#{"s" unless count == 1}" }
339
+ puts ""
340
+ end
216
341
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scryer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ram Laxman Yadav
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-08-11 00:00:00.000000000 Z
11
+ date: 2026-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -25,18 +25,21 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '13.0'
27
27
  description: |
28
- One scan, one report: security, performance, duplicate-code, and dependency audits for Ruby
29
- and Rails — the ground Brakeman, bundler-audit, Reek, and custom glue scripts usually split
30
- between them, covered by a single `scryer` command. (Style/lint is RuboCop's job — Scryer
31
- doesn't touch that.)
28
+ Scryer scans a Ruby/Rails codebase for security vulnerabilities, performance problems,
29
+ dependency risk, and code-quality issues, then ranks everything it finds by severity across
30
+ all of those categories — so a scan ends with one answer to "what's most worth fixing," not
31
+ four separate reports to reconcile by hand. (Style/lint is RuboCop's job — Scryer doesn't
32
+ touch that, except one narrow check.)
32
33
 
33
- Detects SQL injection, mass assignment, hardcoded secrets, XSS, weak crypto, and more; N+1
34
- queries, missing pagination, and other performance heuristics; near-duplicate code; and
35
- known-vulnerable gems via a live OSV.dev dependency audit — on by default, every run.
34
+ Detects SQL injection, mass assignment, SSRF, path traversal, IDOR, insecure JWT/CORS/session
35
+ config, hardcoded secrets, XSS, weak crypto, and more; N+1 queries, missing pagination, and
36
+ other performance heuristics; near-duplicate code; and known-vulnerable gems via a live
37
+ OSV.dev dependency audit — on by default, every run.
36
38
 
37
39
  Every finding includes a human-reviewable suggested fix — never auto-applied, optionally
38
40
  rewritten against your actual code by any LLM you configure. Reports in JSON, self-contained
39
- HTML, or CSV. Zero runtime dependencies beyond Ruby's own stdlib.
41
+ HTML, CSV, or SARIF (for GitHub Code Scanning). Zero runtime dependencies beyond Ruby's own
42
+ stdlib.
40
43
  email:
41
44
  executables:
42
45
  - scryer
@@ -54,12 +57,16 @@ files:
54
57
  - lib/scryer/ai_client.rb
55
58
  - lib/scryer/ai_fix_suggester.rb
56
59
  - lib/scryer/ast.rb
60
+ - lib/scryer/authorization_watcher.rb
61
+ - lib/scryer/baseline.rb
57
62
  - lib/scryer/cache_extractor.rb
58
63
  - lib/scryer/cli.rb
59
64
  - lib/scryer/dependency_audit.rb
60
65
  - lib/scryer/duplicate_detector.rb
61
66
  - lib/scryer/finding.rb
67
+ - lib/scryer/fix_verifier.rb
62
68
  - lib/scryer/method_extractor.rb
69
+ - lib/scryer/minitest.rb
63
70
  - lib/scryer/performance_rules/inefficient_save_loop_rule.rb
64
71
  - lib/scryer/performance_rules/missing_pagination_rule.rb
65
72
  - lib/scryer/performance_rules/n_plus_one_query_rule.rb
@@ -68,16 +75,39 @@ files:
68
75
  - lib/scryer/query_watcher.rb
69
76
  - lib/scryer/railtie.rb
70
77
  - lib/scryer/report_renderer.rb
78
+ - lib/scryer/rspec.rb
71
79
  - lib/scryer/rule.rb
72
80
  - lib/scryer/rule_set.rb
81
+ - lib/scryer/rules/action_cable_forgery_protection_rule.rb
82
+ - lib/scryer/rules/active_storage_inline_disposition_rule.rb
83
+ - lib/scryer/rules/active_storage_missing_content_type_validation_rule.rb
84
+ - lib/scryer/rules/authentication_bypass_rule.rb
73
85
  - lib/scryer/rules/command_injection_rule.rb
86
+ - lib/scryer/rules/consider_all_requests_local_rule.rb
87
+ - lib/scryer/rules/cors_misconfiguration_rule.rb
74
88
  - lib/scryer/rules/csrf_protection_rule.rb
89
+ - lib/scryer/rules/force_ssl_rule.rb
90
+ - lib/scryer/rules/graphql_missing_query_limits_rule.rb
91
+ - lib/scryer/rules/hardcoded_basic_auth_rule.rb
92
+ - lib/scryer/rules/hardcoded_secret_key_base_rule.rb
75
93
  - lib/scryer/rules/hardcoded_secret_rule.rb
94
+ - lib/scryer/rules/host_authorization_disabled_rule.rb
95
+ - lib/scryer/rules/idor_rule.rb
96
+ - lib/scryer/rules/insecure_cookie_serializer_rule.rb
97
+ - lib/scryer/rules/job_raw_params_rule.rb
98
+ - lib/scryer/rules/jwt_insecure_rule.rb
76
99
  - lib/scryer/rules/mass_assignment_rule.rb
100
+ - lib/scryer/rules/missing_authorization_rule.rb
101
+ - lib/scryer/rules/missing_policy_scope_rule.rb
77
102
  - lib/scryer/rules/open_redirect_rule.rb
103
+ - lib/scryer/rules/path_traversal_rule.rb
104
+ - lib/scryer/rules/security_headers_rule.rb
78
105
  - lib/scryer/rules/sql_injection_rule.rb
106
+ - lib/scryer/rules/ssrf_rule.rb
79
107
  - lib/scryer/rules/unsafe_deserialization_rule.rb
108
+ - lib/scryer/rules/verbose_production_log_level_rule.rb
80
109
  - lib/scryer/rules/weak_crypto_rule.rb
110
+ - lib/scryer/rules/weak_session_cookie_rule.rb
81
111
  - lib/scryer/rules/xss_unsafe_html_rule.rb
82
112
  - lib/scryer/scanner.rb
83
113
  - lib/scryer/style_rules/frozen_string_literal_rule.rb
@@ -110,6 +140,5 @@ requirements: []
110
140
  rubygems_version: 3.5.11
111
141
  signing_key:
112
142
  specification_version: 4
113
- summary: Ruby/Rails security & quality auditor — one command instead of Brakeman +
114
- bundler-audit + Reek.
143
+ summary: Ruby/Rails application security & risk auditor — tells you what to fix first.
115
144
  test_files: []