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();
   }));