2

I want to have a node script that executes a bash script with an argument that has newline and tab characters.

Node script

const { exec } = require('child_process');

const description = "Incremented values\n\n- android\n\n\t- version: 1.9.4\n\n\t- build: 27\n\n- ios\n\n\t- version: 1.9.4\n\n\t- build: 27";

const childProcess = exec(`sh test.sh "{description}"`);
childProcess.stdout.pipe(process.stdout)
childProcess.stderr.pipe(process.stderr)
childProcess.on('exit', function (exitCode) {
    if (exitCode !== 0) {
        console.log('\x1b[31m', "Exit code: ", exitCode)
    }
    else {
        console.log("no failures")
    }
});

Bash script (test.sh):

DESCRIPTION=$1

generate_create_data() {
  cat <<EOF
{
  "title": "Release",
  "description": "$DESCRIPTION",
  "state": "OPEN",
  "destination": {
    "branch": {
      "name": "master"
    }
  },
  "source": {
    "branch": {
      "name": "release/v1.9.4"
    }
  }
}
EOF
}

set -x

curl https://fakeurl.com \
    -u un:pw \
    --request POST \
    --header 'Content-Type: application/json' \
    -d "$(generate_create_data)"

Unfortunately it looks like while passing the argument from node to the script the newline and tab characters are interpreted

with set -x you can see this.

...
 -d '{
  "title": "Release",
  "description": "Incremented values

- android

        - version: 1.9.4

        - build: 27

- ios

        - version: 1.9.4

        - build: 27",
  "state": "OPEN",
  ...

If I execute the bash script like so

sh test.sh "Incremented values\n\n- android\n\n\t- version: 1.9.4\n\n\t- build: 27\n\n- ios\n\n\t- version: 1.9.4\n\n\t- build: 27"

the newline and tab characters are not interpreted and with set -x enabled you can see that

 -d '{
  "title": "Release",
  "description": "Incremented values\n\n- android\n\n\t- version: 1.9.4\n\n\t- build: 27\n\n- ios\n\n\t- version: 1.9.4\n\n\t- build: 27",
  "state": "OPEN",
  ...

how do I pass the description variable correctly from node to the bash script so that curl does not try to interpret the newline and tab characters?

Things I've tried

Encoding it as a buffer

const childProcess = exec(`sh src/increment/git/test.sh "${description}"`, {encoding: 'buffer'});

Escaping special characters while passing the argument

const childProcess = exec(`sh src/increment/git/test.sh $'${description}'`);

Base64 encoding

...
const description = "Incremented values\n\n- android\n\n\t- version: 1.9.4\n\n\t- build: 27\n\n- ios\n\n\t- version: 1.9.4\n\n\t- build: 27";
const buff = Buffer.from(description, 'utf-8')
const base64description = buff.toString('base64');

const childProcess = exec(`sh src/increment/git/test.sh "${base64description}"`);
...
B64DESCRIPTION=$1
DESCRIPTION=`echo $B64DESCRIPTION | base64 --decode`
...

This gave me the same result as before

TheHeuman
  • 152
  • 1
  • 2
  • 15

2 Answers2

1

I solved this by adding an extra backslash before each \n and \t

const description = "Incremented values\n\n- android\n\n\t- version: 1.9.4\n\n\t- build: 27\n\n- ios\n\n\t- version: 1.9.4\n\n\t- build: 27"
const rawDescription = description.replace(/\n/g, "\\n").replace(/\t/g, "\\t")

const childProcess = exec(`sh src/increment/git/test.sh "${rawDescription}"`);

How I got there

At first I tried to solve this by replacing all single backslashes with two backslashes

rawDescription = description.replace("\\", "\\\\")

But of course there aren't actually and backslash characters in my original string. Just the escape sequences \n and \t so I replaced those escape sequences instead. shout out to this answer for getting me over the line!

TheHeuman
  • 152
  • 1
  • 2
  • 15
0
function quoteShellArg(arg){
  if(arg.indexOf("\x00") !== -1) {
    throw new Error('it is impossible to escape null bytes for shell arguments.');
  }
  return "'"+arg.replace(/\'/g,'\'\\\'\'')+"'";
}

const childProcess = exec('sh src/increment/git/test.sh ' + quoteShellArg(description));

this works the same way as PHP's escapeshellarg()-running-on-linux, and this is how to do it when running on a unixy-y enviorment (Linux, MacOS, *BSD, etc), but this won't work when running on Windows (unless you're running on WSL or Cygwin or something, then it will work), but given that you're invoking sh, it's a fair assumtion that you're running on a unix-y env :)

hanshenrik
  • 19,904
  • 4
  • 43
  • 89
  • Unfortunately this does not work for me, same result as original. I solved it by replacing all the \n and \t with \\n and \\t. – TheHeuman Apr 08 '21 at 16:30
  • @TheHeuman hmm then it's probably a curl problem, have you tried using `--data-binary` instead of `-d` in curl? – hanshenrik Apr 08 '21 at 17:20
  • Yeah, that was one of the first things I tried. The problem wasn't on the bash script side, but the node side. I think the command may have been executing with the new lines interpreted which messed up the script – TheHeuman Apr 08 '21 at 19:19