From d6f5dc331b44474b19a52caaf85fa4d637b13c8e Mon Sep 17 00:00:00 2001 From: Michael Lehmann Date: Mon, 1 Jun 2026 13:55:23 -0700 Subject: [PATCH] fix: assumeRole failing from session tag size too large (#1808) * minor readme fixes and print tags when debugging * redo changes from #1780 * reworked new tags/priority behavior, add drop tags and retry logic * minor edits to documentation, README update for clarity * linting fix --- README.md | 83 +++++++++++++++++------- src/assumeRole.ts | 56 ++++++++++------- test/index.test.ts | 136 +++++++++++++++------------------------- test/mockinputs.test.ts | 2 - 4 files changed, 143 insertions(+), 134 deletions(-) diff --git a/README.md b/README.md index b67698c..09f4b69 100644 --- a/README.md +++ b/README.md @@ -168,6 +168,7 @@ detail. | role-session-name | Defaults to "GitHubActions", but may be changed if required. | No | | role-skip-session-tagging | Skips session tagging if set. | No | | transitive-tag-keys | Define a list of transitive tag keys to pass when assuming a role. | No | +| custom-tags | Additional tags to apply to the assumed role session. Must be a JSON object provided as a string. Custom tags are not usable with OIDC or web identity token authentication. | No | | inline-session-policy | You may further restrict the assumed role policy by defining an inline policy here. | No | | managed-session-policies | You may further restrict the assumed role policy by specifying a managed policy here. | No | | output-credentials | When set, outputs fetched credentials as action step output. (Outputs aws-access-key-id, aws-secret-access-key, aws-session-token, aws-account-id, authenticated-arn, and aws-expiration). Defaults to false. | No | @@ -180,6 +181,8 @@ detail. | allowed-account-ids | A comma-delimited list of expected AWS account IDs. The action will fail if we receive credentials for the wrong account. | No | | force-skip-oidc | When set, the action will skip using GitHub OIDC provider even if the id-token permission is set. | No | | action-timeout-s | Global timeout for the action in seconds. If set to a value greater than 0, the action will fail if it takes longer than this time to complete. | No | +| no-proxy | Hosts to skip for the proxy configuration. | No | +| sts-endpoint | Custom STS endpoint URL. Use this to point to an STS-compatible API (e.g. MinIO, LocalStack) instead of the default AWS STS endpoint for the region. | No | @@ -350,8 +353,7 @@ documentation for `GITHUB_` environment variable definitions][gh-env-vars]) [gh-env-vars]: https://docs.github.com/en/actions/reference/workflows-and-actions/variables#default-environment-variables -**Protected tags** are always emitted when session tags are used, and cannot be -overridden via `custom-tags`: +**Default tags** are always emitted when session tags are used. | Key | Value | | ---------- | ----------------- | @@ -363,21 +365,24 @@ overridden via `custom-tags`: | Commit | GITHUB_SHA | | Branch | GITHUB_REF | -**Overrideable tags** are automatically added to the set of default session tags -but may be overridden via `custom-tags`. AWS has a maximum limit of 50 session -tags; tags from this list are dropped in reverse priority order if your -`custom-tags` set plus the protected set exceeds this limit. +**Droppable tags** are automatically added to the set of default session tags. +If the session tags exceed the [packed size limit][packed-size-limit], these +tags will be dropped, and the AssumeRole call will be retried. If it still +fails, the action will error out. (It is difficult to predict the packed size +before making the call, as session tags and session policies are compressed into +a binary format as part of the call.) -| Key | Value | Priority | -| --------------- | ----------------------- | -------- | -| EventName | GITHUB_EVENT_NAME | 1 | -| BaseRef | GITHUB_BASE_REF | 2 | -| HeadRef | GITHUB_HEAD_REF | 3 | -| RefName | GITHUB_REF_NAME | 4 | -| RunId | GITHUB_RUN_ID | 5 | -| RefType | GITHUB_REF_TYPE | 6 | -| Job | GITHUB_JOB | 7 | -| TriggeringActor | GITHUB_TRIGGERING_ACTOR | 8 | +[packed-size-limit]: + https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_know + +| Key | Value | +| --------------- | ----------------------- | +| EventName | GITHUB_EVENT_NAME | +| BaseRef | GITHUB_BASE_REF | +| HeadRef | GITHUB_HEAD_REF | +| RunId | GITHUB_RUN_ID | +| Job | GITHUB_JOB | +| TriggeringActor | GITHUB_TRIGGERING_ACTOR | Tags whose source environment variable is unset are omitted (e.g., `BaseRef` and `HeadRef` are only set on `pull_request` events). @@ -385,21 +390,21 @@ Tags whose source environment variable is unset are omitted (e.g., `BaseRef` and _Note: all tag values must conform to [the tag requirements][sts-tag-requirements]. Values longer than 256 characters will be truncated, and characters outside the -allowed set will be replaced with an underscore (`_`).\_ +allowed set will be replaced with an underscore (`_`)._ [sts-tag-requirements]: https://docs.aws.amazon.com/STS/latest/APIReference/API_Tag.html -The action will use session tagging by default unless you are using OIDC. +The action will use session tagging by default unless you are using OIDC or a +Web Identify Token File. To [forward session tags to subsequent sessions in a role -chain][session-tag-chaining], you can use +chain][session-tag-chaining], you can use the `transitive-tag-keys` input to +specify the keys of the tags to be passed. [session-tag-chaining]: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_role-chaining -the `transitive-tag-keys` input to specify the keys of the tags to be passed. - _Note that all subsequent roles in the chain must have `role-skip-session-tagging` set to `true`_ @@ -416,9 +421,10 @@ with: ### Custom session tags You can add custom session tags using the `custom-tags` input, which accepts a -JSON object. Custom tags cannot override protected tags, but they can override -overrideable tags (in which case the overrideable tag's slot is freed for the -next overrideable tag in the priority list, if any). +JSON object. Custom tags cannot override existing tags. Note that AWS allows a +maximum of 50 tags (so you can supply a maximum of 43 custom tags), although it +is likely that you will exceed the [packed size limit][packed-size-limit] +before you exceed the maximum number of tags. ```yaml uses: aws-actions/configure-aws-credentials@v6 @@ -617,6 +623,35 @@ For further information on OIDC and GitHub Actions, please see: - [GitHub docs: Configuring OpenID Connect in Amazon Web Services](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services) - [GitHub changelog: GitHub Actions: Secure cloud deployments with OpenID Connect](https://github.blog/changelog/2021-10-27-github-actions-secure-cloud-deployments-with-openid-connect/) +## Getting Credentials in AWS Self-Hosted Runners + +If you are running GitHub Actions in a self-hosted runner using an AWS Service +(such as Codebuild or EKS) and you have properly configured the service, +credentials should be available by default; the AWS CLI will fetch credentials +using the AWS_CONTAINER_CREDENTIALS_FULL_URI or +AWS_CONTAINER_CREDENTIALS_RELATIVE_URI environment variables. However, you may +still want to use this action if you need to export those credentials for use +with other tools in your workflow. You may also want to use this action in +scenarios where you need to use that 'default' role to assume another role. + +To export credentials, simply run the action with `role-to-assume` set to the +default role of the container. + +To assume another role from the container's default role, use the +`role-chaining: true` flag, so that the action fetches the default credentials +from the environment before assuming the other role. + +If you are using EKS Pod Identities and encountering an error related to the +packed size of session tags, you must either run the action with +`role-skip-session-tagging: true` to disable the tags set by the action, or +[disable EKS session tagging][eks-disable-session-tagging] in the EKS settings +to disable the tags that are automatically set by the EKS Pod Identity Service. +Check the values of the action's session tags and the session tags that are +added by EKS so you can keep the set of tags which is more useful to you. + +[eks-disable-session-tagging]: + https://docs.aws.amazon.com/eks/latest/userguide/pod-id-abac.html#pod-id-abac-tags + ## Compatibility with non-GitHub Actions environments This action has been sucessfully tested with diff --git a/src/assumeRole.ts b/src/assumeRole.ts index ebf27a6..95f6bdc 100644 --- a/src/assumeRole.ts +++ b/src/assumeRole.ts @@ -2,7 +2,11 @@ import assert from 'node:assert'; import path from 'node:path'; import * as core from '@actions/core'; import type { AssumeRoleCommandInput, STSClient, Tag } from '@aws-sdk/client-sts'; -import { AssumeRoleCommand, AssumeRoleWithWebIdentityCommand } from '@aws-sdk/client-sts'; +import { + AssumeRoleCommand, + AssumeRoleWithWebIdentityCommand, + PackedPolicyTooLargeException, +} from '@aws-sdk/client-sts'; import type { CredentialsClient } from './CredentialsClient'; import { errorMessage, isDefined, readFileUtf8, sanitizeGitHubVariables } from './helpers'; @@ -42,6 +46,7 @@ async function assumeRoleWithWebIdentityTokenFile( core.info('Assuming role with web identity token file'); try { delete params.Tags; + delete params.TransitiveTagKeys; const creds = await client.send( new AssumeRoleWithWebIdentityCommand({ ...params, @@ -60,6 +65,13 @@ async function assumeRoleWithCredentials(params: AssumeRoleCommandInput, client: const creds = await client.send(new AssumeRoleCommand({ ...params })); return creds; } catch (error) { + if (error instanceof PackedPolicyTooLargeException) { + core.info('Session tag size is too large; dropping droppable tags and retrying.'); + const droppableKeys = new Set(DROPPABLE_TAG_SOURCES.map((s) => s.key)); + params.Tags = params.Tags?.filter((tag) => !droppableKeys.has(tag.Key ?? '')); + const creds = await client.send(new AssumeRoleCommand({ ...params })); + return creds; + } throw new Error(`Could not assume role with user credentials: ${errorMessage(error)}`); } } @@ -86,8 +98,8 @@ const MAX_TAG_KEY_LENGTH = 128; const MAX_TAG_VALUE_LENGTH = 256; const MAX_SESSION_TAGS = 50; -// Identity/audit primitives. Always emitted and cannot be overridden by custom-tags. -const PROTECTED_TAG_SOURCES: ReadonlyArray<{ key: string; envVar: string }> = [ +// Identity/audit primitives. Always emitted and cannot be dropped. +const NON_DROPPABLE_TAG_SOURCES: ReadonlyArray<{ key: string; envVar: string }> = [ { key: 'Repository', envVar: 'GITHUB_REPOSITORY' }, { key: 'Workflow', envVar: 'GITHUB_WORKFLOW' }, { key: 'Action', envVar: 'GITHUB_ACTION' }, @@ -96,21 +108,22 @@ const PROTECTED_TAG_SOURCES: ReadonlyArray<{ key: string; envVar: string }> = [ { key: 'Branch', envVar: 'GITHUB_REF' }, ]; -// Convenience metadata. Custom-tags may override (suppresses the default for that key). -// Listed in priority order; lower-priority entries are dropped first if the user's custom-tags -// would push the total above MAX_SESSION_TAGS. -const OVERRIDEABLE_TAG_SOURCES_BY_PRIORITY: ReadonlyArray<{ key: string; envVar: string }> = [ +// Convenience metadata. If the AssumeRole call fails due to compressed size of +// session tags being too large, we will drop these tags and retry once. +const DROPPABLE_TAG_SOURCES: ReadonlyArray<{ key: string; envVar: string }> = [ { key: 'EventName', envVar: 'GITHUB_EVENT_NAME' }, { key: 'BaseRef', envVar: 'GITHUB_BASE_REF' }, { key: 'HeadRef', envVar: 'GITHUB_HEAD_REF' }, - { key: 'RefName', envVar: 'GITHUB_REF_NAME' }, { key: 'RunId', envVar: 'GITHUB_RUN_ID' }, - { key: 'RefType', envVar: 'GITHUB_REF_TYPE' }, { key: 'Job', envVar: 'GITHUB_JOB' }, { key: 'TriggeringActor', envVar: 'GITHUB_TRIGGERING_ACTOR' }, ]; -const PROTECTED_TAG_KEYS = new Set(['GitHub', ...PROTECTED_TAG_SOURCES.map((s) => s.key)]); +const PROTECTED_TAG_KEYS = new Set([ + 'GitHub', + ...NON_DROPPABLE_TAG_SOURCES.map((s) => s.key), + ...DROPPABLE_TAG_SOURCES.map((s) => s.key), +]); export function parseAndValidateCustomTags(customTags: string, existingTags: Tag[]): Tag[] { let parsed: unknown; @@ -197,7 +210,13 @@ export async function assumeRole(params: assumeRoleParams) { // Build session tags. Values are sanitized because the AWS tag value spec is more // restrictive than permissible characters in environment variables. const protectedTags: Tag[] = [{ Key: 'GitHub', Value: 'Actions' }]; - for (const { key, envVar } of PROTECTED_TAG_SOURCES) { + for (const { key, envVar } of NON_DROPPABLE_TAG_SOURCES) { + const value = process.env[envVar]; + if (value) { + protectedTags.push({ Key: key, Value: sanitizeGitHubVariables(value) }); + } + } + for (const { key, envVar } of DROPPABLE_TAG_SOURCES) { const value = process.env[envVar]; if (value) { protectedTags.push({ Key: key, Value: sanitizeGitHubVariables(value) }); @@ -205,26 +224,15 @@ export async function assumeRole(params: assumeRoleParams) { } const parsedCustomTags: Tag[] = customTags ? parseAndValidateCustomTags(customTags, protectedTags) : []; - const customTagKeys = new Set(parsedCustomTags.map((t) => t.Key)); - const availableOverrideableSlots = MAX_SESSION_TAGS - protectedTags.length - parsedCustomTags.length; - const overrideableTags: Tag[] = []; - for (const { key, envVar } of OVERRIDEABLE_TAG_SOURCES_BY_PRIORITY) { - if (overrideableTags.length >= availableOverrideableSlots) break; - if (customTagKeys.has(key)) continue; - const value = process.env[envVar]; - if (value) { - overrideableTags.push({ Key: key, Value: sanitizeGitHubVariables(value) }); - } - } - - const tagArray: Tag[] = [...protectedTags, ...overrideableTags, ...parsedCustomTags]; + const tagArray: Tag[] = [...protectedTags, ...parsedCustomTags]; 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(JSON.stringify(tagArray)); } //only populate transitiveTagKeys array if user is actually using session tagging diff --git a/test/index.test.ts b/test/index.test.ts index 3970951..d03181a 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -3,6 +3,7 @@ import { AssumeRoleCommand, AssumeRoleWithWebIdentityCommand, GetCallerIdentityCommand, + PackedPolicyTooLargeException, STSClient, } from '@aws-sdk/client-sts'; import { mockClient } from 'aws-sdk-client-mock'; @@ -202,6 +203,18 @@ describe('Configure AWS Credentials', {}, () => { expect(core.setOutput).toHaveBeenCalledTimes(2); expect(core.setFailed).not.toHaveBeenCalled(); }); + it('does not send Tags or TransitiveTagKeys to AssumeRoleWithWebIdentity', async () => { + // AssumeRoleWithWebIdentity reads session tags from JWT claims, not the request. + // Both fields must be stripped before the STS call. + vi.mocked(core.getMultilineInput).mockImplementation((name: string) => { + if (name === 'transitive-tag-keys') return ['Repository']; + return []; + }); + await run(); + const callInput = mockedSTSClient.commandCalls(AssumeRoleWithWebIdentityCommand)[0].args[0].input; + expect(callInput.Tags).toBeUndefined(); + expect(callInput.TransitiveTagKeys).toBeUndefined(); + }); }); describe('Assume existing role', {}, () => { @@ -282,9 +295,9 @@ describe('Configure AWS Credentials', {}, () => { await run(); const tags = mockedSTSClient.commandCalls(AssumeRoleCommand)[0].args[0].input.Tags ?? []; // 7 protected (GitHub + Repository, Workflow, Action, Actor, Commit, Branch) - // + 8 overrideable (EventName, BaseRef, HeadRef, RefName, RunId, RefType, Job, TriggeringActor). - // No custom-tags, all env vars set in mocks.envs → all 15 should be present, nothing else. - expect(tags).toHaveLength(15); + // + 6 droppable (EventName, BaseRef, HeadRef, RunId, Job, TriggeringActor). + // No custom-tags, all env vars set in mocks.envs → all 13 should be present, nothing else. + expect(tags).toHaveLength(13); const tagsByKey = Object.fromEntries(tags.map((t) => [t.Key, t.Value])); expect(tagsByKey).toEqual({ GitHub: 'Actions', @@ -297,14 +310,12 @@ describe('Configure AWS Credentials', {}, () => { EventName: 'pull_request', BaseRef: 'main', HeadRef: 'feature-branch', - RefName: 'feature-branch', RunId: '16412345678', - RefType: 'branch', Job: 'build', TriggeringActor: 'MY-USERNAME_bot_', }); }); - it('omits overrideable tags whose env vars are unset', {}, async () => { + it('omits droppable tags whose env vars are unset', {}, async () => { vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.IAM_ASSUMEROLE_INPUTS)); delete process.env.GITHUB_BASE_REF; delete process.env.GITHUB_HEAD_REF; @@ -318,6 +329,27 @@ describe('Configure AWS Credentials', {}, () => { expect(tagKeys).toContain('EventName'); expect(tagKeys).toContain('RunId'); }); + it('drops droppable tags and retries on PackedPolicyTooLargeException', {}, async () => { + vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.IAM_ASSUMEROLE_INPUTS)); + mockedSTSClient + .on(AssumeRoleCommand) + .rejectsOnce(new PackedPolicyTooLargeException({ message: 'too large', $metadata: {} })) + .resolvesOnce(mocks.outputs.STS_CREDENTIALS); + await run(); + expect(core.info).toHaveBeenCalledWith('Session tag size is too large; dropping droppable tags and retrying.'); + const retryInput = mockedSTSClient.commandCalls(AssumeRoleCommand)[1].args[0].input; + const retryTagKeys = (retryInput.Tags ?? []).map((t) => t.Key); + expect(retryTagKeys).not.toContain('EventName'); + expect(retryTagKeys).not.toContain('BaseRef'); + expect(retryTagKeys).not.toContain('HeadRef'); + expect(retryTagKeys).not.toContain('RunId'); + expect(retryTagKeys).not.toContain('Job'); + expect(retryTagKeys).not.toContain('TriggeringActor'); + // Protected tags remain + expect(retryTagKeys).toContain('GitHub'); + expect(retryTagKeys).toContain('Repository'); + expect(core.setFailed).not.toHaveBeenCalled(); + }); it('sanitizes invalid characters in env-derived tag values', {}, async () => { vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.IAM_ASSUMEROLE_INPUTS)); process.env.GITHUB_HEAD_REF = 'feature/has spaces&bad?chars'; @@ -370,8 +402,6 @@ describe('Configure AWS Credentials', {}, () => { { Key: 'EventName', Value: 'pull_request' }, { Key: 'RunId', Value: '16412345678' }, { Key: 'Job', Value: 'build' }, - { Key: 'RefName', Value: 'feature-branch' }, - { Key: 'RefType', Value: 'branch' }, { Key: 'TriggeringActor', Value: 'MY-USERNAME_bot_' }, { Key: 'Environment', Value: 'Production' }, { Key: 'Team', Value: 'DevOps' }, @@ -420,7 +450,7 @@ describe('Configure AWS Credentials', {}, () => { await run(); expect(core.warning).toHaveBeenCalledWith(expect.stringContaining("'custom-tags' is set but will be ignored")); }); - it('lets custom tags override overrideable default tag keys', {}, async () => { + it('rejects custom tags that conflict with droppable tag keys', {}, async () => { vi.mocked(core.getInput).mockImplementation( mocks.getInput({ ...mocks.IAM_ASSUMEROLE_INPUTS, @@ -428,13 +458,10 @@ describe('Configure AWS Credentials', {}, () => { }), ); await run(); - const tags = mockedSTSClient.commandCalls(AssumeRoleCommand)[0].args[0].input.Tags ?? []; - const eventNameTags = tags.filter((t) => t.Key === 'EventName'); - const baseRefTags = tags.filter((t) => t.Key === 'BaseRef'); - expect(eventNameTags).toHaveLength(1); - expect(eventNameTags[0]?.Value).toBe('workflow_dispatch'); - expect(baseRefTags).toHaveLength(1); - expect(baseRefTags[0]?.Value).toBe('release/2026'); + expect(core.setFailed).toHaveBeenCalledWith( + "custom-tags: key 'EventName' conflicts with a protected session tag set by this action and cannot be overridden", + ); + expect(mockedSTSClient.commandCalls(AssumeRoleCommand)).toHaveLength(0); }); it('rejects custom tags that conflict with the protected Branch tag', {}, async () => { // Regression guard: Branch was a default before v6.2 and must remain unoverridable. @@ -450,62 +477,10 @@ describe('Configure AWS Credentials', {}, () => { ); expect(mockedSTSClient.commandCalls(AssumeRoleCommand)).toHaveLength(0); }); - it('drops lower-priority overrideable tags when custom-tags would exceed the session-tag limit', {}, async () => { - // 7 protected (GitHub + 6 from PROTECTED_TAG_SOURCES) + 40 custom = 47 used → 3 overrideable slots. - // The first 3 overrideable tags by priority are EventName, BaseRef, HeadRef (RefName, RunId, RefType, - // Job, TriggeringActor must be dropped). + it('rejects custom-tags that would exceed the session-tag limit', {}, async () => { + // 13 existing tags (7 non-droppable + 6 droppable) + 38 custom = 51 > 50. const customTagsObj: Record = {}; - for (let i = 0; i < 40; i++) { - customTagsObj[`Custom${i}`] = `value${i}`; - } - vi.mocked(core.getInput).mockImplementation( - mocks.getInput({ - ...mocks.IAM_ASSUMEROLE_INPUTS, - 'custom-tags': JSON.stringify(customTagsObj), - }), - ); - await run(); - const tags = mockedSTSClient.commandCalls(AssumeRoleCommand)[0].args[0].input.Tags ?? []; - const tagKeys = tags.map((t) => t.Key); - expect(tags).toHaveLength(50); - expect(tagKeys).toContain('Branch'); - expect(tagKeys).toContain('EventName'); - expect(tagKeys).toContain('BaseRef'); - expect(tagKeys).toContain('HeadRef'); - expect(tagKeys).not.toContain('RefName'); - expect(tagKeys).not.toContain('RunId'); - expect(tagKeys).not.toContain('RefType'); - expect(tagKeys).not.toContain('Job'); - expect(tagKeys).not.toContain('TriggeringActor'); - }); - it('overridden overrideable tags free a slot for a lower-priority overrideable tag', {}, async () => { - // Same 40-custom-tag scenario as above, but one of the customs overrides BaseRef. - // BaseRef no longer competes for the overrideable budget, so the next-priority overrideable (RefName) gets in. - const customTagsObj: Record = { BaseRef: 'release/2026' }; - for (let i = 0; i < 39; i++) { - customTagsObj[`Custom${i}`] = `value${i}`; - } - vi.mocked(core.getInput).mockImplementation( - mocks.getInput({ - ...mocks.IAM_ASSUMEROLE_INPUTS, - 'custom-tags': JSON.stringify(customTagsObj), - }), - ); - await run(); - const tags = mockedSTSClient.commandCalls(AssumeRoleCommand)[0].args[0].input.Tags ?? []; - const tagKeys = tags.map((t) => t.Key); - expect(tags).toHaveLength(50); - expect(tagKeys).toContain('Branch'); - expect(tagKeys).toContain('EventName'); - expect(tagKeys).toContain('BaseRef'); - expect(tagKeys).toContain('HeadRef'); - expect(tagKeys).toContain('RefName'); - expect(tagKeys).not.toContain('RunId'); - }); - it('rejects custom-tags that would exceed the session-tag limit on their own', {}, async () => { - // 7 protected + 44 custom = 51, which is over 50 even with zero overrideable tags. - const customTagsObj: Record = {}; - for (let i = 0; i < 44; i++) { + for (let i = 0; i < 38; i++) { customTagsObj[`Custom${i}`] = `value${i}`; } vi.mocked(core.getInput).mockImplementation( @@ -518,12 +493,10 @@ describe('Configure AWS Credentials', {}, () => { expect(core.setFailed).toHaveBeenCalledWith(expect.stringContaining('would exceed the AWS limit of 50')); expect(mockedSTSClient.commandCalls(AssumeRoleCommand)).toHaveLength(0); }); - it('drops transitive-tag-keys entries that refer to evicted overrideable tags', {}, async () => { - // Force eviction of all overrideable tags below EventName/BaseRef/HeadRef. The user transitive-tags - // RunId (which gets evicted) and Repository (which is protected and stays). The TransitiveTagKeys - // payload must include only the keys that actually appear in Tags. + it('allows custom-tags up to the session-tag limit', {}, async () => { + // 13 existing tags + 37 custom = 50, exactly at the limit. const customTagsObj: Record = {}; - for (let i = 0; i < 40; i++) { + for (let i = 0; i < 37; i++) { customTagsObj[`Custom${i}`] = `value${i}`; } vi.mocked(core.getInput).mockImplementation( @@ -532,15 +505,10 @@ describe('Configure AWS Credentials', {}, () => { 'custom-tags': JSON.stringify(customTagsObj), }), ); - vi.mocked(core.getMultilineInput).mockImplementation((name: string) => { - if (name === 'transitive-tag-keys') return ['Repository', 'RunId']; - return []; - }); await run(); - const callInput = mockedSTSClient.commandCalls(AssumeRoleCommand)[0].args[0].input; - const tagKeys = (callInput.Tags ?? []).map((t) => t.Key); - expect(tagKeys).not.toContain('RunId'); - expect(callInput.TransitiveTagKeys).toEqual(['Repository']); + expect(core.setFailed).not.toHaveBeenCalled(); + const tags = mockedSTSClient.commandCalls(AssumeRoleCommand)[0].args[0].input.Tags ?? []; + expect(tags).toHaveLength(50); }); }); diff --git a/test/mockinputs.test.ts b/test/mockinputs.test.ts index cfc4125..68ee8a0 100644 --- a/test/mockinputs.test.ts +++ b/test/mockinputs.test.ts @@ -105,8 +105,6 @@ const envs = { 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]',