mirror of
https://github.com/aws-actions/configure-aws-credentials.git
synced 2026-06-06 03:17:04 +00:00
Compare commits
13 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
037dd16322 |
||
|
|
89a34d9b83 |
||
|
|
d63f12fba5 |
||
|
|
aefb6ea018 |
||
|
|
bf27562715 | ||
|
|
4f3ef32554 |
||
|
|
26b365ff2f |
||
|
|
262ce4cfb5 |
||
|
|
e7f100cf4c |
||
|
|
bbbffeab00 | ||
|
|
d6f5dc331b |
||
|
|
12014c0798 |
||
|
|
4ab3589ed2 |
10 changed files with 722 additions and 848 deletions
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
".release-please-manifest.json": "4.0.2",
|
||||
"package.json": "6.0.0",
|
||||
".": "6.1.2"
|
||||
".": "6.2.0"
|
||||
}
|
||||
|
|
|
|||
16
CHANGELOG.md
16
CHANGELOG.md
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
## [6.1.2](https://github.com/aws-actions/configure-aws-credentials/compare/v6.1.1...v6.1.2) (2026-05-26)
|
||||
|
||||
## [6.2.0](https://github.com/aws-actions/configure-aws-credentials/compare/v6.1.3...v6.2.0) (2026-06-01)
|
||||
|
||||
### Features
|
||||
|
||||
|
|
@ -14,11 +13,22 @@ All notable changes to this project will be documented in this file. See [standa
|
|||
* expose run id in STS client user-agent ([#1774](https://github.com/aws-actions/configure-aws-credentials/issues/1774)) ([29d1be3](https://github.com/aws-actions/configure-aws-credentials/commit/29d1be30273e7ef371d59fccf6ec54572c64ec89))
|
||||
* support custom STS endpoints ([#1762](https://github.com/aws-actions/configure-aws-credentials/issues/1762)) ([8d52d05](https://github.com/aws-actions/configure-aws-credentials/commit/8d52d05d7a4521fa52b39de50cb6114b12e5c332))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* skip credential check on output-env-credentials: false ([#1778](https://github.com/aws-actions/configure-aws-credentials/issues/1778)) ([58e7c47](https://github.com/aws-actions/configure-aws-credentials/commit/58e7c47adf77846879008deadfeeef8a6969fe6c))
|
||||
* assumeRole failing from session tag size too large ([#1808](https://github.com/aws-actions/configure-aws-credentials/issues/1808)) ([d6f5dc3](https://github.com/aws-actions/configure-aws-credentials/commit/d6f5dc331b44474b19a52caaf85fa4d637b13c8e))
|
||||
|
||||
## [6.1.3](https://github.com/aws-actions/configure-aws-credentials/compare/v6.1.2...v6.1.3) (2026-05-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix: allow kubelet token symlink in [#1805](https://github.com/aws-actions/configure-aws-credentials/issues/1805)
|
||||
|
||||
## [6.1.2](https://github.com/aws-actions/configure-aws-credentials/compare/v6.1.1...v6.1.2) (2026-05-26)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* additional filesystem checks ([#1799](https://github.com/aws-actions/configure-aws-credentials/issues/1799)) ([c39f282](https://github.com/aws-actions/configure-aws-credentials/commit/c39f282697aca8a78c522ecf1f7da9899a31432c))
|
||||
* skip credential check on output-env-credentials: false ([#1778](https://github.com/aws-actions/configure-aws-credentials/issues/1778)) ([58e7c47](https://github.com/aws-actions/configure-aws-credentials/commit/58e7c47adf77846879008deadfeeef8a6969fe6c))
|
||||
|
||||
## [6.1.1](https://github.com/aws-actions/configure-aws-credentials/compare/v6.1.0...v6.1.1) (2026-05-05)
|
||||
|
||||
|
|
|
|||
85
README.md
85
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 |
|
||||
|
||||
</details>
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -584,7 +590,7 @@ claims ([1][gh-blog-oidc], [2][sub-claim-custom]).
|
|||
|
||||
> **Warning:** Avoid `ForAllValues:` in `Allow` statements. These operators
|
||||
> return true when the claim is absent or misspelled, which can lead to
|
||||
> uninended access. Instead, use `StringEquals` or `StringLike` operators to
|
||||
> unintended access. Instead, use `StringEquals` or `StringLike` operators to
|
||||
> check for specific claim values.
|
||||
|
||||
[least-privilege]:
|
||||
|
|
@ -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
|
||||
|
|
|
|||
42
THIRD-PARTY
42
THIRD-PARTY
|
|
@ -644,7 +644,7 @@ Apache License
|
|||
|
||||
The following npm package may be included in this product:
|
||||
|
||||
- @aws-sdk/client-sts@3.1049.0
|
||||
- @aws-sdk/client-sts@3.1061.0
|
||||
|
||||
This package contains the following license:
|
||||
|
||||
|
|
@ -854,9 +854,9 @@ Apache License
|
|||
|
||||
The following npm packages may be included in this product:
|
||||
|
||||
- @aws-sdk/signature-v4-multi-region@3.996.27
|
||||
- @smithy/core@3.24.5
|
||||
- @smithy/types@4.14.2
|
||||
- @aws-sdk/signature-v4-multi-region@3.996.31
|
||||
- @smithy/core@3.24.6
|
||||
- @smithy/types@4.14.3
|
||||
|
||||
These packages each contain the following license:
|
||||
|
||||
|
|
@ -1254,7 +1254,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
|
||||
The following npm package may be included in this product:
|
||||
|
||||
- @aws-sdk/core@3.974.15
|
||||
- @aws-sdk/core@3.974.17
|
||||
|
||||
This package contains the following license:
|
||||
|
||||
|
|
@ -1674,18 +1674,18 @@ Apache License
|
|||
|
||||
The following npm packages may be included in this product:
|
||||
|
||||
- @aws-sdk/credential-provider-env@3.972.41
|
||||
- @aws-sdk/credential-provider-ini@3.972.42
|
||||
- @aws-sdk/credential-provider-node@3.972.43
|
||||
- @aws-sdk/token-providers@3.1049.0
|
||||
- @aws-sdk/types@3.973.9
|
||||
- @aws-sdk/credential-provider-env@3.972.43
|
||||
- @aws-sdk/credential-provider-ini@3.972.48
|
||||
- @aws-sdk/credential-provider-node@3.972.50
|
||||
- @aws-sdk/token-providers@3.1060.0
|
||||
- @aws-sdk/types@3.973.10
|
||||
- @aws-sdk/util-locate-window@3.965.5
|
||||
- @aws-sdk/xml-builder@3.972.26
|
||||
- @smithy/credential-provider-imds@4.3.3
|
||||
- @smithy/fetch-http-handler@5.4.3
|
||||
- @aws-sdk/xml-builder@3.972.27
|
||||
- @smithy/credential-provider-imds@4.3.7
|
||||
- @smithy/fetch-http-handler@5.4.6
|
||||
- @smithy/is-array-buffer@2.2.0
|
||||
- @smithy/node-http-handler@4.7.3
|
||||
- @smithy/signature-v4@5.4.5
|
||||
- @smithy/node-http-handler@4.7.6
|
||||
- @smithy/signature-v4@5.4.6
|
||||
- @smithy/util-buffer-from@2.2.0
|
||||
- @smithy/util-utf8@2.3.0
|
||||
|
||||
|
|
@ -1897,9 +1897,9 @@ Apache License
|
|||
|
||||
The following npm packages may be included in this product:
|
||||
|
||||
- @aws-sdk/credential-provider-process@3.972.38
|
||||
- @aws-sdk/credential-provider-sso@3.972.42
|
||||
- @aws-sdk/credential-provider-web-identity@3.972.42
|
||||
- @aws-sdk/credential-provider-process@3.972.43
|
||||
- @aws-sdk/credential-provider-sso@3.972.47
|
||||
- @aws-sdk/credential-provider-web-identity@3.972.47
|
||||
|
||||
These packages each contain the following license:
|
||||
|
||||
|
|
@ -2109,9 +2109,9 @@ Apache License
|
|||
|
||||
The following npm packages may be included in this product:
|
||||
|
||||
- @aws-sdk/credential-provider-http@3.972.40
|
||||
- @aws-sdk/credential-provider-login@3.972.42
|
||||
- @aws-sdk/nested-clients@3.997.10
|
||||
- @aws-sdk/credential-provider-http@3.972.45
|
||||
- @aws-sdk/credential-provider-login@3.972.47
|
||||
- @aws-sdk/nested-clients@3.997.15
|
||||
|
||||
These packages each contain the following license:
|
||||
|
||||
|
|
|
|||
196
dist/index.js
generated
vendored
196
dist/index.js
generated
vendored
|
|
@ -28857,7 +28857,7 @@ var init_constants4 = __esm({
|
|||
TRANSIENT_ERROR_CODES = ["TimeoutError", "RequestTimeout", "RequestTimeoutException"];
|
||||
TRANSIENT_ERROR_STATUS_CODES = [500, 502, 503, 504];
|
||||
NODEJS_TIMEOUT_ERROR_CODES = ["ECONNRESET", "ECONNREFUSED", "EPIPE", "ETIMEDOUT"];
|
||||
NODEJS_NETWORK_ERROR_CODES = ["EHOSTUNREACH", "ENETUNREACH", "ENOTFOUND"];
|
||||
NODEJS_NETWORK_ERROR_CODES = ["EHOSTUNREACH", "ENETUNREACH", "ENOTFOUND", "EAI_AGAIN"];
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -29019,9 +29019,6 @@ function bindRetryMiddleware(isStreamingPayload2) {
|
|||
try {
|
||||
retryToken = await retryStrategy.refreshRetryTokenForRetry(retryToken, retryErrorInfo);
|
||||
} catch (refreshError) {
|
||||
if (typeof refreshError.$backoff === "number") {
|
||||
await cooldown(refreshError.$backoff);
|
||||
}
|
||||
if (!lastError.$metadata) {
|
||||
lastError.$metadata = {};
|
||||
}
|
||||
|
|
@ -29031,8 +29028,10 @@ function bindRetryMiddleware(isStreamingPayload2) {
|
|||
}
|
||||
attempts = retryToken.getRetryCount();
|
||||
const delay = retryToken.getRetryDelay();
|
||||
totalRetryDelay += delay;
|
||||
await cooldown(delay);
|
||||
totalRetryDelay += (retryToken?.$retryLog?.acquisitionDelay ?? 0) + delay;
|
||||
if (delay > 0) {
|
||||
await cooldown(delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -29267,6 +29266,9 @@ var init_DefaultRetryToken = __esm({
|
|||
count;
|
||||
cost;
|
||||
longPoll;
|
||||
$retryLog = {
|
||||
acquisitionDelay: 0
|
||||
};
|
||||
constructor(delay, count, cost, longPoll) {
|
||||
this.delay = delay;
|
||||
this.count = count;
|
||||
|
|
@ -29318,8 +29320,8 @@ var init_StandardRetryStrategy = __esm({
|
|||
};
|
||||
StandardRetryStrategy = class {
|
||||
mode = RETRY_MODES.STANDARD;
|
||||
capacity = INITIAL_RETRY_TOKENS;
|
||||
retryBackoffStrategy;
|
||||
capacity = INITIAL_RETRY_TOKENS;
|
||||
maxAttemptsProvider;
|
||||
baseDelay;
|
||||
constructor(arg1) {
|
||||
|
|
@ -29353,13 +29355,17 @@ var init_StandardRetryStrategy = __esm({
|
|||
retryDelay = Math.max(delayFromErrorType, Math.min(errorInfo.retryAfterHint.getTime() - Date.now(), delayFromErrorType + 5e3));
|
||||
}
|
||||
if (!shouldRetry) {
|
||||
throw Object.assign(new Error("No retry token available"), {
|
||||
$backoff: Retry.v2026 && retryCode === refusal.capacity && isLongPoll ? retryDelay : 0
|
||||
});
|
||||
const longPollBackoff = Retry.v2026 && retryCode === refusal.capacity && isLongPoll ? retryDelay : 0;
|
||||
if (longPollBackoff > 0) {
|
||||
await new Promise((r5) => setTimeout(r5, longPollBackoff));
|
||||
}
|
||||
} else {
|
||||
const capacityCost = this.getCapacityCost(errorType);
|
||||
this.capacity -= capacityCost;
|
||||
return new DefaultRetryToken(retryDelay, token.getRetryCount() + 1, capacityCost, token.isLongPoll?.() ?? false);
|
||||
const nextToken = new DefaultRetryToken(0, token.getRetryCount() + 1, capacityCost, token.isLongPoll?.() ?? false);
|
||||
await new Promise((r5) => setTimeout(r5, retryDelay));
|
||||
nextToken.$retryLog.acquisitionDelay = retryDelay;
|
||||
return nextToken;
|
||||
}
|
||||
}
|
||||
throw new Error("No retry token available");
|
||||
|
|
@ -29454,11 +29460,10 @@ var init_ConfiguredRetryStrategy = __esm({
|
|||
} else {
|
||||
this.computeNextBackoffDelay = computeNextBackoffDelay;
|
||||
}
|
||||
}
|
||||
async refreshRetryTokenForRetry(tokenToRenew, errorInfo) {
|
||||
const token = await super.refreshRetryTokenForRetry(tokenToRenew, errorInfo);
|
||||
token.getRetryDelay = () => this.computeNextBackoffDelay(token.getRetryCount());
|
||||
return token;
|
||||
this.retryBackoffStrategy.computeNextBackoffDelay = (completedAttempt) => {
|
||||
const nextAttempt = completedAttempt + 1;
|
||||
return this.computeNextBackoffDelay(nextAttempt);
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -29658,6 +29663,7 @@ var init_configurations = __esm({
|
|||
init_AdaptiveRetryStrategy();
|
||||
init_StandardRetryStrategy();
|
||||
init_config3();
|
||||
init_retries_2026_config();
|
||||
ENV_MAX_ATTEMPTS = "AWS_MAX_ATTEMPTS";
|
||||
CONFIG_MAX_ATTEMPTS = "max_attempts";
|
||||
NODE_MAX_ATTEMPT_CONFIG_OPTIONS = {
|
||||
|
|
@ -29683,13 +29689,27 @@ var init_configurations = __esm({
|
|||
},
|
||||
default: DEFAULT_MAX_ATTEMPTS
|
||||
};
|
||||
resolveRetryConfig = (input) => {
|
||||
resolveRetryConfig = (input, defaults) => {
|
||||
const { retryStrategy, retryMode } = input;
|
||||
const maxAttempts = normalizeProvider(input.maxAttempts ?? DEFAULT_MAX_ATTEMPTS);
|
||||
const { defaultMaxAttempts = DEFAULT_MAX_ATTEMPTS, defaultBaseDelay = Retry.delay() } = defaults ?? {};
|
||||
const maxAttemptsProvider = normalizeProvider(input.maxAttempts ?? defaultMaxAttempts);
|
||||
let controller = retryStrategy ? Promise.resolve(retryStrategy) : void 0;
|
||||
const getDefault = async () => await normalizeProvider(retryMode)() === RETRY_MODES.ADAPTIVE ? new AdaptiveRetryStrategy(maxAttempts) : new StandardRetryStrategy(maxAttempts);
|
||||
const getDefault = async () => {
|
||||
const maxAttempts = await maxAttemptsProvider();
|
||||
const adaptive = await normalizeProvider(retryMode)() === RETRY_MODES.ADAPTIVE;
|
||||
if (adaptive) {
|
||||
return new AdaptiveRetryStrategy(maxAttemptsProvider, {
|
||||
maxAttempts,
|
||||
baseDelay: defaultBaseDelay
|
||||
});
|
||||
}
|
||||
return new StandardRetryStrategy({
|
||||
maxAttempts,
|
||||
baseDelay: defaultBaseDelay
|
||||
});
|
||||
};
|
||||
return Object.assign(input, {
|
||||
maxAttempts,
|
||||
maxAttempts: maxAttemptsProvider,
|
||||
retryStrategy: () => controller ??= getDefault()
|
||||
});
|
||||
};
|
||||
|
|
@ -33625,7 +33645,7 @@ var require_package = __commonJS({
|
|||
module2.exports = {
|
||||
name: "@aws-sdk/client-sts",
|
||||
description: "AWS SDK for JavaScript Sts Client for Node.js, Browser and React Native",
|
||||
version: "3.1049.0",
|
||||
version: "3.1061.0",
|
||||
scripts: {
|
||||
build: "concurrently 'yarn:build:types' 'yarn:build:es' && yarn build:cjs",
|
||||
"build:cjs": "node ../../scripts/compilation/inline client-sts",
|
||||
|
|
@ -33651,18 +33671,18 @@ var require_package = __commonJS({
|
|||
dependencies: {
|
||||
"@aws-crypto/sha256-browser": "5.2.0",
|
||||
"@aws-crypto/sha256-js": "5.2.0",
|
||||
"@aws-sdk/core": "^3.974.12",
|
||||
"@aws-sdk/credential-provider-node": "^3.972.43",
|
||||
"@aws-sdk/signature-v4-multi-region": "^3.996.27",
|
||||
"@aws-sdk/types": "^3.973.8",
|
||||
"@smithy/core": "^3.24.2",
|
||||
"@smithy/fetch-http-handler": "^5.4.2",
|
||||
"@smithy/node-http-handler": "^4.7.2",
|
||||
"@smithy/types": "^4.14.1",
|
||||
"@aws-sdk/core": "^3.974.17",
|
||||
"@aws-sdk/credential-provider-node": "^3.972.50",
|
||||
"@aws-sdk/signature-v4-multi-region": "^3.996.31",
|
||||
"@aws-sdk/types": "^3.973.10",
|
||||
"@smithy/core": "^3.24.6",
|
||||
"@smithy/fetch-http-handler": "^5.4.6",
|
||||
"@smithy/node-http-handler": "^4.7.6",
|
||||
"@smithy/types": "^4.14.3",
|
||||
tslib: "^2.6.2"
|
||||
},
|
||||
devDependencies: {
|
||||
"@smithy/snapshot-testing": "^2.1.2",
|
||||
"@smithy/snapshot-testing": "^2.1.7",
|
||||
"@tsconfig/node20": "20.1.8",
|
||||
"@types/node": "^20.14.8",
|
||||
concurrently: "7.0.0",
|
||||
|
|
@ -33753,7 +33773,6 @@ var require_dist_cjs7 = __commonJS({
|
|||
var require_dist_cjs8 = __commonJS({
|
||||
"node_modules/@smithy/credential-provider-imds/dist-cjs/index.js"(exports2) {
|
||||
"use strict";
|
||||
var node_url = require("node:url");
|
||||
var config = (init_config2(), __toCommonJS(config_exports));
|
||||
var node_http = require("node:http");
|
||||
var protocols2 = (init_protocols(), __toCommonJS(protocols_exports));
|
||||
|
|
@ -33838,14 +33857,8 @@ var require_dist_cjs8 = __commonJS({
|
|||
return buffer.toString();
|
||||
};
|
||||
var CMDS_IP = "169.254.170.2";
|
||||
var GREENGRASS_HOSTS = {
|
||||
localhost: true,
|
||||
"127.0.0.1": true
|
||||
};
|
||||
var GREENGRASS_PROTOCOLS = {
|
||||
"http:": true,
|
||||
"https:": true
|
||||
};
|
||||
var GREENGRASS_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1"]);
|
||||
var GREENGRASS_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
|
||||
var getCmdsUri = async ({ logger: logger2 }) => {
|
||||
if (process.env[ENV_CMDS_RELATIVE_URI]) {
|
||||
return {
|
||||
|
|
@ -33854,21 +33867,28 @@ var require_dist_cjs8 = __commonJS({
|
|||
};
|
||||
}
|
||||
if (process.env[ENV_CMDS_FULL_URI]) {
|
||||
const parsed = node_url.parse(process.env[ENV_CMDS_FULL_URI]);
|
||||
if (!parsed.hostname || !(parsed.hostname in GREENGRASS_HOSTS)) {
|
||||
let parsed;
|
||||
try {
|
||||
parsed = new URL(process.env[ENV_CMDS_FULL_URI]);
|
||||
} catch {
|
||||
throw new config.CredentialsProviderError(`${process.env[ENV_CMDS_FULL_URI]} is not a valid container metadata service URL`, { tryNextLink: false, logger: logger2 });
|
||||
}
|
||||
if (!parsed.hostname || !GREENGRASS_HOSTS.has(parsed.hostname)) {
|
||||
throw new config.CredentialsProviderError(`${parsed.hostname} is not a valid container metadata service hostname`, {
|
||||
tryNextLink: false,
|
||||
logger: logger2
|
||||
});
|
||||
}
|
||||
if (!parsed.protocol || !(parsed.protocol in GREENGRASS_PROTOCOLS)) {
|
||||
if (!parsed.protocol || !GREENGRASS_PROTOCOLS.has(parsed.protocol)) {
|
||||
throw new config.CredentialsProviderError(`${parsed.protocol} is not a valid container metadata service protocol`, {
|
||||
tryNextLink: false,
|
||||
logger: logger2
|
||||
});
|
||||
}
|
||||
return {
|
||||
...parsed,
|
||||
protocol: parsed.protocol,
|
||||
hostname: parsed.hostname,
|
||||
path: parsed.pathname + parsed.search,
|
||||
port: parsed.port ? parseInt(parsed.port, 10) : void 0
|
||||
};
|
||||
}
|
||||
|
|
@ -35107,11 +35127,9 @@ Set AWS_CONTAINER_CREDENTIALS_FULL_URI or AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
|
|||
}
|
||||
const url = new URL(host);
|
||||
(0, checkUrl_1.checkUrl)(url, options.logger);
|
||||
const requestHandler = node_http_handler_1.NodeHttpHandler.create({
|
||||
requestTimeout: options.timeout ?? 1e3,
|
||||
connectionTimeout: options.timeout ?? 1e3
|
||||
});
|
||||
return (0, retry_wrapper_1.retryWrapper)(async () => {
|
||||
const requestHandler = node_http_handler_1.NodeHttpHandler.create({ connectionTimeout: options.timeout ?? 1e3 });
|
||||
const requestTimeout = options.timeout ?? 1e3;
|
||||
const provider = (0, retry_wrapper_1.retryWrapper)(async () => {
|
||||
const request = (0, requestHelpers_1.createGetRequest)(url);
|
||||
if (token) {
|
||||
request.headers.Authorization = token;
|
||||
|
|
@ -35119,12 +35137,19 @@ Set AWS_CONTAINER_CREDENTIALS_FULL_URI or AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
|
|||
request.headers.Authorization = (await promises_1.default.readFile(tokenFile)).toString();
|
||||
}
|
||||
try {
|
||||
const result = await requestHandler.handle(request);
|
||||
const result = await requestHandler.handle(request, { requestTimeout });
|
||||
return (0, requestHelpers_1.getCredentials)(result.response).then((creds) => (0, client_1.setCredentialFeature)(creds, "CREDENTIALS_HTTP", "z"));
|
||||
} catch (e5) {
|
||||
throw new config_1.CredentialsProviderError(String(e5), { logger: options.logger });
|
||||
}
|
||||
}, options.maxRetries ?? 3, options.timeout ?? 1e3);
|
||||
return async () => {
|
||||
try {
|
||||
return await provider();
|
||||
} finally {
|
||||
requestHandler.destroy?.();
|
||||
}
|
||||
};
|
||||
};
|
||||
exports2.fromHttp = fromHttp;
|
||||
}
|
||||
|
|
@ -35221,7 +35246,7 @@ var init_package = __esm({
|
|||
"node_modules/@aws-sdk/nested-clients/package.json"() {
|
||||
package_default = {
|
||||
name: "@aws-sdk/nested-clients",
|
||||
version: "3.997.10",
|
||||
version: "3.997.15",
|
||||
description: "Nested clients for AWS SDK packages.",
|
||||
main: "./dist-cjs/index.js",
|
||||
module: "./dist-es/index.js",
|
||||
|
|
@ -35250,13 +35275,13 @@ var init_package = __esm({
|
|||
dependencies: {
|
||||
"@aws-crypto/sha256-browser": "5.2.0",
|
||||
"@aws-crypto/sha256-js": "5.2.0",
|
||||
"@aws-sdk/core": "^3.974.12",
|
||||
"@aws-sdk/signature-v4-multi-region": "^3.996.27",
|
||||
"@aws-sdk/types": "^3.973.8",
|
||||
"@smithy/core": "^3.24.2",
|
||||
"@smithy/fetch-http-handler": "^5.4.2",
|
||||
"@smithy/node-http-handler": "^4.7.2",
|
||||
"@smithy/types": "^4.14.1",
|
||||
"@aws-sdk/core": "^3.974.17",
|
||||
"@aws-sdk/signature-v4-multi-region": "^3.996.31",
|
||||
"@aws-sdk/types": "^3.973.10",
|
||||
"@smithy/core": "^3.24.6",
|
||||
"@smithy/fetch-http-handler": "^5.4.6",
|
||||
"@smithy/node-http-handler": "^4.7.6",
|
||||
"@smithy/types": "^4.14.3",
|
||||
tslib: "^2.6.2"
|
||||
},
|
||||
devDependencies: {
|
||||
|
|
@ -44813,9 +44838,18 @@ var require_dist_cjs18 = __commonJS({
|
|||
let activeLock;
|
||||
let passiveLock;
|
||||
let credentials;
|
||||
let forceRefreshLock;
|
||||
const provider = async (options) => {
|
||||
if (options?.forceRefresh) {
|
||||
return await chain2(options);
|
||||
if (!forceRefreshLock) {
|
||||
forceRefreshLock = chain2(options).then((c5) => {
|
||||
credentials = c5;
|
||||
}).finally(() => {
|
||||
forceRefreshLock = void 0;
|
||||
});
|
||||
}
|
||||
await forceRefreshLock;
|
||||
return credentials;
|
||||
}
|
||||
if (credentials?.expiration) {
|
||||
if (credentials?.expiration?.getTime() < Date.now()) {
|
||||
|
|
@ -44986,7 +45020,7 @@ var require_errors2 = __commonJS({
|
|||
}
|
||||
};
|
||||
exports2.MalformedPolicyDocumentException = MalformedPolicyDocumentException2;
|
||||
var PackedPolicyTooLargeException2 = class _PackedPolicyTooLargeException extends STSServiceException_1.STSServiceException {
|
||||
var PackedPolicyTooLargeException3 = class _PackedPolicyTooLargeException extends STSServiceException_1.STSServiceException {
|
||||
name = "PackedPolicyTooLargeException";
|
||||
$fault = "client";
|
||||
constructor(opts) {
|
||||
|
|
@ -44998,7 +45032,7 @@ var require_errors2 = __commonJS({
|
|||
Object.setPrototypeOf(this, _PackedPolicyTooLargeException.prototype);
|
||||
}
|
||||
};
|
||||
exports2.PackedPolicyTooLargeException = PackedPolicyTooLargeException2;
|
||||
exports2.PackedPolicyTooLargeException = PackedPolicyTooLargeException3;
|
||||
var RegionDisabledException2 = class _RegionDisabledException extends STSServiceException_1.STSServiceException {
|
||||
name = "RegionDisabledException";
|
||||
$fault = "client";
|
||||
|
|
@ -74060,6 +74094,7 @@ async function assumeRoleWithWebIdentityTokenFile(params, client, webIdentityTok
|
|||
info("Assuming role with web identity token file");
|
||||
try {
|
||||
delete params.Tags;
|
||||
delete params.TransitiveTagKeys;
|
||||
const creds = await client.send(
|
||||
new import_client_sts2.AssumeRoleWithWebIdentityCommand({
|
||||
...params,
|
||||
|
|
@ -74077,6 +74112,13 @@ async function assumeRoleWithCredentials(params, client) {
|
|||
const creds = await client.send(new import_client_sts2.AssumeRoleCommand({ ...params }));
|
||||
return creds;
|
||||
} catch (error3) {
|
||||
if (error3 instanceof import_client_sts2.PackedPolicyTooLargeException) {
|
||||
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((tag2) => !droppableKeys.has(tag2.Key ?? ""));
|
||||
const creds = await client.send(new import_client_sts2.AssumeRoleCommand({ ...params }));
|
||||
return creds;
|
||||
}
|
||||
throw new Error(`Could not assume role with user credentials: ${errorMessage(error3)}`);
|
||||
}
|
||||
}
|
||||
|
|
@ -74085,7 +74127,7 @@ var TAG_VALUE_REGEX = /^[\p{L}\p{Z}\p{N}_.:/=+\-@]*$/u;
|
|||
var MAX_TAG_KEY_LENGTH = 128;
|
||||
var MAX_TAG_VALUE_LENGTH2 = 256;
|
||||
var MAX_SESSION_TAGS = 50;
|
||||
var PROTECTED_TAG_SOURCES = [
|
||||
var NON_DROPPABLE_TAG_SOURCES = [
|
||||
{ key: "Repository", envVar: "GITHUB_REPOSITORY" },
|
||||
{ key: "Workflow", envVar: "GITHUB_WORKFLOW" },
|
||||
{ key: "Action", envVar: "GITHUB_ACTION" },
|
||||
|
|
@ -74093,17 +74135,19 @@ var PROTECTED_TAG_SOURCES = [
|
|||
{ key: "Commit", envVar: "GITHUB_SHA" },
|
||||
{ key: "Branch", envVar: "GITHUB_REF" }
|
||||
];
|
||||
var OVERRIDEABLE_TAG_SOURCES_BY_PRIORITY = [
|
||||
var DROPPABLE_TAG_SOURCES = [
|
||||
{ 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" }
|
||||
];
|
||||
var PROTECTED_TAG_KEYS = /* @__PURE__ */ new Set(["GitHub", ...PROTECTED_TAG_SOURCES.map((s) => s.key)]);
|
||||
var PROTECTED_TAG_KEYS = /* @__PURE__ */ new Set([
|
||||
"GitHub",
|
||||
...NON_DROPPABLE_TAG_SOURCES.map((s) => s.key),
|
||||
...DROPPABLE_TAG_SOURCES.map((s) => s.key)
|
||||
]);
|
||||
function parseAndValidateCustomTags(customTags, existingTags) {
|
||||
let parsed;
|
||||
try {
|
||||
|
|
@ -74175,30 +74219,26 @@ async function assumeRole(params) {
|
|||
throw new Error("Missing required environment variables. Are you running in GitHub Actions?");
|
||||
}
|
||||
const protectedTags = [{ 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) });
|
||||
}
|
||||
}
|
||||
const parsedCustomTags = customTags ? parseAndValidateCustomTags(customTags, protectedTags) : [];
|
||||
const customTagKeys = new Set(parsedCustomTags.map((t) => t.Key));
|
||||
const availableOverrideableSlots = MAX_SESSION_TAGS - protectedTags.length - parsedCustomTags.length;
|
||||
const overrideableTags = [];
|
||||
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 = [...protectedTags, ...overrideableTags, ...parsedCustomTags];
|
||||
const tagArray = [...protectedTags, ...parsedCustomTags];
|
||||
const tags = roleSkipSessionTagging ? void 0 : tagArray;
|
||||
if (!tags) {
|
||||
debug("Role session tagging has been skipped.");
|
||||
} else {
|
||||
debug(`${tags.length} role session tags are being used:`);
|
||||
debug(JSON.stringify(tagArray));
|
||||
}
|
||||
const transitiveTagKeysArray = roleSkipSessionTagging ? void 0 : transitiveTagKeys?.filter((key) => tags?.some((tag2) => tag2.Key === key));
|
||||
let roleArn = roleToAssume;
|
||||
|
|
|
|||
1027
package-lock.json
generated
1027
package-lock.json
generated
File diff suppressed because it is too large
Load diff
16
package.json
16
package.json
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "configure-aws-credentials",
|
||||
"description": "A GitHub Action to configure AWS credentials",
|
||||
"version": "6.1.1",
|
||||
"version": "6.2.0",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"lint": "biome check --error-on-warnings ./src ./test && markdownlint -i node_modules -i CHANGELOG.md '**/*.md'",
|
||||
|
|
@ -18,23 +18,23 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@aws-sdk/credential-provider-env": "^3.972.39",
|
||||
"@biomejs/biome": "2.4.15",
|
||||
"@smithy/property-provider": "^4.3.4",
|
||||
"@biomejs/biome": "2.4.16",
|
||||
"@smithy/property-provider": "^4.3.6",
|
||||
"@types/node": "^25.9.1",
|
||||
"@vitest/coverage-v8": "4.1.5",
|
||||
"@vitest/coverage-v8": "4.1.8",
|
||||
"aws-sdk-client-mock": "^4.1.0",
|
||||
"esbuild": "^0.28.0",
|
||||
"generate-license-file": "^4.1.1",
|
||||
"generate-license-file": "^4.2.1",
|
||||
"json-schema": "^0.4.0",
|
||||
"markdownlint-cli": "^0.48.0",
|
||||
"memfs": "^4.57.2",
|
||||
"memfs": "^4.57.6",
|
||||
"standard-version": "^9.5.0",
|
||||
"typescript": "^6.0.3",
|
||||
"vitest": "4.1.5"
|
||||
"vitest": "4.1.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "^3.0.1",
|
||||
"@aws-sdk/client-sts": "^3.1049.0",
|
||||
"@aws-sdk/client-sts": "^3.1061.0",
|
||||
"@smithy/node-http-handler": "^4.7.3",
|
||||
"proxy-agent": "^8.0.1"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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<string>(['GitHub', ...PROTECTED_TAG_SOURCES.map((s) => s.key)]);
|
||||
const PROTECTED_TAG_KEYS = new Set<string>([
|
||||
'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
|
||||
|
|
|
|||
|
|
@ -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<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++) {
|
||||
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<string, string> = {};
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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]',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue