LeafKit HTML-escaping is not working correctly when a template prints a collection (Array / Dictionary) via #(value). This can result in XSS, allowing potentially untrusted input to be rendered unescaped.
LeafKit attempts to escape expressions during serialization, but due to LeafData.htmlEscaped()'s implementation, when the escaped type's conversion to String is marked as .ambiguous (as it is the case for Arrays and Dictionaries), an unescaped self is returned.
Note: I recommend first looking at the POC, before taking a look at the details below, as it is simple. In the detailed, verbose analysis below, I explored the functions involved in more detail, in hopes that it will help you understand and locate this issue.
LeafSerializer's serialize private function below. This is where the leafData is .htmlEscaped(), and then serialized.https://github.com/vapor/leaf-kit/blob/8ff06839d8b3ddf74032d2ade01e3453eb556d30/Sources/LeafKit/LeafSerialize/LeafSerializer.swift#L60-L66
LeafData.htmlEscaped() method uses the LeafData.string computed property to convert itself to a string. Then, it calls the htmlEscaped() method on it. However, if the string conversion fails, notice that an unescaped, unsafe self is returned (line 324 below):https://github.com/vapor/leaf-kit/blob/8ff06839d8b3ddf74032d2ade01e3453eb556d30/Sources/LeafKit/LeafData/LeafData.swift#L321-L328
.string may return nil, if the escaped value is not a string already, a convesion is attempted, which may fail.https://github.com/vapor/leaf-kit/blob/8ff06839d8b3ddf74032d2ade01e3453eb556d30/Sources/LeafKit/LeafData/LeafData.swift#L211-L216
In this specific case, the conversion fails at line 303 below, when...
1.14.2Exploitability
AV:NAC:LAT:NPR:NUI:NVulnerable System
VC:NVI:NVA:NSubsequent System
SC:LSI:NSA:N6.9/CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:L/SI:N/SA:N