@samitouri / QOS-React-2 / commits / 8ca058ac4e

Split escapeTextForBrowser into escapeTextContentForBrowser and quoteAttributeValueForBrowser

Andreas Svensson committed Sep 18, 2014 at 19:12 UTC 8ca058ac4ec15517908cd7afff7abc0511925494
9 files changed +115 -24
src/browser/ui/ReactDOMComponent.js
+2 -2
@@ -25,7 +25,7 @@ var ReactMultiChild = require('ReactMultiChild');
25 var ReactPerf = require('ReactPerf');
26
27 var assign = require('Object.assign');
28 -var escapeTextForBrowser = require('escapeTextForBrowser');
28 +var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
29 var invariant = require('invariant');
30 var isEventSupported = require('isEventSupported');
31 var keyOf = require('keyOf');
@@ -284,7 +284,7 @@ ReactDOMComponent.Mixin = {
284 CONTENT_TYPES[typeof props.children] ? props.children : null;
285 var childrenToUse = contentToUse != null ? null : props.children;
286 if (contentToUse != null) {
287 - return prefix + escapeTextForBrowser(contentToUse);
287 + return prefix + escapeTextContentForBrowser(contentToUse);
288 } else if (childrenToUse != null) {
289 var mountImages = this.mountChildren(
290 childrenToUse,
src/browser/ui/ReactDOMTextComponent.js
+2 -2
@@ -18,7 +18,7 @@ var ReactComponentBrowserEnvironment =
18 var ReactDOMComponent = require('ReactDOMComponent');
19
20 var assign = require('Object.assign');
21 -var escapeTextForBrowser = require('escapeTextForBrowser');
21 +var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
22 var invariant = require('invariant');
23
24 /**
@@ -67,7 +67,7 @@ assign(ReactDOMTextComponent.prototype, {
67 */
68 mountComponent: function(rootID, transaction, context) {
69 this._rootNodeID = rootID;
70 - var escapedText = escapeTextForBrowser(this._stringText);
70 + var escapedText = escapeTextContentForBrowser(this._stringText);
71
72 if (transaction.renderToStaticMarkup) {
73 // Normally we'd wrap this in a `span` for the reasons stated above, but
src/browser/ui/__tests__/ReactDOMComponent-test.js
+17
@@ -431,6 +431,23 @@ describe('ReactDOMComponent', function() {
431 'style={{marginRight: spacing + \'em\'}} when using JSX.'
432 );
433 });
434 +
435 + it("should properly escape text content and attributes values", function() {
436 + expect(
437 + React.renderToStaticMarkup(
438 + React.DOM.div({
439 + title: '\'"<>&',
440 + style: {
441 + textAlign: '\'"<>&'
442 + }
443 + }, '\'"<>&')
444 + )
445 + ).toBe(
446 + '<div title="&#x27;&quot;&lt;&gt;&amp;" style="text-align:&#x27;&quot;&lt;&gt;&amp;;">' +
447 + '&#x27;&quot;&lt;&gt;&amp;' +
448 + '</div>'
449 + );
450 + });
451 });
452
453 describe('unmountComponent', function() {
src/browser/ui/dom/DOMPropertyOperations.js
+7 -6
@@ -14,7 +14,8 @@
14
15 var DOMProperty = require('DOMProperty');
16
17 -var escapeTextForBrowser = require('escapeTextForBrowser');
17 +var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
18 +var quoteAttributeValueForBrowser = require('quoteAttributeValueForBrowser');
19 var memoizeStringOnly = require('memoizeStringOnly');
20 var warning = require('warning');
21
@@ -27,7 +28,7 @@ function shouldIgnoreValue(name, value) {
28 }
29
30 var processAttributeNameAndPrefix = memoizeStringOnly(function(name) {
30 - return escapeTextForBrowser(name) + '="';
31 + return escapeTextContentForBrowser(name) + '=';
32 });
33
34 if (__DEV__) {
@@ -82,7 +83,7 @@ var DOMPropertyOperations = {
83 */
84 createMarkupForID: function(id) {
85 return processAttributeNameAndPrefix(DOMProperty.ID_ATTRIBUTE_NAME) +
85 - escapeTextForBrowser(id) + '"';
86 + quoteAttributeValueForBrowser(id);
87 },
88
89 /**
@@ -101,16 +102,16 @@ var DOMPropertyOperations = {
102 var attributeName = DOMProperty.getAttributeName[name];
103 if (DOMProperty.hasBooleanValue[name] ||
104 (DOMProperty.hasOverloadedBooleanValue[name] && value === true)) {
104 - return escapeTextForBrowser(attributeName);
105 + return escapeTextContentForBrowser(attributeName);
106 }
107 return processAttributeNameAndPrefix(attributeName) +
107 - escapeTextForBrowser(value) + '"';
108 + quoteAttributeValueForBrowser(value);
109 } else if (DOMProperty.isCustomAttribute(name)) {
110 if (value == null) {
111 return '';
112 }
113 return processAttributeNameAndPrefix(name) +
113 - escapeTextForBrowser(value) + '"';
114 + quoteAttributeValueForBrowser(value);
115 } else if (__DEV__) {
116 warnUnknownProperty(name);
117 }
src/browser/ui/dom/setTextContent.js
+2 -2
@@ -19,7 +19,7 @@
19 "use strict";
20
21 var ExecutionEnvironment = require('ExecutionEnvironment');
22 -var escapeTextForBrowser = require('escapeTextForBrowser');
22 +var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
23 var setInnerHTML = require('setInnerHTML');
24
25 /**
@@ -39,7 +39,7 @@ var setTextContent = function(node, text) {
39 if (ExecutionEnvironment.canUseDOM) {
40 if (!('textContent' in document.documentElement)) {
41 setTextContent = function(node, text) {
42 - setInnerHTML(node, escapeTextForBrowser(text));
42 + setInnerHTML(node, escapeTextContentForBrowser(text));
43 };
44 }
45 }
src/utils/__tests__/escapeTextContentForBrowser-test.js renamed
+8 -8
@@ -11,17 +11,17 @@
11
12 'use strict';
13
14 -describe('escapeTextForBrowser', function() {
14 +describe('escapeTextContentForBrowser', function() {
15
16 - var escapeTextForBrowser = require('escapeTextForBrowser');
16 + var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
17
18 it('should escape boolean to string', function() {
19 - expect(escapeTextForBrowser(true)).toBe('true');
20 - expect(escapeTextForBrowser(false)).toBe('false');
19 + expect(escapeTextContentForBrowser(true)).toBe('true');
20 + expect(escapeTextContentForBrowser(false)).toBe('false');
21 });
22
23 it('should escape object to string', function() {
24 - var escaped = escapeTextForBrowser({
24 + var escaped = escapeTextContentForBrowser({
25 toString: function() {
26 return 'ponys';
27 }
@@ -31,17 +31,17 @@ describe('escapeTextForBrowser', function() {
31 });
32
33 it('should escape number to string', function() {
34 - expect(escapeTextForBrowser(42)).toBe('42');
34 + expect(escapeTextContentForBrowser(42)).toBe('42');
35 });
36
37 it('should escape string', function() {
38 - var escaped = escapeTextForBrowser('<script type=\'\' src=""></script>');
38 + var escaped = escapeTextContentForBrowser('<script type=\'\' src=""></script>');
39 expect(escaped).not.toContain('<');
40 expect(escaped).not.toContain('>');
41 expect(escaped).not.toContain('\'');
42 expect(escaped).not.toContain('\"');
43
44 - escaped = escapeTextForBrowser('&');
44 + escaped = escapeTextContentForBrowser('&');
45 expect(escaped).toBe('&amp;');
46 });
47
src/utils/__tests__/quoteAttributeValueForBrowser-test.js new
+48
@@ -0,0 +1,48 @@
1 +/**
2 + * Copyright 2013-2015, Facebook, Inc.
3 + * All rights reserved.
4 + *
5 + * This source code is licensed under the BSD-style license found in the
6 + * LICENSE file in the root directory of this source tree. An additional grant
7 + * of patent rights can be found in the PATENTS file in the same directory.
8 + *
9 + * @emails react-core
10 + */
11 +
12 +"use strict";
13 +
14 +describe('quoteAttributeValueForBrowser', function() {
15 +
16 + var quoteAttributeValueForBrowser = require('quoteAttributeValueForBrowser');
17 +
18 + it('should escape boolean to string', function() {
19 + expect(quoteAttributeValueForBrowser(true)).toBe('"true"');
20 + expect(quoteAttributeValueForBrowser(false)).toBe('"false"');
21 + });
22 +
23 + it('should escape object to string', function() {
24 + var escaped = quoteAttributeValueForBrowser({
25 + toString: function() {
26 + return 'ponys';
27 + }
28 + });
29 +
30 + expect(escaped).toBe('"ponys"');
31 + });
32 +
33 + it('should escape number to string', function() {
34 + expect(quoteAttributeValueForBrowser(42)).toBe('"42"');
35 + });
36 +
37 + it('should escape string', function() {
38 + var escaped = quoteAttributeValueForBrowser('<script type=\'\' src=""></script>');
39 + expect(escaped).not.toContain('<');
40 + expect(escaped).not.toContain('>');
41 + expect(escaped).not.toContain('\'');
42 + expect(escaped.substr(1, -1)).not.toContain('\"');
43 +
44 + escaped = quoteAttributeValueForBrowser('&');
45 + expect(escaped).toBe('"&amp;"');
46 + });
47 +
48 +});
src/utils/escapeTextContentForBrowser.js renamed
+3 -4
@@ -6,8 +6,7 @@
6 * LICENSE file in the root directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
8 *
9 - * @providesModule escapeTextForBrowser
10 - * @typechecks static-only
9 + * @providesModule escapeTextContentForBrowser
10 */
11
12 'use strict';
@@ -32,8 +31,8 @@ function escaper(match) {
31 * @param {*} text Text value to escape.
32 * @return {string} An escaped string.
33 */
35 -function escapeTextForBrowser(text) {
34 +function escapeTextContentForBrowser(text) {
35 return ('' + text).replace(ESCAPE_REGEX, escaper);
36 }
37
39 -module.exports = escapeTextForBrowser;
38 +module.exports = escapeTextContentForBrowser;
src/utils/quoteAttributeValueForBrowser.js new
+26
@@ -0,0 +1,26 @@
1 +/**
2 + * Copyright 2013-2015, Facebook, Inc.
3 + * All rights reserved.
4 + *
5 + * This source code is licensed under the BSD-style license found in the
6 + * LICENSE file in the root directory of this source tree. An additional grant
7 + * of patent rights can be found in the PATENTS file in the same directory.
8 + *
9 + * @providesModule quoteAttributeValueForBrowser
10 + */
11 +
12 +"use strict";
13 +
14 +var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
15 +
16 +/**
17 + * Escapes attribute value to prevent scripting attacks.
18 + *
19 + * @param {*} value Value to escape.
20 + * @return {string} An escaped string.
21 + */
22 +function quoteAttributeValueForBrowser(value) {
23 + return '"' + escapeTextContentForBrowser(value) + '"';
24 +}
25 +
26 +module.exports = quoteAttributeValueForBrowser;