web: Don't allow arbitrary upload of save files for technical reasons

This commit is contained in:
Daniel Jacobs 2023-03-13 12:59:59 -04:00 committed by Nathan Adams
parent 54ad95beb3
commit 94ff2891e9
3 changed files with 25 additions and 32 deletions

View File

@ -23,7 +23,6 @@ overrides:
- plugin:@typescript-eslint/recommended - plugin:@typescript-eslint/recommended
rules: rules:
'@typescript-eslint/no-non-null-assertion': 'off' '@typescript-eslint/no-non-null-assertion': 'off'
'no-control-regex': 0
'@typescript-eslint/no-unused-vars': '@typescript-eslint/no-unused-vars':
- error - error
- argsIgnorePattern: ^_ - argsIgnorePattern: ^_

View File

@ -243,10 +243,6 @@ export class RufflePlayer extends HTMLElement {
if (backupSaves) { if (backupSaves) {
backupSaves.addEventListener("click", this.backupSaves.bind(this)); backupSaves.addEventListener("click", this.backupSaves.bind(this));
} }
const restoreSave = this.saveManager.querySelector("#restore-save");
if (restoreSave) {
restoreSave.addEventListener("change", this.restoreSave.bind(this));
}
this.contextMenuElement = this.shadow.getElementById("context-menu")!; this.contextMenuElement = this.shadow.getElementById("context-menu")!;
window.addEventListener("pointerdown", this.pointerDown.bind(this)); window.addEventListener("pointerdown", this.pointerDown.bind(this));
@ -874,40 +870,20 @@ export class RufflePlayer extends HTMLElement {
} }
/** /**
* Restore save from SOL file. * Replace save from SOL file.
* *
* @param event The change event fired * @param event The change event fired
* @param solKey The localStorage save file key
*/ */
async restoreSave(event: Event): Promise<void> { async replaceSOL(event: Event, solKey: string): Promise<void> {
const fileInput = <HTMLInputElement>event.target; const fileInput = <HTMLInputElement>event.target;
const reader = new FileReader(); const reader = new FileReader();
reader.addEventListener("load", () => { reader.addEventListener("load", () => {
if (reader.result && typeof reader.result === "string") { if (reader.result && typeof reader.result === "string") {
const b64Regex = new RegExp("data:.*;base64,"); const b64Regex = new RegExp("data:.*;base64,");
const unprintable = new RegExp("[\u0000-\u001f]*");
const nullChar = "\u0000";
const b64SolData = reader.result.replace(b64Regex, ""); const b64SolData = reader.result.replace(b64Regex, "");
const solDataNamePlus = atob(b64SolData)
.slice(10)
.replace(unprintable, "");
const solName = solDataNamePlus.substring(
0,
solDataNamePlus.indexOf(nullChar)
);
const solKey = this.swfUrl
? this.swfUrl.hostname +
this.swfUrl.pathname +
"/" +
solName
: document.location.hostname + "//" + solName;
if (this.isB64SOL(b64SolData)) { if (this.isB64SOL(b64SolData)) {
if (localStorage[solKey]) { if (localStorage[solKey]) {
alert(
"Save data with key " +
solKey +
" already exists. Delete it first."
);
} else {
this.saveManager.close(); this.saveManager.close();
localStorage.setItem(solKey, b64SolData); localStorage.setItem(solKey, b64SolData);
this.populateSaves(); this.populateSaves();
@ -962,12 +938,31 @@ export class RufflePlayer extends HTMLElement {
solName + ".sol" solName + ".sol"
) )
); );
const replaceCol = document.createElement("TD");
const replaceInput = <HTMLInputElement>(
document.createElement("INPUT")
);
replaceInput.type = "file";
replaceInput.className = "replace-save";
replaceInput.id = "replace-save-" + key;
const replaceLabel = <HTMLLabelElement>(
document.createElement("LABEL")
);
replaceLabel.htmlFor = "replace-save-" + key;
replaceLabel.textContent = "Replace";
replaceLabel.className = "save-option";
replaceInput.addEventListener("change", (event) =>
this.replaceSOL(event, key)
);
replaceCol.appendChild(replaceInput);
replaceCol.appendChild(replaceLabel);
const deleteCol = document.createElement("TD"); const deleteCol = document.createElement("TD");
deleteCol.textContent = "Delete"; deleteCol.textContent = "Delete";
deleteCol.className = "save-option"; deleteCol.className = "save-option";
deleteCol.addEventListener("click", () => this.deleteSave(key)); deleteCol.addEventListener("click", () => this.deleteSave(key));
row.appendChild(keyCol); row.appendChild(keyCol);
row.appendChild(downloadCol); row.appendChild(downloadCol);
row.appendChild(replaceCol);
row.appendChild(deleteCol); row.appendChild(deleteCol);
saveTable.appendChild(row); saveTable.appendChild(row);
} }

View File

@ -285,6 +285,9 @@ ruffleShadowTemplate.innerHTML = `
#restore-save { #restore-save {
display: none; display: none;
} }
.replace-save {
display: none;
}
.save-option { .save-option {
display: inline-block; display: inline-block;
padding: 3px 10px; padding: 3px 10px;
@ -324,10 +327,6 @@ ruffleShadowTemplate.innerHTML = `
<span id="close-modal">&times;</span> <span id="close-modal">&times;</span>
<div class="general-save-options"> <div class="general-save-options">
<span class="save-option" id="backup-saves">Backup all saves (download all sols)</span> <span class="save-option" id="backup-saves">Backup all saves (download all sols)</span>
<span id="restore-save-paragraph">
<input type="file" accept=".sol" id="restore-save">
<label class="save-option" for="restore-save" id="restore-save-label">Restore save (upload sol)</label>
</span>
</div> </div>
<table id="local-saves"></table> <table id="local-saves"></table>
</dialog> </dialog>