diff --git a/httpobs/docs/api.md b/httpobs/docs/api.md index d386177..ce1c904 100644 --- a/httpobs/docs/api.md +++ b/httpobs/docs/api.md @@ -485,7 +485,7 @@ Example: "score_modifier": 0 }, "x-xss-protection": { - "expectation": "x-xss-protection-1-mode-block", + "expectation": "x-xss-protection-disabled", "name": "x-xss-protection", "output": { "data": "1; mode=block" diff --git a/httpobs/docs/scoring.md b/httpobs/docs/scoring.md index 1b3fbfa..f6d5483 100644 --- a/httpobs/docs/scoring.md +++ b/httpobs/docs/scoring.md @@ -147,9 +147,8 @@ x-frame-options-header-invalid | `X-Frame-Options` (XFO) header cannot be recogn [X-XSS-Protection](https://infosec.mozilla.org/guidelines/web_security#x-xss-protection) | Description | Modifier --- | --- | :---: -x-xss-protection-not-needed-due-to-csp | `X-XSS-Protection` header not needed due to strong Content Security Policy (CSP) header | 0 x-xss-protection-enabled-mode-block | `X-XSS-Protection` header set to `1; mode=block` | 0 x-xss-protection-enabled | `X-XSS-Protection` header set to `1` | 0 -x-xss-protection-disabled | `X-XSS-Protection` header set to `0` (disabled) | -10 -x-xss-protection-not-implemented | `X-XSS-Protection` header not implemented | -10 -x-xss-protection-header-invalid | `X-XSS-Protection` header cannot be recognized | -10 +x-xss-protection-disabled | `X-XSS-Protection` header set to `0` (disabled) | 0 +x-xss-protection-not-implemented | `X-XSS-Protection` header not implemented | 0 +x-xss-protection-header-invalid | `X-XSS-Protection` header cannot be recognized | -5 diff --git a/httpobs/scanner/analyzer/headers.py b/httpobs/scanner/analyzer/headers.py index 8886739..d9ed680 100644 --- a/httpobs/scanner/analyzer/headers.py +++ b/httpobs/scanner/analyzer/headers.py @@ -836,14 +836,13 @@ def x_frame_options(reqs: dict, expectation='x-frame-options-sameorigin-or-deny' @scored_test -def x_xss_protection(reqs: dict, expectation='x-xss-protection-1-mode-block') -> dict: +def x_xss_protection(reqs: dict, expectation='x-xss-protection-disabled') -> dict: """ :param reqs: dictionary containing all the request and response objects :param expectation: test expectation - x-xss-protection-enabled-mode-block: X-XSS-Protection set to "1; block" [default] + x-xss-protection-enabled-mode-block: X-XSS-Protection set to "1; block" x-xss-protection-enabled: X-XSS-Protection set to "1" - x-xss-protection-not-needed-due-to-csp: no X-XSS-Protection header, but CSP blocks inline nonsense - x-xss-protection-disabled: X-XSS-Protection set to "0" (disabled) + x-xss-protection-disabled: X-XSS-Protection set to "0" (disabled) [default] x-xss-protection-not-implemented: X-XSS-Protection header missing x-xss-protection-header-invalid :return: dictionary with: @@ -908,15 +907,10 @@ def x_xss_protection(reqs: dict, expectation='x-xss-protection-1-mode-block') -> output['pass'] = True elif valid and not enabled: output['result'] = 'x-xss-protection-disabled' + output['pass'] = True else: output['result'] = 'x-xss-protection-not-implemented' - - # Allow sites to skip out of having X-XSS-Protection if they implement a strong CSP policy - # Note that having an invalid XXSSP setting will still trigger, even with a good CSP policy - if valid and output['pass'] is False: - if content_security_policy(reqs)['pass']: - output['pass'] = True - output['result'] = 'x-xss-protection-not-needed-due-to-csp' + output['pass'] = True return output diff --git a/httpobs/scanner/grader/grade.py b/httpobs/scanner/grader/grade.py index e509585..e9fc490 100644 --- a/httpobs/scanner/grader/grade.py +++ b/httpobs/scanner/grader/grade.py @@ -366,28 +366,24 @@ }, # X-XSS-Protection 'x-xss-protection-enabled-mode-block': { - 'description': 'X-XSS-Protection header set to "1; mode=block"', + 'description': 'Deprecated X-XSS-Protection header set to "1; mode=block"', 'modifier': 0, }, 'x-xss-protection-enabled': { - 'description': 'X-XSS-Protection header set to "1"', - 'modifier': 0, - }, - 'x-xss-protection-not-needed-due-to-csp': { - 'description': 'X-XSS-Protection header not needed due to strong Content Security Policy (CSP) header', + 'description': 'Deprecated X-XSS-Protection header set to "1"', 'modifier': 0, }, 'x-xss-protection-disabled': { - 'description': 'X-XSS-Protection header set to "0" (disabled)', - 'modifier': -10, + 'description': 'Deprecated X-XSS-Protection header set to "0" (disabled)', + 'modifier': 0, }, 'x-xss-protection-not-implemented': { - 'description': 'X-XSS-Protection header not implemented', - 'modifier': -10, + 'description': 'Deprecated X-XSS-Protection header not implemented', + 'modifier': 0, }, 'x-xss-protection-header-invalid': { - 'description': 'X-XSS-Protection header cannot be recognized', - 'modifier': -10, + 'description': 'Deprecated X-XSS-Protection header cannot be recognized', + 'modifier': -5, }, # Generic results 'html-not-parsable': { @@ -424,7 +420,7 @@ def get_grade_and_likelihood_for_score(score: int) -> tuple: def get_score_description(result) -> str: - return SCORE_TABLE[result]['description'] + return SCORE_TABLE.get(result, {'description': ''})['description'] def get_score_modifier(result) -> int: diff --git a/httpobs/tests/unittests/test_headers.py b/httpobs/tests/unittests/test_headers.py index cb2720e..2531f1f 100644 --- a/httpobs/tests/unittests/test_headers.py +++ b/httpobs/tests/unittests/test_headers.py @@ -1429,7 +1429,7 @@ def test_missing(self): result = x_xss_protection(self.reqs) self.assertEquals('x-xss-protection-not-implemented', result['result']) - self.assertFalse(result['pass']) + self.assertTrue(result['pass']) def test_header_invalid(self): for value in ('whimsy', '2; mode=block', '1; mode=block; mode=block', '1; mode=block, 1; mode=block'): @@ -1446,7 +1446,7 @@ def test_disabled(self): result = x_xss_protection(self.reqs) self.assertEquals('x-xss-protection-disabled', result['result']) - self.assertFalse(result['pass']) + self.assertTrue(result['pass']) def test_enabled_noblock(self): for value in ('1', '1 '): @@ -1464,12 +1464,3 @@ def test_enabled_block(self): self.assertEquals('x-xss-protection-enabled-mode-block', result['result']) self.assertTrue(result['pass']) - - def test_enabled_via_csp(self): - reqs = empty_requests() - set_header(reqs['responses']['auto'], 'Content-Security-Policy', "object-src 'none'; script-src 'none'") - - result = x_xss_protection(reqs) - - self.assertEquals('x-xss-protection-not-needed-due-to-csp', result['result']) - self.assertTrue(result['pass'])