ai-cred 1.0.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.
Files changed (64) hide show
  1. package/dist/cli/commands/add.d.ts +8 -0
  2. package/dist/cli/commands/add.js +145 -0
  3. package/dist/cli/commands/add.js.map +1 -0
  4. package/dist/cli/commands/get.d.ts +7 -0
  5. package/dist/cli/commands/get.js +53 -0
  6. package/dist/cli/commands/get.js.map +1 -0
  7. package/dist/cli/commands/list.d.ts +7 -0
  8. package/dist/cli/commands/list.js +118 -0
  9. package/dist/cli/commands/list.js.map +1 -0
  10. package/dist/cli/commands/remove.d.ts +7 -0
  11. package/dist/cli/commands/remove.js +53 -0
  12. package/dist/cli/commands/remove.js.map +1 -0
  13. package/dist/cli/commands/update.d.ts +8 -0
  14. package/dist/cli/commands/update.js +140 -0
  15. package/dist/cli/commands/update.js.map +1 -0
  16. package/dist/cli/index.d.ts +7 -0
  17. package/dist/cli/index.js +25 -0
  18. package/dist/cli/index.js.map +1 -0
  19. package/dist/cli/utils/display.d.ts +14 -0
  20. package/dist/cli/utils/display.js +31 -0
  21. package/dist/cli/utils/display.js.map +1 -0
  22. package/dist/cli/utils/prompt.d.ts +6 -0
  23. package/dist/cli/utils/prompt.js +66 -0
  24. package/dist/cli/utils/prompt.js.map +1 -0
  25. package/dist/core/audit-logger.d.ts +25 -0
  26. package/dist/core/audit-logger.js +41 -0
  27. package/dist/core/audit-logger.js.map +1 -0
  28. package/dist/core/audit-logger.test.d.ts +1 -0
  29. package/dist/core/audit-logger.test.js +89 -0
  30. package/dist/core/audit-logger.test.js.map +1 -0
  31. package/dist/core/errors.d.ts +23 -0
  32. package/dist/core/errors.js +78 -0
  33. package/dist/core/errors.js.map +1 -0
  34. package/dist/core/keychain-adapter.d.ts +65 -0
  35. package/dist/core/keychain-adapter.js +221 -0
  36. package/dist/core/keychain-adapter.js.map +1 -0
  37. package/dist/core/keychain-adapter.test.d.ts +1 -0
  38. package/dist/core/keychain-adapter.test.js +331 -0
  39. package/dist/core/keychain-adapter.test.js.map +1 -0
  40. package/dist/e2e/environment-isolation.test.d.ts +1 -0
  41. package/dist/e2e/environment-isolation.test.js +112 -0
  42. package/dist/e2e/environment-isolation.test.js.map +1 -0
  43. package/dist/server/index.d.ts +7 -0
  44. package/dist/server/index.js +26 -0
  45. package/dist/server/index.js.map +1 -0
  46. package/dist/server/tools.d.ts +10 -0
  47. package/dist/server/tools.js +230 -0
  48. package/dist/server/tools.js.map +1 -0
  49. package/dist/server/tools.test.d.ts +7 -0
  50. package/dist/server/tools.test.js +606 -0
  51. package/dist/server/tools.test.js.map +1 -0
  52. package/dist/types/credential.d.ts +77 -0
  53. package/dist/types/credential.js +46 -0
  54. package/dist/types/credential.js.map +1 -0
  55. package/dist/types/credential.test.d.ts +1 -0
  56. package/dist/types/credential.test.js +316 -0
  57. package/dist/types/credential.test.js.map +1 -0
  58. package/dist/utils/validation.d.ts +15 -0
  59. package/dist/utils/validation.js +34 -0
  60. package/dist/utils/validation.js.map +1 -0
  61. package/dist/utils/validation.test.d.ts +1 -0
  62. package/dist/utils/validation.test.js +139 -0
  63. package/dist/utils/validation.test.js.map +1 -0
  64. package/package.json +35 -0
@@ -0,0 +1,221 @@
1
+ /**
2
+ * KeychainAdapter: Wraps the macOS `security` CLI for all credential operations.
3
+ *
4
+ * Security invariants enforced throughout this file:
5
+ * - SEC-01: Every exec() call targets this.keychainPath (dedicated keychain only)
6
+ * - SEC-02: All subprocess calls use execFile (not exec) with argument arrays --
7
+ * shell string interpolation is never used
8
+ * - SEC-04: list() returns metadata only -- no secret values, no -d/-w flags
9
+ * - SEC-05: No logging of subprocess arguments or output (they may contain credentials)
10
+ */
11
+ import { execFile } from 'node:child_process';
12
+ import { promisify } from 'node:util';
13
+ import { existsSync } from 'node:fs';
14
+ import { homedir } from 'node:os';
15
+ import { join } from 'node:path';
16
+ import { CredentialSchema } from '../types/credential.js';
17
+ import { buildServiceKey } from '../utils/validation.js';
18
+ import { KeychainError, toKeychainError } from './errors.js';
19
+ // Re-export KeychainError so callers can import from this module too
20
+ export { KeychainError } from './errors.js';
21
+ const KEYCHAIN_PASSWORD = 'ai-cred';
22
+ const KEYCHAIN_FILENAME = 'ai-cred.keychain-db';
23
+ const ACCOUNT = 'default';
24
+ const APP_MARKER = 'ai-cred';
25
+ const DEFAULT_KEYCHAIN_PATH = join(homedir(), 'Library', 'Keychains', KEYCHAIN_FILENAME);
26
+ const execFileAsync = promisify(execFile);
27
+ export class KeychainAdapter {
28
+ keychainPath;
29
+ constructor(keychainPath) {
30
+ this.keychainPath = keychainPath ?? DEFAULT_KEYCHAIN_PATH;
31
+ }
32
+ /**
33
+ * Internal helper: invoke the macOS `security` binary with the given args.
34
+ *
35
+ * SECURITY: Never log args (may include credentials in -w values).
36
+ * SECURITY: Errors are converted via toKeychainError which strips subprocess output.
37
+ */
38
+ async exec(args, operationContext) {
39
+ try {
40
+ const { stdout, stderr } = await execFileAsync('/usr/bin/security', args);
41
+ return { stdout, stderr };
42
+ }
43
+ catch (err) {
44
+ const op = operationContext?.operation ?? 'unknown';
45
+ const svc = operationContext?.service;
46
+ throw toKeychainError(err, op, svc);
47
+ }
48
+ }
49
+ /**
50
+ * Initialize the dedicated keychain.
51
+ *
52
+ * First run: creates keychain, disables auto-lock, adds to search list.
53
+ * Every run: unlocks the keychain so operations can proceed.
54
+ */
55
+ async initialize() {
56
+ if (!existsSync(this.keychainPath)) {
57
+ // --- First run: create and configure the keychain ---
58
+ // Create the keychain with the internal password
59
+ await this.exec(['create-keychain', '-p', KEYCHAIN_PASSWORD, this.keychainPath], { operation: 'create-keychain' });
60
+ // Disable auto-lock: set-keychain-settings with no flags clears the
61
+ // lock-on-sleep and lock-after-timeout settings
62
+ await this.exec(['set-keychain-settings', this.keychainPath], { operation: 'set-keychain-settings' });
63
+ // Preserve existing keychains when adding our keychain to the search list
64
+ const { stdout: listOut } = await this.exec(['list-keychains', '-d', 'user'], { operation: 'list-keychains' });
65
+ // Each line is a quoted path such as: " \"/path/to/login.keychain-db\"\n"
66
+ const existingKeychains = listOut
67
+ .split('\n')
68
+ .map((line) => line.trim().replace(/^"|"$/g, ''))
69
+ .filter((p) => p.length > 0);
70
+ await this.exec(['list-keychains', '-d', 'user', '-s', this.keychainPath, ...existingKeychains], { operation: 'list-keychains-set' });
71
+ }
72
+ // Always unlock on startup (handles both first-run and subsequent runs)
73
+ await this.exec(['unlock-keychain', '-p', KEYCHAIN_PASSWORD, this.keychainPath], { operation: 'unlock-keychain' });
74
+ }
75
+ /**
76
+ * Store a credential in the dedicated keychain.
77
+ * Uses -U to update if the entry already exists.
78
+ */
79
+ async store(env, service, credential) {
80
+ const serviceKey = buildServiceKey(env, service);
81
+ const jsonData = JSON.stringify(credential);
82
+ await this.exec([
83
+ 'add-generic-password',
84
+ '-a', ACCOUNT,
85
+ '-s', serviceKey,
86
+ '-w', jsonData,
87
+ '-U',
88
+ '-j', credential.type,
89
+ '-D', APP_MARKER,
90
+ this.keychainPath,
91
+ ], { operation: 'add-generic-password', service: serviceKey });
92
+ }
93
+ /**
94
+ * Retrieve a credential from the dedicated keychain.
95
+ * Validates the returned JSON against CredentialSchema before returning.
96
+ */
97
+ async get(env, service) {
98
+ const serviceKey = buildServiceKey(env, service);
99
+ const { stdout } = await this.exec(['find-generic-password', '-s', serviceKey, '-a', ACCOUNT, '-w', this.keychainPath], { operation: 'find-generic-password', service: serviceKey });
100
+ let parsed;
101
+ try {
102
+ parsed = JSON.parse(stdout.trim());
103
+ }
104
+ catch {
105
+ throw new KeychainError('find-generic-password', -1, serviceKey);
106
+ }
107
+ const result = CredentialSchema.safeParse(parsed);
108
+ if (!result.success) {
109
+ throw new KeychainError('find-generic-password', -1, serviceKey);
110
+ }
111
+ return result.data;
112
+ }
113
+ /**
114
+ * Delete a credential from the dedicated keychain.
115
+ */
116
+ async delete(env, service) {
117
+ const serviceKey = buildServiceKey(env, service);
118
+ await this.exec(['delete-generic-password', '-s', serviceKey, '-a', ACCOUNT, this.keychainPath], { operation: 'delete-generic-password', service: serviceKey });
119
+ }
120
+ /**
121
+ * List all ai-cred credentials as metadata (no secret values) (SEC-04).
122
+ *
123
+ * Uses dump-keychain without -d flag so password data is NEVER retrieved.
124
+ * Filters entries by APP_MARKER in the description field.
125
+ */
126
+ async list() {
127
+ const { stdout } = await this.exec(
128
+ // dump-keychain without -d: retrieves attributes only, never passwords
129
+ ['dump-keychain', this.keychainPath], { operation: 'dump-keychain' });
130
+ return parseKeychainDump(stdout);
131
+ }
132
+ /**
133
+ * Delete the entire keychain file. Intended for test cleanup only.
134
+ */
135
+ async destroy() {
136
+ await this.exec(['delete-keychain', this.keychainPath], { operation: 'delete-keychain' });
137
+ }
138
+ }
139
+ // ---------------------------------------------------------------------------
140
+ // Internal: keychain dump parser
141
+ // ---------------------------------------------------------------------------
142
+ /**
143
+ * Parse the output of `security dump-keychain` and return CredentialMetadata
144
+ * for every entry that belongs to ai-cred.
145
+ *
146
+ * SEC-04: This function operates only on the attribute dump output (no -d flag
147
+ * was used), so no password data is ever present in `raw`.
148
+ */
149
+ function parseKeychainDump(raw) {
150
+ if (!raw.trim())
151
+ return [];
152
+ // Split into per-entry blocks on lines that start with "keychain:"
153
+ const blocks = raw.split(/(?=^keychain:)/m).filter((b) => b.trim().length > 0);
154
+ const results = [];
155
+ for (const block of blocks) {
156
+ const svce = extractAttribute(block, 'svce');
157
+ const desc = extractAttribute(block, 'desc');
158
+ const icmt = extractAttribute(block, 'icmt');
159
+ const cdat = extractTimestamp(block, 'cdat');
160
+ const mdat = extractTimestamp(block, 'mdat');
161
+ // Filter: only entries written by ai-cred (desc == APP_MARKER, svce starts with 'ai-cred/')
162
+ if (!svce || desc !== APP_MARKER)
163
+ continue;
164
+ if (!svce.startsWith('ai-cred/'))
165
+ continue;
166
+ // Parse service key: ai-cred/{env}/{service}
167
+ const parts = svce.split('/');
168
+ if (parts.length < 3)
169
+ continue;
170
+ const environment = parts[1];
171
+ const service = parts.slice(2).join('/');
172
+ const type = icmt ?? 'unknown';
173
+ results.push({
174
+ service,
175
+ environment,
176
+ type,
177
+ createdAt: cdat ?? '',
178
+ modifiedAt: mdat ?? '',
179
+ });
180
+ }
181
+ return results;
182
+ }
183
+ /**
184
+ * Extract a blob attribute value from a keychain dump block.
185
+ * Matches lines like: "svce"<blob>="my-service-name"
186
+ */
187
+ function extractAttribute(block, attr) {
188
+ const re = new RegExp(`"${attr}"<blob>="(.*?)"`);
189
+ const m = re.exec(block);
190
+ return m?.[1];
191
+ }
192
+ /**
193
+ * Extract and normalise a timedate attribute from a keychain dump block.
194
+ * Matches lines like:
195
+ * "cdat"<timedate>=0x303230... "20230101120000Z\000"
196
+ *
197
+ * The human-readable value appears after the hex dump, inside quotes,
198
+ * terminated by a \000 escape. We parse the YYYYMMDDHHmmssZ format into
199
+ * an ISO 8601 string.
200
+ */
201
+ function extractTimestamp(block, attr) {
202
+ const re = new RegExp(`"${attr}"<timedate>=0x[0-9A-Fa-f]+\\s+"(.*?)\\\\000"`);
203
+ const m = re.exec(block);
204
+ if (!m?.[1])
205
+ return undefined;
206
+ return parseMacTimestamp(m[1]);
207
+ }
208
+ /**
209
+ * Convert a macOS keychain timestamp (YYYYMMDDHHmmssZ) to ISO 8601.
210
+ * Returns the original string if it does not match the expected format.
211
+ */
212
+ function parseMacTimestamp(ts) {
213
+ // Format: 20230615143022Z
214
+ const re = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})Z$/;
215
+ const m = re.exec(ts.trim());
216
+ if (!m)
217
+ return ts;
218
+ const [, year, month, day, hour, min, sec] = m;
219
+ return `${year}-${month}-${day}T${hour}:${min}:${sec}Z`;
220
+ }
221
+ //# sourceMappingURL=keychain-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keychain-adapter.js","sourceRoot":"","sources":["../../src/core/keychain-adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,331 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2
+ import { existsSync } from 'node:fs';
3
+ import * as os from 'node:os';
4
+ import * as path from 'node:path';
5
+ import { KeychainAdapter } from './keychain-adapter.js';
6
+ import { KeychainError, toKeychainError } from './errors.js';
7
+ // ---------------------------------------------------------------------------
8
+ // Skip integration tests if the macOS security binary is not available
9
+ // (e.g., running in a Linux CI environment)
10
+ // ---------------------------------------------------------------------------
11
+ const SECURITY_BINARY = '/usr/bin/security';
12
+ const hasSecurityBinary = existsSync(SECURITY_BINARY);
13
+ // ---------------------------------------------------------------------------
14
+ // KeychainError unit tests
15
+ // ---------------------------------------------------------------------------
16
+ describe('KeychainError', () => {
17
+ it('maps exit code 44 to "Credential not found" with service name', () => {
18
+ const err = new KeychainError('get', 44, 'test-svc');
19
+ expect(err.message).toContain('Credential not found');
20
+ expect(err.message).toContain('test-svc');
21
+ });
22
+ it('maps exit code 44 without service name', () => {
23
+ const err = new KeychainError('get', 44);
24
+ expect(err.message).toBe('Credential not found');
25
+ });
26
+ it('maps exit code 51 to "Keychain is locked"', () => {
27
+ const err = new KeychainError('store', 51);
28
+ expect(err.message).toContain('Keychain is locked');
29
+ });
30
+ it('maps exit code 36 to "Keychain access was denied"', () => {
31
+ const err = new KeychainError('get', 36);
32
+ expect(err.message).toContain('Keychain access was denied');
33
+ });
34
+ it('maps exit code 45 to "Credential already exists" with service', () => {
35
+ const err = new KeychainError('store', 45, 'ai-cred/prod/jenkins');
36
+ expect(err.message).toContain('Credential already exists');
37
+ expect(err.message).toContain('ai-cred/prod/jenkins');
38
+ });
39
+ it('maps unknown exit code to generic message with code', () => {
40
+ const err = new KeychainError('unknown-op', 99);
41
+ expect(err.message).toContain('99');
42
+ });
43
+ it('never includes credential data in error message (SEC-05)', () => {
44
+ const err = new KeychainError('store', 44, 'my-service', 'password=secret&token=abc');
45
+ expect(err.message).not.toContain('password');
46
+ expect(err.message).not.toContain('secret');
47
+ expect(err.message).not.toContain('token');
48
+ expect(err.message).not.toContain('abc');
49
+ expect(err.toString()).not.toContain('password');
50
+ expect(err.toString()).not.toContain('secret');
51
+ // stack should not contain raw credential data either
52
+ if (err.stack) {
53
+ expect(err.stack).not.toContain('password=secret');
54
+ }
55
+ });
56
+ it('has correct name property', () => {
57
+ const err = new KeychainError('get', 44);
58
+ expect(err.name).toBe('KeychainError');
59
+ });
60
+ it('has correct operation and exitCode properties', () => {
61
+ const err = new KeychainError('find-generic-password', 44, 'ai-cred/dev/test');
62
+ expect(err.operation).toBe('find-generic-password');
63
+ expect(err.exitCode).toBe(44);
64
+ expect(err.service).toBe('ai-cred/dev/test');
65
+ });
66
+ it('is an instanceof Error and instanceof KeychainError', () => {
67
+ const err = new KeychainError('get', 44);
68
+ expect(err instanceof Error).toBe(true);
69
+ expect(err instanceof KeychainError).toBe(true);
70
+ });
71
+ });
72
+ // ---------------------------------------------------------------------------
73
+ // toKeychainError unit tests
74
+ // ---------------------------------------------------------------------------
75
+ describe('toKeychainError', () => {
76
+ it('extracts numeric code from subprocess error', () => {
77
+ const mockError = { code: 44 };
78
+ const err = toKeychainError(mockError, 'find-generic-password', 'test-svc');
79
+ expect(err).toBeInstanceOf(KeychainError);
80
+ expect(err.exitCode).toBe(44);
81
+ expect(err.operation).toBe('find-generic-password');
82
+ expect(err.service).toBe('test-svc');
83
+ });
84
+ it('extracts exitCode property when code is not numeric', () => {
85
+ const mockError = { code: 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER', exitCode: 51 };
86
+ const err = toKeychainError(mockError, 'store');
87
+ expect(err.exitCode).toBe(51);
88
+ });
89
+ it('uses -1 for unrecognized error objects', () => {
90
+ const err = toKeychainError('some string error', 'op');
91
+ expect(err.exitCode).toBe(-1);
92
+ });
93
+ it('uses -1 when error is null', () => {
94
+ const err = toKeychainError(null, 'op');
95
+ expect(err.exitCode).toBe(-1);
96
+ });
97
+ it('does NOT forward stderr or subprocess output into error message (SEC-05)', () => {
98
+ const mockError = {
99
+ code: 44,
100
+ stderr: 'password=supersecret apiToken=mytoken',
101
+ stdout: 'secret data here',
102
+ message: 'Command failed: password=abc123',
103
+ };
104
+ const err = toKeychainError(mockError, 'find-generic-password', 'svc');
105
+ expect(err.message).not.toContain('password');
106
+ expect(err.message).not.toContain('supersecret');
107
+ expect(err.message).not.toContain('mytoken');
108
+ expect(err.message).not.toContain('abc123');
109
+ expect(err.message).not.toContain('secret');
110
+ });
111
+ });
112
+ // ---------------------------------------------------------------------------
113
+ // Integration tests — require real macOS keychain
114
+ // ---------------------------------------------------------------------------
115
+ describe.skipIf(!hasSecurityBinary)('KeychainAdapter integration', () => {
116
+ const tempKeychainPath = path.join(os.tmpdir(), `ai-cred-test-${Date.now()}.keychain-db`);
117
+ let adapter;
118
+ beforeAll(async () => {
119
+ adapter = new KeychainAdapter(tempKeychainPath);
120
+ await adapter.initialize();
121
+ });
122
+ afterAll(async () => {
123
+ try {
124
+ await adapter.destroy();
125
+ }
126
+ catch {
127
+ // ignore — file may already be cleaned up
128
+ }
129
+ // Verify no leftover keychain file
130
+ expect(existsSync(tempKeychainPath)).toBe(false);
131
+ });
132
+ it('initialize creates keychain file', () => {
133
+ expect(existsSync(tempKeychainPath)).toBe(true);
134
+ });
135
+ it('store and get round-trip (SSH credential) (CRED-01)', async () => {
136
+ await adapter.store('dev', 'my-server', {
137
+ type: 'ssh',
138
+ host: '192.168.1.1',
139
+ port: 22,
140
+ username: 'root',
141
+ password: 'secret123',
142
+ });
143
+ const result = await adapter.get('dev', 'my-server');
144
+ expect(result.type).toBe('ssh');
145
+ if (result.type === 'ssh') {
146
+ expect(result.host).toBe('192.168.1.1');
147
+ expect(result.username).toBe('root');
148
+ expect(result.password).toBe('secret123');
149
+ }
150
+ });
151
+ it('store and get round-trip (Jenkins credential) (CRED-02)', async () => {
152
+ await adapter.store('dev', 'jenkins', {
153
+ type: 'jenkins',
154
+ url: 'https://jenkins.local',
155
+ username: 'admin',
156
+ apiToken: 'jenkins-token-abc',
157
+ });
158
+ const result = await adapter.get('dev', 'jenkins');
159
+ expect(result.type).toBe('jenkins');
160
+ if (result.type === 'jenkins') {
161
+ expect(result.url).toBe('https://jenkins.local');
162
+ expect(result.username).toBe('admin');
163
+ expect(result.apiToken).toBe('jenkins-token-abc');
164
+ }
165
+ });
166
+ it('store and get round-trip (Portainer credential) (CRED-03)', async () => {
167
+ await adapter.store('dev', 'portainer', {
168
+ type: 'portainer',
169
+ url: 'https://portainer.local',
170
+ apiToken: 'portainer-token-xyz',
171
+ });
172
+ const result = await adapter.get('dev', 'portainer');
173
+ expect(result.type).toBe('portainer');
174
+ if (result.type === 'portainer') {
175
+ expect(result.url).toBe('https://portainer.local');
176
+ expect(result.apiToken).toBe('portainer-token-xyz');
177
+ }
178
+ });
179
+ it('store and get round-trip (AWS credential) (CRED-04)', async () => {
180
+ await adapter.store('dev', 'aws-main', {
181
+ type: 'aws',
182
+ accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
183
+ secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
184
+ region: 'us-east-1',
185
+ profile: 'dev-profile',
186
+ });
187
+ const result = await adapter.get('dev', 'aws-main');
188
+ expect(result.type).toBe('aws');
189
+ if (result.type === 'aws') {
190
+ expect(result.accessKeyId).toBe('AKIAIOSFODNN7EXAMPLE');
191
+ expect(result.secretAccessKey).toBe('wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY');
192
+ expect(result.region).toBe('us-east-1');
193
+ expect(result.profile).toBe('dev-profile');
194
+ }
195
+ });
196
+ it('store and get round-trip (Generic API key) (CRED-05)', async () => {
197
+ await adapter.store('dev', 'openai', {
198
+ type: 'api-key',
199
+ key: 'sk-testkey12345',
200
+ url: 'https://api.openai.com',
201
+ notes: 'OpenAI dev key',
202
+ });
203
+ const result = await adapter.get('dev', 'openai');
204
+ expect(result.type).toBe('api-key');
205
+ if (result.type === 'api-key') {
206
+ expect(result.key).toBe('sk-testkey12345');
207
+ expect(result.url).toBe('https://api.openai.com');
208
+ expect(result.notes).toBe('OpenAI dev key');
209
+ }
210
+ });
211
+ it('list returns metadata only, never secrets (SEC-04)', async () => {
212
+ const results = await adapter.list();
213
+ expect(Array.isArray(results)).toBe(true);
214
+ expect(results.length).toBeGreaterThan(0);
215
+ // Each entry must have the required metadata fields
216
+ for (const entry of results) {
217
+ expect(entry).toHaveProperty('service');
218
+ expect(entry).toHaveProperty('environment');
219
+ expect(entry).toHaveProperty('type');
220
+ expect(entry).toHaveProperty('createdAt');
221
+ expect(entry).toHaveProperty('modifiedAt');
222
+ // Must NOT have any secret fields
223
+ const entryRecord = entry;
224
+ expect(entryRecord['password']).toBeUndefined();
225
+ expect(entryRecord['apiToken']).toBeUndefined();
226
+ expect(entryRecord['secretAccessKey']).toBeUndefined();
227
+ expect(entryRecord['key']).toBeUndefined();
228
+ expect(entryRecord['accessKeyId']).toBeUndefined();
229
+ }
230
+ // Stringify entire list and confirm no secret values appear
231
+ const serialized = JSON.stringify(results);
232
+ expect(serialized).not.toContain('secret123');
233
+ expect(serialized).not.toContain('jenkins-token-abc');
234
+ expect(serialized).not.toContain('portainer-token-xyz');
235
+ expect(serialized).not.toContain('wJalrXUtnFEMI');
236
+ expect(serialized).not.toContain('sk-testkey12345');
237
+ });
238
+ it('delete removes credential', async () => {
239
+ // Store a temporary credential
240
+ await adapter.store('staging', 'temp-delete-test', {
241
+ type: 'api-key',
242
+ key: 'temp-key-to-delete',
243
+ });
244
+ // Verify it exists
245
+ const before = await adapter.get('staging', 'temp-delete-test');
246
+ expect(before.type).toBe('api-key');
247
+ // Delete it
248
+ await adapter.delete('staging', 'temp-delete-test');
249
+ // Should now throw with exit code 44
250
+ let threw = false;
251
+ try {
252
+ await adapter.get('staging', 'temp-delete-test');
253
+ }
254
+ catch (err) {
255
+ threw = true;
256
+ expect(err).toBeInstanceOf(KeychainError);
257
+ if (err instanceof KeychainError) {
258
+ expect(err.exitCode).toBe(44);
259
+ }
260
+ }
261
+ expect(threw).toBe(true);
262
+ });
263
+ it('get non-existent credential throws KeychainError with exit code 44', async () => {
264
+ let threw = false;
265
+ try {
266
+ await adapter.get('prod', 'does-not-exist-xyz');
267
+ }
268
+ catch (err) {
269
+ threw = true;
270
+ expect(err).toBeInstanceOf(KeychainError);
271
+ if (err instanceof KeychainError) {
272
+ expect(err.exitCode).toBe(44);
273
+ // Error message must NOT contain any credential data (SEC-05)
274
+ expect(err.message).not.toContain('password');
275
+ expect(err.message).not.toContain('secret');
276
+ expect(err.message).not.toContain('token');
277
+ }
278
+ }
279
+ expect(threw).toBe(true);
280
+ });
281
+ it('store with update (-U flag) overwrites existing credential', async () => {
282
+ // Store initial credential
283
+ await adapter.store('staging', 'update-test', {
284
+ type: 'api-key',
285
+ key: 'original-key',
286
+ });
287
+ // Overwrite with new credential
288
+ await adapter.store('staging', 'update-test', {
289
+ type: 'api-key',
290
+ key: 'updated-key',
291
+ });
292
+ // Retrieve — should be the updated credential
293
+ const result = await adapter.get('staging', 'update-test');
294
+ expect(result.type).toBe('api-key');
295
+ if (result.type === 'api-key') {
296
+ expect(result.key).toBe('updated-key');
297
+ }
298
+ });
299
+ it('environment isolation: prod/jenkins and dev/jenkins are independent', async () => {
300
+ await adapter.store('prod', 'jenkins', {
301
+ type: 'jenkins',
302
+ url: 'https://prod-jenkins.internal',
303
+ username: 'prod-admin',
304
+ apiToken: 'prod-token-111',
305
+ });
306
+ await adapter.store('dev', 'jenkins-isolated', {
307
+ type: 'jenkins',
308
+ url: 'https://dev-jenkins.internal',
309
+ username: 'dev-user',
310
+ apiToken: 'dev-token-222',
311
+ });
312
+ const prodResult = await adapter.get('prod', 'jenkins');
313
+ const devResult = await adapter.get('dev', 'jenkins-isolated');
314
+ expect(prodResult.type).toBe('jenkins');
315
+ if (prodResult.type === 'jenkins') {
316
+ expect(prodResult.url).toBe('https://prod-jenkins.internal');
317
+ expect(prodResult.apiToken).toBe('prod-token-111');
318
+ }
319
+ expect(devResult.type).toBe('jenkins');
320
+ if (devResult.type === 'jenkins') {
321
+ expect(devResult.url).toBe('https://dev-jenkins.internal');
322
+ expect(devResult.apiToken).toBe('dev-token-222');
323
+ }
324
+ // Values should not cross-contaminate
325
+ if (prodResult.type === 'jenkins' && devResult.type === 'jenkins') {
326
+ expect(prodResult.apiToken).not.toBe(devResult.apiToken);
327
+ expect(prodResult.url).not.toBe(devResult.url);
328
+ }
329
+ });
330
+ });
331
+ //# sourceMappingURL=keychain-adapter.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keychain-adapter.test.js","sourceRoot":"","sources":["../../src/core/keychain-adapter.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};