Skip to content

Commit 384b5b9

Browse files
committed
Merge branch 'main' of github.com:AikidoSec/node-RASP into beta
* 'main' of github.com:AikidoSec/node-RASP: (33 commits) Prevent ReDoS Fix multiple control chars Remove unused code Check blocked users every time but log once Remove some comments Update comment Allow passing a Router to `addExpressMiddleware` Add comments Fix unit tests Fix path traversal in path Add comment Remove unused import Disable Function sink for now Fix test file brackets Extend comment Add more tests Fix url path traversal bypass Remove logs Increase timeout for n8n test Add debug logs for CI only failure ...
2 parents 67a35b0 + 916e44a commit 384b5b9

File tree

70 files changed

+32364
-21573
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+32364
-21573
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,12 @@ See list above for supported database drivers.
8787
### Data serialization tools
8888

8989
*[`xml2js`](https://www.npmjs.com/package/xml2js) 0.6.x, 0.5.x, ^0.4.18
90-
*[`fast-xml-parser`](https://www.npmjs.com/package/fast-xml-parser) 4.x
90+
*[`fast-xml-parser`](https://www.npmjs.com/package/fast-xml-parser) 5.x, 4.x
9191
*[`xml-js`](https://www.npmjs.com/package/xml-js) 1.x
9292

9393
### Shell tools
9494

95-
*[`ShellJS`](https://www.npmjs.com/package/shelljs) 0.8.x, 0.7.x
95+
*[`ShellJS`](https://www.npmjs.com/package/shelljs) 0.9.x, 0.8.x, 0.7.x
9696

9797
### Routers
9898

benchmarks/express/package-lock.json

Lines changed: 452 additions & 406 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

benchmarks/hono-pg/package-lock.json

Lines changed: 22 additions & 22 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

benchmarks/operations/benchmark.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ const modules = [
1414
module: "nosqli",
1515
name: "NoSQL query",
1616
},
17+
/*
18+
Disabled because functionName.constructor === Function is false after patching global
1719
{
1820
module: "jsinjection",
1921
name: "`new Function(...)`",
20-
},
22+
},*/
2123
{
2224
module: "shelli",
2325
name: "Shell command",

docs/express.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,19 @@ Zen.addExpressMiddleware(app);
5959
app.get(...);
6060
```
6161

62+
You can also pass a `Router` instance to `Zen.addExpressMiddleware`:
63+
64+
```js
65+
const router = express.Router();
66+
67+
// Note: The middleware should be executed once per request
68+
Zen.addExpressMiddleware(router);
69+
70+
router.get(...);
71+
72+
app.use(router);
73+
```
74+
6275
## Debug mode
6376

6477
If you need to debug the firewall, you can run your express app with the environment variable `AIKIDO_DEBUG` set to `true`:

end2end/tests/express-mongodb.code-injection.test.js

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,25 @@ t.test("it blocks in blocking mode", (t) => {
3838
timeout(2000)
3939
.then(() => {
4040
return Promise.all([
41-
fetch("http://127.0.0.1:4000/hello/hans", {
42-
signal: AbortSignal.timeout(5000),
43-
}),
44-
fetch(`http://127.0.0.1:4000/hello/${encodeURIComponent(`hans" //`)}`, {
45-
signal: AbortSignal.timeout(5000),
46-
}),
41+
fetch(
42+
`http://127.0.0.1:4000/where?title=${encodeURIComponent("Test'||'a")}`,
43+
{
44+
signal: AbortSignal.timeout(5000),
45+
}
46+
),
47+
fetch(
48+
`http://127.0.0.1:4000/where?title=${encodeURIComponent("Test' && sleep(10000); '")}`,
49+
{
50+
signal: AbortSignal.timeout(5000),
51+
}
52+
),
4753
]);
4854
})
49-
.then(([safeName, unsafeName]) => {
50-
t.equal(safeName.status, 200);
51-
t.equal(unsafeName.status, 500);
55+
.then(([req1, req2]) => {
56+
t.equal(req1.status, 500);
57+
t.equal(req2.status, 500);
5258
t.match(stdout, /Starting agent/);
53-
t.match(stdout, /Zen has blocked a JavaScript injection/);
59+
t.match(stdout, /Zen has blocked a NoSQL injection/);
5460
})
5561
.catch((error) => {
5662
t.fail(error);
@@ -83,19 +89,25 @@ t.test("it does not block in dry mode", (t) => {
8389
timeout(2000)
8490
.then(() =>
8591
Promise.all([
86-
fetch("http://127.0.0.1:4001/hello/hans", {
87-
signal: AbortSignal.timeout(5000),
88-
}),
89-
fetch(`http://127.0.0.1:4001/hello/${encodeURIComponent(`hans" //`)}`, {
90-
signal: AbortSignal.timeout(5000),
91-
}),
92+
fetch(
93+
`http://127.0.0.1:4001/where?title=${encodeURIComponent("Test'||'a")}`,
94+
{
95+
signal: AbortSignal.timeout(5000),
96+
}
97+
),
98+
fetch(
99+
`http://127.0.0.1:4001/where?title=${encodeURIComponent("Test' && sleep(10000); '")}`,
100+
{
101+
signal: AbortSignal.timeout(5000),
102+
}
103+
),
92104
])
93105
)
94-
.then(([safeName, unsafeName]) => {
95-
t.equal(safeName.status, 200);
96-
t.equal(unsafeName.status, 200);
106+
.then(([req1, req2]) => {
107+
t.equal(req1.status, 200);
108+
t.equal(req2.status, 200);
97109
t.match(stdout, /Starting agent/);
98-
t.match(stdout, /Zen has detected a JavaScript injection/);
110+
t.match(stdout, /Zen has detected a NoSQL injection/);
99111
})
100112
.catch((error) => {
101113
t.fail(error);

end2end/tests/express-mysql2.test.js

Lines changed: 68 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,11 @@ t.test("it blocks in blocking mode", (t) => {
3838
return Promise.all([
3939
fetch(
4040
`http://localhost:4000/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats;-- H")}`,
41-
{
42-
signal: AbortSignal.timeout(5000),
43-
}
41+
{ signal: AbortSignal.timeout(5000) }
4442
),
4543
fetch(
4644
`http://localhost:4000/cats/${encodeURIComponent("Njuska'; DELETE FROM cats;-- H")}`,
47-
{
48-
signal: AbortSignal.timeout(5000),
49-
}
45+
{ signal: AbortSignal.timeout(5000) }
5046
),
5147
fetch("http://localhost:4000/?petname=Njuska", {
5248
signal: AbortSignal.timeout(5000),
@@ -93,15 +89,11 @@ t.test("it does not block in dry mode", (t) => {
9389
Promise.all([
9490
fetch(
9591
`http://localhost:4001/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats;-- H")}`,
96-
{
97-
signal: AbortSignal.timeout(5000),
98-
}
92+
{ signal: AbortSignal.timeout(5000) }
9993
),
10094
fetch(
10195
`http://localhost:4001/cats/${encodeURIComponent("Njuska'; DELETE FROM cats;-- H")}`,
102-
{
103-
signal: AbortSignal.timeout(5000),
104-
}
96+
{ signal: AbortSignal.timeout(5000) }
10597
),
10698
fetch("http://localhost:4001/?petname=Njuska", {
10799
signal: AbortSignal.timeout(5000),
@@ -114,6 +106,70 @@ t.test("it does not block in dry mode", (t) => {
114106
t.equal(normalSearch.status, 200);
115107
t.match(stdout, /Starting agent/);
116108
t.notMatch(stderr, /Zen has blocked an SQL injection/);
109+
t.notMatch(
110+
stderr,
111+
/Some packages can't be protected because they were imported before Zen was initialized\. Please make sure to import Zen as the first module in your application\./
112+
);
113+
})
114+
.catch((error) => {
115+
t.fail(error.message);
116+
})
117+
.finally(() => {
118+
server.kill();
119+
});
120+
});
121+
122+
t.test("it prints wrong import warning", (t) => {
123+
const server = spawn(`node`, [pathToApp, "4002"], {
124+
env: {
125+
...process.env,
126+
AIKIDO_DEBUG: "true",
127+
AIKIDO_BLOCK: "true",
128+
TEST_IMPORT_TOO_LATE: "true",
129+
},
130+
});
131+
132+
server.on("close", () => {
133+
t.end();
134+
});
135+
136+
let stdout = "";
137+
server.stdout.on("data", (data) => {
138+
stdout += data.toString();
139+
});
140+
141+
let stderr = "";
142+
server.stderr.on("data", (data) => {
143+
stderr += data.toString();
144+
});
145+
146+
// Wait for the server to start
147+
timeout(2000)
148+
.then(() =>
149+
Promise.all([
150+
fetch(
151+
`http://localhost:4002/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats;-- H")}`,
152+
{ signal: AbortSignal.timeout(5000) }
153+
),
154+
fetch(
155+
`http://localhost:4002/cats/${encodeURIComponent("Njuska'; DELETE FROM cats;-- H")}`,
156+
{ signal: AbortSignal.timeout(5000) }
157+
),
158+
fetch("http://localhost:4002/?petname=Njuska", {
159+
signal: AbortSignal.timeout(5000),
160+
}),
161+
])
162+
)
163+
.then(([sqlInjection, sqlInjection2, normalSearch]) => {
164+
t.equal(sqlInjection.status, 200);
165+
t.equal(sqlInjection2.status, 200);
166+
t.equal(normalSearch.status, 200);
167+
t.match(stdout, /Starting agent/);
168+
t.match(
169+
stderr,
170+
/Some packages can't be protected because they were imported before Zen was initialized\. Please make sure to import Zen as the first module in your application\./
171+
);
172+
t.notMatch(stderr, /Zen has blocked an SQL injection/);
117173
})
118174
.catch((error) => {
119175
t.fail(error.message);

end2end/tests/n8n.test.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
11
const t = require("tap");
2-
const { spawnSync, spawn, execSync } = require("child_process");
2+
const { spawn, execSync } = require("child_process");
33
const { resolve, join } = require("path");
44
const timeout = require("../timeout");
55

66
const pathToApp = resolve(__dirname, "../../sample-apps/n8n");
7+
const dataFolder = join(pathToApp, ".n8n");
8+
9+
t.before(() => {
10+
// Delete the .n8n folder if it exists
11+
try {
12+
execSync(`rm -rf ${dataFolder}`);
13+
} catch (error) {
14+
// Ignore error
15+
}
16+
});
717

818
t.test("it logs in", (t) => {
919
const port = 5678;
@@ -14,6 +24,7 @@ t.test("it logs in", (t) => {
1424
AIKIDO_BLOCK: "true",
1525
NODE_OPTIONS: "-r @aikidosec/firewall",
1626
N8N_PORT: port.toString(),
27+
N8N_USER_FOLDER: dataFolder,
1728
},
1829
cwd: pathToApp,
1930
});
@@ -37,7 +48,7 @@ t.test("it logs in", (t) => {
3748
});
3849

3950
// Wait for the server to start
40-
timeout(5000)
51+
timeout(8000)
4152
.then(() => {
4253
return fetch(`http://127.0.0.1:${port}/rest/owner/setup`, {
4354
method: "POST",

library/agent/Agent.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ export class Agent {
468468
}
469469
}
470470

471-
wrapInstalledPackages(wrappers);
471+
wrapInstalledPackages(wrappers, this.serverless);
472472

473473
// Send startup event and wait for config
474474
// Then start heartbeats and polling for config changes

library/agent/Source.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export const SOURCES = [
88
"xml",
99
"subdomains",
1010
"markUnsafe",
11+
"url",
1112
] as const;
1213

1314
export type Source = (typeof SOURCES)[number];

0 commit comments

Comments
 (0)