diff --git a/ava/actives/xss.py b/ava/actives/xss.py index e33131e..02445c0 100644 --- a/ava/actives/xss.py +++ b/ava/actives/xss.py @@ -1,9 +1,10 @@ import string -from urllib.parse import urlparse +from urllib.parse import urlparse, unquote from bs4 import BeautifulSoup from ava.common import utility from ava.common.check import _ValueCheck from ava.common.constant import HTTP +import re # metadata name = __name__ @@ -80,7 +81,9 @@ class CrossSiteScriptingLinkCheck(_ValueCheck): def __init__(self): """Define static payload""" payloads = [ - "javascript:{}()" + "javascript:{}()", + "%22);{}();//", + "%27);{}();//" ] # generate random and add to payloads @@ -102,13 +105,14 @@ def check(self, response, payload): if 'Content-Type' in response.headers and HTTP.CONTENT_TYPE.HTML not in response.headers['Content-Type']: return False - # check + # look for payload in href attribute which starts with "javascript:" soup = BeautifulSoup(response.text, "html.parser") - tags = soup.find("a", attrs={"href": payload}) - if tags: - return True - else: - return False + tags = soup.findAll("a", attrs={"href": re.compile(r"^javascript:")}) + for tag in tags: + text = unquote(tag["href"][len("javascript:"):]) + if self._random in utility.parse_javascript(text): + return True + return False class CrossSiteScriptingScriptSrcCheck(_ValueCheck): diff --git a/tests/actives/test_actives_xss.py b/tests/actives/test_actives_xss.py index e0f06ea..f87caed 100644 --- a/tests/actives/test_actives_xss.py +++ b/tests/actives/test_actives_xss.py @@ -82,7 +82,11 @@ def test_check_true_negative(self, check, response): class TestCrossSiteScriptingLinkCheck: payloads = [ - "javascript:avascan()" + "javascript:avascan()", + '");avascan();//', + "%22);avascan();//", + "');avascan();//", + "%27);avascan();//" ] @pytest.fixture @@ -103,6 +107,16 @@ def test_check_true_positive(self, check, response): test = check.check(response, check._payloads[0]) assert test + # true positive + response.text = 'Link' + test = check.check(response, check._payloads[1]) + assert test + + # true positive urlencode + response.text = '
Link' + test = check.check(response, check._payloads[2]) + assert test + def test_check_true_negative(self, check, response): response.headers = {'Content-Type': ''} @@ -122,6 +136,11 @@ def test_check_true_negative(self, check, response): test = check.check(response, check._payloads[0]) assert not test + # true negative escape + response.text = 'Link' + test = check.check(response, check._payloads[1]) + assert not test + # true negative application/json response.text = '{}' response.headers['Content-Type'] = "application/json"