@sun-asterisk/sunlint 1.3.45 → 1.3.47
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.
- package/core/adapters/sunlint-rule-adapter.js +16 -0
- package/core/cli-action-handler.js +3 -0
- package/core/cli-program.js +7 -0
- package/core/rule-selection-service.js +96 -3
- package/engines/heuristic-engine.js +6 -1
- package/package.json +2 -2
- package/skill-assets/sunlint-code-quality/rules/ruby/C006-verb-noun-functions.md +63 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/C013-no-dead-code.md +48 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/C014-dependency-injection.md +42 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/C017-no-constructor-logic.md +42 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/C018-generic-errors.md +41 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/C019-error-log-level.md +41 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/C020-no-unused-imports.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/C022-no-unused-variables.md +31 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/C023-no-duplicate-names.md +39 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/C024-centralize-constants.md +35 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/C029-catch-log-root-cause.md +34 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/C030-custom-error-classes.md +32 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/C033-separate-data-access.md +52 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/C035-error-context-logging.md +34 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/C041-no-hardcoded-secrets.md +29 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/C042-boolean-naming.md +38 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/C052-controller-parsing.md +37 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/C060-superclass-logic.md +38 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/C067-no-hardcoded-config.md +37 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S003-open-redirect.md +58 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S004-no-log-credentials.md +38 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S005-server-authorization.md +37 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S006-default-credentials.md +29 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S007-output-encoding.md +31 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S009-approved-crypto.md +31 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S010-csprng.md +30 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S011-encrypted-client-hello.md +27 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S012-secrets-management.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S013-tls-connections.md +30 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S016-no-sensitive-query-string.md +37 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S017-parameterized-queries.md +33 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S019-email-input-sanitization.md +31 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S020-eval-code-execution.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S022-context-escaping.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S023-dynamic-js-encoding.md +33 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S025-server-validation.md +30 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S026-tls-encryption.md +30 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S027-mtls-validation.md +26 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S028-upload-limits.md +33 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S029-csrf-protection.md +32 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S030-directory-browsing.md +30 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S031-secure-cookie-flag.md +27 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S032-httponly-cookie.md +26 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S033-samesite-cookie.md +29 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S034-host-prefix-cookie.md +30 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S035-app-hostnames.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S036-internal-file-paths.md +37 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S037-anti-cache-headers.md +31 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S039-tls-certificate-validation.md +29 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S041-logout-invalidation.md +31 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S042-long-lived-sessions.md +27 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S044-critical-changes-reauth.md +34 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S045-brute-force-protection.md +33 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S047-oauth-csrf-protection.md +33 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S048-oauth-redirect-validation.md +29 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S049-auth-code-expiry.md +31 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S050-token-entropy.md +26 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S051-password-length.md +38 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S052-otp-entropy.md +25 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S053-generic-error-messages.md +33 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S054-no-default-admin.md +29 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S055-content-type-validation.md +24 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S056-log-injection.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S057-synchronized-time.md +18 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/S058-ssrf-protection.md +39 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Catch and Log Root Cause
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: facilitates faster debugging and resolution
|
|
5
|
+
tags: error-handling, maintenance, quality
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Catch and Log Root Cause
|
|
9
|
+
|
|
10
|
+
When catching an error, ensure you log the original error message and backtrace to facilitate debugging.
|
|
11
|
+
|
|
12
|
+
**Incorrect (losing context):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
begin
|
|
16
|
+
process_data
|
|
17
|
+
rescue => e
|
|
18
|
+
Rails.logger.error "Something went wrong" # No root cause logged
|
|
19
|
+
end
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Correct (logging root cause):**
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
begin
|
|
26
|
+
process_data
|
|
27
|
+
rescue => e
|
|
28
|
+
Rails.logger.error "Data processing failed: #{e.message}"
|
|
29
|
+
Rails.logger.error e.backtrace.join("\n") # Log backtrace for context
|
|
30
|
+
end
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Tools:** Manual Review, Sentry/Honeybadger
|
|
34
|
+
---
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Custom Error Classes
|
|
3
|
+
impact: LOW
|
|
4
|
+
impactDescription: allows specific rescue blocks and better error hierarchy
|
|
5
|
+
tags: error-handling, architecture, quality
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Custom Error Classes
|
|
9
|
+
|
|
10
|
+
Define specific error classes for your application domain rather than relying on generic ones.
|
|
11
|
+
|
|
12
|
+
**Incorrect (generic errors):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
def process(amount)
|
|
16
|
+
raise "Invalid amount" if amount <= 0 # Generic RuntimeError
|
|
17
|
+
end
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Correct (custom errors):**
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
class PaymentError < StandardError; end
|
|
24
|
+
class InvalidAmountError < PaymentError; end
|
|
25
|
+
|
|
26
|
+
def process(amount)
|
|
27
|
+
raise InvalidAmountError, "Amount must be positive" if amount <= 0
|
|
28
|
+
end
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Tools:** PR review
|
|
32
|
+
---
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Separate Processing and Data Access
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: enables testable business logic and better code organization
|
|
5
|
+
tags: separation-of-concerns, repository, service, architecture, quality
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Separate Processing and Data Access
|
|
9
|
+
|
|
10
|
+
Keep business logic separate from direct database access. Use Service Objects to handle logic and ActiveRecord models primarily for data access and persistence.
|
|
11
|
+
|
|
12
|
+
**Incorrect (mixed concerns):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
class OrderController < ApplicationController
|
|
16
|
+
def create
|
|
17
|
+
# Business logic in controller
|
|
18
|
+
if params[:amount] > 100
|
|
19
|
+
discount = params[:amount] * 0.1
|
|
20
|
+
total = params[:amount] - discount
|
|
21
|
+
end
|
|
22
|
+
Order.create(amount: total, user_id: current_user.id)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Correct (separated concerns):**
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
# app/services/create_order_service.rb
|
|
31
|
+
class CreateOrderService
|
|
32
|
+
def initialize(user, params)
|
|
33
|
+
@user = user
|
|
34
|
+
@params = params
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def call
|
|
38
|
+
total = calculate_total(@params[:amount])
|
|
39
|
+
Order.create!(amount: total, user: @user)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def calculate_total(amount)
|
|
45
|
+
return amount if amount <= 100
|
|
46
|
+
amount * 0.9
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Tools:** Manual Review
|
|
52
|
+
---
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Include Context in Error Logging
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: provides necessary details for triaging issues
|
|
5
|
+
tags: logging, error-handling, maintenance, quality
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Include Context in Error Logging
|
|
9
|
+
|
|
10
|
+
When an error occurs, include relevant contextual data (IDs, parameters, state) in the log message.
|
|
11
|
+
|
|
12
|
+
**Incorrect (missing context):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
begin
|
|
16
|
+
update_profile(params)
|
|
17
|
+
rescue => e
|
|
18
|
+
Rails.logger.error "Profile update failed: #{e.message}"
|
|
19
|
+
end
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Correct (with context):**
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
begin
|
|
26
|
+
update_profile(params)
|
|
27
|
+
rescue => e
|
|
28
|
+
# Log which record failed and with what data
|
|
29
|
+
Rails.logger.error "Profile update failed for User ID: #{current_user.id}. Params: #{params.inspect}. Error: #{e.message}"
|
|
30
|
+
end
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Tools:** Manual Review
|
|
34
|
+
---
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: No Hardcoded Secrets
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: prevents sensitive data exposure and potential security breaches
|
|
5
|
+
tags: security, secrets, vulnerability, quality
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## No Hardcoded Secrets
|
|
9
|
+
|
|
10
|
+
Never hardcode passwords, API keys, or tokens in your source code. Use environment variables or encrypted credential files.
|
|
11
|
+
|
|
12
|
+
**Incorrect (hardcoded secret):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
client = Stripe::Client.new(api_key: "sk_test_51Mz...")
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Correct (config/credentials):**
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
# Using Rails credentials
|
|
22
|
+
client = Stripe::Client.new(api_key: Rails.application.credentials.stripe_api_key)
|
|
23
|
+
|
|
24
|
+
# Or using ENV
|
|
25
|
+
client = Stripe::Client.new(api_key: ENV['STRIPE_API_KEY'])
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Tools:** git-secrets, Brakeman, RuboCop
|
|
29
|
+
---
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Boolean Naming Conventions
|
|
3
|
+
impact: LOW
|
|
4
|
+
impactDescription: improves readability and makes boolean logic intuitive
|
|
5
|
+
tags: naming, boolean, readability, conventions, quality
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Boolean Naming Conventions
|
|
9
|
+
|
|
10
|
+
In Ruby, boolean-returning methods (predicates) should always end with a question mark (`?`). Avoid prefixes like `is_` or `has_` except when it makes sense as part of the method name before the `?`.
|
|
11
|
+
|
|
12
|
+
**Incorrect (vague or non-idiomatic):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
def valid
|
|
16
|
+
# returns true/false
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def is_admin
|
|
20
|
+
end
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Correct (Ruby idiomatic predicate):**
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
# Standard Ruby Style
|
|
27
|
+
def valid?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def admin?
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def has_permission?
|
|
34
|
+
end
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Tools:** RuboCop (`Naming/PredicateName`)
|
|
38
|
+
---
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Strong Parameters for Controller Input
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: prevents Mass Assignment vulnerabilities
|
|
5
|
+
tags: rails, security, mass-assignment, vulnerability, quality
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Strong Parameters for Controller Input
|
|
9
|
+
|
|
10
|
+
Explicitly define which parameters are allowed for mass assignment in Rails controllers to prevent malicious users from updating unintended fields.
|
|
11
|
+
|
|
12
|
+
**Incorrect (unsafe params):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
def update
|
|
16
|
+
@user = User.find(params[:id])
|
|
17
|
+
@user.update(params[:user]) # Mass alignment vulnerability
|
|
18
|
+
end
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Correct (strong parameters):**
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
def update
|
|
25
|
+
@user = User.find(params[:id])
|
|
26
|
+
@user.update(user_params)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def user_params
|
|
32
|
+
params.require(:user).permit(:first_name, :last_name, :email)
|
|
33
|
+
end
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Tools:** Brakeman, Rails default
|
|
37
|
+
---
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Composition and Concerns over Deep Inheritance
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: avoids fragile base classes and promotes reusable logic
|
|
5
|
+
tags: architecture, inheritance, composition, rails-concerns, quality
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Composition and Concerns over Deep Inheritance
|
|
9
|
+
|
|
10
|
+
Avoid deep class hierarchies. In Rails, use `ActiveSupport::Concern` to extract shared behavior into modules that can be included in multiple classes.
|
|
11
|
+
|
|
12
|
+
**Incorrect (deep inheritance):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
class BaseUser < ActiveRecord::Base; end
|
|
16
|
+
class StaffUser < BaseUser; end
|
|
17
|
+
class AdminUser < StaffUser; end # Too deep, fragile
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Correct (using concerns):**
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
# app/models/concerns/authenticatable.rb
|
|
24
|
+
module Authenticatable
|
|
25
|
+
extend ActiveSupport::Concern
|
|
26
|
+
|
|
27
|
+
included do
|
|
28
|
+
validates :email, presence: true
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class User < ApplicationRecord
|
|
33
|
+
include Authenticatable
|
|
34
|
+
end
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Tools:** RuboCop
|
|
38
|
+
---
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: No Hardcoded Configuration
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: enables environment-specific configurations without code changes
|
|
5
|
+
tags: configuration, environment, quality
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## No Hardcoded Configuration
|
|
9
|
+
|
|
10
|
+
Avoid hardcoding settings like service endpoints, timeouts, or business thresholds in the code. Use configuration files or environment variables.
|
|
11
|
+
|
|
12
|
+
**Incorrect (hardcoded values):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
class ExternalApi
|
|
16
|
+
def endpoint
|
|
17
|
+
"https://api.production.com/v1" # Hardcoded URL
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Correct (configured values):**
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
# config/settings.yml
|
|
26
|
+
# production:
|
|
27
|
+
# api_endpoint: "https://api.production.com/v1"
|
|
28
|
+
|
|
29
|
+
class ExternalApi
|
|
30
|
+
def endpoint
|
|
31
|
+
Rails.configuration.api_endpoint
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Tools:** Manual Review
|
|
37
|
+
---
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: URL Redirects Must Be In Allow List
|
|
3
|
+
impact: LOW
|
|
4
|
+
impactDescription: prevents open redirect vulnerabilities
|
|
5
|
+
tags: redirect, url, allow-list, validation, security
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## URL Redirects Must Be In Allow List
|
|
9
|
+
|
|
10
|
+
Open redirect vulnerabilities allows attackers to redirect users to malicious sites, often used in phishing attacks. In Rails, always validate external redirects.
|
|
11
|
+
|
|
12
|
+
**Incorrect (unvalidated redirect URL):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
# Open redirect vulnerability
|
|
16
|
+
def redirect_to_url
|
|
17
|
+
url = params[:url]
|
|
18
|
+
redirect_to url # Attacker: ?url=https://evil.com
|
|
19
|
+
end
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Correct (allow list or relative path):**
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
ALLOWED_HOSTS = ['example.com', 'app.example.com']
|
|
26
|
+
|
|
27
|
+
def redirect_to_url
|
|
28
|
+
url = params[:url]
|
|
29
|
+
uri = URI.parse(url)
|
|
30
|
+
|
|
31
|
+
if ALLOWED_HOSTS.include?(uri.host)
|
|
32
|
+
redirect_to url, allow_other_host: true
|
|
33
|
+
else
|
|
34
|
+
redirect_to root_path, alert: "Invalid redirect"
|
|
35
|
+
end
|
|
36
|
+
rescue URI::InvalidURIError
|
|
37
|
+
redirect_to root_path
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Or force relative path
|
|
41
|
+
def safe_redirect
|
|
42
|
+
path = params[:path]
|
|
43
|
+
# Ensure it starts with / and not //
|
|
44
|
+
if path.start_with?('/') && !path.start_with?('//')
|
|
45
|
+
redirect_to path
|
|
46
|
+
else
|
|
47
|
+
redirect_to root_path
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Protection strategies:**
|
|
53
|
+
1. Allow list of trusted domains via `allow_other_host: true` after validation.
|
|
54
|
+
2. Use relative URLs only.
|
|
55
|
+
3. Validate URIs with `URI.parse`.
|
|
56
|
+
|
|
57
|
+
**Tools:** Brakeman, Manual Review
|
|
58
|
+
---
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: No Sensitive Information in Logs
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: prevents accidental exposure of credentials in logs
|
|
5
|
+
tags: security, logging, credentials, vulnerability
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## No Sensitive Information in Logs
|
|
9
|
+
|
|
10
|
+
Ensure sensitive data like passwords, credit card numbers, and tokens are filtered from application logs.
|
|
11
|
+
|
|
12
|
+
**Incorrect (logging sensitive data):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
# In a controller
|
|
16
|
+
def create
|
|
17
|
+
Rails.logger.info "Creating user with params: #{params.inspect}"
|
|
18
|
+
# Log output: ... "password"=>"secret123", "credit_card"=>"1234..."
|
|
19
|
+
end
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Correct (parameter filtering):**
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
# config/initializers/filter_parameter_logging.rb
|
|
26
|
+
Rails.application.config.filter_parameters += [
|
|
27
|
+
:password, :password_confirmation, :token, :credit_card, :ssn
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
# In controller
|
|
31
|
+
def create
|
|
32
|
+
# Parameters will be automatically filtered in logs
|
|
33
|
+
# Log output: ... "password"=>"[FILTERED]"
|
|
34
|
+
end
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Tools:** Brakeman, Rails default
|
|
38
|
+
---
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Ensure Proper Server-Side Authorization
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: prevents unauthorized access to resources and data
|
|
5
|
+
tags: security, authorization, access-control, quality
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Ensure Proper Server-Side Authorization
|
|
9
|
+
|
|
10
|
+
Always verify that the current user has permission to perform the requested action on the specific resource. Do not rely solely on authentication.
|
|
11
|
+
|
|
12
|
+
**Incorrect (missing authorization):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
def show
|
|
16
|
+
@post = Post.find(params[:id])
|
|
17
|
+
# No check if @post belongs to current_user
|
|
18
|
+
end
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Correct (using Pundit or CanCanCan):**
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
# Using Pundit
|
|
25
|
+
def show
|
|
26
|
+
@post = Post.find(params[:id])
|
|
27
|
+
authorize @post # Checks PostPolicy#show?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Using scopes
|
|
31
|
+
def index
|
|
32
|
+
@posts = policy_scope(Post) # Only returns posts user can see
|
|
33
|
+
end
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Tools:** Brakeman, Pundit, CanCanCan
|
|
37
|
+
---
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Avoid Default Credentials
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: prevents easy access for attackers through well-known defaults
|
|
5
|
+
tags: security, credentials, authentication, hardcoded
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Avoid Default Credentials
|
|
9
|
+
|
|
10
|
+
Never use default passwords or hardcoded administrative credentials in production. Ensure every environment has unique, strong credentials.
|
|
11
|
+
|
|
12
|
+
**Incorrect (default values in code):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
ADMIN_PASSWORD = "admin" # Default password
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Correct (randomized or environment-specific):**
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
# Use Rails credentials
|
|
22
|
+
ADMIN_PASSWORD = Rails.application.credentials.admin_password
|
|
23
|
+
|
|
24
|
+
# Or use ENV with a fail-safe check
|
|
25
|
+
raise "Set ADMIN_PASSWORD" if Rails.env.production? && ENV['ADMIN_PASSWORD'].blank?
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Tools:** Manual Review, Security audits
|
|
29
|
+
---
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Safe Output Encoding
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: prevents Cross-Site Scripting (XSS) attacks
|
|
5
|
+
tags: security, xss, encoding, rails-erb
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Safe Output Encoding
|
|
9
|
+
|
|
10
|
+
Rails ERB automatically escapes output by default. Avoid using `.html_safe` or `raw` on user-supplied data as it bypasses this protection.
|
|
11
|
+
|
|
12
|
+
**Incorrect (bypassing auto-escaping):**
|
|
13
|
+
|
|
14
|
+
```erb
|
|
15
|
+
<%# XSS vulnerability if params[:name] contains <script> %>
|
|
16
|
+
<%= raw "Hello, #{params[:name]}" %>
|
|
17
|
+
<%= "Your bio: #{user.bio}".html_safe %>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Correct (relying on auto-escaping):**
|
|
21
|
+
|
|
22
|
+
```erb
|
|
23
|
+
<%# Safe by default %>
|
|
24
|
+
<%= "Hello, #{params[:name]}" %>
|
|
25
|
+
|
|
26
|
+
<%# If you must use HTML, sanitize it %>
|
|
27
|
+
<%= sanitize(user.bio) %>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Tools:** Brakeman, Rails default
|
|
31
|
+
---
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Approved Cryptographic Algorithms
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: ensures data is protected by strong, vetted encryption
|
|
5
|
+
tags: security, cryptography, encryption, standards
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Approved Cryptographic Algorithms
|
|
9
|
+
|
|
10
|
+
Avoid custom or weak cryptographic algorithms (like MD5, SHA1 for passwords). Use industry standards like AES-256 for encryption and BCrypt for password hashing.
|
|
11
|
+
|
|
12
|
+
**Incorrect (weak or custom crypto):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
# MD5 is insecure for password hashing
|
|
16
|
+
digest = Digest::MD5.hexdigest(password)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Correct (industry standards):**
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
# For passwords
|
|
23
|
+
password_digest = BCrypt::Password.create(password)
|
|
24
|
+
|
|
25
|
+
# For symmetric encryption (Rails MessageEncryptor uses AES-256-GCM by default)
|
|
26
|
+
crypt = ActiveSupport::MessageEncryptor.new(key)
|
|
27
|
+
encrypted_data = crypt.encrypt_and_sign(data)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Tools:** Brakeman, Manual Review
|
|
31
|
+
---
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Cryptographically Secure Pseudo-Random Number Generators (CSPRNG)
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: prevents predictable random values that can be exploited
|
|
5
|
+
tags: security, cryptography, random
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use CSPRNG
|
|
9
|
+
|
|
10
|
+
For security-sensitive random values (tokens, salts, temporary passwords), use `SecureRandom` instead of the basic `rand` method.
|
|
11
|
+
|
|
12
|
+
**Incorrect (predictable random):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
# rand is not cryptographically secure
|
|
16
|
+
temp_token = rand(100000..999999).to_s
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Correct (SecureRandom):**
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
require 'securerandom'
|
|
23
|
+
|
|
24
|
+
# Use hex, base64 or uuid for unique tokens
|
|
25
|
+
temp_token = SecureRandom.hex(16)
|
|
26
|
+
session_token = SecureRandom.uuid
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Tools:** Brakeman, Manual Review
|
|
30
|
+
---
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Encrypted Connection Parameters
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: ensures sensitive connection details are not exposed in transport or transit
|
|
5
|
+
tags: security, encryption, connections
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Encrypted Connection Parameters
|
|
9
|
+
|
|
10
|
+
Ensure connection strings and parameters for external services are encrypted or managed via secure credential stores.
|
|
11
|
+
|
|
12
|
+
**Incorrect (plaintext secrets in strings):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
DATABASE_URL = "postgres://user:mypassword@db.example.com/mydb"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Correct (using credentials):**
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
# In database.yml
|
|
22
|
+
# production:
|
|
23
|
+
# url: <%= Rails.application.credentials.db_url %>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Tools:** Manual Review
|
|
27
|
+
---
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Secure Secrets Management
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: prevents secrets from being leaked through source control or compromised environments
|
|
5
|
+
tags: security, secrets, infrastructure
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Secure Secrets Management
|
|
9
|
+
|
|
10
|
+
Use specialized secrets management tools (HCP Vault, AWS Secrets Manager, Rails Credentials) instead of plaintext environment variables or local files.
|
|
11
|
+
|
|
12
|
+
**Incorrect (plaintext file or hardcoded):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
# .env file (often accidentally committed)
|
|
16
|
+
API_KEY=12345
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Correct (Rails Credentials):**
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
# EDITOR=vim rails credentials:edit
|
|
23
|
+
# This creates/edits config/credentials.yml.enc (encrypted)
|
|
24
|
+
api_key = Rails.application.credentials.api_key
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Tools:** Brakeman, git-secrets
|
|
28
|
+
---
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use TLS for All Connections
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: protects data in transit from eavesdropping and interception
|
|
5
|
+
tags: security, tls, ssl, transport
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use TLS for All Connections
|
|
9
|
+
|
|
10
|
+
Always use HTTPS/TLS for all external API calls and database connections. In Rails, enforce SSL application-wide.
|
|
11
|
+
|
|
12
|
+
**Incorrect (insecure protocol):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
# HTTP is insecure
|
|
16
|
+
response = Net::HTTP.get(URI("http://api.example.com/data"))
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Correct (force SSL/TLS):**
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
# config/environments/production.rb
|
|
23
|
+
config.force_ssl = true
|
|
24
|
+
|
|
25
|
+
# Using secure protocol
|
|
26
|
+
response = Net::HTTP.get(URI("https://api.example.com/data"))
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Tools:** Brakeman, Manual Review
|
|
30
|
+
---
|