diff --git a/end2end/getFreePort.js b/end2end/getFreePort.js new file mode 100644 index 000000000..36fa5f1e8 --- /dev/null +++ b/end2end/getFreePort.js @@ -0,0 +1,14 @@ +const { randomInt } = require("crypto"); +const used = []; + +module.exports = function getFreePort(t) { + const port = randomInt(3000, 10000) + t.childId; + + if (used.includes(port)) { + return getFreePort(t); + } + + used.push(port); + + return port; +}; diff --git a/end2end/package-lock.json b/end2end/package-lock.json index 540f68c8e..ea509ecc4 100644 --- a/end2end/package-lock.json +++ b/end2end/package-lock.json @@ -7,7 +7,8 @@ "name": "end2end", "dependencies": { "@supercharge/promise-pool": "^3.1.1", - "tap": "^18.7.0" + "tap": "^18.7.0", + "wait-on": "^8.0.1" } }, "node_modules/@alcalzone/ansi-tokenize": { @@ -43,6 +44,19 @@ "node": ">=12" } }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -264,6 +278,24 @@ "node": ">=14" } }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" + }, "node_modules/@sigstore/bundle": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.3.2.tgz", @@ -941,6 +973,11 @@ "node": ">=16" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/auto-bind": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-5.0.1.tgz", @@ -952,6 +989,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1340,6 +1387,17 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1411,6 +1469,14 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/diff": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", @@ -1506,6 +1572,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/foreground-child": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", @@ -1521,6 +1606,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fromentries": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", @@ -1966,6 +2064,18 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -2068,6 +2178,25 @@ "node": "^16.14.0 || >=18.0.0" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -2090,6 +2219,14 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", @@ -2651,6 +2788,11 @@ "node": ">=10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -2815,6 +2957,14 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -3491,6 +3641,24 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/wait-on": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-8.0.1.tgz", + "integrity": "sha512-1wWQOyR2LVVtaqrcIL2+OM+x7bkpmzVROa0Nf6FryXkS+er5Sa1kzFGjzZRqLnHa3n1rACFLeTwUqE1ETL9Mig==", + "dependencies": { + "axios": "^1.7.7", + "joi": "^17.13.3", + "lodash": "^4.17.21", + "minimist": "^1.2.8", + "rxjs": "^7.8.1" + }, + "bin": { + "wait-on": "bin/wait-on" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/walk-up-path": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-3.0.1.tgz", diff --git a/end2end/package.json b/end2end/package.json index 53387d8bc..bd2fa3614 100644 --- a/end2end/package.json +++ b/end2end/package.json @@ -3,9 +3,10 @@ "private": true, "dependencies": { "@supercharge/promise-pool": "^3.1.1", - "tap": "^18.7.0" + "tap": "^18.7.0", + "wait-on": "^8.0.1" }, "scripts": { - "test": "AIKIDO_CI=true tap tests/*.js --allow-empty-coverage -j 1" + "test": "AIKIDO_CI=true tap tests/*.js --allow-empty-coverage" } } diff --git a/end2end/server/app.js b/end2end/server/app.js index fb1844f02..52796cd55 100644 --- a/end2end/server/app.js +++ b/end2end/server/app.js @@ -1,4 +1,4 @@ -// This is a insecure mock server for testing purposes +// This is an insecure mock server for testing purposes const express = require("express"); const config = require("./src/handlers/getConfig"); const captureEvent = require("./src/handlers/captureEvent"); diff --git a/end2end/tests/big-payloads.test.js b/end2end/tests/big-payloads.test.js index 35fe63306..084523816 100644 --- a/end2end/tests/big-payloads.test.js +++ b/end2end/tests/big-payloads.test.js @@ -1,7 +1,8 @@ const t = require("tap"); const { spawn } = require("child_process"); const { resolve } = require("path"); -const timeout = require("../timeout"); +const waitOn = require("../waitOn"); +const getFreePort = require("../getFreePort"); const { PromisePool } = require("@supercharge/promise-pool"); const pathToApp = resolve( @@ -11,7 +12,8 @@ const pathToApp = resolve( ); t.test("it does not crash if many attacks with big payloads", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4000"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true", AIKIDO_BLOCKING: "true" }, }); @@ -20,7 +22,7 @@ t.test("it does not crash if many attacks with big payloads", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -36,7 +38,7 @@ t.test("it does not crash if many attacks with big payloads", (t) => { const amount = 2000; // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => { return PromisePool.withConcurrency(3) .for(Array.from({ length: amount })) @@ -49,7 +51,7 @@ t.test("it does not crash if many attacks with big payloads", (t) => { })), }; - return await fetch(`http://localhost:4000/search`, { + return await fetch(`http://localhost:${port}/search`, { method: "POST", signal: AbortSignal.timeout(5000), body: JSON.stringify(filter), @@ -64,7 +66,7 @@ t.test("it does not crash if many attacks with big payloads", (t) => { ); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); diff --git a/end2end/tests/express-graphql.test.js b/end2end/tests/express-graphql.test.js index c3ae92f86..7ef10dea5 100644 --- a/end2end/tests/express-graphql.test.js +++ b/end2end/tests/express-graphql.test.js @@ -1,7 +1,8 @@ const t = require("tap"); const { spawn } = require("child_process"); const { resolve } = require("path"); -const timeout = require("../timeout"); +const waitOn = require("../waitOn"); +const getFreePort = require("../getFreePort"); const pathToApp = resolve( __dirname, @@ -10,7 +11,8 @@ const pathToApp = resolve( ); t.test("it blocks in blocking mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4000"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true", AIKIDO_BLOCKING: "true" }, }); @@ -19,7 +21,7 @@ t.test("it blocks in blocking mode", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -33,10 +35,10 @@ t.test("it blocks in blocking mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => { return Promise.all([ - fetch(`http://localhost:4000/graphql`, { + fetch(`http://127.0.0.1:${port}/graphql`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ @@ -44,7 +46,7 @@ t.test("it blocks in blocking mode", (t) => { }), signal: AbortSignal.timeout(5000), }), - fetch(`http://localhost:4000/graphql`, { + fetch(`http://127.0.0.1:${port}/graphql`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ @@ -64,7 +66,7 @@ t.test("it blocks in blocking mode", (t) => { t.match(stdout, /Starting agent/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); @@ -72,7 +74,8 @@ t.test("it blocks in blocking mode", (t) => { }); t.test("it does not block in dry mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4001"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true" }, }); @@ -91,10 +94,10 @@ t.test("it does not block in dry mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => Promise.all([ - fetch(`http://localhost:4000/graphql`, { + fetch(`http://127.0.0.1:${port}/graphql`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ @@ -102,7 +105,7 @@ t.test("it does not block in dry mode", (t) => { }), signal: AbortSignal.timeout(5000), }), - fetch(`http://localhost:4000/graphql`, { + fetch(`http://127.0.0.1:${port}/graphql`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ @@ -123,7 +126,7 @@ t.test("it does not block in dry mode", (t) => { t.notMatch(stderr, /Zen has blocked an SQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); diff --git a/end2end/tests/express-mariadb.test.js b/end2end/tests/express-mariadb.test.js index 02eec40c6..7e61abde9 100644 --- a/end2end/tests/express-mariadb.test.js +++ b/end2end/tests/express-mariadb.test.js @@ -1,7 +1,8 @@ const t = require("tap"); const { spawn } = require("child_process"); const { resolve } = require("path"); -const timeout = require("../timeout"); +const waitOn = require("../waitOn"); +const getFreePort = require("../getFreePort"); const pathToApp = resolve( __dirname, @@ -10,7 +11,8 @@ const pathToApp = resolve( ); t.test("it blocks in blocking mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4000"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true", AIKIDO_BLOCKING: "true" }, }); @@ -19,7 +21,7 @@ t.test("it blocks in blocking mode", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -33,16 +35,16 @@ t.test("it blocks in blocking mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => { return Promise.all([ fetch( - `http://localhost:4000/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats;-- H")}`, + `http://localhost:${port}/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats;-- H")}`, { signal: AbortSignal.timeout(5000), } ), - fetch("http://localhost:4000/?petname=Njuska", { + fetch(`http://localhost:${port}/?petname=Njuska`, { signal: AbortSignal.timeout(5000), }), ]); @@ -54,7 +56,7 @@ t.test("it blocks in blocking mode", (t) => { t.match(stderr, /Zen has blocked an SQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); @@ -62,7 +64,8 @@ t.test("it blocks in blocking mode", (t) => { }); t.test("it does not block in dry mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4001"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true" }, }); @@ -81,16 +84,16 @@ t.test("it does not block in dry mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => Promise.all([ fetch( - `http://localhost:4001/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats;-- H")}`, + `http://localhost:${port}/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats;-- H")}`, { signal: AbortSignal.timeout(5000), } ), - fetch("http://localhost:4001/?petname=Njuska", { + fetch(`http://localhost:${port}/?petname=Njuska`, { signal: AbortSignal.timeout(5000), }), ]) @@ -102,7 +105,7 @@ t.test("it does not block in dry mode", (t) => { t.notMatch(stderr, /Zen has blocked an SQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); diff --git a/end2end/tests/express-mongodb.shell-injection.test.js b/end2end/tests/express-mongodb.shell-injection.test.js index 7209b3d34..4a7f99698 100644 --- a/end2end/tests/express-mongodb.shell-injection.test.js +++ b/end2end/tests/express-mongodb.shell-injection.test.js @@ -1,7 +1,8 @@ const t = require("tap"); const { spawn } = require("child_process"); const { resolve } = require("path"); -const timeout = require("../timeout"); +const waitOn = require("../waitOn"); +const getFreePort = require("../getFreePort"); const pathToApp = resolve( __dirname, @@ -10,7 +11,8 @@ const pathToApp = resolve( ); t.test("it blocks in blocking mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4000"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true", AIKIDO_BLOCK: "true" }, }); @@ -19,7 +21,7 @@ t.test("it blocks in blocking mode", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -33,10 +35,10 @@ t.test("it blocks in blocking mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => { return Promise.all([ - fetch("http://localhost:4000/ls", { + fetch(`http://localhost:${port}/ls`, { method: "POST", signal: AbortSignal.timeout(5000), headers: { @@ -46,7 +48,7 @@ t.test("it blocks in blocking mode", (t) => { directory: "'; ls ~", }), }), - fetch("http://localhost:4000/ls", { + fetch(`http://localhost:${port}/ls`, { method: "POST", signal: AbortSignal.timeout(5000), headers: { @@ -65,7 +67,7 @@ t.test("it blocks in blocking mode", (t) => { t.match(stderr, /Zen has blocked a shell injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); @@ -73,7 +75,8 @@ t.test("it blocks in blocking mode", (t) => { }); t.test("it does not block in dry mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4001"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true" }, }); @@ -92,10 +95,10 @@ t.test("it does not block in dry mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => Promise.all([ - fetch("http://localhost:4001/ls", { + fetch(`http://localhost:${port}/ls`, { method: "POST", signal: AbortSignal.timeout(5000), headers: { @@ -105,7 +108,7 @@ t.test("it does not block in dry mode", (t) => { directory: "'; ls ~; echo '", }), }), - fetch("http://localhost:4001/ls", { + fetch(`http://localhost:${port}/ls`, { method: "POST", signal: AbortSignal.timeout(5000), headers: { @@ -124,7 +127,7 @@ t.test("it does not block in dry mode", (t) => { t.notMatch(stderr, /Zen has blocked a shell injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); diff --git a/end2end/tests/express-mongodb.ssrf.test.js b/end2end/tests/express-mongodb.ssrf.test.js index 2255875b6..d7a690249 100644 --- a/end2end/tests/express-mongodb.ssrf.test.js +++ b/end2end/tests/express-mongodb.ssrf.test.js @@ -3,7 +3,8 @@ const { spawn } = require("child_process"); const { readFile } = require("fs/promises"); const { createServer } = require("http"); const { resolve } = require("path"); -const timeout = require("../timeout"); +const waitOn = require("../waitOn"); +const getFreePort = require("../getFreePort"); const pathToApp = resolve( __dirname, @@ -13,7 +14,8 @@ const pathToApp = resolve( const testServerUrl = "http://localhost:5874"; const safeImage = "https://nodejs.org/static/images/favicons/favicon.png"; -const unsafeImage = "http://local.aikido.io:5875/favicon.png"; +const port = getFreePort(t); +const unsafeImage = `http://local.aikido.io:${port}/favicon.png`; t.setTimeout(60000); @@ -33,7 +35,7 @@ t.before(async () => { } }); - server.listen(5875, () => { + server.listen(port, () => { resolve(); }); @@ -51,7 +53,8 @@ t.beforeEach(async () => { }); t.test("it blocks in blocking mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4000"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true", @@ -66,7 +69,7 @@ t.test("it blocks in blocking mode", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -80,14 +83,17 @@ t.test("it blocks in blocking mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => { return Promise.all([ - fetch(`http://127.0.0.1:4000/images/${encodeURIComponent(safeImage)}`, { - signal: AbortSignal.timeout(5000), - }), fetch( - `http://127.0.0.1:4000/images/${encodeURIComponent(unsafeImage)}`, + `http://127.0.0.1:${port}/images/${encodeURIComponent(safeImage)}`, + { + signal: AbortSignal.timeout(5000), + } + ), + fetch( + `http://127.0.0.1:${port}/images/${encodeURIComponent(unsafeImage)}`, { signal: AbortSignal.timeout(5000), } @@ -121,7 +127,7 @@ t.test("it blocks in blocking mode", (t) => { t.match(attack.attack.stack, /express-async-handler/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); @@ -129,7 +135,8 @@ t.test("it blocks in blocking mode", (t) => { }); t.test("it does not block in dry mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4001"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true", @@ -153,14 +160,17 @@ t.test("it does not block in dry mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => Promise.all([ - fetch(`http://127.0.0.1:4001/images/${encodeURIComponent(safeImage)}`, { - signal: AbortSignal.timeout(5000), - }), fetch( - `http://127.0.0.1:4001/images/${encodeURIComponent(unsafeImage)}`, + `http://127.0.0.1:${port}/images/${encodeURIComponent(safeImage)}`, + { + signal: AbortSignal.timeout(5000), + } + ), + fetch( + `http://127.0.0.1:${port}/images/${encodeURIComponent(unsafeImage)}`, { signal: AbortSignal.timeout(5000), } @@ -174,7 +184,7 @@ t.test("it does not block in dry mode", (t) => { t.notMatch(stderr, /Zen has blocked a server-side request forgery/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); diff --git a/end2end/tests/express-mongodb.test.js b/end2end/tests/express-mongodb.test.js index 9a508b769..0607e33e6 100644 --- a/end2end/tests/express-mongodb.test.js +++ b/end2end/tests/express-mongodb.test.js @@ -1,7 +1,9 @@ const t = require("tap"); const { spawn } = require("child_process"); const { resolve } = require("path"); -const timeout = require("../timeout"); +const waitOn = require("../waitOn"); +const getFreePort = require("../getFreePort"); +const { setTimeout } = require("timers/promises"); const pathToApp = resolve( __dirname, @@ -12,7 +14,8 @@ const pathToApp = resolve( t.setTimeout(60000); t.test("it blocks in blocking mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4000"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true", AIKIDO_BLOCKING: "true" }, }); @@ -21,7 +24,7 @@ t.test("it blocks in blocking mode", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -35,13 +38,13 @@ t.test("it blocks in blocking mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => { return Promise.all([ - fetch("http://127.0.0.1:4000/?search[$ne]=null", { + fetch(`http://127.0.0.1:${port}/?search[$ne]=null`, { signal: AbortSignal.timeout(5000), }), - fetch("http://127.0.0.1:4000/?search=title", { + fetch(`http://127.0.0.1:${port}/?search=title`, { signal: AbortSignal.timeout(5000), }), ]); @@ -53,7 +56,7 @@ t.test("it blocks in blocking mode", (t) => { t.match(stderr, /Zen has blocked a NoSQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); @@ -61,7 +64,8 @@ t.test("it blocks in blocking mode", (t) => { }); t.test("it does not block in dry mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4001"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true" }, }); @@ -80,13 +84,13 @@ t.test("it does not block in dry mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => Promise.all([ - fetch("http://127.0.0.1:4001/?search[$ne]=null", { + fetch(`http://127.0.0.1:${port}/?search[$ne]=null`, { signal: AbortSignal.timeout(5000), }), - fetch("http://127.0.0.1:4001/?search=title", { + fetch(`http://127.0.0.1:${port}/?search=title`, { signal: AbortSignal.timeout(5000), }), ]) @@ -98,7 +102,7 @@ t.test("it does not block in dry mode", (t) => { t.notMatch(stderr, /Zen has blocked a NoSQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); @@ -106,6 +110,7 @@ t.test("it does not block in dry mode", (t) => { }); t.test("it blocks in blocking mode (with open telemetry enabled)", (t) => { + const port = getFreePort(t); const server = spawn( `node`, [ @@ -113,7 +118,7 @@ t.test("it blocks in blocking mode (with open telemetry enabled)", (t) => { "--require", "@opentelemetry/auto-instrumentations-node/register", pathToApp, - "4002", + port, ], { cwd: resolve(__dirname, "../../sample-apps/express-mongodb"), @@ -134,7 +139,7 @@ t.test("it blocks in blocking mode (with open telemetry enabled)", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -148,15 +153,16 @@ t.test("it blocks in blocking mode (with open telemetry enabled)", (t) => { }); // Wait for the server to start - timeout(6000) + waitOn(port) .then(() => { return Promise.all([ - fetch("http://127.0.0.1:4002/?search[$ne]=null", { + fetch(`http://127.0.0.1:${port}/?search[$ne]=null`, { signal: AbortSignal.timeout(5000), }), - fetch("http://127.0.0.1:4002/?search=title", { + fetch(`http://127.0.0.1:${port}/?search=title`, { signal: AbortSignal.timeout(5000), }), + setTimeout(4000), ]); }) .then(([noSQLInjection, normalSearch]) => { @@ -167,7 +173,7 @@ t.test("it blocks in blocking mode (with open telemetry enabled)", (t) => { t.match(stderr, /Zen has blocked a NoSQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill("SIGINT"); @@ -175,6 +181,7 @@ t.test("it blocks in blocking mode (with open telemetry enabled)", (t) => { }); t.test("it does not block in dry mode (with open telemetry enabled)", (t) => { + const port = getFreePort(t); const server = spawn( `node`, [ @@ -182,7 +189,7 @@ t.test("it does not block in dry mode (with open telemetry enabled)", (t) => { "--require", "@opentelemetry/auto-instrumentations-node/register", pathToApp, - "4003", + port, ], { cwd: resolve(__dirname, "../../sample-apps/express-mongodb"), @@ -212,15 +219,16 @@ t.test("it does not block in dry mode (with open telemetry enabled)", (t) => { }); // Wait for the server to start - timeout(6000) + waitOn(port) .then(() => Promise.all([ - fetch("http://127.0.0.1:4003/?search[$ne]=null", { + fetch(`http://127.0.0.1:${port}/?search[$ne]=null`, { signal: AbortSignal.timeout(5000), }), - fetch("http://127.0.0.1:4003/?search=title", { + fetch(`http://127.0.0.1:${port}/?search=title`, { signal: AbortSignal.timeout(5000), }), + setTimeout(4000), ]) ) .then(([noSQLInjection, normalSearch]) => { @@ -231,7 +239,7 @@ t.test("it does not block in dry mode (with open telemetry enabled)", (t) => { t.notMatch(stderr, /Zen has blocked a NoSQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill("SIGINT"); diff --git a/end2end/tests/express-mongoose.test.js b/end2end/tests/express-mongoose.test.js index 3abe88025..bc974159a 100644 --- a/end2end/tests/express-mongoose.test.js +++ b/end2end/tests/express-mongoose.test.js @@ -1,7 +1,8 @@ const t = require("tap"); const { spawn } = require("child_process"); const { resolve } = require("path"); -const timeout = require("../timeout"); +const waitOn = require("../waitOn"); +const getFreePort = require("../getFreePort"); const pathToApp = resolve( __dirname, @@ -10,7 +11,8 @@ const pathToApp = resolve( ); t.test("it blocks in blocking mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4000"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true", AIKIDO_BLOCKING: "true" }, }); @@ -19,7 +21,7 @@ t.test("it blocks in blocking mode", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -33,13 +35,13 @@ t.test("it blocks in blocking mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => { return Promise.all([ - fetch("http://localhost:4000/?search[$ne]=null", { + fetch(`http://localhost:${port}/?search[$ne]=null`, { signal: AbortSignal.timeout(5000), }), - fetch("http://localhost:4000/?search=title", { + fetch(`http://localhost:${port}/?search=title`, { signal: AbortSignal.timeout(5000), }), ]); @@ -51,7 +53,7 @@ t.test("it blocks in blocking mode", (t) => { t.match(stderr, /Zen has blocked a NoSQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); @@ -59,7 +61,8 @@ t.test("it blocks in blocking mode", (t) => { }); t.test("it does not block in dry mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4001"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true" }, }); @@ -78,13 +81,13 @@ t.test("it does not block in dry mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => Promise.all([ - fetch("http://localhost:4001/?search[$ne]=null", { + fetch(`http://localhost:${port}/?search[$ne]=null`, { signal: AbortSignal.timeout(5000), }), - fetch("http://localhost:4001/?search=title", { + fetch(`http://localhost:${port}/?search=title`, { signal: AbortSignal.timeout(5000), }), ]) @@ -96,7 +99,7 @@ t.test("it does not block in dry mode", (t) => { t.notMatch(stderr, /Zen has blocked a NoSQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); diff --git a/end2end/tests/express-mysql.test.js b/end2end/tests/express-mysql.test.js index 863c9a31d..a072fbca1 100644 --- a/end2end/tests/express-mysql.test.js +++ b/end2end/tests/express-mysql.test.js @@ -1,7 +1,8 @@ const t = require("tap"); const { spawn } = require("child_process"); const { resolve } = require("path"); -const timeout = require("../timeout"); +const waitOn = require("../waitOn"); +const getFreePort = require("../getFreePort"); const pathToApp = resolve( __dirname, @@ -10,7 +11,8 @@ const pathToApp = resolve( ); t.test("it blocks in blocking mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4000"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true", AIKIDO_BLOCKING: "true" }, }); @@ -19,7 +21,7 @@ t.test("it blocks in blocking mode", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -33,16 +35,16 @@ t.test("it blocks in blocking mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => { return Promise.all([ fetch( - `http://localhost:4000/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats;-- H")}`, + `http://localhost:${port}/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats;-- H")}`, { signal: AbortSignal.timeout(5000), } ), - fetch("http://localhost:4000/cats", { + fetch(`http://localhost:${port}/cats`, { signal: AbortSignal.timeout(5000), method: "POST", body: "Njuska'); DELETE FROM cats;-- H", @@ -50,10 +52,10 @@ t.test("it blocks in blocking mode", (t) => { "Content-Type": "application/xml", }, }), - fetch("http://localhost:4000/?petname=Njuska", { + fetch(`http://localhost:${port}/?petname=Njuska`, { signal: AbortSignal.timeout(5000), }), - fetch("http://localhost:4000/cats", { + fetch(`http://localhost:${port}/cats`, { signal: AbortSignal.timeout(5000), method: "POST", body: "Njuska", @@ -72,7 +74,7 @@ t.test("it blocks in blocking mode", (t) => { t.match(stderr, /Zen has blocked an SQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); @@ -80,7 +82,8 @@ t.test("it blocks in blocking mode", (t) => { }); t.test("it does not block in dry mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4001"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true" }, }); @@ -99,16 +102,16 @@ t.test("it does not block in dry mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => Promise.all([ fetch( - `http://localhost:4001/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats;-- H")}`, + `http://localhost:${port}/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats;-- H")}`, { signal: AbortSignal.timeout(5000), } ), - fetch("http://localhost:4001/cats", { + fetch(`http://localhost:${port}/cats`, { signal: AbortSignal.timeout(5000), method: "POST", body: "Njuska'); DELETE FROM cats;-- H", @@ -116,10 +119,10 @@ t.test("it does not block in dry mode", (t) => { "Content-Type": "application/xml", }, }), - fetch("http://localhost:4001/?petname=Njuska", { + fetch(`http://localhost:${port}/?petname=Njuska`, { signal: AbortSignal.timeout(5000), }), - fetch("http://localhost:4001/cats", { + fetch(`http://localhost:${port}/cats`, { signal: AbortSignal.timeout(5000), method: "POST", body: "Njuska", @@ -138,7 +141,7 @@ t.test("it does not block in dry mode", (t) => { t.notMatch(stderr, /Zen has blocked an SQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); diff --git a/end2end/tests/express-mysql2.test.js b/end2end/tests/express-mysql2.test.js index c9b35588a..2a569dae4 100644 --- a/end2end/tests/express-mysql2.test.js +++ b/end2end/tests/express-mysql2.test.js @@ -1,7 +1,8 @@ const t = require("tap"); const { spawn } = require("child_process"); const { resolve } = require("path"); -const timeout = require("../timeout"); +const waitOn = require("../waitOn"); +const getFreePort = require("../getFreePort"); const pathToApp = resolve( __dirname, @@ -10,7 +11,8 @@ const pathToApp = resolve( ); t.test("it blocks in blocking mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4000"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true", AIKIDO_BLOCKING: "true" }, }); @@ -19,7 +21,7 @@ t.test("it blocks in blocking mode", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -33,22 +35,22 @@ t.test("it blocks in blocking mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => { return Promise.all([ fetch( - `http://localhost:4000/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats;-- H")}`, + `http://127.0.0.1:${port}/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats;-- H")}`, { signal: AbortSignal.timeout(5000), } ), fetch( - `http://localhost:4000/cats/${encodeURIComponent("Njuska'; DELETE FROM cats;-- H")}`, + `http://127.0.0.1:${port}/cats/${encodeURIComponent("Njuska'; DELETE FROM cats;-- H")}`, { signal: AbortSignal.timeout(5000), } ), - fetch("http://localhost:4000/?petname=Njuska", { + fetch(`http://127.0.0.1:${port}/?petname=Njuska`, { signal: AbortSignal.timeout(5000), }), ]); @@ -61,7 +63,7 @@ t.test("it blocks in blocking mode", (t) => { t.match(stderr, /Zen has blocked an SQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); @@ -69,7 +71,8 @@ t.test("it blocks in blocking mode", (t) => { }); t.test("it does not block in dry mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4001"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true" }, }); @@ -88,22 +91,22 @@ t.test("it does not block in dry mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => Promise.all([ fetch( - `http://localhost:4001/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats;-- H")}`, + `http://127.0.0.1:${port}/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats;-- H")}`, { signal: AbortSignal.timeout(5000), } ), fetch( - `http://localhost:4001/cats/${encodeURIComponent("Njuska'; DELETE FROM cats;-- H")}`, + `http://127.0.0.1:${port}/cats/${encodeURIComponent("Njuska'; DELETE FROM cats;-- H")}`, { signal: AbortSignal.timeout(5000), } ), - fetch("http://localhost:4001/?petname=Njuska", { + fetch(`http://127.0.0.1:${port}/?petname=Njuska`, { signal: AbortSignal.timeout(5000), }), ]) @@ -116,7 +119,7 @@ t.test("it does not block in dry mode", (t) => { t.notMatch(stderr, /Zen has blocked an SQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); diff --git a/end2end/tests/express-path-traversal.test.js b/end2end/tests/express-path-traversal.test.js index 1654fecca..f7d389766 100644 --- a/end2end/tests/express-path-traversal.test.js +++ b/end2end/tests/express-path-traversal.test.js @@ -1,7 +1,8 @@ const t = require("tap"); const { spawn } = require("child_process"); const { resolve } = require("path"); -const timeout = require("../timeout"); +const waitOn = require("../waitOn"); +const getFreePort = require("../getFreePort"); const pathToApp = resolve( __dirname, @@ -10,7 +11,8 @@ const pathToApp = resolve( ); t.test("it blocks in blocking mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4000"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true", AIKIDO_BLOCKING: "true" }, }); @@ -19,7 +21,7 @@ t.test("it blocks in blocking mode", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -33,17 +35,17 @@ t.test("it blocks in blocking mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => { return Promise.all([ fetch( - "http://localhost:4000/?content=blablabla&filename=/../TestDoc.txt", + `http://localhost:${port}/?content=blablabla&filename=/../TestDoc.txt`, { signal: AbortSignal.timeout(5000), } ), fetch( - "http://localhost:4000/?content=blablabla&filename=/TestDoc.txt", + `http://localhost:${port}/?content=blablabla&filename=/TestDoc.txt`, { signal: AbortSignal.timeout(5000), } @@ -57,7 +59,7 @@ t.test("it blocks in blocking mode", (t) => { t.match(stderr, /Zen has blocked a path traversal attack/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); @@ -65,7 +67,8 @@ t.test("it blocks in blocking mode", (t) => { }); t.test("it does not block in dry mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4001"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true" }, }); @@ -84,17 +87,17 @@ t.test("it does not block in dry mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => Promise.all([ fetch( - "http://localhost:4001/?content=blablabla&filename=/../TestDoc.txt", + `http://localhost:${port}/?content=blablabla&filename=/../TestDoc.txt`, { signal: AbortSignal.timeout(5000), } ), fetch( - "http://localhost:4001/?content=blablabla&filename=/TestDoc.txt", + `http://localhost:${port}/?content=blablabla&filename=/TestDoc.txt`, { signal: AbortSignal.timeout(5000), } @@ -108,7 +111,7 @@ t.test("it does not block in dry mode", (t) => { t.notMatch(stderr, /Zen has blocked a path traversal attack/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); diff --git a/end2end/tests/express-postgres.test.js b/end2end/tests/express-postgres.test.js index b445e05ca..b145990b2 100644 --- a/end2end/tests/express-postgres.test.js +++ b/end2end/tests/express-postgres.test.js @@ -1,7 +1,8 @@ const t = require("tap"); const { spawn } = require("child_process"); const { resolve } = require("path"); -const timeout = require("../timeout"); +const waitOn = require("../waitOn"); +const getFreePort = require("../getFreePort"); const pathToApp = resolve( __dirname, @@ -10,7 +11,8 @@ const pathToApp = resolve( ); t.test("it blocks in blocking mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4000"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true", AIKIDO_BLOCKING: "true" }, }); @@ -19,7 +21,7 @@ t.test("it blocks in blocking mode", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -33,28 +35,28 @@ t.test("it blocks in blocking mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => { return Promise.all([ fetch( - `http://localhost:4000/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats_2;-- H")}`, + `http://localhost:${port}/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats_2;-- H")}`, { signal: AbortSignal.timeout(5000), } ), - fetch(`http://localhost:4000/string-concat`, { + fetch(`http://localhost:${port}/string-concat`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ petname: ["'", "1)", "(0,1)", "(1", "'"] }), signal: AbortSignal.timeout(5000), }), fetch( - `http://localhost:4000/string-concat?petname='&petname=1)&petname=(0,1)&petname=(1&petname='`, + `http://localhost:${port}/string-concat?petname='&petname=1)&petname=(0,1)&petname=(1&petname='`, { signal: AbortSignal.timeout(5000), } ), - fetch("http://localhost:4000/?petname=Njuska", { + fetch(`http://localhost:${port}/?petname=Njuska`, { signal: AbortSignal.timeout(5000), }), ]); @@ -70,7 +72,7 @@ t.test("it blocks in blocking mode", (t) => { } ) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); @@ -78,7 +80,8 @@ t.test("it blocks in blocking mode", (t) => { }); t.test("it does not block in dry mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4001"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true" }, }); @@ -97,28 +100,28 @@ t.test("it does not block in dry mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => Promise.all([ fetch( - `http://localhost:4001/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats_2;-- H")}`, + `http://localhost:${port}/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats_2;-- H")}`, { signal: AbortSignal.timeout(5000), } ), - fetch(`http://localhost:4001/string-concat`, { + fetch(`http://localhost:${port}/string-concat`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ petname: ["'", "1)", "(0,1)", "(1", "'"] }), signal: AbortSignal.timeout(5000), }), fetch( - `http://localhost:4001/string-concat?petname='&petname=1)&petname=(0,1)&petname=(1&petname='`, + `http://localhost:${port}/string-concat?petname='&petname=1)&petname=(0,1)&petname=(1&petname='`, { signal: AbortSignal.timeout(5000), } ), - fetch("http://localhost:4001/?petname=Njuska", { + fetch(`http://localhost:${port}/?petname=Njuska`, { signal: AbortSignal.timeout(5000), }), ]) @@ -134,7 +137,7 @@ t.test("it does not block in dry mode", (t) => { } ) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); diff --git a/end2end/tests/fastify-mysql2.test.js b/end2end/tests/fastify-mysql2.test.js index acbeaed9e..faa114013 100644 --- a/end2end/tests/fastify-mysql2.test.js +++ b/end2end/tests/fastify-mysql2.test.js @@ -1,7 +1,8 @@ const t = require("tap"); const { spawn } = require("child_process"); const { resolve } = require("path"); -const timeout = require("../timeout"); +const waitOn = require("../waitOn"); +const getFreePort = require("../getFreePort"); const pathToApp = resolve( __dirname, @@ -10,7 +11,8 @@ const pathToApp = resolve( ); t.test("it blocks in blocking mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4000"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true", AIKIDO_BLOCKING: "true" }, }); @@ -19,7 +21,7 @@ t.test("it blocks in blocking mode", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -33,19 +35,19 @@ t.test("it blocks in blocking mode", (t) => { }); // Wait for the server to start - timeout(4000) + waitOn(port) .then(() => { return Promise.all([ fetch( - `http://127.0.0.1:4000/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats;-- H")}`, + `http://127.0.0.1:${port}/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats;-- H")}`, { signal: AbortSignal.timeout(5000), } ), - fetch("http://127.0.0.1:4000/?petname=Njuska", { + fetch(`http://127.0.0.1:${port}/?petname=Njuska`, { signal: AbortSignal.timeout(5000), }), - fetch("http://127.0.0.1:4000/context", { + fetch(`http://127.0.0.1:${port}/context`, { signal: AbortSignal.timeout(5000), }), ]); @@ -57,7 +59,7 @@ t.test("it blocks in blocking mode", (t) => { t.match(stdout, /Zen has blocked an SQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); @@ -65,7 +67,8 @@ t.test("it blocks in blocking mode", (t) => { }); t.test("it does not block in dry mode", (t) => { - const server = spawn(`node`, [pathToApp, "4001"], { + const port = getFreePort(t); + const server = spawn(`node`, [pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true" }, }); @@ -84,16 +87,16 @@ t.test("it does not block in dry mode", (t) => { }); // Wait for the server to start - timeout(4000) + waitOn(port) .then(() => Promise.all([ fetch( - `http://127.0.0.1:4001/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats;-- H")}`, + `http://127.0.0.1:${port}/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats;-- H")}`, { signal: AbortSignal.timeout(5000), } ), - fetch("http://127.0.0.1:4001/?petname=Njuska", { + fetch(`http://127.0.0.1:${port}/?petname=Njuska`, { signal: AbortSignal.timeout(5000), }), ]) @@ -105,7 +108,7 @@ t.test("it does not block in dry mode", (t) => { t.notMatch(stdout, /Zen has blocked an SQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); diff --git a/end2end/tests/hapi-postgres.test.js b/end2end/tests/hapi-postgres.test.js index 2b30757e9..b01fe96d6 100644 --- a/end2end/tests/hapi-postgres.test.js +++ b/end2end/tests/hapi-postgres.test.js @@ -1,7 +1,8 @@ const t = require("tap"); const { spawn } = require("child_process"); const { resolve } = require("path"); -const timeout = require("../timeout"); +const waitOn = require("../waitOn"); +const getFreePort = require("../getFreePort"); const pathToApp = resolve( __dirname, @@ -10,7 +11,8 @@ const pathToApp = resolve( ); t.test("it blocks in blocking mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4000"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true", AIKIDO_BLOCKING: "true" }, }); @@ -19,7 +21,7 @@ t.test("it blocks in blocking mode", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -33,16 +35,16 @@ t.test("it blocks in blocking mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => { return Promise.all([ fetch( - `http://127.0.0.1:4000/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats;-- H")}`, + `http://127.0.0.1:${port}/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats;-- H")}`, { signal: AbortSignal.timeout(5000), } ), - fetch("http://127.0.0.1:4000/?petname=Njuska", { + fetch(`http://127.0.0.1:${port}/?petname=Njuska`, { signal: AbortSignal.timeout(5000), }), ]); @@ -54,7 +56,7 @@ t.test("it blocks in blocking mode", (t) => { t.match(await noSQLInjection.text(), /Zen has blocked an SQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); @@ -62,7 +64,8 @@ t.test("it blocks in blocking mode", (t) => { }); t.test("it does not block in dry mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4001"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true" }, }); @@ -81,16 +84,16 @@ t.test("it does not block in dry mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => Promise.all([ fetch( - `http://127.0.0.1:4001/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats;-- H")}`, + `http://127.0.0.1:${port}/?petname=${encodeURIComponent("Njuska'); DELETE FROM cats;-- H")}`, { signal: AbortSignal.timeout(5000), } ), - fetch("http://127.0.0.1:4001/?petname=Njuska", { + fetch(`http://127.0.0.1:${port}/?petname=Njuska`, { signal: AbortSignal.timeout(5000), }), ]) @@ -105,7 +108,7 @@ t.test("it does not block in dry mode", (t) => { ); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); diff --git a/end2end/tests/hono-mongodb.test.js b/end2end/tests/hono-mongodb.test.js index 5b813ea6d..598a621a2 100644 --- a/end2end/tests/hono-mongodb.test.js +++ b/end2end/tests/hono-mongodb.test.js @@ -1,7 +1,8 @@ const t = require("tap"); const { spawn } = require("child_process"); const { resolve } = require("path"); -const timeout = require("../timeout"); +const waitOn = require("../waitOn"); +const getFreePort = require("../getFreePort"); const pathToApp = resolve( __dirname, @@ -12,7 +13,8 @@ const pathToApp = resolve( t.setTimeout(60000); t.test("it blocks in blocking mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4000"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true", AIKIDO_BLOCKING: "true" }, }); @@ -21,7 +23,7 @@ t.test("it blocks in blocking mode", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -35,16 +37,16 @@ t.test("it blocks in blocking mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then((a) => { return Promise.all([ - fetch("http://127.0.0.1:4000/search", { + fetch(`http://127.0.0.1:${port}/search`, { method: "POST", signal: AbortSignal.timeout(5000), body: JSON.stringify({ title: { $ne: null } }), headers: { "Content-Type": "application/json" }, }), - fetch("http://127.0.0.1:4000/search", { + fetch(`http://127.0.0.1:${port}/search`, { method: "POST", signal: AbortSignal.timeout(5000), body: JSON.stringify({ title: "title" }), @@ -59,7 +61,7 @@ t.test("it blocks in blocking mode", (t) => { t.match(stderr, /Zen has blocked a NoSQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); @@ -67,7 +69,8 @@ t.test("it blocks in blocking mode", (t) => { }); t.test("it does not block in dry mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4001"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true" }, }); @@ -86,16 +89,16 @@ t.test("it does not block in dry mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then((a) => { return Promise.all([ - fetch("http://127.0.0.1:4001/search", { + fetch(`http://127.0.0.1:${port}/search`, { method: "POST", signal: AbortSignal.timeout(5000), body: JSON.stringify({ title: { $ne: null } }), headers: { "Content-Type": "application/json" }, }), - fetch("http://127.0.0.1:4001/search", { + fetch(`http://127.0.0.1:${port}/search`, { method: "POST", signal: AbortSignal.timeout(5000), body: JSON.stringify({ title: "title" }), @@ -110,7 +113,7 @@ t.test("it does not block in dry mode", (t) => { t.notMatch(stderr, /Zen has blocked a NoSQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); diff --git a/end2end/tests/hono-sqlite3.test.js b/end2end/tests/hono-sqlite3.test.js index a428124fd..c75324cec 100644 --- a/end2end/tests/hono-sqlite3.test.js +++ b/end2end/tests/hono-sqlite3.test.js @@ -1,7 +1,8 @@ const t = require("tap"); const { spawn } = require("child_process"); const { resolve } = require("path"); -const timeout = require("../timeout"); +const waitOn = require("../waitOn"); +const getFreePort = require("../getFreePort"); const pathToApp = resolve( __dirname, @@ -10,7 +11,8 @@ const pathToApp = resolve( ); t.test("it blocks in blocking mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4002"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true", AIKIDO_BLOCKING: "true" }, }); @@ -19,7 +21,7 @@ t.test("it blocks in blocking mode", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -33,10 +35,10 @@ t.test("it blocks in blocking mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => { return Promise.all([ - fetch("http://127.0.0.1:4002/add", { + fetch(`http://127.0.0.1:${port}/add`, { method: "POST", body: JSON.stringify({ name: "Test'), ('Test2');--" }), headers: { @@ -44,7 +46,7 @@ t.test("it blocks in blocking mode", (t) => { }, signal: AbortSignal.timeout(5000), }), - fetch("http://127.0.0.1:4002/add", { + fetch(`http://127.0.0.1:${port}/add`, { method: "POST", body: JSON.stringify({ name: "Miau" }), headers: { @@ -61,7 +63,7 @@ t.test("it blocks in blocking mode", (t) => { t.match(stderr, /Zen has blocked an SQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); @@ -69,7 +71,8 @@ t.test("it blocks in blocking mode", (t) => { }); t.test("it does not block in dry mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4003"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true" }, }); @@ -88,10 +91,10 @@ t.test("it does not block in dry mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => Promise.all([ - fetch("http://127.0.0.1:4003/add", { + fetch(`http://127.0.0.1:${port}/add`, { method: "POST", body: JSON.stringify({ name: "Test'), ('Test2');--" }), headers: { @@ -99,7 +102,7 @@ t.test("it does not block in dry mode", (t) => { }, signal: AbortSignal.timeout(5000), }), - fetch("http://127.0.0.1:4003/add", { + fetch(`http://127.0.0.1:${port}/add`, { method: "POST", body: JSON.stringify({ name: "Miau" }), headers: { @@ -116,7 +119,7 @@ t.test("it does not block in dry mode", (t) => { t.notMatch(stderr, /Zen has blocked an SQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); diff --git a/end2end/tests/hono-xml-rate-limiting.test.js b/end2end/tests/hono-xml-rate-limiting.test.js index f9f75a1a1..5ea330455 100644 --- a/end2end/tests/hono-xml-rate-limiting.test.js +++ b/end2end/tests/hono-xml-rate-limiting.test.js @@ -1,7 +1,8 @@ const t = require("tap"); const { spawn } = require("child_process"); const { resolve } = require("path"); -const timeout = require("../timeout"); +const waitOn = require("../waitOn"); +const getFreePort = require("../getFreePort"); const pathToApp = resolve(__dirname, "../../sample-apps/hono-xml", "app.js"); const testServerUrl = "http://localhost:5874"; @@ -43,7 +44,8 @@ t.beforeEach(async () => { }); t.test("it rate limits requests", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4002"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true", @@ -58,7 +60,7 @@ t.test("it rate limits requests", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -72,9 +74,9 @@ t.test("it rate limits requests", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(async () => { - const resp1 = await fetch("http://127.0.0.1:4002/add", { + const resp1 = await fetch(`http://127.0.0.1:${port}/add`, { method: "POST", body: "Njuska", headers: { @@ -84,7 +86,7 @@ t.test("it rate limits requests", (t) => { }); t.same(resp1.status, 200); - const resp2 = await fetch("http://127.0.0.1:4002/add", { + const resp2 = await fetch(`http://127.0.0.1:${port}/add`, { method: "POST", body: "Harry", headers: { @@ -95,7 +97,7 @@ t.test("it rate limits requests", (t) => { t.same(resp2.status, 429); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); @@ -103,7 +105,8 @@ t.test("it rate limits requests", (t) => { }); t.test("user rate limiting works", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4003"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true", @@ -118,7 +121,7 @@ t.test("user rate limiting works", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -132,9 +135,9 @@ t.test("user rate limiting works", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(async () => { - const resp1 = await fetch("http://127.0.0.1:4003/add", { + const resp1 = await fetch(`http://127.0.0.1:${port}/add`, { method: "POST", body: "Njuska", headers: { @@ -145,7 +148,7 @@ t.test("user rate limiting works", (t) => { }); t.same(resp1.status, 200); - const resp2 = await fetch("http://127.0.0.1:4003/add", { + const resp2 = await fetch(`http://127.0.0.1:${port}/add`, { method: "POST", body: "Harry", headers: { @@ -156,7 +159,7 @@ t.test("user rate limiting works", (t) => { }); t.same(resp2.status, 200); - const resp3 = await fetch("http://127.0.0.1:4003/add", { + const resp3 = await fetch(`http://127.0.0.1:${port}/add`, { method: "POST", body: "Njuska", headers: { @@ -168,7 +171,7 @@ t.test("user rate limiting works", (t) => { t.same(resp3.status, 429); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); diff --git a/end2end/tests/hono-xml.test.js b/end2end/tests/hono-xml.test.js index 20334bf20..a9020016a 100644 --- a/end2end/tests/hono-xml.test.js +++ b/end2end/tests/hono-xml.test.js @@ -1,12 +1,14 @@ const t = require("tap"); const { spawn } = require("child_process"); const { resolve } = require("path"); -const timeout = require("../timeout"); +const waitOn = require("../waitOn"); +const getFreePort = require("../getFreePort"); const pathToApp = resolve(__dirname, "../../sample-apps/hono-xml", "app.js"); t.test("it blocks in blocking mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4002"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true", AIKIDO_BLOCKING: "true" }, }); @@ -15,7 +17,7 @@ t.test("it blocks in blocking mode", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -29,10 +31,10 @@ t.test("it blocks in blocking mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => { return Promise.all([ - fetch("http://127.0.0.1:4002/add", { + fetch(`http://127.0.0.1:${port}/add`, { method: "POST", body: "Njuska'); DELETE FROM cats;-- H", headers: { @@ -40,7 +42,7 @@ t.test("it blocks in blocking mode", (t) => { }, signal: AbortSignal.timeout(5000), }), - fetch("http://127.0.0.1:4002/add-attribute", { + fetch(`http://127.0.0.1:${port}/add-attribute`, { method: "POST", body: ``, headers: { @@ -48,7 +50,7 @@ t.test("it blocks in blocking mode", (t) => { }, signal: AbortSignal.timeout(5000), }), - fetch("http://127.0.0.1:4002/add-fast", { + fetch(`http://127.0.0.1:${port}/add-fast`, { method: "POST", body: "Njuska'); DELETE FROM cats;-- H", headers: { @@ -56,7 +58,7 @@ t.test("it blocks in blocking mode", (t) => { }, signal: AbortSignal.timeout(5000), }), - fetch("http://127.0.0.1:4002/add-fast-attribute", { + fetch(`http://127.0.0.1:${port}/add-fast-attribute`, { method: "POST", body: ``, headers: { @@ -64,7 +66,7 @@ t.test("it blocks in blocking mode", (t) => { }, signal: AbortSignal.timeout(5000), }), - fetch("http://127.0.0.1:4002/add", { + fetch(`http://127.0.0.1:${port}/add`, { method: "POST", body: "Miau", headers: { @@ -92,7 +94,7 @@ t.test("it blocks in blocking mode", (t) => { } ) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); @@ -100,7 +102,8 @@ t.test("it blocks in blocking mode", (t) => { }); t.test("it does not block in dry mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4003"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true" }, }); @@ -119,10 +122,10 @@ t.test("it does not block in dry mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => Promise.all([ - fetch("http://127.0.0.1:4003/add", { + fetch(`http://127.0.0.1:${port}/add`, { method: "POST", body: "Njuska'); DELETE FROM cats;-- H", headers: { @@ -130,7 +133,7 @@ t.test("it does not block in dry mode", (t) => { }, signal: AbortSignal.timeout(5000), }), - fetch("http://127.0.0.1:4003/add-attribute", { + fetch(`http://127.0.0.1:${port}/add-attribute`, { method: "POST", body: ``, headers: { @@ -138,7 +141,7 @@ t.test("it does not block in dry mode", (t) => { }, signal: AbortSignal.timeout(5000), }), - fetch("http://127.0.0.1:4003/add-fast", { + fetch(`http://127.0.0.1:${port}/add-fast`, { method: "POST", body: "Njuska'); DELETE FROM cats;-- H", headers: { @@ -146,7 +149,7 @@ t.test("it does not block in dry mode", (t) => { }, signal: AbortSignal.timeout(5000), }), - fetch("http://127.0.0.1:4003/add-fast-attribute", { + fetch(`http://127.0.0.1:${port}/add-fast-attribute`, { method: "POST", body: ``, headers: { @@ -154,7 +157,7 @@ t.test("it does not block in dry mode", (t) => { }, signal: AbortSignal.timeout(5000), }), - fetch("http://127.0.0.1:4003/add", { + fetch(`http://127.0.0.1:${port}/add`, { method: "POST", body: "Miau", headers: { @@ -182,7 +185,7 @@ t.test("it does not block in dry mode", (t) => { } ) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); diff --git a/end2end/tests/http2.test.js b/end2end/tests/http2.test.js index bec6291f3..784856143 100644 --- a/end2end/tests/http2.test.js +++ b/end2end/tests/http2.test.js @@ -1,15 +1,17 @@ const t = require("tap"); const { spawn } = require("child_process"); const { resolve } = require("path"); -const timeout = require("../timeout"); +const getFreePort = require("../getFreePort"); const { connect } = require("http2"); +const { setTimeout } = require("timers/promises"); const pathToApp = resolve(__dirname, "../../sample-apps/http2", "index.js"); process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; t.test("it blocks in blocking mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4002"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true", AIKIDO_BLOCKING: "true" }, }); @@ -18,7 +20,7 @@ t.test("it blocks in blocking mode", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -32,13 +34,13 @@ t.test("it blocks in blocking mode", (t) => { }); // Wait for the server to start - timeout(2000) + setTimeout(2000) .then(() => { return Promise.all([ fetch( - "https://127.0.0.1:4002?url=https://www.cloudflare.com/favicon.ico" + `https://127.0.0.1:${port}?url=https://www.cloudflare.com/favicon.ico` ), - fetch("https://127.0.0.1:4002?url=http://localhost"), + fetch(`https://127.0.0.1:${port}?url=http://localhost`), ]); }) .then(([nonSSRF, ssrf]) => { @@ -48,7 +50,7 @@ t.test("it blocks in blocking mode", (t) => { t.match(stderr, /Zen has blocked a server-side request forgery/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); @@ -56,7 +58,8 @@ t.test("it blocks in blocking mode", (t) => { }); t.test("it does not block in dry mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4003"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true" }, }); @@ -75,13 +78,13 @@ t.test("it does not block in dry mode", (t) => { }); // Wait for the server to start - timeout(2000) + setTimeout(2000) .then(() => Promise.all([ fetch( - "https://127.0.0.1:4003?url=https://www.cloudflare.com/favicon.ico" + `https://127.0.0.1:${port}?url=https://www.cloudflare.com/favicon.ico` ), - fetch("https://127.0.0.1:4003?url=http://localhost"), + fetch(`https://127.0.0.1:${port}?url=http://localhost`), ]) ) .then(([nonSSRF, ssrf]) => { @@ -92,7 +95,7 @@ t.test("it does not block in dry mode", (t) => { t.match(stderr, /fetch failed/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); diff --git a/end2end/tests/koa-sqlite3.test.js b/end2end/tests/koa-sqlite3.test.js index df9ba5b53..0aee5fe65 100644 --- a/end2end/tests/koa-sqlite3.test.js +++ b/end2end/tests/koa-sqlite3.test.js @@ -1,12 +1,14 @@ const t = require("tap"); const { spawn } = require("child_process"); const { resolve } = require("path"); -const timeout = require("../timeout"); +const waitOn = require("../waitOn"); +const getFreePort = require("../getFreePort"); const pathToApp = resolve(__dirname, "../../sample-apps/koa-sqlite3", "app.js"); t.test("it blocks in blocking mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4002"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true", AIKIDO_BLOCKING: "true" }, }); @@ -15,7 +17,7 @@ t.test("it blocks in blocking mode", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -29,10 +31,10 @@ t.test("it blocks in blocking mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => { return Promise.all([ - fetch("http://127.0.0.1:4002/add", { + fetch(`http://127.0.0.1:${port}/add`, { method: "POST", body: JSON.stringify({ name: "Test'), ('Test2');--" }), headers: { @@ -40,7 +42,7 @@ t.test("it blocks in blocking mode", (t) => { }, signal: AbortSignal.timeout(5000), }), - fetch("http://127.0.0.1:4002/add", { + fetch(`http://127.0.0.1:${port}/add`, { method: "POST", body: JSON.stringify({ name: "Miau" }), headers: { @@ -57,7 +59,7 @@ t.test("it blocks in blocking mode", (t) => { t.match(stderr, /Zen has blocked an SQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); @@ -65,7 +67,8 @@ t.test("it blocks in blocking mode", (t) => { }); t.test("it does not block in dry mode", (t) => { - const server = spawn(`node`, ["--preserve-symlinks", pathToApp, "4003"], { + const port = getFreePort(t); + const server = spawn(`node`, ["--preserve-symlinks", pathToApp, port], { env: { ...process.env, AIKIDO_DEBUG: "true" }, }); @@ -84,10 +87,10 @@ t.test("it does not block in dry mode", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(() => Promise.all([ - fetch("http://127.0.0.1:4003/add", { + fetch(`http://127.0.0.1:${port}/add`, { method: "POST", body: JSON.stringify({ name: "Test'), ('Test2');--" }), headers: { @@ -95,7 +98,7 @@ t.test("it does not block in dry mode", (t) => { }, signal: AbortSignal.timeout(5000), }), - fetch("http://127.0.0.1:4003/add", { + fetch(`http://127.0.0.1:${port}/add`, { method: "POST", body: JSON.stringify({ name: "Miau" }), headers: { @@ -112,7 +115,7 @@ t.test("it does not block in dry mode", (t) => { t.notMatch(stderr, /Zen has blocked an SQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); diff --git a/end2end/tests/nestjs-fastify-reate-limiting.test.js b/end2end/tests/nestjs-fastify-rate-limiting.test.js similarity index 86% rename from end2end/tests/nestjs-fastify-reate-limiting.test.js rename to end2end/tests/nestjs-fastify-rate-limiting.test.js index 6f8c649b0..f1c1e86f4 100644 --- a/end2end/tests/nestjs-fastify-reate-limiting.test.js +++ b/end2end/tests/nestjs-fastify-rate-limiting.test.js @@ -1,7 +1,8 @@ const t = require("tap"); const { spawn, spawnSync } = require("child_process"); const { resolve } = require("path"); -const timeout = require("../timeout"); +const waitOn = require("../waitOn"); +const getFreePort = require("../getFreePort"); const pathToApp = resolve(__dirname, "../../sample-apps/nestjs-fastify"); const testServerUrl = "http://localhost:5874"; @@ -53,6 +54,7 @@ t.beforeEach(async () => { }); t.test("it rate limits requests", (t) => { + const port = getFreePort(t); const server = spawn(`node`, ["--preserve-symlinks", "dist/main"], { cwd: pathToApp, env: { @@ -61,7 +63,7 @@ t.test("it rate limits requests", (t) => { AIKIDO_BLOCKING: "true", AIKIDO_TOKEN: token, AIKIDO_URL: testServerUrl, - PORT: "4002", + PORT: port, }, }); @@ -70,7 +72,7 @@ t.test("it rate limits requests", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -84,9 +86,9 @@ t.test("it rate limits requests", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(async () => { - const resp1 = await fetch("http://127.0.0.1:4002/cats", { + const resp1 = await fetch(`http://127.0.0.1:${port}/cats`, { method: "POST", body: JSON.stringify({ name: "Njuska" }), headers: { @@ -96,7 +98,7 @@ t.test("it rate limits requests", (t) => { }); t.same(resp1.status, 201); - const resp2 = await fetch("http://127.0.0.1:4002/cats", { + const resp2 = await fetch(`http://127.0.0.1:${port}/cats`, { method: "POST", body: JSON.stringify({ name: "Harry" }), headers: { @@ -107,7 +109,7 @@ t.test("it rate limits requests", (t) => { t.same(resp2.status, 429); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); @@ -115,6 +117,7 @@ t.test("it rate limits requests", (t) => { }); t.test("user rate limiting works", (t) => { + const port = getFreePort(t); const server = spawn(`node`, ["--preserve-symlinks", "dist/main"], { cwd: pathToApp, env: { @@ -123,7 +126,7 @@ t.test("user rate limiting works", (t) => { AIKIDO_BLOCKING: "true", AIKIDO_TOKEN: token, AIKIDO_URL: testServerUrl, - PORT: "4003", + PORT: port, }, }); @@ -132,7 +135,7 @@ t.test("user rate limiting works", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -146,9 +149,9 @@ t.test("user rate limiting works", (t) => { }); // Wait for the server to start - timeout(2000) + waitOn(port) .then(async () => { - const resp1 = await fetch("http://127.0.0.1:4003/cats", { + const resp1 = await fetch(`http://127.0.0.1:${port}/cats`, { method: "POST", body: JSON.stringify({ name: "Njuska" }), headers: { @@ -159,7 +162,7 @@ t.test("user rate limiting works", (t) => { }); t.same(resp1.status, 201); - const resp2 = await fetch("http://127.0.0.1:4003/cats", { + const resp2 = await fetch(`http://127.0.0.1:${port}/cats`, { method: "POST", body: JSON.stringify({ name: "Harry" }), headers: { @@ -170,7 +173,7 @@ t.test("user rate limiting works", (t) => { }); t.same(resp2.status, 201); - const resp3 = await fetch("http://127.0.0.1:4003/cats", { + const resp3 = await fetch(`http://127.0.0.1:${port}/cats`, { method: "POST", body: JSON.stringify({ name: "Harry" }), headers: { @@ -182,7 +185,7 @@ t.test("user rate limiting works", (t) => { t.same(resp3.status, 429); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); diff --git a/end2end/tests/nestjs-fastify.test.js b/end2end/tests/nestjs-fastify.test.js index 1fba4eb0b..33a96a56d 100644 --- a/end2end/tests/nestjs-fastify.test.js +++ b/end2end/tests/nestjs-fastify.test.js @@ -1,7 +1,8 @@ const t = require("tap"); const { spawnSync, spawn } = require("child_process"); const { resolve } = require("path"); -const timeout = require("../timeout"); +const waitOn = require("../waitOn"); +const getFreePort = require("../getFreePort"); const pathToApp = resolve(__dirname, "../../sample-apps/nestjs-fastify"); @@ -16,12 +17,13 @@ t.before(() => { }); t.test("it blocks in blocking mode", (t) => { + const port = getFreePort(t); const server = spawn(`node`, ["--preserve-symlinks", "dist/main"], { env: { ...process.env, AIKIDO_DEBUG: "true", AIKIDO_BLOCK: "true", - PORT: "4000", + PORT: port, }, cwd: pathToApp, }); @@ -31,7 +33,7 @@ t.test("it blocks in blocking mode", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -45,10 +47,10 @@ t.test("it blocks in blocking mode", (t) => { }); // Wait for the server to start - timeout(5000) + waitOn(port) .then(() => { return Promise.all([ - fetch("http://127.0.0.1:4000/cats", { + fetch(`http://127.0.0.1:${port}/cats`, { method: "POST", headers: { "Content-Type": "application/json", @@ -68,7 +70,7 @@ t.test("it blocks in blocking mode", (t) => { t.match(stderr, /Zen has blocked an SQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); @@ -76,12 +78,13 @@ t.test("it blocks in blocking mode", (t) => { }); t.test("it does not block in non-blocking mode", (t) => { + const port = getFreePort(t); const server = spawn(`node`, ["--preserve-symlinks", "dist/main"], { env: { ...process.env, AIKIDO_DEBUG: "true", AIKIDO_BLOCK: "false", - PORT: "4001", + PORT: port, }, cwd: pathToApp, }); @@ -91,7 +94,7 @@ t.test("it does not block in non-blocking mode", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -105,10 +108,10 @@ t.test("it does not block in non-blocking mode", (t) => { }); // Wait for the server to start - timeout(5000) + waitOn(port) .then(() => { return Promise.all([ - fetch("http://127.0.0.1:4001/cats", { + fetch(`http://127.0.0.1:${port}/cats`, { method: "POST", headers: { "Content-Type": "application/json", @@ -125,7 +128,7 @@ t.test("it does not block in non-blocking mode", (t) => { t.notMatch(stderr, /Zen has blocked an SQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); diff --git a/end2end/tests/nestjs-sentry.test.js b/end2end/tests/nestjs-sentry.test.js index 1db24e1b2..705a21ece 100644 --- a/end2end/tests/nestjs-sentry.test.js +++ b/end2end/tests/nestjs-sentry.test.js @@ -1,7 +1,8 @@ const t = require("tap"); const { spawnSync, spawn, execSync } = require("child_process"); const { resolve } = require("path"); -const timeout = require("../timeout"); +const waitOn = require("../waitOn"); +const getFreePort = require("../getFreePort"); const pathToApp = resolve(__dirname, "../../sample-apps/nestjs-sentry"); @@ -16,12 +17,13 @@ t.before(() => { }); t.test("it blocks in blocking mode", (t) => { + const port = getFreePort(t); const server = spawn(`node`, ["--preserve-symlinks", "dist/main"], { env: { ...process.env, AIKIDO_DEBUG: "true", AIKIDO_BLOCK: "true", - PORT: "4000", + PORT: port, }, cwd: pathToApp, }); @@ -31,7 +33,7 @@ t.test("it blocks in blocking mode", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -45,14 +47,14 @@ t.test("it blocks in blocking mode", (t) => { }); // Wait for the server to start - timeout(5000) + waitOn(port) .then(() => { return Promise.all([ - fetch("http://127.0.0.1:4000/releases", { + fetch(`http://127.0.0.1:${port}/releases`, { method: "GET", signal: AbortSignal.timeout(5000), }), - fetch("http://127.0.0.1:4000/cats", { + fetch(`http://127.0.0.1:${port}/cats`, { method: "POST", headers: { "Content-Type": "application/json", @@ -72,7 +74,7 @@ t.test("it blocks in blocking mode", (t) => { t.match(stderr, /Zen has blocked an SQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); @@ -80,12 +82,13 @@ t.test("it blocks in blocking mode", (t) => { }); t.test("it does not block in non-blocking mode", (t) => { + const port = getFreePort(t); const server = spawn(`node`, ["--preserve-symlinks", "dist/main"], { env: { ...process.env, AIKIDO_DEBUG: "true", AIKIDO_BLOCK: "false", - PORT: "4001", + PORT: port, }, cwd: pathToApp, }); @@ -95,7 +98,7 @@ t.test("it does not block in non-blocking mode", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -109,14 +112,14 @@ t.test("it does not block in non-blocking mode", (t) => { }); // Wait for the server to start - timeout(5000) + waitOn(port) .then(() => { return Promise.all([ - fetch("http://127.0.0.1:4001/releases", { + fetch(`http://127.0.0.1:${port}/releases`, { method: "GET", signal: AbortSignal.timeout(5000), }), - fetch("http://127.0.0.1:4001/cats", { + fetch(`http://127.0.0.1:${port}/cats`, { method: "POST", headers: { "Content-Type": "application/json", @@ -136,7 +139,7 @@ t.test("it does not block in non-blocking mode", (t) => { t.notMatch(stderr, /Zen has blocked an SQL injection/); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); diff --git a/end2end/tests/nextjs-standalone.test.js b/end2end/tests/nextjs-standalone.test.js index edbcfdfba..4d102cd1f 100644 --- a/end2end/tests/nextjs-standalone.test.js +++ b/end2end/tests/nextjs-standalone.test.js @@ -1,7 +1,8 @@ const t = require("tap"); const { spawnSync, spawn, execSync } = require("child_process"); const { resolve, join } = require("path"); -const timeout = require("../timeout"); +const waitOn = require("../waitOn"); +const getFreePort = require("../getFreePort"); const { cpSync, writeFileSync } = require("fs"); const pathToApp = resolve(__dirname, "../../sample-apps/nextjs-standalone"); @@ -37,6 +38,7 @@ t.before(() => { }); t.test("it blocks in blocking mode", (t) => { + const port = getFreePort(t); const server = spawn( `node`, ["--preserve-symlinks", "-r", "@aikidosec/firewall", "server.js"], @@ -45,7 +47,7 @@ t.test("it blocks in blocking mode", (t) => { ...process.env, AIKIDO_DEBUG: "true", AIKIDO_BLOCK: "true", - PORT: 4000, + PORT: port, }, cwd: join(pathToApp, ".next/standalone"), } @@ -56,7 +58,7 @@ t.test("it blocks in blocking mode", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -70,14 +72,14 @@ t.test("it blocks in blocking mode", (t) => { }); // Wait for the server to start - timeout(5000) - .then((a) => { + waitOn(port) + .then(() => { return Promise.all([ - fetch("http://127.0.0.1:4000/files?path=.%27;env%27", { + fetch(`http://127.0.0.1:${port}/files?path=.%27;env%27`, { method: "GET", signal: AbortSignal.timeout(5000), }), - fetch("http://127.0.0.1:4000/files", { + fetch(`http://127.0.0.1:${port}/files`, { method: "POST", signal: AbortSignal.timeout(5000), headers: { @@ -85,11 +87,11 @@ t.test("it blocks in blocking mode", (t) => { }, body: JSON.stringify({ path: `.';env'` }), }), - fetch("http://127.0.0.1:4000/files", { + fetch(`http://127.0.0.1:${port}/files`, { method: "GET", signal: AbortSignal.timeout(5000), }), - fetch("http://127.0.0.1:4000/cats", { + fetch(`http://127.0.0.1:${port}/cats`, { method: "POST", body: JSON.stringify({ name: "Kitty'); DELETE FROM cats;-- H" }), headers: { @@ -111,7 +113,7 @@ t.test("it blocks in blocking mode", (t) => { } ) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); @@ -119,6 +121,7 @@ t.test("it blocks in blocking mode", (t) => { }); t.test("it does not block in dry mode", (t) => { + const port = getFreePort(t); const server = spawn( `node`, ["--preserve-symlinks", "-r", "@aikidosec/firewall", "server.js"], @@ -126,7 +129,7 @@ t.test("it does not block in dry mode", (t) => { env: { ...process.env, AIKIDO_DEBUG: "true", - PORT: 4001, + PORT: port, }, cwd: join(pathToApp, ".next/standalone"), } @@ -137,7 +140,7 @@ t.test("it does not block in dry mode", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -151,14 +154,14 @@ t.test("it does not block in dry mode", (t) => { }); // Wait for the server to start - timeout(5000) - .then((a) => { + waitOn(port) + .then(() => { return Promise.all([ - fetch("http://127.0.0.1:4001/files?path=.%27;env%27", { + fetch(`http://127.0.0.1:${port}/files?path=.%27;env%27`, { method: "GET", signal: AbortSignal.timeout(5000), }), - fetch("http://127.0.0.1:4001/files", { + fetch(`http://127.0.0.1:${port}/files`, { method: "POST", signal: AbortSignal.timeout(5000), headers: { @@ -166,11 +169,11 @@ t.test("it does not block in dry mode", (t) => { }, body: JSON.stringify({ path: `.';env'` }), }), - fetch("http://127.0.0.1:4001/files", { + fetch(`http://127.0.0.1:${port}/files`, { method: "GET", signal: AbortSignal.timeout(5000), }), - fetch("http://127.0.0.1:4001/cats", { + fetch(`http://127.0.0.1:${port}/cats`, { method: "POST", body: JSON.stringify({ name: "Kitty'); DELETE FROM cats;-- H" }), headers: { @@ -192,7 +195,7 @@ t.test("it does not block in dry mode", (t) => { } ) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); diff --git a/end2end/tests/strapi.test.js b/end2end/tests/strapi.test.js index fcac2c896..108f9c854 100644 --- a/end2end/tests/strapi.test.js +++ b/end2end/tests/strapi.test.js @@ -1,7 +1,8 @@ const t = require("tap"); const { spawnSync, spawn, execSync } = require("child_process"); const { resolve, join } = require("path"); -const timeout = require("../timeout"); +const waitOn = require("../waitOn"); +const getFreePort = require("../getFreePort"); const pathToApp = resolve(__dirname, "../../sample-apps/strapi"); @@ -21,12 +22,14 @@ t.before(() => { // We initially wrapped the Router class by hooking into the constructor // This results in weird behaviour where the router returns 405 for all requests t.test("it does not return 405 for register admin", (t) => { + const port = getFreePort(t); const server = spawn(`node_modules/.bin/strapi`, ["start"], { env: { ...process.env, AIKIDO_DEBUG: "true", AIKIDO_BLOCK: "true", NODE_OPTIONS: "-r @aikidosec/firewall", + PORT: port, }, cwd: pathToApp, }); @@ -36,7 +39,7 @@ t.test("it does not return 405 for register admin", (t) => { }); server.on("error", (err) => { - t.fail(err.message); + t.fail(err); }); let stdout = ""; @@ -50,10 +53,10 @@ t.test("it does not return 405 for register admin", (t) => { }); // Wait for the server to start - timeout(5000) + waitOn(port) .then(() => { return Promise.all([ - fetch("http://127.0.0.1:1337/admin/register-admin", { + fetch(`http://127.0.0.1:${port}/admin/register-admin`, { method: "POST", headers: { "Content-Type": "application/json", @@ -67,7 +70,7 @@ t.test("it does not return 405 for register admin", (t) => { t.equal(registerAdmin.status, 400); }) .catch((error) => { - t.fail(error.message); + t.fail(error); }) .finally(() => { server.kill(); diff --git a/end2end/timeout.js b/end2end/timeout.js deleted file mode 100644 index 14719f34a..000000000 --- a/end2end/timeout.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = async function timeout(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)); -}; diff --git a/end2end/waitOn.js b/end2end/waitOn.js new file mode 100644 index 000000000..b551cb27a --- /dev/null +++ b/end2end/waitOn.js @@ -0,0 +1,8 @@ +const waitOn = require("wait-on"); + +module.exports = function (port) { + return waitOn({ + resources: ["tcp:localhost:" + port], + log: true, + }); +}; diff --git a/sample-apps/express-graphql/app.js b/sample-apps/express-graphql/app.js index c128155c0..1b4ce1062 100644 --- a/sample-apps/express-graphql/app.js +++ b/sample-apps/express-graphql/app.js @@ -106,7 +106,7 @@ function getRootPage() { `; } -async function main() { +async function main(port) { dbConnection = await createConnection(); const app = express(); @@ -120,9 +120,20 @@ async function main() { app.all("/graphql", createHandler({ schema })); - app.listen(4000, () => { - console.log("Listening on port 4000"); + app.listen(port, () => { + console.log(`Listening on port ${port}`); }); } -main(); +function getPort() { + const port = parseInt(process.argv[2], 10) || 4000; + + if (isNaN(port)) { + console.error("Invalid port"); + process.exit(1); + } + + return port; +} + +main(getPort()); diff --git a/sample-apps/express-mysql2/app.js b/sample-apps/express-mysql2/app.js index 09cf8d0a0..43f083906 100644 --- a/sample-apps/express-mysql2/app.js +++ b/sample-apps/express-mysql2/app.js @@ -92,15 +92,8 @@ async function main(port) { }) ); - return new Promise((resolve, reject) => { - try { - app.listen(port, () => { - console.log(`Listening on port ${port}`); - resolve(); - }); - } catch (err) { - reject(err); - } + app.listen(port, () => { + console.log(`Listening on port ${port}`); }); } diff --git a/sample-apps/nestjs-fastify/package-lock.json b/sample-apps/nestjs-fastify/package-lock.json index d439f1bc4..816ad0137 100644 --- a/sample-apps/nestjs-fastify/package-lock.json +++ b/sample-apps/nestjs-fastify/package-lock.json @@ -30,73 +30,6 @@ "name": "@aikidosec/firewall", "version": "0.0.0", "license": "AGPL-3.0-or-later", - "devDependencies": { - "@fastify/cookie": "^10.0.0", - "@google-cloud/functions-framework": "^3.3.0", - "@google-cloud/pubsub": "^4.3.3", - "@graphql-tools/executor": "^1.3.2", - "@hapi/hapi": "^21.3.10", - "@hono/node-server": "^1.12.2", - "@koa/bodyparser": "^5.1.1", - "@koa/router": "^13.0.0", - "@sinonjs/fake-timers": "^11.2.2", - "@types/aws-lambda": "^8.10.131", - "@types/cookie-parser": "^1.4.6", - "@types/express": "^4.17.21", - "@types/follow-redirects": "^1.14.4", - "@types/ip": "^1.1.3", - "@types/koa": "^2.15.0", - "@types/koa__router": "^12.0.4", - "@types/mysql": "^2.15.25", - "@types/needle": "^3.3.0", - "@types/node": "^22.3.0", - "@types/pg": "^8.11.0", - "@types/qs": "^6.9.11", - "@types/shell-quote": "^1.7.5", - "@types/sinonjs__fake-timers": "^8.1.5", - "@types/supertest": "^6.0.2", - "@types/xml2js": "^0.4.14", - "@typescript-eslint/eslint-plugin": "^8.4.0", - "@typescript-eslint/parser": "^8.4.0", - "aws-sdk": "^2.1595.0", - "axios": "^1.7.3", - "better-sqlite3": "^11.2.0", - "bson-objectid": "^2.0.4", - "cookie-parser": "^1.4.6", - "eslint": "^8.56.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-import": "^2.29.1", - "eslint-plugin-prettier": "^5.1.3", - "express": "^5.0.0", - "express-async-handler": "^1.2.0", - "fast-xml-parser": "^4.4.0", - "fastify": "^5.0.0", - "follow-redirects": "^1.15.6", - "graphql": "^16.8.2", - "hono": "^4.4.2", - "koa": "^2.15.3", - "koa-router": "^12.0.1", - "mariadb": "^3.3.2", - "mongodb": "^6.3.0", - "mysql": "^2.18.1", - "mysql2": "^3.10.0", - "needle": "^3.3.1", - "node-fetch": "^2", - "percentile": "^1.6.0", - "pg": "^8.11.3", - "postgres": "^3.4.4", - "prettier": "^3.2.4", - "shell-quote": "^1.8.1", - "shelljs": "^0.8.5", - "sqlite3": "^5.1.7", - "supertest": "^6.3.4", - "tap": "^18.6.1", - "type-fest": "^4.24.0", - "typescript": "^5.3.3", - "undici": "^6.12.0", - "xml-js": "^1.6.11", - "xml2js": "^0.6.2" - }, "engines": { "node": ">=16" } diff --git a/sample-apps/nestjs-sentry/package-lock.json b/sample-apps/nestjs-sentry/package-lock.json index 566a9bc13..bb558b905 100644 --- a/sample-apps/nestjs-sentry/package-lock.json +++ b/sample-apps/nestjs-sentry/package-lock.json @@ -32,73 +32,6 @@ "name": "@aikidosec/firewall", "version": "0.0.0", "license": "AGPL-3.0-or-later", - "devDependencies": { - "@fastify/cookie": "^10.0.0", - "@google-cloud/functions-framework": "^3.3.0", - "@google-cloud/pubsub": "^4.3.3", - "@graphql-tools/executor": "^1.3.2", - "@hapi/hapi": "^21.3.10", - "@hono/node-server": "^1.12.2", - "@koa/bodyparser": "^5.1.1", - "@koa/router": "^13.0.0", - "@sinonjs/fake-timers": "^11.2.2", - "@types/aws-lambda": "^8.10.131", - "@types/cookie-parser": "^1.4.6", - "@types/express": "^4.17.21", - "@types/follow-redirects": "^1.14.4", - "@types/ip": "^1.1.3", - "@types/koa": "^2.15.0", - "@types/koa__router": "^12.0.4", - "@types/mysql": "^2.15.25", - "@types/needle": "^3.3.0", - "@types/node": "^22.3.0", - "@types/pg": "^8.11.0", - "@types/qs": "^6.9.11", - "@types/shell-quote": "^1.7.5", - "@types/sinonjs__fake-timers": "^8.1.5", - "@types/supertest": "^6.0.2", - "@types/xml2js": "^0.4.14", - "@typescript-eslint/eslint-plugin": "^8.4.0", - "@typescript-eslint/parser": "^8.4.0", - "aws-sdk": "^2.1595.0", - "axios": "^1.7.3", - "better-sqlite3": "^11.2.0", - "bson-objectid": "^2.0.4", - "cookie-parser": "^1.4.6", - "eslint": "^8.56.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-import": "^2.29.1", - "eslint-plugin-prettier": "^5.1.3", - "express": "^5.0.0", - "express-async-handler": "^1.2.0", - "fast-xml-parser": "^4.4.0", - "fastify": "^5.0.0", - "follow-redirects": "^1.15.6", - "graphql": "^16.8.2", - "hono": "^4.4.2", - "koa": "^2.15.3", - "koa-router": "^12.0.1", - "mariadb": "^3.3.2", - "mongodb": "^6.3.0", - "mysql": "^2.18.1", - "mysql2": "^3.10.0", - "needle": "^3.3.1", - "node-fetch": "^2", - "percentile": "^1.6.0", - "pg": "^8.11.3", - "postgres": "^3.4.4", - "prettier": "^3.2.4", - "shell-quote": "^1.8.1", - "shelljs": "^0.8.5", - "sqlite3": "^5.1.7", - "supertest": "^6.3.4", - "tap": "^18.6.1", - "type-fest": "^4.24.0", - "typescript": "^5.3.3", - "undici": "^6.12.0", - "xml-js": "^1.6.11", - "xml2js": "^0.6.2" - }, "engines": { "node": ">=16" } diff --git a/sample-apps/strapi/package-lock.json b/sample-apps/strapi/package-lock.json index 2dc1eabe0..4dc6cb920 100644 --- a/sample-apps/strapi/package-lock.json +++ b/sample-apps/strapi/package-lock.json @@ -25,6 +25,7 @@ } }, "../../build": { + "name": "@aikidosec/firewall", "version": "0.0.0", "license": "AGPL-3.0-or-later", "engines": {