Skip to content

Commit f1ca7b1

Browse files
committed
GridStackWidget.content only used as textContent
* fix #2736 * All instances of `el.innerHTML = 'some content'` have been removed for security reason as it opens up some potential for accidental XSS * addWidget(w: GridStackWidget) only signature * fixed RTL demo see readme V11 for more info
1 parent 86cabf2 commit f1ca7b1

File tree

12 files changed

+128
-128
lines changed

12 files changed

+128
-128
lines changed

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ Join us on Slack: [https://gridstackjs.slack.com](https://join.slack.com/t/grids
4747
- [Migrating to v8](#migrating-to-v8)
4848
- [Migrating to v9](#migrating-to-v9)
4949
- [Migrating to v10](#migrating-to-v10)
50+
- [Migrating to v11](#migrating-to-v11)
5051
- [jQuery Application](#jquery-application)
5152
- [Changes](#changes)
5253
- [The Team](#the-team)
@@ -460,6 +461,21 @@ breaking change:
460461
* 1 column mode switch is no longer by default (`columnOpts` is not defined) as too many new users had issues. Instead set it explicitly (see above).
461462
* `oneColumnModeDomSort` has been removed. Planning to support per column layouts at some future times. TBD
462463
464+
## Migrating to v11
465+
466+
All instances of `el.innerHTML = 'some content'` have been removed for security reason as it opens up some potential for accidental XSS. we now create DIV directly or use `el.textContent = w.content` for `GridStackWidget.content` field.
467+
468+
breaking change:
469+
* if you code relies on `GridStackWidget.content` with real HTML (like a few demos) it is up to you to do this:
470+
```ts
471+
// NOTE: REAL apps would sanitize-html or DOMPurify before blinding setting innerHTML. see #2736
472+
GridStack.renderCB = function(el, w) {
473+
el.innerHTML = w.content;
474+
};
475+
```
476+
* V11 add new `GridStack.renderCB` that is called for you to create the widget content (entire GridStackWidget is passed so you can use id or some other field as logic) while GS creates the 2 needed parent divs + classes, unlike `GridStack.addRemoveCB` which doesn't create anything for you. Both can be handy for Angular/React/Vue frameworks.
477+
* `addWidget(w: GridStackWidget)` is now the only supported format, no more string content passing. You will need to create content yourself (`Util.createWidgetDivs()` can be used to create parent divs) then call `makeWidget(el)` instead.
478+
463479
# jQuery Application
464480
465481
This is **old and no longer apply to v6+**. You'll need to use v5.1.1 and before

demo/column.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ <h1>column() grid demo (fix cellHeight)</h1>
4747
</div>
4848

4949
<script type="text/javascript">
50+
// NOTE: REAL apps would sanitize-html or DOMPurify before blinding setting innerHTML. see #2736
51+
GridStack.renderCB = function(el, w) {
52+
el.innerHTML = w.content;
53+
};
54+
5055
let test1 = [ // DOM order will be 0,1,2,3,4,5,6 vs column1 = 0,1,4,3,2,5,6
5156
/* match karma testing
5257
{x: 0, y: 0, w: 4, h: 2},

demo/knockout.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ <h1>knockout.js Demo</h1>
3636
}
3737

3838
let item = items.find(function (i) { return i.nodeType == 1 });
39-
grid.addWidget(item);
39+
grid.makeWidget(item);
4040
ko.utils.domNodeDisposal.addDisposeCallback(item, function () {
4141
grid.removeWidget(item);
4242
});

demo/right-to-left(rtl).html

Lines changed: 25 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -5,94 +5,44 @@
55
<meta http-equiv="X-UA-Compatible" content="IE=edge">
66
<meta name="viewport" content="width=device-width, initial-scale=1">
77
<title>Right-To-Left (RTL) demo</title>
8-
9-
<link rel="stylesheet" href="demo.css"/>
108
<style type="text/css">
9+
/* don't include demo.css as that has some forced text aligment */
10+
@import "../dist/gridstack.css";
11+
.grid-stack {
12+
background: #FAFAD2;
13+
}
1114
.grid-stack-item-content {
12-
text-align: right; /* override demo.css */
15+
background-color: #18bc9c;
1316
}
1417
</style>
15-
16-
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.0/knockout-min.js"></script>
1718
<script src="../dist/gridstack-all.js"></script>
1819
</head>
1920
<body>
20-
<div class="container-fluid">
21-
<h1>RTL Demo</h1>
22-
<div>
23-
<a class="btn btn-primary" data-bind="click: addNewWidget">Add Widget</a>
24-
</div>
25-
<br>
26-
<div data-bind="component: {name: 'dashboard-grid', params: $data}"></div>
21+
<h1>RTL Demo</h1>
22+
<div>
23+
<button onClick="addWidget()">Add Widget</button>
2724
</div>
25+
<br>
26+
<div class="grid-stack"></div>
2827

2928
<script type="text/javascript">
30-
ko.components.register('dashboard-grid', {
31-
viewModel: {
32-
createViewModel: function (controller, componentInfo) {
33-
let ViewModel = function (controller, componentInfo) {
34-
let grid = null;
35-
36-
this.widgets = controller.widgets;
37-
38-
this.afterAddWidget = function (items) {
39-
if (!grid) {
40-
grid = GridStack.init({auto: false});
41-
}
42-
43-
let item = items.find(function (i) { return i.nodeType == 1 });
44-
grid.addWidget(item);
45-
ko.utils.domNodeDisposal.addDisposeCallback(item, function () {
46-
grid.removeWidget(item);
47-
});
48-
};
49-
};
50-
51-
return new ViewModel(controller, componentInfo);
52-
}
53-
},
54-
template:
55-
[
56-
'<div class="grid-stack" data-bind="foreach: {data: widgets, afterRender: afterAddWidget}">',
57-
' <div class="grid-stack-item" data-bind="attr: {\'gs-x\': $data.x, \'gs-y\': $data.y, \'gs-w\': $data.w, \'gs-h\': $data.h, \'gs-auto-position\': $data.auto_position}">',
58-
' <div class="grid-stack-item-content"><center><button data-bind="click: $root.deleteWidget">Delete me</button><br><h5 data-bind="text: index" /></center><br><p>lorem ipsum</p></div>',
59-
' </div>',
60-
'</div> '
61-
].join('')
62-
});
63-
64-
let Controller = function (widgets) {
65-
let self = this;
66-
67-
this.widgets = ko.observableArray(widgets);
68-
69-
this.addNewWidget = function () {
70-
this.widgets.push({
71-
x: 0,
72-
y: 0,
73-
w: Math.floor(1 + 3 * Math.random()),
74-
h: Math.floor(1 + 3 * Math.random()),
75-
auto_position: true,
76-
index: 'auto'
77-
});
78-
return false;
79-
};
80-
81-
this.deleteWidget = function (item) {
82-
self.widgets.remove(item);
83-
return false;
29+
let items = [
30+
{x: 0, y: 0, w: 2, h: 1},
31+
{x: 2, y: 0, w: 4, h: 1},
32+
{x: 6, y: 0, w: 2, h: 2},
33+
];
34+
items.forEach((item, i) => item.content = 'item ' + i);
35+
let grid = GridStack.init({rtl: true, children: items});
36+
37+
function addWidget() {
38+
let w = {
39+
w: Math.round(1 + 3 * Math.random()),
40+
h: Math.round(1 + 3 * Math.random()),
41+
content: 'new item',
8442
};
43+
grid.addWidget(w);
8544
};
8645

87-
let widgets = [
88-
{x: 0, y: 0, w: 2, h: 2, index: 1},
89-
{x: 2, y: 0, w: 4, h: 2, index: 2},
90-
{x: 6, y: 0, w: 2, h: 4, index: 3},
91-
{x: 1, y: 2, w: 4, h: 2, index: 4}
92-
];
93-
94-
let controller = new Controller(widgets);
95-
ko.applyBindings(controller);
9646
</script>
9747
</body>
9848
</html>

demo/serialization.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ <h1>Serialization demo</h1>
2424
</div>
2525
<script src="events.js"></script>
2626
<script type="text/javascript">
27+
// NOTE: REAL apps would sanitize-html or DOMPurify before blinding setting innerHTML. see #2736
28+
GridStack.renderCB = function(el, w) {
29+
el.innerHTML = w.content;
30+
};
31+
2732
let serializedData = [
2833
{x: 0, y: 0, w: 2, h: 2},
2934
{x: 2, y: 1, w: 2, h: 3,

demo/sizeToContent.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ <h1>sizeToContent options demo</h1>
6161

6262
</div>
6363
<script type="text/javascript">
64+
// NOTE: REAL apps would sanitize-html or DOMPurify before blinding setting innerHTML. see #2736
65+
GridStack.renderCB = function(el, w) {
66+
el.innerHTML = w.content;
67+
};
68+
6469
let text ='some very large content that will normally not fit in the window.'
6570
text = text + text;
6671
let count = 0;

demo/web1.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ <h1>Web demo 1</h1>
2323
<div class="grid-stack"></div>
2424

2525
<script type="text/javascript">
26+
// NOTE: REAL apps would sanitize-html or DOMPurify before blinding setting innerHTML. see #2736
27+
GridStack.renderCB = function(el, w) {
28+
el.innerHTML = w.content;
29+
};
30+
2631
let grid = GridStack.init({
2732
cellHeight: 70,
2833
});

demo/web2.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ <h1>Advanced Demo</h1>
5757
</div>
5858

5959
<script type="text/javascript">
60+
// NOTE: REAL apps would sanitize-html or DOMPurify before blinding setting innerHTML. see #2736
61+
GridStack.renderCB = function(el, w) {
62+
el.innerHTML = w.content;
63+
};
6064

6165
let grid = GridStack.init({
6266
cellHeight: 70,

demo/website.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,10 @@ <h1>Used by</h1>
336336
<!-- /.container -->
337337

338338
<script type="text/javascript">
339+
// NOTE: REAL apps would sanitize-html or DOMPurify before blinding setting innerHTML. see #2736
340+
GridStack.renderCB = function(el, w) {
341+
el.innerHTML = w.content;
342+
};
339343
var simple = [
340344
{x: 0, y: 0, w: 4, h: 2, content: '1'},
341345
{x: 4, y: 0, w: 4, h: 4, content: '2'},

0 commit comments

Comments
 (0)