Skip to content

Commit

Permalink
1. Refine for ZK-5766: DomPurify fails with partial html content
Browse files Browse the repository at this point in the history
2. Upgrade DomPurify to 3.1.6
  • Loading branch information
jumperchen committed Oct 17, 2024
1 parent 9d4273e commit 0090c1b
Show file tree
Hide file tree
Showing 26 changed files with 315 additions and 300 deletions.
12 changes: 12 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,12 @@ module.exports = {
'htmlOutput': true, // true to indicate that the function returns encoded HTML
'sanitized': true // true to indicate that the function returns sanitized HTML
},
'zUtl.encodeXMLAttribute': { // zUtl.encodeXMLAttribute()
'htmlInput': true,
'safe': true,
'htmlOutput': true, // true to indicate that the function returns encoded HTML
'sanitized': true // true to indicate that the function returns sanitized HTML
},
'.$s': {
'htmlInput': true,
'htmlOutput': true,
Expand Down Expand Up @@ -428,6 +434,12 @@ module.exports = {
'htmlOutput': true, // true to indicate that the function returns encoded HTML
'sanitized': true // true to indicate that the function returns sanitized HTML
},
'zUtl.encodeXMLAttribute': { // zUtl.encodeXMLAttribute()
'htmlInput': true,
'safe': true,
'htmlOutput': true, // true to indicate that the function returns encoded HTML
'sanitized': true // true to indicate that the function returns sanitized HTML
},
'.$s': {
'htmlInput': true,
'htmlOutput': true,
Expand Down
9 changes: 5 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
"zkless-engine": "^1.1.13"
},
"dependencies": {
"dompurify": "^3.1.6",
"dompurify": "^3.1.7",
"moment": "^2.29.4",
"moment-timezone": "^0.5.43"
},
Expand Down
442 changes: 222 additions & 220 deletions zk/src/main/resources/web/js/zk/ext/purify.js

Large diffs are not rendered by default.

24 changes: 12 additions & 12 deletions zk/src/main/resources/web/js/zk/widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2253,10 +2253,10 @@ new zul.wnd.Window({
if (fc = this.desktop) {
//3. generate HTML
var out = new zk.Buffer();
if (tagBeg) out.push(DOMPurify.sanitize(tagBeg));
if (tagBeg) out.push(/*safe*/tagBeg);
for (var j = 0, len = wgts.length; j < len; ++j)
wgts[j].redraw(out);
if (tagEnd) out.push(DOMPurify.sanitize(tagEnd));
if (tagEnd) out.push(/*safe*/tagEnd);

//4. update DOM
// eslint-disable-next-line @microsoft/sdl/no-html-method
Expand Down Expand Up @@ -3065,7 +3065,7 @@ new zul.wnd.Window({
domStyle_(no?: DomStyleOptions): string {
var out = '', s: string | undefined;
if (s = this.z$display) //see au.js
out += 'display:' + s + ';';
out += 'display:' + zUtl.encodeXMLAttribute(s) + ';';
else if (!this.isVisible() && (!no || !no.visible))
out += 'display:none;';

Expand All @@ -3075,17 +3075,17 @@ new zul.wnd.Window({
out += ';';
}
if ((!no || !no.width) && (s = this.getWidth()))
out += 'width:' + s + ';';
out += 'width:' + zUtl.encodeXMLAttribute(s) + ';';
if ((!no || !no.height) && (s = this.getHeight()))
out += 'height:' + s + ';';
out += 'height:' + zUtl.encodeXMLAttribute(s) + ';';
if ((!no || !no.left) && (s = this.getLeft()))
out += 'left:' + s + ';';
out += 'left:' + zUtl.encodeXMLAttribute(s) + ';';
if ((!no || !no.top) && (s = this.getTop()))
out += 'top:' + s + ';';
out += 'top:' + zUtl.encodeXMLAttribute(s) + ';';
let zIndex;
if ((!no || !no.zIndex) && ((zIndex = this.getZIndex() as number)) >= 0)
out += 'z-index:' + zIndex + ';';
return DOMPurify.sanitize(out);
return out;
}

/**
Expand All @@ -3111,7 +3111,7 @@ new zul.wnd.Window({
s = /*safe*/ this.getSclass();
if (!no || !no.zclass)
z = /*safe*/ this.getZclass();
let domClass = s ? z ? s + ' ' + z : s : z || '',
let domClass = zUtl.encodeXMLAttribute(s ? z ? s + ' ' + z : s : z || ''),
n = this.$n();
// FIX ZK-5137: modifying sclass clears vflex="1 here to avoid circular dependency issue in ZK 10
if (n) {
Expand All @@ -3124,7 +3124,7 @@ new zul.wnd.Window({
}
}
}
return DOMPurify.sanitize(domClass);
return domClass;
}

/**
Expand Down Expand Up @@ -3182,7 +3182,7 @@ new zul.wnd.Window({
if (this.domExtraAttrs) {
outHtml += this.domExtraAttrs_();
}
return DOMPurify.sanitize(outHtml);
return outHtml;
}

// B80-ZK-2957
Expand Down Expand Up @@ -3231,7 +3231,7 @@ new zul.wnd.Window({
*/
domTextStyleAttr_(): string | undefined {
const html = this.getStyle();
return DOMPurify.sanitize(html ? zUtl.appendAttr('style', jq.filterTextStyle(html)) : (html ?? ''));
return html ? zUtl.appendAttr('style', jq.filterTextStyle(zUtl.encodeXMLAttribute(html))) : (html ?? '');
}

/** Replaces the specified DOM element with the HTML content generated this widget.
Expand Down
10 changes: 5 additions & 5 deletions zul/src/main/resources/web/js/zul/LabelImageWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ export abstract class LabelImageWidget<TElement extends HTMLElement = HTMLElemen
*/
domImage_(): string {
var img = this._image;
return DOMPurify.sanitize(img ? '<img src="' + img + '" align="absmiddle" alt="" aria-hidden="true">' : '');
return img ? '<img src="' + zUtl.encodeXMLAttribute(img) + '" align="absmiddle" alt="" aria-hidden="true">' : '';
}

/**
Expand All @@ -290,17 +290,17 @@ export abstract class LabelImageWidget<TElement extends HTMLElement = HTMLElemen
let html = '<span class="z-icon-stack" aria-hidden="true">';
for (let i = 0, icon_length = icon.length; i < icon_length; i++) {
if (icon[i]) { // add icon if icon[i] not undefined
html += '<i class="' + /*safe*/ icon[i] + '"';
html += '<i class="' + /*safe*/ zUtl.encodeXMLAttribute(icon[i]) + '"';
if (tooltip?.[i]) { // add iconTooltip if iconTooltip[i] not undefined
html += ' title="' + /*safe*/ tooltip[i] + '"';
html += ' title="' + /*safe*/ zUtl.encodeXMLAttribute(tooltip[i]) + '"';
}
html += '></i>';
}
}
html += '</span>';
return DOMPurify.sanitize(html);
return html;
} else {
return DOMPurify.sanitize('<i class="' + icon[0] + '"' + (tooltip?.[0] ? ' title="' + tooltip[0] + '"' : '') + ' aria-hidden="true"></i>');
return '<i class="' + zUtl.encodeXMLAttribute(icon[0]) + '"' + (tooltip?.[0] ? ' title="' + zUtl.encodeXMLAttribute(tooltip[0]) + '"' : '') + ' aria-hidden="true"></i>';
}
}
return '';
Expand Down
4 changes: 2 additions & 2 deletions zul/src/main/resources/web/js/zul/box/Box.ts
Original file line number Diff line number Diff line change
Expand Up @@ -792,7 +792,7 @@ export class Box extends zul.Widget {
return ''; //if hoz and not splitter, display handled in _childInnerAttrs

if (!child.isVisible()) html += ' style="display:none"';
return DOMPurify.sanitize(html);
return html;
}

/** @internal */
Expand Down Expand Up @@ -823,7 +823,7 @@ export class Box extends zul.Widget {

if (!vert && !child.isVisible()) style += style ? ';display:none' : 'display:none';
if (!vert) style += style ? ';height:100%' : 'height:100%';
return DOMPurify.sanitize(style ? html + ' style="' + style + '"' : html);
return style ? html + ' style="' + style + '"' : html;
}

/** @internal */
Expand Down
10 changes: 5 additions & 5 deletions zul/src/main/resources/web/js/zul/grid/Row.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,15 +300,15 @@ export class Row extends zul.Widget<HTMLTableRowElement> implements zul.mesh.Ite
if (isDetail) {
var wd = child.getWidth();
if (wd)
style += 'width:' + wd + ';';
style += 'width:' + zUtl.encodeXMLAttribute(wd) + ';';
}
if (hgh || align || valign) {
if (hgh)
style += 'height:' + hgh + ';';
style += 'height:' + zUtl.encodeXMLAttribute(hgh) + ';';
if (align)
style += 'text-align:' + align + ';';
style += 'text-align:' + zUtl.encodeXMLAttribute(align) + ';';
if (valign)
style += 'vertical-align:' + valign + ';';
style += 'vertical-align:' + zUtl.encodeXMLAttribute(valign) + ';';
}
var clx = isDetail ? child.$s('outer') : this.$s('inner'),
attrs = '';
Expand All @@ -323,7 +323,7 @@ export class Row extends zul.Widget<HTMLTableRowElement> implements zul.mesh.Ite
attrs += ' aria-hidden="true"';
clx += ' ' + this.$s('hidden-column');
}
return DOMPurify.sanitize(attrs + ' class="' + clx + '"');
return attrs + ' class="' + clx + '"';
}

/**
Expand Down
10 changes: 5 additions & 5 deletions zul/src/main/resources/web/js/zul/inp/InputWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -626,21 +626,21 @@ export class InputWidget<ValueType = unknown> extends zul.Widget<HTMLInputElemen
vHTML = /*safe*/ this._tabindex;
if (vHTML != undefined) html += ' tabindex="' + vHTML + '"';
vHTML = /*safe*/ this._name;
if (vHTML) html += ' name="' + vHTML + '"';
if (vHTML) html += ' name="' + zUtl.encodeXMLAttribute(vHTML as never) + '"';
if (this._disabled) html += ' disabled="disabled"';
if (this._readonly) html += ' readonly="readonly"';
if (this._placeholder) html += ' placeholder="' + zUtl.encodeXML(this._placeholder) + '"';
if (this._inputAttributes) {
for (var key in this._inputAttributes) {
var val = this._inputAttributes[key];
html += (' ' + /*safe*/ key + '="' + /*safe*/ val + '"');
html += (' ' + /*safe*/ key + '="' + zUtl.encodeXMLAttribute(val) + '"');
}
}

var /*safe*/ s = jq.filterTextStyle(this.domStyle_({width: true, height: true, top: true, left: true}));
if (s) html += ' style="' + /*safe*/ s + '"';

return DOMPurify.sanitize(html);
return html;
}

/** @internal */
Expand Down Expand Up @@ -881,7 +881,7 @@ export class InputWidget<ValueType = unknown> extends zul.Widget<HTMLInputElemen

if (this.desktop) { //err not visible if not attached //B85-ZK-3321
jq(this.getInputNode()).addClass(this.$s('invalid'));

interface CustomConstraint extends zul.inp.SimpleConstraint {
showCustomError?: (inp: InputWidget<ValueType>, msg: string) => boolean;
}
Expand Down Expand Up @@ -1083,7 +1083,7 @@ export class InputWidget<ValueType = unknown> extends zul.Widget<HTMLInputElemen
//value not reset yet so wait a moment
}
}

/** @internal */
override focus_(timeout?: number): boolean {
zk(this.getInputNode()).focus(timeout);
Expand Down
4 changes: 2 additions & 2 deletions zul/src/main/resources/web/js/zul/menu/Menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ export class Menu extends zul.LabelImageWidget implements zul.LabelImageWidgetWi
/*safe*/ iconSclass = this.domIcon_();

if (img)
/*safe*/ img = '<img id="' + this.uuid + '-img" src="' + /*safe*/ img + '" class="' + this.$s('image') + '" align="absmiddle" alt="" aria-hidden="true" />'
/*safe*/ img = '<img id="' + this.uuid + '-img" src="' + /*safe*/ zUtl.encodeXMLAttribute(img) + '" class="' + this.$s('image') + '" align="absmiddle" alt="" aria-hidden="true" />'
+ (iconSclass ? ' ' + /*safe*/ iconSclass : '');
else {
if (iconSclass) {
Expand All @@ -232,7 +232,7 @@ export class Menu extends zul.LabelImageWidget implements zul.LabelImageWidgetWi
+ this.$s('image') + '" align="absmiddle" alt="" aria-hidden="true" />';
}
}
return DOMPurify.sanitize(img + label + separator + icon);
return img + label + separator + icon;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions zul/src/main/resources/web/js/zul/menu/Menuitem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ export class Menuitem extends zul.LabelImageWidget implements zul.LabelImageWidg
/*safe*/ iconSclass = this.domIcon_();

if (img)
/*safe*/ img = '<img src="' + /*safe*/ img + '" class="' + this.$s('image') + '" align="absmiddle" alt="" aria-hidden="true" />'
/*safe*/ img = '<img src="' + zUtl.encodeXMLAttribute(img) + '" class="' + this.$s('image') + '" align="absmiddle" alt="" aria-hidden="true" />'
+ (iconSclass ? ' ' + /*safe*/ iconSclass : '');
else {
if (iconSclass) {
Expand All @@ -376,7 +376,7 @@ export class Menuitem extends zul.LabelImageWidget implements zul.LabelImageWidg
+ ' src="data:image/png;base64,R0lGODlhAQABAIAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" class="' + this.$s('image') + '" align="absmiddle" alt="" aria-hidden="true" />';
}
}
return DOMPurify.sanitize(img + (this.isAutocheck() || this.isCheckmark() ? icon : '') + label);
return img + (this.isAutocheck() || this.isCheckmark() ? icon : '') + label;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion zul/src/main/resources/web/js/zul/menu/mold/menuitem.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function menuitem$mold$(out) {

out.push('<li', this.domAttrs_(), ' role="none">');

out.push('<a role="' + (chechmark ? 'menuitemcheckbox' : 'menuitem') + '" href="', this.getHref() ? /*safe*/ zUtl.encodeXMLAttribute(DOMPurify.sanitize(this.getHref())) : 'javascript:void(0);', '"');
out.push('<a role="' + (chechmark ? 'menuitemcheckbox' : 'menuitem') + '" href="', this.getHref() ? /*safe*/ zUtl.encodeXMLAttribute(this.getHref()) : 'javascript:void(0);', '"');
if (target)
out.push(' target="', zUtl.encodeXML(target), '"');
out.push(' id="', uuid, '-a" class="', this.$s('content'), '"',
Expand Down
8 changes: 4 additions & 4 deletions zul/src/main/resources/web/js/zul/mesh/Auxheader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,14 @@ export abstract class Auxheader extends zul.mesh.HeaderWidget {

return this;
}

/** @internal */
override domAttrs_(no?: zk.DomAttrsOptions): string {
var /*safe*/ s = super.domAttrs_(no);
if (this._colspan != 1)
s += ` colspan="${this._colspan}"`;
s += ` colspan="${zUtl.encodeXMLAttribute(this._colspan as never)}"`;
if (this._rowspan != 1)
s += ` rowspan="${this._rowspan}"`;
return DOMPurify.sanitize(s);
s += ` rowspan="${zUtl.encodeXMLAttribute(this._rowspan as never)}"`;
return s;
}
}
4 changes: 2 additions & 2 deletions zul/src/main/resources/web/js/zul/tab/Tab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,9 @@ export class Tab extends zul.LabelImageWidget implements zul.LabelImageWidgetWit
if (!img) {
img = iconSclass;
} else
/*safe*/ img = '<img src="' + /*safe*/ img + '" class="' + this.$s('image') + '" alt="" aria-hidden="true"/>'
/*safe*/ img = '<img src="' + zUtl.encodeXMLAttribute(img) + '" class="' + this.$s('image') + '" alt="" aria-hidden="true"/>'
+ (iconSclass ? ' ' + /*safe*/ iconSclass : '');
return DOMPurify.sanitize(label ? img + ' ' + label : img);
return label ? img + ' ' + label : img;
}

//bug #3014664
Expand Down
6 changes: 3 additions & 3 deletions zul/src/main/resources/web/js/zul/utl/Iframe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,11 +195,11 @@ export class Iframe extends zul.Widget<HTMLIFrameElement> {
if ('auto' != v)
attr += ' scrolling="' + ('true' === v ? 'yes' : 'false' === v ? 'no' : v) + '"';
if ((v = this._align))
attr += ' align="' + v + '"';
attr += ' align="' + zUtl.encodeXMLAttribute(v) + '"';
if ((v = this._name))
attr += ' name="' + v + '"';
attr += ' name="' + zUtl.encodeXMLAttribute(v) + '"';
if ((v = this._autohide))
attr += ' z_autohide="' + v + '"';
return DOMPurify.sanitize(attr);
return attr;
}
}
6 changes: 3 additions & 3 deletions zul/src/main/resources/web/js/zul/utl/mold/style.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/* style.js
Purpose:
Description:
History:
Thu Jun 30 14:33:41 TST 2011, Created by tomyeh
Expand All @@ -15,7 +15,7 @@ function style$mold$(out) {

out.push('<div style="display:none" id="', this.uuid, '">&#160;');
if ((src = this._src))
out.push('<link id="', this.uuid, '-real" rel="stylesheet" type="text/css" href="', DOMPurify.sanitize(src), '"');
out.push('<link id="', this.uuid, '-real" rel="stylesheet" type="text/css" href="', zUtl.encodeXMLAttribute(src), '"');
else
out.push('<style id="', this.uuid, '-real"');

Expand Down
6 changes: 3 additions & 3 deletions zul/src/main/resources/web/js/zul/wgt/A.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,14 +254,14 @@ export class A extends zul.LabelImageWidget<HTMLAnchorElement> implements zul.La
var /*safe*/ attr = super.domAttrs_(no),
v: string | undefined;
if (v = this.getTarget())
attr += ` target="${v}"`;
attr += ` target="${zUtl.encodeXMLAttribute(v)}"`;
if (v = this.getHref())
attr += ` href="${v}"`;
attr += ` href="${zUtl.encodeXMLAttribute(v)}"`;
else
attr += ' href="javascript:;"';
if (this._disabled)
attr += ' disabled="disabled"';
return DOMPurify.sanitize(attr);
return attr;
}

/** @internal */
Expand Down
Loading

0 comments on commit 0090c1b

Please sign in to comment.