bilt-toolkit 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 (116) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +168 -0
  3. package/bin/bilt.js +2 -0
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +161 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/commands/doctor.d.ts +13 -0
  9. package/dist/commands/doctor.d.ts.map +1 -0
  10. package/dist/commands/doctor.js +194 -0
  11. package/dist/commands/doctor.js.map +1 -0
  12. package/dist/commands/fix.d.ts +12 -0
  13. package/dist/commands/fix.d.ts.map +1 -0
  14. package/dist/commands/fix.js +273 -0
  15. package/dist/commands/fix.js.map +1 -0
  16. package/dist/commands/init.d.ts +11 -0
  17. package/dist/commands/init.d.ts.map +1 -0
  18. package/dist/commands/init.js +101 -0
  19. package/dist/commands/init.js.map +1 -0
  20. package/dist/commands/scan.d.ts +17 -0
  21. package/dist/commands/scan.d.ts.map +1 -0
  22. package/dist/commands/scan.js +267 -0
  23. package/dist/commands/scan.js.map +1 -0
  24. package/dist/commands/undo.d.ts +12 -0
  25. package/dist/commands/undo.d.ts.map +1 -0
  26. package/dist/commands/undo.js +90 -0
  27. package/dist/commands/undo.js.map +1 -0
  28. package/dist/commands/watch.d.ts +10 -0
  29. package/dist/commands/watch.d.ts.map +1 -0
  30. package/dist/commands/watch.js +68 -0
  31. package/dist/commands/watch.js.map +1 -0
  32. package/dist/config/config.d.ts +8 -0
  33. package/dist/config/config.d.ts.map +1 -0
  34. package/dist/config/config.js +75 -0
  35. package/dist/config/config.js.map +1 -0
  36. package/dist/core/fix/confirm.d.ts +21 -0
  37. package/dist/core/fix/confirm.d.ts.map +1 -0
  38. package/dist/core/fix/confirm.js +60 -0
  39. package/dist/core/fix/confirm.js.map +1 -0
  40. package/dist/core/fix/env-fix.d.ts +40 -0
  41. package/dist/core/fix/env-fix.d.ts.map +1 -0
  42. package/dist/core/fix/env-fix.js +186 -0
  43. package/dist/core/fix/env-fix.js.map +1 -0
  44. package/dist/core/fix/snapshot.d.ts +30 -0
  45. package/dist/core/fix/snapshot.d.ts.map +1 -0
  46. package/dist/core/fix/snapshot.js +186 -0
  47. package/dist/core/fix/snapshot.js.map +1 -0
  48. package/dist/core/rules/entropy.d.ts +29 -0
  49. package/dist/core/rules/entropy.d.ts.map +1 -0
  50. package/dist/core/rules/entropy.js +117 -0
  51. package/dist/core/rules/entropy.js.map +1 -0
  52. package/dist/core/rules/providers.d.ts +12 -0
  53. package/dist/core/rules/providers.d.ts.map +1 -0
  54. package/dist/core/rules/providers.js +184 -0
  55. package/dist/core/rules/providers.js.map +1 -0
  56. package/dist/core/rules/secret-rules.d.ts +3 -0
  57. package/dist/core/rules/secret-rules.d.ts.map +1 -0
  58. package/dist/core/rules/secret-rules.js +205 -0
  59. package/dist/core/rules/secret-rules.js.map +1 -0
  60. package/dist/core/scan/env.d.ts +47 -0
  61. package/dist/core/scan/env.d.ts.map +1 -0
  62. package/dist/core/scan/env.js +301 -0
  63. package/dist/core/scan/env.js.map +1 -0
  64. package/dist/core/scan/framework.d.ts +26 -0
  65. package/dist/core/scan/framework.d.ts.map +1 -0
  66. package/dist/core/scan/framework.js +254 -0
  67. package/dist/core/scan/framework.js.map +1 -0
  68. package/dist/core/scan/gitignore.d.ts +20 -0
  69. package/dist/core/scan/gitignore.d.ts.map +1 -0
  70. package/dist/core/scan/gitignore.js +217 -0
  71. package/dist/core/scan/gitignore.js.map +1 -0
  72. package/dist/core/scan/secrets.d.ts +27 -0
  73. package/dist/core/scan/secrets.d.ts.map +1 -0
  74. package/dist/core/scan/secrets.js +247 -0
  75. package/dist/core/scan/secrets.js.map +1 -0
  76. package/dist/core/score/health.d.ts +29 -0
  77. package/dist/core/score/health.d.ts.map +1 -0
  78. package/dist/core/score/health.js +73 -0
  79. package/dist/core/score/health.js.map +1 -0
  80. package/dist/core/watch/watcher.d.ts +29 -0
  81. package/dist/core/watch/watcher.d.ts.map +1 -0
  82. package/dist/core/watch/watcher.js +176 -0
  83. package/dist/core/watch/watcher.js.map +1 -0
  84. package/dist/index.d.ts +2 -0
  85. package/dist/index.d.ts.map +1 -0
  86. package/dist/index.js +2 -0
  87. package/dist/index.js.map +1 -0
  88. package/dist/plugins/interface.d.ts +12 -0
  89. package/dist/plugins/interface.d.ts.map +1 -0
  90. package/dist/plugins/interface.js +91 -0
  91. package/dist/plugins/interface.js.map +1 -0
  92. package/dist/plugins/loader.d.ts +10 -0
  93. package/dist/plugins/loader.d.ts.map +1 -0
  94. package/dist/plugins/loader.js +104 -0
  95. package/dist/plugins/loader.js.map +1 -0
  96. package/dist/plugins/official/docker.d.ts +4 -0
  97. package/dist/plugins/official/docker.d.ts.map +1 -0
  98. package/dist/plugins/official/docker.js +111 -0
  99. package/dist/plugins/official/docker.js.map +1 -0
  100. package/dist/plugins/official/terraform.d.ts +4 -0
  101. package/dist/plugins/official/terraform.d.ts.map +1 -0
  102. package/dist/plugins/official/terraform.js +131 -0
  103. package/dist/plugins/official/terraform.js.map +1 -0
  104. package/dist/types/index.d.ts +158 -0
  105. package/dist/types/index.d.ts.map +1 -0
  106. package/dist/types/index.js +3 -0
  107. package/dist/types/index.js.map +1 -0
  108. package/dist/ui/format.d.ts +43 -0
  109. package/dist/ui/format.d.ts.map +1 -0
  110. package/dist/ui/format.js +207 -0
  111. package/dist/ui/format.js.map +1 -0
  112. package/dist/ui/reporter.d.ts +39 -0
  113. package/dist/ui/reporter.d.ts.map +1 -0
  114. package/dist/ui/reporter.js +254 -0
  115. package/dist/ui/reporter.js.map +1 -0
  116. package/package.json +65 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../../../src/core/watch/watcher.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,UAAU,EAAe,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAIhF,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAyFD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAC1B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GACnC,aAAa,CAoFf;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvE"}
@@ -0,0 +1,176 @@
1
+ // ─── File Watcher ────────────────────────────────────────────────────────────
2
+ //
3
+ // Watches the project directory for file changes and runs incremental
4
+ // secret + env scans on modified files. Uses chokidar for cross-platform
5
+ // file watching with 300ms debouncing.
6
+ // ─────────────────────────────────────────────────────────────────────────────
7
+ import chokidar from "chokidar";
8
+ import fs from "node:fs/promises";
9
+ import { scanFileForSecrets } from "../scan/secrets.js";
10
+ import { SECRET_RULES } from "../rules/secret-rules.js";
11
+ // ─── Constants ───────────────────────────────────────────────────────────────
12
+ /** Default debounce interval in milliseconds. */
13
+ const DEFAULT_DEBOUNCE_MS = 300;
14
+ /** Directories and file patterns to always ignore. */
15
+ const IGNORED_PATTERNS = [
16
+ "**/node_modules/**",
17
+ "**/.git/**",
18
+ "**/.bilt/**",
19
+ "**/dist/**",
20
+ "**/build/**",
21
+ "**/.next/**",
22
+ "**/.nuxt/**",
23
+ "**/.svelte-kit/**",
24
+ "**/__pycache__/**",
25
+ ];
26
+ /** Binary file extensions to skip. */
27
+ const BINARY_EXTENSIONS = new Set([
28
+ ".png",
29
+ ".jpg",
30
+ ".jpeg",
31
+ ".gif",
32
+ ".bmp",
33
+ ".ico",
34
+ ".webp",
35
+ ".svg",
36
+ ".mp3",
37
+ ".mp4",
38
+ ".avi",
39
+ ".mov",
40
+ ".webm",
41
+ ".zip",
42
+ ".tar",
43
+ ".gz",
44
+ ".rar",
45
+ ".7z",
46
+ ".exe",
47
+ ".dll",
48
+ ".so",
49
+ ".dylib",
50
+ ".woff",
51
+ ".woff2",
52
+ ".ttf",
53
+ ".eot",
54
+ ".otf",
55
+ ".pdf",
56
+ ".doc",
57
+ ".docx",
58
+ ".xls",
59
+ ".xlsx",
60
+ ".lock",
61
+ ".map",
62
+ ]);
63
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
64
+ /**
65
+ * Check if a file path has a binary extension.
66
+ */
67
+ function isBinaryFile(filePath) {
68
+ const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
69
+ return BINARY_EXTENSIONS.has(ext);
70
+ }
71
+ /**
72
+ * Create a debounced version of a function that coalesces rapid calls
73
+ * into a single invocation after a quiet period.
74
+ */
75
+ function debounce(fn, delayMs) {
76
+ let timer = null;
77
+ return (...args) => {
78
+ if (timer !== null)
79
+ clearTimeout(timer);
80
+ timer = setTimeout(() => {
81
+ timer = null;
82
+ fn(...args);
83
+ }, delayMs);
84
+ };
85
+ }
86
+ // ─── Public API ──────────────────────────────────────────────────────────────
87
+ /**
88
+ * Start watching a project directory for file changes.
89
+ *
90
+ * On each change:
91
+ * 1. Read the modified file.
92
+ * 2. Run secret-detection rules against its content.
93
+ * 3. Invoke the `onEvent` callback with the resulting findings.
94
+ *
95
+ * The watcher ignores `node_modules`, `.git`, `.bilt`, `dist`, `build`,
96
+ * and binary files. Changes are debounced (default 300ms) to avoid
97
+ * flooding the callback during bulk operations like `npm install`.
98
+ *
99
+ * @param projectDir Root directory to watch.
100
+ * @param config Bilt configuration (used for entropy threshold & ignore patterns).
101
+ * @param onEvent Callback invoked with watch events.
102
+ * @returns A handle with a `close()` method to stop watching.
103
+ */
104
+ export function startWatcher(projectDir, config, onEvent) {
105
+ // Merge user-configured ignore patterns with defaults
106
+ const ignorePatterns = [...IGNORED_PATTERNS, ...config.ignore];
107
+ // Combine built-in rules with custom rules
108
+ const allRules = [...SECRET_RULES, ...config.customRules];
109
+ const watcher = chokidar.watch(projectDir, {
110
+ ignored: ignorePatterns,
111
+ persistent: true,
112
+ ignoreInitial: true,
113
+ awaitWriteFinish: {
114
+ stabilityThreshold: 200,
115
+ pollInterval: 100,
116
+ },
117
+ });
118
+ /**
119
+ * Process a single file event.
120
+ */
121
+ const processFile = async (eventType, filePath) => {
122
+ // Skip binary files
123
+ if (isBinaryFile(filePath))
124
+ return;
125
+ let content;
126
+ try {
127
+ content = await fs.readFile(filePath, "utf-8");
128
+ }
129
+ catch {
130
+ // File may have been deleted between the event and the read
131
+ return;
132
+ }
133
+ const findings = scanFileForSecrets(content, filePath, allRules, config.entropyThreshold);
134
+ const event = {
135
+ type: eventType,
136
+ file: filePath,
137
+ findings,
138
+ timestamp: new Date(),
139
+ };
140
+ onEvent(event);
141
+ };
142
+ // Create debounced handler
143
+ const debouncedProcess = debounce((eventType, filePath) => {
144
+ void processFile(eventType, filePath);
145
+ }, DEFAULT_DEBOUNCE_MS);
146
+ // Attach event handlers
147
+ watcher.on("add", (filePath) => {
148
+ debouncedProcess("add", filePath);
149
+ });
150
+ watcher.on("change", (filePath) => {
151
+ debouncedProcess("change", filePath);
152
+ });
153
+ watcher.on("unlink", (filePath) => {
154
+ const event = {
155
+ type: "unlink",
156
+ file: filePath,
157
+ findings: [],
158
+ timestamp: new Date(),
159
+ };
160
+ onEvent(event);
161
+ });
162
+ return {
163
+ close: async () => {
164
+ await watcher.close();
165
+ },
166
+ };
167
+ }
168
+ /**
169
+ * Stop a running watcher.
170
+ *
171
+ * @param watcher The watcher handle returned by `startWatcher`.
172
+ */
173
+ export async function stopWatcher(watcher) {
174
+ await watcher.close();
175
+ }
176
+ //# sourceMappingURL=watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../../src/core/watch/watcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,12 @@
1
+ import type { PluginManifest, PluginContext, PluginResult, BiltConfig } from "../types/index.js";
2
+ export type { PluginManifest, PluginContext, PluginResult };
3
+ /**
4
+ * Runtime type-guard: validates that an unknown value conforms to PluginManifest.
5
+ */
6
+ export declare function validatePlugin(plugin: unknown): plugin is PluginManifest;
7
+ /**
8
+ * Build a PluginContext from the project root and config.
9
+ * Collects file list, parses .env files, and gathers git info.
10
+ */
11
+ export declare function createPluginContext(rootDir: string, config: BiltConfig): Promise<PluginContext>;
12
+ //# sourceMappingURL=interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/plugins/interface.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,YAAY,EACZ,UAAU,EACX,MAAM,mBAAmB,CAAC;AAG3B,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,EAAE,CAAC;AAI5D;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,cAAc,CAexE;AAID;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,aAAa,CAAC,CA2DxB"}
@@ -0,0 +1,91 @@
1
+ // ─── Plugin Interface ────────────────────────────────────────────────────────
2
+ // Re-exports plugin types and provides runtime validation + context creation.
3
+ import { promises as fs } from "node:fs";
4
+ import path from "node:path";
5
+ import fg from "fast-glob";
6
+ import { simpleGit } from "simple-git";
7
+ // ─── Runtime Validation ──────────────────────────────────────────────────────
8
+ /**
9
+ * Runtime type-guard: validates that an unknown value conforms to PluginManifest.
10
+ */
11
+ export function validatePlugin(plugin) {
12
+ if (plugin === null || typeof plugin !== "object")
13
+ return false;
14
+ const p = plugin;
15
+ if (typeof p["name"] !== "string" || p["name"].length === 0)
16
+ return false;
17
+ if (typeof p["version"] !== "string" || p["version"].length === 0)
18
+ return false;
19
+ if (typeof p["description"] !== "string")
20
+ return false;
21
+ if (typeof p["check"] !== "function")
22
+ return false;
23
+ // fix is optional but must be a function if present
24
+ if (p["fix"] !== undefined && typeof p["fix"] !== "function")
25
+ return false;
26
+ return true;
27
+ }
28
+ // ─── Context Builder ─────────────────────────────────────────────────────────
29
+ /**
30
+ * Build a PluginContext from the project root and config.
31
+ * Collects file list, parses .env files, and gathers git info.
32
+ */
33
+ export async function createPluginContext(rootDir, config) {
34
+ // Gather all project files (respecting ignore patterns)
35
+ const files = await fg("**/*", {
36
+ cwd: rootDir,
37
+ dot: true,
38
+ ignore: config.ignore,
39
+ onlyFiles: true,
40
+ });
41
+ // Parse env vars from .env files
42
+ const envVars = new Map();
43
+ const envFiles = files.filter((f) => path.basename(f).startsWith(".env") && !f.includes("node_modules"));
44
+ for (const envFile of envFiles) {
45
+ const fullPath = path.join(rootDir, envFile);
46
+ try {
47
+ const content = await fs.readFile(fullPath, "utf-8");
48
+ const vars = new Map();
49
+ for (const line of content.split("\n")) {
50
+ const trimmed = line.trim();
51
+ if (trimmed === "" || trimmed.startsWith("#"))
52
+ continue;
53
+ const eqIdx = trimmed.indexOf("=");
54
+ if (eqIdx === -1)
55
+ continue;
56
+ const key = trimmed.slice(0, eqIdx).trim();
57
+ const value = trimmed
58
+ .slice(eqIdx + 1)
59
+ .trim()
60
+ .replace(/^["']|["']$/g, "");
61
+ vars.set(key, value);
62
+ }
63
+ envVars.set(envFile, vars);
64
+ }
65
+ catch {
66
+ // Skip unreadable files
67
+ }
68
+ }
69
+ // Git info
70
+ let isRepo = false;
71
+ let branch;
72
+ try {
73
+ const git = simpleGit(rootDir);
74
+ isRepo = await git.checkIsRepo();
75
+ if (isRepo) {
76
+ const status = await git.status();
77
+ branch = status.current ?? undefined;
78
+ }
79
+ }
80
+ catch {
81
+ isRepo = false;
82
+ }
83
+ return {
84
+ rootDir,
85
+ files,
86
+ envVars,
87
+ git: { isRepo, branch },
88
+ config,
89
+ };
90
+ }
91
+ //# sourceMappingURL=interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interface.js","sourceRoot":"","sources":["../../src/plugins/interface.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { BiltConfig, PluginManifest } from "../types/index.js";
2
+ /**
3
+ * Load all plugins from:
4
+ * 1. `bilt-plugin-*` packages in node_modules
5
+ * 2. Explicit paths in config.plugins
6
+ *
7
+ * Each module is dynamically imported and validated at runtime.
8
+ */
9
+ export declare function loadPlugins(config: BiltConfig, rootDir: string): Promise<PluginManifest[]>;
10
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/plugins/loader.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAKpE;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,cAAc,EAAE,CAAC,CA0D3B"}
@@ -0,0 +1,104 @@
1
+ // ─── Plugin Loader ───────────────────────────────────────────────────────────
2
+ // Discovers, loads, and validates plugins from node_modules and config paths.
3
+ import { createRequire } from "node:module";
4
+ import path from "node:path";
5
+ import { promises as fs } from "node:fs";
6
+ import { validatePlugin } from "./interface.js";
7
+ const require = createRequire(import.meta.url);
8
+ /**
9
+ * Load all plugins from:
10
+ * 1. `bilt-plugin-*` packages in node_modules
11
+ * 2. Explicit paths in config.plugins
12
+ *
13
+ * Each module is dynamically imported and validated at runtime.
14
+ */
15
+ export async function loadPlugins(config, rootDir) {
16
+ const plugins = [];
17
+ const seen = new Set();
18
+ // ── 1. Discover bilt-plugin-* in node_modules ────────────────────────
19
+ const nodeModulesDir = path.join(rootDir, "node_modules");
20
+ try {
21
+ const entries = await fs.readdir(nodeModulesDir, { withFileTypes: true });
22
+ for (const entry of entries) {
23
+ if (entry.isDirectory() && entry.name.startsWith("bilt-plugin-")) {
24
+ const pluginPath = path.join(nodeModulesDir, entry.name);
25
+ const loaded = await tryLoadPlugin(pluginPath);
26
+ if (loaded && !seen.has(loaded.name)) {
27
+ seen.add(loaded.name);
28
+ plugins.push(loaded);
29
+ }
30
+ }
31
+ // Also check @bilt/ scoped packages
32
+ if (entry.isDirectory() && entry.name === "@bilt") {
33
+ const scopedDir = path.join(nodeModulesDir, "@bilt");
34
+ try {
35
+ const scopedEntries = await fs.readdir(scopedDir, {
36
+ withFileTypes: true,
37
+ });
38
+ for (const se of scopedEntries) {
39
+ if (se.isDirectory() && se.name.startsWith("plugin-")) {
40
+ const pluginPath = path.join(scopedDir, se.name);
41
+ const loaded = await tryLoadPlugin(pluginPath);
42
+ if (loaded && !seen.has(loaded.name)) {
43
+ seen.add(loaded.name);
44
+ plugins.push(loaded);
45
+ }
46
+ }
47
+ }
48
+ }
49
+ catch {
50
+ // Scoped directory not readable
51
+ }
52
+ }
53
+ }
54
+ }
55
+ catch {
56
+ // node_modules doesn't exist — fine
57
+ }
58
+ // ── 2. Load explicit plugin paths from config ────────────────────────
59
+ for (const pluginRef of config.plugins) {
60
+ const resolved = path.isAbsolute(pluginRef)
61
+ ? pluginRef
62
+ : path.resolve(rootDir, pluginRef);
63
+ const loaded = await tryLoadPlugin(resolved);
64
+ if (loaded && !seen.has(loaded.name)) {
65
+ seen.add(loaded.name);
66
+ plugins.push(loaded);
67
+ }
68
+ }
69
+ return plugins;
70
+ }
71
+ /**
72
+ * Attempt to dynamically import a plugin from a given path, validate, return.
73
+ * Returns null if loading fails or validation fails.
74
+ */
75
+ async function tryLoadPlugin(pluginPath) {
76
+ try {
77
+ // Try dynamic import (ESM)
78
+ let mod;
79
+ try {
80
+ mod = await import(pluginPath);
81
+ }
82
+ catch {
83
+ // Fallback to require (CJS)
84
+ try {
85
+ mod = require(pluginPath);
86
+ }
87
+ catch {
88
+ return null;
89
+ }
90
+ }
91
+ // Unwrap default export if present
92
+ const exported = mod !== null && typeof mod === "object" && "default" in mod
93
+ ? mod["default"]
94
+ : mod;
95
+ if (validatePlugin(exported)) {
96
+ return exported;
97
+ }
98
+ return null;
99
+ }
100
+ catch {
101
+ return null;
102
+ }
103
+ }
104
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/plugins/loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { PluginManifest } from "../../types/index.js";
2
+ declare const plugin: PluginManifest;
3
+ export default plugin;
4
+ //# sourceMappingURL=docker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker.d.ts","sourceRoot":"","sources":["../../../src/plugins/official/docker.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,cAAc,EAIf,MAAM,sBAAsB,CAAC;AA0B9B,QAAA,MAAM,MAAM,EAAE,cAwGb,CAAC;eAEa,MAAM"}
@@ -0,0 +1,111 @@
1
+ // ─── Docker Plugin ───────────────────────────────────────────────────────────
2
+ // Scans Dockerfiles for hardcoded secrets and validates .dockerignore.
3
+ import { promises as fs } from "node:fs";
4
+ import path from "node:path";
5
+ // Patterns that indicate hardcoded secrets in Dockerfile ENV directives
6
+ const SECRET_PATTERNS = [
7
+ {
8
+ name: "API key",
9
+ pattern: /ENV\s+\w*(API_KEY|APIKEY|API_SECRET)\w*\s*=\s*["']?(?!(\$\{|<|your_|changeme|placeholder|xxx))\S+/i,
10
+ },
11
+ {
12
+ name: "password",
13
+ pattern: /ENV\s+\w*(PASSWORD|PASSWD|DB_PASS)\w*\s*=\s*["']?(?!(\$\{|<|your_|changeme|placeholder|xxx))\S+/i,
14
+ },
15
+ {
16
+ name: "token",
17
+ pattern: /ENV\s+\w*(TOKEN|SECRET_KEY|PRIVATE_KEY|ACCESS_KEY)\w*\s*=\s*["']?(?!(\$\{|<|your_|changeme|placeholder|xxx))\S+/i,
18
+ },
19
+ {
20
+ name: "connection string",
21
+ pattern: /ENV\s+\w*(DATABASE_URL|REDIS_URL|MONGO_URI|CONNECTION_STRING)\w*\s*=\s*["']?(?!(\$\{|<|your_|changeme|placeholder|xxx))\S+/i,
22
+ },
23
+ ];
24
+ const plugin = {
25
+ name: "bilt-plugin-docker",
26
+ version: "1.0.0",
27
+ description: "Scans Dockerfiles for hardcoded secrets and validates .dockerignore configuration.",
28
+ async check(context) {
29
+ const findings = [];
30
+ // ── Scan Dockerfiles for ENV with hardcoded secrets ────────────────
31
+ const dockerfiles = context.files.filter((f) => path.basename(f) === "Dockerfile" ||
32
+ path.basename(f).startsWith("Dockerfile.") ||
33
+ f.endsWith(".dockerfile"));
34
+ for (const dockerFile of dockerfiles) {
35
+ const fullPath = path.join(context.rootDir, dockerFile);
36
+ let content;
37
+ try {
38
+ content = await fs.readFile(fullPath, "utf-8");
39
+ }
40
+ catch {
41
+ continue;
42
+ }
43
+ const lines = content.split("\n");
44
+ for (let i = 0; i < lines.length; i++) {
45
+ const line = lines[i];
46
+ const trimmed = line.trim();
47
+ // Skip comments and empty lines
48
+ if (trimmed.startsWith("#") || trimmed === "")
49
+ continue;
50
+ // Skip ARG directives that use build-args (safe pattern)
51
+ if (/^ARG\s/i.test(trimmed))
52
+ continue;
53
+ for (const { name, pattern } of SECRET_PATTERNS) {
54
+ if (pattern.test(trimmed)) {
55
+ findings.push({
56
+ id: `docker-env-secret-${dockerFile}-${i + 1}`,
57
+ severity: "critical",
58
+ category: "plugin-finding",
59
+ message: `Hardcoded ${name} found in Dockerfile ENV directive`,
60
+ file: dockerFile,
61
+ line: i + 1,
62
+ suggestion: "Use ARG with --build-arg or mount secrets at runtime instead of hardcoding in ENV.",
63
+ });
64
+ break; // One finding per line
65
+ }
66
+ }
67
+ }
68
+ }
69
+ // ── Check .dockerignore exists and excludes .env ──────────────────
70
+ if (dockerfiles.length > 0) {
71
+ const dockerignorePath = path.join(context.rootDir, ".dockerignore");
72
+ try {
73
+ const content = await fs.readFile(dockerignorePath, "utf-8");
74
+ const lines = content
75
+ .split("\n")
76
+ .map((l) => l.trim())
77
+ .filter((l) => l !== "" && !l.startsWith("#"));
78
+ const coversEnv = lines.some((l) => l === ".env" ||
79
+ l === ".env*" ||
80
+ l === ".env.*" ||
81
+ l === "*.env" ||
82
+ l === ".env.local" ||
83
+ l === ".env.production");
84
+ if (!coversEnv) {
85
+ findings.push({
86
+ id: "docker-dockerignore-env",
87
+ severity: "warning",
88
+ category: "plugin-finding",
89
+ message: ".dockerignore exists but does not exclude .env files — secrets may be copied into image",
90
+ file: ".dockerignore",
91
+ suggestion: "Add `.env*` to your .dockerignore to prevent copying secrets into the Docker image.",
92
+ });
93
+ }
94
+ }
95
+ catch {
96
+ // .dockerignore doesn't exist
97
+ findings.push({
98
+ id: "docker-no-dockerignore",
99
+ severity: "warning",
100
+ category: "plugin-finding",
101
+ message: "No .dockerignore found — .env files may be copied into Docker image during build",
102
+ file: dockerfiles[0],
103
+ suggestion: "Create a .dockerignore file and add `.env*` to prevent secrets from entering your image.",
104
+ });
105
+ }
106
+ }
107
+ return { findings };
108
+ },
109
+ };
110
+ export default plugin;
111
+ //# sourceMappingURL=docker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker.js","sourceRoot":"","sources":["../../../src/plugins/official/docker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { PluginManifest } from "../../types/index.js";
2
+ declare const plugin: PluginManifest;
3
+ export default plugin;
4
+ //# sourceMappingURL=terraform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terraform.d.ts","sourceRoot":"","sources":["../../../src/plugins/official/terraform.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,cAAc,EAIf,MAAM,sBAAsB,CAAC;AAwC9B,QAAA,MAAM,MAAM,EAAE,cA0Hb,CAAC;eAEa,MAAM"}