Commit 0c993788 by cahrens Committed by Eric Fischer

Enabling XSS vulnerability flag for bok choy tests

parent b95dadcf
...@@ -4,7 +4,7 @@ Auto-auth page (used to automatically log in during testing). ...@@ -4,7 +4,7 @@ Auto-auth page (used to automatically log in during testing).
import re import re
import urllib import urllib
from bok_choy.page_object import PageObject, unguarded from bok_choy.page_object import PageObject, unguarded, XSS_INJECTION
from . import AUTH_BASE_URL from . import AUTH_BASE_URL
...@@ -17,7 +17,7 @@ class AutoAuthPage(PageObject): ...@@ -17,7 +17,7 @@ class AutoAuthPage(PageObject):
CONTENT_REGEX = r'.+? user (?P<username>\S+) \((?P<email>.+?)\) with password \S+ and user_id (?P<user_id>\d+)$' CONTENT_REGEX = r'.+? user (?P<username>\S+) \((?P<email>.+?)\) with password \S+ and user_id (?P<user_id>\d+)$'
def __init__(self, browser, username=None, email=None, password=None, staff=None, course_id=None, def __init__(self, browser, username=None, email=None, password=None, full_name=None, staff=None, course_id=None,
enrollment_mode=None, roles=None): enrollment_mode=None, roles=None):
""" """
Auto-auth is an end-point for HTTP GET requests. Auto-auth is an end-point for HTTP GET requests.
...@@ -25,6 +25,7 @@ class AutoAuthPage(PageObject): ...@@ -25,6 +25,7 @@ class AutoAuthPage(PageObject):
but you can also specify credentials using querystring parameters. but you can also specify credentials using querystring parameters.
`username`, `email`, and `password` are the user's credentials (strings) `username`, `email`, and `password` are the user's credentials (strings)
'full_name' is the profile full name value
`staff` is a boolean indicating whether the user is global staff. `staff` is a boolean indicating whether the user is global staff.
`course_id` is the ID of the course to enroll the student in. `course_id` is the ID of the course to enroll the student in.
Currently, this has the form "org/number/run" Currently, this has the form "org/number/run"
...@@ -42,6 +43,8 @@ class AutoAuthPage(PageObject): ...@@ -42,6 +43,8 @@ class AutoAuthPage(PageObject):
if username is not None: if username is not None:
self._params['username'] = username self._params['username'] = username
self._params['full_name'] = full_name if full_name is not None else XSS_INJECTION
if email is not None: if email is not None:
self._params['email'] = email self._params['email'] = email
......
...@@ -16,6 +16,7 @@ from path import Path as path ...@@ -16,6 +16,7 @@ from path import Path as path
from bok_choy.javascript import js_defined from bok_choy.javascript import js_defined
from bok_choy.web_app_test import WebAppTest from bok_choy.web_app_test import WebAppTest
from bok_choy.promise import EmptyPromise, Promise from bok_choy.promise import EmptyPromise, Promise
from bok_choy.page_object import XSS_INJECTION
from opaque_keys.edx.locator import CourseLocator from opaque_keys.edx.locator import CourseLocator
from pymongo import MongoClient, ASCENDING from pymongo import MongoClient, ASCENDING
from openedx.core.lib.tests.assertions.events import assert_event_matches, is_matching_event, EventMatchTolerates from openedx.core.lib.tests.assertions.events import assert_event_matches, is_matching_event, EventMatchTolerates
...@@ -640,7 +641,7 @@ class UniqueCourseTest(WebAppTest): ...@@ -640,7 +641,7 @@ class UniqueCourseTest(WebAppTest):
'org': 'test_org', 'org': 'test_org',
'number': self.unique_id, 'number': self.unique_id,
'run': 'test_run', 'run': 'test_run',
'display_name': 'Test Course' + self.unique_id 'display_name': 'Test Course' + XSS_INJECTION + self.unique_id
} }
@property @property
......
...@@ -6,6 +6,7 @@ from unittest import skip ...@@ -6,6 +6,7 @@ from unittest import skip
from nose.plugins.attrib import attr from nose.plugins.attrib import attr
from bok_choy.web_app_test import WebAppTest from bok_choy.web_app_test import WebAppTest
from bok_choy.page_object import XSS_INJECTION
from ...pages.lms.account_settings import AccountSettingsPage from ...pages.lms.account_settings import AccountSettingsPage
from ...pages.lms.auto_auth import AutoAuthPage from ...pages.lms.auto_auth import AutoAuthPage
...@@ -33,12 +34,12 @@ class AccountSettingsTestMixin(EventsTestMixin, WebAppTest): ...@@ -33,12 +34,12 @@ class AccountSettingsTestMixin(EventsTestMixin, WebAppTest):
self.account_settings_page.visit() self.account_settings_page.visit()
self.account_settings_page.wait_for_ajax() self.account_settings_page.wait_for_ajax()
def log_in_as_unique_user(self, email=None): def log_in_as_unique_user(self, email=None, full_name=None):
""" """
Create a unique user and return the account's username and id. Create a unique user and return the account's username and id.
""" """
username = "test_{uuid}".format(uuid=self.unique_id[0:6]) username = "test_{uuid}".format(uuid=self.unique_id[0:6])
auto_auth_page = AutoAuthPage(self.browser, username=username, email=email).visit() auto_auth_page = AutoAuthPage(self.browser, username=username, email=email, full_name=full_name).visit()
user_id = auto_auth_page.get_user_id() user_id = auto_auth_page.get_user_id()
return username, user_id return username, user_id
...@@ -122,7 +123,8 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, WebAppTest): ...@@ -122,7 +123,8 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, WebAppTest):
Initialize account and pages. Initialize account and pages.
""" """
super(AccountSettingsPageTest, self).setUp() super(AccountSettingsPageTest, self).setUp()
self.username, self.user_id = self.log_in_as_unique_user() self.full_name = XSS_INJECTION
self.username, self.user_id = self.log_in_as_unique_user(full_name=self.full_name)
self.visit_account_settings_page() self.visit_account_settings_page()
def test_page_view_event(self): def test_page_view_event(self):
...@@ -259,16 +261,16 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, WebAppTest): ...@@ -259,16 +261,16 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, WebAppTest):
self._test_text_field( self._test_text_field(
u'name', u'name',
u'Full Name', u'Full Name',
self.username, self.full_name,
u'@', u'@',
[u'another name', self.username], [u'another name', self.full_name],
) )
actual_events = self.wait_for_events(event_filter=self.settings_changed_event_filter, number_of_matches=2) actual_events = self.wait_for_events(event_filter=self.settings_changed_event_filter, number_of_matches=2)
self.assert_events_match( self.assert_events_match(
[ [
self.expected_settings_changed_event('name', self.username, 'another name'), self.expected_settings_changed_event('name', self.full_name, 'another name'),
self.expected_settings_changed_event('name', 'another name', self.username), self.expected_settings_changed_event('name', 'another name', self.full_name),
], ],
actual_events actual_events
) )
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
Base classes used by studio tests. Base classes used by studio tests.
""" """
from bok_choy.web_app_test import WebAppTest from bok_choy.web_app_test import WebAppTest
from bok_choy.page_object import XSS_INJECTION
from ...pages.studio.auto_auth import AutoAuthPage from ...pages.studio.auto_auth import AutoAuthPage
from ...fixtures.course import CourseFixture from ...fixtures.course import CourseFixture
from ...fixtures.library import LibraryFixture from ...fixtures.library import LibraryFixture
...@@ -15,11 +16,12 @@ class StudioCourseTest(UniqueCourseTest): ...@@ -15,11 +16,12 @@ class StudioCourseTest(UniqueCourseTest):
Base class for all Studio course tests. Base class for all Studio course tests.
""" """
def setUp(self, is_staff=False): def setUp(self, is_staff=False, test_xss=True): # pylint: disable=arguments-differ
""" """
Install a course with no content using a fixture. Install a course with no content using a fixture.
""" """
super(StudioCourseTest, self).setUp() super(StudioCourseTest, self).setUp()
self.test_xss = test_xss
self.install_course_fixture(is_staff) self.install_course_fixture(is_staff)
def install_course_fixture(self, is_staff=False): def install_course_fixture(self, is_staff=False):
...@@ -30,8 +32,21 @@ class StudioCourseTest(UniqueCourseTest): ...@@ -30,8 +32,21 @@ class StudioCourseTest(UniqueCourseTest):
self.course_info['org'], self.course_info['org'],
self.course_info['number'], self.course_info['number'],
self.course_info['run'], self.course_info['run'],
self.course_info['display_name'] self.course_info['display_name'],
) )
if self.test_xss:
xss_injected_unique_id = XSS_INJECTION + self.unique_id
test_improper_escaping = {u"value": xss_injected_unique_id}
self.course_fixture.add_advanced_settings({
"advertised_start": test_improper_escaping,
"info_sidebar_name": test_improper_escaping,
"cert_name_short": test_improper_escaping,
"cert_name_long": test_improper_escaping,
"display_organization": test_improper_escaping,
"display_coursenumber": test_improper_escaping,
})
self.course_info['display_organization'] = xss_injected_unique_id
self.course_info['display_coursenumber'] = xss_injected_unique_id
self.populate_course_fixture(self.course_fixture) self.populate_course_fixture(self.course_fixture)
self.course_fixture.install() self.course_fixture.install()
self.user = self.course_fixture.user self.user = self.course_fixture.user
......
...@@ -61,8 +61,8 @@ class CourseTeamPageTest(StudioCourseTest): ...@@ -61,8 +61,8 @@ class CourseTeamPageTest(StudioCourseTest):
def check_course_equality(course1, course2): def check_course_equality(course1, course2):
""" Compares to course dictionaries using org, number and run as keys""" """ Compares to course dictionaries using org, number and run as keys"""
return ( return (
course1['org'] == course2['org'] and course1['org'] == course2['display_organization'] and
course1['number'] == course2['number'] and course1['number'] == course2['display_coursenumber'] and
course1['run'] == course2['run'] course1['run'] == course2['run']
) )
......
...@@ -19,7 +19,7 @@ class CertificatesTest(StudioCourseTest): ...@@ -19,7 +19,7 @@ class CertificatesTest(StudioCourseTest):
Tests for settings/certificates Page. Tests for settings/certificates Page.
""" """
def setUp(self): # pylint: disable=arguments-differ def setUp(self): # pylint: disable=arguments-differ
super(CertificatesTest, self).setUp(is_staff=True) super(CertificatesTest, self).setUp(is_staff=True, test_xss=False)
self.certificates_page = CertificatesPage( self.certificates_page = CertificatesPage(
self.browser, self.browser,
self.course_info['org'], self.course_info['org'],
......
...@@ -18,6 +18,7 @@ from ..pages.lms.auto_auth import AutoAuthPage as LmsAutoAuthPage ...@@ -18,6 +18,7 @@ from ..pages.lms.auto_auth import AutoAuthPage as LmsAutoAuthPage
from ..tests.lms.test_lms_user_preview import verify_expected_problem_visibility from ..tests.lms.test_lms_user_preview import verify_expected_problem_visibility
from bok_choy.promise import EmptyPromise from bok_choy.promise import EmptyPromise
from bok_choy.page_object import XSS_INJECTION
@attr('shard_5') @attr('shard_5')
...@@ -28,8 +29,8 @@ class EndToEndCohortedCoursewareTest(ContainerBase): ...@@ -28,8 +29,8 @@ class EndToEndCohortedCoursewareTest(ContainerBase):
super(EndToEndCohortedCoursewareTest, self).setUp(is_staff=is_staff) super(EndToEndCohortedCoursewareTest, self).setUp(is_staff=is_staff)
self.staff_user = self.user self.staff_user = self.user
self.content_group_a = "Content Group A" self.content_group_a = "Content Group A" + XSS_INJECTION
self.content_group_b = "Content Group B" self.content_group_b = "Content Group B" + XSS_INJECTION
# Create a student who will be in "Cohort A" # Create a student who will be in "Cohort A"
self.cohort_a_student_username = "cohort_a_student" self.cohort_a_student_username = "cohort_a_student"
......
...@@ -25,6 +25,7 @@ BOKCHOY_OPTS = [ ...@@ -25,6 +25,7 @@ BOKCHOY_OPTS = [
('default_store=', 's', 'Default modulestore'), ('default_store=', 's', 'Default modulestore'),
('test_dir=', 'd', 'Directory for finding tests (relative to common/test/acceptance)'), ('test_dir=', 'd', 'Directory for finding tests (relative to common/test/acceptance)'),
('num_processes=', 'n', 'Number of test threads (for multiprocessing)'), ('num_processes=', 'n', 'Number of test threads (for multiprocessing)'),
('verify_xss', 'x', 'Run XSS vulnerability tests'),
make_option("--verbose", action="store_const", const=2, dest="verbosity"), make_option("--verbose", action="store_const", const=2, dest="verbosity"),
make_option("-q", "--quiet", action="store_const", const=0, dest="verbosity"), make_option("-q", "--quiet", action="store_const", const=0, dest="verbosity"),
make_option("-v", "--verbosity", action="count", dest="verbosity"), make_option("-v", "--verbosity", action="count", dest="verbosity"),
...@@ -43,6 +44,7 @@ def parse_bokchoy_opts(options): ...@@ -43,6 +44,7 @@ def parse_bokchoy_opts(options):
'test_spec': getattr(options, 'test_spec', None), 'test_spec': getattr(options, 'test_spec', None),
'fasttest': getattr(options, 'fasttest', False), 'fasttest': getattr(options, 'fasttest', False),
'num_processes': int(getattr(options, 'num_processes', 1)), 'num_processes': int(getattr(options, 'num_processes', 1)),
'verify_xss': getattr(options, 'verify_xss', False),
'serversonly': getattr(options, 'serversonly', False), 'serversonly': getattr(options, 'serversonly', False),
'testsonly': getattr(options, 'testsonly', False), 'testsonly': getattr(options, 'testsonly', False),
'default_store': getattr(options, 'default_store', os.environ.get('DEFAULT_STORE', 'split')), 'default_store': getattr(options, 'default_store', os.environ.get('DEFAULT_STORE', 'split')),
......
...@@ -4,6 +4,7 @@ Run just this test with: paver test_lib -t pavelib/paver_tests/test_paver_bok_ch ...@@ -4,6 +4,7 @@ Run just this test with: paver test_lib -t pavelib/paver_tests/test_paver_bok_ch
""" """
import os import os
import unittest import unittest
from test.test_support import EnvironmentVarGuard
from paver.easy import BuildFailure from paver.easy import BuildFailure
from pavelib.utils.test.suites import BokChoyTestSuite from pavelib.utils.test.suites import BokChoyTestSuite
...@@ -15,7 +16,7 @@ class TestPaverBokChoyCmd(unittest.TestCase): ...@@ -15,7 +16,7 @@ class TestPaverBokChoyCmd(unittest.TestCase):
Paver Bok Choy Command test cases Paver Bok Choy Command test cases
""" """
def _expected_command(self, name, store=None): def _expected_command(self, name, store=None, verify_xss=False):
""" """
Returns the command that is expected to be run for the given test spec Returns the command that is expected to be run for the given test spec
and store. and store.
...@@ -27,6 +28,7 @@ class TestPaverBokChoyCmd(unittest.TestCase): ...@@ -27,6 +28,7 @@ class TestPaverBokChoyCmd(unittest.TestCase):
"BOK_CHOY_HAR_DIR='{repo_dir}/test_root/log{shard_str}/hars' " "BOK_CHOY_HAR_DIR='{repo_dir}/test_root/log{shard_str}/hars' "
"BOKCHOY_A11Y_CUSTOM_RULES_FILE='{repo_dir}/{a11y_custom_file}' " "BOKCHOY_A11Y_CUSTOM_RULES_FILE='{repo_dir}/{a11y_custom_file}' "
"SELENIUM_DRIVER_LOG_DIR='{repo_dir}/test_root/log{shard_str}' " "SELENIUM_DRIVER_LOG_DIR='{repo_dir}/test_root/log{shard_str}' "
"VERIFY_XSS='{verify_xss}' "
"nosetests {repo_dir}/common/test/acceptance/{exp_text} " "nosetests {repo_dir}/common/test/acceptance/{exp_text} "
"--with-xunit " "--with-xunit "
"--xunit-file={repo_dir}/reports/bok_choy{shard_str}/xunit.xml " "--xunit-file={repo_dir}/reports/bok_choy{shard_str}/xunit.xml "
...@@ -37,12 +39,14 @@ class TestPaverBokChoyCmd(unittest.TestCase): ...@@ -37,12 +39,14 @@ class TestPaverBokChoyCmd(unittest.TestCase):
shard_str='/shard_' + self.shard if self.shard else '', shard_str='/shard_' + self.shard if self.shard else '',
exp_text=name, exp_text=name,
a11y_custom_file='node_modules/edx-custom-a11y-rules/lib/custom_a11y_rules.js', a11y_custom_file='node_modules/edx-custom-a11y-rules/lib/custom_a11y_rules.js',
verify_xss=verify_xss
) )
return expected_statement return expected_statement
def setUp(self): def setUp(self):
super(TestPaverBokChoyCmd, self).setUp() super(TestPaverBokChoyCmd, self).setUp()
self.shard = os.environ.get('SHARD') self.shard = os.environ.get('SHARD')
self.env_var_override = EnvironmentVarGuard()
def test_default(self): def test_default(self):
suite = BokChoyTestSuite('') suite = BokChoyTestSuite('')
...@@ -89,6 +93,18 @@ class TestPaverBokChoyCmd(unittest.TestCase): ...@@ -89,6 +93,18 @@ class TestPaverBokChoyCmd(unittest.TestCase):
suite = BokChoyTestSuite('', serversonly=True) suite = BokChoyTestSuite('', serversonly=True)
self.assertEqual(suite.cmd, "") self.assertEqual(suite.cmd, "")
def test_verify_xss(self):
suite = BokChoyTestSuite('', verify_xss=True)
name = 'tests'
self.assertEqual(suite.cmd, self._expected_command(name=name, verify_xss=True))
def test_verify_xss_env_var(self):
self.env_var_override.set('VERIFY_XSS', 'True')
with self.env_var_override:
suite = BokChoyTestSuite('')
name = 'tests'
self.assertEqual(suite.cmd, self._expected_command(name=name, verify_xss=True))
def test_test_dir(self): def test_test_dir(self):
test_dir = 'foo' test_dir = 'foo'
suite = BokChoyTestSuite('', test_dir=test_dir) suite = BokChoyTestSuite('', test_dir=test_dir)
......
...@@ -37,6 +37,7 @@ class BokChoyTestSuite(TestSuite): ...@@ -37,6 +37,7 @@ class BokChoyTestSuite(TestSuite):
default_store - modulestore to use when running tests (split or draft) default_store - modulestore to use when running tests (split or draft)
num_processes - number of processes or threads to use in tests. Recommendation is that this num_processes - number of processes or threads to use in tests. Recommendation is that this
is less than or equal to the number of available processors. is less than or equal to the number of available processors.
verify_xss - when set, check for XSS vulnerabilities in the page HTML.
See nosetest documentation: http://nose.readthedocs.org/en/latest/usage.html See nosetest documentation: http://nose.readthedocs.org/en/latest/usage.html
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
...@@ -53,6 +54,7 @@ class BokChoyTestSuite(TestSuite): ...@@ -53,6 +54,7 @@ class BokChoyTestSuite(TestSuite):
self.default_store = kwargs.get('default_store', None) self.default_store = kwargs.get('default_store', None)
self.verbosity = kwargs.get('verbosity', DEFAULT_VERBOSITY) self.verbosity = kwargs.get('verbosity', DEFAULT_VERBOSITY)
self.num_processes = kwargs.get('num_processes', DEFAULT_NUM_PROCESSES) self.num_processes = kwargs.get('num_processes', DEFAULT_NUM_PROCESSES)
self.verify_xss = kwargs.get('verify_xss', False)
self.extra_args = kwargs.get('extra_args', '') self.extra_args = kwargs.get('extra_args', '')
self.har_dir = self.log_dir / 'hars' self.har_dir = self.log_dir / 'hars'
self.a11y_file = Env.BOK_CHOY_A11Y_CUSTOM_RULES_FILE self.a11y_file = Env.BOK_CHOY_A11Y_CUSTOM_RULES_FILE
...@@ -223,6 +225,7 @@ class BokChoyTestSuite(TestSuite): ...@@ -223,6 +225,7 @@ class BokChoyTestSuite(TestSuite):
"BOK_CHOY_HAR_DIR='{}'".format(self.har_dir), "BOK_CHOY_HAR_DIR='{}'".format(self.har_dir),
"BOKCHOY_A11Y_CUSTOM_RULES_FILE='{}'".format(self.a11y_file), "BOKCHOY_A11Y_CUSTOM_RULES_FILE='{}'".format(self.a11y_file),
"SELENIUM_DRIVER_LOG_DIR='{}'".format(self.log_dir), "SELENIUM_DRIVER_LOG_DIR='{}'".format(self.log_dir),
"VERIFY_XSS='{}'".format(self.verify_xss),
"nosetests", "nosetests",
test_spec, test_spec,
"{}".format(self.verbosity_processes_string()) "{}".format(self.verbosity_processes_string())
......
...@@ -136,7 +136,7 @@ django_debug_toolbar==1.3.2 ...@@ -136,7 +136,7 @@ django_debug_toolbar==1.3.2
# Used for testing # Used for testing
before_after==0.1.3 before_after==0.1.3
bok-choy==0.4.10 # bok-choy==0.4.10
chrono==1.0.2 chrono==1.0.2
coverage==4.0.2 coverage==4.0.2
ddt==0.8.0 ddt==0.8.0
......
...@@ -98,3 +98,5 @@ git+https://github.com/edx/xblock-lti-consumer.git@v1.0.3#egg=xblock-lti-consume ...@@ -98,3 +98,5 @@ git+https://github.com/edx/xblock-lti-consumer.git@v1.0.3#egg=xblock-lti-consume
-e git+https://github.com/mitodl/edx-sga@172a90fd2738f8142c10478356b2d9ed3e55334a#egg=edx-sga -e git+https://github.com/mitodl/edx-sga@172a90fd2738f8142c10478356b2d9ed3e55334a#egg=edx-sga
-e git+https://github.com/open-craft/xblock-poll@e7a6c95c300e95c51e42bfd1eba70489c05a6527#egg=xblock-poll -e git+https://github.com/open-craft/xblock-poll@e7a6c95c300e95c51e42bfd1eba70489c05a6527#egg=xblock-poll
git+https://github.com/edx-solutions/xblock-drag-and-drop-v2@v2.0.4#egg=xblock-drag-and-drop-v2==2.0.4 git+https://github.com/edx-solutions/xblock-drag-and-drop-v2@v2.0.4#egg=xblock-drag-and-drop-v2==2.0.4
-e git+https://github.com/edx/bok-choy@christina/xss#egg=bok-choy
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment