summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/common/security-features/subresource
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/__init__.py0
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/audio.py18
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/document.py12
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/empty.py14
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/font.py76
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/image.py116
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/referrer.py4
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/script.py14
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/shared-worker.py13
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/static-import.py61
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/stylesheet.py61
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/subresource.py199
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/svg.py37
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/template/document.html.template16
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/template/font.css.template9
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/template/image.css.template3
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/template/script.js.template3
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/template/shared-worker.js.template5
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/template/static-import.js.template1
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/template/svg.css.template3
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/template/svg.embedded.template5
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/template/worker.js.template3
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/video.py17
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/worker.py13
-rw-r--r--testing/web-platform/tests/common/security-features/subresource/xhr.py16
25 files changed, 719 insertions, 0 deletions
diff --git a/testing/web-platform/tests/common/security-features/subresource/__init__.py b/testing/web-platform/tests/common/security-features/subresource/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/__init__.py
diff --git a/testing/web-platform/tests/common/security-features/subresource/audio.py b/testing/web-platform/tests/common/security-features/subresource/audio.py
new file mode 100644
index 0000000000..f16a0f7fbb
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/audio.py
@@ -0,0 +1,18 @@
+import os, sys
+from wptserve.utils import isomorphic_decode
+import importlib
+subresource = importlib.import_module("common.security-features.subresource.subresource")
+
+def generate_payload(request, server_data):
+ file = os.path.join(request.doc_root, u"webaudio", u"resources",
+ u"sin_440Hz_-6dBFS_1s.wav")
+ return open(file, "rb").read()
+
+
+def main(request, response):
+ handler = lambda data: generate_payload(request, data)
+ subresource.respond(request,
+ response,
+ payload_generator = handler,
+ access_control_allow_origin = b"*",
+ content_type = b"audio/wav")
diff --git a/testing/web-platform/tests/common/security-features/subresource/document.py b/testing/web-platform/tests/common/security-features/subresource/document.py
new file mode 100644
index 0000000000..52b684a4d9
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/document.py
@@ -0,0 +1,12 @@
+import os, sys
+from wptserve.utils import isomorphic_decode
+import importlib
+subresource = importlib.import_module("common.security-features.subresource.subresource")
+
+def generate_payload(server_data):
+ return subresource.get_template(u"document.html.template") % server_data
+
+def main(request, response):
+ subresource.respond(request,
+ response,
+ payload_generator = generate_payload)
diff --git a/testing/web-platform/tests/common/security-features/subresource/empty.py b/testing/web-platform/tests/common/security-features/subresource/empty.py
new file mode 100644
index 0000000000..312e12cbed
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/empty.py
@@ -0,0 +1,14 @@
+import os, sys
+from wptserve.utils import isomorphic_decode
+import importlib
+subresource = importlib.import_module("common.security-features.subresource.subresource")
+
+def generate_payload(server_data):
+ return u''
+
+def main(request, response):
+ subresource.respond(request,
+ response,
+ payload_generator = generate_payload,
+ access_control_allow_origin = b"*",
+ content_type = b"text/plain")
diff --git a/testing/web-platform/tests/common/security-features/subresource/font.py b/testing/web-platform/tests/common/security-features/subresource/font.py
new file mode 100644
index 0000000000..7900079cdf
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/font.py
@@ -0,0 +1,76 @@
+import os, sys
+from base64 import decodebytes
+
+from wptserve.utils import isomorphic_decode
+import importlib
+subresource = importlib.import_module("common.security-features.subresource.subresource")
+
+
+def generate_payload(request, server_data):
+ data = (u'{"headers": %(headers)s}') % server_data
+ if b"id" in request.GET:
+ request.server.stash.put(request.GET[b"id"], data)
+ # Simple base64 encoded .tff font
+ return decodebytes(b"AAEAAAANAIAAAwBQRkZUTU6u6MkAAAXcAAAAHE9TLzJWYW"
+ b"QKAAABWAAAAFZjbWFwAA8D7wAAAcAAAAFCY3Z0IAAhAnkA"
+ b"AAMEAAAABGdhc3D//wADAAAF1AAAAAhnbHlmCC6aTwAAAx"
+ b"QAAACMaGVhZO8ooBcAAADcAAAANmhoZWEIkAV9AAABFAAA"
+ b"ACRobXR4EZQAhQAAAbAAAAAQbG9jYQBwAFQAAAMIAAAACm"
+ b"1heHAASQA9AAABOAAAACBuYW1lehAVOgAAA6AAAAIHcG9z"
+ b"dP+uADUAAAWoAAAAKgABAAAAAQAAMhPyuV8PPPUACwPoAA"
+ b"AAAMU4Lm0AAAAAxTgubQAh/5wFeAK8AAAACAACAAAAAAAA"
+ b"AAEAAAK8/5wAWgXcAAAAAAV4AAEAAAAAAAAAAAAAAAAAAA"
+ b"AEAAEAAAAEAAwAAwAAAAAAAgAAAAEAAQAAAEAALgAAAAAA"
+ b"AQXcAfQABQAAAooCvAAAAIwCigK8AAAB4AAxAQIAAAIABg"
+ b"kAAAAAAAAAAAABAAAAAAAAAAAAAAAAUGZFZABAAEEAQQMg"
+ b"/zgAWgK8AGQAAAABAAAAAAAABdwAIQAAAAAF3AAABdwAZA"
+ b"AAAAMAAAADAAAAHAABAAAAAAA8AAMAAQAAABwABAAgAAAA"
+ b"BAAEAAEAAABB//8AAABB////wgABAAAAAAAAAQYAAAEAAA"
+ b"AAAAAAAQIAAAACAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAA"
+ b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAA"
+ b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ b"AAAAAAAAAAAAAAAAAAAhAnkAAAAqACoAKgBGAAAAAgAhAA"
+ b"ABKgKaAAMABwAusQEALzyyBwQA7TKxBgXcPLIDAgDtMgCx"
+ b"AwAvPLIFBADtMrIHBgH8PLIBAgDtMjMRIREnMxEjIQEJ6M"
+ b"fHApr9ZiECWAAAAwBk/5wFeAK8AAMABwALAAABNSEVATUh"
+ b"FQE1IRUB9AH0/UQDhPu0BRQB9MjI/tTIyP7UyMgAAAAAAA"
+ b"4ArgABAAAAAAAAACYATgABAAAAAAABAAUAgQABAAAAAAAC"
+ b"AAYAlQABAAAAAAADACEA4AABAAAAAAAEAAUBDgABAAAAAA"
+ b"AFABABNgABAAAAAAAGAAUBUwADAAEECQAAAEwAAAADAAEE"
+ b"CQABAAoAdQADAAEECQACAAwAhwADAAEECQADAEIAnAADAA"
+ b"EECQAEAAoBAgADAAEECQAFACABFAADAAEECQAGAAoBRwBD"
+ b"AG8AcAB5AHIAaQBnAGgAdAAgACgAYwApACAAMgAwADAAOA"
+ b"AgAE0AbwB6AGkAbABsAGEAIABDAG8AcgBwAG8AcgBhAHQA"
+ b"aQBvAG4AAENvcHlyaWdodCAoYykgMjAwOCBNb3ppbGxhIE"
+ b"NvcnBvcmF0aW9uAABNAGEAcgBrAEEAAE1hcmtBAABNAGUA"
+ b"ZABpAHUAbQAATWVkaXVtAABGAG8AbgB0AEYAbwByAGcAZQ"
+ b"AgADIALgAwACAAOgAgAE0AYQByAGsAQQAgADoAIAA1AC0A"
+ b"MQAxAC0AMgAwADAAOAAARm9udEZvcmdlIDIuMCA6IE1hcm"
+ b"tBIDogNS0xMS0yMDA4AABNAGEAcgBrAEEAAE1hcmtBAABW"
+ b"AGUAcgBzAGkAbwBuACAAMAAwADEALgAwADAAMAAgAABWZX"
+ b"JzaW9uIDAwMS4wMDAgAABNAGEAcgBrAEEAAE1hcmtBAAAA"
+ b"AgAAAAAAAP+DADIAAAABAAAAAAAAAAAAAAAAAAAAAAAEAA"
+ b"AAAQACACQAAAAAAAH//wACAAAAAQAAAADEPovuAAAAAMU4"
+ b"Lm0AAAAAxTgubQ==")
+
+def generate_report_headers_payload(request, server_data):
+ stashed_data = request.server.stash.take(request.GET[b"id"])
+ return stashed_data
+
+def main(request, response):
+ handler = lambda data: generate_payload(request, data)
+ content_type = b'application/x-font-truetype'
+
+ if b"report-headers" in request.GET:
+ handler = lambda data: generate_report_headers_payload(request, data)
+ content_type = b'application/json'
+
+ subresource.respond(request,
+ response,
+ payload_generator = handler,
+ content_type = content_type,
+ access_control_allow_origin = b"*")
diff --git a/testing/web-platform/tests/common/security-features/subresource/image.py b/testing/web-platform/tests/common/security-features/subresource/image.py
new file mode 100644
index 0000000000..5c9a0c063c
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/image.py
@@ -0,0 +1,116 @@
+import os, sys, array, math
+
+from io import BytesIO
+
+from wptserve.utils import isomorphic_decode
+
+import importlib
+subresource = importlib.import_module("common.security-features.subresource.subresource")
+
+class Image:
+ """This class partially implements the interface of the PIL.Image.Image.
+ One day in the future WPT might support the PIL module or another imaging
+ library, so this hacky BMP implementation will no longer be required.
+ """
+ def __init__(self, width, height):
+ self.width = width
+ self.height = height
+ self.img = bytearray([0 for i in range(3 * width * height)])
+
+ @staticmethod
+ def new(mode, size, color=0):
+ return Image(size[0], size[1])
+
+ def _int_to_bytes(self, number):
+ packed_bytes = [0, 0, 0, 0]
+ for i in range(4):
+ packed_bytes[i] = number & 0xFF
+ number >>= 8
+
+ return packed_bytes
+
+ def putdata(self, color_data):
+ for y in range(self.height):
+ for x in range(self.width):
+ i = x + y * self.width
+ if i > len(color_data) - 1:
+ return
+
+ self.img[i * 3: i * 3 + 3] = color_data[i][::-1]
+
+ def save(self, f, type):
+ assert type == "BMP"
+ # 54 bytes of preambule + image color data.
+ filesize = 54 + 3 * self.width * self.height
+ # 14 bytes of header.
+ bmpfileheader = bytearray([ord('B'), ord('M')] + self._int_to_bytes(filesize) +
+ [0, 0, 0, 0, 54, 0, 0, 0])
+ # 40 bytes of info.
+ bmpinfoheader = bytearray([40, 0, 0, 0] +
+ self._int_to_bytes(self.width) +
+ self._int_to_bytes(self.height) +
+ [1, 0, 24] + (25 * [0]))
+
+ padlength = (4 - (self.width * 3) % 4) % 4
+ bmppad = bytearray([0, 0, 0])
+ padding = bmppad[0 : padlength]
+
+ f.write(bmpfileheader)
+ f.write(bmpinfoheader)
+
+ for i in range(self.height):
+ offset = self.width * (self.height - i - 1) * 3
+ f.write(self.img[offset : offset + 3 * self.width])
+ f.write(padding)
+
+def encode_string_as_bmp_image(string_data):
+ data_bytes = array.array("B", string_data.encode("utf-8"))
+
+ num_bytes = len(data_bytes)
+
+ # Encode data bytes to color data (RGB), one bit per channel.
+ # This is to avoid errors due to different color spaces used in decoding.
+ color_data = []
+ for byte in data_bytes:
+ p = [int(x) * 255 for x in '{0:08b}'.format(byte)]
+ color_data.append((p[0], p[1], p[2]))
+ color_data.append((p[3], p[4], p[5]))
+ color_data.append((p[6], p[7], 0))
+
+ # Render image.
+ num_pixels = len(color_data)
+ sqrt = int(math.ceil(math.sqrt(num_pixels)))
+ img = Image.new("RGB", (sqrt, sqrt), "black")
+ img.putdata(color_data)
+
+ # Flush image to string.
+ f = BytesIO()
+ img.save(f, "BMP")
+ f.seek(0)
+
+ return f.read()
+
+def generate_payload(request, server_data):
+ data = (u'{"headers": %(headers)s}') % server_data
+ if b"id" in request.GET:
+ request.server.stash.put(request.GET[b"id"], data)
+ data = encode_string_as_bmp_image(data)
+ return data
+
+def generate_report_headers_payload(request, server_data):
+ stashed_data = request.server.stash.take(request.GET[b"id"])
+ return stashed_data
+
+def main(request, response):
+ handler = lambda data: generate_payload(request, data)
+ content_type = b'image/bmp'
+
+ if b"report-headers" in request.GET:
+ handler = lambda data: generate_report_headers_payload(request, data)
+ content_type = b'application/json'
+
+ subresource.respond(request,
+ response,
+ payload_generator = handler,
+ content_type = content_type,
+ access_control_allow_origin = b"*")
diff --git a/testing/web-platform/tests/common/security-features/subresource/referrer.py b/testing/web-platform/tests/common/security-features/subresource/referrer.py
new file mode 100644
index 0000000000..e36631479e
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/referrer.py
@@ -0,0 +1,4 @@
+def main(request, response):
+ referrer = request.headers.get(b"referer", b"")
+ response_headers = [(b"Content-Type", b"text/javascript")]
+ return (200, response_headers, b"window.referrer = '" + referrer + b"'")
diff --git a/testing/web-platform/tests/common/security-features/subresource/script.py b/testing/web-platform/tests/common/security-features/subresource/script.py
new file mode 100644
index 0000000000..9701816b9f
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/script.py
@@ -0,0 +1,14 @@
+import os, sys
+from wptserve.utils import isomorphic_decode
+
+import importlib
+subresource = importlib.import_module("common.security-features.subresource.subresource")
+
+def generate_payload(server_data):
+ return subresource.get_template(u"script.js.template") % server_data
+
+def main(request, response):
+ subresource.respond(request,
+ response,
+ payload_generator = generate_payload,
+ content_type = b"application/javascript")
diff --git a/testing/web-platform/tests/common/security-features/subresource/shared-worker.py b/testing/web-platform/tests/common/security-features/subresource/shared-worker.py
new file mode 100644
index 0000000000..bdfb61bbb3
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/shared-worker.py
@@ -0,0 +1,13 @@
+import os, sys
+from wptserve.utils import isomorphic_decode
+import importlib
+subresource = importlib.import_module("common.security-features.subresource.subresource")
+
+def generate_payload(server_data):
+ return subresource.get_template(u"shared-worker.js.template") % server_data
+
+def main(request, response):
+ subresource.respond(request,
+ response,
+ payload_generator = generate_payload,
+ content_type = b"application/javascript")
diff --git a/testing/web-platform/tests/common/security-features/subresource/static-import.py b/testing/web-platform/tests/common/security-features/subresource/static-import.py
new file mode 100644
index 0000000000..3c3a6f6871
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/static-import.py
@@ -0,0 +1,61 @@
+import os, sys, json
+from urllib.parse import unquote
+
+from wptserve.utils import isomorphic_decode
+import importlib
+subresource = importlib.import_module("common.security-features.subresource.subresource")
+
+def get_csp_value(value):
+ '''
+ Returns actual CSP header values (e.g. "worker-src 'self'") for the
+ given string used in PolicyDelivery's value (e.g. "worker-src-self").
+ '''
+
+ # script-src
+ # Test-related scripts like testharness.js and inline scripts containing
+ # test bodies.
+ # 'unsafe-inline' is added as a workaround here. This is probably not so
+ # bad, as it shouldn't intefere non-inline-script requests that we want to
+ # test.
+ if value == 'script-src-wildcard':
+ return "script-src * 'unsafe-inline'"
+ if value == 'script-src-self':
+ return "script-src 'self' 'unsafe-inline'"
+ # Workaround for "script-src 'none'" would be more complicated, because
+ # - "script-src 'none' 'unsafe-inline'" is handled somehow differently from
+ # "script-src 'none'", i.e.
+ # https://w3c.github.io/webappsec-csp/#match-url-to-source-list Step 3
+ # handles the latter but not the former.
+ # - We need nonce- or path-based additional values to allow same-origin
+ # test scripts like testharness.js.
+ # Therefore, we disable 'script-src-none' tests for now in
+ # `/content-security-policy/spec.src.json`.
+ if value == 'script-src-none':
+ return "script-src 'none'"
+
+ # worker-src
+ if value == 'worker-src-wildcard':
+ return 'worker-src *'
+ if value == 'worker-src-self':
+ return "worker-src 'self'"
+ if value == 'worker-src-none':
+ return "worker-src 'none'"
+ raise Exception('Invalid delivery_value: %s' % value)
+
+def generate_payload(request):
+ import_url = unquote(isomorphic_decode(request.GET[b'import_url']))
+ return subresource.get_template(u"static-import.js.template") % {
+ u"import_url": import_url
+ }
+
+def main(request, response):
+ def payload_generator(_): return generate_payload(request)
+ maybe_additional_headers = {}
+ if b'contentSecurityPolicy' in request.GET:
+ csp = unquote(isomorphic_decode(request.GET[b'contentSecurityPolicy']))
+ maybe_additional_headers[b'Content-Security-Policy'] = get_csp_value(csp)
+ subresource.respond(request,
+ response,
+ payload_generator = payload_generator,
+ content_type = b"application/javascript",
+ maybe_additional_headers = maybe_additional_headers)
diff --git a/testing/web-platform/tests/common/security-features/subresource/stylesheet.py b/testing/web-platform/tests/common/security-features/subresource/stylesheet.py
new file mode 100644
index 0000000000..05db249250
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/stylesheet.py
@@ -0,0 +1,61 @@
+import os, sys
+from wptserve.utils import isomorphic_decode
+import importlib
+subresource = importlib.import_module("common.security-features.subresource.subresource")
+
+def generate_payload(request, server_data):
+ data = (u'{"headers": %(headers)s}') % server_data
+ type = b'image'
+ if b"type" in request.GET:
+ type = request.GET[b"type"]
+
+ if b"id" in request.GET:
+ request.server.stash.put(request.GET[b"id"], data)
+
+ if type == b'image':
+ return subresource.get_template(u"image.css.template") % {u"id": isomorphic_decode(request.GET[b"id"])}
+
+ elif type == b'font':
+ return subresource.get_template(u"font.css.template") % {u"id": isomorphic_decode(request.GET[b"id"])}
+
+ elif type == b'svg':
+ return subresource.get_template(u"svg.css.template") % {
+ u"id": isomorphic_decode(request.GET[b"id"]),
+ u"property": isomorphic_decode(request.GET[b"property"])}
+
+ # A `'stylesheet-only'`-type stylesheet has no nested resources; this is
+ # useful in tests that cover referrers for stylesheet fetches (e.g. fetches
+ # triggered by `@import` statements).
+ elif type == b'stylesheet-only':
+ return u''
+
+def generate_import_rule(request, server_data):
+ return u"@import url('%(url)s');" % {
+ u"url": subresource.create_url(request, swap_origin=True,
+ query_parameter_to_remove=u"import-rule")
+ }
+
+def generate_report_headers_payload(request, server_data):
+ stashed_data = request.server.stash.take(request.GET[b"id"])
+ return stashed_data
+
+def main(request, response):
+ payload_generator = lambda data: generate_payload(request, data)
+ content_type = b"text/css"
+ referrer_policy = b"unsafe-url"
+ if b"import-rule" in request.GET:
+ payload_generator = lambda data: generate_import_rule(request, data)
+
+ if b"report-headers" in request.GET:
+ payload_generator = lambda data: generate_report_headers_payload(request, data)
+ content_type = b'application/json'
+
+ if b"referrer-policy" in request.GET:
+ referrer_policy = request.GET[b"referrer-policy"]
+
+ subresource.respond(
+ request,
+ response,
+ payload_generator = payload_generator,
+ content_type = content_type,
+ maybe_additional_headers = { b"Referrer-Policy": referrer_policy })
diff --git a/testing/web-platform/tests/common/security-features/subresource/subresource.py b/testing/web-platform/tests/common/security-features/subresource/subresource.py
new file mode 100644
index 0000000000..b3c055a93a
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/subresource.py
@@ -0,0 +1,199 @@
+import os, json
+from urllib.parse import parse_qsl, SplitResult, urlencode, urlsplit, urlunsplit
+
+from wptserve.utils import isomorphic_decode, isomorphic_encode
+
+def get_template(template_basename):
+ script_directory = os.path.dirname(os.path.abspath(isomorphic_decode(__file__)))
+ template_directory = os.path.abspath(os.path.join(script_directory,
+ u"template"))
+ template_filename = os.path.join(template_directory, template_basename)
+
+ with open(template_filename, "r") as f:
+ return f.read()
+
+
+def redirect(url, response):
+ response.add_required_headers = False
+ response.writer.write_status(301)
+ response.writer.write_header(b"access-control-allow-origin", b"*")
+ response.writer.write_header(b"location", isomorphic_encode(url))
+ response.writer.end_headers()
+ response.writer.write(u"")
+
+
+# TODO(kristijanburnik): subdomain_prefix is a hardcoded value aligned with
+# referrer-policy-test-case.js. The prefix should be configured in one place.
+def __get_swapped_origin_netloc(netloc, subdomain_prefix = u"www1."):
+ if netloc.startswith(subdomain_prefix):
+ return netloc[len(subdomain_prefix):]
+ else:
+ return subdomain_prefix + netloc
+
+
+# Creates a URL (typically a redirect target URL) that is the same as the
+# current request URL `request.url`, except for:
+# - When `swap_scheme` or `swap_origin` is True, its scheme/origin is changed
+# to the other one. (http <-> https, ws <-> wss, etc.)
+# - For `downgrade`, we redirect to a URL that would be successfully loaded
+# if and only if upgrade-insecure-request is applied.
+# - `query_parameter_to_remove` parameter is removed from query part.
+# Its default is "redirection" to avoid redirect loops.
+def create_url(request,
+ swap_scheme=False,
+ swap_origin=False,
+ downgrade=False,
+ query_parameter_to_remove=u"redirection"):
+ parsed = urlsplit(request.url)
+ destination_netloc = parsed.netloc
+
+ scheme = parsed.scheme
+ if swap_scheme:
+ scheme = u"http" if parsed.scheme == u"https" else u"https"
+ hostname = parsed.netloc.split(u':')[0]
+ port = request.server.config[u"ports"][scheme][0]
+ destination_netloc = u":".join([hostname, str(port)])
+
+ if downgrade:
+ # These rely on some unintuitive cleverness due to WPT's test setup:
+ # 'Upgrade-Insecure-Requests' does not upgrade the port number,
+ # so we use URLs in the form `http://[domain]:[https-port]`,
+ # which will be upgraded to `https://[domain]:[https-port]`.
+ # If the upgrade fails, the load will fail, as we don't serve HTTP over
+ # the secure port.
+ if parsed.scheme == u"https":
+ scheme = u"http"
+ elif parsed.scheme == u"wss":
+ scheme = u"ws"
+ else:
+ raise ValueError(u"Downgrade redirection: Invalid scheme '%s'" %
+ parsed.scheme)
+ hostname = parsed.netloc.split(u':')[0]
+ port = request.server.config[u"ports"][parsed.scheme][0]
+ destination_netloc = u":".join([hostname, str(port)])
+
+ if swap_origin:
+ destination_netloc = __get_swapped_origin_netloc(destination_netloc)
+
+ parsed_query = parse_qsl(parsed.query, keep_blank_values=True)
+ parsed_query = [x for x in parsed_query if x[0] != query_parameter_to_remove]
+
+ destination_url = urlunsplit(SplitResult(
+ scheme = scheme,
+ netloc = destination_netloc,
+ path = parsed.path,
+ query = urlencode(parsed_query),
+ fragment = None))
+
+ return destination_url
+
+
+def preprocess_redirection(request, response):
+ if b"redirection" not in request.GET:
+ return False
+
+ redirection = request.GET[b"redirection"]
+
+ if redirection == b"no-redirect":
+ return False
+ elif redirection == b"keep-scheme":
+ redirect_url = create_url(request, swap_scheme=False)
+ elif redirection == b"swap-scheme":
+ redirect_url = create_url(request, swap_scheme=True)
+ elif redirection == b"downgrade":
+ redirect_url = create_url(request, downgrade=True)
+ elif redirection == b"keep-origin":
+ redirect_url = create_url(request, swap_origin=False)
+ elif redirection == b"swap-origin":
+ redirect_url = create_url(request, swap_origin=True)
+ else:
+ raise ValueError(u"Invalid redirection type '%s'" % isomorphic_decode(redirection))
+
+ redirect(redirect_url, response)
+ return True
+
+
+def preprocess_stash_action(request, response):
+ if b"action" not in request.GET:
+ return False
+
+ action = request.GET[b"action"]
+
+ key = request.GET[b"key"]
+ stash = request.server.stash
+ path = request.GET[b"path"] if b"path" in request.GET \
+ else isomorphic_encode(request.url.split(u'?')[0])
+
+ if action == b"put":
+ value = isomorphic_decode(request.GET[b"value"])
+ stash.take(key=key, path=path)
+ stash.put(key=key, value=value, path=path)
+ response_data = json.dumps({u"status": u"success", u"result": isomorphic_decode(key)})
+ elif action == b"purge":
+ value = stash.take(key=key, path=path)
+ return False
+ elif action == b"take":
+ value = stash.take(key=key, path=path)
+ if value is None:
+ status = u"allowed"
+ else:
+ status = u"blocked"
+ response_data = json.dumps({u"status": status, u"result": value})
+ else:
+ return False
+
+ response.add_required_headers = False
+ response.writer.write_status(200)
+ response.writer.write_header(b"content-type", b"text/javascript")
+ response.writer.write_header(b"cache-control", b"no-cache; must-revalidate")
+ response.writer.end_headers()
+ response.writer.write(response_data)
+ return True
+
+
+def __noop(request, response):
+ return u""
+
+
+def respond(request,
+ response,
+ status_code = 200,
+ content_type = b"text/html",
+ payload_generator = __noop,
+ cache_control = b"no-cache; must-revalidate",
+ access_control_allow_origin = b"*",
+ maybe_additional_headers = None):
+ if preprocess_redirection(request, response):
+ return
+
+ if preprocess_stash_action(request, response):
+ return
+
+ response.add_required_headers = False
+ response.writer.write_status(status_code)
+
+ if access_control_allow_origin != None:
+ response.writer.write_header(b"access-control-allow-origin",
+ access_control_allow_origin)
+ response.writer.write_header(b"content-type", content_type)
+ response.writer.write_header(b"cache-control", cache_control)
+
+ additional_headers = maybe_additional_headers or {}
+ for header, value in additional_headers.items():
+ response.writer.write_header(header, value)
+
+ response.writer.end_headers()
+
+ new_headers = {}
+ new_val = []
+ for key, val in request.headers.items():
+ if len(val) == 1:
+ new_val = isomorphic_decode(val[0])
+ else:
+ new_val = [isomorphic_decode(x) for x in val]
+ new_headers[isomorphic_decode(key)] = new_val
+
+ server_data = {u"headers": json.dumps(new_headers, indent = 4)}
+
+ payload = payload_generator(server_data)
+ response.writer.write(payload)
diff --git a/testing/web-platform/tests/common/security-features/subresource/svg.py b/testing/web-platform/tests/common/security-features/subresource/svg.py
new file mode 100644
index 0000000000..9c569e3bf5
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/svg.py
@@ -0,0 +1,37 @@
+import os, sys
+from wptserve.utils import isomorphic_decode
+import importlib
+subresource = importlib.import_module("common.security-features.subresource.subresource")
+
+def generate_payload(request, server_data):
+ data = (u'{"headers": %(headers)s}') % server_data
+ if b"id" in request.GET:
+ with request.server.stash.lock:
+ request.server.stash.take(request.GET[b"id"])
+ request.server.stash.put(request.GET[b"id"], data)
+ return u"<svg xmlns='http://www.w3.org/2000/svg'></svg>"
+
+def generate_payload_embedded(request, server_data):
+ return subresource.get_template(u"svg.embedded.template") % {
+ u"id": isomorphic_decode(request.GET[b"id"]),
+ u"property": isomorphic_decode(request.GET[b"property"])}
+
+def generate_report_headers_payload(request, server_data):
+ stashed_data = request.server.stash.take(request.GET[b"id"])
+ return stashed_data
+
+def main(request, response):
+ handler = lambda data: generate_payload(request, data)
+ content_type = b'image/svg+xml'
+
+ if b"embedded-svg" in request.GET:
+ handler = lambda data: generate_payload_embedded(request, data)
+
+ if b"report-headers" in request.GET:
+ handler = lambda data: generate_report_headers_payload(request, data)
+ content_type = b'application/json'
+
+ subresource.respond(request,
+ response,
+ payload_generator = handler,
+ content_type = content_type)
diff --git a/testing/web-platform/tests/common/security-features/subresource/template/document.html.template b/testing/web-platform/tests/common/security-features/subresource/template/document.html.template
new file mode 100644
index 0000000000..141711c148
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/template/document.html.template
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>This page reports back it's request details to the parent frame</title>
+ </head>
+ <body>
+ <script>
+ var result = {
+ location: document.location.toString(),
+ referrer: document.referrer.length > 0 ? document.referrer : undefined,
+ headers: %(headers)s
+ };
+ parent.postMessage(result, "*");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/common/security-features/subresource/template/font.css.template b/testing/web-platform/tests/common/security-features/subresource/template/font.css.template
new file mode 100644
index 0000000000..9d1e9c421c
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/template/font.css.template
@@ -0,0 +1,9 @@
+@font-face {
+ font-family: 'wpt';
+ font-style: normal;
+ font-weight: normal;
+ src: url(/common/security-features/subresource/font.py?id=%(id)s) format('truetype');
+}
+body {
+ font-family: 'wpt';
+}
diff --git a/testing/web-platform/tests/common/security-features/subresource/template/image.css.template b/testing/web-platform/tests/common/security-features/subresource/template/image.css.template
new file mode 100644
index 0000000000..dfe41f1bf1
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/template/image.css.template
@@ -0,0 +1,3 @@
+div.styled::before {
+ content:url(/common/security-features/subresource/image.py?id=%(id)s)
+}
diff --git a/testing/web-platform/tests/common/security-features/subresource/template/script.js.template b/testing/web-platform/tests/common/security-features/subresource/template/script.js.template
new file mode 100644
index 0000000000..e2edf21819
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/template/script.js.template
@@ -0,0 +1,3 @@
+postMessage({
+ "headers": %(headers)s
+}, "*");
diff --git a/testing/web-platform/tests/common/security-features/subresource/template/shared-worker.js.template b/testing/web-platform/tests/common/security-features/subresource/template/shared-worker.js.template
new file mode 100644
index 0000000000..c3f109e4a9
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/template/shared-worker.js.template
@@ -0,0 +1,5 @@
+onconnect = function(e) {
+ e.ports[0].postMessage({
+ "headers": %(headers)s
+ });
+};
diff --git a/testing/web-platform/tests/common/security-features/subresource/template/static-import.js.template b/testing/web-platform/tests/common/security-features/subresource/template/static-import.js.template
new file mode 100644
index 0000000000..095459b547
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/template/static-import.js.template
@@ -0,0 +1 @@
+import '%(import_url)s';
diff --git a/testing/web-platform/tests/common/security-features/subresource/template/svg.css.template b/testing/web-platform/tests/common/security-features/subresource/template/svg.css.template
new file mode 100644
index 0000000000..c2e509cc3b
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/template/svg.css.template
@@ -0,0 +1,3 @@
+path {
+ %(property)s: url(/common/security-features/subresource/svg.py?id=%(id)s#invalidFragment);
+}
diff --git a/testing/web-platform/tests/common/security-features/subresource/template/svg.embedded.template b/testing/web-platform/tests/common/security-features/subresource/template/svg.embedded.template
new file mode 100644
index 0000000000..5986c4800a
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/template/svg.embedded.template
@@ -0,0 +1,5 @@
+<?xml version='1.0' standalone='no'?>
+<?xml-stylesheet href='stylesheet.py?id=%(id)s&amp;type=svg&amp;property=%(property)s' type='text/css'?>
+<svg xmlns='http://www.w3.org/2000/svg'>
+ <path d='M 50,5 95,100 5,100 z' />
+</svg>
diff --git a/testing/web-platform/tests/common/security-features/subresource/template/worker.js.template b/testing/web-platform/tests/common/security-features/subresource/template/worker.js.template
new file mode 100644
index 0000000000..817dd8c87a
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/template/worker.js.template
@@ -0,0 +1,3 @@
+postMessage({
+ "headers": %(headers)s
+});
diff --git a/testing/web-platform/tests/common/security-features/subresource/video.py b/testing/web-platform/tests/common/security-features/subresource/video.py
new file mode 100644
index 0000000000..7cfbbfa68c
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/video.py
@@ -0,0 +1,17 @@
+import os, sys
+from wptserve.utils import isomorphic_decode
+import importlib
+subresource = importlib.import_module("common.security-features.subresource.subresource")
+
+def generate_payload(request, server_data):
+ file = os.path.join(request.doc_root, u"media", u"movie_5.ogv")
+ return open(file, "rb").read()
+
+
+def main(request, response):
+ handler = lambda data: generate_payload(request, data)
+ subresource.respond(request,
+ response,
+ payload_generator = handler,
+ access_control_allow_origin = b"*",
+ content_type = b"video/ogg")
diff --git a/testing/web-platform/tests/common/security-features/subresource/worker.py b/testing/web-platform/tests/common/security-features/subresource/worker.py
new file mode 100644
index 0000000000..f655633b5d
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/worker.py
@@ -0,0 +1,13 @@
+import os, sys
+from wptserve.utils import isomorphic_decode
+import importlib
+subresource = importlib.import_module("common.security-features.subresource.subresource")
+
+def generate_payload(server_data):
+ return subresource.get_template(u"worker.js.template") % server_data
+
+def main(request, response):
+ subresource.respond(request,
+ response,
+ payload_generator = generate_payload,
+ content_type = b"application/javascript")
diff --git a/testing/web-platform/tests/common/security-features/subresource/xhr.py b/testing/web-platform/tests/common/security-features/subresource/xhr.py
new file mode 100644
index 0000000000..75921e9156
--- /dev/null
+++ b/testing/web-platform/tests/common/security-features/subresource/xhr.py
@@ -0,0 +1,16 @@
+import os, sys
+from wptserve.utils import isomorphic_decode
+import importlib
+subresource = importlib.import_module("common.security-features.subresource.subresource")
+
+def generate_payload(server_data):
+ data = (u'{"headers": %(headers)s}') % server_data
+ return data
+
+def main(request, response):
+ subresource.respond(request,
+ response,
+ payload_generator = generate_payload,
+ access_control_allow_origin = b"*",
+ content_type = b"application/json",
+ cache_control = b"no-store")