CVE-2014-2235 (HTML, published 2014-03-05T16:37Z)
Description: value: Cross-site scripting (XSS) vulnerability in Askbot before 0.7.49 allows remote attackers to inject arbitrary web script or HTML via vectors related to the question search form.
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)