Found while reviewing #469 (fixed in #480), which now reports the drop but does not change it.
ElementAndAttributePolicyBasedSanitizerPolicy keeps an open-element entry for every element it emits, and gates text by the nearest kept entry. When an ElementPolicy renames an element into one whose content HtmlStreamRenderer writes literally, such as style, the renderer refuses every tag that arrives inside it, but the policy does not know that: it pushes the nested element as kept, opens the text gate for it, and emits its text, which the renderer appends to the literal content.
PolicyFactory f = new HtmlPolicyBuilder()
.allowElements((name, attrs) -> "style", "div")
.allowElements("b", "style") // no allowTextIn("style")
.toFactory();
f.sanitize("<div>a<b>bold</b>c</div>");
// <style>bold</style>
a and c are dropped, because text was not allowed in style, but bold reaches the stylesheet as raw text through the gate the policy opened for b. The closeability check in the renderer still refuses </style and comment tricks in that text, so it stays inside the element, and the only route to it is a policy that renames an element into a raw-text one, so this is a policy-author mistake rather than a bypass. But text lands where the author said no text may go, and the policy's stack and the renderer's disagree about what is open for the rest of that element.
Suggested fix: while a kept literal-content element is open, the policy should treat a nested start tag as dropped (deferOpenTag), whether or not text is allowed in the literal element, so that the text inside it follows the literal element's own gate and the reporter sees the drop as the policy's. That needs a flag for "inside a kept literal-content element" separate from the current one, which is set only when text is allowed there. Then the renderer's literal-content drop site would be unreachable through the library policy and remain as defense for other callers.
Reproduced on main at fad01c1.
Found while reviewing #469 (fixed in #480), which now reports the drop but does not change it.
ElementAndAttributePolicyBasedSanitizerPolicykeeps an open-element entry for every element it emits, and gates text by the nearest kept entry. When anElementPolicyrenames an element into one whose contentHtmlStreamRendererwrites literally, such asstyle, the renderer refuses every tag that arrives inside it, but the policy does not know that: it pushes the nested element as kept, opens the text gate for it, and emits its text, which the renderer appends to the literal content.aandcare dropped, because text was not allowed instyle, butboldreaches the stylesheet as raw text through the gate the policy opened forb. The closeability check in the renderer still refuses</styleand comment tricks in that text, so it stays inside the element, and the only route to it is a policy that renames an element into a raw-text one, so this is a policy-author mistake rather than a bypass. But text lands where the author said no text may go, and the policy's stack and the renderer's disagree about what is open for the rest of that element.Suggested fix: while a kept literal-content element is open, the policy should treat a nested start tag as dropped (
deferOpenTag), whether or not text is allowed in the literal element, so that the text inside it follows the literal element's own gate and the reporter sees the drop as the policy's. That needs a flag for "inside a kept literal-content element" separate from the current one, which is set only when text is allowed there. Then the renderer's literal-content drop site would be unreachable through the library policy and remain as defense for other callers.Reproduced on
mainat fad01c1.