Skip to content

Commit

Permalink
Merge branch 'main' of github.com:AikidoSec/node-RASP into speed-up-ci
Browse files Browse the repository at this point in the history
* 'main' of github.com:AikidoSec/node-RASP:
  Use separate table for postgres sink tests
  Upgrade Zen internals to v0.1.35
  Use cURL to send requests to server
  Use req.destroy()
  Use t.fail(error)
  Pass body to `req.end(...)` instead of `req.write(...)`
  Assert headers for fetch helper
  • Loading branch information
hansott committed Jan 15, 2025
2 parents 9ee1606 + 3b8081a commit 74aec8c
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 33 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
INTERNALS_VERSION = v0.1.34
INTERNALS_VERSION = v0.1.35
INTERNALS_URL = https://github.com/AikidoSec/zen-internals/releases/download/$(INTERNALS_VERSION)
TARBALL = zen_internals.tgz
CHECKSUM_FILE = zen_internals.tgz.sha256sum
Expand Down
34 changes: 32 additions & 2 deletions library/helpers/fetch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as t from "tap";
import { createServer, Server } from "http";
import { fetch } from "./fetch";
import { gzip } from "zlib";
import { getMajorNodeVersion } from "./getNodeVersion";

let server: Server;

Expand All @@ -13,6 +14,7 @@ t.beforeEach(async () => {
req.on("end", () => {
const body = JSON.stringify({
method: req.method,
headers: req.headers,
body: Buffer.concat(chunks).toString(),
});
if (req.headers["accept-encoding"] === "gzip") {
Expand Down Expand Up @@ -50,7 +52,14 @@ t.test("should make a GET request", async (t) => {
const response = await fetch({ url });

t.equal(response.statusCode, 200);
t.same(JSON.parse(response.body), { method: "GET", body: "" });
t.same(JSON.parse(response.body), {
method: "GET",
body: "",
headers: {
host: `localhost:${(server.address() as any).port}`,
connection: getMajorNodeVersion() >= 19 ? "keep-alive" : "close",
},
});
});

t.test("should make a GET request with gzip", async (t) => {
Expand All @@ -63,7 +72,15 @@ t.test("should make a GET request with gzip", async (t) => {
});

t.equal(response.statusCode, 200);
t.same(JSON.parse(response.body), { method: "GET", body: "" });
t.same(JSON.parse(response.body), {
method: "GET",
body: "",
headers: {
host: `localhost:${(server.address() as any).port}`,
connection: getMajorNodeVersion() >= 19 ? "keep-alive" : "close",
"accept-encoding": "gzip",
},
});
});

t.test("should make a POST request with body", async (t) => {
Expand All @@ -79,6 +96,12 @@ t.test("should make a POST request with body", async (t) => {
t.same(JSON.parse(response.body), {
method: "POST",
body: '{"key":"value"}',
headers: {
host: `localhost:${(server.address() as any).port}`,
connection: getMajorNodeVersion() >= 19 ? "keep-alive" : "close",
"content-type": "application/json",
"content-length": "15",
},
});
});

Expand All @@ -98,5 +121,12 @@ t.test("should make a POST request with body and gzip", async (t) => {
t.same(JSON.parse(response.body), {
method: "POST",
body: '{"key":"value"}',
headers: {
host: `localhost:${(server.address() as any).port}`,
connection: getMajorNodeVersion() >= 19 ? "keep-alive" : "close",
"content-type": "application/json",
"content-length": "15",
"accept-encoding": "gzip",
},
});
});
3 changes: 1 addition & 2 deletions library/helpers/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@ async function request({
reject(error);
});

req.write(body);
req.end();
req.end(body);
});
}

Expand Down
18 changes: 9 additions & 9 deletions library/sinks/Postgresjs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ t.test("it inspects query method calls and blocks if needed", async (t) => {

try {
await sql`
CREATE TABLE IF NOT EXISTS cats (
CREATE TABLE IF NOT EXISTS cats_2 (
petname varchar(255)
);
`;
await sql`TRUNCATE cats`;
await sql`TRUNCATE cats_2`;

const cats = [
{
Expand All @@ -42,23 +42,23 @@ t.test("it inspects query method calls and blocks if needed", async (t) => {
},
];

await sql`insert into cats ${sql(cats, "petname")}`;
await sql`insert into cats_2 ${sql(cats, "petname")}`;

const transactionResult = await sql.begin((sql) => [
sql`SELECT * FROM cats`,
sql`SELECT * FROM cats_2`,
]);
t.same(transactionResult[0], cats);

t.same(await sql`select * from ${sql("cats")}`, cats);
t.same(await sql.unsafe("SELECT * FROM cats"), cats);
t.same(await sql`select * from ${sql("cats_2")}`, cats);
t.same(await sql.unsafe("SELECT * FROM cats_2"), cats);

await runWithContext(context, async () => {
t.same(await sql`select * from ${sql("cats")}`, cats);
t.same(await sql.unsafe("SELECT * FROM cats"), cats);
t.same(await sql`select * from ${sql("cats_2")}`, cats);
t.same(await sql.unsafe("SELECT * FROM cats_2"), cats);

const error = await t.rejects(async () => {
await sql.unsafe(
`SELECT * FROM cats WHERE petname = test; -- should be blocked`
`SELECT * FROM cats_2 WHERE petname = test; -- should be blocked`
);
});

Expand Down
79 changes: 63 additions & 16 deletions library/sources/HTTPServer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ import { getContext } from "../agent/Context";
import { fetch } from "../helpers/fetch";
import { wrap } from "../helpers/wrap";
import { HTTPServer } from "./HTTPServer";
import { join } from "path";
import { createTestAgent } from "../helpers/createTestAgent";
import type { Blocklist } from "../agent/api/fetchBlockedLists";
import * as fetchBlockedLists from "../agent/api/fetchBlockedLists";
import { mkdtemp, writeFile, unlink } from "fs/promises";
import { exec } from "child_process";
import { promisify } from "util";
const execAsync = promisify(exec);

// Before require("http")
const api = new ReportingAPIForTesting({
Expand Down Expand Up @@ -359,33 +364,72 @@ function generateJsonPayload(sizeInMb: number) {
return JSON.stringify("a".repeat(sizeInBytes));
}

t.test("it sends 413 when body is larger than 20 Mb", async (t) => {
// Send request using curl
// Returns the response message + status code in stdout
// We need this because http.request throws EPIPE write error when the server closes the connection abruptly
// We cannot read the status code or the response message when that happens
async function sendUsingCurl({
url,
method,
headers,
body,
timeoutInMS,
}: {
url: URL;
method: string;
headers: Record<string, string>;
body: string;
timeoutInMS: number;
}) {
const headersString = Object.entries(headers)
.map(([key, value]) => `-H "${key}: ${value}"`)
.join(" ");

const tmpDir = await mkdtemp("/tmp/aikido-");
const tmpFile = join(tmpDir, "/body.json");
await writeFile(tmpFile, body, { encoding: "utf-8" });

const { stdout } = await execAsync(
`curl -X ${method} ${headersString} -d @${tmpFile} ${url} --max-time ${timeoutInMS / 1000} --write-out "%{http_code}"`
);
await unlink(tmpFile);

return stdout;
}

t.only("it sends 413 when body is larger than 20 Mb", async (t) => {
// Enables body parsing
process.env.NEXT_DEPLOYMENT_ID = "";

const server = http.createServer((req, res) => {
t.fail();
res.end();
});

await new Promise<void>((resolve) => {
server.listen(3320, () => {
fetch({
sendUsingCurl({
url: new URL("http://localhost:3320"),
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: generateJsonPayload(21),
timeoutInMS: 2000,
}).then(({ body, statusCode }) => {
t.equal(
body,
"This request was aborted by Aikido firewall because the body size exceeded the maximum allowed size. Use AIKIDO_MAX_BODY_SIZE_MB to increase the limit."
);
t.equal(statusCode, 413);
server.close();
resolve();
});
})
.then((stdout) => {
t.equal(
stdout,
"This request was aborted by Aikido firewall because the body size exceeded the maximum allowed size. Use AIKIDO_MAX_BODY_SIZE_MB to increase the limit.413"
);
})
.catch((error) => {
t.fail(error);
})
.finally(() => {
server.close();
resolve();
});
});
});
});
Expand Down Expand Up @@ -431,7 +475,7 @@ t.test("it uses limit from AIKIDO_MAX_BODY_SIZE_MB", async (t) => {
server.listen(3322, () => {
process.env.AIKIDO_MAX_BODY_SIZE_MB = "1";
Promise.all([
fetch({
sendUsingCurl({
url: new URL("http://localhost:3322"),
method: "POST",
headers: {
Expand All @@ -440,7 +484,7 @@ t.test("it uses limit from AIKIDO_MAX_BODY_SIZE_MB", async (t) => {
body: generateJsonPayload(1),
timeoutInMS: 2000,
}),
fetch({
sendUsingCurl({
url: new URL("http://localhost:3322"),
method: "POST",
headers: {
Expand All @@ -451,11 +495,14 @@ t.test("it uses limit from AIKIDO_MAX_BODY_SIZE_MB", async (t) => {
}),
])
.then(([response1, response2]) => {
t.equal(response1.statusCode, 200);
t.equal(response2.statusCode, 413);
t.equal(response1, "200");
t.same(
response2,
"This request was aborted by Aikido firewall because the body size exceeded the maximum allowed size. Use AIKIDO_MAX_BODY_SIZE_MB to increase the limit.413"
);
})
.catch((error) => {
t.fail(`Unexpected error: ${error.message} ${error.stack}`);
t.fail(error);
})
.finally(() => {
server.close();
Expand Down
12 changes: 9 additions & 3 deletions library/sources/http-server/readBodyStream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type BodyReadResult =

export async function readBodyStream(
req: IncomingMessage,
res: ServerResponse<IncomingMessage>,
res: ServerResponse,
agent: Agent
): Promise<BodyReadResult> {
let body = "";
Expand All @@ -28,7 +28,10 @@ export async function readBodyStream(
if (bodySize + chunk.length > maxBodySize) {
res.statusCode = 413;
res.end(
"This request was aborted by Aikido firewall because the body size exceeded the maximum allowed size. Use AIKIDO_MAX_BODY_SIZE_MB to increase the limit."
"This request was aborted by Aikido firewall because the body size exceeded the maximum allowed size. Use AIKIDO_MAX_BODY_SIZE_MB to increase the limit.",
() => {
req.destroy();
}
);
agent.getInspectionStatistics().onAbortedRequest();

Expand All @@ -44,7 +47,10 @@ export async function readBodyStream(
} catch {
res.statusCode = 500;
res.end(
"Aikido firewall encountered an error while reading the request body."
"Aikido firewall encountered an error while reading the request body.",
() => {
req.destroy();
}
);

return {
Expand Down

0 comments on commit 74aec8c

Please sign in to comment.