Save data sanitization

- Run in `Editor.beforeMount`, as well as when the
options modal is closed
- Recursively validates all token colors and
replaces them with bright red if they're invalid
This commit is contained in:
nickofolas 2022-01-30 10:38:58 -06:00
parent dd77deca7b
commit 21e27dfab6
2 changed files with 48 additions and 9 deletions

@ -25,7 +25,7 @@ import { Settings } from "../../Settings/Settings";
import { iTutorialNextStep, ITutorial, iTutorialSteps } from "../../InteractiveTutorial"; import { iTutorialNextStep, ITutorial, iTutorialSteps } from "../../InteractiveTutorial";
import { debounce } from "lodash"; import { debounce } from "lodash";
import { saveObject } from "../../SaveObject"; import { saveObject } from "../../SaveObject";
import { loadThemes, makeTheme } from "./themes"; import { loadThemes, makeTheme, sanitizeTheme } from "./themes";
import { GetServer } from "../../Server/AllServers"; import { GetServer } from "../../Server/AllServers";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
@ -330,6 +330,7 @@ export function Root(props: IProps): React.ReactElement {
monaco.languages.typescript.javascriptDefaults.addExtraLib(source, "netscript.d.ts"); monaco.languages.typescript.javascriptDefaults.addExtraLib(source, "netscript.d.ts");
monaco.languages.typescript.typescriptDefaults.addExtraLib(source, "netscript.d.ts"); monaco.languages.typescript.typescriptDefaults.addExtraLib(source, "netscript.d.ts");
loadThemes(monaco); loadThemes(monaco);
sanitizeTheme(Settings.EditorTheme);
monaco.editor.defineTheme("customTheme", makeTheme(Settings.EditorTheme)); monaco.editor.defineTheme("customTheme", makeTheme(Settings.EditorTheme));
} }
@ -895,6 +896,7 @@ export function Root(props: IProps): React.ReactElement {
<OptionsModal <OptionsModal
open={optionsOpen} open={optionsOpen}
onClose={() => { onClose={() => {
sanitizeTheme(Settings.EditorTheme);
monacoRef.current?.editor.defineTheme("customTheme", makeTheme(Settings.EditorTheme)); monacoRef.current?.editor.defineTheme("customTheme", makeTheme(Settings.EditorTheme));
setOptionsOpen(false); setOptionsOpen(false);
}} }}
@ -906,6 +908,7 @@ export function Root(props: IProps): React.ReactElement {
vim: Settings.MonacoVim, vim: Settings.MonacoVim,
}} }}
save={(options: Options) => { save={(options: Options) => {
sanitizeTheme(Settings.EditorTheme);
monacoRef.current?.editor.defineTheme("customTheme", makeTheme(Settings.EditorTheme)); monacoRef.current?.editor.defineTheme("customTheme", makeTheme(Settings.EditorTheme));
setOptions(options); setOptions(options);
Settings.MonacoTheme = options.theme; Settings.MonacoTheme = options.theme;

@ -1,12 +1,15 @@
export interface IScriptEditorTheme { export interface IScriptEditorTheme {
[key: string]: any;
base: string; base: string;
inherit: boolean; inherit: boolean;
common: { common: {
[key: string]: string;
accent: string; accent: string;
bg: string; bg: string;
fg: string; fg: string;
}; };
syntax: { syntax: {
[key: string]: string;
tag: string; tag: string;
entity: string; entity: string;
string: string; string: string;
@ -18,13 +21,16 @@ export interface IScriptEditorTheme {
error: string; error: string;
}; };
ui: { ui: {
[key: string]: any;
line: string; line: string;
panel: { panel: {
[key: string]: string;
bg: string; bg: string;
selected: string; selected: string;
border: string; border: string;
}; };
selection: { selection: {
[key: string]: string;
bg: string; bg: string;
}; };
}; };
@ -34,20 +40,20 @@ export const defaultMonacoTheme: IScriptEditorTheme = {
base: "vs-dark", base: "vs-dark",
inherit: true, inherit: true,
common: { common: {
accent: "b5cea8", accent: "B5CEA8",
bg: "1E1E1E", bg: "1E1E1E",
fg: "D4D4D4", fg: "D4D4D4",
}, },
syntax: { syntax: {
tag: "569cd6", tag: "569CD6",
entity: "569cd6", entity: "569CD6",
string: "ce9178", string: "CE9178",
regexp: "646695", regexp: "646695",
markup: "569cd6", markup: "569CD6",
keyword: "569cd6", keyword: "569CD6",
comment: "6A9955", comment: "6A9955",
constant: "569cd6", constant: "569CD6",
error: "f44747" error: "F44747"
}, },
ui: { ui: {
line: "1E1E1E", line: "1E1E1E",
@ -62,6 +68,36 @@ export const defaultMonacoTheme: IScriptEditorTheme = {
} }
} }
// Regex used for token color validation
// https://github.com/microsoft/vscode/blob/973684056e67153952f495fce93bf50d0ec0b892/src/vs/editor/common/languages/supports/tokenization.ts#L153
const colorRegExp = /^#?([0-9A-Fa-f]{6})([0-9A-Fa-f]{2})?$/;
// Recursively sanitize the theme data to prevent errors
// Invalid data will be replaced with FF0000 (bright red)
export const sanitizeTheme = (theme: IScriptEditorTheme): void => {
for (const [k, v] of Object.entries(theme)) {
switch (k) {
case "base":
if (!["vs-dark", "vs"].includes(theme.base)) theme.base = "vs-dark";
continue;
case "inherit":
if (typeof theme.inherit !== "boolean") theme.inherit = true;
continue;
}
const repairBlock = (block: { [key: string]: any }): void => {
for (const [k, v] of Object.entries(block)) {
if (typeof v === "object") {
repairBlock(v as { [key: string]: string });
} else {
if (!v.match(colorRegExp)) block[k] = "FF0000";
}
}
}
repairBlock(v);
}
}
export function makeTheme(theme: IScriptEditorTheme): any { export function makeTheme(theme: IScriptEditorTheme): any {
const themeRules = [ const themeRules = [
{ {