1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-21 15:04:11 -05:00

fix(ext/net): validate port in Deno.{connect,serve,listen} (#24399)

Co-authored-by: Will Leach <4619280+melbourne2991@users.noreply.github.com>
Co-authored-by: Luca Casonato <hello@lcas.dev>
Co-authored-by: David Sherret <dsherret@gmail.com>
This commit is contained in:
seb 2024-08-20 14:25:41 -07:00 committed by GitHub
parent 37279e0b0a
commit a3a54bc747
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 113 additions and 6 deletions

View file

@ -33,9 +33,12 @@ const UDP_DGRAM_MAXSIZE = 65507;
const {
Error,
Number,
NumberIsNaN,
NumberIsInteger,
ObjectPrototypeIsPrototypeOf,
ObjectDefineProperty,
PromiseResolve,
RangeError,
SafeSet,
SetPrototypeAdd,
SetPrototypeDelete,
@ -531,10 +534,11 @@ const listenOptionApiName = Symbol("listenOptionApiName");
function listen(args) {
switch (args.transport ?? "tcp") {
case "tcp": {
const port = validatePort(args.port);
const { 0: rid, 1: addr } = op_net_listen_tcp(
{
hostname: args.hostname ?? "0.0.0.0",
port: Number(args.port),
port,
},
args.reusePort,
args.loadBalanced ?? false,
@ -558,14 +562,33 @@ function listen(args) {
}
}
function validatePort(maybePort) {
if (typeof maybePort !== "number" && typeof maybePort !== "string") {
throw new TypeError(`Invalid port (expected number): ${maybePort}`);
}
if (maybePort === "") throw new TypeError("Invalid port: ''");
const port = Number(maybePort);
if (!NumberIsInteger(port)) {
if (NumberIsNaN(port) && !NumberIsNaN(maybePort)) {
throw new TypeError(`Invalid port: '${maybePort}'`);
} else {
throw new TypeError(`Invalid port: ${maybePort}`);
}
} else if (port < 0 || port > 65535) {
throw new RangeError(`Invalid port (out of range): ${maybePort}`);
}
return port;
}
function createListenDatagram(udpOpFn, unixOpFn) {
return function listenDatagram(args) {
switch (args.transport) {
case "udp": {
const port = validatePort(args.port);
const { 0: rid, 1: addr } = udpOpFn(
{
hostname: args.hostname ?? "127.0.0.1",
port: args.port,
port,
},
args.reuseAddress ?? false,
args.loopback ?? false,
@ -590,10 +613,11 @@ function createListenDatagram(udpOpFn, unixOpFn) {
async function connect(args) {
switch (args.transport ?? "tcp") {
case "tcp": {
const port = validatePort(args.port);
const { 0: rid, 1: localAddr, 2: remoteAddr } = await op_net_connect_tcp(
{
hostname: args.hostname ?? "127.0.0.1",
port: args.port,
port,
},
);
localAddr.transport = "tcp";
@ -626,4 +650,5 @@ export {
shutdown,
TcpConn,
UnixConn,
validatePort,
};

View file

@ -17,13 +17,12 @@ import {
op_tls_start,
} from "ext:core/ops";
const {
Number,
ObjectDefineProperty,
TypeError,
SymbolFor,
} = primordials;
import { Conn, Listener } from "ext:deno_net/01_net.js";
import { Conn, Listener, validatePort } from "ext:deno_net/01_net.js";
class TlsConn extends Conn {
#rid = 0;
@ -259,6 +258,7 @@ function listenTls({
if (transport !== "tcp") {
throw new TypeError(`Unsupported transport: '${transport}'`);
}
port = validatePort(port);
if (!hasTlsKeyPairOptions(arguments[0])) {
throw new TypeError(
@ -267,7 +267,7 @@ function listenTls({
}
const keyPair = loadTlsKeyPair("Deno.listenTls", arguments[0]);
const { 0: rid, 1: localAddr } = op_net_listen_tls(
{ hostname, port: Number(port) },
{ hostname, port },
{ alpnProtocols, reusePort },
keyPair,
);

View file

@ -861,6 +861,88 @@ Deno.test(
},
);
Deno.test({ permissions: { net: true } }, async function validPortString() {
const server = Deno.serve({
handler: (_request) => new Response(),
port: "4501" as unknown as number,
});
assertEquals(server.addr.transport, "tcp");
assertEquals(server.addr.port, 4501);
await server.shutdown();
});
Deno.test({ permissions: { net: true } }, function invalidPortFloat() {
assertThrows(
() =>
Deno.serve({
handler: (_request) => new Response(),
port: 45.1,
}),
TypeError,
`Invalid port: 45.1`,
);
});
Deno.test({ permissions: { net: true } }, function invalidPortNaN() {
assertThrows(
() =>
Deno.serve({
handler: (_request) => new Response(),
port: NaN,
}),
TypeError,
`Invalid port: NaN`,
);
});
Deno.test({ permissions: { net: true } }, function invalidPortString() {
assertThrows(
() =>
Deno.serve({
handler: (_request) => new Response(),
port: "some-non-number-string" as unknown as number,
}),
TypeError,
`Invalid port: 'some-non-number-string'`,
);
});
Deno.test({ permissions: { net: true } }, function invalidPortTooSmall() {
assertThrows(
() =>
Deno.serve({
handler: (_request) => new Response(),
port: -111,
}),
RangeError,
`Invalid port (out of range): -111`,
);
});
Deno.test({ permissions: { net: true } }, function invalidPortTooLarge() {
assertThrows(
() =>
Deno.serve({
handler: (_request) => new Response(),
port: 100000,
}),
RangeError,
`Invalid port (out of range): 100000`,
);
});
Deno.test({ permissions: { net: true } }, function invalidPortType() {
assertThrows(
() =>
Deno.serve({
handler: (_request) => new Response(),
port: true as unknown as number,
}),
TypeError,
`Invalid port (expected number): true`,
);
});
function createUrlTest(
name: string,
methodAndPath: string,