mirror of
https://github.com/aws-actions/configure-aws-credentials.git
synced 2026-09-02 05:55:10 +09:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e5247cb6a7 |
@@ -15,7 +15,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Stale issue job
|
name: Stale issue job
|
||||||
steps:
|
steps:
|
||||||
- uses: aws-actions/stale-issue-cleanup@v7
|
- uses: aws-actions/stale-issue-cleanup@v6
|
||||||
with:
|
with:
|
||||||
# Setting messages to an empty string will cause the automation to skip
|
# Setting messages to an empty string will cause the automation to skip
|
||||||
# that category
|
# that category
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
".release-please-manifest.json": "4.0.2",
|
".release-please-manifest.json": "4.0.2",
|
||||||
"package.json": "6.0.0",
|
"package.json": "6.0.0",
|
||||||
".": "6.2.4"
|
".": "6.2.2"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,22 +2,6 @@
|
|||||||
|
|
||||||
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.
|
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.2.4](https://github.com/aws-actions/configure-aws-credentials/compare/v6.2.3...v6.2.4) (2026-08-31)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* account-ids handling, mask proxy as secret in logs ([#1943](https://github.com/aws-actions/configure-aws-credentials/issues/1943)) ([aa65264](https://github.com/aws-actions/configure-aws-credentials/commit/aa6526434b08748f8776b29964e3f1f5d90e7b63))
|
|
||||||
* skip backoff sleep after the final retryAndBackoff attempt ([#1937](https://github.com/aws-actions/configure-aws-credentials/issues/1937)) ([3852440](https://github.com/aws-actions/configure-aws-credentials/commit/3852440c21363386b7b790605685d08a7c1a4876))
|
|
||||||
|
|
||||||
## [6.2.3](https://github.com/aws-actions/configure-aws-credentials/compare/v6.2.2...v6.2.3) (2026-07-22)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* attach git credentials before Tag Major Version push ([#1877](https://github.com/aws-actions/configure-aws-credentials/issues/1877)) ([9ae780b](https://github.com/aws-actions/configure-aws-credentials/commit/9ae780b171afa8c5a3a6a2d154a765b709492482))
|
|
||||||
* PackedPolicyTooLarge detection in STS tags ([#1899](https://github.com/aws-actions/configure-aws-credentials/issues/1899)) ([fa8d6a5](https://github.com/aws-actions/configure-aws-credentials/commit/fa8d6a57bbf44b34439fb080bbdadc7c92c285eb))
|
|
||||||
|
|
||||||
## [6.2.2](https://github.com/aws-actions/configure-aws-credentials/compare/v6.2.1...v6.2.2) (2026-07-07)
|
## [6.2.2](https://github.com/aws-actions/configure-aws-credentials/compare/v6.2.1...v6.2.2) (2026-07-07)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -28,12 +28,6 @@ these are incredibly useful:
|
|||||||
- Any modifications you've made relevant to the bug
|
- Any modifications you've made relevant to the bug
|
||||||
- Anything unusual about your environment or deployment
|
- Anything unusual about your environment or deployment
|
||||||
|
|
||||||
We also ask that you refrain from opening issues via the `gh` CLI or GitHub
|
|
||||||
API. These methods bypass our issue templates and therefore don't apply the
|
|
||||||
proper labels or workflows that we use. Note that AI agents typically do not
|
|
||||||
properly use issue templates. Issues that don't have the proper labels
|
|
||||||
applied may be closed without comment.
|
|
||||||
|
|
||||||
## Contributing via Pull Requests
|
## Contributing via Pull Requests
|
||||||
|
|
||||||
Contributions via pull requests are much appreciated. Before sending us a pull
|
Contributions via pull requests are much appreciated. Before sending us a pull
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ Authenticate to AWS in GitHub Actions (and others)! Works especially well with
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Configure AWS Credentials
|
- name: Configure AWS Credentials
|
||||||
uses: aws-actions/configure-aws-credentials@v6.2.3
|
uses: aws-actions/configure-aws-credentials@v6.1.0
|
||||||
with:
|
with:
|
||||||
role-to-assume: <Role ARN you created in step 2>
|
role-to-assume: <Role ARN you created in step 2>
|
||||||
aws-region: <AWS Region you want to use>
|
aws-region: <AWS Region you want to use>
|
||||||
@@ -250,7 +250,7 @@ specify the profile name as an environment variable in the job step:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Configure AWS Credentials
|
- name: Configure AWS Credentials
|
||||||
uses: aws-actions/configure-aws-credentials@v6.2.3
|
uses: aws-actions/configure-aws-credentials@v6.1.0
|
||||||
with:
|
with:
|
||||||
aws-region: us-east-1
|
aws-region: us-east-1
|
||||||
role-to-assume: arn:aws:iam::123456789100:role/my-role
|
role-to-assume: arn:aws:iam::123456789100:role/my-role
|
||||||
@@ -268,14 +268,14 @@ step environment variables:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Configure AWS credentials
|
- name: Configure AWS credentials
|
||||||
uses: aws-actions/configure-aws-credentials@v6.2.3
|
uses: aws-actions/configure-aws-credentials@v6.1.0
|
||||||
with:
|
with:
|
||||||
aws-region: us-east-1
|
aws-region: us-east-1
|
||||||
role-to-assume: arn:aws:iam::123456789100:role/my-first-role
|
role-to-assume: arn:aws:iam::123456789100:role/my-first-role
|
||||||
aws-profile: firstRoleInChain
|
aws-profile: firstRoleInChain
|
||||||
|
|
||||||
- name: assume second role
|
- name: assume second role
|
||||||
uses: aws-actions/configure-aws-credentials@v6.2.3
|
uses: aws-actions/configure-aws-credentials@v6.1.0
|
||||||
with:
|
with:
|
||||||
aws-region: us-east-2
|
aws-region: us-east-2
|
||||||
role-to-assume: arn:aws:iam::987654321000:role/my-second-role
|
role-to-assume: arn:aws:iam::987654321000:role/my-second-role
|
||||||
@@ -311,7 +311,7 @@ this action will always consider the `HTTP_PROXY` environment variable.
|
|||||||
Manually configured proxy:
|
Manually configured proxy:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
uses: aws-actions/configure-aws-credentials@v6.2.3
|
uses: aws-actions/configure-aws-credentials@v6.1.0
|
||||||
with:
|
with:
|
||||||
aws-region: us-east-2
|
aws-region: us-east-2
|
||||||
role-to-assume: my-github-actions-role
|
role-to-assume: my-github-actions-role
|
||||||
@@ -458,7 +458,7 @@ line.
|
|||||||
<summary>Inline session policy examples</summary>
|
<summary>Inline session policy examples</summary>
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
uses: aws-actions/configure-aws-credentials@v6.2.3
|
uses: aws-actions/configure-aws-credentials@v6.1.0
|
||||||
with:
|
with:
|
||||||
inline-session-policy: '{"Version":"2012-10-17","Statement":[{"Sid":"Stmt1","Effect":"Allow","Action":"s3:List*","Resource":"*"}]}'
|
inline-session-policy: '{"Version":"2012-10-17","Statement":[{"Sid":"Stmt1","Effect":"Allow","Action":"s3:List*","Resource":"*"}]}'
|
||||||
```
|
```
|
||||||
@@ -466,7 +466,7 @@ with:
|
|||||||
Or we can have a nicely formatted JSON as well:
|
Or we can have a nicely formatted JSON as well:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
uses: aws-actions/configure-aws-credentials@v6.2.3
|
uses: aws-actions/configure-aws-credentials@v6.1.0
|
||||||
with:
|
with:
|
||||||
inline-session-policy: >-
|
inline-session-policy: >-
|
||||||
{
|
{
|
||||||
@@ -494,7 +494,7 @@ the role.
|
|||||||
<summary>Managed session policy examples</summary>
|
<summary>Managed session policy examples</summary>
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
uses: aws-actions/configure-aws-credentials@v6.2.3
|
uses: aws-actions/configure-aws-credentials@v6.1.0
|
||||||
with:
|
with:
|
||||||
managed-session-policies: arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
|
managed-session-policies: arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
|
||||||
```
|
```
|
||||||
@@ -502,7 +502,7 @@ with:
|
|||||||
And we can pass multiple managed policies likes this:
|
And we can pass multiple managed policies likes this:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
uses: aws-actions/configure-aws-credentials@v6.2.3
|
uses: aws-actions/configure-aws-credentials@v6.1.0
|
||||||
with:
|
with:
|
||||||
managed-session-policies: |
|
managed-session-policies: |
|
||||||
arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
|
arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
|
||||||
@@ -548,7 +548,7 @@ specify the audience through the `audience` input:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Configure AWS Credentials for China region audience
|
- name: Configure AWS Credentials for China region audience
|
||||||
uses: aws-actions/configure-aws-credentials@v6.2.3
|
uses: aws-actions/configure-aws-credentials@v6.1.0
|
||||||
with:
|
with:
|
||||||
audience: sts.amazonaws.com.cn
|
audience: sts.amazonaws.com.cn
|
||||||
aws-region: cn-northwest-1
|
aws-region: cn-northwest-1
|
||||||
@@ -600,9 +600,8 @@ claims ([1][gh-blog-oidc], [2][sub-claim-custom]).
|
|||||||
|
|
||||||
#### Immutable subject claims
|
#### Immutable subject claims
|
||||||
|
|
||||||
Repositories created on github.com on or after 15 July 2026, older
|
Repositories created on github.com on or after 15 July 2026, and older
|
||||||
repositories that have opted in, and older repositories that have been renamed
|
repositories that have opted in, emit an [immutable `sub` claim][immutable-sub].
|
||||||
since 15 July 2026, emit an [immutable `sub` claim][immutable-sub].
|
|
||||||
This claim appends the permanent numeric ID of the organization and of the
|
This claim appends the permanent numeric ID of the organization and of the
|
||||||
repository after each name, separated by `@`, so that a recycled org or
|
repository after each name, separated by `@`, so that a recycled org or
|
||||||
repository name cannot be used to mint tokens matching a stale trust policy.
|
repository name cannot be used to mint tokens matching a stale trust policy.
|
||||||
@@ -709,7 +708,7 @@ Provider. The audience would still be `sts.amazonaws.com` by default.
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Configure AWS Credentials
|
- name: Configure AWS Credentials
|
||||||
uses: aws-actions/configure-aws-credentials@v6.2.3
|
uses: aws-actions/configure-aws-credentials@v6.1.0
|
||||||
with:
|
with:
|
||||||
aws-region: us-east-2
|
aws-region: us-east-2
|
||||||
role-to-assume: arn:aws:iam::123456789100:role/my-github-actions-role
|
role-to-assume: arn:aws:iam::123456789100:role/my-github-actions-role
|
||||||
@@ -725,13 +724,13 @@ environment variable and use it to assume the role
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Configure AWS Credentials
|
- name: Configure AWS Credentials
|
||||||
uses: aws-actions/configure-aws-credentials@v6.2.3
|
uses: aws-actions/configure-aws-credentials@v6.1.0
|
||||||
with:
|
with:
|
||||||
aws-region: us-east-2
|
aws-region: us-east-2
|
||||||
role-to-assume: arn:aws:iam::123456789100:role/my-github-actions-role
|
role-to-assume: arn:aws:iam::123456789100:role/my-github-actions-role
|
||||||
role-session-name: MySessionName
|
role-session-name: MySessionName
|
||||||
- name: Configure other AWS Credentials
|
- name: Configure other AWS Credentials
|
||||||
uses: aws-actions/configure-aws-credentials@v6.2.3
|
uses: aws-actions/configure-aws-credentials@v6.1.0
|
||||||
with:
|
with:
|
||||||
aws-region: us-east-2
|
aws-region: us-east-2
|
||||||
role-to-assume: arn:aws:iam::987654321000:role/my-second-role
|
role-to-assume: arn:aws:iam::987654321000:role/my-second-role
|
||||||
@@ -753,7 +752,7 @@ alternatively, the `TagSession` permission can be omitted if you are using the
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Configure AWS Credentials
|
- name: Configure AWS Credentials
|
||||||
uses: aws-actions/configure-aws-credentials@v6.2.3
|
uses: aws-actions/configure-aws-credentials@v6.1.0
|
||||||
with:
|
with:
|
||||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
@@ -774,7 +773,7 @@ like `role-to-assume: my-github-actions-role`.
|
|||||||
```yaml
|
```yaml
|
||||||
- name: Configure AWS Credentials 1
|
- name: Configure AWS Credentials 1
|
||||||
id: creds
|
id: creds
|
||||||
uses: aws-actions/configure-aws-credentials@v6.2.3
|
uses: aws-actions/configure-aws-credentials@v6.1.0
|
||||||
with:
|
with:
|
||||||
aws-region: us-east-2
|
aws-region: us-east-2
|
||||||
role-to-assume: arn:aws:iam::123456789100:role/my-github-actions-role
|
role-to-assume: arn:aws:iam::123456789100:role/my-github-actions-role
|
||||||
@@ -783,7 +782,7 @@ like `role-to-assume: my-github-actions-role`.
|
|||||||
run: |
|
run: |
|
||||||
aws sts get-caller-identity
|
aws sts get-caller-identity
|
||||||
- name: Configure AWS Credentials 2
|
- name: Configure AWS Credentials 2
|
||||||
uses: aws-actions/configure-aws-credentials@v6.2.3
|
uses: aws-actions/configure-aws-credentials@v6.1.0
|
||||||
with:
|
with:
|
||||||
aws-region: us-east-2
|
aws-region: us-east-2
|
||||||
aws-access-key-id: ${{ steps.creds.outputs.aws-access-key-id }}
|
aws-access-key-id: ${{ steps.creds.outputs.aws-access-key-id }}
|
||||||
@@ -814,14 +813,14 @@ provided.
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Configure AWS Credentials for Dev
|
- name: Configure AWS Credentials for Dev
|
||||||
uses: aws-actions/configure-aws-credentials@v6.2.3
|
uses: aws-actions/configure-aws-credentials@v6.1.0
|
||||||
with:
|
with:
|
||||||
aws-region: us-east-1
|
aws-region: us-east-1
|
||||||
role-to-assume: arn:aws:iam::111111111111:role/dev-role
|
role-to-assume: arn:aws:iam::111111111111:role/dev-role
|
||||||
aws-profile: dev
|
aws-profile: dev
|
||||||
|
|
||||||
- name: Configure AWS Credentials for Prod
|
- name: Configure AWS Credentials for Prod
|
||||||
uses: aws-actions/configure-aws-credentials@v6.2.3
|
uses: aws-actions/configure-aws-credentials@v6.1.0
|
||||||
with:
|
with:
|
||||||
aws-region: us-west-2
|
aws-region: us-west-2
|
||||||
role-to-assume: arn:aws:iam::222222222222:role/prod-role
|
role-to-assume: arn:aws:iam::222222222222:role/prod-role
|
||||||
|
|||||||
+23
-23
@@ -222,7 +222,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
|
|
||||||
The following npm package may be included in this product:
|
The following npm package may be included in this product:
|
||||||
|
|
||||||
- @aws-sdk/client-sts@3.1116.0
|
- @aws-sdk/client-sts@3.1091.0
|
||||||
|
|
||||||
This package contains the following license:
|
This package contains the following license:
|
||||||
|
|
||||||
@@ -432,9 +432,9 @@ Apache License
|
|||||||
|
|
||||||
The following npm packages may be included in this product:
|
The following npm packages may be included in this product:
|
||||||
|
|
||||||
- @aws-sdk/signature-v4-multi-region@3.996.46
|
- @aws-sdk/signature-v4-multi-region@3.996.41
|
||||||
- @smithy/core@3.33.3
|
- @smithy/core@3.29.6
|
||||||
- @smithy/types@4.17.2
|
- @smithy/types@4.16.1
|
||||||
|
|
||||||
These packages each contain the following license:
|
These packages each contain the following license:
|
||||||
|
|
||||||
@@ -832,7 +832,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||||||
|
|
||||||
The following npm package may be included in this product:
|
The following npm package may be included in this product:
|
||||||
|
|
||||||
- @aws-sdk/core@3.977.9
|
- @aws-sdk/core@3.975.3
|
||||||
|
|
||||||
This package contains the following license:
|
This package contains the following license:
|
||||||
|
|
||||||
@@ -1042,16 +1042,16 @@ Apache License
|
|||||||
|
|
||||||
The following npm packages may be included in this product:
|
The following npm packages may be included in this product:
|
||||||
|
|
||||||
- @aws-sdk/credential-provider-env@3.972.70
|
- @aws-sdk/credential-provider-env@3.972.59
|
||||||
- @aws-sdk/credential-provider-ini@3.973.15
|
- @aws-sdk/credential-provider-ini@3.973.4
|
||||||
- @aws-sdk/credential-provider-node@3.972.81
|
- @aws-sdk/credential-provider-node@3.972.70
|
||||||
- @aws-sdk/token-providers@3.1116.0
|
- @aws-sdk/token-providers@3.1088.0
|
||||||
- @aws-sdk/types@3.974.5
|
- @aws-sdk/types@3.974.2
|
||||||
- @aws-sdk/xml-builder@3.972.40
|
- @aws-sdk/xml-builder@3.972.36
|
||||||
- @smithy/credential-provider-imds@4.5.2
|
- @smithy/credential-provider-imds@4.4.11
|
||||||
- @smithy/fetch-http-handler@5.7.2
|
- @smithy/fetch-http-handler@5.6.8
|
||||||
- @smithy/node-http-handler@4.11.3
|
- @smithy/node-http-handler@4.9.8
|
||||||
- @smithy/signature-v4@5.6.12
|
- @smithy/signature-v4@5.6.7
|
||||||
|
|
||||||
These packages each contain the following license:
|
These packages each contain the following license:
|
||||||
|
|
||||||
@@ -1261,9 +1261,9 @@ Apache License
|
|||||||
|
|
||||||
The following npm packages may be included in this product:
|
The following npm packages may be included in this product:
|
||||||
|
|
||||||
- @aws-sdk/credential-provider-process@3.972.70
|
- @aws-sdk/credential-provider-process@3.972.59
|
||||||
- @aws-sdk/credential-provider-sso@3.973.14
|
- @aws-sdk/credential-provider-sso@3.973.3
|
||||||
- @aws-sdk/credential-provider-web-identity@3.972.76
|
- @aws-sdk/credential-provider-web-identity@3.972.65
|
||||||
|
|
||||||
These packages each contain the following license:
|
These packages each contain the following license:
|
||||||
|
|
||||||
@@ -1473,9 +1473,9 @@ Apache License
|
|||||||
|
|
||||||
The following npm packages may be included in this product:
|
The following npm packages may be included in this product:
|
||||||
|
|
||||||
- @aws-sdk/credential-provider-http@3.972.72
|
- @aws-sdk/credential-provider-http@3.972.61
|
||||||
- @aws-sdk/credential-provider-login@3.972.77
|
- @aws-sdk/credential-provider-login@3.972.66
|
||||||
- @aws-sdk/nested-clients@3.997.44
|
- @aws-sdk/nested-clients@3.997.33
|
||||||
|
|
||||||
These packages each contain the following license:
|
These packages each contain the following license:
|
||||||
|
|
||||||
@@ -1485,7 +1485,7 @@ Apache-2.0
|
|||||||
|
|
||||||
The following npm package may be included in this product:
|
The following npm package may be included in this product:
|
||||||
|
|
||||||
- ip-address@10.4.0
|
- ip-address@10.2.0
|
||||||
|
|
||||||
This package contains the following license:
|
This package contains the following license:
|
||||||
|
|
||||||
@@ -1740,7 +1740,7 @@ SOFTWARE.
|
|||||||
|
|
||||||
The following npm package may be included in this product:
|
The following npm package may be included in this product:
|
||||||
|
|
||||||
- undici@6.28.0
|
- undici@6.27.0
|
||||||
|
|
||||||
This package contains the following license:
|
This package contains the following license:
|
||||||
|
|
||||||
|
|||||||
+6
-88
@@ -1929,11 +1929,7 @@ var require_request = __commonJS({
|
|||||||
} else if (typeof val[i] === "object") {
|
} else if (typeof val[i] === "object") {
|
||||||
throw new InvalidArgumentError(`invalid ${key} header`);
|
throw new InvalidArgumentError(`invalid ${key} header`);
|
||||||
} else {
|
} else {
|
||||||
const str = `${val[i]}`;
|
arr.push(`${val[i]}`);
|
||||||
if (!isValidHeaderValue(str)) {
|
|
||||||
throw new InvalidArgumentError(`invalid ${key} header`);
|
|
||||||
}
|
|
||||||
arr.push(str);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val = arr;
|
val = arr;
|
||||||
@@ -1945,9 +1941,6 @@ var require_request = __commonJS({
|
|||||||
val = "";
|
val = "";
|
||||||
} else {
|
} else {
|
||||||
val = `${val}`;
|
val = `${val}`;
|
||||||
if (!isValidHeaderValue(val)) {
|
|
||||||
throw new InvalidArgumentError(`invalid ${key} header`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (headerName === "host") {
|
if (headerName === "host") {
|
||||||
if (request.host !== null) {
|
if (request.host !== null) {
|
||||||
@@ -5678,7 +5671,6 @@ var require_client_h1 = __commonJS({
|
|||||||
RequestContentLengthMismatchError,
|
RequestContentLengthMismatchError,
|
||||||
ResponseContentLengthMismatchError,
|
ResponseContentLengthMismatchError,
|
||||||
RequestAbortedError,
|
RequestAbortedError,
|
||||||
InvalidArgumentError,
|
|
||||||
HeadersTimeoutError,
|
HeadersTimeoutError,
|
||||||
HeadersOverflowError,
|
HeadersOverflowError,
|
||||||
SocketError,
|
SocketError,
|
||||||
@@ -6405,16 +6397,8 @@ var require_client_h1 = __commonJS({
|
|||||||
}
|
}
|
||||||
body = bodyStream.stream;
|
body = bodyStream.stream;
|
||||||
contentLength = bodyStream.length;
|
contentLength = bodyStream.length;
|
||||||
} else if (util.isBlobLike(body) && request.contentType == null) {
|
} else if (util.isBlobLike(body) && request.contentType == null && body.type) {
|
||||||
const contentType = body.type;
|
headers.push("content-type", body.type);
|
||||||
if (contentType) {
|
|
||||||
const contentTypeValue = `${contentType}`;
|
|
||||||
if (!util.isValidHeaderValue(contentTypeValue)) {
|
|
||||||
util.errorRequest(client, request, new InvalidArgumentError("invalid content-type header"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
headers.push("content-type", contentTypeValue);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (body && typeof body.read === "function") {
|
if (body && typeof body.read === "function") {
|
||||||
body.read(0);
|
body.read(0);
|
||||||
@@ -8966,24 +8950,6 @@ var require_retry_handler = __commonJS({
|
|||||||
const current = Date.now();
|
const current = Date.now();
|
||||||
return new Date(retryAfter).getTime() - current;
|
return new Date(retryAfter).getTime() - current;
|
||||||
}
|
}
|
||||||
function validatePartialResponseContentLength(headers, range, statusCode, retryCount) {
|
|
||||||
const contentLength = headers["content-length"];
|
|
||||||
if (contentLength == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (!Number.isFinite(range.start) || !Number.isFinite(range.end)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const length = Number(contentLength);
|
|
||||||
const expectedLength = range.end - range.start + 1;
|
|
||||||
if (!Number.isFinite(length) || length !== expectedLength) {
|
|
||||||
return new RequestRetryError("Content-Length mismatch", statusCode, {
|
|
||||||
headers,
|
|
||||||
data: { count: retryCount }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
var RetryHandler = class _RetryHandler {
|
var RetryHandler = class _RetryHandler {
|
||||||
constructor(opts, handlers) {
|
constructor(opts, handlers) {
|
||||||
const { retryOptions, ...dispatchOpts } = opts;
|
const { retryOptions, ...dispatchOpts } = opts;
|
||||||
@@ -9156,11 +9122,6 @@ var require_retry_handler = __commonJS({
|
|||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const contentLengthError = validatePartialResponseContentLength(headers, contentRange, statusCode, this.retryCount);
|
|
||||||
if (contentLengthError != null) {
|
|
||||||
this.abort(contentLengthError);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const { start, size, end = size - 1 } = contentRange;
|
const { start, size, end = size - 1 } = contentRange;
|
||||||
assert(this.start === start, "content-range mismatch");
|
assert(this.start === start, "content-range mismatch");
|
||||||
assert(this.end == null || this.end === end, "content-range mismatch");
|
assert(this.end == null || this.end === end, "content-range mismatch");
|
||||||
@@ -9178,11 +9139,6 @@ var require_retry_handler = __commonJS({
|
|||||||
statusMessage
|
statusMessage
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const contentLengthError = validatePartialResponseContentLength(headers, range, statusCode, this.retryCount);
|
|
||||||
if (contentLengthError != null) {
|
|
||||||
this.abort(contentLengthError);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const { start, size, end = size - 1 } = range;
|
const { start, size, end = size - 1 } = range;
|
||||||
assert(
|
assert(
|
||||||
start != null && Number.isFinite(start),
|
start != null && Number.isFinite(start),
|
||||||
@@ -16028,48 +15984,14 @@ var require_util6 = __commonJS({
|
|||||||
for (let i = 0; i < path.length; ++i) {
|
for (let i = 0; i < path.length; ++i) {
|
||||||
const code = path.charCodeAt(i);
|
const code = path.charCodeAt(i);
|
||||||
if (code < 32 || // exclude CTLs (0-31)
|
if (code < 32 || // exclude CTLs (0-31)
|
||||||
code > 126 || // exclude DEL and non-ascii
|
code === 127 || // DEL
|
||||||
code === 59) {
|
code === 59) {
|
||||||
throw new Error("Invalid cookie path");
|
throw new Error("Invalid cookie path");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function isLetterOrDigit(code) {
|
|
||||||
return code >= 48 && code <= 57 || // 0-9
|
|
||||||
code >= 65 && code <= 90 || // A-Z
|
|
||||||
code >= 97 && code <= 122;
|
|
||||||
}
|
|
||||||
function validateCookieDomain(domain) {
|
function validateCookieDomain(domain) {
|
||||||
if (domain === " ") {
|
if (domain.startsWith("-") || domain.endsWith(".") || domain.endsWith("-")) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (domain.length > 255) {
|
|
||||||
throw new Error("Invalid cookie domain");
|
|
||||||
}
|
|
||||||
let labelLength = 0;
|
|
||||||
for (let i = 0; i < domain.length; ++i) {
|
|
||||||
const code = domain.charCodeAt(i);
|
|
||||||
if (code === 46) {
|
|
||||||
if (labelLength === 0) {
|
|
||||||
throw new Error("Invalid cookie domain");
|
|
||||||
}
|
|
||||||
if (domain.charCodeAt(i - 1) === 45) {
|
|
||||||
throw new Error("Invalid cookie domain");
|
|
||||||
}
|
|
||||||
labelLength = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (labelLength === 0 && !isLetterOrDigit(code)) {
|
|
||||||
throw new Error("Invalid cookie domain");
|
|
||||||
}
|
|
||||||
if (!isLetterOrDigit(code) && code !== 45) {
|
|
||||||
throw new Error("Invalid cookie domain");
|
|
||||||
}
|
|
||||||
if (++labelLength > 63) {
|
|
||||||
throw new Error("Invalid cookie domain");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (labelLength === 0 || domain.charCodeAt(domain.length - 1) === 45) {
|
|
||||||
throw new Error("Invalid cookie domain");
|
throw new Error("Invalid cookie domain");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16152,11 +16074,7 @@ var require_util6 = __commonJS({
|
|||||||
throw new Error("Invalid unparsed");
|
throw new Error("Invalid unparsed");
|
||||||
}
|
}
|
||||||
const [key, ...value] = part.split("=");
|
const [key, ...value] = part.split("=");
|
||||||
const trimmedKey = key.trim();
|
out.push(`${key.trim()}=${value.join("=")}`);
|
||||||
const joinedValue = value.join("=");
|
|
||||||
validateCookieName(trimmedKey);
|
|
||||||
validateCookieValue(joinedValue);
|
|
||||||
out.push(`${trimmedKey}=${joinedValue}`);
|
|
||||||
}
|
}
|
||||||
return out.join("; ");
|
return out.join("; ");
|
||||||
}
|
}
|
||||||
|
|||||||
+2230
-3944
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,14 @@
|
|||||||
|
# Examples
|
||||||
|
|
||||||
|
## [federated-setup](./federated-setup/README.md)
|
||||||
|
|
||||||
|
The directory contains templates for setting up the `configure-aws-credentials`
|
||||||
|
federation between your GitHub Organization/repository and your AWS account.
|
||||||
|
|
||||||
|
## [cfn-deploy-example](./cfn-deploy-example/README.md)
|
||||||
|
|
||||||
|
Repository example uses aws-action `configure-aws-credentials` with OIDC
|
||||||
|
federation template
|
||||||
|
[github-actions-oidc-federation-and-role](./github-actions-oidc-federation-and-role.yml).
|
||||||
|
Example demonstrates a repository that deploys AWS CloudFormation template using
|
||||||
|
cfn-deploy GitHub Action.
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
name: 'compliance'
|
||||||
|
## run ci testing on all push events
|
||||||
|
on: [push]
|
||||||
|
jobs:
|
||||||
|
## Guard rule set
|
||||||
|
sast-guard:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- uses: grolston/guard-action@main
|
||||||
|
with:
|
||||||
|
data_directory: './cloudformation/' ## change to your template directory
|
||||||
|
rule_set: 'FedRAMP-Moderate'
|
||||||
|
show_summary: 'all'
|
||||||
|
output_format: 'single-line-summary'
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
name: deploy
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
env:
|
||||||
|
AWS_DEFAULT_REGION: us-east-1
|
||||||
|
AWS_DEFAULT_OUTPUT: json
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy-cfn:
|
||||||
|
name: deploy
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# These permissions are needed to interact with GitHub’s OIDC Token endpoint.
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
- name: Configure AWS Credentials
|
||||||
|
uses: aws-actions/configure-aws-credentials@v6
|
||||||
|
with:
|
||||||
|
aws-region: us-east-1
|
||||||
|
## the following creates an ARN based on the values entered into github secrets
|
||||||
|
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_DEPLOY_ROLE }}
|
||||||
|
role-session-name: myGitHubActions
|
||||||
|
- name: Deploy EC2 Bastion
|
||||||
|
uses: aws-actions/aws-cloudformation-github-deploy@v1.3.0
|
||||||
|
with:
|
||||||
|
name: myEC2bastion
|
||||||
|
## change to path to template in your github repo
|
||||||
|
template: cloudformation/ec2-bastion.yml
|
||||||
|
capabilities: CAPABILITY_IAM, CAPABILITY_NAMED_IAM
|
||||||
|
no-fail-on-empty-changeset: "1"
|
||||||
|
## parameter set in github secrets
|
||||||
|
parameter-overrides: "pVpc=${{ secrets.VPC_ID }},pSubnet=${{ secrets.SUBNET_ID }}"
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# cfn-deploy example
|
||||||
|
|
||||||
|
Example uses aws-action `configure-aws-credentials` with OIDC federation. Prior
|
||||||
|
to using this example project, the user needs to deploy the
|
||||||
|
[github-actions-oidc-federation-and-role](../federated-setup/github-actions-oidc-federation-and-role.yml)
|
||||||
|
template in the AWS account they want to deploy the CloudFormation template
|
||||||
|
into. Specify the GitHub Organization name, repository name, and the specific
|
||||||
|
branch you want to deploy on.
|
||||||
|
|
||||||
|
Within the [github/workflows](./.github/workflows/) directory there is a
|
||||||
|
[compliance.yml](./.github/workflows/compliance.yml) and a
|
||||||
|
[deploy.yml](./.github/workflows/deploy.yml). The deploy.yml file leverages the
|
||||||
|
aws-action `configure-aws-credentials` and accesses GitHub Action Secrets for
|
||||||
|
some of the variables. The compliance.yml runs static application security
|
||||||
|
testing using cfn-guard.
|
||||||
|
|
||||||
|
To use the example you will need to set the following GitHub Action Secrets:
|
||||||
|
|
||||||
|
| Secret Key | Used With | Description |
|
||||||
|
| --------------- | -------------------------------- | ---------------------------------------- |
|
||||||
|
| AWS_ACCOUNT_ID | configure-aws-credentials | The AWS account ID |
|
||||||
|
| AWS_DEPLOY_ROLE | configure-aws-credentials | The name of the IAM role |
|
||||||
|
| VPC_ID | aws-cloudformation-github-deploy | VPC ID the EC2 Bastion is deployed to |
|
||||||
|
| SUBNET_ID | aws-cloudformation-github-deploy | Subnet ID the EC2 Bastion is deployed to |
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
---
|
||||||
|
AWSTemplateFormatVersion: "2010-09-09"
|
||||||
|
Description: EC2 bastion for latest AWS Linux 2 EC2 deployment
|
||||||
|
Metadata:
|
||||||
|
AWS::CloudFormation::Interface:
|
||||||
|
ParameterGroups:
|
||||||
|
- Label:
|
||||||
|
default: "EC2 Configuration"
|
||||||
|
Parameters:
|
||||||
|
- pTagNameValue
|
||||||
|
- pOperatingSystem
|
||||||
|
- pInstanceType
|
||||||
|
- pVolumeSize
|
||||||
|
- pEbsDeleteOnTermination
|
||||||
|
- Label:
|
||||||
|
default: "Network Configuration"
|
||||||
|
Parameters:
|
||||||
|
- pVpc
|
||||||
|
- pSubnet
|
||||||
|
ParameterLabels:
|
||||||
|
pOperatingSystem:
|
||||||
|
default: "Operating System"
|
||||||
|
pInstanceType:
|
||||||
|
default: "Instance Type"
|
||||||
|
pTagNameValue:
|
||||||
|
default: "EC2 Name"
|
||||||
|
pVolumeSize:
|
||||||
|
default: "Volume Size"
|
||||||
|
pEbsDeleteOnTermination:
|
||||||
|
default: "Delete EBS Volume on Termination"
|
||||||
|
pSubnet:
|
||||||
|
default: "Subnet"
|
||||||
|
pVpc:
|
||||||
|
default: "VPC"
|
||||||
|
Parameters:
|
||||||
|
pSubnet:
|
||||||
|
Description: The subnet to launch the instance in to. It must be part of the VPC chosen above.
|
||||||
|
Type: AWS::EC2::Subnet::Id
|
||||||
|
pVpc:
|
||||||
|
Description: The VPC to launch the EC2 instance in to.
|
||||||
|
Type: AWS::EC2::VPC::Id
|
||||||
|
pOperatingSystem:
|
||||||
|
Type: "AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>"
|
||||||
|
Default: "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-ebs"
|
||||||
|
pInstanceType:
|
||||||
|
Description: Desired Instance Size
|
||||||
|
Type: String
|
||||||
|
Default: t3.small
|
||||||
|
AllowedValues:
|
||||||
|
- t3.small
|
||||||
|
- t3.medium
|
||||||
|
- t3.nano
|
||||||
|
pTagNameValue:
|
||||||
|
Description: "Required: Enter the tag name you'd like applied to the instance. Tag Name gives the name to the EC2 instance."
|
||||||
|
Type: String
|
||||||
|
MinLength: 1
|
||||||
|
Default: "myBastion"
|
||||||
|
pVolumeSize:
|
||||||
|
Description:
|
||||||
|
Enter the number of GBs you want your volume to be. The minimum value
|
||||||
|
is 8 GBs
|
||||||
|
Type: Number
|
||||||
|
Default: 50
|
||||||
|
MinValue: 8
|
||||||
|
pEbsDeleteOnTermination:
|
||||||
|
Description: "Specify if the EBS volume should be deleted if EC2 is deleted."
|
||||||
|
Type: String
|
||||||
|
Default: true
|
||||||
|
AllowedValues:
|
||||||
|
- true
|
||||||
|
- false
|
||||||
|
Rules:
|
||||||
|
SubnetInVPC:
|
||||||
|
Assertions:
|
||||||
|
- Assert: !EachMemberIn
|
||||||
|
- !ValueOfAll
|
||||||
|
- AWS::EC2::Subnet::Id
|
||||||
|
- VpcId
|
||||||
|
- !RefAll "AWS::EC2::VPC::Id"
|
||||||
|
AssertDescription: All subnets must in the VPC
|
||||||
|
Resources:
|
||||||
|
rSecurityGroupDefault:
|
||||||
|
Type: AWS::EC2::SecurityGroup
|
||||||
|
Properties:
|
||||||
|
GroupDescription: !Sub "Default SG for SC Product ${pTagNameValue} "
|
||||||
|
VpcId: !Ref pVpc
|
||||||
|
SecurityGroupEgress:
|
||||||
|
- Description: Outbound unrestricted traffic
|
||||||
|
IpProtocol: "-1"
|
||||||
|
CidrIp: 0.0.0.0/0
|
||||||
|
Tags:
|
||||||
|
- Key: Name
|
||||||
|
Value: !Ref pTagNameValue
|
||||||
|
rLinuxEc2:
|
||||||
|
Type: AWS::EC2::Instance
|
||||||
|
Metadata:
|
||||||
|
guard:
|
||||||
|
SuppressedRules:
|
||||||
|
- 'EC2_INSTANCE_DETAILED_MONITORING_ENABLED'
|
||||||
|
Properties:
|
||||||
|
ImageId: !Ref pOperatingSystem
|
||||||
|
IamInstanceProfile: !Ref rec2InstanceProfile
|
||||||
|
Monitoring: false
|
||||||
|
InstanceType: !Ref pInstanceType
|
||||||
|
EbsOptimized: true
|
||||||
|
SourceDestCheck: true
|
||||||
|
SubnetId: !Ref pSubnet
|
||||||
|
SecurityGroupIds:
|
||||||
|
- !Ref rSecurityGroupDefault
|
||||||
|
BlockDeviceMappings:
|
||||||
|
- DeviceName: "/dev/xvda"
|
||||||
|
Ebs:
|
||||||
|
VolumeSize: !Ref pVolumeSize
|
||||||
|
DeleteOnTermination: !Ref pEbsDeleteOnTermination
|
||||||
|
Tags:
|
||||||
|
- Key: Name
|
||||||
|
Value: !Ref pTagNameValue
|
||||||
|
UserData:
|
||||||
|
Fn::Base64:
|
||||||
|
yum update -y
|
||||||
|
## Instance Profiles
|
||||||
|
## EC2 IAM Roles
|
||||||
|
rEc2Role:
|
||||||
|
Type: AWS::IAM::Role
|
||||||
|
Properties:
|
||||||
|
RoleName: !Sub "ec2-role-${AWS::StackName}"
|
||||||
|
AssumeRolePolicyDocument:
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Principal:
|
||||||
|
Service: [ec2.amazonaws.com]
|
||||||
|
Action: ['sts:AssumeRole']
|
||||||
|
Path: /
|
||||||
|
ManagedPolicyArns:
|
||||||
|
- !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore'
|
||||||
|
- !Sub 'arn:${AWS::Partition}:iam::aws:policy/CloudWatchAgentServerPolicy'
|
||||||
|
rec2InstanceProfile:
|
||||||
|
Type: AWS::IAM::InstanceProfile
|
||||||
|
Properties:
|
||||||
|
InstanceProfileName: !Sub "ec2-profile-${AWS::StackName}"
|
||||||
|
Path: /
|
||||||
|
Roles:
|
||||||
|
- !Ref rEc2Role
|
||||||
|
Outputs:
|
||||||
|
oLinuxEc2InstanceId:
|
||||||
|
Description: Resource ID of the newly created EC2 instance
|
||||||
|
Value: !Ref rLinuxEc2
|
||||||
|
oLinuxEc2PrivateIP:
|
||||||
|
Description: Private IP Address for EC2
|
||||||
|
Value: !GetAtt rLinuxEc2.PrivateIp
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# federated-setup
|
||||||
|
|
||||||
|
## [github-action-oidc-federation](./github-actions-oidc-federation.yml)
|
||||||
|
|
||||||
|
Setup of the OIDC federation between your GitHub Organization/repository and
|
||||||
|
your AWS account.
|
||||||
|
|
||||||
|
## [github-actions-oidc-federation-and-role](./github-actions-oidc-federation-and-role.yml)
|
||||||
|
|
||||||
|
Setup of the OIDC federation between your GitHub Organization/repository and
|
||||||
|
your AWS account along with a role that only executes on specific branch.
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
---
|
||||||
|
AWSTemplateFormatVersion: "2010-09-09"
|
||||||
|
Description: Github Actions configuration - OIDC IAM IdP and associated role CI/CD
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
GitHubOrganization:
|
||||||
|
Type: String
|
||||||
|
Description: This is the root organization or personal account where repos are stored (Case Sensitive)
|
||||||
|
|
||||||
|
RepositoryName:
|
||||||
|
Type: String
|
||||||
|
Description: The repo(s) these roles will have access to. (Use * for all org or personal repos)
|
||||||
|
Default: "*"
|
||||||
|
|
||||||
|
BranchName:
|
||||||
|
Type: String
|
||||||
|
Description: Name of the git branch to to trust. (Use * for all branches)
|
||||||
|
Default: "*"
|
||||||
|
|
||||||
|
RoleName:
|
||||||
|
Type: String
|
||||||
|
Description: Name the Role
|
||||||
|
|
||||||
|
UseExistingProvider:
|
||||||
|
Type: String
|
||||||
|
Description: "Only one GitHub Provider can exists. Choose yes if one is already present in account"
|
||||||
|
Default: "no"
|
||||||
|
AllowedValues:
|
||||||
|
- "yes"
|
||||||
|
- "no"
|
||||||
|
|
||||||
|
Conditions:
|
||||||
|
|
||||||
|
CreateProvider: !Equals ["no", !Ref UseExistingProvider]
|
||||||
|
|
||||||
|
Resources:
|
||||||
|
|
||||||
|
IdpGitHubOidc:
|
||||||
|
Type: AWS::IAM::OIDCProvider
|
||||||
|
Condition: CreateProvider
|
||||||
|
Properties:
|
||||||
|
Url: https://token.actions.githubusercontent.com
|
||||||
|
ClientIdList:
|
||||||
|
- sts.amazonaws.com
|
||||||
|
- !Sub https://github.com/${GitHubOrganization}/${RepositoryName}
|
||||||
|
ThumbprintList:
|
||||||
|
- 6938fd4d98bab03faadb97b34396831e3780aea1
|
||||||
|
Tags:
|
||||||
|
- Key: Name
|
||||||
|
Value: !Sub ${RoleName}-OIDC-Provider
|
||||||
|
|
||||||
|
RoleGithubActions:
|
||||||
|
Type: AWS::IAM::Role
|
||||||
|
Properties:
|
||||||
|
RoleName: !Ref RoleName
|
||||||
|
AssumeRolePolicyDocument:
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Action: sts:AssumeRoleWithWebIdentity
|
||||||
|
Principal:
|
||||||
|
Federated: !If
|
||||||
|
- CreateProvider
|
||||||
|
- !Ref IdpGitHubOidc
|
||||||
|
- !Sub arn:${AWS::Partition}:iam::${AWS::AccountId}:oidc-provider/token.actions.githubusercontent.com
|
||||||
|
Condition:
|
||||||
|
StringLike:
|
||||||
|
token.actions.githubusercontent.com:sub: !Sub repo:${GitHubOrganization}/${RepositoryName}:ref:refs/heads/${BranchName}
|
||||||
|
ManagedPolicyArns:
|
||||||
|
## edit the managed policy to give least privileges
|
||||||
|
- !Sub arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess
|
||||||
|
|
||||||
|
Outputs:
|
||||||
|
|
||||||
|
IdpGitHubOidc:
|
||||||
|
Condition: CreateProvider
|
||||||
|
Description: "ARN of Github OIDC Provider"
|
||||||
|
Value: !GetAtt IdpGitHubOidc.Arn
|
||||||
|
|
||||||
|
RoleGithubActionsARN:
|
||||||
|
Description: "CICD Role for GitHub Actions"
|
||||||
|
Value: !GetAtt RoleGithubActions.Arn
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
AWSTemplateFormatVersion: "2010-09-09"
|
||||||
|
Description: Github Actions configuration - OIDC IAM IdP Federation
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
GitHubOrganization:
|
||||||
|
Type: String
|
||||||
|
Description: This is the root organization or personal account where repos are stored (Case Sensitive)
|
||||||
|
Default: ""
|
||||||
|
|
||||||
|
RepositoryName:
|
||||||
|
Type: String
|
||||||
|
Description: The repo(s) these roles will have access to. (Use * for all org or personal repos)
|
||||||
|
Default: "*"
|
||||||
|
|
||||||
|
RoleName:
|
||||||
|
Type: String
|
||||||
|
Description: Name the Role
|
||||||
|
Default: ""
|
||||||
|
|
||||||
|
|
||||||
|
Resources:
|
||||||
|
|
||||||
|
IdpGitHubOidc:
|
||||||
|
Type: AWS::IAM::OIDCProvider
|
||||||
|
Properties:
|
||||||
|
Url: https://token.actions.githubusercontent.com
|
||||||
|
ClientIdList:
|
||||||
|
- sts.amazonaws.com
|
||||||
|
- !Sub https://github.com/${GitHubOrganization}/${RepositoryName}
|
||||||
|
ThumbprintList:
|
||||||
|
- 6938fd4d98bab03faadb97b34396831e3780aea1
|
||||||
|
Tags:
|
||||||
|
- Key: Name
|
||||||
|
Value: !Sub ${RoleName}-OIDC-Provider
|
||||||
|
|
||||||
|
|
||||||
|
Outputs:
|
||||||
|
|
||||||
|
IdpGitHubOidc:
|
||||||
|
Description: "ARN of Github OIDC Provider"
|
||||||
|
Value: !GetAtt IdpGitHubOidc.Arn
|
||||||
Generated
+392
-390
File diff suppressed because it is too large
Load Diff
+9
-10
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "configure-aws-credentials",
|
"name": "configure-aws-credentials",
|
||||||
"description": "A GitHub Action to configure AWS credentials",
|
"description": "A GitHub Action to configure AWS credentials",
|
||||||
"version": "6.2.4",
|
"version": "6.2.2",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"lint": "biome check --error-on-warnings ./src ./test && markdownlint -i node_modules -i CHANGELOG.md '**/*.md'",
|
"lint": "biome check --error-on-warnings ./src ./test && markdownlint -i node_modules -i CHANGELOG.md '**/*.md'",
|
||||||
@@ -17,26 +17,25 @@
|
|||||||
"organization": true
|
"organization": true
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@aws-sdk/credential-provider-env": "^3.972.70",
|
"@aws-sdk/credential-provider-env": "^3.972.59",
|
||||||
"@biomejs/biome": "2.5.10",
|
"@biomejs/biome": "2.5.4",
|
||||||
"@smithy/property-provider": "^4.5.2",
|
"@smithy/property-provider": "^4.4.11",
|
||||||
"@types/node": "^26.2.0",
|
"@types/node": "^26.1.1",
|
||||||
"@vitest/coverage-v8": "4.1.10",
|
"@vitest/coverage-v8": "4.1.10",
|
||||||
"aws-sdk-client-mock": "^4.1.0",
|
"aws-sdk-client-mock": "^4.1.0",
|
||||||
"esbuild": "^0.28.2",
|
"esbuild": "^0.28.1",
|
||||||
"generate-license-file": "^4.2.1",
|
"generate-license-file": "^4.2.1",
|
||||||
"json-schema": "^0.4.0",
|
"json-schema": "^0.4.0",
|
||||||
"markdownlint-cli": "^0.49.1",
|
"markdownlint-cli": "^0.49.1",
|
||||||
"memfs": "^4.68.1",
|
"memfs": "^4.64.0",
|
||||||
"standard-version": "^9.5.0",
|
"standard-version": "^9.5.0",
|
||||||
"typescript": "^7.0.2",
|
"typescript": "^7.0.2",
|
||||||
"vitest": "4.1.10"
|
"vitest": "4.1.10"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^3.0.1",
|
"@actions/core": "^3.0.1",
|
||||||
"@aws-sdk/client-sts": "^3.1116.0",
|
"@aws-sdk/client-sts": "^3.1091.0",
|
||||||
"@aws-sdk/credential-provider-node": "^3.972.63",
|
"@smithy/node-http-handler": "^4.9.8",
|
||||||
"@smithy/node-http-handler": "^4.11.3",
|
|
||||||
"proxy-agent": "^8.0.2"
|
"proxy-agent": "^8.0.2"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import { info } from '@actions/core';
|
import { info } from '@actions/core';
|
||||||
import { STSClient } from '@aws-sdk/client-sts';
|
import { STSClient } from '@aws-sdk/client-sts';
|
||||||
import { defaultProvider } from '@aws-sdk/credential-provider-node';
|
|
||||||
import type { AwsCredentialIdentity } from '@aws-sdk/types';
|
import type { AwsCredentialIdentity } from '@aws-sdk/types';
|
||||||
import { NodeHttpHandler } from '@smithy/node-http-handler';
|
import { NodeHttpHandler } from '@smithy/node-http-handler';
|
||||||
import { ProxyAgent } from 'proxy-agent';
|
import { ProxyAgent } from 'proxy-agent';
|
||||||
import { buildCustomUserAgent, errorMessage, getCallerIdentity, maskProxyCredentials } from './helpers';
|
import { buildCustomUserAgent, errorMessage, getCallerIdentity } from './helpers';
|
||||||
import { ProxyResolver } from './ProxyResolver';
|
import { ProxyResolver } from './ProxyResolver';
|
||||||
|
|
||||||
if (!process.env.AWS_EXECUTION_ENV) {
|
if (!process.env.AWS_EXECUTION_ENV) {
|
||||||
@@ -32,7 +31,6 @@ export class CredentialsClient {
|
|||||||
}
|
}
|
||||||
if (props.proxyServer) {
|
if (props.proxyServer) {
|
||||||
info('Configuring proxy handler for STS client');
|
info('Configuring proxy handler for STS client');
|
||||||
maskProxyCredentials(props.proxyServer);
|
|
||||||
const proxyOptions: { httpProxy: string; httpsProxy: string; noProxy?: string } = {
|
const proxyOptions: { httpProxy: string; httpsProxy: string; noProxy?: string } = {
|
||||||
httpProxy: props.proxyServer,
|
httpProxy: props.proxyServer,
|
||||||
httpsProxy: props.proxyServer,
|
httpsProxy: props.proxyServer,
|
||||||
@@ -107,15 +105,9 @@ export class CredentialsClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async loadCredentials() {
|
private async loadCredentials() {
|
||||||
// Previously we constructed a new client, but that picks up the default provider chain including the endpoint.
|
const config = {} as { requestHandler?: NodeHttpHandler };
|
||||||
// Explicitly calling the default provider chain allows us to pass in the endpoint and region as well as the
|
if (this.requestHandler !== undefined) config.requestHandler = this.requestHandler;
|
||||||
// proxy config.
|
const client = new STSClient(config);
|
||||||
return defaultProvider({
|
return client.config.credentials();
|
||||||
clientConfig: {
|
|
||||||
...(this.region !== undefined && { region: this.region }),
|
|
||||||
...(this.stsEndpoint !== undefined && { endpoint: this.stsEndpoint }),
|
|
||||||
...(this.requestHandler !== undefined && { requestHandler: this.requestHandler }),
|
|
||||||
},
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-30
@@ -5,6 +5,7 @@ import type { Credentials, STSClient } from '@aws-sdk/client-sts';
|
|||||||
import { GetCallerIdentityCommand } from '@aws-sdk/client-sts';
|
import { GetCallerIdentityCommand } from '@aws-sdk/client-sts';
|
||||||
import type { AwsCredentialIdentity } from '@aws-sdk/types';
|
import type { AwsCredentialIdentity } from '@aws-sdk/types';
|
||||||
import type { UserAgent } from '@smithy/types';
|
import type { UserAgent } from '@smithy/types';
|
||||||
|
import type { CredentialsClient } from './CredentialsClient';
|
||||||
|
|
||||||
const MAX_TAG_VALUE_LENGTH = 256;
|
const MAX_TAG_VALUE_LENGTH = 256;
|
||||||
const SANITIZATION_CHARACTER = '_';
|
const SANITIZATION_CHARACTER = '_';
|
||||||
@@ -166,13 +167,15 @@ export function exportAccountId(identity: { Account: string; Arn: string }, mask
|
|||||||
// Validates that the account of the already-resolved caller identity is in the allow-list provided via the
|
// Validates that the account of the already-resolved caller identity is in the allow-list provided via the
|
||||||
// `allowed-account-ids` input.
|
// `allowed-account-ids` input.
|
||||||
export function validateAccountId(expectedAccountIds: string[] | undefined, account: string | undefined): void {
|
export function validateAccountId(expectedAccountIds: string[] | undefined, account: string | undefined): void {
|
||||||
const allowedAccountIds = expectedAccountIds?.filter((id) => id !== '') ?? [];
|
if (!expectedAccountIds || expectedAccountIds.length === 0 || expectedAccountIds[0] === '') {
|
||||||
if (allowedAccountIds.length === 0) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!account || !allowedAccountIds.includes(account)) {
|
if (!account || !expectedAccountIds.includes(account)) {
|
||||||
// Account IDs are deliberately omitted: this error reaches the job log before any mask exists.
|
throw new Error(
|
||||||
throw new Error('The account ID of the provided credentials does not match any of the allowed account IDs');
|
`The account ID of the provided credentials (${
|
||||||
|
account ?? 'unknown'
|
||||||
|
}) does not match any of the expected account IDs: ${expectedAccountIds.join(', ')}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,29 +193,6 @@ export function toCredentialIdentity(creds?: Partial<Credentials>): AwsCredentia
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Registers any userinfo embedded in a proxy URL as secrets so it is masked in job logs.
|
|
||||||
// First the literal proxy string, then any username/password components if parseable.
|
|
||||||
// If the username/password is percent-encoded, the decoded form is also masked.
|
|
||||||
export function maskProxyCredentials(proxyServer: string): void {
|
|
||||||
core.setSecret(proxyServer);
|
|
||||||
let url: URL;
|
|
||||||
try {
|
|
||||||
url = new URL(proxyServer);
|
|
||||||
} catch (_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (const part of [url.username, url.password]) {
|
|
||||||
if (!part) continue;
|
|
||||||
core.setSecret(part);
|
|
||||||
try {
|
|
||||||
const decoded = decodeURIComponent(part);
|
|
||||||
if (decoded !== part) core.setSecret(decoded);
|
|
||||||
} catch (_) {
|
|
||||||
// malformed percent-encoding; the raw form is already masked
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tags have a more restrictive set of acceptable characters than GitHub environment variables can.
|
// Tags have a more restrictive set of acceptable characters than GitHub environment variables can.
|
||||||
// This replaces anything not conforming to the tag restrictions by inverting the regular expression.
|
// This replaces anything not conforming to the tag restrictions by inverting the regular expression.
|
||||||
// See the AWS documentation for constraint specifics https://docs.aws.amazon.com/STS/latest/APIReference/API_Tag.html.
|
// See the AWS documentation for constraint specifics https://docs.aws.amazon.com/STS/latest/APIReference/API_Tag.html.
|
||||||
@@ -280,13 +260,13 @@ export async function retryAndBackoff<T>(
|
|||||||
`Retrying after ${Math.floor(delay)}ms.`,
|
`Retrying after ${Math.floor(delay)}ms.`,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await sleep(delay);
|
||||||
|
|
||||||
if (nextRetry >= maxRetries) {
|
if (nextRetry >= maxRetries) {
|
||||||
core.info(`Retry${opName}: reached max retries (${maxRetries}); giving up.`);
|
core.info(`Retry${opName}: reached max retries (${maxRetries}); giving up.`);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
await sleep(delay);
|
|
||||||
|
|
||||||
return await retryAndBackoff(fn, isRetryable, maxRetries, nextRetry, base, label);
|
return await retryAndBackoff(fn, isRetryable, maxRetries, nextRetry, base, label);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -301,6 +281,19 @@ export function isDefined<T>(i: T | undefined | null): i is T {
|
|||||||
}
|
}
|
||||||
/* c8 ignore stop */
|
/* c8 ignore stop */
|
||||||
|
|
||||||
|
export async function areCredentialsValid(credentialsClient: CredentialsClient) {
|
||||||
|
const client = credentialsClient.stsClient;
|
||||||
|
try {
|
||||||
|
const identity = await client.send(new GetCallerIdentityCommand({}));
|
||||||
|
if (identity.Account) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Like core.getBooleanInput, but respects the required option.
|
* Like core.getBooleanInput, but respects the required option.
|
||||||
*
|
*
|
||||||
|
|||||||
+5
-13
@@ -3,12 +3,12 @@ import type { AssumeRoleCommandOutput } from '@aws-sdk/client-sts';
|
|||||||
import { assumeRole } from './assumeRole';
|
import { assumeRole } from './assumeRole';
|
||||||
import { CredentialsClient } from './CredentialsClient';
|
import { CredentialsClient } from './CredentialsClient';
|
||||||
import {
|
import {
|
||||||
|
areCredentialsValid,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
exportAccountId,
|
exportAccountId,
|
||||||
exportCredentials,
|
exportCredentials,
|
||||||
exportRegion,
|
exportRegion,
|
||||||
getBooleanInput,
|
getBooleanInput,
|
||||||
getCallerIdentity,
|
|
||||||
retryAndBackoff,
|
retryAndBackoff,
|
||||||
toCredentialIdentity,
|
toCredentialIdentity,
|
||||||
translateEnvVariables,
|
translateEnvVariables,
|
||||||
@@ -53,8 +53,8 @@ export async function run() {
|
|||||||
});
|
});
|
||||||
const roleChaining = getBooleanInput('role-chaining', { required: false });
|
const roleChaining = getBooleanInput('role-chaining', { required: false });
|
||||||
const outputCredentials = getBooleanInput('output-credentials', { required: false });
|
const outputCredentials = getBooleanInput('output-credentials', { required: false });
|
||||||
// Default to always outputting environment credentials unless profile is specified. If profile is specified,
|
// Default to always outputting environment credentials unless profile is specified. If profile is specified, default
|
||||||
// default to no environment credentials (but still output them if the user specifically requests it).
|
// to no environment credentials (but still output them if the user specifically requests it).
|
||||||
const outputEnvCredentials = getBooleanInput('output-env-credentials', { required: false, default: !awsProfile });
|
const outputEnvCredentials = getBooleanInput('output-env-credentials', { required: false, default: !awsProfile });
|
||||||
const unsetCurrentCredentials = getBooleanInput('unset-current-credentials', { required: false });
|
const unsetCurrentCredentials = getBooleanInput('unset-current-credentials', { required: false });
|
||||||
let disableRetry = getBooleanInput('disable-retry', { required: false });
|
let disableRetry = getBooleanInput('disable-retry', { required: false });
|
||||||
@@ -165,16 +165,8 @@ export async function run() {
|
|||||||
|
|
||||||
//if the user wants to attempt to use existing credentials, check if we have some already
|
//if the user wants to attempt to use existing credentials, check if we have some already
|
||||||
if (useExistingCredentials) {
|
if (useExistingCredentials) {
|
||||||
const identity = await (async () => {
|
const validCredentials = await areCredentialsValid(credentialsClient);
|
||||||
try {
|
if (validCredentials) {
|
||||||
return await getCallerIdentity(credentialsClient.stsClient);
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
if (identity) {
|
|
||||||
// The allowed-account-ids guardrail applies to reused credentials too.
|
|
||||||
validateAccountId(expectedAccountIds, identity.Account);
|
|
||||||
core.notice('Pre-existing credentials are valid. No need to generate new ones.');
|
core.notice('Pre-existing credentials are valid. No need to generate new ones.');
|
||||||
if (timeoutId) clearTimeout(timeoutId);
|
if (timeoutId) clearTimeout(timeoutId);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -53,15 +53,8 @@ export function parseIni(iniData: string): Record<string, Record<string, string>
|
|||||||
export function stringifyIni(data: Record<string, Record<string, string>>): string {
|
export function stringifyIni(data: Record<string, Record<string, string>>): string {
|
||||||
const sections: string[] = [];
|
const sections: string[] = [];
|
||||||
for (const [sectionName, sectionData] of Object.entries(data)) {
|
for (const [sectionName, sectionData] of Object.entries(data)) {
|
||||||
if (/[\r\n]/.test(sectionName)) {
|
|
||||||
throw new Error('INI section names must not contain newline characters');
|
|
||||||
}
|
|
||||||
const lines: string[] = [`[${sectionName}]`];
|
const lines: string[] = [`[${sectionName}]`];
|
||||||
for (const [key, value] of Object.entries(sectionData)) {
|
for (const [key, value] of Object.entries(sectionData)) {
|
||||||
// A newline in a key or value would inject arbitrary INI lines (e.g. credential_process).
|
|
||||||
if (/[\r\n]/.test(key) || /[\r\n]/.test(value)) {
|
|
||||||
throw new Error('INI keys and values must not contain newline characters');
|
|
||||||
}
|
|
||||||
lines.push(`${key} = ${value}`);
|
lines.push(`${key} = ${value}`);
|
||||||
}
|
}
|
||||||
sections.push(lines.join('\n'));
|
sections.push(lines.join('\n'));
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
import { describe, expect, it, vi } from 'vitest';
|
|
||||||
|
|
||||||
vi.mock('@aws-sdk/credential-provider-node', () => ({
|
|
||||||
defaultProvider: vi.fn(() => async () => ({ accessKeyId: 'AKIA', secretAccessKey: 'secret' })),
|
|
||||||
}));
|
|
||||||
|
|
||||||
import { defaultProvider } from '@aws-sdk/credential-provider-node';
|
|
||||||
import { CredentialsClient } from '../src/CredentialsClient';
|
|
||||||
|
|
||||||
describe('CredentialsClient', {}, () => {
|
|
||||||
it('pins ambient credential resolution to the configured region and STS endpoint', {}, async () => {
|
|
||||||
const client = new CredentialsClient({
|
|
||||||
region: 'eu-west-1',
|
|
||||||
stsEndpoint: 'https://sts.example.com',
|
|
||||||
roleChaining: false,
|
|
||||||
});
|
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: any required to call private method
|
|
||||||
await (client as any).loadCredentials();
|
|
||||||
expect(defaultProvider).toHaveBeenCalledWith({
|
|
||||||
clientConfig: expect.objectContaining({ region: 'eu-west-1', endpoint: 'https://sts.example.com' }),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('omits unset client config values from ambient credential resolution', {}, async () => {
|
|
||||||
const client = new CredentialsClient({ region: 'eu-west-1', roleChaining: false });
|
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: any required to call private method
|
|
||||||
await (client as any).loadCredentials();
|
|
||||||
expect(defaultProvider).toHaveBeenLastCalledWith({ clientConfig: { region: 'eu-west-1' } });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -126,45 +126,6 @@ describe('Configure AWS Credentials helpers', {}, () => {
|
|||||||
expect(core.exportVariable).toHaveBeenCalledWith('AWS_SESSION_TOKEN', '');
|
expect(core.exportVariable).toHaveBeenCalledWith('AWS_SESSION_TOKEN', '');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('maskProxyCredentials', {}, () => {
|
|
||||||
it('masks username and password embedded in a proxy URL', {}, () => {
|
|
||||||
helpers.maskProxyCredentials('http://user:secretpass@proxy.example.com:8080');
|
|
||||||
expect(core.setSecret).toHaveBeenCalledWith('user');
|
|
||||||
expect(core.setSecret).toHaveBeenCalledWith('secretpass');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('masks both encoded and decoded forms of the credentials', {}, () => {
|
|
||||||
helpers.maskProxyCredentials('http://user:p%40ss@proxy.example.com:8080');
|
|
||||||
expect(core.setSecret).toHaveBeenCalledWith('p%40ss');
|
|
||||||
expect(core.setSecret).toHaveBeenCalledWith('p@ss');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('masks the whole value even without embedded credentials or when unparseable', {}, () => {
|
|
||||||
helpers.maskProxyCredentials('http://proxy.example.com:8080');
|
|
||||||
expect(core.setSecret).toHaveBeenCalledWith('http://proxy.example.com:8080');
|
|
||||||
helpers.maskProxyCredentials('not a url');
|
|
||||||
expect(core.setSecret).toHaveBeenCalledWith('not a url');
|
|
||||||
// no username/password parts, so exactly one mask per call
|
|
||||||
expect(core.setSecret).toHaveBeenCalledTimes(2);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('validateAccountId', {}, () => {
|
|
||||||
it('enforces the allow-list even when the first element is empty', {}, () => {
|
|
||||||
expect(() => helpers.validateAccountId(['', '999999999999'], '111111111111')).toThrow(/does not match/);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('passes an allowed account despite empty entries in the list', {}, () => {
|
|
||||||
expect(() => helpers.validateAccountId(['', '111111111111'], '111111111111')).not.toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('skips validation only when no non-empty entries exist', {}, () => {
|
|
||||||
expect(() => helpers.validateAccountId(undefined, '111111111111')).not.toThrow();
|
|
||||||
expect(() => helpers.validateAccountId([], '111111111111')).not.toThrow();
|
|
||||||
expect(() => helpers.validateAccountId([''], '111111111111')).not.toThrow();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('filesystem helpers', {}, () => {
|
describe('filesystem helpers', {}, () => {
|
||||||
describe('isSymlink', {}, () => {
|
describe('isSymlink', {}, () => {
|
||||||
it('returns true for a symlink', {}, () => {
|
it('returns true for a symlink', {}, () => {
|
||||||
|
|||||||
+5
-47
@@ -841,7 +841,7 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
|
|
||||||
await run();
|
await run();
|
||||||
expect(core.setFailed).toHaveBeenCalledWith(
|
expect(core.setFailed).toHaveBeenCalledWith(
|
||||||
'The account ID of the provided credentials does not match any of the allowed account IDs',
|
'The account ID of the provided credentials (111111111111) does not match any of the expected account IDs: 999999999999',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -861,7 +861,7 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
|
|
||||||
await run();
|
await run();
|
||||||
expect(core.setFailed).toHaveBeenCalledWith(
|
expect(core.setFailed).toHaveBeenCalledWith(
|
||||||
'The account ID of the provided credentials does not match any of the allowed account IDs',
|
'The account ID of the provided credentials (111111111111) does not match any of the expected account IDs: 999999999999, 888888888888',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -917,7 +917,7 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
|
|
||||||
await run();
|
await run();
|
||||||
expect(core.setFailed).toHaveBeenCalledWith(
|
expect(core.setFailed).toHaveBeenCalledWith(
|
||||||
'The account ID of the provided credentials does not match any of the allowed account IDs',
|
'The account ID of the provided credentials (111111111111) does not match any of the expected account IDs: 999999999999',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -936,7 +936,7 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
|
|
||||||
await run();
|
await run();
|
||||||
expect(core.setFailed).toHaveBeenCalledWith(
|
expect(core.setFailed).toHaveBeenCalledWith(
|
||||||
'The account ID of the provided credentials does not match any of the allowed account IDs',
|
'The account ID of the provided credentials (111111111111) does not match any of the expected account IDs: 999999999999',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -956,7 +956,7 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
|
|
||||||
await run();
|
await run();
|
||||||
expect(core.setFailed).toHaveBeenCalledWith(
|
expect(core.setFailed).toHaveBeenCalledWith(
|
||||||
'The account ID of the provided credentials does not match any of the allowed account IDs',
|
'The account ID of the provided credentials (111111111111) does not match any of the expected account IDs: 999999999999',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1015,33 +1015,6 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
await run();
|
await run();
|
||||||
expect(core.setFailed).not.toHaveBeenCalled();
|
expect(core.setFailed).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fails on the use-existing-credentials path when the account is not allowed', async () => {
|
|
||||||
vi.mocked(core.getInput).mockImplementation(
|
|
||||||
mocks.getInput({
|
|
||||||
...mocks.USE_EXISTING_CREDENTIALS_INPUTS,
|
|
||||||
'allowed-account-ids': '999999999999',
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
|
||||||
|
|
||||||
await run();
|
|
||||||
expect(core.setFailed).toHaveBeenCalledWith(expect.stringContaining('does not match'));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('reuses existing credentials when their account is allowed', async () => {
|
|
||||||
vi.mocked(core.getInput).mockImplementation(
|
|
||||||
mocks.getInput({
|
|
||||||
...mocks.USE_EXISTING_CREDENTIALS_INPUTS,
|
|
||||||
'allowed-account-ids': '111111111111',
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
|
||||||
|
|
||||||
await run();
|
|
||||||
expect(core.notice).toHaveBeenCalledWith('Pre-existing credentials are valid. No need to generate new ones.');
|
|
||||||
expect(core.setFailed).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Global Timeout Configuration', {}, () => {
|
describe('Global Timeout Configuration', {}, () => {
|
||||||
@@ -1267,21 +1240,6 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
|
|
||||||
expect(core.setFailed).not.toHaveBeenCalled();
|
expect(core.setFailed).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('masks credentials embedded in the proxy URL', async () => {
|
|
||||||
vi.mocked(core.getInput).mockImplementation(
|
|
||||||
mocks.getInput({
|
|
||||||
...mocks.GH_OIDC_INPUTS,
|
|
||||||
'http-proxy': 'http://user:secretpass@proxy.example.com:8080',
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
await run();
|
|
||||||
|
|
||||||
expect(core.setSecret).toHaveBeenCalledWith('user');
|
|
||||||
expect(core.setSecret).toHaveBeenCalledWith('secretpass');
|
|
||||||
expect(core.setFailed).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('AWS Profile Support', {}, () => {
|
describe('AWS Profile Support', {}, () => {
|
||||||
|
|||||||
@@ -114,22 +114,6 @@ describe('Profile Manager', {}, () => {
|
|||||||
const result = stringifyIni({ dev: {} });
|
const result = stringifyIni({ dev: {} });
|
||||||
expect(result).toBe('[dev]\n');
|
expect(result).toBe('[dev]\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rejects values containing newlines', {}, () => {
|
|
||||||
expect(() =>
|
|
||||||
stringifyIni({ dev: { aws_session_token: 'token\n[injected]\ncredential_process = evil' } }),
|
|
||||||
).toThrow('must not contain newline characters');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('rejects keys containing newlines', {}, () => {
|
|
||||||
expect(() => stringifyIni({ dev: { 'key\ninjected': 'val' } })).toThrow('must not contain newline characters');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('rejects section names containing newlines', {}, () => {
|
|
||||||
expect(() => stringifyIni({ 'dev\r\n[injected]': { key: 'val' } })).toThrow(
|
|
||||||
'must not contain newline characters',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('validateProfileName', {}, () => {
|
describe('validateProfileName', {}, () => {
|
||||||
@@ -439,24 +423,6 @@ describe('Profile Manager', {}, () => {
|
|||||||
expect(configParsed['profile dev'].region).toBe('us-east-1');
|
expect(configParsed['profile dev'].region).toBe('us-east-1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('refuses to write credentials containing newlines instead of injecting profiles', {}, () => {
|
|
||||||
expect(() =>
|
|
||||||
writeProfileFiles(
|
|
||||||
'dev',
|
|
||||||
{
|
|
||||||
AccessKeyId: 'AKIAIOSFODNN7EXAMPLE',
|
|
||||||
SecretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
|
|
||||||
SessionToken: 'token\n[injected]\ncredential_process = evil-command',
|
|
||||||
},
|
|
||||||
'us-east-1',
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
).toThrow('must not contain newline characters');
|
|
||||||
|
|
||||||
const credsPath = getProfileFilePaths().credentials;
|
|
||||||
expect(fs.existsSync(credsPath)).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('uses correct section naming for default profile', {}, () => {
|
it('uses correct section naming for default profile', {}, () => {
|
||||||
writeProfileFiles(
|
writeProfileFiles(
|
||||||
'default',
|
'default',
|
||||||
|
|||||||
Reference in New Issue
Block a user