Browse Source

Added a CodeQL workflow to check for bugs through code analysis. Fixed numerous bugs and implemented safeguards as already reported.

n1474335 4 years ago
parent
commit
1abc46058c

+ 32 - 0
.github/workflows/codeql.yml

@@ -0,0 +1,32 @@
+name: "CodeQL"
+
+on:
+  push:
+    branches: [ master ]
+  pull_request:
+    # The branches below must be a subset of the branches above
+    branches: [ master ]
+  schedule:
+    - cron: '22 17 * * 5'
+
+jobs:
+  analyze:
+    name: Analyze
+    runs-on: ubuntu-latest
+
+    strategy:
+      fail-fast: false
+      matrix:
+        language: [ 'javascript' ]
+
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v2
+
+    - name: Initialize CodeQL
+      uses: github/codeql-action/init@v1
+      with:
+        languages: ${{ matrix.language }}
+
+    - name: Perform CodeQL Analysis
+      uses: github/codeql-action/analyze@v1

+ 1 - 1
Gruntfile.js

@@ -174,7 +174,7 @@ module.exports = function (grunt) {
             // previous one fails. & would coninue on a fail
             .join("&&")
             // Windows does not support \n properly
-            .replace("\n", "\\n");
+            .replace(/\n/g, "\\n");
     }
 
     grunt.initConfig({

+ 1 - 1
src/core/ChefWorker.js

@@ -212,7 +212,7 @@ self.loadRequiredModules = function(recipeConfig) {
         if (!(module in OpModules)) {
             log.info(`Loading ${module} module`);
             self.sendStatusMessage(`Loading ${module} module`);
-            self.importScripts(`${self.docURL}/modules/${module}.js`);
+            self.importScripts(`${self.docURL}/modules/${module}.js`); // lgtm [js/client-side-unvalidated-url-redirection]
             self.sendStatusMessage("");
         }
     });

+ 1 - 1
src/core/Dish.mjs

@@ -207,7 +207,7 @@ class Dish {
         const data = new Uint8Array(this.value.slice(0, 2048)),
             types = detectFileType(data);
 
-        if (!types.length || !types[0].mime || !types[0].mime === "text/plain") {
+        if (!types.length || !types[0].mime || !(types[0].mime === "text/plain")) {
             return null;
         } else {
             return types[0].mime;

+ 3 - 4
src/core/Utils.mjs

@@ -705,7 +705,7 @@ class Utils {
      */
     static stripHtmlTags(htmlStr, removeScriptAndStyle=false) {
         if (removeScriptAndStyle) {
-            htmlStr = htmlStr.replace(/<(script|style)[^>]*>.*<\/(script|style)>/gmi, "");
+            htmlStr = htmlStr.replace(/<(script|style)[^>]*>.*?<\/(script|style)>/gi, "");
         }
         return htmlStr.replace(/<[^>]+>/g, "");
     }
@@ -729,11 +729,10 @@ class Utils {
             ">": "&gt;",
             '"': "&quot;",
             "'": "&#x27;", // &apos; not recommended because it's not in the HTML spec
-            "/": "&#x2F;", // forward slash is included as it helps end an HTML entity
             "`": "&#x60;"
         };
 
-        return str.replace(/[&<>"'/`]/g, function (match) {
+        return str.replace(/[&<>"'`]/g, function (match) {
             return HTML_CHARS[match];
         });
     }
@@ -879,7 +878,7 @@ class Utils {
         while ((m = recipeRegex.exec(recipe))) {
             // Translate strings in args back to double-quotes
             args = m[2]
-                .replace(/"/g, '\\"') // Escape double quotes
+                .replace(/"/g, '\\"') // Escape double quotes  lgtm [js/incomplete-sanitization]
                 .replace(/(^|,|{|:)'/g, '$1"') // Replace opening ' with "
                 .replace(/([^\\]|(?:\\\\)+)'(,|:|}|$)/g, '$1"$2') // Replace closing ' with "
                 .replace(/\\'/g, "'"); // Unescape single quotes

+ 1 - 1
src/core/config/scripts/newOperation.mjs

@@ -121,7 +121,7 @@ prompt.get(schema, (err, result) => {
 
     const moduleName = result.opName.replace(/\w\S*/g, txt => {
         return txt.charAt(0).toUpperCase() + txt.substr(1);
-    }).replace(/[\s-()/./]/g, "");
+    }).replace(/[\s-()./]/g, "");
 
 
     const template = `/**

+ 3 - 3
src/core/lib/Base85.mjs

@@ -32,9 +32,9 @@ export const ALPHABET_OPTIONS = [
  * @returns {string}
  */
 export function alphabetName(alphabet) {
-    alphabet = alphabet.replace("'", "&apos;");
-    alphabet = alphabet.replace("\"", "&quot;");
-    alphabet = alphabet.replace("\\", "&bsol;");
+    alphabet = alphabet.replace(/'/g, "&apos;");
+    alphabet = alphabet.replace(/"/g, "&quot;");
+    alphabet = alphabet.replace(/\\/g, "&bsol;");
     let name;
 
     ALPHABET_OPTIONS.forEach(function(a) {

+ 5 - 5
src/core/lib/Charts.mjs

@@ -86,8 +86,8 @@ export function getScatterValues(input, recordDelimiter, fieldDelimiter, columnH
     }
 
     values = values.map(row => {
-        const x = parseFloat(row[0], 10),
-            y = parseFloat(row[1], 10);
+        const x = parseFloat(row[0]),
+            y = parseFloat(row[1]);
 
         if (Number.isNaN(x)) throw new OperationError("Values must be numbers in base 10.");
         if (Number.isNaN(y)) throw new OperationError("Values must be numbers in base 10.");
@@ -121,8 +121,8 @@ export function getScatterValuesWithColour(input, recordDelimiter, fieldDelimite
     }
 
     values = values.map(row => {
-        const x = parseFloat(row[0], 10),
-            y = parseFloat(row[1], 10),
+        const x = parseFloat(row[0]),
+            y = parseFloat(row[1]),
             colour = row[2];
 
         if (Number.isNaN(x)) throw new OperationError("Values must be numbers in base 10.");
@@ -157,7 +157,7 @@ export function getSeriesValues(input, recordDelimiter, fieldDelimiter, columnHe
     values.forEach(row => {
         const serie = row[0],
             xVal = row[1],
-            val = parseFloat(row[2], 10);
+            val = parseFloat(row[2]);
 
         if (Number.isNaN(val)) throw new OperationError("Values must be numbers in base 10.");
 

+ 1 - 1
src/core/lib/FileType.mjs

@@ -213,7 +213,7 @@ function locatePotentialSig(buf, sig, offset) {
 export function isType(type, buf) {
     const types = detectFileType(buf);
 
-    if (!(types && types.length)) return false;
+    if (!types.length) return false;
 
     if (typeof type === "string") {
         return types.reduce((acc, t) => {

+ 1 - 1
src/core/lib/Stream.mjs

@@ -177,7 +177,7 @@ export default class Stream {
 
         // Get the skip table.
         const skiptable = preprocess(val, length);
-        let found = true;
+        let found;
 
         while (this.position < this.length) {
             // Until we hit the final element of val in the stream.

+ 2 - 2
src/core/operations/ExtractEmailAddresses.mjs

@@ -39,8 +39,8 @@ class ExtractEmailAddresses extends Operation {
      */
     run(input, args) {
         const displayTotal = args[0],
-        // email regex from: https://www.regextester.com/98066
-            regex = /(?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9])?\.)+[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/ig;
+            // email regex from: https://www.regextester.com/98066
+            regex = /(?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9])?\.)+[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}\])/ig;
         return search(input, regex, null, displayTotal);
     }
 

+ 1 - 1
src/core/operations/FromBCD.mjs

@@ -95,7 +95,7 @@ class FromBCD extends Operation {
         if (!packed) {
             // Discard each high nibble
             for (let i = 0; i < nibbles.length; i++) {
-                nibbles.splice(i, 1);
+                nibbles.splice(i, 1); // lgtm [js/loop-iteration-skipped-due-to-shifting]
             }
         }
 

+ 1 - 1
src/core/operations/FromBase.mjs

@@ -46,7 +46,7 @@ class FromBase extends Operation {
         }
 
         const number = input.replace(/\s/g, "").split(".");
-        let result = new BigNumber(number[0], radix) || 0;
+        let result = new BigNumber(number[0], radix);
 
         if (number.length === 1) return result;
 

+ 4 - 4
src/core/operations/FromBase32.mjs

@@ -84,10 +84,10 @@ class FromBase32 extends Operation {
             chr5 = ((enc7 & 7) << 5) | enc8;
 
             output.push(chr1);
-            if (enc2 & 3 !== 0 || enc3 !== 32) output.push(chr2);
-            if (enc4 & 15 !== 0 || enc5 !== 32) output.push(chr3);
-            if (enc5 & 1 !== 0 || enc6 !== 32) output.push(chr4);
-            if (enc7 & 7 !== 0 || enc8 !== 32) output.push(chr5);
+            if ((enc2 & 3) !== 0 || enc3 !== 32) output.push(chr2);
+            if ((enc4 & 15) !== 0 || enc5 !== 32) output.push(chr3);
+            if ((enc5 & 1) !== 0 || enc6 !== 32) output.push(chr4);
+            if ((enc7 & 7) !== 0 || enc8 !== 32) output.push(chr5);
         }
 
         return output;

+ 1 - 1
src/core/operations/Lorenz.mjs

@@ -376,7 +376,7 @@ class Lorenz extends Operation {
             // Psi wheels only move sometimes, dependent on M37 current setting and limitations
 
             const basicmotor = m37lug;
-            let totalmotor = basicmotor;
+            let totalmotor;
             let lim = 0;
 
             p5[2] = p5[1];

+ 1 - 1
src/core/operations/OpticalCharacterRecognition.mjs

@@ -51,7 +51,7 @@ class OpticalCharacterRecognition extends Operation {
     async run(input, args) {
         const [showConfidence] = args;
 
-        if (!isWorkerEnvironment()) throw OperationError("This operation only works in a browser");
+        if (!isWorkerEnvironment()) throw new OperationError("This operation only works in a browser");
 
         const type = isImage(input);
         if (!type) {

+ 4 - 4
src/core/operations/PHPDeserialize.mjs

@@ -111,7 +111,7 @@ class PHPDeserialize extends Operation {
                     } else {
                         const numberCheck = lastItem.match(/[0-9]+/);
                         if (args[0] && numberCheck && numberCheck[0].length === lastItem.length) {
-                            result.push("\"" + lastItem + "\": " + item);
+                            result.push('"' + lastItem + '": ' + item);
                         } else {
                             result.push(lastItem + ": " + item);
                         }
@@ -149,11 +149,11 @@ class PHPDeserialize extends Operation {
                     const length = readUntil(":");
                     expect("\"");
                     const value = read(length);
-                    expect("\";");
+                    expect('";');
                     if (args[0]) {
-                        return "\"" + value.replace(/"/g, "\\\"") + "\"";
+                        return '"' + value.replace(/"/g, '\\"') + '"'; // lgtm [js/incomplete-sanitization]
                     } else {
-                        return "\"" + value + "\"";
+                        return '"' + value + '"';
                     }
                 }
 

+ 1 - 1
src/core/operations/ParseIPv6Address.mjs

@@ -169,7 +169,7 @@ class ParseIPv6Address extends Operation {
 
 
             // Detect possible EUI-64 addresses
-            if ((ipv6[5] & 0xff === 0xff) && (ipv6[6] >>> 8 === 0xfe)) {
+            if (((ipv6[5] & 0xff) === 0xff) && (ipv6[6] >>> 8 === 0xfe)) {
                 output += "\n\nThis IPv6 address contains a modified EUI-64 address, identified by the presence of FF:FE in the 12th and 13th octets.";
 
                 const intIdent = Utils.hex(ipv6[4] >>> 8) + ":" + Utils.hex(ipv6[4] & 0xff) + ":" +

+ 1 - 1
src/core/operations/ParseSSHHostKey.mjs

@@ -87,7 +87,7 @@ class ParseSSHHostKey extends Operation {
      * @returns {byteArray}
      */
     convertKeyToBinary(inputKey, inputFormat) {
-        const keyPattern = new RegExp(/^(?:[ssh]|[ecdsa-sha2])\S+\s+(\S*)/),
+        const keyPattern = new RegExp(/^(?:ssh|ecdsa-sha2)\S+\s+(\S*)/),
             keyMatch = inputKey.match(keyPattern);
 
         if (keyMatch) {

+ 1 - 1
src/core/operations/RegularExpression.mjs

@@ -45,7 +45,7 @@ class RegularExpression extends Operation {
                     },
                     {
                         name: "Email address",
-                        value: "(?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*\")@(?:(?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9])?\\.)+[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9])?|\\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\\])"
+                        value: "(?:[\\u00A0-\\uD7FF\\uE000-\\uFFFFa-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[\\u00A0-\\uD7FF\\uE000-\\uFFFFa-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[\\u00A0-\\uD7FF\\uE000-\\uFFFFa-z0-9](?:[\\u00A0-\\uD7FF\\uE000-\\uFFFF-a-z0-9-]*[\\u00A0-\\uD7FF\\uE000-\\uFFFFa-z0-9])?\\.)+[\\u00A0-\\uD7FF\\uE000-\\uFFFFa-z0-9](?:[\\u00A0-\\uD7FF\\uE000-\\uFFFFa-z0-9-]*[\\u00A0-\\uD7FF\\uE000-\\uFFFFa-z0-9])?|\\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\\.){3}\\])"
                     },
                     {
                         name: "URL",

+ 1 - 1
src/core/operations/SharpenImage.mjs

@@ -81,7 +81,7 @@ class SharpenImage extends Operation {
 
             if (isWorkerEnvironment())
                 self.sendStatusMessage("Sharpening image... (Blurring cloned image)");
-            const blurImage = gaussianBlur(image.clone(), radius, 3);
+            const blurImage = gaussianBlur(image.clone(), radius);
 
 
             if (isWorkerEnvironment())

+ 2 - 2
src/core/operations/Sort.mjs

@@ -125,7 +125,7 @@ class Sort extends Operation {
                 const ret = a_[i].localeCompare(b_[i]); // Compare strings
                 if (ret !== 0) return ret;
             }
-            if (!isNaN(a_[i]) && !isNaN(a_[i])) { // Compare numbers
+            if (!isNaN(a_[i]) && !isNaN(b_[i])) { // Compare numbers
                 if (a_[i] - b_[i] !== 0) return a_[i] - b_[i];
             }
         }
@@ -163,7 +163,7 @@ class Sort extends Operation {
                 const ret = a_[i].localeCompare(b_[i]); // Compare strings
                 if (ret !== 0) return ret;
             }
-            if (!isNaN(a_[i]) && !isNaN(a_[i])) { // Compare numbers
+            if (!isNaN(a_[i]) && !isNaN(b_[i])) { // Compare numbers
                 if (a_[i] - b_[i] !== 0) return a_[i] - b_[i];
             }
         }

+ 1 - 1
src/core/operations/ToCharcode.mjs

@@ -52,7 +52,7 @@ class ToCharcode extends Operation {
         const delim = Utils.charRep(args[0] || "Space"),
             base = args[1];
         let output = "",
-            padding = 2,
+            padding,
             ordinal;
 
         if (base < 2 || base > 36) {

+ 0 - 1
src/core/operations/UnescapeUnicodeCharacters.mjs

@@ -47,7 +47,6 @@ class UnescapeUnicodeCharacters extends Operation {
         while ((m = regex.exec(input))) {
             // Add up to match
             output += input.slice(i, m.index);
-            i = m.index;
 
             // Add match
             output += Utils.chr(parseInt(m[1], 16));

+ 2 - 2
src/web/HTMLIngredient.mjs

@@ -153,7 +153,7 @@ class HTMLIngredient {
                 for (i = 0; i < this.value.length; i++) {
                     if ((m = this.value[i].match(/\[([a-z0-9 -()^]+)\]/i))) {
                         html += `<optgroup label="${m[1]}">`;
-                    } else if ((m = this.value[i].match(/\[\/([a-z0-9 -()^]+)\]/i))) {
+                    } else if (this.value[i].match(/\[\/([a-z0-9 -()^]+)\]/i)) {
                         html += "</optgroup>";
                     } else {
                         html += `<option ${this.defaultIndex === i ? "selected" : ""}>${this.value[i]}</option>`;
@@ -177,7 +177,7 @@ class HTMLIngredient {
                 for (i = 0; i < this.value.length; i++) {
                     if ((m = this.value[i].name.match(/\[([a-z0-9 -()^]+)\]/i))) {
                         html += `<optgroup label="${m[1]}">`;
-                    } else if ((m = this.value[i].name.match(/\[\/([a-z0-9 -()^]+)\]/i))) {
+                    } else if (this.value[i].name.match(/\[\/([a-z0-9 -()^]+)\]/i)) {
                         html += "</optgroup>";
                     } else {
                         const val = this.type === "populateMultiOption" ?

+ 6 - 4
src/web/HTMLOperation.mjs

@@ -5,6 +5,7 @@
  */
 
 import HTMLIngredient from "./HTMLIngredient.mjs";
+import Utils from "../core/Utils.mjs";
 
 
 /**
@@ -72,7 +73,7 @@ class HTMLOperation {
      * @returns {string}
      */
     toFullHtml() {
-        let html = `<div class="op-title">${this.name}</div>
+        let html = `<div class="op-title">${Utils.escapeHtml(this.name)}</div>
         <div class="ingredients">`;
 
         for (let i = 0; i < this.ingList.length; i++) {
@@ -151,15 +152,16 @@ class HTMLOperation {
  */
 function titleFromWikiLink(url) {
     const splitURL = url.split("/");
-    if (splitURL.indexOf("wikipedia.org") < 0 && splitURL.indexOf("forensicswiki.org") < 0) {
+    if (!splitURL.includes("wikipedia.org") && !splitURL.includes("forensicswiki.xyz")) {
         // Not a wiki link, return full URL
         return `<a href='${url}' target='_blank'>More Information<i class='material-icons inline-icon'>open_in_new</i></a>`;
     }
 
-    const wikiName = splitURL.indexOf("forensicswiki.org") < 0 ? "Wikipedia" : "Forensics Wiki";
+    const wikiName = splitURL.includes("forensicswiki.xyz") ? "Forensics Wiki" : "Wikipedia";
 
     const pageTitle = decodeURIComponent(splitURL[splitURL.length - 1])
-        .replace(/_/g, " ");
+        .replace(/_/g, " ")
+        .replace(/index\.php\?title=/g, "");
     return `<a href='${url}' target='_blank'>${pageTitle}<i class='material-icons inline-icon'>open_in_new</i></a> on ${wikiName}`;
 }
 

+ 1 - 1
src/web/html/index.html

@@ -153,7 +153,7 @@
                         <script type="text/javascript">
                             // Must be text/javascript rather than application/javascript otherwise IE won't recognise it...
                             if (navigator.userAgent && navigator.userAgent.match(/Trident/)) {
-                                document.write("Internet Explorer is not supported, please use Firefox or Chrome instead");
+                                document.getElementById("notice").innerHTML += "Internet Explorer is not supported, please use Firefox or Chrome instead";
                                 alert("Internet Explorer is not supported, please use Firefox or Chrome instead");
                             }
                         </script>

+ 2 - 2
src/web/waiters/ControlsWaiter.mjs

@@ -102,7 +102,7 @@ class ControlsWaiter {
         const saveLinkEl = document.getElementById("save-link");
         const saveLink = this.generateStateUrl(includeRecipe, includeInput, recipeConfig);
 
-        saveLinkEl.innerHTML = Utils.truncate(saveLink, 120);
+        saveLinkEl.innerHTML = Utils.escapeHtml(Utils.truncate(saveLink, 120));
         saveLinkEl.setAttribute("href", saveLink);
     }
 
@@ -138,7 +138,7 @@ class ControlsWaiter {
 
         const params = [
             includeRecipe ? ["recipe", recipeStr] : undefined,
-            includeInput ? ["input", input] : undefined,
+            includeInput ? ["input", Utils.escapeHtml(input)] : undefined,
         ];
 
         const hash = params

+ 2 - 6
src/web/waiters/InputWaiter.mjs

@@ -510,10 +510,6 @@ class InputWaiter {
         if (inputNum !== activeTab) return;
 
         const fileLoaded = document.getElementById("input-file-loaded");
-        let oldProgress = fileLoaded.textContent;
-        if (oldProgress !== "Error") {
-            oldProgress = parseInt(oldProgress.replace("%", ""), 10);
-        }
         if (progress === "error") {
             fileLoaded.textContent = "Error";
             fileLoaded.style.color = "#FF0000";
@@ -1276,7 +1272,7 @@ class InputWaiter {
         const func = function(time) {
             if (this.mousedown) {
                 this.changeTabRight();
-                const newTime = (time > 50) ? time = time - 10 : 50;
+                const newTime = (time > 50) ? time - 10 : 50;
                 setTimeout(func.bind(this, [newTime]), newTime);
             }
         };
@@ -1293,7 +1289,7 @@ class InputWaiter {
         const func = function(time) {
             if (this.mousedown) {
                 this.changeTabLeft();
-                const newTime = (time > 50) ? time = time - 10 : 50;
+                const newTime = (time > 50) ? time - 10 : 50;
                 setTimeout(func.bind(this, [newTime]), newTime);
             }
         };

+ 1 - 1
src/web/waiters/OperationsWaiter.mjs

@@ -121,7 +121,7 @@ class OperationsWaiter {
             if (nameMatch || descPos >= 0) {
                 const operation = new HTMLOperation(opName, this.app.operations[opName], this.app, this.manager);
                 if (highlight) {
-                    operation.highlightSearchStrings(calcMatchRanges(idxs) || [], [[descPos, inStr.length]]);
+                    operation.highlightSearchStrings(calcMatchRanges(idxs), [[descPos, inStr.length]]);
                 }
 
                 if (nameMatch) {

+ 2 - 4
src/web/waiters/OutputWaiter.mjs

@@ -306,8 +306,6 @@ class OutputWaiter {
                     outputText.value = "";
                     outputHtml.innerHTML = "";
 
-                    lines = 0;
-                    length = 0;
                     this.toggleLoader(false);
                     return;
                 }
@@ -765,7 +763,7 @@ class OutputWaiter {
         const func = function(time) {
             if (this.mousedown) {
                 this.changeTabRight();
-                const newTime = (time > 50) ? time = time - 10 : 50;
+                const newTime = (time > 50) ? time - 10 : 50;
                 setTimeout(func.bind(this, [newTime]), newTime);
             }
         };
@@ -782,7 +780,7 @@ class OutputWaiter {
         const func = function(time) {
             if (this.mousedown) {
                 this.changeTabLeft();
-                const newTime = (time > 50) ? time = time - 10 : 50;
+                const newTime = (time > 50) ? time - 10 : 50;
                 setTimeout(func.bind(this, [newTime]), newTime);
             }
         };

+ 1 - 1
src/web/waiters/RecipeWaiter.mjs

@@ -316,7 +316,7 @@ class RecipeWaiter {
                     };
                 } else if (ingList[j].getAttribute("type") === "number") {
                     // number
-                    ingredients[j] = parseFloat(ingList[j].value, 10);
+                    ingredients[j] = parseFloat(ingList[j].value);
                 } else {
                     // all others
                     ingredients[j] = ingList[j].value;