From ccfa5fb87b086c19a575fdd82f910eacfbe050a7 Mon Sep 17 00:00:00 2001 From: Andrey Mnatsakanov Date: Thu, 16 Jul 2026 12:42:42 +0200 Subject: [PATCH] fix(js): validate the submitter argument to requestSubmit() (#424) (#426) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HTMLFormElement.requestSubmit(submitter) accepted any element and submitted silently. Per the HTML spec (and Chrome) it must throw a TypeError when the submitter is not a submit button, and a NotFoundError DOMException when the submitter is not owned by the form; both checks run before the submit event. Adds an _isSubmitButton predicate (a ' + + '' + + '' + + '' + + ''; + const f = document.getElementById('vf'); + f.addEventListener('submit', (e) => e.preventDefault()); + const probe = (fn) => { + try { fn(); return "no-throw"; } + catch (e) { + return (e instanceof DOMException) ? "DOMException:" + e.name + : (e && e.constructor && e.constructor.name) || String(e); + } + }; + return JSON.stringify({ + div: probe(() => f.requestSubmit(document.getElementById('d'))), + plain: probe(() => f.requestSubmit(document.getElementById('plain'))), + text: probe(() => f.requestSubmit(document.getElementById('inp'))), + outside: probe(() => f.requestSubmit(document.getElementById('outside'))), + valid: probe(() => f.requestSubmit(document.getElementById('ok'))), + noArg: probe(() => f.requestSubmit()), + nullArg: probe(() => f.requestSubmit(null)), + }); + })()"#, + "returnByValue": true + }), + session_id, + ) + .await; + + let val = serde_json::from_str::(v["result"]["value"].as_str().unwrap()).unwrap(); + assert_eq!(val["div"], "TypeError", "a non-button submitter must throw TypeError"); + assert_eq!(val["plain"], "TypeError", "type=button is not a submit button"); + assert_eq!(val["text"], "TypeError", "input type=text is not a submit button"); + assert_eq!( + val["outside"], "DOMException:NotFoundError", + "a submit button not owned by the form must throw NotFoundError" + ); + assert_eq!(val["valid"], "no-throw", "the form's own submit button must be accepted"); + assert_eq!(val["noArg"], "no-throw", "requestSubmit() with no submitter is valid"); + assert_eq!(val["nullArg"], "no-throw", "requestSubmit(null) means submit from the form itself"); +} diff --git a/crates/obscura-js/js/bootstrap.js b/crates/obscura-js/js/bootstrap.js index 4009bed..b0d37cf 100644 --- a/crates/obscura-js/js/bootstrap.js +++ b/crates/obscura-js/js/bootstrap.js @@ -1150,6 +1150,17 @@ function _htmlAttrName(el, n) { return n; } +// A submit button per the HTML spec: a