diff --git a/pipeline/fiftyone_pipeline_cloudrequestengine/examples/tests/test_cloud_mixed_web_offline.py b/pipeline/fiftyone_pipeline_cloudrequestengine/examples/tests/test_cloud_mixed_web_offline.py index 47fe209e..8ba78fef 100644 --- a/pipeline/fiftyone_pipeline_cloudrequestengine/examples/tests/test_cloud_mixed_web_offline.py +++ b/pipeline/fiftyone_pipeline_cloudrequestengine/examples/tests/test_cloud_mixed_web_offline.py @@ -302,3 +302,14 @@ def test_licence_key_gets_an_engine_only_for_what_it_asks_for(cloud): def test_licence_key_never_reaches_the_page(cloud): cloud(EVERY_SECTION) assert "a-licence-key" not in page_for(license_key="a-licence-key") + + +def test_the_web_request_is_not_sent_to_the_cloud(cloud): + # The web integration adds the request itself as server.http-request for + # the usage sharing element, and it is never evidence another element + # reads. + stub = cloud(EVERY_SECTION) + page_for(resource_key="a-resource-key") + + url, data = stub.requests[-1] + assert not any("http-request" in key for key in data), data diff --git a/pipeline/fiftyone_pipeline_core/src/fiftyone_pipeline_core/evidence.py b/pipeline/fiftyone_pipeline_core/src/fiftyone_pipeline_core/evidence.py index f7067647..02b45ce7 100644 --- a/pipeline/fiftyone_pipeline_core/src/fiftyone_pipeline_core/evidence.py +++ b/pipeline/fiftyone_pipeline_core/src/fiftyone_pipeline_core/evidence.py @@ -20,6 +20,11 @@ # such notice(s) shall fulfill the requirements of that article. # ********************************************************************* +# The key the web integration gives the web request itself, which the usage +# sharing element reads. No element uses it as evidence, so filter() always +# sets it aside, whatever the elements' filters accept. +HTTP_REQUEST_KEY = "server.http-request" + class Evidence: def __init__(self, flowdata): @@ -32,6 +37,7 @@ def __init__(self, flowdata): self.evidence = {} self.evidence_filtered = False self.flowdata = flowdata + self.unaccepted = {} def add(self, key, value): """! @@ -89,10 +95,25 @@ def get_all(self): return self.evidence + def get_unaccepted(self): + """! + The evidence filter() set aside, being the keys no FlowElement in + the pipeline accepts and the web request itself. The usage sharing + element accepts no evidence, and shares what it finds here and in + get_all(). + + @rtype: dict + @return: The evidence set aside, empty until filter() runs + """ + + return self.unaccepted + def filter(self) -> None: """! Filter evidence using `filter_evidence_key` and - `filter_evidence` methods of each FlowElement in the pipeline + `filter_evidence` methods of each FlowElement in the pipeline. The + keys that do not pass, and the web request under HTTP_REQUEST_KEY, + move to get_unaccepted(). """ if self.evidence_filtered: @@ -100,11 +121,11 @@ def filter(self) -> None: # Loop over a copy of the keys, as keys are removed on the way. for key in list(self.evidence): - if not any([ + if str(key).lower() == HTTP_REQUEST_KEY or not any([ flow_element.filter_evidence_key(key) for flow_element in self.flowdata.pipeline.flow_elements ]): - del self.evidence[key] + self.unaccepted[key] = self.evidence.pop(key) self.evidence_filtered = True diff --git a/pipeline/fiftyone_pipeline_core/src/fiftyone_pipeline_core/web.py b/pipeline/fiftyone_pipeline_core/src/fiftyone_pipeline_core/web.py index d3f96cf0..d51ca012 100644 --- a/pipeline/fiftyone_pipeline_core/src/fiftyone_pipeline_core/web.py +++ b/pipeline/fiftyone_pipeline_core/src/fiftyone_pipeline_core/web.py @@ -20,13 +20,30 @@ # such notice(s) shall fulfill the requirements of that article. # ********************************************************************* -from flask import request +from types import MappingProxyType + +from flask import has_request_context, request, session + +from .evidence import HTTP_REQUEST_KEY + +def request_line_path(request): + + """! + The path of the request line as the server received it, when the server + says what that was, and otherwise the path Flask decoded. + """ + + raw = request.environ.get("RAW_URI") or request.environ.get("REQUEST_URI") + if raw: + return raw.split("?", 1)[0].split("#", 1)[0] + return request.path def webevidence(request): """! - Get evidence from a web request (gets headers, cookies, query parameters - and the fields of a posted form) + Get evidence from a web request (gets headers, cookies, session values, + query parameters and the fields of a posted form, and the request itself + as server.http-request) @type request: Request @param request: A Request object @@ -59,6 +76,14 @@ def webevidence(request): webevidence["query." + field] = value + # Values stored in the session. They are read without marking the session + # as accessed, which would add a Vary: Cookie header to the response, and + # only plain values are taken. + if has_request_context(): + for name, value in dict.items(session._get_current_object()): + if isinstance(value, (str, int, float, bool)): + webevidence["session." + name] = value + webevidence["server.client-ip"] = request.remote_addr webevidence["server.host-ip"] = request.host @@ -68,6 +93,17 @@ def webevidence(request): else: webevidence["header.protocol"] = "http" + # The request itself, for the usage sharing element, which accepts no + # evidence and shares what it finds. A read-only view of the parts above + # and the method and path of the request line, keyed as evidence is. It + # leaves out the Cookie header, which cookies already cover. Evidence + # keeps it from every other element. + view = {key: value for key, value in webevidence.items() + if key not in ("header.cookie", "server.host-ip")} + view["server.request-method"] = request.method + view["server.request-path"] = request_line_path(request) + webevidence[HTTP_REQUEST_KEY] = MappingProxyType(view) + return webevidence def set_response_header(flowData, response): diff --git a/pipeline/fiftyone_pipeline_core/tests/test_evidence_unaccepted.py b/pipeline/fiftyone_pipeline_core/tests/test_evidence_unaccepted.py new file mode 100644 index 00000000..caf0edad --- /dev/null +++ b/pipeline/fiftyone_pipeline_core/tests/test_evidence_unaccepted.py @@ -0,0 +1,110 @@ +# ********************************************************************* +# This Original Work is copyright of 51 Degrees Mobile Experts Limited. +# Copyright 2026 51 Degrees Mobile Experts Limited, Davidson House, +# Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. +# +# This Original Work is licensed under the European Union Public Licence +# (EUPL) v.1.2 and is subject to its terms as set out below. +# +# If a copy of the EUPL was not distributed with this file, You can obtain +# one at https://opensource.org/licenses/EUPL-1.2. +# +# The 'Compatible Licences' set out in the Appendix to the EUPL (as may be +# amended by the European Commission) shall be deemed incompatible for +# the purposes of the Work and the provisions of the compatibility +# clause in Article 5 of the EUPL shall not apply. +# +# If using the Work as, or as part of, a network application, by +# including the attribution notice(s) required under Article 5 of the EUPL +# in the end user terms of the application under an appropriate heading, +# such notice(s) shall fulfill the requirements of that article. +# ********************************************************************* + +"""The evidence a pipeline's elements do not accept, and the web request the +web integration adds as server.http-request.""" + +import unittest + +from flask import Flask, request, session + +from fiftyone_pipeline_core.pipelinebuilder import PipelineBuilder +from fiftyone_pipeline_core.web import webevidence + +from .classes.exampleflowelement1 import ExampleFlowElement1 + +# No JavaScript or set header elements, so the only filter is that of +# ExampleFlowElement1, which accepts header.user-agent. +NO_DEFAULT_ELEMENTS = { + "add_javascript_builder": False, + "use_setheader_properties": False, +} + + +class UnacceptedEvidenceTests(unittest.TestCase): + + def test_evidence_no_element_accepts_is_set_aside(self): + pipeline = PipelineBuilder(NO_DEFAULT_ELEMENTS) \ + .add(ExampleFlowElement1()).build() + flowdata = pipeline.create_flowdata() + flowdata.evidence.add("header.user-agent", "a browser") + flowdata.evidence.add("header.x-test", "set aside") + flowdata.process() + + self.assertEqual( + {"header.user-agent": "a browser"}, flowdata.evidence.get_all()) + self.assertEqual( + {"header.x-test": "set aside"}, + flowdata.evidence.get_unaccepted()) + + def test_the_web_request_is_added_and_set_aside(self): + """No element reads server.http-request as evidence, even an element + that accepts every key, as the JavaScript builder does.""" + + pipeline = PipelineBuilder().add(ExampleFlowElement1()).build() + app = Flask(__name__) + with app.test_request_context( + "/?campaign=a", headers={"User-Agent": "a browser", + "X-Test": "a header"}): + evidence = webevidence(request) + flowdata = pipeline.create_flowdata() + flowdata.evidence.add_from_dict(evidence) + flowdata.process() + + self.assertNotIn("server.http-request", flowdata.evidence.get_all()) + view = flowdata.evidence.get_unaccepted()["server.http-request"] + self.assertEqual("a header", view["header.x-test"]) + self.assertEqual("a browser", view["header.user-agent"]) + self.assertEqual("a", view["query.campaign"]) + # A read-only view of the request, not evidence an element can + # change. + with self.assertRaises(TypeError): + view["header.x-test"] = "changed" + + def test_the_web_request_has_every_part_the_specification_names(self): + app = Flask(__name__) + app.secret_key = "a secret" + with app.test_request_context( + "/page?campaign=a", + headers={"User-Agent": "a browser", + "Cookie": "sessionid=cookievalue"}, + environ_base={"REMOTE_ADDR": "203.0.113.5"}): + session["51D_one"] = "first" + session["theme"] = "dark" + view = webevidence(request)["server.http-request"] + + self.assertEqual("a browser", view["header.user-agent"]) + self.assertEqual("cookievalue", view["cookie.sessionid"]) + self.assertEqual("a", view["query.campaign"]) + self.assertEqual("first", view["session.51D_one"]) + self.assertEqual("dark", view["session.theme"]) + self.assertEqual("203.0.113.5", view["server.client-ip"]) + self.assertEqual("http", view["header.protocol"]) + self.assertEqual("GET", view["server.request-method"]) + self.assertEqual("/page", view["server.request-path"]) + # The Cookie header carries every cookie, and cookies are their own + # part of the view. + self.assertNotIn("header.cookie", view) + + +if __name__ == "__main__": + unittest.main() diff --git a/pipeline/fiftyone_pipeline_engines_fiftyone/src/fiftyone_pipeline_engines_fiftyone/share_usage.py b/pipeline/fiftyone_pipeline_engines_fiftyone/src/fiftyone_pipeline_engines_fiftyone/share_usage.py index 210dfb0a..ec2a657a 100644 --- a/pipeline/fiftyone_pipeline_engines_fiftyone/src/fiftyone_pipeline_engines_fiftyone/share_usage.py +++ b/pipeline/fiftyone_pipeline_engines_fiftyone/src/fiftyone_pipeline_engines_fiftyone/share_usage.py @@ -29,12 +29,19 @@ from importlib.metadata import version import requests +from fiftyone_pipeline_core.basiclist_evidence_keyfilter import \ + BasicListEvidenceKeyFilter from fiftyone_pipeline_engines.engine import Engine from .share_usage_evidencekeyfilter import ShareUsageEvidenceKeyFilter, \ ShareUsageTrackerEvidenceKeyFilter from .share_usage_tracker import ShareUsageTracker +# The key the web integration gives the web request itself. Written out +# rather than imported, so the element still loads beside an older +# fiftyone_pipeline_core. +HTTP_REQUEST_KEY = "server.http-request" + # The maximum length of a piece of evidence's value which can be # added to the usage data being sent. SHARE_USAGE_MAX_EVIDENCE_LENGTH = 512 @@ -123,11 +130,13 @@ def __init__( included = None if query_whitelist is None else \ list(query_whitelist) + ["session-id", "sequence"] - # When sessions are tracked, the pipeline keeps the session cookie - # for the tracker. The data sent leaves it out unless all evidence + # The element accepts no evidence, so that caching, the ETag and the + # Vary header never depend on it. It shares what it finds instead, + # and these filters decide which of that is shared and which tells + # a repeat apart. When sessions are tracked the tracker reads the + # session cookie, which the data sent leaves out unless all evidence # is shared. - self.evidence_key_filter = ShareUsageEvidenceKeyFilter( - cookie, included, header_blacklist, track_session, share_all) + self.evidence_key_filter = BasicListEvidenceKeyFilter([]) self.data_evidence_key_filter = ShareUsageEvidenceKeyFilter( cookie, included, header_blacklist, False, share_all) self.tracker_evidence_key_filter = \ @@ -214,14 +223,40 @@ def get_evidence_key_filter(self): """! - The share useage element comes with its own evidence - key filter that uses the whitelists and blacklists to - determine which evidence to share - + The usage sharing element accepts no evidence. See found_evidence + for what it shares. """ return self.evidence_key_filter + def found_evidence(self, flow_data): + + """! + The evidence the element finds to share. These are the values of the + web request, when the web integration added it as + server.http-request, and every evidence value in the flow data, + whether an element accepted it or not. A key found in both places + takes the evidence value. The web request itself is never one of + them. + + @type flow_data: FlowData + @param flow_data: The flow data being processed + @rtype: dict + @return: The evidence found, before the sharing rules apply + """ + + evidence = flow_data.evidence + # An older fiftyone_pipeline_core sets nothing aside. + unaccepted = getattr(evidence, "get_unaccepted", dict)() + web_request = unaccepted.get(HTTP_REQUEST_KEY) or \ + evidence.get(HTTP_REQUEST_KEY) or {} + found = {} + for source in (web_request, unaccepted, evidence.get_all()): + for key, value in source.items(): + if str(key).lower() != HTTP_REQUEST_KEY: + found[key] = value + return found + def share_send_usage(self): """! Internal method to send the share usage bundle to the 51Degrees servers @@ -319,8 +354,11 @@ def process_internal(self, flow_data): """ if random.uniform(0, 1) <= self.share_percentage: - cachekey = self.tracker_evidence_key_filter.filter_evidence(flow_data.evidence.get_all()) - cachekey = json.dumps(cachekey) + # A hash of the values that would be shared, as the element + # accepts no evidence of its own. + cachekey = self.tracker_evidence_key_filter.filter_evidence( + self.found_evidence(flow_data)) + cachekey = json.dumps(cachekey, sort_keys=True, default=str) share = self.tracker.track(cachekey) if share: self.tracker.set_cache_value(cachekey) @@ -336,7 +374,7 @@ def get_data_from_evidence(self, flow_data): """ data = ShareUsageData() - for key, value in flow_data.evidence.get_all().items(): + for key, value in self.found_evidence(flow_data).items(): if key == 'server.client-ip': # The client IP is dealt with separately for backwards # compatibility purposes. diff --git a/pipeline/fiftyone_pipeline_engines_fiftyone/src/fiftyone_pipeline_engines_fiftyone/share_usage_evidencekeyfilter.py b/pipeline/fiftyone_pipeline_engines_fiftyone/src/fiftyone_pipeline_engines_fiftyone/share_usage_evidencekeyfilter.py index 5f2292f3..dc4d4264 100644 --- a/pipeline/fiftyone_pipeline_engines_fiftyone/src/fiftyone_pipeline_engines_fiftyone/share_usage_evidencekeyfilter.py +++ b/pipeline/fiftyone_pipeline_engines_fiftyone/src/fiftyone_pipeline_engines_fiftyone/share_usage_evidencekeyfilter.py @@ -26,16 +26,15 @@ # this are the ones 51Degrees sets, and are shared. FIFTYONE_PREFIX = "51d_" -# The Cookie header carries every cookie, including the ones the cookie rule -# leaves out, so it is not shared unless all evidence is. -BLOCKED_HEADERS = ("cookie",) - # Evidence that is never shared, whatever the settings, matched as the whole # key or after a '.'. 'id.email' and 'id.salt' are a raw email address and the # salt used to make hashed email identifiers. 'request-query' is the whole query # string, which would undo the rules for its parameters. 'request-path' would # make each page a visitor opens look like a new session to the tracker. -NEVER_SHARED_KEYS = ("id.email", "id.salt", "request-query", "request-path") +# 'header.cookie' is the Cookie header, which carries every cookie, and the +# cookies that are shared are shared as their own 'cookie.' evidence. +NEVER_SHARED_KEYS = ( + "id.email", "id.salt", "request-query", "request-path", "header.cookie") class ShareUsageEvidenceKeyFilter(BasicListEvidenceKeyFilter): """ @@ -110,8 +109,7 @@ def filter_evidence_key(self, key): elif prefix == "header": # Add the header to the list if the header name does not # appear in the list of blocked headers - return field not in self.header_blacklist and \ - field not in BLOCKED_HEADERS + return field not in self.header_blacklist elif prefix == "cookie": # Only add cookies that start with the 51Degrees cookie # prefix diff --git a/pipeline/fiftyone_pipeline_engines_fiftyone/tests/test_share_usage_accepts_no_evidence.py b/pipeline/fiftyone_pipeline_engines_fiftyone/tests/test_share_usage_accepts_no_evidence.py new file mode 100644 index 00000000..e683f856 --- /dev/null +++ b/pipeline/fiftyone_pipeline_engines_fiftyone/tests/test_share_usage_accepts_no_evidence.py @@ -0,0 +1,208 @@ +# ********************************************************************* +# This Original Work is copyright of 51 Degrees Mobile Experts Limited. +# Copyright 2026 51 Degrees Mobile Experts Limited, Davidson House, +# Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. +# +# This Original Work is licensed under the European Union Public Licence +# (EUPL) v.1.2 and is subject to its terms as set out below. +# +# If a copy of the EUPL was not distributed with this file, You can obtain +# one at https://opensource.org/licenses/EUPL-1.2. +# +# The 'Compatible Licences' set out in the Appendix to the EUPL (as may be +# amended by the European Commission) shall be deemed incompatible for +# the purposes of the Work and the provisions of the compatibility +# clause in Article 5 of the EUPL shall not apply. +# +# If using the Work as, or as part of, a network application, by +# including the attribution notice(s) required under Article 5 of the EUPL +# in the end user terms of the application under an appropriate heading, +# such notice(s) shall fulfill the requirements of that article. +# ********************************************************************* + +"""The usage sharing element accepts no evidence, so a pipeline's accepted +evidence, and the cache keys, ETag and Vary header built from it, never +depend on it. It shares what it finds in the flow data and in the web +request instead, by the same rules as before.""" + +import unittest + +from flask import Flask, request + +from fiftyone_pipeline_core.basiclist_evidence_keyfilter import \ + BasicListEvidenceKeyFilter +from fiftyone_pipeline_core.flowelement import FlowElement +from fiftyone_pipeline_core.pipelinebuilder import PipelineBuilder +from fiftyone_pipeline_core.web import webevidence +from fiftyone_pipeline_engines_fiftyone.share_usage import ShareUsage + +# No JavaScript or set header elements, so the filters are those of the +# usage sharing element and of UserAgentElement alone. +NO_DEFAULT_ELEMENTS = { + "add_javascript_builder": False, + "use_setheader_properties": False, +} + +CHROME = ("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " + "(KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36") + + +class UserAgentElement(FlowElement): + """A stand-in for an engine, accepting the User-Agent alone.""" + + def __init__(self): + super(UserAgentElement, self).__init__() + self.datakey = "useragent" + + def process_internal(self, flowdata): + pass + + def get_evidence_key_filter(self): + return BasicListEvidenceKeyFilter(["header.user-agent"]) + + +def usage_element(**settings): + """A usage sharing element that keeps what it would share, because its + package size is never reached, and sends nothing.""" + + return ShareUsage( + requested_package_size=1000, endpoint="http://127.0.0.1:9", + **settings) + + +def pipeline_with(usage): + return PipelineBuilder(NO_DEFAULT_ELEMENTS) \ + .add(UserAgentElement()).add(usage).build() + + +def request_evidence(headers, path="/"): + """The evidence the web integration makes from a request.""" + + with Flask(__name__).test_request_context(path, headers=headers): + return webevidence(request) + + +class ShareUsageAcceptsNoEvidenceTests(unittest.TestCase): + + def test_the_pipeline_accepts_no_header_for_usage_sharing(self): + usage = usage_element() + pipeline = pipeline_with(usage) + + for key in ("header.if-none-match", "header.x-test", + "cookie.51d_screenpixelswidth", "server.client-ip"): + self.assertFalse(usage.get_evidence_key_filter() + .filter_evidence_key(key), key) + self.assertFalse(any( + element.filter_evidence_key(key) + for element in pipeline.flow_elements), key) + self.assertTrue(any( + element.filter_evidence_key("header.user-agent") + for element in pipeline.flow_elements)) + + def test_a_revalidation_has_the_same_accepted_evidence(self): + """The ETag of a client-side endpoint hashes the evidence the + pipeline accepts, so a revalidation, which adds If-None-Match, has + to leave that evidence as it was.""" + + pipeline = pipeline_with(usage_element()) + accepted = [] + for headers in ({"User-Agent": CHROME}, + {"User-Agent": CHROME, "If-None-Match": '"a-tag"'}): + flowdata = pipeline.create_flowdata() + flowdata.evidence.add_from_dict(request_evidence(headers)) + flowdata.process() + accepted.append(dict(flowdata.evidence.get_all())) + + self.assertEqual(accepted[0], accepted[1]) + self.assertNotIn("header.if-none-match", accepted[1]) + + def test_the_web_request_is_shared_by_the_same_rules(self): + usage = usage_element(header_blacklist=["x-blocked"]) + flowdata = pipeline_with(usage).create_flowdata() + flowdata.evidence.add_from_dict(request_evidence({ + "User-Agent": CHROME, + "X-Test": "only usage sharing reads this", + "X-Blocked": "never shared", + "Cookie": "51D_ScreenPixelsWidth=1920; other=private", + }, "/?51D_GetHighEntropyValues=abc&password=secret")) + flowdata.process() + + shared = "".join(usage.share_data) + self.assertIn( + '
only usage sharing reads this
', + shared) + self.assertIn('
', shared) + self.assertIn( + '1920', shared) + self.assertIn( + 'abc', shared) + for left_out in ('Name="x-blocked"', 'Name="cookie"', "private", + "secret", "http-request"): + self.assertNotIn(left_out, shared) + + def test_the_method_is_shared_and_the_path_and_cookie_header_are_not(self): + """Even when all evidence is shared.""" + + usage = usage_element(share_all=True) + flowdata = pipeline_with(usage).create_flowdata() + flowdata.evidence.add_from_dict(request_evidence({ + "User-Agent": CHROME, + "Cookie": "sessionid=cookievalue", + }, "/page?a=1")) + flowdata.process() + + shared = "".join(usage.share_data) + self.assertIn("request-method", shared) + self.assertIn('cookievalue', + shared) + self.assertNotIn("/page", shared) + self.assertNotIn('
', shared) + + def test_a_key_in_both_places_is_shared_once_with_the_evidence_value(self): + usage = usage_element() + flowdata = pipeline_with(usage).create_flowdata() + flowdata.evidence.add_from_dict( + request_evidence({"X-Test": "from the request"})) + flowdata.evidence.add("header.x-test", "from the evidence") + flowdata.process() + + shared = "".join(usage.share_data) + self.assertEqual(1, shared.count('Name="x-test"')) + self.assertIn( + '
from the evidence
', shared) + + def test_evidence_added_by_hand_is_shared_as_before(self): + usage = usage_element() + flowdata = pipeline_with(usage).create_flowdata() + flowdata.evidence.add("header.user-agent", CHROME) + flowdata.evidence.add("header.x-test", "by hand") + flowdata.evidence.add("cookie.51D_ScreenPixelsWidth", "1920") + flowdata.evidence.add("cookie.other", "private") + flowdata.evidence.add("server.client-ip", "203.0.113.42") + flowdata.process() + + shared = "".join(usage.share_data) + self.assertIn('
by hand
', shared) + self.assertIn( + '1920', shared) + self.assertIn("203.0.113.42", shared) + self.assertNotIn("private", shared) + + def test_a_repeat_is_told_apart_by_what_would_be_shared(self): + """The tracker's hash is of the values that would be shared, so a + header no other element accepts still tells two requests apart, + and a repeat of the same request is shared once.""" + + usage = usage_element() + pipeline = pipeline_with(usage) + for value in ("first", "first", "second"): + flowdata = pipeline.create_flowdata() + flowdata.evidence.add_from_dict( + request_evidence({"User-Agent": CHROME, "X-Test": value})) + flowdata.process() + + self.assertEqual(2, len(usage.share_data)) + + +if __name__ == "__main__": + unittest.main() diff --git a/pipeline/fiftyone_pipeline_engines_fiftyone/tests/test_share_usage_evidencekeyfilter.py b/pipeline/fiftyone_pipeline_engines_fiftyone/tests/test_share_usage_evidencekeyfilter.py index 16184d52..03ab1380 100644 --- a/pipeline/fiftyone_pipeline_engines_fiftyone/tests/test_share_usage_evidencekeyfilter.py +++ b/pipeline/fiftyone_pipeline_engines_fiftyone/tests/test_share_usage_evidencekeyfilter.py @@ -112,7 +112,7 @@ def test_share_all(self): keys = ShareUsageEvidenceKeyFilter(share_all=True) - self.assertTrue(keys.filter_evidence_key("header.cookie")) + self.assertFalse(keys.filter_evidence_key("header.cookie")) self.assertTrue(keys.filter_evidence_key("cookie.sessionid")) self.assertTrue(keys.filter_evidence_key("query.password")) self.assertTrue(keys.filter_evidence_key("query.id.usage")) @@ -127,7 +127,8 @@ def test_share_all(self): def test_never_shared_in_every_mode(self): """! Check that the email address, the salt for hashed email - identifiers, the query string and the path are never shared, + identifiers, the query string, the path and the Cookie header are never + shared, whether bare or after a prefix, and without regard to case. """ @@ -140,7 +141,8 @@ def test_never_shared_in_every_mode(self): "query.id.email", "QUERY.ID.EMAIL", "header.id.salt", "HEADER.ID.SALT", "server.request-query", "SERVER.REQUEST-QUERY", - "server.request-path", "SERVER.REQUEST-PATH"): + "server.request-path", "SERVER.REQUEST-PATH", + "header.cookie", "HEADER.COOKIE"): self.assertFalse(keys.filter_evidence_key(key), key) def test_never_shared_keys_match_whole_parts(self): diff --git a/pipeline/fiftyone_pipeline_engines_fiftyone/tests/test_usage.py b/pipeline/fiftyone_pipeline_engines_fiftyone/tests/test_usage.py index 4f1bea40..d501af16 100644 --- a/pipeline/fiftyone_pipeline_engines_fiftyone/tests/test_usage.py +++ b/pipeline/fiftyone_pipeline_engines_fiftyone/tests/test_usage.py @@ -574,9 +574,9 @@ def test_empty_query_whitelist(self): def test_session_cookie(self): """! - Check that with session tracking on the session cookie is kept in - the evidence, but neither it nor any other ordinary cookie is - shared. + Check that with session tracking on the tracker reads the session + cookie, which the element does not accept as evidence, and that + neither it nor any other ordinary cookie is shared. """ usage = ShareUsage( @@ -595,7 +595,8 @@ def test_session_cookie(self): flowdata.process() self.waitForUsageThreads() - self.assertTrue(usage.get_evidence_key_filter().filter_evidence_key("cookie.sessionid")) + self.assertFalse(usage.get_evidence_key_filter().filter_evidence_key("cookie.sessionid")) + self.assertTrue(usage.tracker_evidence_key_filter.filter_evidence_key("cookie.sessionid")) self.assertEqual(len(app.received), 1) self.assertIn("some user agent", app.received[0]) self.assertNotIn("session value", app.received[0])