From 87c7501a67f7b065fe46b90c126eb6f5c91dfa91 Mon Sep 17 00:00:00 2001 From: gusthoff Date: Sun, 22 Mar 2026 18:47:42 +0100 Subject: [PATCH 1/7] Fix typo 'functionilty' -> 'functionality' in download message Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/ts/strings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/ts/strings.ts b/frontend/src/ts/strings.ts index 97155bed1..a09e0c6b4 100644 --- a/frontend/src/ts/strings.ts +++ b/frontend/src/ts/strings.ts @@ -12,7 +12,7 @@ export const RELOAD_CONFIRM_MSG = export const DOWNLOAD_TOOLTIP = 'Download source files'; export const DOWNLOAD_MAINTENANCE = -'The download functionilty is currently undergoing maintenance'; +'The download functionality is currently undergoing maintenance'; // const SETTINGS_TOOLTIP = 'Modify settings for this editor'; export const SETTINGS_TABBED_EDITOR_LABEL = From f999f6300ae5b12fc7e4d2c918d4a24dac9401d3 Mon Sep 17 00:00:00 2001 From: gusthoff Date: Sun, 22 Mar 2026 18:47:50 +0100 Subject: [PATCH 2/7] Fix JSDoc comment typo 'delay-+' -> 'delay' in ServerWorker Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/ts/server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/ts/server.ts b/frontend/src/ts/server.ts index 67d3ddb60..831e8bec2 100644 --- a/frontend/src/ts/server.ts +++ b/frontend/src/ts/server.ts @@ -84,7 +84,7 @@ export class ServerWorker { /** * Delay function - * @param {number} ms - Number of milliseconds to delay-+ + * @param {number} ms - Number of milliseconds to delay * @returns {Promise} - A promise to await */ public static delay(ms: number): Promise { From 2764c793da007965f5a103b334099508f580427e Mon Sep 17 00:00:00 2001 From: gusthoff Date: Sun, 22 Mar 2026 18:48:02 +0100 Subject: [PATCH 3/7] Replace bare except with except Exception in check_code_block.py Bare except clauses catch KeyboardInterrupt and SystemExit, making the script impossible to interrupt cleanly via Ctrl+C. Co-Authored-By: Claude Sonnet 4.6 --- frontend/py_modules/code_projects/check_code_block.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/py_modules/code_projects/check_code_block.py b/frontend/py_modules/code_projects/check_code_block.py index 5bc8e6332..4ab9116c6 100755 --- a/frontend/py_modules/code_projects/check_code_block.py +++ b/frontend/py_modules/code_projects/check_code_block.py @@ -71,7 +71,7 @@ def set_versions(): gnat_version = run("gnat", "--version").partition('\n')[0] gnat_prove_version = run("gnatprove", "--version").partition('\n')[0] gprbuild_version = run("gprbuild", "--version").partition('\n')[0] - except: + except Exception: gcc_version = "" if gcc_version is None else gcc_version gnat_version = "" if gnat_version is None else gnat_version gnat_prove_version = "" if gnat_prove_version is None else gnat_prove_version @@ -153,7 +153,7 @@ def cleanup_project(language, project_filename, main_file): try: ref_block_check = checks.BlockCheck.from_json_file() - except: + except Exception: pass if ref_block_check is not None and not force_checks: From 6176497d4c90825ef530c32953d2557a381b46bd Mon Sep 17 00:00:00 2001 From: gusthoff Date: Sun, 22 Mar 2026 18:48:22 +0100 Subject: [PATCH 4/7] Fix wrong test description in getElemsByTag test block The getElemsByTag describe block had a copy-paste description saying 'have the class' instead of 'have the tag'. Co-Authored-By: Claude Sonnet 4.6 --- frontend/tests/ts/dom-utils.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/tests/ts/dom-utils.spec.ts b/frontend/tests/ts/dom-utils.spec.ts index ef806e08f..f0a22c2c8 100644 --- a/frontend/tests/ts/dom-utils.spec.ts +++ b/frontend/tests/ts/dom-utils.spec.ts @@ -59,7 +59,7 @@ describe('getElemsByTag', () => { c.classList.add('diff-class'); parent.appendChild(c); - it('should return an array with the elems that have the class', () => { + it('should return an array with the elems that have the tag', () => { const elems = getElemsByTag(parent, 'button'); expect(elems).to.have.length(2); }); From 766212d16d9d850357a99e589198aebbcab14f5f Mon Sep 17 00:00:00 2001 From: gusthoff Date: Sun, 22 Mar 2026 18:50:41 +0100 Subject: [PATCH 5/7] Use strict equality operators (=== / !==) throughout frontend TS Replace all loose == and != comparisons with === and !==. None of these caused behavioral bugs (all were same-type comparisons), but they were inconsistent with TypeScript best practices. Affected files: widget.ts, download.ts, areas.ts, sandbox-redirect.ts Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/ts/areas.ts | 4 ++-- frontend/src/ts/download.ts | 6 +++--- frontend/src/ts/sandbox-redirect.ts | 4 ++-- frontend/src/ts/widget.ts | 14 +++++++------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/frontend/src/ts/areas.ts b/frontend/src/ts/areas.ts index 66458ddc0..c9fa1ee89 100644 --- a/frontend/src/ts/areas.ts +++ b/frontend/src/ts/areas.ts @@ -174,8 +174,8 @@ export class LabArea extends Area { this.button.addEventListener('click', () => { this.button.classList.toggle('active'); - if (this.container.style.display == '' || - this.container.style.display == 'block') { + if (this.container.style.display === '' || + this.container.style.display === 'block') { this.container.style.display = 'none'; } else { this.container.style.display = 'block'; diff --git a/frontend/src/ts/download.ts b/frontend/src/ts/download.ts index dcdf230b8..d12c6a929 100644 --- a/frontend/src/ts/download.ts +++ b/frontend/src/ts/download.ts @@ -131,7 +131,7 @@ export function parseSwitches(switches: UnparsedSwitches): ParsedSwitches { * @returns {Array} the filenames of each potential 'main' file. */ export function findMains(files: ResourceList): Array { - if (files.length == 1) return [files[0].basename]; + if (files.length === 1) return [files[0].basename]; const mains: Array = []; const fileNames = new Set(files.map((f) => f.basename)); for (const f of files) { @@ -158,7 +158,7 @@ export function findMains(files: ResourceList): Array { * file extension removed. */ export function getMain(files: ResourceList, main: string): string { - if (main == '') { + if (main === '') { const potentialMains = findMains(files); if (potentialMains.length == 1) { main = potentialMains[0]; @@ -168,7 +168,7 @@ export function getMain(files: ResourceList, main: string): string { } } main = main.split('.')[0]; - if (main == '') return ''; + if (main === '') return ''; return `for Main use ("${main}");`; } diff --git a/frontend/src/ts/sandbox-redirect.ts b/frontend/src/ts/sandbox-redirect.ts index a36f19013..899679183 100644 --- a/frontend/src/ts/sandbox-redirect.ts +++ b/frontend/src/ts/sandbox-redirect.ts @@ -15,10 +15,10 @@ export function sandboxRedirect(): void { const cookieValue = cookies.get(cookieName) as string; const cookieReferenceValue = "true"; - if (cookieValue != cookieReferenceValue) { + if (cookieValue !== cookieReferenceValue) { const passw = prompt("Enter site password:") - if (passw != "Ada") { + if (passw !== "Ada") { const msg = 'You have reached learn-sandbox, the learn testing site. ' + 'This is reserved for testers only. You will be directed to the main ' + 'learn.adacore.com site after pressing OK.'; diff --git a/frontend/src/ts/widget.ts b/frontend/src/ts/widget.ts index ea536cd47..21810df32 100644 --- a/frontend/src/ts/widget.ts +++ b/frontend/src/ts/widget.ts @@ -76,7 +76,7 @@ class Widget { // Parse files const files = getElemsByClass(this.container, 'file'); // Check to make sure we have files in the widget - if (files.length == 0) { + if (files.length === 0) { throw Error('Malformed widget: No files present.'); } for (const file of files) { @@ -148,7 +148,7 @@ class Widget { // Get tabbed view status from cookie const cookieTabbedView = cookies.get('tabbed_view') as string; - const tabbedView = (cookieTabbedView != 'false'); + const tabbedView = (cookieTabbedView !== 'false'); // attach handlers to the settings bar items const tabSetting = @@ -184,7 +184,7 @@ class Widget { const themeSetting = this.getElem('settings-bar', 'theme-setting') as HTMLInputElement; // Set checkbox according to value from cookie - themeSetting.checked = (cookieTheme == 'dark'); + themeSetting.checked = (cookieTheme === 'dark'); this.setTheme(cookieTheme); themeSetting.addEventListener('change', () => { @@ -403,7 +403,7 @@ class Widget { */ private setTheme(themeStr : string) : void { let theme = EditorTheme.Light; - if (themeStr == 'dark') { + if (themeStr === 'dark') { theme = EditorTheme.Dark; } @@ -555,7 +555,7 @@ class Widget { // Lines that contain a sloc are clickable: const cb = (): void => { - if (window.getSelection()?.toString() == '') { + if (window.getSelection()?.toString() === '') { view.header.scrollIntoView(true); view.header.click(); // Jump to corresponding line @@ -567,7 +567,7 @@ class Widget { // If the message if of type info, addInfo // Otherwise, addMsg - if (msgType == 'info') { + if (msgType === 'info') { homeArea.addInfo(outMsg, cb); } else { homeArea.addMsg(outMsg, cb); @@ -594,7 +594,7 @@ class Widget { } if (data.completed) { - if (data.status != 0) { + if (data.status !== 0) { this.outputArea.addError(Strings.EXIT_STATUS_LABEL + ': ' + data.status); } From 9dadfe2a1c3035879aea0eaf8573f60304ff4d45 Mon Sep 17 00:00:00 2001 From: gusthoff Date: Sun, 22 Mar 2026 18:51:14 +0100 Subject: [PATCH 6/7] Add error handling for JSON.parse calls on data-switches attribute All three JSON.parse calls on widget/download switch data were unprotected. A malformed data-switches attribute would throw an uncaught exception silently breaking functionality. - download.ts getUnparsedSwitches(): rethrow with descriptive message - widget.ts download button handler: show error and return early - widget.ts buttonCB(): show error, hide spinner, and return early Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/ts/download.ts | 7 ++++++- frontend/src/ts/widget.ts | 17 +++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/frontend/src/ts/download.ts b/frontend/src/ts/download.ts index d12c6a929..2fe2685d4 100644 --- a/frontend/src/ts/download.ts +++ b/frontend/src/ts/download.ts @@ -91,7 +91,12 @@ export function getLanguages(files: ResourceList): string { * @returns {UnparsedSwitches} switches. */ export function getUnparsedSwitches(rawSwitches: string): UnparsedSwitches { - const parsed = JSON.parse(rawSwitches); + let parsed; + try { + parsed = JSON.parse(rawSwitches); + } catch { + throw new Error(`Failed to parse switches JSON: ${rawSwitches}`); + } const switches: UnparsedSwitches = {Builder: [], Compiler: []}; for (const k in switches) { if (k in parsed) { diff --git a/frontend/src/ts/widget.ts b/frontend/src/ts/widget.ts index 21810df32..8f6482574 100644 --- a/frontend/src/ts/widget.ts +++ b/frontend/src/ts/widget.ts @@ -220,7 +220,13 @@ class Widget { dlButton.addEventListener('click', async () => { this.outputArea.reset(); const files = this.collectResources(); - const switches = JSON.parse(this.container.dataset.switches as string); + let switches; + try { + switches = JSON.parse(this.container.dataset.switches as string); + } catch { + this.outputArea.addError(Strings.INTERNAL_ERROR_MESSAGE); + return; + } const activeSwitches: UnparsedSwitches = { Builder: switches['Builder'], Compiler: this.getActiveCompilerSwitches()}; @@ -474,7 +480,14 @@ class Widget { const files = this.collectResources(); - const switches = JSON.parse(this.container.dataset.switches as string); + let switches; + try { + switches = JSON.parse(this.container.dataset.switches as string); + } catch { + this.outputArea.addError(Strings.INTERNAL_ERROR_MESSAGE); + this.outputArea.showSpinner(false); + return; + } switches['Compiler'] = this.getActiveCompilerSwitches(); const serverData: RunProgram.TSData = { From d34bdaa7e26e4199ecec119e96820557fab3a570 Mon Sep 17 00:00:00 2001 From: gusthoff Date: Sun, 22 Mar 2026 18:51:31 +0100 Subject: [PATCH 7/7] Replace innerHTML string concatenation with safe DOM methods in widget.ts Two locations built HTML by concatenating untrusted strings into innerHTML, creating an XSS vector if switch names, titles, or error messages ever contain HTML special characters. - initCompilerSwitches(): build tooltip via createElement/textContent/appendChild - widgetFactory() catch block: build error paragraph via createElement/textContent Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/ts/widget.ts | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/frontend/src/ts/widget.ts b/frontend/src/ts/widget.ts index 8f6482574..7466572f0 100644 --- a/frontend/src/ts/widget.ts +++ b/frontend/src/ts/widget.ts @@ -386,18 +386,22 @@ class Widget { 'compiler-switch-help-info')[0]; d.addEventListener('click', () => { if (! d.classList.contains('disabled')) { - d.innerHTML = ''; + d.textContent = ''; d.classList.add('disabled'); } }); b.addEventListener('click', () => { - d.innerHTML = '' + switchName + ': ' + - b.title + '
' + - '
(' + - Strings.COMPILER_SWITCH_REMOVE_HELP_MESSAGE + - ')
'; - + d.textContent = ''; + const bold = document.createElement('b'); + bold.textContent = switchName; + d.appendChild(bold); + d.appendChild(document.createTextNode(': ' + b.title)); + d.appendChild(document.createElement('br')); + const helpDiv = document.createElement('div'); + helpDiv.classList.add('compiler-switch-help-info-click-remove'); + helpDiv.textContent = '(' + Strings.COMPILER_SWITCH_REMOVE_HELP_MESSAGE + ')'; + d.appendChild(helpDiv); d.classList.remove('disabled'); }); } @@ -729,13 +733,14 @@ export function widgetFactory(widgets: Array): void { console.error('Error:', error); // clear the offending element to remove any processing that was done - element.innerHTML = ''; + element.textContent = ''; // add an error message to the page in its place const errorDiv = document.createElement('div'); - errorDiv.innerHTML = '

An error has occured processing this widget.' + - Strings.INTERNAL_ERROR_MESSAGE + '

'; - + const errorP = document.createElement('p'); + errorP.textContent = 'An error has occured processing this widget.' + + Strings.INTERNAL_ERROR_MESSAGE; + errorDiv.appendChild(errorP); element.appendChild(errorDiv); } }