Skip to content

Commit 1d6e61e

Browse files
committed
Improve undici perf
1 parent 4879c29 commit 1d6e61e

File tree

4 files changed

+60
-37
lines changed

4 files changed

+60
-37
lines changed

library/sinks/Fetch.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ export class Fetch implements Wrapper {
1717

1818
private inspectHostname(
1919
agent: Agent,
20-
hostname: URL,
20+
hostname: Hostname,
2121
port: number | undefined
2222
): InterceptorResult {
2323
// Let the agent know that we are connecting to this hostname
2424
// This is to build a list of all hostnames that the application is connecting to
2525
if (typeof port === "number" && port > 0) {
26-
agent.onConnectHostname(hostname.hostname, port);
26+
agent.onConnectHostname(hostname.asString(), port);
2727
}
2828
const context = getContext();
2929

@@ -32,7 +32,7 @@ export class Fetch implements Wrapper {
3232
}
3333

3434
return checkContextForSSRF({
35-
hostname: Hostname.fromURL(hostname),
35+
hostname: hostname,
3636
operation: "fetch",
3737
context: context,
3838
port: port,
@@ -45,7 +45,11 @@ export class Fetch implements Wrapper {
4545
if (typeof args[0] === "string" && args[0].length > 0) {
4646
const url = tryParseURL(args[0]);
4747
if (url) {
48-
const attack = this.inspectHostname(agent, url, getPortFromURL(url));
48+
const attack = this.inspectHostname(
49+
agent,
50+
Hostname.fromURL(url),
51+
getPortFromURL(url)
52+
);
4953
if (attack) {
5054
return attack;
5155
}
@@ -59,7 +63,11 @@ export class Fetch implements Wrapper {
5963
if (Array.isArray(args[0])) {
6064
const url = tryParseURL(args[0].toString());
6165
if (url) {
62-
const attack = this.inspectHostname(agent, url, getPortFromURL(url));
66+
const attack = this.inspectHostname(
67+
agent,
68+
Hostname.fromURL(url),
69+
getPortFromURL(url)
70+
);
6371
if (attack) {
6472
return attack;
6573
}
@@ -70,7 +78,7 @@ export class Fetch implements Wrapper {
7078
if (args[0] instanceof URL && args[0].hostname.length > 0) {
7179
const attack = this.inspectHostname(
7280
agent,
73-
args[0],
81+
Hostname.fromURL(args[0]),
7482
getPortFromURL(args[0])
7583
);
7684
if (attack) {
@@ -82,7 +90,11 @@ export class Fetch implements Wrapper {
8290
if (args[0] instanceof Request) {
8391
const url = tryParseURL(args[0].url);
8492
if (url) {
85-
const attack = this.inspectHostname(agent, url, getPortFromURL(url));
93+
const attack = this.inspectHostname(
94+
agent,
95+
Hostname.fromURL(url),
96+
getPortFromURL(url)
97+
);
8698
if (attack) {
8799
return attack;
88100
}

library/sinks/Undici.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,28 +27,24 @@ const methods = [
2727
export class Undici implements Wrapper {
2828
private inspectHostname(
2929
agent: Agent,
30-
hostname: string,
30+
hostname: Hostname,
3131
port: number | undefined,
3232
method: string
3333
): InterceptorResult {
3434
// Let the agent know that we are connecting to this hostname
3535
// This is to build a list of all hostnames that the application is connecting to
3636
if (typeof port === "number" && port > 0) {
37-
agent.onConnectHostname(hostname, port);
37+
agent.onConnectHostname(hostname.asString(), port);
3838
}
39+
3940
const context = getContext();
4041

4142
if (!context) {
4243
return undefined;
4344
}
4445

45-
const hostnameURL = tryParseURL(`http://${hostname}`);
46-
if (!hostnameURL) {
47-
return undefined;
48-
}
49-
5046
return checkContextForSSRF({
51-
hostname: Hostname.fromURL(hostnameURL),
47+
hostname: hostname,
5248
operation: `undici.${method}`,
5349
context,
5450
port,
Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,79 @@
11
import * as t from "tap";
2+
import { Hostname } from "../../vulnerabilities/ssrf/Hostname";
23
import { getHostnameAndPortFromArgs as get } from "./getHostnameAndPortFromArgs";
34
import { parse as parseUrl } from "url";
45

56
t.test("it works with url string", async (t) => {
67
t.same(get(["http://localhost:4000"]), {
7-
hostname: "localhost",
8+
hostname: Hostname.fromString("localhost"),
89
port: 4000,
910
});
1011
t.same(get(["http://localhost?test=1"]), {
11-
hostname: "localhost",
12+
hostname: Hostname.fromString("localhost"),
1213
port: 80,
1314
});
1415
t.same(get(["https://localhost"]), {
15-
hostname: "localhost",
16+
hostname: Hostname.fromString("localhost"),
1617
port: 443,
1718
});
1819
});
1920

2021
t.test("it works with url object", async (t) => {
2122
t.same(get([new URL("http://localhost:4000")]), {
22-
hostname: "localhost",
23+
hostname: Hostname.fromString("localhost"),
2324
port: 4000,
2425
});
2526
t.same(get([new URL("http://localhost?test=1")]), {
26-
hostname: "localhost",
27+
hostname: Hostname.fromString("localhost"),
2728
port: 80,
2829
});
2930
t.same(get([new URL("https://localhost")]), {
30-
hostname: "localhost",
31+
hostname: Hostname.fromString("localhost"),
3132
port: 443,
3233
});
3334
});
3435

3536
t.test("it works with an array of strings", async (t) => {
3637
t.same(get([["http://localhost:4000"]]), {
37-
hostname: "localhost",
38+
hostname: Hostname.fromString("localhost"),
3839
port: 4000,
3940
});
4041
t.same(get([["http://localhost?test=1"]]), {
41-
hostname: "localhost",
42+
hostname: Hostname.fromString("localhost"),
4243
port: 80,
4344
});
4445
t.same(get([["https://localhost"]]), {
45-
hostname: "localhost",
46+
hostname: Hostname.fromString("localhost"),
4647
port: 443,
4748
});
4849
});
4950

5051
t.test("it works with an legacy url object", async (t) => {
5152
t.same(get([parseUrl("http://localhost:4000")]), {
52-
hostname: "localhost",
53+
hostname: Hostname.fromString("localhost"),
5354
port: 4000,
5455
});
5556
t.same(get([parseUrl("http://localhost?test=1")]), {
56-
hostname: "localhost",
57+
hostname: Hostname.fromString("localhost"),
5758
port: 80,
5859
});
5960
t.same(get([parseUrl("https://localhost")]), {
60-
hostname: "localhost",
61+
hostname: Hostname.fromString("localhost"),
6162
port: 443,
6263
});
6364
});
6465

6566
t.test("it works with an options object containing origin", async (t) => {
6667
t.same(get([{ origin: "http://localhost:4000" }]), {
67-
hostname: "localhost",
68+
hostname: Hostname.fromString("localhost"),
6869
port: 4000,
6970
});
7071
t.same(get([{ origin: "http://localhost?test=1" }]), {
71-
hostname: "localhost",
72+
hostname: Hostname.fromString("localhost"),
7273
port: 80,
7374
});
7475
t.same(get([{ origin: "https://localhost" }]), {
75-
hostname: "localhost",
76+
hostname: Hostname.fromString("localhost"),
7677
port: 443,
7778
});
7879
});
@@ -81,15 +82,15 @@ t.test(
8182
"it works with an options object containing protocol, hostname and port",
8283
async (t) => {
8384
t.same(get([{ protocol: "http:", hostname: "localhost", port: 4000 }]), {
84-
hostname: "localhost",
85+
hostname: Hostname.fromString("localhost"),
8586
port: 4000,
8687
});
8788
t.same(get([{ hostname: "localhost", port: 4000 }]), {
88-
hostname: "localhost",
89+
hostname: Hostname.fromString("localhost"),
8990
port: 4000,
9091
});
9192
t.same(get([{ protocol: "https:", hostname: "localhost" }]), {
92-
hostname: "localhost",
93+
hostname: Hostname.fromString("localhost"),
9394
port: 443,
9495
});
9596
}
@@ -104,3 +105,11 @@ t.test("without hostname", async (t) => {
104105
t.same(get([{}]), undefined);
105106
t.same(get([{ protocol: "https:", port: 4000 }]), undefined);
106107
});
108+
109+
t.test("invalid hostname", async (t) => {
110+
t.same(get([{ protocol: "https:", hostname: " " }]), undefined);
111+
});
112+
113+
t.test("empty args", async (t) => {
114+
t.same(get([]), undefined);
115+
});

library/sinks/undici/getHostnameAndPortFromArgs.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { getPortFromURL } from "../../helpers/getPortFromURL";
22
import { tryParseURL } from "../../helpers/tryParseURL";
3+
import { Hostname } from "../../vulnerabilities/ssrf/Hostname";
34
import { isOptionsObject } from "../http-request/isOptionsObject";
45

56
type HostnameAndPort = {
6-
hostname: string;
7+
hostname: Hostname;
78
port: number | undefined;
89
};
910

@@ -36,7 +37,7 @@ export function getHostnameAndPortFromArgs(
3637
// If url is not undefined, extract the hostname and port
3738
if (url && url.hostname.length > 0) {
3839
return {
39-
hostname: url.hostname,
40+
hostname: Hostname.fromURL(url),
4041
port: getPortFromURL(url),
4142
};
4243
}
@@ -60,7 +61,7 @@ function parseOptionsObject(obj: any): HostnameAndPort | undefined {
6061
const url = tryParseURL(obj.origin);
6162
if (url) {
6263
return {
63-
hostname: url.hostname,
64+
hostname: Hostname.fromURL(url),
6465
port: getPortFromURL(url),
6566
};
6667
}
@@ -87,8 +88,13 @@ function parseOptionsObject(obj: any): HostnameAndPort | undefined {
8788
return undefined;
8889
}
8990

91+
const hostname = Hostname.fromString(obj.hostname);
92+
if (!hostname) {
93+
return undefined;
94+
}
95+
9096
return {
91-
hostname: obj.hostname,
97+
hostname,
9298
port,
9399
};
94100
}

0 commit comments

Comments
 (0)