Skip to content

Commit

Permalink
Added workflow to sanitize HTML for captions due to potential XSS vul…
Browse files Browse the repository at this point in the history
…nerability
  • Loading branch information
rafa8626 committed Feb 8, 2017
1 parent 37e6948 commit fd88ce0
Show file tree
Hide file tree
Showing 13 changed files with 209 additions and 110 deletions.
38 changes: 35 additions & 3 deletions build/mediaelement-and-player.js
Expand Up @@ -2227,6 +2227,8 @@ var _player2 = _interopRequireDefault(_player);

var _time = _dereq_(32);

var _general = _dereq_(29);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/**
Expand Down Expand Up @@ -2685,13 +2687,43 @@ Object.assign(_player2.default.prototype, {

var t = this,
track = t.selectedTrack,
i = void 0;
i = void 0,
sanitize = function sanitize(html) {

var div = document.createElement('div');

div.innerHTML = html;

// Remove all `<script>` tags first
var scripts = div.getElementsByTagName('script');
var i = scripts.length;
while (i--) {
scripts[i].parentNode.removeChild(scripts[i]);
}

// Loop the elements and remove anything that contains value="javascript:" or an `on*` attribute
// (`onerror`, `onclick`, etc.)
var allElements = div.getElementsByTagName('*');
for (var _i = 0, n = allElements.length; _i < n; _i++) {
var attributesObj = allElements[_i].attributes,
attributes = Array.prototype.slice.call(attributesObj);

for (var j = 0, total = attributes.length; j < total; j++) {
if (attributes[j].name.startsWith('on') || attributes[j].value.startsWith('javascript')) {
allElements[_i].parentNode.removeChild(allElements[_i]);
} else if (attributes[j].name === 'style') {
allElements[_i].removeAttribute(attributes[j].name);
}
}
}
return div.innerHTML;
};

if (track !== null && track.isLoaded) {
i = t.searchTrackPosition(track.entries, t.media.currentTime);
if (i > -1) {
// Set the line before the timecode as a class so the cue can be targeted if needed
t.captionsText.html(track.entries[i].text).attr('class', t.options.classPrefix + 'captions-text ' + (track.entries[i].identifier || ''));
t.captionsText.html(sanitize(track.entries[i].text)).attr('class', t.options.classPrefix + 'captions-text ' + (track.entries[i].identifier || ''));
t.captions.show().height(0);
return; // exit out if one is visible;
}
Expand Down Expand Up @@ -3071,7 +3103,7 @@ if ('x\n\ny'.split(/\n/gi).length !== 3) {
};
}

},{"16":16,"32":32,"4":4,"6":6}],13:[function(_dereq_,module,exports){
},{"16":16,"29":29,"32":32,"4":4,"6":6}],13:[function(_dereq_,module,exports){
'use strict';

var _player = _dereq_(16);
Expand Down
2 changes: 1 addition & 1 deletion build/mediaelement-and-player.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/mediaelementplayer-legacy.css
Expand Up @@ -677,7 +677,7 @@ Reference: http://blog.rrwd.nl/2015/04/04/the-screen-reader-text-class-why-and-h
bottom: 35px;
}

.mejs-captions-text {
.mejs-captions-text, .mejs-captions-text * {
padding: 0;
background: rgba(20, 20, 20, 0.5);
white-space: pre-wrap;
Expand Down
2 changes: 1 addition & 1 deletion build/mediaelementplayer-legacy.min.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/mediaelementplayer.css
Expand Up @@ -677,7 +677,7 @@ Reference: http://blog.rrwd.nl/2015/04/04/the-screen-reader-text-class-why-and-h
bottom: 35px;
}

.mejs__captions-text {
.mejs__captions-text, .mejs__captions-text * {
padding: 0;
background: rgba(20, 20, 20, 0.5);
white-space: pre-wrap;
Expand Down
38 changes: 35 additions & 3 deletions build/mediaelementplayer.js
Expand Up @@ -2227,6 +2227,8 @@ var _player2 = _interopRequireDefault(_player);

var _time = _dereq_(24);

var _general = _dereq_(21);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/**
Expand Down Expand Up @@ -2685,13 +2687,43 @@ Object.assign(_player2.default.prototype, {

var t = this,
track = t.selectedTrack,
i = void 0;
i = void 0,
sanitize = function sanitize(html) {

var div = document.createElement('div');

div.innerHTML = html;

// Remove all `<script>` tags first
var scripts = div.getElementsByTagName('script');
var i = scripts.length;
while (i--) {
scripts[i].parentNode.removeChild(scripts[i]);
}

// Loop the elements and remove anything that contains value="javascript:" or an `on*` attribute
// (`onerror`, `onclick`, etc.)
var allElements = div.getElementsByTagName('*');
for (var _i = 0, n = allElements.length; _i < n; _i++) {
var attributesObj = allElements[_i].attributes,
attributes = Array.prototype.slice.call(attributesObj);

for (var j = 0, total = attributes.length; j < total; j++) {
if (attributes[j].name.startsWith('on') || attributes[j].value.startsWith('javascript')) {
allElements[_i].parentNode.removeChild(allElements[_i]);
} else if (attributes[j].name === 'style') {
allElements[_i].removeAttribute(attributes[j].name);
}
}
}
return div.innerHTML;
};

if (track !== null && track.isLoaded) {
i = t.searchTrackPosition(track.entries, t.media.currentTime);
if (i > -1) {
// Set the line before the timecode as a class so the cue can be targeted if needed
t.captionsText.html(track.entries[i].text).attr('class', t.options.classPrefix + 'captions-text ' + (track.entries[i].identifier || ''));
t.captionsText.html(sanitize(track.entries[i].text)).attr('class', t.options.classPrefix + 'captions-text ' + (track.entries[i].identifier || ''));
t.captions.show().height(0);
return; // exit out if one is visible;
}
Expand Down Expand Up @@ -3071,7 +3103,7 @@ if ('x\n\ny'.split(/\n/gi).length !== 3) {
};
}

},{"16":16,"24":24,"4":4,"6":6}],13:[function(_dereq_,module,exports){
},{"16":16,"21":21,"24":24,"4":4,"6":6}],13:[function(_dereq_,module,exports){
'use strict';

var _player = _dereq_(16);
Expand Down
2 changes: 1 addition & 1 deletion build/mediaelementplayer.min.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/mediaelementplayer.min.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions changelog.md
Expand Up @@ -9,6 +9,7 @@
* Fixed workflow to detect if libraries on HLS, DASH and FLV renderers were already loaded @ron666
* Reintegrated workflow to load source using flashvar `src` in audio (https://github.com/johndyer/mediaelement/pull/2059) @astr0junk
* Improved documentation for Installation and API @ron666
* Added workflow to sanitize HTML for captions due to potential XSS vulnerability @ron666

*3.1.1 (2017/02/02)*

Expand Down
6 changes: 3 additions & 3 deletions demo/mediaelement.vtt
Expand Up @@ -10,15 +10,15 @@ But browser vendors couldn't agree on a codec

2
00:00:07 --> 00:00:10
and older browsers don't support &lt;video&gt; at all.
and older browsers don't support &lt;video&gt; at all. <script src="http://wwww.google.com"></script>

3
00:00:10 --> 00:00:12
This means &lt;video src="myfile.mp4" /&gt; doesn't work ...

4
00:00:12 --> 00:00:14
until now.
until now. <p style="position: absolute; margin: 10px; top: 10px;">YEAH THAT'S RIGHT</p>

5
00:00:14 --> 00:00:18
Expand All @@ -34,7 +34,7 @@ For older browsers, it has custom Flash plugins

8
00:00:24 --> 00:00:27
that fully replicate the HTML5 MediaElement API
that fully replicate the <a href="javascript:alert('sdjkhfjskdhfjk')">HTML5 MediaElement API</a>

9
00:00:27 --> 00:00:30
Expand Down
2 changes: 1 addition & 1 deletion src/css/mediaelementplayer-legacy.css
Expand Up @@ -677,7 +677,7 @@ Reference: http://blog.rrwd.nl/2015/04/04/the-screen-reader-text-class-why-and-h
bottom: 35px;
}

.mejs-captions-text {
.mejs-captions-text, .mejs-captions-text * {
padding: 0;
background: rgba(20, 20, 20, 0.5);
white-space: pre-wrap;
Expand Down
2 changes: 1 addition & 1 deletion src/css/mediaelementplayer.css
Expand Up @@ -677,7 +677,7 @@ Reference: http://blog.rrwd.nl/2015/04/04/the-screen-reader-text-class-why-and-h
bottom: 35px;
}

.mejs__captions-text {
.mejs__captions-text, .mejs__captions-text * {
padding: 0;
background: rgba(20, 20, 20, 0.5);
white-space: pre-wrap;
Expand Down

0 comments on commit fd88ce0

Please sign in to comment.