chrome-tools 1.1.5 → 1.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,6 +17,16 @@ export declare class KeychainAccessError extends Error {
17
17
  * Keychain permission (denied/dismissed prompt, locked, or non-interactive).
18
18
  */
19
19
  export declare function isKeychainAccessDenied(err: unknown): err is KeychainAccessError;
20
+ export type KeychainPasswordProvider = (service: string) => string;
21
+ /**
22
+ * Returns the current global keychain password provider hook.
23
+ */
24
+ export declare function getKeychainPasswordProvider(): KeychainPasswordProvider | null;
25
+ /**
26
+ * Sets the global keychain password provider hook, allowing downstream apps (like Electron)
27
+ * to intercept keychain password lookups and resolve them (e.g. via native node-keytar).
28
+ */
29
+ export declare function setKeychainPasswordProvider(provider: KeychainPasswordProvider | null): void;
20
30
  /**
21
31
  * Retrieves a browser's "Safe Storage" password from the macOS Keychain.
22
32
  * Caches the result per service for subsequent calls within the same process.
@@ -1 +1 @@
1
- import{execFileSync as e}from"node:child_process";import{pbkdf2Sync as n}from"node:crypto";const t=Buffer.from("saltysalt"),r="Chrome Safe Storage",s=44,o=36,i=45;export class KeychainAccessError extends Error{service;kind;constructor(e,n,t){super(e),this.name="KeychainAccessError",this.service=n,this.kind=t}get isAccessDenied(){return"access-denied"===this.kind}}export function isKeychainAccessDenied(e){return e instanceof KeychainAccessError&&e.isAccessDenied}const c=new Map,a=new Map;function h(e,n,t){const r="See docs/remote-keychain-access.md for non-interactive / SSH setup.";let c,a;n===s||/could not be found/i.test(t)?(c="not-found",a=`No '${e}' item found in the Keychain.\nRun this browser at least once on this machine so it creates the key.`):n===o||/interaction.*not allowed/i.test(t)?(c="access-denied",a="The Keychain needs a GUI authorization prompt that can't be shown here "+(Boolean(process.env.SSH_CONNECTION||process.env.SSH_TTY)||!process.env.TERM_PROGRAM?"(you appear to be on a remote/SSH session).\n":".\n")+'Authorize once from a GUI Terminal (click "Always Allow"), then unlock the\nkeychain in this session:\n security unlock-keychain ~/Library/Keychains/login.keychain-db\n'+r):n===i?(c="access-denied",a=`Keychain authorization failed (wrong password or denied).\nTry: security unlock-keychain ~/Library/Keychains/login.keychain-db\n${r}`):(c="access-denied",a=`macOS showed a Keychain access prompt that was denied or dismissed.\nRe-run the command and click "Allow" (or "Always Allow"), or authorize once with:\n security find-generic-password -w -s "${e}"\n${r}`);const h=[null!==n?`exit ${n}`:null,t||null].filter(Boolean).join(": ");return new KeychainAccessError(`Could not retrieve ${e} key from macOS Keychain.\n${a}`+(h?`\n(security: ${h})`:""),e,c)}export function getKeychainPassword(n=r){const t=c.get(n);if(void 0!==t)return t;let s;try{s=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(!s)throw new KeychainAccessError(`Keychain returned an empty ${n} key.\nThe item may exist with no password. Re-run the browser to recreate it.`,n,"empty");return c.set(n,s),s}export function getChromeKeychainPassword(){return getKeychainPassword(r)}export function deriveEncryptionKey(e=r,s){const o=a.get(e);if(void 0!==o)return o;const i=s||getKeychainPassword(e);if(!i)throw new Error("Keychain password cannot be empty for key derivation.");const c=n(i,t,1003,16,"sha1");return a.set(e,c),c}export function clearKeychainCache(){c.clear(),a.clear()}
1
+ import{execFileSync as e}from"node:child_process";import{pbkdf2Sync as n}from"node:crypto";const t=Buffer.from("saltysalt"),r="Chrome Safe Storage",o=44,s=36,i=45;export class KeychainAccessError extends Error{service;kind;constructor(e,n,t){super(e),this.name="KeychainAccessError",this.service=n,this.kind=t}get isAccessDenied(){return"access-denied"===this.kind}}export function isKeychainAccessDenied(e){return e instanceof KeychainAccessError&&e.isAccessDenied}const c=new Map,a=new Map;function d(e,n,t){const r="See docs/remote-keychain-access.md for non-interactive / SSH setup.";let c,a;n===o||/could not be found/i.test(t)?(c="not-found",a=`No '${e}' item found in the Keychain.\nRun this browser at least once on this machine so it creates the key.`):n===s||/interaction.*not allowed/i.test(t)?(c="access-denied",a="The Keychain needs a GUI authorization prompt that can't be shown here "+(Boolean(process.env.SSH_CONNECTION||process.env.SSH_TTY)||!process.env.TERM_PROGRAM?"(you appear to be on a remote/SSH session).\n":".\n")+'Authorize once from a GUI Terminal (click "Always Allow"), then unlock the\nkeychain in this session:\n security unlock-keychain ~/Library/Keychains/login.keychain-db\n'+r):n===i?(c="access-denied",a=`Keychain authorization failed (wrong password or denied).\nTry: security unlock-keychain ~/Library/Keychains/login.keychain-db\n${r}`):(c="access-denied",a=`macOS showed a Keychain access prompt that was denied or dismissed.\nRe-run the command and click "Allow" (or "Always Allow"), or authorize once with:\n security find-generic-password -w -s "${e}"\n${r}`);const d=[null!==n?`exit ${n}`:null,t||null].filter(Boolean).join(": ");return new KeychainAccessError(`Could not retrieve ${e} key from macOS Keychain.\n${a}`+(d?`\n(security: ${d})`:""),e,c)}let h=null;export function getKeychainPasswordProvider(){return h}export function setKeychainPasswordProvider(e){h=e}export function getKeychainPassword(n=r){const t=c.get(n);if(void 0!==t)return t;let o;if(h)o=h(n);else try{o=e("security",["find-generic-password","-w","-s",n],{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim()}catch(e){const{status:t,stderr:r}=function(e){const n=e;return{status:"number"==typeof n?.status?n.status:null,stderr:(n?.stderr??"").toString().trim()}}(e);throw d(n,t,r)}if(!o)throw new KeychainAccessError(`Keychain returned an empty ${n} key.\nThe item may exist with no password. Re-run the browser to recreate it.`,n,"empty");return c.set(n,o),o}export function getChromeKeychainPassword(){return getKeychainPassword(r)}export function deriveEncryptionKey(e=r,o){const s=a.get(e);if(void 0!==s)return s;const i=o||getKeychainPassword(e);if(!i)throw new Error("Keychain password cannot be empty for key derivation.");const c=n(i,t,1003,16,"sha1");return a.set(e,c),c}export function clearKeychainCache(){c.clear(),a.clear()}
@@ -1 +1 @@
1
- import{execFileSync as 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
+ import{execFileSync as r}from"node:child_process";import{homedir as o}from"node:os";import{join as t}from"node:path";import{pbkdf2Sync as e}from"node:crypto";import{decryptValueBuffer as n}from"../crypto.js";import{getActiveBrowser as i}from"../browsers.js";import{getKeychainPasswordProvider as p}from"../keychain.js";const c=Buffer.from("saltysalt"),s=new Map;export const linux={name:"linux",getDefaultChromeDir(){const r=process.env.XDG_CONFIG_HOME||t(o(),".config");return t(r,...i().dirs.linux.split("/"))},getDecryptionKey(){const o=i().linuxSecretApp||"chrome",t=s.get(o);if(void 0!==t)return t;const n=e(function(o){const t=p();if(t)return t(o);try{const t=r("secret-tool",["lookup","application",o],{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim();if(t)return t}catch{}return"peanuts"}(o),c,1,16,"sha1");return s.set(o,n),n},decrypt:(r,o)=>n(r,o),clearCache(){s.clear()}};
@@ -13,7 +13,7 @@
13
13
  export { getDefaultChromeDir, discoverProfiles, getProfileMetadata, getProfileStates, resolveProfilePath, resolveTargetProfiles, detectInstalledBrowsers, type DiscoveredProfile, type ProfileMetadata, type ProfileState, type ResolveProfilesOptions, type InstalledBrowser, } from "./core/chrome.js";
14
14
  export { BROWSERS, listBrowsers, resolveBrowser, getActiveBrowser, setActiveBrowser, type BrowserId, type BrowserDescriptor, } from "./core/browsers.js";
15
15
  export { selectPlatform, getPlatformChromeDir, deriveEncryptionKey, decryptValue, decryptValueBuffer, clearKeyCache, type ChromePlatform, } from "./core/platform/index.js";
16
- export { getKeychainPassword, getChromeKeychainPassword, clearKeychainCache, } from "./core/keychain.js";
16
+ export { getKeychainPassword, getChromeKeychainPassword, clearKeychainCache, setKeychainPasswordProvider, getKeychainPasswordProvider, type KeychainPasswordProvider, } from "./core/keychain.js";
17
17
  export { chromeTimeToDate, } from "./core/crypto.js";
18
18
  export { queryDatabase, tableExists, } from "./core/db.js";
19
19
  export { getPasswords } from "./modules/passwords.js";
package/dist/src/index.js CHANGED
@@ -1 +1 @@
1
- export{getDefaultChromeDir,discoverProfiles,getProfileMetadata,getProfileStates,resolveProfilePath,resolveTargetProfiles,detectInstalledBrowsers}from"./core/chrome.js";export{BROWSERS,listBrowsers,resolveBrowser,getActiveBrowser,setActiveBrowser}from"./core/browsers.js";export{selectPlatform,getPlatformChromeDir,deriveEncryptionKey,decryptValue,decryptValueBuffer,clearKeyCache}from"./core/platform/index.js";export{getKeychainPassword,getChromeKeychainPassword,clearKeychainCache}from"./core/keychain.js";export{chromeTimeToDate}from"./core/crypto.js";export{queryDatabase,tableExists}from"./core/db.js";export{getPasswords}from"./modules/passwords.js";export{getBookmarks}from"./modules/bookmarks.js";export{getHistory,getSearchTerms}from"./modules/history.js";export{getCookies}from"./modules/cookies.js";export{getLocalStorage}from"./modules/local-storage.js";export{getAutofillEntries,getCreditCards,getAddresses}from"./modules/autofill.js";export{getDownloads}from"./modules/downloads.js";export{getExtensions}from"./modules/extensions.js";export{getSearchEngines}from"./modules/search-engines.js";export{getTopSites}from"./modules/top-sites.js";
1
+ export{getDefaultChromeDir,discoverProfiles,getProfileMetadata,getProfileStates,resolveProfilePath,resolveTargetProfiles,detectInstalledBrowsers}from"./core/chrome.js";export{BROWSERS,listBrowsers,resolveBrowser,getActiveBrowser,setActiveBrowser}from"./core/browsers.js";export{selectPlatform,getPlatformChromeDir,deriveEncryptionKey,decryptValue,decryptValueBuffer,clearKeyCache}from"./core/platform/index.js";export{getKeychainPassword,getChromeKeychainPassword,clearKeychainCache,setKeychainPasswordProvider,getKeychainPasswordProvider}from"./core/keychain.js";export{chromeTimeToDate}from"./core/crypto.js";export{queryDatabase,tableExists}from"./core/db.js";export{getPasswords}from"./modules/passwords.js";export{getBookmarks}from"./modules/bookmarks.js";export{getHistory,getSearchTerms}from"./modules/history.js";export{getCookies}from"./modules/cookies.js";export{getLocalStorage}from"./modules/local-storage.js";export{getAutofillEntries,getCreditCards,getAddresses}from"./modules/autofill.js";export{getDownloads}from"./modules/downloads.js";export{getExtensions}from"./modules/extensions.js";export{getSearchEngines}from"./modules/search-engines.js";export{getTopSites}from"./modules/top-sites.js";
@@ -17,6 +17,7 @@ export interface CreditCardOptions {
17
17
  chromeDir?: string;
18
18
  profile?: string;
19
19
  browser?: string;
20
+ decryptionKey?: Buffer;
20
21
  }
21
22
  export interface CreditCardEntry {
22
23
  profile: string;
@@ -65,7 +66,7 @@ export declare function getAutofillEntries({ chromeDir, profile, browser, search
65
66
  * @param options - Targeting options.
66
67
  * @returns Array of credit card entries.
67
68
  */
68
- export declare function getCreditCards({ chromeDir, profile, browser }?: CreditCardOptions): CreditCardEntry[];
69
+ export declare function getCreditCards({ chromeDir, profile, browser, decryptionKey }?: CreditCardOptions): CreditCardEntry[];
69
70
  /**
70
71
  * Retrieves saved addresses from Chrome's Web Data database.
71
72
  *
@@ -1 +1 @@
1
- import{join as e}from"node:path";import{resolveTargetProfiles as t}from"../core/chrome.js";import{deriveEncryptionKey as o,decryptValue as n}from"../core/platform/index.js";import{chromeTimeToDate as r}from"../core/crypto.js";import{queryDatabase as i,tableExists as a}from"../core/db.js";export function getAutofillEntries({chromeDir:o,profile:n,browser:d,search:c,limit:s=100}={}){const u=t({chromeDir:o,profile:n,browser:d}),l=[];for(const t of u){const o=e(t.path,"Web Data");try{if(!a(o,"autofill"))continue;let e="SELECT name, value, date_created, date_last_used, count\n FROM autofill ORDER BY date_last_used DESC";s&&(e+=` LIMIT ${parseInt(s,10)}`);const n=i(o,e);for(const e of n){const o={profile:t.name,name:e.name||"",value:e.value||"",dateCreated:r(e.date_created?1000000n*BigInt(e.date_created)+11644473600000000n:0),dateLastUsed:r(e.date_last_used?1000000n*BigInt(e.date_last_used)+11644473600000000n:0),count:e.count||0};if(c){const e=c.toLowerCase();if(!(o.name&&o.name.toLowerCase().includes(e)||o.value&&o.value.toLowerCase().includes(e)))continue}l.push(o)}}catch{}}return l}export function getCreditCards({chromeDir:d,profile:c,browser:s}={}){const 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
+ import{join as e}from"node:path";import{resolveTargetProfiles as t}from"../core/chrome.js";import{deriveEncryptionKey as o,decryptValue as n}from"../core/platform/index.js";import{chromeTimeToDate as r}from"../core/crypto.js";import{queryDatabase as i,tableExists as a}from"../core/db.js";export function getAutofillEntries({chromeDir:o,profile:n,browser:d,search:c,limit:s=100}={}){const u=t({chromeDir:o,profile:n,browser:d}),l=[];for(const t of u){const o=e(t.path,"Web Data");try{if(!a(o,"autofill"))continue;let e="SELECT name, value, date_created, date_last_used, count\n FROM autofill ORDER BY date_last_used DESC";s&&(e+=` LIMIT ${parseInt(s,10)}`);const n=i(o,e);for(const e of n){const o={profile:t.name,name:e.name||"",value:e.value||"",dateCreated:r(e.date_created?1000000n*BigInt(e.date_created)+11644473600000000n:0),dateLastUsed:r(e.date_last_used?1000000n*BigInt(e.date_last_used)+11644473600000000n:0),count:e.count||0};if(c){const e=c.toLowerCase();if(!(o.name&&o.name.toLowerCase().includes(e)||o.value&&o.value.toLowerCase().includes(e)))continue}l.push(o)}}catch{}}return l}export function getCreditCards({chromeDir:d,profile:c,browser:s,decryptionKey:u}={}){const l=t({chromeDir:d,profile:c,browser:s}),f=u||o(),m=[];for(const t of l){const o=e(t.path,"Web Data");try{if(!a(o,"credit_cards"))continue;const e=i(o,"SELECT guid, name_on_card, expiration_month, expiration_year,\n card_number_encrypted, date_modified, origin, use_count,\n use_date, nickname\n FROM credit_cards ORDER BY use_date DESC");for(const o of e){let e="";o.card_number_encrypted&&(e=n(o.card_number_encrypted,f)),m.push({profile:t.name,guid:o.guid||"",nameOnCard:o.name_on_card||"",expirationMonth:o.expiration_month||0,expirationYear:o.expiration_year||0,cardNumber:e,nickname:o.nickname||"",origin:o.origin||"",useCount:o.use_count||0,dateModified:r(o.date_modified?1000000n*BigInt(o.date_modified)+11644473600000000n:0)})}}catch{}}return m}export function getAddresses({chromeDir:o,profile:n,browser:d}={}){const c=t({chromeDir:o,profile:n,browser:d}),s=[];for(const t of c){const o=e(t.path,"Web Data");try{if(!a(o,"autofill_profiles"))continue;const e=i(o,"SELECT guid, company_name, street_address, dependent_locality,\n city, state, zipcode, country_code, date_modified,\n origin, use_count, use_date, language_code\n FROM autofill_profiles ORDER BY use_date DESC");let n=[],d=[],c=[];try{a(o,"autofill_profile_names")&&(n=i(o,"SELECT guid, first_name, middle_name, last_name, full_name FROM autofill_profile_names"))}catch{}try{a(o,"autofill_profile_emails")&&(d=i(o,"SELECT guid, email FROM autofill_profile_emails"))}catch{}try{a(o,"autofill_profile_phones")&&(c=i(o,"SELECT guid, number FROM autofill_profile_phones"))}catch{}const u=new Map;for(const e of n)u.set(e.guid,e.full_name||[e.first_name,e.middle_name,e.last_name].filter(Boolean).join(" "));const l=new Map;for(const e of d)l.set(e.guid,e.email||"");const f=new Map;for(const e of c)f.set(e.guid,e.number||"");for(const o of e)s.push({profile:t.name,guid:o.guid||"",fullName:u.get(o.guid)||"",email:l.get(o.guid)||"",phone:f.get(o.guid)||"",companyName:o.company_name||"",streetAddress:o.street_address||"",city:o.city||"",state:o.state||"",zipcode:o.zipcode||"",countryCode:o.country_code||"",origin:o.origin||"",useCount:o.use_count||0,dateModified:r(o.date_modified?1000000n*BigInt(o.date_modified)+11644473600000000n:0)})}catch{}}return s}
@@ -6,6 +6,7 @@ export interface CookieOptions {
6
6
  url?: string;
7
7
  decrypt?: boolean;
8
8
  limit?: number;
9
+ decryptionKey?: Buffer;
9
10
  }
10
11
  export interface CookieEntry {
11
12
  profile: string;
@@ -31,7 +32,7 @@ declare function pathMatch(urlPath: string, cookiePath: string): boolean;
31
32
  * @param options - Targeting and filtering options.
32
33
  * @returns Array of cookie entries.
33
34
  */
34
- export declare function getCookies({ chromeDir, profile, browser, domain, url, decrypt, limit, }?: CookieOptions): CookieEntry[];
35
+ export declare function getCookies({ chromeDir, profile, browser, domain, url, decrypt, limit, decryptionKey, }?: CookieOptions): CookieEntry[];
35
36
  export declare const _test: {
36
37
  parseURL: typeof parseURL;
37
38
  getPossibleHostKeys: typeof getPossibleHostKeys;
@@ -1 +1 @@
1
- import{join as t}from"node:path";import{createHash as e}from"node:crypto";import{resolveTargetProfiles as o}from"../core/chrome.js";import{deriveEncryptionKey as n,decryptValueBuffer as s}from"../core/platform/index.js";import{chromeTimeToDate as r}from"../core/crypto.js";import{queryDatabase as i,tableExists as c}from"../core/db.js";function a(t,o,n){const r=s(t,o);if(!Buffer.isBuffer(r))return r;const i=e("sha256").update(n).digest();return(r.length>=i.length&&r.subarray(0,i.length).equals(i)?r.subarray(i.length):r).toString("utf-8")}function u(t){if(!t)return null;try{return new URL(t)}catch{try{if(!/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(t))return new URL("https://"+t)}catch{}}return null}function p(t){const e=new Set;e.add(t),e.add("."+t);const o=t.split(".");for(let t=1;t<o.length;t++){const n=o.slice(t).join(".");e.add(n),e.add("."+n)}return Array.from(e)}function h(t,e){return t===e||!(!e.startsWith(".")||t!==e.slice(1)&&!t.endsWith(e))}function l(t,e){const o=t||"/",n=e||"/";return"/"===n||(o===n||!(!o.startsWith(n)||!n.endsWith("/")&&"/"!==o[n.length]))}export function getCookies({chromeDir:e,profile:s,browser:f,domain:m,url:d,decrypt:_=!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
+ import{join as t}from"node:path";import{createHash as e}from"node:crypto";import{resolveTargetProfiles as o}from"../core/chrome.js";import{deriveEncryptionKey as n,decryptValueBuffer as s}from"../core/platform/index.js";import{chromeTimeToDate as r}from"../core/crypto.js";import{queryDatabase as i,tableExists as c}from"../core/db.js";function a(t,o,n){const r=s(t,o);if(!Buffer.isBuffer(r))return r;const i=e("sha256").update(n).digest();return(r.length>=i.length&&r.subarray(0,i.length).equals(i)?r.subarray(i.length):r).toString("utf-8")}function u(t){if(!t)return null;try{return new URL(t)}catch{try{if(!/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(t))return new URL("https://"+t)}catch{}}return null}function p(t){const e=new Set;e.add(t),e.add("."+t);const o=t.split(".");for(let t=1;t<o.length;t++){const n=o.slice(t).join(".");e.add(n),e.add("."+n)}return Array.from(e)}function h(t,e){return t===e||!(!e.startsWith(".")||t!==e.slice(1)&&!t.endsWith(e))}function l(t,e){const o=t||"/",n=e||"/";return"/"===n||(o===n||!(!o.startsWith(n)||!n.endsWith("/")&&"/"!==o[n.length]))}export function getCookies({chromeDir:e,profile:s,browser:f,domain:m,url:d,decrypt:y=!0,limit:_=100,decryptionKey:g}={}){const k=o({chromeDir:e,profile:s,browser:f}),j=y?g||n():null,x=[],R=u(d);for(const e of k){const o=t(e.path,"Cookies");try{if(!c(o,"cookies"))continue;let t="SELECT host_key, name, value, encrypted_value, path,\n expires_utc, is_secure, is_httponly, samesite,\n creation_utc, last_access_utc\n FROM cookies";const n=[],s=[];if(m)n.push("host_key LIKE ?"),s.push(`%${m}%`);else if(R){const t=p(R.hostname),e=t.map(()=>"?").join(", ");n.push(`host_key IN (${e})`),s.push(...t)}n.length>0&&(t+=` WHERE ${n.join(" AND ")}`),t+=" ORDER BY host_key, name",_&&!d&&(t+=` LIMIT ${parseInt(_,10)}`);const u=i(o,t,s);for(const t of u){if(R){if(!h(R.hostname,t.host_key||""))continue;if(!l(R.pathname||"/",t.path||"/"))continue;if(t.is_secure&&"https:"!==R.protocol)continue}let o=t.value||"";!o&&t.encrypted_value&&j&&(o=a(t.encrypted_value,j,t.host_key||"")),x.push({profile:e.name,domain:t.host_key||"",name:t.name||"",value:o,path:t.path||"/",expires:r(t.expires_utc||0),secure:Boolean(t.is_secure),httpOnly:Boolean(t.is_httponly),sameSite:["unspecified","lax","strict","none"][t.samesite]||"unspecified",created:r(t.creation_utc||0),lastAccessed:r(t.last_access_utc||0)})}}catch{}}return _&&d?x.slice(0,parseInt(_,10)):x}export const _test={parseURL:u,getPossibleHostKeys:p,domainMatch:h,pathMatch:l};
@@ -3,6 +3,7 @@ export interface PasswordOptions {
3
3
  profile?: string;
4
4
  browser?: string;
5
5
  search?: string;
6
+ decryptionKey?: Buffer;
6
7
  }
7
8
  /** Which on-disk store a password came from. */
8
9
  export type PasswordSource = "device" | "account";
@@ -24,4 +25,4 @@ export interface PasswordEntry {
24
25
  * @param options - Options for profile selection and filtering.
25
26
  * @returns Array of decrypted password entries.
26
27
  */
27
- export declare function getPasswords({ chromeDir, profile, search, browser }?: PasswordOptions): PasswordEntry[];
28
+ export declare function getPasswords({ chromeDir, profile, search, browser, decryptionKey }?: PasswordOptions): PasswordEntry[];
@@ -1 +1 @@
1
- import{join as r}from"node:path";import{resolveTargetProfiles as o}from"../core/chrome.js";import{deriveEncryptionKey as e,decryptValue as s}from"../core/platform/index.js";import{chromeTimeToDate as t}from"../core/crypto.js";import{queryDatabase as a,tableExists as c}from"../core/db.js";const n=[{file:"Login Data",source:"device"},{file:"Login Data For Account",source:"account"}];export function getPasswords({chromeDir:i,profile:u,search:l,browser:f}={}){const m=o({chromeDir:i,profile:u,browser:f}),p=e(),d=[],_=l?.toLowerCase();for(const o of m)for(const e of n){const n=r(o.path,e.file);try{if(!c(n,"logins"))continue;const r=a(n,"SELECT origin_url, username_value, password_value, date_last_used\n FROM logins ORDER BY origin_url");for(const a of r){const r={profile:o.name,source:e.source,url:a.origin_url,username:a.username_value,password:s(a.password_value,p),lastUsed:t(a.date_last_used||0)};(!_||r.url&&r.url.toLowerCase().includes(_)||r.username&&r.username.toLowerCase().includes(_))&&d.push(r)}}catch(r){d.push({profile:o.name,source:e.source,error:r.message})}}return d.filter(r=>!r.error)}
1
+ import{join as r}from"node:path";import{resolveTargetProfiles as o}from"../core/chrome.js";import{deriveEncryptionKey as e,decryptValue as s}from"../core/platform/index.js";import{chromeTimeToDate as t}from"../core/crypto.js";import{queryDatabase as c,tableExists as n}from"../core/db.js";const a=[{file:"Login Data",source:"device"},{file:"Login Data For Account",source:"account"}];export function getPasswords({chromeDir:i,profile:u,search:l,browser:f,decryptionKey:m}={}){const p=o({chromeDir:i,profile:u,browser:f}),d=m||e(),_=[],g=l?.toLowerCase();for(const o of p)for(const e of a){const a=r(o.path,e.file);try{if(!n(a,"logins"))continue;const r=c(a,"SELECT origin_url, username_value, password_value, date_last_used\n FROM logins ORDER BY origin_url");for(const c of r){const r={profile:o.name,source:e.source,url:c.origin_url,username:c.username_value,password:s(c.password_value,d),lastUsed:t(c.date_last_used||0)};(!g||r.url&&r.url.toLowerCase().includes(g)||r.username&&r.username.toLowerCase().includes(g))&&_.push(r)}}catch(r){_.push({profile:o.name,source:e.source,error:r.message})}}return _.filter(r=>!r.error)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chrome-tools",
3
- "version": "1.1.5",
3
+ "version": "1.1.6",
4
4
  "description": "CLI & Node.js library for chrome",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",