Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
8503b1b5
Commit
8503b1b5
authored
Oct 05, 2020
by
Himanshu Kapoor
Committed by
Paul Slaughter
Oct 05, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Do not sanitize tooltip if it does not contain any html
https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43572
parent
fb24b25c
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
121 additions
and
53 deletions
+121
-53
app/assets/javascripts/vue_shared/directives/tooltip.js
app/assets/javascripts/vue_shared/directives/tooltip.js
+5
-0
spec/frontend/vue_shared/directives/tooltip_spec.js
spec/frontend/vue_shared/directives/tooltip_spec.js
+116
-53
No files found.
app/assets/javascripts/vue_shared/directives/tooltip.js
View file @
8503b1b5
import
$
from
'
jquery
'
;
import
'
~/commons/bootstrap
'
;
import
{
parseBoolean
}
from
'
~/lib/utils/common_utils
'
;
export
default
{
bind
(
el
)
{
...
...
@@ -9,6 +10,10 @@ export default {
$
(
el
).
tooltip
({
trigger
:
'
hover
'
,
delay
,
// By default, sanitize is run even if there is no `html` or `template` present
// so let's optimize to only run this when necessary.
// https://github.com/twbs/bootstrap/blob/c5966de27395a407f9a3d20d0eb2ff8e8fb7b564/js/src/tooltip.js#L716
sanitize
:
parseBoolean
(
el
.
dataset
.
html
)
||
Boolean
(
el
.
dataset
.
template
),
});
},
...
...
spec/frontend/vue_shared/directives/tooltip_spec.js
View file @
8503b1b5
import
$
from
'
jquery
'
;
import
{
escape
}
from
'
lodash
'
;
import
{
mount
}
from
'
@vue/test-utils
'
;
import
tooltip
from
'
~/vue_shared/directives/tooltip
'
;
const
DEFAULT_TOOLTIP_TEMPLATE
=
'
<div v-tooltip :title="tooltip"></div>
'
;
const
HTML_TOOLTIP_TEMPLATE
=
'
<div v-tooltip data-html="true" :title="tooltip"></div>
'
;
describe
(
'
Tooltip directive
'
,
()
=>
{
let
vm
;
let
wrapper
;
function
createTooltipContainer
({
template
=
DEFAULT_TOOLTIP_TEMPLATE
,
text
=
'
some text
'
,
}
=
{})
{
wrapper
=
mount
(
{
directives
:
{
tooltip
},
data
:
()
=>
({
tooltip
:
text
}),
template
,
},
{
attachToDocument
:
true
},
);
}
async
function
showTooltip
()
{
$
(
wrapper
.
vm
.
$el
).
tooltip
(
'
show
'
);
jest
.
runOnlyPendingTimers
();
await
wrapper
.
vm
.
$nextTick
();
}
function
findTooltipInnerHtml
()
{
return
document
.
querySelector
(
'
.tooltip-inner
'
).
innerHTML
;
}
function
findTooltipHtml
()
{
return
document
.
querySelector
(
'
.tooltip
'
).
innerHTML
;
}
afterEach
(()
=>
{
if
(
vm
)
{
vm
.
$destroy
();
}
wrapper
.
destroy
();
wrapper
=
null
;
});
describe
(
'
with a single tooltip
'
,
()
=>
{
beforeEach
(()
=>
{
const
wrapper
=
mount
(
{
directives
:
{
tooltip
,
},
data
()
{
return
{
tooltip
:
'
some text
'
,
};
},
template
:
'
<div v-tooltip :title="tooltip"></div>
'
,
},
{
attachToDocument
:
true
},
);
vm
=
wrapper
.
vm
;
});
it
(
'
should have tooltip plugin applied
'
,
()
=>
{
expect
(
$
(
vm
.
$el
).
data
(
'
bs.tooltip
'
)).
toBeDefined
();
createTooltipContainer
();
expect
(
$
(
wrapper
.
vm
.
$el
).
data
(
'
bs.tooltip
'
)).
toBeDefined
();
});
it
(
'
displays the title as tooltip
'
,
()
=>
{
$
(
vm
.
$el
).
tooltip
(
'
show
'
);
createTooltipContainer
();
$
(
wrapper
.
vm
.
$el
).
tooltip
(
'
show
'
);
jest
.
runOnlyPendingTimers
();
const
tooltipElement
=
document
.
querySelector
(
'
.tooltip-inner
'
);
...
...
@@ -44,52 +61,98 @@ describe('Tooltip directive', () => {
expect
(
tooltipElement
.
textContent
).
toContain
(
'
some text
'
);
});
it
(
'
updates a visible tooltip
'
,
()
=>
{
$
(
vm
.
$el
).
tooltip
(
'
show
'
);
it
.
each
`
condition | template | sanitize
${
'
does not contain any html
'
}
|
${
DEFAULT_TOOLTIP_TEMPLATE
}
|
${
false
}
${
'
contains html
'
}
|
${
HTML_TOOLTIP_TEMPLATE
}
|
${
true
}
`
(
'
passes sanitize=$sanitize if the tooltip $condition
'
,
({
template
,
sanitize
})
=>
{
createTooltipContainer
({
template
});
expect
(
$
(
wrapper
.
vm
.
$el
).
data
(
'
bs.tooltip
'
).
config
.
sanitize
).
toEqual
(
sanitize
);
});
it
(
'
updates a visible tooltip
'
,
async
()
=>
{
createTooltipContainer
();
$
(
wrapper
.
vm
.
$el
).
tooltip
(
'
show
'
);
jest
.
runOnlyPendingTimers
();
const
tooltipElement
=
document
.
querySelector
(
'
.tooltip-inner
'
);
vm
.
tooltip
=
'
other text
'
;
wrapper
.
vm
.
tooltip
=
'
other text
'
;
jest
.
runOnlyPendingTimers
();
await
wrapper
.
vm
.
$nextTick
();
expect
(
tooltipElement
.
textContent
).
toContain
(
'
other text
'
);
});
describe
(
'
tooltip sanitization
'
,
()
=>
{
it
(
'
reads tooltip content as text if data-html is not passed
'
,
async
()
=>
{
createTooltipContainer
({
text
:
'
sample text<script>alert("XSS!!")</script>
'
});
return
vm
.
$nextTick
().
then
(()
=>
{
expect
(
tooltipElement
.
textContent
).
toContain
(
'
other text
'
);
await
showTooltip
();
const
result
=
findTooltipInnerHtml
();
expect
(
result
).
toEqual
(
'
sample text<script>alert("XSS!!")</script>
'
);
});
it
(
'
sanitizes tooltip if data-html is passed
'
,
async
()
=>
{
createTooltipContainer
({
template
:
HTML_TOOLTIP_TEMPLATE
,
text
:
'
sample text<script>alert("XSS!!")</script>
'
,
});
await
showTooltip
();
const
result
=
findTooltipInnerHtml
();
expect
(
result
).
toEqual
(
'
sample text
'
);
expect
(
result
).
not
.
toContain
(
'
XSS!!
'
);
});
it
(
'
sanitizes tooltip if data-template is passed
'
,
async
()
=>
{
const
tooltipTemplate
=
escape
(
'
<div class="tooltip" role="tooltip"><div onclick="alert(
\'
XSS!
\'
)" class="arrow"></div><div class="tooltip-inner"></div></div>
'
,
);
createTooltipContainer
({
template
:
`<div v-tooltip :title="tooltip" data-html="false" data-template="
${
tooltipTemplate
}
"></div>`
,
});
await
showTooltip
();
const
result
=
findTooltipHtml
();
expect
(
result
).
toEqual
(
// objectionable element is removed
'
<div class="arrow"></div><div class="tooltip-inner">some text</div>
'
,
);
expect
(
result
).
not
.
toContain
(
'
XSS!!
'
);
});
});
});
describe
(
'
with multiple tooltips
'
,
()
=>
{
beforeEach
(()
=>
{
const
wrapper
=
mount
(
{
directives
:
{
tooltip
,
},
template
:
`
<div>
<div
v-tooltip
class="js-look-for-tooltip"
title="foo">
</div>
<div
v-tooltip
title="bar">
</div>
createTooltipContainer
({
template
:
`
<div>
<div
v-tooltip
class="js-look-for-tooltip"
title="foo">
</div>
`
,
},
{
attachToDocument
:
true
},
);
vm
=
wrapper
.
vm
;
<div
v-tooltip
title="bar">
</div>
</div>
`
,
});
});
it
(
'
should have tooltip plugin applied to all instances
'
,
()
=>
{
expect
(
$
(
vm
.
$el
)
$
(
wrapper
.
vm
.
$el
)
.
find
(
'
.js-look-for-tooltip
'
)
.
data
(
'
bs.tooltip
'
),
).
toBeDefined
();
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment