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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +322 -0
- data/README.md +546 -43
- data/lib/generators/scryer/USAGE +10 -2
- data/lib/generators/scryer/templates/scryer_initializer.rb +15 -0
- data/lib/scryer/ai_fix_suggester.rb +29 -9
- data/lib/scryer/ast.rb +95 -6
- data/lib/scryer/authorization_watcher.rb +156 -0
- data/lib/scryer/baseline.rb +75 -0
- data/lib/scryer/cli.rb +209 -11
- data/lib/scryer/dependency_audit.rb +108 -5
- data/lib/scryer/finding.rb +6 -0
- data/lib/scryer/fix_verifier.rb +82 -0
- data/lib/scryer/minitest.rb +48 -0
- data/lib/scryer/performance_rules/inefficient_save_loop_rule.rb +32 -0
- data/lib/scryer/performance_rules/missing_pagination_rule.rb +1 -0
- data/lib/scryer/performance_rules/n_plus_one_query_rule.rb +1 -0
- data/lib/scryer/performance_rules/unbounded_table_scan_rule.rb +1 -0
- data/lib/scryer/report_renderer.rb +637 -44
- data/lib/scryer/rspec.rb +55 -0
- data/lib/scryer/rule.rb +22 -2
- data/lib/scryer/rules/action_cable_forgery_protection_rule.rb +50 -0
- data/lib/scryer/rules/active_storage_inline_disposition_rule.rb +50 -0
- data/lib/scryer/rules/active_storage_missing_content_type_validation_rule.rb +79 -0
- data/lib/scryer/rules/authentication_bypass_rule.rb +95 -0
- data/lib/scryer/rules/command_injection_rule.rb +3 -0
- data/lib/scryer/rules/consider_all_requests_local_rule.rb +51 -0
- data/lib/scryer/rules/cors_misconfiguration_rule.rb +100 -0
- data/lib/scryer/rules/csrf_protection_rule.rb +60 -11
- data/lib/scryer/rules/force_ssl_rule.rb +48 -0
- data/lib/scryer/rules/graphql_missing_query_limits_rule.rb +106 -0
- data/lib/scryer/rules/hardcoded_basic_auth_rule.rb +51 -0
- data/lib/scryer/rules/hardcoded_secret_key_base_rule.rb +58 -0
- data/lib/scryer/rules/hardcoded_secret_rule.rb +3 -0
- data/lib/scryer/rules/host_authorization_disabled_rule.rb +50 -0
- data/lib/scryer/rules/idor_rule.rb +165 -0
- data/lib/scryer/rules/insecure_cookie_serializer_rule.rb +47 -0
- data/lib/scryer/rules/job_raw_params_rule.rb +131 -0
- data/lib/scryer/rules/jwt_insecure_rule.rb +123 -0
- data/lib/scryer/rules/mass_assignment_rule.rb +34 -10
- data/lib/scryer/rules/missing_authorization_rule.rb +103 -0
- data/lib/scryer/rules/missing_policy_scope_rule.rb +134 -0
- data/lib/scryer/rules/open_redirect_rule.rb +3 -0
- data/lib/scryer/rules/path_traversal_rule.rb +110 -0
- data/lib/scryer/rules/security_headers_rule.rb +133 -0
- data/lib/scryer/rules/sql_injection_rule.rb +3 -0
- data/lib/scryer/rules/ssrf_rule.rb +139 -0
- data/lib/scryer/rules/unsafe_deserialization_rule.rb +3 -0
- data/lib/scryer/rules/verbose_production_log_level_rule.rb +53 -0
- data/lib/scryer/rules/weak_crypto_rule.rb +37 -2
- data/lib/scryer/rules/weak_session_cookie_rule.rb +51 -0
- data/lib/scryer/rules/xss_unsafe_html_rule.rb +41 -0
- data/lib/scryer/style_rules/frozen_string_literal_rule.rb +1 -0
- data/lib/scryer/version.rb +1 -1
- data/lib/scryer.rb +15 -0
- data/lib/tasks/scryer.rake +138 -13
- metadata +41 -12
|
@@ -14,6 +14,7 @@ module Scryer
|
|
|
14
14
|
self.category = "performance"
|
|
15
15
|
self.default_severity = "warning"
|
|
16
16
|
self.title = "Per-record save/update inside a loop"
|
|
17
|
+
self.confidence = "medium"
|
|
17
18
|
|
|
18
19
|
LOOP_METHODS = %w[each each_with_index].freeze
|
|
19
20
|
# Genuinely argless in normal use — a bare `:call` node, never wrapped
|
|
@@ -24,7 +25,25 @@ module Scryer
|
|
|
24
25
|
# avoids double-counting the inner call node these wrap.
|
|
25
26
|
ARG_METHODS = %w[update update! update_attribute update_attributes].freeze
|
|
26
27
|
|
|
28
|
+
# `db/seeds.rb` (and the multi-file `db/seeds/*.rb` convention Rails
|
|
29
|
+
# supports via `Rails.application.load_seed`) is a one-time, manually
|
|
30
|
+
# run setup script, not a request-handling hot path — the whole
|
|
31
|
+
# premise of this rule (per-record round-trips scale badly as load
|
|
32
|
+
# grows) doesn't apply to a script a developer runs once at setup
|
|
33
|
+
# time for a small, fixed reference dataset (roles, plans, countries,
|
|
34
|
+
# ...). Flagging `roles.each { |r| r.save! }` there is just noise, so
|
|
35
|
+
# this rule skips findings whose file is exactly this path or under
|
|
36
|
+
# this directory. Deliberately file-path-based rather than trying to
|
|
37
|
+
# infer "small dataset" from the AST (which isn't reliably knowable —
|
|
38
|
+
# a seed file can still iterate a CSV of arbitrary size), and narrow
|
|
39
|
+
# to this one well-known Rails convention rather than any file that
|
|
40
|
+
# merely "looks like a script" elsewhere in the app.
|
|
41
|
+
SEED_FILE = "db/seeds.rb"
|
|
42
|
+
SEED_DIR_PREFIX = "db/seeds/"
|
|
43
|
+
|
|
27
44
|
def scan
|
|
45
|
+
return [] if seed_file?
|
|
46
|
+
|
|
28
47
|
findings = []
|
|
29
48
|
|
|
30
49
|
Ast.each_node(sexp) do |node|
|
|
@@ -58,6 +77,19 @@ module Scryer
|
|
|
58
77
|
|
|
59
78
|
private
|
|
60
79
|
|
|
80
|
+
# `Scanner` builds `file` by globbing `File.join(root, dir, "**", "*.rb")`
|
|
81
|
+
# with `dir` defaulting to `"."`, so the relative path it hands rules is
|
|
82
|
+
# actually `"./db/seeds.rb"`, not `"db/seeds.rb"` — a plain `==`/
|
|
83
|
+
# `start_with?` against the un-prefixed string silently never matches
|
|
84
|
+
# against a real scan (verified: without stripping the prefix here,
|
|
85
|
+
# `db/seeds.rb` still fired in a real `Scryer::Scanner.new(root:,
|
|
86
|
+
# dirs: ["."]).call` run). Strip a leading `./` before comparing so
|
|
87
|
+
# this matches how the path actually arrives, not an assumed form.
|
|
88
|
+
def seed_file?
|
|
89
|
+
normalized = file.to_s.delete_prefix("./")
|
|
90
|
+
normalized == SEED_FILE || normalized.start_with?(SEED_DIR_PREFIX)
|
|
91
|
+
end
|
|
92
|
+
|
|
61
93
|
def block_param_name(block_node)
|
|
62
94
|
return nil unless Ast.tagged?(block_node, :do_block, :brace_block)
|
|
63
95
|
|
|
@@ -12,6 +12,7 @@ module Scryer
|
|
|
12
12
|
self.category = "performance"
|
|
13
13
|
self.default_severity = "warning"
|
|
14
14
|
self.title = "Possible unbounded result set on an index action"
|
|
15
|
+
self.confidence = "medium"
|
|
15
16
|
|
|
16
17
|
QUERY_METHODS = %w[all where].freeze
|
|
17
18
|
BOUND_METHODS = %w[limit page per paginate find_each find_in_batches first take].freeze
|
|
@@ -21,6 +21,7 @@ module Scryer
|
|
|
21
21
|
self.category = "performance"
|
|
22
22
|
self.default_severity = "warning"
|
|
23
23
|
self.title = "Possible N+1 query inside a loop"
|
|
24
|
+
self.confidence = "medium"
|
|
24
25
|
|
|
25
26
|
QUERY_METHODS = %w[where all find find_by find_by! order limit].freeze
|
|
26
27
|
LOOP_METHODS = %w[each map collect each_with_index].freeze
|