diff --git a/src/Tracy/Bar/assets/bar.js b/src/Tracy/Bar/assets/bar.js
index f7f1dc546..d64c677cd 100644
--- a/src/Tracy/Bar/assets/bar.js
+++ b/src/Tracy/Bar/assets/bar.js
@@ -41,7 +41,8 @@ class Panel {
let elem = this.elem;
this.init = function () {};
- elem.innerHTML = elem.tracyContent = elem.dataset.tracyContent;
+ elem.tracyContent = elem.dataset.tracyContent;
+ elem.innerHTML = Tracy.trusted.html(elem.tracyContent);
delete elem.dataset.tracyContent;
Tracy.Dumper.init(Debug.shadow);
evalScripts(elem);
@@ -164,7 +165,7 @@ class Panel {
doc.body.id = 'tracy-debug';
let script = doc.createElement('script');
- script.src = baseUrl + '_tracy_bar=js&XDEBUG_SESSION_STOP=1';
+ script.src = Tracy.trusted.scriptURL(baseUrl + '_tracy_bar=js&XDEBUG_SESSION_STOP=1');
script.async = true;
script.addEventListener('load', () => {
win.Tracy.adoptStyleSheets(doc, ['shared', 'bar']);
@@ -173,10 +174,10 @@ class Panel {
doc.head.appendChild(script);
let meta = this.elem.parentElement.lastElementChild;
- doc.body.innerHTML = '
'
+ doc.body.innerHTML = Tracy.trusted.html('
'
+ '
' + this.elem.tracyContent + '
'
+ meta.outerHTML
- + '
';
+ + '
');
evalScripts(doc.body);
if (this.elem.querySelector('h1')) {
doc.title = this.elem.querySelector('h1').textContent;
@@ -417,7 +418,7 @@ class Debug {
// #tracy-debug wrapper is kept for backward compatibility of third-party panel CSS
Debug.layer = document.createElement('tracy-div');
Debug.layer.setAttribute('id', 'tracy-debug');
- Debug.layer.innerHTML = content;
+ Debug.layer.innerHTML = Tracy.trusted.html(content);
shadow.appendChild(Debug.layer);
(document.body || document.documentElement).appendChild(host);
@@ -467,9 +468,9 @@ class Debug {
});
}
- Debug.layer.insertAdjacentHTML('beforeend', content.panels);
+ Debug.layer.insertAdjacentHTML('beforeend', Tracy.trusted.html(content.panels));
evalScripts(Debug.layer);
- Debug.bar.elem.insertAdjacentHTML('beforeend', content.bar);
+ Debug.bar.elem.insertAdjacentHTML('beforeend', Tracy.trusted.html(content.bar));
let ajaxBar = Debug.bar.elem.querySelector('.tracy-row:last-child');
Debug.shadow.querySelectorAll('.tracy-panel').forEach((panel) => {
@@ -556,7 +557,7 @@ class Debug {
Debug.scriptElem.remove();
}
Debug.scriptElem = document.createElement('script');
- Debug.scriptElem.src = url;
+ Debug.scriptElem.src = Tracy.trusted.scriptURL(url);
(document.body || document.documentElement).appendChild(Debug.scriptElem);
}
}
@@ -567,7 +568,7 @@ function evalScripts(elem) {
if ((!script.hasAttribute('type') || script.type === 'text/javascript' || script.type === 'application/javascript') && !script.tracyEvaluated) {
let document = script.ownerDocument;
let dolly = document.createElement('script');
- dolly.textContent = script.textContent;
+ dolly.textContent = Tracy.trusted.script(script.textContent);
(document.body || document.documentElement).appendChild(dolly);
dolly.remove();
script.tracyEvaluated = true;
diff --git a/src/Tracy/BlueScreen/assets/bluescreen.js b/src/Tracy/BlueScreen/assets/bluescreen.js
index 7249b0dd3..ec2a302ca 100644
--- a/src/Tracy/BlueScreen/assets/bluescreen.js
+++ b/src/Tracy/BlueScreen/assets/bluescreen.js
@@ -105,7 +105,7 @@ class BlueScreen {
if (host) {
host.remove();
}
- document.body.insertAdjacentHTML('beforeend', content);
+ document.body.insertAdjacentHTML('beforeend', Tracy.trusted.html(content));
BlueScreen.init(true);
}
}
diff --git a/src/Tracy/Dumper/assets/dumper.js b/src/Tracy/Dumper/assets/dumper.js
index e6e792f3c..2e0fabdc1 100644
--- a/src/Tracy/Dumper/assets/dumper.js
+++ b/src/Tracy/Dumper/assets/dumper.js
@@ -341,7 +341,7 @@ function createEl(el, attrs, content) {
}
if (content && content.html !== undefined) {
- el.innerHTML = content.html;
+ el.innerHTML = Tracy.trusted.html(content.html);
return el;
}
diff --git a/src/Tracy/assets/helpers.js b/src/Tracy/assets/helpers.js
index fdb161f2e..3aa715ae4 100644
--- a/src/Tracy/assets/helpers.js
+++ b/src/Tracy/assets/helpers.js
@@ -18,6 +18,26 @@ let Tracy = window.Tracy = window.Tracy || {};
// returns the real event target even when the event crossed a shadow DOM boundary
Tracy.retarget = (e) => e.composedPath()[0] ?? e.target;
+// passes Tracy's own markup and scripts to sinks that a page enforcing Trusted Types
+// (require-trusted-types-for 'script') refuses plain strings at
+Tracy.trusted = Tracy.trusted || (() => {
+ let policy;
+ try {
+ policy = window.trustedTypes?.createPolicy('tracy', {
+ createHTML: (s) => s,
+ createScript: (s) => s,
+ createScriptURL: (s) => s,
+ });
+ } catch {
+ // the page's trusted-types directive doesn't allow the name
+ }
+ return {
+ html: (s) => policy ? policy.createHTML(s) : s,
+ script: (s) => policy ? policy.createScript(s) : s,
+ scriptURL: (s) => policy ? policy.createScriptURL(s) : s,
+ };
+})();
+
// registry of CSS sources filled by the loader (DeferredContent, Dumper::renderAssets)
Tracy.css = Tracy.css || {};