chrome-tools 1.0.8 → 1.1.1
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.d.ts +2 -1
- package/dist/bin/cli.js +1 -1
- package/dist/src/core/chrome.d.ts +11 -1
- package/dist/src/core/chrome.js +1 -1
- package/dist/src/core/crypto.d.ts +15 -3
- package/dist/src/core/crypto.js +1 -1
- package/dist/src/core/platform/darwin.d.ts +10 -0
- package/dist/src/core/platform/darwin.js +1 -0
- package/dist/src/core/platform/index.d.ts +27 -0
- package/dist/src/core/platform/index.js +1 -0
- package/dist/src/core/platform/linux.d.ts +10 -0
- package/dist/src/core/platform/linux.js +1 -0
- package/dist/src/core/platform/types.d.ts +34 -0
- package/dist/src/core/platform/types.js +1 -0
- package/dist/src/core/platform/win32.d.ts +12 -0
- package/dist/src/core/platform/win32.js +1 -0
- package/dist/src/index.d.ts +3 -2
- package/dist/src/index.js +1 -1
- package/dist/src/modules/autofill.js +1 -1
- package/dist/src/modules/cookies.js +1 -1
- package/dist/src/modules/passwords.js +1 -1
- package/package.json +5 -1
package/dist/bin/cli.d.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* chrome-tools CLI
|
|
4
4
|
*
|
|
5
|
-
* A comprehensive command-line interface for extracting Chrome browser data on
|
|
5
|
+
* A comprehensive command-line interface for extracting Chrome browser data on
|
|
6
|
+
* macOS, Windows, and Linux.
|
|
6
7
|
* Supports subcommands and shortcut flags for quick access to all data types.
|
|
7
8
|
*/
|
|
8
9
|
export {};
|
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 r from"cli-table3";import{discoverProfiles as n,getProfileMetadata as l,getProfileStates as a,getDefaultChromeDir as c}from"../src/core/chrome.js";import{getPasswords as m}from"../src/modules/passwords.js";import{getBookmarks as p}from"../src/modules/bookmarks.js";import{getHistory as d,getSearchTerms as f}from"../src/modules/history.js";import{getCookies as h}from"../src/modules/cookies.js";import{getAutofillEntries as u,getCreditCards as g,getAddresses as b}from"../src/modules/autofill.js";import{getDownloads as y}from"../src/modules/downloads.js";import{getExtensions as w}from"../src/modules/extensions.js";import{getSearchEngines as S}from"../src/modules/search-engines.js";import{getTopSites as k}from"../src/modules/top-sites.js";const P=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}(),j=P.PROFILE_PATH||c(),L=P.PROFILE_NAME||void 0;function v(o){console.log(JSON.stringify(o,null,2))}function W(o,e){return new r({head:o.map(o=>i.bold.white(o)),colWidths:e,wordWrap:!0,style:{border:["grey"],head:[]}})}function D(o,e){0===e?console.log(i.yellow(`\n⚠ No ${o} found.\n`)):console.log(i.green(`\n✔ Found ${e} ${o}.\n`))}function N(o){return o.option("--profile-path <path>","Custom Chrome Profile Path",j).option("--profile <name>","Target a specific profile",L).option("--json","Output as JSON")}function C(o){return o.option("--search <term>","Filter results (case-insensitive)")}function U(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")).version(O.version).option("--passwords","Shortcut: list saved passwords").option("--bookmarks","Shortcut: list bookmarks").option("--profiles","Shortcut: list profiles").option("--profile-path <path>","Custom Chrome Profile Path",j).option("--profile <name>","Target a specific profile",L).option("--json","Output as JSON").option("--search <term>","Filter results (for shortcut flags)").option("--limit <n>","Limit results (for shortcut flags)");const $=G.command("profiles").description("List Chrome profiles");function x(o){const e=n(o.profilePath),t=l(o.profilePath);if(o.json){const o=new Map(t.map(o=>[o.directory,o]));return v(e.map(e=>({...e,...o.get(e.name)||{}})))}if(0===e.length)return void console.log(i.yellow("\n⚠ No Chrome profiles found.\n"));const s=new Map(t.map(o=>[o.directory,o]));console.log(i.bold(`\n📁 Discovered ${e.length} profile(s):\n`));const r=W(["Directory","Display Name","Email","Path"],[16,22,30,50]);for(const o of e){const e=s.get(o.name)||{};r.push([i.cyan(o.name),i.white(e.name||o.name),i.dim(e.email||"—"),i.dim(o.path)])}console.log(r.toString()),D("profile(s)",e.length)}N($).action((o,e)=>{x(e.optsWithGlobals())}),$.command("states").description("Show detailed profile states with available data files").option("--profile-path <path>","Custom Chrome Profile Path",j).option("--json","Output as JSON").action((o,e)=>function(o){const e=a(o.profilePath);if(o.json)return v(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 E(o){const e=m({chromeDir:o.profilePath,profile:o.profile,search:o.search});if(o.json)return v(e);if(0===e.length)return D("passwords",0);console.log(i.bold("\n🔑 Saved Passwords:\n"));const t=new Set(e.map(o=>o.profile)).size>1,s=W(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()),D("password(s)",e.length)}C(N(G.command("passwords").description("List saved passwords"))).action((o,e)=>E(e.optsWithGlobals()));function F(o){const e=p({chromeDir:o.profilePath,profile:o.profile,folder:o.folder,search:o.search});if(o.json)return v(e);if(0===e.length)return D("bookmarks",0);console.log(i.bold("\n📚 Bookmarks:\n"));const t=W(["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()),D("bookmark(s)",e.length)}C(N(G.command("bookmarks").description("List bookmarks"))).option("--folder <name>","Filter by folder name").action((o,e)=>F(e.optsWithGlobals()));C(U(N(G.command("history").description("List browsing history")))).action((o,e)=>{const t=e.optsWithGlobals(),s=d({chromeDir:t.profilePath,profile:t.profile,search:t.search,limit:parseInt(t.limit,10)||100});if(t.json)return v(s);if(0===s.length)return D("history entries",0);console.log(i.bold("\n🕐 Browsing History:\n"));const r=W(["URL","Title","Visits","Last Visit"],[50,30,8,22]);for(const o of s)r.push([i.cyan(o.url),i.white(o.title||"—"),i.yellow(String(o.visitCount)),i.dim(o.lastVisit)]);console.log(r.toString()),D("history entry(ies)",s.length)});U(N(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 v(s);if(0===s.length)return D("search terms",0);console.log(i.bold("\n🔍 Search Terms:\n"));const r=W(["Term","URL"],[40,80]);for(const o of s)r.push([i.white(o.term),i.dim(o.url)]);console.log(r.toString()),D("search term(s)",s.length)});U(N(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=h({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 v(s);if(0===s.length)return D("cookies",0);console.log(i.bold("\n🍪 Cookies:\n"));const r=W(["Domain","Name","Value","Secure","Expires"],[30,25,30,8,22]);for(const o of s)r.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(r.toString()),D("cookie(s)",s.length)});C(U(N(G.command("autofill").description("List autofill entries")))).action((o,e)=>{const t=e.optsWithGlobals(),s=u({chromeDir:t.profilePath,profile:t.profile,search:t.search,limit:parseInt(t.limit,10)||100});if(t.json)return v(s);if(0===s.length)return D("autofill entries",0);console.log(i.bold("\n📝 Autofill Entries:\n"));const r=W(["Field Name","Value","Count","Last Used"],[25,40,8,22]);for(const o of s)r.push([i.cyan(o.name),i.white(o.value),i.yellow(String(o.count)),i.dim(o.dateLastUsed)]);console.log(r.toString()),D("autofill entry(ies)",s.length)});N(G.command("credit-cards").description("List saved credit cards")).action((o,e)=>{const t=e.optsWithGlobals(),s=g({chromeDir:t.profilePath,profile:t.profile});if(t.json)return v(s);if(0===s.length)return D("credit cards",0);console.log(i.bold("\n💳 Saved Credit Cards:\n"));const r=W(["Name on Card","Card Number","Expires","Nickname"],[25,25,14,20]);for(const o of s)r.push([i.white(o.nameOnCard||"—"),i.yellow(o.cardNumber||"—"),i.dim(`${o.expirationMonth}/${o.expirationYear}`),i.dim(o.nickname||"—")]);console.log(r.toString()),D("credit card(s)",s.length)});N(G.command("addresses").description("List saved addresses")).action((o,e)=>{const t=e.optsWithGlobals(),s=b({chromeDir:t.profilePath,profile:t.profile});if(t.json)return v(s);if(0===s.length)return D("addresses",0);console.log(i.bold("\n🏠 Saved Addresses:\n"));const r=W(["Name","Email","Phone","Address","City/State/Zip"],[20,25,16,30,25]);for(const o of s)r.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(r.toString()),D("address(es)",s.length)});C(U(N(G.command("downloads").description("List download history")))).action((o,e)=>{const t=e.optsWithGlobals(),s=y({chromeDir:t.profilePath,profile:t.profile,search:t.search,limit:parseInt(t.limit,10)||100});if(t.json)return v(s);if(0===s.length)return D("downloads",0);console.log(i.bold("\n⬇️ Download History:\n"));const r=W(["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):"—";r.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(r.toString()),D("download(s)",s.length)});C(N(G.command("extensions").description("List installed extensions"))).action((o,e)=>{const t=e.optsWithGlobals(),s=w({chromeDir:t.profilePath,profile:t.profile,search:t.search});if(t.json)return v(s);if(0===s.length)return D("extensions",0);console.log(i.bold("\n🧩 Installed Extensions:\n"));const r=W(["Name","Version","ID","Enabled","Webstore"],[30,12,36,9,9]);for(const o of s)r.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(r.toString()),D("extension(s)",s.length)});N(G.command("search-engines").description("List configured search engines")).action((o,e)=>{const t=e.optsWithGlobals(),s=S({chromeDir:t.profilePath,profile:t.profile});if(t.json)return v(s);if(0===s.length)return D("search engines",0);console.log(i.bold("\n🔎 Search Engines:\n"));const r=W(["Name","Keyword","URL","Active"],[20,16,60,8]);for(const o of s)r.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(r.toString()),D("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]}`}N(G.command("top-sites").description("List top sites")).action((o,e)=>{const t=e.optsWithGlobals(),s=k({chromeDir:t.profilePath,profile:t.profile});if(t.json)return v(s);if(0===s.length)return D("top sites",0);console.log(i.bold("\n⭐ Top Sites:\n"));const r=W(["Rank","Title","URL"],[8,30,80]);for(const o of s)r.push([i.yellow(String(o.rank+1)),i.white(o.title||"—"),i.cyan(o.url)]);console.log(r.toString()),D("top site(s)",s.length)}),G.parse();const T=G.opts();G.args.length>0||(T.passwords?E(T):T.bookmarks?F(T):T.profiles?x(T):G.help());
|
|
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();
|
|
@@ -30,7 +30,8 @@ export interface ResolveProfilesOptions {
|
|
|
30
30
|
profile?: string;
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
|
-
* Returns the default Chrome User Data directory for
|
|
33
|
+
* Returns the default Chrome User Data directory for the current OS.
|
|
34
|
+
* Delegates to the active platform backend (macOS/Windows/Linux).
|
|
34
35
|
* @returns The absolute path to Chrome's User Data.
|
|
35
36
|
*/
|
|
36
37
|
export declare function getDefaultChromeDir(): string;
|
|
@@ -58,6 +59,15 @@ export declare function getProfileMetadata(chromeDir?: string): ProfileMetadata[
|
|
|
58
59
|
* @returns Enriched profile state objects.
|
|
59
60
|
*/
|
|
60
61
|
export declare function getProfileStates(chromeDir?: string): ProfileState[];
|
|
62
|
+
/**
|
|
63
|
+
* Recursively computes the total size, in bytes, of a directory's contents.
|
|
64
|
+
* Symbolic links are not followed and unreadable entries are skipped so the
|
|
65
|
+
* walk never throws on a single permission error.
|
|
66
|
+
*
|
|
67
|
+
* @param dirPath - Absolute path to the directory to measure.
|
|
68
|
+
* @returns Total size of all contained files in bytes (0 if missing/unreadable).
|
|
69
|
+
*/
|
|
70
|
+
export declare function getDirectorySize(dirPath: string): number;
|
|
61
71
|
/**
|
|
62
72
|
* Resolves a profile name to its directory path.
|
|
63
73
|
*
|
package/dist/src/core/chrome.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as e,readdirSync as t,readFileSync as r}from"node:fs";import{
|
|
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 a}from"./platform/index.js";const n=["Login Data","History","Bookmarks","Cookies","Web Data","Preferences"];export function getDefaultChromeDir(){return a()}export function discoverProfiles(r){const o=r||getDefaultChromeDir();if(!o||!e(o))return[];try{const r=t(o,{withFileTypes:!0}),a=[];for(const t of r){if(!t.isDirectory())continue;const r=i(o,t.name);n.some(t=>e(i(r,t)))&&a.push({name:t.name,path:r})}return a}catch{return[]}}export function getProfileMetadata(t){const o=t||getDefaultChromeDir(),a=i(o,"Local State");if(!e(a))return[];try{const e=r(a,"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),a=getProfileMetadata(r),s=new Map;for(const e of a)s.set(e.directory,e);return o.map(t=>{const r=s.get(t.name),o=t.path,a={};for(const t of n)a[t.replace(/\s+/g,"_").toLowerCase()]=e(i(o,t));return a.top_sites=e(i(o,"Top Sites")),a.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:a}})}export function getDirectorySize(r){if(!r||!e(r))return 0;let a,n=0;try{a=t(r,{withFileTypes:!0})}catch{return 0}for(const e of a){const t=i(r,e.name);try{if(e.isSymbolicLink())continue;e.isDirectory()?n+=getDirectorySize(t):e.isFile()&&(n+=o(t).size)}catch{}}return n}export function resolveProfilePath(t,r){const o=t||getDefaultChromeDir(),a=i(o,r);return e(a)?a:null}export function resolveTargetProfiles({profilePath:e,chromeDir:t,profile:r}={}){const o=e||t||getDefaultChromeDir(),i=discoverProfiles(o);if(r){const e=i.filter(e=>e.name===r);if(0===e.length)throw new Error(`Profile "${r}" not found in ${o}`);return e}return i}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Decrypts a password/value buffer encrypted by Chrome on macOS.
|
|
3
|
-
* Chrome v10+
|
|
4
|
-
*
|
|
2
|
+
* Decrypts a password/value buffer encrypted by Chrome on macOS or Linux.
|
|
3
|
+
* Chrome v10+ prefixes the encrypted blob with "v10" or "v11", followed by
|
|
4
|
+
* AES-128-CBC ciphertext. (Windows uses AES-256-GCM — see decryptValueBufferGcm.)
|
|
5
5
|
*
|
|
6
6
|
* @param encryptedBlob - The encrypted blob from the database.
|
|
7
7
|
* @param derivedKey - The derived 16-byte key.
|
|
@@ -16,6 +16,18 @@ export declare function decryptValue(encryptedBlob: Buffer | Uint8Array | null |
|
|
|
16
16
|
* @returns The decrypted plaintext bytes, or an error placeholder.
|
|
17
17
|
*/
|
|
18
18
|
export declare function decryptValueBuffer(encryptedBlob: Buffer | Uint8Array | null | undefined, derivedKey: Buffer): Buffer | string;
|
|
19
|
+
/**
|
|
20
|
+
* Decrypts a Windows Chrome `v10` blob using AES-256-GCM.
|
|
21
|
+
*
|
|
22
|
+
* Unlike the macOS/Linux CBC scheme, Windows blobs carry their own 12-byte
|
|
23
|
+
* nonce after the version prefix and a 16-byte GCM auth tag at the end. The
|
|
24
|
+
* `key` is the 32-byte AES key recovered from `Local State` via DPAPI.
|
|
25
|
+
*
|
|
26
|
+
* @param encryptedBlob - The encrypted blob from the database.
|
|
27
|
+
* @param key - The 32-byte AES-256 key.
|
|
28
|
+
* @returns The decrypted plaintext bytes, or a placeholder string.
|
|
29
|
+
*/
|
|
30
|
+
export declare function decryptValueBufferGcm(encryptedBlob: Buffer | Uint8Array | null | undefined, key: Buffer): Buffer | string;
|
|
19
31
|
/**
|
|
20
32
|
* Converts a Chrome/Webkit timestamp (microseconds since 1601-01-01)
|
|
21
33
|
* to a readable local Date string.
|
package/dist/src/core/crypto.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createDecipheriv as r}from"node:crypto";const t=Buffer.alloc(16," ");export function decryptValue(r,t){const e=decryptValueBuffer(r,t);return Buffer.isBuffer(e)?e.toString("utf-8"):e}export function decryptValueBuffer(e,n){if(!e||0===e.length)return Buffer.alloc(0);const f=Buffer.isBuffer(e)?e:Buffer.from(e),u=f.subarray(0,3).toString("utf-8");if("v10"!==u&&"v11"!==u)return"<unsupported encryption format>";const o=f.subarray(3);try{const e=r("aes-128-cbc",n,t);let f=e.update(o);return f=Buffer.concat([f,e.final()]),f}catch{return"<decryption failed>"}}export function chromeTimeToDate(r){if(!r||0===r||0n===r)return"N/A";try{const t=BigInt(r),e=Number((t-11644473600000000n)/1000n),n=new Date(e);return isNaN(n.getTime())?"N/A":n.toLocaleString()}catch{return"N/A"}}
|
|
1
|
+
import{createDecipheriv as r}from"node:crypto";const t=Buffer.alloc(16," ");export function decryptValue(r,t){const e=decryptValueBuffer(r,t);return Buffer.isBuffer(e)?e.toString("utf-8"):e}export function decryptValueBuffer(e,n){if(!e||0===e.length)return Buffer.alloc(0);const f=Buffer.isBuffer(e)?e:Buffer.from(e),u=f.subarray(0,3).toString("utf-8");if("v10"!==u&&"v11"!==u)return"<unsupported encryption format>";const o=f.subarray(3);try{const e=r("aes-128-cbc",n,t);let f=e.update(o);return f=Buffer.concat([f,e.final()]),f}catch{return"<decryption failed>"}}export function decryptValueBufferGcm(t,e){if(!t||0===t.length)return Buffer.alloc(0);const n=Buffer.isBuffer(t)?t:Buffer.from(t),f=n.subarray(0,3).toString("utf-8");if("v10"!==f&&"v11"!==f)return"<unsupported encryption format>";if(n.length<31)return"<decryption failed>";const u=n.subarray(3,15),o=n.subarray(n.length-16),c=n.subarray(15,n.length-16);try{const t=r("aes-256-gcm",e,u);return t.setAuthTag(o),Buffer.concat([t.update(c),t.final()])}catch{return"<decryption failed>"}}export function chromeTimeToDate(r){if(!r||0===r||0n===r)return"N/A";try{const t=BigInt(r),e=Number((t-11644473600000000n)/1000n),n=new Date(e);return isNaN(n.getTime())?"N/A":n.toLocaleString()}catch{return"N/A"}}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ChromePlatform } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* macOS backend.
|
|
4
|
+
*
|
|
5
|
+
* Key source: the "Chrome Safe Storage" password from the login Keychain,
|
|
6
|
+
* stretched with PBKDF2 (1003 iterations) — implemented in `core/keychain.ts`.
|
|
7
|
+
* Cipher: AES-128-CBC with a fixed 16-space IV — implemented in `core/crypto.ts`.
|
|
8
|
+
* Both are reused here verbatim so existing macOS behavior and tests are intact.
|
|
9
|
+
*/
|
|
10
|
+
export declare const darwin: ChromePlatform;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{homedir as o}from"node:os";import{join as r}from"node:path";import{deriveEncryptionKey as e,clearKeychainCache as t}from"../keychain.js";import{decryptValueBuffer as p}from"../crypto.js";export const darwin={name:"darwin",getDefaultChromeDir:()=>r(o(),"Library","Application Support","Google","Chrome"),getDecryptionKey:()=>e(),decrypt:(o,r)=>p(o,r),clearCache(){t()}};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform selection + facades.
|
|
3
|
+
*
|
|
4
|
+
* This is the ONLY place that reads `process.platform`. Everything else in the
|
|
5
|
+
* codebase imports the facades below (`getPlatformChromeDir`, `deriveEncryptionKey`,
|
|
6
|
+
* `decryptValue`, `decryptValueBuffer`, `clearKeyCache`) and stays OS-agnostic.
|
|
7
|
+
*/
|
|
8
|
+
import type { ChromePlatform } from "./types.js";
|
|
9
|
+
export type { ChromePlatform } from "./types.js";
|
|
10
|
+
/**
|
|
11
|
+
* Returns the backend for the current OS, cached after first call.
|
|
12
|
+
* @throws On an unsupported platform.
|
|
13
|
+
*/
|
|
14
|
+
export declare function selectPlatform(): ChromePlatform;
|
|
15
|
+
/** Default Chrome User Data directory for the current OS. */
|
|
16
|
+
export declare function getPlatformChromeDir(): string;
|
|
17
|
+
/**
|
|
18
|
+
* Returns the symmetric key for decrypting this profile's secrets.
|
|
19
|
+
* @param chromeDir - Needed on Windows, where the key lives in `Local State`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function deriveEncryptionKey(chromeDir?: string): Buffer;
|
|
22
|
+
/** Decrypts a Chrome blob and returns the plaintext bytes (or a placeholder string). */
|
|
23
|
+
export declare function decryptValueBuffer(blob: Buffer | Uint8Array | null | undefined, key: Buffer): Buffer | string;
|
|
24
|
+
/** Decrypts a Chrome blob and returns the plaintext as a UTF-8 string. */
|
|
25
|
+
export declare function decryptValue(blob: Buffer | Uint8Array | null | undefined, key: Buffer): string;
|
|
26
|
+
/** Clears the active backend's cached key material. */
|
|
27
|
+
export declare function clearKeyCache(): void;
|
|
@@ -0,0 +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()}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ChromePlatform } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Linux backend.
|
|
4
|
+
*
|
|
5
|
+
* Key source: "Chrome Safe Storage" from the Secret Service (gnome-keyring /
|
|
6
|
+
* KWallet) via `secret-tool`, or the hardcoded "peanuts" fallback, stretched
|
|
7
|
+
* with PBKDF2 (1 iteration).
|
|
8
|
+
* Cipher: AES-128-CBC with a fixed 16-space IV (same primitive as macOS).
|
|
9
|
+
*/
|
|
10
|
+
export declare const linux: ChromePlatform;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{execFileSync as o}from"node:child_process";import{homedir as t}from"node:os";import{join as e}from"node:path";import{pbkdf2Sync as r}from"node:crypto";import{decryptValueBuffer as n}from"../crypto.js";const p=Buffer.from("saltysalt");let c=null;export const linux={name:"linux",getDefaultChromeDir(){const o=process.env.XDG_CONFIG_HOME||e(t(),".config");return e(o,"google-chrome")},getDecryptionKey:()=>(null!==c||(c=r(function(){try{const t=o("secret-tool",["lookup","application","chrome"],{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim();if(t)return t}catch{}return"peanuts"}(),p,1,16,"sha1")),c),decrypt:(o,t)=>n(o,t),clearCache(){c=null}};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The platform-specific backend contract.
|
|
3
|
+
*
|
|
4
|
+
* Everything that differs between operating systems is bundled here because the
|
|
5
|
+
* three concerns are coupled — a Windows key only works with the Windows cipher,
|
|
6
|
+
* etc. Each backend (darwin/win32/linux) implements this once; the rest of the
|
|
7
|
+
* codebase stays platform-agnostic and talks to the selected backend through the
|
|
8
|
+
* facades in `./index.ts`.
|
|
9
|
+
*/
|
|
10
|
+
export interface ChromePlatform {
|
|
11
|
+
/** Node platform identifier this backend handles (e.g. "darwin", "win32", "linux"). */
|
|
12
|
+
readonly name: string;
|
|
13
|
+
/** Absolute path to Chrome's User Data directory for this OS. */
|
|
14
|
+
getDefaultChromeDir(): string;
|
|
15
|
+
/**
|
|
16
|
+
* Returns the symmetric key used to decrypt this profile's secrets, ready to
|
|
17
|
+
* hand to {@link decrypt}. May read the OS key store and is cached internally.
|
|
18
|
+
*
|
|
19
|
+
* @param chromeDir - User Data dir, needed on platforms (Windows) where the
|
|
20
|
+
* key lives inside `Local State`. Defaults to {@link getDefaultChromeDir}.
|
|
21
|
+
* @throws An actionable Error if the key cannot be obtained.
|
|
22
|
+
*/
|
|
23
|
+
getDecryptionKey(chromeDir?: string): Buffer;
|
|
24
|
+
/**
|
|
25
|
+
* Decrypts a single Chrome `v10`/`v11` blob using this platform's scheme
|
|
26
|
+
* (AES-128-CBC on macOS/Linux, AES-256-GCM on Windows).
|
|
27
|
+
*
|
|
28
|
+
* @returns The plaintext bytes, or a `<...>` placeholder string on a
|
|
29
|
+
* recognized but undecryptable blob (never throws).
|
|
30
|
+
*/
|
|
31
|
+
decrypt(blob: Buffer, key: Buffer): Buffer | string;
|
|
32
|
+
/** Clears any cached key material. Useful for tests / re-authentication. */
|
|
33
|
+
clearCache(): void;
|
|
34
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ChromePlatform } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Windows backend.
|
|
4
|
+
*
|
|
5
|
+
* Key source: `os_crypt.encrypted_key` in Local State — base64, "DPAPI"-prefixed,
|
|
6
|
+
* unprotected via DPAPI into a raw 32-byte AES-256 key (no PBKDF2 on Windows).
|
|
7
|
+
* Cipher: AES-256-GCM (nonce + tag carried in each blob).
|
|
8
|
+
*
|
|
9
|
+
* Note: Chrome 127+ adds app-bound ("v20") keys requiring elevation; those are
|
|
10
|
+
* not handled here. Standard v10/v11 cookies and passwords decrypt fine.
|
|
11
|
+
*/
|
|
12
|
+
export declare const win32: ChromePlatform;
|
|
@@ -0,0 +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 a=Buffer.from("DPAPI","ascii");let c=null;export const win32={name:"win32",getDefaultChromeDir(){const e=process.env.LOCALAPPDATA||(process.env.USERPROFILE?o(process.env.USERPROFILE,"AppData","Local"):"");return o(e,"Google","Chrome","User Data")},getDecryptionKey(n){if(null!==c)return c;const s=n||this.getDefaultChromeDir(),i=o(s,"Local State");if(!r(i))throw new Error(`Could not find Chrome's "Local State" at ${i}.\nRun Google Chrome at least once so it creates the encryption key.`);let p;try{const e=t(i,"utf-8");p=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(!p)throw new Error("No os_crypt.encrypted_key found in Local State.\nThis profile may predate Chrome's encrypted-key scheme.");const l=Buffer.from(p,"base64");if(!l.subarray(0,a.length).equals(a))throw new Error('Unexpected encrypted_key format (missing "DPAPI" prefix).');return c=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")}(l.subarray(a.length)),c},decrypt:(e,r)=>n(e,r),clearCache(){c=null}};
|
package/dist/src/index.d.ts
CHANGED
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
* const profiles = discoverProfiles();
|
|
12
12
|
*/
|
|
13
13
|
export { getDefaultChromeDir, discoverProfiles, getProfileMetadata, getProfileStates, resolveProfilePath, resolveTargetProfiles, type DiscoveredProfile, type ProfileMetadata, type ProfileState, type ResolveProfilesOptions, } from "./core/chrome.js";
|
|
14
|
-
export {
|
|
15
|
-
export {
|
|
14
|
+
export { selectPlatform, getPlatformChromeDir, deriveEncryptionKey, decryptValue, decryptValueBuffer, clearKeyCache, type ChromePlatform, } from "./core/platform/index.js";
|
|
15
|
+
export { getChromeKeychainPassword, clearKeychainCache, } from "./core/keychain.js";
|
|
16
|
+
export { chromeTimeToDate, } from "./core/crypto.js";
|
|
16
17
|
export { queryDatabase, tableExists, } from "./core/db.js";
|
|
17
18
|
export { getPasswords } from "./modules/passwords.js";
|
|
18
19
|
export { getBookmarks } from "./modules/bookmarks.js";
|
package/dist/src/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{getDefaultChromeDir,discoverProfiles,getProfileMetadata,getProfileStates,resolveProfilePath,resolveTargetProfiles}from"./core/chrome.js";export{
|
|
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 +1 @@
|
|
|
1
|
-
import{join as e}from"node:path";import{resolveTargetProfiles as t}from"../core/chrome.js";import{deriveEncryptionKey as o}from"../core/
|
|
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 i}from"../core/crypto.js";import{queryDatabase as a,tableExists as r}from"../core/db.js";export function getAutofillEntries({chromeDir:o,profile:n,search:d,limit:c=100}={}){const s=t({chromeDir:o,profile:n}),u=[];for(const t of s){const o=e(t.path,"Web Data");try{if(!r(o,"autofill"))continue;let e="SELECT name, value, date_created, date_last_used, count\n FROM autofill ORDER BY date_last_used DESC";c&&(e+=` LIMIT ${parseInt(c,10)}`);const n=a(o,e);for(const e of n){const o={profile:t.name,name:e.name||"",value:e.value||"",dateCreated:i(e.date_created?1000000n*BigInt(e.date_created)+11644473600000000n:0),dateLastUsed:i(e.date_last_used?1000000n*BigInt(e.date_last_used)+11644473600000000n:0),count:e.count||0};if(d){const e=d.toLowerCase();if(!(o.name&&o.name.toLowerCase().includes(e)||o.value&&o.value.toLowerCase().includes(e)))continue}u.push(o)}}catch{}}return u}export function getCreditCards({chromeDir:d,profile:c}={}){const s=t({chromeDir:d,profile:c}),u=o(),l=[];for(const t of s){const o=e(t.path,"Web Data");try{if(!r(o,"credit_cards"))continue;const e=a(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,u)),l.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:i(o.date_modified?1000000n*BigInt(o.date_modified)+11644473600000000n:0)})}}catch{}}return l}export function getAddresses({chromeDir:o,profile:n}={}){const d=t({chromeDir:o,profile:n}),c=[];for(const t of d){const o=e(t.path,"Web Data");try{if(!r(o,"autofill_profiles"))continue;const e=a(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=[],s=[];try{r(o,"autofill_profile_names")&&(n=a(o,"SELECT guid, first_name, middle_name, last_name, full_name FROM autofill_profile_names"))}catch{}try{r(o,"autofill_profile_emails")&&(d=a(o,"SELECT guid, email FROM autofill_profile_emails"))}catch{}try{r(o,"autofill_profile_phones")&&(s=a(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 s)f.set(e.guid,e.number||"");for(const o of e)c.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:i(o.date_modified?1000000n*BigInt(o.date_modified)+11644473600000000n:0)})}catch{}}return c}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as t}from"node:path";import{createHash as e}from"node:crypto";import{resolveTargetProfiles as
|
|
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,domain:f,url:m,decrypt:d=!0,limit:_=100}={}){const y=o({chromeDir:e,profile:s}),g=d?n():null,k=[],j=u(m);for(const e of y){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(f)n.push("host_key LIKE ?"),s.push(`%${f}%`);else if(j){const t=p(j.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",_&&!m&&(t+=` LIMIT ${parseInt(_,10)}`);const u=i(o,t,s);for(const t of u){if(j){if(!h(j.hostname,t.host_key||""))continue;if(!l(j.pathname||"/",t.path||"/"))continue;if(t.is_secure&&"https:"!==j.protocol)continue}let o=t.value||"";!o&&t.encrypted_value&&g&&(o=a(t.encrypted_value,g,t.host_key||"")),k.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 _&&m?k.slice(0,parseInt(_,10)):k}export const _test={parseURL:u,getPossibleHostKeys:p,domainMatch:h,pathMatch:l};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as r}from"node:path";import{resolveTargetProfiles as o}from"../core/chrome.js";import{deriveEncryptionKey as e}from"../core/
|
|
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 l=o({chromeDir:i,profile:u}),m=e(),f=[];for(const o of l){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,m),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}f.push(r)}}catch(r){f.push({profile:o.name,error:r.message})}}return f.filter(r=>!r.error)}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chrome-tools",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "CLI & Node.js library for chrome",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/src/index.js",
|
|
@@ -39,7 +39,11 @@
|
|
|
39
39
|
"autofill",
|
|
40
40
|
"browser",
|
|
41
41
|
"macos",
|
|
42
|
+
"windows",
|
|
43
|
+
"linux",
|
|
44
|
+
"cross-platform",
|
|
42
45
|
"keychain",
|
|
46
|
+
"dpapi",
|
|
43
47
|
"cli"
|
|
44
48
|
],
|
|
45
49
|
"author": "",
|