Skip to content

Commit 1c2c39f

Browse files
authored
feat: detect identifier sql-injection (#501)
1 parent 5cf60f6 commit 1c2c39f

File tree

15 files changed

+138
-25
lines changed

15 files changed

+138
-25
lines changed

.changeset/sunny-moments-jam.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@nodesecure/tracer": major
3+
"@nodesecure/estree-ast-utils": minor
4+
"@nodesecure/js-x-ray": minor
5+
---
6+
7+
feat: detect identifier sql-injection

workspaces/estree-ast-utils/src/getCallExpressionArguments.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { ESTree } from "meriyah";
44

55
// Import Internal Dependencies
66
import { concatBinaryExpression } from "./concatBinaryExpression.ts";
7+
import { toLiteral } from "./toLiteral.ts";
78
import {
89
type DefaultOptions,
910
noop
@@ -42,6 +43,12 @@ export function getCallExpressionArguments(
4243

4344
break;
4445
}
46+
case "TemplateLiteral": {
47+
const literal = toLiteral(arg);
48+
literalsNode.push(literal);
49+
50+
break;
51+
}
4552
case "BinaryExpression": {
4653
const concatenatedBinaryExpr = [
4754
...concatBinaryExpression(arg, { externalIdentifierLookup })

workspaces/estree-ast-utils/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ export * from "./getMemberExpressionIdentifier.ts";
77
export * from "./getVariableDeclarationIdentifiers.ts";
88
export type { DefaultOptions } from "./options.ts";
99
export * from "./utils/is.ts";
10+
export * from "./toLiteral.ts";
1011

workspaces/js-x-ray/src/utils/toLiteral.ts renamed to workspaces/estree-ast-utils/src/toLiteral.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ import type { ESTree } from "meriyah";
44
export function toLiteral(templateLiteral: ESTree.TemplateLiteral) {
55
return templateLiteral.quasis.map(({ tail, value: { raw } }, i) => (tail ? raw : `${raw}\${${i}}`)).join("");
66
}
7+

workspaces/estree-ast-utils/test/getCallExpressionArguments.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,15 @@ describe("getCallExpressionArguments", () => {
6060

6161
assert.deepEqual(args, ["hello world"]);
6262
});
63+
64+
test("resolve the TemplateLiteral and return is Literal value", () => {
65+
/* eslint-disable-next-line no-template-curly-in-string */
66+
const [astNode] = codeToAst("foo(`hello ${name}`);");
67+
const args = getCallExpressionArguments(
68+
getExpressionFromStatement(astNode)
69+
);
70+
71+
/* eslint-disable-next-line no-template-curly-in-string */
72+
assert.deepEqual(args, ["hello ${0}"]);
73+
});
6374
});

workspaces/js-x-ray/test/utils/toLiteral.spec.ts renamed to workspaces/estree-ast-utils/test/toLiteral.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import assert from "node:assert";
33
import { describe, it } from "node:test";
44

55
// Import Internal Dependencies
6-
import { toLiteral } from "../../src/utils/toLiteral.ts";
6+
import { toLiteral } from "../src/toLiteral.ts";
77

88
describe("toLiteral", () => {
99
it("should transform a TemplateLiteral to a literal", () => {
@@ -93,3 +93,4 @@ describe("toLiteral", () => {
9393
}), `hello \${${0}} world \${${1}} `);
9494
});
9595
});
96+

workspaces/js-x-ray/src/ProbeRunner.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ export class ProbeRunner {
229229

230230
if (node.type === "CallExpression") {
231231
const id = getCallExpressionIdentifier(node, {
232-
externalIdentifierLookup: (name) => this.sourceFile.tracer.literalIdentifiers.get(name) ?? null
232+
externalIdentifierLookup: (name) => this.sourceFile.tracer.literalIdentifiers.get(name)?.value ?? null
233233
});
234234
if (id !== null) {
235235
tracedIdentifierReport = this.sourceFile.tracer.getDataFromIdentifier(id);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ function validateMemberExpression(
104104
ctx: ProbeContext
105105
): [boolean, any?] {
106106
const iter = getMemberExpressionIdentifier(node, {
107-
externalIdentifierLookup: (name: string) => ctx.sourceFile.tracer.literalIdentifiers.get(name) ?? null
107+
externalIdentifierLookup: (name: string) => ctx.sourceFile.tracer.literalIdentifiers.get(name)?.value ?? null
108108
});
109109

110110
const jsTypeName = iter.next().value;

workspaces/js-x-ray/src/probes/isRequire/RequireCallExpressionWalker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ export class RequireCallExpressionWalker {
9595
const nodeArguments = getCallExpressionArguments(
9696
node,
9797
{
98-
externalIdentifierLookup: (name) => this.tracer.literalIdentifiers.get(name) ?? null
98+
externalIdentifierLookup: (name) => this.tracer.literalIdentifiers.get(name)?.value ?? null
9999
}
100100
);
101101

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ function main(
9393
case "Identifier":
9494
if (sourceFile.tracer.literalIdentifiers.has(arg.name)) {
9595
sourceFile.addDependency(
96-
sourceFile.tracer.literalIdentifiers.get(arg.name)!,
96+
sourceFile.tracer.literalIdentifiers.get(arg.name)?.value!,
9797
node.loc
9898
);
9999
}
@@ -115,7 +115,7 @@ function main(
115115
case "ArrayExpression": {
116116
const value = [
117117
...arrayExpressionToString(arg, {
118-
externalIdentifierLookup: (name) => tracer.literalIdentifiers.get(name) ?? null
118+
externalIdentifierLookup: (name) => tracer.literalIdentifiers.get(name)?.value ?? null
119119
})
120120
]
121121
.join("")
@@ -143,7 +143,7 @@ function main(
143143

144144
try {
145145
const iter = concatBinaryExpression(arg, {
146-
externalIdentifierLookup: (name) => tracer.literalIdentifiers.get(name) ?? null,
146+
externalIdentifierLookup: (name) => tracer.literalIdentifiers.get(name)?.value ?? null,
147147
stopOnUnsupportedNode: true
148148
});
149149

0 commit comments

Comments
 (0)