fix: allow kubelet token symlink (#1805)

* Revert "chore: Update dist"

This reverts commit 5548f3441b.

* Revert "chore: document container credentials provider support (and delete transitive tags in AssumeRoleWithWebIdentity) (#1780)"

This reverts commit 77cd089899.

* Revert "chore: replay 6.2 devel changes onto main (#1800)"

This reverts commit 87eb0cf693.

* fix: allow kubelet token symlink

Closes #1804
This commit is contained in:
Tom Keller
2026-05-28 13:25:29 -07:00
committed by GitHub
parent 5548f3441b
commit 217d17914b
21 changed files with 10100 additions and 5953 deletions
+5 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
-57
View File
@@ -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
View File
@@ -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',
);
});
});