5
0
Fork 0
mirror of https://github.com/hashicorp/vault-action.git synced 2025-11-07 15:16:56 +00:00

Merge branch 'main' into wildcard-selector

This commit is contained in:
keattang 2023-09-08 15:37:57 +10:00 committed by GitHub
commit e716b61e11
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 1437 additions and 863 deletions

22
.github/workflows/actionlint.yaml vendored Normal file
View file

@ -0,0 +1,22 @@
name: Lint GitHub Actions Workflows
on:
push:
paths:
- '.github/workflows/**'
jobs:
actionlint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- name: "Lint workflow files"
uses: docker://docker.mirror.hashicorp.services/rhysd/actionlint:latest
with:
# Ignore actionlint errors from strict typing for outputs that we use
# in our e2e tests.
# This error occurs because vault-action's outputs are dynamic but
# actionlint expects action.yml to define them.
args: >
-ignore "property \"othersecret\" is not defined in object type"
-ignore "property \"jsonstring\" is not defined in object type"
-ignore "property \"jsonstringmultiline\" is not defined in object type"

View file

@ -1,25 +1,19 @@
on:
push:
branches:
- main
pull_request_target:
types: [opened, reopened, synchronize]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.ref }}
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- uses: actions/setup-node@v3
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
with:
node-version: '16.14.0'
- name: Setup NPM Cache
uses: actions/cache@v1
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@ -39,19 +33,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.ref }}
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- name: Run docker-compose
run: docker-compose up -d vault
- uses: actions/setup-node@v3
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
with:
node-version: '16.14.0'
- name: Setup NPM Cache
uses: actions/cache@v1
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@ -75,21 +67,19 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.ref }}
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- name: Run docker-compose
run: docker-compose up -d vault-enterprise
env:
VAULT_LICENSE_CI: ${{ secrets.VAULT_LICENSE_CI }}
- uses: actions/setup-node@v3
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
with:
node-version: '16.14.0'
- name: Setup NPM Cache
uses: actions/cache@v1
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@ -113,19 +103,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.ref }}
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- name: Run docker-compose
run: docker-compose up -d vault
- uses: actions/setup-node@v3
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
with:
node-version: '16.14.0'
- name: Setup NPM Cache
uses: actions/cache@v1
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@ -174,28 +162,55 @@ jobs:
/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;
- 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:e2e
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@v3
with:
ref: ${{ github.ref }}
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- name: Run docker-compose
run: docker-compose up -d vault-tls
- uses: actions/setup-node@v3
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
with:
node-version: '16.14.0'
- name: Setup NPM Cache
uses: actions/cache@v1
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@ -219,7 +234,7 @@ jobs:
- name: Test Vault Action (default KV V2)
uses: ./
id: kv-secrets
id: kv-secrets-tls
with:
url: https://localhost:8200
token: ${{ env.VAULT_TOKEN }}
@ -268,32 +283,6 @@ jobs:
clientKey: ${{ secrets.VAULT_CLIENT_KEY }}
- name: Verify Vault Action Outputs
run: npm run test:e2e-tls
run: npm run test:integration:e2e-tls
env:
OTHER_SECRET_OUTPUT: ${{ steps.kv-secrets.outputs.otherSecret }}
# Removing publish step for now.
# publish:
# if: github.event_name == 'push' && contains(github.ref, 'main')
# runs-on: ubuntu-latest
# needs: [build, integration, e2e]
# steps:
# - uses: actions/checkout@v1
# - uses: actions/setup-node@v3
# with:
# node-version: '16.14.0'
# - name: setup npm cache
# uses: actions/cache@v1
# with:
# path: ~/.npm
# key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
# restore-keys: |
# ${{ runner.os }}-node-
# - name: npm install
# run: npm ci
# - name: release
# if: success() && endsWith(github.ref, 'main')
# run: npx semantic-release
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
OTHER_SECRET_OUTPUT: ${{ steps.kv-secrets-tls.outputs.otherSecret }}

View file

@ -1,3 +1,4 @@
name: JIRA Sync
on:
issues:
types: [opened, closed, deleted, reopened]
@ -5,68 +6,12 @@ on:
types: [opened, closed, reopened]
issue_comment: # Also triggers when commenting on a PR from the conversation view
types: [created]
name: Jira Sync
jobs:
sync:
runs-on: ubuntu-latest
name: Jira sync
steps:
- name: Login
uses: atlassian/gajira-login@v2.0.0
env:
JIRA_BASE_URL: ${{ secrets.JIRA_SYNC_BASE_URL }}
JIRA_USER_EMAIL: ${{ secrets.JIRA_SYNC_USER_EMAIL }}
JIRA_API_TOKEN: ${{ secrets.JIRA_SYNC_API_TOKEN }}
- name: Preprocess
if: github.event.action == 'opened' || github.event.action == 'created'
id: preprocess
run: |
if [[ "${{ github.event_name }}" == "pull_request_target" ]]; then
echo "::set-output name=type::PR"
else
echo "::set-output name=type::ISS"
fi
- name: Create ticket
if: github.event.action == 'opened'
uses: tomhjp/gh-action-jira-create@v0.2.0
with:
project: VAULT
issuetype: "GH Issue"
summary: "${{ github.event.repository.name }} [${{ steps.preprocess.outputs.type }} #${{ github.event.issue.number || github.event.pull_request.number }}]: ${{ github.event.issue.title || github.event.pull_request.title }}"
description: "${{ github.event.issue.body || github.event.pull_request.body }}\n\n_Created from GitHub Action for ${{ github.event.issue.html_url || github.event.pull_request.html_url }} from ${{ github.actor }}_"
# customfield_10089 is Issue Link custom field
# customfield_10091 is team custom field
extraFields: '{"fixVersions": [{"name": "TBD"}], "customfield_10091": ["ecosystem", "applications"], "customfield_10089": "${{ github.event.issue.html_url || github.event.pull_request.html_url }}"}'
- name: Search
if: github.event.action != 'opened'
id: search
uses: tomhjp/gh-action-jira-search@v0.2.1
with:
# cf[10089] is Issue Link custom field
jql: 'project = "VAULT" and cf[10089]="${{ github.event.issue.html_url || github.event.pull_request.html_url }}"'
- name: Sync comment
if: github.event.action == 'created' && steps.search.outputs.issue
uses: tomhjp/gh-action-jira-comment@v0.2.0
with:
issue: ${{ steps.search.outputs.issue }}
comment: "${{ github.actor }} ${{ github.event.review.state || 'commented' }}:\n\n${{ github.event.comment.body || github.event.review.body }}\n\n${{ github.event.comment.html_url || github.event.review.html_url }}"
- name: Close ticket
if: (github.event.action == 'closed' || github.event.action == 'deleted') && steps.search.outputs.issue
uses: atlassian/gajira-transition@v2.0.1
with:
issue: ${{ steps.search.outputs.issue }}
transition: Closed
- name: Reopen ticket
if: github.event.action == 'reopened' && steps.search.outputs.issue
uses: atlassian/gajira-transition@v2.0.1
with:
issue: ${{ steps.search.outputs.issue }}
transition: "Pending Triage"
uses: hashicorp/vault-workflows-common/.github/workflows/jira.yaml@main
secrets:
JIRA_SYNC_BASE_URL: ${{ secrets.JIRA_SYNC_BASE_URL }}
JIRA_SYNC_USER_EMAIL: ${{ secrets.JIRA_SYNC_USER_EMAIL }}
JIRA_SYNC_API_TOKEN: ${{ secrets.JIRA_SYNC_API_TOKEN }}
with:
teams-array: '["ecosystem", "applications-eco"]'

View file

@ -1,24 +1,61 @@
# This is a sample workflow to help test contributions
# Change the branch name, url and token to fit with your own environment
# Use 'on: push' instead of 'on: local-test' if you wish to run the test on github
# If running locally with act, run the workflow with 'act local-test'
# To run this locally with act use:
# act workflow_dispatch -j local-test
#
# If you have permissions, you can run this workflow via the GitHub UI.
# Otherwise, use 'on: push' instead of 'on: workflow_dispatch'.
# Don't forget to revert the file changes and invalidate any tokens that were committed before opening a pull-request
on: local-test
# Don't forget to revert the file changes and invalidate any tokens that were
# committed before opening a pull request.
on: workflow_dispatch
name: local-test
jobs:
build:
local-test:
name: local-test
runs-on: ubuntu-latest
steps:
- name: Import Secrets
uses: hashicorp/vault-action@YOUR_BRANCH_NAME
with:
url: http://localhost:8200
method: token
token: testtoken
secrets: |
secret/data/test secret | SAMPLE_SECRET;
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
with:
node-version: '16.14.0'
- name: NPM Install
run: npm ci
- name: NPM Build
run: npm run build
- name: Setup Vault
run: node ./integrationTests/e2e/setup.js
env:
VAULT_HOST: localhost
VAULT_PORT: 8200
- name: Import Secrets
id: import-secrets
# use the local changes
uses: ./
# run against a specific version of vault-action
# uses: hashicorp/vault-action@v2.1.2
with:
url: http://localhost:8200
method: token
token: testtoken
secrets: |
secret/data/test-json-string jsonString;
- name: Check Secrets
run: |
touch secrets.json
echo "${{ steps.import-secrets.outputs.jsonString }}" >> secrets.json
- name: Check json file format
run: |
echo
cat secrets.json
jq -c . < secrets.json

3
.gitignore vendored
View file

@ -59,3 +59,6 @@ typings/
# next.js build output
.next
# GoLand IDE project files
.idea

View file

@ -1,5 +1,47 @@
## Unreleased
* Add changes here
## 2.7.3 (July 13, 2023)
Bugs:
* Revert to the handling of secrets in JSON format since v2.1.2 [GH-478](https://github.com/hashicorp/vault-action/pull/478)
## 2.7.2 (July 6, 2023)
Bugs:
* Fix a regression that broke support for secrets in JSON format [GH-473](https://github.com/hashicorp/vault-action/pull/473)
## 2.7.1 (July 3, 2023)
Bugs:
* Revert [GH-466](https://github.com/hashicorp/vault-action/pull/466) which caused a regression in secrets stored as JSON strings [GH-471](https://github.com/hashicorp/vault-action/pull/471)
## 2.7.0 (June 21, 2023)
Bugs:
* Fix a regression that broke support for secrets in JSON format [GH-466](https://github.com/hashicorp/vault-action/pull/466)
Improvements:
* Fix a warning about outputToken being an unexpected input [GH-461](https://github.com/hashicorp/vault-action/pull/461)
## 2.6.0 (June 7, 2023)
Features:
* Add ability to set the `vault_token` output to contain the Vault token after authentication [GH-441](https://github.com/hashicorp/vault-action/pull/441)
* Add support for userpass and ldap authentication methods [GH-440](https://github.com/hashicorp/vault-action/pull/440)
* Define an output, `errorMessage`, for vault-action's error messages so subsequent steps can read the errors [GH-446](https://github.com/hashicorp/vault-action/pull/446)
Bugs:
* Handle undefined response in getSecrets error handler [GH-431](https://github.com/hashicorp/vault-action/pull/431)
## 2.5.0 (Jan 26th, 2023)
Features:
@ -32,7 +74,7 @@ Bugs:
* Errors due to replication delay for tokens will now be retried [GH-333](https://github.com/hashicorp/vault-action/pull/333)
Improvements:
* bump got from 11.5.1 to 11.8.5 [GH-344](https://github.com/hashicorp/vault-action/pull/344)
* bump got from 11.5.1 to 11.8.5 [GH-344](https://github.com/hashicorp/vault-action/pull/344)
## 2.4.1 (April 28th, 2022)
@ -40,11 +82,11 @@ Improvements:
* Make secrets parameter optional [GH-299](https://github.com/hashicorp/vault-action/pull/299)
* auth/jwt: make "role" input optional [GH-291](https://github.com/hashicorp/vault-action/pull/291)
* Write a better error message when secret not found [GH-306](https://github.com/hashicorp/vault-action/pull/306)
* bump jest-when from 2.7.2 to 3.5.1 [GH-294](https://github.com/hashicorp/vault-action/pull/294)
* bump node-fetch from 2.6.1 to 2.6.7 [GH-308](https://github.com/hashicorp/vault-action/pull/308)
* bump @types/jest from 26.0.23 to 27.4.1 [GH-297](https://github.com/hashicorp/vault-action/pull/297)
* bump trim-off-newlines from 1.0.1 to 1.0.3 [GH-309](https://github.com/hashicorp/vault-action/pull/309)
* bump moment from 2.28.0 to 2.29.2 [GH-304](https://github.com/hashicorp/vault-action/pull/304)
* bump jest-when from 2.7.2 to 3.5.1 [GH-294](https://github.com/hashicorp/vault-action/pull/294)
* bump node-fetch from 2.6.1 to 2.6.7 [GH-308](https://github.com/hashicorp/vault-action/pull/308)
* bump @types/jest from 26.0.23 to 27.4.1 [GH-297](https://github.com/hashicorp/vault-action/pull/297)
* bump trim-off-newlines from 1.0.1 to 1.0.3 [GH-309](https://github.com/hashicorp/vault-action/pull/309)
* bump moment from 2.28.0 to 2.29.2 [GH-304](https://github.com/hashicorp/vault-action/pull/304)
* bump @types/got from 9.6.11 to 9.6.12 [GH-266](https://github.com/hashicorp/vault-action/pull/266)
## 2.4.0 (October 21st, 2021)

3
Makefile Normal file
View file

@ -0,0 +1,3 @@
.PHONY: local-test
local-test:
docker compose down; docker-compose up -d vault && act workflow_dispatch -j local-test

101
README.md
View file

@ -19,6 +19,8 @@ A helper action for easily pulling secrets from HashiCorp Vault™.
- [GitHub](#github)
- [JWT with OIDC Provider](#jwt-with-oidc-provider)
- [Kubernetes](#kubernetes)
- [Userpass](#userpass)
- [Ldap](#ldap)
- [Other Auth Methods](#other-auth-methods)
- [Key Syntax](#key-syntax)
- [Simple Key](#simple-key)
@ -44,6 +46,7 @@ jobs:
steps:
# ...
- name: Import Secrets
id: import-secrets
uses: hashicorp/vault-action@v2
with:
url: https://vault.mycompany.com:8200
@ -56,6 +59,39 @@ jobs:
# ...
```
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 }}"
# ...
```
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
# ...
```
Which with our example would yield a file containing:
```json
{
"ACCESS_KEY_ID": "MY_KEY_ID",
"SECRET_ACCESS_KEY": "MY_SECRET_KEY",
"NPM_TOKEN": "MY_NPM_TOKEN"
}
```
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
@ -222,6 +258,40 @@ with:
kubernetesTokenPath: /var/run/secrets/kubernetes.io/serviceaccount/token # default token path
```
### Userpass
The [Userpass auth method](https://developer.hashicorp.com/vault/docs/auth/userpass) allows
your GitHub Actions workflow to authenticate to Vault with a username and password.
Set the username and password as GitHub secrets and pass them to the
`username` and `password` parameters.
This is not the same as ldap or okta auth methods.
```yaml
with:
url: https://vault.mycompany.com:8200
caCertificate: ${{ secrets.VAULT_CA_CERT }}
method: userpass
username: ${{ secrets.VAULT_USERNAME }}
password: ${{ secrets.VAULT_PASSWORD }}
```
### Ldap
The [LDAP auth method](https://developer.hashicorp.com/vault/docs/auth/ldap) allows
your GitHub Actions workflow to authenticate to Vault with a username and password inturn verfied with ldap servers.
Set the username and password as GitHub secrets and pass them to the
`username` and `password` parameters.
```yaml
with:
url: https://vault.mycompany.com:8200
caCertificate: ${{ secrets.VAULT_CA_CERT }}
method: ldap
username: ${{ secrets.VAULT_USERNAME }}
password: ${{ secrets.VAULT_PASSWORD }}
```
### Other Auth Methods
If any other method is specified and you provide an `authPayload`, the action will
@ -231,7 +301,8 @@ attempt to `POST` to `auth/${method}/login` with the provided payload and parse
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
{{ Secret Path }} {{ Secret Key or Selector }} | {{ Env/Output Variable Name }}
@ -410,10 +481,13 @@ Here are all the inputs available through `with`:
| `jwtGithubAudience` | Identifies the recipient ("aud" claim) that the JWT is intended for |`sigstore`| |
| `jwtTtl` | Time in seconds, after which token expires | | 3600 |
| `kubernetesTokenPath` | The path to the service-account secret with the jwt token for kubernetes based authentication |`/var/run/secrets/kubernetes.io/serviceaccount/token` | |
| `username` | The username of the user to log in to Vault as. Available to both Userpass and LDAP auth methods | | |
| `password` | The password of the user to log in to Vault as. Available to both Userpass and LDAP auth methods | | |
| `authPayload` | The JSON payload to be sent to Vault when using a custom authentication method. | | |
| `extraHeaders` | A string of newline separated extra headers to include on every request. | | |
| `exportEnv` | Whether or not export secrets as environment variables. | `true` | |
| `exportToken` | Whether or not export Vault token as environment variables (i.e VAULT_TOKEN). | `false` | |
| `outputToken` | Whether or not to set the `vault_token` output to contain the Vault token after authentication. | `false` | |
| `caCertificate` | Base64 encoded CA certificate the server certificate was signed with. | | |
| `clientCertificate` | Base64 encoded client certificate the action uses to authenticate with Vault when mTLS is enabled. | | |
| `clientKey` | Base64 encoded client key the action uses to authenticate with Vault when mTLS is enabled. | | |
@ -479,18 +553,23 @@ $ npm run test:integration:basic # Choose one of: basic, enterprise, e2e, e2e-tl
### Running the action locally
You can use the [act](https://github.com/nektos/act) command to test your changes locally if desired. Unfortunately it is not currently possible to use uncommitted local changes for a shared workfow. You will still need to push
the changes you would like to validate beforehand. Even if a commit is necessary, this is still a more detailed and faster feedback loop than waiting for the action to be executed by Github in a different repository.
You can use the [act](https://github.com/nektos/act) command to test your
changes locally.
Edit the ./.github/workflows/local-test.yaml file and add any steps necessary
to test your changes. You may have to additionally edit the Vault url, token
and secret path if you are not using one of the provided containerized
instances. The `local-test` job will call the ./integrationTests/e2e/setup.js
script to bootstrap your local Vault instance with secrets.
Run your feature branch locally:
Push your changes into a feature branch.
```sh
$ git checkout -b my-feature-branch
$ git commit -m "testing new changes"
$ git push
act workflow_dispatch -j local-test
```
Edit the ./.github/workflows/local-test.yaml file to use your new feature branch. You may have to additionally edit the vault url, token and secret path if you are not using one of the provided containerized instance.
Run your feature branch locally.
Or use the provided make target which will also spin up a Vault container:
```sh
$ act local-test
```
make local-test
```

View file

@ -36,6 +36,12 @@ inputs:
description: 'The path to the Kubernetes service account secret'
required: false
default: '/var/run/secrets/kubernetes.io/serviceaccount/token'
username:
description: 'The username of the user to log in to Vault as. Available to both Userpass and LDAP auth methods'
required: false
password:
description: 'The password of the user to log in to Vault as. Available to both Userpass and LDAP auth methods'
required: false
authPayload:
description: 'The JSON payload to be sent to Vault when using a custom authentication method.'
required: false
@ -50,6 +56,10 @@ inputs:
description: 'Whether or not export Vault token as environment variables.'
default: 'false'
required: false
outputToken:
description: 'Whether or not to set the `vault_token` output to contain the Vault token after authentication.'
default: 'false'
required: false
caCertificate:
description: 'Base64 encoded CA certificate to verify the Vault server certificate.'
required: false

33
dist/index.js vendored
View file

@ -18521,7 +18521,7 @@ const { WILDCARD } = __nccwpck_require__(4438);
const { auth: { retrieveToken }, secrets: { getSecrets } } = __nccwpck_require__(4351);
const AUTH_METHODS = ['approle', 'token', 'github', 'jwt', 'kubernetes'];
const AUTH_METHODS = ['approle', 'token', 'github', 'jwt', 'kubernetes', 'ldap', 'userpass'];
const ENCODING_TYPES = ['base64', 'hex', 'utf8'];
async function exportSecrets() {
@ -18529,6 +18529,7 @@ async function exportSecrets() {
const vaultNamespace = core.getInput('namespace', { required: false });
const extraHeaders = parseHeadersInput('extraHeaders', { required: false });
const exportEnv = core.getInput('exportEnv', { required: false }) != 'false';
const outputToken = (core.getInput('outputToken', { required: false }) || 'false').toLowerCase() != 'false';
const exportToken = (core.getInput('exportToken', { required: false }) || 'false').toLowerCase() != 'false';
const secretsInput = core.getInput('secrets', { required: false });
@ -18585,11 +18586,14 @@ async function exportSecrets() {
}
const vaultToken = await retrieveToken(vaultMethod, got.extend(defaultOptions));
core.setSecret(vaultToken)
defaultOptions.headers['X-Vault-Token'] = vaultToken;
const client = got.extend(defaultOptions);
if (outputToken === true) {
core.setOutput('vault_token', `${vaultToken}`);
}
if (exportToken === true) {
command.issue('add-mask', vaultToken);
core.exportVariable('VAULT_TOKEN', `${vaultToken}`);
}
@ -18619,7 +18623,7 @@ async function exportSecrets() {
for (const line of value.replace(/\r/g, '').split('\n')) {
if (line.length > 0) {
command.issue('add-mask', line);
core.setSecret(line);
}
}
if (exportEnv) {
@ -18754,7 +18758,8 @@ const defaultKubernetesTokenPath = '/var/run/secrets/kubernetes.io/serviceaccoun
* @param {import('got').Got} client
*/
async function retrieveToken(method, client) {
const path = core.getInput('path', { required: false }) || method;
let path = core.getInput('path', { required: false }) || method;
path = `v1/auth/${path}/login`
switch (method) {
case 'approle': {
@ -18793,6 +18798,13 @@ async function retrieveToken(method, client) {
}
return await getClientToken(client, method, path, { jwt: data, role: role })
}
case 'userpass':
case 'ldap': {
const username = core.getInput('username', { required: true });
const password = core.getInput('password', { required: true });
path = path + `/${username}`
return await getClientToken(client, method, path, { password: password })
}
default: {
if (!method || method === 'token') {
@ -18850,12 +18862,12 @@ async function getClientToken(client, method, path, payload) {
responseType,
};
core.debug(`Retrieving Vault Token from v1/auth/${path}/login endpoint`);
core.debug(`Retrieving Vault Token from ${path} endpoint`);
/** @type {import('got').Response<VaultLoginResponse>} */
let response;
try {
response = await client.post(`v1/auth/${path}/login`, options);
response = await client.post(`${path}`, options);
} catch (err) {
if (err instanceof got.HTTPError) {
throw Error(`failed to retrieve vault token. code: ${err.code}, message: ${err.message}, vaultResponse: ${JSON.stringify(err.response.body)}`)
@ -18966,7 +18978,7 @@ async function getSecrets(secretRequests, client) {
responseCache.set(requestPath, body);
} catch (error) {
const {response} = error;
if (response.statusCode === 404) {
if (response?.statusCode === 404) {
throw Error(`Unable to retrieve result for "${path}" because it was not found: ${response.body.trim()}`)
}
throw error
@ -19018,13 +19030,14 @@ async function getSecrets(secretRequests, client) {
);
}
}
return results;
}
/**
* Uses a Jsonata selector retrieve a bit of data from the result
* @param {object} data
* @param {string} selector
* @param {object} data
* @param {string} selector
*/
async function selectData(data, selector) {
const ata = jsonata(selector);
@ -19085,6 +19098,7 @@ module.exports = {
selectData
}
/***/ }),
/***/ 1608:
@ -19297,6 +19311,7 @@ const { exportSecrets } = __nccwpck_require__(3348);
try {
await core.group('Get Vault Secrets', exportSecrets);
} catch (error) {
core.setOutput("errorMessage", error.message);
core.setFailed(error.message);
}
})();

View file

@ -2,7 +2,7 @@
version: "3.0"
services:
vault:
image: vault:latest
image: hashicorp/vault:latest
environment:
VAULT_DEV_ROOT_TOKEN_ID: testtoken
ports:
@ -17,7 +17,7 @@ services:
- 8200:8200
privileged: true
vault-tls:
image: vault:latest
image: hashicorp/vault:latest
hostname: vault-tls
environment:
VAULT_CAPATH: /etc/vault/ca.crt

View file

@ -0,0 +1,134 @@
jest.mock('@actions/core');
jest.mock('@actions/core/lib/command');
const core = require('@actions/core');
const got = require('got');
const { when } = require('jest-when');
const { exportSecrets } = require('../../src/action');
const vaultUrl = `http://${process.env.VAULT_HOST || 'localhost'}:${process.env.VAULT_PORT || '8200'}`;
const vaultToken = `${process.env.VAULT_TOKEN || 'testtoken'}`
describe('authenticate with approle', () => {
let roleId;
let secretId;
beforeAll(async () => {
try {
// Verify Connection
await got(`${vaultUrl}/v1/secret/config`, {
headers: {
'X-Vault-Token': vaultToken,
},
});
await got(`${vaultUrl}/v1/secret/data/approle-test`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken,
},
json: {
data: {
secret: 'SUPERSECRET_WITH_APPROLE',
},
},
});
// Enable approle
try {
await got(`${vaultUrl}/v1/sys/auth/approle`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken
},
json: {
type: 'approle'
},
});
} catch (error) {
const {response} = error;
if (response.statusCode === 400 && response.body.includes("path is already in use")) {
// Approle might already be enabled from previous test runs
} else {
throw error;
}
}
// Create policies
await got(`${vaultUrl}/v1/sys/policies/acl/test`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken
},
json: {
"name":"test",
"policy":"path \"auth/approle/*\" {\n capabilities = [\"read\", \"list\"]\n}\npath \"auth/approle/role/my-role/role-id\"\n{\n capabilities = [\"create\", \"read\", \"update\", \"delete\", \"list\"]\n}\npath \"auth/approle/role/my-role/secret-id\"\n{\n capabilities = [\"create\", \"read\", \"update\", \"delete\", \"list\"]\n}\n\npath \"secret/data/*\" {\n capabilities = [\"list\"]\n}\npath \"secret/metadata/*\" {\n capabilities = [\"list\"]\n}\n\npath \"secret/data/approle-test\" {\n capabilities = [\"read\", \"list\"]\n}\npath \"secret/metadata/approle-test\" {\n capabilities = [\"read\", \"list\"]\n}\n"
},
});
// Create approle
await got(`${vaultUrl}/v1/auth/approle/role/my-role`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken
},
json: {
policies: 'test'
},
});
// Get role-id
const roldIdResponse = await got(`${vaultUrl}/v1/auth/approle/role/my-role/role-id`, {
headers: {
'X-Vault-Token': vaultToken
},
responseType: 'json',
});
roleId = roldIdResponse.body.data.role_id;
// Get secret-id
const secretIdResponse = await got(`${vaultUrl}/v1/auth/approle/role/my-role/secret-id`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken
},
responseType: 'json',
});
secretId = secretIdResponse.body.data.secret_id;
} catch(err) {
console.warn('Create approle', err.response.body);
throw err;
}
});
beforeEach(() => {
jest.resetAllMocks();
when(core.getInput)
.calledWith('method', expect.anything())
.mockReturnValueOnce('approle');
when(core.getInput)
.calledWith('roleId', expect.anything())
.mockReturnValueOnce(roleId);
when(core.getInput)
.calledWith('secretId', expect.anything())
.mockReturnValueOnce(secretId);
when(core.getInput)
.calledWith('url', expect.anything())
.mockReturnValueOnce(`${vaultUrl}`);
});
function mockInput(secrets) {
when(core.getInput)
.calledWith('secrets', expect.anything())
.mockReturnValueOnce(secrets);
}
it('authenticate with approle', async() => {
mockInput('secret/data/approle-test secret');
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('SECRET', 'SUPERSECRET_WITH_APPROLE');
})
});

View file

@ -0,0 +1,116 @@
jest.mock('@actions/core');
jest.mock('@actions/core/lib/command');
const core = require('@actions/core');
const got = require('got');
const { when } = require('jest-when');
const { exportSecrets } = require('../../src/action');
const vaultUrl = `http://${process.env.VAULT_HOST || 'localhost'}:${process.env.VAULT_PORT || '8200'}`;
const vaultToken = `${process.env.VAULT_TOKEN || 'testtoken'}`
describe('authenticate with userpass', () => {
const username = `testUsername`;
const password = `testPassword`;
beforeAll(async () => {
try {
// Verify Connection
await got(`${vaultUrl}/v1/secret/config`, {
headers: {
'X-Vault-Token': vaultToken,
},
});
await got(`${vaultUrl}/v1/secret/data/userpass-test`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken,
},
json: {
data: {
secret: 'SUPERSECRET_WITH_USERPASS',
},
},
});
// Enable userpass
try {
await got(`${vaultUrl}/v1/sys/auth/userpass`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken
},
json: {
type: 'userpass'
},
});
} catch (error) {
const {response} = error;
if (response.statusCode === 400 && response.body.includes("path is already in use")) {
// Userpass might already be enabled from previous test runs
} else {
throw error;
}
}
// Create policies
await got(`${vaultUrl}/v1/sys/policies/acl/userpass-test`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken
},
json: {
"name":"userpass-test",
"policy":`path \"auth/userpass/*\" {\n capabilities = [\"read\", \"list\"]\n}\npath \"auth/userpass/users/${username}\"\n{\n capabilities = [\"create\", \"read\", \"update\", \"delete\", \"list\"]\n}\n\npath \"secret/data/*\" {\n capabilities = [\"list\"]\n}\npath \"secret/metadata/*\" {\n capabilities = [\"list\"]\n}\n\npath \"secret/data/userpass-test\" {\n capabilities = [\"read\", \"list\"]\n}\npath \"secret/metadata/userpass-test\" {\n capabilities = [\"read\", \"list\"]\n}\n`
},
});
// Create user
await got(`${vaultUrl}/v1/auth/userpass/users/${username}`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken
},
json: {
password: `${password}`,
policies: 'userpass-test'
},
});
} catch(err) {
console.warn('Create user in userpass', err.response.body);
throw err;
}
});
beforeEach(() => {
jest.resetAllMocks();
when(core.getInput)
.calledWith('method', expect.anything())
.mockReturnValueOnce('userpass');
when(core.getInput)
.calledWith('username', expect.anything())
.mockReturnValueOnce(username);
when(core.getInput)
.calledWith('password', expect.anything())
.mockReturnValueOnce(password);
when(core.getInput)
.calledWith('url', expect.anything())
.mockReturnValueOnce(`${vaultUrl}`);
});
function mockInput(secrets) {
when(core.getInput)
.calledWith('secrets', expect.anything())
.mockReturnValueOnce(secrets);
}
it('authenticate with userpass', async() => {
mockInput('secret/data/userpass-test secret');
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('SECRET', 'SUPERSECRET_WITH_USERPASS');
})
});

View file

@ -9,5 +9,9 @@ describe('e2e', () => {
expect(process.env.OTHERALTSECRET).toBe("OTHERCUSTOMSECRET");
expect(process.env.FOO).toBe("bar");
expect(process.env.NAMED_CUBBYSECRET).toBe("zap");
expect(process.env.SUBSEQUENT_TEST_SECRET).toBe("SUBSEQUENT_TEST_SECRET");
expect(process.env.JSONSTRING).toBe('{"x":1,"y":"qux"}');
expect(process.env.JSONSTRINGMULTILINE).toBe('{"x": 1, "y": "q\\nux"}');
expect(process.env.JSONDATA).toBe('{"x":1,"y":"qux"}');
});
});

View file

@ -1,7 +1,9 @@
const got = require('got');
const vaultUrl = `${process.env.VAULT_HOST}:${process.env.VAULT_PORT}`;
const vaultToken = `${process.env.VAULT_TOKEN || 'testtoken'}`
const vaultToken = `${process.env.VAULT_TOKEN}` === undefined ? `${process.env.VAULT_TOKEN}` : "testtoken";
const jsonStringMultiline = '{"x": 1, "y": "q\\nux"}';
(async () => {
try {
@ -36,6 +38,44 @@ const vaultToken = `${process.env.VAULT_TOKEN || 'testtoken'}`
}
});
await got(`http://${vaultUrl}/v1/secret/data/test-json-string`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken,
},
json: {
data: {
// this is stored in Vault as a string
jsonString: '{"x":1,"y":"qux"}',
},
},
});
await got(`http://${vaultUrl}/v1/secret/data/test-json-data`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken,
},
json: {
data: {
// this is stored in Vault as a map
jsonData: {"x":1,"y":"qux"},
},
},
});
await got(`http://${vaultUrl}/v1/secret/data/test-json-string-multiline`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken,
},
json: {
data: {
jsonStringMultiline,
},
},
});
await got(`http://${vaultUrl}/v1/sys/mounts/my-secret`, {
method: 'POST',
headers: {
@ -76,6 +116,18 @@ const vaultToken = `${process.env.VAULT_TOKEN || 'testtoken'}`
zip: 'zap',
},
});
await got(`http://${vaultUrl}/v1/secret/data/subsequent-test`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken,
},
json: {
data: {
secret: 'SUBSEQUENT_TEST_SECRET',
},
},
});
} catch (error) {
console.log(error);
process.exit(1);

1378
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -8,8 +8,8 @@
"test": "jest",
"test:integration:basic": "jest -c integrationTests/basic/jest.config.js",
"test:integration:enterprise": "jest -c integrationTests/enterprise/jest.config.js",
"test:e2e": "jest -c integrationTests/e2e/jest.config.js",
"test:e2e-tls": "jest -c integrationTests/e2e-tls/jest.config.js"
"test:integration:e2e": "jest -c integrationTests/e2e/jest.config.js",
"test:integration:e2e-tls": "jest -c integrationTests/e2e-tls/jest.config.js"
},
"files": [
"src/**/*",
@ -35,8 +35,8 @@
"homepage": "https://github.com/hashicorp/vault-action#readme",
"dependencies": {
"got": "^11.8.5",
"jsonata": "^2.0.2",
"jsrsasign": "^10.6.1"
"jsonata": "^2.0.3",
"jsrsasign": "^10.8.6"
},
"peerDependencies": {
"@actions/core": ">=1 <2"
@ -44,7 +44,7 @@
"devDependencies": {
"@actions/core": "^1.10.0",
"@vercel/ncc": "^0.36.1",
"jest": "^29.4.3",
"jest": "^29.5.0",
"jest-when": "^3.5.2",
"mock-http-server": "^1.4.5"
}

View file

@ -8,7 +8,7 @@ const { WILDCARD } = require('./constants');
const { auth: { retrieveToken }, secrets: { getSecrets } } = require('./index');
const AUTH_METHODS = ['approle', 'token', 'github', 'jwt', 'kubernetes'];
const AUTH_METHODS = ['approle', 'token', 'github', 'jwt', 'kubernetes', 'ldap', 'userpass'];
const ENCODING_TYPES = ['base64', 'hex', 'utf8'];
async function exportSecrets() {
@ -16,6 +16,7 @@ async function exportSecrets() {
const vaultNamespace = core.getInput('namespace', { required: false });
const extraHeaders = parseHeadersInput('extraHeaders', { required: false });
const exportEnv = core.getInput('exportEnv', { required: false }) != 'false';
const outputToken = (core.getInput('outputToken', { required: false }) || 'false').toLowerCase() != 'false';
const exportToken = (core.getInput('exportToken', { required: false }) || 'false').toLowerCase() != 'false';
const secretsInput = core.getInput('secrets', { required: false });
@ -72,11 +73,14 @@ async function exportSecrets() {
}
const vaultToken = await retrieveToken(vaultMethod, got.extend(defaultOptions));
core.setSecret(vaultToken)
defaultOptions.headers['X-Vault-Token'] = vaultToken;
const client = got.extend(defaultOptions);
if (outputToken === true) {
core.setOutput('vault_token', `${vaultToken}`);
}
if (exportToken === true) {
command.issue('add-mask', vaultToken);
core.exportVariable('VAULT_TOKEN', `${vaultToken}`);
}
@ -106,7 +110,7 @@ async function exportSecrets() {
for (const line of value.replace(/\r/g, '').split('\n')) {
if (line.length > 0) {
command.issue('add-mask', line);
core.setSecret(line);
}
}
if (exportEnv) {

View file

@ -184,6 +184,11 @@ describe('exportSecrets', () => {
.mockReturnValueOnce(doExport);
}
function mockOutputToken(doOutput) {
when(core.getInput)
.calledWith('outputToken', expect.anything())
.mockReturnValueOnce(doOutput);
}
function mockEncodeType(doEncode) {
when(core.getInput)
.calledWith('secretEncodingType', expect.anything())
@ -215,6 +220,55 @@ describe('exportSecrets', () => {
expect(core.setOutput).toBeCalledWith('key', '1');
});
it('JSON data secret retrieval', async () => {
const jsonData = {"x":1,"y":2};
let result = JSON.stringify(jsonData);
mockInput('test key');
mockVaultData({
key: jsonData,
});
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('KEY', result);
expect(core.setOutput).toBeCalledWith('key', result);
});
it('JSON string secret retrieval', async () => {
const jsonString = '{"x":1,"y":2}';
mockInput('test key');
mockVaultData({
key: jsonString,
});
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('KEY', jsonString);
expect(core.setOutput).toBeCalledWith('key', jsonString);
});
it('multi-line JSON string secret retrieval', async () => {
const jsonString = `
{
"x":1,
"y":"bar"
}
`;
mockInput('test key');
mockVaultData({
key: jsonString,
});
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('KEY', jsonString);
expect(core.setOutput).toBeCalledWith('key', jsonString);
});
it('intl secret retrieval', async () => {
mockInput('测试 测试');
mockVaultData({
@ -323,13 +377,36 @@ describe('exportSecrets', () => {
await exportSecrets();
expect(command.issue).toBeCalledTimes(1);
expect(core.setSecret).toBeCalledTimes(2);
expect(command.issue).toBeCalledWith('add-mask', 'secret');
expect(core.setSecret).toBeCalledWith('secret');
expect(core.setOutput).toBeCalledWith('key', 'secret');
})
it('multi-line secret gets masked for each line', async () => {
it('multi-line secret', async () => {
const multiLineString = `ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSU
GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3
Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XA
NrRFi9wrf+M7Q==`;
mockInput('test key');
mockVaultData({
key: multiLineString
});
mockExportToken("false")
await exportSecrets();
expect(core.setSecret).toBeCalledTimes(5); // 1 for each non-empty line + VAULT_TOKEN
expect(core.setSecret).toBeCalledWith("ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSU");
expect(core.setSecret).toBeCalledWith("GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3");
expect(core.setSecret).toBeCalledWith("Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XA");
expect(core.setSecret).toBeCalledWith("NrRFi9wrf+M7Q==");
expect(core.setOutput).toBeCalledWith('key', multiLineString);
})
it('multi-line secret gets masked for each non-empty line', async () => {
const multiLineString = `a multi-line string
with blank lines
@ -343,10 +420,10 @@ with blank lines
await exportSecrets();
expect(command.issue).toBeCalledTimes(2); // 1 for each non-empty line.
expect(core.setSecret).toBeCalledTimes(3); // 1 for each non-empty line.
expect(command.issue).toBeCalledWith('add-mask', 'a multi-line string');
expect(command.issue).toBeCalledWith('add-mask', 'with blank lines');
expect(core.setSecret).toBeCalledWith('a multi-line string');
expect(core.setSecret).toBeCalledWith('with blank lines');
expect(core.setOutput).toBeCalledWith('key', multiLineString);
})
@ -358,4 +435,13 @@ with blank lines
expect(core.exportVariable).toBeCalledTimes(1);
expect(core.exportVariable).toBeCalledWith('VAULT_TOKEN', 'EXAMPLE');
})
it('output only Vault token, no secrets', async () => {
mockOutputToken("true")
await exportSecrets();
expect(core.setOutput).toBeCalledTimes(1);
expect(core.setOutput).toBeCalledWith('vault_token', 'EXAMPLE');
})
});

View file

@ -11,7 +11,8 @@ const defaultKubernetesTokenPath = '/var/run/secrets/kubernetes.io/serviceaccoun
* @param {import('got').Got} client
*/
async function retrieveToken(method, client) {
const path = core.getInput('path', { required: false }) || method;
let path = core.getInput('path', { required: false }) || method;
path = `v1/auth/${path}/login`
switch (method) {
case 'approle': {
@ -50,6 +51,13 @@ async function retrieveToken(method, client) {
}
return await getClientToken(client, method, path, { jwt: data, role: role })
}
case 'userpass':
case 'ldap': {
const username = core.getInput('username', { required: true });
const password = core.getInput('password', { required: true });
path = path + `/${username}`
return await getClientToken(client, method, path, { password: password })
}
default: {
if (!method || method === 'token') {
@ -107,12 +115,12 @@ async function getClientToken(client, method, path, payload) {
responseType,
};
core.debug(`Retrieving Vault Token from v1/auth/${path}/login endpoint`);
core.debug(`Retrieving Vault Token from ${path} endpoint`);
/** @type {import('got').Response<VaultLoginResponse>} */
let response;
try {
response = await client.post(`v1/auth/${path}/login`, options);
response = await client.post(`${path}`, options);
} catch (err) {
if (err instanceof got.HTTPError) {
throw Error(`failed to retrieve vault token. code: ${err.code}, message: ${err.message}, vaultResponse: ${JSON.stringify(err.response.body)}`)

View file

@ -5,6 +5,7 @@ const { exportSecrets } = require('./action');
try {
await core.group('Get Vault Secrets', exportSecrets);
} catch (error) {
core.setOutput("errorMessage", error.message);
core.setFailed(error.message);
}
})();

View file

@ -66,4 +66,4 @@ describe('exportSecrets retries', () => {
done();
});
});
});
});

View file

@ -41,7 +41,7 @@ async function getSecrets(secretRequests, client) {
responseCache.set(requestPath, body);
} catch (error) {
const {response} = error;
if (response.statusCode === 404) {
if (response?.statusCode === 404) {
throw Error(`Unable to retrieve result for "${path}" because it was not found: ${response.body.trim()}`)
}
throw error
@ -98,8 +98,8 @@ async function getSecrets(secretRequests, client) {
/**
* Uses a Jsonata selector retrieve a bit of data from the result
* @param {object} data
* @param {string} selector
* @param {object} data
* @param {string} selector
*/
async function selectData(data, selector) {
const ata = jsonata(selector);
@ -158,4 +158,4 @@ const selectAndAppendResults = async (
module.exports = {
getSecrets,
selectData
}
}