CVE-2015-10049: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: A vulnerability was found in Overdrive Eletrônica course-builder up to 1.7.x and classified as problematic. Affected by this issue is some unknown functionality of the file coursebuilder/modules/oeditor/oeditor.html. The manipulation leads to cross site scripting. The attack may be launched remotely. Upgrading to version 1.8.0 is able to address this issue. The name of the patch is e39645fd714adb7e549908780235911ae282b21b. It is recommended to upgrade the affected component. The identifier of this vulnerability is VDB-218372.
CWE description: 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.
CVSS v3 base score: 6.1
Published: 2023-01-15T18:15Z
Repository: https://github.com/overdrive-diy/course-builder
Language: Python
Files changed: coursebuilder/modules/oeditor/oeditor.html, coursebuilder/modules/oeditor/oeditor.py, coursebuilder/tests/functional/admin_settings.py
Commit date: 2015-04-03 02:31:46 -0400
Commit message: Fix XSS vulnerability in which unescaped template values were being pasted into OEditor fields.
	Change on 2015/03/24 by jorr <jorr@google.com>
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=89464922

Diff: diff --git a/coursebuilder/modules/oeditor/oeditor.html b/coursebuilder/modules/oeditor/oeditor.html
index b6f9bb09..72707ccc 100644
--- a/coursebuilder/modules/oeditor/oeditor.html
+++ b/coursebuilder/modules/oeditor/oeditor.html
@@ -68,36 +68,36 @@
   (function() {
     //keep all our global objects here
     var cb_global = {};
 
     // various URL's needed by the form
-    cb_global.get_url = '{{ get_url }}';
-    cb_global.save_url = '{{ save_url }}';
+    cb_global.get_url = '{{ get_url|js_string }}';
+    cb_global.save_url = '{{ save_url|js_string }}';
     cb_global.save_args = JSON.parse("{{ save_args|js_string }}");
-    cb_global.exit_button_caption = '{{ exit_button_caption }}';
-    cb_global.exit_url = '{{ exit_url }}';
-    cb_global.save_method = '{{ save_method }}';
+    cb_global.exit_button_caption = '{{ exit_button_caption|js_string }}';
+    cb_global.exit_url = '{{ exit_url|js_string }}';
+    cb_global.save_method = '{{ save_method|js_string }}';
     cb_global.auto_return = {% if auto_return %}true{% else %}false{% endif %};
     cb_global.bundle_lib_files = {% if bundle_lib_files %}true{% else %}false{% endif %};
 
     // If this value is '', deletions are not allowed
-    cb_global.delete_url = '{{ delete_url }}';
-    cb_global.delete_method = '{{ delete_method }}';
+    cb_global.delete_url = '{{ delete_url|js_string }}';
+    cb_global.delete_method = '{{ delete_method|js_string }}';
 
     // button captions
-    cb_global.delete_button_caption = '{{ delete_button_caption }}'
-    cb_global.save_button_caption = '{{ save_button_caption }}'
+    cb_global.delete_button_caption = '{{ delete_button_caption|js_string }}'
+    cb_global.save_button_caption = '{{ save_button_caption|js_string }}'
     cb_global.custom_rte_tag_icons = JSON.parse("{{ custom_rte_tag_icons|js_string }}");
 
     cb_global.required_modules = [{% for module in required_modules %}
-        '{{ module }}'{% if not loop.last %},{% endif %}{% endfor %}];
+        '{{ module|js_string }}'{% if not loop.last %},{% endif %}{% endfor %}];
     cb_global.schema = JSON.parse("{{ schema|js_string }}");
-    cb_global.delete_message = '{{ delete_message }}';
+    cb_global.delete_message = '{{ delete_message|js_string }}';
     cb_global.can_highlight_code = {% if can_highlight_code %}true{% else %}false{% endif %};
     cb_global.load_schema_with_annotations = function(schema) {
       {% for annotation in schema_annotations %}
-        schema.root{% for element in annotation[0] %}['{{element}}']{% endfor %}
+        schema.root{% for element in annotation[0] %}['{{element|js_string}}']{% endfor %}
           = JSON.parse("{{ annotation[1]|js_string }}")
       {% endfor %}
     }
     cb_global.onFormLoad = function(Y) {
       {% for file in extra_js_files %}
diff --git a/coursebuilder/modules/oeditor/oeditor.py b/coursebuilder/modules/oeditor/oeditor.py
index a78f4481..6d4bfcbe 100644
--- a/coursebuilder/modules/oeditor/oeditor.py
+++ b/coursebuilder/modules/oeditor/oeditor.py
@@ -145,11 +145,11 @@ class ObjectEditor(object):
             'schema': schema_json,
             'get_url': '%s?%s' % (get_url, urllib.urlencode(get_args, True)),
             'save_url': post_url,
             'save_args': transforms.dumps(post_args),
             'exit_button_caption': exit_button_caption,
-            'exit_url': jinja2.Markup(exit_url),  # suppress & -> &amp; in url
+            'exit_url': exit_url,
             'required_modules': COMMON_REQUIRED_MODULES + required_modules,
             'extra_css_files': extra_css_files or [],
             'extra_js_files': extra_js_files or [],
             'schema_annotations': [
                 (item[0], transforms.dumps(item[1])) for item in annotations],
diff --git a/coursebuilder/tests/functional/admin_settings.py b/coursebuilder/tests/functional/admin_settings.py
index a3696523..154b65cd 100644
--- a/coursebuilder/tests/functional/admin_settings.py
+++ b/coursebuilder/tests/functional/admin_settings.py
@@ -557,7 +557,8 @@ class ExitUrlTest(actions.TestBase):
             'exit_url': 'dashboard?%s' % urllib.urlencode({
                 'action': 'analytics',
                 'tab': 'data_pump'})})
         response = self.get(url)
         self.assertIn(
-            'cb_global.exit_url = \'dashboard?action=analytics&tab=data_pump\'',
+            'cb_global.exit_url = \'dashboard?action=analytics'
+            '\\u0026tab=data_pump\'',
             response.body)

Vulnerable code:     cb_global.get_url = '{{ get_url }}';
    cb_global.save_url = '{{ save_url }}';
    cb_global.exit_button_caption = '{{ exit_button_caption }}';
    cb_global.exit_url = '{{ exit_url }}';
    cb_global.save_method = '{{ save_method }}';
    cb_global.delete_url = '{{ delete_url }}';
    cb_global.delete_method = '{{ delete_method }}';
    cb_global.delete_button_caption = '{{ delete_button_caption }}'
    cb_global.save_button_caption = '{{ save_button_caption }}'
        '{{ module }}'{% if not loop.last %},{% endif %}{% endfor %}];
    cb_global.delete_message = '{{ delete_message }}';
        schema.root{% for element in annotation[0] %}['{{element}}']{% endfor %}
            'exit_url': jinja2.Markup(exit_url),  # suppress & -> &amp; in url
            'cb_global.exit_url = \'dashboard?action=analytics&tab=data_pump\'',
Fixed code:     cb_global.get_url = '{{ get_url|js_string }}';
    cb_global.save_url = '{{ save_url|js_string }}';
    cb_global.exit_button_caption = '{{ exit_button_caption|js_string }}';
    cb_global.exit_url = '{{ exit_url|js_string }}';
    cb_global.save_method = '{{ save_method|js_string }}';
    cb_global.delete_url = '{{ delete_url|js_string }}';
    cb_global.delete_method = '{{ delete_method|js_string }}';
    cb_global.delete_button_caption = '{{ delete_button_caption|js_string }}'
    cb_global.save_button_caption = '{{ save_button_caption|js_string }}'
        '{{ module|js_string }}'{% if not loop.last %},{% endif %}{% endfor %}];
    cb_global.delete_message = '{{ delete_message|js_string }}';
        schema.root{% for element in annotation[0] %}['{{element|js_string}}']{% endfor %}
            'exit_url': exit_url,
            'cb_global.exit_url = \'dashboard?action=analytics'
            '\\u0026tab=data_pump\'',