-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix code injection in autocomplete module #5064
Conversation
7526340
to
0cdbd7f
Compare
0cdbd7f
to
1d16276
Compare
1d16276
to
58f5a9c
Compare
Fixed to use default escapeMarkup function. |
58f5a9c
to
662ce1a
Compare
662ce1a
to
9a47c26
Compare
formatResult: function (state, container, query) { | ||
var term = this._lastTerm || null; // same as query.term | ||
formatResult: function (state, container, query, escapeMarkup) { | ||
var term = this._lastTerm || (query ? query.term : null) || null; // same as query.term | ||
|
||
if (container) { | ||
// Append the select id to the element for styling. | ||
container.attr('data-value', state.id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was pre-existing, nothing you added :) It seems to add the raw value to the element attribute. It does not trigger any script execution but stood out now that the other areas are escaping values.
e.g. data-value="" is the raw input. <div class="select2-result-label" id="select2-result-label-21" role="option" data-value="<script>"><b><script></b></div>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"term" is the query text you entered.
In the case of the this._lasterm variable, it will be updated when the query is executed as an ajax request (for example, autocomplete on an add memeber page).
However, in case of autocomplete of add group page, All option values are updated before page rendering, not ajax request.
As a result, the query text you entered is updated in the "query.term" variable.
If you test without the patch, you will see that the query text is not bolded.(on add group page)
|
||
if (container) { | ||
// Append the select id to the element for styling. | ||
container.attr('data-value', state.id); | ||
} | ||
|
||
return state.text.split(term).join(term && term.bold()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This original line is a bit confusing to me. I'm not sure what it was intended to do. I understand it as:
It seems to always create a 2 element array with empty strings then joins those with the bolded term.
state.text.split(term) => ['', '']
['', ''].join(term && term.bold()) => <b>apples</b>
logic:
state.text
and term
seem to be identical values. So the state.text
is split on itself (e.g. 'apples'.split('apples')
) which gives an array with 2 empty elements. Then the array is joined by the term and if the term exists, bold it (e.g. ['', ''].join('apples' && 'apples'.bold())
, if the term is null it returns null.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"state.text" means full text and "term" is the query text you entered.
eg) state.text : "<script>alert('test');</script>", term : alert
after split : ["<script>", "('test');</script>"]
after join : "<script>alert('test');</script>"
eg) state.text : "<script>alert('test');</script>", term : null
after split : ["<script>alert('test');</script>"]
after join : "<script>alert('test');</script>"
result.push(escapeMarkup ? escapeMarkup(this) : this); | ||
}); | ||
|
||
return result.join(term && (escapeMarkup ? escapeMarkup(term) : term).bold()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the above comment in mind, you could probably be able to get away with removing lines 197 to 200, because its escaping empty strings. And then changing line 202 to something like return state.text.split(term).join(term && (escapeMarkup ? escapeMarkup(term) : term).bold());
But test before making changes, i've looked at it too long lol
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my opinion, xss issue will not be resolved without escaping state.text.
Fixes #
Prevent code injection into autocomplete module
e.g.)
Proposed fixes:
Check markup in select2 library before rendering
Features:
Please [X] all the boxes above that apply