Skip to content

Commit

Permalink
Traverse config and error out if secrets are inline
Browse files Browse the repository at this point in the history
  • Loading branch information
derrickreimer committed Jul 13, 2020
1 parent bd54d51 commit 0fd5ce1
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 0 deletions.
45 changes: 45 additions & 0 deletions src/cmds/deploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const messages = require('../messages');
const shim = require('../shim');
const env = require('process').env;
const { stripIndent } = require('common-tags');
const { traverse } = require('../traverse');

const indent = (text, depth = 2) => {
return text
Expand Down Expand Up @@ -145,6 +146,13 @@ exports.builder = yargs => {
describe: 'API endpoint'
});

yargs.option('force', {
alias: 'f',
describe: 'Skip verifying that secrets reference environment variables',
type: 'boolean',
default: false
});

yargs.option('file', {
describe: 'Path to the local `statickit.json` file',
default: 'statickit.json'
Expand All @@ -169,6 +177,43 @@ exports.handler = async args => {
return;
}

let parsedRawConfig;

try {
parsedRawConfig = JSON.parse(rawConfig);
} catch (err) {
log.error('Configuration could not be parsed');
process.exitCode = 1;
return;
}

// Traverse the config and validate that certain specially-named keys
// reference environment variables.
let invalidKeys = [];
const sensitiveKeys = ['apiKey', 'apiSecret', 'secretKey'];

traverse(parsedRawConfig, (key, value) => {
if (
sensitiveKeys.indexOf(key) > -1 &&
!value.match(/^\$([A-Za-z0-9_]+)$/)
) {
invalidKeys.push(key);
}
});

if (!args.force && invalidKeys.length > 0) {
log.error(
`The following properties must reference environment variables: ${invalidKeys.join(
', '
)}`
);

log.meta('To override this, use the `-f` flag.');

process.exitCode = 1;
return;
}

// Replace environment variable $-references with the actual values
// If the environment variable is not defined, store in an array an present
// an error to the user.
Expand Down
24 changes: 24 additions & 0 deletions src/traverse.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
function isArray(o) {
return Object.prototype.toString.call(o) === '[object Array]';
}

function traverse(obj, fn) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
let value = obj[key];

if (isArray(value)) {
value.forEach(element => {
if (typeof element === 'object' && element !== null)
traverse(element, fn);
});
} else if (typeof value === 'object' && value !== null) {
traverse(value, fn);
} else {
fn(key, value);
}
}
}
}

module.exports = { traverse };
56 changes: 56 additions & 0 deletions test/cmds/__snapshots__/deploy.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,14 @@ Array [
]
`;
exports[`skips validating inline secrets with force flag 1`] = `
Array [
Array [
"✔ Deployment succeeded (xxxx-xxxx-xxxx)",
],
]
`;
exports[`substitutes all referenced environment variables 1`] = `
Array [
Array [
Expand All @@ -190,6 +198,54 @@ Array [
]
`;
exports[`throws an error if apiKey properties do not point to env vars 1`] = `
Array [
Array [
"✕ The following properties must reference environment variables: apiKey",
],
]
`;
exports[`throws an error if apiKey properties do not point to env vars 2`] = `
Array [
Array [
"> To override this, use the \`-f\` flag.",
],
]
`;
exports[`throws an error if apiSecret properties do not point to env vars 1`] = `
Array [
Array [
"✕ The following properties must reference environment variables: apiSecret",
],
]
`;
exports[`throws an error if apiSecret properties do not point to env vars 2`] = `
Array [
Array [
"> To override this, use the \`-f\` flag.",
],
]
`;
exports[`throws an error if secretKey properties do not point to env vars 1`] = `
Array [
Array [
"✕ The following properties must reference environment variables: secretKey",
],
]
`;
exports[`throws an error if secretKey properties do not point to env vars 2`] = `
Array [
Array [
"> To override this, use the \`-f\` flag.",
],
]
`;
exports[`throws an error if undefined env vars are referenced 1`] = `
Array [
Array [
Expand Down
76 changes: 76 additions & 0 deletions test/cmds/deploy.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ afterEach(() => {
console.log.mockRestore();
console.error.mockRestore();
shim.install.mockReset();
deploy.request.mockReset();
});

it('sends a deploy request with the right params', async () => {
Expand Down Expand Up @@ -221,3 +222,78 @@ it('throws an error if undefined env vars are referenced', async () => {
await cmd.handler({ config: JSON.stringify(config), key: 'xxx' });
expect(console.error.mock.calls).toMatchSnapshot();
});

it('throws an error if apiKey properties do not point to env vars', async () => {
const config = {
forms: {
contactForm: {
actions: [
{
apiKey: 'my-inline-key'
}
]
}
}
};

await cmd.handler({ config: JSON.stringify(config), key: 'xxx' });
expect(console.error.mock.calls).toMatchSnapshot();
expect(console.log.mock.calls).toMatchSnapshot();
});

it('throws an error if apiSecret properties do not point to env vars', async () => {
const config = {
forms: {
contactForm: {
actions: [
{
apiSecret: 'my-inline-key'
}
]
}
}
};

await cmd.handler({ config: JSON.stringify(config), key: 'xxx' });
expect(console.error.mock.calls).toMatchSnapshot();
expect(console.log.mock.calls).toMatchSnapshot();
});

it('throws an error if secretKey properties do not point to env vars', async () => {
const config = {
forms: {
contactForm: {
actions: [
{
secretKey: 'my-inline-key'
}
]
}
}
};

await cmd.handler({ config: JSON.stringify(config), key: 'xxx' });
expect(console.error.mock.calls).toMatchSnapshot();
expect(console.log.mock.calls).toMatchSnapshot();
});

it('skips validating inline secrets with force flag', async () => {
const config = {
apiKey: 'my-inline-key'
};

deploy.request.mockImplementation(params => {
expect(params.config.apiKey).toBe('my-inline-key');
return Promise.resolve({
status: 200,
data: { id: 'xxxx-xxxx-xxxx', shim: null }
});
});

await cmd.handler({
config: JSON.stringify(config),
key: 'xxx',
force: true
});
expect(console.log.mock.calls).toMatchSnapshot();
});

0 comments on commit 0fd5ce1

Please sign in to comment.