Fixed CSP directive value parsing accepted character range
Bug: 845961
Change-Id: Ifc9609058cd7cbd268785db46534e3ed09da6ce3
Reviewed-on: https://chromium-review.googlesource.com/1071510
Commit-Queue: Andy Paicu <andypaicu@chromium.org>
Reviewed-by: Mike West <mkwst@chromium.org>
Cr-Commit-Position: refs/heads/master@{#561834}
diff --git a/content-security-policy/embedded-enforcement/required_csp-header-crlf.html b/content-security-policy/embedded-enforcement/required_csp-header-crlf.html
new file mode 100644
index 0000000..87bda1b
--- /dev/null
+++ b/content-security-policy/embedded-enforcement/required_csp-header-crlf.html
@@ -0,0 +1,144 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Embedded Enforcement: Sec-Required-CSP header.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="support/testharness-helper.sub.js"></script>
+</head>
+<body>
+ <script>
+ var tests = [
+ // CRLF characters
+ { "name": "\\r\\n character after directive name",
+ "csp": "script-src\r\n'unsafe-inline'",
+ "expected": null },
+ { "name": "\\r\\n character in directive value",
+ "csp": "script-src 'unsafe-inline'\r\n'unsafe-eval'",
+ "expected": null },
+ { "name": "\\n character after directive name",
+ "csp": "script-src\n'unsafe-inline'",
+ "expected": null },
+ { "name": "\\n character in directive value",
+ "csp": "script-src 'unsafe-inline'\n'unsafe-eval'",
+ "expected": null },
+ { "name": "\\r character after directive name",
+ "csp": "script-src\r'unsafe-inline'",
+ "expected": null },
+ { "name": "\\r character in directive value",
+ "csp": "script-src 'unsafe-inline'\r'unsafe-eval'",
+ "expected": null },
+
+ // HTML encoded CRLF characters
+ { "name": "%0D%0A character after directive name",
+ "csp": "script-src%0D%0A'unsafe-inline'",
+ "expected": null },
+ { "name": "%0D%0A character in directive value",
+ "csp": "script-src 'unsafe-inline'%0D%0A'unsafe-eval'",
+ "expected": null },
+ { "name": "%0A character after directive name",
+ "csp": "script-src%0A'unsafe-inline'",
+ "expected": null },
+ { "name": "%0A character in directive value",
+ "csp": "script-src 'unsafe-inline'%0A'unsafe-eval'",
+ "expected": null },
+ { "name": "%0D character after directive name",
+ "csp": "script-src%0D'unsafe-inline'",
+ "expected": null },
+ { "name": "%0D character in directive value",
+ "csp": "script-src 'unsafe-inline'%0D'unsafe-eval'",
+ "expected": null },
+
+ // Attempt HTTP Header injection
+ { "name": "Attempt injecting after directive name using \\r\\n",
+ "csp": "script-src\r\nTest-Header-Injection: dummy",
+ "expected": null },
+ { "name": "Attempt injecting after directive name using \\r",
+ "csp": "script-src\rTest-Header-Injection: dummy",
+ "expected": null },
+ { "name": "Attempt injecting after directive name using \\n",
+ "csp": "script-src\nTest-Header-Injection: dummy",
+ "expected": null },
+
+ { "name": "Attempt injecting after directive value using \\r\\n",
+ "csp": "script-src example.com\r\nTest-Header-Injection: dummy",
+ "expected": null },
+ { "name": "Attempt injecting after directive value using \\r",
+ "csp": "script-src example.com\rTest-Header-Injection: dummy",
+ "expected": null },
+ { "name": "Attempt injecting after directive value using \\n",
+ "csp": "script-src example.com\nTest-Header-Injection: dummy",
+ "expected": null },
+
+ { "name": "Attempt injecting after semicolon using \\r\\n",
+ "csp": "script-src example.com;\r\nTest-Header-Injection: dummy",
+ "expected": null },
+ { "name": "Attempt injecting after semicolon using \\r",
+ "csp": "script-src example.com;\rTest-Header-Injection: dummy",
+ "expected": null },
+ { "name": "Attempt injecting after semicolon using \\n",
+ "csp": "script-src example.com;\nTest-Header-Injection: dummy",
+ "expected": null },
+
+ { "name": "Attempt injecting after space between name and value using \\r\\n",
+ "csp": "script-src \r\nTest-Header-Injection: dummy",
+ "expected": null },
+ { "name": "Attempt injecting after space between name and value using \\r",
+ "csp": "script-src \rTest-Header-Injection: dummy",
+ "expected": null },
+ { "name": "Attempt injecting after space between name and value using \\n",
+ "csp": "script-src \nTest-Header-Injection: dummy",
+ "expected": null },
+
+ // Attempt HTTP Header injection using URL encoded characters
+ { "name": "Attempt injecting after directive name using %0D%0A",
+ "csp": "script-src%0D%0ATest-Header-Injection: dummy",
+ "expected": null },
+ { "name": "Attempt injecting after directive name using %0D",
+ "csp": "script-src%0DTest-Header-Injection: dummy",
+ "expected": null },
+ { "name": "Attempt injecting after directive name using %0A",
+ "csp": "script-src%0ATest-Header-Injection: dummy",
+ "expected": null },
+
+ { "name": "Attempt injecting after directive value using %0D%0A",
+ "csp": "script-src example.com%0D%0ATest-Header-Injection: dummy",
+ "expected": null },
+ { "name": "Attempt injecting after directive value using %0D",
+ "csp": "script-src example.com%0DTest-Header-Injection: dummy",
+ "expected": null },
+ { "name": "Attempt injecting after directive value using %0A",
+ "csp": "script-src example.com%0ATest-Header-Injection: dummy",
+ "expected": null },
+
+ { "name": "Attempt injecting after semicolon using %0D%0A",
+ "csp": "script-src example.com;%0D%0ATest-Header-Injection: dummy",
+ "expected": null },
+ { "name": "Attempt injecting after semicolon using %0D",
+ "csp": "script-src example.com;%0DTest-Header-Injection: dummy",
+ "expected": null },
+ { "name": "Attempt injecting after semicolon using %0A",
+ "csp": "script-src example.com;%0ATest-Header-Injection: dummy",
+ "expected": null },
+
+ { "name": "Attempt injecting after space between name and value using %0D%0A",
+ "csp": "script-src %0D%0ATest-Header-Injection: dummy",
+ "expected": null },
+ { "name": "Attempt injecting after space between name and value using %0D",
+ "csp": "script-src %0DTest-Header-Injection: dummy",
+ "expected": null },
+ { "name": "Attempt injecting after space between name and value using %0A",
+ "csp": "script-src %0ATest-Header-Injection: dummy",
+ "expected": null },
+
+ ];
+
+ tests.forEach(test => {
+ async_test(t => {
+ var url = generateURLString(Host.SAME_ORIGIN, PolicyHeader.REQUIRED_CSP);
+ assert_required_csp(t, url, test.csp, [test.expected]);
+ }, "Test CRLF: " + test.name);
+ });
+ </script>
+</body>
+</html>
diff --git a/content-security-policy/embedded-enforcement/support/echo-required-csp.py b/content-security-policy/embedded-enforcement/support/echo-required-csp.py
index 930a1f6..6063cc0 100644
--- a/content-security-policy/embedded-enforcement/support/echo-required-csp.py
+++ b/content-security-policy/embedded-enforcement/support/echo-required-csp.py
@@ -1,8 +1,13 @@
import json
def main(request, response):
- header = request.headers.get("Sec-Required-CSP");
message = {}
+
+ header = request.headers.get("Test-Header-Injection");
+ message['test_header_injection'] = header if header else None
+
+ header = request.headers.get("Sec-Required-CSP");
message['required_csp'] = header if header else None
+
second_level_iframe_code = ""
if "include_second_level_iframe" in request.GET:
if "second_level_iframe_csp" in request.GET and request.GET["second_level_iframe_csp"] <> "":
diff --git a/content-security-policy/embedded-enforcement/support/testharness-helper.sub.js b/content-security-policy/embedded-enforcement/support/testharness-helper.sub.js
index 127a94b..6d2e64f 100644
--- a/content-security-policy/embedded-enforcement/support/testharness-helper.sub.js
+++ b/content-security-policy/embedded-enforcement/support/testharness-helper.sub.js
@@ -91,6 +91,10 @@
assert_unreached('Child iframes have unexpected csp:"' + e.data['required_csp'] + '"');
expected.splice(expected.indexOf(e.data['required_csp']), 1);
+
+ if (e.data['test_header_injection'] != null)
+ assert_unreached('HTTP header injection was successful');
+
if (expected.length == 0)
t.done();
}));