Search code examples
javascriptnode.jsjsonnetlify

Is there a secure way to prevent exec from stripping quotes?


I want to run a command via node's child process exec method that will include a JSON string which itself will include quote characters. It appears that exec strips these by default. Is there a way to prevent this?

I'm trying to automate the creation of build hooks in netlify as part of some automated QA we're trying to currently build out. Presently, I'm doing this using the netlify cli, specifically their netlify api createSiteBuildHook method. I want to capture the output of this command as well as inject some data into the command so I'm trying to approach this using node's child process exec() command. The issue I've run into here is that the netlify command expects a JSON string as an argument for the command and as it turns out, exec strips quotes by default, I'm presuming for security reasons. I'm hoping there's a way around this.

A few alternatives I've tried and considered:

  • I previously tried approaching this using the netlify api js client, however we've been running into issues with automating the refreshing of the api token. This is being run as part of an automated build process in CircleCI so it's abstracted from any interaction. I want to avoid a developer having to go in and update this as a manual step every time our builds and auto tests stop working because we've exceeded the rate limit or whatever.
  • I've tried using escaping of quotes such as \", ^" etc to no avail.
  • I've tried using alternative child process methods like spawn or execFile but I seem to be getting the same issue.
  • A final alternative is to abstract this entirely to bash. This isn't entirely unworkable, it would just be fiddly and annoying. I'd have to save the output in a file, then load that file and interrogate it when I want to get the url for my newly generated build hook etc. If I can do this work in js it would save a lot of time.

The command I'm using to generate build hooks:

yarn netlify api createSiteBuildHook -d {"site_id":"my-site-id","body":{"title":"my-title","branch":"my-branch"}}

The code I'm using to run this via exec:

yargs
  .command(
    'createBuildHook',
    'Creates a build hook in netlify',
    {},
    async () => {
      const buildHookRequest = JSON.stringify({
        site_id: process.env.NETLIFY_MASTER_ID,
        body: {
          title: branchName(),
          branch: branchName(),
        }
      });

      const cmd = exec(`yarn netlify api createSiteBuildHook -d ${buildHookRequest}`, (err, stout) => {
        if (err) {
          console.log(`Error: ${err}`); 
        }
        console.log(`Output: ${stout}`);
      });
    }
  )
  .demandCommand().argv;

The error I get when I try to run my command:

$ yarn qa-automate:createBuildHook
yarn run v1.16.0
$ yarn qa-automate createBuildHook
$ babel-node --presets @babel/preset-env -- ./qa-automation/cli.js createBuildHook
Error: Error: Command failed: yarn netlify api createSiteBuildHook -d {"site_id":"my-site-id","body":{"title":"my-title","branch":"my-branch"}}
SyntaxError: Unexpected token s in JSON at position 1
    at JSON.parse (<anonymous>)
    at APICommand.run (C:/Users/owen_jones/web-work/website-assets/node_modules/netlify-cli/src/commands/api.js:45:61)
error Command failed with exit code 1.

Output: $ C:\Users\owen_jones\web-work\website-assets\node_modules\.bin\netlify api createSiteBuildHook -d {site_id:my-site-id,body:{title:my-title,branch:my-branch}}
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

Done in 3.92s.

You can see in the output that it's stripped the quotes. It could be that something else is doing the stripping but I can't work out what.


Solution

  • It's not exec, it's the shell.

    You can probably work around it by escaping every quote (and backslash) with a backslash:

    const cmd = exec(`yarn netlify api createSiteBuildHook -d ${buildHookRequest.replace(/[\\"]/g, "\\$&")}`, (err, stout) => {
    

    That's probably not a robust general purpose solution, but it's probably good enough for what you're doing. (My quick local test passed through the JSON well enough doing that.)