diff --git a/.changeset/fix-foreach-script-template-sanitization.md b/.changeset/fix-foreach-script-template-sanitization.md
new file mode 100644
index 000000000..a883fa74b
--- /dev/null
+++ b/.changeset/fix-foreach-script-template-sanitization.md
@@ -0,0 +1,8 @@
+---
+'@tko/binding.foreach': patch
+---
+
+Fix `makeTemplateNode` bypassing HTML sanitization for `").appendTo(document.body)
+ $template.text('
')
+
+ try {
+ options.templateSizeLimit = 10
+
+ assert.throws(function () {
+ applyBindings(['G1'], target[0])
+ }, /Template is too long/)
+ } finally {
+ options.templateSizeLimit = originalTemplateSizeLimit
+ $template.remove()
+ }
+ })
+
+ it('rejects nested script markup in named ").appendTo(document.body)
+ $template.text('')
+
+ try {
+ options.allowScriptTagsInTemplates = false
+
+ assert.throws(function () {
+ applyBindings(['G1'], target[0])
+ }, /Script-tag in template detected/)
+ } finally {
+ options.allowScriptTagsInTemplates = originalAllowScriptTagsInTemplates
+ $template.remove()
+ }
+ })
+
+ it('sanitizes named ").appendTo(document.body)
+ const list = ['G1']
+ let sanitizedHtml = ''
+ $template.text('')
+
+ try {
+ options.sanitizeHtmlTemplate = function (html: string) {
+ sanitizedHtml = html
+ return html.replace('', function () {
const target = $('')
const list = ['H1', 'H2']
diff --git a/packages/binding.foreach/src/foreach.ts b/packages/binding.foreach/src/foreach.ts
index 72ef35ce6..550cc8909 100644
--- a/packages/binding.foreach/src/foreach.ts
+++ b/packages/binding.foreach/src/foreach.ts
@@ -5,7 +5,15 @@
// Employing sound techniques to make a faster Knockout foreach binding.
// --------
-import { arrayForEach, cleanNode, options, virtualElements, domData, domNodeIsContainedBy } from '@tko/utils'
+import {
+ arrayForEach,
+ cleanNode,
+ options,
+ virtualElements,
+ domData,
+ domNodeIsContainedBy,
+ parseHtmlForTemplateNodes
+} from '@tko/utils'
import { isObservable, unwrap, observable } from '@tko/observable'
@@ -13,8 +21,6 @@ import type { ObservableArray } from '@tko/observable'
import { contextFor, applyBindingsToDescendants, AsyncBindingHandler } from '@tko/bind'
-import type { AllBindings } from '@tko/bind'
-
// Utilities
const MAX_LIST_SIZE = Number.MAX_SAFE_INTEGER
@@ -56,8 +62,7 @@ function makeTemplateNode(sourceNode) {
// For e.g. tags
parentNode = sourceNode.content
} else if (sourceNode.tagName === 'SCRIPT') {
- parentNode = document.createElement('div')
- parentNode.innerHTML = sourceNode.text
+ parentNode = parseHtmlForTemplateNodes(sourceNode.text, sourceNode.ownerDocument)
} else {
// Anything else e.g.
parentNode = sourceNode