env-secrets 0.1.9 → 0.2.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/.devcontainer/devcontainer.json +29 -0
- package/.github/workflows/release.yml +12 -2
- package/.github/workflows/unittests.yaml +73 -4
- package/README.md +195 -43
- package/__e2e__/index.test.ts +37 -0
- package/__tests__/index.test.ts +316 -31
- package/__tests__/vaults/utils.test.ts +183 -0
- package/__tests__/version.test.ts +8 -0
- package/dist/vaults/secretsmanager.js +29 -40
- package/jest.config.js +2 -1
- package/package.json +14 -9
- package/src/vaults/secretsmanager.ts +34 -45
package/__tests__/index.test.ts
CHANGED
|
@@ -1,37 +1,322 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import { Command, Argument } from 'commander';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import Debug from 'debug';
|
|
4
|
+
|
|
5
|
+
// Mock external dependencies
|
|
6
|
+
jest.mock('commander');
|
|
7
|
+
jest.mock('node:child_process');
|
|
8
|
+
jest.mock('debug', () => jest.fn());
|
|
9
|
+
jest.mock('../src/vaults/secretsmanager', () => ({
|
|
10
|
+
secretsmanager: jest.fn()
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
// Mock the version import
|
|
14
|
+
jest.mock('../src/version', () => ({
|
|
15
|
+
LIB_VERSION: '1.0.0'
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
// Import after mocking
|
|
19
|
+
import { secretsmanager } from '../src/vaults/secretsmanager';
|
|
20
|
+
|
|
21
|
+
// Mock the actual module under test
|
|
22
|
+
const mockSpawn = spawn as jest.MockedFunction<typeof spawn>;
|
|
23
|
+
const mockDebug = Debug as jest.MockedFunction<typeof Debug>;
|
|
24
|
+
const mockSecretsmanager = secretsmanager as jest.MockedFunction<
|
|
25
|
+
typeof secretsmanager
|
|
26
|
+
>;
|
|
27
|
+
const mockCommand = Command as jest.MockedClass<typeof Command>;
|
|
28
|
+
|
|
29
|
+
describe('index.ts CLI functionality', () => {
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
jest.clearAllMocks();
|
|
32
|
+
|
|
33
|
+
// Reset process.env
|
|
34
|
+
process.env = { ...process.env };
|
|
35
|
+
|
|
36
|
+
// Setup mock debug
|
|
37
|
+
const mockDebugInstance = jest.fn() as any;
|
|
38
|
+
mockDebug.mockReturnValue(mockDebugInstance);
|
|
15
39
|
});
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
40
|
+
|
|
41
|
+
describe('AWS command action logic', () => {
|
|
42
|
+
it('should call secretsmanager with correct options', async () => {
|
|
43
|
+
const mockEnv = { SECRET_KEY: 'secret_value' };
|
|
44
|
+
mockSecretsmanager.mockResolvedValue(mockEnv);
|
|
45
|
+
|
|
46
|
+
const options = {
|
|
47
|
+
secret: 'my-secret',
|
|
48
|
+
profile: 'my-profile',
|
|
49
|
+
region: 'us-east-1'
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Simulate the action logic
|
|
53
|
+
let env = await mockSecretsmanager(options);
|
|
54
|
+
env = Object.assign({}, process.env, env);
|
|
55
|
+
|
|
56
|
+
expect(mockSecretsmanager).toHaveBeenCalledWith(options);
|
|
57
|
+
expect(env).toEqual(
|
|
58
|
+
expect.objectContaining({
|
|
59
|
+
SECRET_KEY: 'secret_value'
|
|
60
|
+
})
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should merge secrets with process.env', async () => {
|
|
65
|
+
const mockSecrets = { SECRET_KEY: 'secret_value' };
|
|
66
|
+
const originalEnv = { ORIGINAL_KEY: 'original_value' };
|
|
67
|
+
process.env = { ...originalEnv };
|
|
68
|
+
|
|
69
|
+
mockSecretsmanager.mockResolvedValue(mockSecrets);
|
|
70
|
+
|
|
71
|
+
// Simulate the action logic
|
|
72
|
+
let env = await mockSecretsmanager({ secret: 'my-secret' });
|
|
73
|
+
env = Object.assign({}, process.env, env);
|
|
74
|
+
|
|
75
|
+
expect(env).toEqual(
|
|
76
|
+
expect.objectContaining({
|
|
77
|
+
ORIGINAL_KEY: 'original_value',
|
|
78
|
+
SECRET_KEY: 'secret_value'
|
|
79
|
+
})
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should spawn a program when provided', async () => {
|
|
84
|
+
const mockEnv = { SECRET_KEY: 'secret_value' };
|
|
85
|
+
mockSecretsmanager.mockResolvedValue(mockEnv);
|
|
86
|
+
|
|
87
|
+
const mockChildProcess = {
|
|
88
|
+
stdio: 'inherit'
|
|
89
|
+
};
|
|
90
|
+
mockSpawn.mockReturnValue(mockChildProcess as any);
|
|
91
|
+
|
|
92
|
+
const program = ['node', 'script.js', 'arg1', 'arg2'];
|
|
93
|
+
const options = { secret: 'my-secret' };
|
|
94
|
+
|
|
95
|
+
// Simulate the action logic
|
|
96
|
+
let env = await mockSecretsmanager(options);
|
|
97
|
+
env = Object.assign({}, process.env, env);
|
|
98
|
+
|
|
99
|
+
if (program && program.length > 0) {
|
|
100
|
+
mockSpawn(program[0], program.slice(1), {
|
|
101
|
+
stdio: 'inherit',
|
|
102
|
+
shell: true,
|
|
103
|
+
env
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
expect(mockSpawn).toHaveBeenCalledWith(
|
|
108
|
+
'node',
|
|
109
|
+
['script.js', 'arg1', 'arg2'],
|
|
110
|
+
{
|
|
111
|
+
stdio: 'inherit',
|
|
112
|
+
shell: true,
|
|
113
|
+
env: expect.objectContaining({
|
|
114
|
+
SECRET_KEY: 'secret_value'
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should not spawn a program when no program is provided', async () => {
|
|
121
|
+
const mockEnv = { SECRET_KEY: 'secret_value' };
|
|
122
|
+
mockSecretsmanager.mockResolvedValue(mockEnv);
|
|
123
|
+
|
|
124
|
+
// Simulate the action logic
|
|
125
|
+
let env = await mockSecretsmanager({ secret: 'my-secret' });
|
|
126
|
+
env = Object.assign({}, process.env, env);
|
|
127
|
+
|
|
128
|
+
const program: string[] = [];
|
|
129
|
+
if (program && program.length > 0) {
|
|
130
|
+
mockSpawn(program[0], program.slice(1), {
|
|
131
|
+
stdio: 'inherit',
|
|
132
|
+
shell: true,
|
|
133
|
+
env
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
expect(mockSpawn).not.toHaveBeenCalled();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should handle empty program array', async () => {
|
|
141
|
+
const mockEnv = { SECRET_KEY: 'secret_value' };
|
|
142
|
+
mockSecretsmanager.mockResolvedValue(mockEnv);
|
|
143
|
+
|
|
144
|
+
// Simulate the action logic
|
|
145
|
+
let env = await mockSecretsmanager({ secret: 'my-secret' });
|
|
146
|
+
env = Object.assign({}, process.env, env);
|
|
147
|
+
|
|
148
|
+
const program: string[] = [];
|
|
149
|
+
if (program && program.length > 0) {
|
|
150
|
+
mockSpawn(program[0], program.slice(1), {
|
|
151
|
+
stdio: 'inherit',
|
|
152
|
+
shell: true,
|
|
153
|
+
env
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
expect(mockSpawn).not.toHaveBeenCalled();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('should handle single program argument', async () => {
|
|
161
|
+
const mockEnv = { SECRET_KEY: 'secret_value' };
|
|
162
|
+
mockSecretsmanager.mockResolvedValue(mockEnv);
|
|
163
|
+
|
|
164
|
+
const mockChildProcess = {
|
|
165
|
+
stdio: 'inherit'
|
|
166
|
+
};
|
|
167
|
+
mockSpawn.mockReturnValue(mockChildProcess as any);
|
|
168
|
+
|
|
169
|
+
const program = ['echo'];
|
|
170
|
+
const options = { secret: 'my-secret' };
|
|
171
|
+
|
|
172
|
+
// Simulate the action logic
|
|
173
|
+
let env = await mockSecretsmanager(options);
|
|
174
|
+
env = Object.assign({}, process.env, env);
|
|
175
|
+
|
|
176
|
+
if (program && program.length > 0) {
|
|
177
|
+
mockSpawn(program[0], program.slice(1), {
|
|
178
|
+
stdio: 'inherit',
|
|
179
|
+
shell: true,
|
|
180
|
+
env
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
expect(mockSpawn).toHaveBeenCalledWith('echo', [], {
|
|
185
|
+
stdio: 'inherit',
|
|
186
|
+
shell: true,
|
|
187
|
+
env: expect.objectContaining({
|
|
188
|
+
SECRET_KEY: 'secret_value'
|
|
189
|
+
})
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('should preserve existing environment variables', async () => {
|
|
194
|
+
const mockSecrets = { SECRET_KEY: 'secret_value' };
|
|
195
|
+
const originalEnv = {
|
|
196
|
+
PATH: '/usr/bin',
|
|
197
|
+
HOME: '/home/user',
|
|
198
|
+
NODE_ENV: 'test'
|
|
199
|
+
};
|
|
200
|
+
process.env = { ...originalEnv };
|
|
201
|
+
|
|
202
|
+
mockSecretsmanager.mockResolvedValue(mockSecrets);
|
|
203
|
+
|
|
204
|
+
// Simulate the action logic
|
|
205
|
+
let env = await mockSecretsmanager({ secret: 'my-secret' });
|
|
206
|
+
env = Object.assign({}, process.env, env);
|
|
207
|
+
|
|
208
|
+
const program = ['echo'];
|
|
209
|
+
if (program && program.length > 0) {
|
|
210
|
+
mockSpawn(program[0], program.slice(1), {
|
|
211
|
+
stdio: 'inherit',
|
|
212
|
+
shell: true,
|
|
213
|
+
env
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
expect(mockSpawn).toHaveBeenCalledWith('echo', [], {
|
|
218
|
+
stdio: 'inherit',
|
|
219
|
+
shell: true,
|
|
220
|
+
env: expect.objectContaining({
|
|
221
|
+
PATH: '/usr/bin',
|
|
222
|
+
HOME: '/home/user',
|
|
223
|
+
NODE_ENV: 'test',
|
|
224
|
+
SECRET_KEY: 'secret_value'
|
|
225
|
+
})
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('should handle secretsmanager returning empty object', async () => {
|
|
230
|
+
mockSecretsmanager.mockResolvedValue({});
|
|
231
|
+
|
|
232
|
+
// Simulate the action logic
|
|
233
|
+
let env = await mockSecretsmanager({ secret: 'my-secret' });
|
|
234
|
+
env = Object.assign({}, process.env, env);
|
|
235
|
+
|
|
236
|
+
const program = ['echo'];
|
|
237
|
+
if (program && program.length > 0) {
|
|
238
|
+
mockSpawn(program[0], program.slice(1), {
|
|
239
|
+
stdio: 'inherit',
|
|
240
|
+
shell: true,
|
|
241
|
+
env
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
expect(mockSpawn).toHaveBeenCalledWith('echo', [], {
|
|
246
|
+
stdio: 'inherit',
|
|
247
|
+
shell: true,
|
|
248
|
+
env: expect.objectContaining({})
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('should handle secretsmanager throwing an error', async () => {
|
|
253
|
+
const error = new Error('AWS connection failed');
|
|
254
|
+
mockSecretsmanager.mockRejectedValue(error);
|
|
255
|
+
|
|
256
|
+
// Should throw - the error should propagate
|
|
257
|
+
await expect(mockSecretsmanager({ secret: 'my-secret' })).rejects.toThrow(
|
|
258
|
+
'AWS connection failed'
|
|
259
|
+
);
|
|
260
|
+
});
|
|
19
261
|
});
|
|
20
|
-
});
|
|
21
262
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
263
|
+
describe('Debug logging', () => {
|
|
264
|
+
it('should create debug instance with correct namespace', () => {
|
|
265
|
+
const debugInstance = mockDebug('env-secrets');
|
|
266
|
+
expect(mockDebug).toHaveBeenCalledWith('env-secrets');
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('should log environment variables when debug is enabled', async () => {
|
|
270
|
+
const mockDebugInstance = jest.fn() as any;
|
|
271
|
+
mockDebug.mockReturnValue(mockDebugInstance);
|
|
272
|
+
|
|
273
|
+
const mockEnv = { SECRET_KEY: 'secret_value' };
|
|
274
|
+
mockSecretsmanager.mockResolvedValue(mockEnv);
|
|
275
|
+
|
|
276
|
+
// Simulate the action logic
|
|
277
|
+
let env = await mockSecretsmanager({ secret: 'my-secret' });
|
|
278
|
+
env = Object.assign({}, process.env, env);
|
|
279
|
+
|
|
280
|
+
// Simulate debug logging
|
|
281
|
+
mockDebugInstance(env);
|
|
282
|
+
|
|
283
|
+
expect(mockDebugInstance).toHaveBeenCalledWith(
|
|
284
|
+
expect.objectContaining({
|
|
285
|
+
SECRET_KEY: 'secret_value'
|
|
286
|
+
})
|
|
287
|
+
);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('should log program execution when program is provided', async () => {
|
|
291
|
+
const mockDebugInstance = jest.fn() as any;
|
|
292
|
+
mockDebug.mockReturnValue(mockDebugInstance);
|
|
293
|
+
|
|
294
|
+
const mockEnv = { SECRET_KEY: 'secret_value' };
|
|
295
|
+
mockSecretsmanager.mockResolvedValue(mockEnv);
|
|
296
|
+
|
|
297
|
+
const mockChildProcess = {
|
|
298
|
+
stdio: 'inherit'
|
|
299
|
+
};
|
|
300
|
+
mockSpawn.mockReturnValue(mockChildProcess as any);
|
|
301
|
+
|
|
302
|
+
const program = ['node', 'script.js', 'arg1'];
|
|
303
|
+
|
|
304
|
+
// Simulate the action logic
|
|
305
|
+
let env = await mockSecretsmanager({ secret: 'my-secret' });
|
|
306
|
+
env = Object.assign({}, process.env, env);
|
|
307
|
+
|
|
308
|
+
if (program && program.length > 0) {
|
|
309
|
+
// Simulate debug logging
|
|
310
|
+
mockDebugInstance(`${program[0]} ${program.slice(1).join(' ')}`);
|
|
311
|
+
|
|
312
|
+
mockSpawn(program[0], program.slice(1), {
|
|
313
|
+
stdio: 'inherit',
|
|
314
|
+
shell: true,
|
|
315
|
+
env
|
|
33
316
|
});
|
|
34
317
|
}
|
|
35
|
-
|
|
318
|
+
|
|
319
|
+
expect(mockDebugInstance).toHaveBeenCalledWith('node script.js arg1');
|
|
320
|
+
});
|
|
36
321
|
});
|
|
37
|
-
}
|
|
322
|
+
});
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import * as os from 'os';
|
|
2
|
+
import {
|
|
3
|
+
replaceWithAstrisk,
|
|
4
|
+
objectToExport,
|
|
5
|
+
objectToEnv
|
|
6
|
+
} from '../../src/vaults/utils';
|
|
7
|
+
|
|
8
|
+
describe('vaults utils', () => {
|
|
9
|
+
describe('replaceWithAstrisk', () => {
|
|
10
|
+
test('should return undefined for undefined input', () => {
|
|
11
|
+
expect(replaceWithAstrisk(undefined)).toBeUndefined();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test('should return undefined for empty string', () => {
|
|
15
|
+
expect(replaceWithAstrisk('')).toBeUndefined();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('should mask middle characters for strings longer than 4 characters', () => {
|
|
19
|
+
expect(replaceWithAstrisk('password123')).toBe('p******d123');
|
|
20
|
+
expect(replaceWithAstrisk('secretkey')).toBe('s****tkey');
|
|
21
|
+
expect(replaceWithAstrisk('verylongpassword')).toBe('v***********word');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('should not mask characters for strings 4 characters or shorter', () => {
|
|
25
|
+
expect(replaceWithAstrisk('1234')).toBe('1234');
|
|
26
|
+
expect(replaceWithAstrisk('abc')).toBe('abc');
|
|
27
|
+
expect(replaceWithAstrisk('a')).toBe('a');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('should preserve first and last 4 characters for longer strings', () => {
|
|
31
|
+
expect(replaceWithAstrisk('abcdefghijklmnop')).toBe('a***********mnop');
|
|
32
|
+
expect(replaceWithAstrisk('1234567890123456')).toBe('1***********3456');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('should handle special characters and unicode', () => {
|
|
36
|
+
expect(replaceWithAstrisk('p@ssw0rd!')).toBe('p****0rd!');
|
|
37
|
+
expect(replaceWithAstrisk('🔑secret🔒')).toBe('🔑*****t🔒');
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('objectToExport', () => {
|
|
42
|
+
test('should convert object to export statements', () => {
|
|
43
|
+
const obj = {
|
|
44
|
+
API_KEY: 'abc123',
|
|
45
|
+
DATABASE_URL: 'postgres://localhost:5432/db',
|
|
46
|
+
DEBUG: 'true'
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const result = objectToExport(obj);
|
|
50
|
+
const expected = `export API_KEY=abc123${os.EOL}export DATABASE_URL=postgres://localhost:5432/db${os.EOL}export DEBUG=true${os.EOL}`;
|
|
51
|
+
|
|
52
|
+
expect(result).toBe(expected);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('should handle empty object', () => {
|
|
56
|
+
const obj = {};
|
|
57
|
+
const result = objectToExport(obj);
|
|
58
|
+
expect(result).toBe('');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('should handle object with various value types', () => {
|
|
62
|
+
const obj = {
|
|
63
|
+
STRING: 'hello',
|
|
64
|
+
NUMBER: 42,
|
|
65
|
+
BOOLEAN: true,
|
|
66
|
+
NULL: null,
|
|
67
|
+
UNDEFINED: undefined
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const result = objectToExport(obj);
|
|
71
|
+
const expected = `export STRING=hello${os.EOL}export NUMBER=42${os.EOL}export BOOLEAN=true${os.EOL}export NULL=null${os.EOL}export UNDEFINED=undefined${os.EOL}`;
|
|
72
|
+
|
|
73
|
+
expect(result).toBe(expected);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('should handle object with special characters in keys and values', () => {
|
|
77
|
+
const obj = {
|
|
78
|
+
'API-KEY': 'abc-123',
|
|
79
|
+
DATABASE_URL: 'postgres://user:pass@localhost:5432/db',
|
|
80
|
+
DEBUG_MODE: 'true'
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const result = objectToExport(obj);
|
|
84
|
+
const expected = `export API-KEY=abc-123${os.EOL}export DATABASE_URL=postgres://user:pass@localhost:5432/db${os.EOL}export DEBUG_MODE=true${os.EOL}`;
|
|
85
|
+
|
|
86
|
+
expect(result).toBe(expected);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('objectToEnv', () => {
|
|
91
|
+
let originalEnv: NodeJS.ProcessEnv;
|
|
92
|
+
|
|
93
|
+
beforeEach(() => {
|
|
94
|
+
originalEnv = { ...process.env };
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
afterEach(() => {
|
|
98
|
+
process.env = originalEnv;
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test('should set environment variables from object', () => {
|
|
102
|
+
const obj = {
|
|
103
|
+
API_KEY: 'abc123',
|
|
104
|
+
DATABASE_URL: 'postgres://localhost:5432/db',
|
|
105
|
+
DEBUG: 'true'
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const result = objectToEnv(obj);
|
|
109
|
+
|
|
110
|
+
expect(process.env.API_KEY).toBe('abc123');
|
|
111
|
+
expect(process.env.DATABASE_URL).toBe('postgres://localhost:5432/db');
|
|
112
|
+
expect(process.env.DEBUG).toBe('true');
|
|
113
|
+
expect(result).toEqual([
|
|
114
|
+
'abc123',
|
|
115
|
+
'postgres://localhost:5432/db',
|
|
116
|
+
'true'
|
|
117
|
+
]);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test('should handle empty object', () => {
|
|
121
|
+
const obj = {};
|
|
122
|
+
const result = objectToEnv(obj);
|
|
123
|
+
|
|
124
|
+
expect(result).toEqual([]);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('should handle object with various value types', () => {
|
|
128
|
+
const obj = {
|
|
129
|
+
STRING: 'hello',
|
|
130
|
+
NUMBER: 42,
|
|
131
|
+
BOOLEAN: true,
|
|
132
|
+
NULL: null,
|
|
133
|
+
UNDEFINED: undefined
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const result = objectToEnv(obj);
|
|
137
|
+
|
|
138
|
+
expect(process.env.STRING).toBe('hello');
|
|
139
|
+
expect(process.env.NUMBER).toBe(42);
|
|
140
|
+
expect(process.env.BOOLEAN).toBe(true);
|
|
141
|
+
expect(process.env.NULL).toBe(null);
|
|
142
|
+
expect(process.env.UNDEFINED).toBe(undefined);
|
|
143
|
+
expect(result).toEqual(['hello', 42, true, null, undefined]);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test('should overwrite existing environment variables', () => {
|
|
147
|
+
// Set initial environment variable
|
|
148
|
+
process.env.EXISTING_VAR = 'old_value';
|
|
149
|
+
|
|
150
|
+
const obj = {
|
|
151
|
+
EXISTING_VAR: 'new_value',
|
|
152
|
+
NEW_VAR: 'new_var_value'
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const result = objectToEnv(obj);
|
|
156
|
+
|
|
157
|
+
expect(process.env.EXISTING_VAR).toBe('new_value');
|
|
158
|
+
expect(process.env.NEW_VAR).toBe('new_var_value');
|
|
159
|
+
expect(result).toEqual(['new_value', 'new_var_value']);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('should handle object with special characters in keys and values', () => {
|
|
163
|
+
const obj = {
|
|
164
|
+
'API-KEY': 'abc-123',
|
|
165
|
+
DATABASE_URL: 'postgres://user:pass@localhost:5432/db',
|
|
166
|
+
DEBUG_MODE: 'true'
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const result = objectToEnv(obj);
|
|
170
|
+
|
|
171
|
+
expect(process.env['API-KEY']).toBe('abc-123');
|
|
172
|
+
expect(process.env['DATABASE_URL']).toBe(
|
|
173
|
+
'postgres://user:pass@localhost:5432/db'
|
|
174
|
+
);
|
|
175
|
+
expect(process.env['DEBUG_MODE']).toBe('true');
|
|
176
|
+
expect(result).toEqual([
|
|
177
|
+
'abc-123',
|
|
178
|
+
'postgres://user:pass@localhost:5432/db',
|
|
179
|
+
'true'
|
|
180
|
+
]);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
});
|
|
@@ -13,67 +13,55 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.secretsmanager = void 0;
|
|
16
|
-
const
|
|
16
|
+
const client_secrets_manager_1 = require("@aws-sdk/client-secrets-manager");
|
|
17
|
+
const client_sts_1 = require("@aws-sdk/client-sts");
|
|
18
|
+
const credential_providers_1 = require("@aws-sdk/credential-providers");
|
|
17
19
|
const debug_1 = __importDefault(require("debug"));
|
|
18
20
|
const debug = (0, debug_1.default)('env-secrets:secretsmanager');
|
|
19
|
-
const checkConnection = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
let value;
|
|
31
|
-
let err;
|
|
32
|
-
yield getCallerPromise
|
|
33
|
-
.then((v) => {
|
|
34
|
-
value = v;
|
|
35
|
-
})
|
|
36
|
-
.catch((e) => {
|
|
37
|
-
err = e;
|
|
38
|
-
});
|
|
39
|
-
if (err) {
|
|
21
|
+
const checkConnection = (region) => __awaiter(void 0, void 0, void 0, function* () {
|
|
22
|
+
const stsClient = new client_sts_1.STSClient({ region });
|
|
23
|
+
const command = new client_sts_1.GetCallerIdentityCommand({});
|
|
24
|
+
try {
|
|
25
|
+
const data = yield stsClient.send(command);
|
|
26
|
+
debug(data);
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
40
30
|
console.error(err);
|
|
41
31
|
return false;
|
|
42
32
|
}
|
|
43
|
-
debug(value);
|
|
44
|
-
return !!value;
|
|
45
33
|
});
|
|
46
34
|
const secretsmanager = (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
47
35
|
const { secret, profile, region } = options;
|
|
48
36
|
const { AWS_ACCESS_KEY_ID: awsAccessKeyId, AWS_SECRET_ACCESS_KEY: awsSecretAccessKey } = process.env;
|
|
37
|
+
let credentials;
|
|
49
38
|
if (profile) {
|
|
50
39
|
debug(`Using profile: ${profile}`);
|
|
51
|
-
|
|
52
|
-
profile
|
|
53
|
-
});
|
|
54
|
-
aws_sdk_1.default.config.credentials = credentials;
|
|
40
|
+
credentials = (0, credential_providers_1.fromIni)({ profile });
|
|
55
41
|
}
|
|
56
42
|
else if (awsAccessKeyId && awsSecretAccessKey) {
|
|
57
43
|
debug('Using environment variables');
|
|
44
|
+
credentials = undefined; // Will use environment variables automatically
|
|
58
45
|
}
|
|
59
46
|
else {
|
|
60
47
|
debug('Using profile: default');
|
|
48
|
+
credentials = (0, credential_providers_1.fromIni)({ profile: 'default' });
|
|
61
49
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
50
|
+
const config = {
|
|
51
|
+
region,
|
|
52
|
+
credentials
|
|
53
|
+
};
|
|
54
|
+
if (!config.region) {
|
|
66
55
|
debug('no region set');
|
|
67
56
|
}
|
|
68
|
-
const connected = yield checkConnection();
|
|
57
|
+
const connected = yield checkConnection(region);
|
|
69
58
|
if (connected) {
|
|
70
|
-
const
|
|
59
|
+
const client = new client_secrets_manager_1.SecretsManagerClient(config);
|
|
71
60
|
try {
|
|
72
|
-
const
|
|
73
|
-
.getSecretValue({
|
|
61
|
+
const command = new client_secrets_manager_1.GetSecretValueCommand({
|
|
74
62
|
SecretId: secret
|
|
75
|
-
})
|
|
76
|
-
|
|
63
|
+
});
|
|
64
|
+
const response = yield client.send(command);
|
|
77
65
|
const secretvalue = response.SecretString;
|
|
78
66
|
try {
|
|
79
67
|
if (secretvalue) {
|
|
@@ -85,10 +73,10 @@ const secretsmanager = (options) => __awaiter(void 0, void 0, void 0, function*
|
|
|
85
73
|
}
|
|
86
74
|
}
|
|
87
75
|
catch (err) {
|
|
88
|
-
if (err && err.
|
|
76
|
+
if (err && err.name === 'ResourceNotFoundException') {
|
|
89
77
|
console.error(`${secret} not found`);
|
|
90
78
|
}
|
|
91
|
-
else if (err && err.
|
|
79
|
+
else if (err && err.name === 'ConfigError') {
|
|
92
80
|
console.error(err.message);
|
|
93
81
|
}
|
|
94
82
|
else {
|
|
@@ -99,6 +87,7 @@ const secretsmanager = (options) => __awaiter(void 0, void 0, void 0, function*
|
|
|
99
87
|
}
|
|
100
88
|
else {
|
|
101
89
|
console.error('Unable to connect to AWS');
|
|
90
|
+
return {};
|
|
102
91
|
}
|
|
103
92
|
});
|
|
104
93
|
exports.secretsmanager = secretsmanager;
|