Re: [webcomponents] More backward-compatible templates

A naive proposal: Can we introduce an alias of <script> element, like
<scr>, and ask template authors to use <scr> instead of <script> inside
<script template>?  Since <scr> is just an alias of <script>, authors can
use it even outside <script template>.

I guess it won't confuse neither the tokenizer nor existing parser, and it
will be polyfill-friendly. It isn't as clean as <template>, but not as ugly
as <+script> IMO. Obviously "scr" sounds ugly so it is great if it has some
unused sensible name.




On Thu, Nov 1, 2012 at 3:14 PM, Adam Barth <w3c@adambarth.com> wrote:

>
>
>
> On Thu, Nov 1, 2012 at 6:33 AM, Maciej Stachowiak <mjs@apple.com> wrote:
>
>>
>> On Nov 1, 2012, at 1:57 PM, Adam Barth <w3c@adambarth.com> wrote:
>>
>>
>>>
>> (5) The nested template fragment parser operates like the template
>>> fragment parser, but with the following additional difference:
>>>      (a) When a close tag named "+script" is encountered which does not
>>> match any currently open script tag:
>>>
>>
>> Let me try to understand what you've written here concretely:
>>
>> 1) We need to change the "end tag open" state to somehow recognize
>> "</+script>" as an end tag rather than as a bogus comment.
>> 2) When the tree builder encounter such an end tag in the ???? state(s),
>> we execute the substeps you've outlined below.
>>
>> The problem with this approach is that nested templates parse differently
>> than top-level templates.  Consider the following example:
>>
>> <script type=template>
>>  <b
>> </script>
>>
>> In this case, none of the nested template parser modifications apply and
>> we'll parse this as normal for HTML.  That means the contents of the
>> template will be "<b" (let's ignore whitespace for simplicity).
>>
>> <script type=template>
>>   <h1>Inbox</h1>
>>   <script type=template>
>>     <b
>>   </+script>
>>  </script>
>>
>> Unfortunately, the nested template in this example parses differently
>> than it did when it was a top-level template.  The problem is that the
>> characters "</+script>" are not recognized by the tokenizer as an end tag
>> because they are encountered by the nested template fragment parser in the
>> "before attribute name" state.  That means they get treated as some sort of
>> bogus attributes of the <b> tag rather than as an end tag.
>>
>>
>> OK. Do you believe this to be a serious problem? I feel like
>> inconsistency in the case of a malformed tag is not a very important
>> problem, but perhaps there are cases that would be more obviously
>> problematic, or reasons not obvious to me to be very concerned about cases
>> exactly like this one.
>>
>
> It's going to lead to subtle parsing bugs in web sites, which usually
> means security vulnerabilities.  :(
>
> Also: can you think of a way to fix this problem? Or alternately, do you
>> believe it's fundamentally not fixable? I've only spent a short amount of
>> time thinking about this approach, and I am not nearly as much an expert on
>> HTML parsing as you are.
>>
>
> I definitely see the appeal of trying to re-use <script> for templates.
>  Unfortunately, I couldn't figure out how to make it work sensibly with
> nested templates, which is why I ended up recommending that we use the
> <template> element.
>
> Another approach we considered was to separate out the "hide from legacy
> user agents" and the "define a template" operations.  That approach pushes
> you towards a design like
>
> <xmp>
>   <template>
>     <h1>Inbox</h1>
>     <template>
>       <h2>Folder</h2>
>     </template>
>   </template>
> </xmp>
>
> You could do the same thing with <script type=something>, but <xmp> is
> shorter (and currently unused).  This approach has a bunch of
> disadvantages, including being verbose and having some unexpected parsing:
>
> <xmp>
>   <template>
>     <div data-foo="<xmp>bar</xmp>">
>       This text is actually outside the template!
>     </div>
>   </template>
> </xmp>
>
> The <script type=template> has similar problems, of course:
>
> <script type=template>
>   <div data-foo="<script>bar</script>">
>     This text is actually outside the template!
>   </div>
> </script>
>
> Perhaps developers have a clearer understanding of such problems from
> having to escape </script> in JavaScript?
>
> All this goofiness eventually convinced me that if we want to support
> nested templates, we ought to use the usual nesting mechanics of HTML,
> which leads to a design like <template> that nests like a normal tag.
>
>           (a.i) Consume the token for the close tag named "+script".
>>>          (a.ii) Crate a DocumentFragment containing that parsed contents
>>> of the fragment.
>>>          (a.iii) [return to the parent template fragment parser] with
>>> the result of step (a.ii) with the parent parser to resume after the
>>> "+script" close tag.
>>>
>>>
>>> This is pretty rough and I'm sure I got some details wrong. But I
>>> believe it demonstrates the following properties:
>>> (B) Allows for perfect fidelity polyfills, because it will manifestly
>>> end the template in the same place that an unaware browser would close the
>>> <script> element.
>>> (C) Does not require multiple levels of escaping.
>>> (A) Can be implemented without changes to the core HTML parser (though
>>> you'd need to introduce a new fragment parsing mode).
>>>
>>
>> I suspect we're quibbling over "no true Scotsman" semantics here, but you
>> obviously need to modify both the HTML tokenizer and tree builder for this
>> approach to work.
>>
>>
>> In principle you could create a whole separate tokenizer and tree
>> builder. But obviously that would probably be a poor choice for a native
>> implementation compared to adding some flags and variable behavior. I'm not
>> even necessarily claiming that all the above properties are advantages, I
>> just wanted to show that there need not be a multi-escapting problem nor
>> necessarily scary complicated changes to the tokenizer states for <script>.
>>
>> I think the biggest advantage to this kind of approach is that it can be
>> polyfilled with full fidelity. But I am in no way wedded to this solution
>> and I am intrigued at the mention of other approaches with this property.
>> The others I know of (external source only, srcdoc like on iframe) seem
>> clearly worse, but there might be other bigger ones.
>>
>
> The xmp-like wrapper also can be polyfilled, as can approaches based on
> HTML comments or attributes.  There's a trade-off, however.  In the long
> view, it's not clear to me how important polyfillability is for a feature.
>  It certainly makes adoption easier in the short term, but if we constrain
> ourselves to designing only features that can be polyfilled at each step,
> we'll end up with a contorted platform.
>
>
>>  (D) Can be implemented with near-identical behavior for XHTML, except
>>> that you'd need an XML fragment parser.
>>>
>>
>> The downside is that nested templates don't parse the same as top-level
>> templates.
>>
>>
>> Indeed. That is in addition to the previously conceded downsides that the
>> syntax is somewhat less congenial.
>>
>> Another issue is that you've also introduced the following security risk:
>>
>> Today, the following line of JavaScript is safe to include in an inline
>> script tag:
>>
>> var x = "</+script><img onerror=alert(1)>";
>>
>> Because that line does not contain "</script>", the string "alert(1)"
>> will be treated as the contents of a string.  However, if that line is
>> included in an inline script inside of a template, the modifications of to
>> the parser above will mean that alert(1) will execute as JavaScript rather
>> than being treated as a string, introducing an XSS vector.
>>
>>
>> I don't follow. Can you give a full example of how this would be included
>> in a template and therefore be executed?
>>
>
> <script>
> x =  "</+script><img onerror=alert(1)>"; // This is safe, there is no
> script execution.
> </script>
>
> <script type=template id=a>
>    <script>
>     x =  "</+script><img onerror=alert(1)>"; // This is not safe, the
> alert(1) executes as script.
>   </+script>
> </script>
>
> <script>
> document.body.appendChild(document.getTemplateById("a").instantiate());
> </script>
>
> You should imagine, of course, the string not being written literally by
> the developer but instead generated on the server side by some code that
> knows how to escape strings for use in inline script tags (e.g., by
> escaping "\" and "</script").  It's certainly possible to defend against
> this XSS vector on the server, but it's one more XSS vector to worry about.
>
>  I hope this clarifies the proposal.
>>>
>>> Notes:
>>> - Just because it's described this way doesn't mean it has to be
>>> implemented this way - implementations could do template parsing in a
>>> single pass with HTML parsing if desired. I wrote it this way mainly to
>>> demonstrate the desired properties/
>>>
>>
>> I'm not sure how we'd be able to that without running multiple copies of
>> the tokenizer state machine in parallel.  The tokenizer states for the
>> template fragment parser aren't going to line up in any meaningful way with
>> the top-level tokenizer's search for an appropriate end tag to escape from
>> the script data states.
>>
>>
>> I'm pretty confident it's *possible* to do a one-pass version of this
>> algorithm, but I am not sure if it is easy, or if it is desirable.
>>
>> What I actually imagined (knowing much less about HTML parsing than you)
>> was that you'd enter a different tokenizer state after encountering a
>> <script template> than a <script>. But defining that state would be
>> challenging.
>>
>
> Sure, but you're going to need a 17x bigger tokenizer state machine
> because you'll need to track all 17 script data states for the top-level
> tokenizer at the same time as you're tracking all the states for the
> template fragment tokenzier.
>
> Adam
>
>


-- 
morrita

Received on Thursday, 1 November 2012 15:43:14 UTC