s3db.js 13.6.1 → 14.0.2
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/README.md +56 -15
- package/dist/s3db.cjs +72446 -39022
- package/dist/s3db.cjs.map +1 -1
- package/dist/s3db.es.js +72172 -38790
- package/dist/s3db.es.js.map +1 -1
- package/mcp/lib/base-handler.js +157 -0
- package/mcp/lib/handlers/connection-handler.js +280 -0
- package/mcp/lib/handlers/query-handler.js +533 -0
- package/mcp/lib/handlers/resource-handler.js +428 -0
- package/mcp/lib/tool-registry.js +336 -0
- package/mcp/lib/tools/connection-tools.js +161 -0
- package/mcp/lib/tools/query-tools.js +267 -0
- package/mcp/lib/tools/resource-tools.js +404 -0
- package/package.json +85 -50
- package/src/clients/memory-client.class.js +346 -191
- package/src/clients/memory-storage.class.js +300 -84
- package/src/clients/s3-client.class.js +7 -6
- package/src/concerns/geo-encoding.js +19 -2
- package/src/concerns/ip.js +59 -9
- package/src/concerns/money.js +8 -1
- package/src/concerns/password-hashing.js +49 -8
- package/src/concerns/plugin-storage.js +186 -18
- package/src/concerns/storage-drivers/filesystem-driver.js +284 -0
- package/src/database.class.js +139 -29
- package/src/errors.js +332 -42
- package/src/plugins/api/auth/oidc-auth.js +66 -17
- package/src/plugins/api/auth/strategies/base-strategy.class.js +74 -0
- package/src/plugins/api/auth/strategies/factory.class.js +63 -0
- package/src/plugins/api/auth/strategies/global-strategy.class.js +44 -0
- package/src/plugins/api/auth/strategies/path-based-strategy.class.js +83 -0
- package/src/plugins/api/auth/strategies/path-rules-strategy.class.js +118 -0
- package/src/plugins/api/concerns/failban-manager.js +106 -57
- package/src/plugins/api/concerns/route-context.js +601 -0
- package/src/plugins/api/index.js +168 -40
- package/src/plugins/api/routes/auth-routes.js +198 -30
- package/src/plugins/api/routes/resource-routes.js +19 -4
- package/src/plugins/api/server/health-manager.class.js +163 -0
- package/src/plugins/api/server/middleware-chain.class.js +310 -0
- package/src/plugins/api/server/router.class.js +472 -0
- package/src/plugins/api/server.js +280 -1303
- package/src/plugins/api/utils/custom-routes.js +17 -5
- package/src/plugins/api/utils/guards.js +76 -17
- package/src/plugins/api/utils/openapi-generator-cached.class.js +133 -0
- package/src/plugins/api/utils/openapi-generator.js +7 -6
- package/src/plugins/audit.plugin.js +30 -8
- package/src/plugins/backup.plugin.js +110 -14
- package/src/plugins/cache/cache.class.js +22 -5
- package/src/plugins/cache/filesystem-cache.class.js +116 -19
- package/src/plugins/cache/memory-cache.class.js +211 -57
- package/src/plugins/cache/multi-tier-cache.class.js +371 -0
- package/src/plugins/cache/partition-aware-filesystem-cache.class.js +168 -47
- package/src/plugins/cache/redis-cache.class.js +552 -0
- package/src/plugins/cache/s3-cache.class.js +17 -8
- package/src/plugins/cache.plugin.js +176 -61
- package/src/plugins/cloud-inventory/drivers/alibaba-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/aws-driver.js +60 -29
- package/src/plugins/cloud-inventory/drivers/azure-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/base-driver.js +16 -2
- package/src/plugins/cloud-inventory/drivers/cloudflare-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/digitalocean-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/hetzner-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/linode-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/mongodb-atlas-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/vultr-driver.js +8 -1
- package/src/plugins/cloud-inventory/index.js +29 -8
- package/src/plugins/cloud-inventory/registry.js +64 -42
- package/src/plugins/cloud-inventory.plugin.js +240 -138
- package/src/plugins/concerns/plugin-dependencies.js +54 -0
- package/src/plugins/concerns/resource-names.js +100 -0
- package/src/plugins/consumers/index.js +10 -2
- package/src/plugins/consumers/sqs-consumer.js +12 -2
- package/src/plugins/cookie-farm-suite.plugin.js +278 -0
- package/src/plugins/cookie-farm.errors.js +73 -0
- package/src/plugins/cookie-farm.plugin.js +869 -0
- package/src/plugins/costs.plugin.js +7 -1
- package/src/plugins/eventual-consistency/analytics.js +94 -19
- package/src/plugins/eventual-consistency/config.js +15 -7
- package/src/plugins/eventual-consistency/consolidation.js +29 -11
- package/src/plugins/eventual-consistency/garbage-collection.js +3 -1
- package/src/plugins/eventual-consistency/helpers.js +39 -14
- package/src/plugins/eventual-consistency/install.js +21 -2
- package/src/plugins/eventual-consistency/utils.js +32 -10
- package/src/plugins/fulltext.plugin.js +38 -11
- package/src/plugins/geo.plugin.js +61 -9
- package/src/plugins/identity/concerns/config.js +61 -0
- package/src/plugins/identity/concerns/mfa-manager.js +15 -2
- package/src/plugins/identity/concerns/rate-limit.js +124 -0
- package/src/plugins/identity/concerns/resource-schemas.js +9 -1
- package/src/plugins/identity/concerns/token-generator.js +29 -4
- package/src/plugins/identity/drivers/auth-driver.interface.js +76 -0
- package/src/plugins/identity/drivers/client-credentials-driver.js +127 -0
- package/src/plugins/identity/drivers/index.js +18 -0
- package/src/plugins/identity/drivers/password-driver.js +122 -0
- package/src/plugins/identity/email-service.js +17 -2
- package/src/plugins/identity/index.js +413 -69
- package/src/plugins/identity/oauth2-server.js +413 -30
- package/src/plugins/identity/oidc-discovery.js +16 -8
- package/src/plugins/identity/rsa-keys.js +115 -35
- package/src/plugins/identity/server.js +166 -45
- package/src/plugins/identity/session-manager.js +53 -7
- package/src/plugins/identity/ui/pages/mfa-verification.js +17 -15
- package/src/plugins/identity/ui/routes.js +363 -255
- package/src/plugins/importer/index.js +153 -20
- package/src/plugins/index.js +9 -2
- package/src/plugins/kubernetes-inventory/index.js +6 -0
- package/src/plugins/kubernetes-inventory/k8s-driver.js +867 -0
- package/src/plugins/kubernetes-inventory/resource-types.js +274 -0
- package/src/plugins/kubernetes-inventory.plugin.js +980 -0
- package/src/plugins/metrics.plugin.js +64 -16
- package/src/plugins/ml/base-model.class.js +25 -15
- package/src/plugins/ml/regression-model.class.js +1 -1
- package/src/plugins/ml.errors.js +57 -25
- package/src/plugins/ml.plugin.js +28 -4
- package/src/plugins/namespace.js +210 -0
- package/src/plugins/plugin.class.js +180 -8
- package/src/plugins/puppeteer/console-monitor.js +729 -0
- package/src/plugins/puppeteer/cookie-manager.js +492 -0
- package/src/plugins/puppeteer/network-monitor.js +816 -0
- package/src/plugins/puppeteer/performance-manager.js +746 -0
- package/src/plugins/puppeteer/proxy-manager.js +478 -0
- package/src/plugins/puppeteer/stealth-manager.js +556 -0
- package/src/plugins/puppeteer.errors.js +81 -0
- package/src/plugins/puppeteer.plugin.js +1327 -0
- package/src/plugins/queue-consumer.plugin.js +69 -14
- package/src/plugins/recon/behaviors/uptime-behavior.js +691 -0
- package/src/plugins/recon/concerns/command-runner.js +148 -0
- package/src/plugins/recon/concerns/diff-detector.js +372 -0
- package/src/plugins/recon/concerns/fingerprint-builder.js +307 -0
- package/src/plugins/recon/concerns/process-manager.js +338 -0
- package/src/plugins/recon/concerns/report-generator.js +478 -0
- package/src/plugins/recon/concerns/security-analyzer.js +571 -0
- package/src/plugins/recon/concerns/target-normalizer.js +68 -0
- package/src/plugins/recon/config/defaults.js +321 -0
- package/src/plugins/recon/config/resources.js +370 -0
- package/src/plugins/recon/index.js +778 -0
- package/src/plugins/recon/managers/dependency-manager.js +174 -0
- package/src/plugins/recon/managers/scheduler-manager.js +179 -0
- package/src/plugins/recon/managers/storage-manager.js +745 -0
- package/src/plugins/recon/managers/target-manager.js +274 -0
- package/src/plugins/recon/stages/asn-stage.js +314 -0
- package/src/plugins/recon/stages/certificate-stage.js +84 -0
- package/src/plugins/recon/stages/dns-stage.js +107 -0
- package/src/plugins/recon/stages/dnsdumpster-stage.js +362 -0
- package/src/plugins/recon/stages/fingerprint-stage.js +71 -0
- package/src/plugins/recon/stages/google-dorks-stage.js +440 -0
- package/src/plugins/recon/stages/http-stage.js +89 -0
- package/src/plugins/recon/stages/latency-stage.js +148 -0
- package/src/plugins/recon/stages/massdns-stage.js +302 -0
- package/src/plugins/recon/stages/osint-stage.js +1373 -0
- package/src/plugins/recon/stages/ports-stage.js +169 -0
- package/src/plugins/recon/stages/screenshot-stage.js +94 -0
- package/src/plugins/recon/stages/secrets-stage.js +514 -0
- package/src/plugins/recon/stages/subdomains-stage.js +295 -0
- package/src/plugins/recon/stages/tls-audit-stage.js +78 -0
- package/src/plugins/recon/stages/vulnerability-stage.js +78 -0
- package/src/plugins/recon/stages/web-discovery-stage.js +113 -0
- package/src/plugins/recon/stages/whois-stage.js +349 -0
- package/src/plugins/recon.plugin.js +75 -0
- package/src/plugins/recon.plugin.js.backup +2635 -0
- package/src/plugins/relation.errors.js +87 -14
- package/src/plugins/replicator.plugin.js +514 -137
- package/src/plugins/replicators/base-replicator.class.js +89 -1
- package/src/plugins/replicators/bigquery-replicator.class.js +66 -22
- package/src/plugins/replicators/dynamodb-replicator.class.js +22 -15
- package/src/plugins/replicators/mongodb-replicator.class.js +22 -15
- package/src/plugins/replicators/mysql-replicator.class.js +52 -17
- package/src/plugins/replicators/planetscale-replicator.class.js +30 -4
- package/src/plugins/replicators/postgres-replicator.class.js +62 -27
- package/src/plugins/replicators/s3db-replicator.class.js +25 -18
- package/src/plugins/replicators/schema-sync.helper.js +3 -3
- package/src/plugins/replicators/sqs-replicator.class.js +8 -2
- package/src/plugins/replicators/turso-replicator.class.js +23 -3
- package/src/plugins/replicators/webhook-replicator.class.js +42 -4
- package/src/plugins/s3-queue.plugin.js +464 -65
- package/src/plugins/scheduler.plugin.js +20 -6
- package/src/plugins/state-machine.plugin.js +40 -9
- package/src/plugins/tfstate/base-driver.js +28 -4
- package/src/plugins/tfstate/errors.js +65 -10
- package/src/plugins/tfstate/filesystem-driver.js +52 -8
- package/src/plugins/tfstate/index.js +163 -90
- package/src/plugins/tfstate/s3-driver.js +64 -6
- package/src/plugins/ttl.plugin.js +72 -17
- package/src/plugins/vector/distances.js +18 -12
- package/src/plugins/vector/kmeans.js +26 -4
- package/src/resource.class.js +115 -19
- package/src/testing/factory.class.js +20 -3
- package/src/testing/seeder.class.js +7 -1
- package/src/clients/memory-client.md +0 -917
- package/src/plugins/cloud-inventory/drivers/mock-drivers.js +0 -449
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GoogleDorksStage
|
|
3
|
+
*
|
|
4
|
+
* Search engine reconnaissance using Google Dorks
|
|
5
|
+
*
|
|
6
|
+
* Discovers:
|
|
7
|
+
* - GitHub repositories
|
|
8
|
+
* - Pastebin leaks
|
|
9
|
+
* - LinkedIn employees
|
|
10
|
+
* - Exposed files (PDF, DOC, XLS)
|
|
11
|
+
* - Subdomains
|
|
12
|
+
* - Login pages
|
|
13
|
+
* - Exposed configs
|
|
14
|
+
*
|
|
15
|
+
* Uses 100% free web scraping (no API key required)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export class GoogleDorksStage {
|
|
19
|
+
constructor(plugin) {
|
|
20
|
+
this.plugin = plugin;
|
|
21
|
+
this.commandRunner = plugin.commandRunner;
|
|
22
|
+
this.config = plugin.config;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Execute Google Dorks search
|
|
27
|
+
* @param {Object} target - Target object with host property
|
|
28
|
+
* @param {Object} options - Google Dorks options
|
|
29
|
+
* @returns {Promise<Object>} Search results
|
|
30
|
+
*/
|
|
31
|
+
async execute(target, options = {}) {
|
|
32
|
+
const domain = this.extractBaseDomain(target.host);
|
|
33
|
+
const companyName = this.extractCompanyName(domain);
|
|
34
|
+
|
|
35
|
+
const result = {
|
|
36
|
+
status: 'ok',
|
|
37
|
+
domain,
|
|
38
|
+
companyName,
|
|
39
|
+
categories: {
|
|
40
|
+
github: null, // GitHub repos/code
|
|
41
|
+
pastebin: null, // Pastebin leaks
|
|
42
|
+
linkedin: null, // LinkedIn employees
|
|
43
|
+
documents: null, // Exposed docs (PDF, DOC, XLS)
|
|
44
|
+
subdomains: null, // Subdomains via Google
|
|
45
|
+
loginPages: null, // Login/admin pages
|
|
46
|
+
configs: null, // Config files
|
|
47
|
+
errors: null // Error pages revealing info
|
|
48
|
+
},
|
|
49
|
+
summary: {
|
|
50
|
+
totalResults: 0,
|
|
51
|
+
totalCategories: 0
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Track individual category results for artifact persistence
|
|
56
|
+
const individual = {};
|
|
57
|
+
|
|
58
|
+
const enabledCategories = options.categories || [
|
|
59
|
+
'github', 'pastebin', 'linkedin', 'documents',
|
|
60
|
+
'subdomains', 'loginPages', 'configs', 'errors'
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
// Execute each category
|
|
64
|
+
for (const category of enabledCategories) {
|
|
65
|
+
try {
|
|
66
|
+
let categoryData = null;
|
|
67
|
+
|
|
68
|
+
switch (category) {
|
|
69
|
+
case 'github':
|
|
70
|
+
categoryData = await this.searchGitHub(domain, companyName, options);
|
|
71
|
+
break;
|
|
72
|
+
case 'pastebin':
|
|
73
|
+
categoryData = await this.searchPastebin(domain, companyName, options);
|
|
74
|
+
break;
|
|
75
|
+
case 'linkedin':
|
|
76
|
+
categoryData = await this.searchLinkedIn(domain, companyName, options);
|
|
77
|
+
break;
|
|
78
|
+
case 'documents':
|
|
79
|
+
categoryData = await this.searchDocuments(domain, options);
|
|
80
|
+
break;
|
|
81
|
+
case 'subdomains':
|
|
82
|
+
categoryData = await this.searchSubdomains(domain, options);
|
|
83
|
+
break;
|
|
84
|
+
case 'loginPages':
|
|
85
|
+
categoryData = await this.searchLoginPages(domain, options);
|
|
86
|
+
break;
|
|
87
|
+
case 'configs':
|
|
88
|
+
categoryData = await this.searchConfigs(domain, options);
|
|
89
|
+
break;
|
|
90
|
+
case 'errors':
|
|
91
|
+
categoryData = await this.searchErrors(domain, options);
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
result.categories[category] = categoryData;
|
|
96
|
+
individual[category] = categoryData; // Store in individual too
|
|
97
|
+
|
|
98
|
+
} catch (error) {
|
|
99
|
+
result.categories[category] = {
|
|
100
|
+
status: 'error',
|
|
101
|
+
message: error.message
|
|
102
|
+
};
|
|
103
|
+
individual[category] = {
|
|
104
|
+
status: 'error',
|
|
105
|
+
message: error.message
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Calculate summary
|
|
111
|
+
let totalResults = 0;
|
|
112
|
+
let totalCategories = 0;
|
|
113
|
+
|
|
114
|
+
for (const [category, data] of Object.entries(result.categories)) {
|
|
115
|
+
if (data && data.status === 'ok') {
|
|
116
|
+
totalCategories++;
|
|
117
|
+
if (data.results) {
|
|
118
|
+
totalResults += data.results.length;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
result.summary.totalResults = totalResults;
|
|
124
|
+
result.summary.totalCategories = totalCategories;
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
_individual: individual,
|
|
128
|
+
_aggregated: result,
|
|
129
|
+
...result // Root level for compatibility
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Search GitHub for company repos/code
|
|
135
|
+
* Dork: site:github.com "companyname"
|
|
136
|
+
*/
|
|
137
|
+
async searchGitHub(domain, companyName, options = {}) {
|
|
138
|
+
const queries = [
|
|
139
|
+
`site:github.com "${companyName}"`,
|
|
140
|
+
`site:github.com "${domain}"`,
|
|
141
|
+
`site:github.com "api" "${domain}"`,
|
|
142
|
+
`site:github.com "config" "${domain}"`
|
|
143
|
+
];
|
|
144
|
+
|
|
145
|
+
const results = [];
|
|
146
|
+
|
|
147
|
+
for (const query of queries) {
|
|
148
|
+
const urls = await this.performGoogleSearch(query, options);
|
|
149
|
+
results.push(...urls.map(url => ({ url, query })));
|
|
150
|
+
|
|
151
|
+
// Rate limit
|
|
152
|
+
await this.sleep(2000);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
status: 'ok',
|
|
157
|
+
results: this.deduplicateResults(results),
|
|
158
|
+
count: results.length
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Search Pastebin for leaks
|
|
164
|
+
* Dork: site:pastebin.com "domain.com"
|
|
165
|
+
*/
|
|
166
|
+
async searchPastebin(domain, companyName, options = {}) {
|
|
167
|
+
const queries = [
|
|
168
|
+
`site:pastebin.com "${domain}"`,
|
|
169
|
+
`site:pastebin.com "${companyName}"`,
|
|
170
|
+
`site:paste2.org "${domain}"`,
|
|
171
|
+
`site:slexy.org "${domain}"`
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
const results = [];
|
|
175
|
+
|
|
176
|
+
for (const query of queries) {
|
|
177
|
+
const urls = await this.performGoogleSearch(query, options);
|
|
178
|
+
results.push(...urls.map(url => ({ url, query })));
|
|
179
|
+
|
|
180
|
+
await this.sleep(2000);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
status: 'ok',
|
|
185
|
+
results: this.deduplicateResults(results),
|
|
186
|
+
count: results.length
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Search LinkedIn for employees
|
|
192
|
+
* Dork: site:linkedin.com/in "companyname"
|
|
193
|
+
*/
|
|
194
|
+
async searchLinkedIn(domain, companyName, options = {}) {
|
|
195
|
+
const queries = [
|
|
196
|
+
`site:linkedin.com/in "${companyName}"`,
|
|
197
|
+
`site:linkedin.com/company/${companyName.toLowerCase().replace(/\s+/g, '-')}`
|
|
198
|
+
];
|
|
199
|
+
|
|
200
|
+
const results = [];
|
|
201
|
+
|
|
202
|
+
for (const query of queries) {
|
|
203
|
+
const urls = await this.performGoogleSearch(query, options);
|
|
204
|
+
results.push(...urls.map(url => ({ url, query })));
|
|
205
|
+
|
|
206
|
+
await this.sleep(2000);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
status: 'ok',
|
|
211
|
+
results: this.deduplicateResults(results),
|
|
212
|
+
count: results.length
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Search for exposed documents
|
|
218
|
+
* Dork: site:domain.com filetype:pdf|doc|xls
|
|
219
|
+
*/
|
|
220
|
+
async searchDocuments(domain, options = {}) {
|
|
221
|
+
const filetypes = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx'];
|
|
222
|
+
const results = [];
|
|
223
|
+
|
|
224
|
+
for (const filetype of filetypes) {
|
|
225
|
+
const query = `site:${domain} filetype:${filetype}`;
|
|
226
|
+
const urls = await this.performGoogleSearch(query, options);
|
|
227
|
+
results.push(...urls.map(url => ({ url, filetype, query })));
|
|
228
|
+
|
|
229
|
+
await this.sleep(2000);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
status: 'ok',
|
|
234
|
+
results: this.deduplicateResults(results),
|
|
235
|
+
count: results.length
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Search for subdomains
|
|
241
|
+
* Dork: site:*.domain.com
|
|
242
|
+
*/
|
|
243
|
+
async searchSubdomains(domain, options = {}) {
|
|
244
|
+
const query = `site:*.${domain}`;
|
|
245
|
+
const urls = await this.performGoogleSearch(query, options);
|
|
246
|
+
|
|
247
|
+
const subdomains = urls.map(url => {
|
|
248
|
+
const match = url.match(/https?:\/\/([^\/]+)/);
|
|
249
|
+
return match ? match[1] : null;
|
|
250
|
+
}).filter(Boolean);
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
status: 'ok',
|
|
254
|
+
results: [...new Set(subdomains)].map(subdomain => ({ subdomain })),
|
|
255
|
+
count: subdomains.length
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Search for login/admin pages
|
|
261
|
+
* Dork: site:domain.com inurl:login|admin|dashboard
|
|
262
|
+
*/
|
|
263
|
+
async searchLoginPages(domain, options = {}) {
|
|
264
|
+
const queries = [
|
|
265
|
+
`site:${domain} inurl:login`,
|
|
266
|
+
`site:${domain} inurl:admin`,
|
|
267
|
+
`site:${domain} inurl:dashboard`,
|
|
268
|
+
`site:${domain} inurl:portal`,
|
|
269
|
+
`site:${domain} intitle:"login" OR intitle:"sign in"`
|
|
270
|
+
];
|
|
271
|
+
|
|
272
|
+
const results = [];
|
|
273
|
+
|
|
274
|
+
for (const query of queries) {
|
|
275
|
+
const urls = await this.performGoogleSearch(query, options);
|
|
276
|
+
results.push(...urls.map(url => ({ url, query })));
|
|
277
|
+
|
|
278
|
+
await this.sleep(2000);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return {
|
|
282
|
+
status: 'ok',
|
|
283
|
+
results: this.deduplicateResults(results),
|
|
284
|
+
count: results.length
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Search for config files
|
|
290
|
+
* Dork: site:domain.com ext:env|config|ini|yml
|
|
291
|
+
*/
|
|
292
|
+
async searchConfigs(domain, options = {}) {
|
|
293
|
+
const queries = [
|
|
294
|
+
`site:${domain} ext:env`,
|
|
295
|
+
`site:${domain} ext:config`,
|
|
296
|
+
`site:${domain} ext:ini`,
|
|
297
|
+
`site:${domain} ext:yml`,
|
|
298
|
+
`site:${domain} ext:yaml`,
|
|
299
|
+
`site:${domain} inurl:config`,
|
|
300
|
+
`site:${domain} intitle:"index of" "config"`
|
|
301
|
+
];
|
|
302
|
+
|
|
303
|
+
const results = [];
|
|
304
|
+
|
|
305
|
+
for (const query of queries) {
|
|
306
|
+
const urls = await this.performGoogleSearch(query, options);
|
|
307
|
+
results.push(...urls.map(url => ({ url, query })));
|
|
308
|
+
|
|
309
|
+
await this.sleep(2000);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return {
|
|
313
|
+
status: 'ok',
|
|
314
|
+
results: this.deduplicateResults(results),
|
|
315
|
+
count: results.length
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Search for error pages (can reveal paths, versions, etc.)
|
|
321
|
+
* Dork: site:domain.com intext:"error" OR intext:"warning"
|
|
322
|
+
*/
|
|
323
|
+
async searchErrors(domain, options = {}) {
|
|
324
|
+
const queries = [
|
|
325
|
+
`site:${domain} intext:"error" OR intext:"exception"`,
|
|
326
|
+
`site:${domain} intext:"stack trace"`,
|
|
327
|
+
`site:${domain} intext:"warning" intitle:"error"`,
|
|
328
|
+
`site:${domain} intext:"mysql" intext:"error"`,
|
|
329
|
+
`site:${domain} intext:"fatal error"`
|
|
330
|
+
];
|
|
331
|
+
|
|
332
|
+
const results = [];
|
|
333
|
+
|
|
334
|
+
for (const query of queries) {
|
|
335
|
+
const urls = await this.performGoogleSearch(query, options);
|
|
336
|
+
results.push(...urls.map(url => ({ url, query })));
|
|
337
|
+
|
|
338
|
+
await this.sleep(2000);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return {
|
|
342
|
+
status: 'ok',
|
|
343
|
+
results: this.deduplicateResults(results),
|
|
344
|
+
count: results.length
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Perform actual Google search
|
|
350
|
+
* Note: Google blocks automated queries, so this uses DuckDuckGo as fallback
|
|
351
|
+
*/
|
|
352
|
+
async performGoogleSearch(query, options = {}) {
|
|
353
|
+
const maxResults = options.maxResults || 10;
|
|
354
|
+
const results = [];
|
|
355
|
+
|
|
356
|
+
try {
|
|
357
|
+
// Try DuckDuckGo HTML search (more permissive than Google)
|
|
358
|
+
const encodedQuery = encodeURIComponent(query);
|
|
359
|
+
const url = `https://html.duckduckgo.com/html/?q=${encodedQuery}`;
|
|
360
|
+
|
|
361
|
+
const response = await fetch(url, {
|
|
362
|
+
headers: {
|
|
363
|
+
'User-Agent': this.config.curl?.userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
|
364
|
+
},
|
|
365
|
+
signal: AbortSignal.timeout ? AbortSignal.timeout(15000) : undefined
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
if (!response.ok) {
|
|
369
|
+
return results;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const html = await response.text();
|
|
373
|
+
|
|
374
|
+
// Parse DuckDuckGo results
|
|
375
|
+
// Format: <a class="result__a" href="//duckduckgo.com/l/?uddg=URL&rut=...">
|
|
376
|
+
const urlMatches = html.matchAll(/class="result__url"[^>]*>([^<]+)</g);
|
|
377
|
+
|
|
378
|
+
for (const match of urlMatches) {
|
|
379
|
+
let resultUrl = match[1].trim();
|
|
380
|
+
|
|
381
|
+
// Clean up URL
|
|
382
|
+
if (resultUrl.startsWith('//')) {
|
|
383
|
+
resultUrl = 'https:' + resultUrl;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
results.push(resultUrl);
|
|
387
|
+
|
|
388
|
+
if (results.length >= maxResults) {
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
} catch (error) {
|
|
394
|
+
// Silently fail
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return results;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Deduplicate results by URL
|
|
402
|
+
*/
|
|
403
|
+
deduplicateResults(results) {
|
|
404
|
+
const seen = new Set();
|
|
405
|
+
return results.filter(item => {
|
|
406
|
+
const url = item.url || item.subdomain;
|
|
407
|
+
if (seen.has(url)) {
|
|
408
|
+
return false;
|
|
409
|
+
}
|
|
410
|
+
seen.add(url);
|
|
411
|
+
return true;
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Extract base domain from host
|
|
417
|
+
*/
|
|
418
|
+
extractBaseDomain(host) {
|
|
419
|
+
const parts = host.split('.');
|
|
420
|
+
if (parts.length > 2) {
|
|
421
|
+
return parts.slice(-2).join('.');
|
|
422
|
+
}
|
|
423
|
+
return host;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Extract company name from domain
|
|
428
|
+
*/
|
|
429
|
+
extractCompanyName(domain) {
|
|
430
|
+
const parts = domain.split('.');
|
|
431
|
+
return parts[0].charAt(0).toUpperCase() + parts[0].slice(1);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Sleep helper
|
|
436
|
+
*/
|
|
437
|
+
async sleep(ms) {
|
|
438
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
439
|
+
}
|
|
440
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HttpStage
|
|
3
|
+
*
|
|
4
|
+
* HTTP header analysis:
|
|
5
|
+
* - Server identification
|
|
6
|
+
* - Security headers
|
|
7
|
+
* - Technology fingerprinting from headers
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export class HttpStage {
|
|
11
|
+
constructor(plugin) {
|
|
12
|
+
this.plugin = plugin;
|
|
13
|
+
this.commandRunner = plugin.commandRunner;
|
|
14
|
+
this.config = plugin.config;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async execute(target) {
|
|
18
|
+
const url = this._buildUrl(target);
|
|
19
|
+
const args = [
|
|
20
|
+
'-I',
|
|
21
|
+
'-sS',
|
|
22
|
+
'-L',
|
|
23
|
+
'--max-time',
|
|
24
|
+
String(Math.ceil(this.config.curl.timeout / 1000)),
|
|
25
|
+
'--user-agent',
|
|
26
|
+
this.config.curl.userAgent,
|
|
27
|
+
url
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
const result = await this.commandRunner.run('curl', args, {
|
|
31
|
+
timeout: this.config.curl.timeout
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (!result.ok) {
|
|
35
|
+
return {
|
|
36
|
+
status: 'unavailable',
|
|
37
|
+
message: result.error?.message || 'curl failed',
|
|
38
|
+
stderr: result.stderr
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const headers = this._parseCurlHeaders(result.stdout);
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
status: 'ok',
|
|
46
|
+
url,
|
|
47
|
+
headers,
|
|
48
|
+
raw: this.config.storage.persistRawOutput ? this._truncateOutput(result.stdout) : undefined
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
_buildUrl(target) {
|
|
53
|
+
const protocol = target.protocol || 'https';
|
|
54
|
+
const port = target.port && target.port !== this._defaultPortForProtocol(protocol)
|
|
55
|
+
? `:${target.port}`
|
|
56
|
+
: '';
|
|
57
|
+
const path = target.path || '';
|
|
58
|
+
return `${protocol}://${target.host}${port}${path}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
_defaultPortForProtocol(protocol) {
|
|
62
|
+
switch (protocol) {
|
|
63
|
+
case 'http':
|
|
64
|
+
return 80;
|
|
65
|
+
case 'https':
|
|
66
|
+
return 443;
|
|
67
|
+
default:
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
_parseCurlHeaders(raw) {
|
|
73
|
+
const lines = raw.split(/\r?\n/).filter(Boolean);
|
|
74
|
+
const headers = {};
|
|
75
|
+
for (const line of lines) {
|
|
76
|
+
if (!line.includes(':')) continue;
|
|
77
|
+
const [key, ...rest] = line.split(':');
|
|
78
|
+
headers[key.trim().toLowerCase()] = rest.join(':').trim();
|
|
79
|
+
}
|
|
80
|
+
return headers;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
_truncateOutput(text, maxLength = 10000) {
|
|
84
|
+
if (!text || text.length <= maxLength) {
|
|
85
|
+
return text;
|
|
86
|
+
}
|
|
87
|
+
return text.substring(0, maxLength) + '\n... (truncated)';
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LatencyStage
|
|
3
|
+
*
|
|
4
|
+
* Network latency measurement:
|
|
5
|
+
* - Ping (ICMP echo) with metrics
|
|
6
|
+
* - Traceroute (mtr or traceroute)
|
|
7
|
+
* - Hop analysis
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export class LatencyStage {
|
|
11
|
+
constructor(plugin) {
|
|
12
|
+
this.plugin = plugin;
|
|
13
|
+
this.commandRunner = plugin.commandRunner;
|
|
14
|
+
this.config = plugin.config;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async execute(target, options = {}) {
|
|
18
|
+
const config = { ...this.config.latency, ...options };
|
|
19
|
+
const results = {};
|
|
20
|
+
|
|
21
|
+
// Execute ping if enabled
|
|
22
|
+
if (config.ping !== false) {
|
|
23
|
+
results.ping = await this.executePing(target);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Execute traceroute if enabled
|
|
27
|
+
if (config.traceroute !== false) {
|
|
28
|
+
results.traceroute = await this.executeTraceroute(target);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return results;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async executePing(target) {
|
|
35
|
+
const args = ['-n', '-c', String(this.config.ping.count), target.host];
|
|
36
|
+
const run = await this.commandRunner.run('ping', args, {
|
|
37
|
+
timeout: this.config.ping.timeout
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (!run.ok) {
|
|
41
|
+
return {
|
|
42
|
+
status: 'unavailable',
|
|
43
|
+
message: run.error?.message || 'Ping failed',
|
|
44
|
+
stderr: run.stderr
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const metrics = this._parsePingOutput(run.stdout);
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
status: 'ok',
|
|
52
|
+
stdout: run.stdout,
|
|
53
|
+
metrics
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async executeTraceroute(target) {
|
|
58
|
+
if (await this.commandRunner.isAvailable('mtr')) {
|
|
59
|
+
const args = [
|
|
60
|
+
'--report',
|
|
61
|
+
'--report-cycles',
|
|
62
|
+
String(this.config.traceroute.cycles),
|
|
63
|
+
'--json',
|
|
64
|
+
target.host
|
|
65
|
+
];
|
|
66
|
+
const mtrResult = await this.commandRunner.run('mtr', args, {
|
|
67
|
+
timeout: this.config.traceroute.timeout,
|
|
68
|
+
maxBuffer: 4 * 1024 * 1024
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (mtrResult.ok) {
|
|
72
|
+
try {
|
|
73
|
+
const parsed = JSON.parse(mtrResult.stdout);
|
|
74
|
+
return {
|
|
75
|
+
status: 'ok',
|
|
76
|
+
type: 'mtr',
|
|
77
|
+
report: parsed
|
|
78
|
+
};
|
|
79
|
+
} catch (error) {
|
|
80
|
+
// Fallback to plain text interpretation
|
|
81
|
+
return {
|
|
82
|
+
status: 'ok',
|
|
83
|
+
type: 'mtr',
|
|
84
|
+
stdout: mtrResult.stdout
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (await this.commandRunner.isAvailable('traceroute')) {
|
|
91
|
+
const tracerouteResult = await this.commandRunner.run(
|
|
92
|
+
'traceroute',
|
|
93
|
+
['-n', target.host],
|
|
94
|
+
{
|
|
95
|
+
timeout: this.config.traceroute.timeout
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
if (tracerouteResult.ok) {
|
|
100
|
+
return {
|
|
101
|
+
status: 'ok',
|
|
102
|
+
type: 'traceroute',
|
|
103
|
+
stdout: tracerouteResult.stdout
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
status: 'unavailable',
|
|
110
|
+
message: 'Neither mtr nor traceroute is available'
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
_parsePingOutput(text) {
|
|
115
|
+
const metrics = {
|
|
116
|
+
packetsTransmitted: null,
|
|
117
|
+
packetsReceived: null,
|
|
118
|
+
packetLoss: null,
|
|
119
|
+
min: null,
|
|
120
|
+
avg: null,
|
|
121
|
+
max: null,
|
|
122
|
+
stdDev: null
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const packetLine = text.split('\n').find((line) => line.includes('packets transmitted'));
|
|
126
|
+
if (packetLine) {
|
|
127
|
+
const match = packetLine.match(/(\d+)\s+packets transmitted,\s+(\d+)\s+received,.*?([\d.]+)% packet loss/);
|
|
128
|
+
if (match) {
|
|
129
|
+
metrics.packetsTransmitted = Number(match[1]);
|
|
130
|
+
metrics.packetsReceived = Number(match[2]);
|
|
131
|
+
metrics.packetLoss = Number(match[3]);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const statsLine = text.split('\n').find((line) => line.includes('min/avg/max'));
|
|
136
|
+
if (statsLine) {
|
|
137
|
+
const match = statsLine.match(/=\s*([\d.]+)\/([\d.]+)\/([\d.]+)\/([\d.]+)/);
|
|
138
|
+
if (match) {
|
|
139
|
+
metrics.min = Number(match[1]);
|
|
140
|
+
metrics.avg = Number(match[2]);
|
|
141
|
+
metrics.max = Number(match[3]);
|
|
142
|
+
metrics.stdDev = Number(match[4]);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return metrics;
|
|
147
|
+
}
|
|
148
|
+
}
|