mirror of
https://github.com/aws-actions/configure-aws-credentials.git
synced 2026-09-02 05:55:10 +09:00
fix: allow kubelet token symlink (#1805)
* Revert "chore: Update dist" This reverts commit5548f3441b. * Revert "chore: document container credentials provider support (and delete transitive tags in AssumeRoleWithWebIdentity) (#1780)" This reverts commit77cd089899. * Revert "chore: replay 6.2 devel changes onto main (#1800)" This reverts commit87eb0cf693. * fix: allow kubelet token symlink Closes #1804
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
import * as core from '@actions/core';
|
||||
import { AssumeRoleWithWebIdentityCommand, GetCallerIdentityCommand, STSClient } from '@aws-sdk/client-sts';
|
||||
import {
|
||||
AssumeRoleWithWebIdentityCommand,
|
||||
GetCallerIdentityCommand,
|
||||
STSClient,
|
||||
} from '@aws-sdk/client-sts';
|
||||
import { mockClient } from 'aws-sdk-client-mock';
|
||||
import { fs, vol } from 'memfs';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
+13
-8
@@ -5,15 +5,20 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { cleanup } from '../src/cleanup';
|
||||
import mocks from './mockinputs.test';
|
||||
|
||||
vi.mock('@actions/core');
|
||||
|
||||
const mockedSTSClient = mockClient(STSClient);
|
||||
|
||||
describe('Configure AWS Credentials cleanup', {}, () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
// Reset mock state
|
||||
vi.restoreAllMocks();
|
||||
mockedSTSClient.reset();
|
||||
vi.mocked(core.getInput).mockReturnValue('');
|
||||
// Mock GitHub Actions core functions
|
||||
vi.spyOn(core, 'exportVariable').mockImplementation((_n, _v) => {});
|
||||
vi.spyOn(core, 'setSecret').mockImplementation((_s) => {});
|
||||
vi.spyOn(core, 'setFailed').mockImplementation((_m) => {});
|
||||
vi.spyOn(core, 'setOutput').mockImplementation((_n, _v) => {});
|
||||
vi.spyOn(core, 'debug').mockImplementation((_m) => {});
|
||||
vi.spyOn(core, 'info').mockImplementation((_m) => {});
|
||||
process.env = {
|
||||
...mocks.envs,
|
||||
AWS_ACCESS_KEY_ID: 'CLEANUPTEST',
|
||||
@@ -34,7 +39,7 @@ describe('Configure AWS Credentials cleanup', {}, () => {
|
||||
expect(core.exportVariable).toHaveBeenCalledWith('AWS_REGION', '');
|
||||
});
|
||||
it('also clears AWS_PROFILE when aws-profile was set', {}, () => {
|
||||
vi.mocked(core.getInput).mockImplementation((name: string) => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation((name: string) => {
|
||||
if (name === 'aws-profile') return 'my-profile';
|
||||
if (name === 'output-env-credentials') return 'true';
|
||||
return '';
|
||||
@@ -45,7 +50,7 @@ describe('Configure AWS Credentials cleanup', {}, () => {
|
||||
expect(core.exportVariable).toHaveBeenCalledWith('AWS_PROFILE', '');
|
||||
});
|
||||
it('skips env cleanup when aws-profile is set without output-env-credentials', {}, () => {
|
||||
vi.mocked(core.getInput).mockImplementation((name: string) => {
|
||||
vi.spyOn(core, 'getInput').mockImplementation((name: string) => {
|
||||
if (name === 'aws-profile') return 'my-profile';
|
||||
return '';
|
||||
});
|
||||
@@ -54,14 +59,14 @@ describe('Configure AWS Credentials cleanup', {}, () => {
|
||||
expect(core.exportVariable).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
it('handles errors', {}, () => {
|
||||
vi.mocked(core.exportVariable).mockImplementationOnce(() => {
|
||||
vi.spyOn(core, 'exportVariable').mockImplementationOnce(() => {
|
||||
throw new Error('Test error');
|
||||
});
|
||||
cleanup();
|
||||
expect(core.setFailed).toHaveBeenCalled();
|
||||
});
|
||||
it(`doesn't export credentials as empty env variables if asked not to`, {}, () => {
|
||||
vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.NO_ENV_CREDS_INPUTS));
|
||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.NO_ENV_CREDS_INPUTS));
|
||||
cleanup();
|
||||
expect(core.exportVariable).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
+72
-26
@@ -28,29 +28,6 @@ describe('Configure AWS Credentials helpers', {}, () => {
|
||||
await expect(helpers.retryAndBackoff(fn, false)).rejects.toMatch('i am not retryable');
|
||||
expect(fn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
it('retries and logs with label at info level', {}, async () => {
|
||||
helpers.withsleep(() => Promise.resolve());
|
||||
const fn = vi.fn().mockRejectedValueOnce(new Error('transient')).mockResolvedValueOnce('success');
|
||||
const result = await helpers.retryAndBackoff(fn, true, 3, 0, 50, 'TestOp');
|
||||
expect(result).toBe('success');
|
||||
expect(fn).toHaveBeenCalledTimes(2);
|
||||
expect(core.info).toHaveBeenCalledWith(expect.stringContaining('Retry TestOp: attempt 1 of 3 failed'));
|
||||
helpers.reset();
|
||||
});
|
||||
it('logs max retries reached with label', {}, async () => {
|
||||
helpers.withsleep(() => Promise.resolve());
|
||||
const fn = vi.fn().mockRejectedValue(new Error('persistent'));
|
||||
await expect(helpers.retryAndBackoff(fn, true, 2, 0, 50, 'TestOp')).rejects.toThrow('persistent');
|
||||
expect(core.info).toHaveBeenCalledWith(expect.stringContaining('Retry TestOp: reached max retries (2)'));
|
||||
helpers.reset();
|
||||
});
|
||||
it('retries without a label (backward compat)', {}, async () => {
|
||||
helpers.withsleep(() => Promise.resolve());
|
||||
const fn = vi.fn().mockRejectedValueOnce(new Error('transient')).mockResolvedValueOnce('ok');
|
||||
await helpers.retryAndBackoff(fn, true, 3);
|
||||
expect(core.info).toHaveBeenCalledWith(expect.stringContaining('Retry: attempt 1 of 3 failed'));
|
||||
helpers.reset();
|
||||
});
|
||||
it('can output creds when told to', {}, () => {
|
||||
vi.spyOn(core, 'setOutput').mockImplementation(() => {});
|
||||
vi.spyOn(core, 'setSecret').mockImplementation(() => {});
|
||||
@@ -107,13 +84,13 @@ describe('Configure AWS Credentials helpers', {}, () => {
|
||||
it('handles getBooleanInput correctly', {}, () => {
|
||||
vi.spyOn(core, 'getInput').mockReturnValue('true');
|
||||
expect(helpers.getBooleanInput('test')).toBe(true);
|
||||
|
||||
|
||||
vi.spyOn(core, 'getInput').mockReturnValue('false');
|
||||
expect(helpers.getBooleanInput('test')).toBe(false);
|
||||
|
||||
|
||||
vi.spyOn(core, 'getInput').mockReturnValue('');
|
||||
expect(helpers.getBooleanInput('test', { default: true })).toBe(true);
|
||||
|
||||
|
||||
vi.spyOn(core, 'getInput').mockReturnValue('invalid');
|
||||
expect(() => helpers.getBooleanInput('test')).toThrow();
|
||||
});
|
||||
@@ -177,6 +154,75 @@ describe('Configure AWS Credentials helpers', {}, () => {
|
||||
fs.mkdirSync('/dir/subdir', { recursive: true });
|
||||
expect(() => helpers.readFileUtf8('/dir/subdir')).toThrow(/not a regular file/);
|
||||
});
|
||||
|
||||
it.skipIf(process.platform === 'win32')(
|
||||
'follows the kubelet projected-token symlink chain at /var/run/secrets/*/serviceaccount/token',
|
||||
() => {
|
||||
fs.mkdirSync('/var/run/secrets/eks.amazonaws.com/serviceaccount/..2026_05_28_00_00_00.123', {
|
||||
recursive: true,
|
||||
});
|
||||
fs.writeFileSync(
|
||||
'/var/run/secrets/eks.amazonaws.com/serviceaccount/..2026_05_28_00_00_00.123/token',
|
||||
'jwt-token',
|
||||
);
|
||||
fs.symlinkSync(
|
||||
'..2026_05_28_00_00_00.123',
|
||||
'/var/run/secrets/eks.amazonaws.com/serviceaccount/..data',
|
||||
);
|
||||
fs.symlinkSync(
|
||||
'..data/token',
|
||||
'/var/run/secrets/eks.amazonaws.com/serviceaccount/token',
|
||||
);
|
||||
expect(helpers.readFileUtf8('/var/run/secrets/eks.amazonaws.com/serviceaccount/token')).toBe('jwt-token');
|
||||
},
|
||||
);
|
||||
|
||||
it.skipIf(process.platform === 'win32')(
|
||||
'still refuses symlinks at lookalike paths outside the allowlist',
|
||||
() => {
|
||||
fs.mkdirSync('/var/run/secrets/eks.amazonaws.com/serviceaccount', { recursive: true });
|
||||
fs.writeFileSync('/var/run/secrets/eks.amazonaws.com/serviceaccount/secret', 'jwt-token');
|
||||
fs.symlinkSync(
|
||||
'/var/run/secrets/eks.amazonaws.com/serviceaccount/secret',
|
||||
'/var/run/secrets/eks.amazonaws.com/serviceaccount/token2',
|
||||
);
|
||||
expect(() =>
|
||||
helpers.readFileUtf8('/var/run/secrets/eks.amazonaws.com/serviceaccount/token2'),
|
||||
).toThrow(/Refusing .* \(.* symbolic link\)/);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('isAllowListed', {}, () => {
|
||||
it.skipIf(process.platform === 'win32')('matches the canonical kubelet projected-token path', () => {
|
||||
expect(
|
||||
helpers.isAllowListed('/var/run/secrets/eks.amazonaws.com/serviceaccount/token'),
|
||||
).toBe(true);
|
||||
expect(
|
||||
helpers.isAllowListed('/var/run/secrets/kubernetes.io/serviceaccount/token'),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it.skipIf(process.platform === 'win32')('rejects nested or unrelated paths', () => {
|
||||
expect(helpers.isAllowListed('/var/run/secrets/serviceaccount/token')).toBe(false);
|
||||
expect(
|
||||
helpers.isAllowListed('/var/run/secrets/a/b/serviceaccount/token'),
|
||||
).toBe(false);
|
||||
expect(
|
||||
helpers.isAllowListed('/var/run/secrets/eks.amazonaws.com/serviceaccount/token2'),
|
||||
).toBe(false);
|
||||
expect(
|
||||
helpers.isAllowListed('/etc/var/run/secrets/foo/serviceaccount/token'),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it.skipIf(process.platform === 'win32')('normalizes path traversal attempts', () => {
|
||||
expect(
|
||||
helpers.isAllowListed(
|
||||
'/var/run/secrets/foo/serviceaccount/../../../../etc/passwd',
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('writeFileUtf8', {}, () => {
|
||||
|
||||
+85
-635
File diff suppressed because it is too large
Load Diff
@@ -6,46 +6,6 @@ const inputs = {
|
||||
'aws-region': 'fake-region-1',
|
||||
'special-characters-workaround': 'true',
|
||||
},
|
||||
CUSTOM_TAGS_INVALID_JSON_INPUTS: {
|
||||
'aws-access-key-id': 'MYAWSACCESSKEYID',
|
||||
'aws-secret-access-key': 'MYAWSSECRETACCESSKEY',
|
||||
'role-to-assume': 'arn:aws:iam::111111111111:role/MY-ROLE',
|
||||
'aws-region': 'fake-region-1',
|
||||
'retry-max-attempts': '1',
|
||||
'custom-tags': 'not a json',
|
||||
},
|
||||
CUSTOM_TAGS_ARRAY_INPUTS: {
|
||||
'aws-access-key-id': 'MYAWSACCESSKEYID',
|
||||
'aws-secret-access-key': 'MYAWSSECRETACCESSKEY',
|
||||
'role-to-assume': 'arn:aws:iam::111111111111:role/MY-ROLE',
|
||||
'aws-region': 'fake-region-1',
|
||||
'retry-max-attempts': '1',
|
||||
'custom-tags': '[1, 2, 3]',
|
||||
},
|
||||
CUSTOM_TAGS_RESERVED_KEY_INPUTS: {
|
||||
'aws-access-key-id': 'MYAWSACCESSKEYID',
|
||||
'aws-secret-access-key': 'MYAWSSECRETACCESSKEY',
|
||||
'role-to-assume': 'arn:aws:iam::111111111111:role/MY-ROLE',
|
||||
'aws-region': 'fake-region-1',
|
||||
'retry-max-attempts': '1',
|
||||
'custom-tags': JSON.stringify({ Repository: 'evil-repo' }),
|
||||
},
|
||||
CUSTOM_TAGS_INVALID_KEY_CHARS_INPUTS: {
|
||||
'aws-access-key-id': 'MYAWSACCESSKEYID',
|
||||
'aws-secret-access-key': 'MYAWSSECRETACCESSKEY',
|
||||
'role-to-assume': 'arn:aws:iam::111111111111:role/MY-ROLE',
|
||||
'aws-region': 'fake-region-1',
|
||||
'retry-max-attempts': '1',
|
||||
'custom-tags': JSON.stringify({ 'invalid{key}': 'value' }),
|
||||
},
|
||||
CUSTOM_TAGS_OBJECT_INPUTS: {
|
||||
'aws-access-key-id': 'MYAWSACCESSKEYID',
|
||||
'aws-secret-access-key': 'MYAWSSECRETACCESSKEY',
|
||||
'role-to-assume': 'arn:aws:iam::111111111111:role/MY-ROLE',
|
||||
'aws-region': 'fake-region-1',
|
||||
'retry-max-attempts': '1',
|
||||
'custom-tags': JSON.stringify({ Environment: 'Production', Team: 'DevOps' }),
|
||||
},
|
||||
IAM_USER_INPUTS: {
|
||||
'aws-access-key-id': 'MYAWSACCESSKEYID',
|
||||
'aws-secret-access-key': 'MYAWSSECRETACCESSKEY',
|
||||
@@ -83,14 +43,6 @@ const inputs = {
|
||||
'output-env-credentials': 'false',
|
||||
'output-credentials': 'true',
|
||||
},
|
||||
IAM_ASSUMEROLE_NO_ENV_INPUTS: {
|
||||
'aws-access-key-id': 'MYAWSACCESSKEYID',
|
||||
'aws-secret-access-key': 'MYAWSSECRETACCESSKEY',
|
||||
'role-to-assume': 'arn:aws:iam::111111111111:role/MY-ROLE',
|
||||
'aws-region': 'fake-region-1',
|
||||
'output-env-credentials': 'false',
|
||||
'output-credentials': 'true',
|
||||
},
|
||||
};
|
||||
|
||||
const envs = {
|
||||
@@ -101,15 +53,6 @@ const envs = {
|
||||
GITHUB_SHA: 'MY-COMMIT-ID',
|
||||
GITHUB_WORKSPACE: '/home/github',
|
||||
GITHUB_ACTIONS: 'true',
|
||||
GITHUB_REF: 'refs/pull/42/merge',
|
||||
GITHUB_EVENT_NAME: 'pull_request',
|
||||
GITHUB_RUN_ID: '16412345678',
|
||||
GITHUB_JOB: 'build',
|
||||
GITHUB_REF_NAME: 'feature-branch',
|
||||
GITHUB_REF_TYPE: 'branch',
|
||||
GITHUB_BASE_REF: 'main',
|
||||
GITHUB_HEAD_REF: 'feature-branch',
|
||||
GITHUB_TRIGGERING_ACTOR: 'MY-USERNAME[bot]',
|
||||
};
|
||||
|
||||
const outputs = {
|
||||
|
||||
+113
-127
@@ -96,10 +96,7 @@ describe('Profile Manager', {}, () => {
|
||||
});
|
||||
|
||||
it('round-trips through parseIni', {}, () => {
|
||||
const data = {
|
||||
dev: { aws_access_key_id: 'AKIA', aws_secret_access_key: 'secret' },
|
||||
'profile prod': { region: 'us-west-2' },
|
||||
};
|
||||
const data = { dev: { aws_access_key_id: 'AKIA', aws_secret_access_key: 'secret' }, 'profile prod': { region: 'us-west-2' } };
|
||||
const roundTripped = parseIni(stringifyIni(data));
|
||||
expect(roundTripped).toEqual(data);
|
||||
});
|
||||
@@ -200,15 +197,10 @@ describe('Profile Manager', {}, () => {
|
||||
const filePath = '/home/runner/.aws/credentials';
|
||||
fs.mkdirSync('/home/runner/.aws', { recursive: true });
|
||||
|
||||
mergeProfileSection(
|
||||
filePath,
|
||||
'dev',
|
||||
{
|
||||
aws_access_key_id: 'AKIAIOSFODNN7EXAMPLE',
|
||||
aws_secret_access_key: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
|
||||
},
|
||||
false,
|
||||
);
|
||||
mergeProfileSection(filePath, 'dev', {
|
||||
aws_access_key_id: 'AKIAIOSFODNN7EXAMPLE',
|
||||
aws_secret_access_key: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
|
||||
}, false);
|
||||
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
const parsed = parseIni(content);
|
||||
@@ -223,26 +215,16 @@ describe('Profile Manager', {}, () => {
|
||||
fs.mkdirSync('/home/runner/.aws', { recursive: true });
|
||||
|
||||
// Create initial profile
|
||||
mergeProfileSection(
|
||||
filePath,
|
||||
'dev',
|
||||
{
|
||||
aws_access_key_id: 'AKIAIOSFODNN7EXAMPLE',
|
||||
aws_secret_access_key: 'devSecretKey',
|
||||
},
|
||||
false,
|
||||
);
|
||||
mergeProfileSection(filePath, 'dev', {
|
||||
aws_access_key_id: 'AKIAIOSFODNN7EXAMPLE',
|
||||
aws_secret_access_key: 'devSecretKey',
|
||||
}, false);
|
||||
|
||||
// Add second profile
|
||||
mergeProfileSection(
|
||||
filePath,
|
||||
'prod',
|
||||
{
|
||||
aws_access_key_id: 'AKIAPRODEXAMPLE',
|
||||
aws_secret_access_key: 'prodSecretKey',
|
||||
},
|
||||
false,
|
||||
);
|
||||
mergeProfileSection(filePath, 'prod', {
|
||||
aws_access_key_id: 'AKIAPRODEXAMPLE',
|
||||
aws_secret_access_key: 'prodSecretKey',
|
||||
}, false);
|
||||
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
const parsed = parseIni(content);
|
||||
@@ -258,28 +240,18 @@ describe('Profile Manager', {}, () => {
|
||||
fs.mkdirSync('/home/runner/.aws', { recursive: true });
|
||||
|
||||
// Create initial profile
|
||||
mergeProfileSection(
|
||||
filePath,
|
||||
'dev',
|
||||
{
|
||||
aws_access_key_id: 'OLD_KEY',
|
||||
aws_secret_access_key: 'oldSecretKey',
|
||||
aws_session_token: 'oldSessionToken',
|
||||
},
|
||||
false,
|
||||
);
|
||||
mergeProfileSection(filePath, 'dev', {
|
||||
aws_access_key_id: 'OLD_KEY',
|
||||
aws_secret_access_key: 'oldSecretKey',
|
||||
aws_session_token: 'oldSessionToken'
|
||||
}, false);
|
||||
|
||||
// Overwrite with new credentials
|
||||
mergeProfileSection(
|
||||
filePath,
|
||||
'dev',
|
||||
{
|
||||
aws_access_key_id: 'NEW_KEY',
|
||||
aws_secret_access_key: 'newSecretKey',
|
||||
aws_session_token: 'newSessionToken',
|
||||
},
|
||||
true,
|
||||
);
|
||||
mergeProfileSection(filePath, 'dev', {
|
||||
aws_access_key_id: 'NEW_KEY',
|
||||
aws_secret_access_key: 'newSecretKey',
|
||||
aws_session_token: 'newSessionToken',
|
||||
}, true);
|
||||
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
const parsed = parseIni(content);
|
||||
@@ -294,27 +266,17 @@ describe('Profile Manager', {}, () => {
|
||||
fs.mkdirSync('/home/runner/.aws', { recursive: true });
|
||||
|
||||
// Create profile with session token
|
||||
mergeProfileSection(
|
||||
filePath,
|
||||
'dev',
|
||||
{
|
||||
aws_access_key_id: 'AKIA',
|
||||
aws_secret_access_key: 'secret',
|
||||
aws_session_token: 'old-token',
|
||||
},
|
||||
false,
|
||||
);
|
||||
mergeProfileSection(filePath, 'dev', {
|
||||
aws_access_key_id: 'AKIA',
|
||||
aws_secret_access_key: 'secret',
|
||||
aws_session_token: 'old-token',
|
||||
}, false);
|
||||
|
||||
// Overwrite without session token
|
||||
mergeProfileSection(
|
||||
filePath,
|
||||
'dev',
|
||||
{
|
||||
aws_access_key_id: 'AKIA2',
|
||||
aws_secret_access_key: 'secret2',
|
||||
},
|
||||
true,
|
||||
);
|
||||
mergeProfileSection(filePath, 'dev', {
|
||||
aws_access_key_id: 'AKIA2',
|
||||
aws_secret_access_key: 'secret2',
|
||||
}, true);
|
||||
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
const parsed = parseIni(content);
|
||||
@@ -329,15 +291,10 @@ describe('Profile Manager', {}, () => {
|
||||
fs.mkdirSync('/home/runner/.aws', { recursive: true });
|
||||
fs.writeFileSync(filePath, '', { mode: 0o600 });
|
||||
|
||||
mergeProfileSection(
|
||||
filePath,
|
||||
'dev',
|
||||
{
|
||||
aws_access_key_id: 'AKIA',
|
||||
aws_secret_access_key: 'secret',
|
||||
},
|
||||
false,
|
||||
);
|
||||
mergeProfileSection(filePath, 'dev', {
|
||||
aws_access_key_id: 'AKIA',
|
||||
aws_secret_access_key: 'secret',
|
||||
}, false);
|
||||
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
const parsed = parseIni(content);
|
||||
@@ -351,31 +308,17 @@ describe('Profile Manager', {}, () => {
|
||||
fs.mkdirSync('/home/runner/.aws', { recursive: true });
|
||||
|
||||
// Create initial profile
|
||||
mergeProfileSection(
|
||||
filePath,
|
||||
'dev',
|
||||
{
|
||||
aws_access_key_id: 'OLD_KEY',
|
||||
aws_secret_access_key: 'oldSecretKey',
|
||||
},
|
||||
false,
|
||||
);
|
||||
mergeProfileSection(filePath, 'dev', {
|
||||
aws_access_key_id: 'OLD_KEY',
|
||||
aws_secret_access_key: 'oldSecretKey',
|
||||
}, false);
|
||||
|
||||
// Overwrite with new credentials
|
||||
expect(() =>
|
||||
mergeProfileSection(
|
||||
filePath,
|
||||
'dev',
|
||||
{
|
||||
aws_access_key_id: 'NEW_KEY',
|
||||
aws_secret_access_key: 'newSecretKey',
|
||||
aws_session_token: 'sessionToken',
|
||||
},
|
||||
false,
|
||||
),
|
||||
).toThrow(
|
||||
`Profile with name "dev" already exists. Please use the overwrite-aws-profile input if you want to overwrite existing profiles.`,
|
||||
);
|
||||
expect(() => mergeProfileSection(filePath, 'dev', {
|
||||
aws_access_key_id: 'NEW_KEY',
|
||||
aws_secret_access_key: 'newSecretKey',
|
||||
aws_session_token: 'sessionToken',
|
||||
}, false)).toThrow(`Profile with name "dev" already exists. Please use the overwrite-aws-profile input if you want to overwrite existing profiles.`);
|
||||
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
const parsed = parseIni(content);
|
||||
@@ -526,7 +469,7 @@ describe('Profile Manager', {}, () => {
|
||||
SecretAccessKey: 'secret',
|
||||
},
|
||||
'us-east-1',
|
||||
false,
|
||||
false
|
||||
),
|
||||
).toThrow('whitespace');
|
||||
});
|
||||
@@ -544,7 +487,7 @@ describe('Profile Manager', {}, () => {
|
||||
SecretAccessKey: 'secret',
|
||||
},
|
||||
'us-east-1',
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
expect(fs.existsSync('/custom/credentials')).toBe(true);
|
||||
@@ -559,7 +502,7 @@ describe('Profile Manager', {}, () => {
|
||||
SecretAccessKey: 'secret',
|
||||
},
|
||||
'us-east-1',
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
expect(core.info).toHaveBeenCalledWith('Writing credentials to profile: dev');
|
||||
@@ -575,7 +518,12 @@ describe('Profile Manager', {}, () => {
|
||||
'[personal]\naws_access_key_id=AKIAPERSONAL\naws_secret_access_key=personalSecret\naws_session_token=personalToken\n',
|
||||
);
|
||||
|
||||
writeProfileFiles('dev', { AccessKeyId: 'AKIADEV', SecretAccessKey: 'devSecret' }, 'us-east-1', false);
|
||||
writeProfileFiles(
|
||||
'dev',
|
||||
{ AccessKeyId: 'AKIADEV', SecretAccessKey: 'devSecret' },
|
||||
'us-east-1',
|
||||
false,
|
||||
);
|
||||
|
||||
const content = fs.readFileSync(credsPath, 'utf-8');
|
||||
const parsed = parseIni(content);
|
||||
@@ -593,9 +541,17 @@ describe('Profile Manager', {}, () => {
|
||||
it('preserves pre-existing config with extra keys', {}, () => {
|
||||
const configPath = getProfileFilePaths().config;
|
||||
fs.mkdirSync(require('node:path').dirname(configPath), { recursive: true });
|
||||
fs.writeFileSync(configPath, '[profile personal]\nregion=eu-west-1\noutput=json\ncli_pager=\n');
|
||||
fs.writeFileSync(
|
||||
configPath,
|
||||
'[profile personal]\nregion=eu-west-1\noutput=json\ncli_pager=\n',
|
||||
);
|
||||
|
||||
writeProfileFiles('dev', { AccessKeyId: 'AKIA', SecretAccessKey: 'secret' }, 'us-east-1', false);
|
||||
writeProfileFiles(
|
||||
'dev',
|
||||
{ AccessKeyId: 'AKIA', SecretAccessKey: 'secret' },
|
||||
'us-east-1',
|
||||
false
|
||||
);
|
||||
|
||||
const content = fs.readFileSync(configPath, 'utf-8');
|
||||
const parsed = parseIni(content);
|
||||
@@ -611,9 +567,17 @@ describe('Profile Manager', {}, () => {
|
||||
it('preserves pre-existing default profile when writing a named profile', {}, () => {
|
||||
const credsPath = getProfileFilePaths().credentials;
|
||||
fs.mkdirSync(require('node:path').dirname(credsPath), { recursive: true });
|
||||
fs.writeFileSync(credsPath, '[default]\naws_access_key_id=AKIADEFAULT\naws_secret_access_key=defaultSecret\n');
|
||||
fs.writeFileSync(
|
||||
credsPath,
|
||||
'[default]\naws_access_key_id=AKIADEFAULT\naws_secret_access_key=defaultSecret\n',
|
||||
);
|
||||
|
||||
writeProfileFiles('dev', { AccessKeyId: 'AKIADEV', SecretAccessKey: 'devSecret' }, 'us-west-2', false);
|
||||
writeProfileFiles(
|
||||
'dev',
|
||||
{ AccessKeyId: 'AKIADEV', SecretAccessKey: 'devSecret' },
|
||||
'us-west-2',
|
||||
false
|
||||
);
|
||||
|
||||
const content = fs.readFileSync(credsPath, 'utf-8');
|
||||
const parsed = parseIni(content);
|
||||
@@ -633,7 +597,12 @@ describe('Profile Manager', {}, () => {
|
||||
'# My important comment\n[personal]\naws_access_key_id=AKIA\naws_secret_access_key=secret\n',
|
||||
);
|
||||
|
||||
writeProfileFiles('dev', { AccessKeyId: 'AKIADEV', SecretAccessKey: 'devSecret' }, 'us-east-1', false);
|
||||
writeProfileFiles(
|
||||
'dev',
|
||||
{ AccessKeyId: 'AKIADEV', SecretAccessKey: 'devSecret' },
|
||||
'us-east-1',
|
||||
false
|
||||
);
|
||||
|
||||
const content = fs.readFileSync(credsPath, 'utf-8') as string;
|
||||
|
||||
@@ -667,7 +636,12 @@ describe('Profile Manager', {}, () => {
|
||||
|
||||
fs.mkdirSync('/custom-creds', { recursive: true });
|
||||
|
||||
writeProfileFiles('dev', { AccessKeyId: 'AKIA', SecretAccessKey: 'secret' }, 'us-east-1', false);
|
||||
writeProfileFiles(
|
||||
'dev',
|
||||
{ AccessKeyId: 'AKIA', SecretAccessKey: 'secret' },
|
||||
'us-east-1',
|
||||
false
|
||||
);
|
||||
|
||||
expect(fs.existsSync('/custom-creds/credentials')).toBe(true);
|
||||
// Config file should be at the default path (under homedir)
|
||||
@@ -684,7 +658,7 @@ describe('Profile Manager', {}, () => {
|
||||
SessionToken: 'FwoGZXIvYXdzEBYaDEXAMPLE',
|
||||
},
|
||||
'us-east-1',
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
const credsPath = getProfileFilePaths().credentials;
|
||||
@@ -699,20 +673,28 @@ describe('Profile Manager', {}, () => {
|
||||
// - LF line endings, trailing newline
|
||||
expect(credContent).toBe(
|
||||
'[dev]\n' +
|
||||
'aws_access_key_id = AKIAIOSFODNN7EXAMPLE\n' +
|
||||
'aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\n' +
|
||||
'aws_session_token = FwoGZXIvYXdzEBYaDEXAMPLE\n',
|
||||
'aws_access_key_id = AKIAIOSFODNN7EXAMPLE\n' +
|
||||
'aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\n' +
|
||||
'aws_session_token = FwoGZXIvYXdzEBYaDEXAMPLE\n',
|
||||
);
|
||||
expect(configContent).toBe(
|
||||
'[profile dev]\n' +
|
||||
'region = us-east-1\n',
|
||||
);
|
||||
expect(configContent).toBe('[profile dev]\n' + 'region = us-east-1\n');
|
||||
});
|
||||
|
||||
it('golden file for multi-profile output', {}, () => {
|
||||
writeProfileFiles('dev', { AccessKeyId: 'AKIADEV', SecretAccessKey: 'devSecret' }, 'us-east-1', false);
|
||||
writeProfileFiles(
|
||||
'dev',
|
||||
{ AccessKeyId: 'AKIADEV', SecretAccessKey: 'devSecret' },
|
||||
'us-east-1',
|
||||
false
|
||||
);
|
||||
writeProfileFiles(
|
||||
'prod',
|
||||
{ AccessKeyId: 'AKIAPROD', SecretAccessKey: 'prodSecret', SessionToken: 'prodToken' },
|
||||
'us-west-2',
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
const credsPath = getProfileFilePaths().credentials;
|
||||
@@ -723,16 +705,20 @@ describe('Profile Manager', {}, () => {
|
||||
|
||||
expect(credContent).toBe(
|
||||
'[dev]\n' +
|
||||
'aws_access_key_id = AKIADEV\n' +
|
||||
'aws_secret_access_key = devSecret\n' +
|
||||
'\n' +
|
||||
'[prod]\n' +
|
||||
'aws_access_key_id = AKIAPROD\n' +
|
||||
'aws_secret_access_key = prodSecret\n' +
|
||||
'aws_session_token = prodToken\n',
|
||||
'aws_access_key_id = AKIADEV\n' +
|
||||
'aws_secret_access_key = devSecret\n' +
|
||||
'\n' +
|
||||
'[prod]\n' +
|
||||
'aws_access_key_id = AKIAPROD\n' +
|
||||
'aws_secret_access_key = prodSecret\n' +
|
||||
'aws_session_token = prodToken\n',
|
||||
);
|
||||
expect(configContent).toBe(
|
||||
'[profile dev]\n' + 'region = us-east-1\n' + '\n' + '[profile prod]\n' + 'region = us-west-2\n',
|
||||
'[profile dev]\n' +
|
||||
'region = us-east-1\n' +
|
||||
'\n' +
|
||||
'[profile prod]\n' +
|
||||
'region = us-west-2\n',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user