Skip to content

Commit 1301533

Browse files
committed
datastore: validate port number (in a general way) in frontend and backend with the same validator
1 parent cd5cb8d commit 1301533

File tree

4 files changed

+52
-16
lines changed

4 files changed

+52
-16
lines changed

src/packages/database/postgres/project-queries.ts

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,21 @@
33
* License: MS-RSL – see LICENSE.md for details
44
*/
55

6+
import debug from "debug";
67
import { omit } from "lodash";
7-
import { PostgreSQL } from "./types";
8+
89
import { callback2 } from "@cocalc/util/async-utils";
10+
import {
11+
DUMMY_SECRET,
12+
PORT_MAX,
13+
PORT_MIN,
14+
validatePortNumber,
15+
} from "@cocalc/util/consts";
16+
import { DatastoreConfig } from "@cocalc/util/types";
917
import { query } from "./query";
10-
import debug from "debug";
18+
import { PostgreSQL } from "./types";
19+
1120
const L = debug("hub:project-queries");
12-
import { DUMMY_SECRET } from "@cocalc/util/consts";
13-
import { DatastoreConfig } from "@cocalc/util/types";
1421

1522
export async function project_has_network_access(
1623
db: PostgreSQL,
@@ -86,16 +93,23 @@ export async function project_datastore_set(
8693
// check data from user
8794
for (const [key, val] of Object.entries(config)) {
8895
if (val == null) continue;
96+
if (key === "port") {
97+
const port = validatePortNumber(val);
98+
if (port == null) {
99+
throw new Error(
100+
`Invalid value -- 'port' must be an integer between ${PORT_MIN} and ${PORT_MAX}`,
101+
);
102+
}
103+
config.port = port;
104+
continue;
105+
}
89106
if (
90107
typeof val !== "string" &&
91108
typeof val !== "boolean" &&
92109
typeof val !== "number"
93110
) {
94111
throw new Error(`Invalid value -- '${key}' is not a valid type`);
95112
}
96-
if (typeof val === "number" && (val < 0 || val > 2 ** 32)) {
97-
throw new Error(`Invalid value -- '${key}' is out of range`);
98-
}
99113
if (typeof val === "string" && val.length > 100000) {
100114
throw new Error(`Invalid value -- '${key}' is too long`);
101115
}

src/packages/frontend/project/settings/datastore.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import Password, {
5151
} from "@cocalc/frontend/components/password";
5252
import { labels } from "@cocalc/frontend/i18n";
5353
import { webapp_client } from "@cocalc/frontend/webapp-client";
54+
import { PORT_MAX, PORT_MIN, validatePortNumber } from "@cocalc/util/consts";
5455
import { DOC_CLOUD_STORAGE_URL } from "@cocalc/util/consts/project";
5556
import { DATASTORE_TITLE } from "@cocalc/util/db-schema/site-defaults";
5657
import { unreachable } from "@cocalc/util/misc";
@@ -83,8 +84,11 @@ const RULE_PORT = [
8384
{
8485
validator: (_: any, value: number | null) => {
8586
if (value == null) return Promise.resolve();
86-
if (!Number.isInteger(value) || value < 1) {
87-
return Promise.reject("Port must be an integer greater or equal to 1.");
87+
const port = validatePortNumber(value);
88+
if (port == null) {
89+
return Promise.reject(
90+
`Port must be an integer between ${PORT_MIN} and ${PORT_MAX}.`,
91+
);
8892
}
8993
return Promise.resolve();
9094
},
@@ -611,12 +615,8 @@ export const Datastore: React.FC<Props> = React.memo((props: Props) => {
611615
async function save_config(values: any): Promise<void> {
612616
const config = { ...values, readonly: form_readonly };
613617
if ("port" in config) {
614-
const portNum = Number(config.port);
615-
if (!Number.isFinite(portNum) || portNum < 1) {
616-
config.port = 22;
617-
} else {
618-
config.port = Math.floor(portNum);
619-
}
618+
const port = validatePortNumber(config.port);
619+
config.port = port ?? 22;
620620
}
621621
try {
622622
await set(config);

src/packages/util/consts/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
2+
* This file is part of CoCalc: Copyright © 2020 – 2025 Sagemath, Inc.
33
* License: MS-RSL – see LICENSE.md for details
44
*/
55

@@ -14,3 +14,5 @@ export { CONAT_LLM_HISTORY_KEY } from "./llm";
1414
export { DUMMY_SECRET } from "./project";
1515

1616
export { SERVER_SETTINGS_ENV_PREFIX } from "./server_settings";
17+
18+
export { PORT_MAX, PORT_MIN, validatePortNumber } from "./portnumber";
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* This file is part of CoCalc: Copyright © 2025 Sagemath, Inc.
3+
* License: MS-RSL – see LICENSE.md for details
4+
*/
5+
6+
export const PORT_MIN = 1;
7+
export const PORT_MAX = 65535;
8+
9+
export function validatePortNumber(port: unknown): number | undefined {
10+
if (port == null || port === "") return;
11+
const value =
12+
typeof port === "number"
13+
? port
14+
: typeof port === "string"
15+
? Number(port)
16+
: NaN;
17+
if (!Number.isInteger(value)) return;
18+
if (value < PORT_MIN || value > PORT_MAX) return;
19+
return value;
20+
}

0 commit comments

Comments
 (0)