ui: use DOMPurify to sanitize inputs to bootbox

This commit is contained in:
Steven Hugg 2022-09-13 22:13:46 -05:00
parent 48eb75882b
commit 088878e8c4
5 changed files with 4303 additions and 3790 deletions

8047
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -11,12 +11,14 @@
"license": "GPL-3.0",
"dependencies": {
"@types/chroma-js": "^2.1.3",
"@types/dompurify": "^2.3.4",
"@types/emscripten": "^1.39.5",
"@wasmer/wasi": "^0.12.0",
"@wasmer/wasmfs": "^0.12.0",
"binaryen": "^101.0.0",
"chroma-js": "^2.1.2",
"clipboard": "^2.0.6",
"dompurify": "^2.4.0",
"error-stack-parser": "^2.0.6",
"fast-png": "^5.0.4",
"file-saver": "^2.0.5",

View File

@ -5,6 +5,8 @@ import { Platform } from "../common/baseplatform";
import { stringToByteArray, getWithBinary, loadScript, getRootBasePlatform } from "../common/util";
import { StateRecorderImpl } from "../common/recorder";
import { importPlatform } from "../platform/_index";
import { saveAs } from "file-saver";
export var platform_id : string; // platform ID string
export var platform : Platform; // platform object

View File

@ -20,6 +20,7 @@ import { AssetEditorView } from "./views/asseteditor";
import { isMobileDevice } from "./views/baseviews";
import { CallStackView, DebugBrowserView } from "./views/treeviews";
import { saveAs } from "file-saver";
import DOMPurify = require("dompurify");
// external libs (TODO)
declare var Tour, GIF, Octokat;
@ -134,12 +135,12 @@ function alertError(s:string) {
setWaitDialog(false);
bootbox.alert({
title: '<span class="glyphicon glyphicon-alert" aria-hidden="true"></span> Alert',
message: s
message: DOMPurify.sanitize(s)
});
}
function alertInfo(s:string) {
setWaitDialog(false);
bootbox.alert(s);
bootbox.alert(DOMPurify.sanitize(s));
}
function fatalError(s:string) {
alertError(s);
@ -510,7 +511,7 @@ function handleFileUpload(files: FileList) {
} else {
qs.file = files[0].name;
bootbox.confirm({
message: "Open '" + qs.file + "' as main project file?",
message: "Open '" + DOMPurify.sanitize(qs.file) + "' as main project file?",
buttons: {
confirm: { label: "Open As New Project" },
cancel: { label: "Include/Link With Project Later" },
@ -550,7 +551,7 @@ function handleFileUpload(files: FileList) {
async function _openLocalDirectory(e) {
var pickerfn = window['showDirectoryPicker'];
if (!pickerfn) {
bootbox.alert(`This browser can't open local files on your computer, yet. Try Chrome.`);
alertError(`This browser can't open local files on your computer, yet. Try Chrome.`);
}
var dirHandle = await pickerfn();
var repoid = dirHandle.name;
@ -569,7 +570,7 @@ async function _openLocalDirectory(e) {
async function promptUser(message: string) : Promise<string> {
return new Promise( (resolve, reject) => {
bootbox.prompt(message, (result) => {
bootbox.prompt(DOMPurify.sanitize(message), (result) => {
resolve(result);
});
});
@ -592,7 +593,7 @@ async function getLocalFilesystem(repoid: string) : Promise<ProjectFilesystem> {
granted = await dirHandle.requestPermission(options);
}
if (granted !== 'granted') {
bootbox.alert(`Could not get permission to access filesystem.`);
alertError(`Could not get permission to access filesystem.`);
return;
}
return {
@ -655,7 +656,7 @@ async function getGithubService() {
function getBoundGithubURL() : string {
var toks = (repo_id||'').split('/');
if (toks.length != 2) {
alertError("<p>You are not in a GitHub repository.</p><p>Choose one from the pulldown, or Import or Publish one.</p>");
alertError("You are not in a GitHub repository. Choose one from the pulldown, or Import or Publish one.");
return null;
}
return 'https://github.com/' + toks[0] + '/' + toks[1];
@ -693,7 +694,7 @@ async function importProjectFromGithub(githuburl:string, replaceURL:boolean) {
}).catch( (e) => {
setWaitDialog(false);
console.log(e);
alertError("<p>Could not import " + githuburl + ".</p>" + e);
alertError("Could not import " + githuburl + "." + e);
});
}
@ -702,7 +703,7 @@ async function _loginToGithub(e) {
gh.login().then(() => {
alertInfo("You are signed in to Github.");
}).catch( (e) => {
alertError("<p>Could not sign in.</p>" + e);
alertError("Could not sign in.</p>" + e);
});
}
@ -801,13 +802,13 @@ function confirmCommit(sess) : Promise<GHSession> {
// anything changed?
if (files.length == 0) {
setWaitDialog(false);
bootbox.alert("No files changed.");
alertInfo("No files changed.");
return;
}
// build commit confirm message
var msg = "";
for (var f of files) {
msg += f.filename + ": " + f.status;
msg += DOMPurify.sanitize(f.filename) + ": " + f.status;
if (f.additions || f.deletions || f.changes) {
msg += " (" + f.additions + " additions, " + f.deletions + " deletions, " + f.changes + " changes)";
};
@ -865,7 +866,7 @@ async function pushChangesToGithub(message:string) {
function _deleteRepository() {
var ghurl = getBoundGithubURL();
if (!ghurl) return;
bootbox.prompt("<p>Are you sure you want to delete this repository (" + ghurl + ") from browser storage?</p><p>All changes since last commit will be lost.</p><p>Type DELETE to proceed.<p>", (yes) => {
bootbox.prompt("<p>Are you sure you want to delete this repository (" + DOMPurify.sanitize(ghurl) + ") from browser storage?</p><p>All changes since last commit will be lost.</p><p>Type DELETE to proceed.<p>", (yes) => {
if (yes.trim().toUpperCase() == "DELETE") {
deleteRepository();
}
@ -968,7 +969,7 @@ function _downloadCassetteFile_apple2(e) {
var blob = new Blob([audout], {type: "audio/wav"});
saveAs(blob, audpath);
stdout += "Then connect your audio output to the cassette input, turn up the volume, and play the audio file.";
alertInfo('<pre style="white-space: pre-wrap">'+stdout+'</pre>');
alertInfo(stdout);
}
});
}
@ -1000,7 +1001,7 @@ function _downloadCassetteFile_vcs(e) {
let blob = new Blob([audout], {type: "audio/wav"});
saveAs(blob, audpath);
stdout += "\nConnect your audio output to the SuperCharger input, turn up the volume, and play the audio file.";
alertInfo('<pre style="white-space: pre-wrap">'+stdout+'</pre>');
alertInfo(stdout);
}
});
});
@ -1029,7 +1030,7 @@ function _revertFile(e) {
if (wnd && wnd.setText) {
var fn = projectWindows.getActiveID();
$.get( "presets/"+getBasePlatform(platform_id)+"/"+fn, (text) => {
bootbox.confirm("Reset '" + fn + "' to default?", (ok) => {
bootbox.confirm("Reset '" + DOMPurify.sanitize(fn) + "' to default?", (ok) => {
if (ok) {
wnd.setText(text);
}
@ -1048,7 +1049,7 @@ function _deleteFile(e) {
var wnd = projectWindows.getActive();
if (wnd && wnd.getPath) {
var fn = projectWindows.getActiveID();
bootbox.confirm("Delete '" + fn + "'?", (ok) => {
bootbox.confirm("Delete '" + DOMPurify.sanitize(fn) + "'?", (ok) => {
if (ok) {
store.removeItem(fn).then( () => {
// if we delete what is selected
@ -1072,7 +1073,7 @@ function _renameFile(e) {
if (wnd && wnd.getPath && current_project.getFile(wnd.getPath())) {
var fn = projectWindows.getActiveID();
bootbox.prompt({
title: "Rename '" + fn + "' to?",
title: "Rename '" + DOMPurify.sanitize(fn) + "' to?",
value: fn,
callback: (newfn) => {
var data = current_project.getFile(wnd.getPath());
@ -1792,8 +1793,8 @@ function addFileToProject(type, ext, linefn) {
var wnd = projectWindows.getActive();
if (wnd && wnd.insertText) {
bootbox.prompt({
title:"Add "+type+" File to Project",
value:"filename"+ext,
title:"Add "+DOMPurify.sanitize(type)+" File to Project",
value:"filename"+DOMPurify.sanitize(ext),
callback:(filename:string) => {
if (filename && filename.trim().length > 0) {
if (!checkEnteredFilename(filename)) return;

View File

@ -1,6 +1,7 @@
import { Toolbar } from "../common/toolbar";
import { VirtualList } from "../common/vlist";
import DOMPurify from "dompurify";
const BUILTIN_INPUT_PORTS = [
'clk', 'reset',
@ -359,7 +360,7 @@ export class WaveformView {
//min: 0,
//max: meta.len-1,
//placeholder: rangestr,
title: `Enter new value for "${meta.label}" (${rangestr}):`,
title: `Enter new value for "${DOMPurify.sanitize(meta.label)}" (${rangestr}):`,
callback: (result) => {
if (result != null) {
var value = parseInt(result);