@sun-asterisk/sunlint 1.3.46 → 1.3.48

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 (102) hide show
  1. package/config/rules/rules-registry-generated.json +1717 -282
  2. package/core/adapters/sunlint-rule-adapter.js +16 -0
  3. package/core/architecture-integration.js +57 -15
  4. package/core/cli-action-handler.js +51 -36
  5. package/core/config-manager.js +6 -0
  6. package/core/config-merger.js +33 -0
  7. package/core/config-validator.js +37 -2
  8. package/core/output-service.js +12 -3
  9. package/core/rule-selection-service.js +24 -3
  10. package/core/scoring-service.js +12 -6
  11. package/core/summary-report-service.js +9 -4
  12. package/engines/heuristic-engine.js +6 -1
  13. package/engines/impact/cli.js +54 -39
  14. package/engines/impact/config/default-config.js +105 -5
  15. package/engines/impact/core/impact-analyzer.js +12 -15
  16. package/engines/impact/core/utils/gitignore-parser.js +123 -0
  17. package/engines/impact/core/utils/method-call-graph.js +272 -87
  18. package/origin-rules/dart-en.md +1 -1
  19. package/origin-rules/go-en.md +231 -0
  20. package/origin-rules/php-en.md +107 -0
  21. package/origin-rules/python-en.md +113 -0
  22. package/origin-rules/ruby-en.md +607 -0
  23. package/package.json +2 -2
  24. package/scripts/copy-arch-detect.js +5 -1
  25. package/scripts/copy-impact-analyzer.js +5 -1
  26. package/scripts/generate-rules-registry.js +30 -14
  27. package/skill-assets/sunlint-code-quality/SKILL.md +3 -2
  28. package/skill-assets/sunlint-code-quality/rules/go/G001-explicit-error-handling.md +53 -0
  29. package/skill-assets/sunlint-code-quality/rules/go/G002-context-first-argument.md +44 -0
  30. package/skill-assets/sunlint-code-quality/rules/go/G003-receiver-consistency.md +38 -0
  31. package/skill-assets/sunlint-code-quality/rules/go/G004-avoid-panic.md +49 -0
  32. package/skill-assets/sunlint-code-quality/rules/go/G005-goroutine-leak-prevention.md +49 -0
  33. package/skill-assets/sunlint-code-quality/rules/go/G006-interface-consumer-definition.md +45 -0
  34. package/skill-assets/sunlint-code-quality/rules/go/GN001-gin-binding-validation.md +57 -0
  35. package/skill-assets/sunlint-code-quality/rules/go/GN002-gin-error-response.md +48 -0
  36. package/skill-assets/sunlint-code-quality/rules/go/GN003-graceful-shutdown.md +57 -0
  37. package/skill-assets/sunlint-code-quality/rules/go/GN004-gin-route-logical-grouping.md +54 -0
  38. package/skill-assets/sunlint-code-quality/rules/ruby/C006-verb-noun-functions.md +63 -0
  39. package/skill-assets/sunlint-code-quality/rules/ruby/C013-no-dead-code.md +48 -0
  40. package/skill-assets/sunlint-code-quality/rules/ruby/C014-dependency-injection.md +42 -0
  41. package/skill-assets/sunlint-code-quality/rules/ruby/C017-no-constructor-logic.md +42 -0
  42. package/skill-assets/sunlint-code-quality/rules/ruby/C018-generic-errors.md +41 -0
  43. package/skill-assets/sunlint-code-quality/rules/ruby/C019-error-log-level.md +41 -0
  44. package/skill-assets/sunlint-code-quality/rules/ruby/C020-no-unused-imports.md +36 -0
  45. package/skill-assets/sunlint-code-quality/rules/ruby/C022-no-unused-variables.md +31 -0
  46. package/skill-assets/sunlint-code-quality/rules/ruby/C023-no-duplicate-names.md +39 -0
  47. package/skill-assets/sunlint-code-quality/rules/ruby/C024-centralize-constants.md +35 -0
  48. package/skill-assets/sunlint-code-quality/rules/ruby/C029-catch-log-root-cause.md +34 -0
  49. package/skill-assets/sunlint-code-quality/rules/ruby/C030-custom-error-classes.md +32 -0
  50. package/skill-assets/sunlint-code-quality/rules/ruby/C033-separate-data-access.md +52 -0
  51. package/skill-assets/sunlint-code-quality/rules/ruby/C035-error-context-logging.md +34 -0
  52. package/skill-assets/sunlint-code-quality/rules/ruby/C041-no-hardcoded-secrets.md +29 -0
  53. package/skill-assets/sunlint-code-quality/rules/ruby/C042-boolean-naming.md +38 -0
  54. package/skill-assets/sunlint-code-quality/rules/ruby/C052-controller-parsing.md +37 -0
  55. package/skill-assets/sunlint-code-quality/rules/ruby/C060-superclass-logic.md +38 -0
  56. package/skill-assets/sunlint-code-quality/rules/ruby/C067-no-hardcoded-config.md +37 -0
  57. package/skill-assets/sunlint-code-quality/rules/ruby/S003-open-redirect.md +58 -0
  58. package/skill-assets/sunlint-code-quality/rules/ruby/S004-no-log-credentials.md +38 -0
  59. package/skill-assets/sunlint-code-quality/rules/ruby/S005-server-authorization.md +37 -0
  60. package/skill-assets/sunlint-code-quality/rules/ruby/S006-default-credentials.md +29 -0
  61. package/skill-assets/sunlint-code-quality/rules/ruby/S007-output-encoding.md +31 -0
  62. package/skill-assets/sunlint-code-quality/rules/ruby/S009-approved-crypto.md +31 -0
  63. package/skill-assets/sunlint-code-quality/rules/ruby/S010-csprng.md +30 -0
  64. package/skill-assets/sunlint-code-quality/rules/ruby/S011-encrypted-client-hello.md +27 -0
  65. package/skill-assets/sunlint-code-quality/rules/ruby/S012-secrets-management.md +28 -0
  66. package/skill-assets/sunlint-code-quality/rules/ruby/S013-tls-connections.md +30 -0
  67. package/skill-assets/sunlint-code-quality/rules/ruby/S016-no-sensitive-query-string.md +37 -0
  68. package/skill-assets/sunlint-code-quality/rules/ruby/S017-parameterized-queries.md +33 -0
  69. package/skill-assets/sunlint-code-quality/rules/ruby/S019-email-input-sanitization.md +31 -0
  70. package/skill-assets/sunlint-code-quality/rules/ruby/S020-eval-code-execution.md +36 -0
  71. package/skill-assets/sunlint-code-quality/rules/ruby/S022-context-escaping.md +36 -0
  72. package/skill-assets/sunlint-code-quality/rules/ruby/S023-dynamic-js-encoding.md +33 -0
  73. package/skill-assets/sunlint-code-quality/rules/ruby/S025-server-validation.md +30 -0
  74. package/skill-assets/sunlint-code-quality/rules/ruby/S026-tls-encryption.md +30 -0
  75. package/skill-assets/sunlint-code-quality/rules/ruby/S027-mtls-validation.md +26 -0
  76. package/skill-assets/sunlint-code-quality/rules/ruby/S028-upload-limits.md +33 -0
  77. package/skill-assets/sunlint-code-quality/rules/ruby/S029-csrf-protection.md +32 -0
  78. package/skill-assets/sunlint-code-quality/rules/ruby/S030-directory-browsing.md +30 -0
  79. package/skill-assets/sunlint-code-quality/rules/ruby/S031-secure-cookie-flag.md +27 -0
  80. package/skill-assets/sunlint-code-quality/rules/ruby/S032-httponly-cookie.md +26 -0
  81. package/skill-assets/sunlint-code-quality/rules/ruby/S033-samesite-cookie.md +29 -0
  82. package/skill-assets/sunlint-code-quality/rules/ruby/S034-host-prefix-cookie.md +30 -0
  83. package/skill-assets/sunlint-code-quality/rules/ruby/S035-app-hostnames.md +28 -0
  84. package/skill-assets/sunlint-code-quality/rules/ruby/S036-internal-file-paths.md +37 -0
  85. package/skill-assets/sunlint-code-quality/rules/ruby/S037-anti-cache-headers.md +31 -0
  86. package/skill-assets/sunlint-code-quality/rules/ruby/S039-tls-certificate-validation.md +29 -0
  87. package/skill-assets/sunlint-code-quality/rules/ruby/S041-logout-invalidation.md +31 -0
  88. package/skill-assets/sunlint-code-quality/rules/ruby/S042-long-lived-sessions.md +27 -0
  89. package/skill-assets/sunlint-code-quality/rules/ruby/S044-critical-changes-reauth.md +34 -0
  90. package/skill-assets/sunlint-code-quality/rules/ruby/S045-brute-force-protection.md +33 -0
  91. package/skill-assets/sunlint-code-quality/rules/ruby/S047-oauth-csrf-protection.md +33 -0
  92. package/skill-assets/sunlint-code-quality/rules/ruby/S048-oauth-redirect-validation.md +29 -0
  93. package/skill-assets/sunlint-code-quality/rules/ruby/S049-auth-code-expiry.md +31 -0
  94. package/skill-assets/sunlint-code-quality/rules/ruby/S050-token-entropy.md +26 -0
  95. package/skill-assets/sunlint-code-quality/rules/ruby/S051-password-length.md +38 -0
  96. package/skill-assets/sunlint-code-quality/rules/ruby/S052-otp-entropy.md +25 -0
  97. package/skill-assets/sunlint-code-quality/rules/ruby/S053-generic-error-messages.md +33 -0
  98. package/skill-assets/sunlint-code-quality/rules/ruby/S054-no-default-admin.md +29 -0
  99. package/skill-assets/sunlint-code-quality/rules/ruby/S055-content-type-validation.md +24 -0
  100. package/skill-assets/sunlint-code-quality/rules/ruby/S056-log-injection.md +28 -0
  101. package/skill-assets/sunlint-code-quality/rules/ruby/S057-synchronized-time.md +18 -0
  102. package/skill-assets/sunlint-code-quality/rules/ruby/S058-ssrf-protection.md +39 -0
@@ -16,29 +16,45 @@ try {
16
16
  const parser = new SimpleRuleParser();
17
17
  const originRulesDir = path.join(__dirname, '..', 'origin-rules');
18
18
  const targetPath = path.join(__dirname, '..', 'config', 'rules', 'rules-registry-generated.json');
19
-
19
+
20
20
  console.log(`Source: ${originRulesDir}`);
21
21
  console.log(`Target: ${targetPath}`);
22
-
22
+
23
23
  // Parse all rules from origin-rules
24
24
  const allRules = parser.parseAllRules(originRulesDir);
25
-
25
+
26
26
  if (allRules.length === 0) {
27
27
  console.error('❌ No rules found in origin-rules directory');
28
28
  process.exit(1);
29
29
  }
30
-
30
+
31
31
  // Convert to registry format
32
32
  const registry = {
33
33
  rules: {}
34
34
  };
35
-
35
+
36
36
  allRules.forEach(rule => {
37
37
  if (rule.id) {
38
+ let category = rule.category || 'Common';
39
+ const id = rule.id;
40
+
41
+ // Smart category detection based on prefix
42
+ if (id.startsWith('S')) {
43
+ category = 'Security';
44
+ } else if (id.startsWith('C')) {
45
+ category = 'Common';
46
+ } else if (['RB', 'G', 'GN', 'J', 'PY', 'P'].some(p => id.startsWith(p))) {
47
+ category = 'Backend';
48
+ } else if (['T', 'R'].some(p => id.startsWith(p))) {
49
+ category = 'Frontend';
50
+ } else if (['K', 'SW', 'D'].some(p => id.startsWith(p))) {
51
+ category = 'Mobile';
52
+ }
53
+
38
54
  registry.rules[rule.id] = {
39
55
  name: rule.title || `${rule.id} Rule`,
40
56
  description: rule.description || 'No description available',
41
- category: rule.category || 'quality',
57
+ category: category,
42
58
  severity: rule.severity || 'major',
43
59
  languages: rule.language ? [rule.language] : ['All languages'],
44
60
  version: rule.version || '1.0.0',
@@ -50,37 +66,37 @@ try {
50
66
  };
51
67
  }
52
68
  });
53
-
69
+
54
70
  // Ensure target directory exists
55
71
  const targetDir = path.dirname(targetPath);
56
72
  if (!fs.existsSync(targetDir)) {
57
73
  fs.mkdirSync(targetDir, { recursive: true });
58
74
  }
59
-
75
+
60
76
  // Write registry file
61
77
  fs.writeFileSync(targetPath, JSON.stringify(registry, null, 2), 'utf8');
62
-
78
+
63
79
  const rulesCount = Object.keys(registry.rules).length;
64
80
  const fileSize = (fs.statSync(targetPath).size / 1024).toFixed(1);
65
-
81
+
66
82
  console.log(`✅ Generated registry with ${rulesCount} rules`);
67
83
  console.log(`📁 File: ${targetPath} (${fileSize} KB)`);
68
84
  console.log('');
69
85
  console.log('📊 Rules by category:');
70
-
86
+
71
87
  // Stats by category
72
88
  const categories = {};
73
89
  Object.values(registry.rules).forEach(rule => {
74
90
  const cat = rule.category || 'unknown';
75
91
  categories[cat] = (categories[cat] || 0) + 1;
76
92
  });
77
-
93
+
78
94
  Object.entries(categories)
79
- .sort(([,a], [,b]) => b - a)
95
+ .sort(([, a], [, b]) => b - a)
80
96
  .forEach(([category, count]) => {
81
97
  console.log(` ${category}: ${count} rules`);
82
98
  });
83
-
99
+
84
100
  } catch (error) {
85
101
  console.error('❌ Error generating registry:', error.message);
86
102
  console.error(error.stack);
@@ -9,7 +9,7 @@ metadata:
9
9
 
10
10
  # SunLint Code Quality & Security Standards
11
11
 
12
- Comprehensive code quality and security optimization guide for all projects, maintained by Sun* Engineering Excellence team. Contains **65 rules** across **6 priority categories**, organized by impact to guide automated code review and generation.
12
+ Comprehensive code quality and security optimization guide for all projects, maintained by Sun* Engineering Excellence team. Contains **75 rules** across **7 priority categories**, organized by impact to guide automated code review and generation.
13
13
 
14
14
  ## When to Apply
15
15
 
@@ -31,6 +31,7 @@ Reference these guidelines when:
31
31
  | 4 | Security - Cryptography & TLS | **HIGH** | 8 | `S0xx` |
32
32
  | 5 | Security - Data Protection | **HIGH** | 10 | `S0xx` |
33
33
  | 6 | Security - Logging & Monitoring | **MEDIUM** | 6 | `S0xx` |
34
+ | 7 | Language Specific (Go/Gin) | **CRITICAL/HIGH** | 10 | `Gxx`/`GNxx` |
34
35
 
35
36
  ---
36
37
 
@@ -173,4 +174,4 @@ For the complete guide with all rules expanded: `AGENTS.md`
173
174
 
174
175
  ---
175
176
 
176
- **Last Updated**: January 2026 | **Version**: 2.3 | **Maintainer**: Sun* Engineering Excellence
177
+ **Last Updated**: February 2026 | **Version**: 2.4.1 | **Maintainer**: Sun* Engineering Excellence
@@ -0,0 +1,53 @@
1
+ ---
2
+ title: Explicit Error Handling
3
+ impact: CRITICAL
4
+ impactDescription: prevents silent failures and ensures robust error recovery
5
+ tags: go, error-handling, quality
6
+ ---
7
+
8
+ ## Explicit Error Handling
9
+
10
+ In Go, errors are values and must be handled explicitly. Ignoring errors using `_` or failing to check them leads to unpredictable state and silent failures.
11
+
12
+ **Incorrect (ignoring errors):**
13
+
14
+ ```go
15
+ func processData(data string) {
16
+ file, _ := os.Open("config.json") // Error ignored
17
+ defer file.Close()
18
+
19
+ bytes, _ := io.ReadAll(file) // Error ignored
20
+ json.Unmarshal(bytes, &config) // Error ignored
21
+ }
22
+ ```
23
+
24
+ **Correct (explicit checking):**
25
+
26
+ ```go
27
+ func processData(data string) error {
28
+ file, err := os.Open("config.json")
29
+ if err != nil {
30
+ return fmt.Errorf("failed to open config: %w", err)
31
+ }
32
+ defer file.Close()
33
+
34
+ bytes, err := io.ReadAll(file)
35
+ if err != nil {
36
+ return fmt.Errorf("failed to read config: %w", err)
37
+ }
38
+
39
+ if err := json.Unmarshal(bytes, &config); err != nil {
40
+ return fmt.Errorf("failed to parse config: %w", err)
41
+ }
42
+
43
+ return nil
44
+ }
45
+ ```
46
+
47
+ **Benefits:**
48
+ - Prevents silent crashes and data corruption
49
+ - Provides clear trace of what went wrong
50
+ - Forces developers to think about failure paths
51
+ - Makes code easier to debug
52
+
53
+ **Tools:** errcheck, golangci-lint, staticcheck
@@ -0,0 +1,44 @@
1
+ ---
2
+ title: Context as First Argument
3
+ impact: MEDIUM
4
+ impactDescription: follows Go idiomatic patterns for cancellation and timeouts
5
+ tags: go, context, patterns, quality
6
+ ---
7
+
8
+ ## Context as First Argument
9
+
10
+ `context.Context` should always be the first parameter of a function, typically named `ctx`. This consistency makes it easy to propagate cancellation, deadlines, and markers across call stacks.
11
+
12
+ **Incorrect (wrong order or missing):**
13
+
14
+ ```go
15
+ func (s *Service) FetchUser(userID string, ctx context.Context) (*User, error) {
16
+ // ctx is second argument
17
+ return s.repo.Get(ctx, userID)
18
+ }
19
+
20
+ func ProcessTask(taskID string) {
21
+ // Missing context for potentially long-running operation
22
+ db.Exec("UPDATE tasks SET status = 'done' WHERE id = ?", taskID)
23
+ }
24
+ ```
25
+
26
+ **Correct (context first):**
27
+
28
+ ```go
29
+ func (s *Service) FetchUser(ctx context.Context, userID string) (*User, error) {
30
+ return s.repo.Get(ctx, userID)
31
+ }
32
+
33
+ func ProcessTask(ctx context.Context, taskID string) error {
34
+ return db.ExecContext(ctx, "UPDATE tasks SET status = 'done' WHERE id = ?", taskID)
35
+ }
36
+ ```
37
+
38
+ **Benefits:**
39
+ - Standardizes API signatures across the codebase
40
+ - Simplifies cancellation propagation
41
+ - Enables proper timeout handling for I/O and external calls
42
+ - Improves observability with trace IDs in context
43
+
44
+ **Tools:** golangci-lint, contextcheck
@@ -0,0 +1,38 @@
1
+ ---
2
+ title: Consistent Receiver Naming
3
+ impact: LOW
4
+ impactDescription: improves readability and consistency
5
+ tags: go, style, quality
6
+ ---
7
+
8
+ ## Consistent Receiver Naming
9
+
10
+ Receiver names should be short (1-2 letters) and consistent across all methods of a type. Do not use generic names like `this`, `self`, or `me`.
11
+
12
+ **Incorrect (inconsistent or verbose):**
13
+
14
+ ```go
15
+ func (this *UserRepository) Get(id string) (*User, error) { ... }
16
+
17
+ func (repo *UserRepository) List() ([]*User, error) { ... }
18
+
19
+ func (self *UserRepository) Update(u *User) error { ... }
20
+ ```
21
+
22
+ **Correct (short and consistent):**
23
+
24
+ ```go
25
+ // Use 'r' consistently for UserRepository
26
+ func (r *UserRepository) Get(id string) (*User, error) { ... }
27
+
28
+ func (r *UserRepository) List() ([]*User, error) { ... }
29
+
30
+ func (r *UserRepository) Update(u *User) error { ... }
31
+ ```
32
+
33
+ **Benefits:**
34
+ - Reduces visual noise in method definitions
35
+ - Follows Go community standards
36
+ - Makes it easier to search and scan methods of the same type
37
+
38
+ **Tools:** golangci-lint, stylecheck
@@ -0,0 +1,49 @@
1
+ ---
2
+ title: Avoid Panic in Production
3
+ impact: HIGH
4
+ impactDescription: prevents application crashes and enables graceful degradation
5
+ tags: go, error-handling, stability, quality
6
+ ---
7
+
8
+ ## Avoid Panic in Production
9
+
10
+ Panic should be reserved for truly unrecoverable programmer errors (e.g., initialization failure where the app cannot start). Business logic and normal I/O should always use `error` returns.
11
+
12
+ **Incorrect (panic for normal errors):**
13
+
14
+ ```go
15
+ func GetUser(id string) *User {
16
+ user, err := db.Find(id)
17
+ if err != nil {
18
+ panic(err) // Crash!
19
+ }
20
+ return user
21
+ }
22
+ ```
23
+
24
+ **Correct (return error):**
25
+
26
+ ```go
27
+ func GetUser(id string) (*User, error) {
28
+ user, err := db.Find(id)
29
+ if err != nil {
30
+ return nil, fmt.Errorf("failed to find user: %w", err)
31
+ }
32
+ return user, nil
33
+ }
34
+
35
+ // In main or init, panic is acceptable if the app MUST die
36
+ func main() {
37
+ config, err := LoadConfig()
38
+ if err != nil {
39
+ panic("critical config missing: " + err.Error())
40
+ }
41
+ }
42
+ ```
43
+
44
+ **Benefits:**
45
+ - Improves application uptime and reliability
46
+ - Allows handlers to recover and return HTTP 500 instead of crashing the process
47
+ - Makes the code more predictable and testable
48
+
49
+ **Tools:** golangci-lint, staticcheck
@@ -0,0 +1,49 @@
1
+ ---
2
+ title: Goroutine Leak Prevention
3
+ impact: HIGH
4
+ impactDescription: prevents memory exhaustion and zombie processes
5
+ tags: go, concurrency, stability, quality
6
+ ---
7
+
8
+ ## Goroutine Leak Prevention
9
+
10
+ Always ensure that a goroutine has a clear termination path, typically via a `context.Context` or a close channel. Goroutines that block indefinitely on channel operations or I/O without a timeout cause memory leaks.
11
+
12
+ **Incorrect (leaky goroutine):**
13
+
14
+ ```go
15
+ func StartWatcher(ch chan string) {
16
+ go func() {
17
+ for msg := range ch { // Blocks forever if ch is never closed
18
+ fmt.Println(msg)
19
+ }
20
+ }()
21
+ }
22
+ ```
23
+
24
+ **Correct (context-aware goroutine):**
25
+
26
+ ```go
27
+ func StartWatcher(ctx context.Context, ch chan string) {
28
+ go func() {
29
+ for {
30
+ select {
31
+ case <-ctx.Done():
32
+ return // Exit cleanly when context is cancelled
33
+ case msg, ok := <-ch:
34
+ if !ok {
35
+ return // Exit when channel is closed
36
+ }
37
+ fmt.Println(msg)
38
+ }
39
+ }
40
+ }()
41
+ }
42
+ ```
43
+
44
+ **Benefits:**
45
+ - Prevents memory leaks and resource exhaustion
46
+ - Ensures clean application shutdown
47
+ - Makes concurrent code more predictable and testable
48
+
49
+ **Tools:** goleak, golangci-lint
@@ -0,0 +1,45 @@
1
+ ---
2
+ title: Define Interfaces at Consumer Side
3
+ impact: MEDIUM
4
+ impactDescription: promotes decoupling and simplifies testing
5
+ tags: go, architecture, interfaces, quality
6
+ ---
7
+
8
+ ## Define Interfaces at Consumer Side
9
+
10
+ In Go, interfaces are satisfied implicitly. Do not define interfaces in the same package where the implementation is defined (producer side). Instead, define the interface in the package that requires the dependency (consumer side).
11
+
12
+ **Incorrect (interface in producer package):**
13
+
14
+ ```go
15
+ // package auth
16
+ type Service interface {
17
+ Login(user, pass string) (string, error)
18
+ }
19
+
20
+ type serviceImpl struct { ... }
21
+ func (s *serviceImpl) Login(u, p string) (string, error) { ... }
22
+ ```
23
+
24
+ **Correct (interface in consumer package):**
25
+
26
+ ```go
27
+ // package auth (implementation only)
28
+ type Service struct { ... }
29
+ func (s *Service) Login(u, p string) (string, error) { ... }
30
+
31
+ // package handler (consumer)
32
+ type AuthProvider interface {
33
+ Login(user, pass string) (string, error)
34
+ }
35
+
36
+ func LoginHandler(auth AuthProvider) { ... }
37
+ ```
38
+
39
+ **Benefits:**
40
+ - Prevents package circular dependencies
41
+ - Allows packages to define exactly what they need from a dependency
42
+ - Makes it easier to mock dependencies for unit testing
43
+ - Keeps the system more loosely coupled
44
+
45
+ **Tools:** Code Review, Architecture rules
@@ -0,0 +1,57 @@
1
+ ---
2
+ title: Use Gin Binding for Validation
3
+ impact: HIGH
4
+ impactDescription: simplifies input handling and ensures consistent validation
5
+ tags: gin, go, validation, quality
6
+ ---
7
+
8
+ ## Use Gin Binding for Validation
9
+
10
+ Leverage Gin's built-in binding mechanism (`ShouldBindJSON`, `ShouldBindQuery`, etc.) and the `validator` library tags. This centralizes validation logic and keeps handlers clean.
11
+
12
+ **Incorrect (manual parsing and validation):**
13
+
14
+ ```go
15
+ func CreateUser(c *gin.Context) {
16
+ var raw map[string]interface{}
17
+ if err := c.BindJSON(&raw); err != nil {
18
+ c.JSON(400, gin.H{"error": "invalid json"})
19
+ return
20
+ }
21
+
22
+ email, ok := raw["email"].(string)
23
+ if !ok || email == "" {
24
+ c.JSON(400, gin.H{"error": "email is required"})
25
+ return
26
+ }
27
+ // ... more manual checks
28
+ }
29
+ ```
30
+
31
+ **Correct (struct binding and tags):**
32
+
33
+ ```go
34
+ type CreateUserRequest struct {
35
+ Email string `json:"email" binding:"required,email"`
36
+ Password string `json:"password" binding:"required,min=8"`
37
+ Age int `json:"age" binding:"gte=18"`
38
+ }
39
+
40
+ func CreateUser(c *gin.Context) {
41
+ var req CreateUserRequest
42
+ if err := c.ShouldBindJSON(&req); err != nil {
43
+ c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
44
+ return
45
+ }
46
+
47
+ // Process validated request
48
+ }
49
+ ```
50
+
51
+ **Benefits:**
52
+ - Reduces boilerplate code in handlers
53
+ - Declarative validation is easier to read and maintain
54
+ - Automatically handles type conversion (e.g., string to int)
55
+ - Provides standard error messages
56
+
57
+ **Tools:** Gin, go-playground/validator
@@ -0,0 +1,48 @@
1
+ ---
2
+ title: Abort with Status for Errors
3
+ impact: MEDIUM
4
+ impactDescription: ensures middleware chain is interrupted and consistent response is sent
5
+ tags: gin, go, error-handling, quality
6
+ ---
7
+
8
+ ## Abort with Status for Errors
9
+
10
+ When a fatal error occurs in a Gin handler or middleware (e.g., auth failure, validation error), use `c.AbortWithStatusJSON` or `c.Abort()` to stop the execution of subsequent handlers in the chain.
11
+
12
+ **Incorrect (not aborting or only partial return):**
13
+
14
+ ```go
15
+ func AuthMiddleware(c *gin.Context) {
16
+ token := c.GetHeader("Authorization")
17
+ if token == "" {
18
+ c.JSON(401, gin.H{"error": "unauthorized"})
19
+ // MISSING c.Abort()! Subsequent handlers WILL execute.
20
+ return
21
+ }
22
+ }
23
+ ```
24
+
25
+ **Correct (using AbortWithStatusJSON):**
26
+
27
+ ```go
28
+ func AuthMiddleware(c *gin.Context) {
29
+ if c.GetHeader("Authorization") == "" {
30
+ c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
31
+ return
32
+ }
33
+ }
34
+
35
+ func Handler(c *gin.Context) {
36
+ if err := someFunc(); err != nil {
37
+ c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "internal error"})
38
+ return
39
+ }
40
+ }
41
+ ```
42
+
43
+ **Benefits:**
44
+ - Prevents security bypasses in middleware
45
+ - Consistent error response pattern
46
+ - Guarantees that only one response is sent to the client
47
+
48
+ **Tools:** Gin
@@ -0,0 +1,57 @@
1
+ ---
2
+ title: Implement Graceful Shutdown
3
+ impact: MEDIUM
4
+ impactDescription: prevents data loss and ensures clean connection handling
5
+ tags: gin, go, stability, quality
6
+ ---
7
+
8
+ ## Implement Graceful Shutdown
9
+
10
+ Web servers should handle termination signals (`SIGINT`, `SIGTERM`) to allow inflight requests to finish and to close database connections cleanly. Gin's `Run()` method blocks and doesn't support this out of the box; use `http.Server` instead.
11
+
12
+ **Incorrect (standard Run):**
13
+
14
+ ```go
15
+ func main() {
16
+ r := gin.Default()
17
+ r.GET("/", handler)
18
+ r.Run(":8080") // Blocks and dies immediately on CTRL+C
19
+ }
20
+ ```
21
+
22
+ **Correct (graceful shutdown):**
23
+
24
+ ```go
25
+ func main() {
26
+ router := gin.Default()
27
+ srv := &http.Server{
28
+ Addr: ":8080",
29
+ Handler: router,
30
+ }
31
+
32
+ go func() {
33
+ if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
34
+ log.Fatalf("listen: %s\n", err)
35
+ }
36
+ }()
37
+
38
+ // Wait for interrupt signal
39
+ quit := make(chan os.Signal, 1)
40
+ signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
41
+ <-quit
42
+
43
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
44
+ defer cancel()
45
+
46
+ if err := srv.Shutdown(ctx); err != nil {
47
+ log.Fatal("Server Shutdown:", err)
48
+ }
49
+ }
50
+ ```
51
+
52
+ **Benefits:**
53
+ - Prevents dropping active user connections
54
+ - Allows finishing long-running database transactions
55
+ - Ensures monitoring tools report clean shutdown instead of "crash"
56
+
57
+ **Tools:** Go Standard Library, Gin
@@ -0,0 +1,54 @@
1
+ ---
2
+ title: Logical Route Grouping
3
+ impact: MEDIUM
4
+ impactDescription: improves code organization and shared middleware management
5
+ tags: gin, go, routing, quality
6
+ ---
7
+
8
+ ## Logical Route Grouping
9
+
10
+ Use `RouterGroup` to organize routes logically (e.g., by version, resource, or access level). This allows applying middleware (like auth or logging) to a specific group of routes without repetition.
11
+
12
+ **Incorrect (flat and repetitive):**
13
+
14
+ ```go
15
+ func RegisterRoutes(r *gin.Engine) {
16
+ r.GET("/api/v1/users", AuthMiddleware(), GetUsers)
17
+ r.POST("/api/v1/users", AuthMiddleware(), CreateUser)
18
+ r.GET("/api/v1/users/:id", AuthMiddleware(), GetUser)
19
+ r.GET("/health", HealthCheck)
20
+ }
21
+ ```
22
+
23
+ **Correct (logical grouping):**
24
+
25
+ ```go
26
+ func RegisterRoutes(r *gin.Engine) {
27
+ // Public routes
28
+ r.GET("/health", HealthCheck)
29
+
30
+ // API v1 group
31
+ v1 := r.Group("/api/v1")
32
+ {
33
+ // Auth required group
34
+ auth := v1.Group("/")
35
+ auth.Use(AuthMiddleware())
36
+ {
37
+ users := auth.Group("/users")
38
+ {
39
+ users.GET("/", GetUsers)
40
+ users.POST("/", CreateUser)
41
+ users.GET("/:id", GetUser)
42
+ }
43
+ }
44
+ }
45
+ }
46
+ ```
47
+
48
+ **Benefits:**
49
+ - Reduces code duplication for middleware application
50
+ - Provides clear structure of the API URL space
51
+ - Simplifies refactoring and versioning (e.g., adding `/v2` group)
52
+ - Improves readability of the main routing configuration
53
+
54
+ **Tools:** Gin
@@ -0,0 +1,63 @@
1
+ ---
2
+ title: Function Names Verb-Noun
3
+ impact: LOW
4
+ impactDescription: makes code self-documenting
5
+ tags: naming, functions, readability, conventions, quality
6
+ ---
7
+
8
+ ## Function Names Verb-Noun
9
+
10
+ Methods in Ruby should describe actions clearly. Action verbs make the purpose manifest.
11
+
12
+ **Incorrect (vague names):**
13
+
14
+ ```ruby
15
+ def user # Noun only
16
+ end
17
+
18
+ def user_data # Noun only
19
+ end
20
+
21
+ def do_something # Vague
22
+ end
23
+
24
+ def handle_stuff # Vague
25
+ end
26
+ ```
27
+
28
+ **Correct (action verbs):**
29
+
30
+ ```ruby
31
+ def fetch_user
32
+ end
33
+
34
+ def create_user_account
35
+ end
36
+
37
+ def validate_email_format?
38
+ end
39
+
40
+ def calculate_total_price
41
+ end
42
+
43
+ def send_confirmation_email
44
+ end
45
+
46
+ def convert_currency_to_usd
47
+ end
48
+ ```
49
+
50
+ **Verb categories:**
51
+
52
+ | Category | Verbs |
53
+ |----------|-------|
54
+ | Retrieval | `get`, `fetch`, `find`, `load`, `query` |
55
+ | Creation | `create`, `build`, `make`, `generate` |
56
+ | Modification | `set`, `update`, `modify`, `change` |
57
+ | Deletion | `delete`, `remove`, `destroy`, `clear` |
58
+ | Validation | `validate`, `verify`, `check`, `ensure` |
59
+ | Computation | `calculate`, `compute`, `parse`, `format` |
60
+ | Boolean | `is`, `has`, `can`, `should`, `will` |
61
+
62
+ **Tools:** PR review, RuboCop
63
+ ---