* chore(deps-dev): bump vitest from 3.2.4 to 4.1.5 (#1748) * chore(deps-dev): bump vitest from 3.2.4 to 4.1.5 Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 3.2.4 to 4.1.5. - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.5/packages/vitest) --- updated-dependencies: - dependency-name: vitest dependency-version: 4.1.5 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * chore(deps-dev): update @vitest/coverage-v8 Bump @vitest/coverage-v8 to ^4.1.5 to match peer dependency, and move vi.mock('node:fs') calls to the top level of test files to reflect actual hoisting semantics required by vitest 4.x. --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tom Keller <kellertk@amazon.com> (cherry picked from commit78f374f6d1) * chore(deps): bump @actions/core from 2.0.3 to 3.0.1 (#1746) * chore(deps): bump @actions/core from 2.0.3 to 3.0.1 Bumps [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core) from 2.0.3 to 3.0.1. - [Changelog](https://github.com/actions/toolkit/blob/main/packages/core/RELEASES.md) - [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/core) --- updated-dependencies: - dependency-name: "@actions/core" dependency-version: 3.0.1 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * chore: update test mocks for @actions/core ESM @actions/core v3 ships as an ESM module with non-configurable exports, breaking vi.spyOn(). Switch to vi.mock('@actions/core') which intercepts at the module loader level. --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tom Keller <kellertk@amazon.com> (cherry picked from commit64d8e82527) * chore(deps): bump @aws-sdk/client-sts from 3.1043.0 to 3.1044.0 (#1754) Bumps [@aws-sdk/client-sts](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-sts) from 3.1043.0 to 3.1044.0. - [Release notes](https://github.com/aws/aws-sdk-js-v3/releases) - [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-sts/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1044.0/clients/client-sts) --- updated-dependencies: - dependency-name: "@aws-sdk/client-sts" dependency-version: 3.1042.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> (cherry picked from commit4cfda40a13) * chore: bump unit test node version (#1758) (cherry picked from commit39d1702721) * chore: automatic major version tagging (#1565) * Update release-please.yml to auto-update version tag * chore: configure release-please auto floating tag --------- Co-authored-by: Tom Keller <kellertk@amazon.com> (cherry picked from commitc36525a567) * feat: Allow custom session tags to be passed when assuming a role (#1759) * Add possibility to input custom session tags * Use json for input to custom-tags, add documentation for custom-tags * Add more examples * Simplify example to avoid parse error * Add input validation for custom tags * Fix unit tests for custom-tags * Add debugging message * Skip failing test for now * Build package * Remove some unused validation for custom tags * feat: add validation for custom session tags Harden the custom-tags feature against misuse and misconfiguration: - Validate input is a JSON object (reject arrays, primitives, null) - Enforce STS tag constraints: key length (128), value length (256), allowed characters - Reject nested object/array values that would silently stringify to '[object Object]' - Block overriding default session tags (GitHub, Repository, Workflow, etc.) - Enforce 50-tag session limit - Warn when custom-tags used with OIDC or web identity - Fix missing await on helpers test assertion - Remove unused CUSTOM_TAGS_JSON_INPUTS fixture - Normalize test mocking to vi.mocked() pattern --------- Co-authored-by: Sylvain Verly <sylvain.verly@gmail.com> (cherry picked from commit61f50f630f) * chore: configure codeql to ignore generated code (#1760) (cherry picked from commitdc2353e57a) * feat: support custom STS endpoints (#1762) Closes #1067. This is a advanced option and is not needed for most deployments. (cherry picked from commit8d52d05d7a) * chore: automate README version bumping (#1763) Closes #1420. (cherry picked from commit07ada0fe07) * feat: add more retry logic and better logging (#1764) Wraps exportAccountId and validateCredentials calls in retryAndBackoff. Closes #1681. Adds a label parameter to retryAndBackoff for better info-level log messages. (cherry picked from commit540d0c13ae) * feat: add regex validation to role-session-name (#1765) Previously invalid role session names would get errors from the STS API instead of this action rejecting them, causing unnecessary retries. Now we check them and fail early. Closes #1656. That FR recommended that we sanitize the name before sending to STS, but instead we error to not silently change the user's selected session name (avoiding the potential security sharp edge) (cherry picked from commite35449909c) * chore: update documentation for environment workflows (#1766) Closes #1238. (cherry picked from commit3f7e1b63d7) * chore(deps): bump @aws-sdk/client-sts from 3.1044.0 to 3.1045.0 (#1767) Bumps [@aws-sdk/client-sts](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-sts) from 3.1044.0 to 3.1045.0. - [Release notes](https://github.com/aws/aws-sdk-js-v3/releases) - [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-sts/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1045.0/clients/client-sts) --- updated-dependencies: - dependency-name: "@aws-sdk/client-sts" dependency-version: 3.1045.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> (cherry picked from commita388f23f7d) * chore(deps-dev): bump @vitest/coverage-v8 from 4.1.5 to 4.1.6 (#1768) Bumps [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8) from 4.1.5 to 4.1.6. - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.6/packages/coverage-v8) --- updated-dependencies: - dependency-name: "@vitest/coverage-v8" dependency-version: 4.1.6 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> (cherry picked from commit1fb495c4b2) * chore(deps-dev): bump @smithy/property-provider from 4.2.14 to 4.3.1 (#1771) Bumps [@smithy/property-provider](https://github.com/smithy-lang/smithy-typescript/tree/HEAD/packages/property-provider) from 4.2.14 to 4.3.1. - [Release notes](https://github.com/smithy-lang/smithy-typescript/releases) - [Changelog](https://github.com/smithy-lang/smithy-typescript/blob/main/packages/property-provider/CHANGELOG.md) - [Commits](https://github.com/smithy-lang/smithy-typescript/commits/@smithy/property-provider@4.3.1/packages/property-provider) --- updated-dependencies: - dependency-name: "@smithy/property-provider" dependency-version: 4.3.1 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> (cherry picked from commit1ab31502aa) * chore(deps): bump @smithy/node-http-handler from 4.6.1 to 4.7.1 (#1770) Bumps [@smithy/node-http-handler](https://github.com/smithy-lang/smithy-typescript/tree/HEAD/packages/node-http-handler) from 4.6.1 to 4.7.1. - [Release notes](https://github.com/smithy-lang/smithy-typescript/releases) - [Changelog](https://github.com/smithy-lang/smithy-typescript/blob/main/packages/node-http-handler/CHANGELOG.md) - [Commits](https://github.com/smithy-lang/smithy-typescript/commits/@smithy/node-http-handler@4.7.1/packages/node-http-handler) --- updated-dependencies: - dependency-name: "@smithy/node-http-handler" dependency-version: 4.7.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> (cherry picked from commitdbd503f368) * chore(deps-dev): bump @biomejs/biome from 2.4.14 to 2.4.15 (#1772) Bumps [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) from 2.4.14 to 2.4.15. - [Release notes](https://github.com/biomejs/biome/releases) - [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md) - [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.4.15/packages/@biomejs/biome) --- updated-dependencies: - dependency-name: "@biomejs/biome" dependency-version: 2.4.15 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> (cherry picked from commit7521c55910) * chore(deps-dev): bump @types/node from 25.6.0 to 25.7.0 (#1773) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 25.6.0 to 25.7.0. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 25.7.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> (cherry picked from commitef734cca81) * feat: expose run id in STS client user-agent (#1774) * feat: expose run id in STS client user-agent Closes #483. This commit modifies the user-agent string so that it includes the GITHUB_RUN_ID and the GITHUB_RUN_ATTEMPT, in the format typically used by the SDK. User agent strings are logged to CloudTrail, allowing users to correlate CloudTrail events with GHA runs. We took this approach instead of logging the ACCESS_KEY_ID as suggested in the issue to avoid logging sensitive information. * feat: add github_action to ua string (cherry picked from commit29d1be3027) * feat: add additional session tags by default (#1775) Closes #390. Note that 50 session tags are the AWS default, and this commit changes our default set from 7 tags to 15 tags. This commit includes logic to split the tags into "required" vs "overridable". Required tags are this action's previous defaults and could never be overridden. Overridable tags are the new set and can be overridden by custom-tags. The action will not add tags if the addition plus the required plus the user's custom tags exceed the AWS limit of 50 total tags. This ensures backwards compat for the tag additions. (cherry picked from commite0ba768507) * chore: document forgejo compatibility (#1776) * chore: document forgejo compatibility * chore: linting fixes (cherry picked from commitf35a7d7d7e) * fix: skip credential check on output-env-credentials: false (#1778) Closes #1554. (cherry picked from commit58e7c47adf) * chore: update README for additional claim support (#1779) * chore: update README for additional claim support * chore: lint fix (whitespace) (cherry picked from commit713aaabfec) * chore(deps): bump @smithy/node-http-handler from 4.7.1 to 4.7.3 (#1781) Bumps [@smithy/node-http-handler](https://github.com/smithy-lang/smithy-typescript/tree/HEAD/packages/node-http-handler) from 4.7.1 to 4.7.3. - [Release notes](https://github.com/smithy-lang/smithy-typescript/releases) - [Changelog](https://github.com/smithy-lang/smithy-typescript/blob/main/packages/node-http-handler/CHANGELOG.md) - [Commits](https://github.com/smithy-lang/smithy-typescript/commits/@smithy/node-http-handler@4.7.3/packages/node-http-handler) --- updated-dependencies: - dependency-name: "@smithy/node-http-handler" dependency-version: 4.7.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> (cherry picked from commita7c33ae483) * chore(deps-dev): bump @types/node from 25.7.0 to 25.9.0 (#1785) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 25.7.0 to 25.9.0. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 25.9.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> (cherry picked from commitffde832a1d) * chore(deps-dev): bump @aws-sdk/credential-provider-env (#1784) Bumps [@aws-sdk/credential-provider-env](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/packages-internal/credential-provider-env) from 3.972.34 to 3.972.38. - [Release notes](https://github.com/aws/aws-sdk-js-v3/releases) - [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/packages-internal/credential-provider-env/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/HEAD/packages-internal/credential-provider-env) --- updated-dependencies: - dependency-name: "@aws-sdk/credential-provider-env" dependency-version: 3.972.38 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> (cherry picked from commitbc1093db1d) * chore(deps-dev): bump @smithy/property-provider from 4.3.1 to 4.3.3 (#1783) Bumps [@smithy/property-provider](https://github.com/smithy-lang/smithy-typescript/tree/HEAD/packages/property-provider) from 4.3.1 to 4.3.3. - [Release notes](https://github.com/smithy-lang/smithy-typescript/releases) - [Changelog](https://github.com/smithy-lang/smithy-typescript/blob/main/packages/property-provider/CHANGELOG.md) - [Commits](https://github.com/smithy-lang/smithy-typescript/commits/@smithy/property-provider@4.3.3/packages/property-provider) --- updated-dependencies: - dependency-name: "@smithy/property-provider" dependency-version: 4.3.3 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> (cherry picked from commitfe6ad3af19) * chore(deps): bump @aws-sdk/client-sts from 3.1045.0 to 3.1049.0 (#1782) Bumps [@aws-sdk/client-sts](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-sts) from 3.1045.0 to 3.1049.0. - [Release notes](https://github.com/aws/aws-sdk-js-v3/releases) - [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-sts/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1049.0/clients/client-sts) --- updated-dependencies: - dependency-name: "@aws-sdk/client-sts" dependency-version: 3.1049.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> (cherry picked from commit4684f47f89) * chore: reconcile lockfile and test formatting * chore: Update dist --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Michael Lehmann <lehmanmj@amazon.com> Co-authored-by: Sylvain Verly <sylvain.verly@gmail.com>
267 lines
12 KiB
TypeScript
267 lines
12 KiB
TypeScript
import * as core from '@actions/core';
|
|
import { fs, vol } from 'memfs';
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import * as helpers from '../src/helpers';
|
|
|
|
vi.mock('node:fs');
|
|
vi.mock('@actions/core');
|
|
|
|
describe('Configure AWS Credentials helpers', {}, () => {
|
|
beforeEach(() => {
|
|
vi.restoreAllMocks();
|
|
vi.clearAllMocks();
|
|
vol.reset();
|
|
});
|
|
it('removes brackets from GitHub Actor', {}, () => {
|
|
const actor = 'actor[bot]';
|
|
expect(helpers.sanitizeGitHubVariables(actor)).toBe('actor_bot_');
|
|
});
|
|
it('can sleep', {}, async () => {
|
|
const sleep = helpers.defaultSleep(10);
|
|
await expect(Promise.race([sleep, new Promise((_, reject) => setTimeout(reject, 20))])).resolves.toBe(undefined);
|
|
});
|
|
it('removes special characters from workflow names', {}, () => {
|
|
expect(helpers.sanitizeGitHubVariables('sdf234@#$%$^&*()_+{}|:"<>?')).toEqual('sdf234@__________+___:____');
|
|
});
|
|
it("doesn't retry non-retryable errors", {}, async () => {
|
|
const fn = vi.fn().mockRejectedValue('i am not retryable');
|
|
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(() => {});
|
|
vi.spyOn(core, 'exportVariable').mockImplementation(() => {});
|
|
helpers.exportCredentials(
|
|
{ AccessKeyId: 'test', SecretAccessKey: 'test', SessionToken: 'test', Expiration: new Date(8640000000000000) },
|
|
true,
|
|
true,
|
|
);
|
|
expect(core.setOutput).toHaveBeenCalledTimes(4);
|
|
expect(core.setSecret).toHaveBeenCalledTimes(3);
|
|
expect(core.exportVariable).toHaveBeenCalledTimes(3);
|
|
});
|
|
it('can unset credentials', {}, () => {
|
|
const env = process.env;
|
|
helpers.unsetCredentials();
|
|
expect(process.env.AWS_ACCESS_KEY_ID).toBeUndefined;
|
|
expect(process.env.AWS_SECRET_ACCESS_KEY).toBeUndefined;
|
|
expect(process.env.AWS_SESSION_TOKEN).toBeUndefined;
|
|
expect(process.env.AWS_REGION).toBeUndefined;
|
|
expect(process.env.AWS_DEFAULT_REGION).toBeUndefined;
|
|
process.env = env;
|
|
});
|
|
it(`won't output credentials to env if told not to`, {}, () => {
|
|
vi.spyOn(core, 'setOutput').mockImplementation(() => {});
|
|
vi.spyOn(core, 'setSecret').mockImplementation(() => {});
|
|
vi.spyOn(core, 'exportVariable').mockImplementation(() => {});
|
|
helpers.exportCredentials(
|
|
{ AccessKeyId: 'test', SecretAccessKey: 'test', SessionToken: 'test', Expiration: new Date(8640000000000000) },
|
|
true,
|
|
false,
|
|
);
|
|
helpers.unsetCredentials(false);
|
|
helpers.exportRegion('fake-test-region', false);
|
|
expect(core.setOutput).toHaveBeenCalledTimes(4);
|
|
expect(core.setSecret).toHaveBeenCalledTimes(3);
|
|
expect(core.exportVariable).toHaveBeenCalledTimes(0);
|
|
});
|
|
|
|
it('verifies credentials without special characters', {}, () => {
|
|
expect(helpers.verifyKeys({ AccessKeyId: 'AKIATEST', SecretAccessKey: 'secretkey' })).toBe(true);
|
|
expect(helpers.verifyKeys({ AccessKeyId: 'AKIA!@#$', SecretAccessKey: 'secret' })).toBe(false);
|
|
expect(helpers.verifyKeys(undefined)).toBe(false);
|
|
});
|
|
|
|
it('translates environment variables', {}, () => {
|
|
process.env.AWS_REGION = 'us-east-1';
|
|
process.env.HTTPS_PROXY = 'https://proxy:8080';
|
|
helpers.translateEnvVariables();
|
|
expect(process.env['INPUT_AWS-REGION']).toBe('us-east-1');
|
|
expect(process.env.HTTP_PROXY).toBe('https://proxy:8080');
|
|
});
|
|
|
|
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();
|
|
});
|
|
|
|
it('clears session token when not provided', {}, () => {
|
|
vi.spyOn(core, 'setSecret').mockImplementation(() => {});
|
|
vi.spyOn(core, 'exportVariable').mockImplementation(() => {});
|
|
process.env.AWS_SESSION_TOKEN = 'old-token';
|
|
helpers.exportCredentials({ AccessKeyId: 'test', SecretAccessKey: 'test' }, false, true);
|
|
expect(core.exportVariable).toHaveBeenCalledWith('AWS_SESSION_TOKEN', '');
|
|
});
|
|
|
|
describe('filesystem helpers', {}, () => {
|
|
describe('isSymlink', {}, () => {
|
|
it('returns true for a symlink', {}, () => {
|
|
fs.mkdirSync('/dir', { recursive: true });
|
|
fs.writeFileSync('/dir/target', 'data');
|
|
fs.symlinkSync('/dir/target', '/dir/link');
|
|
expect(helpers.isSymlink('/dir/link')).toBe(true);
|
|
});
|
|
|
|
it('returns false for a regular file', {}, () => {
|
|
fs.mkdirSync('/dir', { recursive: true });
|
|
fs.writeFileSync('/dir/file', 'data');
|
|
expect(helpers.isSymlink('/dir/file')).toBe(false);
|
|
});
|
|
|
|
it('returns false for a missing path', {}, () => {
|
|
expect(helpers.isSymlink('/nonexistent')).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('readFileUtf8', {}, () => {
|
|
it('returns content for a regular file', {}, () => {
|
|
fs.mkdirSync('/dir', { recursive: true });
|
|
fs.writeFileSync('/dir/file', 'hello');
|
|
expect(helpers.readFileUtf8('/dir/file')).toBe('hello');
|
|
});
|
|
|
|
it('returns null when the file does not exist', {}, () => {
|
|
fs.mkdirSync('/dir', { recursive: true });
|
|
expect(helpers.readFileUtf8('/dir/missing')).toBe(null);
|
|
});
|
|
|
|
it('refuses to read through a symlink at the target', {}, () => {
|
|
fs.mkdirSync('/dir', { recursive: true });
|
|
fs.writeFileSync('/dir/secret', 'sensitive');
|
|
fs.symlinkSync('/dir/secret', '/dir/link');
|
|
expect(() => helpers.readFileUtf8('/dir/link')).toThrow(/Refusing .* \(.* symbolic link\)/);
|
|
});
|
|
|
|
it('refuses to read when the parent directory is a symlink', {}, () => {
|
|
fs.mkdirSync('/real/.aws', { recursive: true });
|
|
fs.writeFileSync('/real/.aws/credentials', 'data');
|
|
fs.mkdirSync('/home', { recursive: true });
|
|
fs.symlinkSync('/real/.aws', '/home/.aws');
|
|
expect(() => helpers.readFileUtf8('/home/.aws/credentials')).toThrow(/Refusing .* \(.* symbolic link\)/);
|
|
});
|
|
|
|
it('refuses to read when the path is a directory', {}, () => {
|
|
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', {}, () => {
|
|
it('writes content with the specified mode', {}, () => {
|
|
fs.mkdirSync('/dir', { recursive: true });
|
|
helpers.writeFileUtf8('/dir/file', 'payload', 0o600);
|
|
expect(fs.readFileSync('/dir/file', 'utf-8')).toBe('payload');
|
|
expect(fs.statSync('/dir/file').mode & 0o777).toBe(0o600);
|
|
});
|
|
|
|
it('refuses to follow a symlink at the target and leaves the target file untouched', {}, () => {
|
|
fs.mkdirSync('/dir', { recursive: true });
|
|
fs.writeFileSync('/dir/target', 'original');
|
|
fs.symlinkSync('/dir/target', '/dir/link');
|
|
expect(() => helpers.writeFileUtf8('/dir/link', 'attacker', 0o600)).toThrow(/Refusing .* \(.* symbolic link\)/);
|
|
expect(fs.readFileSync('/dir/target', 'utf-8')).toBe('original');
|
|
});
|
|
|
|
it.skipIf(process.platform === 'win32')('tightens mode on existing files', () => {
|
|
fs.mkdirSync('/dir', { recursive: true });
|
|
fs.writeFileSync('/dir/file', 'old', { mode: 0o644 });
|
|
helpers.writeFileUtf8('/dir/file', 'new', 0o600);
|
|
expect(fs.statSync('/dir/file').mode & 0o777).toBe(0o600);
|
|
});
|
|
});
|
|
|
|
describe('mkdir', {}, () => {
|
|
it('is idempotent on a regular directory', {}, () => {
|
|
helpers.mkdir('/some/nested/dir', 0o700);
|
|
helpers.mkdir('/some/nested/dir', 0o700);
|
|
expect(fs.statSync('/some/nested/dir').isDirectory()).toBe(true);
|
|
});
|
|
|
|
it('refuses when the target directory is a symlink', {}, () => {
|
|
fs.mkdirSync('/real', { recursive: true });
|
|
fs.mkdirSync('/home', { recursive: true });
|
|
fs.symlinkSync('/real', '/home/.aws');
|
|
expect(() => helpers.mkdir('/home/.aws', 0o700)).toThrow(/Refusing .* \(.* symbolic link\)/);
|
|
});
|
|
});
|
|
});
|
|
});
|