@sun-asterisk/sunlint 1.3.46 → 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/rule-selection-service.js +24 -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,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
|
+
---
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Do Not Pass Sensitive Data In Query String
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: prevents credential leakage in logs and history
|
|
5
|
+
tags: url, query-string, sensitive-data, leakage, security
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Do Not Pass Sensitive Data In Query String
|
|
9
|
+
|
|
10
|
+
Query strings appear in logs, browser history, referrer headers, and can be cached. Avoid passing tokens, passwords, or PII in URLs.
|
|
11
|
+
|
|
12
|
+
**Incorrect (sensitive data in URL):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
# Tokens/Passwords in GET params
|
|
16
|
+
get "/login?user=admin&token=#{access_token}"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Correct (sensitive data in body or headers):**
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
# Use POST with Request Body
|
|
23
|
+
post "/login", params: { user: "admin", token: access_token }
|
|
24
|
+
|
|
25
|
+
# Pass tokens in Authorization Header
|
|
26
|
+
headers = { 'Authorization' => "Bearer #{access_token}" }
|
|
27
|
+
response = RestClient.get("https://api.example.com", headers)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Where query strings leak:**
|
|
31
|
+
- Server access logs (nginx, Apache, Rails logs)
|
|
32
|
+
- Browser history
|
|
33
|
+
- Referrer headers
|
|
34
|
+
- Proxy/CDN logs
|
|
35
|
+
|
|
36
|
+
**Tools:** Brakeman, Manual Review
|
|
37
|
+
---
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Parameterized Queries
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: prevents SQL Injection attacks
|
|
5
|
+
tags: security, sql-injection, database, rails-active-record
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Parameterized Queries
|
|
9
|
+
|
|
10
|
+
Never build SQL queries using string interpolation of user-supplied data. Use ActiveRecord's built-in parameterization or sanitization methods.
|
|
11
|
+
|
|
12
|
+
**Incorrect (SQL Injection vulnerability):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
User.where("name = '#{params[:name]}'")
|
|
16
|
+
# Attacker name: ' OR '1'='1
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Correct (parameterized queries):**
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
# Using Hash syntax (auto-parameterized)
|
|
23
|
+
User.where(name: params[:name])
|
|
24
|
+
|
|
25
|
+
# Using Array placeholders
|
|
26
|
+
User.where("name = ?", params[:name])
|
|
27
|
+
|
|
28
|
+
# Using Named placeholders
|
|
29
|
+
User.where("name = :name", name: params[:name])
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Tools:** Brakeman, RuboCop (`Rails/WhereNotWithInterpolation`)
|
|
33
|
+
---
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Sanitize Email Input
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: prevents injection attacks via email fields
|
|
5
|
+
tags: security, e-mail, validation, sanitization
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Sanitize Email Input
|
|
9
|
+
|
|
10
|
+
When handling email addresses, validate the format and sanitize the input before using it in mailers or database queries.
|
|
11
|
+
|
|
12
|
+
**Incorrect (missing validation):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
def send_email
|
|
16
|
+
@user_email = params[:email]
|
|
17
|
+
UserMailer.welcome(@user_email).deliver_now
|
|
18
|
+
end
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Correct (regex validation):**
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
class User < ApplicationRecord
|
|
25
|
+
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
|
|
26
|
+
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }
|
|
27
|
+
end
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Tools:** Rails Validations, Mail gem
|
|
31
|
+
---
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Avoid Dynamic Code Execution
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: prevents Remote Code Execution (RCE) vulnerabilities
|
|
5
|
+
tags: security, eval, rce, dynamic-code
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Avoid Dynamic Code Execution
|
|
9
|
+
|
|
10
|
+
Avoid using `eval`, `instance_eval`, `class_eval`, or `send` with untrusted user input, as it can lead to arbitrary code execution.
|
|
11
|
+
|
|
12
|
+
**Incorrect (unsafe eval):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
# Extremely dangerous: RCE vulnerability
|
|
16
|
+
eval(params[:code])
|
|
17
|
+
|
|
18
|
+
# Dangerous: Method calling vulnerability
|
|
19
|
+
User.send(params[:method]) # Attacker: ?method=destroy_all
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Correct (safe alternatives):**
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
# Use a white-list for dynamic methods
|
|
26
|
+
ALLOWED_METHODS = ['profile', 'settings'].freeze
|
|
27
|
+
if ALLOWED_METHODS.include?(params[:method])
|
|
28
|
+
user.send(params[:method])
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Use JSON parser instead of eval for data
|
|
32
|
+
data = JSON.parse(params[:json_string])
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Tools:** Brakeman, RuboCop (`Security/Eval`)
|
|
36
|
+
---
|