chrome-tools 1.1.1 → 1.1.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/dist/bin/cli.js +1 -1
- package/dist/src/core/browsers.d.ts +69 -0
- package/dist/src/core/browsers.js +1 -0
- package/dist/src/core/chrome.d.ts +27 -1
- package/dist/src/core/chrome.js +1 -1
- package/dist/src/core/keychain.d.ts +14 -6
- package/dist/src/core/keychain.js +1 -1
- package/dist/src/core/platform/darwin.d.ts +4 -2
- package/dist/src/core/platform/darwin.js +1 -1
- package/dist/src/core/platform/index.d.ts +1 -0
- package/dist/src/core/platform/index.js +1 -1
- package/dist/src/core/platform/linux.d.ts +3 -3
- package/dist/src/core/platform/linux.js +1 -1
- package/dist/src/core/platform/win32.js +1 -1
- package/dist/src/index.d.ts +3 -2
- package/dist/src/index.js +1 -1
- package/dist/src/modules/autofill.d.ts +6 -3
- package/dist/src/modules/autofill.js +1 -1
- package/dist/src/modules/bookmarks.d.ts +2 -1
- package/dist/src/modules/bookmarks.js +1 -1
- package/dist/src/modules/cookies.d.ts +2 -1
- package/dist/src/modules/cookies.js +1 -1
- package/dist/src/modules/downloads.d.ts +2 -1
- package/dist/src/modules/downloads.js +1 -1
- package/dist/src/modules/extensions.d.ts +2 -1
- package/dist/src/modules/extensions.js +1 -1
- package/dist/src/modules/history.d.ts +4 -2
- package/dist/src/modules/history.js +1 -1
- package/dist/src/modules/passwords.d.ts +2 -1
- package/dist/src/modules/passwords.js +1 -1
- package/dist/src/modules/search-engines.d.ts +2 -1
- package/dist/src/modules/search-engines.js +1 -1
- package/dist/src/modules/top-sites.d.ts +2 -1
- package/dist/src/modules/top-sites.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 t}from"node:path";import{Command as s}from"commander";import i from"chalk";import n from"cli-table3";import{discoverProfiles as r,getProfileMetadata as l,getProfileStates as a,getDefaultChromeDir as c,getDirectorySize as m}from"../src/core/chrome.js";import{getPasswords as p}from"../src/modules/passwords.js";import{getBookmarks as d}from"../src/modules/bookmarks.js";import{getHistory as h,getSearchTerms as f}from"../src/modules/history.js";import{getCookies as u}from"../src/modules/cookies.js";import{getAutofillEntries as g,getCreditCards as w,getAddresses as y}from"../src/modules/autofill.js";import{getDownloads as b}from"../src/modules/downloads.js";import{getExtensions as S}from"../src/modules/extensions.js";import{getSearchEngines as k}from"../src/modules/search-engines.js";import{getTopSites as P}from"../src/modules/top-sites.js";const j=function(){const s={...process.env},i=t(process.cwd(),".env");if(e(i))try{const e=o(i,"utf8");for(const o of e.split(/\r?\n/)){const e=o.trim();if(!e||e.startsWith("#"))continue;const t=e.indexOf("=");if(-1!==t){const o=e.substring(0,t).trim();let i=e.substring(t+1).trim();(i.startsWith('"')&&i.endsWith('"')||i.startsWith("'")&&i.endsWith("'"))&&(i=i.slice(1,-1)),s[o]=i}}}catch{}return s}(),L=j.PROFILE_PATH||c(),v=j.PROFILE_NAME||void 0;function W(o){console.log(JSON.stringify(o,null,2))}function D(o,e){return new n({head:o.map(o=>i.bold.white(o)),colWidths:e,wordWrap:!0,style:{border:["grey"],head:[]}})}function N(o,e){0===e?console.log(i.yellow(`\n⚠ No ${o} found.\n`)):console.log(i.green(`\n✔ Found ${e} ${o}.\n`))}function C(o){return o.option("--profile-path <path>","Custom Chrome Profile Path",L).option("--profile <name>","Target a specific profile",v).option("--json","Output as JSON")}function U(o){return o.option("--search <term>","Filter results (case-insensitive)")}function x(o,e=100){return o.option("--limit <n>","Limit number of results",String(e))}const G=new s,O=JSON.parse(o(new URL("../../package.json",import.meta.url),"utf8"));G.name("chrome-tools").description(i.bold.cyan("Chrome Data Toolkit")+i.dim(" — Extract passwords, bookmarks, history, cookies, and more from Chrome on macOS, Windows, and Linux")).version(O.version).option("--passwords","Shortcut: list saved passwords").option("--bookmarks","Shortcut: list bookmarks").option("--profiles","Shortcut: list profiles").option("--size","Show each profile's size on disk when listing profiles (slower)").option("--profile-path <path>","Custom Chrome Profile Path",L).option("--profile <name>","Target a specific profile",v).option("--json","Output as JSON").option("--search <term>","Filter results (for shortcut flags)").option("--limit <n>","Limit results (for shortcut flags)").action(o=>{o.passwords?B(o):o.bookmarks?F(o):E(o)});const $=G.command("profiles").description("List Chrome profiles");function E(o){const e=r(o.profilePath),t=l(o.profilePath),s=Boolean(o.size);if(o.json){const o=new Map(t.map(o=>[o.directory,o]));return W(e.map(e=>({...e,...o.get(e.name)||{},...s?{sizeBytes:m(e.path)}:{}})))}if(0===e.length)return void console.log(i.yellow("\n⚠ No Chrome profiles found.\n"));const n=new Map(t.map(o=>[o.directory,o]));console.log(i.bold(`\n📁 Discovered ${e.length} profile(s):\n`));const a=D(s?["Directory","Display Name","Email","Size","Path"]:["Directory","Display Name","Email","Path"],s?[16,22,30,10,42]:[16,22,30,50]);for(const o of e){const e=n.get(o.name)||{},t=[i.cyan(o.name),i.white(e.name||o.name),i.dim(e.email||"—")];s&&t.push(i.yellow(R(m(o.path)))),t.push(i.dim(o.path)),a.push(t)}console.log(a.toString()),N("profile(s)",e.length)}C($).option("--size","Compute and show each profile's size on disk (slower)").action((o,e)=>{E(e.optsWithGlobals())}),$.command("states").description("Show detailed profile states with available data files").option("--profile-path <path>","Custom Chrome Profile Path",L).option("--json","Output as JSON").action((o,e)=>function(o){const e=a(o.profilePath);if(o.json)return W(e);if(0===e.length)return void console.log(i.yellow("\n⚠ No Chrome profiles found.\n"));console.log(i.bold(`\n📊 Profile States (${e.length} profile(s)):\n`));for(const o of e){console.log(i.bold.cyan(` ${o.displayName}`)+i.dim(` (${o.directory})`)),o.email&&console.log(i.dim(` Email: ${o.email}`)),console.log(i.dim(` Path: ${o.path}`));const e=o.dataFiles,t=Object.entries(e).filter(([,o])=>o).map(([o])=>o),s=Object.entries(e).filter(([,o])=>!o).map(([o])=>o);console.log(` Available: ${t.map(o=>i.green(o)).join(", ")||i.dim("none")}`),s.length>0&&console.log(` Missing: ${s.map(o=>i.dim(o)).join(", ")}`),console.log()}}(e.optsWithGlobals()));function B(o){const e=p({chromeDir:o.profilePath,profile:o.profile,search:o.search});if(o.json)return W(e);if(0===e.length)return N("passwords",0);console.log(i.bold("\n🔑 Saved Passwords:\n"));const t=new Set(e.map(o=>o.profile)).size>1,s=D(t?["Profile","URL","Username","Password","Last Used"]:["URL","Username","Password","Last Used"],t?[12,40,24,22,20]:[45,28,25,22]);for(const o of e){const e=[i.cyan(o.url),i.green(o.username||"<empty>"),i.yellow(o.password||"<empty>"),i.dim(o.lastUsed)];t&&e.unshift(i.magenta(o.profile)),s.push(e)}console.log(s.toString()),N("password(s)",e.length)}U(C(G.command("passwords").description("List saved passwords"))).action((o,e)=>B(e.optsWithGlobals()));function F(o){const e=d({chromeDir:o.profilePath,profile:o.profile,folder:o.folder,search:o.search});if(o.json)return W(e);if(0===e.length)return N("bookmarks",0);console.log(i.bold("\n📚 Bookmarks:\n"));const t=D(["Title","URL","Folder","Date Added"],[30,50,25,20]);for(const o of e)t.push([i.white(o.title),i.cyan(o.url),i.dim(o.folder||"/"),i.dim(o.dateAdded)]);console.log(t.toString()),N("bookmark(s)",e.length)}U(C(G.command("bookmarks").description("List bookmarks"))).option("--folder <name>","Filter by folder name").action((o,e)=>F(e.optsWithGlobals()));U(x(C(G.command("history").description("List browsing history")))).action((o,e)=>{const t=e.optsWithGlobals(),s=h({chromeDir:t.profilePath,profile:t.profile,search:t.search,limit:parseInt(t.limit,10)||100});if(t.json)return W(s);if(0===s.length)return N("history entries",0);console.log(i.bold("\n🕐 Browsing History:\n"));const n=D(["URL","Title","Visits","Last Visit"],[50,30,8,22]);for(const o of s)n.push([i.cyan(o.url),i.white(o.title||"—"),i.yellow(String(o.visitCount)),i.dim(o.lastVisit)]);console.log(n.toString()),N("history entry(ies)",s.length)});x(C(G.command("search-terms").description("List search terms from history"))).action((o,e)=>{const t=e.optsWithGlobals(),s=f({chromeDir:t.profilePath,profile:t.profile,limit:parseInt(t.limit,10)||100});if(t.json)return W(s);if(0===s.length)return N("search terms",0);console.log(i.bold("\n🔍 Search Terms:\n"));const n=D(["Term","URL"],[40,80]);for(const o of s)n.push([i.white(o.term),i.dim(o.url)]);console.log(n.toString()),N("search term(s)",s.length)});x(C(G.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)=>{const t=e.optsWithGlobals(),s=u({chromeDir:t.profilePath,profile:t.profile,domain:t.domain,url:t.url,decrypt:!1!==t.decrypt,limit:parseInt(t.limit,10)||100});if(t.json)return W(s);if(0===s.length)return N("cookies",0);console.log(i.bold("\n🍪 Cookies:\n"));const n=D(["Domain","Name","Value","Secure","Expires"],[30,25,30,8,22]);for(const o of s)n.push([i.cyan(o.domain),i.white(o.name),i.dim(o.value?o.value.substring(0,50)+(o.value.length>50?"…":""):"—"),o.secure?i.green("✓"):i.dim("✗"),i.dim(o.expires)]);console.log(n.toString()),N("cookie(s)",s.length)});U(x(C(G.command("autofill").description("List autofill entries")))).action((o,e)=>{const t=e.optsWithGlobals(),s=g({chromeDir:t.profilePath,profile:t.profile,search:t.search,limit:parseInt(t.limit,10)||100});if(t.json)return W(s);if(0===s.length)return N("autofill entries",0);console.log(i.bold("\n📝 Autofill Entries:\n"));const n=D(["Field Name","Value","Count","Last Used"],[25,40,8,22]);for(const o of s)n.push([i.cyan(o.name),i.white(o.value),i.yellow(String(o.count)),i.dim(o.dateLastUsed)]);console.log(n.toString()),N("autofill entry(ies)",s.length)});C(G.command("credit-cards").description("List saved credit cards")).action((o,e)=>{const t=e.optsWithGlobals(),s=w({chromeDir:t.profilePath,profile:t.profile});if(t.json)return W(s);if(0===s.length)return N("credit cards",0);console.log(i.bold("\n💳 Saved Credit Cards:\n"));const n=D(["Name on Card","Card Number","Expires","Nickname"],[25,25,14,20]);for(const o of s)n.push([i.white(o.nameOnCard||"—"),i.yellow(o.cardNumber||"—"),i.dim(`${o.expirationMonth}/${o.expirationYear}`),i.dim(o.nickname||"—")]);console.log(n.toString()),N("credit card(s)",s.length)});C(G.command("addresses").description("List saved addresses")).action((o,e)=>{const t=e.optsWithGlobals(),s=y({chromeDir:t.profilePath,profile:t.profile});if(t.json)return W(s);if(0===s.length)return N("addresses",0);console.log(i.bold("\n🏠 Saved Addresses:\n"));const n=D(["Name","Email","Phone","Address","City/State/Zip"],[20,25,16,30,25]);for(const o of s)n.push([i.white(o.fullName||"—"),i.cyan(o.email||"—"),i.dim(o.phone||"—"),i.white(o.streetAddress||"—"),i.dim([o.city,o.state,o.zipcode].filter(Boolean).join(", ")||"—")]);console.log(n.toString()),N("address(es)",s.length)});U(x(C(G.command("downloads").description("List download history")))).action((o,e)=>{const t=e.optsWithGlobals(),s=b({chromeDir:t.profilePath,profile:t.profile,search:t.search,limit:parseInt(t.limit,10)||100});if(t.json)return W(s);if(0===s.length)return N("downloads",0);console.log(i.bold("\n⬇️ Download History:\n"));const n=D(["File","URL","Size","State","Date"],[35,40,12,12,20]);for(const o of s){const e=o.targetPath?o.targetPath.split("/").pop():"—",t=o.totalBytes>0?R(o.totalBytes):"—";n.push([i.white(e||""),i.cyan(o.url?o.url.substring(0,60):"—"),i.dim(t),"complete"===o.state?i.green(o.state):i.yellow(o.state),i.dim(o.startTime)])}console.log(n.toString()),N("download(s)",s.length)});U(C(G.command("extensions").description("List installed extensions"))).action((o,e)=>{const t=e.optsWithGlobals(),s=S({chromeDir:t.profilePath,profile:t.profile,search:t.search});if(t.json)return W(s);if(0===s.length)return N("extensions",0);console.log(i.bold("\n🧩 Installed Extensions:\n"));const n=D(["Name","Version","ID","Enabled","Webstore"],[30,12,36,9,9]);for(const o of s)n.push([i.white(o.name),i.dim(o.version),i.dim(o.id),o.enabled?i.green("✓"):i.red("✗"),o.fromWebstore?i.green("✓"):i.dim("✗")]);console.log(n.toString()),N("extension(s)",s.length)});C(G.command("search-engines").description("List configured search engines")).action((o,e)=>{const t=e.optsWithGlobals(),s=k({chromeDir:t.profilePath,profile:t.profile});if(t.json)return W(s);if(0===s.length)return N("search engines",0);console.log(i.bold("\n🔎 Search Engines:\n"));const n=D(["Name","Keyword","URL","Active"],[20,16,60,8]);for(const o of s)n.push([i.white(o.name),i.cyan(o.keyword),i.dim(o.url?o.url.substring(0,80):"—"),o.isActive?i.green("✓"):i.dim("✗")]);console.log(n.toString()),N("search engine(s)",s.length)});function R(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]}`}C(G.command("top-sites").description("List top sites")).action((o,e)=>{const t=e.optsWithGlobals(),s=P({chromeDir:t.profilePath,profile:t.profile});if(t.json)return W(s);if(0===s.length)return N("top sites",0);console.log(i.bold("\n⭐ Top Sites:\n"));const n=D(["Rank","Title","URL"],[8,30,80]);for(const o of s)n.push([i.yellow(String(o.rank+1)),i.white(o.title||"—"),i.cyan(o.url)]);console.log(n.toString()),N("top site(s)",s.length)}),G.parse();
|
|
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}from"../src/core/platform/index.js";import{getPasswords as f}from"../src/modules/passwords.js";import{getBookmarks as h}from"../src/modules/bookmarks.js";import{getHistory as u,getSearchTerms as g}from"../src/modules/history.js";import{getCookies as w}from"../src/modules/cookies.js";import{getAutofillEntries as b,getCreditCards as y,getAddresses as S}from"../src/modules/autofill.js";import{getDownloads as v}from"../src/modules/downloads.js";import{getExtensions as k}from"../src/modules/extensions.js";import{getSearchEngines as L}from"../src/modules/search-engines.js";import{getTopSites as P}from"../src/modules/top-sites.js";const D=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}(),j=D.PROFILE_PATH||void 0,B=D.PROFILE_NAME||void 0,C=D.BROWSER||D.CHROME_BROWSER||void 0;function N(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 U(o,e){0===e?console.log(s.yellow(`\n⚠ No ${o} found.\n`)):console.log(s.green(`\n✔ Found ${e} ${o}.\n`))}let $=null;function E(o,e){return $?($.push({browser:p().name,results:e}),!0):!!o.json&&(N(e),!0)}function O(o){const e=o.optsWithGlobals(),r=Boolean(e.browser)||Boolean(e.allBrowsers)||Boolean(C);return e.browser=e.browser||C||void 0,e.profilePath||(e.profilePath=r?void 0:j),e.profile||(e.profile=r?void 0:B),e}function x(o,e){e.allBrowsers?function(o,e){const r=d();if(0===r.length)return e.json?N([]):void console.log(s.yellow("\n⚠ No supported Chromium browsers found on this machine.\n"));const t={...e,profilePath:void 0};if(e.json){$=[];try{for(const e of r){m(e.browser.id);try{o(t)}catch(o){$.push({browser:e.browser.name,results:{error:o?.message??String(o)}})}}N($)}finally{$=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){console.log(s.red(` ⚠ Skipped: ${o?.message??String(o)}`))}}}(o,e):(m(e.browser),o(e))}function F(o){const e=d({includeMissing:!0});if(o.json)return N(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 W(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 M(o){return o.option("--search <term>","Filter results (case-insensitive)")}function T(o,e=100){return o.option("--limit <n>","Limit number of results",String(e))}const A=new t,I=JSON.parse(o(new URL("../../package.json",import.meta.url),"utf8"));A.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(I.version).option("--passwords","Shortcut: list saved passwords").option("--bookmarks","Shortcut: list bookmarks").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=O(e);r.browsers?F(r):r.passwords?x(H,r):r.bookmarks?x(G,r):x(V,r)});const z=A.command("profiles").description("List Chrome profiles");W(z).option("--size","Compute and show each profile's size on disk (slower)").action((o,e)=>{x(V,O(e))});W(z.command("states").description("Show detailed profile states with available data files")).action((o,e)=>x(J,O(e)));function V(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 E(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(_(c(o.path)))),r.push(s.dim(o.path)),a.push(r)}console.log(a.toString()),U("profile(s)",e.length)}function J(o){const e=a(o.profilePath);if(o.json)E(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"))}A.command("browsers").description("List supported browsers and detect which are installed").option("--json","Output as JSON").action((o,e)=>F(O(e)));function H(o){const e=f({chromeDir:o.profilePath,profile:o.profile,search:o.search});if(E(o,e))return;if(0===e.length)return U("passwords",0);console.log(s.bold("\n🔑 Saved Passwords:\n"));const r=new Set(e.map(o=>o.profile)).size>1,t=R(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()),U("password(s)",e.length)}M(W(A.command("passwords").description("List saved passwords"))).action((o,e)=>x(H,O(e)));function G(o){const e=h({chromeDir:o.profilePath,profile:o.profile,folder:o.folder,search:o.search});if(E(o,e))return;if(0===e.length)return U("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()),U("bookmark(s)",e.length)}M(W(A.command("bookmarks").description("List bookmarks"))).option("--folder <name>","Filter by folder name").action((o,e)=>x(G,O(e)));M(T(W(A.command("history").description("List browsing history")))).action((o,e)=>x(o=>{const e=u({chromeDir:o.profilePath,profile:o.profile,search:o.search,limit:parseInt(o.limit,10)||100});if(E(o,e))return;if(0===e.length)return U("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()),U("history entry(ies)",e.length)},O(e)));T(W(A.command("search-terms").description("List search terms from history"))).action((o,e)=>x(o=>{const e=g({chromeDir:o.profilePath,profile:o.profile,limit:parseInt(o.limit,10)||100});if(E(o,e))return;if(0===e.length)return U("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()),U("search term(s)",e.length)},O(e)));T(W(A.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)=>x(o=>{const e=w({chromeDir:o.profilePath,profile:o.profile,domain:o.domain,url:o.url,decrypt:!1!==o.decrypt,limit:parseInt(o.limit,10)||100});if(E(o,e))return;if(0===e.length)return U("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()),U("cookie(s)",e.length)},O(e)));M(T(W(A.command("autofill").description("List autofill entries")))).action((o,e)=>x(o=>{const e=b({chromeDir:o.profilePath,profile:o.profile,search:o.search,limit:parseInt(o.limit,10)||100});if(E(o,e))return;if(0===e.length)return U("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()),U("autofill entry(ies)",e.length)},O(e)));W(A.command("credit-cards").description("List saved credit cards")).action((o,e)=>x(o=>{const e=y({chromeDir:o.profilePath,profile:o.profile});if(E(o,e))return;if(0===e.length)return U("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()),U("credit card(s)",e.length)},O(e)));W(A.command("addresses").description("List saved addresses")).action((o,e)=>x(o=>{const e=S({chromeDir:o.profilePath,profile:o.profile});if(E(o,e))return;if(0===e.length)return U("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()),U("address(es)",e.length)},O(e)));M(T(W(A.command("downloads").description("List download history")))).action((o,e)=>x(o=>{const e=v({chromeDir:o.profilePath,profile:o.profile,search:o.search,limit:parseInt(o.limit,10)||100});if(E(o,e))return;if(0===e.length)return U("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?_(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()),U("download(s)",e.length)},O(e)));M(W(A.command("extensions").description("List installed extensions"))).action((o,e)=>x(o=>{const e=k({chromeDir:o.profilePath,profile:o.profile,search:o.search});if(E(o,e))return;if(0===e.length)return U("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()),U("extension(s)",e.length)},O(e)));W(A.command("search-engines").description("List configured search engines")).action((o,e)=>x(o=>{const e=L({chromeDir:o.profilePath,profile:o.profile});if(E(o,e))return;if(0===e.length)return U("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()),U("search engine(s)",e.length)},O(e)));function _(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]}`}W(A.command("top-sites").description("List top sites")).action((o,e)=>x(o=>{const e=P({chromeDir:o.profilePath,profile:o.profile});if(E(o,e))return;if(0===e.length)return U("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()),U("top site(s)",e.length)},O(e)));try{A.parse()}catch(o){console.error(s.red(`\n✖ ${o?.message??String(o)}\n`)),process.env.DEBUG&&console.error(o),process.exit(1)}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chromium-browser registry — the single source of truth for what differs
|
|
3
|
+
* between Chromium-based browsers (Chrome, Edge, Brave, Comet, …).
|
|
4
|
+
*
|
|
5
|
+
* Every supported browser is a Chromium fork, so the *decryption* is identical
|
|
6
|
+
* per OS (AES-128-CBC + PBKDF2 on macOS/Linux, AES-256-GCM + DPAPI on Windows).
|
|
7
|
+
* Only two things vary by browser:
|
|
8
|
+
* 1. the default profile data directory, and
|
|
9
|
+
* 2. the OS-keystore "Safe Storage" label (macOS Keychain service name /
|
|
10
|
+
* Linux Secret Service application).
|
|
11
|
+
*
|
|
12
|
+
* "Browser" is therefore a small dimension orthogonal to "platform": the
|
|
13
|
+
* platform backends (darwin/win32/linux) read the active browser's dir + label
|
|
14
|
+
* from here and stay otherwise unchanged.
|
|
15
|
+
*/
|
|
16
|
+
export type BrowserId = "chrome" | "chrome-beta" | "chrome-canary" | "chromium" | "edge" | "brave" | "comet" | "vivaldi" | "opera";
|
|
17
|
+
/** Subpaths to a browser's User Data dir, relative to each OS's app-data base. */
|
|
18
|
+
export interface BrowserDirs {
|
|
19
|
+
/** Under `~/Library/Application Support`. */
|
|
20
|
+
darwin: string;
|
|
21
|
+
/** Under `%LOCALAPPDATA%`. Includes the trailing `User Data` segment. */
|
|
22
|
+
win32: string;
|
|
23
|
+
/** Under `$XDG_CONFIG_HOME` (or `~/.config`). */
|
|
24
|
+
linux: string;
|
|
25
|
+
}
|
|
26
|
+
export interface BrowserDescriptor {
|
|
27
|
+
/** Stable identifier used on the CLI (`--browser <id>`) and in the API. */
|
|
28
|
+
readonly id: BrowserId;
|
|
29
|
+
/** Human-readable display name, e.g. "Microsoft Edge". */
|
|
30
|
+
readonly name: string;
|
|
31
|
+
/** Extra names accepted on the CLI (case-insensitive), e.g. "msedge". */
|
|
32
|
+
readonly aliases?: readonly string[];
|
|
33
|
+
/**
|
|
34
|
+
* macOS Keychain service name (a.k.a. the "<Browser> Safe Storage" item) used
|
|
35
|
+
* as the basis for the AES key. Also reused as a human label in messages.
|
|
36
|
+
*/
|
|
37
|
+
readonly safeStorageLabel: string;
|
|
38
|
+
/** Linux Secret Service `application` attribute used by `secret-tool lookup`. */
|
|
39
|
+
readonly linuxSecretApp?: string;
|
|
40
|
+
/** Per-OS User Data subpaths. */
|
|
41
|
+
readonly dirs: BrowserDirs;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Supported browsers. macOS paths/labels are verified; a few Windows/Linux
|
|
45
|
+
* paths for niche browsers (Comet, Opera) are best-effort and easy to correct —
|
|
46
|
+
* each is a single edit here, with no other code change required.
|
|
47
|
+
*/
|
|
48
|
+
export declare const BROWSERS: Record<BrowserId, BrowserDescriptor>;
|
|
49
|
+
/** All supported browsers, in a stable display order. */
|
|
50
|
+
export declare function listBrowsers(): BrowserDescriptor[];
|
|
51
|
+
/**
|
|
52
|
+
* Resolves a browser by id, display name, or alias (case-insensitive).
|
|
53
|
+
* @returns The descriptor, or `null` if unrecognized.
|
|
54
|
+
*/
|
|
55
|
+
export declare function resolveBrowser(idOrName: string): BrowserDescriptor | null;
|
|
56
|
+
/** The browser currently targeted by dir/key lookups. Defaults to Chrome. */
|
|
57
|
+
export declare function getActiveBrowser(): BrowserDescriptor;
|
|
58
|
+
/**
|
|
59
|
+
* Sets the active browser for subsequent dir/key lookups.
|
|
60
|
+
*
|
|
61
|
+
* Caches in the platform backends are keyed by their real inputs (Keychain
|
|
62
|
+
* service label, chromeDir, Linux secret app), so switching browsers never
|
|
63
|
+
* returns a stale key — no cache flush is needed here.
|
|
64
|
+
*
|
|
65
|
+
* @param idOrName - Browser id/name/alias. Empty/undefined resets to Chrome.
|
|
66
|
+
* @returns The now-active descriptor.
|
|
67
|
+
* @throws If `idOrName` is non-empty but unrecognized.
|
|
68
|
+
*/
|
|
69
|
+
export declare function setActiveBrowser(idOrName?: string): BrowserDescriptor;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const BROWSERS={chrome:{id:"chrome",name:"Google Chrome",aliases:["google-chrome","google chrome"],safeStorageLabel:"Chrome Safe Storage",linuxSecretApp:"chrome",dirs:{darwin:"Google/Chrome",win32:"Google/Chrome/User Data",linux:"google-chrome"}},"chrome-beta":{id:"chrome-beta",name:"Google Chrome Beta",safeStorageLabel:"Chrome Safe Storage",linuxSecretApp:"chrome",dirs:{darwin:"Google/Chrome Beta",win32:"Google/Chrome Beta/User Data",linux:"google-chrome-beta"}},"chrome-canary":{id:"chrome-canary",name:"Google Chrome Canary",aliases:["canary"],safeStorageLabel:"Chrome Safe Storage",linuxSecretApp:"chrome",dirs:{darwin:"Google/Chrome Canary",win32:"Google/Chrome SxS/User Data",linux:"google-chrome-unstable"}},chromium:{id:"chromium",name:"Chromium",safeStorageLabel:"Chromium Safe Storage",linuxSecretApp:"chromium",dirs:{darwin:"Chromium",win32:"Chromium/User Data",linux:"chromium"}},edge:{id:"edge",name:"Microsoft Edge",aliases:["msedge","microsoft-edge","microsoft edge"],safeStorageLabel:"Microsoft Edge Safe Storage",linuxSecretApp:"chromium",dirs:{darwin:"Microsoft Edge",win32:"Microsoft/Edge/User Data",linux:"microsoft-edge"}},brave:{id:"brave",name:"Brave",aliases:["brave-browser"],safeStorageLabel:"Brave Safe Storage",linuxSecretApp:"chrome",dirs:{darwin:"BraveSoftware/Brave-Browser",win32:"BraveSoftware/Brave-Browser/User Data",linux:"BraveSoftware/Brave-Browser"}},comet:{id:"comet",name:"Comet",aliases:["perplexity","perplexity-comet"],safeStorageLabel:"Comet Safe Storage",linuxSecretApp:"chromium",dirs:{darwin:"Comet",win32:"Perplexity/Comet/User Data",linux:"comet"}},vivaldi:{id:"vivaldi",name:"Vivaldi",safeStorageLabel:"Chrome Safe Storage",linuxSecretApp:"chrome",dirs:{darwin:"Vivaldi",win32:"Vivaldi/User Data",linux:"vivaldi"}},opera:{id:"opera",name:"Opera",safeStorageLabel:"Opera Safe Storage",linuxSecretApp:"chromium",dirs:{darwin:"com.operasoftware.Opera",win32:"Opera Software/Opera Stable",linux:"opera"}}};const e="chrome";export function listBrowsers(){return Object.values(BROWSERS)}export function resolveBrowser(e){if(!e)return null;const r=e.trim().toLowerCase();for(const e of listBrowsers()){if(e.id.toLowerCase()===r)return e;if(e.name.toLowerCase()===r)return e;if(e.aliases?.some(e=>e.toLowerCase()===r))return e}return null}let r=BROWSERS[e];export function getActiveBrowser(){return r}export function setActiveBrowser(o){if(!o||!o.trim())return r=BROWSERS[e],r;const a=resolveBrowser(o);if(!a){const e=listBrowsers().map(e=>e.id).join(", ");throw new Error(`Unknown browser "${o}". Supported: ${e}.`)}return r=a,r}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type BrowserDescriptor } from "./browsers.js";
|
|
1
2
|
export interface DiscoveredProfile {
|
|
2
3
|
name: string;
|
|
3
4
|
path: string;
|
|
@@ -28,6 +29,8 @@ export interface ResolveProfilesOptions {
|
|
|
28
29
|
profilePath?: string;
|
|
29
30
|
chromeDir?: string;
|
|
30
31
|
profile?: string;
|
|
32
|
+
/** Target a specific browser (id/name/alias). Applied only when provided. */
|
|
33
|
+
browser?: string;
|
|
31
34
|
}
|
|
32
35
|
/**
|
|
33
36
|
* Returns the default Chrome User Data directory for the current OS.
|
|
@@ -35,6 +38,29 @@ export interface ResolveProfilesOptions {
|
|
|
35
38
|
* @returns The absolute path to Chrome's User Data.
|
|
36
39
|
*/
|
|
37
40
|
export declare function getDefaultChromeDir(): string;
|
|
41
|
+
export interface InstalledBrowser {
|
|
42
|
+
/** Registry descriptor for the detected browser. */
|
|
43
|
+
browser: BrowserDescriptor;
|
|
44
|
+
/** Absolute path to this browser's default User Data directory. */
|
|
45
|
+
chromeDir: string;
|
|
46
|
+
/** Whether the data directory exists on disk. */
|
|
47
|
+
installed: boolean;
|
|
48
|
+
/** Number of discovered profiles (0 when not installed). */
|
|
49
|
+
profileCount: number;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Probes every supported browser for an on-disk profile directory.
|
|
53
|
+
*
|
|
54
|
+
* Temporarily switches the active browser to resolve each one's default dir,
|
|
55
|
+
* then restores the previously-active browser before returning.
|
|
56
|
+
*
|
|
57
|
+
* @param options.includeMissing - When true, also returns browsers with no data
|
|
58
|
+
* directory (installed: false). Defaults to false (installed browsers only).
|
|
59
|
+
* @returns One entry per matching browser.
|
|
60
|
+
*/
|
|
61
|
+
export declare function detectInstalledBrowsers({ includeMissing }?: {
|
|
62
|
+
includeMissing?: boolean;
|
|
63
|
+
}): InstalledBrowser[];
|
|
38
64
|
/**
|
|
39
65
|
* Scans the Chrome User Data directory for profile subdirectories.
|
|
40
66
|
* A directory is considered a profile if it contains at least one known data file.
|
|
@@ -84,4 +110,4 @@ export declare function resolveProfilePath(chromeDir: string, profileName: strin
|
|
|
84
110
|
* @param options - Resolution options.
|
|
85
111
|
* @returns Array of targeted profiles.
|
|
86
112
|
*/
|
|
87
|
-
export declare function resolveTargetProfiles({ profilePath, chromeDir, profile, }?: ResolveProfilesOptions): DiscoveredProfile[];
|
|
113
|
+
export declare function resolveTargetProfiles({ profilePath, chromeDir, profile, browser, }?: ResolveProfilesOptions): DiscoveredProfile[];
|
package/dist/src/core/chrome.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as e,readdirSync as t,readFileSync as r,statSync as o}from"node:fs";import{join as i}from"node:path";import{getPlatformChromeDir as
|
|
1
|
+
import{existsSync as e,readdirSync as t,readFileSync as r,statSync as o}from"node:fs";import{join as i}from"node:path";import{getPlatformChromeDir as n}from"./platform/index.js";import{listBrowsers as a,getActiveBrowser as s,setActiveBrowser as c}from"./browsers.js";const f=["Login Data","History","Bookmarks","Cookies","Web Data","Preferences"];export function getDefaultChromeDir(){return n()}export function detectInstalledBrowsers({includeMissing:t=!1}={}){const r=s(),o=[];try{for(const r of a()){c(r.id);const i=getDefaultChromeDir(),n=Boolean(i)&&e(i);(n||t)&&o.push({browser:r,chromeDir:i,installed:n,profileCount:n?discoverProfiles(i).length:0})}}finally{c(r.id)}return o}export function discoverProfiles(r){const o=r||getDefaultChromeDir();if(!o||!e(o))return[];try{const r=t(o,{withFileTypes:!0}),n=[];for(const t of r){if(!t.isDirectory())continue;const r=i(o,t.name);f.some(t=>e(i(r,t)))&&n.push({name:t.name,path:r})}return n}catch{return[]}}export function getProfileMetadata(t){const o=t||getDefaultChromeDir(),n=i(o,"Local State");if(!e(n))return[];try{const e=r(n,"utf-8"),t=JSON.parse(e),o=t?.profile?.info_cache;return o&&"object"==typeof o?Object.entries(o).map(([e,t])=>{const r=t;return{directory:e,name:r.name||e,gaiaName:r.gaia_name||"",userName:r.user_name||"",email:r.user_name||"",avatarIcon:r.avatar_icon||"",isUsingDefaultName:r.is_using_default_name??!0,isEphemeral:r.is_ephemeral??!1,activeTime:r.active_time||0}}):[]}catch{return[]}}export function getProfileStates(t){const r=t||getDefaultChromeDir(),o=discoverProfiles(r),n=getProfileMetadata(r),a=new Map;for(const e of n)a.set(e.directory,e);return o.map(t=>{const r=a.get(t.name),o=t.path,n={};for(const t of f)n[t.replace(/\s+/g,"_").toLowerCase()]=e(i(o,t));return n.top_sites=e(i(o,"Top Sites")),n.extensions=e(i(o,"Extensions")),{directory:t.name,path:o,displayName:r?.name||t.name,gaiaName:r?.gaiaName||"",email:r?.email||"",avatarIcon:r?.avatarIcon||"",isEphemeral:r?.isEphemeral??!1,activeTime:r?.activeTime||0,dataFiles:n}})}export function getDirectorySize(r){if(!r||!e(r))return 0;let n,a=0;try{n=t(r,{withFileTypes:!0})}catch{return 0}for(const e of n){const t=i(r,e.name);try{if(e.isSymbolicLink())continue;e.isDirectory()?a+=getDirectorySize(t):e.isFile()&&(a+=o(t).size)}catch{}}return a}export function resolveProfilePath(t,r){const o=t||getDefaultChromeDir(),n=i(o,r);return e(n)?n:null}export function resolveTargetProfiles({profilePath:e,chromeDir:t,profile:r,browser:o}={}){o&&c(o);const i=e||t||getDefaultChromeDir(),n=discoverProfiles(i);if(r){const e=n.filter(e=>e.name===r);if(0===e.length)throw new Error(`Profile "${r}" not found in ${i}`);return e}return n}
|
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Retrieves
|
|
3
|
-
* Caches the result for subsequent calls within the same process.
|
|
2
|
+
* Retrieves a browser's "Safe Storage" password from the macOS Keychain.
|
|
3
|
+
* Caches the result per service for subsequent calls within the same process.
|
|
4
4
|
*
|
|
5
|
+
* @param service - Keychain service name, e.g. "Chrome Safe Storage".
|
|
5
6
|
* @returns The raw keychain password.
|
|
6
7
|
* @throws If the password could not be retrieved.
|
|
7
8
|
*/
|
|
9
|
+
export declare function getKeychainPassword(service?: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Backward-compatible alias for {@link getKeychainPassword} using Chrome's item.
|
|
12
|
+
* @deprecated Prefer {@link getKeychainPassword} with an explicit service name.
|
|
13
|
+
*/
|
|
8
14
|
export declare function getChromeKeychainPassword(): string;
|
|
9
15
|
/**
|
|
10
|
-
* Derives the AES-128-CBC key from
|
|
11
|
-
* Caches the result
|
|
16
|
+
* Derives the AES-128-CBC key from a browser's Keychain password using PBKDF2.
|
|
17
|
+
* Caches the result per service.
|
|
12
18
|
*
|
|
19
|
+
* @param service - Keychain service name. Defaults to Chrome's.
|
|
13
20
|
* @param keychainPassword - The raw password. If omitted, fetched from Keychain.
|
|
14
21
|
* @returns The derived 16-byte key.
|
|
15
22
|
*/
|
|
16
|
-
export declare function deriveEncryptionKey(keychainPassword?: string): Buffer;
|
|
23
|
+
export declare function deriveEncryptionKey(service?: string, keychainPassword?: string): Buffer;
|
|
17
24
|
/**
|
|
18
|
-
* Clears cached keychain data. Useful for testing or
|
|
25
|
+
* Clears cached keychain data for all browsers. Useful for testing or
|
|
26
|
+
* re-authentication.
|
|
19
27
|
*/
|
|
20
28
|
export declare function clearKeychainCache(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
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,i=36,s=45,a=new Map,c=new Map;function h(e,n,t){const r="See docs/remote-keychain-access.md for non-interactive / SSH setup.";let a;a=n===o||/could not be found/i.test(t)?`No '${e}' item found in the Keychain.\nRun this browser at least once on this machine so it creates the key.`:n===i||/interaction.*not allowed/i.test(t)?"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===s?`Keychain authorization failed (wrong password or denied).\nTry: security unlock-keychain ~/Library/Keychains/login.keychain-db\n${r}`:`macOS likely showed a Keychain access prompt that was denied or dismissed.\nRun \`security find-generic-password -w -s "${e}"\` in a GUI\nTerminal and click "Always Allow".\n${r}`;const c=[null!==n?`exit ${n}`:null,t||null].filter(Boolean).join(": ");return new Error(`Could not retrieve ${e} key from macOS Keychain.\n${a}`+(c?`\n(security: ${c})`:""))}export function getKeychainPassword(n=r){const t=a.get(n);if(void 0!==t)return t;let o;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 h(n,t,r)}if(!o)throw new Error(`Keychain returned an empty ${n} key.\nThe item may exist with no password. Re-run the browser to recreate it.`);return a.set(n,o),o}export function getChromeKeychainPassword(){return getKeychainPassword(r)}export function deriveEncryptionKey(e=r,o){const i=c.get(e);if(void 0!==i)return i;const s=o||getKeychainPassword(e);if(!s)throw new Error("Keychain password cannot be empty for key derivation.");const a=n(s,t,1003,16,"sha1");return c.set(e,a),a}export function clearKeychainCache(){a.clear(),c.clear()}
|
|
@@ -2,9 +2,11 @@ import type { ChromePlatform } from "./types.js";
|
|
|
2
2
|
/**
|
|
3
3
|
* macOS backend.
|
|
4
4
|
*
|
|
5
|
-
* Key source: the "
|
|
5
|
+
* Key source: the active browser's "<Browser> Safe Storage" password from the
|
|
6
|
+
* login Keychain (e.g. "Chrome Safe Storage", "Microsoft Edge Safe Storage"),
|
|
6
7
|
* stretched with PBKDF2 (1003 iterations) — implemented in `core/keychain.ts`.
|
|
7
8
|
* Cipher: AES-128-CBC with a fixed 16-space IV — implemented in `core/crypto.ts`.
|
|
8
|
-
*
|
|
9
|
+
* The browser only changes *which* Keychain item and data dir are used; the
|
|
10
|
+
* crypto is identical across all Chromium forks.
|
|
9
11
|
*/
|
|
10
12
|
export declare const darwin: ChromePlatform;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{homedir as
|
|
1
|
+
import{homedir as r}from"node:os";import{join as o}from"node:path";import{deriveEncryptionKey as e,clearKeychainCache as t}from"../keychain.js";import{decryptValueBuffer as i}from"../crypto.js";import{getActiveBrowser as p}from"../browsers.js";export const darwin={name:"darwin",getDefaultChromeDir:()=>o(r(),"Library","Application Support",...p().dirs.darwin.split("/")),getDecryptionKey:()=>e(p().safeStorageLabel),decrypt:(r,o)=>i(r,o),clearCache(){t()}};
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { ChromePlatform } from "./types.js";
|
|
9
9
|
export type { ChromePlatform } from "./types.js";
|
|
10
|
+
export { BROWSERS, listBrowsers, resolveBrowser, getActiveBrowser, setActiveBrowser, type BrowserId, type BrowserDescriptor, } from "../browsers.js";
|
|
10
11
|
/**
|
|
11
12
|
* Returns the backend for the current OS, cached after first call.
|
|
12
13
|
* @throws On an unsupported platform.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{darwin as r}from"./darwin.js";import{win32 as e}from"./win32.js";import{linux as t}from"./linux.js";let o=null;export function selectPlatform(){if(o)return o;switch(process.platform){case"darwin":o=r;break;case"win32":o=e;break;case"linux":o=t;break;default:throw new Error(`Unsupported platform: ${process.platform}. chrome-tools supports macOS, Windows, and Linux.`)}return o}export function getPlatformChromeDir(){return selectPlatform().getDefaultChromeDir()}export function deriveEncryptionKey(r){return selectPlatform().getDecryptionKey(r)}export function decryptValueBuffer(r,e){if(!r||0===r.length)return Buffer.alloc(0);const t=Buffer.isBuffer(r)?r:Buffer.from(r);return selectPlatform().decrypt(t,e)}export function decryptValue(r,e){const t=decryptValueBuffer(r,e);return Buffer.isBuffer(t)?t.toString("utf-8"):t}export function clearKeyCache(){selectPlatform().clearCache()}
|
|
1
|
+
import{darwin as r}from"./darwin.js";import{win32 as e}from"./win32.js";import{linux as t}from"./linux.js";export{BROWSERS,listBrowsers,resolveBrowser,getActiveBrowser,setActiveBrowser}from"../browsers.js";let o=null;export function selectPlatform(){if(o)return o;switch(process.platform){case"darwin":o=r;break;case"win32":o=e;break;case"linux":o=t;break;default:throw new Error(`Unsupported platform: ${process.platform}. chrome-tools supports macOS, Windows, and Linux.`)}return o}export function getPlatformChromeDir(){return selectPlatform().getDefaultChromeDir()}export function deriveEncryptionKey(r){return selectPlatform().getDecryptionKey(r)}export function decryptValueBuffer(r,e){if(!r||0===r.length)return Buffer.alloc(0);const t=Buffer.isBuffer(r)?r:Buffer.from(r);return selectPlatform().decrypt(t,e)}export function decryptValue(r,e){const t=decryptValueBuffer(r,e);return Buffer.isBuffer(t)?t.toString("utf-8"):t}export function clearKeyCache(){selectPlatform().clearCache()}
|
|
@@ -2,9 +2,9 @@ import type { ChromePlatform } from "./types.js";
|
|
|
2
2
|
/**
|
|
3
3
|
* Linux backend.
|
|
4
4
|
*
|
|
5
|
-
* Key source: "
|
|
6
|
-
* KWallet) via `secret-tool`, or the hardcoded
|
|
7
|
-
* with PBKDF2 (1 iteration).
|
|
5
|
+
* Key source: the active browser's "Safe Storage" password from the Secret
|
|
6
|
+
* Service (gnome-keyring / KWallet) via `secret-tool`, or the hardcoded
|
|
7
|
+
* "peanuts" fallback, stretched with PBKDF2 (1 iteration).
|
|
8
8
|
* Cipher: AES-128-CBC with a fixed 16-space IV (same primitive as macOS).
|
|
9
9
|
*/
|
|
10
10
|
export declare const linux: ChromePlatform;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{execFileSync as o}from"node:child_process";import{homedir as
|
|
1
|
+
import{execFileSync as o}from"node:child_process";import{homedir as r}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";const p=Buffer.from("saltysalt"),c=new Map;export const linux={name:"linux",getDefaultChromeDir(){const o=process.env.XDG_CONFIG_HOME||t(r(),".config");return t(o,...i().dirs.linux.split("/"))},getDecryptionKey(){const r=i().linuxSecretApp||"chrome",t=c.get(r);if(void 0!==t)return t;const n=e(function(r){try{const t=o("secret-tool",["lookup","application",r],{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim();if(t)return t}catch{}return"peanuts"}(r),p,1,16,"sha1");return c.set(r,n),n},decrypt:(o,r)=>n(o,r),clearCache(){c.clear()}};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{execFileSync as e}from"node:child_process";import{existsSync as r,readFileSync as t}from"node:fs";import{join as o}from"node:path";import{decryptValueBufferGcm as n}from"../crypto.js";const
|
|
1
|
+
import{execFileSync as e}from"node:child_process";import{existsSync as r,readFileSync as t}from"node:fs";import{join as o}from"node:path";import{decryptValueBufferGcm as n}from"../crypto.js";import{getActiveBrowser as s}from"../browsers.js";const c=Buffer.from("DPAPI","ascii"),a=new Map;export const win32={name:"win32",getDefaultChromeDir(){const e=process.env.LOCALAPPDATA||(process.env.USERPROFILE?o(process.env.USERPROFILE,"AppData","Local"):"");return o(e,...s().dirs.win32.split("/"))},getDecryptionKey(n){const s=n||this.getDefaultChromeDir(),i=a.get(s);if(void 0!==i)return i;const p=o(s,"Local State");if(!r(p))throw new Error(`Could not find Chrome's "Local State" at ${p}.\nRun Google Chrome at least once so it creates the encryption key.`);let y;try{const e=t(p,"utf-8");y=JSON.parse(e)?.os_crypt?.encrypted_key}catch(e){throw new Error(`Failed to read os_crypt.encrypted_key from Local State: ${e.message}`)}if(!y)throw new Error("No os_crypt.encrypted_key found in Local State.\nThis profile may predate Chrome's encrypted-key scheme.");const m=Buffer.from(y,"base64");if(!m.subarray(0,c.length).equals(c))throw new Error('Unexpected encrypted_key format (missing "DPAPI" prefix).');const l=function(r){const t=["$ErrorActionPreference='Stop';","Add-Type -AssemblyName System.Security;","$b=[Console]::In.ReadToEnd().Trim();","$enc=[Convert]::FromBase64String($b);","$dec=[System.Security.Cryptography.ProtectedData]::Unprotect($enc,$null,[System.Security.Cryptography.DataProtectionScope]::CurrentUser);","[Console]::Out.Write([Convert]::ToBase64String($dec));"].join(" ");let o;try{o=e("powershell.exe",["-NoProfile","-NonInteractive","-Command",t],{input:r.toString("base64"),encoding:"utf-8"})}catch(e){throw new Error(`DPAPI decryption via PowerShell failed.\nThe encrypted_key can only be unprotected by the same Windows user that created it. Run as that user on the same machine.\n(powershell: ${e?.message??e})`)}return Buffer.from(o.trim(),"base64")}(m.subarray(c.length));return a.set(s,l),l},decrypt:(e,r)=>n(e,r),clearCache(){a.clear()}};
|
package/dist/src/index.d.ts
CHANGED
|
@@ -10,9 +10,10 @@
|
|
|
10
10
|
* const bookmarks = getBookmarks({ search: 'github' });
|
|
11
11
|
* const profiles = discoverProfiles();
|
|
12
12
|
*/
|
|
13
|
-
export { getDefaultChromeDir, discoverProfiles, getProfileMetadata, getProfileStates, resolveProfilePath, resolveTargetProfiles, type DiscoveredProfile, type ProfileMetadata, type ProfileState, type ResolveProfilesOptions, } from "./core/chrome.js";
|
|
13
|
+
export { getDefaultChromeDir, discoverProfiles, getProfileMetadata, getProfileStates, resolveProfilePath, resolveTargetProfiles, detectInstalledBrowsers, type DiscoveredProfile, type ProfileMetadata, type ProfileState, type ResolveProfilesOptions, type InstalledBrowser, } from "./core/chrome.js";
|
|
14
|
+
export { BROWSERS, listBrowsers, resolveBrowser, getActiveBrowser, setActiveBrowser, type BrowserId, type BrowserDescriptor, } from "./core/browsers.js";
|
|
14
15
|
export { selectPlatform, getPlatformChromeDir, deriveEncryptionKey, decryptValue, decryptValueBuffer, clearKeyCache, type ChromePlatform, } from "./core/platform/index.js";
|
|
15
|
-
export { getChromeKeychainPassword, clearKeychainCache, } from "./core/keychain.js";
|
|
16
|
+
export { getKeychainPassword, getChromeKeychainPassword, clearKeychainCache, } from "./core/keychain.js";
|
|
16
17
|
export { chromeTimeToDate, } from "./core/crypto.js";
|
|
17
18
|
export { queryDatabase, tableExists, } from "./core/db.js";
|
|
18
19
|
export { getPasswords } from "./modules/passwords.js";
|
package/dist/src/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{getDefaultChromeDir,discoverProfiles,getProfileMetadata,getProfileStates,resolveProfilePath,resolveTargetProfiles}from"./core/chrome.js";export{selectPlatform,getPlatformChromeDir,deriveEncryptionKey,decryptValue,decryptValueBuffer,clearKeyCache}from"./core/platform/index.js";export{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{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}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{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,6 +1,7 @@
|
|
|
1
1
|
export interface AutofillOptions {
|
|
2
2
|
chromeDir?: string;
|
|
3
3
|
profile?: string;
|
|
4
|
+
browser?: string;
|
|
4
5
|
search?: string;
|
|
5
6
|
limit?: number;
|
|
6
7
|
}
|
|
@@ -15,6 +16,7 @@ export interface AutofillEntry {
|
|
|
15
16
|
export interface CreditCardOptions {
|
|
16
17
|
chromeDir?: string;
|
|
17
18
|
profile?: string;
|
|
19
|
+
browser?: string;
|
|
18
20
|
}
|
|
19
21
|
export interface CreditCardEntry {
|
|
20
22
|
profile: string;
|
|
@@ -31,6 +33,7 @@ export interface CreditCardEntry {
|
|
|
31
33
|
export interface AddressOptions {
|
|
32
34
|
chromeDir?: string;
|
|
33
35
|
profile?: string;
|
|
36
|
+
browser?: string;
|
|
34
37
|
}
|
|
35
38
|
export interface AddressEntry {
|
|
36
39
|
profile: string;
|
|
@@ -54,7 +57,7 @@ export interface AddressEntry {
|
|
|
54
57
|
* @param options - Filtering and targeting options.
|
|
55
58
|
* @returns Array of autofill entries.
|
|
56
59
|
*/
|
|
57
|
-
export declare function getAutofillEntries({ chromeDir, profile, search, limit, }?: AutofillOptions): AutofillEntry[];
|
|
60
|
+
export declare function getAutofillEntries({ chromeDir, profile, browser, search, limit, }?: AutofillOptions): AutofillEntry[];
|
|
58
61
|
/**
|
|
59
62
|
* Retrieves saved credit cards from Chrome's Web Data database.
|
|
60
63
|
* Card numbers are encrypted and will be decrypted.
|
|
@@ -62,11 +65,11 @@ export declare function getAutofillEntries({ chromeDir, profile, search, limit,
|
|
|
62
65
|
* @param options - Targeting options.
|
|
63
66
|
* @returns Array of credit card entries.
|
|
64
67
|
*/
|
|
65
|
-
export declare function getCreditCards({ chromeDir, profile }?: CreditCardOptions): CreditCardEntry[];
|
|
68
|
+
export declare function getCreditCards({ chromeDir, profile, browser }?: CreditCardOptions): CreditCardEntry[];
|
|
66
69
|
/**
|
|
67
70
|
* Retrieves saved addresses from Chrome's Web Data database.
|
|
68
71
|
*
|
|
69
72
|
* @param options - Targeting options.
|
|
70
73
|
* @returns Array of address entries.
|
|
71
74
|
*/
|
|
72
|
-
export declare function getAddresses({ chromeDir, profile }?: AddressOptions): AddressEntry[];
|
|
75
|
+
export declare function getAddresses({ chromeDir, profile, browser }?: AddressOptions): AddressEntry[];
|
|
@@ -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
|
|
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 u=t({chromeDir:d,profile:c,browser:s}),l=o(),f=[];for(const t of u){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,l)),f.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 f}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}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export interface BookmarkOptions {
|
|
2
2
|
chromeDir?: string;
|
|
3
3
|
profile?: string;
|
|
4
|
+
browser?: string;
|
|
4
5
|
folder?: string;
|
|
5
6
|
search?: string;
|
|
6
7
|
}
|
|
@@ -20,4 +21,4 @@ export interface BookmarkEntry {
|
|
|
20
21
|
* @param options - Options for filtering and targeting.
|
|
21
22
|
* @returns Filtered bookmark entries.
|
|
22
23
|
*/
|
|
23
|
-
export declare function getBookmarks({ chromeDir, profile, folder, search }?: BookmarkOptions): BookmarkEntry[];
|
|
24
|
+
export declare function getBookmarks({ chromeDir, profile, folder, search, browser }?: BookmarkOptions): BookmarkEntry[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as o,readFileSync as r}from"node:fs";import{join as e}from"node:path";import{resolveTargetProfiles as t}from"../core/chrome.js";import{chromeTimeToDate as
|
|
1
|
+
import{existsSync as o,readFileSync as r}from"node:fs";import{join as e}from"node:path";import{resolveTargetProfiles as t}from"../core/chrome.js";import{chromeTimeToDate as s}from"../core/crypto.js";function n(o){return o&&"0"!==o?s(BigInt(o)):"N/A"}function i(o,r=""){const e=[];if(!o)return e;const t=r?`${r}/${o.name||""}`:o.name||"";if("url"===o.type&&e.push({title:o.name||"",url:o.url||"",folder:r,dateAdded:n(o.date_added),dateLastUsed:n(o.date_last_used),id:o.id,guid:o.guid||""}),o.children&&Array.isArray(o.children))for(const r of o.children)e.push(...i(r,t));return e}export function getBookmarks({chromeDir:s,profile:n,folder:c,search:f,browser:d}={}){const a=t({chromeDir:s,profile:n,browser:d}),l=[];for(const t of a){const s=e(t.path,"Bookmarks");if(o(s))try{const o=r(s,"utf-8"),e=JSON.parse(o).roots;if(!e)continue;const n=["bookmark_bar","other","synced"];for(const o of n)if(e[o]){const r=i(e[o],"");for(const o of r)o.profile=t.name,l.push(o)}}catch{}}let u=l;if(c){const o=c.toLowerCase();u=u.filter(r=>r.folder.toLowerCase().includes(o))}if(f){const o=f.toLowerCase();u=u.filter(r=>r.title.toLowerCase().includes(o)||r.url.toLowerCase().includes(o))}return u}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export interface CookieOptions {
|
|
2
2
|
chromeDir?: string;
|
|
3
3
|
profile?: string;
|
|
4
|
+
browser?: string;
|
|
4
5
|
domain?: string;
|
|
5
6
|
url?: string;
|
|
6
7
|
decrypt?: boolean;
|
|
@@ -30,7 +31,7 @@ declare function pathMatch(urlPath: string, cookiePath: string): boolean;
|
|
|
30
31
|
* @param options - Targeting and filtering options.
|
|
31
32
|
* @returns Array of cookie entries.
|
|
32
33
|
*/
|
|
33
|
-
export declare function getCookies({ chromeDir, profile, domain, url, decrypt, limit, }?: CookieOptions): CookieEntry[];
|
|
34
|
+
export declare function getCookies({ chromeDir, profile, browser, domain, url, decrypt, limit, }?: CookieOptions): CookieEntry[];
|
|
34
35
|
export declare const _test: {
|
|
35
36
|
parseURL: typeof parseURL;
|
|
36
37
|
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,
|
|
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:_=!0,limit:y=100}={}){const g=o({chromeDir:e,profile:s,browser:f}),k=_?n():null,j=[],x=u(d);for(const e of g){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(x){const t=p(x.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",y&&!d&&(t+=` LIMIT ${parseInt(y,10)}`);const u=i(o,t,s);for(const t of u){if(x){if(!h(x.hostname,t.host_key||""))continue;if(!l(x.pathname||"/",t.path||"/"))continue;if(t.is_secure&&"https:"!==x.protocol)continue}let o=t.value||"";!o&&t.encrypted_value&&k&&(o=a(t.encrypted_value,k,t.host_key||"")),j.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 y&&d?j.slice(0,parseInt(y,10)):j}export const _test={parseURL:u,getPossibleHostKeys:p,domainMatch:h,pathMatch:l};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export interface DownloadOptions {
|
|
2
2
|
chromeDir?: string;
|
|
3
3
|
profile?: string;
|
|
4
|
+
browser?: string;
|
|
4
5
|
search?: string;
|
|
5
6
|
limit?: number;
|
|
6
7
|
}
|
|
@@ -25,4 +26,4 @@ export interface DownloadEntry {
|
|
|
25
26
|
* @param options - Targeting, filtering, and limit options.
|
|
26
27
|
* @returns Array of download entries.
|
|
27
28
|
*/
|
|
28
|
-
export declare function getDownloads({ chromeDir, profile, search, limit, }?: DownloadOptions): DownloadEntry[];
|
|
29
|
+
export declare function getDownloads({ chromeDir, profile, browser, search, limit, }?: DownloadOptions): DownloadEntry[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as e}from"node:path";import{resolveTargetProfiles as t}from"../core/chrome.js";import{chromeTimeToDate as r}from"../core/crypto.js";import{queryDatabase as o,tableExists as n}from"../core/db.js";const a={0:"in_progress",1:"complete",2:"cancelled",3:"interrupted"},s={0:"not_dangerous",1:"dangerous_file",2:"dangerous_url",3:"dangerous_content",4:"maybe_dangerous_content",5:"uncommon_content",6:"user_validated",7:"dangerous_host",8:"potentially_unwanted",9:"allowlisted_by_policy"};export function getDownloads({chromeDir:i,profile:_,
|
|
1
|
+
import{join as e}from"node:path";import{resolveTargetProfiles as t}from"../core/chrome.js";import{chromeTimeToDate as r}from"../core/crypto.js";import{queryDatabase as o,tableExists as n}from"../core/db.js";const a={0:"in_progress",1:"complete",2:"cancelled",3:"interrupted"},s={0:"not_dangerous",1:"dangerous_file",2:"dangerous_url",3:"dangerous_content",4:"maybe_dangerous_content",5:"uncommon_content",6:"user_validated",7:"dangerous_host",8:"potentially_unwanted",9:"allowlisted_by_policy"};export function getDownloads({chromeDir:i,profile:_,browser:l,search:d,limit:c=100}={}){const m=t({chromeDir:i,profile:_,browser:l}),p=[];for(const t of m){const i=e(t.path,"History");try{if(!n(i,"downloads"))continue;let e="SELECT id, target_path, tab_url, total_bytes, received_bytes,\n start_time, end_time, mime_type, original_mime_type,\n state, danger_type, opened, referrer, site_url\n FROM downloads ORDER BY start_time DESC";c&&(e+=` LIMIT ${parseInt(c,10)}`);const _=o(i,e);for(const e of _){const o={profile:t.name,targetPath:e.target_path||"",url:e.tab_url||"",siteUrl:e.site_url||"",referrer:e.referrer||"",totalBytes:e.total_bytes||0,receivedBytes:e.received_bytes||0,mimeType:e.mime_type||e.original_mime_type||"",state:a[e.state]||`unknown(${e.state})`,dangerType:s[e.danger_type]||`unknown(${e.danger_type})`,opened:Boolean(e.opened),startTime:r(e.start_time||0),endTime:r(e.end_time||0)};if(d){const e=d.toLowerCase();if(!(o.targetPath&&o.targetPath.toLowerCase().includes(e)||o.url&&o.url.toLowerCase().includes(e)))continue}p.push(o)}}catch{}}return p}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export interface ExtensionOptions {
|
|
2
2
|
chromeDir?: string;
|
|
3
3
|
profile?: string;
|
|
4
|
+
browser?: string;
|
|
4
5
|
search?: string;
|
|
5
6
|
}
|
|
6
7
|
export interface ExtensionEntry {
|
|
@@ -22,4 +23,4 @@ export interface ExtensionEntry {
|
|
|
22
23
|
* @param options - Targeting and filtering options.
|
|
23
24
|
* @returns Array of extension entries.
|
|
24
25
|
*/
|
|
25
|
-
export declare function getExtensions({ chromeDir, profile, search }?: ExtensionOptions): ExtensionEntry[];
|
|
26
|
+
export declare function getExtensions({ chromeDir, profile, search, browser }?: ExtensionOptions): ExtensionEntry[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as e,readFileSync as o}from"node:fs";import{join as t}from"node:path";import{resolveTargetProfiles as n}from"../core/chrome.js";export function getExtensions({chromeDir:r,profile:i,search:s}={}){const
|
|
1
|
+
import{existsSync as e,readFileSync as o}from"node:fs";import{join as t}from"node:path";import{resolveTargetProfiles as n}from"../core/chrome.js";export function getExtensions({chromeDir:r,profile:i,search:s,browser:c}={}){const a=n({chromeDir:r,profile:i,browser:c}),f=[];for(const n of a){let r=null;for(const i of["Secure Preferences","Preferences"]){const s=t(n.path,i);if(e(s))try{r=JSON.parse(o(s,"utf-8"));break}catch{continue}}if(!r)continue;const i=r?.extensions?.settings;if(i&&"object"==typeof i)for(const[e,o]of Object.entries(i)){const t=o;if(5===t.location||10===t.location)continue;const r=t.manifest||{},i={profile:n.name,id:e,name:r.name||t.name||e,version:r.version||t.version||"unknown",description:r.description||"",enabled:1===t.state,homepageUrl:r.homepage_url||"",updateUrl:r.update_url||"",installTime:t.install_time||"",fromWebstore:t.from_webstore||!1,location:t.location||0};if(i.name.startsWith("__MSG_")&&(i.name=e),i.description.startsWith("__MSG_")&&(i.description=""),s){const e=s.toLowerCase();if(!(i.name&&i.name.toLowerCase().includes(e)||i.id&&i.id.toLowerCase().includes(e)))continue}f.push(i)}}return f}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export interface HistoryOptions {
|
|
2
2
|
chromeDir?: string;
|
|
3
3
|
profile?: string;
|
|
4
|
+
browser?: string;
|
|
4
5
|
search?: string;
|
|
5
6
|
limit?: number;
|
|
6
7
|
}
|
|
@@ -14,6 +15,7 @@ export interface HistoryEntry {
|
|
|
14
15
|
export interface SearchTermOptions {
|
|
15
16
|
chromeDir?: string;
|
|
16
17
|
profile?: string;
|
|
18
|
+
browser?: string;
|
|
17
19
|
limit?: number;
|
|
18
20
|
}
|
|
19
21
|
export interface SearchTermEntry {
|
|
@@ -27,11 +29,11 @@ export interface SearchTermEntry {
|
|
|
27
29
|
* @param options - Filtering and targeting options.
|
|
28
30
|
* @returns Array of history entries.
|
|
29
31
|
*/
|
|
30
|
-
export declare function getHistory({ chromeDir, profile, search, limit }?: HistoryOptions): HistoryEntry[];
|
|
32
|
+
export declare function getHistory({ chromeDir, profile, search, limit, browser }?: HistoryOptions): HistoryEntry[];
|
|
31
33
|
/**
|
|
32
34
|
* Retrieves search terms from Chrome's History database.
|
|
33
35
|
*
|
|
34
36
|
* @param options - Targeting and limit options.
|
|
35
37
|
* @returns Array of search term entries.
|
|
36
38
|
*/
|
|
37
|
-
export declare function getSearchTerms({ chromeDir, profile, limit }?: SearchTermOptions): SearchTermEntry[];
|
|
39
|
+
export declare function getSearchTerms({ chromeDir, profile, limit, browser }?: SearchTermOptions): SearchTermEntry[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as t}from"node:path";import{resolveTargetProfiles as r}from"../core/chrome.js";import{chromeTimeToDate as o}from"../core/crypto.js";import{queryDatabase as e,tableExists as i}from"../core/db.js";export function getHistory({chromeDir:s,profile:c,search:n,limit:l=100}={}){const
|
|
1
|
+
import{join as t}from"node:path";import{resolveTargetProfiles as r}from"../core/chrome.js";import{chromeTimeToDate as o}from"../core/crypto.js";import{queryDatabase as e,tableExists as i}from"../core/db.js";export function getHistory({chromeDir:s,profile:c,search:n,limit:l=100,browser:u}={}){const m=r({chromeDir:s,profile:c,browser:u}),f=[];for(const r of m){const s=t(r.path,"History");try{if(!i(s,"urls"))continue;let t="SELECT url, title, visit_count, last_visit_time\n FROM urls ORDER BY last_visit_time DESC";l&&(t+=` LIMIT ${parseInt(l,10)}`);const c=e(s,t);for(const t of c){const e={profile:r.name,url:t.url,title:t.title||"",visitCount:t.visit_count||0,lastVisit:o(t.last_visit_time||0)};if(n){const t=n.toLowerCase();if(!(e.url&&e.url.toLowerCase().includes(t)||e.title&&e.title.toLowerCase().includes(t)))continue}f.push(e)}}catch{}}return f}export function getSearchTerms({chromeDir:o,profile:s,limit:c=100,browser:n}={}){const l=r({chromeDir:o,profile:s,browser:n}),u=[];for(const r of l){const o=t(r.path,"History");try{if(!i(o,"keyword_search_terms"))continue;let t="SELECT kst.term, u.url\n FROM keyword_search_terms kst\n JOIN urls u ON kst.url_id = u.id\n ORDER BY u.last_visit_time DESC";c&&(t+=` LIMIT ${parseInt(c,10)}`);const s=e(o,t);for(const t of s)u.push({profile:r.name,term:t.term||"",url:t.url||""})}catch{}}return u}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export interface PasswordOptions {
|
|
2
2
|
chromeDir?: string;
|
|
3
3
|
profile?: string;
|
|
4
|
+
browser?: string;
|
|
4
5
|
search?: string;
|
|
5
6
|
}
|
|
6
7
|
export interface PasswordEntry {
|
|
@@ -17,4 +18,4 @@ export interface PasswordEntry {
|
|
|
17
18
|
* @param options - Options for profile selection and filtering.
|
|
18
19
|
* @returns Array of decrypted password entries.
|
|
19
20
|
*/
|
|
20
|
-
export declare function getPasswords({ chromeDir, profile, search }?: PasswordOptions): PasswordEntry[];
|
|
21
|
+
export declare function getPasswords({ chromeDir, profile, search, browser }?: 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 n,tableExists as a}from"../core/db.js";export function getPasswords({chromeDir:i,profile:u,search:c}={}){const
|
|
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 n,tableExists as a}from"../core/db.js";export function getPasswords({chromeDir:i,profile:u,search:c,browser:l}={}){const m=o({chromeDir:i,profile:u,browser:l}),f=e(),p=[];for(const o of m){const e=r(o.path,"Login Data");try{if(!a(e,"logins"))continue;const r=n(e,"SELECT origin_url, username_value, password_value, date_last_used\n FROM logins ORDER BY origin_url");for(const e of r){const r={profile:o.name,url:e.origin_url,username:e.username_value,password:s(e.password_value,f),lastUsed:t(e.date_last_used||0)};if(c){const o=c.toLowerCase();if(!(r.url&&r.url.toLowerCase().includes(o)||r.username&&r.username.toLowerCase().includes(o)))continue}p.push(r)}}catch(r){p.push({profile:o.name,error:r.message})}}return p.filter(r=>!r.error)}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export interface SearchEngineOptions {
|
|
2
2
|
chromeDir?: string;
|
|
3
3
|
profile?: string;
|
|
4
|
+
browser?: string;
|
|
4
5
|
}
|
|
5
6
|
export interface SearchEngineEntry {
|
|
6
7
|
profile: string;
|
|
@@ -19,4 +20,4 @@ export interface SearchEngineEntry {
|
|
|
19
20
|
* @param options - Targeting options.
|
|
20
21
|
* @returns Array of search engine entries.
|
|
21
22
|
*/
|
|
22
|
-
export declare function getSearchEngines({ chromeDir, profile }?: SearchEngineOptions): SearchEngineEntry[];
|
|
23
|
+
export declare function getSearchEngines({ chromeDir, profile, browser }?: SearchEngineOptions): SearchEngineEntry[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as
|
|
1
|
+
import{join as r}from"node:path";import{resolveTargetProfiles as o}from"../core/chrome.js";import{queryDatabase as e,tableExists as i}from"../core/db.js";export function getSearchEngines({chromeDir:t,profile:s,browser:n}={}){const c=o({chromeDir:t,profile:s,browser:n}),a=[];for(const o of c){const t=r(o.path,"Web Data");try{if(!i(t,"keywords"))continue;const r=e(t,"SELECT id, short_name, keyword, url, suggestions_url,\n favicon_url, prepopulate_id, is_active\n FROM keywords ORDER BY short_name");for(const e of r)a.push({profile:o.name,id:e.id,name:e.short_name||"",keyword:e.keyword||"",url:e.url||"",suggestionsUrl:e.suggestions_url||"",faviconUrl:e.favicon_url||"",isDefault:1===e.prepopulate_id,isActive:1===e.is_active})}catch{}}return a}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export interface TopSiteOptions {
|
|
2
2
|
chromeDir?: string;
|
|
3
3
|
profile?: string;
|
|
4
|
+
browser?: string;
|
|
4
5
|
}
|
|
5
6
|
export interface TopSiteEntry {
|
|
6
7
|
profile: string;
|
|
@@ -15,4 +16,4 @@ export interface TopSiteEntry {
|
|
|
15
16
|
* @param options - Targeting options.
|
|
16
17
|
* @returns Array of top site entries.
|
|
17
18
|
*/
|
|
18
|
-
export declare function getTopSites({ chromeDir, profile }?: TopSiteOptions): TopSiteEntry[];
|
|
19
|
+
export declare function getTopSites({ chromeDir, profile, browser }?: TopSiteOptions): TopSiteEntry[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as r}from"node:path";import{resolveTargetProfiles as
|
|
1
|
+
import{join as r}from"node:path";import{resolveTargetProfiles as o}from"../core/chrome.js";import{queryDatabase as t,tableExists as e}from"../core/db.js";export function getTopSites({chromeDir:i,profile:s,browser:c}={}){const n=o({chromeDir:i,profile:s,browser:c}),p=[];for(const o of n){const i=r(o.path,"Top Sites");try{if(!e(i,"top_sites"))continue;const r=t(i,"SELECT url, url_rank, title, redirects\n FROM top_sites ORDER BY url_rank ASC");for(const t of r)p.push({profile:o.name,url:t.url||"",title:t.title||"",rank:t.url_rank??0,redirects:t.redirects||""})}catch{}}return p}
|