@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: Validate OAuth Redirect URIs
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: prevents redirect hijacking and token leakage
|
|
5
|
+
tags: security, oauth, redirect, validation
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Validate OAuth Redirect URIs
|
|
9
|
+
|
|
10
|
+
Ensure that the redirect URI sent in the authorization request matches the values pre-registered for your application.
|
|
11
|
+
|
|
12
|
+
**Incorrect (dynamic/unvalidated redirect):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
# Allowing user-supplied redirect_uri
|
|
16
|
+
redirect_uri = params[:redirect_uri]
|
|
17
|
+
redirect_to "https://provider.com/auth?redirect_uri=#{redirect_uri}"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Correct (pre-registered URI):**
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
# Hardcode or use config
|
|
24
|
+
CALLBACK_URL = "https://app.com/auth/callback"
|
|
25
|
+
redirect_to "https://provider.com/auth?redirect_uri=#{CALLBACK_URL}"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Tools:** Manual Review
|
|
29
|
+
---
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Enforce Token Expiration
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: limits the lifespan of stolen tokens or temporary credentials
|
|
5
|
+
tags: security, session, token, timeout
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Enforce Token Expiration
|
|
9
|
+
|
|
10
|
+
All authorization codes, access tokens, and temporary files should have a strict, short expiration time.
|
|
11
|
+
|
|
12
|
+
**Incorrect (no expiry):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
class ResetToken < ApplicationRecord
|
|
16
|
+
# Token never expires
|
|
17
|
+
end
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Correct (expiration check):**
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
class ResetToken < ApplicationRecord
|
|
24
|
+
def valid?
|
|
25
|
+
created_at > 2.hours.ago
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Tools:** Manual Review
|
|
31
|
+
---
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use High Entropy for Tokens
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: ensures tokens are unpredictable and cannot be guessed
|
|
5
|
+
tags: security, tokens, entropy
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use High Entropy for Tokens
|
|
9
|
+
|
|
10
|
+
Always use cryptographically secure random values (CSPRNG) with sufficient entropy (at least 128 bits) for all security tokens.
|
|
11
|
+
|
|
12
|
+
**Incorrect (low entropy):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
token = rand(10**10).to_s # Not long enough and not secure
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Correct (High entropy):**
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
require 'securerandom'
|
|
22
|
+
token = SecureRandom.hex(32) # 256 bits of entropy
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Tools:** Brakeman, SecureRandom
|
|
26
|
+
---
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Enforce Stricter Password Policies
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: ensures users use strong, difficult-to-crack passwords
|
|
5
|
+
tags: security, password, authentication
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Enforce Stricter Password Policies
|
|
9
|
+
|
|
10
|
+
Require a minimum password length and complexity (uppercase, lowercase, numbers, symbols) to protect against credential guessing.
|
|
11
|
+
|
|
12
|
+
**Incorrect (weak password requirements):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
class User < ApplicationRecord
|
|
16
|
+
has_secure_password
|
|
17
|
+
validates :password, length: { minimum: 4 } # Too short
|
|
18
|
+
end
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Correct (strong policy):**
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
class User < ApplicationRecord
|
|
25
|
+
has_secure_password
|
|
26
|
+
validates :password, length: { minimum: 12 }
|
|
27
|
+
# Complexity check
|
|
28
|
+
validate :password_complexity
|
|
29
|
+
|
|
30
|
+
def password_complexity
|
|
31
|
+
return if password.blank? || password =~ /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{12,}$/
|
|
32
|
+
errors.add :password, 'Complexity requirement not met'
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Tools:** Devise-security, Manual Review
|
|
38
|
+
---
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Ensure Strong OTP Entropy
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: prevents OTP guessing attacks
|
|
5
|
+
tags: security, mfa, otp, entropy
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Ensure Strong OTP Entropy
|
|
9
|
+
|
|
10
|
+
When using One-Time Passwords (OTP), ensure they are generated using a secure random source and have sufficient length (at least 6 digits).
|
|
11
|
+
|
|
12
|
+
**Incorrect (not secure):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
otp = rand(9999) # Only 4 digits, predictable rand
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Correct (SecureRandom + 6 digits):**
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
otp = SecureRandom.random_number(1_000_000).to_s.rjust(6, '0')
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Tools:** ROTP gem, Brakeman
|
|
25
|
+
---
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Generic Error Messages in Auth
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: prevents account enumeration by hiding which part of the authentication failed
|
|
5
|
+
tags: security, authentication, privacy
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Generic Error Messages in Auth
|
|
9
|
+
|
|
10
|
+
Avoid indicating whether the email or the password was incorrect. Use generic messages like "Invalid email or password".
|
|
11
|
+
|
|
12
|
+
**Incorrect (reveals existence):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
if @user.nil?
|
|
16
|
+
render json: { error: "User not found" } # Enumeration possible
|
|
17
|
+
elsif !@user.authenticate(params[:password])
|
|
18
|
+
render json: { error: "Incorrect password" }
|
|
19
|
+
end
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Correct (generic message):**
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
if @user&.authenticate(params[:password])
|
|
26
|
+
login(@user)
|
|
27
|
+
else
|
|
28
|
+
render json: { error: "Invalid email or password" }, status: :unauthorized
|
|
29
|
+
end
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Tools:** Manual Review
|
|
33
|
+
---
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: No Default or Static Admin Accounts
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: prevents easy administrative access through well-known or static credentials
|
|
5
|
+
tags: security, authorization, credentials, admin
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## No Default or Static Admin Accounts
|
|
9
|
+
|
|
10
|
+
Avoid creating default users or admin accounts with hardcoded passwords in seeds or initial configuration.
|
|
11
|
+
|
|
12
|
+
**Incorrect (default seed):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
# db/seeds.rb
|
|
16
|
+
User.create!(email: "admin@example.com", password: "password", admin: true)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Correct (env-driven seed):**
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
User.find_or_create_by!(email: ENV['INITIAL_ADMIN_EMAIL']) do |u|
|
|
23
|
+
u.password = ENV['INITIAL_ADMIN_PASSWORD']
|
|
24
|
+
u.admin = true
|
|
25
|
+
end
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Tools:** Manual Review
|
|
29
|
+
---
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Validate Content-Type for Uploads
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: prevents uploading of malicious executable files disguised as data
|
|
5
|
+
tags: security, uploads, validation, mimetypes
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Validate Content-Type for Uploads
|
|
9
|
+
|
|
10
|
+
Always validate the `Content-Type` and file extension of uploaded files. Check the magic bytes/file header rather than just the extension.
|
|
11
|
+
|
|
12
|
+
**Correct (ActiveStorage + Marcel):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
# Rails ActiveStorage uses Marcel gem to check magic bytes
|
|
16
|
+
class User < ApplicationRecord
|
|
17
|
+
has_one_attached :avatar
|
|
18
|
+
|
|
19
|
+
validates :avatar, content_type: ['image/png', 'image/jpeg']
|
|
20
|
+
end
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Tools:** ActiveStorage Validations gem
|
|
24
|
+
---
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Prevent Log Injection
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: prevents attackers from corrupting logs or misleading auditors
|
|
5
|
+
tags: security, logging, sanitization
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Prevent Log Injection
|
|
9
|
+
|
|
10
|
+
Sanitize all user-input data before including it in log files to prevent attackers from injecting newlines or carriage returns.
|
|
11
|
+
|
|
12
|
+
**Incorrect (unsafe logging):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
# Attacker name: "admin\n[INFO] Login successful for admin"
|
|
16
|
+
logger.info "User update attempted by #{params[:user_name]}"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Correct (sanitized logging):**
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
# Replace newlines with spaces or use json logger
|
|
23
|
+
sanitized_name = params[:user_name].gsub(/[\n\r]/, " ")
|
|
24
|
+
logger.info "User update attempted by #{sanitized_name}"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Tools:** Lograge (default Rails JSON logging avoids this)
|
|
28
|
+
---
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Ensure Synchronized System Time
|
|
3
|
+
impact: LOW
|
|
4
|
+
impactDescription: crucial for log correlation, audit trails, and time-based security tokens
|
|
5
|
+
tags: security, infrastructure, timing
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Ensure Synchronized System Time
|
|
9
|
+
|
|
10
|
+
Ensure all servers in your environment are synchronized using a network time protocol (NTP). This is critical for authentication tokens (OTP, JWT) and log correlation.
|
|
11
|
+
|
|
12
|
+
**Details:**
|
|
13
|
+
- Configure NTP/Chrony on all production servers.
|
|
14
|
+
- Monitor time synchronization offset.
|
|
15
|
+
- Important for TOTP (MFA) and token expiry checks.
|
|
16
|
+
|
|
17
|
+
**Tools:** NTP, Chrony, CloudWatch Time Sync
|
|
18
|
+
---
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Prevent Server-Side Request Forgery (SSRF)
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: prevents attackers from accessing internal resources or services
|
|
5
|
+
tags: security, ssrf, vulnerability, net-http
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Prevent Server-Side Request Forgery (SSRF)
|
|
9
|
+
|
|
10
|
+
Never allow users to provide the full URL or IP address for your server to fetch. Use an allow-list of domains or a secure proxy.
|
|
11
|
+
|
|
12
|
+
**Incorrect (unvalidated SSRF):**
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
def fetch_external_report
|
|
16
|
+
# Attacker url: http://localhost:5432 or http://169.254.169.254/metadata
|
|
17
|
+
response = Net::HTTP.get(URI(params[:url]))
|
|
18
|
+
render body: response
|
|
19
|
+
end
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Correct (validated domain):**
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
ALLOWED_DOMAINS = ['trusted-report-source.com']
|
|
26
|
+
|
|
27
|
+
def fetch_external_report
|
|
28
|
+
uri = URI(params[:url])
|
|
29
|
+
if ALLOWED_DOMAINS.include?(uri.host)
|
|
30
|
+
response = Net::HTTP.get(uri)
|
|
31
|
+
render body: response
|
|
32
|
+
else
|
|
33
|
+
render_403
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Tools:** Brakeman, ssrf_filter gem
|
|
39
|
+
---
|