diff --git a/.github/workflows/actionlint.yaml b/.github/workflows/actionlint.yaml index b8c9d8e9..ee79a646 100644 --- a/.github/workflows/actionlint.yaml +++ b/.github/workflows/actionlint.yaml @@ -16,6 +16,4 @@ jobs: # 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" + args: '-ignore "property \"othersecret\" is not defined in object type"' diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c4de30c9..778f18c9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -153,27 +153,6 @@ jobs: my-secret/test altSecret | NAMED_ALTSECRET ; my-secret/nested/test otherAltSecret ; - # The ordering of these two Test Vault Action JSON String Format steps matters - - name: Test Vault Action JSON String Format (part 1/2) - id: import-secrets - uses: ./ - with: - url: http://localhost:8200 - token: testtoken - secrets: | - secret/data/test-json-string jsonString | JSON_STRING ; - secret/data/test-json-data jsonData | JSON_DATA ; - - - name: Test Vault Action JSON String Format (part 2/2) - run: | - echo "${{ steps.import-secrets.outputs.jsonString }}" > string.json - echo "${{ steps.import-secrets.outputs.jsonData }}" > data.json - cat string.json - cat data.json - # we should be able to parse the output as JSON - jq -c . < string.json - jq -c . < data.json - - name: Test Vault Action (cubbyhole) uses: ./ with: diff --git a/docker-compose.yml b/docker-compose.yml index 3238489f..6a8ee521 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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 diff --git a/integrationTests/e2e/e2e.test.js b/integrationTests/e2e/e2e.test.js index 3e43490f..6495d14e 100644 --- a/integrationTests/e2e/e2e.test.js +++ b/integrationTests/e2e/e2e.test.js @@ -10,10 +10,5 @@ describe('e2e', () => { expect(process.env.FOO).toBe("bar"); expect(process.env.NAMED_CUBBYSECRET).toBe("zap"); expect(process.env.SUBSEQUENT_TEST_SECRET).toBe("SUBSEQUENT_TEST_SECRET"); - - const jsonString = '{"x":1,"y":"qux"}'; - let jsonResult = JSON.stringify(jsonString); - jsonResult = jsonResult.substring(1, jsonResult.length - 1); - expect(process.env.JSON_STRING).toBe(jsonResult); }); }); diff --git a/integrationTests/e2e/setup.js b/integrationTests/e2e/setup.js index 0766820f..96f2295f 100644 --- a/integrationTests/e2e/setup.js +++ b/integrationTests/e2e/setup.js @@ -3,7 +3,6 @@ const got = require('got'); const vaultUrl = `${process.env.VAULT_HOST}:${process.env.VAULT_PORT}`; const vaultToken = `${process.env.VAULT_TOKEN}` === undefined ? `${process.env.VAULT_TOKEN}` : "testtoken"; - (async () => { try { // Verify Connection @@ -37,30 +36,6 @@ const vaultToken = `${process.env.VAULT_TOKEN}` === undefined ? `${process.env.V } }); - await got(`http://${vaultUrl}/v1/secret/data/test-json-string`, { - method: 'POST', - headers: { - 'X-Vault-Token': vaultToken, - }, - json: { - data: { - jsonString: '{"x":1,"y":"qux"}', - }, - }, - }); - - await got(`http://${vaultUrl}/v1/secret/data/test-json-data`, { - method: 'POST', - headers: { - 'X-Vault-Token': vaultToken, - }, - json: { - data: { - jsonData: {"x":1,"y":"qux"}, - }, - }, - }); - await got(`http://${vaultUrl}/v1/sys/mounts/my-secret`, { method: 'POST', headers: { diff --git a/src/action.test.js b/src/action.test.js index f6304007..49c33cd3 100644 --- a/src/action.test.js +++ b/src/action.test.js @@ -220,22 +220,6 @@ describe('exportSecrets', () => { expect(core.setOutput).toBeCalledWith('key', '1'); }); - it('json secret retrieval', async () => { - const jsonString = '{"x":1,"y":2}'; - let result = JSON.stringify(jsonString); - result = result.substring(1, result.length - 1); - - mockInput('test key'); - mockVaultData({ - key: jsonString, - }); - - await exportSecrets(); - - expect(core.exportVariable).toBeCalledWith('KEY', result); - expect(core.setOutput).toBeCalledWith('key', result); - }); - it('intl secret retrieval', async () => { mockInput('测试 测试'); mockVaultData({ @@ -350,31 +334,7 @@ describe('exportSecrets', () => { expect(core.setOutput).toBeCalledWith('key', 'secret'); }) - 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("EXAMPLE"); // called for 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 () => { + it('multi-line secret gets masked for each line', async () => { const multiLineString = `a multi-line string with blank lines @@ -388,7 +348,7 @@ with blank lines await exportSecrets(); - expect(core.setSecret).toBeCalledTimes(3); // 1 for each non-empty line + VAULT_TOKEN + expect(core.setSecret).toBeCalledTimes(3); // 1 for each non-empty line. expect(core.setSecret).toBeCalledWith('a multi-line string'); expect(core.setSecret).toBeCalledWith('with blank lines'); diff --git a/src/retries.test.js b/src/retries.test.js index 285a7f25..132edd52 100644 --- a/src/retries.test.js +++ b/src/retries.test.js @@ -66,4 +66,4 @@ describe('exportSecrets retries', () => { done(); }); }); -}); +}); \ No newline at end of file diff --git a/src/secrets.js b/src/secrets.js index bc810a81..45b26e0a 100644 --- a/src/secrets.js +++ b/src/secrets.js @@ -1,5 +1,6 @@ const jsonata = require("jsonata"); + /** * @typedef {Object} SecretRequest * @property {string} path @@ -66,20 +67,12 @@ 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); - let d = await ata.evaluate(data); - if (isJSON(d)) { - // If we already have JSON we will not "stringify" it yet so that we - // don't end up calling JSON.parse. This would break the secrets that - // are stored as JSON. See: https://github.com/hashicorp/vault-action/issues/194 - result = d; - } else { - result = JSON.stringify(d); - } + let result = JSON.stringify(await ata.evaluate(data)); // Compat for custom engines if (!result && ((ata.ast().type === "path" && ata.ast()['steps'].length === 1) || ata.ast().type === "string") && selector !== 'data' && 'data' in data) { result = JSON.stringify(await jsonata(`data.${selector}`).evaluate(data)); @@ -88,44 +81,12 @@ async function selectData(data, selector) { } if (result.startsWith(`"`)) { - // we need to strip the beginning and ending quotes otherwise it will - // always successfully parse as JSON - result = result.substring(1, result.length - 1); - if (!isJSON(result)) { - // add the quotes back so we can parse it into a Javascript object - // to allow support for multi-line secrets. See https://github.com/hashicorp/vault-action/issues/160 - result = `"${result}"` - result = JSON.parse(result); - } - } else if (isJSON(result)) { - // This is required to support secrets in JSON format. - // See https://github.com/hashicorp/vault-action/issues/194 and https://github.com/hashicorp/vault-action/pull/173 - result = JSON.stringify(result); - result = result.substring(1, result.length - 1); + result = JSON.parse(result); } return result; } -/** - * isJSON returns true if str parses as a valid JSON string - * @param {string} str - */ -function isJSON(str) { - if (typeof str !== "string"){ - return false; - } - - try { - JSON.parse(str); - } catch (e) { - return false; - } - - return true; -} - module.exports = { getSecrets, selectData -} - +} \ No newline at end of file