diff --git a/packages/web-extension/package.json b/packages/web-extension/package.json
index 1f1897b..f59f000 100644
--- a/packages/web-extension/package.json
+++ b/packages/web-extension/package.json
@@ -1,6 +1,6 @@
{
"name": "carbon-devtools-v10",
- "version": "2.8.4",
+ "version": "2.8.6",
"private": true,
"description": "A basic set of tools for teams building live Carbon pages for Carbon v10.",
"main": "dist/manifest.json",
diff --git a/packages/web-extension/src/inject/components/Highlight/index.js b/packages/web-extension/src/inject/components/Highlight/index.js
index 913da22..c59be05 100644
--- a/packages/web-extension/src/inject/components/Highlight/index.js
+++ b/packages/web-extension/src/inject/components/Highlight/index.js
@@ -80,8 +80,12 @@ function addHighlight(component, options = {}) {
comp.width >= contentMax &&
comp.height >= contentMax
) {
- highlight.innerHTML = `${options.content}`;
- setContenSize(highlight, highlight.querySelector('span'));
+ // Create span element safely
+ const span = document.createElement('span');
+ span.textContent = options.content;
+ highlight.textContent = '';
+ highlight.appendChild(span);
+ setContenSize(highlight, span);
}
}
}
@@ -113,7 +117,7 @@ function removeHighlight(component) {
component.setAttribute('class', '');
component.setAttribute('style', '');
component.setAttribute('data-highlightid', '');
- component.innerHTML = '';
+ component.textContent = '';
}
function removeAllHighlights() {
diff --git a/packages/web-extension/src/inject/components/Specs/Dependencies/index.js b/packages/web-extension/src/inject/components/Specs/Dependencies/index.js
index 8bd6311..06a914b 100644
--- a/packages/web-extension/src/inject/components/Specs/Dependencies/index.js
+++ b/packages/web-extension/src/inject/components/Specs/Dependencies/index.js
@@ -14,6 +14,19 @@ const selectors = Object.keys(allComponents).join(',');
const specsDependenciesClass = `${prefix}--specs-dependencies-tooltip`;
+function escapeHTML(value) {
+ return String(value).replace(/[&<>"']/g, (char) => {
+ const escapeMap = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": ''',
+ };
+ return escapeMap[char];
+ });
+}
+
function highlightSpecsDependencies(target) {
let componentName;
let componentIdentified = false;
@@ -32,7 +45,7 @@ function highlightSpecsDependencies(target) {
componentName = siblings.pop(); // pull off the last item for point name
dependencies = findAllDomShadow(selectors, target); // get dependencies
- tooltipContent += `
${componentName}
`;
+ tooltipContent += `${escapeHTML(componentName)}
`;
// manage siblings
if (siblings.length) {
@@ -84,7 +97,7 @@ function highlightSpecsDependencies(target) {
if (dependencyName && unique.indexOf(dependencyName) < 0) {
unique.push(dependencyName);
- tooltipContent += `${dependencyName}`;
+ tooltipContent += `${escapeHTML(dependencyName)}`;
}
}
@@ -97,7 +110,7 @@ function highlightSpecsDependencies(target) {
if (unique.length > 0) {
tooltipContent = tooltipContent.replace(
//g,
- `${unique.length} `
+ `${escapeHTML(unique.length)} `
);
}
}
diff --git a/packages/web-extension/src/inject/components/Specs/Ratio/index.js b/packages/web-extension/src/inject/components/Specs/Ratio/index.js
index a05ffff..9db9a32 100644
--- a/packages/web-extension/src/inject/components/Specs/Ratio/index.js
+++ b/packages/web-extension/src/inject/components/Specs/Ratio/index.js
@@ -10,6 +10,19 @@ import {
const { prefix } = settings;
+function escapeHTML(value) {
+ return String(value).replace(/[&<>"']/g, (char) => {
+ const escapeMap = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": ''',
+ };
+ return escapeMap[char];
+ });
+}
+
const aspectRatiosCalc = aspectRatios.map((ratio) => {
const vals = ratio.split(':');
return vals[0] / vals[1];
@@ -42,7 +55,7 @@ function highlightSpecsRatio(target) {
tooltipContent += `${width}x${height}`;
}
- tooltipContent += `${componentName}`;
+ tooltipContent += `${escapeHTML(componentName)}`;
addHighlight(target, { ...highlightOptions, type: 'specs' });
updateTooltipContent(tooltipContent);
diff --git a/packages/web-extension/src/inject/components/Specs/Spacing/index.js b/packages/web-extension/src/inject/components/Specs/Spacing/index.js
index 9ee8bb1..414e280 100644
--- a/packages/web-extension/src/inject/components/Specs/Spacing/index.js
+++ b/packages/web-extension/src/inject/components/Specs/Spacing/index.js
@@ -165,11 +165,16 @@ function positionSpacer(
spacer.dataset.component = componentName;
if (value >= 16) {
- spacer.innerHTML = `${value}`;
+ // Create span element safely
+ const span = document.createElement('span');
+ span.className = 'value';
+ span.textContent = value;
+ spacer.textContent = '';
+ spacer.appendChild(span);
if (value < spacer.querySelector('.value').offsetWidth) {
// if the text doesn't fit in the box then let's remove the text
- spacer.innerHTML = '';
+ spacer.textContent = '';
}
}
@@ -300,7 +305,7 @@ function resetAllSpacers() {
}
function resetSpacer(spacer) {
- spacer.innerHTML = ``;
+ spacer.textContent = '';
spacer.style.width = null;
spacer.style.height = null;
spacer.style.minHeight = null;
diff --git a/packages/web-extension/src/inject/components/Tooltip/index.js b/packages/web-extension/src/inject/components/Tooltip/index.js
index 2b7feff..d46490f 100644
--- a/packages/web-extension/src/inject/components/Tooltip/index.js
+++ b/packages/web-extension/src/inject/components/Tooltip/index.js
@@ -19,10 +19,19 @@ function initTooltip() {
const tooltipHTML = document.createElement('div');
tooltipHTML.classList.add(tooltipClass);
tooltipHTML.setAttribute('data-floating-menu-direction', 'top');
- tooltipHTML.innerHTML = `
-
-
- `;
+
+ // Create caret span
+ const caret = document.createElement('span');
+ caret.classList.add(`${tooltipClass}__caret`);
+
+ // Create content div
+ const content = document.createElement('div');
+ content.classList.add(`${tooltipClass}__content`);
+ content.setAttribute('tabindex', '-1');
+ content.setAttribute('role', 'dialog');
+
+ tooltipHTML.appendChild(caret);
+ tooltipHTML.appendChild(content);
devtoolsContainer.appendChild(tooltipHTML);
}
}
@@ -30,7 +39,11 @@ function initTooltip() {
function updateTooltipContent(content) {
const tooltipContent = body.querySelector('.' + tooltipContentClass);
- tooltipContent.innerHTML = content;
+ // Use template element for HTML parsing; callers must escape untrusted dynamic text.
+ const template = document.createElement('template');
+ template.innerHTML = String(content);
+ tooltipContent.textContent = '';
+ tooltipContent.appendChild(template.content);
}
function positionTooltip(component) {
@@ -150,24 +163,33 @@ function showHideTooltip(show) {
}
}
+function escapeHTML(value) {
+ return String(value)
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+}
+
function __specValueItem(type, value) {
let html;
if (type === 'warning') {
html = `
- ${value}
+ ${escapeHTML(value)}
`;
} else {
html = ``;
if (type) {
- html += ``;
+ html += ``;
}
if (value) {
- html += `${value}
`;
+ html += `${escapeHTML(value)}
`;
}
html += ``;
@@ -190,11 +212,11 @@ function __specsContainer(groups) {
groupsContent += `