diff --git a/docs/index.html b/docs/index.html index fee36a6..d0f12f6 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1537,6 +1537,14 @@

AI Security Standards Crosswalk

return div.innerHTML; } + // Only a real DOM node may be appended. Anything else — most importantly a + // string that reached here from the URL — becomes a text node, so it is + // rendered as text and can never be parsed as markup. + function toNode(value) { + if (value && typeof value.nodeType === 'number') return value; + return document.createTextNode(String(value)); + } + function el(tag, attrs, children) { var node = document.createElement(tag); if (attrs) { @@ -1549,8 +1557,8 @@

AI Security Standards Crosswalk

} if (children) { if (typeof children === 'string') node.textContent = children; - else if (Array.isArray(children)) children.forEach(function(c) { if (c) node.appendChild(c); }); - else node.appendChild(children); + else if (Array.isArray(children)) children.forEach(function(c) { if (c) node.appendChild(toNode(c)); }); + else node.appendChild(toNode(children)); } return node; } @@ -1674,6 +1682,31 @@

AI Security Standards Crosswalk

'/review': renderReview }; + // Resolve a control id supplied in the URL to the canonical string held in + // our own data, searching the same three places renderControlDetail reads: + // the framework registry, the backlink index, then the mappings in DATA. + // Returns null when nothing matches. Callers render the returned value + // rather than the URL, so nothing user-supplied is ever put on the page — + // a control id that matches nothing could only have produced an empty page + // echoing the URL back anyway. + function resolveControlId(fwName, controlId) { + var regFw = Object.prototype.hasOwnProperty.call(FW_REGISTRY_MAP, fwName) ? FW_REGISTRY_MAP[fwName] : null; + if (regFw) { + var regControl = (regFw.controls || []).find(function(c) { return c.control_id === controlId; }); + if (regControl) return regControl.control_id; + } + var blKey = fwName + '::' + controlId; + var bl = Object.prototype.hasOwnProperty.call(BACKLINK_MAP, blKey) ? BACKLINK_MAP[blKey] : null; + if (bl) return bl.control_id; + var found = null; + DATA.forEach(function(e) { + (e.mappings || []).forEach(function(m) { + if (found === null && m.framework === fwName && m.control_id === controlId) found = m.control_id; + }); + }); + return found; + } + function getRoute() { var hash = window.location.hash || '#/'; // Extract the path part (before any query params) @@ -1709,9 +1742,20 @@

AI Security Standards Crosswalk

if (fwControlMatch) { var fwNameCtrl = decodeURIComponent(fwControlMatch[1]); var ctrlId = decodeURIComponent(fwControlMatch[2]); - renderControlDetail(app, fwNameCtrl, ctrlId); - window.scrollTo(0, 0); - return; + // Resolve both halves of the deep link against our own data and render + // the resolved values, never the URL text. The framework name gets the + // same allow-list the plain framework route uses below; an unknown + // framework or control falls through to the frameworks index. + var fwIdx = FRAMEWORKS.indexOf(fwNameCtrl); + if (fwIdx !== -1) { + var knownFw = FRAMEWORKS[fwIdx]; + var knownCtrl = resolveControlId(knownFw, ctrlId); + if (knownCtrl !== null) { + renderControlDetail(app, knownFw, knownCtrl); + window.scrollTo(0, 0); + return; + } + } } // Framework detail is a full page, not a modal if (frameworkMatch) {