@sun-asterisk/sunlint 1.3.48 → 1.3.49

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 (152) hide show
  1. package/core/file-targeting-service.js +148 -15
  2. package/core/init-command.js +118 -70
  3. package/core/project-detector.js +517 -0
  4. package/core/tui-select.js +245 -0
  5. package/engines/arch-detect/rules/layered/l001-presentation-layer.js +7 -15
  6. package/engines/arch-detect/rules/layered/l002-business-layer.js +7 -15
  7. package/engines/arch-detect/rules/layered/l003-data-layer.js +7 -15
  8. package/engines/arch-detect/rules/layered/l004-model-layer.js +7 -15
  9. package/engines/arch-detect/rules/layered/l005-layer-separation.js +22 -2
  10. package/engines/arch-detect/rules/layered/l006-dependency-direction.js +8 -5
  11. package/engines/arch-detect/rules/modular/m005-no-deep-imports.js +67 -29
  12. package/engines/arch-detect/rules/presentation/pr001-view-layer.js +16 -9
  13. package/engines/arch-detect/rules/presentation/pr006-router-layer.js +33 -8
  14. package/engines/arch-detect/rules/presentation/pr007-interactor-layer.js +35 -6
  15. package/engines/arch-detect/rules/project-scanner/ps003-framework-detection.js +56 -10
  16. package/package.json +1 -1
  17. package/skill-assets/sunlint-code-quality/rules/dart/C006-verb-noun-functions.md +45 -0
  18. package/skill-assets/sunlint-code-quality/rules/dart/C013-no-dead-code.md +53 -0
  19. package/skill-assets/sunlint-code-quality/rules/dart/C014-dependency-injection.md +92 -0
  20. package/skill-assets/sunlint-code-quality/rules/dart/C017-no-constructor-logic.md +62 -0
  21. package/skill-assets/sunlint-code-quality/rules/dart/C018-generic-errors.md +57 -0
  22. package/skill-assets/sunlint-code-quality/rules/dart/C019-error-log-level.md +50 -0
  23. package/skill-assets/sunlint-code-quality/rules/dart/C020-no-unused-imports.md +46 -0
  24. package/skill-assets/sunlint-code-quality/rules/dart/C022-no-unused-variables.md +50 -0
  25. package/skill-assets/sunlint-code-quality/rules/dart/C023-no-duplicate-names.md +56 -0
  26. package/skill-assets/sunlint-code-quality/rules/dart/C024-centralize-constants.md +75 -0
  27. package/skill-assets/sunlint-code-quality/rules/dart/C029-catch-log-root-cause.md +53 -0
  28. package/skill-assets/sunlint-code-quality/rules/dart/C030-custom-error-classes.md +86 -0
  29. package/skill-assets/sunlint-code-quality/rules/dart/C033-separate-data-access.md +90 -0
  30. package/skill-assets/sunlint-code-quality/rules/dart/C035-error-context-logging.md +62 -0
  31. package/skill-assets/sunlint-code-quality/rules/dart/C041-no-hardcoded-secrets.md +75 -0
  32. package/skill-assets/sunlint-code-quality/rules/dart/C042-boolean-naming.md +73 -0
  33. package/skill-assets/sunlint-code-quality/rules/dart/C052-widget-parsing.md +84 -0
  34. package/skill-assets/sunlint-code-quality/rules/dart/C060-superclass-logic.md +91 -0
  35. package/skill-assets/sunlint-code-quality/rules/dart/C067-no-hardcoded-config.md +108 -0
  36. package/skill-assets/sunlint-code-quality/rules/go-gin/AGENTS.md +149 -0
  37. package/skill-assets/sunlint-code-quality/rules/go-gin/GN001-abort-after-response.md +75 -0
  38. package/skill-assets/sunlint-code-quality/rules/go-gin/GN002-request-context.md +64 -0
  39. package/skill-assets/sunlint-code-quality/rules/go-gin/GN003-bind-error-handling.md +70 -0
  40. package/skill-assets/sunlint-code-quality/rules/go-gin/GN004-dependency-injection.md +78 -0
  41. package/skill-assets/sunlint-code-quality/rules/go-gin/GN005-route-groups-middleware.md +71 -0
  42. package/skill-assets/sunlint-code-quality/rules/go-gin/GN006-http-status-codes.md +91 -0
  43. package/skill-assets/sunlint-code-quality/rules/go-gin/GN007-release-mode.md +64 -0
  44. package/skill-assets/sunlint-code-quality/rules/go-gin/GN008-struct-validation-tags.md +90 -0
  45. package/skill-assets/sunlint-code-quality/rules/go-gin/GN009-recovery-middleware.md +68 -0
  46. package/skill-assets/sunlint-code-quality/rules/go-gin/GN010-context-scope.md +68 -0
  47. package/skill-assets/sunlint-code-quality/rules/go-gin/GN011-middleware-concerns.md +92 -0
  48. package/skill-assets/sunlint-code-quality/rules/go-gin/GN012-no-log-sensitive.md +84 -0
  49. package/skill-assets/sunlint-code-quality/rules/java/J001-try-with-resources.md +86 -0
  50. package/skill-assets/sunlint-code-quality/rules/java/J002-equals-and-hashcode.md +88 -0
  51. package/skill-assets/sunlint-code-quality/rules/java/J003-string-comparison.md +72 -0
  52. package/skill-assets/sunlint-code-quality/rules/java/J004-use-java-time.md +91 -0
  53. package/skill-assets/sunlint-code-quality/rules/java/J005-no-print-stack-trace.md +80 -0
  54. package/skill-assets/sunlint-code-quality/rules/java/J006-no-system-println.md +89 -0
  55. package/skill-assets/sunlint-code-quality/rules/java/J007-proper-logger.md +91 -0
  56. package/skill-assets/sunlint-code-quality/rules/java/J008-thread-safe-singleton.md +119 -0
  57. package/skill-assets/sunlint-code-quality/rules/java/J009-utility-class-constructor.md +82 -0
  58. package/skill-assets/sunlint-code-quality/rules/java/J010-preserve-stack-trace.md +119 -0
  59. package/skill-assets/sunlint-code-quality/rules/java/J011-null-safe-compare.md +88 -0
  60. package/skill-assets/sunlint-code-quality/rules/java/J012-use-enum-collections.md +104 -0
  61. package/skill-assets/sunlint-code-quality/rules/java/J013-return-empty-not-null.md +102 -0
  62. package/skill-assets/sunlint-code-quality/rules/java/J014-hardcoded-crypto-key.md +108 -0
  63. package/skill-assets/sunlint-code-quality/rules/java/J015-optional-instead-of-null.md +109 -0
  64. package/skill-assets/sunlint-code-quality/rules/php-laravel/AGENTS.md +124 -0
  65. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV001-form-request-validation.md +64 -0
  66. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV002-eager-load-no-n-plus-1.md +58 -0
  67. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV003-config-not-env.md +54 -0
  68. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV004-fillable-mass-assignment.md +51 -0
  69. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV005-policies-gates-authorization.md +71 -0
  70. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV006-queue-heavy-tasks.md +68 -0
  71. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV007-hash-passwords.md +51 -0
  72. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV008-route-model-binding.md +67 -0
  73. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV009-api-resources.md +72 -0
  74. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV010-chunk-large-datasets.md +58 -0
  75. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV011-db-transactions.md +73 -0
  76. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV012-service-layer.md +78 -0
  77. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV013-testing-factories.md +75 -0
  78. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV014-service-container.md +61 -0
  79. package/skill-assets/sunlint-code-quality/rules/python/P001-mutable-default-argument.md +55 -0
  80. package/skill-assets/sunlint-code-quality/rules/python/P002-specify-file-encoding.md +45 -0
  81. package/skill-assets/sunlint-code-quality/rules/python/P003-context-manager-for-resources.md +54 -0
  82. package/skill-assets/sunlint-code-quality/rules/python/P004-no-bare-except.md +65 -0
  83. package/skill-assets/sunlint-code-quality/rules/python/P005-use-isinstance.md +60 -0
  84. package/skill-assets/sunlint-code-quality/rules/python/P006-timezone-aware-datetime.md +58 -0
  85. package/skill-assets/sunlint-code-quality/rules/python/P007-use-pathlib.md +62 -0
  86. package/skill-assets/sunlint-code-quality/rules/python/P008-no-wildcard-import.md +52 -0
  87. package/skill-assets/sunlint-code-quality/rules/python/P009-logging-lazy-format.md +50 -0
  88. package/skill-assets/sunlint-code-quality/rules/python/P010-exception-chaining.md +57 -0
  89. package/skill-assets/sunlint-code-quality/rules/python/P011-subprocess-check.md +59 -0
  90. package/skill-assets/sunlint-code-quality/rules/python/P012-requests-timeout.md +70 -0
  91. package/skill-assets/sunlint-code-quality/rules/python/P013-no-global-statement.md +73 -0
  92. package/skill-assets/sunlint-code-quality/rules/python/P014-no-modify-collection-while-iterating.md +66 -0
  93. package/skill-assets/sunlint-code-quality/rules/python/P015-prefer-fstrings.md +61 -0
  94. package/skill-assets/sunlint-code-quality/rules/ruby-rails/AGENTS.md +121 -0
  95. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR001-strong-parameters.md +55 -0
  96. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR002-eager-load-includes.md +51 -0
  97. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR003-service-objects.md +99 -0
  98. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR004-active-job-background.md +67 -0
  99. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR005-pagination.md +53 -0
  100. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR006-find-each-batches.md +53 -0
  101. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR007-http-status-codes.md +76 -0
  102. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR008-before-action-auth.md +77 -0
  103. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR009-rails-credentials.md +61 -0
  104. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR010-scopes.md +57 -0
  105. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR011-counter-cache.md +59 -0
  106. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR012-render-json-status.md +42 -0
  107. package/skill-assets/sunlint-code-quality/rules/swift/C006-verb-noun-functions.md +37 -0
  108. package/skill-assets/sunlint-code-quality/rules/swift/C013-no-dead-code.md +55 -0
  109. package/skill-assets/sunlint-code-quality/rules/swift/C014-dependency-injection.md +69 -0
  110. package/skill-assets/sunlint-code-quality/rules/swift/C017-no-constructor-logic.md +66 -0
  111. package/skill-assets/sunlint-code-quality/rules/swift/C018-generic-errors.md +64 -0
  112. package/skill-assets/sunlint-code-quality/rules/swift/C019-error-log-level.md +64 -0
  113. package/skill-assets/sunlint-code-quality/rules/swift/C020-no-unused-imports.md +47 -0
  114. package/skill-assets/sunlint-code-quality/rules/swift/C022-no-unused-variables.md +46 -0
  115. package/skill-assets/sunlint-code-quality/rules/swift/C023-no-duplicate-names.md +55 -0
  116. package/skill-assets/sunlint-code-quality/rules/swift/C024-centralize-constants.md +68 -0
  117. package/skill-assets/sunlint-code-quality/rules/swift/C029-catch-log-root-cause.md +69 -0
  118. package/skill-assets/sunlint-code-quality/rules/swift/C030-custom-error-classes.md +77 -0
  119. package/skill-assets/sunlint-code-quality/rules/swift/C033-separate-data-access.md +89 -0
  120. package/skill-assets/sunlint-code-quality/rules/swift/C035-error-context-logging.md +66 -0
  121. package/skill-assets/sunlint-code-quality/rules/swift/C041-no-hardcoded-secrets.md +65 -0
  122. package/skill-assets/sunlint-code-quality/rules/swift/C042-boolean-naming.md +60 -0
  123. package/skill-assets/sunlint-code-quality/rules/swift/C052-controller-parsing.md +67 -0
  124. package/skill-assets/sunlint-code-quality/rules/swift/C060-superclass-logic.md +95 -0
  125. package/skill-assets/sunlint-code-quality/rules/swift/C067-no-hardcoded-config.md +80 -0
  126. package/skill-assets/sunlint-code-quality/rules/swift/S003-sql-injection.md +65 -0
  127. package/skill-assets/sunlint-code-quality/rules/swift/S004-no-log-credentials.md +67 -0
  128. package/skill-assets/sunlint-code-quality/rules/swift/S005-server-authorization.md +73 -0
  129. package/skill-assets/sunlint-code-quality/rules/swift/S006-default-credentials.md +76 -0
  130. package/skill-assets/sunlint-code-quality/rules/swift/S007-output-encoding.md +96 -0
  131. package/skill-assets/sunlint-code-quality/rules/swift/S009-approved-crypto.md +86 -0
  132. package/skill-assets/sunlint-code-quality/rules/swift/S010-csprng.md +71 -0
  133. package/skill-assets/sunlint-code-quality/rules/swift/S011-insecure-deserialization.md +74 -0
  134. package/skill-assets/sunlint-code-quality/rules/swift/S012-secrets-management.md +81 -0
  135. package/skill-assets/sunlint-code-quality/rules/swift/S013-tls-connections.md +67 -0
  136. package/skill-assets/sunlint-code-quality/rules/swift/S017-parameterized-queries.md +86 -0
  137. package/skill-assets/sunlint-code-quality/rules/swift/S019-session-management.md +131 -0
  138. package/skill-assets/sunlint-code-quality/rules/swift/S020-kvc-injection.md +91 -0
  139. package/skill-assets/sunlint-code-quality/rules/swift/S025-input-validation.md +125 -0
  140. package/skill-assets/sunlint-code-quality/rules/swift/S029-brute-force-protection.md +120 -0
  141. package/skill-assets/sunlint-code-quality/rules/swift/S036-path-traversal.md +102 -0
  142. package/skill-assets/sunlint-code-quality/rules/swift/S039-tls-certificate-validation.md +109 -0
  143. package/skill-assets/sunlint-code-quality/rules/swift/S041-logout-invalidation.md +103 -0
  144. package/skill-assets/sunlint-code-quality/rules/swift/S043-password-hashing.md +116 -0
  145. package/skill-assets/sunlint-code-quality/rules/swift/S044-critical-changes-reauth.md +145 -0
  146. package/skill-assets/sunlint-code-quality/rules/swift/S045-debug-info-exposure.md +116 -0
  147. package/skill-assets/sunlint-code-quality/rules/swift/S046-unvalidated-redirect.md +140 -0
  148. package/skill-assets/sunlint-code-quality/rules/swift/S051-token-expiry.md +134 -0
  149. package/skill-assets/sunlint-code-quality/rules/swift/S053-jwt-validation.md +139 -0
  150. package/skill-assets/sunlint-code-quality/rules/swift/S059-background-snapshot-protection.md +113 -0
  151. package/skill-assets/sunlint-code-quality/rules/swift/S060-data-protection-api.md +106 -0
  152. package/skill-assets/sunlint-code-quality/rules/swift/S061-jailbreak-detection.md +132 -0
@@ -0,0 +1,86 @@
1
+ ---
2
+ title: Always Use try-with-resources for AutoCloseable
3
+ impact: HIGH
4
+ impactDescription: prevents resource leaks by ensuring streams, connections, and other closeable resources are always closed
5
+ tags: resource-management, best-practice, java, error-prone
6
+ ---
7
+
8
+ ## Always Use try-with-resources for AutoCloseable
9
+
10
+ Since Java 7, the `try`-with-resources statement automatically closes any resource that implements `AutoCloseable` or `Closeable`. Using manual `finally` blocks to close resources is verbose, error-prone, and can silently swallow exceptions thrown during close.
11
+
12
+ **Incorrect (manual finally block):**
13
+
14
+ ```java
15
+ public void readFile(String path) throws IOException {
16
+ InputStream in = null;
17
+ try {
18
+ in = new FileInputStream(path);
19
+ // process stream
20
+ int b = in.read();
21
+ } catch (IOException e) {
22
+ logger.error("Failed to read file", e);
23
+ throw e;
24
+ } finally {
25
+ if (in != null) {
26
+ try {
27
+ in.close(); // exception here swallows the original
28
+ } catch (IOException ignored) {}
29
+ }
30
+ }
31
+ }
32
+
33
+ public void queryDatabase(Connection conn) throws SQLException {
34
+ Statement stmt = null;
35
+ ResultSet rs = null;
36
+ try {
37
+ stmt = conn.createStatement();
38
+ rs = stmt.executeQuery("SELECT * FROM users");
39
+ } finally {
40
+ if (rs != null) rs.close(); // may throw, hiding original
41
+ if (stmt != null) stmt.close();
42
+ }
43
+ }
44
+ ```
45
+
46
+ **Correct (try-with-resources):**
47
+
48
+ ```java
49
+ public void readFile(String path) throws IOException {
50
+ try (InputStream in = new FileInputStream(path)) {
51
+ // process stream
52
+ int b = in.read();
53
+ }
54
+ // in is automatically closed even if an exception occurs
55
+ }
56
+
57
+ public void queryDatabase(Connection conn) throws SQLException {
58
+ try (Statement stmt = conn.createStatement();
59
+ ResultSet rs = stmt.executeQuery("SELECT * FROM users")) {
60
+ while (rs.next()) {
61
+ // process results
62
+ }
63
+ }
64
+ // both stmt and rs are closed in reverse declaration order
65
+ }
66
+
67
+ // Also works with custom AutoCloseable resources
68
+ public void processResource() throws Exception {
69
+ try (MyService service = new MyService()) {
70
+ service.execute();
71
+ }
72
+ }
73
+ ```
74
+
75
+ **Key benefits:**
76
+ - Resources are closed in reverse declaration order, automatically.
77
+ - Original exceptions are never swallowed by close failures (suppressed exceptions).
78
+ - Cleaner, less boilerplate code.
79
+
80
+ **Resources that must use try-with-resources:**
81
+ - `InputStream` / `OutputStream` / `Reader` / `Writer`
82
+ - `Connection` / `Statement` / `ResultSet` (JDBC)
83
+ - `HttpClient` / `Socket`
84
+ - Any class implementing `AutoCloseable`
85
+
86
+ **Tools:** PMD (`UseTryWithResources`), SonarQube (`S2093`), IntelliJ Inspections
@@ -0,0 +1,88 @@
1
+ ---
2
+ title: Override equals() and hashCode() Together
3
+ impact: HIGH
4
+ impactDescription: violating the equals-hashCode contract breaks HashMap, HashSet, and other hash-based collections silently
5
+ tags: correctness, contract, java, error-prone
6
+ ---
7
+
8
+ ## Override equals() and hashCode() Together
9
+
10
+ In Java, `equals()` and `hashCode()` share a contract: objects that are equal (via `equals()`) **must** have the same hash code. If you override one without overriding the other, hash-based collections (`HashMap`, `HashSet`, `Hashtable`) will malfunction — objects may become unreachable or duplicates may appear.
11
+
12
+ **Incorrect (only overrides equals):**
13
+
14
+ ```java
15
+ public class User {
16
+ private Long id;
17
+ private String email;
18
+
19
+ @Override
20
+ public boolean equals(Object o) {
21
+ if (this == o) return true;
22
+ if (!(o instanceof User)) return false;
23
+ User user = (User) o;
24
+ return Objects.equals(id, user.id);
25
+ }
26
+ // Missing hashCode! — HashSet will treat two equal Users as different objects
27
+ }
28
+
29
+ // Result: map.put(user1); map.get(user2) returns null even if user1.equals(user2)
30
+ Set<User> set = new HashSet<>();
31
+ set.add(new User(1L, "a@b.com"));
32
+ set.contains(new User(1L, "a@b.com")); // false! Bug!
33
+ ```
34
+
35
+ **Incorrect (only overrides hashCode):**
36
+
37
+ ```java
38
+ public class Order {
39
+ private Long id;
40
+
41
+ @Override
42
+ public int hashCode() {
43
+ return Objects.hash(id);
44
+ }
45
+ // Missing equals! — equals uses identity (==) by default
46
+ }
47
+ ```
48
+
49
+ **Correct (both overridden consistently):**
50
+
51
+ ```java
52
+ public class User {
53
+ private Long id;
54
+ private String email;
55
+
56
+ @Override
57
+ public boolean equals(Object o) {
58
+ if (this == o) return true;
59
+ if (!(o instanceof User)) return false;
60
+ User user = (User) o;
61
+ return Objects.equals(id, user.id);
62
+ }
63
+
64
+ @Override
65
+ public int hashCode() {
66
+ return Objects.hash(id); // same fields as equals
67
+ }
68
+ }
69
+
70
+ // Using records (Java 16+): equals and hashCode are auto-generated
71
+ public record User(Long id, String email) {}
72
+
73
+ // Using Lombok:
74
+ @EqualsAndHashCode(onlyExplicitlyIncluded = true)
75
+ public class User {
76
+ @EqualsAndHashCode.Include
77
+ private Long id;
78
+ private String email;
79
+ }
80
+ ```
81
+
82
+ **Rules to follow:**
83
+ - Use the **same fields** in both `equals()` and `hashCode()`.
84
+ - Prefer `Objects.equals()` and `Objects.hash()` over manual null checks.
85
+ - Consider using Lombok `@EqualsAndHashCode` or Java Records for value objects.
86
+ - Never include mutable fields in `hashCode()` if the object will be stored in a hash collection.
87
+
88
+ **Tools:** PMD (`OverrideBothEqualsAndHashcode`), Checkstyle (`EqualsHashCode`), IntelliJ Inspections
@@ -0,0 +1,72 @@
1
+ ---
2
+ title: Use equals() for String and Object Comparison, Not ==
3
+ impact: HIGH
4
+ impactDescription: using == compares references, not values, causing silent bugs that only appear at runtime
5
+ tags: correctness, java, error-prone, string
6
+ ---
7
+
8
+ ## Use equals() for String and Object Comparison, Not ==
9
+
10
+ In Java, `==` checks **reference equality** (are these the same object in memory?). For Strings and other objects, you almost always want **value equality** — use `.equals()`. While string literals may be interned (sharing references), strings from variables, method returns, or `new String(...)` will have different references, making `==` unreliable.
11
+
12
+ **Incorrect (using == for string comparison):**
13
+
14
+ ```java
15
+ String status = getStatusFromDatabase(); // returns "ACTIVE"
16
+ if (status == "ACTIVE") { // BUG: may be false even when value is "ACTIVE"
17
+ activateUser();
18
+ }
19
+
20
+ String s1 = new String("hello");
21
+ String s2 = new String("hello");
22
+ if (s1 == s2) { // always false — different objects
23
+ // never executed
24
+ }
25
+
26
+ public boolean isAdmin(String role) {
27
+ return role == "ADMIN"; // unreliable
28
+ }
29
+ ```
30
+
31
+ **Correct (using equals for string comparison):**
32
+
33
+ ```java
34
+ String status = getStatusFromDatabase();
35
+ if ("ACTIVE".equals(status)) { // null-safe: won't NPE if status is null
36
+ activateUser();
37
+ }
38
+
39
+ // Or with Objects.equals for null-safety on both sides:
40
+ if (Objects.equals(status, "ACTIVE")) {
41
+ activateUser();
42
+ }
43
+
44
+ public boolean isAdmin(String role) {
45
+ return "ADMIN".equals(role); // preferred: literal first to avoid NPE
46
+ // or: return role != null && role.equals("ADMIN");
47
+ }
48
+
49
+ // For enums: use == (enums are singletons, == is correct and preferred)
50
+ if (status == Status.ACTIVE) { // correct for enums
51
+ activateUser();
52
+ }
53
+ ```
54
+
55
+ **Literal-first pattern:**
56
+
57
+ Placing the literal on the left side of `equals()` is a common defensive pattern — if the variable is `null`, the call returns `false` instead of throwing `NullPointerException`:
58
+
59
+ ```java
60
+ // Risky: may throw NPE if name is null
61
+ if (name.equals("John")) { ... }
62
+
63
+ // Safe: returns false if name is null
64
+ if ("John".equals(name)) { ... }
65
+ ```
66
+
67
+ **Key distinctions:**
68
+ - `String` and all objects: use `.equals()` or `Objects.equals()`
69
+ - `enum` types: use `==` (enums are singleton instances)
70
+ - Primitives (`int`, `boolean`, etc.): use `==`
71
+
72
+ **Tools:** PMD (`CompareObjectsWithEquals`, `UseEqualsToCompareStrings`), FindBugs/SpotBugs (`ES_COMPARING_STRINGS_WITH_EQ`), IntelliJ Inspections
@@ -0,0 +1,91 @@
1
+ ---
2
+ title: Use java.time Instead of Legacy Date and Calendar
3
+ impact: MEDIUM
4
+ impactDescription: java.util.Date and Calendar are mutable, thread-unsafe, and poorly designed; java.time provides a modern, immutable, thread-safe API
5
+ tags: best-practice, java, modernization, thread-safety
6
+ ---
7
+
8
+ ## Use java.time Instead of Legacy Date and Calendar
9
+
10
+ `java.util.Date` and `java.util.Calendar` were introduced in Java 1.0 and 1.1 respectively. They are mutable (not thread-safe), have confusing APIs (months are 0-indexed in `Calendar`), and are generally considered a design failure. Since Java 8, the `java.time` package (JSR-310) provides a comprehensive, immutable, thread-safe date/time API.
11
+
12
+ **Incorrect (using legacy Date/Calendar):**
13
+
14
+ ```java
15
+ import java.util.Date;
16
+ import java.util.Calendar;
17
+
18
+ public class OrderService {
19
+ public Date calculateDeliveryDate(Date orderDate, int days) {
20
+ Calendar cal = Calendar.getInstance();
21
+ cal.setTime(orderDate);
22
+ cal.add(Calendar.DAY_OF_MONTH, days); // Calendar.DAY_OF_MONTH is error-prone
23
+ return cal.getTime(); // mutable — caller can modify the returned value
24
+ }
25
+
26
+ public boolean isExpired(Date expiryDate) {
27
+ return new Date().after(expiryDate); // new Date() for "now" is verbose
28
+ }
29
+
30
+ // Storing timestamps
31
+ private Date createdAt = new Date(); // mutable — bad for records
32
+ }
33
+ ```
34
+
35
+ **Correct (using java.time):**
36
+
37
+ ```java
38
+ import java.time.LocalDate;
39
+ import java.time.LocalDateTime;
40
+ import java.time.ZonedDateTime;
41
+ import java.time.ZoneId;
42
+ import java.time.Instant;
43
+ import java.time.Duration;
44
+
45
+ public class OrderService {
46
+ public LocalDate calculateDeliveryDate(LocalDate orderDate, int days) {
47
+ return orderDate.plusDays(days); // fluent, immutable, clear
48
+ }
49
+
50
+ public boolean isExpired(LocalDateTime expiryDateTime) {
51
+ return LocalDateTime.now().isAfter(expiryDateTime);
52
+ }
53
+
54
+ // Storing timestamps
55
+ private final Instant createdAt = Instant.now(); // immutable
56
+
57
+ // Working with timezones
58
+ public ZonedDateTime getOrderTimeInJST(Instant instant) {
59
+ return instant.atZone(ZoneId.of("Asia/Tokyo"));
60
+ }
61
+
62
+ // Duration/period calculation
63
+ public long getDaysUntilDeadline(LocalDate deadline) {
64
+ return Duration.between(
65
+ LocalDate.now().atStartOfDay(),
66
+ deadline.atStartOfDay()
67
+ ).toDays();
68
+ }
69
+ }
70
+ ```
71
+
72
+ **Migration guide:**
73
+
74
+ | Legacy | java.time replacement |
75
+ |--------|----------------------|
76
+ | `java.util.Date` | `java.time.Instant` (for UTC timestamps) |
77
+ | `java.util.Calendar` | `java.time.ZonedDateTime` |
78
+ | `java.sql.Date` | `java.time.LocalDate` |
79
+ | `java.sql.Timestamp` | `java.time.LocalDateTime` or `Instant` |
80
+ | `SimpleDateFormat` | `java.time.format.DateTimeFormatter` |
81
+
82
+ **Key types in java.time:**
83
+ - `LocalDate` — date without time/timezone (e.g., 2024-01-15)
84
+ - `LocalTime` — time without date/timezone
85
+ - `LocalDateTime` — date + time, no timezone
86
+ - `ZonedDateTime` — date + time + timezone
87
+ - `Instant` — a Unix timestamp (UTC)
88
+ - `Duration` — time-based amount (seconds, nanoseconds)
89
+ - `Period` — date-based amount (years, months, days)
90
+
91
+ **Tools:** PMD (`ReplaceJavaUtilDate`, `ReplaceJavaUtilCalendar`), SonarQube (`S2143`)
@@ -0,0 +1,80 @@
1
+ ---
2
+ title: Do Not Use printStackTrace(); Use a Logger
3
+ impact: MEDIUM
4
+ impactDescription: printStackTrace() outputs to stderr, cannot be controlled by log configuration, and may expose sensitive stack traces in production
5
+ tags: logging, best-practice, java, security
6
+ ---
7
+
8
+ ## Do Not Use printStackTrace(); Use a Logger
9
+
10
+ `e.printStackTrace()` writes directly to `System.err` — it bypasses the logging framework entirely, cannot be filtered, redirected, or formatted by log configuration, and in production environments the output may be lost or may expose sensitive internal details (class names, method names, library versions) to attackers.
11
+
12
+ **Incorrect (using printStackTrace):**
13
+
14
+ ```java
15
+ public void processPayment(Payment payment) {
16
+ try {
17
+ paymentGateway.charge(payment);
18
+ } catch (PaymentException e) {
19
+ e.printStackTrace(); // writes to System.err — uncontrolled output
20
+ }
21
+ }
22
+
23
+ public User findUser(Long id) {
24
+ try {
25
+ return userRepository.findById(id).orElseThrow();
26
+ } catch (Exception e) {
27
+ e.printStackTrace(); // loses structured logging, no context
28
+ return null;
29
+ }
30
+ }
31
+ ```
32
+
33
+ **Correct (using a logger):**
34
+
35
+ ```java
36
+ import org.slf4j.Logger;
37
+ import org.slf4j.LoggerFactory;
38
+
39
+ public class PaymentService {
40
+ private static final Logger logger = LoggerFactory.getLogger(PaymentService.class);
41
+
42
+ public void processPayment(Payment payment) {
43
+ try {
44
+ paymentGateway.charge(payment);
45
+ } catch (PaymentException e) {
46
+ // Log at ERROR with context AND the exception (includes stack trace in log output)
47
+ logger.error("Payment failed for orderId={}, amount={}",
48
+ payment.getOrderId(), payment.getAmount(), e);
49
+ throw new PaymentProcessingException("Payment failed", e);
50
+ }
51
+ }
52
+
53
+ public User findUser(Long id) {
54
+ try {
55
+ return userRepository.findById(id)
56
+ .orElseThrow(() -> new UserNotFoundException("User not found: " + id));
57
+ } catch (UserNotFoundException e) {
58
+ logger.warn("User lookup failed: userId={}", id, e);
59
+ throw e;
60
+ }
61
+ }
62
+ }
63
+ ```
64
+
65
+ **Differences between logging levels for exceptions:**
66
+
67
+ ```java
68
+ // ERROR: unexpected, system-level failure, requires immediate attention
69
+ logger.error("Database connection lost", e);
70
+
71
+ // WARN: recoverable error, degraded functionality
72
+ logger.warn("Payment gateway timeout, retrying: orderId={}", orderId, e);
73
+
74
+ // INFO: expected business exception (user not found, validation fail)
75
+ logger.info("Login attempt failed: username={}", username, e);
76
+ ```
77
+
78
+ **Passing the exception as the last argument** to SLF4J/Logback/Log4j2 automatically appends a full stack trace to the log entry — equivalent to calling `printStackTrace()` but through the proper logging pipeline.
79
+
80
+ **Tools:** PMD (`AvoidPrintStackTrace`), SonarQube (`S1148`), SpotBugs
@@ -0,0 +1,89 @@
1
+ ---
2
+ title: Do Not Use System.out.println in Production Code
3
+ impact: MEDIUM
4
+ impactDescription: System.out/err output bypasses logging configuration, cannot be disabled without code changes, and may leak sensitive data
5
+ tags: logging, best-practice, java, production
6
+ ---
7
+
8
+ ## Do Not Use System.out.println in Production Code
9
+
10
+ `System.out.println` and `System.err.println` write directly to standard output/error streams. In production:
11
+ - They cannot be filtered, leveled, or structured.
12
+ - They cannot be turned off without changing code and redeploying.
13
+ - They do not carry context (timestamp, thread, class name) unless added manually.
14
+ - Output may be lost in containerized or cloud environments.
15
+ - They may print sensitive data (credentials, PII) in server logs.
16
+
17
+ **Incorrect:**
18
+
19
+ ```java
20
+ public class AuthService {
21
+ public User authenticate(String username, String password) {
22
+ System.out.println("Authenticating user: " + username); // logs PII
23
+ System.out.println("password = " + password); // SECURITY RISK
24
+ try {
25
+ User user = userRepository.findByUsername(username);
26
+ System.out.println("User found: " + user.getEmail()); // PII leak
27
+ return user;
28
+ } catch (Exception e) {
29
+ System.err.println("Auth failed: " + e.getMessage()); // unstructured
30
+ return null;
31
+ }
32
+ }
33
+ }
34
+
35
+ public class DataProcessor {
36
+ public void process(List<Order> orders) {
37
+ System.out.println("Processing " + orders.size() + " orders"); // no structured log
38
+ for (Order order : orders) {
39
+ System.out.println("Order: " + order); // cannot be filtered
40
+ }
41
+ }
42
+ }
43
+ ```
44
+
45
+ **Correct (using SLF4J/Logback):**
46
+
47
+ ```java
48
+ import org.slf4j.Logger;
49
+ import org.slf4j.LoggerFactory;
50
+
51
+ public class AuthService {
52
+ private static final Logger logger = LoggerFactory.getLogger(AuthService.class);
53
+
54
+ public User authenticate(String username, String password) {
55
+ logger.debug("Authenticating user: username={}", username);
56
+ // Never log the password
57
+ try {
58
+ User user = userRepository.findByUsername(username);
59
+ logger.info("Authentication successful: userId={}", user.getId());
60
+ return user;
61
+ } catch (UserNotFoundException e) {
62
+ logger.warn("Authentication failed: username={}", username);
63
+ throw e;
64
+ }
65
+ }
66
+ }
67
+
68
+ public class DataProcessor {
69
+ private static final Logger logger = LoggerFactory.getLogger(DataProcessor.class);
70
+
71
+ public void process(List<Order> orders) {
72
+ logger.info("Starting order processing: count={}", orders.size());
73
+ for (Order order : orders) {
74
+ logger.debug("Processing order: orderId={}", order.getId());
75
+ }
76
+ logger.info("Order processing complete: count={}", orders.size());
77
+ }
78
+ }
79
+ ```
80
+
81
+ **Exception for tests and scripts:**
82
+
83
+ `System.out.println` is acceptable only in:
84
+ - `main()` methods of CLI tools and standalone scripts
85
+ - Unit test setup code (though test frameworks' reporters are better)
86
+
87
+ Mark these with a `@SuppressWarnings("PMD.SystemPrintln")` annotation if needed.
88
+
89
+ **Tools:** PMD (`SystemPrintln`), SonarQube (`S106`), Checkstyle
@@ -0,0 +1,91 @@
1
+ ---
2
+ title: Logger Must Be private static final
3
+ impact: MEDIUM
4
+ impactDescription: improper logger declaration can cause memory leaks, serialization issues, and incorrect class attribution in log output
5
+ tags: logging, best-practice, java, naming
6
+ ---
7
+
8
+ ## Logger Must Be private static final
9
+
10
+ A logger field should always be declared as `private static final` and named after the class it belongs to. This ensures:
11
+ - **`private`**: Not accessible from outside the class, encapsulated properly.
12
+ - **`static`**: Shared across all instances — not re-created per object, avoiding memory overhead.
13
+ - **`final`**: Cannot be reassigned — prevents accidental reassignment.
14
+ - **Named with the correct class**: Ensures log entries display the correct source class.
15
+
16
+ **Incorrect:**
17
+
18
+ ```java
19
+ // Not static — a new logger is created for every object instance
20
+ public class OrderService {
21
+ private final Logger logger = LoggerFactory.getLogger(OrderService.class); // bad: not static
22
+ }
23
+
24
+ // Not private — accessible from outside
25
+ public class UserService {
26
+ public static final Logger LOGGER = LoggerFactory.getLogger(UserService.class); // bad: public
27
+ }
28
+
29
+ // Wrong class name — log entries will show wrong class
30
+ public class PaymentService {
31
+ private static final Logger logger = LoggerFactory.getLogger(OrderService.class); // bad: wrong class
32
+ }
33
+
34
+ // Using java.util.logging without naming it LOG or logger consistently
35
+ public class ReportService {
36
+ private static Logger l = Logger.getLogger("reports"); // bad: name "l" is vague; string name is wrong
37
+ }
38
+ ```
39
+
40
+ **Correct:**
41
+
42
+ ```java
43
+ import org.slf4j.Logger;
44
+ import org.slf4j.LoggerFactory;
45
+
46
+ public class OrderService {
47
+ private static final Logger logger = LoggerFactory.getLogger(OrderService.class);
48
+
49
+ public void createOrder(Order order) {
50
+ logger.info("Creating order: orderId={}", order.getId());
51
+ }
52
+ }
53
+
54
+ // For java.util.logging (standard library):
55
+ import java.util.logging.Logger;
56
+
57
+ public class ReportService {
58
+ private static final Logger logger = Logger.getLogger(ReportService.class.getName());
59
+ }
60
+
61
+ // For Log4j 2:
62
+ import org.apache.logging.log4j.Logger;
63
+ import org.apache.logging.log4j.LogManager;
64
+
65
+ public class NotificationService {
66
+ private static final Logger logger = LogManager.getLogger(NotificationService.class);
67
+ }
68
+
69
+ // With Lombok @Slf4j annotation (auto-generates private static final logger):
70
+ import lombok.extern.slf4j.Slf4j;
71
+
72
+ @Slf4j
73
+ public class InventoryService {
74
+ public void checkStock(Long productId) {
75
+ log.info("Checking stock: productId={}", productId); // 'log' is auto-generated
76
+ }
77
+ }
78
+ ```
79
+
80
+ **Standard conventions:**
81
+ - Variable name: `logger` (preferred) or `log` (common with Lombok)
82
+ - Avoid `LOG`, `LOGGER` (all-caps suggests a compile-time constant, loggers are runtime objects)
83
+ - Always pass the owning class literal: `LoggerFactory.getLogger(MyClass.class)`
84
+
85
+ **Frameworks supported:**
86
+ - SLF4J + Logback (recommended)
87
+ - Log4j 2
88
+ - `java.util.logging` (JUL, standard library)
89
+ - Lombok `@Slf4j`, `@Log4j2`, `@CommonsLog`
90
+
91
+ **Tools:** PMD (`ProperLogger`), SonarQube (`S1312`), IntelliJ Inspections