Repository navigation
Keep width and height on SafeHTML images - #15383
Conversation
Build Artifacts
Smoke test screenshot |
b0fb645 to
a01a12c
Compare
There was a problem hiding this comment.
This broadly makes sense, and you've properly compensated for why the width and height were originally stripped - but why wouldn't we just add them as props on the image component (and then shouldn't that allow the attributes to stay even if they're forbidden?)
Props alone won't keep them: DOMPurify strips FORBID_ATTR before mapNode reads props, and attributeNameCheck only covers custom elements.
- Have SafeHTML keep any attribute declared as a prop by the component rendering that tag (
HTMLComponentsandcustomComponents), then declarewidth/heightonSafeHtmlImageand drop theimg-only hook. - Enforce the forbid list in an
uponSanitizeAttributehook, not withforceKeepAttr, which skips_isValidAttributeand would let thesrcprop bypassALLOWED_URI_REGEXP.
The style handling can be simpler too:
- The carried style already reaches the
imgviav-bind="$attrs";parseStyleStringand theinstance.proxy.$attrsworkaround only exist to merge it into the image's own:stylea second time. - Move the border into the scoped style as
border: 1px solid var(--tokens-fineLine)and drop:style. Same forSafeHtmlTable, after whichparseStyleString.jscan go.
| // A percentage height resolves against the flex-stretched wrapper, whose | ||
| // height is the image's own, so it would shrink the image; drop it. | ||
| const height = computed(() => { | ||
| const { height } = instance.proxy.$attrs; |
There was a problem hiding this comment.
Can you not just add width and height to props?
There was a problem hiding this comment.
Done in 0859fd2: width/height are now props, read in place of $attrs. Searched SafeHTML/*.vue for other $attrs reads: 4 matches; the 2 size reads changed, the 2 style reads (image, table) stay since Vue reserves style as a prop name.
Props alone would not keep them:
FORBID_ATTRis absolute in DOMPurify:_isValidAttributereturns false before any custom-element check.validPropsonly feedsattributeNameCheckforcustomComponentstags;imggoes throughHTMLComponents, which it never reads.- Sanitising runs before
mapNodebuilds props, so a removed attribute never reaches the component.
So index.js still drops FORBID_ATTR and strips the size attributes from non-img elements in a hook.
0859fd2 to
6b471a4
Compare
|
Only the inline props thread was addressed; the review body still stands:
|
6b471a4 to
282f127
Compare
|
Prop allowlist:
Styles:
In the capture, the carried style, the border and the 800px table width (4 columns) all come through without |
A height-only image derives its width from the natural ratio, as Studio's editor does. Match/associate chips keep their fixed box. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The image border and table width move to CSS, so nothing merges into the carried style and parseStyleString goes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
282f127 to
d657170
Compare
rtibbles
left a comment
There was a problem hiding this comment.
Manually QA'd in the QTI sandbox: all six QA steps pass, including associate chips with sized images. No console errors.

Summary
<img>keepswidth/height; other elements still lose them.$attrs; the image border and table width move to CSS, andparseStyleStringgoes.References
Fixes #15378. First seen: learningequality/studio#6189.
Reviewer guidance
pnpm test-jest packages/kolibri-common/components/SafeHTML.80vhcap — should it hold for tall images?QA steps
Setup:
pnpm devserver, a learner, a channel imported via Device > Channels with Safe HTML5 articles holding each step's HTML; QTI steps use/en/learn/#/qti_sandbox/. Images over 400×300.height="50%": authored size, natural ratio, 1px border.Evidence
Sized images
50%droppedPhone viewport and lightbox
s2-phone-lightbox-open-close.webm
Styled images
Tables
QTI sized images
s5-qti-select-submit-hint.webm
QTI consecutive items
s6-qti-step-styled-unstyled-styled.webm
QTI then Safe HTML5
Interaction chips
AI usage
Used Claude Code to plan and implement the sanitizer, layout and chip changes with their tests. Verified with the SafeHTML, QTI viewer and Safe HTML5 viewer Jest suites and prek.
Deviations from the issue spec
width/heighton<img>kept "including when only one is set"heightdropped: it collapses the image.width/heightapplied bySafeHTML@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly
How was this generated?
🟡 Waiting for feedback
Last updated: 2026-09-26 05:04 UTC