code-secret-handler 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/LICENSE +79 -0
  2. package/README.md +221 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +121 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/env/envWriter.d.ts +9 -0
  8. package/dist/env/envWriter.d.ts.map +1 -0
  9. package/dist/env/envWriter.js +76 -0
  10. package/dist/env/envWriter.js.map +1 -0
  11. package/dist/env/keyNamer.d.ts +13 -0
  12. package/dist/env/keyNamer.d.ts.map +1 -0
  13. package/dist/env/keyNamer.js +33 -0
  14. package/dist/env/keyNamer.js.map +1 -0
  15. package/dist/interactive/confirm.d.ts +16 -0
  16. package/dist/interactive/confirm.d.ts.map +1 -0
  17. package/dist/interactive/confirm.js +72 -0
  18. package/dist/interactive/confirm.js.map +1 -0
  19. package/dist/license/gitContext.d.ts +16 -0
  20. package/dist/license/gitContext.d.ts.map +1 -0
  21. package/dist/license/gitContext.js +117 -0
  22. package/dist/license/gitContext.js.map +1 -0
  23. package/dist/license/licenseCheck.d.ts +22 -0
  24. package/dist/license/licenseCheck.d.ts.map +1 -0
  25. package/dist/license/licenseCheck.js +119 -0
  26. package/dist/license/licenseCheck.js.map +1 -0
  27. package/dist/license/telemetry.d.ts +17 -0
  28. package/dist/license/telemetry.d.ts.map +1 -0
  29. package/dist/license/telemetry.js +87 -0
  30. package/dist/license/telemetry.js.map +1 -0
  31. package/dist/reporter/console.d.ts +3 -0
  32. package/dist/reporter/console.d.ts.map +1 -0
  33. package/dist/reporter/console.js +96 -0
  34. package/dist/reporter/console.js.map +1 -0
  35. package/dist/reporter/csv.d.ts +6 -0
  36. package/dist/reporter/csv.d.ts.map +1 -0
  37. package/dist/reporter/csv.js +56 -0
  38. package/dist/reporter/csv.js.map +1 -0
  39. package/dist/reporter/index.d.ts +13 -0
  40. package/dist/reporter/index.d.ts.map +1 -0
  41. package/dist/reporter/index.js +52 -0
  42. package/dist/reporter/index.js.map +1 -0
  43. package/dist/reporter/json.d.ts +7 -0
  44. package/dist/reporter/json.d.ts.map +1 -0
  45. package/dist/reporter/json.js +39 -0
  46. package/dist/reporter/json.js.map +1 -0
  47. package/dist/reporter/markdown.d.ts +6 -0
  48. package/dist/reporter/markdown.d.ts.map +1 -0
  49. package/dist/reporter/markdown.js +106 -0
  50. package/dist/reporter/markdown.js.map +1 -0
  51. package/dist/rewriter/index.d.ts +9 -0
  52. package/dist/rewriter/index.d.ts.map +1 -0
  53. package/dist/rewriter/index.js +95 -0
  54. package/dist/rewriter/index.js.map +1 -0
  55. package/dist/rewriter/langDetector.d.ts +7 -0
  56. package/dist/rewriter/langDetector.d.ts.map +1 -0
  57. package/dist/rewriter/langDetector.js +96 -0
  58. package/dist/rewriter/langDetector.js.map +1 -0
  59. package/dist/rewriter/templates/csharp.d.ts +8 -0
  60. package/dist/rewriter/templates/csharp.d.ts.map +1 -0
  61. package/dist/rewriter/templates/csharp.js +36 -0
  62. package/dist/rewriter/templates/csharp.js.map +1 -0
  63. package/dist/rewriter/templates/generic.d.ts +7 -0
  64. package/dist/rewriter/templates/generic.d.ts.map +1 -0
  65. package/dist/rewriter/templates/generic.js +40 -0
  66. package/dist/rewriter/templates/generic.js.map +1 -0
  67. package/dist/rewriter/templates/go.d.ts +8 -0
  68. package/dist/rewriter/templates/go.d.ts.map +1 -0
  69. package/dist/rewriter/templates/go.js +56 -0
  70. package/dist/rewriter/templates/go.js.map +1 -0
  71. package/dist/rewriter/templates/java.d.ts +7 -0
  72. package/dist/rewriter/templates/java.d.ts.map +1 -0
  73. package/dist/rewriter/templates/java.js +31 -0
  74. package/dist/rewriter/templates/java.js.map +1 -0
  75. package/dist/rewriter/templates/javascript.d.ts +10 -0
  76. package/dist/rewriter/templates/javascript.d.ts.map +1 -0
  77. package/dist/rewriter/templates/javascript.js +54 -0
  78. package/dist/rewriter/templates/javascript.js.map +1 -0
  79. package/dist/rewriter/templates/php.d.ts +8 -0
  80. package/dist/rewriter/templates/php.d.ts.map +1 -0
  81. package/dist/rewriter/templates/php.js +41 -0
  82. package/dist/rewriter/templates/php.js.map +1 -0
  83. package/dist/rewriter/templates/properties.d.ts +9 -0
  84. package/dist/rewriter/templates/properties.d.ts.map +1 -0
  85. package/dist/rewriter/templates/properties.js +39 -0
  86. package/dist/rewriter/templates/properties.js.map +1 -0
  87. package/dist/rewriter/templates/python.d.ts +8 -0
  88. package/dist/rewriter/templates/python.d.ts.map +1 -0
  89. package/dist/rewriter/templates/python.js +48 -0
  90. package/dist/rewriter/templates/python.js.map +1 -0
  91. package/dist/rewriter/templates/ruby.d.ts +8 -0
  92. package/dist/rewriter/templates/ruby.d.ts.map +1 -0
  93. package/dist/rewriter/templates/ruby.js +40 -0
  94. package/dist/rewriter/templates/ruby.js.map +1 -0
  95. package/dist/rewriter/templates/rust.d.ts +7 -0
  96. package/dist/rewriter/templates/rust.d.ts.map +1 -0
  97. package/dist/rewriter/templates/rust.js +27 -0
  98. package/dist/rewriter/templates/rust.js.map +1 -0
  99. package/dist/rewriter/templates/shell.d.ts +7 -0
  100. package/dist/rewriter/templates/shell.d.ts.map +1 -0
  101. package/dist/rewriter/templates/shell.js +42 -0
  102. package/dist/rewriter/templates/shell.js.map +1 -0
  103. package/dist/rewriter/templates/toml.d.ts +9 -0
  104. package/dist/rewriter/templates/toml.d.ts.map +1 -0
  105. package/dist/rewriter/templates/toml.js +34 -0
  106. package/dist/rewriter/templates/toml.js.map +1 -0
  107. package/dist/rewriter/templates/yaml.d.ts +8 -0
  108. package/dist/rewriter/templates/yaml.d.ts.map +1 -0
  109. package/dist/rewriter/templates/yaml.js +37 -0
  110. package/dist/rewriter/templates/yaml.js.map +1 -0
  111. package/dist/scanner/detector.d.ts +18 -0
  112. package/dist/scanner/detector.d.ts.map +1 -0
  113. package/dist/scanner/detector.js +156 -0
  114. package/dist/scanner/detector.js.map +1 -0
  115. package/dist/scanner/fileWalker.d.ts +6 -0
  116. package/dist/scanner/fileWalker.d.ts.map +1 -0
  117. package/dist/scanner/fileWalker.js +146 -0
  118. package/dist/scanner/fileWalker.js.map +1 -0
  119. package/dist/scanner/index.d.ts +8 -0
  120. package/dist/scanner/index.d.ts.map +1 -0
  121. package/dist/scanner/index.js +74 -0
  122. package/dist/scanner/index.js.map +1 -0
  123. package/dist/scanner/rules/aws.d.ts +6 -0
  124. package/dist/scanner/rules/aws.d.ts.map +1 -0
  125. package/dist/scanner/rules/aws.js +90 -0
  126. package/dist/scanner/rules/aws.js.map +1 -0
  127. package/dist/scanner/rules/azure.d.ts +6 -0
  128. package/dist/scanner/rules/azure.d.ts.map +1 -0
  129. package/dist/scanner/rules/azure.js +86 -0
  130. package/dist/scanner/rules/azure.js.map +1 -0
  131. package/dist/scanner/rules/cloud.d.ts +11 -0
  132. package/dist/scanner/rules/cloud.d.ts.map +1 -0
  133. package/dist/scanner/rules/cloud.js +1066 -0
  134. package/dist/scanner/rules/cloud.js.map +1 -0
  135. package/dist/scanner/rules/database.d.ts +6 -0
  136. package/dist/scanner/rules/database.d.ts.map +1 -0
  137. package/dist/scanner/rules/database.js +283 -0
  138. package/dist/scanner/rules/database.js.map +1 -0
  139. package/dist/scanner/rules/gcp.d.ts +6 -0
  140. package/dist/scanner/rules/gcp.d.ts.map +1 -0
  141. package/dist/scanner/rules/gcp.js +108 -0
  142. package/dist/scanner/rules/gcp.js.map +1 -0
  143. package/dist/scanner/rules/generic.d.ts +13 -0
  144. package/dist/scanner/rules/generic.d.ts.map +1 -0
  145. package/dist/scanner/rules/generic.js +305 -0
  146. package/dist/scanner/rules/generic.js.map +1 -0
  147. package/dist/scanner/rules/index.d.ts +18 -0
  148. package/dist/scanner/rules/index.d.ts.map +1 -0
  149. package/dist/scanner/rules/index.js +40 -0
  150. package/dist/scanner/rules/index.js.map +1 -0
  151. package/dist/scanner/rules/jwt.d.ts +6 -0
  152. package/dist/scanner/rules/jwt.d.ts.map +1 -0
  153. package/dist/scanner/rules/jwt.js +97 -0
  154. package/dist/scanner/rules/jwt.js.map +1 -0
  155. package/dist/scanner/rules/network.d.ts +7 -0
  156. package/dist/scanner/rules/network.d.ts.map +1 -0
  157. package/dist/scanner/rules/network.js +166 -0
  158. package/dist/scanner/rules/network.js.map +1 -0
  159. package/dist/scanner/rules/privateKey.d.ts +6 -0
  160. package/dist/scanner/rules/privateKey.d.ts.map +1 -0
  161. package/dist/scanner/rules/privateKey.js +60 -0
  162. package/dist/scanner/rules/privateKey.js.map +1 -0
  163. package/dist/scanner/rules/ssh.d.ts +6 -0
  164. package/dist/scanner/rules/ssh.d.ts.map +1 -0
  165. package/dist/scanner/rules/ssh.js +49 -0
  166. package/dist/scanner/rules/ssh.js.map +1 -0
  167. package/dist/types.d.ts +78 -0
  168. package/dist/types.d.ts.map +1 -0
  169. package/dist/types.js +3 -0
  170. package/dist/types.js.map +1 -0
  171. package/package.json +54 -0
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rewriteToml = rewriteToml;
4
+ /**
5
+ * Rewrites TOML files:
6
+ * - Replaces literal secret with a reference comment and env var placeholder.
7
+ * - TOML does not support env var interpolation natively, so we replace
8
+ * the value with "${KEY}" and add a guidance comment.
9
+ */
10
+ function rewriteToml(ctx) {
11
+ const { finding, fileContent } = ctx;
12
+ const { rawValue, envKey } = finding;
13
+ let rewrittenContent = fileContent;
14
+ let replacements = 0;
15
+ const quoteStyles = ['"', "'"];
16
+ for (const q of quoteStyles) {
17
+ const escaped = rawValue.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
18
+ const re = new RegExp(`${q}${escaped}${q}`, 'g');
19
+ if (re.test(rewrittenContent)) {
20
+ // Replace with env var reference as a string — note: not all TOML loaders support this
21
+ rewrittenContent = rewrittenContent.replace(re, `"$\{${envKey}}" # Set ${envKey} in environment`);
22
+ replacements++;
23
+ break;
24
+ }
25
+ }
26
+ return {
27
+ filePath: finding.file,
28
+ originalContent: fileContent,
29
+ rewrittenContent,
30
+ replacements,
31
+ dotenvInjected: false,
32
+ };
33
+ }
34
+ //# sourceMappingURL=toml.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toml.js","sourceRoot":"","sources":["../../../src/rewriter/templates/toml.ts"],"names":[],"mappings":";;;AAEA;;;;;GAKG;AACH,qBAA4B,GAAmB;IAC7C,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,GAAG,CAAC;IACrC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAErC,IAAI,gBAAgB,GAAG,WAAW,CAAC;IACnC,IAAI,YAAY,GAAG,CAAC,CAAC;IAErB,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC/B,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;QAChE,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QACjD,IAAI,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAC9B,uFAAuF;YACvF,gBAAgB,GAAG,gBAAgB,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,MAAM,aAAa,MAAM,iBAAiB,CAAC,CAAC;YACnG,YAAY,EAAE,CAAC;YACf,MAAM;QACR,CAAC;IACH,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,OAAO,CAAC,IAAI;QACtB,eAAe,EAAE,WAAW;QAC5B,gBAAgB;QAChB,YAAY;QACZ,cAAc,EAAE,KAAK;KACtB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { RewriteContext, RewriteResult } from '../../types';
2
+ /**
3
+ * Rewrites YAML files:
4
+ * - Replaces literal secret with `${KEY}` (shell-style substitution)
5
+ * - Adds a comment suggesting the use of an env-var substitution tool.
6
+ */
7
+ export declare function rewriteYaml(ctx: RewriteContext): RewriteResult;
8
+ //# sourceMappingURL=yaml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yaml.d.ts","sourceRoot":"","sources":["../../../src/rewriter/templates/yaml.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5D;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,aAAa,CA+B9D"}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rewriteYaml = rewriteYaml;
4
+ /**
5
+ * Rewrites YAML files:
6
+ * - Replaces literal secret with `${KEY}` (shell-style substitution)
7
+ * - Adds a comment suggesting the use of an env-var substitution tool.
8
+ */
9
+ function rewriteYaml(ctx) {
10
+ const { finding, fileContent } = ctx;
11
+ const { rawValue, envKey } = finding;
12
+ let rewrittenContent = fileContent;
13
+ let replacements = 0;
14
+ const quoteStyles = ["'", '"', ''];
15
+ for (const q of quoteStyles) {
16
+ const escaped = rawValue.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
17
+ const re = new RegExp(`${q}${escaped}${q}`, 'g');
18
+ if (re.test(rewrittenContent)) {
19
+ rewrittenContent = rewrittenContent.replace(re, `\${${envKey}}`);
20
+ replacements++;
21
+ break;
22
+ }
23
+ }
24
+ if (replacements > 0 && !/# env-var substitution/i.test(rewrittenContent)) {
25
+ rewrittenContent =
26
+ `# Note: Use envsubst, docker-compose, or a similar tool to substitute \${...} env vars.\n` +
27
+ rewrittenContent;
28
+ }
29
+ return {
30
+ filePath: finding.file,
31
+ originalContent: fileContent,
32
+ rewrittenContent,
33
+ replacements,
34
+ dotenvInjected: false,
35
+ };
36
+ }
37
+ //# sourceMappingURL=yaml.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yaml.js","sourceRoot":"","sources":["../../../src/rewriter/templates/yaml.ts"],"names":[],"mappings":";;;AAEA;;;;GAIG;AACH,qBAA4B,GAAmB;IAC7C,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,GAAG,CAAC;IACrC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAErC,IAAI,gBAAgB,GAAG,WAAW,CAAC;IACnC,IAAI,YAAY,GAAG,CAAC,CAAC;IAErB,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IACnC,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;QAChE,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QACjD,IAAI,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAC9B,gBAAgB,GAAG,gBAAgB,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,MAAM,GAAG,CAAC,CAAC;YACjE,YAAY,EAAE,CAAC;YACf,MAAM;QACR,CAAC;IACH,CAAC;IAED,IAAI,YAAY,GAAG,CAAC,IAAI,CAAC,yBAAyB,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAC1E,gBAAgB;YACd,2FAA2F;gBAC3F,gBAAgB,CAAC;IACrB,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,OAAO,CAAC,IAAI;QACtB,eAAe,EAAE,WAAW;QAC5B,gBAAgB;QAChB,YAAY;QACZ,cAAc,EAAE,KAAK;KACtB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { Finding, Rule } from '../types';
2
+ /**
3
+ * Generates the env variable name for a finding using context from the line.
4
+ * Falls back to the rule's envKeyPrefix.
5
+ */
6
+ export declare function generateEnvKeyBase(rule: Rule, lineContent: string, filePath: string): string;
7
+ /**
8
+ * Scans a single file for secrets using all rules.
9
+ * Returns a list of raw findings (envKey not yet deduplicated).
10
+ */
11
+ export declare function scanFile(filePath: string): Finding[];
12
+ /**
13
+ * Deduplicates env keys across all findings.
14
+ * If two findings would share the same key, appends _2, _3, etc.
15
+ * Also accepts existing .env keys to avoid collisions with them.
16
+ */
17
+ export declare function deduplicateEnvKeys(findings: Finding[], existingKeys: Set<string>): Finding[];
18
+ //# sourceMappingURL=detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detector.d.ts","sourceRoot":"","sources":["../../src/scanner/detector.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAsDzC;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAoB5F;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,EAAE,CA4DpD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,EAAE,CAa5F"}
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.generateEnvKeyBase = generateEnvKeyBase;
7
+ exports.scanFile = scanFile;
8
+ exports.deduplicateEnvKeys = deduplicateEnvKeys;
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const rules_1 = require("./rules");
12
+ /**
13
+ * Values that are commonly used as placeholder/example values — skip these.
14
+ */
15
+ const PLACEHOLDER_PATTERNS = [
16
+ /^your[_-]/i,
17
+ /^xxx+$/i,
18
+ /^<.+>$/, // <YOUR_KEY>
19
+ /^\$\{.+\}$/, // ${MY_VAR}
20
+ /^\$[A-Z_]+$/, // $MY_VAR
21
+ /^process\.env\./,
22
+ /^os\.environ/,
23
+ /^ENV\[/,
24
+ /^System\.getenv/,
25
+ /^getenv\(/,
26
+ /^placeholder$/i,
27
+ /^changeme$/i,
28
+ /^change_me$/i,
29
+ /^replace_?me$/i,
30
+ /^todo$/i,
31
+ /^fixme$/i,
32
+ /^insert[_-]?here$/i,
33
+ /^example$/i,
34
+ /^test$/i,
35
+ /^dummy$/i,
36
+ /^fake$/i,
37
+ /^mock$/i,
38
+ /^secret$/i, // bare word "secret" — likely a placeholder
39
+ /^password$/i, // bare word "password"
40
+ /^12345+$/,
41
+ /^0{8,}$/,
42
+ /^1{8,}$/,
43
+ /^null$/i,
44
+ /^undefined$/i,
45
+ /^none$/i,
46
+ /^empty$/i,
47
+ /^n\/a$/i,
48
+ ];
49
+ /**
50
+ * Minimum lengths for secret values to avoid false positives.
51
+ */
52
+ const MIN_SECRET_LENGTH = 4;
53
+ /**
54
+ * Returns true if the value looks like a placeholder/template value.
55
+ */
56
+ function isPlaceholder(value) {
57
+ if (value.length < MIN_SECRET_LENGTH)
58
+ return true;
59
+ return PLACEHOLDER_PATTERNS.some(p => p.test(value));
60
+ }
61
+ /**
62
+ * Generates the env variable name for a finding using context from the line.
63
+ * Falls back to the rule's envKeyPrefix.
64
+ */
65
+ function generateEnvKeyBase(rule, lineContent, filePath) {
66
+ // Try to infer a more specific name from the variable name on the line
67
+ const assignmentMatch = lineContent.match(/(?:const|let|var|val|final|private|public|protected|static)?\s*([A-Za-z_][A-Za-z0-9_]*)\s*(?:[:=])/);
68
+ if (assignmentMatch) {
69
+ const varName = assignmentMatch[1]
70
+ .replace(/([a-z])([A-Z])/g, '$1_$2') // camelCase → snake_case
71
+ .toUpperCase();
72
+ // Only use var name if it looks meaningful and is not too generic
73
+ const genericNames = new Set(['VALUE', 'VAL', 'V', 'S', 'K', 'KEY', 'DATA', 'RESULT', 'RES', 'X', 'Y']);
74
+ if (!genericNames.has(varName) && varName.length > 2) {
75
+ return varName;
76
+ }
77
+ }
78
+ // Fall back to the rule's prefix
79
+ return rule.envKeyPrefix;
80
+ }
81
+ /**
82
+ * Scans a single file for secrets using all rules.
83
+ * Returns a list of raw findings (envKey not yet deduplicated).
84
+ */
85
+ function scanFile(filePath) {
86
+ let content;
87
+ try {
88
+ content = fs_1.default.readFileSync(filePath, 'utf-8');
89
+ }
90
+ catch {
91
+ // Binary or unreadable — skip
92
+ return [];
93
+ }
94
+ // Skip files larger than 5 MB
95
+ const stats = fs_1.default.statSync(filePath);
96
+ if (stats.size > 5 * 1024 * 1024)
97
+ return [];
98
+ const findings = [];
99
+ const lines = content.split('\n');
100
+ for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
101
+ const lineContent = lines[lineIdx];
102
+ // Skip comment-only lines (best-effort)
103
+ const trimmed = lineContent.trim();
104
+ if (trimmed.startsWith('//') ||
105
+ trimmed.startsWith('#') ||
106
+ trimmed.startsWith('*') ||
107
+ trimmed.startsWith('/*') ||
108
+ trimmed.startsWith('<!--')) {
109
+ continue;
110
+ }
111
+ for (const rule of rules_1.ALL_RULES) {
112
+ // Reset lastIndex for global regexes
113
+ rule.pattern.lastIndex = 0;
114
+ const match = rule.pattern.exec(lineContent);
115
+ if (!match)
116
+ continue;
117
+ const rawValue = match[rule.valueGroup] ?? match[0];
118
+ if (!rawValue || isPlaceholder(rawValue))
119
+ continue;
120
+ const column = lineContent.indexOf(rawValue) + 1;
121
+ const envKey = generateEnvKeyBase(rule, lineContent, filePath);
122
+ findings.push({
123
+ file: path_1.default.resolve(filePath),
124
+ line: lineIdx + 1,
125
+ column,
126
+ rule,
127
+ rawValue,
128
+ lineContent,
129
+ envKey,
130
+ });
131
+ // Only match first rule per line to avoid duplicates
132
+ break;
133
+ }
134
+ }
135
+ return findings;
136
+ }
137
+ /**
138
+ * Deduplicates env keys across all findings.
139
+ * If two findings would share the same key, appends _2, _3, etc.
140
+ * Also accepts existing .env keys to avoid collisions with them.
141
+ */
142
+ function deduplicateEnvKeys(findings, existingKeys) {
143
+ const usedKeys = new Set(existingKeys);
144
+ return findings.map(finding => {
145
+ let candidate = finding.envKey;
146
+ if (usedKeys.has(candidate)) {
147
+ let suffix = 2;
148
+ while (usedKeys.has(`${candidate}_${suffix}`))
149
+ suffix++;
150
+ candidate = `${candidate}_${suffix}`;
151
+ }
152
+ usedKeys.add(candidate);
153
+ return { ...finding, envKey: candidate };
154
+ });
155
+ }
156
+ //# sourceMappingURL=detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detector.js","sourceRoot":"","sources":["../../src/scanner/detector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Recursively walks `targetDir` and returns the absolute paths of all
3
+ * scannable files, respecting the nearest .gitignore.
4
+ */
5
+ export declare function walkFiles(targetDir: string): Promise<string[]>;
6
+ //# sourceMappingURL=fileWalker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileWalker.d.ts","sourceRoot":"","sources":["../../src/scanner/fileWalker.ts"],"names":[],"mappings":"AAuHA;;;GAGG;AACH,wBAAsB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAqBpE"}
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.walkFiles = walkFiles;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const ignore_1 = __importDefault(require("ignore"));
10
+ const fast_glob_1 = __importDefault(require("fast-glob"));
11
+ /**
12
+ * File extensions we will attempt to scan for secrets.
13
+ * Binary and compiled files are excluded.
14
+ */
15
+ const SCANNABLE_EXTENSIONS = new Set([
16
+ // Web / JS ecosystem
17
+ '.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs', '.vue', '.svelte',
18
+ // Config / env
19
+ '.env', '.env.local', '.env.development', '.env.staging', '.env.production',
20
+ '.json', '.jsonc', '.json5',
21
+ '.yaml', '.yml',
22
+ '.toml',
23
+ '.ini',
24
+ '.properties',
25
+ '.conf', '.config',
26
+ '.xml',
27
+ // Backend languages
28
+ '.py', '.rb', '.php', '.java', '.kt', '.kts', '.scala',
29
+ '.go', '.rs', '.cs', '.vb', '.fs', '.fsx',
30
+ '.cpp', '.c', '.h', '.hpp', '.cc',
31
+ '.swift', '.m', '.mm',
32
+ '.dart',
33
+ '.ex', '.exs', // Elixir
34
+ '.erl', '.hrl', // Erlang
35
+ '.clj', '.cljs', // Clojure
36
+ '.hs', '.lhs', // Haskell
37
+ '.lua',
38
+ '.pl', '.pm', // Perl
39
+ '.r', '.R', // R
40
+ // Shell / scripting
41
+ '.sh', '.bash', '.zsh', '.fish', '.ps1', '.psm1', '.bat', '.cmd',
42
+ // Infrastructure as code
43
+ '.tf', '.tfvars', // Terraform
44
+ '.hcl',
45
+ '.dockerfile',
46
+ // Misc
47
+ '.gradle', '.groovy',
48
+ '.sql',
49
+ '.graphql', '.gql',
50
+ '.proto',
51
+ '.md', '.mdx', '.txt', '.log',
52
+ ]);
53
+ /**
54
+ * File names (without extension, or exact names) that should always be scanned.
55
+ */
56
+ const SCANNABLE_FILENAMES = new Set([
57
+ 'Dockerfile', 'docker-compose', 'docker-compose.yml', 'docker-compose.yaml',
58
+ '.env', '.envrc', 'Makefile', 'Procfile', 'Gemfile', 'Rakefile',
59
+ '.npmrc', '.yarnrc', '.pypirc', '.netrc', '.htpasswd',
60
+ ]);
61
+ /**
62
+ * Patterns for paths that should always be excluded regardless of .gitignore.
63
+ */
64
+ const ALWAYS_IGNORE_GLOBS = [
65
+ '**/node_modules/**',
66
+ '**/.git/**',
67
+ '**/dist/**',
68
+ '**/build/**',
69
+ '**/out/**',
70
+ '**/__pycache__/**',
71
+ '**/*.pyc',
72
+ '**/*.pyo',
73
+ '**/vendor/**',
74
+ '**/.yarn/**',
75
+ '**/.pnp.*',
76
+ '**/coverage/**',
77
+ '**/.nyc_output/**',
78
+ '**/*.min.js',
79
+ '**/*.min.css',
80
+ '**/*.map',
81
+ '**/*.lock', // package-lock.json, yarn.lock, etc.
82
+ '**/Cargo.lock',
83
+ '**/*.png', '**/*.jpg', '**/*.jpeg', '**/*.gif', '**/*.ico',
84
+ '**/*.svg', '**/*.webp', '**/*.avif',
85
+ '**/*.woff', '**/*.woff2', '**/*.ttf', '**/*.eot',
86
+ '**/*.pdf', '**/*.zip', '**/*.tar', '**/*.gz', '**/*.tgz',
87
+ '**/*.exe', '**/*.dll', '**/*.so', '**/*.dylib',
88
+ '**/*.jar', '**/*.war', '**/*.ear', '**/*.class',
89
+ '**/*.DS_Store',
90
+ ];
91
+ /**
92
+ * Loads a .gitignore file from the given directory and returns an `ignore` instance.
93
+ */
94
+ function loadGitIgnore(dir) {
95
+ const ig = (0, ignore_1.default)();
96
+ const gitignorePath = path_1.default.join(dir, '.gitignore');
97
+ if (fs_1.default.existsSync(gitignorePath)) {
98
+ const content = fs_1.default.readFileSync(gitignorePath, 'utf-8');
99
+ ig.add(content);
100
+ }
101
+ // Also check nested .gitignore files by adding the built-in always-ignore patterns
102
+ ig.add(ALWAYS_IGNORE_GLOBS.map(g => g.replace(/\*\*\//g, '')));
103
+ return ig;
104
+ }
105
+ /**
106
+ * Determines whether a file is scannable based on its extension or filename.
107
+ */
108
+ function isScannableFile(filePath) {
109
+ const basename = path_1.default.basename(filePath);
110
+ const ext = path_1.default.extname(filePath).toLowerCase();
111
+ if (SCANNABLE_FILENAMES.has(basename))
112
+ return true;
113
+ if (ext && SCANNABLE_EXTENSIONS.has(ext))
114
+ return true;
115
+ // Files with no extension that look like config files
116
+ if (!ext && basename.startsWith('.'))
117
+ return true;
118
+ return false;
119
+ }
120
+ /**
121
+ * Recursively walks `targetDir` and returns the absolute paths of all
122
+ * scannable files, respecting the nearest .gitignore.
123
+ */
124
+ async function walkFiles(targetDir) {
125
+ const absoluteDir = path_1.default.resolve(targetDir);
126
+ const ig = loadGitIgnore(absoluteDir);
127
+ const allFiles = await (0, fast_glob_1.default)('**/*', {
128
+ cwd: absoluteDir,
129
+ absolute: true,
130
+ onlyFiles: true,
131
+ dot: true,
132
+ ignore: ALWAYS_IGNORE_GLOBS,
133
+ followSymbolicLinks: false,
134
+ });
135
+ return allFiles.filter(filePath => {
136
+ const relative = path_1.default.relative(absoluteDir, filePath);
137
+ // Apply gitignore rules
138
+ if (ig.ignores(relative))
139
+ return false;
140
+ // Apply extension/filename filter
141
+ if (!isScannableFile(filePath))
142
+ return false;
143
+ return true;
144
+ });
145
+ }
146
+ //# sourceMappingURL=fileWalker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileWalker.js","sourceRoot":"","sources":["../../src/scanner/fileWalker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { ScanSummary } from '../types';
2
+ /**
3
+ * Main scanner orchestrator.
4
+ * Walks the target directory, scans every file, deduplicates env keys,
5
+ * and returns a full ScanSummary.
6
+ */
7
+ export declare function runScanner(targetDir: string, envFilePath: string): Promise<ScanSummary>;
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scanner/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAW,WAAW,EAAE,MAAM,UAAU,CAAC;AAqBhD;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA8C7F"}
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.runScanner = runScanner;
7
+ const ora_1 = __importDefault(require("ora"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const fileWalker_1 = require("./fileWalker");
11
+ const detector_1 = require("./detector");
12
+ /**
13
+ * Reads existing keys from a .env file to seed the deduplication set.
14
+ */
15
+ function readExistingEnvKeys(envFilePath) {
16
+ const keys = new Set();
17
+ if (!fs_1.default.existsSync(envFilePath))
18
+ return keys;
19
+ const content = fs_1.default.readFileSync(envFilePath, 'utf-8');
20
+ for (const line of content.split('\n')) {
21
+ const trimmed = line.trim();
22
+ if (!trimmed || trimmed.startsWith('#'))
23
+ continue;
24
+ const eqIdx = trimmed.indexOf('=');
25
+ if (eqIdx > 0)
26
+ keys.add(trimmed.slice(0, eqIdx).trim());
27
+ }
28
+ return keys;
29
+ }
30
+ /**
31
+ * Main scanner orchestrator.
32
+ * Walks the target directory, scans every file, deduplicates env keys,
33
+ * and returns a full ScanSummary.
34
+ */
35
+ async function runScanner(targetDir, envFilePath) {
36
+ const spinner = (0, ora_1.default)({
37
+ text: `Discovering files in ${path_1.default.resolve(targetDir)} …`,
38
+ color: 'cyan',
39
+ }).start();
40
+ let files;
41
+ try {
42
+ files = await (0, fileWalker_1.walkFiles)(targetDir);
43
+ }
44
+ catch (err) {
45
+ spinner.fail('Failed to walk directory');
46
+ throw err;
47
+ }
48
+ spinner.text = `Scanning ${files.length} files for secrets …`;
49
+ const allFindings = [];
50
+ const filesWithSecrets = new Set();
51
+ for (const filePath of files) {
52
+ const fileFindings = (0, detector_1.scanFile)(filePath);
53
+ if (fileFindings.length > 0) {
54
+ allFindings.push(...fileFindings);
55
+ filesWithSecrets.add(filePath);
56
+ }
57
+ spinner.text = `Scanned ${files.indexOf(filePath) + 1}/${files.length} files — ${allFindings.length} secret(s) found so far …`;
58
+ }
59
+ spinner.succeed(`Scan complete: ${files.length} files scanned, ${allFindings.length} secret(s) found in ${filesWithSecrets.size} file(s).`);
60
+ // Deduplicate env keys, considering what's already in .env
61
+ const existingKeys = readExistingEnvKeys(envFilePath);
62
+ const deduplicated = (0, detector_1.deduplicateEnvKeys)(allFindings, existingKeys);
63
+ return {
64
+ targetDir: path_1.default.resolve(targetDir),
65
+ filesScanned: files.length,
66
+ filesWithSecrets: filesWithSecrets.size,
67
+ totalFindings: deduplicated.length,
68
+ findings: deduplicated,
69
+ envFile: path_1.default.resolve(envFilePath),
70
+ dryRun: false, // set by caller
71
+ scannedAt: new Date().toISOString(),
72
+ };
73
+ }
74
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/scanner/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { Rule } from '../../types';
2
+ /**
3
+ * AWS credential patterns.
4
+ */
5
+ export declare const awsRules: Rule[];
6
+ //# sourceMappingURL=aws.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aws.d.ts","sourceRoot":"","sources":["../../../src/scanner/rules/aws.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAEnC;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,IAAI,EAkF1B,CAAC"}
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.awsRules = void 0;
4
+ /**
5
+ * AWS credential patterns.
6
+ */
7
+ exports.awsRules = [
8
+ {
9
+ id: 'aws-access-key-id',
10
+ description: 'AWS Access Key ID',
11
+ // AWS access key IDs start with AKIA, ABIA, ACCA, or ASIA
12
+ pattern: /(?:aws[_-]?access[_-]?key[_-]?id|aws[_-]?access[_-]?key)\s*[:=]\s*['"\`]?((?:AKIA|ABIA|ACCA|ASIA)[A-Z0-9]{16})['"\`]?/i,
13
+ valueGroup: 1,
14
+ envKeyPrefix: 'AWS_ACCESS_KEY_ID',
15
+ severity: 'critical',
16
+ },
17
+ {
18
+ id: 'aws-access-key-id-bare',
19
+ description: 'Bare AWS Access Key ID (without variable name context)',
20
+ pattern: /['"\`]((?:AKIA|ABIA|ACCA|ASIA)[A-Z0-9]{16})['"\`]/,
21
+ valueGroup: 1,
22
+ envKeyPrefix: 'AWS_ACCESS_KEY_ID',
23
+ severity: 'critical',
24
+ },
25
+ {
26
+ id: 'aws-secret-access-key',
27
+ description: 'AWS Secret Access Key',
28
+ pattern: /(?:aws[_-]?secret[_-]?access[_-]?key|aws[_-]?secret[_-]?key)\s*[:=]\s*['"\`]([A-Za-z0-9+/]{40})['"\`]/i,
29
+ valueGroup: 1,
30
+ envKeyPrefix: 'AWS_SECRET_ACCESS_KEY',
31
+ severity: 'critical',
32
+ },
33
+ {
34
+ id: 'aws-session-token',
35
+ description: 'AWS Session Token',
36
+ pattern: /(?:aws[_-]?session[_-]?token|aws[_-]?security[_-]?token)\s*[:=]\s*['"\`]([A-Za-z0-9+/=]{100,})['"\`]/i,
37
+ valueGroup: 1,
38
+ envKeyPrefix: 'AWS_SESSION_TOKEN',
39
+ severity: 'critical',
40
+ },
41
+ {
42
+ id: 'aws-account-id',
43
+ description: 'AWS Account ID',
44
+ pattern: /(?:aws[_-]?account[_-]?id|aws[_-]?account)\s*[:=]\s*['"\`]?(\d{12})['"\`]?/i,
45
+ valueGroup: 1,
46
+ envKeyPrefix: 'AWS_ACCOUNT_ID',
47
+ severity: 'high',
48
+ },
49
+ {
50
+ id: 'aws-mws-key',
51
+ description: 'AWS MWS (Marketplace Web Service) Key',
52
+ pattern: /amzn\.mws\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i,
53
+ valueGroup: 0,
54
+ envKeyPrefix: 'AWS_MWS_KEY',
55
+ severity: 'critical',
56
+ },
57
+ {
58
+ id: 'aws-region',
59
+ description: 'AWS Region hardcoded assignment',
60
+ pattern: /(?:aws[_-]?region)\s*[:=]\s*['"\`](us-east-[12]|us-west-[12]|eu-west-[123]|eu-central-1|ap-southeast-[12]|ap-northeast-[123]|sa-east-1|ca-central-1|ap-south-1|ap-east-1|me-south-1|af-south-1)['"\`]/i,
61
+ valueGroup: 1,
62
+ envKeyPrefix: 'AWS_REGION',
63
+ severity: 'low',
64
+ },
65
+ {
66
+ id: 'aws-arn',
67
+ description: 'Hardcoded AWS ARN (resource identifier)',
68
+ pattern: /(?:arn[_-]?value|resource[_-]?arn|target[_-]?arn|(?:sns|sqs|lambda|iam|s3)[_-]?arn)\s*[:=]\s*['"\`](arn:aws:[a-z0-9\-]+:[a-z0-9\-]*:\d{12}:[^'"\`\s]+)['"\`]/i,
69
+ valueGroup: 1,
70
+ envKeyPrefix: 'AWS_ARN',
71
+ severity: 'medium',
72
+ },
73
+ {
74
+ id: 'aws-s3-bucket',
75
+ description: 'AWS S3 bucket name hardcoded assignment',
76
+ pattern: /(?:s3[_-]?bucket(?:[_-]?name)?|bucket[_-]?name|AWS_S3_BUCKET)\s*[:=]\s*['"\`]([a-z0-9][a-z0-9\-\.]{1,61}[a-z0-9])['"\`]/i,
77
+ valueGroup: 1,
78
+ envKeyPrefix: 'AWS_S3_BUCKET',
79
+ severity: 'medium',
80
+ },
81
+ {
82
+ id: 'aws-cloudfront-key-pair-id',
83
+ description: 'AWS CloudFront Key Pair ID',
84
+ pattern: /(?:cloudfront[_-]?key[_-]?pair[_-]?id|CLOUDFRONT_KEY_PAIR_ID)\s*[:=]\s*['"\`]([A-Z0-9]{14,20})['"\`]/i,
85
+ valueGroup: 1,
86
+ envKeyPrefix: 'CLOUDFRONT_KEY_PAIR_ID',
87
+ severity: 'high',
88
+ },
89
+ ];
90
+ //# sourceMappingURL=aws.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aws.js","sourceRoot":"","sources":["../../../src/scanner/rules/aws.ts"],"names":[],"mappings":";;;AAEA;;GAEG;AACU,QAAA,QAAQ,GAAW;IAC9B;QACE,EAAE,EAAE,mBAAmB;QACvB,WAAW,EAAE,mBAAmB;QAChC,0DAA0D;QAC1D,OAAO,EAAE,wHAAwH;QACjI,UAAU,EAAE,CAAC;QACb,YAAY,EAAE,mBAAmB;QACjC,QAAQ,EAAE,UAAU;KACrB;IACD;QACE,EAAE,EAAE,wBAAwB;QAC5B,WAAW,EAAE,wDAAwD;QACrE,OAAO,EAAE,mDAAmD;QAC5D,UAAU,EAAE,CAAC;QACb,YAAY,EAAE,mBAAmB;QACjC,QAAQ,EAAE,UAAU;KACrB;IACD;QACE,EAAE,EAAE,uBAAuB;QAC3B,WAAW,EAAE,uBAAuB;QACpC,OAAO,EAAE,wGAAwG;QACjH,UAAU,EAAE,CAAC;QACb,YAAY,EAAE,uBAAuB;QACrC,QAAQ,EAAE,UAAU;KACrB;IACD;QACE,EAAE,EAAE,mBAAmB;QACvB,WAAW,EAAE,mBAAmB;QAChC,OAAO,EAAE,uGAAuG;QAChH,UAAU,EAAE,CAAC;QACb,YAAY,EAAE,mBAAmB;QACjC,QAAQ,EAAE,UAAU;KACrB;IACD;QACE,EAAE,EAAE,gBAAgB;QACpB,WAAW,EAAE,gBAAgB;QAC7B,OAAO,EAAE,6EAA6E;QACtF,UAAU,EAAE,CAAC;QACb,YAAY,EAAE,gBAAgB;QAC9B,QAAQ,EAAE,MAAM;KACjB;IACD;QACE,EAAE,EAAE,aAAa;QACjB,WAAW,EAAE,uCAAuC;QACpD,OAAO,EAAE,0EAA0E;QACnF,UAAU,EAAE,CAAC;QACb,YAAY,EAAE,aAAa;QAC3B,QAAQ,EAAE,UAAU;KACrB;IACD;QACE,EAAE,EAAE,YAAY;QAChB,WAAW,EAAE,iCAAiC;QAC9C,OAAO,EAAE,wMAAwM;QACjN,UAAU,EAAE,CAAC;QACb,YAAY,EAAE,YAAY;QAC1B,QAAQ,EAAE,KAAK;KAChB;IACD;QACE,EAAE,EAAE,SAAS;QACb,WAAW,EAAE,yCAAyC;QACtD,OAAO,EAAE,+JAA+J;QACxK,UAAU,EAAE,CAAC;QACb,YAAY,EAAE,SAAS;QACvB,QAAQ,EAAE,QAAQ;KACnB;IACD;QACE,EAAE,EAAE,eAAe;QACnB,WAAW,EAAE,yCAAyC;QACtD,OAAO,EAAE,0HAA0H;QACnI,UAAU,EAAE,CAAC;QACb,YAAY,EAAE,eAAe;QAC7B,QAAQ,EAAE,QAAQ;KACnB;IACD;QACE,EAAE,EAAE,4BAA4B;QAChC,WAAW,EAAE,4BAA4B;QACzC,OAAO,EAAE,uGAAuG;QAChH,UAAU,EAAE,CAAC;QACb,YAAY,EAAE,wBAAwB;QACtC,QAAQ,EAAE,MAAM;KACjB;CACF,CAAC"}