The bok-choy framework includes the ability to perform XSS (cross-site scripting) audits on web pages using a short XSS locator defined in https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#XSS_Locator.
You might already have some bok-choy tests written for your web application. To leverage existing bok-choy tests and have them fail on finding XSS vulnerabilities, follow these steps.
- Insert the
XSS_INJECTION
string defined inbok_choy.page_object
into your page content. - Set the
VERIFY_XSS
environment variable toTrue
.
export VERIFY_XSS=True
With this environment variable set, an XSS audit is triggered whenever a page object's q
method is called. The audit will detect improper escaping both in HTML and in Javascript
that is embedded within HTML.
If errors are found on the page, an XSSExposureError is raised.
Here is an example of a bok-choy test that will check for XSS vulnerabilities. It clicks a button on the page, and the user's name is inserted into the page. If the user name is not properly escaped, the display of the name (which is data provided by the user and thus potentially malicious) can cause XSS issues.
In the case of the test_button_click_output
test case in the example below,
an audit will be done in the click_button()
, output()
, and visit()
method calls,
as each of those will call out to q
.
If any XSS errors are found, then the test case will fail with an XSSExposureError.
from bok_choy.page_object import PageObject, XSS_INJECTION
class MyPage(PageObject):
def url(self):
return 'https://www.mysite.com/page'
def is_browser_on_page(self):
return self.q(css='div#fixture button').present
def click_button(self):
"""
Click on the button element (id="button").
On my example page this will trigger an ajax call
that updates the #output div with the user's name.
"""
self.q(css='div#fixture button').first.click()
self.wait_for_ajax()
@property
def output(self):
"""
Return the contents of the "#output" div on the page.
In the example page, it will contain the user's name after being
updated by the ajax call that is triggered by clicking the button.
"""
text_list = self.q(css='#output').text
if len(text_list) < 1:
return None
else:
return text_list[0]
class MyPageTest(WebAppTest):
def setUp(self):
"""
Log in as a particular user.
"""
super(MyPageTest, self).setUp()
self.user_name = XSS_INJECTION
self.log_in_as_user(self.user_name)
def test_button_click_output(self):
page = MyPage(self.browser)
page.visit()
page.click_button()
self.assertEqual(page.output, self.user_name)
def log_in_as_user(self, user_name):
"""
Would be implemented to log in as a particular user
with a potentially malicious, user-provided name.
"""
pass