Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

TC39 proposal to identify tagged template string array objects

License

NotificationsYou must be signed in to change notification settings

tc39/proposal-array-is-template-object

Authors:@mikesamuel,@kotoChampions:@littledan,@ljharbReviewers:@erights,@jridgewell

Provides a way for template tag functions to tell whether they were called with a template string bundle.

Table of Contents

Use cases & Prior Discussions

Distinguishing strings from a trusted developer from strings that may be attacker controlled

IssueWICG/trusted-types#96 describes a scenario where a template tag assumes that the literal strings were authored by a trusted developer but that the interpolated values may not be.

result=sensitiveOperation`trusted0${untrusted} trusted1`// Authored by dev          ^^^^^^^^                ^^^^^^^^// May come from outside                ^^^^^^^^^

This proposal would provide enough context to warn or error out when this is not the case.

function(trustedStrings, ...untrustedArguments){if(Reflect.isTemplateObject(trustedStrings)// instanceof provides a same-Realm guarantee for early frozen objects.&&trustedStringsinstanceofArray){// Proceed knowing that trustedStrings come from// the JavaScript module's authors.}else{// Do not trust trustedStrings}}

This assumes that an attacker cannot get a string toeval ornew Function as in

constattackerControlledString='((x) => x)`evil string`';// Naive codeletx=eval(attackerControlledString)console.log(Reflect.isTemplateObject(x));

Many other security assumptions break if an attacker can execute arbitrary code, so this check is still useful.

An Example

Here's an example of howisTemplateObject lets a tag function wisely use a sensitive operation, namelyCreate a Trusted Type.The sensitive operation is not directly accessible to the tag function's callers since it's in a local scope.This assumes that TT'sfirst-come-first-serve name restrictions solve provisioning, letting only authorized callers access the sensitive operation.

const{ Array, Reflect, TypeError}=globalThis;const{ createPolicy}=trustedTypes;const{ isTemplateObject}=Reflect;const{error:consoleErr}=console;/** * A tag function that produces *TrustedHTML* or null if the * policy name "trustedHTMLTagFunction" is not available. */exporttrustedHTML=(()=>{// We use TrustedType's first-come-first-serve policy name restrictions// to provision this scope with sensitiveOperation.constpolicyName='trustedHTMLTagFunction';letpolicy;try{policy=createPolicy('trustedHTMLTagFunction',{createHTML(s){returns}});}catch(ex){consoleErr(`${policyName} is not an allowed trustedTypes policy name`);returnnull;}// This is the sensitive operation.const{ createHTML}=policy;// This tag function uses isTemplateObject to reject strings that// do not appear in user code in the same realm.//// With a reliable isTemplateObject check, the attack surface is// <= |set of template applications in trusted code|.//// That set is finite.//// Without a reliable isTemplateObject check, the attack surface is// <= |set of attacker controlled strings|.  That is, in practice,// unbounded.//// This assumes no attacker has eval.consttrustedHTMLTagFunction=(strings)=>{if(isTemplateObject(strings)&&stringsinstanceofArray){returncreateHTML(strings.raw[0]);}thrownewTypeError("Expected template object");};// With the check it's safe to export this tag function that closes// over a sensitive operation to anyone.returntrustedHTMLTagFunction;})()

WithoutisArrayTemplate, this can be bypassed:

// A naive, but non-malicious function.functionf(x){// People trust trustedHTMLTagFunction.// Our HTML is trustworthy because <bad argument> so we'll just// piggyback off that by using a value that looks like a template object.// What could possibly go wrong?consts=dodgyMarkdownToHTMLConverter(x);constpseudoTemplateObject=[s];pseudoTemplateObject.raw=Object.freeze([s]);returntrustedHTML(Object.freeze(pseudoTemplateObject));}// An attacker controlled string reaches f().constpayload='<img onerror=alert(document.origin) src=x>';console.log(`f(${JSON.stringify(payload)}) =${f(payload)}`);

The threat model here involves three actors:

  • A team offirst-party developers (in conjunction with security specialists) decides to trust the tag function.
  • A maliciousattacker controls a string in the variablepayload.
  • Non-malicious but confusable third-party library tries to provide a higher level of service by forging a template object.It assumes its clients are comfortable with trustingdodgyMarkdownToHTMLConverter to produce HTML for the current origin.

We've addressed this threat model when the first-party developers can be less tolerant of risk than the most risk tolerant third party dependency w.r.t. HTML injection.

This simple implementation doesn't deal with interpolations.A more thorough implementation could docontextual autoescaping.

What this is not

This is not an attempt to determine whether the current function was called as a template literal.See the linked issue as to why that is untenable. Especially the discussion around threat models,eval, and tail-call optimizations that weighed against alternate approaches.

Possible Spec Language

You can browse theecmarkup output or browse thesource.

Tests

The test262draft testswhich would be added undertest/built-ins/Reflect

Related Work

If theliterals proposal were to advance, this proposal would be unnecessary since they both cover the use cases from this document.

About

TC39 proposal to identify tagged template string array objects

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Contributors8


[8]ページ先頭

©2009-2025 Movatter.jp