@sun-asterisk/sunlint 1.3.16 → 1.3.17

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 (50) hide show
  1. package/config/rule-analysis-strategies.js +3 -3
  2. package/config/rules/enhanced-rules-registry.json +40 -20
  3. package/core/cli-action-handler.js +2 -2
  4. package/core/config-merger.js +28 -6
  5. package/core/constants/defaults.js +1 -1
  6. package/core/file-targeting-service.js +72 -4
  7. package/core/output-service.js +21 -4
  8. package/engines/heuristic-engine.js +5 -0
  9. package/package.json +1 -1
  10. package/rules/common/C002_no_duplicate_code/README.md +115 -0
  11. package/rules/common/C002_no_duplicate_code/analyzer.js +615 -219
  12. package/rules/common/C002_no_duplicate_code/test-cases/api-handlers.ts +64 -0
  13. package/rules/common/C002_no_duplicate_code/test-cases/data-processor.ts +46 -0
  14. package/rules/common/C002_no_duplicate_code/test-cases/good-example.tsx +40 -0
  15. package/rules/common/C002_no_duplicate_code/test-cases/product-service.ts +57 -0
  16. package/rules/common/C002_no_duplicate_code/test-cases/user-service.ts +49 -0
  17. package/rules/common/C008/analyzer.js +40 -0
  18. package/rules/common/C008/config.json +20 -0
  19. package/rules/common/C008/ts-morph-analyzer.js +1067 -0
  20. package/rules/common/C018_no_throw_generic_error/analyzer.js +1 -1
  21. package/rules/common/C018_no_throw_generic_error/symbol-based-analyzer.js +27 -3
  22. package/rules/common/C024_no_scatter_hardcoded_constants/symbol-based-analyzer.js +504 -162
  23. package/rules/common/C029_catch_block_logging/analyzer.js +499 -89
  24. package/rules/common/C033_separate_service_repository/README.md +131 -20
  25. package/rules/common/C033_separate_service_repository/analyzer.js +1 -1
  26. package/rules/common/C033_separate_service_repository/symbol-based-analyzer.js +417 -274
  27. package/rules/common/C041_no_sensitive_hardcode/analyzer.js +144 -254
  28. package/rules/common/C041_no_sensitive_hardcode/config.json +50 -0
  29. package/rules/common/C041_no_sensitive_hardcode/symbol-based-analyzer.js +575 -0
  30. package/rules/common/C067_no_hardcoded_config/analyzer.js +17 -16
  31. package/rules/common/C067_no_hardcoded_config/symbol-based-analyzer.js +3477 -659
  32. package/rules/docs/C002_no_duplicate_code.md +276 -11
  33. package/rules/index.js +5 -1
  34. package/rules/security/S006_no_plaintext_recovery_codes/analyzer.js +266 -88
  35. package/rules/security/S006_no_plaintext_recovery_codes/symbol-based-analyzer.js +805 -0
  36. package/rules/security/S010_no_insecure_encryption/README.md +78 -0
  37. package/rules/security/S010_no_insecure_encryption/analyzer.js +463 -398
  38. package/rules/security/S013_tls_enforcement/README.md +51 -0
  39. package/rules/security/S013_tls_enforcement/analyzer.js +99 -0
  40. package/rules/security/S013_tls_enforcement/config.json +41 -0
  41. package/rules/security/S013_tls_enforcement/symbol-based-analyzer.js +339 -0
  42. package/rules/security/S014_tls_version_enforcement/README.md +354 -0
  43. package/rules/security/S014_tls_version_enforcement/analyzer.js +118 -0
  44. package/rules/security/S014_tls_version_enforcement/config.json +56 -0
  45. package/rules/security/S014_tls_version_enforcement/symbol-based-analyzer.js +194 -0
  46. package/rules/security/S055_content_type_validation/analyzer.js +121 -279
  47. package/rules/security/S055_content_type_validation/symbol-based-analyzer.js +346 -0
  48. package/rules/tests/C002_no_duplicate_code.test.js +111 -22
  49. package/rules/common/C029_catch_block_logging/analyzer-smart-pipeline.js +0 -755
  50. package/rules/common/C041_no_sensitive_hardcode/ast-analyzer.js +0 -296
@@ -0,0 +1,64 @@
1
+ // ❌ VIOLATION: Monkey coding - Copy-paste error handling
2
+
3
+ export class ApiHandlers {
4
+ // Get user by ID with error handling
5
+ async getUserById(id: number) {
6
+ try {
7
+ const response = await fetch(`/api/users/${id}`);
8
+
9
+ if (!response.ok) {
10
+ const error = await response.json();
11
+ console.error('Error fetching user:', error);
12
+ throw new Error(error.message || 'Failed to fetch user');
13
+ }
14
+
15
+ const data = await response.json();
16
+ return data;
17
+
18
+ } catch (error) {
19
+ console.error('Network error:', error);
20
+ throw new Error('Network request failed');
21
+ }
22
+ }
23
+
24
+ // Get product by ID - SAME error handling!
25
+ async getProductById(id: number) {
26
+ // DUPLICATE: Same try-catch, error handling pattern (monkey coding!)
27
+ try {
28
+ const response = await fetch(`/api/products/${id}`);
29
+
30
+ if (!response.ok) {
31
+ const error = await response.json();
32
+ console.error('Error fetching product:', error);
33
+ throw new Error(error.message || 'Failed to fetch product');
34
+ }
35
+
36
+ const data = await response.json();
37
+ return data;
38
+
39
+ } catch (error) {
40
+ console.error('Network error:', error);
41
+ throw new Error('Network request failed');
42
+ }
43
+ }
44
+
45
+ // Get order by ID - same pattern
46
+ async getOrderById(id: number) {
47
+ try {
48
+ const response = await fetch(`/api/orders/${id}`);
49
+
50
+ if (!response.ok) {
51
+ const error = await response.json();
52
+ console.error('Error fetching order:', error);
53
+ throw new Error(error.message || 'Failed to fetch order');
54
+ }
55
+
56
+ const data = await response.json();
57
+ return data;
58
+
59
+ } catch (error) {
60
+ console.error('Network error:', error);
61
+ throw new Error('Network request failed');
62
+ }
63
+ }
64
+ }
@@ -0,0 +1,46 @@
1
+ // ❌ VIOLATION: Monkey coding - Copy-paste data transformation
2
+
3
+ export class DataProcessor {
4
+ // Process user data from API
5
+ processUserData(rawData: any[]) {
6
+ return rawData
7
+ .filter(item => item !== null && item !== undefined)
8
+ .map(item => ({
9
+ id: item.id,
10
+ name: item.name?.trim() || 'Unknown',
11
+ email: item.email?.toLowerCase() || '',
12
+ status: item.active ? 'active' : 'inactive',
13
+ createdAt: new Date(item.created_at)
14
+ }))
15
+ .sort((a, b) => a.name.localeCompare(b.name));
16
+ }
17
+
18
+ // Process product data from API - SAME transformation logic!
19
+ processProductData(rawData: any[]) {
20
+ // DUPLICATE: Same filtering, mapping, sorting pattern (monkey coding!)
21
+ return rawData
22
+ .filter(item => item !== null && item !== undefined)
23
+ .map(item => ({
24
+ id: item.id,
25
+ name: item.name?.trim() || 'Unknown',
26
+ price: item.price || 0,
27
+ status: item.available ? 'active' : 'inactive',
28
+ createdAt: new Date(item.created_at)
29
+ }))
30
+ .sort((a, b) => a.name.localeCompare(b.name));
31
+ }
32
+
33
+ // Process order data - same pattern again
34
+ processOrderData(rawData: any[]) {
35
+ return rawData
36
+ .filter(item => item !== null && item !== undefined)
37
+ .map(item => ({
38
+ id: item.id,
39
+ customer: item.customer_name?.trim() || 'Unknown',
40
+ total: item.total_amount || 0,
41
+ status: item.paid ? 'active' : 'inactive',
42
+ createdAt: new Date(item.order_date)
43
+ }))
44
+ .sort((a, b) => a.customer.localeCompare(b.customer));
45
+ }
46
+ }
@@ -0,0 +1,40 @@
1
+ // ✅ GOOD: No violation - These are intentional patterns (different names, simple wrappers)
2
+
3
+ import React from 'react';
4
+
5
+ // Different button variants - intentional pattern, not duplicate
6
+ export function PrimaryButton({ className, ...props }: React.ComponentProps<'button'>) {
7
+ return (
8
+ <button
9
+ className={`btn-primary ${className}`}
10
+ {...props}
11
+ />
12
+ );
13
+ }
14
+
15
+ export function SecondaryButton({ className, ...props }: React.ComponentProps<'button'>) {
16
+ return (
17
+ <button
18
+ className={`btn-secondary ${className}`}
19
+ {...props}
20
+ />
21
+ );
22
+ }
23
+
24
+ export function DangerButton({ className, ...props }: React.ComponentProps<'button'>) {
25
+ return (
26
+ <button
27
+ className={`btn-danger ${className}`}
28
+ {...props}
29
+ />
30
+ );
31
+ }
32
+
33
+ // Short functions - below minimum lines
34
+ export function add(a: number, b: number) {
35
+ return a + b;
36
+ }
37
+
38
+ export function subtract(a: number, b: number) {
39
+ return a - b;
40
+ }
@@ -0,0 +1,57 @@
1
+ // ❌ VIOLATION: Monkey coding - Copy-paste calculation logic
2
+
3
+ export class ProductService {
4
+ // Calculate price with tax for online orders
5
+ calculateOnlinePrice(price: number, quantity: number) {
6
+ const subtotal = price * quantity;
7
+ const tax = subtotal * 0.1; // 10% tax
8
+ const shipping = subtotal > 100 ? 0 : 10; // Free shipping over $100
9
+ const discount = subtotal > 200 ? subtotal * 0.05 : 0; // 5% discount over $200
10
+ const total = subtotal + tax + shipping - discount;
11
+
12
+ return {
13
+ subtotal,
14
+ tax,
15
+ shipping,
16
+ discount,
17
+ total
18
+ };
19
+ }
20
+
21
+ // Calculate price with tax for store orders - SAME calculation!
22
+ calculateStorePrice(price: number, quantity: number) {
23
+ // DUPLICATE: Exactly same calculation logic (monkey coding!)
24
+ const subtotal = price * quantity;
25
+ const tax = subtotal * 0.1; // 10% tax
26
+ const shipping = subtotal > 100 ? 0 : 10; // Free shipping over $100
27
+ const discount = subtotal > 200 ? subtotal * 0.05 : 0; // 5% discount over $200
28
+ const total = subtotal + tax + shipping - discount;
29
+
30
+ return {
31
+ subtotal,
32
+ tax,
33
+ shipping,
34
+ discount,
35
+ total
36
+ };
37
+ }
38
+
39
+ // Wholesale pricing - slightly different but still copy-paste
40
+ calculateWholesalePrice(price: number, quantity: number) {
41
+ const subtotal = price * quantity;
42
+ const tax = subtotal * 0.1; // 10% tax
43
+ const shipping = subtotal > 100 ? 0 : 10; // Free shipping over $100
44
+ const discount = subtotal > 200 ? subtotal * 0.05 : 0; // 5% discount over $200
45
+ const wholesaleDiscount = quantity > 50 ? subtotal * 0.1 : 0; // Extra 10% for bulk
46
+ const total = subtotal + tax + shipping - discount - wholesaleDiscount;
47
+
48
+ return {
49
+ subtotal,
50
+ tax,
51
+ shipping,
52
+ discount,
53
+ wholesaleDiscount,
54
+ total
55
+ };
56
+ }
57
+ }
@@ -0,0 +1,49 @@
1
+ // ❌ VIOLATION: Monkey coding - Copy-paste entire functions
2
+
3
+ export class UserService {
4
+ // Function 1: Process user payment
5
+ async processUserPayment(userId: number, amount: number) {
6
+ const user = await this.getUserData(userId);
7
+
8
+ if (amount <= 0) {
9
+ throw new Error('Invalid payment amount');
10
+ }
11
+
12
+ const fee = amount * 0.029 + 0.30; // Stripe fee
13
+ const total = amount + fee;
14
+
15
+ const result = await this.chargeCard(user.cardId, total);
16
+
17
+ if (!result.success) {
18
+ throw new Error('Payment failed');
19
+ }
20
+
21
+ await this.recordTransaction(userId, total, 'payment');
22
+ return { success: true, transactionId: result.id };
23
+ }
24
+
25
+ // Function 2: Process user refund - EXACT COPY-PASTE!
26
+ async processUserRefund(userId: number, amount: number) {
27
+ const user = await this.getUserData(userId);
28
+
29
+ if (amount <= 0) {
30
+ throw new Error('Invalid payment amount');
31
+ }
32
+
33
+ const fee = amount * 0.029 + 0.30; // Stripe fee
34
+ const total = amount + fee;
35
+
36
+ const result = await this.chargeCard(user.cardId, total);
37
+
38
+ if (!result.success) {
39
+ throw new Error('Payment failed');
40
+ }
41
+
42
+ await this.recordTransaction(userId, total, 'payment');
43
+ return { success: true, transactionId: result.id };
44
+ }
45
+
46
+ private async getUserData(userId: number) { return {}; }
47
+ private async chargeCard(cardId: string, amount: number) { return { success: true, id: '123' }; }
48
+ private async recordTransaction(userId: number, amount: number, type: string) { }
49
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * C008 Analyzer - Minimize Variable Scope (Declare Near Usage)
3
+ *
4
+ * Uses ts-morph (AST-based) for 100% accurate analysis with intelligent filtering:
5
+ * - Module-level constants (UPPER_CASE = value)
6
+ * - Redux Toolkit patterns (createAsyncThunk, createSlice)
7
+ * - Storybook meta exports
8
+ * - React hooks (useState, useEffect, etc.)
9
+ * - CSS-in-JS (keyframes, styled, css)
10
+ *
11
+ * Following Rule C005: Single responsibility - variable scope detection
12
+ * Following Rule C006: Verb-noun naming
13
+ */
14
+
15
+ const C008TsMorphAnalyzer = require('./ts-morph-analyzer');
16
+
17
+ class C008Analyzer {
18
+ constructor(semanticEngine = null, options = {}) {
19
+ this.analyzer = new C008TsMorphAnalyzer(semanticEngine, options);
20
+ this.initialized = false;
21
+ }
22
+
23
+ async initialize(semanticEngine = null) {
24
+ if (!this.initialized) {
25
+ await this.analyzer.initialize(semanticEngine);
26
+ this.initialized = true;
27
+ }
28
+ }
29
+
30
+ async analyze(files, language, options = {}) {
31
+ // Ensure initialized before analysis
32
+ if (!this.initialized) {
33
+ await this.initialize();
34
+ }
35
+ return this.analyzer.analyze(files, language, options);
36
+ }
37
+ }
38
+
39
+ module.exports = C008Analyzer;
40
+
@@ -0,0 +1,20 @@
1
+ {
2
+ "ruleId": "C008",
3
+ "name": "Minimize Variable Scope - Declare Near Usage",
4
+ "description": "Variables should be declared as close as possible to where they are first used to improve code locality and reduce cognitive load",
5
+ "category": "code-quality",
6
+ "severity": "warning",
7
+ "languages": ["typescript", "javascript"],
8
+ "enabled": true,
9
+ "metadata": {
10
+ "tags": ["readability", "maintainability", "best-practice"],
11
+ "principleId": "CODE_QUALITY",
12
+ "version": "1.0.0",
13
+ "status": "active"
14
+ },
15
+ "options": {
16
+ "maxLineDistance": 10,
17
+ "allowTopOfBlock": true,
18
+ "ignoreConst": false
19
+ }
20
+ }