Skip to content
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

Merged
merged 1 commit into from
Mar 24, 2020

Conversation

usingsky
Copy link
Contributor

@usingsky usingsky commented Nov 8, 2019

Fixes #
Prevent code injection into autocomplete module
e.g.)

  • make group with name "<script>alert('injection')</script>
  • change the group of dataset.
  • injection code is performed by pressing the autocomplete input element

Proposed fixes:

Check markup in select2 library before rendering

Features:

  • includes tests covering changes
  • includes updated documentation
  • includes user-visible changes
  • includes API changes
  • includes bugfix for possible backport

Please [X] all the boxes above that apply

@usingsky
Copy link
Contributor Author

usingsky commented Dec 2, 2019

Fixed to use default escapeMarkup function.
refer to formatResult

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);
Copy link
Contributor

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>&lt;script&gt;</b></div>

Copy link
Contributor Author

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());
Copy link
Contributor

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.

Copy link
Contributor Author

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());
Copy link
Contributor

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

Copy link
Contributor Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants