{"id":26581671,"url":"https://github.com/besworks/shadow-script","last_synced_at":"2025-03-23T07:31:10.321Z","repository":{"id":182215674,"uuid":"492366950","full_name":"besworks/shadow-script","owner":"besworks","description":"Test cases for various methods of injecting scripts into ShadowRoots.","archived":false,"fork":false,"pushed_at":"2025-03-16T23:26:57.000Z","size":17,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-17T00:26:12.434Z","etag":null,"topics":["eval","examples","injection","javascript","js","reseach","scope","shadowroot","testing","tests","webcomponents"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/besworks.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2022-05-15T02:13:52.000Z","updated_at":"2025-03-16T23:44:12.000Z","dependencies_parsed_at":"2023-07-19T06:41:52.556Z","dependency_job_id":null,"html_url":"https://github.com/besworks/shadow-script","commit_stats":null,"previous_names":["besworks/shadow-script"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/besworks%2Fshadow-script","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/besworks%2Fshadow-script/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/besworks%2Fshadow-script/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/besworks%2Fshadow-script/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/besworks","download_url":"https://codeload.github.com/besworks/shadow-script/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245071592,"owners_count":20556340,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["eval","examples","injection","javascript","js","reseach","scope","shadowroot","testing","tests","webcomponents"],"created_at":"2025-03-23T07:31:09.875Z","updated_at":"2025-03-23T07:31:10.308Z","avatar_url":"https://github.com/besworks.png","language":null,"readme":"# Injecting Scripts into ShadowRoots\n\nTest cases for various methods of injecting scripts into ShadowRoots.\n\n```html\n\u003cdiv id=\"host\"\u003e\u003c/div\u003e\n\u003cscript type=\"module\"\u003e\n  let host = document.querySelector('#host');\n  let shadow = host.attachShadow({ mode:'closed' });\n  let temp = document.createElement('template');\n  temp.innerHTML=`\u003cscript\u003e console.log(this);`;\n  shadow.append(document.importNode(temp.content, true));\n\u003c/script\u003e\n```\n\nThe above script would fail because scripts inserted via innerHTML are not evaluated. This is an expected security measure to prevent the arbitrary execution of scripts injected by malicious third parties. But sometimes, you need to bypass those protections to run trusted scripts inside of a ShadowRoot. Presented below are 5 methods to achieve this goal :\n\n- [Declared Templates](./examples/declared.html)\n- [Contextual Templates](./examples/contextual.html)\n- [Duplicated Scripts](./examples/duped.html)\n- [Spoofed Document](./examples/spoofed.html)\n- [Scoped Evaluation](./examples/scoped.html)\n\nThe first four of these methods do indeed execute scripts inside the ShadowRoot but they all suffer from one major drawback. The scripts are evaluated in the global scope with no access to the ShadowDOM they were injected into.\n\nThe fifth method actually allows us to set the scope of our script and gain access to the ShadowRoot. If used irresponsibly though, this technique can expose your component to script injection attacks.\n\n## Advanced Tests\n\nBelow are two detailed examples of using the **Scoped Evaluation** method. The first contains a demonstration of why you should never use this method with an open Shadow Root. The second demonstrates a safe use of this technique to evaulate trusted scripts embedded in the ShadowDOM of a Custom Element :\n\n- [Open ShadowRoot Eval (DANGEROUS!)](./examples/dangerous.html)\n- [Closed ShadowRoot Eval](./examples/closed.html)\n\n## Further Considerations\n\nUsing the **Closed ShadowRoot Eval** method above causes any script element added to the ShadowRoot to be evaluated on every `connectedCallback`. This might not always be ideal. You may want to do a boolean check to only process the scripts the first time the element is connected to the DOM.\n\n```javascript\n#initialized=false;\n\nconnectedCallback() {\n  !this.#initialized \u0026\u0026 this.#processScripts() \u0026\u0026 (this.#initialized=true);\n}\n```\n\nDo not inject untrusted markup into your ShadowRoot while using this technique, any script elements that content might contain will be executed. You will soon be able to use the [Element.setHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/setHTML) method of the upcoming [HTML Sanitizer API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Sanitizer_API) to import untrusted markup in a way guaranteed to be free of inline script elements. Until then, a safer method would be to specifically target the script element that you want to execute.\n\n```javascript\nget template() {\n  return `\u003cscript id=\"test\"\u003econsole.log(this);`;\n}\n\nconnectedCallback() {\n  const test = this.shadowRoot.querySelector('#test');\n  Function(test.innerHTML).bind(this.shadowRoot)();\n}\n```\n\n## Discussion\n\nFurther discussion on this topic is in https://github.com/WICG/webcomponents/issues/717#issuecomment-1126786185\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbesworks%2Fshadow-script","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbesworks%2Fshadow-script","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbesworks%2Fshadow-script/lists"}