From 918e9527c53c4e18be7f383a874c136537693e82 Mon Sep 17 00:00:00 2001
From: brendt
Date: Wed, 15 Jul 2026 12:50:32 +0200
Subject: [PATCH] wip
---
.../view/src/Attributes/ExpressionAttribute.php | 6 +++++-
tests/Integration/View/TempestViewRendererTest.php | 13 +++++++++++++
2 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/packages/view/src/Attributes/ExpressionAttribute.php b/packages/view/src/Attributes/ExpressionAttribute.php
index 0fd8209d8..67b9acc8d 100644
--- a/packages/view/src/Attributes/ExpressionAttribute.php
+++ b/packages/view/src/Attributes/ExpressionAttribute.php
@@ -70,7 +70,11 @@ public static function render(string $name, mixed $value): string
return '';
}
- $resolvedValue = self::resolveValue($value);
+ $resolvedValue = htmlspecialchars(
+ string: self::resolveValue($value),
+ flags: ENT_QUOTES | ENT_SUBSTITUTE,
+ encoding: 'UTF-8',
+ );
if ($resolvedValue === '') {
return '';
diff --git a/tests/Integration/View/TempestViewRendererTest.php b/tests/Integration/View/TempestViewRendererTest.php
index c0d5b6e5f..a162e8885 100644
--- a/tests/Integration/View/TempestViewRendererTest.php
+++ b/tests/Integration/View/TempestViewRendererTest.php
@@ -1000,4 +1000,17 @@ public function test_whitespace_with_blank_lines_between_inline_elements_is_pres
Test
'),
);
}
+
+ public function test_expressions_attributes_are_escaped(): void
+ {
+ $html = $this->view->render(view(
+ 'click',
+ v: 'x" onmouseover="alert(document.cookie)',
+ ));
+
+ $this->assertSnippetsMatch(
+ 'click',
+ $html,
+ );
+ }
}