diff --git a/.github/workflows/actionlint.yaml b/.github/workflows/actionlint.yaml index 28b25c4..66e7018 100644 --- a/.github/workflows/actionlint.yaml +++ b/.github/workflows/actionlint.yaml @@ -8,7 +8,7 @@ jobs: actionlint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: "Lint workflow files" uses: docker://docker.mirror.hashicorp.services/rhysd/actionlint:latest with: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 56c98ef..1d00bea 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,283 +6,282 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 - with: - node-version: '20.9.0' + - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 + with: + node-version: "20.9.0" - - name: Setup NPM Cache - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 - with: - path: ~/.npm - key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-node- + - name: Setup NPM Cache + uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- - - name: NPM Install - run: npm ci + - name: NPM Install + run: npm ci - - name: NPM Build - run: npm run build + - name: NPM Build + run: npm run build - - name: NPM Run Test - run: npm run test + - name: NPM Run Test + run: npm run test integrationOSS: runs-on: ubuntu-latest steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: Run docker-compose - run: docker-compose up -d vault + - name: Run docker compose + run: docker compose up -d vault - - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 - with: - node-version: '20.9.0' + - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 + with: + node-version: "20.9.0" - - name: Setup NPM Cache - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 - with: - path: ~/.npm - key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-node- + - name: Setup NPM Cache + uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- - - name: NPM Install - run: npm ci + - name: NPM Install + run: npm ci - - name: NPM Build - run: npm run build + - name: NPM Build + run: npm run build - - name: NPM Run test;integration:basic - run: npm run test:integration:basic - env: - VAULT_HOST: localhost - VAULT_PORT: 8200 - CI: true + - name: NPM Run test;integration:basic + run: npm run test:integration:basic + env: + VAULT_HOST: localhost + VAULT_PORT: 8200 + CI: true integrationEnterprise: runs-on: ubuntu-latest steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: Run docker-compose - run: docker-compose up -d vault-enterprise - env: - VAULT_LICENSE_CI: ${{ secrets.VAULT_LICENSE_CI }} + - name: Run docker compose + run: docker compose up -d vault-enterprise + env: + VAULT_LICENSE_CI: ${{ secrets.VAULT_LICENSE_CI }} - - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 - with: - node-version: '20.9.0' + - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 + with: + node-version: "20.9.0" - - name: Setup NPM Cache - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 - with: - path: ~/.npm - key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-node- + - name: Setup NPM Cache + uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- - - name: NPM Install - run: npm ci + - name: NPM Install + run: npm ci - - name: NPM Build - run: npm run build + - name: NPM Build + run: npm run build - - name: NPM Run test:integration:enterprise - run: npm run test:integration:enterprise - env: - VAULT_HOST: localhost - VAULT_PORT: 8200 - CI: true + - name: NPM Run test:integration:enterprise + run: npm run test:integration:enterprise + env: + VAULT_HOST: localhost + VAULT_PORT: 8200 + CI: true e2e: runs-on: ubuntu-latest steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: Run docker-compose - run: docker-compose up -d vault + - name: Run docker compose + run: docker compose up -d vault - - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 - with: - node-version: '20.9.0' + - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 + with: + node-version: "20.9.0" - - name: Setup NPM Cache - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 - with: - path: ~/.npm - key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-node- + - name: Setup NPM Cache + uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- - - name: NPM Install - run: npm ci + - name: NPM Install + run: npm ci - - name: NPM Build - run: npm run build + - name: NPM Build + run: npm run build - - name: Setup Vault - run: node ./integrationTests/e2e/setup.js - env: - VAULT_HOST: localhost - VAULT_PORT: 8200 + - name: Setup Vault + run: node ./integrationTests/e2e/setup.js + env: + VAULT_HOST: localhost + VAULT_PORT: 8200 - - name: Test Vault Action (default KV V2) - uses: ./ - id: kv-secrets - with: - url: http://localhost:8200 - token: testtoken - secrets: | - secret/data/test secret ; - secret/data/test secret | NAMED_SECRET ; - secret/data/nested/test otherSecret ; + - name: Test Vault Action (default KV V2) + uses: ./ + id: kv-secrets + with: + url: http://localhost:8200 + token: testtoken + secrets: | + secret/data/test secret ; + secret/data/test secret | NAMED_SECRET ; + secret/data/nested/test otherSecret ; - - name: Test Vault Action (default KV V1) - uses: ./ - with: - url: http://localhost:8200 - token: testtoken - secrets: | - my-secret/test altSecret ; - my-secret/test altSecret | NAMED_ALTSECRET ; - my-secret/nested/test otherAltSecret ; + - name: Test Vault Action (default KV V1) + uses: ./ + with: + url: http://localhost:8200 + token: testtoken + secrets: | + my-secret/test altSecret ; + my-secret/test altSecret | NAMED_ALTSECRET ; + my-secret/nested/test otherAltSecret ; - - name: Test Vault Action (cubbyhole) - uses: ./ - with: - url: http://localhost:8200 - token: testtoken - secrets: | - /cubbyhole/test foo ; - /cubbyhole/test zip | NAMED_CUBBYSECRET ; + - name: Test Vault Action (cubbyhole) + uses: ./ + with: + url: http://localhost:8200 + token: testtoken + secrets: | + /cubbyhole/test foo ; + /cubbyhole/test zip | NAMED_CUBBYSECRET ; - # The ordering of these two Test Vault Action Overwrites Env Vars In Subsequent Action steps matters - # They should come before the Verify Vault Action Outputs step - - name: Test Vault Action Overwrites Env Vars In Subsequent Action (part 1/2) - uses: ./ - with: - url: http://localhost:8200/ - token: testtoken - secrets: | - secret/data/test secret | SUBSEQUENT_TEST_SECRET; + # The ordering of these two Test Vault Action Overwrites Env Vars In Subsequent Action steps matters + # They should come before the Verify Vault Action Outputs step + - name: Test Vault Action Overwrites Env Vars In Subsequent Action (part 1/2) + uses: ./ + with: + url: http://localhost:8200/ + token: testtoken + secrets: | + secret/data/test secret | SUBSEQUENT_TEST_SECRET; - - name: Test Vault Action Overwrites Env Vars In Subsequent Action (part 2/2) - uses: ./ - with: - url: http://localhost:8200/ - token: testtoken - secrets: | - secret/data/subsequent-test secret | SUBSEQUENT_TEST_SECRET; + - name: Test Vault Action Overwrites Env Vars In Subsequent Action (part 2/2) + uses: ./ + with: + url: http://localhost:8200/ + token: testtoken + secrets: | + secret/data/subsequent-test secret | SUBSEQUENT_TEST_SECRET; - - name: Test JSON Secrets - uses: ./ - with: - url: http://localhost:8200 - token: testtoken - secrets: | - secret/data/test-json-data jsonData; - secret/data/test-json-string jsonString; - secret/data/test-json-string-multiline jsonStringMultiline; - - - name: Verify Vault Action Outputs - run: npm run test:integration:e2e - env: - OTHER_SECRET_OUTPUT: ${{ steps.kv-secrets.outputs.otherSecret }} + - name: Test JSON Secrets + uses: ./ + with: + url: http://localhost:8200 + token: testtoken + secrets: | + secret/data/test-json-data jsonData; + secret/data/test-json-string jsonString; + secret/data/test-json-string-multiline jsonStringMultiline; + - name: Verify Vault Action Outputs + run: npm run test:integration:e2e + env: + OTHER_SECRET_OUTPUT: ${{ steps.kv-secrets.outputs.otherSecret }} e2e-tls: runs-on: ubuntu-latest steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: Run docker-compose - run: docker-compose up -d vault-tls + - name: Run docker compose + run: docker compose up -d vault-tls - - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 - with: - node-version: '20.9.0' + - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 + with: + node-version: "20.9.0" - - name: Setup NPM Cache - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 - with: - path: ~/.npm - key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-node- + - name: Setup NPM Cache + uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- - - name: NPM Install - run: npm ci + - name: NPM Install + run: npm ci - - name: NPM Build - run: npm run build + - name: NPM Build + run: npm run build - - name: Setup Vault - run: node ./integrationTests/e2e-tls/setup.js - env: - VAULT_HOST: localhost - VAULT_PORT: 8200 - VAULTCA: ${{ secrets.VAULTCA }} - VAULT_CLIENT_CERT: ${{ secrets.VAULT_CLIENT_CERT }} - VAULT_CLIENT_KEY: ${{ secrets.VAULT_CLIENT_KEY }} + - name: Setup Vault + run: node ./integrationTests/e2e-tls/setup.js + env: + VAULT_HOST: localhost + VAULT_PORT: 8200 + VAULTCA: ${{ secrets.VAULTCA }} + VAULT_CLIENT_CERT: ${{ secrets.VAULT_CLIENT_CERT }} + VAULT_CLIENT_KEY: ${{ secrets.VAULT_CLIENT_KEY }} - - name: Test Vault Action (default KV V2) - uses: ./ - id: kv-secrets-tls - with: - url: https://localhost:8200 - token: ${{ env.VAULT_TOKEN }} - caCertificate: ${{ secrets.VAULTCA }} - clientCertificate: ${{ secrets.VAULT_CLIENT_CERT }} - clientKey: ${{ secrets.VAULT_CLIENT_KEY }} - secrets: | - secret/data/test secret ; - secret/data/test secret | NAMED_SECRET ; - secret/data/nested/test otherSecret ; + - name: Test Vault Action (default KV V2) + uses: ./ + id: kv-secrets-tls + with: + url: https://localhost:8200 + token: ${{ env.VAULT_TOKEN }} + caCertificate: ${{ secrets.VAULTCA }} + clientCertificate: ${{ secrets.VAULT_CLIENT_CERT }} + clientKey: ${{ secrets.VAULT_CLIENT_KEY }} + secrets: | + secret/data/test secret ; + secret/data/test secret | NAMED_SECRET ; + secret/data/nested/test otherSecret ; - - name: Test Vault Action (tlsSkipVerify) - uses: ./ - with: - url: https://localhost:8200 - token: ${{ env.VAULT_TOKEN }} - tlsSkipVerify: true - clientCertificate: ${{ secrets.VAULT_CLIENT_CERT }} - clientKey: ${{ secrets.VAULT_CLIENT_KEY }} - secrets: | - secret/data/tlsSkipVerify skip ; + - name: Test Vault Action (tlsSkipVerify) + uses: ./ + with: + url: https://localhost:8200 + token: ${{ env.VAULT_TOKEN }} + tlsSkipVerify: true + clientCertificate: ${{ secrets.VAULT_CLIENT_CERT }} + clientKey: ${{ secrets.VAULT_CLIENT_KEY }} + secrets: | + secret/data/tlsSkipVerify skip ; - - name: Test Vault Action (default KV V1) - uses: ./ - with: - url: https://localhost:8200 - token: ${{ env.VAULT_TOKEN }} - caCertificate: ${{ secrets.VAULTCA }} - clientCertificate: ${{ secrets.VAULT_CLIENT_CERT }} - clientKey: ${{ secrets.VAULT_CLIENT_KEY }} - secrets: | - my-secret/test altSecret ; - my-secret/test altSecret | NAMED_ALTSECRET ; - my-secret/nested/test otherAltSecret ; + - name: Test Vault Action (default KV V1) + uses: ./ + with: + url: https://localhost:8200 + token: ${{ env.VAULT_TOKEN }} + caCertificate: ${{ secrets.VAULTCA }} + clientCertificate: ${{ secrets.VAULT_CLIENT_CERT }} + clientKey: ${{ secrets.VAULT_CLIENT_KEY }} + secrets: | + my-secret/test altSecret ; + my-secret/test altSecret | NAMED_ALTSECRET ; + my-secret/nested/test otherAltSecret ; - - name: Test Vault Action (cubbyhole) - uses: ./ - with: - url: https://localhost:8200 - token: ${{ env.VAULT_TOKEN }} - secrets: | - /cubbyhole/test foo ; - /cubbyhole/test zip | NAMED_CUBBYSECRET ; - caCertificate: ${{ secrets.VAULTCA }} - clientCertificate: ${{ secrets.VAULT_CLIENT_CERT }} - clientKey: ${{ secrets.VAULT_CLIENT_KEY }} + - name: Test Vault Action (cubbyhole) + uses: ./ + with: + url: https://localhost:8200 + token: ${{ env.VAULT_TOKEN }} + secrets: | + /cubbyhole/test foo ; + /cubbyhole/test zip | NAMED_CUBBYSECRET ; + caCertificate: ${{ secrets.VAULTCA }} + clientCertificate: ${{ secrets.VAULT_CLIENT_CERT }} + clientKey: ${{ secrets.VAULT_CLIENT_KEY }} - - name: Verify Vault Action Outputs - run: npm run test:integration:e2e-tls - env: - OTHER_SECRET_OUTPUT: ${{ steps.kv-secrets-tls.outputs.otherSecret }} + - name: Verify Vault Action Outputs + run: npm run test:integration:e2e-tls + env: + OTHER_SECRET_OUTPUT: ${{ steps.kv-secrets-tls.outputs.otherSecret }} diff --git a/.github/workflows/local-test.yaml b/.github/workflows/local-test.yaml index 21585c8..dc283d3 100644 --- a/.github/workflows/local-test.yaml +++ b/.github/workflows/local-test.yaml @@ -18,9 +18,9 @@ jobs: name: local-test runs-on: ubuntu-latest steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 + - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 with: node-version: '20.9.0' @@ -50,7 +50,7 @@ jobs: secret/data/test-json-string jsonString; secret/data/test-json-data jsonData; - - uses: actions/github-script@v7 + - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: github-token: "foobar" script: | diff --git a/CHANGELOG.md b/CHANGELOG.md index cb24162..1d7eec2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,28 @@ -## Unreleased +## 3.3.0 (March 3, 2025) + +Features: +* Wildcard secret imports can use `**` to retain case of exported env keys [GH-545](https://github.com/hashicorp/vault-action/pull/545) + +## 3.2.0 (March 3, 2025) + +Improvements: + +* Add retry for jwt auth login to fix intermittent login failures [GH-574](https://github.com/hashicorp/vault-action/pull/574) + +## 3.1.0 (January 9, 2025) Improvements: * fix wildcard handling when field contains dot [GH-542](https://github.com/hashicorp/vault-action/pull/542) +* bump body-parser from 1.20.0 to 1.20.3 +* bump braces from 3.0.2 to 3.0.3 +* bump cross-spawn from 7.0.3 to 7.0.6 +* bump micromatch from 4.0.5 to 4.0.8 Features: * `secretId` is no longer required for approle to support advanced use cases like machine login when `bind_secret_id` is false. [GH-522](https://github.com/hashicorp/vault-action/pull/522) -* Wildcard secret imports can use `**` to retain case of exported env keys [GH-545](https://github.com/hashicorp/vault-action/pull/545) +* Use `pki` configuration to generate certificates from Vault [GH-564](https://github.com/hashicorp/vault-action/pull/564) ## 3.0.0 (February 15, 2024) diff --git a/Makefile b/Makefile index fef4953..137dbdb 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,3 @@ .PHONY: local-test local-test: - docker compose down; docker-compose up -d vault && act workflow_dispatch -j local-test -W .github/workflows/local-test.yaml + docker compose down; docker compose up -d vault && act workflow_dispatch -j local-test -W .github/workflows/local-test.yaml diff --git a/README.md b/README.md index 889e65a..b3939f5 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ is not meant to modify Vault’s state. - [Simple Key](#simple-key) - [Set Output Variable Name](#set-output-variable-name) - [Multiple Secrets](#multiple-secrets) + - [KV secrets engine version 2](#kv-secrets-engine-version-2) - [Other Secret Engines](#other-secret-engines) - [Adding Extra Headers](#adding-extra-headers) - [HashiCorp Cloud Platform or Vault Enterprise](#hashicorp-cloud-platform-or-vault-enterprise) @@ -45,46 +46,51 @@ is not meant to modify Vault’s state. ```yaml jobs: - build: - # ... - steps: - # ... - - name: Import Secrets - id: import-secrets - uses: hashicorp/vault-action@v2 - with: - url: https://vault.mycompany.com:8200 - token: ${{ secrets.VAULT_TOKEN }} - caCertificate: ${{ secrets.VAULT_CA_CERT }} - secrets: | - secret/data/ci/aws accessKey | AWS_ACCESS_KEY_ID ; - secret/data/ci/aws secretKey | AWS_SECRET_ACCESS_KEY ; - secret/data/ci npm_token - # ... + build: + # ... + steps: + # ... + - name: Import Secrets + id: import-secrets + uses: hashicorp/vault-action@v2 + with: + url: https://vault.mycompany.com:8200 + token: ${{ secrets.VAULT_TOKEN }} + caCertificate: ${{ secrets.VAULT_CA_CERT }} + secrets: | + secret/data/ci/aws accessKey | AWS_ACCESS_KEY_ID ; + secret/data/ci/aws secretKey | AWS_SECRET_ACCESS_KEY ; + secret/data/ci npm_token + # ... ``` Retrieved secrets are available as environment variables or outputs for subsequent steps: + ```yaml #... - - name: Step following 'Import Secrets' - run: | - ACCESS_KEY_ID = "${{ env.AWS_ACCESS_KEY_ID }}" - SECRET_ACCESS_KEY = "${{ steps.import-secrets.outputs.AWS_SECRET_ACCESS_KEY }}" - # ... +- name: Step following 'Import Secrets' + run: | + ACCESS_KEY_ID = "${{ env.AWS_ACCESS_KEY_ID }}" + SECRET_ACCESS_KEY = "${{ steps.import-secrets.outputs.AWS_SECRET_ACCESS_KEY }}" + +# ... ``` -If your project needs a format other than env vars and step outputs, you can use additional steps to transform them into the desired format. +If your project needs a format other than env vars and step outputs, you can use additional steps to transform them into the desired format. For example, a common pattern is to save all the secrets in a JSON file: + ```yaml #... - - name: Step following 'Import Secrets' - run: | - touch secrets.json - echo '${{ toJson(steps.import-secrets.outputs) }}' >> secrets.json - # ... +- name: Step following 'Import Secrets' + run: | + touch secrets.json + echo '${{ toJson(steps.import-secrets.outputs) }}' >> secrets.json + +# ... ``` Which with our example would yield a file containing: + ```json { "ACCESS_KEY_ID": "MY_KEY_ID", @@ -95,7 +101,6 @@ Which with our example would yield a file containing: Note that all secrets are masked so programs need to read the file themselves otherwise all values will be replaced with a `***` placeholder. - ## Authentication Methods Consider using a [Vault authentication method](https://www.vaultproject.io/docs/auth) such as the JWT auth method with @@ -109,7 +114,7 @@ and Vault using the Each GitHub Actions workflow receives an auto-generated OIDC token with claims to establish the identity of the workflow. -__Vault Configuration__ +**Vault Configuration**
Click to toggle instructions for configuring Vault. @@ -120,7 +125,6 @@ Pass the following parameters to your auth method configuration: - `oidc_discovery_url`: `https://token.actions.githubusercontent.com` - `bound_issuer`: `https://token.actions.githubusercontent.com` - Configure a [Vault role](https://www.vaultproject.io/api/auth/jwt#create-role) for the auth method. - `role_type`: `jwt` @@ -136,12 +140,12 @@ Configure a [Vault role](https://www.vaultproject.io/api/auth/jwt#create-role) f - For wildcard (non-exact) matches, use `bound_claims`. - - `bound_claims_type`: `glob` + - `bound_claims_type`: `glob` - - `bound_claims`: JSON object. Maps one or more claim names to corresponding wildcard values. - ```json - {"sub": "repo:/*"} - ``` + - `bound_claims`: JSON object. Maps one or more claim names to corresponding wildcard values. + ```json + { "sub": "repo:/*" } + ``` - For exact matches, use `bound_subject`. @@ -154,17 +158,17 @@ Configure a [Vault role](https://www.vaultproject.io/api/auth/jwt#create-role) f
-__GitHub Actions Workflow__ +**GitHub Actions Workflow** In the GitHub Actions workflow, the workflow needs permissions to read contents and write the ID token. ```yaml jobs: - retrieve-secret: - permissions: - contents: read - id-token: write + retrieve-secret: + permissions: + contents: read + id-token: write ``` In the action, provide the name of the Vault role you created to the `role` parameter. @@ -319,7 +323,7 @@ with: The `secrets` parameter is a set of multiple secret requests separated by the `;` character. -Each secret request consists of the `path` and the `key` of the desired secret, and optionally the desired Env Var output name. +Each secret request consists of the `path` and the `key` of the desired secret, and optionally the desired Env Var output name. Note that the selector is using [JSONata](https://docs.jsonata.org/overview.html) and certain characters in keys may need to be escaped. ```raw @@ -332,7 +336,7 @@ To retrieve a key `npmToken` from path `secret/data/ci` that has value `somelong ```yaml with: - secrets: secret/data/ci npmToken + secrets: secret/data/ci npmToken ``` `vault-action` will automatically normalize the given secret selector key, and set the follow as environment variables for the following steps in the current job: @@ -345,12 +349,12 @@ You can also access the secret via outputs: ```yaml steps: - # ... - - name: Import Secrets - id: secrets - # Import config... - - name: Sensitive Operation - run: "my-cli --token '${{ steps.secrets.outputs.npmToken }}'" + # ... + - name: Import Secrets + id: secrets + # Import config... + - name: Sensitive Operation + run: "my-cli --token '${{ steps.secrets.outputs.npmToken }}'" ``` _**Note:** If you'd like to only use outputs and disable automatic environment variables, you can set the `exportEnv` option to `false`._ @@ -361,7 +365,7 @@ However, if you want to set it to a specific name, say `NPM_TOKEN`, you could do ```yaml with: - secrets: secret/data/ci npmToken | NPM_TOKEN + secrets: secret/data/ci npmToken | NPM_TOKEN ``` With that, `vault-action` will now use your requested name and output: @@ -378,7 +382,6 @@ steps: # Import config... - name: Sensitive Operation run: "my-cli --token '${{ steps.secrets.outputs.NPM_TOKEN }}'" - ``` ### Multiple Secrets @@ -387,19 +390,21 @@ This action can take multi-line input, so say you had your AWS keys stored in a ```yaml with: - secrets: | - secret/data/ci/aws accessKey | AWS_ACCESS_KEY_ID ; - secret/data/ci/aws secretKey | AWS_SECRET_ACCESS_KEY + secrets: | + secret/data/ci/aws accessKey | AWS_ACCESS_KEY_ID ; + secret/data/ci/aws secretKey | AWS_SECRET_ACCESS_KEY ``` -You can specify a wildcard * for the key name to get all keys in the path. If you provide an output name with the wildcard, the name will be prepended to the key name: + +You can specify a wildcard \* for the key name to get all keys in the path. If you provide an output name with the wildcard, the name will be prepended to the key name: ```yaml with: - secrets: | - secret/data/ci/aws * | MYAPP_ ; + secrets: | + secret/data/ci/aws * | MYAPP_ ; ``` -When using the `exportEnv` option all exported keys will be normalized to uppercase. For example, the key `SecretKey` would be exported as `MYAPP_SECRETKEY`. You disable uppercase normalization by specifying double asterisks `**` in the selector path: +When using the `exportEnv` option all exported keys will be normalized to uppercase. For example, the key `SecretKey` would be exported as `MYAPP_SECRETKEY`. +You can disable uppercase normalization by specifying double asterisks `**` in the selector path: ```yaml with: @@ -407,20 +412,59 @@ with: secret/data/ci/aws ** | MYAPP_ ; ``` +### KV secrets engine version 2 + +When accessing secrets from the KV secrets engine version 2, Vault Action +requires the full path to the secret. This is the same path that would be used +in a Vault policy for the secret. You can find the full path to your secret by +performing a `kv get` command like the following: + +```bash +$ vault kv get secret/test +== Secret Path == +secret/data/test + +... +``` + +Note that the full path is not `secret/test`, but `secret/data/test`. + +## PKI Certificate Requests + +You can use the `pki` option to generate a certificate and private key for a given role. + +````yaml +with: + pki: | + pki/issue/rolename {"common_name": "role.mydomain.com", "ttl": "1h"} ; + pki/issue/otherrole {"common_name": "otherrole.mydomain.com", "ttl": "1h"} ; +``` + +Resulting in: + +```bash +ROLENAME_CA=-----BEGIN CERTIFICATE-----... +ROLENAME_CERT=-----BEGIN CERTIFICATE-----... +ROLENAME_KEY=-----BEGIN RSA PRIVATE KEY-----... +ROLENAME_CA_CHAIN=-----BEGIN CERTIFICATE-----... +OTHERROLE_CA=-----BEGIN CERTIFICATE-----... +OTHERROLE_CERT=-----BEGIN CERTIFICATE-----... +OTHERROLE_KEY=-----BEGIN RSA PRIVATE KEY-----... +OTHERROLE_CA_CHAIN=-----BEGIN CERTIFICATE-----... +```` + ## Other Secret Engines Vault Action currently supports retrieving secrets from any engine where secrets -are retrieved via `GET` requests. This means secret engines such as PKI are currently -not supported due to their requirement of sending parameters along with the request -(such as `common_name`). +are retrieved via `GET` requests, except for the PKI engine as noted above. For example, to request a secret from the `cubbyhole` secret engine: ```yaml with: - secrets: | - /cubbyhole/foo foo ; - /cubbyhole/foo zip | MY_KEY ; + secrets: | + /cubbyhole/foo foo ; + /cubbyhole/foo zip | MY_KEY ; ``` Resulting in: @@ -448,12 +492,12 @@ If you ever need to add extra headers to the vault request, say if you need to a ```yaml with: - secrets: | - secret/ci/aws accessKey | AWS_ACCESS_KEY_ID ; - secret/ci/aws secretKey | AWS_SECRET_ACCESS_KEY - extraHeaders: | - X-Secure-Id: ${{ secrets.SECURE_ID }} - X-Secure-Secret: ${{ secrets.SECURE_SECRET }} + secrets: | + secret/data/ci/aws accessKey | AWS_ACCESS_KEY_ID ; + secret/data/ci/aws secretKey | AWS_SECRET_ACCESS_KEY + extraHeaders: | + X-Secure-Id: ${{ secrets.SECURE_ID }} + X-Secure-Secret: ${{ secrets.SECURE_SECRET }} ``` This will automatically add the `x-secure-id` and `x-secure-secret` headers to every request to Vault. @@ -471,19 +515,37 @@ parameter specifying the namespace. In HCP Vault, the namespace defaults to `adm ```yaml steps: - # ... - - name: Import Secrets - uses: hashicorp/vault-action - with: - url: https://vault-enterprise.mycompany.com:8200 - caCertificate: ${{ secrets.VAULT_CA_CERT }} - method: token - token: ${{ secrets.VAULT_TOKEN }} - namespace: admin - secrets: | - secret/ci/aws accessKey | AWS_ACCESS_KEY_ID ; - secret/ci/aws secretKey | AWS_SECRET_ACCESS_KEY ; - secret/ci npm_token + # ... + - name: Import Secrets + uses: hashicorp/vault-action + with: + url: https://vault-enterprise.mycompany.com:8200 + method: token + token: ${{ secrets.VAULT_TOKEN }} + namespace: admin + secrets: | + secret/data/ci/aws accessKey | AWS_ACCESS_KEY_ID ; + secret/data/ci/aws secretKey | AWS_SECRET_ACCESS_KEY ; + secret/data/ci npm_token +``` + +Alternatively, you may need to authenticate to the root namespace and retrieve +a secret from a different namespace. To do this, do not set the `namespace` +parameter. Instead set the namespace in the secret path. For example, `/secret/data/app`: + +```yaml +steps: + # ... + - name: Import Secrets + uses: hashicorp/vault-action + with: + url: https://vault-enterprise.mycompany.com:8200 + method: token + token: ${{ secrets.VAULT_TOKEN }} + secrets: | + namespace-1/secret/data/ci/aws accessKey | AWS_ACCESS_KEY_ID ; + namespace-1/secret/data/ci/aws secretKey | AWS_SECRET_ACCESS_KEY ; + namespace-1/secret/data/ci npm_token ``` ## Reference @@ -676,9 +738,10 @@ To make it simpler to consume certain secrets as env vars, if no Env/Output Var ## Contributing If you wish to contribute to this project, the following dependencies are recommended for local development: + - [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) to install dependencies, build project and run tests - [docker](https://docs.docker.com/get-docker/) to run the pre-configured vault containers for acceptance tests -- [docker-compose](https://docs.docker.com/compose/) to spin up the pre-configured vault containers for acceptance tests +- [docker compose](https://docs.docker.com/compose/) to spin up the pre-configured vault containers for acceptance tests - [act](https://github.com/nektos/act) to run the vault-action locally ### Build @@ -691,11 +754,11 @@ $ npm install && npm run build ### Vault test instance -The Github Action needs access to a working Vault instance to function. +The Github Action needs access to a working Vault instance to function. Multiple docker configurations are available via the docker-compose.yml file to run containers compatible with the various acceptance test suites. ```sh -$ docker-compose up -d vault # Choose one of: vault, vault-enterprise, vault-tls depending on which tests you would like to run +$ docker compose up -d vault # Choose one of: vault, vault-enterprise, vault-tls depending on which tests you would like to run ``` Instead of using one of the dockerized instance, you can also use your own local or remote Vault instance by exporting these environment variables: diff --git a/action.yml b/action.yml index e3d2855..94c738a 100644 --- a/action.yml +++ b/action.yml @@ -7,6 +7,9 @@ inputs: secrets: description: 'A semicolon-separated list of secrets to retrieve. These will automatically be converted to environmental variable keys. See README for more details' required: false + pki: + description: 'A semicolon-separated list of certificates to generate. These will automatically be converted to environment variable keys. Cannot be used with "secrets". See README for more details' + required: false namespace: description: 'The Vault namespace from which to query secrets. Vault Enterprise only, unset by default' required: false diff --git a/dist/index.js b/dist/index.js index 3f08f86..adb2590 100644 --- a/dist/index.js +++ b/dist/index.js @@ -18537,7 +18537,7 @@ const jsonata = __nccwpck_require__(4245); const { normalizeOutputKey } = __nccwpck_require__(1608); const { WILDCARD, WILDCARD_UPPERCASE } = __nccwpck_require__(4438); -const { auth: { retrieveToken }, secrets: { getSecrets } } = __nccwpck_require__(4351); +const { auth: { retrieveToken }, secrets: { getSecrets }, pki: { getCertificates } } = __nccwpck_require__(4351); const AUTH_METHODS = ['approle', 'token', 'github', 'jwt', 'kubernetes', 'ldap', 'userpass']; const ENCODING_TYPES = ['base64', 'hex', 'utf8']; @@ -18553,6 +18553,16 @@ async function exportSecrets() { const secretsInput = core.getInput('secrets', { required: false }); const secretRequests = parseSecretsInput(secretsInput); + const pkiInput = core.getInput('pki', { required: false }); + let pkiRequests = []; + if (pkiInput) { + if (secretsInput) { + throw Error('You cannot provide both "secrets" and "pki" inputs.'); + } + + pkiRequests = parsePkiInput(pkiInput); + } + const secretEncodingType = core.getInput('secretEncodingType', { required: false }); const vaultMethod = (core.getInput('method', { required: false }) || 'token').toLowerCase(); @@ -18615,12 +18625,12 @@ async function exportSecrets() { core.exportVariable('VAULT_TOKEN', `${vaultToken}`); } - const requests = secretRequests.map(request => { - const { path, selector } = request; - return request; - }); - - const results = await getSecrets(requests, client); + let results = []; + if (pkiRequests.length > 0) { + results = await getCertificates(pkiRequests, client); + } else { + results = await getSecrets(secretRequests, client); + } for (const result of results) { @@ -18659,6 +18669,43 @@ async function exportSecrets() { * @property {string} selector */ +/** + * Parses a pki input string into key paths and the request parameters. + * @param {string} pkiInput + */ +function parsePkiInput(pkiInput) { + if (!pkiInput) { + return [] + } + + const secrets = pkiInput + .split(';') + .filter(key => !!key) + .map(key => key.trim()) + .filter(key => key.length !== 0); + + return secrets.map(secret => { + const path = secret.substring(0, secret.indexOf(' ')); + const parameters = secret.substring(secret.indexOf(' ') + 1); + + core.debug(`ℹ Parsing PKI: ${path} with parameters: ${parameters}`); + + if (!path || !parameters) { + throw Error(`You must provide a valid path and parameters. Input: "${secret}"`); + } + + let outputVarName = path.split('/').pop(); + let envVarName = normalizeOutputKey(outputVarName); + + return { + path, + envVarName, + outputVarName, + parameters: JSON.parse(parameters), + }; + }); +} + /** * Parses a secrets input string into key paths and their resulting environment variable name. * @param {string} secretsInput @@ -18770,6 +18817,8 @@ const fs = __nccwpck_require__(7147); const { default: got } = __nccwpck_require__(3061); const defaultKubernetesTokenPath = '/var/run/secrets/kubernetes.io/serviceaccount/token' +const retries = 5 +const retries_delay = 3000 /*** * Authenticate with Vault and retrieve a Vault token that can be used for requests. * @param {string} method @@ -18800,7 +18849,10 @@ async function retrieveToken(method, client) { const githubAudience = core.getInput('jwtGithubAudience', { required: false }); if (!privateKey) { - jwt = await core.getIDToken(githubAudience) + jwt = await retryAsyncFunction(retries, retries_delay, core.getIDToken, githubAudience) + .then((result) => { + return result; + }); } else { jwt = generateJwt(privateKey, keyPassword, Number(tokenTtl)); } @@ -18907,6 +18959,30 @@ async function getClientToken(client, method, path, payload) { } } +/*** + * Generic function for retrying an async function + * @param {number} retries + * @param {number} delay + * @param {Function} func + * @param {any[]} args + */ +async function retryAsyncFunction(retries, delay, func, ...args) { + let attempt = 0; + while (attempt < retries) { + try { + const result = await func(...args); + return result; + } catch (error) { + attempt++; + if (attempt < retries) { + await new Promise(resolve => setTimeout(resolve, delay)); + } else { + throw error; + } + } + } +} + /*** * @typedef {Object} VaultLoginResponse * @property {{ @@ -18945,10 +19021,94 @@ module.exports = { const auth = __nccwpck_require__(4915); const secrets = __nccwpck_require__(8452); +const pki = __nccwpck_require__(1973); module.exports = { auth, - secrets + secrets, + pki +}; + +/***/ }), + +/***/ 1973: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const { normalizeOutputKey } = __nccwpck_require__(1608); +const core = __nccwpck_require__(2186); + +/** A map of postfix values mapped to the key in the certificate response and a transformer function */ +const outputMap = { + cert: { key: 'certificate', tx: (v) => v }, + key: { key: 'private_key', tx: (v) => v }, + ca: { key: 'issuing_ca', tx: (v) => v }, + ca_chain: { key: 'ca_chain', tx: (v) => v.join('\n') }, +}; + +/** + * @typedef PkiRequest + * @type {object} + * @property {string} path - The path to the PKI endpoint + * @property {Record} parameters - The parameters to send to the PKI endpoint + * @property {string} envVarName - The name of the environment variable to set + * @property {string} outputVarName - The name of the output variable to set + */ + +/** + * @typedef {Object} PkiResponse + * @property {PkiRequest} request + * @property {string} value + * @property {boolean} cachedResponse + */ + +/** + * Generate and return the certificates from the PKI engine + * @param {Array} pkiRequests + * @param {import('got').Got} client + * @return {Promise>} + */ +async function getCertificates(pkiRequests, client) { + /** @type Array */ + let results = []; + + for (const pkiRequest of pkiRequests) { + const { path, parameters } = pkiRequest; + + const requestPath = `v1/${path}`; + let body; + try { + const result = await client.post(requestPath, { + body: JSON.stringify(parameters), + }); + body = result.body; + } catch (error) { + core.error(`✘ ${error.response?.body ?? error.message}`); + throw error; + } + + body = JSON.parse(body); + + core.info(`✔ Successfully generated certificate (serial number ${body.data.serial_number})`); + + Object.entries(outputMap).forEach(([key, value]) => { + const val = value.tx(body.data[value.key]); + results.push({ + request: { + ...pkiRequest, + envVarName: normalizeOutputKey(`${pkiRequest.envVarName}_${key}`, true), + outputVarName: normalizeOutputKey(`${pkiRequest.outputVarName}_${key}`), + }, + value: val, + cachedResponse: false, + }); + }); + } + + return results; +} + +module.exports = { + getCertificates, }; /***/ }), diff --git a/integrationTests/basic/integration.test.js b/integrationTests/basic/integration.test.js index d7f56e1..3060466 100644 --- a/integrationTests/basic/integration.test.js +++ b/integrationTests/basic/integration.test.js @@ -112,6 +112,69 @@ describe('integration', () => { "other-Secret-dash": 'OTHERCUSTOMSECRET', }, }); + + // Enable pki engine + try { + await got(`${vaultUrl}/v1/sys/mounts/pki`, { + method: 'POST', + headers: { + 'X-Vault-Token': vaultToken, + }, + json: { + type: 'pki' + } + }); + } catch (error) { + const {response} = error; + if (response.statusCode === 400 && response.body.includes("path is already in use")) { + // Engine might already be enabled from previous test runs + } else { + throw error; + } + } + + // Configure Root CA + try { + await got(`${vaultUrl}/v1/pki/root/generate/internal`, { + method: 'POST', + headers: { + 'X-Vault-Token': vaultToken, + }, + json: { + common_name: 'test', + ttl: '24h', + }, + }); + } catch (error) { + const {response} = error; + if (response.statusCode === 400 && response.body.includes("already exists")) { + // Root CA might already be configured from previous test runs + } else { + throw error; + } + } + + // Configure PKI Role + try { + await got(`${vaultUrl}/v1/pki/roles/Test`, { + method: 'POST', + headers: { + 'X-Vault-Token': vaultToken, + }, + json: { + allowed_domains: ['test'], + allow_bare_domains: true, + max_ttl: '1h', + }, + }); + } catch (error) { + const {response} = error; + if (response.statusCode === 400 && response.body.includes("already exists")) { + // Role might already be configured from previous test runs + } else { + throw error; + } + } }); beforeEach(() => { @@ -132,6 +195,12 @@ describe('integration', () => { .mockReturnValueOnce(secrets); } + function mockPkiInput(pki) { + when(core.getInput) + .calledWith('pki', expect.anything()) + .mockReturnValueOnce(pki); + } + function mockIgnoreNotFound(shouldIgnore) { when(core.getInput) .calledWith('ignoreNotFound', expect.anything()) @@ -162,6 +231,19 @@ describe('integration', () => { expect(core.exportVariable).toBeCalledWith('NAMED_SECRET', 'SUPERSECRET'); }) + it('gets a pki certificate', async () => { + mockPkiInput('pki/issue/Test {"common_name":"test","ttl":"1h"}'); + + await exportSecrets(); + + expect(core.exportVariable).toBeCalledTimes(4); + + expect(core.exportVariable).toBeCalledWith('TEST_KEY', expect.anything()); + expect(core.exportVariable).toBeCalledWith('TEST_CERT', expect.anything()); + expect(core.exportVariable).toBeCalledWith('TEST_CA', expect.anything()); + expect(core.exportVariable).toBeCalledWith('TEST_CA_CHAIN', expect.anything()); + }); + it('get simple secret', async () => { mockInput('secret/data/test secret'); diff --git a/integrationTests/basic/jwt_auth.test.js b/integrationTests/basic/jwt_auth.test.js index df2703b..e2744a1 100644 --- a/integrationTests/basic/jwt_auth.test.js +++ b/integrationTests/basic/jwt_auth.test.js @@ -97,6 +97,8 @@ describe('jwt auth', () => { } }); + // write the jwt config, the jwt role will be written on a per-test + // basis since the audience may vary await got(`${vaultUrl}/v1/auth/jwt/config`, { method: 'POST', headers: { @@ -108,22 +110,6 @@ describe('jwt auth', () => { } }); - await got(`${vaultUrl}/v1/auth/jwt/role/default`, { - method: 'POST', - headers: { - 'X-Vault-Token': vaultToken, - }, - json: { - role_type: 'jwt', - bound_audiences: null, - bound_claims: { - iss: 'vault-action' - }, - user_claim: 'iss', - policies: ['reader'] - } - }); - await got(`${vaultUrl}/v1/secret/data/test`, { method: 'POST', headers: { @@ -138,6 +124,24 @@ describe('jwt auth', () => { }); describe('authenticate with private key', () => { + beforeAll(async () => { + await got(`${vaultUrl}/v1/auth/jwt/role/default`, { + method: 'POST', + headers: { + 'X-Vault-Token': vaultToken, + }, + json: { + role_type: 'jwt', + bound_audiences: null, + bound_claims: { + iss: 'vault-action' + }, + user_claim: 'iss', + policies: ['reader'] + } + }); + }); + beforeEach(() => { jest.resetAllMocks(); @@ -170,6 +174,22 @@ describe('jwt auth', () => { describe('authenticate with Github OIDC', () => { beforeAll(async () => { + await got(`${vaultUrl}/v1/auth/jwt/role/default`, { + method: 'POST', + headers: { + 'X-Vault-Token': vaultToken, + }, + json: { + role_type: 'jwt', + bound_audiences: 'https://github.com/hashicorp/vault-action', + bound_claims: { + iss: 'vault-action' + }, + user_claim: 'iss', + policies: ['reader'] + } + }); + await got(`${vaultUrl}/v1/auth/jwt/role/default-sigstore`, { method: 'POST', headers: { @@ -177,7 +197,7 @@ describe('jwt auth', () => { }, json: { role_type: 'jwt', - bound_audiences: null, + bound_audiences: 'sigstore', bound_claims: { iss: 'vault-action', aud: 'sigstore', diff --git a/package-lock.json b/package-lock.json index e22ddac..e2cef18 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1396,21 +1396,22 @@ "dev": true }, "node_modules/body-parser": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", - "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "dev": true, + "license": "MIT", "dependencies": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.10.3", - "raw-body": "2.5.1", + "qs": "6.13.0", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -1445,12 +1446,13 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -1508,6 +1510,7 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -1551,14 +1554,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", "dev": true, + "license": "MIT", "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1753,10 +1774,11 @@ "dev": true }, "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -1789,10 +1811,11 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -1880,6 +1903,7 @@ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -1912,6 +1936,21 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -1968,6 +2007,39 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -2069,10 +2141,11 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -2186,14 +2259,25 @@ } }, "node_modules/get-intrinsic": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", - "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", "dev": true, + "license": "MIT", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2208,6 +2292,20 @@ "node": ">=8.0.0" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -2249,6 +2347,19 @@ "node": ">=4" } }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/got": { "version": "11.8.6", "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", @@ -2279,18 +2390,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2301,10 +2400,11 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -2313,10 +2413,11 @@ } }, "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -2340,6 +2441,7 @@ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dev": true, + "license": "MIT", "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", @@ -2356,6 +2458,7 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -2397,6 +2500,7 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -2489,6 +2593,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -3396,6 +3501,16 @@ "tmpl": "1.0.5" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -3412,12 +3527,13 @@ "dev": true }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -3602,10 +3718,14 @@ } }, "node_modules/object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3868,12 +3988,13 @@ ] }, "node_modules/qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -3892,10 +4013,11 @@ } }, "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dev": true, + "license": "MIT", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -3985,7 +4107,8 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/semver": { "version": "6.3.1", @@ -4024,14 +4147,76 @@ } }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4231,6 +4416,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -5585,21 +5771,21 @@ "dev": true }, "body-parser": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", - "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "dev": true, "requires": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.10.3", - "raw-body": "2.5.1", + "qs": "6.13.0", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -5632,12 +5818,12 @@ } }, "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" } }, "browserslist": { @@ -5702,14 +5888,24 @@ } } }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + } + }, + "call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dev": true, + "requires": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" } }, "callsites": { @@ -5849,9 +6045,9 @@ } }, "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "dev": true }, "convert-source-map": { @@ -5876,9 +6072,9 @@ } }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -5952,6 +6148,17 @@ "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true }, + "dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "requires": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -5999,6 +6206,27 @@ "is-arrayish": "^0.2.1" } }, + "es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true + }, + "es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "requires": { + "es-errors": "^1.3.0" + } + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -6075,9 +6303,9 @@ } }, "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -6166,14 +6394,21 @@ "dev": true }, "get-intrinsic": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", - "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" } }, "get-package-type": { @@ -6182,6 +6417,16 @@ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true }, + "get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "requires": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + } + }, "get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -6208,6 +6453,12 @@ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, + "gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true + }, "got": { "version": "11.8.6", "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", @@ -6232,15 +6483,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -6248,15 +6490,15 @@ "dev": true }, "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true }, "hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, "requires": { "function-bind": "^1.1.2" @@ -7081,6 +7323,12 @@ "tmpl": "1.0.5" } }, + "math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -7094,12 +7342,12 @@ "dev": true }, "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "requires": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" } }, @@ -7233,9 +7481,9 @@ } }, "object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", "dev": true }, "on-finished": { @@ -7418,12 +7666,12 @@ "dev": true }, "qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dev": true, "requires": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" } }, "random-bytes": { @@ -7433,9 +7681,9 @@ "dev": true }, "raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dev": true, "requires": { "bytes": "3.1.2", @@ -7535,14 +7783,51 @@ "dev": true }, "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + } + }, + "side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + } + }, + "side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + } + }, + "side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" } }, "signal-exit": { diff --git a/src/action.js b/src/action.js index ab53df3..3dcf1f4 100644 --- a/src/action.js +++ b/src/action.js @@ -6,7 +6,7 @@ const jsonata = require('jsonata'); const { normalizeOutputKey } = require('./utils'); const { WILDCARD, WILDCARD_UPPERCASE } = require('./constants'); -const { auth: { retrieveToken }, secrets: { getSecrets } } = require('./index'); +const { auth: { retrieveToken }, secrets: { getSecrets }, pki: { getCertificates } } = require('./index'); const AUTH_METHODS = ['approle', 'token', 'github', 'jwt', 'kubernetes', 'ldap', 'userpass']; const ENCODING_TYPES = ['base64', 'hex', 'utf8']; @@ -22,6 +22,16 @@ async function exportSecrets() { const secretsInput = core.getInput('secrets', { required: false }); const secretRequests = parseSecretsInput(secretsInput); + const pkiInput = core.getInput('pki', { required: false }); + let pkiRequests = []; + if (pkiInput) { + if (secretsInput) { + throw Error('You cannot provide both "secrets" and "pki" inputs.'); + } + + pkiRequests = parsePkiInput(pkiInput); + } + const secretEncodingType = core.getInput('secretEncodingType', { required: false }); const vaultMethod = (core.getInput('method', { required: false }) || 'token').toLowerCase(); @@ -84,12 +94,12 @@ async function exportSecrets() { core.exportVariable('VAULT_TOKEN', `${vaultToken}`); } - const requests = secretRequests.map(request => { - const { path, selector } = request; - return request; - }); - - const results = await getSecrets(requests, client); + let results = []; + if (pkiRequests.length > 0) { + results = await getCertificates(pkiRequests, client); + } else { + results = await getSecrets(secretRequests, client); + } for (const result of results) { @@ -128,6 +138,43 @@ async function exportSecrets() { * @property {string} selector */ +/** + * Parses a pki input string into key paths and the request parameters. + * @param {string} pkiInput + */ +function parsePkiInput(pkiInput) { + if (!pkiInput) { + return [] + } + + const secrets = pkiInput + .split(';') + .filter(key => !!key) + .map(key => key.trim()) + .filter(key => key.length !== 0); + + return secrets.map(secret => { + const path = secret.substring(0, secret.indexOf(' ')); + const parameters = secret.substring(secret.indexOf(' ') + 1); + + core.debug(`ℹ Parsing PKI: ${path} with parameters: ${parameters}`); + + if (!path || !parameters) { + throw Error(`You must provide a valid path and parameters. Input: "${secret}"`); + } + + let outputVarName = path.split('/').pop(); + let envVarName = normalizeOutputKey(outputVarName); + + return { + path, + envVarName, + outputVarName, + parameters: JSON.parse(parameters), + }; + }); +} + /** * Parses a secrets input string into key paths and their resulting environment variable name. * @param {string} secretsInput diff --git a/src/auth.js b/src/auth.js index 630ad1e..1d0f9b1 100644 --- a/src/auth.js +++ b/src/auth.js @@ -5,6 +5,8 @@ const fs = require('fs'); const { default: got } = require('got'); const defaultKubernetesTokenPath = '/var/run/secrets/kubernetes.io/serviceaccount/token' +const retries = 5 +const retries_delay = 3000 /*** * Authenticate with Vault and retrieve a Vault token that can be used for requests. * @param {string} method @@ -35,7 +37,10 @@ async function retrieveToken(method, client) { const githubAudience = core.getInput('jwtGithubAudience', { required: false }); if (!privateKey) { - jwt = await core.getIDToken(githubAudience) + jwt = await retryAsyncFunction(retries, retries_delay, core.getIDToken, githubAudience) + .then((result) => { + return result; + }); } else { jwt = generateJwt(privateKey, keyPassword, Number(tokenTtl)); } @@ -142,6 +147,30 @@ async function getClientToken(client, method, path, payload) { } } +/*** + * Generic function for retrying an async function + * @param {number} retries + * @param {number} delay + * @param {Function} func + * @param {any[]} args + */ +async function retryAsyncFunction(retries, delay, func, ...args) { + let attempt = 0; + while (attempt < retries) { + try { + const result = await func(...args); + return result; + } catch (error) { + attempt++; + if (attempt < retries) { + await new Promise(resolve => setTimeout(resolve, delay)); + } else { + throw error; + } + } + } +} + /*** * @typedef {Object} VaultLoginResponse * @property {{ diff --git a/src/auth.test.js b/src/auth.test.js index ac689d3..8d19142 100644 --- a/src/auth.test.js +++ b/src/auth.test.js @@ -85,4 +85,23 @@ describe("test retrival for token", () => { const url = got.post.mock.calls[0][0] expect(url).toContain('differentK8sPath') }) + + it("test retrieval with jwt", async () => { + const method = "jwt" + const jwtToken = "someTestToken" + const testRole = "testRole" + const privateKeyRaw = "" + + mockApiResponse() + mockInput("role", testRole) + mockInput("jwtPrivateKey", privateKeyRaw) + core.getIDToken = jest.fn() + core.getIDToken.mockReturnValueOnce(jwtToken) + const token = await retrieveToken(method, got) + expect(token).toEqual(testToken) + const payload = got.post.mock.calls[0][1].json + expect(payload).toEqual({ jwt: jwtToken, role: testRole }) + const url = got.post.mock.calls[0][0] + expect(url).toContain('jwt') + }) }) diff --git a/src/index.js b/src/index.js index d1d673b..b004eb2 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,9 @@ const auth = require('./auth'); const secrets = require('./secrets'); +const pki = require('./pki'); module.exports = { auth, - secrets + secrets, + pki }; \ No newline at end of file diff --git a/src/pki.js b/src/pki.js new file mode 100644 index 0000000..395a2c2 --- /dev/null +++ b/src/pki.js @@ -0,0 +1,76 @@ +const { normalizeOutputKey } = require('./utils'); +const core = require('@actions/core'); + +/** A map of postfix values mapped to the key in the certificate response and a transformer function */ +const outputMap = { + cert: { key: 'certificate', tx: (v) => v }, + key: { key: 'private_key', tx: (v) => v }, + ca: { key: 'issuing_ca', tx: (v) => v }, + ca_chain: { key: 'ca_chain', tx: (v) => v.join('\n') }, +}; + +/** + * @typedef PkiRequest + * @type {object} + * @property {string} path - The path to the PKI endpoint + * @property {Record} parameters - The parameters to send to the PKI endpoint + * @property {string} envVarName - The name of the environment variable to set + * @property {string} outputVarName - The name of the output variable to set + */ + +/** + * @typedef {Object} PkiResponse + * @property {PkiRequest} request + * @property {string} value + * @property {boolean} cachedResponse + */ + +/** + * Generate and return the certificates from the PKI engine + * @param {Array} pkiRequests + * @param {import('got').Got} client + * @return {Promise>} + */ +async function getCertificates(pkiRequests, client) { + /** @type Array */ + let results = []; + + for (const pkiRequest of pkiRequests) { + const { path, parameters } = pkiRequest; + + const requestPath = `v1/${path}`; + let body; + try { + const result = await client.post(requestPath, { + body: JSON.stringify(parameters), + }); + body = result.body; + } catch (error) { + core.error(`✘ ${error.response?.body ?? error.message}`); + throw error; + } + + body = JSON.parse(body); + + core.info(`✔ Successfully generated certificate (serial number ${body.data.serial_number})`); + + Object.entries(outputMap).forEach(([key, value]) => { + const val = value.tx(body.data[value.key]); + results.push({ + request: { + ...pkiRequest, + envVarName: normalizeOutputKey(`${pkiRequest.envVarName}_${key}`, true), + outputVarName: normalizeOutputKey(`${pkiRequest.outputVarName}_${key}`), + }, + value: val, + cachedResponse: false, + }); + }); + } + + return results; +} + +module.exports = { + getCertificates, +}; \ No newline at end of file