mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-22 15:43:49 +01:00
MISC: Validate hostname and port of RFA (#1721)
This commit is contained in:
parent
f49d3b32a5
commit
810cfc8b21
@ -1,28 +1,46 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Button, Link, TextField, Tooltip, Typography } from "@mui/material";
|
import { Button, Link, TextField, Tooltip, Typography } from "@mui/material";
|
||||||
import { GameOptionsPage } from "./GameOptionsPage";
|
import { GameOptionsPage } from "./GameOptionsPage";
|
||||||
import { Settings } from "../../Settings/Settings";
|
import { isValidConnectionHostname, isValidConnectionPort, Settings } from "../../Settings/Settings";
|
||||||
import { ConnectionBauble } from "./ConnectionBauble";
|
import { ConnectionBauble } from "./ConnectionBauble";
|
||||||
import { isRemoteFileApiConnectionLive, newRemoteFileApiConnection } from "../../RemoteFileAPI/RemoteFileAPI";
|
import { isRemoteFileApiConnectionLive, newRemoteFileApiConnection } from "../../RemoteFileAPI/RemoteFileAPI";
|
||||||
|
|
||||||
export const RemoteAPIPage = (): React.ReactElement => {
|
export const RemoteAPIPage = (): React.ReactElement => {
|
||||||
|
const [remoteFileApiHostname, setRemoteFileApiHostname] = useState(Settings.RemoteFileApiAddress);
|
||||||
const [remoteFileApiPort, setRemoteFileApiPort] = useState(Settings.RemoteFileApiPort);
|
const [remoteFileApiPort, setRemoteFileApiPort] = useState(Settings.RemoteFileApiPort);
|
||||||
const [remoteFileApiAddress, setRemoteFileApiAddress] = useState(Settings.RemoteFileApiAddress);
|
|
||||||
|
|
||||||
function handleRemoteFileApiPortChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
function handleRemoteFileApiHostnameChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
setRemoteFileApiPort(Number(event.target.value));
|
let newValue = event.target.value.trim();
|
||||||
Settings.RemoteFileApiPort = Number(event.target.value);
|
// Empty string will be automatically changed to "localhost".
|
||||||
|
if (newValue === "") {
|
||||||
|
newValue = "localhost";
|
||||||
|
}
|
||||||
|
if (!isValidConnectionHostname(newValue)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setRemoteFileApiHostname(newValue);
|
||||||
|
Settings.RemoteFileApiAddress = newValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRemoteFileApiAddressChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
function handleRemoteFileApiPortChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
setRemoteFileApiAddress(String(event.target.value));
|
let newValue = event.target.value.trim();
|
||||||
Settings.RemoteFileApiAddress = String(event.target.value);
|
// Empty string will be automatically changed to "0".
|
||||||
|
if (newValue === "") {
|
||||||
|
newValue = "0";
|
||||||
|
}
|
||||||
|
const port = Number.parseInt(newValue);
|
||||||
|
// Disallow invalid ports but still allow the player to set port to 0. Setting it to 0 means that RFA is disabled.
|
||||||
|
if (port !== 0 && !isValidConnectionPort(port)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setRemoteFileApiPort(port);
|
||||||
|
Settings.RemoteFileApiPort = port;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GameOptionsPage title="Remote API">
|
<GameOptionsPage title="Remote API">
|
||||||
<Typography>
|
<Typography>
|
||||||
These settings control the Remote API for bitburner. This is typically used to write scripts using an external
|
These settings control the Remote API for Bitburner. This is typically used to write scripts using an external
|
||||||
text editor and then upload files to the home server.
|
text editor and then upload files to the home server.
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography>
|
<Typography>
|
||||||
@ -37,18 +55,17 @@ export const RemoteAPIPage = (): React.ReactElement => {
|
|||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
<Typography>
|
<Typography>
|
||||||
This address is used to connect to a Remote API, please ensure that it matches with your Remote API address.
|
This hostname is used to connect to a Remote API, please ensure that it matches with your Remote API
|
||||||
Default localhost.
|
hostname. Default: localhost.
|
||||||
</Typography>
|
</Typography>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<TextField
|
<TextField
|
||||||
key={"remoteAPIAddress"}
|
|
||||||
InputProps={{
|
InputProps={{
|
||||||
startAdornment: <Typography>Address: </Typography>,
|
startAdornment: <Typography>Hostname: </Typography>,
|
||||||
}}
|
}}
|
||||||
value={remoteFileApiAddress}
|
value={remoteFileApiHostname}
|
||||||
onChange={handleRemoteFileApiAddressChange}
|
onChange={handleRemoteFileApiHostnameChange}
|
||||||
placeholder="localhost"
|
placeholder="localhost"
|
||||||
size={"medium"}
|
size={"medium"}
|
||||||
/>
|
/>
|
||||||
@ -63,10 +80,9 @@ export const RemoteAPIPage = (): React.ReactElement => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<TextField
|
<TextField
|
||||||
key={"remoteAPIPort"}
|
|
||||||
InputProps={{
|
InputProps={{
|
||||||
startAdornment: (
|
startAdornment: (
|
||||||
<Typography color={remoteFileApiPort > 0 && remoteFileApiPort <= 65535 ? "success" : "error"}>
|
<Typography color={isValidConnectionPort(remoteFileApiPort) ? "success" : "error"}>
|
||||||
Port:
|
Port:
|
||||||
</Typography>
|
</Typography>
|
||||||
),
|
),
|
||||||
|
@ -4,6 +4,45 @@ import { defaultStyles } from "../Themes/Styles";
|
|||||||
import { CursorStyle, CursorBlinking, WordWrapOptions } from "../ScriptEditor/ui/Options";
|
import { CursorStyle, CursorBlinking, WordWrapOptions } from "../ScriptEditor/ui/Options";
|
||||||
import { defaultMonacoTheme } from "../ScriptEditor/ui/themes";
|
import { defaultMonacoTheme } from "../ScriptEditor/ui/themes";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function won't be able to catch **all** invalid hostnames, and it's still fine. In order to validate a hostname
|
||||||
|
* properly, we need to import a good validation library or write one by ourselves. I think that it's unnecessary.
|
||||||
|
*
|
||||||
|
* Some invalid hostnames that we don't catch:
|
||||||
|
* - Invalid/missing TLD: "abc".
|
||||||
|
* - Use space character: "a a.com"
|
||||||
|
* - Use non-http schemes in the hostname: "ftp://a.com"
|
||||||
|
* - etc.
|
||||||
|
*/
|
||||||
|
export function isValidConnectionHostname(hostname: string): boolean {
|
||||||
|
/**
|
||||||
|
* We expect a hostname, but the player may mistakenly put other unexpected things. We will try to catch common mistakes:
|
||||||
|
* - Specify a scheme: http or https.
|
||||||
|
* - Specify a port.
|
||||||
|
* - Specify a pathname or search params.
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
// Check scheme.
|
||||||
|
if (hostname.startsWith("http://") || hostname.startsWith("https://")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Parse to a URL with a default scheme.
|
||||||
|
const url = new URL(`http://${hostname}`);
|
||||||
|
// Check port, pathname, and search params.
|
||||||
|
if (url.port !== "" || url.pathname !== "/" || url.search !== "") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Invalid hostname: ${hostname}`, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isValidConnectionPort(port: number): boolean {
|
||||||
|
return Number.isFinite(port) && port > 0 && port <= 65535;
|
||||||
|
}
|
||||||
|
|
||||||
/** The current options the player has customized to their play style. */
|
/** The current options the player has customized to their play style. */
|
||||||
export const Settings = {
|
export const Settings = {
|
||||||
/** How many servers per page */
|
/** How many servers per page */
|
||||||
@ -125,5 +164,15 @@ export const Settings = {
|
|||||||
save.EditorTheme && Object.assign(Settings.EditorTheme, save.EditorTheme);
|
save.EditorTheme && Object.assign(Settings.EditorTheme, save.EditorTheme);
|
||||||
delete save.theme, save.styles, save.overview, save.EditorTheme;
|
delete save.theme, save.styles, save.overview, save.EditorTheme;
|
||||||
Object.assign(Settings, save);
|
Object.assign(Settings, save);
|
||||||
|
/**
|
||||||
|
* The hostname and port of RFA have not been validated properly, so the save data may contain invalid data. In that
|
||||||
|
* case, we set them to the default value.
|
||||||
|
*/
|
||||||
|
if (!isValidConnectionHostname(Settings.RemoteFileApiAddress)) {
|
||||||
|
Settings.RemoteFileApiAddress = "localhost";
|
||||||
|
}
|
||||||
|
if (!isValidConnectionPort(Settings.RemoteFileApiPort)) {
|
||||||
|
Settings.RemoteFileApiPort = 0;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user