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/.gitattributes +18 -0
- package/README.fr.md +819 -825
- package/README.md +822 -822
- package/bin/muaddib.js +868 -849
- package/docker/Dockerfile +19 -18
- package/docker/sandbox-runner.sh +313 -292
- package/package.json +61 -61
- package/src/commands/evaluate.js +484 -484
- package/src/diff.js +411 -411
- package/src/hooks-init.js +264 -258
- package/src/index.js +439 -437
- package/src/ioc/scraper.js +1293 -1293
- package/src/monitor.js +1817 -1764
- package/src/sandbox.js +631 -620
- package/src/scanner/ast-detectors.js +946 -933
- package/src/scanner/ast.js +96 -96
- package/src/scanner/github-actions.js +96 -96
- package/src/scanner/module-graph.js +2 -2
- package/src/scanner/obfuscation.js +128 -128
- package/src/scanner/shell.js +48 -48
- package/src/shared/download.js +181 -171
- package/src/webhook.js +413 -353
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
//
|
|
158
|
-
config = `repos
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
exit
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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 };
|