diff --git a/preview/preview-stub-empty.js b/preview/preview-stub-empty.js
new file mode 100644
index 0000000..157194c
--- /dev/null
+++ b/preview/preview-stub-empty.js
@@ -0,0 +1,46 @@
+// Preview-only stub for the chrome.* extension API with realistic sample data.
+// Loaded before the real popup.js / results.js in preview/*.html files.
+// NOT shipped — build.sh only copies the explicit SHARED_FILES list.
+(function () {
+ const NOW = Date.now();
+ const MIN = 60 * 1000;
+ const HOUR = 60 * MIN;
+ const DAY = 24 * HOUR;
+
+ const SAMPLE_FINDINGS = [
+ { id: "f1", severity: "critical", provider: "AWS", patternName: "AWS Access Key ID", match: "AKIAIOSFODNN7EXAMPLE", type: "inline-script", domain: "dashboard.acme.io", url: "https://dashboard.acme.io/assets/app.js", pageUrl: "https://dashboard.acme.io/", timestamp: NOW - 4 * MIN },
+ { id: "f2", severity: "critical", provider: "Stripe", patternName: "Stripe Live Secret Key", match: "sk_live_4eC39HqLyj…T1zdp7dc", type: "network-response", domain: "api.shopfront.dev", url: "https://api.shopfront.dev/v2/config", pageUrl: "https://shopfront.dev/", timestamp: NOW - 26 * MIN },
+ { id: "f3", severity: "critical", provider: "OpenAI", patternName: "OpenAI API Key", match: "sk-proj-abc123def456ghi789jkl", type: "web-storage", domain: "playground.internal.tools", url: "https://playground.internal.tools/", pageUrl: "https://playground.internal.tools/", timestamp: NOW - 2 * HOUR },
+ { id: "f4", severity: "high", provider: "GitHub", patternName: "GitHub Personal Access Token", match: "ghp_16C7e42F292c6912E7710c838347Ae178B4a", type: "html-comment", domain: "gitmirror.example.org", url: "https://gitmirror.example.org/login", pageUrl: "https://gitmirror.example.org/", timestamp: NOW - 3 * HOUR },
+ { id: "f5", severity: "high", provider: "Slack", patternName: "Slack Bot Token", match: "xoxb-17653672481-19874698323-pdLjLl…CwXgE", type: "inline-script", domain: "status.teamchat.app", url: "https://status.teamchat.app/embed.js", pageUrl: "https://status.teamchat.app/", timestamp: NOW - 5 * HOUR },
+ { id: "f6", severity: "high", provider: "MongoDB", patternName: "MongoDB Connection String", match: "mongodb+srv://admin:p4ssw0rd@cluster0.mongodb.net/prod", type: "data-attribute", domain: "metrics.saasly.co", url: "https://metrics.saasly.co/", pageUrl: "https://metrics.saasly.co/", timestamp: NOW - 9 * HOUR },
+ { id: "f7", severity: "high", provider: "DOM Scan", patternName: "Sensitive Meta Tag", match: "csrf-token=9f8e7d6c5b4a3210", type: "meta-tag", domain: "legacy.corpnet.com", url: "https://legacy.corpnet.com/index.html", pageUrl: "https://legacy.corpnet.com/", timestamp: NOW - 14 * HOUR },
+ { id: "f8", severity: "medium", provider: "Generic", patternName: "JSON Web Token (JWT)", match: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U", type: "url-param", domain: "auth.redirectlab.net", url: "https://auth.redirectlab.net/callback?token=eyJhbGciOi...", pageUrl: "https://redirectlab.net/", timestamp: NOW - 20 * HOUR },
+ { id: "f9", severity: "medium", provider: "DOM Scan", patternName: "Hidden Form Field", match: "session_id=a8f5f167f44f4964e6c998dee827110c", type: "hidden-input", domain: "shop.example.com", url: "https://shop.example.com/checkout", pageUrl: "https://shop.example.com/", timestamp: NOW - 1 * DAY },
+ { id: "f10", severity: "medium", provider: "URL Scan", patternName: "High-Entropy URL Parameter", match: "sig=4c4f6e6f2d9a4b8c8d7e6f5a4b3c2d1e", type: "url-param", domain: "cdn.medialab.io", url: "https://cdn.medialab.io/v/assets?sig=4c4f6e...", pageUrl: "https://medialab.io/", timestamp: NOW - 2 * DAY },
+ { id: "f11", severity: "medium", provider: "Web Storage", patternName: "localStorage Secret", match: "refresh_token=def502001a2b3c4d5e6f", type: "web-storage", domain: "app.notesync.dev", url: "https://app.notesync.dev/", pageUrl: "https://app.notesync.dev/", timestamp: NOW - 3 * DAY },
+ { id: "f12", severity: "low", provider: "Generic", patternName: "Keyword: client_id", match: "client_id=Iv1.8a61f9b3a7aba766", type: "script-src", domain: "login.oauthflow.app", url: "https://login.oauthflow.app/js/oauth.js?client_id=Iv1.8a61f9b3a7aba766", pageUrl: "https://login.oauthflow.app/", timestamp: NOW - 4 * DAY },
+ ];
+
+ const SAMPLE_KEYWORDS = [
+ "key", "api_key", "apikey", "api-key", "secret", "token",
+ "access_token", "auth", "credential", "password", "client_id", "client_secret",
+ ];
+
+ window.chrome = {
+ runtime: {
+ getManifest: () => ({ version: "2.2.0" }),
+ sendMessage: async (msg) => {
+ switch (msg && msg.type) {
+ case "getFindings": return { findings: [] };
+ case "getKeywords": return { keywords: SAMPLE_KEYWORDS.slice() };
+ case "addKeyword": return { ok: true };
+ case "removeKeyword": return { ok: true };
+ case "removeFinding": return { ok: true };
+ case "clearFindings": return { ok: true };
+ default: return {};
+ }
+ },
+ },
+ };
+})();
diff --git a/preview/preview-stub.js b/preview/preview-stub.js
index b8e17d2..8440831 100644
--- a/preview/preview-stub.js
+++ b/preview/preview-stub.js
@@ -29,7 +29,7 @@
window.chrome = {
runtime: {
- getManifest: () => ({ version: "2.1.1" }),
+ getManifest: () => ({ version: "2.2.0" }),
sendMessage: async (msg) => {
switch (msg && msg.type) {
case "getFindings": return { findings: SAMPLE_FINDINGS.slice() };
diff --git a/preview/results-preview-empty.html b/preview/results-preview-empty.html
new file mode 100644
index 0000000..55d32ad
--- /dev/null
+++ b/preview/results-preview-empty.html
@@ -0,0 +1,122 @@
+
+
+
+
+
+ KeyFinder — Findings Console
+
+
+
+
+
+
+
+
+
+ LATEST
+
+
+
+
+
+
+ findings.log
+
+
+
+
+
+ | # |
+ Severity |
+ Provider |
+ Pattern |
+ Match |
+ Type |
+ Domain |
+ Source |
+ Time |
+ |
+
+
+
+
+
+
+
No findings yet
+
KeyFinder passively scans every page you visit. Browse around — leaked keys, tokens and credentials will surface here.
+
tip · add custom keywords from the extension popup
+
+
+
+
+
+
+
+
+
diff --git a/preview/store-popup.html b/preview/store-popup.html
new file mode 100644
index 0000000..24cd9fb
--- /dev/null
+++ b/preview/store-popup.html
@@ -0,0 +1,78 @@
+
+
+
+
+ KeyFinder — store shot (popup)
+
+
+
+
+
+
+
KEYFINDER v2.2.0 — PASSIVE SCANNER
+
Catch leaked keys
while you browse.
+
KeyFinder passively scans every page for exposed API keys, tokens and credentials — across scripts, DOM, storage and network responses.
+
+ - 80+ detection patterns · entropy analysis
+ - 10 attack surfaces · SPA-aware rescanning
+ - local-only storage · zero config
+
+
+
+
+
+
+
+
diff --git a/preview/store.css b/preview/store.css
new file mode 100644
index 0000000..d15052d
--- /dev/null
+++ b/preview/store.css
@@ -0,0 +1,120 @@
+/* Store-listing composition backdrop (preview only, not shipped) */
+
+body.store {
+ width: auto;
+ min-width: 0;
+ background: #04060a;
+ overflow: hidden;
+}
+
+.stage {
+ position: relative;
+ z-index: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 72px;
+ width: 1280px;
+ height: 800px;
+ padding: 0 64px;
+}
+
+/* ambient brand glows */
+.stage::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ background:
+ radial-gradient(560px 420px at 12% 18%, rgba(245,165,36,.09) 0%, transparent 60%),
+ radial-gradient(620px 480px at 88% 82%, rgba(69,208,196,.07) 0%, transparent 60%);
+ pointer-events: none;
+}
+
+.copy { max-width: 560px; }
+
+.kicker {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 10px;
+ font-weight: 700;
+ letter-spacing: .22em;
+ color: var(--green);
+ border: 1px solid rgba(61,220,151,.32);
+ background: rgba(61,220,151,.06);
+ padding: 5px 9px;
+ border-radius: 2px;
+ margin-bottom: 26px;
+}
+.kicker::before {
+ content: "";
+ width: 5px;
+ height: 5px;
+ border-radius: 50%;
+ background: var(--green);
+ box-shadow: 0 0 8px var(--green);
+}
+
+.copy h1 {
+ font-size: 46px;
+ font-weight: 700;
+ line-height: 1.08;
+ letter-spacing: .01em;
+ color: #fff;
+ margin-bottom: 20px;
+}
+.copy h1 em {
+ font-style: normal;
+ color: var(--amber);
+}
+
+.copy p {
+ font-size: 13px;
+ line-height: 1.7;
+ color: var(--text-dim);
+ margin-bottom: 26px;
+ max-width: 440px;
+}
+
+.spec {
+ list-style: none;
+ display: flex;
+ flex-direction: column;
+ gap: 9px;
+}
+.spec li {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ font-size: 11px;
+ letter-spacing: .08em;
+ color: var(--text-faint);
+ text-transform: uppercase;
+}
+.spec li::before {
+ content: "▸";
+ color: var(--teal);
+ font-size: 10px;
+}
+
+/* floating popup frame */
+.popup-frame {
+ position: relative;
+ flex: none;
+ width: 372px;
+ border: 1px solid var(--line-strong);
+ border-radius: 8px;
+ overflow: hidden;
+ box-shadow:
+ 0 0 0 1px rgba(245,165,36,.08),
+ 0 24px 80px rgba(0,0,0,.65),
+ 0 0 120px rgba(245,165,36,.05);
+}
+.popup-frame::before {
+ content: "";
+ position: absolute;
+ top: 0; left: 0; right: 0;
+ height: 1px;
+ background: linear-gradient(90deg, transparent, rgba(245,165,36,.6), transparent);
+ z-index: 2;
+}
diff --git a/store/store-1-dashboard.png b/store/store-1-dashboard.png
new file mode 100644
index 0000000..df6b482
Binary files /dev/null and b/store/store-1-dashboard.png differ
diff --git a/store/store-2-popup.png b/store/store-2-popup.png
new file mode 100644
index 0000000..29e2df0
Binary files /dev/null and b/store/store-2-popup.png differ
diff --git a/store/store-3-empty.png b/store/store-3-empty.png
new file mode 100644
index 0000000..35c3d22
Binary files /dev/null and b/store/store-3-empty.png differ