Releases: hershel-theodore-layton/sgml-stream
Hardystonite
This release ignores the "constant property" $this->tagName in ElementWithOpenTag... traits.
When upgrading, please try out Diopside v0.6.0 first. This release assumes you have upgraded and are free of warnings on v0.6.0.
This release removes the requirement to declare protected string $tagName = '...'; in classes that use the ElementWithOpenTag... traits. v0.6.0 uses this property, but emits a warning if the value does not match static::TAG_NAME. This release removes that behavior. All releases below v0.6.0 are not aware of the static::TAG_NAME constant, and always used $this->tagName without warnings. It is safe to remove protected string $tagName = '...'; from your classes. You may decide to keep them, but sgml-stream will ignore the value. This keeps your code compatible with v0.6.x and below. No warnings will be emitted on v0.7.0 if you declare an incorrect value.
Diopside
This release of sgml-stream drops support for hhvm < 4.102.
Breaking Change
A new abstract constant has been introduced to ElementWithOpenTagOnly, ElementWithOpenAndCloseTagsAndUnescapedContent, and ElementWithOpenAndCloseTags. If you are using these traits, you should declare these constants in such a way that they match the declaration of protected string $tagName = ...;.
final xhp class my_webcomponent extends HTMLStream\HTMLElementBase {
use SGMLStream\ElementWithOpenAndCloseTags;
protected string $tagName = 'my-webcomponent';
const string TAG_NAME = 'my-webcomponent';
}When this Element is rendered, it will emit an \E_USER_WARNING if the values do not match.
Warning: $this->tagName does not match static::TAG_NAME in YourNamespace\my_webcomponent.
- property value: 'something-else'
- constant value: 'my-webcomponent'
Assigning new values to $this->tagName is not supported.
The value of $this->tagName will be used, but future versions will use the value of the TAG_NAME class constant.
The behavior of the rendering does not change. So if you reassigned the property, the element is still rendered with the value of $this->tagName. If you are reassigning $this->tagName = 'something-else'; somewhere, I urge you to stop using the trait. A future version will ignore the value of the property and always use the value of the constant.
In short, this means that everyone who is able to use hhvm 4.102 or greater, should be able to use SGMLStream v0.6.0. The typechecker will point out all use sites where you are missing a constant declaration. Declaring const string TAG_NAME = 'SGML_STREAM_FIXME'; or something of that nature, would not change your rendered document.
Why now?
Before we open sourced SGMLStream, we used a class constant in-place of a property. These traits used to be abstract base classes, which have had support for abstract constants for ages. When open sourcing the library, we decided that a trait would do better, since it reduces the amount and complexity of inheritance hierarchies. Constant support in traits was very new at that time, so we decided to temporarily use a property instead. The current release of SGMLStream drops support for all versions of hhvm that do not support trait constants.
trait ElementWithOpenAndCloseTags {
require extends RootElement;
private bool $hasBeenStreamed = false;
/**
* For `<div>`, use `div`. For `<span>`, use `span`.
*/
<<_Private\UnstableAPI(
'This property is intended to mimic a constant. Constants in traits are '.
'supported since https://hhvm.com/blog/2021/02/16/hhvm-4.97.html.',
)>>
protected string $tagName;
/* ... */
}Sillimanite
This release requires sgml-stream-interfaces v0.5.0 (Sillimanite)
Depending on your use of sgml-stream and your understanding of the guard in ElementWithOpenAndCloseTagsAndUnescapedContent this release may be a SECURITY CRITICAL RELEASE for you.
The fix breaks backwards compatibility for documents which (ab)used the incorrect content guard implementation in ElementWithOpenAndCloseTagsAndUnescapedContent to break out of the tag (which html-stream uses in <style> and <script>).
This release fixes a bug in the ElementWithOpenAndCloseTagsAndUnescapedContent trait. The test for the fix can be found here.
The trait embeds a single unescaped child string into the output. This can break the document structure, since the content may contain an end tag. Previous versions tried to prevent this by making sure the child did not contain </tagname. The HTML spec states that </TagName, </TAGNAME, and even </TaGName should be interpreted as </tagname. The trait only guarded against the lowercase version.
// Given the following code:
$it = <html><style>{$untrusted_styles}</style></html>;
await $it->toHTMLStringAsync();
// $untrusted_styles can break out of the `<style>...</style>` like so:
// `</STYLE><marquee>like it is 1999</marquee><style>`This would be parsed like so:
<html>
<style></STYLE>
<marquee>like it is 1999</marquee>
<style></style>
</html>The marquee element is rendered and your page has been defaced. This is obviously not limited to just the marquee tag.
Rationale
Q: Why does this release state this " may be a SECURITY CRITICAL RELEASE for you"? This seems like a pretty serious security issue.
A: We agree that this is a pretty serious security issue. Tags using this trait may break the document structure and untrusted malicious children can embed arbitrary elements. This behavior is unexpected and the partial guard may have given users a false sense of security. It most certainly surprised me.
If you depended on this behavior, please see the sgml-stream documentation about ElementWithOpenAndCloseTagsAndUnescapedContent. It explicitly states that this guard is NOT a protection mechanism that should be relied upon.
ElementWithOpenAndCloseTagsAndUnescapedChildrenis used for<style>and<script>. You don't need to use it, unless you need to embed some RAWTEXT. This trait enforces that you have zero or one child. This child must be a string. This trait places this string between your tags without escaping it. It also attempts to prevent your content from accidentally closing the tag. This is NOT a protection mechanism that should be relied upon. SGML parsers are tricky and there might be cases that we simply didn't cover. We are no SGML parser writers and might have misinterpreted the spec, which leaves a door open to break out of the RAWTEXT state.
Q: Did you know this in advance? or alternatively Did you leave this in on purpose?
A: No, I have known about this for less than two hours. I have known that HTML is not sensitive to capitalization for years. It just didn't come to mind when writing the guard. I do indeed understand that the quote above makes you ask these questions. It is a far to uncanny description of today's events. I must assure you, this was not premeditated.
Footnote
Also note that xhp-lib's equivalent to ElementWithOpenAndCloseTagsAndUnescapedContent has no checks for closing tags are present.
Magnetite
This release adds the a new Element type, DissolvableElement.
This is a highly performant element type meant for pure elements which merely wrap a Streamable and add some behavior. This element type dissolves when the stream is being built, instead of at stream time. This reduces the amount of small strings and objects floating around in your program. For more information on this element type, see the element type docs and the docblock.
Ozone - patch 1
This release attempts to fix an extremely rare bug in AwaitableSnippet.
If you have been getting inexplicable SnippetNotPrimedExceptions on an very very very small fraction of requests, this release might be the solution to your troubles. On v0.3.0 and below there was a possible ordering of Awaitables which would make AwaitableSnippet call ->feedBytesToConsumerAsync() before calling ->primeAsync() on this snippet.
This ordering is as rare as hens' teeth and I couldn't repro it in a test. It required the internal Awaitable to resolve right before the call to ->feedBytesToConsumerAsync() was made. If there are any awaits on the execution path between the moment that it resolved and the call, this bug could not happen. It the method was called before the Awaitable resolved, this bug could not happen.
For more information about this, see 76f668a.
Very minor BC break
If your ->primeAsync() method is NOT an async method and throws an Exception synchronously, it is now caught. Exceptions thrown from within an async ->primeAsync() behave like they always did (bubbling up to the Renderer). I don't not consider this a breaking change worthy of a .y version bump.
Ozone
Oxygen - patch 1
Changes sgml-stream-interfaces requirement to ^0.2. This allows you to install this package directly. With dev-master you would have to depend on sgml-stream-interfaces directly, or set a minimum-stability requirement of dev.
No code changes.
Oxygen
This is the first release of sgml-stream.
This release contains the core parts of our internal implementation of sgml-stream. We are switching over to the open source implementation internally, but this may take a couple more days. Initial experiments / partial migrations to the open source implementation have been successful. This core is a partial reimplementation in some key areas. Our internal implementation was just to tightly bound to our own code. We don't expect weirdness to occur if you would start using v0.2 today. However, we don't have systematic test coverage yet. We do have some core xhp behaviors specified in open-xhp-specification. This mostly pins down attributes, since we previously behave(d) differently from xhp-lib in some areas. Once we've imported the open source sgml-stream fully, any weird behaviors will probably be noticed and ironed out quickly.