CVE-2014-2236 (HTML, published 2014-03-05T16:37Z)
Description: value: Multiple cross-site scripting (XSS) vulnerabilities in Askbot before 0.7.49 allow remote attackers to inject arbitrary web script or HTML via vectors related to the (1) tag or (2) user search forms.
Severity: MEDIUM (CVSS v3 base score: )
Weakness: CWE-79 \u2014 Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
Repository: https://github.com/ASKBOT/askbot-devel
Files changed:
askbot/doc/source/changelog.rst
askbot/media/js/live_search.js
askbot/media/style/style.css
askbot/media/style/style.less
askbot/templates/meta/bottom_scripts.html
askbot/templates/tags.html
askbot/templates/tags/content.html
askbot/templates/tags/header.html
askbot/templates/widgets/search_bar.html
askbot/views/readers.py
Commit message: added instant search to the tags page


Vulnerable code:
    element.html(gettext('search or ask your question'));
.tags-page input[type="submit"].searchBtn,
.tags-page,
{% import "macros.html" as macros %}
<!-- Tabs -->
{% include "tags/header.html" %}
{% if tag_list_type == 'list' %}
    {% if not tags.object_list %}
        <span>{% trans %}Nothing found{% endtrans %}</span>
    {% endif %}
    {% if tags.object_list %}
        <div class='clearfix'></div>
        <ul class='tags'>
            {% for tag in tags.object_list %}
            <li>
                {{ macros.tag_widget(
                            tag=tag.name,
                            html_tag='div',
                            truncate_long_tag=True,
                            extra_content='<span class="tag-number">&#215; ' ~
                                            tag.used_count|intcomma ~ '</span>'
                    )
                }}
            </li>
            {% endfor %}
        </ul>
        <div class="clean"></div>
        <div class="pager">
            {{macros.paginator(paginator_context)}}
        </div>
    {% endif %}
{% else %}
    <div class="clearfix"></div>
    {% if not tags %}
        <span>{% trans %}Nothing found{% endtrans %}</span>
    {% endif %}
    {{ macros.tag_cloud(tags=tags, font_sizes=font_size, search_state=search_state) }}
{% endif %}

            <h1 class="section-title">{% trans %}Tags, matching "{{ stag }}"{% endtrans %}</h1>
                href="{% url tags %}?sort=name"
                href="{% url tags %}?sort=used"
        value="{{ query|default_if_none('') }}" 
    sortby = request.GET.get('sort', 'used')
        page = int(request.GET.get('page', '1'))
    query = request.GET.get('query', '').strip()
        query_params['name__icontains': query]
    return render(request, 'tags.html', data)

Fixed code:
* Added instant search to the tags page
    this._promptText = gettext('search or ask your question');
InputToolTip.prototype.setPromptText = function(text) {
    this._promptText = text;
};

    element.html(this._promptText);

/**
 * @constructor
 */
var TagSearch = function() {
    WrappedElement.call(this);
    this._isRunning = false;
};
inherits(TagSearch, WrappedElement);

TagSearch.prototype.getQuery = function() {
    return $.trim(this._element.val());
};

TagSearch.prototype.setQuery = function(val) {
    this._element.val(val);
};

TagSearch.prototype.getSort = function() {
    //todo: read it off the page
    var link = $('.tabBar a.on');
    if (link.length === 1) {
        var sort = link.attr('id').replace('sort_', '');
        if (sort === 'name' || sort === 'used') {
            return sort;
        }
    }
    return 'name';
};

TagSearch.prototype.getIsRunning = function() {
    return this._isRunning;
};

TagSearch.prototype.setIsRunning = function(val) {
    this._isRunning = val;
};

TagSearch.prototype.renderResult = function(html) {
    this._contentBox.html(html);
};

TagSearch.prototype.runSearch = function() {
    var data = {
        'query': this.getQuery(),
        'sort': this.getSort(),
        'page': '1'
    };
    var me = this;
    $.ajax({
        dataType: 'json',
        data: data,
        cache: false,
        url: askbot['urls']['tags'],
        success: function(data) {
            if (data['success']) {
                me.renderResult(data['html']);
                me.setIsRunning(false);
            }
        },
        error: function() { me.setIsRunning(false); }
    });
    me.setIsRunning(true);
};

TagSearch.prototype.getToolTip = function() {
    return this._toolTip;
};

TagSearch.prototype.makeKeyUpHandler = function() {
    var me = this;
    return function(evt) {
        var keyCode = getKeyCode(evt);
        if (me.getIsRunning() === false) {
            me.runSearch();
        }
    };
};

TagSearch.prototype.makeKeyDownHandler = function() {
    var me = this;
    var xButton = this._xButton;
    return function(evt) {
        var query = me.getQuery();
        var keyCode = getKeyCode(evt);
        var toolTip = me.getToolTip();
        if (keyCode === 27) {//escape
            me.setQuery('');
            toolTip.show();
            xButton.hide();
            return;
        }
        if (keyCode === 8 || keyCode === 48) {//del or backspace
            if (query.length === 1) {
                toolTip.show();
                xButton.hide();
            }
        } else {
            toolTip.hide();
            xButton.show();
        }
    };
};

TagSearch.prototype.reset = function() {
    if (this.getIsRunning() === false) {
        this.setQuery('');
        this._toolTip.show();
        this._xButton.hide();
        this.runSearch();
        this._element.focus();
    }
};

TagSearch.prototype.decorate = function(element) {
    this._element = element;
    this._contentBox = $('#ContentLeft');
    this._xButton = $('input[name=reset_query]');
    element.keyup(this.makeKeyUpHandler());
    element.keydown(this.makeKeyDownHandler());

    var me = this;
    this._xButton.click(function(){ me.reset() });

    var toolTip = new InputToolTip();
    toolTip.setPromptText(askbot['data']['tagSearchPromptText']);
    toolTip.setClickHandler(function() {
        element.focus();
    });
    element.after(toolTip.getElement());
    //below is called after getElement, b/c element must be defined
    if (this.getQuery() !== '') {
        toolTip.hide();//hide if search query is not empty
    }
    this._toolTip = toolTip;
};
        } else if (activeTab === 'tags') {
            var search = new TagSearch();
            search.decorate(searchInput);
{% include "tags/content.html" %}
        askbot['data']['tagSearchPromptText'] = "{% trans %}search for tags{% endtrans %}";
    {% include "tags/custom_javascript.httml" ignore missing %}
{% import "macros.html" as macros %}
{% include "tags/header.html" %}
{% if tag_list_type == 'list' %}
    {% if not tags.object_list %}
        <span>{% trans %}Nothing found{% endtrans %}</span>
    {% endif %}
    {% if tags.object_list %}
        <div class='clearfix'></div>
        <ul class='tags'>
            {% for tag in tags.object_list %}
            <li>
                {{ macros.tag_widget(
                            tag=tag.name,
                            html_tag='div',
                            truncate_long_tag=True,
                            extra_content='<span class="tag-number">&#215; ' ~
                                            tag.used_count|intcomma ~ '</span>'
                    )
                }}
            </li>
            {% endfor %}
        </ul>
        <div class="clean"></div>
        <div class="pager">
            {{macros.paginator(paginator_context)}}
        </div>
    {% endif %}
{% else %}
    <div class="clearfix"></div>
    {% if not tags %}
        <span>{% trans %}Nothing found{% endtrans %}</span>
    {% endif %}
    {{ macros.tag_cloud(tags=tags, font_sizes=font_size, search_state=search_state) }}
{% endif %}
    {% set tag_query = stag|escape %}
            <h1 class="section-title">{% trans %}Tags, matching "{{ tag_query }}"{% endtrans %}</h1>
                href="{% url tags %}?sort=name{% if tag_query %}&query={{ tag_query }}{% endif %}"
                href="{% url tags %}?sort=used{% if tag_query %}&query={{ tag_query }}{% endif %}"
        {% set query=stag %}
        value="{{ query|default_if_none('')|escape }}" 
    post_data = request.GET
    sortby = post_data.get('sort', 'used')
        page = int(post_data.get('page', '1'))
    query = post_data.get('query', '').strip()
        query_params['name__icontains'] = query
    if request.is_ajax():
        template = get_template('tags/content.html')
        template_context = RequestContext(request, data)
        json_data = {'success': True, 'html': template.render(template_context)}
        json_string = simplejson.dumps(json_data)
        return HttpResponse(json_string, mimetype='application/json')
    else:
        return render(request, 'tags.html', data)