feat: add additional session tags by default
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.
This commit is contained in:
parent
6795889618
commit
9ca54121eb
6 changed files with 8773 additions and 13670 deletions
33
README.md
33
README.md
|
|
@ -350,6 +350,9 @@ 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`:
|
||||
|
||||
| Key | Value |
|
||||
| ---------- | ----------------- |
|
||||
| GitHub | "Actions" |
|
||||
|
|
@ -357,14 +360,32 @@ definitions][gh-env-vars])
|
|||
| Workflow | GITHUB_WORKFLOW |
|
||||
| Action | GITHUB_ACTION |
|
||||
| Actor | GITHUB_ACTOR |
|
||||
| Branch | GITHUB_REF |
|
||||
| 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.
|
||||
|
||||
| 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 |
|
||||
|
||||
Tags whose source environment variable is unset are omitted (e.g., `BaseRef`
|
||||
and `HeadRef` are only set on `pull_request` events).
|
||||
|
||||
_Note: all tag values must conform to
|
||||
[the tag requirements](https://docs.aws.amazon.com/STS/latest/APIReference/API_Tag.html).
|
||||
Particularly, `GITHUB_WORKFLOW` will be truncated if it's too long. If
|
||||
`GITHUB_ACTOR` or `GITHUB_WORKFLOW` contain invalid characters, the characters
|
||||
will be replaced with an '\*'._
|
||||
Values longer than 256 characters will be truncated, and characters outside the
|
||||
allowed set will be replaced with an underscore (`_`)._
|
||||
|
||||
The action will use session tagging by default unless you are using OIDC.
|
||||
|
||||
|
|
@ -391,7 +412,9 @@ with:
|
|||
### 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.
|
||||
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).
|
||||
|
||||
```yaml
|
||||
uses: aws-actions/configure-aws-credentials@v6
|
||||
|
|
|
|||
13
THIRD-PARTY
13
THIRD-PARTY
|
|
@ -431,12 +431,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
-----------
|
||||
|
||||
The following npm packages may be included in this product:
|
||||
The following npm package may be included in this product:
|
||||
|
||||
- @aws-crypto/crc32@5.2.0
|
||||
- @aws-crypto/util@5.2.0
|
||||
|
||||
These packages each contain the following license:
|
||||
This package contains the following license:
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
|
|
@ -644,7 +643,7 @@ Apache License
|
|||
|
||||
The following npm packages may be included in this product:
|
||||
|
||||
- @aws-sdk/client-sts@3.1045.0
|
||||
- @aws-sdk/client-sts@3.1044.0
|
||||
- @aws-sdk/util-user-agent-browser@3.972.10
|
||||
- @aws-sdk/util-user-agent-node@3.973.24
|
||||
- @smithy/middleware-retry@4.5.7
|
||||
|
|
@ -868,7 +867,7 @@ The following npm packages may be included in this product:
|
|||
- @aws-sdk/middleware-sdk-s3@3.972.37
|
||||
- @aws-sdk/middleware-user-agent@3.972.38
|
||||
- @aws-sdk/signature-v4-multi-region@3.996.25
|
||||
- @smithy/core@3.24.1
|
||||
- @smithy/core@3.23.17
|
||||
- @smithy/invalid-dependency@4.2.14
|
||||
- @smithy/middleware-serde@4.2.20
|
||||
- @smithy/protocol-http@5.3.14
|
||||
|
|
@ -1710,8 +1709,8 @@ The following npm packages may be included in this product:
|
|||
- @smithy/middleware-content-length@4.2.14
|
||||
- @smithy/middleware-endpoint@4.4.32
|
||||
- @smithy/middleware-stack@4.2.14
|
||||
- @smithy/node-http-handler@4.7.1
|
||||
- @smithy/property-provider@4.3.1
|
||||
- @smithy/node-http-handler@4.6.1
|
||||
- @smithy/property-provider@4.2.14
|
||||
- @smithy/shared-ini-file-loader@4.4.9
|
||||
- @smithy/signature-v4@5.3.14
|
||||
- @smithy/util-base64@4.3.2
|
||||
|
|
|
|||
16266
dist/index.js
generated
vendored
16266
dist/index.js
generated
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -87,6 +87,32 @@ 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 }> = [
|
||||
{ key: 'Repository', envVar: 'GITHUB_REPOSITORY' },
|
||||
{ key: 'Workflow', envVar: 'GITHUB_WORKFLOW' },
|
||||
{ key: 'Action', envVar: 'GITHUB_ACTION' },
|
||||
{ key: 'Actor', envVar: 'GITHUB_ACTOR' },
|
||||
{ key: 'Commit', envVar: 'GITHUB_SHA' },
|
||||
{ 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 }> = [
|
||||
{ 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<string>(['GitHub', ...PROTECTED_TAG_SOURCES.map((s) => s.key)]);
|
||||
|
||||
export function parseAndValidateCustomTags(customTags: string, existingTags: Tag[]): Tag[] {
|
||||
let parsed: unknown;
|
||||
try {
|
||||
|
|
@ -99,7 +125,6 @@ export function parseAndValidateCustomTags(customTags: string, existingTags: Tag
|
|||
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)) {
|
||||
|
|
@ -129,9 +154,9 @@ export function parseAndValidateCustomTags(customTags: string, existingTags: Tag
|
|||
`custom-tags: value for key '${key}' contains invalid characters. Allowed: unicode letters, digits, spaces, and _.:/=+-@`,
|
||||
);
|
||||
}
|
||||
if (reservedKeys.has(key)) {
|
||||
if (PROTECTED_TAG_KEYS.has(key)) {
|
||||
throw new Error(
|
||||
`custom-tags: key '${key}' conflicts with a default session tag set by this action and cannot be overridden`,
|
||||
`custom-tags: key '${key}' conflicts with a protected session tag set by this action and cannot be overridden`,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -170,27 +195,31 @@ export async function assumeRole(params: assumeRoleParams) {
|
|||
throw new Error('Missing required environment variables. Are you running in GitHub Actions?');
|
||||
}
|
||||
|
||||
// Load role session tags
|
||||
const tagArray: Tag[] = [
|
||||
{ Key: 'GitHub', Value: 'Actions' },
|
||||
{ Key: 'Repository', Value: GITHUB_REPOSITORY },
|
||||
{ Key: 'Workflow', Value: sanitizeGitHubVariables(GITHUB_WORKFLOW) },
|
||||
{ Key: 'Action', Value: GITHUB_ACTION },
|
||||
{ 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),
|
||||
});
|
||||
// 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) {
|
||||
const value = process.env[envVar];
|
||||
if (value) {
|
||||
protectedTags.push({ Key: key, Value: sanitizeGitHubVariables(value) });
|
||||
}
|
||||
}
|
||||
|
||||
if (customTags) {
|
||||
const parsed = parseAndValidateCustomTags(customTags, tagArray);
|
||||
tagArray.push(...parsed);
|
||||
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 tags = roleSkipSessionTagging ? undefined : tagArray;
|
||||
if (!tags) {
|
||||
|
|
|
|||
|
|
@ -245,6 +245,74 @@ describe('Configure AWS Credentials', {}, () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Default session 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('emits exactly the expected default tag set with no custom-tags', {}, async () => {
|
||||
vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.IAM_ASSUMEROLE_INPUTS));
|
||||
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);
|
||||
const tagsByKey = Object.fromEntries(tags.map((t) => [t.Key, t.Value]));
|
||||
expect(tagsByKey).toEqual({
|
||||
GitHub: 'Actions',
|
||||
Repository: 'MY-REPOSITORY-NAME',
|
||||
Workflow: 'MY-WORKFLOW-ID',
|
||||
Action: 'MY-ACTION-NAME',
|
||||
Actor: 'MY-USERNAME_bot_',
|
||||
Commit: 'MY-COMMIT-ID',
|
||||
Branch: 'refs/pull/42/merge',
|
||||
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 () => {
|
||||
vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.IAM_ASSUMEROLE_INPUTS));
|
||||
delete process.env.GITHUB_BASE_REF;
|
||||
delete process.env.GITHUB_HEAD_REF;
|
||||
delete process.env.GITHUB_TRIGGERING_ACTOR;
|
||||
await run();
|
||||
const tags = mockedSTSClient.commandCalls(AssumeRoleCommand)[0].args[0].input.Tags ?? [];
|
||||
const tagKeys = tags.map((t) => t.Key);
|
||||
expect(tagKeys).not.toContain('BaseRef');
|
||||
expect(tagKeys).not.toContain('HeadRef');
|
||||
expect(tagKeys).not.toContain('TriggeringActor');
|
||||
expect(tagKeys).toContain('EventName');
|
||||
expect(tagKeys).toContain('RunId');
|
||||
});
|
||||
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';
|
||||
await run();
|
||||
expect(mockedSTSClient.commandCalls(AssumeRoleCommand)[0].args[0].input).toMatchObject({
|
||||
Tags: expect.arrayContaining([{ Key: 'HeadRef', Value: 'feature/has spaces_bad_chars' }]),
|
||||
});
|
||||
});
|
||||
it('truncates env-derived tag values longer than 256 characters', {}, async () => {
|
||||
vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.IAM_ASSUMEROLE_INPUTS));
|
||||
process.env.GITHUB_HEAD_REF = 'a'.repeat(300);
|
||||
await run();
|
||||
const tags = mockedSTSClient.commandCalls(AssumeRoleCommand)[0].args[0].input.Tags ?? [];
|
||||
const headRef = tags.find((t) => t.Key === 'HeadRef');
|
||||
expect(headRef?.Value).toHaveLength(256);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Custom Tags', {}, () => {
|
||||
beforeEach(() => {
|
||||
mockedSTSClient.on(AssumeRoleCommand).resolvesOnce(mocks.outputs.STS_CREDENTIALS);
|
||||
|
|
@ -273,6 +341,15 @@ describe('Configure AWS Credentials', {}, () => {
|
|||
{ Key: 'Action', Value: 'MY-ACTION-NAME' },
|
||||
{ Key: 'Actor', Value: 'MY-USERNAME_bot_' },
|
||||
{ Key: 'Commit', Value: 'MY-COMMIT-ID' },
|
||||
{ Key: 'Branch', Value: 'refs/pull/42/merge' },
|
||||
{ Key: 'BaseRef', Value: 'main' },
|
||||
{ Key: 'HeadRef', Value: 'feature-branch' },
|
||||
{ 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' },
|
||||
]),
|
||||
|
|
@ -286,11 +363,11 @@ describe('Configure AWS Credentials', {}, () => {
|
|||
);
|
||||
expect(mockedSTSClient.commandCalls(AssumeRoleCommand)).toHaveLength(0);
|
||||
});
|
||||
it('rejects custom tags that conflict with default session tags', {}, async () => {
|
||||
it('rejects custom tags that conflict with protected 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",
|
||||
"custom-tags: key 'Repository' conflicts with a protected session tag set by this action and cannot be overridden",
|
||||
);
|
||||
expect(mockedSTSClient.commandCalls(AssumeRoleCommand)).toHaveLength(0);
|
||||
});
|
||||
|
|
@ -320,6 +397,128 @@ 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 () => {
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.IAM_ASSUMEROLE_INPUTS,
|
||||
'custom-tags': JSON.stringify({ EventName: 'workflow_dispatch', BaseRef: 'release/2026' }),
|
||||
}),
|
||||
);
|
||||
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');
|
||||
});
|
||||
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.
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.IAM_ASSUMEROLE_INPUTS,
|
||||
'custom-tags': JSON.stringify({ Branch: 'evil-branch' }),
|
||||
}),
|
||||
);
|
||||
await run();
|
||||
expect(core.setFailed).toHaveBeenCalledWith(
|
||||
"custom-tags: key 'Branch' conflicts with a protected session tag set by this action and cannot be overridden",
|
||||
);
|
||||
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).
|
||||
const customTagsObj: Record<string, string> = {};
|
||||
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<string, string> = { 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<string, string> = {};
|
||||
for (let i = 0; i < 44; i++) {
|
||||
customTagsObj[`Custom${i}`] = `value${i}`;
|
||||
}
|
||||
vi.mocked(core.getInput).mockImplementation(
|
||||
mocks.getInput({
|
||||
...mocks.IAM_ASSUMEROLE_INPUTS,
|
||||
'custom-tags': JSON.stringify(customTagsObj),
|
||||
}),
|
||||
);
|
||||
await run();
|
||||
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.
|
||||
const customTagsObj: Record<string, string> = {};
|
||||
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),
|
||||
}),
|
||||
);
|
||||
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']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Odd inputs', {}, () => {
|
||||
|
|
@ -1283,6 +1482,8 @@ describe('Configure AWS Credentials', {}, () => {
|
|||
it('omits tokens when env vars are unset, with no warning', async () => {
|
||||
vi.resetModules();
|
||||
delete process.env.GITHUB_ACTION;
|
||||
delete process.env.GITHUB_RUN_ID;
|
||||
delete process.env.GITHUB_RUN_ATTEMPT;
|
||||
const ua = await getCustomUserAgent();
|
||||
expect(ua).toEqual([['configure-aws-credentials-for-github-actions']]);
|
||||
expect(core.warning).not.toHaveBeenCalled();
|
||||
|
|
@ -1314,6 +1515,8 @@ describe('Configure AWS Credentials', {}, () => {
|
|||
it('rejects GITHUB_ACTION containing whitespace or other characters', async () => {
|
||||
vi.resetModules();
|
||||
process.env.GITHUB_ACTION = 'has space';
|
||||
delete process.env.GITHUB_RUN_ID;
|
||||
delete process.env.GITHUB_RUN_ATTEMPT;
|
||||
const ua = await getCustomUserAgent();
|
||||
expect(ua).toEqual([['configure-aws-credentials-for-github-actions']]);
|
||||
expect(core.warning).toHaveBeenCalledWith('GITHUB_ACTION has unexpected format; omitting from User-Agent');
|
||||
|
|
|
|||
|
|
@ -93,6 +93,15 @@ 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 = {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue