def cookies(reqs: dict, expectation='cookies-secure-with-httponly-sessions') -> dict: """ :param reqs: dictionary containing all the request and response objects :param expectation: test expectation cookies-secure-with-httponly-sessions-and-samesite: All cookies are secure, use HttpOnly if needed, and SameSite cookies-secure-with-httponly-sessions: All cookies have secure flag set, all session cookies are HttpOnly cookies-without-secure-flag-but-protected-by-hsts: Cookies don't have secure, but site uses HSTS cookies-session-without-secure-flag-but-protected-by-hsts: Same, but session cookie cookies-without-secure-flag: Cookies set without secure flag cookies-samesite-flag-invalid: Cookies set with invalid SameSite value (must be either unset, Strict, or Lax) cookies-session-without-secure-flag: Session cookies lack the Secure flag cookies-session-without-httponly-flag: Session cookies lack the HttpOnly flag cookies-not-found: No cookies found in HTTP requests :return: dictionary with: data: the cookie jar expectation: test expectation pass: whether the site's configuration met its expectation result: short string describing the result of the test """ output = { 'data': None, 'expectation': expectation, 'pass': False, 'result': None, 'sameSite': None, } session = reqs['session'] # all requests and their associated cookies # The order of how bad the various results are goodness = [ 'cookies-without-secure-flag-but-protected-by-hsts', 'cookies-without-secure-flag', 'cookies-session-without-secure-flag-but-protected-by-hsts', 'cookies-samesite-flag-invalid', 'cookies-anticsrf-without-samesite-flag', 'cookies-session-without-httponly-flag', 'cookies-session-without-secure-flag' ] # TODO: Support cookies set over http-equiv (ugh) # https://github.com/mozilla/http-observatory/issues/265 # Get their HTTP Strict Transport Security status, which can help when cookies are set without Secure hsts = strict_transport_security(reqs)['pass'] # If there are no cookies if not session.cookies: output['result'] = 'cookies-not-found' else: jar = {} # There are certain cookies we ignore, because they are set by service providers and sites have # no control over them. for cookie in COOKIES_TO_DELETE: del (session.cookies[cookie]) for cookie in session.cookies: # The HttpOnly and SameSite functionality is a bit broken cookie.httponly = cookie.samesite = False for key in cookie._rest: if key.lower() == 'httponly' and getattr(cookie, 'httponly') is False: cookie.httponly = True elif key.lower() == 'samesite' and getattr( cookie, 'samesite') is False: if cookie._rest[key] is True or cookie._rest[key].strip( ).lower() == 'strict': cookie.samesite = 'Strict' output['sameSite'] = True elif cookie._rest[key].strip().lower() == 'lax': cookie.samesite = 'Lax' output['sameSite'] = True else: output['result'] = only_if_worse( 'cookies-samesite-flag-invalid', output['result'], goodness) # Add it to the jar jar[cookie.name] = { i: getattr(cookie, i, None) for i in [ 'domain', 'expires', 'httponly', 'max-age', 'path', 'port', 'samesite', 'secure' ] } # Is it a session identifier or an anti-csrf token? sessionid = any(i in cookie.name.lower() for i in ('login', 'sess')) anticsrf = True if 'csrf' in cookie.name.lower() else False if not cookie.secure and hsts: output['result'] = only_if_worse( 'cookies-without-secure-flag-but-protected-by-hsts', output['result'], goodness) elif not cookie.secure: output['result'] = only_if_worse('cookies-without-secure-flag', output['result'], goodness) # Anti-CSRF tokens should be set using the SameSite option if anticsrf and not cookie.samesite: output['result'] = only_if_worse( 'cookies-anticsrf-without-samesite-flag', output['result'], goodness) # Login and session cookies should be set with Secure if sessionid and not cookie.secure and hsts: output['result'] = only_if_worse( 'cookies-session-without-secure-flag-but-protected-by-hsts', output['result'], goodness) elif sessionid and not cookie.secure: output['result'] = only_if_worse( 'cookies-session-without-secure-flag', output['result'], goodness) # Login and session cookies should be set with HttpOnly if sessionid and not cookie.httponly: output['result'] = only_if_worse( 'cookies-session-without-httponly-flag', output['result'], goodness) # Store whether or not we saw SameSite cookies, if cookies were set if output['result'] is None: if output['sameSite']: output[ 'result'] = 'cookies-secure-with-httponly-sessions-and-samesite' else: output['result'] = 'cookies-secure-with-httponly-sessions' output['sameSite'] = False # Save the cookie jar output['data'] = jar if len(str(jar)) < 32768 else {} # Check to see if the test passed or failed if output['result'] in ( 'cookies-not-found', 'cookies-secure-with-httponly-sessions-and-samesite', expectation): output['pass'] = True return output
def cookies(reqs: dict, expectation='cookies-secure-with-httponly-sessions') -> dict: """ :param reqs: dictionary containing all the request and response objects :param expectation: test expectation cookies-secure-with-httponly-sessions: All cookies have secure flag set, all session cookies are HttpOnly cookies-without-secure-flag-but-protected-by-hsts: Cookies don't have secure, but site uses HSTS cookies-session-without-secure-flag-but-protected-by-hsts: Same, but session cookie cookies-without-secure-flag: Cookies set without secure flag cookies-session-without-secure-flag: Session cookies lack the Secure flag cookies-session-without-httponly-flag: Session cookies lack the HttpOnly flag cookies-not-found: No cookies found in HTTP requests :return: dictionary with: data: the cookie jar expectation: test expectation pass: whether the site's configuration met its expectation result: short string describing the result of the test """ output = { 'data': None, 'expectation': expectation, 'pass': False, 'result': None, } session = reqs['session'] # all requests and their associated cookies # The order of how bad the various results are goodness = ['cookies-without-secure-flag-but-protected-by-hsts', 'cookies-without-secure-flag', 'cookies-session-without-secure-flag-but-protected-by-hsts', 'cookies-session-without-secure-flag', 'cookies-session-without-httponly-flag'] # Get their HTTP Strict Transport Security status, which can help when cookies are set without Secure hsts = strict_transport_security(reqs)['pass'] # If there are no cookies if not session.cookies: output['result'] = 'cookies-not-found' else: jar = {} for cookie in session.cookies: # The httponly functionality is a bit broken if not hasattr(cookie, 'httponly'): if 'httponly' in [key.lower() for key in cookie._rest]: cookie.httponly = True else: cookie.httponly = False # Add it to the jar jar[cookie.name] = {i: getattr(cookie, i, None) for i in ['domain', 'expires', 'httponly', 'max-age', 'path', 'port', 'secure']} # Is it a session identifier? sessionid = any(i in cookie.name.lower() for i in ('login', 'sess')) if not cookie.secure and hsts: output['result'] = only_if_worse('cookies-without-secure-flag-but-protected-by-hsts', output['result'], goodness) elif not cookie.secure: output['result'] = only_if_worse('cookies-without-secure-flag', output['result'], goodness) # Login and session cookies should be set with Secure if sessionid and not cookie.secure and hsts: output['result'] = only_if_worse('cookies-session-without-secure-flag-but-protected-by-hsts', output['result'], goodness) elif sessionid and not cookie.secure: output['result'] = only_if_worse('cookies-session-without-secure-flag', output['result'], goodness) # Login and session cookies should be set with HttpOnly if sessionid and not cookie.httponly: output['result'] = only_if_worse('cookies-session-without-httponly-flag', output['result'], goodness) # Save the cookie jar output['data'] = jar if len(str(jar)) < 32768 else {} # Got through the cookie check properly if not output['result']: output['result'] = 'cookies-secure-with-httponly-sessions' # Check to see if the test passed or failed if output['result'] in ('cookies-not-found', expectation): output['pass'] = True return output
def cookies(reqs: dict, expectation='cookies-secure-with-httponly-sessions') -> dict: """ :param reqs: dictionary containing all the request and response objects :param expectation: test expectation cookies-secure-with-httponly-sessions: All cookies have secure flag set, all session cookies are HttpOnly cookies-without-secure-flag-but-protected-by-hsts: Cookies don't have secure, but site uses HSTS cookies-session-without-secure-flag-but-protected-by-hsts: Same, but session cookie cookies-without-secure-flag: Cookies set without secure flag cookies-session-without-secure-flag: Session cookies lack the Secure flag cookies-session-without-httponly-flag: Session cookies lack the HttpOnly flag cookies-not-found: No cookies found in HTTP requests :return: dictionary with: data: the cookie jar expectation: test expectation pass: whether the site's configuration met its expectation result: short string describing the result of the test """ output = { 'data': None, 'expectation': expectation, 'pass': False, 'result': None, } session = reqs['session'] # all requests and their associated cookies # The order of how bad the various results are goodness = [ 'cookies-without-secure-flag-but-protected-by-hsts', 'cookies-without-secure-flag', 'cookies-session-without-secure-flag-but-protected-by-hsts', 'cookies-session-without-httponly-flag', 'cookies-session-without-secure-flag' ] # TODO: Support cookies set over http-equiv (ugh) # https://github.com/mozilla/http-observatory/issues/265 # Get their HTTP Strict Transport Security status, which can help when cookies are set without Secure hsts = strict_transport_security(reqs)['pass'] # If there are no cookies if not session.cookies: output['result'] = 'cookies-not-found' else: jar = {} # Ignore the CloudFlare __cfduid tracking cookies. They *are* actually bad, but it is out of a site's # control. See https://github.com/mozilla/http-observatory/issues/121 for additional details. Hopefully # this will eventually be fixed on CloudFlare's end. del (session.cookies['__cfduid']) for cookie in session.cookies: # The httponly functionality is a bit broken if not hasattr(cookie, 'httponly'): if 'httponly' in [key.lower() for key in cookie._rest]: cookie.httponly = True else: cookie.httponly = False # Add it to the jar jar[cookie.name] = { i: getattr(cookie, i, None) for i in [ 'domain', 'expires', 'httponly', 'max-age', 'path', 'port', 'secure' ] } # Is it a session identifier? sessionid = any(i in cookie.name.lower() for i in ('login', 'sess')) if not cookie.secure and hsts: output['result'] = only_if_worse( 'cookies-without-secure-flag-but-protected-by-hsts', output['result'], goodness) elif not cookie.secure: output['result'] = only_if_worse('cookies-without-secure-flag', output['result'], goodness) # Login and session cookies should be set with Secure if sessionid and not cookie.secure and hsts: output['result'] = only_if_worse( 'cookies-session-without-secure-flag-but-protected-by-hsts', output['result'], goodness) elif sessionid and not cookie.secure: output['result'] = only_if_worse( 'cookies-session-without-secure-flag', output['result'], goodness) # Login and session cookies should be set with HttpOnly if sessionid and not cookie.httponly: output['result'] = only_if_worse( 'cookies-session-without-httponly-flag', output['result'], goodness) # Save the cookie jar output['data'] = jar if len(str(jar)) < 32768 else {} # Got through the cookie check properly if not output['result']: output['result'] = 'cookies-secure-with-httponly-sessions' # Check to see if the test passed or failed if output['result'] in ('cookies-not-found', expectation): output['pass'] = True return output
def subresource_integrity(reqs: dict, expectation='sri-implemented-and-external-scripts-loaded-securely') -> dict: """ :param reqs: dictionary containing all the request and response objects :param expectation: test expectation sri-implemented-and-all-scripts-loaded-securely: all same origin, and uses SRI sri-implemented-and-external-scripts-loaded-securely: integrity attribute exists on all external scripts, and scripts loaded [default for HTML] sri-implemented-but-external-scripts-not-loaded-securely: SRI implemented, but with scripts loaded over HTTP sri-not-implemented-but-external-scripts-loaded-securely: SRI isn't implemented, but all scripts are loaded over HTTPS sri-not-implemented-and-external-scripts-not-loaded-securely: SRI isn't implemented, and scripts are downloaded over HTTP sri-not-implemented-but-all-scripts-loaded-from-secure-origin: SRI isn't implemented, but all scripts come from secure origins (self) sri-not-implemented-but-no-scripts-loaded: SRI isn't implemented, because the page doesn't load any scripts sri-not-implemented-response-not-html: SRI isn't needed, because the page isn't HTML [default for non-HTML] request-did-not-return-status-code-200: Only look for SRI on pages that returned 200, not things like 404s html-not-parsable: Can't parse the page's content :return: dictionary with: data: all external scripts and their integrity / crossorigin attributes expectation: test expectation pass: whether the site's external scripts met expectations result: short string describing the result of the test """ output = { 'data': {}, 'expectation': expectation, 'pass': False, 'result': None, } response = reqs['responses']['auto'] # The order of how "good" the results are goodness = ['sri-implemented-and-all-scripts-loaded-securely', 'sri-implemented-and-external-scripts-loaded-securely', 'sri-implemented-but-external-scripts-not-loaded-securely', 'sri-not-implemented-but-external-scripts-loaded-securely', 'sri-not-implemented-and-external-scripts-not-loaded-securely', 'sri-not-implemented-response-not-html'] # If the response to get / fails if response.status_code != 200: output['result'] = 'request-did-not-return-status-code-200' # If the content isn't HTML, there's no scripts to load; this is okay elif response.headers.get('Content-Type', '').split(';')[0] not in ('text/html', 'application/xhtml+xml'): output['result'] = 'sri-not-implemented-response-not-html' else: # Try to parse the HTML try: soup = bs(reqs['resources']['/'], 'html.parser') except: output['result'] = 'html-not-parsable' return output # Track to see if any scripts were on foreign TLDs scripts_on_foreign_origin = False # Get all the scripts scripts = soup.find_all('script') for script in scripts: if script.has_attr('src'): # Script tag parameters src = urlparse(script['src']) integrity = script.get('integrity') crossorigin = script.get('crossorigin') # Check to see if they're on the same second-level domain # TODO: update the PSL list on startup psl = PublicSuffixList() samesld = True if (psl.privatesuffix(urlparse(response.url).netloc) == psl.privatesuffix(src.netloc)) else False # Check to see if it's the same origin or second-level domain if src.netloc == '' or samesld: secureorigin = True elif src.netloc != '' and '.' not in src.netloc: # like localhost secureorigin = False scripts_on_foreign_origin = True else: secureorigin = False scripts_on_foreign_origin = True # See if it's a secure scheme if src.scheme == 'https' or (src.scheme == '' and urlparse(response.url).scheme == 'https'): securescheme = True else: securescheme = False # Add it to the scripts data result, if it's not a relative URI if not secureorigin: output['data'][script['src']] = { 'crossorigin': crossorigin, 'integrity': integrity } if integrity and not securescheme: output['result'] = only_if_worse('sri-implemented-but-external-scripts-not-loaded-securely', output['result'], goodness) elif not integrity and securescheme: output['result'] = only_if_worse('sri-not-implemented-but-external-scripts-loaded-securely', output['result'], goodness) elif not integrity and not securescheme: output['result'] = only_if_worse('sri-not-implemented-and-external-scripts' '-not-loaded-securely', output['result'], goodness) # Grant bonus even if they use SRI on the same origin else: if integrity and securescheme and not output['result']: output['result'] = 'sri-implemented-and-all-scripts-loaded-securely' # If the page doesn't load any scripts if not scripts: output['result'] = 'sri-not-implemented-but-no-scripts-loaded' # If all the scripts are loaded from a secure origin, not triggering a need for SRI elif scripts and not scripts_on_foreign_origin and not output['result']: output['result'] = 'sri-not-implemented-but-all-scripts-loaded-from-secure-origin' # If the page loaded from a foreign origin, but everything included SRI elif scripts and scripts_on_foreign_origin and not output['result']: output['result'] = only_if_worse('sri-implemented-and-external-scripts-loaded-securely', output['result'], goodness) # Code defensively on the size of the data output['data'] = output['data'] if len(str(output['data'])) < 32768 else {} # Check to see if the test passed or failed if output['result'] in ('sri-implemented-and-all-scripts-loaded-securely', 'sri-implemented-and-external-scripts-loaded-securely', 'sri-not-implemented-response-not-html', 'sri-not-implemented-but-all-scripts-loaded-from-secure-origin', 'sri-not-implemented-but-no-scripts-loaded', expectation): output['pass'] = True return output
def subresource_integrity(reqs: dict, expectation='sri-implemented-and-external-scripts-loaded-securely') -> dict: """ :param reqs: dictionary containing all the request and response objects :param expectation: test expectation sri-implemented-and-all-scripts-loaded-securely: all same origin, and uses SRI sri-implemented-and-external-scripts-loaded-securely: integrity attribute exists on all external scripts, and scripts loaded [default for HTML] sri-implemented-but-external-scripts-not-loaded-securely: SRI implemented, but with scripts loaded over HTTP sri-not-implemented-but-external-scripts-loaded-securely: SRI isn't implemented, but all scripts are loaded over HTTPS sri-not-implemented-and-external-scripts-not-loaded-securely: SRI isn't implemented, and scripts are downloaded over HTTP sri-not-implemented-but-all-scripts-loaded-from-secure-origin: SRI isn't implemented, but all scripts come from secure origins (self) sri-not-implemented-but-no-scripts-loaded: SRI isn't implemented, because the page doesn't load any scripts sri-not-implemented-response-not-html: SRI isn't needed, because the page isn't HTML [default for non-HTML] request-did-not-return-status-code-200: Only look for SRI on pages that returned 200, not things like 404s html-not-parsable: Can't parse the page's content :return: dictionary with: data: all external scripts and their integrity / crossorigin attributes expectation: test expectation pass: whether the site's external scripts met expectations result: short string describing the result of the test """ output = { 'data': {}, 'expectation': expectation, 'pass': False, 'result': None, } response = reqs['responses']['auto'] # The order of how "good" the results are goodness = ['sri-implemented-and-all-scripts-loaded-securely', 'sri-implemented-and-external-scripts-loaded-securely', 'sri-implemented-but-external-scripts-not-loaded-securely', 'sri-not-implemented-but-external-scripts-loaded-securely', 'sri-not-implemented-and-external-scripts-not-loaded-securely', 'sri-not-implemented-response-not-html'] # If the content isn't HTML, there's no scripts to load; this is okay if response.headers.get('Content-Type', '').split(';')[0] not in HTML_TYPES: output['result'] = 'sri-not-implemented-response-not-html' else: # Try to parse the HTML try: soup = bs(reqs['resources']['__path__'], 'html.parser') except: output['result'] = 'html-not-parsable' return output # Track to see if any scripts were on foreign TLDs scripts_on_foreign_origin = False # Get all the scripts scripts = soup.find_all('script') for script in scripts: if script.has_attr('src'): # Script tag parameters src = urlparse(script['src']) integrity = script.get('integrity') crossorigin = script.get('crossorigin') # Check to see if they're on the same second-level domain # TODO: update the PSL list on startup psl = PublicSuffixList() samesld = True if (psl.privatesuffix(urlparse(response.url).netloc) == psl.privatesuffix(src.netloc)) else False if src.scheme == '': if src.netloc == '': # Relative URL (src="/path") relativeorigin = True relativeprotocol = False else: # Relative protocol (src="//host/path") relativeorigin = False relativeprotocol = True else: relativeorigin = False relativeprotocol = False # Check to see if it's the same origin or second-level domain if relativeorigin or (samesld and not relativeprotocol): secureorigin = True else: secureorigin = False scripts_on_foreign_origin = True # See if it's a secure scheme if src.scheme == 'https' or (relativeorigin and urlparse(response.url).scheme == 'https'): securescheme = True else: securescheme = False # Add it to the scripts data result, if it's not a relative URI if not secureorigin: output['data'][script['src']] = { 'crossorigin': crossorigin, 'integrity': integrity } if integrity and not securescheme: output['result'] = only_if_worse('sri-implemented-but-external-scripts-not-loaded-securely', output['result'], goodness) elif not integrity and securescheme: output['result'] = only_if_worse('sri-not-implemented-but-external-scripts-loaded-securely', output['result'], goodness) elif not integrity and not securescheme and samesld: output['result'] = only_if_worse('sri-not-implemented-and-external-scripts' '-not-loaded-securely', output['result'], goodness) elif not integrity and not securescheme: output['result'] = only_if_worse('sri-not-implemented-and-external-scripts' '-not-loaded-securely', output['result'], goodness) # Grant bonus even if they use SRI on the same origin else: if integrity and securescheme and not output['result']: output['result'] = 'sri-implemented-and-all-scripts-loaded-securely' # If the page doesn't load any scripts if not scripts: output['result'] = 'sri-not-implemented-but-no-scripts-loaded' # If all the scripts are loaded from a secure origin, not triggering a need for SRI elif scripts and not scripts_on_foreign_origin and not output['result']: output['result'] = 'sri-not-implemented-but-all-scripts-loaded-from-secure-origin' # If the page loaded from a foreign origin, but everything included SRI elif scripts and scripts_on_foreign_origin and not output['result']: output['result'] = only_if_worse('sri-implemented-and-external-scripts-loaded-securely', output['result'], goodness) # Code defensively on the size of the data output['data'] = output['data'] if len(str(output['data'])) < 32768 else {} # Check to see if the test passed or failed if output['result'] in ('sri-implemented-and-all-scripts-loaded-securely', 'sri-implemented-and-external-scripts-loaded-securely', 'sri-not-implemented-response-not-html', 'sri-not-implemented-but-all-scripts-loaded-from-secure-origin', 'sri-not-implemented-but-no-scripts-loaded', expectation): output['pass'] = True return output
def cookies(reqs: dict, expectation='cookies-secure-with-httponly-sessions') -> dict: """ :param reqs: dictionary containing all the request and response objects :param expectation: test expectation cookies-secure-with-httponly-sessions-and-samesite: All cookies are secure, use HttpOnly if needed, and SameSite cookies-secure-with-httponly-sessions: All cookies have secure flag set, all session cookies are HttpOnly cookies-without-secure-flag-but-protected-by-hsts: Cookies don't have secure, but site uses HSTS cookies-session-without-secure-flag-but-protected-by-hsts: Same, but session cookie cookies-without-secure-flag: Cookies set without secure flag cookies-samesite-flag-invalid: Cookies set with invalid SameSite value (must be either unset, Strict, or Lax) cookies-session-without-secure-flag: Session cookies lack the Secure flag cookies-session-without-httponly-flag: Session cookies lack the HttpOnly flag cookies-not-found: No cookies found in HTTP requests :return: dictionary with: data: the cookie jar expectation: test expectation pass: whether the site's configuration met its expectation result: short string describing the result of the test """ output = { 'data': None, 'expectation': expectation, 'pass': False, 'result': None, 'sameSite': None, } session = reqs['session'] # all requests and their associated cookies # The order of how bad the various results are goodness = ['cookies-without-secure-flag-but-protected-by-hsts', 'cookies-without-secure-flag', 'cookies-session-without-secure-flag-but-protected-by-hsts', 'cookies-samesite-flag-invalid', 'cookies-anticsrf-without-samesite-flag', 'cookies-session-without-httponly-flag', 'cookies-session-without-secure-flag'] # TODO: Support cookies set over http-equiv (ugh) # https://github.com/mozilla/http-observatory/issues/265 # Get their HTTP Strict Transport Security status, which can help when cookies are set without Secure hsts = strict_transport_security(reqs)['pass'] # If there are no cookies if not session.cookies: output['result'] = 'cookies-not-found' else: jar = {} # There are certain cookies we ignore, because they are set by service providers and sites have # no control over them. for cookie in COOKIES_TO_DELETE: del(session.cookies[cookie]) for cookie in session.cookies: # The HttpOnly and SameSite functionality is a bit broken cookie.httponly = cookie.samesite = False for key in cookie._rest: if key.lower() == 'httponly' and getattr(cookie, 'httponly') is False: cookie.httponly = True elif key.lower() == 'samesite' and getattr(cookie, 'samesite') is False: if cookie._rest[key] is True or cookie._rest[key].strip().lower() == 'strict': cookie.samesite = 'Strict' output['sameSite'] = True elif cookie._rest[key].strip().lower() == 'lax': cookie.samesite = 'Lax' output['sameSite'] = True else: output['result'] = only_if_worse('cookies-samesite-flag-invalid', output['result'], goodness) # Add it to the jar jar[cookie.name] = {i: getattr(cookie, i, None) for i in ['domain', 'expires', 'httponly', 'max-age', 'path', 'port', 'samesite', 'secure']} # Is it a session identifier or an anti-csrf token? sessionid = any(i in cookie.name.lower() for i in ('login', 'sess')) anticsrf = True if 'csrf' in cookie.name.lower() else False if not cookie.secure and hsts: output['result'] = only_if_worse('cookies-without-secure-flag-but-protected-by-hsts', output['result'], goodness) elif not cookie.secure: output['result'] = only_if_worse('cookies-without-secure-flag', output['result'], goodness) # Anti-CSRF tokens should be set using the SameSite option if anticsrf and not cookie.samesite: output['result'] = only_if_worse('cookies-anticsrf-without-samesite-flag', output['result'], goodness) # Login and session cookies should be set with Secure if sessionid and not cookie.secure and hsts: output['result'] = only_if_worse('cookies-session-without-secure-flag-but-protected-by-hsts', output['result'], goodness) elif sessionid and not cookie.secure: output['result'] = only_if_worse('cookies-session-without-secure-flag', output['result'], goodness) # Login and session cookies should be set with HttpOnly if sessionid and not cookie.httponly: output['result'] = only_if_worse('cookies-session-without-httponly-flag', output['result'], goodness) # Store whether or not we saw SameSite cookies, if cookies were set if output['result'] is None: if output['sameSite']: output['result'] = 'cookies-secure-with-httponly-sessions-and-samesite' else: output['result'] = 'cookies-secure-with-httponly-sessions' output['sameSite'] = False # Save the cookie jar output['data'] = jar if len(str(jar)) < 32768 else {} # Check to see if the test passed or failed if output['result'] in ('cookies-not-found', 'cookies-secure-with-httponly-sessions-and-samesite', expectation): output['pass'] = True return output