ui: start template safety cleanup
This commit is contained in:
@@ -2,14 +2,14 @@
|
||||
|
||||
## Current
|
||||
|
||||
### `feat/frontend-xss-hardening`
|
||||
### `feat/frontend-template-safety-cleanup`
|
||||
|
||||
Status: in_progress
|
||||
|
||||
DoD:
|
||||
- API-provided values are not rendered into `innerHTML` on user-facing admin pages
|
||||
- error strings from API responses are rendered through text nodes
|
||||
- streaming admin pages use DOM construction instead of HTML string concatenation for dynamic cards
|
||||
- local UI helpers do not use `innerHTML` for static SVG or icon insertion
|
||||
- wizard shell and workspace shell use DOM-based icon and fragment construction
|
||||
- remaining HTML-bearing i18n/template paths are explicitly isolated and tracked
|
||||
|
||||
## Next
|
||||
|
||||
|
||||
+17
-1
@@ -18,6 +18,16 @@ function tfKey(key, vars) {
|
||||
return window.tf ? tf(key, vars) : tKey(key);
|
||||
}
|
||||
|
||||
function buildIconSvg(href, width, height) {
|
||||
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
svg.setAttribute('width', String(width));
|
||||
svg.setAttribute('height', String(height));
|
||||
var use = document.createElementNS('http://www.w3.org/2000/svg', 'use');
|
||||
use.setAttribute('href', href);
|
||||
svg.appendChild(use);
|
||||
return svg;
|
||||
}
|
||||
|
||||
function mapKeyRecord(record) {
|
||||
var apiKey = record.api_key || {};
|
||||
return {
|
||||
@@ -310,7 +320,13 @@ document.getElementById('copy-key-btn').addEventListener('click', function() {
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(value).catch(function() {});
|
||||
}
|
||||
this.innerHTML = '<svg width="13" height="13"><use href="' + (window.APP_BASE || '') + 'icons/general/check.svg#icon"/></svg>';
|
||||
this.replaceChildren(
|
||||
buildIconSvg(
|
||||
(window.APP_BASE || '') + 'icons/general/check.svg#icon',
|
||||
13,
|
||||
13
|
||||
)
|
||||
);
|
||||
if (window.CrankUi) {
|
||||
window.CrankUi.info(
|
||||
tKey('apikeys.toast.copy_message'),
|
||||
|
||||
+22
-5
@@ -12,6 +12,16 @@ function tfKey(key, vars) {
|
||||
return typeof tf === 'function' ? tf(key, vars) : key;
|
||||
}
|
||||
|
||||
function buildIconSvg(href, width, height) {
|
||||
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
svg.setAttribute('width', String(width));
|
||||
svg.setAttribute('height', String(height));
|
||||
var use = document.createElementNS('http://www.w3.org/2000/svg', 'use');
|
||||
use.setAttribute('href', href);
|
||||
svg.appendChild(use);
|
||||
return svg;
|
||||
}
|
||||
|
||||
function workspaceColor(index) {
|
||||
return ['#0d9488', '#7c3aed', '#0891b2', '#f59e0b', '#2563eb'][index % 5];
|
||||
}
|
||||
@@ -139,10 +149,11 @@ function renderWorkspaceList() {
|
||||
item.appendChild(info);
|
||||
|
||||
if (active) {
|
||||
var check = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
check.setAttribute('width', '12');
|
||||
check.setAttribute('height', '12');
|
||||
check.innerHTML = '<use href="' + (window.APP_BASE || '') + 'icons/general/check.svg#icon"></use>';
|
||||
var check = buildIconSvg(
|
||||
(window.APP_BASE || '') + 'icons/general/check.svg#icon',
|
||||
12,
|
||||
12
|
||||
);
|
||||
item.appendChild(check);
|
||||
}
|
||||
|
||||
@@ -160,7 +171,13 @@ function renderWorkspaceList() {
|
||||
var dd = document.getElementById('ws-dropdown');
|
||||
if (dd) dd.style.display = 'none';
|
||||
});
|
||||
manageLink.innerHTML = '<svg width="14" height="14"><use href="' + (window.APP_BASE || '') + 'icons/general/settings.svg#icon"/></svg>';
|
||||
manageLink.appendChild(
|
||||
buildIconSvg(
|
||||
(window.APP_BASE || '') + 'icons/general/settings.svg#icon',
|
||||
14,
|
||||
14
|
||||
)
|
||||
);
|
||||
manageLink.appendChild(document.createTextNode(tKey('settings.ws.title')));
|
||||
list.appendChild(manageLink);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user