HS: disable-scripting security attribute (+1 test)

Add hs-scripting-disabled? helper that walks the ancestor chain checking
for the disable-scripting attribute. Guard hs-activate! with this check.
Add disable-scripting to generator BOOL_ATTRS so the attribute is emitted
in generated test setup code. Regen'd spec.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-05 04:49:39 +00:00
parent 79190e4dac
commit 7190a8b1d2
3 changed files with 20 additions and 6 deletions

View File

@@ -99,6 +99,22 @@
;; Called once at page load. Finds all elements with _ attribute, ;; Called once at page load. Finds all elements with _ attribute,
;; compiles their hyperscript, and activates them. ;; compiles their hyperscript, and activates them.
(define
hs-scripting-disabled?
(fn
(el)
(if
(= el nil)
false
(if
(dom-get-attr el "disable-scripting")
true
(hs-scripting-disabled? (dom-parent el))))))
;; ── Boot subtree: for dynamic content ───────────────────────────
;; Called after HTMX swaps or dynamic DOM insertion.
;; Only activates elements within the given root.
(define (define
hs-activate! hs-activate!
(fn (fn
@@ -108,7 +124,7 @@
(let (let
((src (dom-get-attr el "_")) (prev (dom-get-data el "hs-script"))) ((src (dom-get-attr el "_")) (prev (dom-get-data el "hs-script")))
(when (when
(and src (not (= src prev))) (and src (not (= src prev)) (not (hs-scripting-disabled? el)))
(when (when
(dom-dispatch el "hyperscript:before:init" nil) (dom-dispatch el "hyperscript:before:init" nil)
(hs-log-event! "hyperscript:init") (hs-log-event! "hyperscript:init")
@@ -132,10 +148,6 @@
(safe-handler el)))))) (safe-handler el))))))
(dom-dispatch el "hyperscript:after:init" nil))))))) (dom-dispatch el "hyperscript:after:init" nil)))))))
;; ── Boot subtree: for dynamic content ───────────────────────────
;; Called after HTMX swaps or dynamic DOM insertion.
;; Only activates elements within the given root.
(define (define
hs-deactivate! hs-deactivate!
(fn (fn

View File

@@ -2526,6 +2526,7 @@
(deftest "on a single div" (deftest "on a single div"
(hs-cleanup!) (hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div"))) (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
(dom-set-attr _el-div "disable-scripting" "")
(dom-set-attr _el-d1 "id" "d1") (dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click add .foo") (dom-set-attr _el-d1 "_" "on click add .foo")
(dom-append (dom-body) _el-div) (dom-append (dom-body) _el-div)

View File

@@ -399,7 +399,8 @@ def parse_html(html):
'children': [], 'parent_idx': None 'children': [], 'parent_idx': None
} }
BOOL_ATTRS = {'checked', 'selected', 'disabled', 'multiple', BOOL_ATTRS = {'checked', 'selected', 'disabled', 'multiple',
'required', 'readonly', 'autofocus', 'hidden', 'open'} 'required', 'readonly', 'autofocus', 'hidden', 'open',
'disable-scripting'}
for name, val in attrs: for name, val in attrs:
if name == 'id': el['id'] = val if name == 'id': el['id'] = val
elif name == 'class': el['classes'] = (val or '').split() elif name == 'class': el['classes'] = (val or '').split()