env-secrets 0.5.0 → 0.5.2
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 +7 -1
- package/__e2e__/README.md +1 -1
- package/__e2e__/aws-cli-help.test.ts +23 -0
- package/__e2e__/aws-exec-args.test.ts +44 -0
- package/__e2e__/aws-get-secrets-args.test.ts +156 -0
- package/__e2e__/aws-output-file-args.test.ts +65 -0
- package/__e2e__/aws-secret-lifecycle.test.ts +316 -0
- package/__e2e__/aws-secret-mutation-args.test.ts +199 -0
- package/__e2e__/utils/aws-e2e-context.ts +50 -0
- package/__e2e__/utils/test-utils.ts +35 -39
- package/__tests__/index.test.ts +5 -3
- package/__tests__/vaults/secretsmanager.test.ts +47 -0
- package/dist/index.js +39 -3
- package/dist/vaults/secretsmanager.js +47 -8
- package/docs/AWS.md +7 -0
- package/package.json +5 -4
- package/src/index.ts +50 -3
- package/src/vaults/secretsmanager.ts +65 -7
- package/__e2e__/index.test.ts +0 -678
package/__e2e__/index.test.ts
DELETED
|
@@ -1,678 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
LocalStackHelper,
|
|
3
|
-
cli,
|
|
4
|
-
cliWithEnv,
|
|
5
|
-
cliWithEnvAndStdin,
|
|
6
|
-
createTempFile,
|
|
7
|
-
cleanupTempFile,
|
|
8
|
-
createTestProfile,
|
|
9
|
-
restoreTestProfile,
|
|
10
|
-
TestSecret,
|
|
11
|
-
CreatedSecret,
|
|
12
|
-
execAwslocalCommand
|
|
13
|
-
} from './utils/test-utils';
|
|
14
|
-
import { debugLog } from './utils/debug-logger';
|
|
15
|
-
import * as fs from 'fs';
|
|
16
|
-
import * as path from 'path';
|
|
17
|
-
import * as os from 'os';
|
|
18
|
-
|
|
19
|
-
describe('End-to-End Tests', () => {
|
|
20
|
-
let localStack: LocalStackHelper;
|
|
21
|
-
let awsDir: string | undefined;
|
|
22
|
-
|
|
23
|
-
beforeAll(async () => {
|
|
24
|
-
localStack = new LocalStackHelper();
|
|
25
|
-
await localStack.waitForLocalStack();
|
|
26
|
-
|
|
27
|
-
// Create test AWS profile
|
|
28
|
-
awsDir = createTestProfile();
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
afterAll(async () => {
|
|
32
|
-
// Clean up all secrets created during this test run
|
|
33
|
-
await localStack.cleanupRunSecrets();
|
|
34
|
-
|
|
35
|
-
// Restore AWS profile
|
|
36
|
-
restoreTestProfile(awsDir);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
// Helper function to create a secret for a test
|
|
40
|
-
const createTestSecret = async (
|
|
41
|
-
secret: TestSecret,
|
|
42
|
-
region?: string
|
|
43
|
-
): Promise<CreatedSecret> => {
|
|
44
|
-
return await localStack.createSecret(secret, region);
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
// Helper function to create LocalStack environment variables
|
|
48
|
-
const getLocalStackEnv = (overrides: Record<string, string> = {}) => ({
|
|
49
|
-
AWS_ENDPOINT_URL: process.env.LOCALSTACK_URL || 'http://localhost:4566',
|
|
50
|
-
AWS_ACCESS_KEY_ID: 'test',
|
|
51
|
-
AWS_SECRET_ACCESS_KEY: 'test',
|
|
52
|
-
AWS_DEFAULT_REGION: 'us-east-1',
|
|
53
|
-
...overrides
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
describe('CLI Help Commands', () => {
|
|
57
|
-
test('should show general help', async () => {
|
|
58
|
-
const result = await cli(['-h']);
|
|
59
|
-
expect(result.code).toBe(0);
|
|
60
|
-
expect(result.stdout).toContain('env-secrets');
|
|
61
|
-
expect(result.stdout).toContain('pull secrets from vaults');
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test('should show AWS command help', async () => {
|
|
65
|
-
const result = await cli(['aws', '-h']);
|
|
66
|
-
expect(result.code).toBe(0);
|
|
67
|
-
expect(result.stdout).toContain('get secrets from AWS secrets manager');
|
|
68
|
-
expect(result.stdout).toContain('--secret');
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
test('should show version', async () => {
|
|
72
|
-
const result = await cli(['--version']);
|
|
73
|
-
expect(result.code).toBe(0);
|
|
74
|
-
expect(result.stdout).toMatch(/^\d+\.\d+\.\d+/);
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
describe('AWS Secrets Manager Integration', () => {
|
|
79
|
-
describe('Using Default AWS Credentials', () => {
|
|
80
|
-
test('should retrieve basic JSON secret', async () => {
|
|
81
|
-
const secret = await createTestSecret({
|
|
82
|
-
name: 'test-secret-basic',
|
|
83
|
-
value:
|
|
84
|
-
'{"API_KEY": "secret123", "DATABASE_URL": "postgres://localhost:5432/test"}',
|
|
85
|
-
description: 'Basic test secret with API key and database URL'
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
const result = await cliWithEnv(
|
|
89
|
-
['aws', '-s', secret.prefixedName],
|
|
90
|
-
getLocalStackEnv()
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
expect(result.code).toBe(0);
|
|
94
|
-
expect(result.stderr).toBe('');
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
test('should retrieve simple string secret', async () => {
|
|
98
|
-
const secret = await createTestSecret({
|
|
99
|
-
name: 'test-secret-simple',
|
|
100
|
-
value: 'simple-string-value',
|
|
101
|
-
description: 'Simple string secret'
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
const result = await cliWithEnv(
|
|
105
|
-
['aws', '-s', secret.prefixedName],
|
|
106
|
-
getLocalStackEnv()
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
expect(result.code).toBe(0);
|
|
110
|
-
expect(result.stderr).toBe('');
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
test('should retrieve complex JSON secret', async () => {
|
|
114
|
-
const secret = await createTestSecret({
|
|
115
|
-
name: 'test-secret-complex',
|
|
116
|
-
value:
|
|
117
|
-
'{"NESTED": {"KEY": "value"}, "ARRAY": [1, 2, 3], "BOOLEAN": true, "NUMBER": 42}',
|
|
118
|
-
description: 'Complex JSON secret'
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
const result = await cliWithEnv(
|
|
122
|
-
['aws', '-s', secret.prefixedName],
|
|
123
|
-
getLocalStackEnv()
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
expect(result.code).toBe(0);
|
|
127
|
-
expect(result.stderr).toBe('');
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
test('should retrieve secret with special characters', async () => {
|
|
131
|
-
const secret = await createTestSecret({
|
|
132
|
-
name: 'test-secret-special-chars',
|
|
133
|
-
value:
|
|
134
|
-
'{"PASSWORD": "p@ssw0rd!#$%", "URL": "https://api.example.com/v1?key=value&other=test"}',
|
|
135
|
-
description: 'Secret with special characters'
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
const result = await cliWithEnv(
|
|
139
|
-
['aws', '-s', secret.prefixedName],
|
|
140
|
-
getLocalStackEnv()
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
expect(result.code).toBe(0);
|
|
144
|
-
expect(result.stderr).toBe('');
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
test('should handle non-existent secret', async () => {
|
|
148
|
-
const result = await cliWithEnv(
|
|
149
|
-
['aws', '-s', 'non-existent-secret'],
|
|
150
|
-
getLocalStackEnv()
|
|
151
|
-
);
|
|
152
|
-
|
|
153
|
-
expect(result.code).toBe(0);
|
|
154
|
-
expect(result.stderr).toContain('non-existent-secret not found');
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
test('should work with custom region', async () => {
|
|
158
|
-
const secret = await createTestSecret(
|
|
159
|
-
{
|
|
160
|
-
name: `test-secret-region-${Date.now()}`,
|
|
161
|
-
value:
|
|
162
|
-
'{"API_KEY": "secret123", "DATABASE_URL": "postgres://localhost:5432/test"}',
|
|
163
|
-
description: 'Basic test secret for us-west-2'
|
|
164
|
-
},
|
|
165
|
-
'us-west-2'
|
|
166
|
-
);
|
|
167
|
-
|
|
168
|
-
const result = await cliWithEnv(
|
|
169
|
-
['aws', '-s', secret.prefixedName, '-r', 'us-west-2'],
|
|
170
|
-
getLocalStackEnv()
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
expect(result.code).toBe(0);
|
|
174
|
-
expect(result.stderr).toBe('');
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
describe('Using AWS Profile', () => {
|
|
179
|
-
test('should retrieve secret using default profile', async () => {
|
|
180
|
-
const secret = await createTestSecret({
|
|
181
|
-
name: 'test-secret-profile',
|
|
182
|
-
value:
|
|
183
|
-
'{"API_KEY": "secret123", "DATABASE_URL": "postgres://localhost:5432/test"}',
|
|
184
|
-
description: 'Secret for profile test'
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
const result = await cliWithEnv(
|
|
188
|
-
['aws', '-s', secret.prefixedName],
|
|
189
|
-
getLocalStackEnv()
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
expect(result.code).toBe(0);
|
|
193
|
-
expect(result.stderr).toBe('');
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
test('should retrieve secret using custom profile', async () => {
|
|
197
|
-
const secret = await createTestSecret({
|
|
198
|
-
name: 'test-secret-profile-custom',
|
|
199
|
-
value:
|
|
200
|
-
'{"API_KEY": "secret123", "DATABASE_URL": "postgres://localhost:5432/test"}',
|
|
201
|
-
description: 'Secret for custom profile test'
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
const result = await cliWithEnv(
|
|
205
|
-
['aws', '-s', secret.prefixedName, '-p', 'env-secrets-test'],
|
|
206
|
-
getLocalStackEnv()
|
|
207
|
-
);
|
|
208
|
-
|
|
209
|
-
expect(result.code).toBe(0);
|
|
210
|
-
expect(result.stderr).toBe('');
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
describe('Output to File', () => {
|
|
215
|
-
test('should write secrets to file', async () => {
|
|
216
|
-
debugLog('Starting test...');
|
|
217
|
-
|
|
218
|
-
const secret = await createTestSecret({
|
|
219
|
-
name: `test-secret-file-${Date.now()}`,
|
|
220
|
-
value:
|
|
221
|
-
'{"API_KEY": "secret123", "DATABASE_URL": "postgres://localhost:5432/test"}',
|
|
222
|
-
description: 'Secret for file output test'
|
|
223
|
-
});
|
|
224
|
-
debugLog('Secret created successfully');
|
|
225
|
-
|
|
226
|
-
const tempFile = path.join(
|
|
227
|
-
os.tmpdir(),
|
|
228
|
-
`env-secrets-test-${Date.now()}.env`
|
|
229
|
-
);
|
|
230
|
-
|
|
231
|
-
const result = await cliWithEnv(
|
|
232
|
-
['aws', '-s', secret.prefixedName, '-o', tempFile],
|
|
233
|
-
getLocalStackEnv()
|
|
234
|
-
);
|
|
235
|
-
|
|
236
|
-
debugLog('CLI result:', {
|
|
237
|
-
code: result.code,
|
|
238
|
-
stdout: result.stdout,
|
|
239
|
-
stderr: result.stderr
|
|
240
|
-
});
|
|
241
|
-
expect(result.code).toBe(0);
|
|
242
|
-
expect(result.stdout).toContain(`Secrets written to ${tempFile}`);
|
|
243
|
-
|
|
244
|
-
// Verify file contents
|
|
245
|
-
const fileContent = fs.readFileSync(tempFile, 'utf8');
|
|
246
|
-
expect(fileContent).toContain('API_KEY=secret123');
|
|
247
|
-
expect(fileContent).toContain(
|
|
248
|
-
'DATABASE_URL=postgres://localhost:5432/test'
|
|
249
|
-
);
|
|
250
|
-
|
|
251
|
-
cleanupTempFile(tempFile);
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
test('should not overwrite existing file', async () => {
|
|
255
|
-
const tempFile = createTempFile('existing content');
|
|
256
|
-
|
|
257
|
-
try {
|
|
258
|
-
const result = await cliWithEnv(
|
|
259
|
-
['aws', '-s', 'test-secret-basic', '-o', tempFile],
|
|
260
|
-
getLocalStackEnv()
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
expect(result.code).toBe(1);
|
|
264
|
-
expect(result.stderr).toContain(
|
|
265
|
-
'already exists and will not be overwritten'
|
|
266
|
-
);
|
|
267
|
-
|
|
268
|
-
// Verify file was not modified
|
|
269
|
-
const fileContent = fs.readFileSync(tempFile, 'utf8');
|
|
270
|
-
expect(fileContent).toBe('existing content');
|
|
271
|
-
} finally {
|
|
272
|
-
cleanupTempFile(tempFile);
|
|
273
|
-
}
|
|
274
|
-
});
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
describe('Program Execution', () => {
|
|
278
|
-
test('should execute program with injected environment variables', async () => {
|
|
279
|
-
const secret = await createTestSecret({
|
|
280
|
-
name: `test-secret-exec-${Date.now()}`,
|
|
281
|
-
value:
|
|
282
|
-
'{"API_KEY": "secret123", "DATABASE_URL": "postgres://localhost:5432/test"}',
|
|
283
|
-
description: 'Secret for program execution test'
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
const result = await cliWithEnv(
|
|
287
|
-
['aws', '-s', secret.prefixedName, 'echo', '$API_KEY'],
|
|
288
|
-
getLocalStackEnv()
|
|
289
|
-
);
|
|
290
|
-
|
|
291
|
-
expect(result.code).toBe(0);
|
|
292
|
-
|
|
293
|
-
// In test mode, the CLI outputs the environment variables as JSON
|
|
294
|
-
const envVars = JSON.parse(result.stdout.trim());
|
|
295
|
-
expect(envVars.API_KEY).toBe('secret123');
|
|
296
|
-
expect(envVars.DATABASE_URL).toBe('postgres://localhost:5432/test');
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
test('should handle program execution errors gracefully', async () => {
|
|
300
|
-
const secret = await createTestSecret({
|
|
301
|
-
name: `test-secret-error-${Date.now()}`,
|
|
302
|
-
value: '{"API_KEY": "secret123"}',
|
|
303
|
-
description: 'Secret for error handling test'
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
const result = await cliWithEnv(
|
|
307
|
-
['aws', '-s', secret.prefixedName, 'nonexistent-command'],
|
|
308
|
-
getLocalStackEnv()
|
|
309
|
-
);
|
|
310
|
-
|
|
311
|
-
expect(result.code).toBe(0);
|
|
312
|
-
|
|
313
|
-
// In test mode, the CLI outputs the environment variables as JSON
|
|
314
|
-
const envVars = JSON.parse(result.stdout.trim());
|
|
315
|
-
expect(envVars.API_KEY).toBe('secret123');
|
|
316
|
-
});
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
describe('Error Handling', () => {
|
|
320
|
-
test('should handle invalid AWS credentials', async () => {
|
|
321
|
-
const result = await cliWithEnv(
|
|
322
|
-
['aws', '-s', 'test-secret-basic'],
|
|
323
|
-
getLocalStackEnv({
|
|
324
|
-
AWS_ACCESS_KEY_ID: 'invalid',
|
|
325
|
-
AWS_SECRET_ACCESS_KEY: 'invalid'
|
|
326
|
-
})
|
|
327
|
-
);
|
|
328
|
-
|
|
329
|
-
// LocalStack accepts any credentials, so this should succeed
|
|
330
|
-
expect(result.code).toBe(0);
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
test('should handle missing secret parameter', async () => {
|
|
334
|
-
const result = await cliWithEnv(['aws'], getLocalStackEnv());
|
|
335
|
-
|
|
336
|
-
expect(result.code).toBe(1);
|
|
337
|
-
expect(result.stderr).toContain('Missing required option --secret');
|
|
338
|
-
});
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
describe('AWS Secret Management Commands', () => {
|
|
342
|
-
test('should create, list, get, and delete a secret', async () => {
|
|
343
|
-
const secretName = `e2e-managed-secret-${Date.now()}`;
|
|
344
|
-
|
|
345
|
-
const createResult = await cliWithEnv(
|
|
346
|
-
['aws', 'secret', 'create', '-n', secretName, '-v', 'initial-value'],
|
|
347
|
-
getLocalStackEnv()
|
|
348
|
-
);
|
|
349
|
-
|
|
350
|
-
expect(createResult.code).toBe(0);
|
|
351
|
-
expect(createResult.stdout).toContain(secretName);
|
|
352
|
-
|
|
353
|
-
const listResult = await cliWithEnv(
|
|
354
|
-
['aws', 'secret', 'list', '--prefix', 'e2e-managed-secret-'],
|
|
355
|
-
getLocalStackEnv()
|
|
356
|
-
);
|
|
357
|
-
expect(listResult.code).toBe(0);
|
|
358
|
-
expect(listResult.stdout).toContain(secretName);
|
|
359
|
-
|
|
360
|
-
const getResult = await cliWithEnv(
|
|
361
|
-
['aws', 'secret', 'get', '-n', secretName],
|
|
362
|
-
getLocalStackEnv()
|
|
363
|
-
);
|
|
364
|
-
expect(getResult.code).toBe(0);
|
|
365
|
-
expect(getResult.stdout).toContain(secretName);
|
|
366
|
-
expect(getResult.stdout).not.toContain('initial-value');
|
|
367
|
-
|
|
368
|
-
const deleteResult = await cliWithEnv(
|
|
369
|
-
[
|
|
370
|
-
'aws',
|
|
371
|
-
'secret',
|
|
372
|
-
'delete',
|
|
373
|
-
'-n',
|
|
374
|
-
secretName,
|
|
375
|
-
'--force-delete-without-recovery',
|
|
376
|
-
'--yes'
|
|
377
|
-
],
|
|
378
|
-
getLocalStackEnv()
|
|
379
|
-
);
|
|
380
|
-
expect(deleteResult.code).toBe(0);
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
test('should update secret value from stdin', async () => {
|
|
384
|
-
const secret = await createTestSecret({
|
|
385
|
-
name: `managed-secret-stdin-${Date.now()}`,
|
|
386
|
-
value: 'initial-value',
|
|
387
|
-
description: 'Secret for stdin update test'
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
const updateResult = await cliWithEnvAndStdin(
|
|
391
|
-
[
|
|
392
|
-
'aws',
|
|
393
|
-
'secret',
|
|
394
|
-
'update',
|
|
395
|
-
'-n',
|
|
396
|
-
secret.prefixedName,
|
|
397
|
-
'--value-stdin'
|
|
398
|
-
],
|
|
399
|
-
getLocalStackEnv(),
|
|
400
|
-
'stdin-updated-value'
|
|
401
|
-
);
|
|
402
|
-
|
|
403
|
-
expect(updateResult.code).toBe(0);
|
|
404
|
-
expect(updateResult.stderr).toBe('');
|
|
405
|
-
|
|
406
|
-
const deleteResult = await cliWithEnv(
|
|
407
|
-
[
|
|
408
|
-
'aws',
|
|
409
|
-
'secret',
|
|
410
|
-
'delete',
|
|
411
|
-
'-n',
|
|
412
|
-
secret.prefixedName,
|
|
413
|
-
'--force-delete-without-recovery',
|
|
414
|
-
'--yes'
|
|
415
|
-
],
|
|
416
|
-
getLocalStackEnv()
|
|
417
|
-
);
|
|
418
|
-
expect(deleteResult.code).toBe(0);
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
test('should append and remove keys on a JSON secret', async () => {
|
|
422
|
-
const secretName = `managed-secret-append-remove-${Date.now()}`;
|
|
423
|
-
const tempFile = path.join(
|
|
424
|
-
os.tmpdir(),
|
|
425
|
-
`env-secrets-append-remove-${Date.now()}.env`
|
|
426
|
-
);
|
|
427
|
-
fs.writeFileSync(tempFile, 'API_KEY=first');
|
|
428
|
-
|
|
429
|
-
const createResult = await cliWithEnv(
|
|
430
|
-
[
|
|
431
|
-
'aws',
|
|
432
|
-
'secret',
|
|
433
|
-
'upsert',
|
|
434
|
-
'--file',
|
|
435
|
-
tempFile,
|
|
436
|
-
'--name',
|
|
437
|
-
secretName,
|
|
438
|
-
'--output',
|
|
439
|
-
'json'
|
|
440
|
-
],
|
|
441
|
-
getLocalStackEnv()
|
|
442
|
-
);
|
|
443
|
-
expect(createResult.code).toBe(0);
|
|
444
|
-
|
|
445
|
-
const appendResult = await cliWithEnv(
|
|
446
|
-
[
|
|
447
|
-
'aws',
|
|
448
|
-
'secret',
|
|
449
|
-
'append',
|
|
450
|
-
'-n',
|
|
451
|
-
secretName,
|
|
452
|
-
'--key',
|
|
453
|
-
'JIRA_EMAIL_TOKEN',
|
|
454
|
-
'-v',
|
|
455
|
-
'blah',
|
|
456
|
-
'--output',
|
|
457
|
-
'json'
|
|
458
|
-
],
|
|
459
|
-
getLocalStackEnv()
|
|
460
|
-
);
|
|
461
|
-
expect(appendResult.code).toBe(0);
|
|
462
|
-
|
|
463
|
-
const afterAppend = await execAwslocalCommand(
|
|
464
|
-
`awslocal secretsmanager get-secret-value --secret-id "${secretName}" --region us-east-1 --query SecretString --output text`,
|
|
465
|
-
getLocalStackEnv()
|
|
466
|
-
);
|
|
467
|
-
expect(JSON.parse(afterAppend.stdout.trim())).toEqual({
|
|
468
|
-
API_KEY: 'first',
|
|
469
|
-
JIRA_EMAIL_TOKEN: 'blah'
|
|
470
|
-
});
|
|
471
|
-
|
|
472
|
-
const removeResult = await cliWithEnv(
|
|
473
|
-
[
|
|
474
|
-
'aws',
|
|
475
|
-
'secret',
|
|
476
|
-
'remove',
|
|
477
|
-
'-n',
|
|
478
|
-
secretName,
|
|
479
|
-
'--key',
|
|
480
|
-
'API_KEY',
|
|
481
|
-
'--output',
|
|
482
|
-
'json'
|
|
483
|
-
],
|
|
484
|
-
getLocalStackEnv()
|
|
485
|
-
);
|
|
486
|
-
expect(removeResult.code).toBe(0);
|
|
487
|
-
|
|
488
|
-
const afterRemove = await execAwslocalCommand(
|
|
489
|
-
`awslocal secretsmanager get-secret-value --secret-id "${secretName}" --region us-east-1 --query SecretString --output text`,
|
|
490
|
-
getLocalStackEnv()
|
|
491
|
-
);
|
|
492
|
-
expect(JSON.parse(afterRemove.stdout.trim())).toEqual({
|
|
493
|
-
JIRA_EMAIL_TOKEN: 'blah'
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
const deleteResult = await cliWithEnv(
|
|
497
|
-
[
|
|
498
|
-
'aws',
|
|
499
|
-
'secret',
|
|
500
|
-
'delete',
|
|
501
|
-
'-n',
|
|
502
|
-
secretName,
|
|
503
|
-
'--force-delete-without-recovery',
|
|
504
|
-
'--yes'
|
|
505
|
-
],
|
|
506
|
-
getLocalStackEnv()
|
|
507
|
-
);
|
|
508
|
-
expect(deleteResult.code).toBe(0);
|
|
509
|
-
cleanupTempFile(tempFile);
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
test('should require confirmation for delete', async () => {
|
|
513
|
-
const secret = await createTestSecret({
|
|
514
|
-
name: `managed-secret-confirm-${Date.now()}`,
|
|
515
|
-
value: 'value',
|
|
516
|
-
description: 'Secret for delete confirmation test'
|
|
517
|
-
});
|
|
518
|
-
|
|
519
|
-
const result = await cliWithEnv(
|
|
520
|
-
['aws', 'secret', 'delete', '-n', secret.prefixedName],
|
|
521
|
-
getLocalStackEnv()
|
|
522
|
-
);
|
|
523
|
-
|
|
524
|
-
expect(result.code).toBe(1);
|
|
525
|
-
expect(result.stderr).toContain('requires --yes confirmation');
|
|
526
|
-
});
|
|
527
|
-
|
|
528
|
-
test('should honor region flag for secret list across multiple regions', async () => {
|
|
529
|
-
const secret = await createTestSecret(
|
|
530
|
-
{
|
|
531
|
-
name: `managed-secret-multi-region-${Date.now()}`,
|
|
532
|
-
value: '{"region":"us-west-2"}',
|
|
533
|
-
description: 'Secret used for region isolation test'
|
|
534
|
-
},
|
|
535
|
-
'us-west-2'
|
|
536
|
-
);
|
|
537
|
-
|
|
538
|
-
const westResult = await cliWithEnv(
|
|
539
|
-
[
|
|
540
|
-
'aws',
|
|
541
|
-
'secret',
|
|
542
|
-
'list',
|
|
543
|
-
'--prefix',
|
|
544
|
-
secret.prefixedName,
|
|
545
|
-
'-r',
|
|
546
|
-
'us-west-2',
|
|
547
|
-
'--output',
|
|
548
|
-
'json'
|
|
549
|
-
],
|
|
550
|
-
getLocalStackEnv()
|
|
551
|
-
);
|
|
552
|
-
expect(westResult.code).toBe(0);
|
|
553
|
-
const westRows = JSON.parse(westResult.stdout) as Array<{
|
|
554
|
-
name: string;
|
|
555
|
-
}>;
|
|
556
|
-
expect(westRows.some((row) => row.name === secret.prefixedName)).toBe(
|
|
557
|
-
true
|
|
558
|
-
);
|
|
559
|
-
|
|
560
|
-
const eastResult = await cliWithEnv(
|
|
561
|
-
[
|
|
562
|
-
'aws',
|
|
563
|
-
'secret',
|
|
564
|
-
'list',
|
|
565
|
-
'--prefix',
|
|
566
|
-
secret.prefixedName,
|
|
567
|
-
'-r',
|
|
568
|
-
'us-east-1',
|
|
569
|
-
'--output',
|
|
570
|
-
'json'
|
|
571
|
-
],
|
|
572
|
-
getLocalStackEnv()
|
|
573
|
-
);
|
|
574
|
-
expect(eastResult.code).toBe(0);
|
|
575
|
-
const eastRows = JSON.parse(eastResult.stdout) as Array<{
|
|
576
|
-
name: string;
|
|
577
|
-
}>;
|
|
578
|
-
expect(eastRows).toEqual([]);
|
|
579
|
-
});
|
|
580
|
-
|
|
581
|
-
test('should upsert secrets from env file', async () => {
|
|
582
|
-
const secretName = `e2e-upsert-${Date.now()}`;
|
|
583
|
-
const tempFile = path.join(
|
|
584
|
-
os.tmpdir(),
|
|
585
|
-
`env-secrets-upsert-${Date.now()}.env`
|
|
586
|
-
);
|
|
587
|
-
|
|
588
|
-
fs.writeFileSync(
|
|
589
|
-
tempFile,
|
|
590
|
-
['# sample', 'export API_KEY=first', 'DB_URL = postgres://one'].join(
|
|
591
|
-
'\n'
|
|
592
|
-
)
|
|
593
|
-
);
|
|
594
|
-
|
|
595
|
-
const firstRun = await cliWithEnv(
|
|
596
|
-
[
|
|
597
|
-
'aws',
|
|
598
|
-
'secret',
|
|
599
|
-
'upsert',
|
|
600
|
-
'--file',
|
|
601
|
-
tempFile,
|
|
602
|
-
'--name',
|
|
603
|
-
secretName,
|
|
604
|
-
'--output',
|
|
605
|
-
'json'
|
|
606
|
-
],
|
|
607
|
-
getLocalStackEnv()
|
|
608
|
-
);
|
|
609
|
-
expect(firstRun.code).toBe(0);
|
|
610
|
-
const firstJson = JSON.parse(firstRun.stdout) as {
|
|
611
|
-
summary: { created: number; updated: number; skipped: number };
|
|
612
|
-
};
|
|
613
|
-
expect(firstJson.summary.created).toBe(1);
|
|
614
|
-
expect(firstJson.summary.updated).toBe(0);
|
|
615
|
-
|
|
616
|
-
const firstSecret = await execAwslocalCommand(
|
|
617
|
-
`awslocal secretsmanager get-secret-value --secret-id "${secretName}" --region us-east-1 --query SecretString --output text`,
|
|
618
|
-
getLocalStackEnv()
|
|
619
|
-
);
|
|
620
|
-
expect(JSON.parse(firstSecret.stdout.trim())).toEqual({
|
|
621
|
-
API_KEY: 'first',
|
|
622
|
-
DB_URL: 'postgres://one'
|
|
623
|
-
});
|
|
624
|
-
|
|
625
|
-
fs.writeFileSync(
|
|
626
|
-
tempFile,
|
|
627
|
-
['export API_KEY=second', 'DB_URL=postgres://two'].join('\n')
|
|
628
|
-
);
|
|
629
|
-
|
|
630
|
-
const secondRun = await cliWithEnv(
|
|
631
|
-
[
|
|
632
|
-
'aws',
|
|
633
|
-
'secret',
|
|
634
|
-
'import',
|
|
635
|
-
'--file',
|
|
636
|
-
tempFile,
|
|
637
|
-
'--name',
|
|
638
|
-
secretName,
|
|
639
|
-
'--output',
|
|
640
|
-
'json'
|
|
641
|
-
],
|
|
642
|
-
getLocalStackEnv()
|
|
643
|
-
);
|
|
644
|
-
expect(secondRun.code).toBe(0);
|
|
645
|
-
const secondJson = JSON.parse(secondRun.stdout) as {
|
|
646
|
-
summary: { created: number; updated: number; skipped: number };
|
|
647
|
-
};
|
|
648
|
-
expect(secondJson.summary.created).toBe(0);
|
|
649
|
-
expect(secondJson.summary.updated).toBe(1);
|
|
650
|
-
expect(secondJson.summary.skipped).toBe(0);
|
|
651
|
-
|
|
652
|
-
const secondSecret = await execAwslocalCommand(
|
|
653
|
-
`awslocal secretsmanager get-secret-value --secret-id "${secretName}" --region us-east-1 --query SecretString --output text`,
|
|
654
|
-
getLocalStackEnv()
|
|
655
|
-
);
|
|
656
|
-
expect(JSON.parse(secondSecret.stdout.trim())).toEqual({
|
|
657
|
-
API_KEY: 'second',
|
|
658
|
-
DB_URL: 'postgres://two'
|
|
659
|
-
});
|
|
660
|
-
|
|
661
|
-
const deleteResult = await cliWithEnv(
|
|
662
|
-
[
|
|
663
|
-
'aws',
|
|
664
|
-
'secret',
|
|
665
|
-
'delete',
|
|
666
|
-
'-n',
|
|
667
|
-
secretName,
|
|
668
|
-
'--force-delete-without-recovery',
|
|
669
|
-
'--yes'
|
|
670
|
-
],
|
|
671
|
-
getLocalStackEnv()
|
|
672
|
-
);
|
|
673
|
-
expect(deleteResult.code).toBe(0);
|
|
674
|
-
cleanupTempFile(tempFile);
|
|
675
|
-
});
|
|
676
|
-
});
|
|
677
|
-
});
|
|
678
|
-
});
|