@executioncontrolprotocol/browser-secrets 0.10.0
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/README.md +50 -0
- package/dist/constants.d.ts +13 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +13 -0
- package/dist/constants.js.map +1 -0
- package/dist/crypto/vault.d.ts +44 -0
- package/dist/crypto/vault.d.ts.map +1 -0
- package/dist/crypto/vault.js +148 -0
- package/dist/crypto/vault.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +96 -0
- package/dist/index.js.map +1 -0
- package/dist/store.d.ts +12 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +58 -0
- package/dist/store.js.map +1 -0
- package/package.json +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# @executioncontrolprotocol/browser-secrets
|
|
2
|
+
|
|
3
|
+
Passphrase-protected encrypted secrets for browser ECP environments.
|
|
4
|
+
|
|
5
|
+
## Fluent helper
|
|
6
|
+
|
|
7
|
+
Environment bindings use `browser("KEY")`, which serializes to `{ "$browser": "KEY" }` in manifests:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { browser, extension } from "@executioncontrolprotocol/browser"
|
|
11
|
+
|
|
12
|
+
extension("@executioncontrolprotocol/openai").with({
|
|
13
|
+
apiKey: browser("OPENAI_API_KEY", { optional: true }),
|
|
14
|
+
})
|
|
15
|
+
extension("@executioncontrolprotocol/browser-secrets").with({})
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Storage model
|
|
19
|
+
|
|
20
|
+
| Artifact | Location | Purpose |
|
|
21
|
+
| -------- | -------- | ------- |
|
|
22
|
+
| KDF salt | `localStorage` (`ecp:browser-secrets:v1:salt`) | PBKDF2 salt |
|
|
23
|
+
| Verifier | `localStorage` (`ecp:browser-secrets:v1:verifier`) | Passphrase check |
|
|
24
|
+
| Secret index | `localStorage` (`ecp:browser-secrets:v1:index`) | Logical key list |
|
|
25
|
+
| Ciphertext | `localStorage` (`ecp:browser-secret:<key>`) | AES-GCM encrypted value |
|
|
26
|
+
| Master key | Memory only after unlock | Never persisted |
|
|
27
|
+
|
|
28
|
+
Crypto: PBKDF2 (310k iterations, SHA-256) + AES-GCM 256-bit.
|
|
29
|
+
|
|
30
|
+
## Public API
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import {
|
|
34
|
+
hasBrowserVault,
|
|
35
|
+
isBrowserVaultUnlocked,
|
|
36
|
+
setupBrowserVault,
|
|
37
|
+
unlockBrowserVault,
|
|
38
|
+
lockBrowserVault,
|
|
39
|
+
setBrowserSecret,
|
|
40
|
+
getBrowserSecret,
|
|
41
|
+
deleteBrowserSecret,
|
|
42
|
+
listBrowserSecretKeys,
|
|
43
|
+
} from "@executioncontrolprotocol/browser-secrets"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The browser runtime re-exports these helpers from `@executioncontrolprotocol/browser`.
|
|
47
|
+
|
|
48
|
+
## Tests
|
|
49
|
+
|
|
50
|
+
Unit tests run with Node Web Crypto and an in-memory storage mock. Browser smoke tests use Vitest browser mode (`npm run test:browser`).
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** localStorage key for vault KDF salt. @category BrowserSecrets */
|
|
2
|
+
export declare const VAULT_SALT_KEY = "ecp:browser-secrets:v1:salt";
|
|
3
|
+
/** localStorage key for passphrase verifier. @category BrowserSecrets */
|
|
4
|
+
export declare const VAULT_VERIFIER_KEY = "ecp:browser-secrets:v1:verifier";
|
|
5
|
+
/** Prefix for encrypted secret entries in localStorage. @category BrowserSecrets */
|
|
6
|
+
export declare const SECRET_STORAGE_PREFIX = "ecp:browser-secret:";
|
|
7
|
+
/** localStorage index of secret key names. @category BrowserSecrets */
|
|
8
|
+
export declare const SECRET_INDEX_KEY = "ecp:browser-secrets:v1:index";
|
|
9
|
+
/** PBKDF2 iteration count for passphrase derivation. @category BrowserSecrets */
|
|
10
|
+
export declare const PBKDF2_ITERATIONS = 310000;
|
|
11
|
+
/** Verifier plaintext constant. @category BrowserSecrets */
|
|
12
|
+
export declare const VAULT_VERIFIER_PLAINTEXT = "ecp-browser-secrets-v1";
|
|
13
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,eAAO,MAAM,cAAc,gCAAgC,CAAA;AAE3D,yEAAyE;AACzE,eAAO,MAAM,kBAAkB,oCAAoC,CAAA;AAEnE,oFAAoF;AACpF,eAAO,MAAM,qBAAqB,wBAAwB,CAAA;AAE1D,uEAAuE;AACvE,eAAO,MAAM,gBAAgB,iCAAiC,CAAA;AAE9D,iFAAiF;AACjF,eAAO,MAAM,iBAAiB,SAAU,CAAA;AAExC,4DAA4D;AAC5D,eAAO,MAAM,wBAAwB,2BAA2B,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** localStorage key for vault KDF salt. @category BrowserSecrets */
|
|
2
|
+
export const VAULT_SALT_KEY = "ecp:browser-secrets:v1:salt";
|
|
3
|
+
/** localStorage key for passphrase verifier. @category BrowserSecrets */
|
|
4
|
+
export const VAULT_VERIFIER_KEY = "ecp:browser-secrets:v1:verifier";
|
|
5
|
+
/** Prefix for encrypted secret entries in localStorage. @category BrowserSecrets */
|
|
6
|
+
export const SECRET_STORAGE_PREFIX = "ecp:browser-secret:";
|
|
7
|
+
/** localStorage index of secret key names. @category BrowserSecrets */
|
|
8
|
+
export const SECRET_INDEX_KEY = "ecp:browser-secrets:v1:index";
|
|
9
|
+
/** PBKDF2 iteration count for passphrase derivation. @category BrowserSecrets */
|
|
10
|
+
export const PBKDF2_ITERATIONS = 310_000;
|
|
11
|
+
/** Verifier plaintext constant. @category BrowserSecrets */
|
|
12
|
+
export const VAULT_VERIFIER_PLAINTEXT = "ecp-browser-secrets-v1";
|
|
13
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,MAAM,CAAC,MAAM,cAAc,GAAG,6BAA6B,CAAA;AAE3D,yEAAyE;AACzE,MAAM,CAAC,MAAM,kBAAkB,GAAG,iCAAiC,CAAA;AAEnE,oFAAoF;AACpF,MAAM,CAAC,MAAM,qBAAqB,GAAG,qBAAqB,CAAA;AAE1D,uEAAuE;AACvE,MAAM,CAAC,MAAM,gBAAgB,GAAG,8BAA8B,CAAA;AAE9D,iFAAiF;AACjF,MAAM,CAAC,MAAM,iBAAiB,GAAG,OAAO,CAAA;AAExC,4DAA4D;AAC5D,MAAM,CAAC,MAAM,wBAAwB,GAAG,wBAAwB,CAAA"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { PBKDF2_ITERATIONS } from "../constants.js";
|
|
2
|
+
/** Minimal key-value storage for vault metadata and ciphertext. @category BrowserSecrets */
|
|
3
|
+
export interface VaultStorage {
|
|
4
|
+
getItem(key: string): string | null;
|
|
5
|
+
setItem(key: string, value: string): void;
|
|
6
|
+
removeItem(key: string): void;
|
|
7
|
+
}
|
|
8
|
+
/** Encrypted secret record persisted in storage. @category BrowserSecrets */
|
|
9
|
+
export interface EncryptedSecretRecord {
|
|
10
|
+
v: 1;
|
|
11
|
+
iv: string;
|
|
12
|
+
ciphertext: string;
|
|
13
|
+
}
|
|
14
|
+
export { PBKDF2_ITERATIONS };
|
|
15
|
+
/** Passphrase-protected AES-GCM vault (master key in memory only). @category BrowserSecrets */
|
|
16
|
+
export declare class BrowserSecretsVault {
|
|
17
|
+
private readonly storage;
|
|
18
|
+
private masterKey;
|
|
19
|
+
constructor(storage: VaultStorage);
|
|
20
|
+
/** Whether vault metadata exists in storage. */
|
|
21
|
+
hasVault(): boolean;
|
|
22
|
+
/** Whether the master key is loaded in memory. */
|
|
23
|
+
isUnlocked(): boolean;
|
|
24
|
+
/** Create a new vault with the given passphrase. */
|
|
25
|
+
setup(passphrase: string): Promise<void>;
|
|
26
|
+
/** Unlock an existing vault; returns false when passphrase is wrong. */
|
|
27
|
+
unlock(passphrase: string): Promise<boolean>;
|
|
28
|
+
/** Clear the in-memory master key. */
|
|
29
|
+
lock(): void;
|
|
30
|
+
/** Encrypt plaintext with the unlocked master key. */
|
|
31
|
+
encrypt(plaintext: string): Promise<EncryptedSecretRecord>;
|
|
32
|
+
/** Decrypt a stored record with the unlocked master key. */
|
|
33
|
+
decrypt(record: EncryptedSecretRecord): Promise<string>;
|
|
34
|
+
private requireKey;
|
|
35
|
+
private encryptWithKey;
|
|
36
|
+
private decryptWithKey;
|
|
37
|
+
}
|
|
38
|
+
/** In-memory storage for unit tests. @category BrowserSecrets */
|
|
39
|
+
export declare function createMemoryVaultStorage(): VaultStorage & {
|
|
40
|
+
data: Map<string, string>;
|
|
41
|
+
};
|
|
42
|
+
/** Default localStorage-backed storage when available. @category BrowserSecrets */
|
|
43
|
+
export declare function createLocalVaultStorage(): VaultStorage;
|
|
44
|
+
//# sourceMappingURL=vault.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vault.d.ts","sourceRoot":"","sources":["../../src/crypto/vault.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EAIlB,MAAM,iBAAiB,CAAA;AAExB,4FAA4F;AAC5F,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IACnC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACzC,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAED,6EAA6E;AAC7E,MAAM,WAAW,qBAAqB;IACpC,CAAC,EAAE,CAAC,CAAA;IACJ,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,EAAE,MAAM,CAAA;CACnB;AA+DD,OAAO,EAAE,iBAAiB,EAAE,CAAA;AAE5B,+FAA+F;AAC/F,qBAAa,mBAAmB;IAGlB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,OAAO,CAAC,SAAS,CAAuB;gBAEX,OAAO,EAAE,YAAY;IAElD,gDAAgD;IAChD,QAAQ,IAAI,OAAO;IAInB,kDAAkD;IAClD,UAAU,IAAI,OAAO;IAIrB,oDAAoD;IAC9C,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY9C,wEAAwE;IAClE,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiBlD,sCAAsC;IACtC,IAAI,IAAI,IAAI;IAIZ,sDAAsD;IAChD,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAKhE,4DAA4D;IACtD,OAAO,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC;IAK7D,OAAO,CAAC,UAAU;YAOJ,cAAc;YAed,cAAc;CAU7B;AAED,iEAAiE;AACjE,wBAAgB,wBAAwB,IAAI,YAAY,GAAG;IAAE,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAQvF;AAED,mFAAmF;AACnF,wBAAgB,uBAAuB,IAAI,YAAY,CAStD"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { PBKDF2_ITERATIONS, VAULT_SALT_KEY, VAULT_VERIFIER_KEY, VAULT_VERIFIER_PLAINTEXT, } from "../constants.js";
|
|
2
|
+
function getCrypto() {
|
|
3
|
+
if (typeof globalThis.crypto?.subtle === "undefined") {
|
|
4
|
+
throw new Error("Web Crypto API is not available");
|
|
5
|
+
}
|
|
6
|
+
return globalThis.crypto;
|
|
7
|
+
}
|
|
8
|
+
function encodeBase64Url(bytes) {
|
|
9
|
+
const bin = String.fromCharCode(...bytes);
|
|
10
|
+
const b64 = typeof btoa === "function"
|
|
11
|
+
? btoa(bin)
|
|
12
|
+
: Buffer.from(bytes).toString("base64");
|
|
13
|
+
return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
14
|
+
}
|
|
15
|
+
function decodeBase64Url(value) {
|
|
16
|
+
const padded = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
17
|
+
const padLen = (4 - (padded.length % 4)) % 4;
|
|
18
|
+
const b64 = padded + "=".repeat(padLen);
|
|
19
|
+
const bin = typeof atob === "function"
|
|
20
|
+
? atob(b64)
|
|
21
|
+
: Buffer.from(b64, "base64").toString("binary");
|
|
22
|
+
return Uint8Array.from(bin, (c) => c.charCodeAt(0));
|
|
23
|
+
}
|
|
24
|
+
function toBufferSource(bytes) {
|
|
25
|
+
return new Uint8Array(bytes);
|
|
26
|
+
}
|
|
27
|
+
function randomBytes(length) {
|
|
28
|
+
const bytes = new Uint8Array(length);
|
|
29
|
+
getCrypto().getRandomValues(bytes);
|
|
30
|
+
return bytes;
|
|
31
|
+
}
|
|
32
|
+
async function deriveKey(passphrase, salt) {
|
|
33
|
+
const enc = new TextEncoder();
|
|
34
|
+
const baseKey = await getCrypto().subtle.importKey("raw", enc.encode(passphrase), "PBKDF2", false, ["deriveKey"]);
|
|
35
|
+
return getCrypto().subtle.deriveKey({
|
|
36
|
+
name: "PBKDF2",
|
|
37
|
+
salt: toBufferSource(salt),
|
|
38
|
+
iterations: PBKDF2_ITERATIONS,
|
|
39
|
+
hash: "SHA-256",
|
|
40
|
+
}, baseKey, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"]);
|
|
41
|
+
}
|
|
42
|
+
export { PBKDF2_ITERATIONS };
|
|
43
|
+
/** Passphrase-protected AES-GCM vault (master key in memory only). @category BrowserSecrets */
|
|
44
|
+
export class BrowserSecretsVault {
|
|
45
|
+
storage;
|
|
46
|
+
masterKey;
|
|
47
|
+
constructor(storage) {
|
|
48
|
+
this.storage = storage;
|
|
49
|
+
}
|
|
50
|
+
/** Whether vault metadata exists in storage. */
|
|
51
|
+
hasVault() {
|
|
52
|
+
return this.storage.getItem(VAULT_SALT_KEY) !== null;
|
|
53
|
+
}
|
|
54
|
+
/** Whether the master key is loaded in memory. */
|
|
55
|
+
isUnlocked() {
|
|
56
|
+
return this.masterKey !== undefined;
|
|
57
|
+
}
|
|
58
|
+
/** Create a new vault with the given passphrase. */
|
|
59
|
+
async setup(passphrase) {
|
|
60
|
+
if (this.hasVault()) {
|
|
61
|
+
throw new Error("Browser secrets vault already exists");
|
|
62
|
+
}
|
|
63
|
+
const salt = randomBytes(16);
|
|
64
|
+
const key = await deriveKey(passphrase, salt);
|
|
65
|
+
const verifier = await this.encryptWithKey(key, VAULT_VERIFIER_PLAINTEXT);
|
|
66
|
+
this.storage.setItem(VAULT_SALT_KEY, encodeBase64Url(salt));
|
|
67
|
+
this.storage.setItem(VAULT_VERIFIER_KEY, JSON.stringify(verifier));
|
|
68
|
+
this.masterKey = key;
|
|
69
|
+
}
|
|
70
|
+
/** Unlock an existing vault; returns false when passphrase is wrong. */
|
|
71
|
+
async unlock(passphrase) {
|
|
72
|
+
const saltRaw = this.storage.getItem(VAULT_SALT_KEY);
|
|
73
|
+
const verifierRaw = this.storage.getItem(VAULT_VERIFIER_KEY);
|
|
74
|
+
if (!saltRaw || !verifierRaw)
|
|
75
|
+
return false;
|
|
76
|
+
const salt = decodeBase64Url(saltRaw);
|
|
77
|
+
const key = await deriveKey(passphrase, salt);
|
|
78
|
+
try {
|
|
79
|
+
const record = JSON.parse(verifierRaw);
|
|
80
|
+
const plain = await this.decryptWithKey(key, record);
|
|
81
|
+
if (plain !== VAULT_VERIFIER_PLAINTEXT)
|
|
82
|
+
return false;
|
|
83
|
+
this.masterKey = key;
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** Clear the in-memory master key. */
|
|
91
|
+
lock() {
|
|
92
|
+
this.masterKey = undefined;
|
|
93
|
+
}
|
|
94
|
+
/** Encrypt plaintext with the unlocked master key. */
|
|
95
|
+
async encrypt(plaintext) {
|
|
96
|
+
const key = this.requireKey();
|
|
97
|
+
return this.encryptWithKey(key, plaintext);
|
|
98
|
+
}
|
|
99
|
+
/** Decrypt a stored record with the unlocked master key. */
|
|
100
|
+
async decrypt(record) {
|
|
101
|
+
const key = this.requireKey();
|
|
102
|
+
return this.decryptWithKey(key, record);
|
|
103
|
+
}
|
|
104
|
+
requireKey() {
|
|
105
|
+
if (!this.masterKey) {
|
|
106
|
+
throw new Error("Browser secrets vault is locked");
|
|
107
|
+
}
|
|
108
|
+
return this.masterKey;
|
|
109
|
+
}
|
|
110
|
+
async encryptWithKey(key, plaintext) {
|
|
111
|
+
const iv = randomBytes(12);
|
|
112
|
+
const enc = new TextEncoder();
|
|
113
|
+
const ciphertext = await getCrypto().subtle.encrypt({ name: "AES-GCM", iv: toBufferSource(iv) }, key, enc.encode(plaintext));
|
|
114
|
+
return {
|
|
115
|
+
v: 1,
|
|
116
|
+
iv: encodeBase64Url(iv),
|
|
117
|
+
ciphertext: encodeBase64Url(new Uint8Array(ciphertext)),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
async decryptWithKey(key, record) {
|
|
121
|
+
const iv = decodeBase64Url(record.iv);
|
|
122
|
+
const data = decodeBase64Url(record.ciphertext);
|
|
123
|
+
const plain = await getCrypto().subtle.decrypt({ name: "AES-GCM", iv: toBufferSource(iv) }, key, toBufferSource(data));
|
|
124
|
+
return new TextDecoder().decode(plain);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/** In-memory storage for unit tests. @category BrowserSecrets */
|
|
128
|
+
export function createMemoryVaultStorage() {
|
|
129
|
+
const data = new Map();
|
|
130
|
+
return {
|
|
131
|
+
data,
|
|
132
|
+
getItem: (k) => data.get(k) ?? null,
|
|
133
|
+
setItem: (k, v) => data.set(k, v),
|
|
134
|
+
removeItem: (k) => data.delete(k),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/** Default localStorage-backed storage when available. @category BrowserSecrets */
|
|
138
|
+
export function createLocalVaultStorage() {
|
|
139
|
+
if (typeof localStorage === "undefined") {
|
|
140
|
+
throw new Error("localStorage is not available");
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
getItem: (k) => localStorage.getItem(k),
|
|
144
|
+
setItem: (k, v) => localStorage.setItem(k, v),
|
|
145
|
+
removeItem: (k) => localStorage.removeItem(k),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=vault.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vault.js","sourceRoot":"","sources":["../../src/crypto/vault.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { BrowserSecretsVault, type VaultStorage } from "./crypto/vault.js";
|
|
2
|
+
/** Replace vault storage (tests). @category BrowserSecrets */
|
|
3
|
+
export declare function setBrowserSecretsStorage(next: VaultStorage): void;
|
|
4
|
+
/** Replace vault instance (tests). @category BrowserSecrets */
|
|
5
|
+
export declare function setBrowserSecretsVault(next: BrowserSecretsVault): void;
|
|
6
|
+
/** Reset to default localStorage-backed vault. @category BrowserSecrets */
|
|
7
|
+
export declare function resetBrowserSecretsVault(): void;
|
|
8
|
+
/** Whether vault metadata exists. @category BrowserSecrets */
|
|
9
|
+
export declare function hasBrowserVault(): boolean;
|
|
10
|
+
/** Whether the vault master key is in memory. @category BrowserSecrets */
|
|
11
|
+
export declare function isBrowserVaultUnlocked(): boolean;
|
|
12
|
+
/** Create a new passphrase-protected vault. @category BrowserSecrets */
|
|
13
|
+
export declare function setupBrowserVault(passphrase: string): Promise<void>;
|
|
14
|
+
/** Unlock an existing vault. @category BrowserSecrets */
|
|
15
|
+
export declare function unlockBrowserVault(passphrase: string): Promise<boolean>;
|
|
16
|
+
/** Lock the vault (clear in-memory key). @category BrowserSecrets */
|
|
17
|
+
export declare function lockBrowserVault(): void;
|
|
18
|
+
/** Store an encrypted secret (vault must be unlocked). @category BrowserSecrets */
|
|
19
|
+
export declare function setBrowserSecret(key: string, value: string): Promise<void>;
|
|
20
|
+
/** Read a decrypted secret when vault is unlocked. @category BrowserSecrets */
|
|
21
|
+
export declare function getBrowserSecret(key: string): Promise<string | undefined>;
|
|
22
|
+
/** Remove a secret from the vault. @category BrowserSecrets */
|
|
23
|
+
export declare function deleteBrowserSecret(key: string): Promise<void>;
|
|
24
|
+
/** List logical secret keys in the index. @category BrowserSecrets */
|
|
25
|
+
export declare function listBrowserSecretKeys(): Promise<string[]>;
|
|
26
|
+
/** Browser encrypted secrets extension. @category BrowserSecrets */
|
|
27
|
+
export declare const browserSecretsExtension: import("@executioncontrolprotocol/core").ExtensionDefinition;
|
|
28
|
+
/** Register `@executioncontrolprotocol/browser-secrets`. @category BrowserSecrets */
|
|
29
|
+
export declare function registerBrowserSecretsExtension(registry?: import("@executioncontrolprotocol/core").Registry): Promise<void>;
|
|
30
|
+
export { createMemoryVaultStorage, createLocalVaultStorage, BrowserSecretsVault } from "./crypto/vault.js";
|
|
31
|
+
export type { VaultStorage, EncryptedSecretRecord } from "./crypto/vault.js";
|
|
32
|
+
export default browserSecretsExtension;
|
|
33
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,mBAAmB,EAGnB,KAAK,YAAY,EAClB,MAAM,mBAAmB,CAAA;AAc1B,8DAA8D;AAC9D,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAGjE;AAED,+DAA+D;AAC/D,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,mBAAmB,GAAG,IAAI,CAGtE;AAED,2EAA2E;AAC3E,wBAAgB,wBAAwB,IAAI,IAAI,CAG/C;AAED,8DAA8D;AAC9D,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAED,0EAA0E;AAC1E,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAED,wEAAwE;AACxE,wBAAsB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEzE;AAED,yDAAyD;AACzD,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE7E;AAED,qEAAqE;AACrE,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAED,mFAAmF;AACnF,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEhF;AAED,+EAA+E;AAC/E,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAE/E;AAED,+DAA+D;AAC/D,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpE;AAED,sEAAsE;AACtE,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAE/D;AAeD,oEAAoE;AACpE,eAAO,MAAM,uBAAuB,8DAS1B,CAAA;AAIV,qFAAqF;AACrF,wBAAsB,+BAA+B,CAAC,QAAQ,oDAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAI9F;AAED,OAAO,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAC1G,YAAY,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAA;AAE5E,eAAe,uBAAuB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { defineExtension, hook, globalRegistry, catalogExtension, BROWSER_SECRETS_RESOLVER_ID, } from "@executioncontrolprotocol/core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { BrowserSecretsVault, createLocalVaultStorage, createMemoryVaultStorage, } from "./crypto/vault.js";
|
|
4
|
+
import { BrowserSecretStore } from "./store.js";
|
|
5
|
+
const EXT_ID = "@executioncontrolprotocol/browser-secrets";
|
|
6
|
+
let storage = typeof localStorage !== "undefined" ? createLocalVaultStorage() : createMemoryVaultStorage();
|
|
7
|
+
let vault = new BrowserSecretsVault(storage);
|
|
8
|
+
let store = new BrowserSecretStore(vault, storage);
|
|
9
|
+
function refreshStore() {
|
|
10
|
+
vault = new BrowserSecretsVault(storage);
|
|
11
|
+
store = new BrowserSecretStore(vault, storage);
|
|
12
|
+
}
|
|
13
|
+
/** Replace vault storage (tests). @category BrowserSecrets */
|
|
14
|
+
export function setBrowserSecretsStorage(next) {
|
|
15
|
+
storage = next;
|
|
16
|
+
refreshStore();
|
|
17
|
+
}
|
|
18
|
+
/** Replace vault instance (tests). @category BrowserSecrets */
|
|
19
|
+
export function setBrowserSecretsVault(next) {
|
|
20
|
+
vault = next;
|
|
21
|
+
store = new BrowserSecretStore(vault, storage);
|
|
22
|
+
}
|
|
23
|
+
/** Reset to default localStorage-backed vault. @category BrowserSecrets */
|
|
24
|
+
export function resetBrowserSecretsVault() {
|
|
25
|
+
storage = typeof localStorage !== "undefined" ? createLocalVaultStorage() : createMemoryVaultStorage();
|
|
26
|
+
refreshStore();
|
|
27
|
+
}
|
|
28
|
+
/** Whether vault metadata exists. @category BrowserSecrets */
|
|
29
|
+
export function hasBrowserVault() {
|
|
30
|
+
return vault.hasVault();
|
|
31
|
+
}
|
|
32
|
+
/** Whether the vault master key is in memory. @category BrowserSecrets */
|
|
33
|
+
export function isBrowserVaultUnlocked() {
|
|
34
|
+
return vault.isUnlocked();
|
|
35
|
+
}
|
|
36
|
+
/** Create a new passphrase-protected vault. @category BrowserSecrets */
|
|
37
|
+
export async function setupBrowserVault(passphrase) {
|
|
38
|
+
await vault.setup(passphrase);
|
|
39
|
+
}
|
|
40
|
+
/** Unlock an existing vault. @category BrowserSecrets */
|
|
41
|
+
export async function unlockBrowserVault(passphrase) {
|
|
42
|
+
return vault.unlock(passphrase);
|
|
43
|
+
}
|
|
44
|
+
/** Lock the vault (clear in-memory key). @category BrowserSecrets */
|
|
45
|
+
export function lockBrowserVault() {
|
|
46
|
+
vault.lock();
|
|
47
|
+
}
|
|
48
|
+
/** Store an encrypted secret (vault must be unlocked). @category BrowserSecrets */
|
|
49
|
+
export async function setBrowserSecret(key, value) {
|
|
50
|
+
await store.set(key, value);
|
|
51
|
+
}
|
|
52
|
+
/** Read a decrypted secret when vault is unlocked. @category BrowserSecrets */
|
|
53
|
+
export async function getBrowserSecret(key) {
|
|
54
|
+
return store.get(key);
|
|
55
|
+
}
|
|
56
|
+
/** Remove a secret from the vault. @category BrowserSecrets */
|
|
57
|
+
export async function deleteBrowserSecret(key) {
|
|
58
|
+
await store.delete(key);
|
|
59
|
+
}
|
|
60
|
+
/** List logical secret keys in the index. @category BrowserSecrets */
|
|
61
|
+
export async function listBrowserSecretKeys() {
|
|
62
|
+
return store.list();
|
|
63
|
+
}
|
|
64
|
+
function attachBrowserSecretsResolver(ctx) {
|
|
65
|
+
const host = ctx.environment;
|
|
66
|
+
if (!host)
|
|
67
|
+
return;
|
|
68
|
+
const resolver = {
|
|
69
|
+
id: BROWSER_SECRETS_RESOLVER_ID,
|
|
70
|
+
async resolve(name) {
|
|
71
|
+
return store.get(name);
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
host.registerConfigResolver(resolver);
|
|
75
|
+
}
|
|
76
|
+
/** Browser encrypted secrets extension. @category BrowserSecrets */
|
|
77
|
+
export const browserSecretsExtension = defineExtension("@executioncontrolprotocol", "browser-secrets")
|
|
78
|
+
.withSupportedRuntimes(["@executioncontrolprotocol/browser"])
|
|
79
|
+
.withConfig(z.object({}))
|
|
80
|
+
.withHooks([
|
|
81
|
+
hook("environment:configuring", attachBrowserSecretsResolver),
|
|
82
|
+
hook("environment:terminate", () => {
|
|
83
|
+
lockBrowserVault();
|
|
84
|
+
}),
|
|
85
|
+
])
|
|
86
|
+
.build();
|
|
87
|
+
catalogExtension(browserSecretsExtension);
|
|
88
|
+
/** Register `@executioncontrolprotocol/browser-secrets`. @category BrowserSecrets */
|
|
89
|
+
export async function registerBrowserSecretsExtension(registry = globalRegistry) {
|
|
90
|
+
if (!registry.getExtension(EXT_ID)) {
|
|
91
|
+
await registry.registerExtension(browserSecretsExtension);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export { createMemoryVaultStorage, createLocalVaultStorage, BrowserSecretsVault } from "./crypto/vault.js";
|
|
95
|
+
export default browserSecretsExtension;
|
|
96
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { BrowserSecretsVault, VaultStorage } from "./crypto/vault.js";
|
|
2
|
+
/** Secret store backed by encrypted localStorage records. @category BrowserSecrets */
|
|
3
|
+
export declare class BrowserSecretStore {
|
|
4
|
+
private readonly vault;
|
|
5
|
+
private readonly storage;
|
|
6
|
+
constructor(vault: BrowserSecretsVault, storage: VaultStorage);
|
|
7
|
+
get(key: string): Promise<string | undefined>;
|
|
8
|
+
set(key: string, value: string): Promise<void>;
|
|
9
|
+
delete(key: string): Promise<void>;
|
|
10
|
+
list(): Promise<string[]>;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,mBAAmB,EAAyB,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAqBjG,sFAAsF;AACtF,qBAAa,kBAAkB;IAE3B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBADP,KAAK,EAAE,mBAAmB,EAC1B,OAAO,EAAE,YAAY;IAGlC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAY7C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS9C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQlC,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;CAGhC"}
|
package/dist/store.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { SECRET_INDEX_KEY, SECRET_STORAGE_PREFIX, } from "./constants.js";
|
|
2
|
+
function readIndex(storage) {
|
|
3
|
+
const raw = storage.getItem(SECRET_INDEX_KEY);
|
|
4
|
+
if (!raw)
|
|
5
|
+
return [];
|
|
6
|
+
try {
|
|
7
|
+
const parsed = JSON.parse(raw);
|
|
8
|
+
return Array.isArray(parsed) ? parsed.filter((k) => typeof k === "string") : [];
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function writeIndex(storage, keys) {
|
|
15
|
+
storage.setItem(SECRET_INDEX_KEY, JSON.stringify([...new Set(keys)].sort()));
|
|
16
|
+
}
|
|
17
|
+
function storageKey(logicalKey) {
|
|
18
|
+
return `${SECRET_STORAGE_PREFIX}${logicalKey}`;
|
|
19
|
+
}
|
|
20
|
+
/** Secret store backed by encrypted localStorage records. @category BrowserSecrets */
|
|
21
|
+
export class BrowserSecretStore {
|
|
22
|
+
vault;
|
|
23
|
+
storage;
|
|
24
|
+
constructor(vault, storage) {
|
|
25
|
+
this.vault = vault;
|
|
26
|
+
this.storage = storage;
|
|
27
|
+
}
|
|
28
|
+
async get(key) {
|
|
29
|
+
if (!this.vault.isUnlocked())
|
|
30
|
+
return undefined;
|
|
31
|
+
const raw = this.storage.getItem(storageKey(key));
|
|
32
|
+
if (!raw)
|
|
33
|
+
return undefined;
|
|
34
|
+
try {
|
|
35
|
+
const record = JSON.parse(raw);
|
|
36
|
+
return await this.vault.decrypt(record);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async set(key, value) {
|
|
43
|
+
const record = await this.vault.encrypt(value);
|
|
44
|
+
this.storage.setItem(storageKey(key), JSON.stringify(record));
|
|
45
|
+
const index = readIndex(this.storage);
|
|
46
|
+
if (!index.includes(key)) {
|
|
47
|
+
writeIndex(this.storage, [...index, key]);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async delete(key) {
|
|
51
|
+
this.storage.removeItem(storageKey(key));
|
|
52
|
+
writeIndex(this.storage, readIndex(this.storage).filter((k) => k !== key));
|
|
53
|
+
}
|
|
54
|
+
async list() {
|
|
55
|
+
return readIndex(this.storage);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@executioncontrolprotocol/browser-secrets",
|
|
3
|
+
"version": "0.10.0",
|
|
4
|
+
"description": "Passphrase-encrypted browser secrets vault for ECP",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc -b"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"@executioncontrolprotocol/core": "^0.10.0",
|
|
24
|
+
"@executioncontrolprotocol/types": "^0.10.0",
|
|
25
|
+
"zod": "^3.24.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@executioncontrolprotocol/core": "0.10.0",
|
|
29
|
+
"@executioncontrolprotocol/types": "0.10.0",
|
|
30
|
+
"typescript": "^5.7.0",
|
|
31
|
+
"zod": "^3.24.0"
|
|
32
|
+
}
|
|
33
|
+
}
|