-
Notifications
You must be signed in to change notification settings - Fork 41
Add setCSPTrustedTypesPolicy()
for CSP trusted types.
#81
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
👋 Hello and thanks for pinging us! This issue or PR has been added to our inbox and a Design Infrastructure first responder will review it soon.
|
I believe this PR still needs:
I could also use thoughts on how to let the callback reject a response. Is it alright to let it throw an error? Should we add anything to the existing error handler for that? Please let me know if 1) it also needs anything else, and 2) any thoughts I might want to keep in mind before writing those? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it make more sense here to pass the whole policy to the element (perhaps IncludeFragmentElement.trustedPolicy
could be a writable field) and then have the code call createHTML
on that? We could also pass the response object as a second arg for additional verification perhaps?
I think this is a reasonable approach, given that there is an accommodation for args in the API: https://developer.mozilla.org/en-US/docs/Web/API/TrustedTypePolicy/createHTML It might mean that the site ends up with more policies than otherwise, as the policy for Let me see how that would look. |
There seems to be at least one fundamental tradeoff, which is that |
[CSP trusted types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/trusted-types) is an API that allows a website to reduce the possibility of XSS by controlling what kind of content can be placed in a "sink" like `.innerHTML`. This commit introduces a flexible callback that allows the calling code to provide its own validation or rejection of an server response for an `<include-fragment-element>`. For example, the site may want to allow the server to send a header to assert that certain HTML is sanitized and safe to use as-is, or the site may want to run the response through a sanitizer. Here is a snippet that looks for such a header and falls back to the `dompurify` library for extremely basic sanitization. ```ts import { setCSPTrustedTypesCallback } from "include-fragment-element"; import { default as DOMPurify } from "dompurify"; const policy = trustedTypes.createPolicy("server-sanitized", { createHTML: (s) => s }); setCSPTrustedTypesCallback(async (r: Response) => { if (r.headers.get("X-Response-Trusted-Types")?.split(",").includes("server-sanitized=true")) { return policy.createHTML(r.text()); } return DOMPurify.sanitize(await r.text(), { RETURN_TRUSTED_TYPE: true }); }); ```
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This LGTM 👍
setCSPTrustedTypesCallback
for CSP trusted types.setCSPTrustedTypesPolicy()
for CSP trusted types.
src/index.ts
Outdated
} | ||
|
||
// TODO: find another way to make this available for testing. | ||
;(globalThis as any).setCSPTrustedTypesPolicy = setCSPTrustedTypesPolicy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test doesn't actually have a way to use exported functions. Do you have a recommendation for how to get access to setCSPTrustedTypesPolicy
from test.js
without this hack?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test suite should be able to import functions. I'd like us to avoid attaching globals (that aren't the elements themselves).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you point me to the mechanism/syntax for importing functions from the test? I only see the test config somehow importing the file separately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apologies I thought we had updated to web-test-runner
. I've now done so in #86 so if you rebase it should be evident how to import the function now, as there is an import
statement at the top of the file.
Just realized this was still marked as approved/reviewed, despite substantial revisions and pending questions. I hope I've marked it correctly now to request another review (certainly no rush, though, given this is late on Friday. :-P). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll let web systems provide specific code feedback but we were able to test this for our use case in trusted types and it worked as expected 🎉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good on behalf of @github/primer-reviewers
Thanks! I still have a few open questions in the comments that I'd love to resolve before merging; could I ask for any thoughts on those? |
src/index.ts
Outdated
@@ -45,8 +62,9 @@ export default class IncludeFragmentElement extends HTMLElement { | |||
this.setAttribute('accept', val) | |||
} | |||
|
|||
// TODO: Should this return a TrustedHTML if available, or always a string? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could use advice on this. On the one hand, it would be less surprising (and backwards-compatible) to always return a string.
However, that string can't be assigned to another .innerHTML
without going through a CSP trusted types policy again.
(Copied from an older comment.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can always mark this as a breaking change and return TrustedHTML
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We discussed this in team pairing and replaced it with a comment, because we're uncertain whom this might break and we don't need it right now. Does that sound alright to you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm largely in favour of this change. I have some small nitpicks, and I think it would be worth us making a breaking change here, to allow for the change in types.
src/index.ts
Outdated
@@ -45,8 +62,9 @@ export default class IncludeFragmentElement extends HTMLElement { | |||
this.setAttribute('accept', val) | |||
} | |||
|
|||
// TODO: Should this return a TrustedHTML if available, or always a string? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can always mark this as a breaking change and return TrustedHTML
Co-authored-by: Matt Langlois <fletchto99@github.com> Co-authored-by: Lucas Garron <lgarron@github.com>
Made two changes based on feedback.
@keithamus we've adjusted the PR to address your feedback above. Mind taking one last pass before we merge in? |
Since the last commit after approval was the requested documentation changes, I'm gonna go ahead and merge and then prep a version bump PR! |
CSP trusted types is an API that allows a website to reduce the possibility of XSS by controlling what kind of content can be placed in a "sink" like
.innerHTML
. Since<include-fragment-element>
is essentially a fancy.innerHTML
convenience, it needs changes to be compatible with CSP trusted types.This PR introduces a flexible callback that allows the calling code to provide its own sanitization or rejection of a server response for an
<include-fragment-element>
. For example, the site may want to allow the server to send a header to assert that certain HTML is sanitized and safe to use as-is, or the site may want to run the response through a sanitizer.See the
README.md
changes for snippets making use of this change: https://github.com/github/include-fragment-element/pull/81/files#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5