diff --git a/README.md b/README.md index 8dce6af..fd4121a 100644 --- a/README.md +++ b/README.md @@ -399,6 +399,16 @@ with: 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: + +```yaml +with: + secrets: | + secret/data/ci/aws ** | MYAPP_ ; +``` + +```yaml + ## Other Secret Engines Vault Action currently supports retrieving secrets from any engine where secrets diff --git a/dist/index.js b/dist/index.js index ef4ffaa..3f08f86 100644 --- a/dist/index.js +++ b/dist/index.js @@ -9686,6 +9686,13 @@ const dateTime = (function () { if (offset === 0 && markerSpec.presentation2 === 't') { componentValue = 'Z'; } + } else if (markerSpec.component === 'P') { + // ยง9.8.4.7 Formatting Other Components + // Formatting P for am/pm + // getDateTimeFragment() always returns am/pm lower case so check for UPPER here + if (markerSpec.names === tcase.UPPER) { + componentValue = componentValue.toUpperCase(); + } } return componentValue; }; @@ -13501,6 +13508,13 @@ var jsonata = (function() { } for(var ii = 0; ii < matches.length; ii++) { var match = matches[ii]; + if (match && (match.isPrototypeOf(result) || match instanceof Object.constructor)) { + throw { + code: "D1010", + stack: (new Error()).stack, + position: expr.position + }; + } // evaluate the update value for each match var update = await evaluate(expr.update, match, environment); // update must be an object @@ -13747,7 +13761,7 @@ var jsonata = (function() { if (typeof err.token == 'undefined' && typeof proc.token !== 'undefined') { err.token = proc.token; } - err.position = proc.position; + err.position = proc.position || err.position; } throw err; } @@ -14180,6 +14194,7 @@ var jsonata = (function() { "T1007": "Attempted to partially apply a non-function. Did you mean ${{{token}}}?", "T1008": "Attempted to partially apply a non-function", "D1009": "Multiple key definitions evaluate to same key: {{value}}", + "D1010": "Attempted to access the Javascript object prototype", // Javascript specific "T1010": "The matcher function argument passed to function {{token}} does not return the correct object structure", "T2001": "The left side of the {{token}} operator must evaluate to a number", "T2002": "The right side of the {{token}} operator must evaluate to a number", @@ -18520,7 +18535,7 @@ const command = __nccwpck_require__(7351); const got = (__nccwpck_require__(3061)["default"]); const jsonata = __nccwpck_require__(4245); const { normalizeOutputKey } = __nccwpck_require__(1608); -const { WILDCARD } = __nccwpck_require__(4438); +const { WILDCARD, WILDCARD_UPPERCASE } = __nccwpck_require__(4438); const { auth: { retrieveToken }, secrets: { getSecrets } } = __nccwpck_require__(4351); @@ -18690,7 +18705,7 @@ function parseSecretsInput(secretsInput) { const selectorAst = jsonata(selectorQuoted).ast(); const selector = selectorQuoted.replace(new RegExp('"', 'g'), ''); - if (selector !== WILDCARD && (selectorAst.type !== "path" || selectorAst.steps[0].stages) && selectorAst.type !== "string" && !outputVarName) { + if (selector !== WILDCARD && selector !== WILDCARD_UPPERCASE && (selectorAst.type !== "path" || selectorAst.steps[0].stages) && selectorAst.type !== "string" && !outputVarName) { throw Error(`You must provide a name for the output key when using json selectors. Input: "${secret}"`); } @@ -18767,7 +18782,7 @@ async function retrieveToken(method, client) { switch (method) { case 'approle': { const vaultRoleId = core.getInput('roleId', { required: true }); - const vaultSecretId = core.getInput('secretId', { required: true }); + const vaultSecretId = core.getInput('secretId', { required: false }); return await getClientToken(client, method, path, { role_id: vaultRoleId, secret_id: vaultSecretId }); } case 'github': { @@ -18914,12 +18929,15 @@ module.exports = { /***/ 4438: /***/ ((module) => { -const WILDCARD = '*'; +const WILDCARD_UPPERCASE = '*'; +const WILDCARD = '**'; module.exports = { - WILDCARD + WILDCARD, + WILDCARD_UPPERCASE, }; + /***/ }), /***/ 4351: @@ -18939,7 +18957,7 @@ module.exports = { /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { const jsonata = __nccwpck_require__(4245); -const { WILDCARD } = __nccwpck_require__(4438); +const { WILDCARD, WILDCARD_UPPERCASE} = __nccwpck_require__(4438); const { normalizeOutputKey } = __nccwpck_require__(1608); const core = __nccwpck_require__(2186); @@ -18966,6 +18984,7 @@ const core = __nccwpck_require__(2186); async function getSecrets(secretRequests, client, ignoreNotFound) { const responseCache = new Map(); let results = []; + let upperCaseEnv = false; for (const secretRequest of secretRequests) { let { path, selector } = secretRequest; @@ -18999,7 +19018,8 @@ async function getSecrets(secretRequests, client, ignoreNotFound) { body = JSON.parse(body); - if (selector == WILDCARD) { + if (selector === WILDCARD || selector === WILDCARD_UPPERCASE) { + upperCaseEnv = selector === WILDCARD_UPPERCASE; let keys = body.data; if (body.data["data"] != undefined) { keys = keys.data; @@ -19018,7 +19038,7 @@ async function getSecrets(secretRequests, client, ignoreNotFound) { } newRequest.outputVarName = normalizeOutputKey(newRequest.outputVarName); - newRequest.envVarName = normalizeOutputKey(newRequest.envVarName,true); + newRequest.envVarName = normalizeOutputKey(newRequest.envVarName, upperCaseEnv); // JSONata field references containing reserved tokens should // be enclosed in backticks @@ -19127,12 +19147,12 @@ module.exports = { * @param {string} dataKey * @param {boolean=} isEnvVar */ -function normalizeOutputKey(dataKey, isEnvVar = false) { +function normalizeOutputKey(dataKey, upperCase = false) { let outputKey = dataKey .replace(".", "__") .replace(new RegExp("-", "g"), "") .replace(/[^\p{L}\p{N}_-]/gu, ""); - if (isEnvVar) { + if (upperCase) { outputKey = outputKey.toUpperCase(); } return outputKey; diff --git a/integrationTests/basic/integration.test.js b/integrationTests/basic/integration.test.js index 93efb14..d7f56e1 100644 --- a/integrationTests/basic/integration.test.js +++ b/integrationTests/basic/integration.test.js @@ -313,7 +313,7 @@ describe('integration', () => { expect(core.exportVariable).toBeCalledWith('FOO', 'bar'); }); - it('wildcard supports cubbyhole', async () => { + it('wildcard supports cubbyhole with uppercase transform', async () => { mockInput('/cubbyhole/test *'); await exportSecrets(); @@ -323,6 +323,32 @@ describe('integration', () => { expect(core.exportVariable).toBeCalledWith('FOO', 'bar'); expect(core.exportVariable).toBeCalledWith('ZIP', 'zap'); }); + + it('wildcard supports cubbyhole with no change in case', async () => { + mockInput('/cubbyhole/test **'); + + await exportSecrets(); + + expect(core.exportVariable).toBeCalledTimes(2); + + expect(core.exportVariable).toBeCalledWith('foo', 'bar'); + expect(core.exportVariable).toBeCalledWith('zip', 'zap'); + }); + + it('wildcard supports cubbyhole with mixed case change', async () => { + mockInput(` + /cubbyhole/test * ; + /cubbyhole/test **`); + + await exportSecrets(); + + expect(core.exportVariable).toBeCalledTimes(4); + + expect(core.exportVariable).toBeCalledWith('FOO', 'bar'); + expect(core.exportVariable).toBeCalledWith('ZIP', 'zap'); + expect(core.exportVariable).toBeCalledWith('foo', 'bar'); + expect(core.exportVariable).toBeCalledWith('zip', 'zap'); + }); it('caches responses', async () => { mockInput(` diff --git a/src/action.js b/src/action.js index 94c8f8e..ab53df3 100644 --- a/src/action.js +++ b/src/action.js @@ -4,7 +4,7 @@ const command = require('@actions/core/lib/command'); const got = require('got').default; const jsonata = require('jsonata'); const { normalizeOutputKey } = require('./utils'); -const { WILDCARD } = require('./constants'); +const { WILDCARD, WILDCARD_UPPERCASE } = require('./constants'); const { auth: { retrieveToken }, secrets: { getSecrets } } = require('./index'); @@ -174,7 +174,7 @@ function parseSecretsInput(secretsInput) { const selectorAst = jsonata(selectorQuoted).ast(); const selector = selectorQuoted.replace(new RegExp('"', 'g'), ''); - if (selector !== WILDCARD && (selectorAst.type !== "path" || selectorAst.steps[0].stages) && selectorAst.type !== "string" && !outputVarName) { + if (selector !== WILDCARD && selector !== WILDCARD_UPPERCASE && (selectorAst.type !== "path" || selectorAst.steps[0].stages) && selectorAst.type !== "string" && !outputVarName) { throw Error(`You must provide a name for the output key when using json selectors. Input: "${secret}"`); } diff --git a/src/constants.js b/src/constants.js index b005350..427f2fc 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,5 +1,7 @@ -const WILDCARD = '*'; +const WILDCARD_UPPERCASE = '*'; +const WILDCARD = '**'; module.exports = { - WILDCARD -}; \ No newline at end of file + WILDCARD, + WILDCARD_UPPERCASE, +}; diff --git a/src/secrets.js b/src/secrets.js index 82375c7..02ed1fb 100644 --- a/src/secrets.js +++ b/src/secrets.js @@ -1,5 +1,5 @@ const jsonata = require("jsonata"); -const { WILDCARD } = require("./constants"); +const { WILDCARD, WILDCARD_UPPERCASE} = require("./constants"); const { normalizeOutputKey } = require("./utils"); const core = require('@actions/core'); @@ -26,6 +26,7 @@ const core = require('@actions/core'); async function getSecrets(secretRequests, client, ignoreNotFound) { const responseCache = new Map(); let results = []; + let upperCaseEnv = false; for (const secretRequest of secretRequests) { let { path, selector } = secretRequest; @@ -59,7 +60,8 @@ async function getSecrets(secretRequests, client, ignoreNotFound) { body = JSON.parse(body); - if (selector == WILDCARD) { + if (selector === WILDCARD || selector === WILDCARD_UPPERCASE) { + upperCaseEnv = selector === WILDCARD_UPPERCASE; let keys = body.data; if (body.data["data"] != undefined) { keys = keys.data; @@ -78,7 +80,7 @@ async function getSecrets(secretRequests, client, ignoreNotFound) { } newRequest.outputVarName = normalizeOutputKey(newRequest.outputVarName); - newRequest.envVarName = normalizeOutputKey(newRequest.envVarName,true); + newRequest.envVarName = normalizeOutputKey(newRequest.envVarName, upperCaseEnv); // JSONata field references containing reserved tokens should // be enclosed in backticks diff --git a/src/utils.js b/src/utils.js index b8dd863..0f5e4de 100644 --- a/src/utils.js +++ b/src/utils.js @@ -3,12 +3,12 @@ * @param {string} dataKey * @param {boolean=} isEnvVar */ -function normalizeOutputKey(dataKey, isEnvVar = false) { +function normalizeOutputKey(dataKey, upperCase = false) { let outputKey = dataKey .replace(".", "__") .replace(new RegExp("-", "g"), "") .replace(/[^\p{L}\p{N}_-]/gu, ""); - if (isEnvVar) { + if (upperCase) { outputKey = outputKey.toUpperCase(); } return outputKey;