muaddib-scanner 2.2.19 → 2.2.22

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.
package/src/hooks-init.js CHANGED
@@ -1,258 +1,264 @@
1
- const { execFileSync } = require('child_process');
2
- const fs = require('fs');
3
- const path = require('path');
4
-
5
- // Read version from package.json for pre-commit config
6
- const PKG_VERSION = (() => {
7
- try {
8
- return 'v' + JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')).version;
9
- } catch {
10
- return 'v1.0.0';
11
- }
12
- })();
13
-
14
- /**
15
- * Detect which hook system is available
16
- */
17
- function detectHookSystem(targetPath) {
18
- const hasHusky = fs.existsSync(path.join(targetPath, '.husky'));
19
- const hasPreCommitConfig = fs.existsSync(path.join(targetPath, '.pre-commit-config.yaml'));
20
- const hasGitHooks = fs.existsSync(path.join(targetPath, '.git', 'hooks'));
21
-
22
- return {
23
- husky: hasHusky,
24
- preCommit: hasPreCommitConfig,
25
- gitHooks: hasGitHooks
26
- };
27
- }
28
-
29
- /**
30
- * Initialize hooks for a project
31
- */
32
- const VALID_MODES = ['scan', 'diff'];
33
- const HOOK_COMMANDS = {
34
- scan: 'npx muaddib scan . --fail-on high',
35
- diff: 'npx muaddib diff HEAD --fail-on high'
36
- };
37
-
38
- async function initHooks(targetPath, options = {}) {
39
- const resolvedPath = path.resolve(targetPath);
40
- const hookType = options.type || 'auto';
41
- const mode = VALID_MODES.includes(options.mode) ? options.mode : 'scan';
42
-
43
- console.log('\n[MUADDIB] Initializing git hooks...\n');
44
-
45
- const detected = detectHookSystem(resolvedPath);
46
-
47
- // Auto-detect or use specified type
48
- let selectedType = hookType;
49
- if (hookType === 'auto') {
50
- if (detected.husky) {
51
- selectedType = 'husky';
52
- } else if (detected.preCommit) {
53
- selectedType = 'pre-commit';
54
- } else {
55
- selectedType = 'git';
56
- }
57
- }
58
-
59
- console.log(`[MUADDIB] Hook system: ${selectedType}`);
60
- console.log(`[MUADDIB] Mode: ${mode === 'diff' ? 'diff (only new threats)' : 'scan (all threats)'}\n`);
61
-
62
- try {
63
- switch (selectedType) {
64
- case 'husky':
65
- await initHusky(resolvedPath, mode);
66
- break;
67
- case 'pre-commit':
68
- await initPreCommit(resolvedPath, mode);
69
- break;
70
- case 'git':
71
- default:
72
- await initGitHook(resolvedPath, mode);
73
- break;
74
- }
75
-
76
- console.log('\n[OK] Git hooks initialized successfully!');
77
- console.log('[INFO] MUAD\'DIB will now run before each commit.\n');
78
-
79
- if (mode === 'diff') {
80
- console.log('[INFO] Using diff mode: only NEW threats will block commits.');
81
- console.log('[INFO] Existing threats in the codebase will be ignored.\n');
82
- }
83
-
84
- return true;
85
- } catch (err) {
86
- console.error(`[ERROR] Failed to initialize hooks: ${err.message}`);
87
- return false;
88
- }
89
- }
90
-
91
- /**
92
- * Initialize husky hooks
93
- */
94
- async function initHusky(targetPath, mode) {
95
- const huskyDir = path.join(targetPath, '.husky');
96
-
97
- // Check if husky is installed
98
- if (!fs.existsSync(huskyDir)) {
99
- console.log('[INFO] Husky not detected. Installing...');
100
- try {
101
- execFileSync('npx', ['husky', 'install'], {
102
- cwd: targetPath,
103
- stdio: 'inherit',
104
- shell: false
105
- });
106
- } catch {
107
- throw new Error('Failed to install husky. Run: npm install -D husky && npx husky install');
108
- }
109
- }
110
-
111
- // Create pre-commit hook
112
- const preCommitPath = path.join(huskyDir, 'pre-commit');
113
- const command = HOOK_COMMANDS[mode];
114
-
115
- const hookContent = `#!/usr/bin/env sh
116
- . "$(dirname -- "$0")/_/husky.sh"
117
-
118
- echo "[MUADDIB] Running security check..."
119
- ${command}
120
- `;
121
-
122
- fs.writeFileSync(preCommitPath, hookContent, { mode: 0o755 });
123
- console.log(`[OK] Created ${preCommitPath}`);
124
- }
125
-
126
- /**
127
- * Initialize pre-commit framework hooks
128
- */
129
- async function initPreCommit(targetPath, mode) {
130
- const configPath = path.join(targetPath, '.pre-commit-config.yaml');
131
-
132
- // Read existing config
133
- let config = '';
134
- if (fs.existsSync(configPath)) {
135
- config = fs.readFileSync(configPath, 'utf8');
136
- }
137
-
138
- // Check if MUAD'DIB is already configured
139
- if (config.includes('muaddib-scanner') || config.includes('muad-dib')) {
140
- console.log('[INFO] MUAD\'DIB already configured in .pre-commit-config.yaml');
141
- return;
142
- }
143
-
144
- // Add MUAD'DIB hook
145
- const hookId = mode === 'diff' ? 'muaddib-diff' : 'muaddib-scan';
146
- const muaddibConfig = `
147
- - repo: https://github.com/DNSZLSK/muad-dib
148
- rev: ${PKG_VERSION}
149
- hooks:
150
- - id: ${hookId}
151
- `;
152
-
153
- if (config.includes('repos:')) {
154
- // Append to existing repos
155
- config = config.replace(/repos:\s*\n/, `repos:\n${muaddibConfig}`);
156
- } else {
157
- // Create new config
158
- config = `repos:${muaddibConfig}`;
159
- }
160
-
161
- fs.writeFileSync(configPath, config);
162
- console.log(`[OK] Updated ${configPath}`);
163
- console.log('[INFO] Run: pre-commit install');
164
- }
165
-
166
- /**
167
- * Initialize native git hooks
168
- */
169
- async function initGitHook(targetPath, mode) {
170
- const gitHooksDir = path.join(targetPath, '.git', 'hooks');
171
-
172
- if (!fs.existsSync(gitHooksDir)) {
173
- throw new Error('Not a git repository. Run: git init');
174
- }
175
-
176
- const preCommitPath = path.join(gitHooksDir, 'pre-commit');
177
- const command = HOOK_COMMANDS[mode];
178
-
179
- const hookContent = `#!/bin/sh
180
- # MUAD'DIB pre-commit hook
181
- # Generated by: muaddib init-hooks
182
-
183
- echo "[MUADDIB] Running security check..."
184
-
185
- ${command}
186
-
187
- EXIT_CODE=$?
188
-
189
- if [ $EXIT_CODE -ne 0 ]; then
190
- echo ""
191
- echo "[MUADDIB] Commit blocked: security threats detected!"
192
- echo "[MUADDIB] Fix the issues or use --no-verify to bypass."
193
- exit 1
194
- fi
195
-
196
- exit 0
197
- `;
198
-
199
- // Backup existing hook (limit to 3 backups)
200
- if (fs.existsSync(preCommitPath)) {
201
- const backup = `${preCommitPath}.backup.${Date.now()}`;
202
- fs.copyFileSync(preCommitPath, backup);
203
- console.log(`[INFO] Backed up existing hook to ${backup}`);
204
-
205
- // Cleanup old backups, keep only 3 most recent
206
- try {
207
- const hooksDir = path.dirname(preCommitPath);
208
- const backups = fs.readdirSync(hooksDir)
209
- .filter(f => f.startsWith('pre-commit.backup.'))
210
- .sort()
211
- .reverse();
212
- for (const old of backups.slice(3)) {
213
- fs.unlinkSync(path.join(hooksDir, old));
214
- }
215
- } catch { /* ignore cleanup errors */ }
216
- }
217
-
218
- fs.writeFileSync(preCommitPath, hookContent, { mode: 0o755 });
219
- console.log(`[OK] Created ${preCommitPath}`);
220
- }
221
-
222
- /**
223
- * Remove MUAD'DIB hooks
224
- */
225
- async function removeHooks(targetPath) {
226
- const resolvedPath = path.resolve(targetPath);
227
-
228
- console.log('\n[MUADDIB] Removing git hooks...\n');
229
-
230
- const detected = detectHookSystem(resolvedPath);
231
-
232
- // Remove husky hook
233
- if (detected.husky) {
234
- const huskyPreCommit = path.join(resolvedPath, '.husky', 'pre-commit');
235
- if (fs.existsSync(huskyPreCommit)) {
236
- const content = fs.readFileSync(huskyPreCommit, 'utf8');
237
- if (content.includes('muaddib')) {
238
- fs.unlinkSync(huskyPreCommit);
239
- console.log('[OK] Removed husky pre-commit hook');
240
- }
241
- }
242
- }
243
-
244
- // Remove git hook
245
- const gitPreCommit = path.join(resolvedPath, '.git', 'hooks', 'pre-commit');
246
- if (fs.existsSync(gitPreCommit)) {
247
- const content = fs.readFileSync(gitPreCommit, 'utf8');
248
- if (content.includes('muaddib') || content.includes('MUADDIB')) {
249
- fs.unlinkSync(gitPreCommit);
250
- console.log('[OK] Removed git pre-commit hook');
251
- }
252
- }
253
-
254
- console.log('\n[OK] MUAD\'DIB hooks removed.\n');
255
- return true;
256
- }
257
-
258
- module.exports = { initHooks, removeHooks, detectHookSystem };
1
+ const { execFileSync } = require('child_process');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ // Read version from package.json for pre-commit config
6
+ const PKG_VERSION = (() => {
7
+ try {
8
+ return 'v' + JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')).version;
9
+ } catch {
10
+ return 'v1.0.0';
11
+ }
12
+ })();
13
+
14
+ /**
15
+ * Detect which hook system is available
16
+ */
17
+ function detectHookSystem(targetPath) {
18
+ const hasHusky = fs.existsSync(path.join(targetPath, '.husky'));
19
+ const hasPreCommitConfig = fs.existsSync(path.join(targetPath, '.pre-commit-config.yaml'));
20
+ const hasGitHooks = fs.existsSync(path.join(targetPath, '.git', 'hooks'));
21
+
22
+ return {
23
+ husky: hasHusky,
24
+ preCommit: hasPreCommitConfig,
25
+ gitHooks: hasGitHooks
26
+ };
27
+ }
28
+
29
+ /**
30
+ * Initialize hooks for a project
31
+ */
32
+ const VALID_MODES = ['scan', 'diff'];
33
+ const HOOK_COMMANDS = {
34
+ scan: 'npx muaddib scan . --fail-on high',
35
+ diff: 'npx muaddib diff HEAD --fail-on high'
36
+ };
37
+
38
+ async function initHooks(targetPath, options = {}) {
39
+ const resolvedPath = path.resolve(targetPath);
40
+ const hookType = options.type || 'auto';
41
+ const mode = VALID_MODES.includes(options.mode) ? options.mode : 'scan';
42
+
43
+ console.log('\n[MUADDIB] Initializing git hooks...\n');
44
+
45
+ const detected = detectHookSystem(resolvedPath);
46
+
47
+ // Auto-detect or use specified type
48
+ let selectedType = hookType;
49
+ if (hookType === 'auto') {
50
+ if (detected.husky) {
51
+ selectedType = 'husky';
52
+ } else if (detected.preCommit) {
53
+ selectedType = 'pre-commit';
54
+ } else {
55
+ selectedType = 'git';
56
+ }
57
+ }
58
+
59
+ console.log(`[MUADDIB] Hook system: ${selectedType}`);
60
+ console.log(`[MUADDIB] Mode: ${mode === 'diff' ? 'diff (only new threats)' : 'scan (all threats)'}\n`);
61
+
62
+ try {
63
+ switch (selectedType) {
64
+ case 'husky':
65
+ await initHusky(resolvedPath, mode);
66
+ break;
67
+ case 'pre-commit':
68
+ await initPreCommit(resolvedPath, mode);
69
+ break;
70
+ case 'git':
71
+ default:
72
+ await initGitHook(resolvedPath, mode);
73
+ break;
74
+ }
75
+
76
+ console.log('\n[OK] Git hooks initialized successfully!');
77
+ console.log('[INFO] MUAD\'DIB will now run before each commit.\n');
78
+
79
+ if (mode === 'diff') {
80
+ console.log('[INFO] Using diff mode: only NEW threats will block commits.');
81
+ console.log('[INFO] Existing threats in the codebase will be ignored.\n');
82
+ }
83
+
84
+ return true;
85
+ } catch (err) {
86
+ console.error(`[ERROR] Failed to initialize hooks: ${err.message}`);
87
+ return false;
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Initialize husky hooks
93
+ */
94
+ async function initHusky(targetPath, mode) {
95
+ const huskyDir = path.join(targetPath, '.husky');
96
+
97
+ // Check if husky is installed
98
+ if (!fs.existsSync(huskyDir)) {
99
+ console.log('[INFO] Husky not detected. Installing...');
100
+ try {
101
+ execFileSync('npx', ['husky', 'install'], {
102
+ cwd: targetPath,
103
+ stdio: 'inherit',
104
+ shell: false
105
+ });
106
+ } catch {
107
+ throw new Error('Failed to install husky. Run: npm install -D husky && npx husky install');
108
+ }
109
+ }
110
+
111
+ // Create pre-commit hook
112
+ const preCommitPath = path.join(huskyDir, 'pre-commit');
113
+ const command = HOOK_COMMANDS[mode];
114
+
115
+ const hookContent = `#!/usr/bin/env sh
116
+ . "$(dirname -- "$0")/_/husky.sh"
117
+
118
+ echo "[MUADDIB] Running security check..."
119
+ ${command}
120
+ `;
121
+
122
+ fs.writeFileSync(preCommitPath, hookContent, { mode: 0o755 });
123
+ if (process.platform !== 'win32') {
124
+ fs.chmodSync(preCommitPath, 0o755);
125
+ }
126
+ console.log(`[OK] Created ${preCommitPath}`);
127
+ }
128
+
129
+ /**
130
+ * Initialize pre-commit framework hooks
131
+ */
132
+ async function initPreCommit(targetPath, mode) {
133
+ const configPath = path.join(targetPath, '.pre-commit-config.yaml');
134
+
135
+ // Read existing config
136
+ let config = '';
137
+ if (fs.existsSync(configPath)) {
138
+ config = fs.readFileSync(configPath, 'utf8');
139
+ }
140
+
141
+ // Check if MUAD'DIB is already configured
142
+ if (config.includes('muaddib-scanner') || config.includes('muad-dib')) {
143
+ console.log('[INFO] MUAD\'DIB already configured in .pre-commit-config.yaml');
144
+ return;
145
+ }
146
+
147
+ // Add MUAD'DIB hook
148
+ const hookId = mode === 'diff' ? 'muaddib-diff' : 'muaddib-scan';
149
+ const muaddibConfig = `
150
+ - repo: https://github.com/DNSZLSK/muad-dib
151
+ rev: ${PKG_VERSION}
152
+ hooks:
153
+ - id: ${hookId}
154
+ `;
155
+
156
+ if (config.includes('repos:')) {
157
+ // Append to existing repos
158
+ config = config.replace(/repos:\s*\n/, `repos:\n${muaddibConfig}`);
159
+ } else {
160
+ // Create new config
161
+ config = `repos:${muaddibConfig}`;
162
+ }
163
+
164
+ fs.writeFileSync(configPath, config);
165
+ console.log(`[OK] Updated ${configPath}`);
166
+ console.log('[INFO] Run: pre-commit install');
167
+ }
168
+
169
+ /**
170
+ * Initialize native git hooks
171
+ */
172
+ async function initGitHook(targetPath, mode) {
173
+ const gitHooksDir = path.join(targetPath, '.git', 'hooks');
174
+
175
+ if (!fs.existsSync(gitHooksDir)) {
176
+ throw new Error('Not a git repository. Run: git init');
177
+ }
178
+
179
+ const preCommitPath = path.join(gitHooksDir, 'pre-commit');
180
+ const command = HOOK_COMMANDS[mode];
181
+
182
+ const hookContent = `#!/bin/sh
183
+ # MUAD'DIB pre-commit hook
184
+ # Generated by: muaddib init-hooks
185
+
186
+ echo "[MUADDIB] Running security check..."
187
+
188
+ ${command}
189
+
190
+ EXIT_CODE=$?
191
+
192
+ if [ $EXIT_CODE -ne 0 ]; then
193
+ echo ""
194
+ echo "[MUADDIB] Commit blocked: security threats detected!"
195
+ echo "[MUADDIB] Fix the issues or use --no-verify to bypass."
196
+ exit 1
197
+ fi
198
+
199
+ exit 0
200
+ `;
201
+
202
+ // Backup existing hook (limit to 3 backups)
203
+ if (fs.existsSync(preCommitPath)) {
204
+ const backup = `${preCommitPath}.backup.${Date.now()}`;
205
+ fs.copyFileSync(preCommitPath, backup);
206
+ console.log(`[INFO] Backed up existing hook to ${backup}`);
207
+
208
+ // Cleanup old backups, keep only 3 most recent
209
+ try {
210
+ const hooksDir = path.dirname(preCommitPath);
211
+ const backups = fs.readdirSync(hooksDir)
212
+ .filter(f => f.startsWith('pre-commit.backup.'))
213
+ .sort()
214
+ .reverse();
215
+ for (const old of backups.slice(3)) {
216
+ fs.unlinkSync(path.join(hooksDir, old));
217
+ }
218
+ } catch { /* ignore cleanup errors */ }
219
+ }
220
+
221
+ fs.writeFileSync(preCommitPath, hookContent, { mode: 0o755 });
222
+ if (process.platform !== 'win32') {
223
+ fs.chmodSync(preCommitPath, 0o755);
224
+ }
225
+ console.log(`[OK] Created ${preCommitPath}`);
226
+ }
227
+
228
+ /**
229
+ * Remove MUAD'DIB hooks
230
+ */
231
+ async function removeHooks(targetPath) {
232
+ const resolvedPath = path.resolve(targetPath);
233
+
234
+ console.log('\n[MUADDIB] Removing git hooks...\n');
235
+
236
+ const detected = detectHookSystem(resolvedPath);
237
+
238
+ // Remove husky hook
239
+ if (detected.husky) {
240
+ const huskyPreCommit = path.join(resolvedPath, '.husky', 'pre-commit');
241
+ if (fs.existsSync(huskyPreCommit)) {
242
+ const content = fs.readFileSync(huskyPreCommit, 'utf8');
243
+ if (content.includes('muaddib')) {
244
+ fs.unlinkSync(huskyPreCommit);
245
+ console.log('[OK] Removed husky pre-commit hook');
246
+ }
247
+ }
248
+ }
249
+
250
+ // Remove git hook
251
+ const gitPreCommit = path.join(resolvedPath, '.git', 'hooks', 'pre-commit');
252
+ if (fs.existsSync(gitPreCommit)) {
253
+ const content = fs.readFileSync(gitPreCommit, 'utf8');
254
+ if (content.includes('muaddib') || content.includes('MUADDIB')) {
255
+ fs.unlinkSync(gitPreCommit);
256
+ console.log('[OK] Removed git pre-commit hook');
257
+ }
258
+ }
259
+
260
+ console.log('\n[OK] MUAD\'DIB hooks removed.\n');
261
+ return true;
262
+ }
263
+
264
+ module.exports = { initHooks, removeHooks, detectHookSystem };