@sun-asterisk/sunlint 1.3.30 → 1.3.32

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.
@@ -0,0 +1,3853 @@
1
+ // rules/common/C067_no_hardcoded_config/symbol-based-analyzer.js
2
+ const { SyntaxKind, Project, Node } = require("ts-morph");
3
+
4
+ class C067SymbolBasedAnalyzer {
5
+ constructor(semanticEngine = null) {
6
+ this.semanticEngine = semanticEngine;
7
+ this.verbose = false;
8
+
9
+ // Common UI/framework strings that should be excluded
10
+ this.UI_STRINGS = [
11
+ "checkbox",
12
+ "button",
13
+ "search",
14
+ "remove",
15
+ "submit",
16
+ "cancel",
17
+ "ok",
18
+ "close",
19
+ "Authorization",
20
+ "User-Agent",
21
+ "Content-Type",
22
+ "Accept",
23
+ "Bearer",
24
+ "ArrowDown",
25
+ "ArrowUp",
26
+ "ArrowLeft",
27
+ "ArrowRight",
28
+ "bottom",
29
+ "top",
30
+ "left",
31
+ "right",
32
+ "next-auth/react",
33
+ "@nestjs/swagger",
34
+ "@nestjs/common",
35
+ "nestjs-pino",
36
+ ];
37
+
38
+ // Test-related strings to exclude
39
+ this.TEST_PATTERNS = [
40
+ /^(test|mock|example|dummy|placeholder|fixture|stub)/i,
41
+ /^(User \d+|Test User|Admin User)/i,
42
+ /^(group\d+|item\d+|element\d+)/i,
43
+ /^(abcdef\d+|123456|test-\w+)/i,
44
+ ];
45
+
46
+ // Configuration patterns to detect - based on Rule C067 requirements
47
+ this.configPatterns = {
48
+ // API URLs and endpoints - external URLs that differ by environment
49
+ urls: {
50
+ regex:
51
+ /^https?:\/\/(?!localhost|127\.0\.0\.1|0\.0\.0\.0)([a-zA-Z0-9-]+\.[a-zA-Z]{2,}|[^\/\s]+\.[^\/\s]+)(\/[^\s]*)?$/,
52
+ exclude: [
53
+ /^https?:\/\/(localhost|127\.0\.0\.1|0\.0\.0\.0)(:\d+)?/, // Local development
54
+ /^https?:\/\/(example\.com|test\.com|dummy\.com)/, // Test domains
55
+ /^(http|https):\/\/\$\{.+\}/, // Template URLs with variables
56
+ ],
57
+ },
58
+
59
+ // Timeouts, retry intervals, batch sizes - environment-dependent numeric values
60
+ environmentNumbers: {
61
+ isEnvironmentDependent: (value, context) => {
62
+ const lowerContext = context.toLowerCase();
63
+
64
+ // Business logic numbers are NOT environment config
65
+ const businessLogicPatterns = [
66
+ /limit|max|min|size|count|length|threshold/i,
67
+ /page|record|item|batch|chunk|export/i,
68
+ /width|height|margin|padding/i,
69
+ /attempt|retry|step/i,
70
+ ];
71
+
72
+ if (businessLogicPatterns.some((pattern) => pattern.test(context))) {
73
+ return false;
74
+ }
75
+
76
+ // Skip common business constants
77
+ const businessConstants = [
78
+ 20000,
79
+ 10000,
80
+ 5000,
81
+ 1000,
82
+ 500,
83
+ 100,
84
+ 50,
85
+ 20,
86
+ 10,
87
+ 5, // Common limits
88
+ 404,
89
+ 500,
90
+ 200,
91
+ 201,
92
+ 400,
93
+ 401,
94
+ 403, // HTTP status codes
95
+ 24,
96
+ 60,
97
+ 3600,
98
+ 86400, // Time constants (hours, minutes, seconds)
99
+ 1,
100
+ 2,
101
+ 3,
102
+ 4,
103
+ 5,
104
+ 6,
105
+ 7,
106
+ 8,
107
+ 9,
108
+ 10, // Simple counters
109
+ ];
110
+
111
+ if (businessConstants.includes(value)) {
112
+ return false;
113
+ }
114
+
115
+ // Timeouts and intervals (values > 1000ms that might differ by environment)
116
+ if (typeof value === "number" && value > 1000) {
117
+ return /timeout|interval|delay|duration|retry|batch/i.test(context);
118
+ }
119
+
120
+ // Port numbers (except common development ports)
121
+ if (typeof value === "number" && value > 1000 && value < 65536) {
122
+ const commonDevPorts = [3000, 8000, 8080, 9000, 5000, 4200, 4000];
123
+ if (!commonDevPorts.includes(value)) {
124
+ return /port|listen|bind|server/i.test(context);
125
+ }
126
+ }
127
+
128
+ return false;
129
+ },
130
+ },
131
+
132
+ // Database and connection strings
133
+ connections: {
134
+ regex: /^(mongodb|mysql|postgres|redis|elasticsearch):\/\/|^jdbc:|^Server=|^Data Source=/i,
135
+ },
136
+
137
+ // Credentials - API keys, passwords, tokens
138
+ credentials: {
139
+ keywords: [
140
+ "apikey",
141
+ "api_key",
142
+ "secret_key",
143
+ "access_token",
144
+ "client_secret",
145
+ "password",
146
+ "token",
147
+ "key",
148
+ ],
149
+ exclude: [
150
+ /must contain|should contain|invalid|error|message/i, // Validation messages
151
+ /description|comment|note/i, // Descriptions
152
+ /^[a-z\s]{10,}$/i, // Long descriptive text
153
+ ],
154
+ },
155
+
156
+ // Feature flags and toggles
157
+ featureFlags: {
158
+ keywords: ["feature", "flag", "toggle", "enable", "disable", "enabled", "disabled"],
159
+ patterns: [
160
+ /^(enable|disable)[A-Z]/, // enableFeature, disableLogging
161
+ /[A-Z][a-z]+(Flag|Toggle|Enabled|Disabled)$/, // newUIFlag, debugEnabled
162
+ /^FEATURE_[A-Z_]+$/, // FEATURE_NEW_UI
163
+ /^(is|has)[A-Z][a-z]+Enabled$/, // isDebugEnabled
164
+ ],
165
+ },
166
+
167
+ // Thresholds and limits that might vary by environment
168
+ thresholds: {
169
+ keywords: ["threshold", "limit", "max", "min"],
170
+ contextPatterns: [
171
+ /memory|cpu|disk|storage/i, // Resource thresholds
172
+ /rate|request|connection/i, // Rate limiting
173
+ /pool|queue|buffer/i, // Resource pools
174
+ ],
175
+ },
176
+
177
+ // Security & Authentication - Phase 1 extension
178
+ security: {
179
+ corsOrigins: {
180
+ keywords: ["cors", "origin", "allowed"],
181
+ patterns: [
182
+ /^https?:\/\/[^\/]+$/, // URLs without paths
183
+ /\.(com|org|net|dev|staging|prod)$/i,
184
+ ],
185
+ },
186
+ sessionConfig: {
187
+ keywords: ["session", "jwt", "token", "auth", "expiry", "expire"],
188
+ timePatterns: [
189
+ /^\d+[smhd]$/, // 24h, 30m, 60s, 7d
190
+ /^\d{3,}$/, // Large numbers (seconds)
191
+ ],
192
+ },
193
+ },
194
+
195
+ // Infrastructure Config - Phase 1 extension
196
+ infrastructure: {
197
+ caching: {
198
+ keywords: ["cache", "ttl", "expire", "redis", "prefix"],
199
+ patterns: [
200
+ /^[a-zA-Z]+:[a-zA-Z]+:/, // Prefixes like "myapp:prod:"
201
+ /^\d{3,}$/, // TTL values in seconds
202
+ ],
203
+ },
204
+ logging: {
205
+ keywords: ["log", "level"],
206
+ levels: ["trace", "debug", "info", "warn", "error", "fatal"],
207
+ },
208
+ performance: {
209
+ keywords: ["worker", "thread", "concurrency", "queue", "upload", "download"],
210
+ contextPatterns: [
211
+ /worker|thread|process/i,
212
+ /concurrency|parallel|queue/i,
213
+ /upload|download|file.*size/i,
214
+ ],
215
+ },
216
+ },
217
+
218
+ // Environment-specific patterns
219
+ environments: {
220
+ names: ["production", "prod", "staging", "stage", "development", "dev", "test"],
221
+ patterns: [
222
+ /^(production|prod|staging|stage|development|dev|test)$/i,
223
+ /\.(prod|staging|dev)\./, // domain patterns
224
+ /_(prod|staging|dev)_/i, // variable patterns
225
+ ],
226
+ },
227
+
228
+ // Service dependencies
229
+ services: {
230
+ keywords: ["service", "endpoint", "host", "port"],
231
+ patterns: [
232
+ /^https?:\/\/[a-zA-Z-]+-service/, // microservice URLs
233
+ /:[0-9]{4,5}$/, // Port numbers
234
+ /service.*url|url.*service/i,
235
+ ],
236
+ },
237
+
238
+ // ============ Phase 2: Critical Configuration Patterns ============
239
+
240
+ // Database & Storage Configuration
241
+ database: {
242
+ poolConfig: {
243
+ keywords: ["pool", "connection", "max", "min", "idle"],
244
+ patterns: [
245
+ /pool.*size|max.*connections?|min.*connections?/i,
246
+ /connection.*pool/i,
247
+ /idle.*timeout|acquire.*timeout/i,
248
+ ],
249
+ },
250
+ queryConfig: {
251
+ keywords: ["query", "timeout", "retry", "transaction"],
252
+ patterns: [
253
+ /query.*timeout|statement.*timeout/i,
254
+ /transaction.*isolation|isolation.*level/i,
255
+ /read.*timeout|write.*timeout/i,
256
+ ],
257
+ },
258
+ schemaNames: {
259
+ keywords: ["table", "collection", "database", "schema", "shard", "partition"],
260
+ patterns: [
261
+ /^[a-z_]+_20\d{2}$/, // Table names with year: users_2024
262
+ /^shard_\d+$|^partition_\d+$/i, // Shard identifiers
263
+ /table.*name|collection.*name/i,
264
+ ],
265
+ },
266
+ },
267
+
268
+ // Security & Authentication (Extended)
269
+ securityExtended: {
270
+ tokenConfig: {
271
+ keywords: ["token", "jwt", "expiry", "expire", "ttl"],
272
+ patterns: [
273
+ /^\d{3,}$/, // Expiry in seconds: 7200, 86400
274
+ /^\d+[smhd]$/, // Human readable: 2h, 30m, 7d
275
+ /expir(y|e|ation)|ttl/i,
276
+ ],
277
+ },
278
+ passwordPolicy: {
279
+ keywords: ["password", "length", "complexity", "require", "min", "max"],
280
+ patterns: [
281
+ /min.*(password|length)|password.*min/i,
282
+ /password.*(complexity|requirement|policy)/i,
283
+ /must.*contain|should.*contain|require.*\d+/i,
284
+ ],
285
+ },
286
+ rateLimiting: {
287
+ keywords: ["rate", "limit", "attempt", "throttle", "max"],
288
+ patterns: [
289
+ /max.*(attempt|tries|request)|attempt.*limit/i,
290
+ /rate.*limit|throttle/i,
291
+ /request.*per.*(minute|hour|second)/i,
292
+ ],
293
+ },
294
+ encryptionConfig: {
295
+ keywords: ["encrypt", "cipher", "algorithm", "mode", "aes", "rsa"],
296
+ patterns: [
297
+ /^(AES|RSA|DES|3DES|Blowfish)-\d+(-[A-Z]+)?$/i, // AES-256-GCM
298
+ /encryption.*algorithm|cipher.*mode/i,
299
+ ],
300
+ },
301
+ oauthConfig: {
302
+ keywords: ["oauth", "scope", "grant", "client"],
303
+ patterns: [
304
+ /^(read|write|admin):[a-z_]+$/i, // OAuth scopes: read:user
305
+ /scope|grant.*type|client.*id/i,
306
+ ],
307
+ },
308
+ },
309
+
310
+ // File System & Paths
311
+ fileSystem: {
312
+ directories: {
313
+ keywords: ["dir", "directory", "path", "folder", "upload", "download", "temp"],
314
+ patterns: [
315
+ /^\/[a-z]+\/[a-z]+\//i, // Unix absolute paths: /var/www/uploads
316
+ /^[A-Z]:\\/i, // Windows paths: C:\Users\...
317
+ /upload.*dir|download.*dir|temp.*dir/i,
318
+ /^\.\/[a-z_-]+\//i, // Relative paths: ./uploads/
319
+ ],
320
+ },
321
+ fileLimits: {
322
+ keywords: ["file", "size", "limit", "max", "upload"],
323
+ patterns: [
324
+ /file.*size|max.*size|size.*limit/i,
325
+ /upload.*limit|download.*limit/i,
326
+ /^\d{6,}$/, // Large byte values: 10485760 (10MB)
327
+ ],
328
+ },
329
+ fileTypes: {
330
+ keywords: ["extension", "type", "allow", "mime", "accept"],
331
+ patterns: [
332
+ /^\.[a-z0-9]{2,4}$/i, // File extensions: .jpg, .pdf
333
+ /allowed.*(type|extension)|accept.*type/i,
334
+ /^(image|video|audio|application)\/[a-z0-9+-]+$/i, // MIME types
335
+ ],
336
+ },
337
+ logPaths: {
338
+ keywords: ["log", "path", "file"],
339
+ patterns: [/^\/var\/log\//i, /\.log$/i, /log.*file|log.*path/i],
340
+ },
341
+ },
342
+
343
+ // ============ Phase 3: Important Configuration Patterns ============
344
+
345
+ // Network & Protocol Configuration
346
+ network: {
347
+ httpConfig: {
348
+ keywords: ["method", "header", "content", "type", "accept"],
349
+ httpMethods: ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"],
350
+ patterns: [
351
+ /^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)$/,
352
+ /allowed.*method|http.*method/i,
353
+ /content-type|accept|user-agent/i,
354
+ ],
355
+ },
356
+ timeouts: {
357
+ keywords: ["connect", "read", "write", "timeout", "socket"],
358
+ patterns: [
359
+ /connect.*timeout|connection.*timeout/i,
360
+ /read.*timeout|write.*timeout/i,
361
+ /socket.*timeout|idle.*timeout/i,
362
+ ],
363
+ },
364
+ bufferConfig: {
365
+ keywords: ["buffer", "size", "socket", "receive", "send"],
366
+ patterns: [/buffer.*size|socket.*buffer/i, /receive.*buffer|send.*buffer/i],
367
+ },
368
+ keepAlive: {
369
+ keywords: ["keepalive", "keep", "alive", "ping", "interval"],
370
+ patterns: [/keep.*alive|keepalive/i, /ping.*interval|heartbeat/i, /websocket.*ping/i],
371
+ },
372
+ },
373
+
374
+ // Business Rules & Limits
375
+ business: {
376
+ pricing: {
377
+ keywords: ["price", "cost", "fee", "charge", "plan"],
378
+ patterns: [
379
+ /^\d+\.\d{2}$/, // Price values: 49.99, 19.95
380
+ /price|cost|fee|charge/i,
381
+ /plan.*(price|cost)/i,
382
+ ],
383
+ },
384
+ quotas: {
385
+ keywords: ["quota", "limit", "plan", "tier", "free", "premium"],
386
+ patterns: [
387
+ /quota|limit.*per.*plan|plan.*limit/i,
388
+ /free.*plan|premium.*plan|enterprise.*plan/i,
389
+ /api.*calls.*per|request.*per.*day/i,
390
+ ],
391
+ },
392
+ discounts: {
393
+ keywords: ["discount", "promo", "coupon", "rate", "percent"],
394
+ patterns: [
395
+ /^0\.[0-9]{1,2}$/, // Decimal rates: 0.15 (15%)
396
+ /discount.*rate|promo.*code/i,
397
+ ],
398
+ },
399
+ trials: {
400
+ keywords: ["trial", "demo", "expiry", "days", "period"],
401
+ patterns: [/trial.*days|trial.*period/i, /demo.*period|expiry.*days/i],
402
+ },
403
+ },
404
+
405
+ // Monitoring & Observability
406
+ monitoring: {
407
+ metricsConfig: {
408
+ keywords: ["metric", "interval", "collect", "sample", "export"],
409
+ patterns: [/metric.*interval|collection.*interval/i, /export.*interval|push.*interval/i],
410
+ },
411
+ alertThresholds: {
412
+ keywords: ["alert", "threshold", "warn", "error", "critical"],
413
+ patterns: [
414
+ /alert.*threshold|threshold.*alert/i,
415
+ /error.*rate|error.*threshold/i,
416
+ /^0\.[0-9]{1,3}$/, // Percentage thresholds: 0.05 (5%)
417
+ ],
418
+ },
419
+ samplingRates: {
420
+ keywords: ["sample", "sampling", "rate", "trace"],
421
+ patterns: [
422
+ /sampling.*rate|sample.*rate/i,
423
+ /trace.*sampling/i,
424
+ /^0\.[0-9]{1,2}$/, // Sampling rates: 0.1 (10%)
425
+ ],
426
+ },
427
+ healthChecks: {
428
+ keywords: ["health", "check", "interval", "ping", "probe"],
429
+ patterns: [/health.*check|liveness|readiness/i, /check.*interval|probe.*interval/i],
430
+ },
431
+ },
432
+
433
+ // ============ Phase 4: Enhancement Configuration Patterns ============
434
+
435
+ // Message Queue & Event Configuration
436
+ messageQueue: {
437
+ queueConfig: {
438
+ keywords: ["queue", "size", "capacity", "max", "buffer"],
439
+ patterns: [/queue.*size|max.*queue|queue.*capacity/i, /buffer.*size.*queue/i],
440
+ },
441
+ messageTTL: {
442
+ keywords: ["message", "ttl", "expiry", "expire", "retention"],
443
+ patterns: [/message.*ttl|message.*expiry/i, /retention.*period|expire.*after/i],
444
+ },
445
+ queueNames: {
446
+ keywords: ["queue", "topic", "exchange", "dlq", "dead"],
447
+ patterns: [
448
+ /^[a-z-]+-queue$/i, // Queue names: user-events-queue
449
+ /dead.*letter|dlq|failed.*messages?/i,
450
+ /topic.*name|exchange.*name/i,
451
+ ],
452
+ },
453
+ consumerConfig: {
454
+ keywords: ["consumer", "group", "partition", "offset"],
455
+ patterns: [/consumer.*group|group.*id/i, /partition.*count|offset.*reset/i],
456
+ },
457
+ },
458
+
459
+ // Deployment & Infrastructure
460
+ deployment: {
461
+ resourceLimits: {
462
+ keywords: ["cpu", "memory", "limit", "request", "resource"],
463
+ patterns: [
464
+ /^\d+m$/i, // CPU millicores: 2000m
465
+ /^\d+[MG]i$/i, // Memory: 4Gi, 512Mi
466
+ /cpu.*limit|memory.*limit/i,
467
+ /resource.*limit|resource.*request/i,
468
+ ],
469
+ },
470
+ scalingConfig: {
471
+ keywords: ["scale", "replica", "min", "max", "threshold"],
472
+ patterns: [
473
+ /min.*replicas?|max.*replicas?/i,
474
+ /scale.*threshold|auto.*scale/i,
475
+ /horizontal.*pod.*autoscaler|hpa/i,
476
+ ],
477
+ },
478
+ regionConfig: {
479
+ keywords: ["region", "zone", "location", "deploy", "availability"],
480
+ patterns: [
481
+ /^[a-z]{2}-[a-z]+-\d+$/i, // AWS regions: us-east-1
482
+ /deploy.*region|region.*name/i,
483
+ /availability.*zone/i,
484
+ ],
485
+ },
486
+ instanceTypes: {
487
+ keywords: ["instance", "type", "machine", "node", "vm"],
488
+ patterns: [
489
+ /^[a-z]\d\.[a-z]+$/i, // AWS instance types: t3.micro
490
+ /instance.*type|machine.*type/i,
491
+ /node.*selector|node.*type/i,
492
+ ],
493
+ },
494
+ },
495
+
496
+ // Third-party Integration
497
+ integration: {
498
+ webhookURLs: {
499
+ keywords: ["webhook", "callback", "notify", "hook"],
500
+ patterns: [/webhook|callback.*url/i, /^https?:\/\/[^\/]+\/webhooks?\//i],
501
+ },
502
+ externalServices: {
503
+ keywords: ["provider", "service", "integration", "api"],
504
+ patterns: [
505
+ /^(stripe|paypal|twilio|sendgrid|slack|github)$/i, // Service names
506
+ /provider.*name|integration.*name/i,
507
+ /external.*service/i,
508
+ ],
509
+ },
510
+ apiVersions: {
511
+ keywords: ["version", "api", "v"],
512
+ patterns: [
513
+ /^v?\d{4}-\d{2}-\d{2}$/i, // Date versions: 2023-10-16
514
+ /^v\d+(\.\d+)?$/i, // Semantic versions: v1, v2.1
515
+ /api.*version/i,
516
+ ],
517
+ },
518
+ channelIds: {
519
+ keywords: ["channel", "room", "chat", "notify"],
520
+ patterns: [
521
+ /^#[a-z-]+$/i, // Slack channels: #production-alerts
522
+ /channel.*id|room.*id/i,
523
+ ],
524
+ },
525
+ },
526
+
527
+ // Localization & Formatting
528
+ localization: {
529
+ timezones: {
530
+ keywords: ["timezone", "zone", "tz"],
531
+ patterns: [
532
+ /^[A-Z][a-z]+\/[A-Z][a-z]+$/i, // IANA timezones: Asia/Tokyo
533
+ /timezone|time.*zone/i,
534
+ ],
535
+ },
536
+ dateFormats: {
537
+ keywords: ["date", "format", "pattern", "time"],
538
+ patterns: [
539
+ /^[YMDHms\-\/:\s]+$/, // Date format patterns: YYYY-MM-DD
540
+ /date.*format|time.*format/i,
541
+ /format.*string.*date/i,
542
+ ],
543
+ },
544
+ currencies: {
545
+ keywords: ["currency", "code", "symbol"],
546
+ patterns: [
547
+ /^[A-Z]{3}$/i, // Currency codes: USD, EUR, JPY
548
+ /currency.*code/i,
549
+ ],
550
+ },
551
+ locales: {
552
+ keywords: ["locale", "language", "lang", "i18n"],
553
+ patterns: [
554
+ /^[a-z]{2}-[A-Z]{2}$/i, // Locale codes: en-US, ja-JP
555
+ /locale|language.*code/i,
556
+ ],
557
+ },
558
+ numberFormats: {
559
+ keywords: ["number", "format", "decimal", "thousand", "separator"],
560
+ patterns: [/number.*format|decimal.*separator/i, /thousand.*separator/i],
561
+ },
562
+ },
563
+
564
+ // ============ Additional Critical Patterns ============
565
+
566
+ // Environment Variable Names (hardcoded)
567
+ environmentVars: {
568
+ keywords: ["process.env", "env"],
569
+ patterns: [
570
+ /^(PROD|DEV|STAGING|TEST)_[A-Z_]+$/i, // Environment-prefixed vars
571
+ /^[A-Z_]+_(PROD|DEV|STAGING|TEST)$/i,
572
+ /^(PRODUCTION|DEVELOPMENT)_/i,
573
+ ],
574
+ },
575
+
576
+ // Third-party Service IDs
577
+ thirdPartyServices: {
578
+ stripe: {
579
+ patterns: [
580
+ /^pk_(test|live)_[a-zA-Z0-9]{24,}$/, // Publishable keys
581
+ /^sk_(test|live)_[a-zA-Z0-9]{24,}$/, // Secret keys
582
+ ],
583
+ },
584
+ googleAnalytics: {
585
+ patterns: [
586
+ /^UA-\d+-\d+$/, // Universal Analytics
587
+ /^G-[A-Z0-9]{10}$/, // GA4
588
+ /^GTM-[A-Z0-9]+$/, // Google Tag Manager
589
+ ],
590
+ },
591
+ sentry: {
592
+ patterns: [/^https?:\/\/[a-f0-9]+@[^\/]+\.ingest\.sentry\.io\/\d+$/i],
593
+ },
594
+ googleMaps: {
595
+ patterns: [
596
+ /^AIzaSy[a-zA-Z0-9_-]{33}$/, // Google Maps API Key
597
+ ],
598
+ },
599
+ firebase: {
600
+ patterns: [/^[a-z0-9-]+\.firebaseapp\.com$/i, /^[a-z0-9-]+\.firebase(io|database)\.com$/i],
601
+ },
602
+ aws: {
603
+ patterns: [
604
+ /^AKIA[A-Z0-9]{16}$/, // AWS Access Key ID
605
+ /^[a-z0-9-]+\.s3\.[a-z0-9-]+\.amazonaws\.com$/i,
606
+ ],
607
+ },
608
+ social: {
609
+ patterns: [
610
+ /^\d{15,16}$/, // Facebook App ID
611
+ /^[a-zA-Z0-9]{25}$/, // Twitter Bearer Token format
612
+ ],
613
+ },
614
+ },
615
+
616
+ // IP Addresses & Hostnames
617
+ ipAddresses: {
618
+ patterns: [
619
+ /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/, // IPv4
620
+ /^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/, // IPv6
621
+ /^(?:[0-9a-fA-F]{1,4}:){1,7}:$/, // IPv6 shortened
622
+ ],
623
+ keywords: ["host", "ip", "address", "server"],
624
+ privateRanges: [/^10\./, /^172\.(1[6-9]|2[0-9]|3[0-1])\./, /^192\.168\./, /^127\./, /^0\.0\.0\.0$/],
625
+ },
626
+
627
+ // Internal Hostnames
628
+ hostnames: {
629
+ patterns: [
630
+ /^[a-z0-9-]+\.(internal|local|corp|lan)$/i,
631
+ /^[a-z0-9-]+-(?:master|slave|replica|primary|secondary)$/i,
632
+ /^(?:db|redis|kafka|mongo|postgres|mysql)-[a-z0-9-]+$/i,
633
+ ],
634
+ keywords: ["host", "hostname", "server", "broker", "endpoint"],
635
+ },
636
+
637
+ // Cron Job Schedules
638
+ cronSchedules: {
639
+ patterns: [
640
+ /^[\d\*\/,\-]+\s+[\d\*\/,\-]+\s+[\d\*\/,\-]+\s+[\d\*\/,\-]+\s+[\d\*\/,\-]+$/, // Standard cron
641
+ /^@(?:yearly|annually|monthly|weekly|daily|hourly|reboot)$/i, // Predefined schedules
642
+ ],
643
+ keywords: ["cron", "schedule", "interval"],
644
+ },
645
+
646
+ // Magic Numbers (Business Logic)
647
+ magicNumbers: {
648
+ // Numbers that appear in business logic contexts
649
+ keywords: ["age", "limit", "max", "min", "threshold", "rate", "tax", "fee", "discount", "commission"],
650
+ businessContexts: [
651
+ /legal.*age|minimum.*age|age.*requirement/i,
652
+ /tax.*rate|vat.*rate|commission.*rate/i,
653
+ /interest.*rate|exchange.*rate/i,
654
+ /shipping.*fee|processing.*fee|service.*fee/i,
655
+ /credit.*limit|withdrawal.*limit|transfer.*limit/i,
656
+ ],
657
+ },
658
+
659
+ // Email & SMS Templates
660
+ messageTemplates: {
661
+ email: {
662
+ keywords: ["subject", "body", "template", "email"],
663
+ patterns: [
664
+ /^[A-Z][a-zA-Z\s]{10,}$/, // English subject lines
665
+ /\{{\s*[a-z_]+\s*\}}/, // Template variables
666
+ ],
667
+ },
668
+ sms: {
669
+ keywords: ["sms", "text", "message"],
670
+ patterns: [/^Your\s+[A-Z]/i, /OTP|verification code|confirm/i],
671
+ },
672
+ },
673
+
674
+ // Version Numbers
675
+ versions: {
676
+ patterns: [
677
+ /^v?\d+\.\d+\.\d+(?:-[a-z0-9.]+)?$/i, // Semantic version: 1.2.3, v1.2.3-beta
678
+ /^v\d+$/i, // API version: v1, v2
679
+ ],
680
+ keywords: ["version", "api.*version", "min.*version", "max.*version"],
681
+ },
682
+
683
+ // Default Pagination
684
+ pagination: {
685
+ keywords: ["page", "limit", "size", "offset", "per.*page"],
686
+ defaults: [10, 20, 25, 50, 100], // Common pagination values
687
+ },
688
+ };
689
+ }
690
+
691
+ async initialize(semanticEngine = null) {
692
+ if (semanticEngine) {
693
+ this.semanticEngine = semanticEngine;
694
+ }
695
+ this.verbose = semanticEngine?.verbose || false;
696
+ }
697
+
698
+ async analyzeFileBasic(filePath, options = {}) {
699
+ const violations = [];
700
+
701
+ try {
702
+ const sourceFile = this.semanticEngine.project.getSourceFile(filePath);
703
+ if (!sourceFile) {
704
+ if (this.verbose) {
705
+ console.log(
706
+ `[DEBUG] 🔍 C067: File not in semantic project, trying standalone: ${filePath.split("/").pop()}`
707
+ );
708
+ }
709
+ // Fallback to standalone analysis if file not in semantic project
710
+ return await this.analyzeFileStandalone(filePath, options);
711
+ }
712
+
713
+ if (this.verbose) {
714
+ console.log(`[DEBUG] 🔍 C067: Analyzing hardcoded config in ${filePath.split("/").pop()}`);
715
+ }
716
+
717
+ // Skip test files and config files themselves
718
+ if (this.isConfigOrTestFile(filePath)) {
719
+ if (this.verbose) {
720
+ console.log(`[DEBUG] 🔍 C067: Skipping config/test file: ${filePath.split("/").pop()}`);
721
+ }
722
+ return violations;
723
+ }
724
+
725
+ // Find hardcoded configuration values
726
+ const hardcodedConfigs = this.findHardcodedConfigs(sourceFile);
727
+
728
+ for (const config of hardcodedConfigs) {
729
+ violations.push({
730
+ ruleId: "C067",
731
+ message: this.createMessage(config),
732
+ filePath: filePath,
733
+ line: config.line,
734
+ column: config.column,
735
+ severity: "warning",
736
+ category: "configuration",
737
+ type: config.type,
738
+ value: config.value,
739
+ suggestion: this.getSuggestion(config.type),
740
+ });
741
+ }
742
+
743
+ if (this.verbose) {
744
+ console.log(`[DEBUG] 🔍 C067: Found ${violations.length} hardcoded config violations`);
745
+ }
746
+
747
+ return violations;
748
+ } catch (error) {
749
+ if (this.verbose) {
750
+ console.error(`[DEBUG] ❌ C067: Symbol analysis error: ${error.message}`);
751
+ }
752
+ throw error;
753
+ }
754
+ }
755
+
756
+ async analyzeFileStandalone(filePath, options = {}) {
757
+ const violations = [];
758
+
759
+ try {
760
+ // Create a standalone ts-morph project for this analysis
761
+ const project = new Project({
762
+ compilerOptions: {
763
+ target: "ES2020",
764
+ module: "CommonJS",
765
+ allowJs: true,
766
+ allowSyntheticDefaultImports: true,
767
+ esModuleInterop: true,
768
+ skipLibCheck: true,
769
+ strict: false,
770
+ },
771
+ useInMemoryFileSystem: true,
772
+ });
773
+
774
+ // Add the source file to the project
775
+ const fs = require("fs");
776
+ const path = require("path");
777
+
778
+ // Check if file exists first
779
+ if (!fs.existsSync(filePath)) {
780
+ throw new Error(`File not found on filesystem: ${filePath}`);
781
+ }
782
+
783
+ // Read file content and create source file
784
+ const fileContent = fs.readFileSync(filePath, "utf8");
785
+ const fileName = path.basename(filePath);
786
+ const sourceFile = project.createSourceFile(fileName, fileContent);
787
+
788
+ if (!sourceFile) {
789
+ throw new Error(`Source file not found: ${filePath}`);
790
+ }
791
+
792
+ if (this.verbose) {
793
+ console.log(
794
+ `[DEBUG] 🔍 C067: Analyzing hardcoded config in ${filePath.split("/").pop()} (standalone)`
795
+ );
796
+ }
797
+
798
+ // Skip test files and config files themselves
799
+ if (this.isConfigOrTestFile(filePath)) {
800
+ if (this.verbose) {
801
+ console.log(`[DEBUG] 🔍 C067: Skipping config/test file: ${filePath.split("/").pop()}`);
802
+ }
803
+ return violations;
804
+ }
805
+
806
+ // Find hardcoded configuration values
807
+ const hardcodedConfigs = this.findHardcodedConfigs(sourceFile);
808
+
809
+ for (const config of hardcodedConfigs) {
810
+ violations.push({
811
+ ruleId: "C067",
812
+ message: this.createMessage(config),
813
+ filePath: filePath,
814
+ line: config.line,
815
+ column: config.column,
816
+ severity: "warning",
817
+ category: "configuration",
818
+ type: config.type,
819
+ value: config.value,
820
+ suggestion: this.getSuggestion(config.type),
821
+ });
822
+ }
823
+
824
+ if (this.verbose) {
825
+ console.log(`[DEBUG] 🔍 C067: Found ${violations.length} hardcoded config violations (standalone)`);
826
+ }
827
+
828
+ // Clean up the project
829
+ project.removeSourceFile(sourceFile);
830
+
831
+ return violations;
832
+ } catch (error) {
833
+ if (this.verbose) {
834
+ console.error(`[DEBUG] ❌ C067: Standalone analysis error: ${error.message}`);
835
+ }
836
+ throw error;
837
+ }
838
+ }
839
+
840
+ isConfigOrTestFile(filePath) {
841
+ // Skip config files themselves and test files, including dummy/test data files
842
+ const fileName = filePath.toLowerCase();
843
+ const configPatterns = [
844
+ /config\.(ts|js|json)$/,
845
+ /\.config\.(ts|js)$/,
846
+ /\.env$/,
847
+ /\.env\./,
848
+ /constants?\.(ts|js)$/, // constants.ts, constant.ts
849
+ /\.constants?\.(ts|js)$/, // app.constants.ts, common.constant.ts
850
+ /settings\.(ts|js)$/,
851
+ /defaults\.(ts|js)$/,
852
+ ];
853
+
854
+ const testPatterns = [
855
+ /\.(test|spec)\.(ts|tsx|js|jsx)$/,
856
+ /\/__tests__\//,
857
+ /\/test\//,
858
+ /\/tests\//,
859
+ /\.stories\.(ts|tsx|js|jsx)$/,
860
+ /\.mock\.(ts|tsx|js|jsx)$/,
861
+ /\/dummy\//, // Skip dummy data files
862
+ /dummy\.(ts|js)$/, // Skip dummy files
863
+ /test-fixtures\//, // Skip test fixture files
864
+ /\.fixture\.(ts|js)$/, // Skip fixture files
865
+ /entity\.(ts|js)$/, // Skip entity/ORM files (contain DB constraints)
866
+ ];
867
+
868
+ return (
869
+ configPatterns.some((pattern) => pattern.test(fileName)) ||
870
+ testPatterns.some((pattern) => pattern.test(fileName))
871
+ );
872
+ }
873
+
874
+ findHardcodedConfigs(sourceFile) {
875
+ const configs = [];
876
+
877
+ // Traverse all nodes in the source file
878
+ sourceFile.forEachDescendant((node) => {
879
+ // Check string literals for URLs, credentials, feature flags
880
+ if (node.getKind() === SyntaxKind.StringLiteral) {
881
+ const config = this.analyzeStringLiteral(node, sourceFile);
882
+ if (config) {
883
+ configs.push(config);
884
+ }
885
+ }
886
+
887
+ // Check numeric literals for timeouts, ports, batch sizes
888
+ if (node.getKind() === SyntaxKind.NumericLiteral) {
889
+ const config = this.analyzeNumericLiteral(node, sourceFile);
890
+ if (config) {
891
+ configs.push(config);
892
+ }
893
+ }
894
+
895
+ // Check boolean literals for feature flags
896
+ if (node.getKind() === SyntaxKind.TrueKeyword || node.getKind() === SyntaxKind.FalseKeyword) {
897
+ const config = this.analyzeBooleanLiteral(node, sourceFile);
898
+ if (config) {
899
+ configs.push(config);
900
+ }
901
+ }
902
+
903
+ // Check template literals for URLs with embedded values
904
+ if (node.getKind() === SyntaxKind.TemplateExpression) {
905
+ const config = this.analyzeTemplateLiteral(node, sourceFile);
906
+ if (config) {
907
+ configs.push(config);
908
+ }
909
+ }
910
+
911
+ // Check property assignments for configuration objects
912
+ if (node.getKind() === SyntaxKind.PropertyAssignment) {
913
+ const config = this.analyzePropertyAssignment(node, sourceFile);
914
+ if (config) {
915
+ configs.push(config);
916
+ }
917
+ }
918
+
919
+ // Check variable declarations for configuration constants
920
+ if (node.getKind() === SyntaxKind.VariableDeclaration) {
921
+ const config = this.analyzeVariableDeclaration(node, sourceFile);
922
+ if (config) {
923
+ configs.push(config);
924
+ }
925
+ }
926
+ });
927
+
928
+ return configs;
929
+ }
930
+
931
+ analyzeStringLiteral(node, sourceFile) {
932
+ const value = node.getLiteralValue();
933
+ const position = sourceFile.getLineAndColumnAtPos(node.getStart());
934
+
935
+ // Skip short strings and common UI values
936
+ if (value.length < 3) return null;
937
+
938
+ const parentContext = this.getParentContext(node);
939
+
940
+ // Skip import paths and module names
941
+ if (this.isImportPath(value, node)) return null;
942
+
943
+ // Skip UI strings and labels
944
+ if (this.isUIString(value)) return null;
945
+
946
+ // Skip Japanese UI text and form labels (NEW - reduces false positives)
947
+ if (this.isJapaneseUIText(value, parentContext)) return null;
948
+
949
+ // Skip form field labels (NEW - reduces false positives)
950
+ if (this.isFormFieldLabel(value, parentContext)) return null;
951
+
952
+ // Skip test data and mocks
953
+ if (this.isTestData(value, parentContext)) return null;
954
+
955
+ // Skip validation messages and error messages
956
+ if (this.isValidationMessage(value, parentContext)) return null;
957
+
958
+ // Skip file names and descriptions
959
+ if (this.isFileNameOrDescription(value, parentContext)) return null;
960
+
961
+ // Skip config keys (like 'api.baseUrl', 'features.newUI', etc.)
962
+ if (this.looksLikeConfigKey(value)) {
963
+ return null;
964
+ }
965
+
966
+ // Skip if this is used in a config service call
967
+ if (
968
+ parentContext.includes("config.get") ||
969
+ parentContext.includes("config.getString") ||
970
+ parentContext.includes("config.getBoolean") ||
971
+ parentContext.includes("config.getNumber")
972
+ ) {
973
+ return null;
974
+ }
975
+
976
+ // Skip if this is a property key in an object literal
977
+ if (this.isPropertyKey(node)) {
978
+ return null;
979
+ }
980
+
981
+ // Check for API URLs and endpoints - Rule C067 requirement
982
+ if (this.configPatterns.urls.regex.test(value)) {
983
+ if (!this.isExcludedUrl(value, node) && this.isEnvironmentDependentUrl(value)) {
984
+ return {
985
+ type: "api_url",
986
+ value: value,
987
+ line: position.line,
988
+ column: position.column,
989
+ node: node,
990
+ suggestion: "Move API URLs to environment variables or config files",
991
+ };
992
+ }
993
+ }
994
+
995
+ // Check for credentials - Rule C067 requirement
996
+ if (this.isRealCredential(value, parentContext)) {
997
+ return {
998
+ type: "credential",
999
+ value: this.maskSensitiveValue(value),
1000
+ line: position.line,
1001
+ column: position.column,
1002
+ node: node,
1003
+ context: parentContext,
1004
+ suggestion: "Move credentials to secure environment variables or vault",
1005
+ };
1006
+ }
1007
+
1008
+ // Check for connection strings - Rule C067 requirement
1009
+ if (this.configPatterns.connections.regex.test(value)) {
1010
+ return {
1011
+ type: "connection_string",
1012
+ value: this.maskSensitiveValue(value),
1013
+ line: position.line,
1014
+ column: position.column,
1015
+ node: node,
1016
+ suggestion: "Move connection strings to environment variables",
1017
+ };
1018
+ }
1019
+
1020
+ // Phase 1 extensions - Security & Infrastructure
1021
+
1022
+ // Check for CORS origins
1023
+ if (this.isCorsOrigin(value, parentContext)) {
1024
+ return {
1025
+ type: "cors_origin",
1026
+ value: value,
1027
+ line: position.line,
1028
+ column: position.column,
1029
+ node: node,
1030
+ suggestion: "Move CORS origins to environment configuration",
1031
+ };
1032
+ }
1033
+
1034
+ // Check for session/JWT configuration
1035
+ if (this.isSessionConfig(value, parentContext)) {
1036
+ return {
1037
+ type: "session_config",
1038
+ value: value,
1039
+ line: position.line,
1040
+ column: position.column,
1041
+ node: node,
1042
+ suggestion: "Move session configuration to environment variables",
1043
+ };
1044
+ }
1045
+
1046
+ // Check for cache configuration
1047
+ if (this.isCacheConfig(value, parentContext)) {
1048
+ return {
1049
+ type: "cache_config",
1050
+ value: value,
1051
+ line: position.line,
1052
+ column: position.column,
1053
+ node: node,
1054
+ suggestion: "Move cache configuration to environment settings",
1055
+ };
1056
+ }
1057
+
1058
+ // Check for log levels
1059
+ if (this.isLogLevel(value, parentContext)) {
1060
+ return {
1061
+ type: "log_level",
1062
+ value: value,
1063
+ line: position.line,
1064
+ column: position.column,
1065
+ node: node,
1066
+ suggestion: "Move log level to environment configuration",
1067
+ };
1068
+ }
1069
+
1070
+ // Check for environment names
1071
+ if (this.isEnvironmentName(value, parentContext)) {
1072
+ return {
1073
+ type: "environment_name",
1074
+ value: value,
1075
+ line: position.line,
1076
+ column: position.column,
1077
+ node: node,
1078
+ suggestion: "Avoid hardcoding environment names, use environment detection",
1079
+ };
1080
+ }
1081
+
1082
+ // Check for service dependencies
1083
+ if (this.isServiceDependency(value, parentContext)) {
1084
+ return {
1085
+ type: "service_dependency",
1086
+ value: value,
1087
+ line: position.line,
1088
+ column: position.column,
1089
+ node: node,
1090
+ suggestion: "Move service URLs to service discovery or configuration",
1091
+ };
1092
+ }
1093
+
1094
+ // ============ Phase 2: Critical String Configurations ============
1095
+
1096
+ // Check for database schema/table names
1097
+ if (this.isSchemaName(value, parentContext)) {
1098
+ return {
1099
+ type: "schema_name",
1100
+ value: value,
1101
+ line: position.line,
1102
+ column: position.column,
1103
+ node: node,
1104
+ suggestion: "Move schema names to database configuration",
1105
+ };
1106
+ }
1107
+
1108
+ // Check for encryption algorithms
1109
+ if (this.isEncryptionConfig(value, parentContext)) {
1110
+ return {
1111
+ type: "encryption_config",
1112
+ value: value,
1113
+ line: position.line,
1114
+ column: position.column,
1115
+ node: node,
1116
+ suggestion: "Move encryption configuration to security settings",
1117
+ };
1118
+ }
1119
+
1120
+ // Check for OAuth scopes
1121
+ if (this.isOAuthConfig(value, parentContext)) {
1122
+ return {
1123
+ type: "oauth_config",
1124
+ value: value,
1125
+ line: position.line,
1126
+ column: position.column,
1127
+ node: node,
1128
+ suggestion: "Move OAuth configuration to authentication settings",
1129
+ };
1130
+ }
1131
+
1132
+ // Check for directories and paths
1133
+ if (this.isDirectory(value, parentContext)) {
1134
+ return {
1135
+ type: "directory",
1136
+ value: value,
1137
+ line: position.line,
1138
+ column: position.column,
1139
+ node: node,
1140
+ suggestion: "Move directory paths to environment configuration",
1141
+ };
1142
+ }
1143
+
1144
+ // Check for file extensions and MIME types
1145
+ if (this.isFileType(value, parentContext)) {
1146
+ return {
1147
+ type: "file_type",
1148
+ value: value,
1149
+ line: position.line,
1150
+ column: position.column,
1151
+ node: node,
1152
+ suggestion: "Move file type restrictions to configuration",
1153
+ };
1154
+ }
1155
+
1156
+ // Check for log file paths
1157
+ if (this.isLogPath(value, parentContext)) {
1158
+ return {
1159
+ type: "log_path",
1160
+ value: value,
1161
+ line: position.line,
1162
+ column: position.column,
1163
+ node: node,
1164
+ suggestion: "Move log paths to logging configuration",
1165
+ };
1166
+ }
1167
+
1168
+ // ============ Phase 3: Important String Configurations ============
1169
+
1170
+ // Check for HTTP methods and headers
1171
+ if (this.isHttpConfig(value, parentContext)) {
1172
+ return {
1173
+ type: "http_config",
1174
+ value: value,
1175
+ line: position.line,
1176
+ column: position.column,
1177
+ node: node,
1178
+ suggestion: "Move HTTP configuration to network settings",
1179
+ };
1180
+ }
1181
+
1182
+ // Check for queue/topic names
1183
+ if (this.isQueueConfig(value, parentContext)) {
1184
+ return {
1185
+ type: "queue_config",
1186
+ value: value,
1187
+ line: position.line,
1188
+ column: position.column,
1189
+ node: node,
1190
+ suggestion: "Move queue names to messaging configuration",
1191
+ };
1192
+ }
1193
+
1194
+ // Check for consumer group IDs
1195
+ if (this.isConsumerConfig(value, parentContext)) {
1196
+ return {
1197
+ type: "consumer_config",
1198
+ value: value,
1199
+ line: position.line,
1200
+ column: position.column,
1201
+ node: node,
1202
+ suggestion: "Move consumer configuration to messaging settings",
1203
+ };
1204
+ }
1205
+
1206
+ // ============ Phase 4: Enhancement String Configurations ============
1207
+
1208
+ // Check for resource limits (Kubernetes style)
1209
+ if (this.isResourceLimit(value, parentContext)) {
1210
+ return {
1211
+ type: "resource_limit",
1212
+ value: value,
1213
+ line: position.line,
1214
+ column: position.column,
1215
+ node: node,
1216
+ suggestion: "Move resource limits to deployment configuration",
1217
+ };
1218
+ }
1219
+
1220
+ // Check for deployment regions
1221
+ if (this.isRegionConfig(value, parentContext)) {
1222
+ return {
1223
+ type: "region_config",
1224
+ value: value,
1225
+ line: position.line,
1226
+ column: position.column,
1227
+ node: node,
1228
+ suggestion: "Move region configuration to deployment settings",
1229
+ };
1230
+ }
1231
+
1232
+ // Check for instance types
1233
+ if (this.isInstanceType(value, parentContext)) {
1234
+ return {
1235
+ type: "instance_type",
1236
+ value: value,
1237
+ line: position.line,
1238
+ column: position.column,
1239
+ node: node,
1240
+ suggestion: "Move instance types to infrastructure configuration",
1241
+ };
1242
+ }
1243
+
1244
+ // Check for webhook URLs
1245
+ if (this.isWebhookURL(value, parentContext)) {
1246
+ return {
1247
+ type: "webhook_url",
1248
+ value: value,
1249
+ line: position.line,
1250
+ column: position.column,
1251
+ node: node,
1252
+ suggestion: "Move webhook URLs to integration configuration",
1253
+ };
1254
+ }
1255
+
1256
+ // Check for external service names
1257
+ if (this.isExternalService(value, parentContext)) {
1258
+ return {
1259
+ type: "external_service",
1260
+ value: value,
1261
+ line: position.line,
1262
+ column: position.column,
1263
+ node: node,
1264
+ suggestion: "Move service provider names to integration configuration",
1265
+ };
1266
+ }
1267
+
1268
+ // Check for API versions
1269
+ if (this.isApiVersion(value, parentContext)) {
1270
+ return {
1271
+ type: "api_version",
1272
+ value: value,
1273
+ line: position.line,
1274
+ column: position.column,
1275
+ node: node,
1276
+ suggestion: "Move API versions to integration configuration",
1277
+ };
1278
+ }
1279
+
1280
+ // Check for channel IDs (Slack, etc.)
1281
+ if (this.isChannelId(value, parentContext)) {
1282
+ return {
1283
+ type: "channel_id",
1284
+ value: value,
1285
+ line: position.line,
1286
+ column: position.column,
1287
+ node: node,
1288
+ suggestion: "Move channel IDs to notification configuration",
1289
+ };
1290
+ }
1291
+
1292
+ // Check for timezones
1293
+ if (this.isTimezone(value, parentContext)) {
1294
+ return {
1295
+ type: "timezone",
1296
+ value: value,
1297
+ line: position.line,
1298
+ column: position.column,
1299
+ node: node,
1300
+ suggestion: "Move timezone to localization configuration",
1301
+ };
1302
+ }
1303
+
1304
+ // Check for date formats
1305
+ if (this.isDateFormat(value, parentContext)) {
1306
+ return {
1307
+ type: "date_format",
1308
+ value: value,
1309
+ line: position.line,
1310
+ column: position.column,
1311
+ node: node,
1312
+ suggestion: "Move date formats to localization configuration",
1313
+ };
1314
+ }
1315
+
1316
+ // Check for currency codes
1317
+ if (this.isCurrency(value, parentContext)) {
1318
+ return {
1319
+ type: "currency",
1320
+ value: value,
1321
+ line: position.line,
1322
+ column: position.column,
1323
+ node: node,
1324
+ suggestion: "Move currency codes to localization configuration",
1325
+ };
1326
+ }
1327
+
1328
+ // Check for locale identifiers
1329
+ if (this.isLocale(value, parentContext)) {
1330
+ return {
1331
+ type: "locale",
1332
+ value: value,
1333
+ line: position.line,
1334
+ column: position.column,
1335
+ node: node,
1336
+ suggestion: "Move locale to localization configuration",
1337
+ };
1338
+ }
1339
+
1340
+ // ============ Additional Critical Configurations ============
1341
+
1342
+ // Check for hardcoded environment variable names
1343
+ if (this.isHardcodedEnvVar(value, parentContext)) {
1344
+ return {
1345
+ type: "environment_var",
1346
+ value: value,
1347
+ line: position.line,
1348
+ column: position.column,
1349
+ node: node,
1350
+ suggestion: "Use environment-agnostic variable names or config service",
1351
+ };
1352
+ }
1353
+
1354
+ // Check for third-party service IDs/keys
1355
+ if (this.isThirdPartyServiceId(value, parentContext)) {
1356
+ return {
1357
+ type: "third_party_service",
1358
+ value: this.maskSensitiveValue(value),
1359
+ line: position.line,
1360
+ column: position.column,
1361
+ node: node,
1362
+ suggestion: "Move API keys/service IDs to secret management (e.g., .env, AWS Secrets Manager)",
1363
+ };
1364
+ }
1365
+
1366
+ // Check for IP addresses
1367
+ if (this.isIPAddress(value, parentContext)) {
1368
+ return {
1369
+ type: "ip_address",
1370
+ value: value,
1371
+ line: position.line,
1372
+ column: position.column,
1373
+ node: node,
1374
+ suggestion: "Move IP addresses to configuration or service discovery",
1375
+ };
1376
+ }
1377
+
1378
+ // Check for internal hostnames
1379
+ if (this.isInternalHostname(value, parentContext)) {
1380
+ return {
1381
+ type: "hostname",
1382
+ value: value,
1383
+ line: position.line,
1384
+ column: position.column,
1385
+ node: node,
1386
+ suggestion: "Move hostnames to configuration or use service discovery",
1387
+ };
1388
+ }
1389
+
1390
+ // Check for cron schedules
1391
+ if (this.isCronSchedule(value, parentContext)) {
1392
+ return {
1393
+ type: "cron_schedule",
1394
+ value: value,
1395
+ line: position.line,
1396
+ column: position.column,
1397
+ node: node,
1398
+ suggestion: "Move cron schedules to scheduler configuration",
1399
+ };
1400
+ }
1401
+
1402
+ // Check for message templates
1403
+ if (this.isMessageTemplate(value, parentContext)) {
1404
+ return {
1405
+ type: "message_template",
1406
+ value: value.substring(0, 50) + (value.length > 50 ? "..." : ""),
1407
+ line: position.line,
1408
+ column: position.column,
1409
+ node: node,
1410
+ suggestion: "Move email/SMS templates to template management system",
1411
+ };
1412
+ }
1413
+
1414
+ // Check for version numbers
1415
+ if (this.isVersionNumber(value, parentContext)) {
1416
+ return {
1417
+ type: "version",
1418
+ value: value,
1419
+ line: position.line,
1420
+ column: position.column,
1421
+ node: node,
1422
+ suggestion: "Move version numbers to build configuration or package.json",
1423
+ };
1424
+ }
1425
+
1426
+ return null;
1427
+ }
1428
+
1429
+ analyzeBooleanLiteral(node, sourceFile) {
1430
+ const value = node.getKind() === SyntaxKind.TrueKeyword;
1431
+ const position = sourceFile.getLineAndColumnAtPos(node.getStart());
1432
+ const parentContext = this.getParentContext(node);
1433
+
1434
+ // Skip React state initial values (NEW - reduces false positives)
1435
+ // Common patterns: useState(false), const [isOpen, setIsOpen] = useState(false)
1436
+ if (/useState|useReducer|createContext/.test(parentContext)) {
1437
+ return null;
1438
+ }
1439
+
1440
+ // Skip component props default values
1441
+ if (/defaultProps|default\s*:/i.test(parentContext)) {
1442
+ return null;
1443
+ }
1444
+
1445
+ // Skip UI state management (open, close, show, hide, loading, disabled, etc.)
1446
+ const uiStatePatterns = [
1447
+ /isOpen|isClose|isShow|isHide/i,
1448
+ /isVisible|isHidden/i,
1449
+ /isLoading|isDisabled|isEnabled|isActive/i,
1450
+ /isChecked|isSelected/i,
1451
+ /hasError|hasWarning/i,
1452
+ /showModal|closeModal|openDialog/i,
1453
+ ];
1454
+
1455
+ if (uiStatePatterns.some((pattern) => pattern.test(parentContext))) {
1456
+ return null;
1457
+ }
1458
+
1459
+ // Check if this boolean is a feature flag or toggle - Rule C067 requirement
1460
+ if (this.isFeatureFlag(parentContext)) {
1461
+ return {
1462
+ type: "feature_flag",
1463
+ value: value,
1464
+ line: position.line,
1465
+ column: position.column,
1466
+ node: node,
1467
+ context: parentContext,
1468
+ suggestion: "Move feature flags to configuration management system",
1469
+ };
1470
+ }
1471
+
1472
+ return null;
1473
+ }
1474
+
1475
+ analyzeNumericLiteral(node, sourceFile) {
1476
+ const value = node.getLiteralValue();
1477
+ const position = sourceFile.getLineAndColumnAtPos(node.getStart());
1478
+ const parentContext = this.getParentContext(node);
1479
+
1480
+ // Check for timeouts and retry intervals - Rule C067 requirement
1481
+ if (this.isTimeout(value, parentContext)) {
1482
+ return {
1483
+ type: "timeout",
1484
+ value: value,
1485
+ line: position.line,
1486
+ column: position.column,
1487
+ node: node,
1488
+ context: parentContext,
1489
+ suggestion: "Move timeout values to configuration files",
1490
+ };
1491
+ }
1492
+
1493
+ // Check for retry intervals - Rule C067 requirement
1494
+ if (this.isRetryInterval(value, parentContext)) {
1495
+ return {
1496
+ type: "retry_interval",
1497
+ value: value,
1498
+ line: position.line,
1499
+ column: position.column,
1500
+ node: node,
1501
+ context: parentContext,
1502
+ suggestion: "Move retry intervals to environment configuration",
1503
+ };
1504
+ }
1505
+
1506
+ // Check for batch sizes - Rule C067 requirement
1507
+ if (this.isBatchSize(value, parentContext)) {
1508
+ return {
1509
+ type: "batch_size",
1510
+ value: value,
1511
+ line: position.line,
1512
+ column: position.column,
1513
+ node: node,
1514
+ context: parentContext,
1515
+ suggestion: "Move batch sizes to configuration management",
1516
+ };
1517
+ }
1518
+
1519
+ // Check for thresholds that might vary by environment
1520
+ if (this.isThreshold(value, parentContext)) {
1521
+ return {
1522
+ type: "threshold",
1523
+ value: value,
1524
+ line: position.line,
1525
+ column: position.column,
1526
+ node: node,
1527
+ context: parentContext,
1528
+ suggestion: "Move thresholds to environment-specific configuration",
1529
+ };
1530
+ }
1531
+
1532
+ // Phase 1 extensions - Infrastructure numeric configs
1533
+
1534
+ // Check for session/JWT expiry times
1535
+ if (this.isSessionConfig(value, parentContext)) {
1536
+ return {
1537
+ type: "session_config",
1538
+ value: value,
1539
+ line: position.line,
1540
+ column: position.column,
1541
+ node: node,
1542
+ context: parentContext,
1543
+ suggestion: "Move session timeouts to environment configuration",
1544
+ };
1545
+ }
1546
+
1547
+ // Check for cache TTL values
1548
+ if (this.isCacheConfig(value, parentContext)) {
1549
+ return {
1550
+ type: "cache_config",
1551
+ value: value,
1552
+ line: position.line,
1553
+ column: position.column,
1554
+ node: node,
1555
+ context: parentContext,
1556
+ suggestion: "Move cache TTL to configuration management",
1557
+ };
1558
+ }
1559
+
1560
+ // Check for performance configuration
1561
+ if (this.isPerformanceConfig(value, parentContext)) {
1562
+ return {
1563
+ type: "performance_config",
1564
+ value: value,
1565
+ line: position.line,
1566
+ column: position.column,
1567
+ node: node,
1568
+ context: parentContext,
1569
+ suggestion: "Move performance settings to environment configuration",
1570
+ };
1571
+ }
1572
+
1573
+ // ============ Phase 2: Critical Numeric Configurations ============
1574
+
1575
+ // Check for database pool sizes
1576
+ if (this.isDatabasePoolConfig(value, parentContext)) {
1577
+ return {
1578
+ type: "database_pool",
1579
+ value: value,
1580
+ line: position.line,
1581
+ column: position.column,
1582
+ node: node,
1583
+ context: parentContext,
1584
+ suggestion: "Move database pool configuration to environment settings",
1585
+ };
1586
+ }
1587
+
1588
+ // Check for query timeouts
1589
+ if (this.isQueryConfig(value, parentContext)) {
1590
+ return {
1591
+ type: "query_config",
1592
+ value: value,
1593
+ line: position.line,
1594
+ column: position.column,
1595
+ node: node,
1596
+ context: parentContext,
1597
+ suggestion: "Move query timeout to database configuration",
1598
+ };
1599
+ }
1600
+
1601
+ // Check for token expiry times
1602
+ if (this.isTokenConfig(value, parentContext)) {
1603
+ return {
1604
+ type: "token_config",
1605
+ value: value,
1606
+ line: position.line,
1607
+ column: position.column,
1608
+ node: node,
1609
+ context: parentContext,
1610
+ suggestion: "Move token expiry to security configuration",
1611
+ };
1612
+ }
1613
+
1614
+ // Check for password policy values
1615
+ if (this.isPasswordPolicy(value, parentContext)) {
1616
+ return {
1617
+ type: "password_policy",
1618
+ value: value,
1619
+ line: position.line,
1620
+ column: position.column,
1621
+ node: node,
1622
+ context: parentContext,
1623
+ suggestion: "Move password policy to security configuration",
1624
+ };
1625
+ }
1626
+
1627
+ // Check for rate limits
1628
+ if (this.isRateLimiting(value, parentContext)) {
1629
+ return {
1630
+ type: "rate_limiting",
1631
+ value: value,
1632
+ line: position.line,
1633
+ column: position.column,
1634
+ node: node,
1635
+ context: parentContext,
1636
+ suggestion: "Move rate limits to security configuration",
1637
+ };
1638
+ }
1639
+
1640
+ // Check for file size limits
1641
+ if (this.isFileLimit(value, parentContext)) {
1642
+ return {
1643
+ type: "file_limit",
1644
+ value: value,
1645
+ line: position.line,
1646
+ column: position.column,
1647
+ node: node,
1648
+ context: parentContext,
1649
+ suggestion: "Move file size limits to upload configuration",
1650
+ };
1651
+ }
1652
+
1653
+ // ============ Phase 3: Important Numeric Configurations ============
1654
+
1655
+ // Check for network timeouts (distinct from general timeouts)
1656
+ if (this.isNetworkTimeout(value, parentContext)) {
1657
+ return {
1658
+ type: "network_timeout",
1659
+ value: value,
1660
+ line: position.line,
1661
+ column: position.column,
1662
+ node: node,
1663
+ context: parentContext,
1664
+ suggestion: "Move network timeouts to network configuration",
1665
+ };
1666
+ }
1667
+
1668
+ // Check for buffer sizes
1669
+ if (this.isBufferConfig(value, parentContext)) {
1670
+ return {
1671
+ type: "buffer_config",
1672
+ value: value,
1673
+ line: position.line,
1674
+ column: position.column,
1675
+ node: node,
1676
+ context: parentContext,
1677
+ suggestion: "Move buffer sizes to network configuration",
1678
+ };
1679
+ }
1680
+
1681
+ // Check for keep-alive intervals
1682
+ if (this.isKeepAliveConfig(value, parentContext)) {
1683
+ return {
1684
+ type: "keepalive_config",
1685
+ value: value,
1686
+ line: position.line,
1687
+ column: position.column,
1688
+ node: node,
1689
+ context: parentContext,
1690
+ suggestion: "Move keep-alive settings to network configuration",
1691
+ };
1692
+ }
1693
+
1694
+ // Check for pricing values
1695
+ if (this.isPricing(value, parentContext)) {
1696
+ return {
1697
+ type: "pricing",
1698
+ value: value,
1699
+ line: position.line,
1700
+ column: position.column,
1701
+ node: node,
1702
+ context: parentContext,
1703
+ suggestion: "Move pricing to business configuration",
1704
+ };
1705
+ }
1706
+
1707
+ // Check for quotas
1708
+ if (this.isQuota(value, parentContext)) {
1709
+ return {
1710
+ type: "quota",
1711
+ value: value,
1712
+ line: position.line,
1713
+ column: position.column,
1714
+ node: node,
1715
+ context: parentContext,
1716
+ suggestion: "Move quotas to business configuration",
1717
+ };
1718
+ }
1719
+
1720
+ // Check for discount rates
1721
+ if (this.isDiscount(value, parentContext)) {
1722
+ return {
1723
+ type: "discount",
1724
+ value: value,
1725
+ line: position.line,
1726
+ column: position.column,
1727
+ node: node,
1728
+ context: parentContext,
1729
+ suggestion: "Move discount rates to pricing configuration",
1730
+ };
1731
+ }
1732
+
1733
+ // Check for trial periods
1734
+ if (this.isTrial(value, parentContext)) {
1735
+ return {
1736
+ type: "trial",
1737
+ value: value,
1738
+ line: position.line,
1739
+ column: position.column,
1740
+ node: node,
1741
+ context: parentContext,
1742
+ suggestion: "Move trial periods to subscription configuration",
1743
+ };
1744
+ }
1745
+
1746
+ // Check for metrics intervals
1747
+ if (this.isMetricsConfig(value, parentContext)) {
1748
+ return {
1749
+ type: "metrics_config",
1750
+ value: value,
1751
+ line: position.line,
1752
+ column: position.column,
1753
+ node: node,
1754
+ context: parentContext,
1755
+ suggestion: "Move metrics intervals to monitoring configuration",
1756
+ };
1757
+ }
1758
+
1759
+ // Check for alert thresholds
1760
+ if (this.isAlertThreshold(value, parentContext)) {
1761
+ return {
1762
+ type: "alert_threshold",
1763
+ value: value,
1764
+ line: position.line,
1765
+ column: position.column,
1766
+ node: node,
1767
+ context: parentContext,
1768
+ suggestion: "Move alert thresholds to monitoring configuration",
1769
+ };
1770
+ }
1771
+
1772
+ // Check for sampling rates
1773
+ if (this.isSamplingRate(value, parentContext)) {
1774
+ return {
1775
+ type: "sampling_rate",
1776
+ value: value,
1777
+ line: position.line,
1778
+ column: position.column,
1779
+ node: node,
1780
+ context: parentContext,
1781
+ suggestion: "Move sampling rates to observability configuration",
1782
+ };
1783
+ }
1784
+
1785
+ // Check for health check intervals
1786
+ if (this.isHealthCheck(value, parentContext)) {
1787
+ return {
1788
+ type: "health_check",
1789
+ value: value,
1790
+ line: position.line,
1791
+ column: position.column,
1792
+ node: node,
1793
+ context: parentContext,
1794
+ suggestion: "Move health check intervals to monitoring configuration",
1795
+ };
1796
+ }
1797
+
1798
+ // ============ Phase 4: Enhancement Numeric Configurations ============
1799
+
1800
+ // Check for message TTL
1801
+ if (this.isMessageTTL(value, parentContext)) {
1802
+ return {
1803
+ type: "message_ttl",
1804
+ value: value,
1805
+ line: position.line,
1806
+ column: position.column,
1807
+ node: node,
1808
+ context: parentContext,
1809
+ suggestion: "Move message TTL to messaging configuration",
1810
+ };
1811
+ }
1812
+
1813
+ // Check for scaling configuration
1814
+ if (this.isScalingConfig(value, parentContext)) {
1815
+ return {
1816
+ type: "scaling_config",
1817
+ value: value,
1818
+ line: position.line,
1819
+ column: position.column,
1820
+ node: node,
1821
+ context: parentContext,
1822
+ suggestion: "Move scaling configuration to deployment settings",
1823
+ };
1824
+ }
1825
+
1826
+ // ============ Additional Critical Numeric Configurations ============
1827
+
1828
+ // Check for magic numbers in business logic
1829
+ if (this.isMagicNumber(value, parentContext)) {
1830
+ return {
1831
+ type: "magic_number",
1832
+ value: value,
1833
+ line: position.line,
1834
+ column: position.column,
1835
+ node: node,
1836
+ context: parentContext,
1837
+ suggestion: "Move business constants to configuration or constants file",
1838
+ };
1839
+ }
1840
+
1841
+ // Check for pagination defaults
1842
+ if (this.isPaginationDefault(value, parentContext)) {
1843
+ return {
1844
+ type: "pagination_default",
1845
+ value: value,
1846
+ line: position.line,
1847
+ column: position.column,
1848
+ node: node,
1849
+ context: parentContext,
1850
+ suggestion: "Move pagination defaults to API configuration",
1851
+ };
1852
+ }
1853
+
1854
+ return null;
1855
+ }
1856
+
1857
+ analyzeTemplateLiteral(node, sourceFile) {
1858
+ // For now, focus on simple template literals that might contain URLs
1859
+ const templateText = node.getFullText();
1860
+ if (templateText.includes("http://") || templateText.includes("https://")) {
1861
+ const position = sourceFile.getLineAndColumnAtPos(node.getStart());
1862
+
1863
+ // Check if it's using environment variables or config
1864
+ if (!templateText.includes("process.env") && !templateText.includes("config.")) {
1865
+ return {
1866
+ type: "template_url",
1867
+ value: templateText.trim(),
1868
+ line: position.line,
1869
+ column: position.column,
1870
+ node: node,
1871
+ };
1872
+ }
1873
+ }
1874
+
1875
+ return null;
1876
+ }
1877
+
1878
+ analyzePropertyAssignment(node, sourceFile) {
1879
+ const nameNode = node.getNameNode();
1880
+ const valueNode = node.getInitializer();
1881
+
1882
+ if (!nameNode || !valueNode) return null;
1883
+
1884
+ const propertyName = nameNode.getText();
1885
+ const position = sourceFile.getLineAndColumnAtPos(node.getStart());
1886
+
1887
+ // Skip ALL field mapping objects and ORM/database entity configurations
1888
+ const ancestorObj = node.getParent();
1889
+ if (ancestorObj && Node.isObjectLiteralExpression(ancestorObj)) {
1890
+ const objParent = ancestorObj.getParent();
1891
+ if (objParent && Node.isVariableDeclaration(objParent)) {
1892
+ const varName = objParent.getName();
1893
+ // Skip field mappings, database schemas, etc.
1894
+ if (/mapping|map|field|column|decode|schema|entity|constraint|table/i.test(varName)) {
1895
+ return null;
1896
+ }
1897
+ }
1898
+
1899
+ // Check if this looks like a table column definition or field mapping
1900
+ const objText = ancestorObj.getText();
1901
+ if (/primaryKeyConstraintName|foreignKeyConstraintName|key.*may contain/i.test(objText)) {
1902
+ return null; // Skip database constraint definitions
1903
+ }
1904
+ }
1905
+
1906
+ // Skip properties that are clearly field mappings or business data
1907
+ const businessLogicProperties = [
1908
+ // Field mappings
1909
+ "key",
1910
+ "field",
1911
+ "dataKey",
1912
+ "valueKey",
1913
+ "labelKey",
1914
+ "sortKey",
1915
+ // Business logic
1916
+ "endpoint",
1917
+ "path",
1918
+ "route",
1919
+ "method",
1920
+ "limit",
1921
+ "pageSize",
1922
+ "batchSize",
1923
+ "maxResults",
1924
+ "retry",
1925
+ "retries",
1926
+ "maxRetries",
1927
+ "attempts",
1928
+ "count",
1929
+ "max",
1930
+ "min",
1931
+ "size",
1932
+ "length",
1933
+ // UI properties
1934
+ "className",
1935
+ "style",
1936
+ "disabled",
1937
+ "readonly",
1938
+ // Database/ORM
1939
+ "primaryKeyConstraintName",
1940
+ "foreignKeyConstraintName",
1941
+ "constraintName",
1942
+ "tableName",
1943
+ "columnName",
1944
+ "schemaName",
1945
+ ];
1946
+
1947
+ const lowerPropertyName = propertyName.toLowerCase();
1948
+ if (businessLogicProperties.some((prop) => lowerPropertyName.includes(prop))) {
1949
+ return null; // Skip these completely
1950
+ }
1951
+
1952
+ // Only check for CLEARLY environment-dependent properties
1953
+ const trulyEnvironmentDependentProps = [
1954
+ "baseurl",
1955
+ "baseURL",
1956
+ "host",
1957
+ "hostname",
1958
+ "server",
1959
+ "endpoint",
1960
+ "apikey",
1961
+ "api_key",
1962
+ "secret_key",
1963
+ "client_secret",
1964
+ "database",
1965
+ "connectionstring",
1966
+ "dbhost",
1967
+ "dbport",
1968
+ "port",
1969
+ "timeout", // Only when they have suspicious values
1970
+ "bucket",
1971
+ "region", // Cloud-specific
1972
+ "clientid",
1973
+ "tenantid", // OAuth-specific
1974
+ ];
1975
+
1976
+ if (!trulyEnvironmentDependentProps.some((prop) => lowerPropertyName.includes(prop))) {
1977
+ return null; // Not clearly environment-dependent
1978
+ }
1979
+
1980
+ let value = null;
1981
+ let configType = null;
1982
+
1983
+ if (valueNode.getKind() === SyntaxKind.StringLiteral) {
1984
+ value = valueNode.getLiteralValue();
1985
+
1986
+ // Only flag URLs or clearly sensitive values
1987
+ if (this.configPatterns.urls.regex.test(value) && this.isEnvironmentDependentUrl(value)) {
1988
+ configType = "url";
1989
+ } else if (this.isRealCredential(value, propertyName)) {
1990
+ configType = "credential";
1991
+ } else {
1992
+ return null; // Skip other string values
1993
+ }
1994
+ } else if (valueNode.getKind() === SyntaxKind.NumericLiteral) {
1995
+ value = valueNode.getLiteralValue();
1996
+ const parentContext = this.getParentContext(node);
1997
+
1998
+ // Only flag numbers that are clearly environment-dependent
1999
+ if (this.configPatterns.environmentNumbers.isEnvironmentDependent(value, parentContext)) {
2000
+ configType = "environment_config";
2001
+ } else {
2002
+ return null;
2003
+ }
2004
+ } else {
2005
+ return null; // Skip other value types
2006
+ }
2007
+
2008
+ if (configType) {
2009
+ return {
2010
+ type: configType,
2011
+ value: value,
2012
+ line: position.line,
2013
+ column: position.column,
2014
+ node: node,
2015
+ propertyName: propertyName,
2016
+ };
2017
+ }
2018
+
2019
+ return null;
2020
+ }
2021
+
2022
+ analyzeVariableDeclaration(node, sourceFile) {
2023
+ const nameNode = node.getNameNode();
2024
+ const initializer = node.getInitializer();
2025
+
2026
+ if (!nameNode || !initializer) return null;
2027
+
2028
+ const variableName = nameNode.getText();
2029
+ const position = sourceFile.getLineAndColumnAtPos(node.getStart());
2030
+
2031
+ // Check if variable name suggests environment-dependent configuration
2032
+ if (this.isEnvironmentDependentProperty(variableName)) {
2033
+ let value = null;
2034
+
2035
+ if (initializer.getKind() === SyntaxKind.StringLiteral) {
2036
+ value = initializer.getLiteralValue();
2037
+ } else if (initializer.getKind() === SyntaxKind.NumericLiteral) {
2038
+ value = initializer.getLiteralValue();
2039
+ }
2040
+
2041
+ if (value !== null && this.looksLikeEnvironmentConfig(variableName, value)) {
2042
+ return {
2043
+ type: "variable_config",
2044
+ value: value,
2045
+ line: position.line,
2046
+ column: position.column,
2047
+ node: node,
2048
+ variableName: variableName,
2049
+ };
2050
+ }
2051
+ }
2052
+
2053
+ return null;
2054
+ }
2055
+
2056
+ getParentContext(node) {
2057
+ // Get surrounding context to understand the purpose of the literal
2058
+ let parent = node.getParent();
2059
+ let context = "";
2060
+
2061
+ // Check if this is a method call argument or property access
2062
+ while (parent && context.length < 100) {
2063
+ const parentText = parent.getText();
2064
+
2065
+ // If parent is CallExpression and this node is an argument, it might be a config key
2066
+ if (parent.getKind() === SyntaxKind.CallExpression) {
2067
+ const callExpr = parent;
2068
+ const methodName = this.getMethodName(callExpr);
2069
+ if (["get", "getBoolean", "getNumber", "getArray", "getString"].includes(methodName)) {
2070
+ return `config.${methodName}()`; // This indicates it's a config key
2071
+ }
2072
+ }
2073
+
2074
+ if (parentText.length < 200) {
2075
+ context = parentText;
2076
+ break;
2077
+ }
2078
+ parent = parent.getParent();
2079
+ }
2080
+
2081
+ return context;
2082
+ }
2083
+
2084
+ getMethodName(callExpression) {
2085
+ const expression = callExpression.getExpression();
2086
+ if (expression.getKind() === SyntaxKind.PropertyAccessExpression) {
2087
+ return expression.getName();
2088
+ }
2089
+ if (expression.getKind() === SyntaxKind.Identifier) {
2090
+ return expression.getText();
2091
+ }
2092
+ return "";
2093
+ }
2094
+
2095
+ isExcludedUrl(value, node) {
2096
+ return this.configPatterns.urls.exclude.some((pattern) => pattern.test(value));
2097
+ }
2098
+
2099
+ isExcludedCredential(value, node) {
2100
+ return this.configPatterns.credentials.exclude.some((pattern) => pattern.test(value));
2101
+ }
2102
+
2103
+ containsCredentialKeyword(context) {
2104
+ const lowerContext = context.toLowerCase();
2105
+
2106
+ // Skip if this looks like a header name or property key definition
2107
+ if (context.includes("':") || context.includes('": ') || context.includes(" = ")) {
2108
+ // This might be a key-value pair where the string is the key
2109
+ return false;
2110
+ }
2111
+
2112
+ return this.configPatterns.credentials.keywords.some((keyword) => lowerContext.includes(keyword));
2113
+ }
2114
+
2115
+ looksLikeUIValue(value, context) {
2116
+ // Check if it's likely a UI-related value (like input type, label, etc.)
2117
+ const uiKeywords = ["input", "type", "field", "label", "placeholder", "text", "button"];
2118
+ const lowerContext = context.toLowerCase();
2119
+ return uiKeywords.some((keyword) => lowerContext.includes(keyword));
2120
+ }
2121
+
2122
+ looksLikeConfigKey(value) {
2123
+ // Check if it looks like a config key path (e.g., 'api.baseUrl', 'features.newUI')
2124
+ if (/^[a-zA-Z][a-zA-Z0-9]*\.[a-zA-Z][a-zA-Z0-9]*(\.[a-zA-Z][a-zA-Z0-9]*)*$/.test(value)) {
2125
+ return true;
2126
+ }
2127
+
2128
+ // Check for other config key patterns
2129
+ const configKeyPatterns = [
2130
+ /^[a-zA-Z][a-zA-Z0-9]*\.[a-zA-Z]/, // dotted notation like 'api.url'
2131
+ /^[A-Z_][A-Z0-9_]*$/, // CONSTANT_CASE like 'API_URL'
2132
+ /^get[A-Z]/, // getter methods like 'getApiUrl'
2133
+ /^config\./, // config namespace
2134
+ /^settings\./, // settings namespace
2135
+ /^env\./, // env namespace
2136
+ ];
2137
+
2138
+ return configKeyPatterns.some((pattern) => pattern.test(value));
2139
+ }
2140
+
2141
+ isPropertyKey(node) {
2142
+ // Check if this string literal is used as a property key in an object literal
2143
+ const parent = node.getParent();
2144
+
2145
+ // If parent is PropertyAssignment and this node is the name, it's a property key
2146
+ if (parent && parent.getKind() === SyntaxKind.PropertyAssignment) {
2147
+ const nameNode = parent.getNameNode();
2148
+ return nameNode === node;
2149
+ }
2150
+
2151
+ return false;
2152
+ }
2153
+
2154
+ isImportPath(value, node) {
2155
+ // Check if this is likely an import path or module name
2156
+ const parent = node.getParent();
2157
+
2158
+ // Check if it's in an import statement
2159
+ let currentNode = parent;
2160
+ while (currentNode) {
2161
+ const kind = currentNode.getKind();
2162
+ if (
2163
+ kind === SyntaxKind.ImportDeclaration ||
2164
+ kind === SyntaxKind.ExportDeclaration ||
2165
+ kind === SyntaxKind.CallExpression
2166
+ ) {
2167
+ const text = currentNode.getText();
2168
+ if (text.includes("require(") || text.includes("import ") || text.includes("from ")) {
2169
+ return true;
2170
+ }
2171
+ }
2172
+ currentNode = currentNode.getParent();
2173
+ }
2174
+
2175
+ // Check for common import path patterns
2176
+ return (
2177
+ /^[@a-z][a-z0-9\-_]*\/|^[a-z][a-z0-9\-_]*$|^\.{1,2}\//.test(value) ||
2178
+ value.endsWith(".js") ||
2179
+ value.endsWith(".ts") ||
2180
+ value.endsWith(".json") ||
2181
+ value.endsWith(".css") ||
2182
+ value.endsWith(".scss") ||
2183
+ value.endsWith(".html")
2184
+ );
2185
+ }
2186
+
2187
+ isUIString(value) {
2188
+ // Check against predefined UI string patterns, but don't skip credentials
2189
+ if (
2190
+ typeof value === "string" &&
2191
+ value.length > 20 &&
2192
+ (/token|key|secret|bearer|auth/i.test(value) || /^[a-f0-9-]{30,}$/i.test(value))
2193
+ ) {
2194
+ // Don't skip potential credentials/tokens even if they contain UI keywords
2195
+ return false;
2196
+ }
2197
+
2198
+ return this.UI_STRINGS.some((pattern) => {
2199
+ if (typeof pattern === "string") {
2200
+ return value === pattern; // Exact match only, not includes
2201
+ } else {
2202
+ return pattern.test(value);
2203
+ }
2204
+ });
2205
+ }
2206
+
2207
+ isJapaneseUIText(value, context) {
2208
+ if (typeof value !== "string") return false;
2209
+
2210
+ // Check for Japanese/CJK characters
2211
+ const hasJapanese = /[\u3000-\u303f\u3040-\u309f\u30a0-\u30ff\uff00-\uff9f\u4e00-\u9faf]/.test(value);
2212
+ if (!hasJapanese) return false;
2213
+
2214
+ // Japanese particles that indicate UI text
2215
+ const japaneseParticles = /[はがをにでとへのもやけだ]/;
2216
+ if (japaneseParticles.test(value)) return true;
2217
+
2218
+ // Common Japanese UI terms
2219
+ const japaneseUITerms = [
2220
+ /ID$/, // xxxID
2221
+ /名$/, // xxx名 (name)
2222
+ /用/, // xxx用 (for xxx)
2223
+ /番号/, // 番号 (number)
2224
+ /情報/, // 情報 (information)
2225
+ /設定/, // 設定 (settings)
2226
+ /画面/, // 画面 (screen)
2227
+ /ボタン/, // ボタン (button)
2228
+ /入力/, // 入力 (input)
2229
+ /選択/, // 選択 (selection)
2230
+ /一覧/, // 一覧 (list)
2231
+ /詳細/, // 詳細 (details)
2232
+ /確認/, // 確認 (confirmation)
2233
+ /登録/, // 登録 (registration)
2234
+ /更新/, // 更新 (update)
2235
+ /削除/, // 削除 (delete)
2236
+ /検索/, // 検索 (search)
2237
+ /表示/, // 表示 (display)
2238
+ /メール/, // メール (email)
2239
+ /電話/, // 電話 (phone)
2240
+ /住所/, // 住所 (address)
2241
+ /連絡/, // 連絡 (contact)
2242
+ /会社/, // 会社 (company)
2243
+ /工場/, // 工場 (factory)
2244
+ /整備/, // 整備 (maintenance)
2245
+ ];
2246
+
2247
+ if (japaneseUITerms.some((pattern) => pattern.test(value))) return true;
2248
+
2249
+ // Check if context suggests it's a label or placeholder
2250
+ const lowerContext = context.toLowerCase();
2251
+ const labelPatterns = [
2252
+ /label|placeholder|title|heading|text/i,
2253
+ /register.*label|field.*label/i,
2254
+ /header.*text|tooltip/i,
2255
+ ];
2256
+
2257
+ if (hasJapanese && labelPatterns.some((pattern) => pattern.test(context))) {
2258
+ return true;
2259
+ }
2260
+
2261
+ return false;
2262
+ }
2263
+
2264
+ isFormFieldLabel(value, context) {
2265
+ if (typeof value !== "string") return false;
2266
+
2267
+ const lowerContext = context.toLowerCase();
2268
+
2269
+ // Check if it's in a form field context
2270
+ const formFieldPatterns = [
2271
+ /register.*label|label.*register/i,
2272
+ /field.*label|label.*field/i,
2273
+ /form.*label|label.*form/i,
2274
+ /input.*label|label.*input/i,
2275
+ /placeholder|tooltip|helper.*text/i,
2276
+ /error.*message|validation.*message/i,
2277
+ /\.label\b|label:/i,
2278
+ ];
2279
+
2280
+ if (formFieldPatterns.some((pattern) => pattern.test(context))) {
2281
+ return true;
2282
+ }
2283
+
2284
+ // Check for React Hook Form's register with label
2285
+ if (context.includes("register(") && context.includes("label")) {
2286
+ return true;
2287
+ }
2288
+
2289
+ return false;
2290
+ }
2291
+
2292
+ isTestData(value, context) {
2293
+ // Check if variable name suggests it's test/mock data
2294
+ const lowerContext = context.toLowerCase();
2295
+ const testVarPatterns = [
2296
+ /mock[a-z]/i, // mockApiKey, mockUser
2297
+ /test[a-z]/i, // testUser, testKey
2298
+ /dummy[a-z]/i, // dummyData
2299
+ /sample[a-z]/i, // sampleData
2300
+ /fake[a-z]/i, // fakeKey
2301
+ /example[a-z]/i, // exampleToken
2302
+ ];
2303
+
2304
+ if (testVarPatterns.some((pattern) => pattern.test(context))) {
2305
+ return true;
2306
+ }
2307
+
2308
+ // Don't skip credentials/tokens even in dummy files if they look real
2309
+ if (
2310
+ typeof value === "string" &&
2311
+ value.length > 20 &&
2312
+ (/^sk-live|^pk-live|^prod-|^production-/i.test(value) || // Clearly production keys
2313
+ /^[a-f0-9]{32,}$/i.test(value) || // Long hex strings
2314
+ /^[A-Za-z0-9+/]{40,}={0,2}$/i.test(value)) // Base64 tokens
2315
+ ) {
2316
+ return false; // Don't skip potential real credentials
2317
+ }
2318
+
2319
+ // Check for test patterns in value - but be more restrictive
2320
+ if (this.TEST_PATTERNS.some((pattern) => pattern.test(value))) {
2321
+ // Only skip if it's clearly test data, not production dummy data
2322
+ const isInTestFile =
2323
+ /\.(test|spec)\.(ts|tsx|js|jsx)$/i.test(context) ||
2324
+ /\/__tests__\//i.test(context) ||
2325
+ /\/test\//i.test(context);
2326
+ return isInTestFile;
2327
+ }
2328
+
2329
+ // Check for test context
2330
+ const testKeywords = ["test", "spec", "mock", "fixture", "stub", "describe", "it("];
2331
+ return testKeywords.some((keyword) => lowerContext.includes(keyword));
2332
+ }
2333
+
2334
+ isValidationMessage(value, context) {
2335
+ // Skip validation/error messages
2336
+ const validationPatterns = [
2337
+ /must contain|should contain|invalid|error|required|missing/i,
2338
+ /password|username|email/i, // Common validation contexts
2339
+ /^[A-Z][a-z\s]{10,}$/, // Sentence-like messages
2340
+ /\s(at least|one|letter|uppercase|lowercase|numeric)/i,
2341
+ ];
2342
+
2343
+ return (
2344
+ validationPatterns.some((pattern) => pattern.test(value)) ||
2345
+ /message|error|validation|description/i.test(context)
2346
+ );
2347
+ }
2348
+
2349
+ isFileNameOrDescription(value, context) {
2350
+ // Skip file names and descriptions
2351
+ const filePatterns = [
2352
+ /\.(csv|json|xml|txt|md)$/i,
2353
+ /^[a-z_\-]+\.(csv|json|xml|txt)$/i,
2354
+ /description|comment|note|foreign key|identity/i,
2355
+ ];
2356
+
2357
+ return (
2358
+ filePatterns.some((pattern) => pattern.test(value)) ||
2359
+ /description|comment|note|identity|foreign|table/i.test(context)
2360
+ );
2361
+ }
2362
+
2363
+ isEnvironmentDependentUrl(value) {
2364
+ // Only flag URLs that are likely to differ between environments
2365
+ const envDependentPatterns = [
2366
+ /\.amazonaws\.com/, // AWS services
2367
+ /\.azure\.com/, // Azure services
2368
+ /\.googleapis\.com/, // Google services
2369
+ /\.herokuapp\.com/, // Heroku apps
2370
+ /\.vercel\.app/, // Vercel deployments
2371
+ /\.netlify\.app/, // Netlify deployments
2372
+ /api\./, // API endpoints
2373
+ /auth\./, // Auth services
2374
+ /\.dev|\.staging|\.prod/i, // Environment-specific domains
2375
+ /\/api\/v\d+/, // Versioned API endpoints
2376
+ /https?:\/\/[^\/]+\.com\/api/, // External API endpoints
2377
+ ];
2378
+
2379
+ // Skip localhost and development URLs
2380
+ if (/localhost|127\.0\.0\.1|0\.0\.0\.0/.test(value)) {
2381
+ return false;
2382
+ }
2383
+
2384
+ return envDependentPatterns.some((pattern) => pattern.test(value));
2385
+ }
2386
+
2387
+ isRealCredential(value, context) {
2388
+ // Check for real credentials, not validation messages
2389
+ const credentialKeywords = this.configPatterns.credentials.keywords;
2390
+ const lowerContext = context.toLowerCase();
2391
+
2392
+ // Must have credential keyword in context
2393
+ if (!credentialKeywords.some((keyword) => lowerContext.includes(keyword))) {
2394
+ return false;
2395
+ }
2396
+
2397
+ // Skip property/field names (camelCase identifiers)
2398
+ // These are field names, not actual credential values
2399
+ if (/^[a-z][a-zA-Z0-9]*$/.test(value)) {
2400
+ // Common field name patterns that are not credentials
2401
+ const fieldNamePatterns = [
2402
+ /^(is|has|can|should)[A-Z]/, // isJoinFPL, hasAccess
2403
+ /Flg$/i, // loginFlg, deleteFlg
2404
+ /Id$/i, // userId, accountId
2405
+ /Key$/i, // sortKey, primaryKey
2406
+ /Name$/i, // userName, keyName
2407
+ /Code$/i, // errorCode, statusCode
2408
+ /Type$/i, // tokenType, keyType
2409
+ /Status$/i, // loginStatus
2410
+ /^[a-z]+[A-Z][a-z]+$/, // General camelCase
2411
+ ];
2412
+
2413
+ if (fieldNamePatterns.some((pattern) => pattern.test(value))) {
2414
+ return false;
2415
+ }
2416
+ }
2417
+
2418
+ // Skip URL paths (routes)
2419
+ if (/^\/[a-z-/]*$/.test(value)) {
2420
+ return false; // /login, /verify-login, etc.
2421
+ }
2422
+
2423
+ // Skip Japanese UI text and labels
2424
+ if (this.isJapaneseUIText(value, context)) {
2425
+ return false;
2426
+ }
2427
+
2428
+ // Skip form field labels
2429
+ if (this.isFormFieldLabel(value, context)) {
2430
+ return false;
2431
+ }
2432
+
2433
+ // Skip if it's excluded (validation messages, etc.)
2434
+ if (this.configPatterns.credentials.exclude.some((pattern) => pattern.test(value))) {
2435
+ return false;
2436
+ }
2437
+
2438
+ // Skip validation messages and descriptions
2439
+ if (this.isValidationMessage(value, context)) {
2440
+ return false;
2441
+ }
2442
+
2443
+ // Real credentials are usually:
2444
+ // - Random strings with mix of chars: abc123XYZ789
2445
+ // - Long alphanumeric: sk-1234567890abcdef
2446
+ // - Base64 encoded: YWJjZGVmZ2hpams=
2447
+ // - UUID format: 550e8400-e29b-41d4-a716-446655440000
2448
+ const looksLikeRealCredential =
2449
+ /^[a-zA-Z0-9+/]{32,}={0,2}$/.test(value) || // Base64
2450
+ /^[a-f0-9]{32,}$/.test(value) || // Hex
2451
+ /^[a-z]{2,4}-[a-zA-Z0-9]{20,}$/.test(value) || // Prefixed keys: sk-..., pk-...
2452
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value); // UUID
2453
+
2454
+ // Must be reasonably long and look like an actual credential value
2455
+ return value.length >= 16 && looksLikeRealCredential && !this.looksLikeUIValue(value, context);
2456
+ }
2457
+
2458
+ isEnvironmentDependentProperty(propertyName) {
2459
+ // Skip UI/framework related property names
2460
+ const uiPropertyPatterns = [
2461
+ /^key[A-Z]/, // keyXxx (UI field keys)
2462
+ /^field[A-Z]/, // fieldXxx
2463
+ /^prop[A-Z]/, // propXxx
2464
+ /^data[A-Z]/, // dataXxx
2465
+ /CheckDisplay/, // UI display control keys
2466
+ /InputPossible/, // UI input control keys
2467
+ /Flag$/, // UI flags
2468
+ /Class$/, // CSS classes
2469
+ /^(disabled|readonly|active)Class$/i, // UI state classes
2470
+ ];
2471
+
2472
+ if (uiPropertyPatterns.some((pattern) => pattern.test(propertyName))) {
2473
+ return false;
2474
+ }
2475
+
2476
+ // Properties that are likely to differ between environments
2477
+ const envDependentProps = [
2478
+ "baseurl",
2479
+ "baseURL",
2480
+ "host",
2481
+ "hostname",
2482
+ "server",
2483
+ "apikey",
2484
+ "api_key",
2485
+ "secret",
2486
+ "token",
2487
+ "password",
2488
+ "credential",
2489
+ "database",
2490
+ "db",
2491
+ "connection",
2492
+ "connectionstring",
2493
+ "timeout", // Only long timeouts
2494
+ "port", // Only non-standard ports
2495
+ "authorization",
2496
+ "auth",
2497
+ "authentication", // Auth headers and codes
2498
+ "apptoken",
2499
+ "devicetoken",
2500
+ "accesstoken",
2501
+ "refreshtoken", // App tokens
2502
+ "code",
2503
+ "hash",
2504
+ "signature",
2505
+ "key", // Various security values
2506
+ "clientsecret",
2507
+ "clientid",
2508
+ "sessionkey", // OAuth and session
2509
+ "requestid",
2510
+ "sessionid",
2511
+ "transactionid",
2512
+ "otp", // Request/session tracking
2513
+ ];
2514
+
2515
+ const lowerName = propertyName.toLowerCase();
2516
+ return envDependentProps.some((prop) => lowerName.includes(prop));
2517
+ }
2518
+
2519
+ looksLikeEnvironmentConfig(propertyName, value) {
2520
+ // Check if this property/value combination looks like environment config
2521
+ const lowerPropertyName = propertyName.toLowerCase();
2522
+
2523
+ if (typeof value === "string") {
2524
+ // Skip test data (common test passwords, etc.)
2525
+ const testDataPatterns = [
2526
+ /^(password123|test123|admin123|user123|wrongpassword|testpassword)$/i,
2527
+ /^(test|mock|dummy|sample|example)/i,
2528
+ /^\/(api|mock|test)/, // Test API paths
2529
+ /^[a-z]+\d+$/i, // Simple test values like 'user1', 'test2'
2530
+ ];
2531
+
2532
+ // Check if variable name suggests it's test/mock data
2533
+ const isMockVariable = /^(mock|test|dummy|sample|fake|example)[A-Z]/i.test(propertyName);
2534
+ if (isMockVariable) {
2535
+ return false; // Skip mock variables even if they contain credentials
2536
+ }
2537
+
2538
+ // Don't skip common test patterns if they appear in credential contexts
2539
+ const isCredentialContext = /token|key|secret|auth|otp|code|password|credential/i.test(propertyName);
2540
+
2541
+ if (!isCredentialContext && testDataPatterns.some((pattern) => pattern.test(value))) {
2542
+ return false;
2543
+ }
2544
+
2545
+ // Skip object property paths and field names
2546
+ const propertyPathPatterns = [
2547
+ /^[a-zA-Z][a-zA-Z0-9]*(\[[0-9]+\])?\.[a-zA-Z][a-zA-Z0-9]*$/, // obj[0].prop, obj.prop
2548
+ /^[a-zA-Z][a-zA-Z0-9]*\.[a-zA-Z][a-zA-Z0-9]*(\.[a-zA-Z][a-zA-Z0-9]*)*$/, // obj.prop.subprop
2549
+ /^[a-zA-Z][a-zA-Z0-9]*(\[[0-9]+\])+$/, // obj[0], obj[0][1]
2550
+ /^(key|field|prop|data)[A-Z]/, // keyXxx, fieldXxx, propXxx, dataXxx
2551
+ /CheckDisplay|InputPossible|Flag$/i, // Common UI field patterns
2552
+ /^exflg|^flg|Support$/i, // Business logic flags
2553
+ ];
2554
+
2555
+ if (propertyPathPatterns.some((pattern) => pattern.test(value))) {
2556
+ return false;
2557
+ }
2558
+
2559
+ // Skip CSS classes and UI constants
2560
+ const uiPatterns = [
2561
+ /^bg-|text-|cursor-|border-|flex-|grid-/, // CSS classes
2562
+ /^(disabled|readonly|active|inactive)$/i, // UI states
2563
+ /class$/i, // className values
2564
+ ];
2565
+
2566
+ if (uiPatterns.some((pattern) => pattern.test(value))) {
2567
+ return false;
2568
+ }
2569
+
2570
+ // Skip internal system identifiers (queue names, service names, route names)
2571
+ const systemIdentifierPatterns = [
2572
+ /-queue$/i, // Queue names
2573
+ /-task$/i, // Task names
2574
+ /-activity$/i, // Activity names
2575
+ /-service$/i, // Service names
2576
+ /-worker$/i, // Worker names
2577
+ /^[A-Z_]+_QUEUE$/, // CONSTANT_QUEUE names
2578
+ /^[A-Z_]+_TASK$/, // CONSTANT_TASK names
2579
+ /^(register|login|logout|reset-password|verify|update)$/i, // Route names
2580
+ /password|token/i && /invalid|expired|attempts|exceeded/i, // Error messages
2581
+ ];
2582
+
2583
+ if (systemIdentifierPatterns.some((pattern) => pattern.test(value))) {
2584
+ return false;
2585
+ }
2586
+
2587
+ // Skip error messages and validation messages
2588
+ const messagePatterns = [
2589
+ /invalid|expired|exceeded|failed|error|success/i,
2590
+ /attempts|required|missing|not found/i,
2591
+ /^[A-Z][a-z\s]{10,}$/, // Sentence-like messages
2592
+ /は|が|を|に|で|と/, // Japanese particles (UI text)
2593
+ /情報|画面|ボタン|入力/, // Japanese UI terms
2594
+ ];
2595
+
2596
+ if (messagePatterns.some((pattern) => pattern.test(value))) {
2597
+ return false;
2598
+ }
2599
+
2600
+ // URLs are environment-dependent
2601
+ if (this.configPatterns.urls.regex.test(value)) {
2602
+ return this.isEnvironmentDependentUrl(value);
2603
+ }
2604
+
2605
+ // Credentials - but exclude test data
2606
+ if (
2607
+ lowerPropertyName.includes("key") ||
2608
+ lowerPropertyName.includes("secret") ||
2609
+ lowerPropertyName.includes("token") ||
2610
+ lowerPropertyName.includes("password")
2611
+ ) {
2612
+ return value.length > 10; // Real secrets are usually longer
2613
+ }
2614
+
2615
+ // Skip short endpoint names or simple strings
2616
+ if (value.length < 10 && !value.includes(".") && !value.includes("/")) {
2617
+ return false;
2618
+ }
2619
+ }
2620
+
2621
+ if (typeof value === "number") {
2622
+ // Only flag environment-dependent numbers
2623
+ return this.configPatterns.environmentNumbers.isEnvironmentDependent(value, propertyName);
2624
+ }
2625
+
2626
+ return true;
2627
+ }
2628
+
2629
+ isCommonConstant(value) {
2630
+ // Common constants that are usually OK to hardcode
2631
+ const commonConstants = [100, 200, 300, 400, 500, 1000, 2000, 3000, 5000, 8080, 3000];
2632
+ return commonConstants.includes(value);
2633
+ }
2634
+
2635
+ isConfigProperty(propertyName) {
2636
+ const configProps = [
2637
+ "url",
2638
+ "endpoint",
2639
+ "baseurl",
2640
+ "apiurl",
2641
+ "host",
2642
+ "port",
2643
+ "timeout",
2644
+ "interval",
2645
+ "delay",
2646
+ "retry",
2647
+ "retries",
2648
+ "username",
2649
+ "password",
2650
+ "apikey",
2651
+ "secret",
2652
+ "token",
2653
+ "database",
2654
+ "connection",
2655
+ "connectionstring",
2656
+ "maxsize",
2657
+ "batchsize",
2658
+ "pagesize",
2659
+ "limit",
2660
+ ];
2661
+
2662
+ const lowerName = propertyName.toLowerCase();
2663
+ return configProps.some((prop) => lowerName.includes(prop));
2664
+ }
2665
+
2666
+ isConfigVariable(variableName) {
2667
+ const configVars = [
2668
+ "api",
2669
+ "url",
2670
+ "endpoint",
2671
+ "host",
2672
+ "port",
2673
+ "timeout",
2674
+ "interval",
2675
+ "delay",
2676
+ "retry",
2677
+ "config",
2678
+ "setting",
2679
+ "constant",
2680
+ ];
2681
+
2682
+ const lowerName = variableName.toLowerCase();
2683
+ return configVars.some((var_) => lowerName.includes(var_));
2684
+ }
2685
+
2686
+ looksLikeHardcodedConfig(name, value) {
2687
+ // Skip obvious constants and UI values
2688
+ if (typeof value === "string") {
2689
+ if (value.length < 3) return false;
2690
+ if (/^(ok|yes|no|true|false|success|error|info|warn)$/i.test(value)) return false;
2691
+ }
2692
+
2693
+ if (typeof value === "number") {
2694
+ if (this.isCommonConstant(value)) return false;
2695
+ }
2696
+
2697
+ return true;
2698
+ }
2699
+
2700
+ // Helper methods for specific configuration types
2701
+ isTimeout(value, context) {
2702
+ const lowerContext = context.toLowerCase();
2703
+ return typeof value === "number" && value > 1000 && /timeout|delay|wait|duration/i.test(context);
2704
+ }
2705
+
2706
+ isRetryInterval(value, context) {
2707
+ const lowerContext = context.toLowerCase();
2708
+ return typeof value === "number" && value > 100 && /retry|interval|backoff|attempt/i.test(context);
2709
+ }
2710
+
2711
+ // Phase 1 extensions - Security & Infrastructure
2712
+ isCorsOrigin(value, context) {
2713
+ if (typeof value !== "string") return false;
2714
+ const lowerContext = context.toLowerCase();
2715
+
2716
+ // Check if it's a URL pattern that matches CORS origin
2717
+ const matchesPattern = this.configPatterns.security.corsOrigins.patterns.some((pattern) =>
2718
+ pattern.test(value)
2719
+ );
2720
+
2721
+ if (!matchesPattern) return false;
2722
+
2723
+ // If context contains CORS-related keywords, it's definitely a CORS origin
2724
+ if (this.configPatterns.security.corsOrigins.keywords.some((keyword) => lowerContext.includes(keyword))) {
2725
+ return true;
2726
+ }
2727
+
2728
+ // Also check variable name patterns for CORS/allowed origins
2729
+ if (/cors|allowed.*origin|origin.*allowed/i.test(context)) {
2730
+ return true;
2731
+ }
2732
+
2733
+ return false;
2734
+ }
2735
+
2736
+ isSessionConfig(value, context) {
2737
+ const lowerContext = context.toLowerCase();
2738
+
2739
+ // Check keywords
2740
+ if (
2741
+ !this.configPatterns.security.sessionConfig.keywords.some((keyword) => lowerContext.includes(keyword))
2742
+ ) {
2743
+ return false;
2744
+ }
2745
+
2746
+ // Check for time patterns
2747
+ if (typeof value === "string") {
2748
+ return this.configPatterns.security.sessionConfig.timePatterns.some((pattern) => pattern.test(value));
2749
+ }
2750
+
2751
+ // Check for numeric values (seconds)
2752
+ if (typeof value === "number" && value > 300) {
2753
+ // > 5 minutes
2754
+ return /session|jwt|token|expiry|expire/i.test(context);
2755
+ }
2756
+
2757
+ return false;
2758
+ }
2759
+
2760
+ isCacheConfig(value, context) {
2761
+ const lowerContext = context.toLowerCase();
2762
+
2763
+ if (
2764
+ !this.configPatterns.infrastructure.caching.keywords.some((keyword) => lowerContext.includes(keyword))
2765
+ ) {
2766
+ return false;
2767
+ }
2768
+
2769
+ if (typeof value === "string") {
2770
+ // Check for Redis prefixes
2771
+ return this.configPatterns.infrastructure.caching.patterns.some((pattern) => pattern.test(value));
2772
+ }
2773
+
2774
+ if (typeof value === "number" && value > 60) {
2775
+ // TTL > 1 minute
2776
+ return /cache|ttl|expire/i.test(context);
2777
+ }
2778
+
2779
+ return false;
2780
+ }
2781
+
2782
+ isLogLevel(value, context) {
2783
+ if (typeof value !== "string") return false;
2784
+ const lowerContext = context.toLowerCase();
2785
+
2786
+ // Check if value is a valid log level
2787
+ const isValidLevel = this.configPatterns.infrastructure.logging.levels.includes(value.toLowerCase());
2788
+ if (!isValidLevel) return false;
2789
+
2790
+ // Check if context suggests it's a log level configuration
2791
+ return /log.*level|level.*log|^log_level$|^loglevel$/i.test(context);
2792
+ }
2793
+
2794
+ isPerformanceConfig(value, context) {
2795
+ const lowerContext = context.toLowerCase();
2796
+
2797
+ if (typeof value !== "number" || value <= 1) {
2798
+ return false;
2799
+ }
2800
+
2801
+ // Check for performance-related keywords in context
2802
+ const hasKeyword = this.configPatterns.infrastructure.performance.keywords.some((keyword) =>
2803
+ lowerContext.includes(keyword)
2804
+ );
2805
+
2806
+ if (!hasKeyword) return false;
2807
+
2808
+ // Check for specific performance patterns
2809
+ return this.configPatterns.infrastructure.performance.contextPatterns.some((pattern) =>
2810
+ pattern.test(context)
2811
+ );
2812
+ }
2813
+
2814
+ isEnvironmentName(value, context) {
2815
+ if (typeof value !== "string") return false;
2816
+ const lowerContext = context.toLowerCase();
2817
+
2818
+ // Check if value is a known environment name
2819
+ const isEnvName =
2820
+ this.configPatterns.environments.names.includes(value.toLowerCase()) ||
2821
+ this.configPatterns.environments.patterns.some((pattern) => pattern.test(value));
2822
+
2823
+ if (!isEnvName) return false;
2824
+
2825
+ // Check if context suggests it's environment configuration
2826
+ return /environment|env|node_env|current.*env/i.test(context);
2827
+ }
2828
+
2829
+ isServiceDependency(value, context) {
2830
+ if (typeof value !== "string") return false;
2831
+ const lowerContext = context.toLowerCase();
2832
+
2833
+ return (
2834
+ this.configPatterns.services.keywords.some((keyword) => lowerContext.includes(keyword)) &&
2835
+ this.configPatterns.services.patterns.some((pattern) => pattern.test(value))
2836
+ );
2837
+ }
2838
+
2839
+ isBatchSize(value, context) {
2840
+ const lowerContext = context.toLowerCase();
2841
+
2842
+ // Skip UI/display related sizes
2843
+ if (/ui|display|view|page.*size|page.*limit|default.*page/i.test(context)) {
2844
+ return false;
2845
+ }
2846
+
2847
+ // Skip small values that are likely UI constants (pagination, etc.)
2848
+ if (typeof value === "number" && value <= 50 && /page|size|limit/i.test(context)) {
2849
+ return false;
2850
+ }
2851
+
2852
+ // Only flag larger batch sizes for processing
2853
+ return (
2854
+ typeof value === "number" &&
2855
+ value > 100 &&
2856
+ /batch|chunk|buffer|pool|queue|processing/i.test(context) &&
2857
+ !/ui|display|view|page/i.test(context)
2858
+ );
2859
+ }
2860
+
2861
+ isThreshold(value, context) {
2862
+ const lowerContext = context.toLowerCase();
2863
+ return (
2864
+ typeof value === "number" &&
2865
+ value > 0 &&
2866
+ this.configPatterns.thresholds.contextPatterns.some((pattern) => pattern.test(context))
2867
+ );
2868
+ }
2869
+
2870
+ isFeatureFlag(context) {
2871
+ const lowerContext = context.toLowerCase();
2872
+ return (
2873
+ this.configPatterns.featureFlags.keywords.some((keyword) => lowerContext.includes(keyword)) ||
2874
+ this.configPatterns.featureFlags.patterns.some((pattern) => pattern.test(context))
2875
+ );
2876
+ }
2877
+
2878
+ // ============ Phase 2: Critical Configuration Helpers ============
2879
+
2880
+ // Database & Storage Configuration helpers
2881
+ isDatabasePoolConfig(value, context) {
2882
+ const lowerContext = context.toLowerCase();
2883
+ if (typeof value !== "number" || value <= 0) return false;
2884
+
2885
+ return this.configPatterns.database.poolConfig.patterns.some((pattern) => pattern.test(context));
2886
+ }
2887
+
2888
+ isQueryConfig(value, context) {
2889
+ const lowerContext = context.toLowerCase();
2890
+
2891
+ if (typeof value === "number" && value > 1000) {
2892
+ return this.configPatterns.database.queryConfig.patterns.some((pattern) => pattern.test(context));
2893
+ }
2894
+
2895
+ if (typeof value === "string") {
2896
+ return this.configPatterns.database.queryConfig.patterns.some((pattern) => pattern.test(context));
2897
+ }
2898
+
2899
+ return false;
2900
+ }
2901
+
2902
+ isSchemaName(value, context) {
2903
+ if (typeof value !== "string") return false;
2904
+ const lowerContext = context.toLowerCase();
2905
+
2906
+ // Check if it matches schema name patterns
2907
+ const matchesPattern = this.configPatterns.database.schemaNames.patterns.some((pattern) =>
2908
+ pattern.test(value)
2909
+ );
2910
+ if (!matchesPattern) return false;
2911
+
2912
+ // Check if context suggests it's a schema/table name
2913
+ return this.configPatterns.database.schemaNames.keywords.some((keyword) =>
2914
+ lowerContext.includes(keyword)
2915
+ );
2916
+ }
2917
+
2918
+ // Security & Authentication (Extended) helpers
2919
+ isTokenConfig(value, context) {
2920
+ const lowerContext = context.toLowerCase();
2921
+
2922
+ if (
2923
+ !this.configPatterns.securityExtended.tokenConfig.keywords.some((keyword) =>
2924
+ lowerContext.includes(keyword)
2925
+ )
2926
+ ) {
2927
+ return false;
2928
+ }
2929
+
2930
+ if (typeof value === "string") {
2931
+ return this.configPatterns.securityExtended.tokenConfig.patterns.some((pattern) => pattern.test(value));
2932
+ }
2933
+
2934
+ if (typeof value === "number" && value > 300) {
2935
+ return /expir|ttl|token|jwt/i.test(context);
2936
+ }
2937
+
2938
+ return false;
2939
+ }
2940
+
2941
+ isPasswordPolicy(value, context) {
2942
+ const lowerContext = context.toLowerCase();
2943
+
2944
+ if (typeof value === "number" && value > 0 && value < 128) {
2945
+ return this.configPatterns.securityExtended.passwordPolicy.patterns.some((pattern) =>
2946
+ pattern.test(context)
2947
+ );
2948
+ }
2949
+
2950
+ if (typeof value === "string") {
2951
+ return this.configPatterns.securityExtended.passwordPolicy.patterns.some((pattern) =>
2952
+ pattern.test(value)
2953
+ );
2954
+ }
2955
+
2956
+ return false;
2957
+ }
2958
+
2959
+ isRateLimiting(value, context) {
2960
+ const lowerContext = context.toLowerCase();
2961
+
2962
+ if (typeof value !== "number" || value <= 0) return false;
2963
+
2964
+ return this.configPatterns.securityExtended.rateLimiting.patterns.some((pattern) =>
2965
+ pattern.test(context)
2966
+ );
2967
+ }
2968
+
2969
+ isEncryptionConfig(value, context) {
2970
+ if (typeof value !== "string") return false;
2971
+ const lowerContext = context.toLowerCase();
2972
+
2973
+ const matchesPattern = this.configPatterns.securityExtended.encryptionConfig.patterns.some((pattern) =>
2974
+ pattern.test(value)
2975
+ );
2976
+ if (!matchesPattern) return false;
2977
+
2978
+ return this.configPatterns.securityExtended.encryptionConfig.keywords.some((keyword) =>
2979
+ lowerContext.includes(keyword)
2980
+ );
2981
+ }
2982
+
2983
+ isOAuthConfig(value, context) {
2984
+ if (typeof value !== "string") return false;
2985
+ const lowerContext = context.toLowerCase();
2986
+
2987
+ const matchesPattern = this.configPatterns.securityExtended.oauthConfig.patterns.some((pattern) =>
2988
+ pattern.test(value)
2989
+ );
2990
+ if (!matchesPattern) return false;
2991
+
2992
+ return this.configPatterns.securityExtended.oauthConfig.keywords.some((keyword) =>
2993
+ lowerContext.includes(keyword)
2994
+ );
2995
+ }
2996
+
2997
+ // File System & Paths helpers
2998
+ isDirectory(value, context) {
2999
+ if (typeof value !== "string") return false;
3000
+ const lowerContext = context.toLowerCase();
3001
+
3002
+ const matchesPattern = this.configPatterns.fileSystem.directories.patterns.some((pattern) =>
3003
+ pattern.test(value)
3004
+ );
3005
+ if (!matchesPattern) return false;
3006
+
3007
+ return this.configPatterns.fileSystem.directories.keywords.some((keyword) =>
3008
+ lowerContext.includes(keyword)
3009
+ );
3010
+ }
3011
+
3012
+ isFileLimit(value, context) {
3013
+ const lowerContext = context.toLowerCase();
3014
+
3015
+ if (typeof value !== "number" || value <= 1000) return false;
3016
+
3017
+ return this.configPatterns.fileSystem.fileLimits.patterns.some((pattern) => pattern.test(context));
3018
+ }
3019
+
3020
+ isFileType(value, context) {
3021
+ if (typeof value !== "string") return false;
3022
+ const lowerContext = context.toLowerCase();
3023
+
3024
+ const matchesPattern = this.configPatterns.fileSystem.fileTypes.patterns.some((pattern) =>
3025
+ pattern.test(value)
3026
+ );
3027
+ if (!matchesPattern) return false;
3028
+
3029
+ return this.configPatterns.fileSystem.fileTypes.keywords.some((keyword) =>
3030
+ lowerContext.includes(keyword)
3031
+ );
3032
+ }
3033
+
3034
+ isLogPath(value, context) {
3035
+ if (typeof value !== "string") return false;
3036
+ const lowerContext = context.toLowerCase();
3037
+
3038
+ return this.configPatterns.fileSystem.logPaths.patterns.some((pattern) => pattern.test(value));
3039
+ }
3040
+
3041
+ // ============ Phase 3: Important Configuration Helpers ============
3042
+
3043
+ // Network & Protocol Configuration helpers
3044
+ isHttpConfig(value, context) {
3045
+ if (typeof value !== "string") return false;
3046
+ const lowerContext = context.toLowerCase();
3047
+
3048
+ // Check if it's an HTTP method
3049
+ if (this.configPatterns.network.httpConfig.httpMethods.includes(value)) {
3050
+ return /method|allowed|http/i.test(context);
3051
+ }
3052
+
3053
+ // Check other HTTP patterns
3054
+ return this.configPatterns.network.httpConfig.patterns.some((pattern) => pattern.test(value));
3055
+ }
3056
+
3057
+ isNetworkTimeout(value, context) {
3058
+ const lowerContext = context.toLowerCase();
3059
+
3060
+ if (typeof value !== "number" || value <= 100) return false;
3061
+
3062
+ return this.configPatterns.network.timeouts.patterns.some((pattern) => pattern.test(context));
3063
+ }
3064
+
3065
+ isBufferConfig(value, context) {
3066
+ const lowerContext = context.toLowerCase();
3067
+
3068
+ if (typeof value !== "number" || value <= 1024) return false;
3069
+
3070
+ return this.configPatterns.network.bufferConfig.patterns.some((pattern) => pattern.test(context));
3071
+ }
3072
+
3073
+ isKeepAliveConfig(value, context) {
3074
+ const lowerContext = context.toLowerCase();
3075
+
3076
+ if (typeof value === "boolean") {
3077
+ return /keepalive|keep.*alive/i.test(context);
3078
+ }
3079
+
3080
+ if (typeof value === "number" && value > 0) {
3081
+ return this.configPatterns.network.keepAlive.patterns.some((pattern) => pattern.test(context));
3082
+ }
3083
+
3084
+ return false;
3085
+ }
3086
+
3087
+ // Business Rules & Limits helpers
3088
+ isPricing(value, context) {
3089
+ const lowerContext = context.toLowerCase();
3090
+
3091
+ if (typeof value === "number" && value > 0) {
3092
+ // Check for price pattern (e.g., 49.99)
3093
+ if (this.configPatterns.business.pricing.patterns.some((pattern) => pattern.test(value.toString()))) {
3094
+ return this.configPatterns.business.pricing.keywords.some((keyword) =>
3095
+ lowerContext.includes(keyword)
3096
+ );
3097
+ }
3098
+ }
3099
+
3100
+ return false;
3101
+ }
3102
+
3103
+ isQuota(value, context) {
3104
+ const lowerContext = context.toLowerCase();
3105
+
3106
+ if (typeof value !== "number" || value <= 0) return false;
3107
+
3108
+ return this.configPatterns.business.quotas.patterns.some((pattern) => pattern.test(context));
3109
+ }
3110
+
3111
+ isDiscount(value, context) {
3112
+ const lowerContext = context.toLowerCase();
3113
+
3114
+ if (typeof value !== "number") return false;
3115
+
3116
+ // Check for decimal rate patterns
3117
+ if (this.configPatterns.business.discounts.patterns.some((pattern) => pattern.test(value.toString()))) {
3118
+ return this.configPatterns.business.discounts.keywords.some((keyword) =>
3119
+ lowerContext.includes(keyword)
3120
+ );
3121
+ }
3122
+
3123
+ return false;
3124
+ }
3125
+
3126
+ isTrial(value, context) {
3127
+ const lowerContext = context.toLowerCase();
3128
+
3129
+ if (typeof value !== "number" || value <= 0) return false;
3130
+
3131
+ return this.configPatterns.business.trials.patterns.some((pattern) => pattern.test(context));
3132
+ }
3133
+
3134
+ // Monitoring & Observability helpers
3135
+ isMetricsConfig(value, context) {
3136
+ const lowerContext = context.toLowerCase();
3137
+
3138
+ if (typeof value !== "number" || value <= 0) return false;
3139
+
3140
+ return this.configPatterns.monitoring.metricsConfig.patterns.some((pattern) => pattern.test(context));
3141
+ }
3142
+
3143
+ isAlertThreshold(value, context) {
3144
+ const lowerContext = context.toLowerCase();
3145
+
3146
+ if (typeof value !== "number") return false;
3147
+
3148
+ return this.configPatterns.monitoring.alertThresholds.patterns.some((pattern) => pattern.test(context));
3149
+ }
3150
+
3151
+ isSamplingRate(value, context) {
3152
+ const lowerContext = context.toLowerCase();
3153
+
3154
+ if (typeof value !== "number" || value < 0 || value > 1) return false;
3155
+
3156
+ return this.configPatterns.monitoring.samplingRates.patterns.some((pattern) => pattern.test(context));
3157
+ }
3158
+
3159
+ isHealthCheck(value, context) {
3160
+ const lowerContext = context.toLowerCase();
3161
+
3162
+ if (typeof value !== "number" || value <= 0) return false;
3163
+
3164
+ return this.configPatterns.monitoring.healthChecks.patterns.some((pattern) => pattern.test(context));
3165
+ }
3166
+
3167
+ // ============ Phase 4: Enhancement Configuration Helpers ============
3168
+
3169
+ // Message Queue & Event Configuration helpers
3170
+ isQueueConfig(value, context) {
3171
+ const lowerContext = context.toLowerCase();
3172
+
3173
+ if (typeof value === "number" && value > 0) {
3174
+ return this.configPatterns.messageQueue.queueConfig.patterns.some((pattern) => pattern.test(context));
3175
+ }
3176
+
3177
+ if (typeof value === "string") {
3178
+ return (
3179
+ this.configPatterns.messageQueue.queueNames.patterns.some((pattern) => pattern.test(value)) &&
3180
+ this.configPatterns.messageQueue.queueNames.keywords.some((keyword) => lowerContext.includes(keyword))
3181
+ );
3182
+ }
3183
+
3184
+ return false;
3185
+ }
3186
+
3187
+ isMessageTTL(value, context) {
3188
+ const lowerContext = context.toLowerCase();
3189
+
3190
+ if (typeof value !== "number" || value <= 0) return false;
3191
+
3192
+ return this.configPatterns.messageQueue.messageTTL.patterns.some((pattern) => pattern.test(context));
3193
+ }
3194
+
3195
+ isConsumerConfig(value, context) {
3196
+ if (typeof value !== "string") return false;
3197
+ const lowerContext = context.toLowerCase();
3198
+
3199
+ // Skip CSS classes (Tailwind, Bootstrap, etc.)
3200
+ // CSS classes typically have: spaces, dashes, colons, brackets
3201
+ const cssIndicators = [
3202
+ /\s+(flex|grid|block|inline|hidden|visible)/, // Layout utilities
3203
+ /\s+(text|bg|border|rounded|shadow|p-|m-|w-|h-)/, // Common CSS utilities
3204
+ /:\w+/, // Pseudo-classes like :hover, data-[...]
3205
+ /\[[\w-]+\]/, // Attribute selectors
3206
+ /(sm|md|lg|xl|2xl):/, // Responsive breakpoints
3207
+ ];
3208
+
3209
+ if (cssIndicators.some((pattern) => pattern.test(value))) {
3210
+ return false;
3211
+ }
3212
+
3213
+ // Skip className/class attributes
3214
+ if (/className|class\s*=/.test(context)) {
3215
+ return false;
3216
+ }
3217
+
3218
+ // Only detect if context has consumer/group/queue keywords
3219
+ const hasConsumerContext = /consumer|queue|group|topic|subscription/i.test(context);
3220
+ if (!hasConsumerContext) return false;
3221
+
3222
+ return this.configPatterns.messageQueue.consumerConfig.patterns.some((pattern) => pattern.test(context));
3223
+ }
3224
+
3225
+ // Deployment & Infrastructure helpers
3226
+ isResourceLimit(value, context) {
3227
+ if (typeof value !== "string") return false;
3228
+ const lowerContext = context.toLowerCase();
3229
+
3230
+ const matchesPattern = this.configPatterns.deployment.resourceLimits.patterns.some((pattern) =>
3231
+ pattern.test(value)
3232
+ );
3233
+ if (!matchesPattern) return false;
3234
+
3235
+ return this.configPatterns.deployment.resourceLimits.keywords.some((keyword) =>
3236
+ lowerContext.includes(keyword)
3237
+ );
3238
+ }
3239
+
3240
+ isScalingConfig(value, context) {
3241
+ const lowerContext = context.toLowerCase();
3242
+
3243
+ if (typeof value !== "number" || value <= 0) return false;
3244
+
3245
+ return this.configPatterns.deployment.scalingConfig.patterns.some((pattern) => pattern.test(context));
3246
+ }
3247
+
3248
+ isRegionConfig(value, context) {
3249
+ if (typeof value !== "string") return false;
3250
+ const lowerContext = context.toLowerCase();
3251
+
3252
+ const matchesPattern = this.configPatterns.deployment.regionConfig.patterns.some((pattern) =>
3253
+ pattern.test(value)
3254
+ );
3255
+ if (!matchesPattern) return false;
3256
+
3257
+ return this.configPatterns.deployment.regionConfig.keywords.some((keyword) =>
3258
+ lowerContext.includes(keyword)
3259
+ );
3260
+ }
3261
+
3262
+ isInstanceType(value, context) {
3263
+ if (typeof value !== "string") return false;
3264
+ const lowerContext = context.toLowerCase();
3265
+
3266
+ const matchesPattern = this.configPatterns.deployment.instanceTypes.patterns.some((pattern) =>
3267
+ pattern.test(value)
3268
+ );
3269
+ if (!matchesPattern) return false;
3270
+
3271
+ return this.configPatterns.deployment.instanceTypes.keywords.some((keyword) =>
3272
+ lowerContext.includes(keyword)
3273
+ );
3274
+ }
3275
+
3276
+ // Third-party Integration helpers
3277
+ isWebhookURL(value, context) {
3278
+ if (typeof value !== "string") return false;
3279
+ const lowerContext = context.toLowerCase();
3280
+
3281
+ // Skip UI text/error messages - they're not webhook URLs!
3282
+ const uiTextIndicators = [
3283
+ /failed|error|success|warning/i,
3284
+ /please|try again|invalid/i,
3285
+ /\s{2,}/, // Multiple spaces (typical in messages)
3286
+ value.length > 100, // Long text is likely a message
3287
+ ];
3288
+
3289
+ if (uiTextIndicators.some((pattern) => (typeof pattern === "boolean" ? pattern : pattern.test(value)))) {
3290
+ return false;
3291
+ }
3292
+
3293
+ // Skip single words that aren't paths
3294
+ if (!/[\/\.]/.test(value) && value.length < 20) {
3295
+ // Single word like "Callback" is not a webhook URL
3296
+ return false;
3297
+ }
3298
+
3299
+ return (
3300
+ this.configPatterns.integration.webhookURLs.patterns.some((pattern) => pattern.test(value)) ||
3301
+ this.configPatterns.integration.webhookURLs.keywords.some((keyword) => lowerContext.includes(keyword))
3302
+ );
3303
+ }
3304
+
3305
+ isExternalService(value, context) {
3306
+ if (typeof value !== "string") return false;
3307
+ const lowerContext = context.toLowerCase();
3308
+
3309
+ const matchesPattern = this.configPatterns.integration.externalServices.patterns.some((pattern) =>
3310
+ pattern.test(value)
3311
+ );
3312
+ if (!matchesPattern) return false;
3313
+
3314
+ return this.configPatterns.integration.externalServices.keywords.some((keyword) =>
3315
+ lowerContext.includes(keyword)
3316
+ );
3317
+ }
3318
+
3319
+ isApiVersion(value, context) {
3320
+ if (typeof value !== "string") return false;
3321
+ const lowerContext = context.toLowerCase();
3322
+
3323
+ const matchesPattern = this.configPatterns.integration.apiVersions.patterns.some((pattern) =>
3324
+ pattern.test(value)
3325
+ );
3326
+ if (!matchesPattern) return false;
3327
+
3328
+ return this.configPatterns.integration.apiVersions.keywords.some((keyword) =>
3329
+ lowerContext.includes(keyword)
3330
+ );
3331
+ }
3332
+
3333
+ isChannelId(value, context) {
3334
+ if (typeof value !== "string") return false;
3335
+ const lowerContext = context.toLowerCase();
3336
+
3337
+ const matchesPattern = this.configPatterns.integration.channelIds.patterns.some((pattern) =>
3338
+ pattern.test(value)
3339
+ );
3340
+ if (!matchesPattern) return false;
3341
+
3342
+ return this.configPatterns.integration.channelIds.keywords.some((keyword) =>
3343
+ lowerContext.includes(keyword)
3344
+ );
3345
+ }
3346
+
3347
+ // Localization & Formatting helpers
3348
+ isTimezone(value, context) {
3349
+ if (typeof value !== "string") return false;
3350
+ const lowerContext = context.toLowerCase();
3351
+
3352
+ const matchesPattern = this.configPatterns.localization.timezones.patterns.some((pattern) =>
3353
+ pattern.test(value)
3354
+ );
3355
+ if (!matchesPattern) return false;
3356
+
3357
+ return this.configPatterns.localization.timezones.keywords.some((keyword) =>
3358
+ lowerContext.includes(keyword)
3359
+ );
3360
+ }
3361
+
3362
+ isDateFormat(value, context) {
3363
+ if (typeof value !== "string") return false;
3364
+ const lowerContext = context.toLowerCase();
3365
+
3366
+ const matchesPattern = this.configPatterns.localization.dateFormats.patterns.some((pattern) =>
3367
+ pattern.test(value)
3368
+ );
3369
+ if (!matchesPattern) return false;
3370
+
3371
+ return this.configPatterns.localization.dateFormats.keywords.some((keyword) =>
3372
+ lowerContext.includes(keyword)
3373
+ );
3374
+ }
3375
+
3376
+ isCurrency(value, context) {
3377
+ if (typeof value !== "string") return false;
3378
+ const lowerContext = context.toLowerCase();
3379
+
3380
+ const matchesPattern = this.configPatterns.localization.currencies.patterns.some((pattern) =>
3381
+ pattern.test(value)
3382
+ );
3383
+ if (!matchesPattern) return false;
3384
+
3385
+ return this.configPatterns.localization.currencies.keywords.some((keyword) =>
3386
+ lowerContext.includes(keyword)
3387
+ );
3388
+ }
3389
+
3390
+ isLocale(value, context) {
3391
+ if (typeof value !== "string") return false;
3392
+ const lowerContext = context.toLowerCase();
3393
+
3394
+ const matchesPattern = this.configPatterns.localization.locales.patterns.some((pattern) =>
3395
+ pattern.test(value)
3396
+ );
3397
+ if (!matchesPattern) return false;
3398
+
3399
+ return this.configPatterns.localization.locales.keywords.some((keyword) =>
3400
+ lowerContext.includes(keyword)
3401
+ );
3402
+ }
3403
+
3404
+ isNumberFormat(value, context) {
3405
+ if (typeof value !== "string") return false;
3406
+ const lowerContext = context.toLowerCase();
3407
+
3408
+ return this.configPatterns.localization.numberFormats.patterns.some((pattern) => pattern.test(context));
3409
+ }
3410
+
3411
+ // ============ Additional Critical Configuration Helpers ============
3412
+
3413
+ // Environment Variable Names (hardcoded)
3414
+ isHardcodedEnvVar(value, context) {
3415
+ if (typeof value !== "string") return false;
3416
+
3417
+ // Check if it's accessing process.env with hardcoded environment-specific name
3418
+ // Look for patterns like: process.env.PROD_API_URL, process.env['DEV_DATABASE']
3419
+ if (!context.includes("process.env")) return false;
3420
+
3421
+ // The value should be the environment variable name itself
3422
+ return this.configPatterns.environmentVars.patterns.some((pattern) => pattern.test(value));
3423
+ }
3424
+
3425
+ // Third-party Service IDs
3426
+ isThirdPartyServiceId(value, context) {
3427
+ if (typeof value !== "string") return false;
3428
+
3429
+ const { thirdPartyServices } = this.configPatterns;
3430
+
3431
+ // Check Stripe keys
3432
+ if (thirdPartyServices.stripe.patterns.some((p) => p.test(value))) {
3433
+ return true;
3434
+ }
3435
+
3436
+ // Check Google Analytics IDs
3437
+ if (thirdPartyServices.googleAnalytics.patterns.some((p) => p.test(value))) {
3438
+ return true;
3439
+ }
3440
+
3441
+ // Check Sentry DSN
3442
+ if (thirdPartyServices.sentry.patterns.some((p) => p.test(value))) {
3443
+ return true;
3444
+ }
3445
+
3446
+ // Check Google Maps API Key
3447
+ if (thirdPartyServices.googleMaps.patterns.some((p) => p.test(value))) {
3448
+ return true;
3449
+ }
3450
+
3451
+ // Check Firebase
3452
+ if (thirdPartyServices.firebase.patterns.some((p) => p.test(value))) {
3453
+ return true;
3454
+ }
3455
+
3456
+ // Check AWS
3457
+ if (thirdPartyServices.aws.patterns.some((p) => p.test(value))) {
3458
+ return true;
3459
+ }
3460
+
3461
+ return false;
3462
+ }
3463
+
3464
+ // IP Addresses
3465
+ isIPAddress(value, context) {
3466
+ if (typeof value !== "string") return false;
3467
+
3468
+ const { ipAddresses } = this.configPatterns;
3469
+
3470
+ // Check if it matches IP pattern
3471
+ const isIP = ipAddresses.patterns.some((pattern) => pattern.test(value));
3472
+ if (!isIP) return false;
3473
+
3474
+ // Skip localhost and common development IPs
3475
+ if (ipAddresses.privateRanges.some((range) => range.test(value))) {
3476
+ // Only flag private IPs if in server/host context
3477
+ return ipAddresses.keywords.some((keyword) => context.toLowerCase().includes(keyword));
3478
+ }
3479
+
3480
+ return true;
3481
+ }
3482
+
3483
+ // Internal Hostnames
3484
+ isInternalHostname(value, context) {
3485
+ if (typeof value !== "string") return false;
3486
+ const lowerContext = context.toLowerCase();
3487
+
3488
+ const matchesPattern = this.configPatterns.hostnames.patterns.some((pattern) => pattern.test(value));
3489
+ if (!matchesPattern) return false;
3490
+
3491
+ return this.configPatterns.hostnames.keywords.some((keyword) => lowerContext.includes(keyword));
3492
+ }
3493
+
3494
+ // Cron Schedules
3495
+ isCronSchedule(value, context) {
3496
+ if (typeof value !== "string") return false;
3497
+ const lowerContext = context.toLowerCase();
3498
+
3499
+ const matchesPattern = this.configPatterns.cronSchedules.patterns.some((pattern) => pattern.test(value));
3500
+ if (!matchesPattern) return false;
3501
+
3502
+ return this.configPatterns.cronSchedules.keywords.some((keyword) => lowerContext.includes(keyword));
3503
+ }
3504
+
3505
+ // Magic Numbers in Business Logic
3506
+ isMagicNumber(value, context) {
3507
+ if (typeof value !== "number") return false;
3508
+
3509
+ // Skip common constants
3510
+ const commonConstants = [0, 1, 2];
3511
+ if (commonConstants.includes(value)) return false;
3512
+
3513
+ // Skip HTTP status codes (200, 404, etc.)
3514
+ if (value >= 100 && value < 600 && Number.isInteger(value)) {
3515
+ // But allow decimal values like 0.1 (tax rate), 0.08 (VAT)
3516
+ return false;
3517
+ }
3518
+
3519
+ const lowerContext = context.toLowerCase();
3520
+
3521
+ // Check if it's in business logic context with specific keywords
3522
+ const hasKeyword = this.configPatterns.magicNumbers.keywords.some((keyword) =>
3523
+ lowerContext.includes(keyword)
3524
+ );
3525
+
3526
+ if (!hasKeyword) return false;
3527
+
3528
+ // Additional check: look for business context patterns
3529
+ const hasBusinessContext = this.configPatterns.magicNumbers.businessContexts.some((pattern) =>
3530
+ pattern.test(context)
3531
+ );
3532
+
3533
+ return hasBusinessContext;
3534
+ }
3535
+
3536
+ // Email & SMS Templates
3537
+ isMessageTemplate(value, context) {
3538
+ if (typeof value !== "string") return false;
3539
+ if (value.length < 10) return false;
3540
+
3541
+ const lowerValue = value.toLowerCase();
3542
+ const lowerContext = context.toLowerCase();
3543
+
3544
+ // Skip error codes/enums (like "OAuthAccountNotLinked", "AccessDenied")
3545
+ // These are constants, not templates!
3546
+ if (/^[A-Z][a-zA-Z]+$/.test(value) && !value.includes(" ") && value.length < 30) {
3547
+ return false;
3548
+ }
3549
+
3550
+ // Skip short strings without template markers
3551
+ if (value.length < 20 && !value.includes("{{") && !value.includes("${")) {
3552
+ return false;
3553
+ }
3554
+
3555
+ // Email subject/body - check both context AND value for keywords
3556
+ const emailKeywordInContext = this.configPatterns.messageTemplates.email.keywords.some((k) =>
3557
+ lowerContext.includes(k)
3558
+ );
3559
+ const emailKeywordInValue = ["welcome", "verify", "password", "reset", "order", "confirm"].some((k) =>
3560
+ lowerValue.includes(k)
3561
+ );
3562
+
3563
+ if (emailKeywordInContext || emailKeywordInValue) {
3564
+ return this.configPatterns.messageTemplates.email.patterns.some((p) => p.test(value));
3565
+ }
3566
+
3567
+ // SMS text - check for OTP, code, verification keywords
3568
+ const smsKeywordInContext = this.configPatterns.messageTemplates.sms.keywords.some((k) =>
3569
+ lowerContext.includes(k)
3570
+ );
3571
+ const smsKeywordInValue = ["otp", "code", "verification", "verify"].some((k) => lowerValue.includes(k));
3572
+
3573
+ if (smsKeywordInContext || smsKeywordInValue) {
3574
+ return this.configPatterns.messageTemplates.sms.patterns.some((p) => p.test(value));
3575
+ }
3576
+
3577
+ return false;
3578
+ }
3579
+
3580
+ // Version Numbers
3581
+ isVersionNumber(value, context) {
3582
+ if (typeof value !== "string") return false;
3583
+ const lowerContext = context.toLowerCase();
3584
+
3585
+ const matchesPattern = this.configPatterns.versions.patterns.some((pattern) => pattern.test(value));
3586
+ if (!matchesPattern) return false;
3587
+
3588
+ return this.configPatterns.versions.keywords.some((keyword) => lowerContext.includes(keyword));
3589
+ }
3590
+
3591
+ // Default Pagination
3592
+ isPaginationDefault(value, context) {
3593
+ if (typeof value !== "number") return false;
3594
+ const lowerContext = context.toLowerCase();
3595
+
3596
+ // Must be a common pagination value
3597
+ if (!this.configPatterns.pagination.defaults.includes(value)) return false;
3598
+
3599
+ // Must be in pagination context
3600
+ return this.configPatterns.pagination.keywords.some((keyword) => lowerContext.includes(keyword));
3601
+ }
3602
+
3603
+ maskSensitiveValue(value) {
3604
+ if (typeof value !== "string" || value.length <= 6) {
3605
+ return value;
3606
+ }
3607
+
3608
+ // Mask sensitive information but show some characters for context
3609
+ const start = value.substring(0, 3);
3610
+ const end = value.substring(value.length - 3);
3611
+ const masked = "*".repeat(Math.min(value.length - 6, 10));
3612
+ return `${start}${masked}${end}`;
3613
+ }
3614
+
3615
+ createMessage(config) {
3616
+ const baseMessage = "Configuration should not be hardcoded in source code.";
3617
+
3618
+ switch (config.type) {
3619
+ case "api_url":
3620
+ return `${baseMessage} API URL '${config.value}' should be managed through environment variables or configuration files.`;
3621
+ case "credential":
3622
+ return `${baseMessage} Credential '${config.value}' must be stored in secure environment variables or a secrets vault.`;
3623
+ case "connection_string":
3624
+ return `${baseMessage} Database connection string should be loaded from environment variables.`;
3625
+ case "timeout":
3626
+ return `${baseMessage} Timeout value ${config.value}ms may need to differ between environments.`;
3627
+ case "retry_interval":
3628
+ return `${baseMessage} Retry interval ${config.value}ms should be configurable per environment.`;
3629
+ case "batch_size":
3630
+ return `${baseMessage} Batch size ${config.value} may vary between development and production environments.`;
3631
+ case "threshold":
3632
+ return `${baseMessage} Threshold value ${config.value} should be environment-configurable.`;
3633
+ case "feature_flag":
3634
+ return `${baseMessage} Feature flag should be managed through a feature flag system or configuration.`;
3635
+ case "cors_origin":
3636
+ return `${baseMessage} CORS origin '${config.value}' should be loaded from environment configuration.`;
3637
+ case "session_config":
3638
+ return `${baseMessage} Session configuration '${config.value}' should be environment-dependent.`;
3639
+ case "cache_config":
3640
+ return `${baseMessage} Cache configuration '${config.value}' should be managed through environment settings.`;
3641
+ case "log_level":
3642
+ return `${baseMessage} Log level '${config.value}' should be configurable per environment.`;
3643
+ case "environment_name":
3644
+ return `${baseMessage} Environment name '${config.value}' should not be hardcoded, use environment detection.`;
3645
+ case "service_dependency":
3646
+ return `${baseMessage} Service URL '${config.value}' should use service discovery or configuration management.`;
3647
+ case "performance_config":
3648
+ return `${baseMessage} Performance setting ${config.value} should be tuned per environment capacity.`;
3649
+
3650
+ // Phase 2: Critical configurations
3651
+ case "database_pool":
3652
+ return `${baseMessage} Database pool size ${config.value} should be optimized per environment capacity.`;
3653
+ case "query_config":
3654
+ return `${baseMessage} Query configuration '${config.value}' should be environment-dependent.`;
3655
+ case "schema_name":
3656
+ return `${baseMessage} Database schema/table name '${config.value}' should be managed through configuration.`;
3657
+ case "token_config":
3658
+ return `${baseMessage} Token configuration '${config.value}' should be externalized for security.`;
3659
+ case "password_policy":
3660
+ return `${baseMessage} Password policy setting ${config.value} should be configurable per environment.`;
3661
+ case "rate_limiting":
3662
+ return `${baseMessage} Rate limit ${config.value} should vary by environment and tier.`;
3663
+ case "encryption_config":
3664
+ return `${baseMessage} Encryption algorithm '${config.value}' should be configurable for compliance requirements.`;
3665
+ case "oauth_config":
3666
+ return `${baseMessage} OAuth scope '${config.value}' should be managed through configuration.`;
3667
+ case "directory":
3668
+ return `${baseMessage} Directory path '${config.value}' should be environment-specific.`;
3669
+ case "file_limit":
3670
+ return `${baseMessage} File size limit ${config.value} bytes should be configurable per environment.`;
3671
+ case "file_type":
3672
+ return `${baseMessage} File type restriction '${config.value}' should be managed in configuration.`;
3673
+ case "log_path":
3674
+ return `${baseMessage} Log file path '${config.value}' should be environment-specific.`;
3675
+
3676
+ // Phase 3: Important configurations
3677
+ case "http_config":
3678
+ return `${baseMessage} HTTP configuration '${config.value}' should be externalized.`;
3679
+ case "network_timeout":
3680
+ return `${baseMessage} Network timeout ${config.value}ms should be tuned per environment.`;
3681
+ case "buffer_config":
3682
+ return `${baseMessage} Buffer size ${config.value} bytes should be configurable.`;
3683
+ case "keepalive_config":
3684
+ return `${baseMessage} Keep-alive setting ${config.value} should be environment-dependent.`;
3685
+ case "pricing":
3686
+ return `${baseMessage} Price ${config.value} should be managed in pricing configuration system.`;
3687
+ case "quota":
3688
+ return `${baseMessage} Quota limit ${config.value} should be configurable per plan/tier.`;
3689
+ case "discount":
3690
+ return `${baseMessage} Discount rate ${config.value} should be managed in pricing configuration.`;
3691
+ case "trial":
3692
+ return `${baseMessage} Trial period ${config.value} days should be configurable.`;
3693
+ case "metrics_config":
3694
+ return `${baseMessage} Metrics interval ${config.value} should be environment-dependent.`;
3695
+ case "alert_threshold":
3696
+ return `${baseMessage} Alert threshold ${config.value} should be tuned per environment.`;
3697
+ case "sampling_rate":
3698
+ return `${baseMessage} Sampling rate ${config.value} should be configurable for observability tuning.`;
3699
+ case "health_check":
3700
+ return `${baseMessage} Health check interval ${config.value} should be environment-specific.`;
3701
+
3702
+ // Phase 4: Enhancement configurations
3703
+ case "queue_config":
3704
+ return `${baseMessage} Queue configuration '${config.value}' should be externalized.`;
3705
+ case "message_ttl":
3706
+ return `${baseMessage} Message TTL ${config.value} should be configurable per environment.`;
3707
+ case "consumer_config":
3708
+ return `${baseMessage} Consumer configuration '${config.value}' should be managed externally.`;
3709
+ case "resource_limit":
3710
+ return `${baseMessage} Resource limit '${config.value}' should be environment-specific.`;
3711
+ case "scaling_config":
3712
+ return `${baseMessage} Scaling configuration ${config.value} should be tuned per environment.`;
3713
+ case "region_config":
3714
+ return `${baseMessage} Deployment region '${config.value}' should be determined at deployment time.`;
3715
+ case "instance_type":
3716
+ return `${baseMessage} Instance type '${config.value}' should be configurable per environment.`;
3717
+ case "webhook_url":
3718
+ return `${baseMessage} Webhook URL '${config.value}' should be environment-specific.`;
3719
+ case "external_service":
3720
+ return `${baseMessage} External service name '${config.value}' should be managed in configuration.`;
3721
+ case "api_version":
3722
+ return `${baseMessage} API version '${config.value}' should be configurable for version management.`;
3723
+ case "channel_id":
3724
+ return `${baseMessage} Channel ID '${config.value}' should be environment-dependent.`;
3725
+ case "timezone":
3726
+ return `${baseMessage} Timezone '${config.value}' should be user-configurable or environment-specific.`;
3727
+ case "date_format":
3728
+ return `${baseMessage} Date format '${config.value}' should be locale-configurable.`;
3729
+ case "currency":
3730
+ return `${baseMessage} Currency code '${config.value}' should be region-configurable.`;
3731
+ case "locale":
3732
+ return `${baseMessage} Locale '${config.value}' should be user-configurable.`;
3733
+ case "number_format":
3734
+ return `${baseMessage} Number format should be locale-configurable.`;
3735
+
3736
+ // Additional critical configurations
3737
+ case "environment_var":
3738
+ return `${baseMessage} Environment variable name '${config.value}' is hardcoded and environment-specific.`;
3739
+ case "third_party_service":
3740
+ return `${baseMessage} Third-party service ID/key '${config.value}' is hardcoded (security risk).`;
3741
+ case "ip_address":
3742
+ return `${baseMessage} IP address '${config.value}' is hardcoded.`;
3743
+ case "hostname":
3744
+ return `${baseMessage} Internal hostname '${config.value}' is hardcoded.`;
3745
+ case "cron_schedule":
3746
+ return `${baseMessage} Cron schedule '${config.value}' is hardcoded.`;
3747
+ case "magic_number":
3748
+ return `${baseMessage} Magic number '${config.value}' in business logic should be a named constant.`;
3749
+ case "message_template":
3750
+ return `${baseMessage} Message template is hardcoded (should be in template system).`;
3751
+ case "version":
3752
+ return `${baseMessage} Version number '${config.value}' is hardcoded.`;
3753
+ case "pagination_default":
3754
+ return `${baseMessage} Pagination default '${config.value}' is hardcoded.`;
3755
+
3756
+ case "property_config":
3757
+ return `${baseMessage} Property '${config.propertyName}' contains value '${config.value}' that may differ between environments.`;
3758
+ case "variable_config":
3759
+ return `${baseMessage} Variable '${config.variableName}' contains value '${config.value}' that should be configurable.`;
3760
+ default:
3761
+ return `${baseMessage} Value '${config.value}' may need to differ between development, staging, and production environments.`;
3762
+ }
3763
+ }
3764
+
3765
+ getSuggestion(type) {
3766
+ const suggestions = {
3767
+ api_url: 'Use process.env.API_BASE_URL or config.get("api.baseUrl")',
3768
+ credential: "Use process.env.API_KEY or secure vault (e.g., AWS Secrets Manager, Azure Key Vault)",
3769
+ connection_string: "Use process.env.DATABASE_URL or connection configuration",
3770
+ timeout: 'Use process.env.REQUEST_TIMEOUT or config.get("timeouts.request")',
3771
+ retry_interval: 'Use process.env.RETRY_INTERVAL or config.get("retry.interval")',
3772
+ batch_size: 'Use process.env.BATCH_SIZE or config.get("processing.batchSize")',
3773
+ threshold: 'Use process.env.MEMORY_THRESHOLD or config.get("limits.memory")',
3774
+ feature_flag:
3775
+ 'Use feature flag service (e.g., LaunchDarkly, ConfigCat) or config.get("features.enabled")',
3776
+ cors_origin: 'Use process.env.CORS_ORIGINS or config.get("security.corsOrigins")',
3777
+ session_config: 'Use process.env.JWT_EXPIRY or config.get("auth.sessionTimeout")',
3778
+ cache_config: 'Use process.env.CACHE_TTL or config.get("cache.defaultTtl")',
3779
+ log_level: 'Use process.env.LOG_LEVEL or config.get("logging.level")',
3780
+ environment_name: "Use process.env.NODE_ENV or runtime environment detection",
3781
+ service_dependency: 'Use service discovery or config.get("services.userService.url")',
3782
+ performance_config: 'Use process.env.WORKER_THREADS or config.get("performance.workers")',
3783
+
3784
+ // Phase 2: Critical configurations
3785
+ database_pool: 'Use process.env.DB_POOL_SIZE or config.get("database.poolSize")',
3786
+ query_config: 'Use process.env.QUERY_TIMEOUT or config.get("database.queryTimeout")',
3787
+ schema_name: 'Use process.env.TABLE_PREFIX or config.get("database.schemaName")',
3788
+ token_config: 'Use process.env.TOKEN_EXPIRY or config.get("auth.tokenExpiry")',
3789
+ password_policy: 'Use process.env.MIN_PASSWORD_LENGTH or config.get("security.passwordPolicy")',
3790
+ rate_limiting: 'Use process.env.RATE_LIMIT or config.get("security.rateLimit")',
3791
+ encryption_config: 'Use process.env.ENCRYPTION_ALGORITHM or config.get("security.encryption")',
3792
+ oauth_config: 'Use process.env.OAUTH_SCOPES or config.get("oauth.scopes")',
3793
+ directory: 'Use process.env.UPLOAD_DIR or config.get("storage.uploadDirectory")',
3794
+ file_limit: 'Use process.env.MAX_FILE_SIZE or config.get("upload.maxFileSize")',
3795
+ file_type: 'Use process.env.ALLOWED_FILE_TYPES or config.get("upload.allowedTypes")',
3796
+ log_path: 'Use process.env.LOG_PATH or config.get("logging.filePath")',
3797
+
3798
+ // Phase 3: Important configurations
3799
+ http_config: 'Use process.env.ALLOWED_METHODS or config.get("http.allowedMethods")',
3800
+ network_timeout: 'Use process.env.CONNECT_TIMEOUT or config.get("network.timeout")',
3801
+ buffer_config: 'Use process.env.BUFFER_SIZE or config.get("network.bufferSize")',
3802
+ keepalive_config: 'Use process.env.KEEPALIVE_INTERVAL or config.get("network.keepAlive")',
3803
+ pricing: 'Use pricing service or config.get("pricing.plans")',
3804
+ quota: 'Use process.env.API_QUOTA or config.get("limits.quotas")',
3805
+ discount: 'Use pricing service or config.get("pricing.discounts")',
3806
+ trial: 'Use process.env.TRIAL_DAYS or config.get("subscription.trialPeriod")',
3807
+ metrics_config: 'Use process.env.METRICS_INTERVAL or config.get("monitoring.metricsInterval")',
3808
+ alert_threshold: 'Use process.env.ALERT_THRESHOLD or config.get("monitoring.alertThresholds")',
3809
+ sampling_rate: 'Use process.env.TRACE_SAMPLING_RATE or config.get("observability.samplingRate")',
3810
+ health_check: 'Use process.env.HEALTH_CHECK_INTERVAL or config.get("monitoring.healthCheck")',
3811
+
3812
+ // Phase 4: Enhancement configurations
3813
+ queue_config: 'Use process.env.QUEUE_SIZE or config.get("messaging.queueSize")',
3814
+ message_ttl: 'Use process.env.MESSAGE_TTL or config.get("messaging.messageTtl")',
3815
+ consumer_config: 'Use process.env.CONSUMER_GROUP or config.get("messaging.consumerGroup")',
3816
+ resource_limit: 'Use Kubernetes limits or config.get("deployment.resources")',
3817
+ scaling_config: 'Use process.env.MIN_REPLICAS or config.get("deployment.autoscaling")',
3818
+ region_config: 'Use deployment configuration or config.get("infrastructure.region")',
3819
+ instance_type: 'Use deployment configuration or config.get("infrastructure.instanceType")',
3820
+ webhook_url: 'Use process.env.WEBHOOK_URL or config.get("integrations.webhookUrl")',
3821
+ external_service: 'Use process.env.PAYMENT_PROVIDER or config.get("integrations.provider")',
3822
+ api_version: 'Use process.env.API_VERSION or config.get("integrations.apiVersion")',
3823
+ channel_id: 'Use process.env.SLACK_CHANNEL or config.get("notifications.channel")',
3824
+ timezone: 'Use process.env.TZ or config.get("localization.timezone")',
3825
+ date_format: 'Use config.get("localization.dateFormat") or user preferences',
3826
+ currency: 'Use config.get("localization.currency") or user/region preferences',
3827
+ locale: 'Use process.env.LOCALE or config.get("localization.locale")',
3828
+ number_format: 'Use config.get("localization.numberFormat") or locale-based formatting',
3829
+
3830
+ // Additional critical configurations
3831
+ environment_var: "Use environment-agnostic names with NODE_ENV detection or config service",
3832
+ third_party_service: "Use AWS Secrets Manager, Azure Key Vault, or .env with git-ignored secrets",
3833
+ ip_address: 'Use service discovery (Consul, Kubernetes DNS) or config.get("services.host")',
3834
+ hostname: 'Use service discovery or config.get("services.internalHostname")',
3835
+ cron_schedule: 'Use process.env.CRON_SCHEDULE or config.get("scheduler.cron")',
3836
+ magic_number: "Define as const BUSINESS_CONSTANT = value in constants file",
3837
+ message_template: 'Use template management (i18n, email service) or config.get("templates.email")',
3838
+ version: "Use package.json version or build-time injection (webpack.DefinePlugin)",
3839
+ pagination_default: 'Use config.get("api.defaultPageSize") or API configuration',
3840
+
3841
+ property_config: "Move to centralized configuration (config.ts, application.properties, or .env)",
3842
+ variable_config: "Use environment variables or configuration service",
3843
+ config_key: "Define configuration keys as constants in a dedicated config module",
3844
+ };
3845
+
3846
+ return (
3847
+ suggestions[type] ||
3848
+ "Move to environment variables, config files, or centralized configuration management"
3849
+ );
3850
+ }
3851
+ }
3852
+
3853
+ module.exports = C067SymbolBasedAnalyzer;