@datahogo/core 0.1.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 (153) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +17 -0
  3. package/dist/engines/config.d.ts +3 -0
  4. package/dist/engines/config.d.ts.map +1 -0
  5. package/dist/engines/config.js +433 -0
  6. package/dist/engines/config.js.map +1 -0
  7. package/dist/engines/dast.d.ts +3 -0
  8. package/dist/engines/dast.d.ts.map +1 -0
  9. package/dist/engines/dast.js +167 -0
  10. package/dist/engines/dast.js.map +1 -0
  11. package/dist/engines/db-rules.d.ts +3 -0
  12. package/dist/engines/db-rules.d.ts.map +1 -0
  13. package/dist/engines/db-rules.js +166 -0
  14. package/dist/engines/db-rules.js.map +1 -0
  15. package/dist/engines/dependencies.d.ts +3 -0
  16. package/dist/engines/dependencies.d.ts.map +1 -0
  17. package/dist/engines/dependencies.js +167 -0
  18. package/dist/engines/dependencies.js.map +1 -0
  19. package/dist/engines/github-actions.d.ts +3 -0
  20. package/dist/engines/github-actions.d.ts.map +1 -0
  21. package/dist/engines/github-actions.js +215 -0
  22. package/dist/engines/github-actions.js.map +1 -0
  23. package/dist/engines/gitleaks.d.ts +3 -0
  24. package/dist/engines/gitleaks.d.ts.map +1 -0
  25. package/dist/engines/gitleaks.js +107 -0
  26. package/dist/engines/gitleaks.js.map +1 -0
  27. package/dist/engines/npm-audit.d.ts +3 -0
  28. package/dist/engines/npm-audit.d.ts.map +1 -0
  29. package/dist/engines/npm-audit.js +113 -0
  30. package/dist/engines/npm-audit.js.map +1 -0
  31. package/dist/engines/patterns.d.ts +3 -0
  32. package/dist/engines/patterns.d.ts.map +1 -0
  33. package/dist/engines/patterns.js +1330 -0
  34. package/dist/engines/patterns.js.map +1 -0
  35. package/dist/engines/secrets.d.ts +4 -0
  36. package/dist/engines/secrets.d.ts.map +1 -0
  37. package/dist/engines/secrets.js +260 -0
  38. package/dist/engines/secrets.js.map +1 -0
  39. package/dist/engines/semgrep.d.ts +3 -0
  40. package/dist/engines/semgrep.d.ts.map +1 -0
  41. package/dist/engines/semgrep.js +173 -0
  42. package/dist/engines/semgrep.js.map +1 -0
  43. package/dist/engines/types.d.ts +30 -0
  44. package/dist/engines/types.d.ts.map +1 -0
  45. package/dist/engines/types.js +3 -0
  46. package/dist/engines/types.js.map +1 -0
  47. package/dist/engines/url-scanner.d.ts +3 -0
  48. package/dist/engines/url-scanner.d.ts.map +1 -0
  49. package/dist/engines/url-scanner.js +469 -0
  50. package/dist/engines/url-scanner.js.map +1 -0
  51. package/dist/index.d.ts +8 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +8 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/orchestrator.d.ts +32 -0
  56. package/dist/orchestrator.d.ts.map +1 -0
  57. package/dist/orchestrator.js +237 -0
  58. package/dist/orchestrator.js.map +1 -0
  59. package/dist/rules/.gitkeep +0 -0
  60. package/dist/rules/ai-llm-security.yaml +199 -0
  61. package/dist/rules/api-security.yaml +489 -0
  62. package/dist/rules/auth-patterns.yaml +406 -0
  63. package/dist/rules/crypto-patterns.yaml +227 -0
  64. package/dist/rules/database-cloud-security.yaml +169 -0
  65. package/dist/rules/general-security.yaml +588 -0
  66. package/dist/rules/injection-patterns.yaml +318 -0
  67. package/dist/rules/nextjs-security.yaml +532 -0
  68. package/dist/rules/node-security.yaml +380 -0
  69. package/dist/rules/rules/.gitkeep +0 -0
  70. package/dist/rules/rules/ai-llm-security.yaml +199 -0
  71. package/dist/rules/rules/api-security.yaml +489 -0
  72. package/dist/rules/rules/auth-patterns.yaml +406 -0
  73. package/dist/rules/rules/crypto-patterns.yaml +227 -0
  74. package/dist/rules/rules/database-cloud-security.yaml +169 -0
  75. package/dist/rules/rules/general-security.yaml +588 -0
  76. package/dist/rules/rules/injection-patterns.yaml +318 -0
  77. package/dist/rules/rules/nextjs-security.yaml +532 -0
  78. package/dist/rules/rules/node-security.yaml +380 -0
  79. package/dist/rules/rules/supabase-security.yaml +387 -0
  80. package/dist/rules/supabase-security.yaml +387 -0
  81. package/dist/scanning/agents/csharp-agent.d.ts +12 -0
  82. package/dist/scanning/agents/csharp-agent.d.ts.map +1 -0
  83. package/dist/scanning/agents/csharp-agent.js +592 -0
  84. package/dist/scanning/agents/csharp-agent.js.map +1 -0
  85. package/dist/scanning/agents/go-agent.d.ts +14 -0
  86. package/dist/scanning/agents/go-agent.d.ts.map +1 -0
  87. package/dist/scanning/agents/go-agent.js +641 -0
  88. package/dist/scanning/agents/go-agent.js.map +1 -0
  89. package/dist/scanning/agents/java-agent.d.ts +8 -0
  90. package/dist/scanning/agents/java-agent.d.ts.map +1 -0
  91. package/dist/scanning/agents/java-agent.js +807 -0
  92. package/dist/scanning/agents/java-agent.js.map +1 -0
  93. package/dist/scanning/agents/javascript-agent.d.ts +8 -0
  94. package/dist/scanning/agents/javascript-agent.d.ts.map +1 -0
  95. package/dist/scanning/agents/javascript-agent.js +77 -0
  96. package/dist/scanning/agents/javascript-agent.js.map +1 -0
  97. package/dist/scanning/agents/mobile-agent.d.ts +8 -0
  98. package/dist/scanning/agents/mobile-agent.d.ts.map +1 -0
  99. package/dist/scanning/agents/mobile-agent.js +916 -0
  100. package/dist/scanning/agents/mobile-agent.js.map +1 -0
  101. package/dist/scanning/agents/php-agent.d.ts +8 -0
  102. package/dist/scanning/agents/php-agent.d.ts.map +1 -0
  103. package/dist/scanning/agents/php-agent.js +679 -0
  104. package/dist/scanning/agents/php-agent.js.map +1 -0
  105. package/dist/scanning/agents/python-agent.d.ts +8 -0
  106. package/dist/scanning/agents/python-agent.d.ts.map +1 -0
  107. package/dist/scanning/agents/python-agent.js +701 -0
  108. package/dist/scanning/agents/python-agent.js.map +1 -0
  109. package/dist/scanning/agents/supabase-agent.d.ts +8 -0
  110. package/dist/scanning/agents/supabase-agent.d.ts.map +1 -0
  111. package/dist/scanning/agents/supabase-agent.js +484 -0
  112. package/dist/scanning/agents/supabase-agent.js.map +1 -0
  113. package/dist/scanning/orchestrator.d.ts +29 -0
  114. package/dist/scanning/orchestrator.d.ts.map +1 -0
  115. package/dist/scanning/orchestrator.js +186 -0
  116. package/dist/scanning/orchestrator.js.map +1 -0
  117. package/dist/scanning/tech-detector.d.ts +12 -0
  118. package/dist/scanning/tech-detector.d.ts.map +1 -0
  119. package/dist/scanning/tech-detector.js +252 -0
  120. package/dist/scanning/tech-detector.js.map +1 -0
  121. package/dist/scanning/types.d.ts +87 -0
  122. package/dist/scanning/types.d.ts.map +1 -0
  123. package/dist/scanning/types.js +5 -0
  124. package/dist/scanning/types.js.map +1 -0
  125. package/dist/utils/child-process.d.ts +2 -0
  126. package/dist/utils/child-process.d.ts.map +1 -0
  127. package/dist/utils/child-process.js +4 -0
  128. package/dist/utils/child-process.js.map +1 -0
  129. package/dist/utils/context-classifier.d.ts +11 -0
  130. package/dist/utils/context-classifier.d.ts.map +1 -0
  131. package/dist/utils/context-classifier.js +79 -0
  132. package/dist/utils/context-classifier.js.map +1 -0
  133. package/dist/utils/exec.d.ts +18 -0
  134. package/dist/utils/exec.d.ts.map +1 -0
  135. package/dist/utils/exec.js +51 -0
  136. package/dist/utils/exec.js.map +1 -0
  137. package/dist/utils/file-filter.d.ts +4 -0
  138. package/dist/utils/file-filter.d.ts.map +1 -0
  139. package/dist/utils/file-filter.js +68 -0
  140. package/dist/utils/file-filter.js.map +1 -0
  141. package/dist/utils/fs.d.ts +2 -0
  142. package/dist/utils/fs.d.ts.map +1 -0
  143. package/dist/utils/fs.js +5 -0
  144. package/dist/utils/fs.js.map +1 -0
  145. package/dist/utils/logger.d.ts +12 -0
  146. package/dist/utils/logger.d.ts.map +1 -0
  147. package/dist/utils/logger.js +27 -0
  148. package/dist/utils/logger.js.map +1 -0
  149. package/dist/utils/post-processor.d.ts +10 -0
  150. package/dist/utils/post-processor.d.ts.map +1 -0
  151. package/dist/utils/post-processor.js +183 -0
  152. package/dist/utils/post-processor.js.map +1 -0
  153. package/package.json +44 -0
@@ -0,0 +1,79 @@
1
+ // Context classifier — determines if a finding is in production code,
2
+ // test files, examples, config, rule definitions, or vendored code.
3
+ // Used to separate real issues from noise in scan reports.
4
+ // Order matters: first match wins.
5
+ // Use (^|\/) to match both "test/foo.ts" and "src/test/foo.ts".
6
+ const PATH_RULES = [
7
+ // Test files
8
+ { pattern: /\.test\.[cm]?[tj]sx?$/, context: "test" },
9
+ { pattern: /\.spec\.[cm]?[tj]sx?$/, context: "test" },
10
+ { pattern: /(^|\/)__tests__\//, context: "test" },
11
+ { pattern: /(^|\/)__mocks__\//, context: "test" },
12
+ { pattern: /(^|\/)fixtures?\//i, context: "test" },
13
+ { pattern: /(^|\/)test\//, context: "test" },
14
+ { pattern: /(^|\/)tests\//, context: "test" },
15
+ { pattern: /(^|\/)e2e\//, context: "test" },
16
+ { pattern: /(^|\/)cypress\//, context: "test" },
17
+ { pattern: /(^|\/)playwright\//, context: "test" },
18
+ { pattern: /\.stories\.[tj]sx?$/, context: "test" },
19
+ { pattern: /vitest\.config/, context: "test" },
20
+ { pattern: /jest\.config/, context: "test" },
21
+ { pattern: /playwright\.config/, context: "test" },
22
+ { pattern: /setup-e2e/, context: "test" },
23
+ { pattern: /setup-test/, context: "test" },
24
+ { pattern: /seed\.(ts|js|sql)$/i, context: "test" },
25
+ { pattern: /(^|\/)seeds?\//, context: "test" },
26
+ // Rule definitions and scanner engine code
27
+ { pattern: /(^|\/)rules\/.*\.ya?ml$/, context: "rule" },
28
+ { pattern: /\.semgrep\.ya?ml$/, context: "rule" },
29
+ { pattern: /\.gitleaks\.toml$/, context: "rule" },
30
+ { pattern: /worker\/src\/engines\//, context: "rule" },
31
+ { pattern: /worker\/src\/scanning\//, context: "rule" },
32
+ // Examples and documentation
33
+ { pattern: /(^|\/)content\//, context: "example" },
34
+ { pattern: /(^|\/)docs\//, context: "example" },
35
+ { pattern: /(^|\/)examples?\//, context: "example" },
36
+ { pattern: /(^|\/)demos?\//, context: "example" },
37
+ { pattern: /(^|\/)tutorials?\//, context: "example" },
38
+ { pattern: /(^|\/)samples?\//, context: "example" },
39
+ { pattern: /\.example\.[^/]+$/, context: "example" },
40
+ { pattern: /(^|\/)messages\/.*\.json$/, context: "example" },
41
+ // Config, scripts, and infrastructure
42
+ { pattern: /(^|\/)scripts\//, context: "config" },
43
+ { pattern: /(^|\/)\.github\/workflows\//, context: "config" },
44
+ { pattern: /(^|\/)\.circleci\//, context: "config" },
45
+ { pattern: /\.gitlab-ci\.ya?ml$/, context: "config" },
46
+ { pattern: /Jenkinsfile$/, context: "config" },
47
+ { pattern: /(^|\/)infra\//, context: "config" },
48
+ { pattern: /(^|\/)terraform\//, context: "config" },
49
+ { pattern: /(^|\/)pulumi\//, context: "config" },
50
+ { pattern: /(^|\/)supabase\/migrations\//, context: "config" },
51
+ { pattern: /(^|\/)migrations\//, context: "config" },
52
+ { pattern: /(^|\/)prisma\/migrations\//, context: "config" },
53
+ // Vendored / generated
54
+ { pattern: /(^|\/)vendor\//, context: "vendored" },
55
+ { pattern: /(^|\/)generated\//, context: "vendored" },
56
+ { pattern: /\.gen\.[tj]sx?$/, context: "vendored" },
57
+ { pattern: /\.generated\.[tj]sx?$/, context: "vendored" },
58
+ { pattern: /(^|\/)dist\//, context: "vendored" },
59
+ { pattern: /(^|\/)node_modules\//, context: "vendored" },
60
+ ];
61
+ export function classifyContext(input) {
62
+ const filePath = input.file_path ?? "";
63
+ // No file path = URL scanner or DB rules findings = always production
64
+ if (!filePath)
65
+ return "production";
66
+ for (const rule of PATH_RULES) {
67
+ if (rule.pattern.test(filePath)) {
68
+ return rule.context;
69
+ }
70
+ }
71
+ return "production";
72
+ }
73
+ export function classifyFindings(findings) {
74
+ return findings.map((f) => ({
75
+ ...f,
76
+ context: classifyContext(f),
77
+ }));
78
+ }
79
+ //# sourceMappingURL=context-classifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-classifier.js","sourceRoot":"","sources":["../../src/utils/context-classifier.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export interface ExecResult {
2
+ stdout: string;
3
+ stderr: string;
4
+ exitCode: number;
5
+ }
6
+ export interface ExecOptions {
7
+ timeoutMs: number;
8
+ cwd?: string;
9
+ env?: Record<string, string>;
10
+ maxBuffer?: number;
11
+ }
12
+ /**
13
+ * Execute a command with timeout. Returns stdout/stderr/exitCode.
14
+ * Throws on timeout or signal kill. Does NOT throw on non-zero exit code
15
+ * (many tools like gitleaks/npm audit exit non-zero when they find issues).
16
+ */
17
+ export declare function execWithTimeout(command: string, args: string[], options: ExecOptions): Promise<ExecResult>;
18
+ //# sourceMappingURL=exec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAID;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,CAAC,CAqDrB"}
@@ -0,0 +1,51 @@
1
+ // Shared utility for running external tool binaries with timeout support.
2
+ // Uses execFile (not exec) to avoid shell injection — critical for a security product.
3
+ import { execFile } from "./child-process.js";
4
+ import { warn } from "./logger.js";
5
+ const DEFAULT_MAX_BUFFER = 10 * 1024 * 1024; // 10MB
6
+ /**
7
+ * Execute a command with timeout. Returns stdout/stderr/exitCode.
8
+ * Throws on timeout or signal kill. Does NOT throw on non-zero exit code
9
+ * (many tools like gitleaks/npm audit exit non-zero when they find issues).
10
+ */
11
+ export function execWithTimeout(command, args, options) {
12
+ return new Promise((resolve, reject) => {
13
+ const child = execFile(command, args, {
14
+ cwd: options.cwd,
15
+ env: options.env ? { ...process.env, ...options.env } : undefined,
16
+ maxBuffer: options.maxBuffer ?? DEFAULT_MAX_BUFFER,
17
+ timeout: 0, // We handle timeout ourselves for graceful kill
18
+ }, (error, stdout, stderr) => {
19
+ clearTimeout(timer);
20
+ if (timedOut) {
21
+ return; // Already rejected by timeout handler
22
+ }
23
+ if (error && error.killed) {
24
+ reject(new Error(`${command} was killed by signal`));
25
+ return;
26
+ }
27
+ // Non-zero exit code is NOT an error — tools like gitleaks exit 1 on findings
28
+ const exitCode = error?.code !== undefined
29
+ ? (typeof error.code === "number" ? error.code : 1)
30
+ : 0;
31
+ resolve({ stdout, stderr, exitCode });
32
+ });
33
+ let timedOut = false;
34
+ const timer = setTimeout(() => {
35
+ timedOut = true;
36
+ warn(`${command} timed out after ${options.timeoutMs}ms, sending SIGTERM`);
37
+ child.kill("SIGTERM");
38
+ // Force kill after 5s if still alive
39
+ const forceKillTimer = setTimeout(() => {
40
+ if (!child.killed) {
41
+ warn(`${command} did not exit after SIGTERM, sending SIGKILL`);
42
+ child.kill("SIGKILL");
43
+ }
44
+ }, 5000);
45
+ forceKillTimer.unref();
46
+ reject(new Error(`${command} timed out after ${options.timeoutMs}ms`));
47
+ }, options.timeoutMs);
48
+ timer.unref();
49
+ });
50
+ }
51
+ //# sourceMappingURL=exec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exec.js","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,uFAAuF;AAEvF,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAenC,MAAM,kBAAkB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO;AAEpD;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAe,EACf,IAAc,EACd,OAAoB;IAEpB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,QAAQ,CACpB,OAAO,EACP,IAAI,EACJ;YACE,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS;YACjE,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,kBAAkB;YAClD,OAAO,EAAE,CAAC,EAAE,gDAAgD;SAC7D,EACD,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACxB,YAAY,CAAC,KAAK,CAAC,CAAC;YAEpB,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,CAAC,sCAAsC;YAChD,CAAC;YAED,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;gBAC1B,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,OAAO,uBAAuB,CAAC,CAAC,CAAC;gBACrD,OAAO;YACT,CAAC;YAED,8EAA8E;YAC9E,MAAM,QAAQ,GAAG,KAAK,EAAE,IAAI,KAAK,SAAS;gBACxC,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;gBACnD,CAAC,CAAC,CAAC,CAAC;YAEN,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;QACxC,CAAC,CACF,CAAC;QAEF,IAAI,QAAQ,GAAG,KAAK,CAAC;QAErB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,QAAQ,GAAG,IAAI,CAAC;YAChB,IAAI,CAAC,GAAG,OAAO,oBAAoB,OAAO,CAAC,SAAS,qBAAqB,CAAC,CAAC;YAC3E,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAEtB,qCAAqC;YACrC,MAAM,cAAc,GAAG,UAAU,CAAC,GAAG,EAAE;gBACrC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;oBAClB,IAAI,CAAC,GAAG,OAAO,8CAA8C,CAAC,CAAC;oBAC/D,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBACxB,CAAC;YACH,CAAC,EAAE,IAAI,CAAC,CAAC;YACT,cAAc,CAAC,KAAK,EAAE,CAAC;YAEvB,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,OAAO,oBAAoB,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC;QACzE,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QAEtB,KAAK,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare function isRelevantFile(filePath: string): boolean;
2
+ export declare function filterFiles(files: Map<string, string>): Map<string, string>;
3
+ export declare function getFileLanguage(filePath: string): string;
4
+ //# sourceMappingURL=file-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-filter.d.ts","sourceRoot":"","sources":["../../src/utils/file-filter.ts"],"names":[],"mappings":"AAiCA,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAgBxD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAQ3E;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUxD"}
@@ -0,0 +1,68 @@
1
+ const SCANNABLE_EXTENSIONS = [
2
+ // JavaScript / TypeScript
3
+ ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs",
4
+ // Languages covered by the multi-tech scan agents
5
+ ".py", ".go", ".java", ".kt", ".kts", ".php", ".cs", ".fs",
6
+ ".rb", ".rs", ".dart",
7
+ // Config / data formats (includes pyproject.toml, Cargo.toml, etc.)
8
+ ".json", ".yaml", ".yml", ".toml", ".gradle", ".sql", ".rules",
9
+ ".csproj", ".fsproj", ".sln",
10
+ ".env", ".env.local", ".env.production",
11
+ ];
12
+ const SCANNABLE_EXACT_FILES = [
13
+ ".gitignore", "Dockerfile", "docker-compose.yml", "docker-compose.yaml",
14
+ ".dockerignore", "Makefile", ".npmrc", ".yarnrc",
15
+ "vercel.json", "netlify.toml", "tsconfig.json",
16
+ ".eslintrc.json", ".eslintrc.js", ".prettierrc",
17
+ "next.config.js", "next.config.ts", "next.config.mjs",
18
+ "firebase.json", "firestore.rules", "storage.rules",
19
+ "supabase/config.toml",
20
+ ".env.example",
21
+ // Manifests the TechDetector relies on for non-JS stacks
22
+ "requirements.txt", "Pipfile", "go.mod", "go.sum",
23
+ "pom.xml", "Gemfile", "Gemfile.lock",
24
+ ];
25
+ const SKIP_DIRECTORIES = [
26
+ "node_modules", ".next", ".git", "dist", "build", ".cache",
27
+ "coverage", ".nyc_output", "__pycache__", ".venv",
28
+ ];
29
+ const MAX_FILE_SIZE = 500_000; // 500KB - skip very large files
30
+ export function isRelevantFile(filePath) {
31
+ // Skip hidden/build directories (allow .github for workflow scanning)
32
+ const parts = filePath.split("/");
33
+ for (const part of parts.slice(0, -1)) {
34
+ if (part === ".github")
35
+ continue; // Allow .github/workflows/
36
+ if (SKIP_DIRECTORIES.includes(part))
37
+ return false;
38
+ if (part.startsWith(".") && part !== ".")
39
+ return false; // Skip other hidden dirs
40
+ }
41
+ const fileName = parts[parts.length - 1];
42
+ // Check exact file matches
43
+ if (SCANNABLE_EXACT_FILES.includes(fileName))
44
+ return true;
45
+ // Check extensions
46
+ return SCANNABLE_EXTENSIONS.some(ext => fileName.endsWith(ext));
47
+ }
48
+ export function filterFiles(files) {
49
+ const filtered = new Map();
50
+ for (const [path, content] of files) {
51
+ if (isRelevantFile(path) && content.length <= MAX_FILE_SIZE) {
52
+ filtered.set(path, content);
53
+ }
54
+ }
55
+ return filtered;
56
+ }
57
+ export function getFileLanguage(filePath) {
58
+ const ext = filePath.split(".").pop()?.toLowerCase();
59
+ const langMap = {
60
+ ts: "typescript", tsx: "typescript",
61
+ js: "javascript", jsx: "javascript", mjs: "javascript", cjs: "javascript",
62
+ json: "json", yaml: "yaml", yml: "yaml",
63
+ sql: "sql", rules: "text",
64
+ env: "dotenv",
65
+ };
66
+ return langMap[ext || ""] || "text";
67
+ }
68
+ //# sourceMappingURL=file-filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-filter.js","sourceRoot":"","sources":["../../src/utils/file-filter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { readFile, rm, access } from "node:fs/promises";
2
+ //# sourceMappingURL=fs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../src/utils/fs.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,5 @@
1
+ // Re-export fs/promises functions for testability.
2
+ // Vitest cannot reliably mock node:fs/promises builtins,
3
+ // so engines import from this module instead.
4
+ export { readFile, rm, access } from "node:fs/promises";
5
+ //# sourceMappingURL=fs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.js","sourceRoot":"","sources":["../../src/utils/fs.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,yDAAyD;AACzD,8CAA8C;AAE9C,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,12 @@
1
+ interface LogContext {
2
+ scanId?: string;
3
+ engine?: string;
4
+ duration?: number;
5
+ [key: string]: unknown;
6
+ }
7
+ export declare function log(message: string, context?: LogContext): void;
8
+ export declare function warn(message: string, context?: LogContext): void;
9
+ export declare function error(message: string, context?: LogContext): void;
10
+ export declare function debug(message: string, context?: LogContext): void;
11
+ export {};
12
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAEA,UAAU,UAAU;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAgBD,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAE/D;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAEhE;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAEjE;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAIjE"}
@@ -0,0 +1,27 @@
1
+ function formatLog(level, message, context) {
2
+ const timestamp = new Date().toISOString();
3
+ const prefix = context?.scanId ? `[scan:${context.scanId}]` : "";
4
+ const enginePrefix = context?.engine ? `[${context.engine}]` : "";
5
+ const extra = context
6
+ ? Object.entries(context)
7
+ .filter(([key]) => key !== "scanId" && key !== "engine")
8
+ .map(([key, value]) => `${key}=${value}`)
9
+ .join(" ")
10
+ : "";
11
+ return `${timestamp} ${level.toUpperCase()} ${prefix}${enginePrefix} ${message}${extra ? " " + extra : ""}`;
12
+ }
13
+ export function log(message, context) {
14
+ console.log(formatLog("info", message, context));
15
+ }
16
+ export function warn(message, context) {
17
+ console.warn(formatLog("warn", message, context));
18
+ }
19
+ export function error(message, context) {
20
+ console.error(formatLog("error", message, context));
21
+ }
22
+ export function debug(message, context) {
23
+ if (process.env.DEBUG === "true") {
24
+ console.debug(formatLog("debug", message, context));
25
+ }
26
+ }
27
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AASA,SAAS,SAAS,CAAC,KAAe,EAAE,OAAe,EAAE,OAAoB;IACvE,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3C,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IACjE,MAAM,YAAY,GAAG,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAClE,MAAM,KAAK,GAAG,OAAO;QACnB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;aACpB,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,QAAQ,CAAC;aACvD,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,KAAK,EAAE,CAAC;aACxC,IAAI,CAAC,GAAG,CAAC;QACd,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO,GAAG,SAAS,IAAI,KAAK,CAAC,WAAW,EAAE,IAAI,MAAM,GAAG,YAAY,IAAI,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AAC9G,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,OAAe,EAAE,OAAoB;IACvD,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,OAAe,EAAE,OAAoB;IACxD,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,OAAe,EAAE,OAAoB;IACzD,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,OAAe,EAAE,OAAoB;IACzD,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;QACjC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,CAAC;AACH,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { FindingData } from "../engines/types.js";
2
+ /**
3
+ * Run all four post-processing steps on the aggregated findings.
4
+ *
5
+ * @param findings Deduplicated, context-classified findings from all engines.
6
+ * @param technologies Technology list from detectTechnologies().technologies.
7
+ * @returns Processed findings ready for score calculation.
8
+ */
9
+ export declare function postProcessFindings(findings: FindingData[], technologies: string[]): FindingData[];
10
+ //# sourceMappingURL=post-processor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"post-processor.d.ts","sourceRoot":"","sources":["../../src/utils/post-processor.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAyMvD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,WAAW,EAAE,EACvB,YAAY,EAAE,MAAM,EAAE,GACrB,WAAW,EAAE,CAMf"}
@@ -0,0 +1,183 @@
1
+ // Post-processing pipeline for scan findings.
2
+ // Runs after all engines complete but before score calculation.
3
+ // Steps (in order):
4
+ // 1. Framework-aware suppressions — mark findings that are known framework
5
+ // requirements so they don't penalize the score.
6
+ // 2. Cross-engine deduplication — merge findings for the same root cause
7
+ // that survived the primary dedup (different file_path, same vuln_id).
8
+ // 3. Contextual notes — add description_simple for specific patterns.
9
+ // 4. Classification — label each finding as "actionable" or "informational".
10
+ // Keys are lowercased technology names as returned by detectTechnologies().
11
+ const FRAMEWORK_RULES = {
12
+ nextjs: [
13
+ {
14
+ vulnerability_id: 63,
15
+ title_contains: "unsafe-inline",
16
+ note: "Next.js App Router requires 'unsafe-inline' in script-src for hydration. This cannot be removed without breaking the framework.",
17
+ },
18
+ {
19
+ vulnerability_id: 99,
20
+ title_contains: "robots.txt",
21
+ note: "robots.txt is standard web practice and expected on production sites.",
22
+ },
23
+ ],
24
+ supabase: [
25
+ {
26
+ vulnerability_id: 67,
27
+ cookie_name_starts: "sb-",
28
+ note: "Supabase Auth manages cookie security flags through its SDK configuration.",
29
+ },
30
+ {
31
+ vulnerability_id: 68,
32
+ cookie_name_starts: "sb-",
33
+ note: "Supabase Auth cookies require client-side JavaScript access for session refresh.",
34
+ },
35
+ ],
36
+ "next-intl": [
37
+ {
38
+ vulnerability_id: 67,
39
+ cookie_name: "NEXT_LOCALE",
40
+ note: "next-intl locale cookie requires client-side JavaScript access for locale synchronization.",
41
+ },
42
+ {
43
+ vulnerability_id: 68,
44
+ cookie_name: "NEXT_LOCALE",
45
+ note: "next-intl locale cookie requires client-side JavaScript access for locale synchronization.",
46
+ },
47
+ ],
48
+ };
49
+ function matchesFrameworkRule(finding, rule) {
50
+ if (finding.vulnerability_id !== rule.vulnerability_id)
51
+ return false;
52
+ if (rule.title_contains !== undefined) {
53
+ if (!finding.title.toLowerCase().includes(rule.title_contains.toLowerCase())) {
54
+ return false;
55
+ }
56
+ }
57
+ if (rule.cookie_name_starts !== undefined) {
58
+ const snippet = finding.code_snippet ?? finding.title ?? "";
59
+ if (!snippet.includes(rule.cookie_name_starts))
60
+ return false;
61
+ }
62
+ if (rule.cookie_name !== undefined) {
63
+ const snippet = finding.code_snippet ?? finding.title ?? "";
64
+ if (!snippet.includes(rule.cookie_name))
65
+ return false;
66
+ }
67
+ return true;
68
+ }
69
+ function applyFrameworkSuppressions(findings, technologies) {
70
+ // Normalize tech names to lowercase for lookup.
71
+ const normalizedTechs = technologies.map((t) => t.toLowerCase());
72
+ // Collect applicable rules based on detected technologies.
73
+ const applicableRules = [];
74
+ for (const tech of normalizedTechs) {
75
+ const rules = FRAMEWORK_RULES[tech];
76
+ if (rules) {
77
+ applicableRules.push(...rules);
78
+ }
79
+ }
80
+ if (applicableRules.length === 0)
81
+ return findings;
82
+ return findings.map((finding) => {
83
+ for (const rule of applicableRules) {
84
+ if (matchesFrameworkRule(finding, rule)) {
85
+ return {
86
+ ...finding,
87
+ is_framework_requirement: true,
88
+ framework_note: rule.note,
89
+ };
90
+ }
91
+ }
92
+ return finding;
93
+ });
94
+ }
95
+ // ---------------------------------------------------------------------------
96
+ // Step 2: Cross-Engine Deduplication
97
+ // ---------------------------------------------------------------------------
98
+ // The primary dedup in orchestrator.ts groups by vulnerability_id + file_path +
99
+ // line_number. That misses cases where two engines report the same root cause
100
+ // at different levels — e.g. the config engine points to next.config.ts while
101
+ // the URL scanner has no file_path at all. Group by vulnerability_id alone when
102
+ // one side lacks a file_path and keep whichever finding HAS a file_path.
103
+ function crossEngineDedup(findings) {
104
+ // Separate findings that have a file_path from those that don't.
105
+ const withPath = [];
106
+ const withoutPath = [];
107
+ for (const f of findings) {
108
+ if (f.file_path) {
109
+ withPath.push(f);
110
+ }
111
+ else {
112
+ withoutPath.push(f);
113
+ }
114
+ }
115
+ // For each no-path finding, check if there is already a with-path finding
116
+ // for the same vulnerability_id. If yes, skip the no-path finding (the
117
+ // with-path one is more actionable). Otherwise keep it.
118
+ const withPathVulnIds = new Set(withPath.map((f) => f.vulnerability_id));
119
+ const filteredWithoutPath = withoutPath.filter((f) => !withPathVulnIds.has(f.vulnerability_id));
120
+ return [...withPath, ...filteredWithoutPath];
121
+ }
122
+ // ---------------------------------------------------------------------------
123
+ // Step 3: Contextual Notes
124
+ // ---------------------------------------------------------------------------
125
+ function addContextualNotes(findings) {
126
+ return findings.map((finding) => {
127
+ // Don't overwrite an existing description_simple.
128
+ if (finding.description_simple)
129
+ return finding;
130
+ if (finding.is_framework_requirement && finding.framework_note) {
131
+ return { ...finding, description_simple: finding.framework_note };
132
+ }
133
+ if (finding.category === "headers" && finding.severity === "info") {
134
+ return {
135
+ ...finding,
136
+ description_simple: "This is informational — no action required.",
137
+ };
138
+ }
139
+ if (finding.severity === "low" && finding.confidence === "low") {
140
+ return {
141
+ ...finding,
142
+ description_simple: "This is a low-confidence observation. Verify manually before acting.",
143
+ };
144
+ }
145
+ return finding;
146
+ });
147
+ }
148
+ // ---------------------------------------------------------------------------
149
+ // Step 4: Classification
150
+ // ---------------------------------------------------------------------------
151
+ function classifyFindings(findings) {
152
+ return findings.map((finding) => {
153
+ let classification;
154
+ if (finding.is_framework_requirement) {
155
+ classification = "informational";
156
+ }
157
+ else if (finding.severity === "info") {
158
+ classification = "informational";
159
+ }
160
+ else {
161
+ classification = "actionable";
162
+ }
163
+ return { ...finding, classification };
164
+ });
165
+ }
166
+ // ---------------------------------------------------------------------------
167
+ // Public API
168
+ // ---------------------------------------------------------------------------
169
+ /**
170
+ * Run all four post-processing steps on the aggregated findings.
171
+ *
172
+ * @param findings Deduplicated, context-classified findings from all engines.
173
+ * @param technologies Technology list from detectTechnologies().technologies.
174
+ * @returns Processed findings ready for score calculation.
175
+ */
176
+ export function postProcessFindings(findings, technologies) {
177
+ const step1 = applyFrameworkSuppressions(findings, technologies);
178
+ const step2 = crossEngineDedup(step1);
179
+ const step3 = addContextualNotes(step2);
180
+ const step4 = classifyFindings(step3);
181
+ return step4;
182
+ }
183
+ //# sourceMappingURL=post-processor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"post-processor.js","sourceRoot":"","sources":["../../src/utils/post-processor.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,gEAAgE;AAChE,oBAAoB;AACpB,6EAA6E;AAC7E,sDAAsD;AACtD,2EAA2E;AAC3E,4EAA4E;AAC5E,wEAAwE;AACxE,+EAA+E;AAmB/E,4EAA4E;AAC5E,MAAM,eAAe,GAAoC;IACvD,MAAM,EAAE;QACN;YACE,gBAAgB,EAAE,EAAE;YACpB,cAAc,EAAE,eAAe;YAC/B,IAAI,EAAE,iIAAiI;SACxI;QACD;YACE,gBAAgB,EAAE,EAAE;YACpB,cAAc,EAAE,YAAY;YAC5B,IAAI,EAAE,uEAAuE;SAC9E;KACF;IACD,QAAQ,EAAE;QACR;YACE,gBAAgB,EAAE,EAAE;YACpB,kBAAkB,EAAE,KAAK;YACzB,IAAI,EAAE,4EAA4E;SACnF;QACD;YACE,gBAAgB,EAAE,EAAE;YACpB,kBAAkB,EAAE,KAAK;YACzB,IAAI,EAAE,kFAAkF;SACzF;KACF;IACD,WAAW,EAAE;QACX;YACE,gBAAgB,EAAE,EAAE;YACpB,WAAW,EAAE,aAAa;YAC1B,IAAI,EAAE,4FAA4F;SACnG;QACD;YACE,gBAAgB,EAAE,EAAE;YACpB,WAAW,EAAE,aAAa;YAC1B,IAAI,EAAE,4FAA4F;SACnG;KACF;CACF,CAAC;AAEF,SAAS,oBAAoB,CAAC,OAAoB,EAAE,IAAmB;IACrE,IAAI,OAAO,CAAC,gBAAgB,KAAK,IAAI,CAAC,gBAAgB;QAAE,OAAO,KAAK,CAAC;IAErE,IAAI,IAAI,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;QACtC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;YAC7E,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,IAAI,IAAI,CAAC,kBAAkB,KAAK,SAAS,EAAE,CAAC;QAC1C,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;QAC5D,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,kBAAkB,CAAC;YAAE,OAAO,KAAK,CAAC;IAC/D,CAAC;IAED,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;QAC5D,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC;YAAE,OAAO,KAAK,CAAC;IACxD,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,0BAA0B,CACjC,QAAuB,EACvB,YAAsB;IAEtB,gDAAgD;IAChD,MAAM,eAAe,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IAEjE,2DAA2D;IAC3D,MAAM,eAAe,GAAoB,EAAE,CAAC;IAC5C,KAAK,MAAM,IAAI,IAAI,eAAe,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,KAAK,EAAE,CAAC;YACV,eAAe,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IAED,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IAElD,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QAC9B,KAAK,MAAM,IAAI,IAAI,eAAe,EAAE,CAAC;YACnC,IAAI,oBAAoB,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;gBACxC,OAAO;oBACL,GAAG,OAAO;oBACV,wBAAwB,EAAE,IAAI;oBAC9B,cAAc,EAAE,IAAI,CAAC,IAAI;iBAC1B,CAAC;YACJ,CAAC;QACH,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,8EAA8E;AAC9E,qCAAqC;AACrC,8EAA8E;AAC9E,gFAAgF;AAChF,8EAA8E;AAC9E,8EAA8E;AAC9E,gFAAgF;AAChF,yEAAyE;AAEzE,SAAS,gBAAgB,CAAC,QAAuB;IAC/C,iEAAiE;IACjE,MAAM,QAAQ,GAAkB,EAAE,CAAC;IACnC,MAAM,WAAW,GAAkB,EAAE,CAAC;IAEtC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC;YAChB,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACnB,CAAC;aAAM,CAAC;YACN,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED,0EAA0E;IAC1E,uEAAuE;IACvE,wDAAwD;IACxD,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAEzE,MAAM,mBAAmB,GAAG,WAAW,CAAC,MAAM,CAC5C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAChD,CAAC;IAEF,OAAO,CAAC,GAAG,QAAQ,EAAE,GAAG,mBAAmB,CAAC,CAAC;AAC/C,CAAC;AAED,8EAA8E;AAC9E,2BAA2B;AAC3B,8EAA8E;AAE9E,SAAS,kBAAkB,CAAC,QAAuB;IACjD,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QAC9B,kDAAkD;QAClD,IAAI,OAAO,CAAC,kBAAkB;YAAE,OAAO,OAAO,CAAC;QAE/C,IAAI,OAAO,CAAC,wBAAwB,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;YAC/D,OAAO,EAAE,GAAG,OAAO,EAAE,kBAAkB,EAAE,OAAO,CAAC,cAAc,EAAE,CAAC;QACpE,CAAC;QAED,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,IAAI,OAAO,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;YAClE,OAAO;gBACL,GAAG,OAAO;gBACV,kBAAkB,EAAE,6CAA6C;aAClE,CAAC;QACJ,CAAC;QAED,IAAI,OAAO,CAAC,QAAQ,KAAK,KAAK,IAAI,OAAO,CAAC,UAAU,KAAK,KAAK,EAAE,CAAC;YAC/D,OAAO;gBACL,GAAG,OAAO;gBACV,kBAAkB,EAChB,sEAAsE;aACzE,CAAC;QACJ,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,8EAA8E;AAC9E,yBAAyB;AACzB,8EAA8E;AAE9E,SAAS,gBAAgB,CAAC,QAAuB;IAC/C,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QAC9B,IAAI,cAA8C,CAAC;QAEnD,IAAI,OAAO,CAAC,wBAAwB,EAAE,CAAC;YACrC,cAAc,GAAG,eAAe,CAAC;QACnC,CAAC;aAAM,IAAI,OAAO,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;YACvC,cAAc,GAAG,eAAe,CAAC;QACnC,CAAC;aAAM,CAAC;YACN,cAAc,GAAG,YAAY,CAAC;QAChC,CAAC;QAED,OAAO,EAAE,GAAG,OAAO,EAAE,cAAc,EAAE,CAAC;IACxC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CACjC,QAAuB,EACvB,YAAsB;IAEtB,MAAM,KAAK,GAAG,0BAA0B,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IACjE,MAAM,KAAK,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACtC,MAAM,KAAK,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACtC,OAAO,KAAK,CAAC;AACf,CAAC"}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@datahogo/core",
3
+ "version": "0.1.0",
4
+ "description": "Data Hogo scan engine — 300+ security checks for JS/TS, Python, Go, Java, PHP, C#, mobile and Supabase. Runs locally.",
5
+ "license": "AGPL-3.0-only",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/datahogo/datahogo.git",
9
+ "directory": "packages/core"
10
+ },
11
+ "homepage": "https://datahogo.com",
12
+ "type": "module",
13
+ "engines": {
14
+ "node": ">=18"
15
+ },
16
+ "main": "dist/index.js",
17
+ "types": "dist/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "default": "./dist/index.js"
22
+ }
23
+ },
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "scripts": {
28
+ "build": "tsc && cp -R src/rules dist/rules",
29
+ "test": "vitest run"
30
+ },
31
+ "keywords": [
32
+ "security",
33
+ "scanner",
34
+ "sast",
35
+ "static-analysis",
36
+ "vulnerability",
37
+ "secrets-detection"
38
+ ],
39
+ "devDependencies": {
40
+ "@types/node": "^20.14.0",
41
+ "typescript": "^5.4.0",
42
+ "vitest": "^1.6.0"
43
+ }
44
+ }