mirror of
https://github.com/momenbasel/keyFinder.git
synced 2026-06-07 16:43:55 +02:00
v2.0.0: Complete rewrite - Manifest V3, enterprise-grade secret detection
- Migrated to Chrome Manifest V3 with service worker architecture - 80+ secret detection patterns covering AWS, GCP, Azure, GitHub, GitLab, Stripe, Slack, Discord, OpenAI, and 30+ other providers - 10 scanning surfaces: inline scripts, external scripts, meta tags, hidden inputs, data attributes, HTML comments, URL params, web storage, cookies, and network response interception - Shannon entropy analysis for detecting undocumented secret formats - MAIN world interceptor for XHR/fetch response scanning and window globals - Professional dark-theme UI with filtering, search, and CSV/JSON export - Zero dependencies - removed jQuery, Bootstrap, font-awesome, popper - Proper XSS-safe DOM rendering throughout - Badge counter on extension icon showing finding count - All frames scanning including iframes
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
const EVENT_NAME = "__kf_finding__";
|
||||
|
||||
function emit(data) {
|
||||
window.dispatchEvent(new CustomEvent(EVENT_NAME, { detail: data }));
|
||||
}
|
||||
|
||||
function shannonEntropy(str) {
|
||||
const len = str.length;
|
||||
if (len === 0) return 0;
|
||||
const freq = {};
|
||||
for (const ch of str) freq[ch] = (freq[ch] || 0) + 1;
|
||||
let entropy = 0;
|
||||
for (const ch in freq) {
|
||||
const p = freq[ch] / len;
|
||||
entropy -= p * Math.log2(p);
|
||||
}
|
||||
return entropy;
|
||||
}
|
||||
|
||||
function isHighEntropy(str) {
|
||||
return str.length >= 12 && shannonEntropy(str) > 3.5;
|
||||
}
|
||||
|
||||
const globalNames = [
|
||||
"API_KEY", "api_key", "apiKey", "apikey",
|
||||
"SECRET", "secret", "secretKey", "secret_key",
|
||||
"TOKEN", "token", "accessToken", "access_token",
|
||||
"AUTH_TOKEN", "authToken", "auth_token",
|
||||
"STRIPE_KEY", "stripeKey", "stripe_key",
|
||||
"FIREBASE_CONFIG", "firebaseConfig",
|
||||
"AWS_ACCESS_KEY", "awsAccessKey",
|
||||
"__NEXT_DATA__", "__NUXT__", "__APP_CONFIG__",
|
||||
"__ENV__", "__CONFIG__", "ENV", "CONFIG",
|
||||
];
|
||||
|
||||
for (const name of globalNames) {
|
||||
try {
|
||||
const val = window[name];
|
||||
if (val === undefined || val === null) continue;
|
||||
const str = typeof val === "object" ? JSON.stringify(val) : String(val);
|
||||
if (str.length < 8 || str === "[object Object]") continue;
|
||||
emit({
|
||||
match: `window.${name}=${str.substring(0, 200)}`,
|
||||
type: "window-global",
|
||||
patternName: "Exposed Global Variable",
|
||||
severity: "high",
|
||||
confidence: typeof val !== "object" && isHighEntropy(str.substring(0, 60)) ? "high" : "medium",
|
||||
provider: "JS Global Scan",
|
||||
isObject: typeof val === "object",
|
||||
rawText: typeof val === "object" ? str.substring(0, 5000) : null,
|
||||
});
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const origXhrOpen = XMLHttpRequest.prototype.open;
|
||||
const origXhrSend = XMLHttpRequest.prototype.send;
|
||||
|
||||
XMLHttpRequest.prototype.open = function (method, url) {
|
||||
this._kfUrl = url;
|
||||
return origXhrOpen.apply(this, arguments);
|
||||
};
|
||||
|
||||
XMLHttpRequest.prototype.send = function () {
|
||||
this.addEventListener("load", function () {
|
||||
try {
|
||||
const ct = this.getResponseHeader("content-type") || "";
|
||||
if (ct.includes("json") || ct.includes("javascript") || ct.includes("text")) {
|
||||
const body = this.responseText;
|
||||
if (body && body.length > 10 && body.length < 500000) {
|
||||
emit({
|
||||
type: "xhr-response",
|
||||
sourceUrl: String(this._kfUrl || ""),
|
||||
rawText: body,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
});
|
||||
return origXhrSend.apply(this, arguments);
|
||||
};
|
||||
|
||||
const origFetch = window.fetch;
|
||||
window.fetch = async function () {
|
||||
const response = await origFetch.apply(this, arguments);
|
||||
try {
|
||||
const url = typeof arguments[0] === "string" ? arguments[0] : arguments[0]?.url || "";
|
||||
const cloned = response.clone();
|
||||
const ct = cloned.headers.get("content-type") || "";
|
||||
if (ct.includes("json") || ct.includes("javascript") || ct.includes("text")) {
|
||||
cloned.text().then((body) => {
|
||||
if (body && body.length > 10 && body.length < 500000) {
|
||||
emit({
|
||||
type: "fetch-response",
|
||||
sourceUrl: String(url || ""),
|
||||
rawText: body,
|
||||
});
|
||||
}
|
||||
}).catch(() => {});
|
||||
}
|
||||
} catch {}
|
||||
return response;
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user