From b6f183841d5272d9ed048ae61594734394fa4c5d Mon Sep 17 00:00:00 2001 From: Tom Keller Date: Mon, 25 May 2026 12:14:49 -0700 Subject: [PATCH] Revert "feat: Allow custom session tags to be passed when assuming a role (#1759)" This reverts commit 61f50f630f383628add73c1eab3f1935ba07da2b. --- .gitignore | 1 - README.md | 15 -------- action.yml | 4 +- src/assumeRole.ts | 77 +------------------------------------- src/index.ts | 9 ----- test/index.test.ts | 82 +---------------------------------------- test/mockinputs.test.ts | 40 -------------------- 7 files changed, 3 insertions(+), 225 deletions(-) diff --git a/.gitignore b/.gitignore index 6841d21..bc18168 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -.history node_modules coverage .DS_Store diff --git a/README.md b/README.md index a05eb6f..f53c131 100644 --- a/README.md +++ b/README.md @@ -382,21 +382,6 @@ with: Actor ``` -### Custom session tags - -You can add custom session tags using the `custom-tags` input, which accepts a -JSON object. Custom tags cannot override the default tags listed above. - -```yaml -uses: aws-actions/configure-aws-credentials@v6 -with: - custom-tags: '{"Environment": "Production", "Team": "Platform"}' -``` - -_Note: custom tags are not supported when using OIDC or web identity token -authentication. In those flows, session tags are controlled by the identity -provider's token claims._ - ### Session policies Session policies are not required, but they allow you to limit the scope of the diff --git a/action.yml b/action.yml index 1228533..e6448c7 100644 --- a/action.yml +++ b/action.yml @@ -1,3 +1,4 @@ + name: '"Configure AWS Credentials" Action for GitHub Actions' description: Configures AWS credentials for use in subsequent steps in a GitHub Action workflow runs: @@ -100,9 +101,6 @@ inputs: action-timeout-s: required: false description: A global timeout in seconds for the action. When the timeout is reached, the action immediately exits. The default is to run without a timeout. - custom-tags: - description: Additional tags to apply to the assumed role session. Must be a JSON object provided as a string. - required: false outputs: aws-account-id: diff --git a/src/assumeRole.ts b/src/assumeRole.ts index a0fd61c..879d81c 100644 --- a/src/assumeRole.ts +++ b/src/assumeRole.ts @@ -78,73 +78,6 @@ export interface assumeRoleParams { webIdentityToken?: string; inlineSessionPolicy?: string; managedSessionPolicies?: { arn: string }[]; - customTags?: string; -} - -const TAG_KEY_REGEX = /^[\p{L}\p{Z}\p{N}_.:/=+\-@]+$/u; -const TAG_VALUE_REGEX = /^[\p{L}\p{Z}\p{N}_.:/=+\-@]*$/u; -const MAX_TAG_KEY_LENGTH = 128; -const MAX_TAG_VALUE_LENGTH = 256; -const MAX_SESSION_TAGS = 50; - -export function parseAndValidateCustomTags(customTags: string, existingTags: Tag[]): Tag[] { - let parsed: unknown; - try { - parsed = JSON.parse(customTags); - } catch { - throw new Error('custom-tags: input is not valid JSON'); - } - - if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) { - throw new Error('custom-tags: input must be a JSON object (not an array or primitive)'); - } - - const reservedKeys = new Set(existingTags.map((tag) => tag.Key)); - const newTags: Tag[] = []; - - for (const [key, value] of Object.entries(parsed)) { - if (typeof value === 'object') { - throw new Error( - `custom-tags: value for key '${key}' must be a string, number, or boolean (not an object or array)`, - ); - } - - const stringValue = String(value); - - if (key.length === 0 || key.length > MAX_TAG_KEY_LENGTH) { - throw new Error(`custom-tags: key '${key}' must be between 1 and ${MAX_TAG_KEY_LENGTH} characters`); - } - if (stringValue.length > MAX_TAG_VALUE_LENGTH) { - throw new Error( - `custom-tags: value for key '${key}' exceeds maximum length of ${MAX_TAG_VALUE_LENGTH} characters`, - ); - } - if (!TAG_KEY_REGEX.test(key)) { - throw new Error( - `custom-tags: key '${key}' contains invalid characters. Allowed: unicode letters, digits, spaces, and _.:/=+-@`, - ); - } - if (stringValue.length > 0 && !TAG_VALUE_REGEX.test(stringValue)) { - throw new Error( - `custom-tags: value for key '${key}' contains invalid characters. Allowed: unicode letters, digits, spaces, and _.:/=+-@`, - ); - } - if (reservedKeys.has(key)) { - throw new Error( - `custom-tags: key '${key}' conflicts with a default session tag set by this action and cannot be overridden`, - ); - } - - newTags.push({ Key: key, Value: stringValue }); - } - - if (existingTags.length + newTags.length > MAX_SESSION_TAGS) { - throw new Error( - `custom-tags: total session tags (${existingTags.length + newTags.length}) would exceed the AWS limit of ${MAX_SESSION_TAGS}`, - ); - } - - return newTags; } export async function assumeRole(params: assumeRoleParams) { @@ -161,7 +94,6 @@ export async function assumeRole(params: assumeRoleParams) { webIdentityToken, inlineSessionPolicy, managedSessionPolicies, - customTags, } = { ...params }; // Load GitHub environment variables @@ -179,24 +111,17 @@ export async function assumeRole(params: assumeRoleParams) { { Key: 'Actor', Value: sanitizeGitHubVariables(GITHUB_ACTOR) }, { Key: 'Commit', Value: GITHUB_SHA }, ]; - if (process.env.GITHUB_REF) { tagArray.push({ Key: 'Branch', Value: sanitizeGitHubVariables(process.env.GITHUB_REF), }); } - - if (customTags) { - const parsed = parseAndValidateCustomTags(customTags, tagArray); - tagArray.push(...parsed); - } - const tags = roleSkipSessionTagging ? undefined : tagArray; if (!tags) { core.debug('Role session tagging has been skipped.'); } else { - core.debug(`${tags.length} role session tags are being used:`); + core.debug(`${tags.length} role session tags are being used.`); } //only populate transitiveTagKeys array if user is actually using session tagging diff --git a/src/index.ts b/src/index.ts index b7e5c9d..5ea2a32 100644 --- a/src/index.ts +++ b/src/index.ts @@ -43,7 +43,6 @@ export async function run() { const roleSkipSessionTagging = getBooleanInput('role-skip-session-tagging', { required: false }); const transitiveTagKeys = core.getMultilineInput('transitive-tag-keys', { required: false }); const proxyServer = core.getInput('http-proxy', { required: false }) || process.env.HTTP_PROXY; - const customTags = core.getInput('custom-tags', { required: false }); const inlineSessionPolicy = core.getInput('inline-session-policy', { required: false }); const managedSessionPolicies = core.getMultilineInput('managed-session-policies', { required: false }).map((p) => { return { arn: p }; @@ -191,13 +190,6 @@ export async function run() { await credentialsClient.validateCredentials(AccessKeyId, roleChaining, expectedAccountIds); sourceAccountId = await exportAccountId(credentialsClient, maskAccountId); } - if (customTags && (useGitHubOIDCProvider() || webIdentityTokenFile)) { - core.warning( - "'custom-tags' is set but will be ignored because session tags cannot be applied when using OIDC or web identity token authentication. " + - 'Tags are controlled by the identity provider token claims in these authentication flows.', - ); - } - // Get role credentials if configured to do so if (roleToAssume) { let roleCredentials: AssumeRoleCommandOutput; @@ -217,7 +209,6 @@ export async function run() { webIdentityToken, inlineSessionPolicy, managedSessionPolicies, - customTags, }); }, !disableRetry, diff --git a/test/index.test.ts b/test/index.test.ts index 84e25a1..f27ee13 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -238,85 +238,6 @@ describe('Configure AWS Credentials', {}, () => { }); }); - describe('Custom Tags', {}, () => { - beforeEach(() => { - mockedSTSClient.on(AssumeRoleCommand).resolvesOnce(mocks.outputs.STS_CREDENTIALS); - mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY }); - // biome-ignore lint/suspicious/noExplicitAny: any required to mock private method - vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials') - .mockResolvedValueOnce({ accessKeyId: 'MYAWSACCESSKEYID' }) - .mockResolvedValueOnce({ accessKeyId: 'STSAWSACCESSKEYID' }); - }); - it('rejects invalid JSON in custom tags', {}, async () => { - vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.CUSTOM_TAGS_INVALID_JSON_INPUTS)); - await run(); - expect(core.setFailed).toHaveBeenCalledWith('custom-tags: input is not valid JSON'); - expect(mockedSTSClient.commandCalls(AssumeRoleCommand)).toHaveLength(0); - }); - it('handles valid custom tags', {}, async () => { - vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.CUSTOM_TAGS_OBJECT_INPUTS)); - await run(); - expect(core.info).toHaveBeenCalledWith('Assuming role with user credentials'); - expect(core.info).toHaveBeenCalledWith('Authenticated as assumedRoleId AROAFAKEASSUMEDROLEID'); - expect(mockedSTSClient.commandCalls(AssumeRoleCommand)[0].args[0].input).toMatchObject({ - Tags: expect.arrayContaining([ - { Key: 'GitHub', Value: 'Actions' }, - { Key: 'Repository', Value: 'MY-REPOSITORY-NAME' }, - { Key: 'Workflow', Value: 'MY-WORKFLOW-ID' }, - { Key: 'Action', Value: 'MY-ACTION-NAME' }, - { Key: 'Actor', Value: 'MY-USERNAME_bot_' }, - { Key: 'Commit', Value: 'MY-COMMIT-ID' }, - { Key: 'Environment', Value: 'Production' }, - { Key: 'Team', Value: 'DevOps' }, - ]), - }); - }); - it('rejects array input for custom tags', {}, async () => { - vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.CUSTOM_TAGS_ARRAY_INPUTS)); - await run(); - expect(core.setFailed).toHaveBeenCalledWith( - 'custom-tags: input must be a JSON object (not an array or primitive)', - ); - expect(mockedSTSClient.commandCalls(AssumeRoleCommand)).toHaveLength(0); - }); - it('rejects custom tags that conflict with default session tags', {}, async () => { - vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.CUSTOM_TAGS_RESERVED_KEY_INPUTS)); - await run(); - expect(core.setFailed).toHaveBeenCalledWith( - "custom-tags: key 'Repository' conflicts with a default session tag set by this action and cannot be overridden", - ); - expect(mockedSTSClient.commandCalls(AssumeRoleCommand)).toHaveLength(0); - }); - it('rejects custom tags with invalid key characters', {}, async () => { - vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.CUSTOM_TAGS_INVALID_KEY_CHARS_INPUTS)); - await run(); - expect(core.setFailed).toHaveBeenCalledWith( - expect.stringContaining("custom-tags: key 'invalid{key}' contains invalid characters"), - ); - expect(mockedSTSClient.commandCalls(AssumeRoleCommand)).toHaveLength(0); - }); - it('warns when custom tags are used with OIDC', {}, async () => { - vi.mocked(core.getInput).mockImplementation( - mocks.getInput({ - ...mocks.GH_OIDC_INPUTS, - 'custom-tags': JSON.stringify({ MyTag: 'value' }), - }), - ); - vi.mocked(core.getIDToken).mockResolvedValue('testoidctoken'); - mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolvesOnce(mocks.outputs.STS_CREDENTIALS); - mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY }); - // biome-ignore lint/suspicious/noExplicitAny: any required to mock private method - vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValue({ - accessKeyId: 'STSAWSACCESSKEYID', - }); - process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token'; - await run(); - expect(core.warning).toHaveBeenCalledWith( - expect.stringContaining("'custom-tags' is set but will be ignored"), - ); - }); - }); - describe('Odd inputs', {}, () => { it('fails when github env vars are missing', {}, async () => { vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.IAM_USER_INPUTS)); @@ -348,7 +269,6 @@ describe('Configure AWS Credentials', {}, () => { await run(); expect(core.setFailed).toHaveBeenCalled(); }); - it('handles improper retry-max-attempts input', {}, async () => { // This should mean we retry one time vi.mocked(core.getInput).mockImplementation( @@ -792,7 +712,7 @@ describe('Configure AWS Credentials', {}, () => { // Get the timeout callback function const timeoutCallback = setTimeoutSpy.mock.calls[0][0] as () => void; - + // Execute the timeout callback timeoutCallback(); diff --git a/test/mockinputs.test.ts b/test/mockinputs.test.ts index a8bdd2a..1caa1fe 100644 --- a/test/mockinputs.test.ts +++ b/test/mockinputs.test.ts @@ -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',