Skip to content

Commit

Permalink
fix(ext/net): validate port in Deno.{connect,serve,listen} (#24399)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
4 people authored Aug 20, 2024
1 parent 37279e0 commit a3a54bc
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 6 deletions.
31 changes: 28 additions & 3 deletions ext/net/01_net.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,12 @@ const UDP_DGRAM_MAXSIZE = 65507;
const {
Error,
Number,
NumberIsNaN,
NumberIsInteger,
ObjectPrototypeIsPrototypeOf,
ObjectDefineProperty,
PromiseResolve,
RangeError,
SafeSet,
SetPrototypeAdd,
SetPrototypeDelete,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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";
Expand Down Expand Up @@ -626,4 +650,5 @@ export {
shutdown,
TcpConn,
UnixConn,
validatePort,
};
6 changes: 3 additions & 3 deletions ext/net/02_tls.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(
Expand All @@ -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,
);
Expand Down
82 changes: 82 additions & 0 deletions tests/unit/serve_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit a3a54bc

Please # to comment.