@sun-asterisk/sunlint 1.3.40 → 1.3.42

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 (172) hide show
  1. package/core/rule-selection-service.js +11 -0
  2. package/package.json +1 -1
  3. package/skill-assets/sunlint-code-quality/rules/dart/D001-recommended-lints.md +36 -0
  4. package/skill-assets/sunlint-code-quality/rules/dart/D002-dispose-resources.md +44 -0
  5. package/skill-assets/sunlint-code-quality/rules/dart/D003-prefer-widget-classes.md +53 -0
  6. package/skill-assets/sunlint-code-quality/rules/dart/D004-avoid-shrinkwrap.md +31 -0
  7. package/skill-assets/sunlint-code-quality/rules/dart/D005-widget-nesting.md +62 -0
  8. package/skill-assets/sunlint-code-quality/rules/dart/D006-large-callbacks.md +54 -0
  9. package/skill-assets/sunlint-code-quality/rules/dart/D007-lifecycle-order.md +44 -0
  10. package/skill-assets/sunlint-code-quality/rules/dart/D008-long-functions.md +37 -0
  11. package/skill-assets/sunlint-code-quality/rules/dart/D009-function-parameters.md +38 -0
  12. package/skill-assets/sunlint-code-quality/rules/dart/D010-cyclomatic-complexity.md +46 -0
  13. package/skill-assets/sunlint-code-quality/rules/dart/D011-named-parameters.md +31 -0
  14. package/skill-assets/sunlint-code-quality/rules/dart/D012-named-booleans.md +28 -0
  15. package/skill-assets/sunlint-code-quality/rules/dart/D013-single-public-class.md +34 -0
  16. package/skill-assets/sunlint-code-quality/rules/dart/D014-safe-collection-access.md +30 -0
  17. package/skill-assets/sunlint-code-quality/rules/dart/D015-copywith-consistency.md +52 -0
  18. package/skill-assets/sunlint-code-quality/rules/dart/D016-project-tests.md +32 -0
  19. package/skill-assets/sunlint-code-quality/rules/dart/D017-review-dependencies.md +24 -0
  20. package/skill-assets/sunlint-code-quality/rules/dart/D018-no-commented-code.md +34 -0
  21. package/skill-assets/sunlint-code-quality/rules/dart/D019-single-child-wrappers.md +31 -0
  22. package/skill-assets/sunlint-code-quality/rules/dart/D020-if-else-limit.md +41 -0
  23. package/skill-assets/sunlint-code-quality/rules/dart/D021-negated-booleans.md +26 -0
  24. package/skill-assets/sunlint-code-quality/rules/dart/D022-setstate-usage.md +35 -0
  25. package/skill-assets/sunlint-code-quality/rules/dart/D023-unnecessary-overrides.md +32 -0
  26. package/skill-assets/sunlint-code-quality/rules/dart/D024-avoid-unnecessary-statefulwidget.md +45 -0
  27. package/skill-assets/sunlint-code-quality/rules/dart/D025-nested-ternaries.md +35 -0
  28. package/skill-assets/sunlint-code-quality/rules/go/C006-verb-noun-functions.md +45 -0
  29. package/skill-assets/sunlint-code-quality/rules/go/C013-no-dead-code.md +48 -0
  30. package/skill-assets/sunlint-code-quality/rules/go/C014-dependency-injection.md +85 -0
  31. package/skill-assets/sunlint-code-quality/rules/go/C017-no-constructor-logic.md +67 -0
  32. package/skill-assets/sunlint-code-quality/rules/go/C018-generic-errors.md +63 -0
  33. package/skill-assets/sunlint-code-quality/rules/go/C019-error-log-level.md +50 -0
  34. package/skill-assets/sunlint-code-quality/rules/go/C020-no-unused-imports.md +45 -0
  35. package/skill-assets/sunlint-code-quality/rules/go/C022-no-unused-variables.md +34 -0
  36. package/skill-assets/sunlint-code-quality/rules/go/C023-no-duplicate-names.md +41 -0
  37. package/skill-assets/sunlint-code-quality/rules/go/C024-centralize-constants.md +55 -0
  38. package/skill-assets/sunlint-code-quality/rules/go/C029-catch-log-root-cause.md +56 -0
  39. package/skill-assets/sunlint-code-quality/rules/go/C030-custom-error-classes.md +69 -0
  40. package/skill-assets/sunlint-code-quality/rules/go/C033-separate-data-access.md +68 -0
  41. package/skill-assets/sunlint-code-quality/rules/go/C035-error-context-logging.md +48 -0
  42. package/skill-assets/sunlint-code-quality/rules/go/C041-no-hardcoded-secrets.md +45 -0
  43. package/skill-assets/sunlint-code-quality/rules/go/C042-boolean-naming.md +42 -0
  44. package/skill-assets/sunlint-code-quality/rules/go/C052-controller-parsing.md +62 -0
  45. package/skill-assets/sunlint-code-quality/rules/go/C060-superclass-logic.md +60 -0
  46. package/skill-assets/sunlint-code-quality/rules/go/C067-no-hardcoded-config.md +51 -0
  47. package/skill-assets/sunlint-code-quality/rules/go/S003-open-redirect.md +80 -0
  48. package/skill-assets/sunlint-code-quality/rules/go/S004-no-log-credentials.md +66 -0
  49. package/skill-assets/sunlint-code-quality/rules/go/S005-server-authorization.md +55 -0
  50. package/skill-assets/sunlint-code-quality/rules/go/S006-default-credentials.md +47 -0
  51. package/skill-assets/sunlint-code-quality/rules/go/S007-output-encoding.md +50 -0
  52. package/skill-assets/sunlint-code-quality/rules/go/S009-approved-crypto.md +63 -0
  53. package/skill-assets/sunlint-code-quality/rules/go/S010-csprng.md +53 -0
  54. package/skill-assets/sunlint-code-quality/rules/go/S011-encrypted-client-hello.md +34 -0
  55. package/skill-assets/sunlint-code-quality/rules/go/S012-secrets-management.md +49 -0
  56. package/skill-assets/sunlint-code-quality/rules/go/S013-tls-connections.md +61 -0
  57. package/skill-assets/sunlint-code-quality/rules/go/S016-no-sensitive-query-string.md +42 -0
  58. package/skill-assets/sunlint-code-quality/rules/go/S017-parameterized-queries.md +36 -0
  59. package/skill-assets/sunlint-code-quality/rules/go/S019-email-input-sanitization.md +44 -0
  60. package/skill-assets/sunlint-code-quality/rules/go/S020-eval-code-execution.md +47 -0
  61. package/skill-assets/sunlint-code-quality/rules/go/S022-context-escaping.md +49 -0
  62. package/skill-assets/sunlint-code-quality/rules/go/S023-dynamic-js-encoding.md +51 -0
  63. package/skill-assets/sunlint-code-quality/rules/go/S025-server-validation.md +57 -0
  64. package/skill-assets/sunlint-code-quality/rules/go/S026-tls-encryption.md +46 -0
  65. package/skill-assets/sunlint-code-quality/rules/go/S027-mtls-validation.md +52 -0
  66. package/skill-assets/sunlint-code-quality/rules/go/S028-upload-limits.md +58 -0
  67. package/skill-assets/sunlint-code-quality/rules/go/S029-csrf-protection.md +53 -0
  68. package/skill-assets/sunlint-code-quality/rules/go/S030-directory-browsing.md +53 -0
  69. package/skill-assets/sunlint-code-quality/rules/go/S031-secure-cookie-flag.md +48 -0
  70. package/skill-assets/sunlint-code-quality/rules/go/S032-httponly-cookie.md +42 -0
  71. package/skill-assets/sunlint-code-quality/rules/go/S033-samesite-cookie.md +49 -0
  72. package/skill-assets/sunlint-code-quality/rules/go/S034-host-prefix-cookie.md +44 -0
  73. package/skill-assets/sunlint-code-quality/rules/go/S035-app-hostnames.md +50 -0
  74. package/skill-assets/sunlint-code-quality/rules/go/S036-internal-file-paths.md +56 -0
  75. package/skill-assets/sunlint-code-quality/rules/go/S037-anti-cache-headers.md +43 -0
  76. package/skill-assets/sunlint-code-quality/rules/go/S039-tls-certificate-validation.md +41 -0
  77. package/skill-assets/sunlint-code-quality/rules/go/S041-logout-invalidation.md +46 -0
  78. package/skill-assets/sunlint-code-quality/rules/go/S042-long-lived-sessions.md +58 -0
  79. package/skill-assets/sunlint-code-quality/rules/go/S044-critical-changes-reauth.md +53 -0
  80. package/skill-assets/sunlint-code-quality/rules/go/S045-brute-force-protection.md +55 -0
  81. package/skill-assets/sunlint-code-quality/rules/go/S047-oauth-csrf-protection.md +51 -0
  82. package/skill-assets/sunlint-code-quality/rules/go/S048-oauth-redirect-validation.md +58 -0
  83. package/skill-assets/sunlint-code-quality/rules/go/S049-auth-code-expiry.md +52 -0
  84. package/skill-assets/sunlint-code-quality/rules/go/S050-token-entropy.md +53 -0
  85. package/skill-assets/sunlint-code-quality/rules/go/S051-password-length.md +49 -0
  86. package/skill-assets/sunlint-code-quality/rules/go/S052-otp-entropy.md +48 -0
  87. package/skill-assets/sunlint-code-quality/rules/go/S053-generic-error-messages.md +51 -0
  88. package/skill-assets/sunlint-code-quality/rules/go/S054-no-default-admin.md +43 -0
  89. package/skill-assets/sunlint-code-quality/rules/go/S055-content-type-validation.md +52 -0
  90. package/skill-assets/sunlint-code-quality/rules/go/S056-log-injection.md +40 -0
  91. package/skill-assets/sunlint-code-quality/rules/go/S057-synchronized-time.md +40 -0
  92. package/skill-assets/sunlint-code-quality/rules/go/S058-ssrf-protection.md +70 -0
  93. package/skill-assets/sunlint-code-quality/rules/ruby/RB001-use-snake-case.md +30 -0
  94. package/skill-assets/sunlint-code-quality/rules/ruby/RB002-use-camel-case.md +38 -0
  95. package/skill-assets/sunlint-code-quality/rules/ruby/RB003-use-screaming-snake-case.md +26 -0
  96. package/skill-assets/sunlint-code-quality/rules/ruby/RB004-predicate-methods.md +36 -0
  97. package/skill-assets/sunlint-code-quality/rules/ruby/RB005-dangerous-methods.md +36 -0
  98. package/skill-assets/sunlint-code-quality/rules/ruby/RB006-indentation.md +32 -0
  99. package/skill-assets/sunlint-code-quality/rules/ruby/RB007-line-length.md +25 -0
  100. package/skill-assets/sunlint-code-quality/rules/ruby/RB008-rescue-exception.md +36 -0
  101. package/skill-assets/sunlint-code-quality/rules/ruby/RB009-save-bang.md +41 -0
  102. package/skill-assets/sunlint-code-quality/rules/ruby/RB010-avoid-n-plus-one.md +32 -0
  103. package/skill-assets/sunlint-code-quality/rules/ruby/RB011-use-find-each.md +30 -0
  104. package/skill-assets/sunlint-code-quality/rules/ruby/RB012-sql-injection.md +29 -0
  105. package/skill-assets/sunlint-code-quality/rules/ruby/RB013-prefer-has-many-through.md +35 -0
  106. package/skill-assets/sunlint-code-quality/rules/ruby/RB014-dependent-associations.md +28 -0
  107. package/skill-assets/sunlint-code-quality/rules/ruby/RB015-modern-validations.md +29 -0
  108. package/skill-assets/sunlint-code-quality/rules/ruby/RB016-thin-controllers.md +45 -0
  109. package/skill-assets/sunlint-code-quality/rules/ruby/RB017-avoid-fat-models.md +41 -0
  110. package/skill-assets/sunlint-code-quality/rules/ruby/RB018-service-objects.md +36 -0
  111. package/skill-assets/sunlint-code-quality/rules/ruby/RB019-avoid-metaprogramming.md +40 -0
  112. package/skill-assets/sunlint-code-quality/rules/ruby/RB020-use-pluck.md +29 -0
  113. package/skill-assets/sunlint-code-quality/rules/ruby/RB021-use-size.md +27 -0
  114. package/skill-assets/sunlint-code-quality/rules/ruby/RB022-order-by-timestamps.md +24 -0
  115. package/skill-assets/sunlint-code-quality/rules/ruby/RB023-where-missing.md +24 -0
  116. package/skill-assets/sunlint-code-quality/rules/ruby/RB024-method-length.md +41 -0
  117. package/skill-assets/sunlint-code-quality/rules/ruby/RB025-parameter-limits.md +28 -0
  118. package/skill-assets/sunlint-code-quality/rules/ruby/RB026-avoid-deep-nesting.md +38 -0
  119. package/skill-assets/sunlint-code-quality/rules/ruby/RB027-guard-clauses.md +37 -0
  120. package/skill-assets/sunlint-code-quality/rules/ruby/RB028-class-length.md +32 -0
  121. package/skill-assets/sunlint-code-quality/rules/ruby/RB029-meaningful-names.md +30 -0
  122. package/skill-assets/sunlint-code-quality/rules/ruby/RB030-dry-principle.md +37 -0
  123. package/skill-assets/sunlint-code-quality/rules/ruby/RB031-mvc-architecture.md +37 -0
  124. package/skill-assets/sunlint-code-quality/rules/ruby/RB032-use-concerns.md +31 -0
  125. package/skill-assets/sunlint-code-quality/rules/ruby/RB033-moderate-callbacks.md +31 -0
  126. package/skill-assets/sunlint-code-quality/rules/ruby/RB034-use-decorators.md +33 -0
  127. package/skill-assets/sunlint-code-quality/rules/ruby/RB035-comprehensive-tests.md +32 -0
  128. package/skill-assets/sunlint-code-quality/rules/ruby/RB036-frozen-string-literal.md +29 -0
  129. package/skill-assets/sunlint-code-quality/rules/ruby/RB037-it-parameter.md +25 -0
  130. package/skill-assets/sunlint-code-quality/rules/ruby/RB038-modern-enum-syntax.md +28 -0
  131. package/skill-assets/sunlint-code-quality/rules/ruby/RB039-solid-adapters.md +29 -0
  132. package/skill-assets/sunlint-code-quality/rules/ruby/RB040-rails-authentication.md +26 -0
  133. package/skill-assets/sunlint-code-quality/rules/ruby/RB041-async-query-loading.md +29 -0
  134. package/skill-assets/sunlint-code-quality/rules/ruby/RB042-hotwire-turbo.md +30 -0
  135. package/skill-assets/sunlint-code-quality/rules/ruby/RB043-use-propshaft.md +27 -0
  136. package/skill-assets/sunlint-code-quality/rules/ruby/RB044-structured-logging.md +35 -0
  137. package/skill-assets/sunlint-code-quality/rules/ruby/RB045-prism-parser.md +29 -0
  138. package/skill-assets/sunlint-code-quality/rules/swift/SW001-block-based-kvo.md +40 -0
  139. package/skill-assets/sunlint-code-quality/rules/swift/SW002-class-delegate-protocol.md +36 -0
  140. package/skill-assets/sunlint-code-quality/rules/swift/SW003-compiler-protocol-init.md +28 -0
  141. package/skill-assets/sunlint-code-quality/rules/swift/SW004-contains-over-filter-count.md +28 -0
  142. package/skill-assets/sunlint-code-quality/rules/swift/SW005-convenience-type.md +34 -0
  143. package/skill-assets/sunlint-code-quality/rules/swift/SW006-discarded-notification-center-observer.md +41 -0
  144. package/skill-assets/sunlint-code-quality/rules/swift/SW007-discouraged-direct-init.md +28 -0
  145. package/skill-assets/sunlint-code-quality/rules/swift/SW008-discouraged-optional-boolean.md +32 -0
  146. package/skill-assets/sunlint-code-quality/rules/swift/SW009-empty-count.md +30 -0
  147. package/skill-assets/sunlint-code-quality/rules/swift/SW010-empty-string.md +30 -0
  148. package/skill-assets/sunlint-code-quality/rules/swift/SW011-explicit-init.md +26 -0
  149. package/skill-assets/sunlint-code-quality/rules/swift/SW012-fatal-error-message.md +28 -0
  150. package/skill-assets/sunlint-code-quality/rules/swift/SW013-for-where.md +30 -0
  151. package/skill-assets/sunlint-code-quality/rules/swift/SW014-force-cast.md +26 -0
  152. package/skill-assets/sunlint-code-quality/rules/swift/SW015-force-try.md +30 -0
  153. package/skill-assets/sunlint-code-quality/rules/swift/SW016-force-unwrapping.md +32 -0
  154. package/skill-assets/sunlint-code-quality/rules/swift/SW017-function-parameter-count.md +37 -0
  155. package/skill-assets/sunlint-code-quality/rules/swift/SW018-large-tuple.md +41 -0
  156. package/skill-assets/sunlint-code-quality/rules/swift/SW019-legacy-constructor.md +28 -0
  157. package/skill-assets/sunlint-code-quality/rules/swift/SW020-nesting.md +38 -0
  158. package/skill-assets/sunlint-code-quality/rules/swift/SW021-no-extension-access-modifier.md +28 -0
  159. package/skill-assets/sunlint-code-quality/rules/swift/SW022-overridden-super-call.md +30 -0
  160. package/skill-assets/sunlint-code-quality/rules/swift/SW023-override-in-extension.md +32 -0
  161. package/skill-assets/sunlint-code-quality/rules/swift/SW024-private-over-fileprivate.md +28 -0
  162. package/skill-assets/sunlint-code-quality/rules/swift/SW025-private-unit-test.md +32 -0
  163. package/skill-assets/sunlint-code-quality/rules/swift/SW026-prohibited-super-call.md +29 -0
  164. package/skill-assets/sunlint-code-quality/rules/swift/SW027-sorted-first-last.md +28 -0
  165. package/skill-assets/sunlint-code-quality/rules/swift/SW028-syntactic-sugar.md +28 -0
  166. package/skill-assets/sunlint-code-quality/rules/swift/SW029-unused-closure-parameter.md +28 -0
  167. package/skill-assets/sunlint-code-quality/rules/swift/SW030-unused-enumerated.md +28 -0
  168. package/skill-assets/sunlint-code-quality/rules/swift/SW031-unused-optional-binding.md +26 -0
  169. package/skill-assets/sunlint-code-quality/rules/swift/SW032-valid-ibinspectable.md +26 -0
  170. package/skill-assets/sunlint-code-quality/rules/swift/SW033-vertical-parameter-alignment.md +36 -0
  171. package/skill-assets/sunlint-code-quality/rules/swift/SW034-void-return.md +28 -0
  172. package/skill-assets/sunlint-code-quality/rules/swift/SW035-weak-delegate.md +28 -0
@@ -0,0 +1,48 @@
1
+ ---
2
+ title: OTPs Must Have 20-bit Entropy Minimum
3
+ impact: MEDIUM
4
+ impactDescription: prevents OTP brute-forcing
5
+ tags: otp, entropy, authentication, 2fa, security
6
+ ---
7
+
8
+ ## OTPs Must Have 20-bit Entropy Minimum
9
+
10
+ Low-entropy OTPs (like 4 digits) can be brute-forced easily. 20 bits of entropy requires at least 6 decimal digits.
11
+
12
+ **Incorrect (low entropy OTPs):**
13
+
14
+ ```go
15
+ // 4 digits = ~13 bits entropy
16
+ otp := fmt.Sprintf("%04d", rand.Intn(10000))
17
+ ```
18
+
19
+ **Correct (high entropy OTPs via crypto/rand):**
20
+
21
+ ```go
22
+ import (
23
+ "crypto/rand"
24
+ "math/big"
25
+ )
26
+
27
+ // 6-digit numeric OTP (≈20 bits entropy)
28
+ func GenerateOTP() string {
29
+ max := big.NewInt(1000000)
30
+ n, _ := rand.Int(rand.Reader, max)
31
+ return fmt.Sprintf("%06d", n)
32
+ }
33
+
34
+ // 8-digit for higher security (≈26 bits)
35
+ func GenerateStrongOTP() string {
36
+ max := big.NewInt(100000000)
37
+ n, _ := rand.Int(rand.Reader, max)
38
+ return fmt.Sprintf("%08d", n)
39
+ }
40
+ ```
41
+
42
+ **OTP requirements:**
43
+ - Must use `crypto/rand` (CSPRNG).
44
+ - Minimum 6 digits (20 bits).
45
+ - Short expiration (5-10 minutes).
46
+ - Rate limit verification attempts.
47
+
48
+ **Tools:** Manual Review, Unit Test
@@ -0,0 +1,51 @@
1
+ ---
2
+ title: Return Generic Error Messages
3
+ impact: HIGH
4
+ impactDescription: prevents information disclosure
5
+ tags: error-messages, information-disclosure, security
6
+ ---
7
+
8
+ ## Return Generic Error Messages
9
+
10
+ Detailed error messages can help attackers understand your system's internals. Return generic messages to end-users.
11
+
12
+ **Incorrect (detailed errors to user):**
13
+
14
+ ```go
15
+ func Handler(w http.ResponseWriter, r *http.Request) {
16
+ err := db.QueryRow("...").Scan(&id)
17
+ if err != nil {
18
+ // Exposes database details!
19
+ http.Error(w, err.Error(), 500)
20
+ return
21
+ }
22
+ }
23
+
24
+ // User enumeration
25
+ if userNotFound {
26
+ http.Error(w, "User john@example.com dose not exist", 404)
27
+ }
28
+ ```
29
+
30
+ **Correct (generic errors with internal logging):**
31
+
32
+ ```go
33
+ func Handler(w http.ResponseWriter, r *http.Request) {
34
+ err := db.QueryRow("...").Scan(&id)
35
+ if err != nil {
36
+ // Log internally
37
+ slog.Error("query failed", "error", err, "request_id", r.Header.Get("X-Request-Id"))
38
+
39
+ // Return generic message
40
+ http.Error(w, "An internal error occurred", 500)
41
+ return
42
+ }
43
+ }
44
+
45
+ // Same message for "user not found" and "wrong password"
46
+ if !userExists || !passwordMatches {
47
+ http.Error(w, "Invalid credentials", 401)
48
+ }
49
+ ```
50
+
51
+ **Tools:** Global Error Middleware, `slog`
@@ -0,0 +1,43 @@
1
+ ---
2
+ title: Avoid Default Admin/Root Accounts
3
+ impact: HIGH
4
+ impactDescription: prevents easy initial access by attackers
5
+ tags: admin, default-accounts, credentials, security
6
+ ---
7
+
8
+ ## Avoid Default Admin/Root Accounts
9
+
10
+ Default accounts with known credentials (e.g., admin/admin) are the first thing attackers check.
11
+
12
+ **Incorrect (default admin in seed):**
13
+
14
+ ```go
15
+ // Seed script
16
+ db.Exec("INSERT INTO users (email, password, role) VALUES ('admin@example.com', 'admin123', 'admin')")
17
+ ```
18
+
19
+ **Correct (secure initial setup):**
20
+
21
+ ```go
22
+ // 1. Setup wizard on first run
23
+ func SetupHandler(w http.ResponseWriter, r *http.Request) {
24
+ if adminExists() {
25
+ http.Error(w, "Setup already done", 403)
26
+ return
27
+ }
28
+ // Form to create first admin...
29
+ }
30
+
31
+ // 2. Or use environment variables for bootstrap
32
+ func BootstrapAdmin() {
33
+ email := os.Getenv("INITIAL_ADMIN_EMAIL")
34
+ pass := os.Getenv("INITIAL_ADMIN_PASSWORD")
35
+
36
+ if pass == "" || len(pass) < 16 {
37
+ log.Fatal("Secure INITIAL_ADMIN_PASSWORD required")
38
+ }
39
+ createAdmin(email, pass)
40
+ }
41
+ ```
42
+
43
+ **Tools:** Security Audit, Configuration Review
@@ -0,0 +1,52 @@
1
+ ---
2
+ title: Validate Content-Type In REST Services
3
+ impact: MEDIUM
4
+ impactDescription: prevents content-type confusion attacks
5
+ tags: rest, content-type, validation, api, security
6
+ ---
7
+
8
+ ## Validate Content-Type In REST Services
9
+
10
+ Accepting unexpected content types can lead to parsing vulnerabilities or bypass security controls.
11
+
12
+ **Incorrect (accepting any content):**
13
+
14
+ ```go
15
+ func Handler(w http.ResponseWriter, r *http.Request) {
16
+ // No check on Content-Type header
17
+ var data map[string]interface{}
18
+ json.NewDecoder(r.Body).Decode(&data)
19
+ }
20
+ ```
21
+
22
+ **Correct (strict content-type validation):**
23
+
24
+ ```go
25
+ func validateContentType(allowed ...string) func(http.Handler) http.Handler {
26
+ return func(next http.Handler) http.Handler {
27
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
28
+ ct := r.Header.Get("Content-Type")
29
+
30
+ isValid := false
31
+ for _, a := range allowed {
32
+ if strings.Contains(strings.ToLower(ct), strings.ToLower(a)) {
33
+ isValid = true
34
+ break
35
+ }
36
+ }
37
+
38
+ if !isValid {
39
+ http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
40
+ return
41
+ }
42
+
43
+ next.ServeHTTP(w, r)
44
+ })
45
+ }
46
+ }
47
+
48
+ // Router usage
49
+ mux.Handle("/api/data", validateContentType("application/json")(http.HandlerFunc(Handler)))
50
+ ```
51
+
52
+ **Tools:** API Gateway, Middleware, OWASP ZAP
@@ -0,0 +1,40 @@
1
+ ---
2
+ title: Protect Against Log Injection
3
+ impact: HIGH
4
+ impactDescription: prevents log forging and exploitation
5
+ tags: logging, injection, sanitization, security
6
+ ---
7
+
8
+ ## Protect Against Log Injection
9
+
10
+ Log injection allows attackers to forge log entries, hide their tracks, or inject malicious control characters.
11
+
12
+ **Incorrect (unsanitized logging):**
13
+
14
+ ```go
15
+ func Handler(w http.ResponseWriter, r *http.Request) {
16
+ user := r.FormValue("user")
17
+ slog.Info("User logged in: " + user)
18
+ // Attacker: "admin\n[ERROR] Payment failed for user: victim"
19
+ }
20
+ ```
21
+
22
+ **Correct (sanitized structured logging):**
23
+
24
+ ```go
25
+ // 1. Use structured logging (automatically handles quotes/escaping in key-value pairs)
26
+ slog.Info("User logged in", "username", sanitizeForLog(user))
27
+
28
+ // 2. Sanitize input
29
+ func sanitizeForLog(input string) string {
30
+ // Replace CRLF/tabs with space
31
+ replacer := strings.NewReplacer("\r", " ", "\n", " ", "\t", " ")
32
+ return replacer.Replace(input)
33
+ }
34
+
35
+ // 3. Recommended: Use JSON logger in production
36
+ logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
37
+ logger.Info("User logged in", "username", user)
38
+ ```
39
+
40
+ **Tools:** `log/slog`, `zap`, `gosec`
@@ -0,0 +1,40 @@
1
+ ---
2
+ title: Use Synchronized Time (UTC) In Logs
3
+ impact: MEDIUM
4
+ impactDescription: enables accurate incident correlation
5
+ tags: logging, time, utc, synchronization, security
6
+ ---
7
+
8
+ ## Use Synchronized Time (UTC) In Logs
9
+
10
+ Inconsistent timestamps across servers/services make incident investigation difficult. Use UTC and ISO 8601 format.
11
+
12
+ **Incorrect (local time/custom formats):**
13
+
14
+ ```go
15
+ // Local time - depends on server TZ
16
+ log.Printf("Event time: %v", time.Now())
17
+
18
+ // Different formats
19
+ fmt.Println(time.Now().Unix())
20
+ fmt.Println(time.Now().Format("2006-01-02"))
21
+ ```
22
+
23
+ **Correct (UTC, ISO 8601):**
24
+
25
+ ```go
26
+ // Use .UTC() and .Format(time.RFC3339)
27
+ slog.Info("User action",
28
+ "timestamp", time.Now().UTC().Format(time.RFC3339Nano),
29
+ "action", "login",
30
+ )
31
+
32
+ // Output: {"timestamp":"2024-01-15T10:30:00.123456Z", "level":"INFO", "message":"User action", "action":"login"}
33
+ ```
34
+
35
+ **Requirements:**
36
+ - Use UTC timezone externally.
37
+ - Use ISO 8601 (RFC 3339) with nanosecond/millisecond precision.
38
+ - Ensure servers are NTP-synchronized.
39
+
40
+ **Tools:** `time` (Standard Library), `slog`, NTP
@@ -0,0 +1,70 @@
1
+ ---
2
+ title: Protect Against SSRF Attacks
3
+ impact: MEDIUM
4
+ impactDescription: prevents internal network access from user input
5
+ tags: ssrf, url, network, internal, security
6
+ ---
7
+
8
+ ## Protect Against SSRF Attacks
9
+
10
+ SSRF allows attackers to make requests from your server to internal services, local files, or cloud metadata endpoints.
11
+
12
+ **Incorrect (accepting user URLs without validation):**
13
+
14
+ ```go
15
+ func Handler(w http.ResponseWriter, r *http.Request) {
16
+ url := r.URL.Query().Get("url")
17
+ resp, _ := http.Get(url) // Attacker controls URL!
18
+ io.Copy(w, resp.Body)
19
+ }
20
+ // Attacker: ?url=http://169.254.169.254/latest/meta-data/
21
+ ```
22
+
23
+ **Correct (URL validation and IP blocking):**
24
+
25
+ ```go
26
+ import (
27
+ "net"
28
+ "net/url"
29
+ )
30
+
31
+ var allowedHosts = []string{"api.example.com", "cdn.example.com"}
32
+
33
+ func SafeFetch(userURL string) (*http.Response, error) {
34
+ parsed, err := url.Parse(userURL)
35
+ if err != nil {
36
+ return nil, err
37
+ }
38
+
39
+ // 1. Protocol whitelist
40
+ if parsed.Scheme != "http" && parsed.Scheme != "https" {
41
+ return nil, errors.New("protocol not allowed")
42
+ }
43
+
44
+ // 2. Host whitelist
45
+ isAllowed := false
46
+ for _, h := range allowedHosts {
47
+ if parsed.Hostname() == h {
48
+ isAllowed = true
49
+ break
50
+ }
51
+ }
52
+
53
+ // 3. Resolve IP and block internal ranges
54
+ ips, _ := net.LookupIP(parsed.Hostname())
55
+ for _, ip := range ips {
56
+ if ip.IsLoopback() || ip.IsPrivate() || ip.IsUnspecified() {
57
+ return nil, errors.New("internal IP blocked")
58
+ }
59
+ }
60
+
61
+ client := &http.Client{
62
+ CheckRedirect: func(req *http.Request, via []*http.Request) error {
63
+ return http.ErrUseLastResponse // Disable redirects
64
+ },
65
+ }
66
+ return client.Get(userURL)
67
+ }
68
+ ```
69
+
70
+ **Tools:** `net/url`, `net.LookupIP`, `gosec` (G107)
@@ -0,0 +1,30 @@
1
+ ---
2
+ title: Use snake_case for symbols, methods, and variables
3
+ impact: MEDIUM
4
+ impactDescription: Follow Ruby community naming conventions for consistency and readability.
5
+ tags: ruby, naming, snake_case, convention
6
+ ---
7
+
8
+ ## Use snake_case for symbols, methods, and variables
9
+
10
+ Follow Ruby community naming conventions for consistency and readability. Use `snake_case` (all lowercase with underscores) for symbols, methods, and variables. Avoid camelCase or other naming styles for these elements.
11
+
12
+ **Incorrect:**
13
+
14
+ ```ruby
15
+ def calculateTotal(price, taxRate)
16
+ totalAmount = price * taxRate
17
+ :orderStatus
18
+ end
19
+ ```
20
+
21
+ **Correct:**
22
+
23
+ ```ruby
24
+ def calculate_total(price, tax_rate)
25
+ total_amount = price * tax_rate
26
+ :order_status
27
+ end
28
+ ```
29
+
30
+ **Tools:** RuboCop (`Naming/VariableName`, `Naming/MethodName`)
@@ -0,0 +1,38 @@
1
+ ---
2
+ title: Use CamelCase for classes and modules
3
+ impact: MEDIUM
4
+ impactDescription: Follow Ruby community naming conventions for classes and modules.
5
+ tags: ruby, naming, CamelCase, architecture
6
+ ---
7
+
8
+ ## Use CamelCase for classes and modules
9
+
10
+ Use `CamelCase` (also known as PascalCase) for class and module names. Keep acronyms uppercase (e.g., `HTTPClient`, `XMLParser`).
11
+
12
+ **Incorrect:**
13
+
14
+ ```ruby
15
+ class User_profile
16
+ end
17
+
18
+ module Api_Helper
19
+ end
20
+
21
+ class HttpClient # Acronym 'HTTP' should be all caps
22
+ end
23
+ ```
24
+
25
+ **Correct:**
26
+
27
+ ```ruby
28
+ class UserProfile
29
+ end
30
+
31
+ module ApiHelper
32
+ end
33
+
34
+ class HTTPClient
35
+ end
36
+ ```
37
+
38
+ **Tools:** RuboCop (`Naming/ClassName`)
@@ -0,0 +1,26 @@
1
+ ---
2
+ title: Use SCREAMING_SNAKE_CASE for constants
3
+ impact: LOW
4
+ impactDescription: Clearly distinguish constants from other identifiers.
5
+ tags: ruby, naming, constants, readability
6
+ ---
7
+
8
+ ## Use SCREAMING_SNAKE_CASE for constants
9
+
10
+ Clearly distinguish constants from other identifiers. Use `SCREAMING_SNAKE_CASE` for constants that do not refer to classes or modules.
11
+
12
+ **Incorrect:**
13
+
14
+ ```ruby
15
+ DefaultTimeout = 30
16
+ Max_Retry_Count = 5
17
+ ```
18
+
19
+ **Correct:**
20
+
21
+ ```ruby
22
+ DEFAULT_TIMEOUT = 30
23
+ MAX_RETRY_COUNT = 5
24
+ ```
25
+
26
+ **Tools:** RuboCop (`Naming/ConstantName`)
@@ -0,0 +1,36 @@
1
+ ---
2
+ title: Predicate methods should end with ?
3
+ impact: MEDIUM
4
+ impactDescription: Make boolean-returning methods immediately recognizable.
5
+ tags: ruby, naming, predicate, boolean
6
+ ---
7
+
8
+ ## Predicate methods should end with ?
9
+
10
+ Make boolean-returning methods immediately recognizable. Methods that return boolean values should end with a question mark (`?`). Avoid prefixing with auxiliary verbs like `is_`, `does_`, or `can_`.
11
+
12
+ **Incorrect:**
13
+
14
+ ```ruby
15
+ def is_valid
16
+ # logic
17
+ end
18
+
19
+ if user.is_authenticated
20
+ # logic
21
+ end
22
+ ```
23
+
24
+ **Correct:**
25
+
26
+ ```ruby
27
+ def valid?
28
+ # logic
29
+ end
30
+
31
+ if user.authenticated?
32
+ # logic
33
+ end
34
+ ```
35
+
36
+ **Tools:** RuboCop (`Naming/PredicateName`)
@@ -0,0 +1,36 @@
1
+ ---
2
+ title: Dangerous methods should end with !
3
+ impact: MEDIUM
4
+ impactDescription: Clearly indicate methods that modify the receiver or can raise exceptions.
5
+ tags: ruby, naming, bang, side-effect
6
+ ---
7
+
8
+ ## Dangerous methods should end with !
9
+
10
+ Clearly indicate methods that modify the receiver or can raise exceptions. Methods that modify the object in place or can raise exceptions should end with a bang (`!`).
11
+
12
+ **Incorrect:**
13
+
14
+ ```ruby
15
+ # Method raises an exception if validation fails
16
+ def save
17
+ # complex logic
18
+ end
19
+
20
+ # In-place sort
21
+ values.sort
22
+ ```
23
+
24
+ **Correct:**
25
+
26
+ ```ruby
27
+ # Recommends save! for raising exceptions
28
+ def save!
29
+ # complex logic
30
+ end
31
+
32
+ # Indicates in-place sort
33
+ values.sort!
34
+ ```
35
+
36
+ **Tools:** RuboCop (`Style/BangPredicate`)
@@ -0,0 +1,32 @@
1
+ ---
2
+ title: Use 2 spaces for indentation
3
+ impact: LOW
4
+ impactDescription: Follow Ruby community standard for code formatting.
5
+ tags: ruby, formatting, indentation, style
6
+ ---
7
+
8
+ ## Use 2 spaces for indentation
9
+
10
+ Follow Ruby community standard for code formatting. Use 2 spaces for indentation, never tabs. Ensure consistent indentation throughout the codebase.
11
+
12
+ **Incorrect:**
13
+
14
+ ```ruby
15
+ def hello
16
+ puts "hello" # Tab used
17
+ end
18
+
19
+ def world
20
+ puts "world" # 4 spaces used
21
+ end
22
+ ```
23
+
24
+ **Correct:**
25
+
26
+ ```ruby
27
+ def hello
28
+ puts "hello" # 2 spaces
29
+ end
30
+ ```
31
+
32
+ **Tools:** RuboCop (`Layout/IndentationWidth`)
@@ -0,0 +1,25 @@
1
+ ---
2
+ title: Keep lines under 120 characters
3
+ impact: LOW
4
+ impactDescription: Improve code readability and prevent horizontal scrolling.
5
+ tags: ruby, formatting, readability, side-scrolling
6
+ ---
7
+
8
+ ## Keep lines under 120 characters
9
+
10
+ Improve code readability and prevent horizontal scrolling. Limit line length to 120 characters maximum. Break long lines into multiple lines when necessary.
11
+
12
+ **Incorrect:**
13
+
14
+ ```ruby
15
+ puts "This is an extremely long string that definitely exceeds the recommended limit of one hundred and twenty characters that we have set for our coding standards in this project."
16
+ ```
17
+
18
+ **Correct:**
19
+
20
+ ```ruby
21
+ puts "This is an extremely long string that definitely exceeds " \
22
+ "the recommended limit of one hundred and twenty characters..."
23
+ ```
24
+
25
+ **Tools:** RuboCop (`Layout/LineLength`)
@@ -0,0 +1,36 @@
1
+ ---
2
+ title: Avoid rescuing the Exception class
3
+ impact: HIGH
4
+ impactDescription: Prevent hiding critical system errors.
5
+ tags: ruby, errors, exception, safety
6
+ ---
7
+
8
+ ## Avoid rescuing the Exception class
9
+
10
+ Prevent hiding critical system errors. Never rescue the generic `Exception` class as it catches system-level errors (like `SignalException`, `NoMemoryError`, `SystemExit`). Rescue specific exception classes or `StandardError` instead.
11
+
12
+ **Incorrect (Dangerous):**
13
+
14
+ ```ruby
15
+ begin
16
+ do_something
17
+ rescue Exception => e
18
+ logger.error(e.message)
19
+ end
20
+ ```
21
+
22
+ **Correct (Safe):**
23
+
24
+ ```ruby
25
+ begin
26
+ do_something
27
+ rescue StandardError => e # Or just 'rescue => e' which defaults to StandardError
28
+ logger.error(e.message)
29
+ end
30
+
31
+ # Or specific
32
+ rescue ActiveRecord::RecordNotFound => e
33
+ # handle
34
+ ```
35
+
36
+ **Tools:** RuboCop (`Lint/RescueException`)
@@ -0,0 +1,41 @@
1
+ ---
2
+ title: Use save! or handle return values
3
+ impact: HIGH
4
+ impactDescription: Ensure database operations are properly validated and errors are not silently ignored.
5
+ tags: rails, database, validations, active-record
6
+ ---
7
+
8
+ ## Use save! or handle return values
9
+
10
+ Ensure database operations are properly validated and errors are not silently ignored. Use bang methods (`save!`, `create!`, `update!`, `destroy!`) to raise exceptions on failure. If using non-bang methods, always check the return value.
11
+
12
+ **Incorrect:**
13
+
14
+ ```ruby
15
+ def create
16
+ @user = User.new(user_params)
17
+ @user.save # Failure will be silent
18
+ redirect_to @user
19
+ end
20
+ ```
21
+
22
+ **Correct:**
23
+
24
+ ```ruby
25
+ def create
26
+ @user = User.new(user_params)
27
+ if @user.save
28
+ redirect_to @user
29
+ else
30
+ render :new
31
+ end
32
+ end
33
+
34
+ # Or using bang
35
+ def create
36
+ @user = User.create!(user_params)
37
+ redirect_to @user
38
+ end
39
+ ```
40
+
41
+ **Tools:** RuboCop (`Rails/SaveBang`)
@@ -0,0 +1,32 @@
1
+ ---
2
+ title: Avoid N+1 queries with eager loading
3
+ impact: HIGH
4
+ impactDescription: Prevent performance issues caused by N+1 query problems.
5
+ tags: rails, performance, database, n+1, active-record
6
+ ---
7
+
8
+ ## Avoid N+1 queries with eager loading
9
+
10
+ Prevent performance issues caused by N+1 query problems. Use `includes`, `preload`, or `eager_load` to load associations upfront. Avoid iterating over collections and accessing associations without eager loading.
11
+
12
+ **Incorrect:**
13
+
14
+ ```ruby
15
+ # Triggers N+1 queries (one query for users, and one query per user for posts)
16
+ users = User.limit(10)
17
+ users.each do |user|
18
+ puts user.posts.count
19
+ end
20
+ ```
21
+
22
+ **Correct:**
23
+
24
+ ```ruby
25
+ # Eager loads posts in 2 queries total
26
+ users = User.includes(:posts).limit(10)
27
+ users.each do |user|
28
+ puts user.posts.count
29
+ end
30
+ ```
31
+
32
+ **Tools:** Bullet gem, RuboCop Rails (`Rails/FindEach`)