From 21e27dfab65eb7faf5a693861ae0f5f1ef5a8c20 Mon Sep 17 00:00:00 2001 From: nickofolas Date: Sun, 30 Jan 2022 10:38:58 -0600 Subject: [PATCH] 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 --- src/ScriptEditor/ui/ScriptEditorRoot.tsx | 5 ++- src/ScriptEditor/ui/themes.ts | 52 ++++++++++++++++++++---- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/ScriptEditor/ui/ScriptEditorRoot.tsx b/src/ScriptEditor/ui/ScriptEditorRoot.tsx index f87e36c97..c661e3c52 100644 --- a/src/ScriptEditor/ui/ScriptEditorRoot.tsx +++ b/src/ScriptEditor/ui/ScriptEditorRoot.tsx @@ -25,7 +25,7 @@ import { Settings } from "../../Settings/Settings"; import { iTutorialNextStep, ITutorial, iTutorialSteps } from "../../InteractiveTutorial"; import { debounce } from "lodash"; import { saveObject } from "../../SaveObject"; -import { loadThemes, makeTheme } from "./themes"; +import { loadThemes, makeTheme, sanitizeTheme } from "./themes"; import { GetServer } from "../../Server/AllServers"; 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.typescriptDefaults.addExtraLib(source, "netscript.d.ts"); loadThemes(monaco); + sanitizeTheme(Settings.EditorTheme); monaco.editor.defineTheme("customTheme", makeTheme(Settings.EditorTheme)); } @@ -895,6 +896,7 @@ export function Root(props: IProps): React.ReactElement { { + sanitizeTheme(Settings.EditorTheme); monacoRef.current?.editor.defineTheme("customTheme", makeTheme(Settings.EditorTheme)); setOptionsOpen(false); }} @@ -906,6 +908,7 @@ export function Root(props: IProps): React.ReactElement { vim: Settings.MonacoVim, }} save={(options: Options) => { + sanitizeTheme(Settings.EditorTheme); monacoRef.current?.editor.defineTheme("customTheme", makeTheme(Settings.EditorTheme)); setOptions(options); Settings.MonacoTheme = options.theme; diff --git a/src/ScriptEditor/ui/themes.ts b/src/ScriptEditor/ui/themes.ts index cf16cece4..6736bbf97 100644 --- a/src/ScriptEditor/ui/themes.ts +++ b/src/ScriptEditor/ui/themes.ts @@ -1,12 +1,15 @@ export interface IScriptEditorTheme { + [key: string]: any; base: string; inherit: boolean; common: { + [key: string]: string; accent: string; bg: string; fg: string; }; syntax: { + [key: string]: string; tag: string; entity: string; string: string; @@ -18,13 +21,16 @@ export interface IScriptEditorTheme { error: string; }; ui: { + [key: string]: any; line: string; panel: { + [key: string]: string; bg: string; selected: string; border: string; }; selection: { + [key: string]: string; bg: string; }; }; @@ -34,20 +40,20 @@ export const defaultMonacoTheme: IScriptEditorTheme = { base: "vs-dark", inherit: true, common: { - accent: "b5cea8", + accent: "B5CEA8", bg: "1E1E1E", fg: "D4D4D4", }, syntax: { - tag: "569cd6", - entity: "569cd6", - string: "ce9178", + tag: "569CD6", + entity: "569CD6", + string: "CE9178", regexp: "646695", - markup: "569cd6", - keyword: "569cd6", + markup: "569CD6", + keyword: "569CD6", comment: "6A9955", - constant: "569cd6", - error: "f44747" + constant: "569CD6", + error: "F44747" }, ui: { 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 { const themeRules = [ {