chrome-tools 1.1.15 → 1.1.18

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.
Files changed (58) hide show
  1. package/dist/chrome_abe_decrypt.exe +0 -0
  2. package/dist/cli.js +8837 -0
  3. package/dist/index.d.ts +629 -0
  4. package/dist/index.js +2532 -0
  5. package/package.json +62 -29
  6. package/bin/chrome_abe_decrypt.exe +0 -0
  7. package/dist/bin/chrome_abe_decrypt.exe +0 -0
  8. package/dist/bin/cli.d.ts +0 -9
  9. package/dist/bin/cli.js +0 -590
  10. package/dist/src/core/browsers.d.ts +0 -73
  11. package/dist/src/core/browsers.js +0 -186
  12. package/dist/src/core/chrome.d.ts +0 -124
  13. package/dist/src/core/chrome.js +0 -259
  14. package/dist/src/core/crypto.d.ts +0 -52
  15. package/dist/src/core/crypto.js +0 -152
  16. package/dist/src/core/db.d.ts +0 -22
  17. package/dist/src/core/db.js +0 -227
  18. package/dist/src/core/keychain.d.ts +0 -57
  19. package/dist/src/core/keychain.js +0 -191
  20. package/dist/src/core/limit.d.ts +0 -4
  21. package/dist/src/core/limit.js +0 -12
  22. package/dist/src/core/platform/darwin.d.ts +0 -12
  23. package/dist/src/core/platform/darwin.js +0 -31
  24. package/dist/src/core/platform/index.d.ts +0 -30
  25. package/dist/src/core/platform/index.js +0 -69
  26. package/dist/src/core/platform/linux.d.ts +0 -10
  27. package/dist/src/core/platform/linux.js +0 -64
  28. package/dist/src/core/platform/types.d.ts +0 -45
  29. package/dist/src/core/platform/types.js +0 -1
  30. package/dist/src/core/platform/win32.d.ts +0 -13
  31. package/dist/src/core/platform/win32.js +0 -329
  32. package/dist/src/core/sql.d.ts +0 -2
  33. package/dist/src/core/sql.js +0 -4
  34. package/dist/src/index.d.ts +0 -28
  35. package/dist/src/index.js +0 -34
  36. package/dist/src/modules/autofill.d.ts +0 -68
  37. package/dist/src/modules/autofill.js +0 -170
  38. package/dist/src/modules/bookmarks.d.ts +0 -22
  39. package/dist/src/modules/bookmarks.js +0 -83
  40. package/dist/src/modules/cookies.d.ts +0 -40
  41. package/dist/src/modules/cookies.js +0 -163
  42. package/dist/src/modules/downloads.d.ts +0 -27
  43. package/dist/src/modules/downloads.js +0 -80
  44. package/dist/src/modules/extensions.d.ts +0 -24
  45. package/dist/src/modules/extensions.js +0 -71
  46. package/dist/src/modules/history.d.ts +0 -34
  47. package/dist/src/modules/history.js +0 -83
  48. package/dist/src/modules/local-storage.d.ts +0 -40
  49. package/dist/src/modules/local-storage.js +0 -455
  50. package/dist/src/modules/passwords.d.ts +0 -50
  51. package/dist/src/modules/passwords.js +0 -198
  52. package/dist/src/modules/search-engines.d.ts +0 -21
  53. package/dist/src/modules/search-engines.js +0 -38
  54. package/dist/src/modules/top-sites.d.ts +0 -17
  55. package/dist/src/modules/top-sites.js +0 -33
  56. package/src/native/build.bat +0 -31
  57. package/src/native/chrome_abe_decrypt.c +0 -336
  58. package/src/native/chrome_abe_decrypt.exe +0 -0
@@ -1,31 +0,0 @@
1
- import { homedir } from "node:os";
2
- import { join } from "node:path";
3
- import { deriveEncryptionKey, clearKeychainCache } from "../keychain.js";
4
- import { decryptValueBuffer } from "../crypto.js";
5
- import { getActiveBrowser } from "../browsers.js";
6
- /**
7
- * macOS backend.
8
- *
9
- * Key source: the active browser's "<Browser> Safe Storage" password from the
10
- * login Keychain (e.g. "Chrome Safe Storage", "Microsoft Edge Safe Storage"),
11
- * stretched with PBKDF2 (1003 iterations) — implemented in `core/keychain.ts`.
12
- * Cipher: AES-128-CBC with a fixed 16-space IV — implemented in `core/crypto.ts`.
13
- * The browser only changes *which* Keychain item and data dir are used; the
14
- * crypto is identical across all Chromium forks.
15
- */
16
- export const darwin = {
17
- name: "darwin",
18
- getDefaultChromeDir(browser = getActiveBrowser()) {
19
- return join(homedir(), "Library", "Application Support", ...browser.dirs.darwin.split("/"));
20
- },
21
- getDecryptionKey(_chromeDir, browser = getActiveBrowser()) {
22
- // The Keychain item is global to the machine, so chromeDir is irrelevant here.
23
- return deriveEncryptionKey(browser.safeStorageLabel);
24
- },
25
- decrypt(blob, key) {
26
- return decryptValueBuffer(blob, key);
27
- },
28
- clearCache() {
29
- clearKeychainCache();
30
- },
31
- };
@@ -1,30 +0,0 @@
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
- import { type BrowserDescriptor } from "../browsers.js";
10
- export type { ChromePlatform } from "./types.js";
11
- export { BROWSERS, listBrowsers, resolveBrowser, requireBrowser, getActiveBrowser, setActiveBrowser, type BrowserId, type BrowserDescriptor, } from "../browsers.js";
12
- export { KeychainAccessError, isKeychainAccessDenied, type KeychainErrorKind, } from "../keychain.js";
13
- /**
14
- * Returns the backend for the current OS, cached after first call.
15
- * @throws On an unsupported platform.
16
- */
17
- export declare function selectPlatform(): ChromePlatform;
18
- /** Default Chrome User Data directory for the current OS. */
19
- export declare function getPlatformChromeDir(browser?: string | BrowserDescriptor): string;
20
- /**
21
- * Returns the symmetric key for decrypting this profile's secrets.
22
- * @param chromeDir - Needed on Windows, where the key lives in `Local State`.
23
- */
24
- export declare function deriveEncryptionKey(chromeDir?: string, browser?: string | BrowserDescriptor): Buffer;
25
- /** Decrypts a Chrome blob and returns the plaintext bytes (or a placeholder string). */
26
- export declare function decryptValueBuffer(blob: Buffer | Uint8Array | null | undefined, key: Buffer): Buffer | string;
27
- /** Decrypts a Chrome blob and returns the plaintext as a UTF-8 string. */
28
- export declare function decryptValue(blob: Buffer | Uint8Array | null | undefined, key: Buffer): string;
29
- /** Clears the active backend's cached key material. */
30
- export declare function clearKeyCache(): void;
@@ -1,69 +0,0 @@
1
- import { darwin } from "./darwin.js";
2
- import { win32 } from "./win32.js";
3
- import { linux } from "./linux.js";
4
- import { getActiveBrowser, requireBrowser } from "../browsers.js";
5
- // Browser selection is orthogonal to platform selection: the backends above
6
- // read the active browser's data dir + Safe Storage label from the registry.
7
- // Re-exported here so the rest of the codebase has one import site for both
8
- // "which OS" and "which browser".
9
- export { BROWSERS, listBrowsers, resolveBrowser, requireBrowser, getActiveBrowser, setActiveBrowser, } from "../browsers.js";
10
- // Keychain access failures (macOS) surface a typed error so the CLI can show a
11
- // clear "No keychain access" message instead of a generic failure.
12
- export { KeychainAccessError, isKeychainAccessDenied, } from "../keychain.js";
13
- let _active = null;
14
- /**
15
- * Returns the backend for the current OS, cached after first call.
16
- * @throws On an unsupported platform.
17
- */
18
- export function selectPlatform() {
19
- if (_active)
20
- return _active;
21
- switch (process.platform) {
22
- case "darwin":
23
- _active = darwin;
24
- break;
25
- case "win32":
26
- _active = win32;
27
- break;
28
- case "linux":
29
- _active = linux;
30
- break;
31
- default:
32
- throw new Error(`Unsupported platform: ${process.platform}. ` +
33
- "chrome-tools supports macOS, Windows, and Linux.");
34
- }
35
- return _active;
36
- }
37
- /** Default Chrome User Data directory for the current OS. */
38
- export function getPlatformChromeDir(browser) {
39
- return selectPlatform().getDefaultChromeDir(resolveTargetBrowser(browser));
40
- }
41
- /**
42
- * Returns the symmetric key for decrypting this profile's secrets.
43
- * @param chromeDir - Needed on Windows, where the key lives in `Local State`.
44
- */
45
- export function deriveEncryptionKey(chromeDir, browser) {
46
- return selectPlatform().getDecryptionKey(chromeDir, resolveTargetBrowser(browser));
47
- }
48
- /** Decrypts a Chrome blob and returns the plaintext bytes (or a placeholder string). */
49
- export function decryptValueBuffer(blob, key) {
50
- if (!blob || blob.length === 0) {
51
- return Buffer.alloc(0);
52
- }
53
- const buf = Buffer.isBuffer(blob) ? blob : Buffer.from(blob);
54
- return selectPlatform().decrypt(buf, key);
55
- }
56
- /** Decrypts a Chrome blob and returns the plaintext as a UTF-8 string. */
57
- export function decryptValue(blob, key) {
58
- const decrypted = decryptValueBuffer(blob, key);
59
- return Buffer.isBuffer(decrypted) ? decrypted.toString("utf-8") : decrypted;
60
- }
61
- /** Clears the active backend's cached key material. */
62
- export function clearKeyCache() {
63
- selectPlatform().clearCache();
64
- }
65
- function resolveTargetBrowser(browser) {
66
- if (!browser)
67
- return getActiveBrowser();
68
- return typeof browser === "string" ? requireBrowser(browser) : browser;
69
- }
@@ -1,10 +0,0 @@
1
- import type { ChromePlatform } from "./types.js";
2
- /**
3
- * Linux backend.
4
- *
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
- * Cipher: AES-128-CBC with a fixed 16-space IV (same primitive as macOS).
9
- */
10
- export declare const linux: ChromePlatform;
@@ -1,64 +0,0 @@
1
- import { execFileSync } from "node:child_process";
2
- import { homedir } from "node:os";
3
- import { join } from "node:path";
4
- import { pbkdf2Sync } from "node:crypto";
5
- import { decryptValueBuffer } from "../crypto.js";
6
- import { getActiveBrowser } from "../browsers.js";
7
- import { getKeychainPasswordProvider } from "../keychain.js";
8
- // Linux uses the same CBC scheme as macOS but with a single PBKDF2 iteration.
9
- const PBKDF2_ITERATIONS = 1;
10
- const PBKDF2_KEY_LENGTH = 16;
11
- const PBKDF2_SALT = Buffer.from("saltysalt");
12
- // Chrome's hardcoded fallback when no secret service (gnome-keyring/kwallet) is
13
- // available — yields the "Basic" (effectively unencrypted) storage scheme.
14
- const FALLBACK_PASSWORD = "peanuts";
15
- // Keyed by Secret Service `application` attribute so different browsers cache
16
- // independently (e.g. under `--all-browsers`).
17
- const _cachedKeys = new Map();
18
- /** Looks up the "Safe Storage" password via libsecret, falling back to "peanuts". */
19
- function getStoragePassword(application) {
20
- const provider = getKeychainPasswordProvider();
21
- if (provider) {
22
- return provider(application);
23
- }
24
- try {
25
- const out = execFileSync("secret-tool", ["lookup", "application", application], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
26
- if (out)
27
- return out;
28
- }
29
- catch {
30
- // secret-tool missing, or no keyring / no entry — fall through to default.
31
- }
32
- return FALLBACK_PASSWORD;
33
- }
34
- /**
35
- * Linux backend.
36
- *
37
- * Key source: the active browser's "Safe Storage" password from the Secret
38
- * Service (gnome-keyring / KWallet) via `secret-tool`, or the hardcoded
39
- * "peanuts" fallback, stretched with PBKDF2 (1 iteration).
40
- * Cipher: AES-128-CBC with a fixed 16-space IV (same primitive as macOS).
41
- */
42
- export const linux = {
43
- name: "linux",
44
- getDefaultChromeDir(browser = getActiveBrowser()) {
45
- const configHome = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
46
- return join(configHome, ...browser.dirs.linux.split("/"));
47
- },
48
- getDecryptionKey(_chromeDir, browser = getActiveBrowser()) {
49
- const application = browser.linuxSecretApp || "chrome";
50
- const cached = _cachedKeys.get(application);
51
- if (cached !== undefined) {
52
- return cached;
53
- }
54
- const key = pbkdf2Sync(getStoragePassword(application), PBKDF2_SALT, PBKDF2_ITERATIONS, PBKDF2_KEY_LENGTH, "sha1");
55
- _cachedKeys.set(application, key);
56
- return key;
57
- },
58
- decrypt(blob, key) {
59
- return decryptValueBuffer(blob, key);
60
- },
61
- clearCache() {
62
- _cachedKeys.clear();
63
- },
64
- };
@@ -1,45 +0,0 @@
1
- import type { BrowserDescriptor } from "../browsers.js";
2
- /**
3
- * The platform-specific backend contract.
4
- *
5
- * Everything that differs between operating systems is bundled here because the
6
- * three concerns are coupled — a Windows key only works with the Windows cipher,
7
- * etc. Each backend (darwin/win32/linux) implements this once; the rest of the
8
- * codebase stays platform-agnostic and talks to the selected backend through the
9
- * facades in `./index.ts`.
10
- */
11
- export interface ChromePlatform {
12
- /** Node platform identifier this backend handles (e.g. "darwin", "win32", "linux"). */
13
- readonly name: string;
14
- /** Absolute path to Chrome's User Data directory for this OS. */
15
- getDefaultChromeDir(browser?: BrowserDescriptor): string;
16
- /**
17
- * Returns the symmetric key used to decrypt this profile's secrets, ready to
18
- * hand to {@link decrypt}. May read the OS key store and is cached internally.
19
- *
20
- * @param chromeDir - User Data dir, needed on platforms (Windows) where the
21
- * key lives inside `Local State`. Defaults to {@link getDefaultChromeDir}.
22
- * @throws An actionable Error if the key cannot be obtained.
23
- */
24
- getDecryptionKey(chromeDir?: string, browser?: BrowserDescriptor): Buffer;
25
- /**
26
- * Returns the App-Bound Encryption key for Chrome 127+ on Windows.
27
- * Only the win32 backend implements this; others return `undefined`.
28
- *
29
- * @param chromeDir - User Data dir containing the `Local State` file.
30
- * @param browser - Browser to target (needed to select the COM CLSID).
31
- * @returns The decrypted 32-byte AES key, or `undefined` if ABE is
32
- * unavailable or unsupported on this platform.
33
- */
34
- getAppBoundKey?(chromeDir?: string, browser?: BrowserDescriptor): Buffer | undefined;
35
- /**
36
- * Decrypts a single Chrome `v10`/`v11`/`v20` blob using this platform's
37
- * scheme (AES-128-CBC on macOS/Linux, AES-256-GCM on Windows).
38
- *
39
- * @returns The plaintext bytes, or a `<...>` placeholder string on a
40
- * recognized but undecryptable blob (never throws).
41
- */
42
- decrypt(blob: Buffer, key: Buffer): Buffer | string;
43
- /** Clears any cached key material. Useful for tests / re-authentication. */
44
- clearCache(): void;
45
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,13 +0,0 @@
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
- * Chrome 127+ adds `os_crypt.app_bound_encrypted_key` — base64, "APPB"-prefixed.
10
- * It is passed directly to Chrome's IElevator COM service via the native helper
11
- * to obtain the 32-byte AES-256 key for "v20" blobs.
12
- */
13
- export declare const win32: ChromePlatform;
@@ -1,329 +0,0 @@
1
- import { execFileSync } from "node:child_process";
2
- import { copyFileSync, existsSync, mkdirSync, readFileSync, statSync } from "node:fs";
3
- import { dirname, join } from "node:path";
4
- import { decryptValueBufferGcm, decryptValueBufferGcmV20 } from "../crypto.js";
5
- import { getActiveBrowser } from "../browsers.js";
6
- // Chrome base64-encodes the DPAPI-protected AES key in Local State, prefixed
7
- // with the ASCII bytes "DPAPI".
8
- const DPAPI_PREFIX = Buffer.from("DPAPI", "ascii");
9
- // Chrome 127+ uses App-Bound Encryption. The key in Local State is prefixed
10
- // with "APPB" and requires the IElevator COM service to decrypt.
11
- const APPB_PREFIX = Buffer.from("APPB", "ascii");
12
- // Keyed by User Data dir so different browsers (each with its own Local State)
13
- // cache independently (e.g. under `--all-browsers`).
14
- const _cachedKeys = new Map();
15
- const _cachedABEKeys = new Map();
16
- let _lastResolvedDir;
17
- let _lastResolvedBrowser;
18
- // Maps browser ids to the COM service identifier used by the native helper.
19
- const ABE_BROWSER_MAP = {
20
- chrome: "chrome",
21
- "chrome-beta": "chrome-beta",
22
- "chrome-canary": "chrome",
23
- chromium: "chrome",
24
- brave: "brave",
25
- edge: "edge",
26
- vivaldi: "chrome",
27
- comet: "chrome",
28
- opera: "chrome",
29
- };
30
- /**
31
- * Unprotects a DPAPI blob for the current user.
32
- *
33
- * Node has no binding for `CryptUnprotectData`, so we shell out to PowerShell's
34
- * `ProtectedData.Unprotect`. This needs no native build step. For a faster,
35
- * dependency-light alternative, swap this for the optional `win-dpapi` addon.
36
- */
37
- function dpapiDecrypt(encrypted) {
38
- const script = [
39
- "$ErrorActionPreference='Stop';",
40
- "Add-Type -AssemblyName System.Security;",
41
- "$b=[Console]::In.ReadToEnd().Trim();",
42
- "$enc=[Convert]::FromBase64String($b);",
43
- "$dec=[System.Security.Cryptography.ProtectedData]::Unprotect($enc,$null,[System.Security.Cryptography.DataProtectionScope]::CurrentUser);",
44
- "[Console]::Out.Write([Convert]::ToBase64String($dec));",
45
- ].join(" ");
46
- let out;
47
- try {
48
- out = execFileSync("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], { input: encrypted.toString("base64"), encoding: "utf-8" });
49
- }
50
- catch (err) {
51
- throw new Error("DPAPI decryption via PowerShell failed.\n" +
52
- "The encrypted_key can only be unprotected by the same Windows user that " +
53
- "created it. Run as that user on the same machine.\n" +
54
- `(powershell: ${err?.message ?? err})`);
55
- }
56
- return Buffer.from(out.trim(), "base64");
57
- }
58
- /**
59
- * Resolves the Local State directory from a profile/chromeDir path.
60
- * `profilePath` may point directly at .../Default. Local State belongs to
61
- * the parent User Data directory.
62
- */
63
- function resolveLocalStateDir(requestedDir) {
64
- const parentDir = dirname(requestedDir);
65
- return existsSync(join(requestedDir, "Local State"))
66
- ? requestedDir
67
- : parentDir !== requestedDir && existsSync(join(parentDir, "Local State"))
68
- ? parentDir
69
- : requestedDir;
70
- }
71
- /**
72
- * Finds the source chrome_abe_decrypt helper executable.
73
- * Searches in order:
74
- * 1. Next to compiled output (dist/bin/)
75
- * 2. In the package bin/ directory
76
- * 3. In src/native/ (development)
77
- * 4. On PATH
78
- */
79
- function findABEHelper() {
80
- const base = import.meta.dirname ?? "";
81
- const candidates = [
82
- join(base, "..", "..", "..", "bin", "chrome_abe_decrypt.exe"),
83
- join(base, "..", "..", "..", "..", "bin", "chrome_abe_decrypt.exe"),
84
- join(base, "..", "..", "..", "..", "src", "native", "chrome_abe_decrypt.exe"),
85
- ];
86
- for (const p of candidates) {
87
- if (existsSync(p))
88
- return p;
89
- }
90
- // Try on PATH
91
- try {
92
- execFileSync("where", ["chrome_abe_decrypt.exe"], { encoding: "utf-8", stdio: "pipe" });
93
- return "chrome_abe_decrypt.exe";
94
- }
95
- catch {
96
- return null;
97
- }
98
- }
99
- /**
100
- * Candidate directories for staging the helper binary to satisfy Chrome's
101
- * Elevation Service caller validation.
102
- *
103
- * In Chromium's `caller_validation.cc`, `MaybeTrimProcessPath()` strips the
104
- * executable name and trailing 'Temp' or 'Application' folders. Executing the
105
- * helper from `<BrowserRoot>\Temp\chrome_abe_decrypt.exe` matches the browser's
106
- * validated root without requiring administrative privileges.
107
- */
108
- function getBrowserTempDirs(browserId) {
109
- const dirs = [];
110
- const progFiles = process.env["ProgramFiles"] || "C:\\Program Files";
111
- const progFilesX86 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
112
- const localAppData = process.env.LOCALAPPDATA ||
113
- (process.env.USERPROFILE ? join(process.env.USERPROFILE, "AppData", "Local") : "");
114
- const subpaths = {
115
- chrome: ["Google\\Chrome"],
116
- "chrome-beta": ["Google\\Chrome Beta"],
117
- "chrome-canary": ["Google\\Chrome SxS"],
118
- chromium: ["Chromium"],
119
- edge: ["Microsoft\\Edge"],
120
- brave: ["BraveSoftware\\Brave-Browser"],
121
- vivaldi: ["Vivaldi"],
122
- };
123
- const rels = subpaths[browserId] || ["Google\\Chrome"];
124
- for (const rel of rels) {
125
- dirs.push(join(progFiles, rel, "Temp"));
126
- dirs.push(join(progFilesX86, rel, "Temp"));
127
- if (localAppData) {
128
- dirs.push(join(localAppData, rel, "Temp"));
129
- }
130
- }
131
- return dirs;
132
- }
133
- /**
134
- * Ensures the helper binary is located in a path that passes the browser
135
- * elevation service's validation checks. Stages a copy to the browser's Temp
136
- * folder if necessary.
137
- */
138
- function findOrStageABEHelper(browserId) {
139
- const sourceExe = findABEHelper();
140
- if (!sourceExe)
141
- return null;
142
- const targetDirs = getBrowserTempDirs(browserId);
143
- for (const targetDir of targetDirs) {
144
- try {
145
- if (!existsSync(targetDir)) {
146
- mkdirSync(targetDir, { recursive: true });
147
- }
148
- const stagedExe = join(targetDir, "chrome_abe_decrypt.exe");
149
- if (!existsSync(stagedExe) || statSync(stagedExe).size !== statSync(sourceExe).size) {
150
- copyFileSync(sourceExe, stagedExe);
151
- }
152
- return stagedExe;
153
- }
154
- catch {
155
- // Permission denied or path not found, try next candidate
156
- continue;
157
- }
158
- }
159
- // Fallback to the original binary location
160
- return sourceExe;
161
- }
162
- let _abeWarningShown = false;
163
- /**
164
- * Decrypts an app_bound_encrypted_key using Chrome's IElevator COM service
165
- * via the native helper binary.
166
- *
167
- * Note: App-Bound Encryption does NOT use standard user DPAPI. The raw
168
- * encrypted payload (with the 4-byte 'APPB' prefix removed) is passed directly
169
- * to the IElevator COM service, which executes the elevated decryption.
170
- */
171
- function abeDecrypt(rawEncryptedBytes, browserId) {
172
- const helperExe = findOrStageABEHelper(browserId);
173
- if (!helperExe) {
174
- if (!_abeWarningShown) {
175
- _abeWarningShown = true;
176
- process.stderr.write("[chrome-tools] chrome_abe_decrypt.exe not found. Chrome 127+ v20-encrypted\n" +
177
- "cookies/passwords cannot be decrypted.\n");
178
- }
179
- return null;
180
- }
181
- const comBrowser = ABE_BROWSER_MAP[browserId] || "chrome";
182
- try {
183
- const out = execFileSync(helperExe, [comBrowser], {
184
- input: rawEncryptedBytes.toString("base64"),
185
- encoding: "utf-8",
186
- timeout: 30000,
187
- stdio: ["pipe", "pipe", "pipe"],
188
- cwd: dirname(helperExe),
189
- });
190
- return Buffer.from(out.trim(), "base64");
191
- }
192
- catch {
193
- return null;
194
- }
195
- }
196
- /**
197
- * Windows backend.
198
- *
199
- * Key source: `os_crypt.encrypted_key` in Local State — base64, "DPAPI"-prefixed,
200
- * unprotected via DPAPI into a raw 32-byte AES-256 key (no PBKDF2 on Windows).
201
- * Cipher: AES-256-GCM (nonce + tag carried in each blob).
202
- *
203
- * Chrome 127+ adds `os_crypt.app_bound_encrypted_key` — base64, "APPB"-prefixed.
204
- * It is passed directly to Chrome's IElevator COM service via the native helper
205
- * to obtain the 32-byte AES-256 key for "v20" blobs.
206
- */
207
- export const win32 = {
208
- name: "win32",
209
- getDefaultChromeDir(browser = getActiveBrowser()) {
210
- const roaming = browser.dirs.win32Base === "roaming";
211
- const appData = roaming
212
- ? process.env.APPDATA ||
213
- (process.env.USERPROFILE
214
- ? join(process.env.USERPROFILE, "AppData", "Roaming")
215
- : "")
216
- : process.env.LOCALAPPDATA ||
217
- (process.env.USERPROFILE
218
- ? join(process.env.USERPROFILE, "AppData", "Local")
219
- : "");
220
- if (!appData) {
221
- throw new Error("Could not resolve the Windows application-data directory.");
222
- }
223
- return join(appData, ...browser.dirs.win32.split("/"));
224
- },
225
- getDecryptionKey(chromeDir, browser = getActiveBrowser()) {
226
- const requestedDir = chromeDir || this.getDefaultChromeDir(browser);
227
- const dir = resolveLocalStateDir(requestedDir);
228
- _lastResolvedDir = dir;
229
- _lastResolvedBrowser = browser;
230
- const cached = _cachedKeys.get(dir);
231
- if (cached !== undefined) {
232
- // Also pre-cache ABE key if not yet cached
233
- if (!_cachedABEKeys.has(dir)) {
234
- try {
235
- this.getAppBoundKey?.(dir, browser);
236
- }
237
- catch {
238
- // Ignore
239
- }
240
- }
241
- return cached;
242
- }
243
- const localStatePath = join(dir, "Local State");
244
- if (!existsSync(localStatePath)) {
245
- throw new Error(`Could not find Chrome's "Local State" at ${localStatePath}.\n` +
246
- "Run Google Chrome at least once so it creates the encryption key.");
247
- }
248
- let encryptedKeyB64;
249
- try {
250
- const raw = readFileSync(localStatePath, "utf-8");
251
- encryptedKeyB64 = JSON.parse(raw)?.os_crypt?.encrypted_key;
252
- }
253
- catch (err) {
254
- throw new Error(`Failed to read os_crypt.encrypted_key from Local State: ${err.message}`);
255
- }
256
- if (!encryptedKeyB64) {
257
- throw new Error("No os_crypt.encrypted_key found in Local State.\n" +
258
- "This profile may predate Chrome's encrypted-key scheme.");
259
- }
260
- const encryptedKey = Buffer.from(encryptedKeyB64, "base64");
261
- if (!encryptedKey.subarray(0, DPAPI_PREFIX.length).equals(DPAPI_PREFIX)) {
262
- throw new Error('Unexpected encrypted_key format (missing "DPAPI" prefix).');
263
- }
264
- const key = dpapiDecrypt(encryptedKey.subarray(DPAPI_PREFIX.length));
265
- _cachedKeys.set(dir, key);
266
- // Also pre-cache the ABE key for v20 blobs
267
- try {
268
- this.getAppBoundKey?.(dir, browser);
269
- }
270
- catch {
271
- // Ignore
272
- }
273
- return key;
274
- },
275
- getAppBoundKey(chromeDir, browser = _lastResolvedBrowser ?? getActiveBrowser()) {
276
- const requestedDir = chromeDir || _lastResolvedDir || this.getDefaultChromeDir(browser);
277
- const dir = resolveLocalStateDir(requestedDir);
278
- _lastResolvedDir = dir;
279
- _lastResolvedBrowser = browser;
280
- const cached = _cachedABEKeys.get(dir);
281
- if (cached !== undefined) {
282
- return cached;
283
- }
284
- const localStatePath = join(dir, "Local State");
285
- if (!existsSync(localStatePath))
286
- return undefined;
287
- let appBoundKeyB64;
288
- try {
289
- const raw = readFileSync(localStatePath, "utf-8");
290
- appBoundKeyB64 = JSON.parse(raw)?.os_crypt?.app_bound_encrypted_key;
291
- }
292
- catch {
293
- return undefined;
294
- }
295
- if (!appBoundKeyB64)
296
- return undefined;
297
- const appBoundKey = Buffer.from(appBoundKeyB64, "base64");
298
- if (!appBoundKey.subarray(0, APPB_PREFIX.length).equals(APPB_PREFIX)) {
299
- return undefined;
300
- }
301
- // App-Bound Encryption: Pass raw encrypted bytes directly to COM elevation service
302
- const abeKey = abeDecrypt(appBoundKey.subarray(APPB_PREFIX.length), browser.id);
303
- if (!abeKey)
304
- return undefined;
305
- const key = abeKey.length > 32 ? abeKey.subarray(abeKey.length - 32) : abeKey;
306
- _cachedABEKeys.set(dir, key);
307
- return key;
308
- },
309
- decrypt(blob, key) {
310
- const prefix = blob.subarray(0, 3).toString("utf-8");
311
- // v20 = App-Bound Encryption (Chrome 127+)
312
- if (prefix === "v20") {
313
- // Try to get the ABE key for this profile
314
- const abeKey = this.getAppBoundKey?.(_lastResolvedDir, _lastResolvedBrowser);
315
- if (!abeKey) {
316
- return "<v20 app-bound encryption - requires chrome_abe_decrypt.exe>";
317
- }
318
- return decryptValueBufferGcmV20(blob, abeKey);
319
- }
320
- // v10/v11 = standard DPAPI + AES-256-GCM
321
- return decryptValueBufferGcm(blob, key);
322
- },
323
- clearCache() {
324
- _cachedKeys.clear();
325
- _cachedABEKeys.clear();
326
- _lastResolvedDir = undefined;
327
- _lastResolvedBrowser = undefined;
328
- },
329
- };
@@ -1,2 +0,0 @@
1
- /** Builds a SQL LIKE pattern with wildcard characters escaped for literal matching. */
2
- export declare function sqlLikePattern(term: string): string;
@@ -1,4 +0,0 @@
1
- /** Builds a SQL LIKE pattern with wildcard characters escaped for literal matching. */
2
- export function sqlLikePattern(term) {
3
- return `%${term.replace(/([%_\\])/g, "\\$1")}%`;
4
- }
@@ -1,28 +0,0 @@
1
- /**
2
- * chrome-tools — Public API
3
- *
4
- * This module re-exports all feature modules for use as a Node.js library.
5
- *
6
- * @example
7
- * import { getPasswords, getBookmarks, discoverProfiles } from 'chrome-tools';
8
- *
9
- * const passwords = getPasswords({ profile: 'Default' });
10
- * const bookmarks = getBookmarks({ search: 'github' });
11
- * const profiles = discoverProfiles();
12
- */
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, requireBrowser, getActiveBrowser, setActiveBrowser, type BrowserId, type BrowserDescriptor, } from "./core/browsers.js";
15
- export { selectPlatform, getPlatformChromeDir, deriveEncryptionKey, decryptValue, decryptValueBuffer, clearKeyCache, type ChromePlatform, } from "./core/platform/index.js";
16
- export { getKeychainPassword, getChromeKeychainPassword, clearKeychainCache, setKeychainPasswordProvider, getKeychainPasswordProvider, type KeychainPasswordProvider, } from "./core/keychain.js";
17
- export { chromeTimeToDate, } from "./core/crypto.js";
18
- export { queryDatabase, tableExists, } from "./core/db.js";
19
- export { getPasswords } from "./modules/passwords.js";
20
- export { getBookmarks } from "./modules/bookmarks.js";
21
- export { getHistory, getSearchTerms } from "./modules/history.js";
22
- export { getCookies, type CookieEntry, type CookieOptions } from "./modules/cookies.js";
23
- export { getLocalStorage, type LocalStorageEntry, type LocalStorageOptions } from "./modules/local-storage.js";
24
- export { getAutofillEntries, getCreditCards, getAddresses } from "./modules/autofill.js";
25
- export { getDownloads } from "./modules/downloads.js";
26
- export { getExtensions } from "./modules/extensions.js";
27
- export { getSearchEngines } from "./modules/search-engines.js";
28
- export { getTopSites } from "./modules/top-sites.js";