Observed while fixing an unsanitized innerHTML sink in #89.
resource-kit/docs/llm-advisor/app.js defines escapeAttr at line 469 and uses it in four places. These four attribute interpolations do not use it, and each carries data that MCP tools can write into the JSON catalog:
- line 604,
data-next="${option.next}" — written by add_decision_node
- line 607,
data-track="${option.track || currentTrack}" — written by add_decision_node
- line 657,
data-model-name="${item}" — model names inside a terminal option's tools, written by add_decision_node. The adjacent button label on the same line does call sanitizeHTML(item), so the label is escaped and the attribute is not.
- line 1005,
id="model-card-${name.replace(/\s+/g, '-')}" — model names from model-info.json, written by update_model_info
A value containing a double quote breaks out of the attribute. None of the current catalog data does.
Line 1005 cannot be fixed on its own: getElementById at line 1024 rebuilds the same string with the same .replace to find the card, so escaping the id without changing the lookup would break the scroll-to-model behaviour when a model name contains an escaped character. Both sites have to change together, which is why this was not folded into #89.
Not introduced by #89. That PR added a new data source to a separate unsanitized innerHTML sink at line 975 and routed it through sanitizeRichHTML; these attribute sites are pre-existing and were found while sweeping for siblings.
Observed while fixing an unsanitized
innerHTMLsink in #89.resource-kit/docs/llm-advisor/app.jsdefinesescapeAttrat line 469 and uses it in four places. These four attribute interpolations do not use it, and each carries data that MCP tools can write into the JSON catalog:data-next="${option.next}"— written byadd_decision_nodedata-track="${option.track || currentTrack}"— written byadd_decision_nodedata-model-name="${item}"— model names inside a terminal option'stools, written byadd_decision_node. The adjacent button label on the same line does callsanitizeHTML(item), so the label is escaped and the attribute is not.id="model-card-${name.replace(/\s+/g, '-')}"— model names frommodel-info.json, written byupdate_model_infoA value containing a double quote breaks out of the attribute. None of the current catalog data does.
Line 1005 cannot be fixed on its own:
getElementByIdat line 1024 rebuilds the same string with the same.replaceto find the card, so escaping theidwithout changing the lookup would break the scroll-to-model behaviour when a model name contains an escaped character. Both sites have to change together, which is why this was not folded into #89.Not introduced by #89. That PR added a new data source to a separate unsanitized
innerHTMLsink at line 975 and routed it throughsanitizeRichHTML; these attribute sites are pre-existing and were found while sweeping for siblings.