agent-vault-cli 0.1.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/.cursor/skills/npm-publish/SKILL.md +58 -0
- package/.github/workflows/ci.yml +67 -0
- package/README.md +164 -0
- package/ROADMAP.md +986 -0
- package/dist/commands/config.d.ts +8 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +67 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/delete.d.ts +7 -0
- package/dist/commands/delete.d.ts.map +1 -0
- package/dist/commands/delete.js +30 -0
- package/dist/commands/delete.js.map +1 -0
- package/dist/commands/login.d.ts +7 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +37 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/register.d.ts +13 -0
- package/dist/commands/register.d.ts.map +1 -0
- package/dist/commands/register.js +160 -0
- package/dist/commands/register.js.map +1 -0
- package/dist/core/audit.d.ts +15 -0
- package/dist/core/audit.d.ts.map +1 -0
- package/dist/core/audit.js +36 -0
- package/dist/core/audit.js.map +1 -0
- package/dist/core/browser.d.ts +7 -0
- package/dist/core/browser.d.ts.map +1 -0
- package/dist/core/browser.js +104 -0
- package/dist/core/browser.js.map +1 -0
- package/dist/core/config.d.ts +9 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +80 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/crypto.d.ts +17 -0
- package/dist/core/crypto.d.ts.map +1 -0
- package/dist/core/crypto.js +90 -0
- package/dist/core/crypto.js.map +1 -0
- package/dist/core/fields.d.ts +5 -0
- package/dist/core/fields.d.ts.map +1 -0
- package/dist/core/fields.js +54 -0
- package/dist/core/fields.js.map +1 -0
- package/dist/core/keychain.d.ts +5 -0
- package/dist/core/keychain.d.ts.map +1 -0
- package/dist/core/keychain.js +97 -0
- package/dist/core/keychain.js.map +1 -0
- package/dist/core/origin.d.ts +25 -0
- package/dist/core/origin.d.ts.map +1 -0
- package/dist/core/origin.js +73 -0
- package/dist/core/origin.js.map +1 -0
- package/dist/core/ratelimit.d.ts +10 -0
- package/dist/core/ratelimit.d.ts.map +1 -0
- package/dist/core/ratelimit.js +70 -0
- package/dist/core/ratelimit.js.map +1 -0
- package/dist/core/secure-memory.d.ts +39 -0
- package/dist/core/secure-memory.d.ts.map +1 -0
- package/dist/core/secure-memory.js +68 -0
- package/dist/core/secure-memory.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +129 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +27 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +58 -0
- package/src/commands/config.ts +84 -0
- package/src/commands/delete.ts +39 -0
- package/src/commands/login.ts +49 -0
- package/src/commands/register.ts +188 -0
- package/src/core/audit.ts +59 -0
- package/src/core/browser.ts +131 -0
- package/src/core/config.ts +91 -0
- package/src/core/crypto.ts +106 -0
- package/src/core/fields.ts +59 -0
- package/src/core/keychain.ts +110 -0
- package/src/core/origin.ts +90 -0
- package/src/core/ratelimit.ts +89 -0
- package/src/core/secure-memory.ts +78 -0
- package/src/index.ts +133 -0
- package/src/types/index.ts +31 -0
- package/tests/browser-password-manager.test.ts +1023 -0
- package/tests/crypto.test.ts +140 -0
- package/tests/e2e.test.ts +565 -0
- package/tests/fixtures/server.ts +59 -0
- package/tests/security.test.ts +113 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +17 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type ConfigAction = 'get' | 'set' | 'unset' | 'list';
|
|
2
|
+
export interface ConfigOptions {
|
|
3
|
+
action: ConfigAction;
|
|
4
|
+
key?: string;
|
|
5
|
+
value?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function config(options: ConfigOptions): Promise<void>;
|
|
8
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,CAAC;AAE5D,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,YAAY,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAiElE"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { loadConfig, getConfigValue, setConfigValue, unsetConfigValue, isValidConfigKey, getValidConfigKeys, } from '../core/config.js';
|
|
2
|
+
export async function config(options) {
|
|
3
|
+
const { action, key, value } = options;
|
|
4
|
+
switch (action) {
|
|
5
|
+
case 'list': {
|
|
6
|
+
const cfg = await loadConfig();
|
|
7
|
+
const entries = Object.entries(cfg);
|
|
8
|
+
if (entries.length === 0) {
|
|
9
|
+
console.log('No configuration values set.');
|
|
10
|
+
console.log(`Available keys: ${getValidConfigKeys().join(', ')}`);
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
for (const [k, v] of entries) {
|
|
14
|
+
console.log(`${k}=${v}`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
case 'get': {
|
|
20
|
+
if (!key) {
|
|
21
|
+
throw new Error('Key is required for get operation');
|
|
22
|
+
}
|
|
23
|
+
if (!isValidConfigKey(key)) {
|
|
24
|
+
throw new Error(`Invalid config key: ${key}. Valid keys: ${getValidConfigKeys().join(', ')}`);
|
|
25
|
+
}
|
|
26
|
+
const val = await getConfigValue(key);
|
|
27
|
+
if (val !== undefined) {
|
|
28
|
+
console.log(val);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
console.log(`(not set)`);
|
|
32
|
+
}
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
case 'set': {
|
|
36
|
+
if (!key) {
|
|
37
|
+
throw new Error('Key is required for set operation');
|
|
38
|
+
}
|
|
39
|
+
if (!isValidConfigKey(key)) {
|
|
40
|
+
throw new Error(`Invalid config key: ${key}. Valid keys: ${getValidConfigKeys().join(', ')}`);
|
|
41
|
+
}
|
|
42
|
+
if (!value) {
|
|
43
|
+
throw new Error('Value is required for set operation');
|
|
44
|
+
}
|
|
45
|
+
await setConfigValue(key, value);
|
|
46
|
+
console.log(`✓ Set ${key}=${value}`);
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
case 'unset': {
|
|
50
|
+
if (!key) {
|
|
51
|
+
throw new Error('Key is required for unset operation');
|
|
52
|
+
}
|
|
53
|
+
if (!isValidConfigKey(key)) {
|
|
54
|
+
throw new Error(`Invalid config key: ${key}. Valid keys: ${getValidConfigKeys().join(', ')}`);
|
|
55
|
+
}
|
|
56
|
+
const deleted = await unsetConfigValue(key);
|
|
57
|
+
if (deleted) {
|
|
58
|
+
console.log(`✓ Unset ${key}`);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
console.log(`${key} was not set`);
|
|
62
|
+
}
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../src/commands/delete.ts"],"names":[],"mappings":"AAGA,UAAU,aAAa;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BzE"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import { deleteRP, getRP } from '../core/keychain.js';
|
|
3
|
+
export async function deleteCommand(options) {
|
|
4
|
+
const config = await getRP(options.origin);
|
|
5
|
+
if (!config) {
|
|
6
|
+
throw new Error(`No credentials found for ${options.origin}`);
|
|
7
|
+
}
|
|
8
|
+
if (!options.force) {
|
|
9
|
+
const { confirm } = await inquirer.prompt([
|
|
10
|
+
{
|
|
11
|
+
type: 'confirm',
|
|
12
|
+
name: 'confirm',
|
|
13
|
+
message: `Delete credentials for ${options.origin}?`,
|
|
14
|
+
default: false,
|
|
15
|
+
},
|
|
16
|
+
]);
|
|
17
|
+
if (!confirm) {
|
|
18
|
+
console.log('Deletion cancelled.');
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const deleted = await deleteRP(options.origin);
|
|
23
|
+
if (deleted) {
|
|
24
|
+
console.log(`✓ Deleted credentials for ${options.origin}`);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
throw new Error(`Failed to delete credentials for ${options.origin}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=delete.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete.js","sourceRoot":"","sources":["../../src/commands/delete.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAOtD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAsB;IACxD,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAE3C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,4BAA4B,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACnB,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAuB;YAC9D;gBACE,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,0BAA0B,OAAO,CAAC,MAAM,GAAG;gBACpD,OAAO,EAAE,KAAK;aACf;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;YACnC,OAAO;QACT,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAE/C,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CAAC,GAAG,CAAC,6BAA6B,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7D,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,KAAK,CAAC,oCAAoC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACxE,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAIA,UAAU,YAAY;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAuChE"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { connectToBrowser, fillField, clickElement } from '../core/browser.js';
|
|
2
|
+
import { extractOrigin, extractAndValidateOrigin } from '../core/origin.js';
|
|
3
|
+
import { getRP } from '../core/keychain.js';
|
|
4
|
+
export async function login(options) {
|
|
5
|
+
const { browser, page } = await connectToBrowser(options.cdp);
|
|
6
|
+
try {
|
|
7
|
+
const currentUrl = page.url();
|
|
8
|
+
const origin = extractAndValidateOrigin(currentUrl);
|
|
9
|
+
// Lookup stored config for this origin
|
|
10
|
+
const config = await getRP(origin);
|
|
11
|
+
if (!config) {
|
|
12
|
+
throw new Error('No credentials found for this site');
|
|
13
|
+
}
|
|
14
|
+
// Verify origin hasn't changed before filling
|
|
15
|
+
const verifyOrigin = extractOrigin(page.url());
|
|
16
|
+
if (verifyOrigin !== origin) {
|
|
17
|
+
throw new Error('Page navigation detected - aborting for security');
|
|
18
|
+
}
|
|
19
|
+
// Fill username and password using STORED selectors
|
|
20
|
+
await fillField(page, config.selectors.username, config.credentials.username);
|
|
21
|
+
await fillField(page, config.selectors.password, config.credentials.password);
|
|
22
|
+
// Verify again after filling
|
|
23
|
+
const postFillOrigin = extractOrigin(page.url());
|
|
24
|
+
if (postFillOrigin !== origin) {
|
|
25
|
+
throw new Error('Page changed during credential fill - possible attack');
|
|
26
|
+
}
|
|
27
|
+
// Optionally click submit
|
|
28
|
+
if (options.submit && config.selectors.submit) {
|
|
29
|
+
await clickElement(page, config.selectors.submit);
|
|
30
|
+
}
|
|
31
|
+
console.log('Login filled successfully');
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
await browser.close();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC5E,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAO5C,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,OAAqB;IAC/C,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,MAAM,gBAAgB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAE9D,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAG,wBAAwB,CAAC,UAAU,CAAC,CAAC;QAEpD,uCAAuC;QACvC,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC;QAEnC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;QACxD,CAAC;QAED,8CAA8C;QAC9C,MAAM,YAAY,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QAC/C,IAAI,YAAY,KAAK,MAAM,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;QACtE,CAAC;QAED,oDAAoD;QACpD,MAAM,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC9E,MAAM,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QAE9E,6BAA6B;QAC7B,MAAM,cAAc,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QACjD,IAAI,cAAc,KAAK,MAAM,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;QAC3E,CAAC;QAED,0BAA0B;QAC1B,IAAI,OAAO,CAAC,MAAM,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;YAC9C,MAAM,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACpD,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;IAC3C,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface RegisterOptions {
|
|
2
|
+
cdp: string;
|
|
3
|
+
usernameSelector: string;
|
|
4
|
+
passwordSelector: string;
|
|
5
|
+
submitSelector?: string;
|
|
6
|
+
username?: string;
|
|
7
|
+
password?: string;
|
|
8
|
+
generatePassword?: boolean;
|
|
9
|
+
force?: boolean;
|
|
10
|
+
allowHttp?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function register(options: RegisterOptions): Promise<void>;
|
|
13
|
+
//# sourceMappingURL=register.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../src/commands/register.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,wBAAsB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAiKtE"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import { connectToBrowser, fillField, validateSelector } from '../core/browser.js';
|
|
3
|
+
import { extractAndValidateOriginSecure, } from '../core/origin.js';
|
|
4
|
+
import { storeRP, getRP } from '../core/keychain.js';
|
|
5
|
+
import { generatePassword } from '../core/crypto.js';
|
|
6
|
+
import { getConfigValue } from '../core/config.js';
|
|
7
|
+
import { SecureString } from '../core/secure-memory.js';
|
|
8
|
+
export async function register(options) {
|
|
9
|
+
const { browser, page } = await connectToBrowser(options.cdp);
|
|
10
|
+
// Use SecureString for sensitive credential handling
|
|
11
|
+
let secureUsername = null;
|
|
12
|
+
let securePassword = null;
|
|
13
|
+
try {
|
|
14
|
+
const currentUrl = page.url();
|
|
15
|
+
// Use secure origin validation with HTTP blocking
|
|
16
|
+
const originOptions = {
|
|
17
|
+
allowHttp: options.allowHttp,
|
|
18
|
+
};
|
|
19
|
+
const origin = await extractAndValidateOriginSecure(currentUrl, originOptions);
|
|
20
|
+
// Validate selectors exist on page (don't expose selector in error)
|
|
21
|
+
const usernameValid = await validateSelector(page, options.usernameSelector);
|
|
22
|
+
const passwordValid = await validateSelector(page, options.passwordSelector);
|
|
23
|
+
if (!usernameValid) {
|
|
24
|
+
throw new Error('Username field selector not found on page');
|
|
25
|
+
}
|
|
26
|
+
if (!passwordValid) {
|
|
27
|
+
throw new Error('Password field selector not found on page');
|
|
28
|
+
}
|
|
29
|
+
if (options.submitSelector) {
|
|
30
|
+
const submitValid = await validateSelector(page, options.submitSelector);
|
|
31
|
+
if (!submitValid) {
|
|
32
|
+
console.warn('Warning: Submit button selector not found on page');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// Check if already registered
|
|
36
|
+
const existing = await getRP(origin);
|
|
37
|
+
if (existing) {
|
|
38
|
+
if (options.force) {
|
|
39
|
+
// Non-interactive: overwrite without prompting
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
const { overwrite } = await inquirer.prompt([
|
|
43
|
+
{
|
|
44
|
+
type: 'confirm',
|
|
45
|
+
name: 'overwrite',
|
|
46
|
+
message: `Credentials already exist for this site. Overwrite?`,
|
|
47
|
+
default: false,
|
|
48
|
+
},
|
|
49
|
+
]);
|
|
50
|
+
if (!overwrite) {
|
|
51
|
+
console.log('Registration cancelled.');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Confirm registration (skip if force or username provided non-interactively)
|
|
57
|
+
if (!options.force && !options.username) {
|
|
58
|
+
const { confirm } = await inquirer.prompt([
|
|
59
|
+
{
|
|
60
|
+
type: 'confirm',
|
|
61
|
+
name: 'confirm',
|
|
62
|
+
message: `Register credentials for this site?`,
|
|
63
|
+
default: true,
|
|
64
|
+
},
|
|
65
|
+
]);
|
|
66
|
+
if (!confirm) {
|
|
67
|
+
console.log('Registration cancelled.');
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Get username
|
|
72
|
+
let usernameValue;
|
|
73
|
+
if (options.username) {
|
|
74
|
+
usernameValue = options.username;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
const defaultUsername = await getConfigValue('defaultUsername');
|
|
78
|
+
const response = await inquirer.prompt([
|
|
79
|
+
{
|
|
80
|
+
type: 'input',
|
|
81
|
+
name: 'username',
|
|
82
|
+
message: 'Enter username/email:',
|
|
83
|
+
default: defaultUsername,
|
|
84
|
+
validate: (input) => input.length > 0 || 'Username is required',
|
|
85
|
+
},
|
|
86
|
+
]);
|
|
87
|
+
usernameValue = response.username;
|
|
88
|
+
}
|
|
89
|
+
secureUsername = new SecureString(usernameValue);
|
|
90
|
+
// Get password
|
|
91
|
+
let passwordValue;
|
|
92
|
+
if (options.password) {
|
|
93
|
+
passwordValue = options.password;
|
|
94
|
+
}
|
|
95
|
+
else if (options.generatePassword) {
|
|
96
|
+
passwordValue = generatePassword();
|
|
97
|
+
// Security: Don't preview any part of the password
|
|
98
|
+
console.log('Secure password generated (stored in keychain)');
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
const { passwordOption } = await inquirer.prompt([
|
|
102
|
+
{
|
|
103
|
+
type: 'list',
|
|
104
|
+
name: 'passwordOption',
|
|
105
|
+
message: 'Password:',
|
|
106
|
+
choices: [
|
|
107
|
+
{ name: 'Generate secure password', value: 'generate' },
|
|
108
|
+
{ name: 'Enter existing password', value: 'enter' },
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
]);
|
|
112
|
+
if (passwordOption === 'generate') {
|
|
113
|
+
passwordValue = generatePassword();
|
|
114
|
+
// Security: Don't preview any part of the password
|
|
115
|
+
console.log('Secure password generated (stored in keychain)');
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
const { enteredPassword } = await inquirer.prompt([
|
|
119
|
+
{
|
|
120
|
+
type: 'password',
|
|
121
|
+
name: 'enteredPassword',
|
|
122
|
+
message: 'Enter password:',
|
|
123
|
+
mask: '*',
|
|
124
|
+
validate: (input) => input.length > 0 || 'Password is required',
|
|
125
|
+
},
|
|
126
|
+
]);
|
|
127
|
+
passwordValue = enteredPassword;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
securePassword = new SecureString(passwordValue);
|
|
131
|
+
const selectors = {
|
|
132
|
+
username: options.usernameSelector,
|
|
133
|
+
password: options.passwordSelector,
|
|
134
|
+
submit: options.submitSelector,
|
|
135
|
+
};
|
|
136
|
+
// Fill form fields using secure values
|
|
137
|
+
await fillField(page, options.usernameSelector, secureUsername.getValue());
|
|
138
|
+
await fillField(page, options.passwordSelector, securePassword.getValue());
|
|
139
|
+
// Store in keychain
|
|
140
|
+
await storeRP({
|
|
141
|
+
origin,
|
|
142
|
+
selectors,
|
|
143
|
+
credentials: {
|
|
144
|
+
username: secureUsername.getValue(),
|
|
145
|
+
password: securePassword.getValue(),
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
console.log('Credentials registered successfully');
|
|
149
|
+
console.log('Complete signup/login in browser.');
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
// Securely clear sensitive data from memory
|
|
153
|
+
if (secureUsername)
|
|
154
|
+
secureUsername.clear();
|
|
155
|
+
if (securePassword)
|
|
156
|
+
securePassword.clear();
|
|
157
|
+
await browser.close();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=register.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../src/commands/register.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type AuditEvent = 'credential_stored' | 'credential_retrieved' | 'credential_deleted' | 'login_filled' | 'config_changed' | 'rate_limit_exceeded';
|
|
2
|
+
/**
|
|
3
|
+
* Log an audit event to the audit log file.
|
|
4
|
+
* Never includes sensitive data like passwords or usernames.
|
|
5
|
+
*/
|
|
6
|
+
export declare function logAuditEvent(event: AuditEvent, options?: {
|
|
7
|
+
origin?: string;
|
|
8
|
+
details?: string;
|
|
9
|
+
success?: boolean;
|
|
10
|
+
}): Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* Get the audit log file path (for admin/debug purposes)
|
|
13
|
+
*/
|
|
14
|
+
export declare function getAuditLogPath(): string;
|
|
15
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/core/audit.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,UAAU,GAClB,mBAAmB,GACnB,sBAAsB,GACtB,oBAAoB,GACpB,cAAc,GACd,gBAAgB,GAChB,qBAAqB,CAAC;AAc1B;;;GAGG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,UAAU,EACjB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GACrE,OAAO,CAAC,IAAI,CAAC,CAiBf;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { appendFile, mkdir } from 'node:fs/promises';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
const AUDIT_DIR = join(homedir(), '.agent-vault');
|
|
5
|
+
const AUDIT_FILE = join(AUDIT_DIR, 'audit.log');
|
|
6
|
+
async function ensureAuditDir() {
|
|
7
|
+
await mkdir(AUDIT_DIR, { recursive: true, mode: 0o700 });
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Log an audit event to the audit log file.
|
|
11
|
+
* Never includes sensitive data like passwords or usernames.
|
|
12
|
+
*/
|
|
13
|
+
export async function logAuditEvent(event, options = {}) {
|
|
14
|
+
try {
|
|
15
|
+
await ensureAuditDir();
|
|
16
|
+
const entry = {
|
|
17
|
+
timestamp: new Date().toISOString(),
|
|
18
|
+
event,
|
|
19
|
+
origin: options.origin,
|
|
20
|
+
details: options.details,
|
|
21
|
+
success: options.success ?? true,
|
|
22
|
+
};
|
|
23
|
+
const line = JSON.stringify(entry) + '\n';
|
|
24
|
+
await appendFile(AUDIT_FILE, line, { mode: 0o600 });
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// Silently fail - audit logging should never break the main flow
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Get the audit log file path (for admin/debug purposes)
|
|
32
|
+
*/
|
|
33
|
+
export function getAuditLogPath() {
|
|
34
|
+
return AUDIT_FILE;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/core/audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,cAAc,CAAC,CAAC;AAClD,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;AAkBhD,KAAK,UAAU,cAAc;IAC3B,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAC3D,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,KAAiB,EACjB,UAAoE,EAAE;IAEtE,IAAI,CAAC;QACH,MAAM,cAAc,EAAE,CAAC;QAEvB,MAAM,KAAK,GAAe;YACxB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,KAAK;YACL,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,IAAI;SACjC,CAAC;QAEF,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;QAC1C,MAAM,UAAU,CAAC,UAAU,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,iEAAiE;IACnE,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,UAAU,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { BrowserConnection } from '../types/index.js';
|
|
2
|
+
export declare function validateCDPEndpoint(endpoint: string): Promise<void>;
|
|
3
|
+
export declare function connectToBrowser(cdpEndpoint: string): Promise<BrowserConnection>;
|
|
4
|
+
export declare function fillField(page: BrowserConnection['page'], selector: string, value: string): Promise<void>;
|
|
5
|
+
export declare function clickElement(page: BrowserConnection['page'], selector: string): Promise<void>;
|
|
6
|
+
export declare function validateSelector(page: BrowserConnection['page'], selector: string): Promise<boolean>;
|
|
7
|
+
//# sourceMappingURL=browser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/core/browser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AA2B3D,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBzE;AAED,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAuCtF;AAED,wBAAsB,SAAS,CAC7B,IAAI,EAAE,iBAAiB,CAAC,MAAM,CAAC,EAC/B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAsB,YAAY,CAChC,IAAI,EAAE,iBAAiB,CAAC,MAAM,CAAC,EAC/B,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,iBAAiB,CAAC,MAAM,CAAC,EAC/B,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,CAOlB"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { chromium } from 'playwright';
|
|
2
|
+
import { getConfigValue } from './config.js';
|
|
3
|
+
const CDP_TIMEOUT_MS = 10000;
|
|
4
|
+
// Default allowed CDP hosts for security
|
|
5
|
+
const DEFAULT_CDP_ALLOWLIST = ['127.0.0.1', 'localhost', '::1'];
|
|
6
|
+
/**
|
|
7
|
+
* Parse CDP allowlist from config
|
|
8
|
+
*/
|
|
9
|
+
async function getCdpAllowlist() {
|
|
10
|
+
const configValue = await getConfigValue('cdpAllowlist');
|
|
11
|
+
if (configValue) {
|
|
12
|
+
return configValue.split(',').map((h) => h.trim().toLowerCase());
|
|
13
|
+
}
|
|
14
|
+
return DEFAULT_CDP_ALLOWLIST;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Check if a CDP endpoint host is in the allowlist
|
|
18
|
+
*/
|
|
19
|
+
async function isAllowedCdpHost(hostname) {
|
|
20
|
+
const allowlist = await getCdpAllowlist();
|
|
21
|
+
return allowlist.includes(hostname.toLowerCase());
|
|
22
|
+
}
|
|
23
|
+
export async function validateCDPEndpoint(endpoint) {
|
|
24
|
+
let url;
|
|
25
|
+
try {
|
|
26
|
+
url = new URL(endpoint);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
throw new Error('Invalid CDP endpoint format');
|
|
30
|
+
}
|
|
31
|
+
if (url.protocol !== 'ws:' && url.protocol !== 'wss:') {
|
|
32
|
+
throw new Error('CDP endpoint must use WebSocket protocol');
|
|
33
|
+
}
|
|
34
|
+
if (!url.hostname) {
|
|
35
|
+
throw new Error('CDP endpoint must have a valid hostname');
|
|
36
|
+
}
|
|
37
|
+
// Check against allowlist
|
|
38
|
+
const isAllowed = await isAllowedCdpHost(url.hostname);
|
|
39
|
+
if (!isAllowed) {
|
|
40
|
+
throw new Error('CDP endpoint host is not in the allowlist. ' +
|
|
41
|
+
'Add it with: vault config set cdpAllowlist "host1,host2"');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export async function connectToBrowser(cdpEndpoint) {
|
|
45
|
+
await validateCDPEndpoint(cdpEndpoint);
|
|
46
|
+
let browser;
|
|
47
|
+
try {
|
|
48
|
+
browser = await Promise.race([
|
|
49
|
+
chromium.connectOverCDP(cdpEndpoint),
|
|
50
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('CDP connection timeout')), CDP_TIMEOUT_MS)),
|
|
51
|
+
]);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
// Sanitize connection errors - don't expose internal details
|
|
55
|
+
if (error instanceof Error) {
|
|
56
|
+
if (error.message.includes('timeout')) {
|
|
57
|
+
throw new Error('CDP connection timeout - ensure browser is running with remote debugging');
|
|
58
|
+
}
|
|
59
|
+
if (error.message.includes('ECONNREFUSED')) {
|
|
60
|
+
throw new Error('Cannot connect to CDP endpoint - ensure browser is running');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
throw new Error('Failed to connect to browser');
|
|
64
|
+
}
|
|
65
|
+
const contexts = browser.contexts();
|
|
66
|
+
if (contexts.length === 0) {
|
|
67
|
+
await browser.close();
|
|
68
|
+
throw new Error('No browser context available');
|
|
69
|
+
}
|
|
70
|
+
const pages = contexts[0].pages();
|
|
71
|
+
if (pages.length === 0) {
|
|
72
|
+
await browser.close();
|
|
73
|
+
throw new Error('No browser page available');
|
|
74
|
+
}
|
|
75
|
+
return { browser, page: pages[0] };
|
|
76
|
+
}
|
|
77
|
+
export async function fillField(page, selector, value) {
|
|
78
|
+
try {
|
|
79
|
+
await page.fill(selector, value);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// Sanitize selector errors - don't expose selector in error message
|
|
83
|
+
throw new Error('Failed to fill form field - selector may be invalid');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export async function clickElement(page, selector) {
|
|
87
|
+
try {
|
|
88
|
+
await page.click(selector);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// Sanitize selector errors
|
|
92
|
+
throw new Error('Failed to click element - selector may be invalid');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
export async function validateSelector(page, selector) {
|
|
96
|
+
try {
|
|
97
|
+
const element = await page.$(selector);
|
|
98
|
+
return element !== null;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/core/browser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { VaultConfig, ConfigKey } from '../types/index.js';
|
|
2
|
+
export declare function loadConfig(): Promise<VaultConfig>;
|
|
3
|
+
export declare function saveConfig(config: VaultConfig): Promise<void>;
|
|
4
|
+
export declare function getConfigValue(key: ConfigKey): Promise<string | undefined>;
|
|
5
|
+
export declare function setConfigValue(key: ConfigKey, value: string): Promise<void>;
|
|
6
|
+
export declare function unsetConfigValue(key: ConfigKey): Promise<boolean>;
|
|
7
|
+
export declare function isValidConfigKey(key: string): key is ConfigKey;
|
|
8
|
+
export declare function getValidConfigKeys(): ConfigKey[];
|
|
9
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAuBhE,wBAAsB,UAAU,IAAI,OAAO,CAAC,WAAW,CAAC,CAOvD;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBnE;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAGhF;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKjF;AAED,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CASvE;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,IAAI,SAAS,CAG9D;AAED,wBAAgB,kBAAkB,IAAI,SAAS,EAAE,CAEhD"}
|