@paa1997/metho 1.0.1 → 1.0.3
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/package.json +8 -2
- package/src/gui.html +22 -1
- package/src/server.js +37 -0
- package/src/steps/katana.js +5 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@paa1997/metho",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Automated recon pipeline: subfinder → gau → filter → katana → findsomething",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,7 +15,13 @@
|
|
|
15
15
|
"start": "node bin/metho.js",
|
|
16
16
|
"gui": "node bin/metho.js --gui"
|
|
17
17
|
},
|
|
18
|
-
"keywords": [
|
|
18
|
+
"keywords": [
|
|
19
|
+
"recon",
|
|
20
|
+
"bugbounty",
|
|
21
|
+
"subdomain",
|
|
22
|
+
"crawl",
|
|
23
|
+
"pipeline"
|
|
24
|
+
],
|
|
19
25
|
"license": "MIT",
|
|
20
26
|
"dependencies": {
|
|
21
27
|
"chalk": "^5.4.1",
|
package/src/gui.html
CHANGED
|
@@ -71,6 +71,14 @@
|
|
|
71
71
|
text-transform: none; letter-spacing: 0; font-weight: 400;
|
|
72
72
|
}
|
|
73
73
|
.skip-checks input[type="checkbox"] { accent-color: #7c3aed; width: 15px; height: 15px; }
|
|
74
|
+
.input-with-btn { display: flex; gap: 8px; }
|
|
75
|
+
.input-with-btn input { flex: 1; }
|
|
76
|
+
.btn-browse {
|
|
77
|
+
background: #2a2a4a; color: #ccc; border: 1px solid #3a3a5a; border-radius: 6px;
|
|
78
|
+
padding: 9px 14px; font-size: 12px; font-weight: 600; cursor: pointer;
|
|
79
|
+
transition: all 0.15s; white-space: nowrap;
|
|
80
|
+
}
|
|
81
|
+
.btn-browse:hover { background: #7c3aed; color: #fff; border-color: #7c3aed; }
|
|
74
82
|
.actions { display: flex; gap: 12px; margin-top: 18px; align-items: center; }
|
|
75
83
|
.btn {
|
|
76
84
|
padding: 10px 28px; border: none; border-radius: 6px;
|
|
@@ -269,7 +277,10 @@
|
|
|
269
277
|
</div>
|
|
270
278
|
<div class="form-group">
|
|
271
279
|
<label>Output Directory</label>
|
|
272
|
-
<
|
|
280
|
+
<div class="input-with-btn">
|
|
281
|
+
<input type="text" id="outputDir" placeholder="./metho-results" value="./metho-results">
|
|
282
|
+
<button class="btn-browse" onclick="browseFolder()">Browse</button>
|
|
283
|
+
</div>
|
|
273
284
|
</div>
|
|
274
285
|
<div class="form-group">
|
|
275
286
|
<label>FindSomething Script (optional override)</label>
|
|
@@ -363,6 +374,16 @@ function getStepNum(name) {
|
|
|
363
374
|
return null;
|
|
364
375
|
}
|
|
365
376
|
|
|
377
|
+
async function browseFolder() {
|
|
378
|
+
try {
|
|
379
|
+
const resp = await fetch('/browse-folder');
|
|
380
|
+
const data = await resp.json();
|
|
381
|
+
if (data.path) {
|
|
382
|
+
document.getElementById('outputDir').value = data.path;
|
|
383
|
+
}
|
|
384
|
+
} catch { /* dialog cancelled or unavailable */ }
|
|
385
|
+
}
|
|
386
|
+
|
|
366
387
|
function esc(str) {
|
|
367
388
|
const d = document.createElement('div');
|
|
368
389
|
d.textContent = str;
|
package/src/server.js
CHANGED
|
@@ -165,6 +165,18 @@ export function startServer(port = 3000) {
|
|
|
165
165
|
return res.end(JSON.stringify({ ok: true }));
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
// GET /browse-folder → open native folder picker dialog
|
|
169
|
+
if (method === 'GET' && url === '/browse-folder') {
|
|
170
|
+
try {
|
|
171
|
+
const folder = await openFolderDialog();
|
|
172
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
173
|
+
return res.end(JSON.stringify({ path: folder }));
|
|
174
|
+
} catch (err) {
|
|
175
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
176
|
+
return res.end(JSON.stringify({ path: null, error: err.message }));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
168
180
|
// GET /file?path=... → read result file
|
|
169
181
|
if (method === 'GET' && url === '/file') {
|
|
170
182
|
const fullUrl = new URL(req.url, `http://localhost:${port}`);
|
|
@@ -259,3 +271,28 @@ function openBrowser(url) {
|
|
|
259
271
|
: `xdg-open "${url}"`;
|
|
260
272
|
exec(cmd, () => {});
|
|
261
273
|
}
|
|
274
|
+
|
|
275
|
+
function openFolderDialog() {
|
|
276
|
+
return new Promise((resolve, reject) => {
|
|
277
|
+
let cmd;
|
|
278
|
+
if (process.platform === 'win32') {
|
|
279
|
+
cmd = 'powershell -Command "Add-Type -AssemblyName System.Windows.Forms; $f = New-Object System.Windows.Forms.FolderBrowserDialog; $f.Description = \'Select output directory\'; if ($f.ShowDialog() -eq \'OK\') { $f.SelectedPath } else { \'\' }"';
|
|
280
|
+
} else if (process.platform === 'darwin') {
|
|
281
|
+
cmd = "osascript -e 'POSIX path of (choose folder with prompt \"Select output directory\")'";
|
|
282
|
+
} else {
|
|
283
|
+
// Linux — try zenity, fall back to kdialog
|
|
284
|
+
cmd = 'zenity --file-selection --directory --title="Select output directory" 2>/dev/null || kdialog --getexistingdirectory ~ 2>/dev/null';
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
exec(cmd, { timeout: 60000 }, (err, stdout) => {
|
|
288
|
+
const path = (stdout || '').trim();
|
|
289
|
+
if (err && !path) {
|
|
290
|
+
return reject(new Error('Dialog cancelled or unavailable'));
|
|
291
|
+
}
|
|
292
|
+
if (!path) {
|
|
293
|
+
return reject(new Error('No folder selected'));
|
|
294
|
+
}
|
|
295
|
+
resolve(path);
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
}
|
package/src/steps/katana.js
CHANGED
|
@@ -14,10 +14,13 @@ export class KatanaStep extends BaseStep {
|
|
|
14
14
|
const chunkSize = config.katanaChunkSize;
|
|
15
15
|
const depth = config.katanaDepth;
|
|
16
16
|
|
|
17
|
+
// Exclude media, images, fonts, stylesheets and other non-useful extensions
|
|
18
|
+
const excludeExt = 'png,jpg,jpeg,gif,svg,ico,webp,bmp,tiff,mp4,mp3,avi,mov,wmv,flv,webm,ogg,wav,css,woff,woff2,ttf,eot,otf,pdf,zip,gz,tar,rar,7z,mp2,mkv';
|
|
19
|
+
|
|
17
20
|
if (lineCount <= chunkSize) {
|
|
18
21
|
// Small enough to run in one shot
|
|
19
22
|
this.logger.debug(`Katana: ${lineCount} URLs, running single batch`);
|
|
20
|
-
const args = ['-list', inputFile, '-d', String(depth), '-o', outputFile];
|
|
23
|
+
const args = ['-list', inputFile, '-d', String(depth), '-ef', excludeExt, '-o', outputFile];
|
|
21
24
|
return this.runCommand('katana', args, { outputFile });
|
|
22
25
|
}
|
|
23
26
|
|
|
@@ -32,7 +35,7 @@ export class KatanaStep extends BaseStep {
|
|
|
32
35
|
for (let i = 0; i < chunks.length; i++) {
|
|
33
36
|
this.logger.info(`Katana chunk ${i + 1}/${chunks.length}...`);
|
|
34
37
|
const chunkOutput = join(tmpDir, `katana-out-${i}.txt`);
|
|
35
|
-
const args = ['-list', chunks[i], '-d', String(depth), '-o', chunkOutput];
|
|
38
|
+
const args = ['-list', chunks[i], '-d', String(depth), '-ef', excludeExt, '-o', chunkOutput];
|
|
36
39
|
|
|
37
40
|
try {
|
|
38
41
|
await this.runCommand('katana', args, { outputFile: chunkOutput });
|