@@ -6,64 +6,133 @@ var util = require('@src/lib/svg_text_utils');
66describe ( 'svg+text utils' , function ( ) {
77 'use strict' ;
88
9- describe ( 'convertToTspans' , function ( ) {
9+ describe ( 'convertToTspans should ' , function ( ) {
1010
1111 function mockTextSVGElement ( txt ) {
1212 return d3 . select ( 'body' )
1313 . append ( 'svg' )
1414 . attr ( 'id' , 'text' )
1515 . append ( 'text' )
1616 . text ( txt )
17- . call ( util . convertToTspans ) ;
17+ . call ( util . convertToTspans )
18+ . attr ( 'transform' , 'translate(50,50)' ) ;
19+ }
20+
21+ function assertAnchorLink ( node , href ) {
22+ var a = node . select ( 'a' ) ;
23+
24+ expect ( a . attr ( 'xlink:href' ) ) . toBe ( href ) ;
25+ expect ( a . attr ( 'xlink:show' ) ) . toBe ( href === null ? null : 'new' ) ;
26+ }
27+
28+ function assertAnchorAttrs ( node ) {
29+ var a = node . select ( 'a' ) ;
30+
31+ var WHITE_LIST = [ 'xlink:href' , 'xlink:show' , 'style' ] ,
32+ attrs = listAttributes ( a . node ( ) ) ;
33+
34+ // check that no other attribute are found in anchor,
35+ // which can be lead to XSS attacks.
36+
37+ var hasWrongAttr = attrs . some ( function ( attr ) {
38+ return WHITE_LIST . indexOf ( attr ) === - 1 ;
39+ } ) ;
40+
41+ expect ( hasWrongAttr ) . toBe ( false ) ;
42+ }
43+
44+ function listAttributes ( node ) {
45+ var items = Array . prototype . slice . call ( node . attributes ) ;
46+
47+ var attrs = items . map ( function ( item ) {
48+ return item . name ;
49+ } ) ;
50+
51+ return attrs ;
1852 }
1953
2054 afterEach ( function ( ) {
2155 d3 . select ( '#text' ) . remove ( ) ;
2256 } ) ;
2357
24- it ( 'checks for XSS attack in href' , function ( ) {
58+ it ( 'check for XSS attack in href' , function ( ) {
2559 var node = mockTextSVGElement (
2660 '<a href="javascript:alert(\'attack\')">XSS</a>'
2761 ) ;
2862
2963 expect ( node . text ( ) ) . toEqual ( 'XSS' ) ;
30- expect ( node . select ( 'a' ) . attr ( 'xlink:href' ) ) . toBe ( null ) ;
64+ assertAnchorAttrs ( node ) ;
65+ assertAnchorLink ( node , null ) ;
3166 } ) ;
3267
33- it ( 'checks for XSS attack in href (with plenty of white spaces)' , function ( ) {
68+ it ( 'check for XSS attack in href (with plenty of white spaces)' , function ( ) {
3469 var node = mockTextSVGElement (
3570 '<a href = " javascript:alert(\'attack\')">XSS</a>'
3671 ) ;
3772
3873 expect ( node . text ( ) ) . toEqual ( 'XSS' ) ;
39- expect ( node . select ( 'a' ) . attr ( 'xlink:href' ) ) . toBe ( null ) ;
74+ assertAnchorAttrs ( node ) ;
75+ assertAnchorLink ( node , null ) ;
4076 } ) ;
4177
42- it ( 'whitelists http hrefs' , function ( ) {
78+ it ( 'whitelist http hrefs' , function ( ) {
4379 var node = mockTextSVGElement (
4480 '<a href="http://bl.ocks.org/">bl.ocks.org</a>'
4581 ) ;
4682
4783 expect ( node . text ( ) ) . toEqual ( 'bl.ocks.org' ) ;
48- expect ( node . select ( 'a' ) . attr ( 'xlink:href' ) ) . toEqual ( 'http://bl.ocks.org/' ) ;
84+ assertAnchorAttrs ( node ) ;
85+ assertAnchorLink ( node , 'http://bl.ocks.org/' ) ;
4986 } ) ;
5087
51- it ( 'whitelists https hrefs' , function ( ) {
88+ it ( 'whitelist https hrefs' , function ( ) {
5289 var node = mockTextSVGElement (
5390 '<a href="https://plot.ly">plot.ly</a>'
5491 ) ;
5592
5693 expect ( node . text ( ) ) . toEqual ( 'plot.ly' ) ;
57- expect ( node . select ( 'a' ) . attr ( 'xlink:href' ) ) . toEqual ( 'https://plot.ly' ) ;
94+ assertAnchorAttrs ( node ) ;
95+ assertAnchorLink ( node , 'https://plot.ly' ) ;
5896 } ) ;
5997
60- it ( 'whitelists mailto hrefs' , function ( ) {
98+ it ( 'whitelist mailto hrefs' , function ( ) {
6199 var node = mockTextSVGElement (
62100 '<a href="mailto:support@plot.ly">support</a>'
63101 ) ;
64102
65103 expect ( node . text ( ) ) . toEqual ( 'support' ) ;
66- expect ( node . select ( 'a' ) . attr ( 'xlink:href' ) ) . toEqual ( 'mailto:support@plot.ly' ) ;
104+ assertAnchorAttrs ( node ) ;
105+ assertAnchorLink ( node , 'mailto:support@plot.ly' ) ;
106+ } ) ;
107+
108+ it ( 'wrap XSS attacks in href' , function ( ) {
109+ var textCases = [
110+ '<a href="XSS\" onmouseover="alert(1)\" style="font-size:300px">Subtitle</a>' ,
111+ '<a href="XSS" onmouseover="alert(1)" style="font-size:300px">Subtitle</a>'
112+ ] ;
113+
114+ textCases . forEach ( function ( textCase ) {
115+ var node = mockTextSVGElement ( textCase ) ;
116+
117+ expect ( node . text ( ) ) . toEqual ( 'Subtitle' ) ;
118+ assertAnchorAttrs ( node ) ;
119+ assertAnchorLink ( node , 'XSS onmouseover=alert(1) style=font-size:300px' ) ;
120+ } ) ;
121+ } ) ;
122+
123+ it ( 'should keep query parameters in href' , function ( ) {
124+ var textCases = [
125+ '<a href="https://abc.com/myFeature.jsp?name=abc&pwd=def">abc.com?shared-key</a>' ,
126+ '<a href="https://abc.com/myFeature.jsp?name=abc&pwd=def">abc.com?shared-key</a>'
127+ ] ;
128+
129+ textCases . forEach ( function ( textCase ) {
130+ var node = mockTextSVGElement ( textCase ) ;
131+
132+ assertAnchorAttrs ( node ) ;
133+ expect ( node . text ( ) ) . toEqual ( 'abc.com?shared-key' ) ;
134+ assertAnchorLink ( node , 'https://abc.com/myFeature.jsp?name=abc&pwd=def' ) ;
135+ } ) ;
67136 } ) ;
68137 } ) ;
69138} ) ;
0 commit comments