|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 #include "TestHarness.h" |
|
6 #include <stdio.h> |
|
7 #include "nsNetUtil.h" |
|
8 #include "nsISiteSecurityService.h" |
|
9 |
|
10 #define EXPECT_SUCCESS(rv, ...) \ |
|
11 PR_BEGIN_MACRO \ |
|
12 if (NS_FAILED(rv)) { \ |
|
13 fail(__VA_ARGS__); \ |
|
14 return false; \ |
|
15 } \ |
|
16 PR_END_MACRO |
|
17 |
|
18 |
|
19 #define EXPECT_FAILURE(rv, ...) \ |
|
20 PR_BEGIN_MACRO \ |
|
21 if (NS_SUCCEEDED(rv)) { \ |
|
22 fail(__VA_ARGS__); \ |
|
23 return false; \ |
|
24 } \ |
|
25 PR_END_MACRO |
|
26 |
|
27 #define REQUIRE_EQUAL(a, b, ...) \ |
|
28 PR_BEGIN_MACRO \ |
|
29 if (a != b) { \ |
|
30 fail(__VA_ARGS__); \ |
|
31 return false; \ |
|
32 } \ |
|
33 PR_END_MACRO |
|
34 |
|
35 bool |
|
36 TestSuccess(const char* hdr, bool extraTokens, |
|
37 uint64_t expectedMaxAge, bool expectedIncludeSubdomains, |
|
38 nsISiteSecurityService* sss) |
|
39 { |
|
40 nsCOMPtr<nsIURI> dummyUri; |
|
41 nsresult rv = NS_NewURI(getter_AddRefs(dummyUri), "https://foo.com/bar.html"); |
|
42 EXPECT_SUCCESS(rv, "Failed to create URI"); |
|
43 |
|
44 uint64_t maxAge = 0; |
|
45 bool includeSubdomains = false; |
|
46 rv = sss->ProcessHeader(nsISiteSecurityService::HEADER_HSTS, dummyUri, hdr, |
|
47 0, &maxAge, &includeSubdomains); |
|
48 EXPECT_SUCCESS(rv, "Failed to process valid header: %s", hdr); |
|
49 |
|
50 REQUIRE_EQUAL(maxAge, expectedMaxAge, "Did not correctly parse maxAge"); |
|
51 REQUIRE_EQUAL(includeSubdomains, expectedIncludeSubdomains, "Did not correctly parse presence/absence of includeSubdomains"); |
|
52 |
|
53 if (extraTokens) { |
|
54 REQUIRE_EQUAL(rv, NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA, |
|
55 "Extra tokens were expected when parsing, but were not encountered."); |
|
56 } else { |
|
57 REQUIRE_EQUAL(rv, NS_OK, "Unexpected tokens found during parsing."); |
|
58 } |
|
59 |
|
60 passed(hdr); |
|
61 return true; |
|
62 } |
|
63 |
|
64 bool TestFailure(const char* hdr, |
|
65 nsISiteSecurityService* sss) |
|
66 { |
|
67 nsCOMPtr<nsIURI> dummyUri; |
|
68 nsresult rv = NS_NewURI(getter_AddRefs(dummyUri), "https://foo.com/bar.html"); |
|
69 EXPECT_SUCCESS(rv, "Failed to create URI"); |
|
70 |
|
71 rv = sss->ProcessHeader(nsISiteSecurityService::HEADER_HSTS, dummyUri, hdr, |
|
72 0, nullptr, nullptr); |
|
73 EXPECT_FAILURE(rv, "Parsed invalid header: %s", hdr); |
|
74 passed(hdr); |
|
75 return true; |
|
76 } |
|
77 |
|
78 |
|
79 int |
|
80 main(int32_t argc, char *argv[]) |
|
81 { |
|
82 nsresult rv; |
|
83 ScopedXPCOM xpcom("STS Parser Tests"); |
|
84 if (xpcom.failed()) |
|
85 return -1; |
|
86 // Initialize a profile folder to ensure a clean shutdown. |
|
87 nsCOMPtr<nsIFile> profile = xpcom.GetProfileDirectory(); |
|
88 if (!profile) { |
|
89 fail("Couldn't get the profile directory."); |
|
90 return -1; |
|
91 } |
|
92 |
|
93 // grab handle to the service |
|
94 nsCOMPtr<nsISiteSecurityService> sss; |
|
95 sss = do_GetService("@mozilla.org/ssservice;1", &rv); |
|
96 NS_ENSURE_SUCCESS(rv, -1); |
|
97 |
|
98 int rv0, rv1; |
|
99 |
|
100 nsTArray<bool> rvs(24); |
|
101 |
|
102 // *** parsing tests |
|
103 printf("*** Attempting to parse valid STS headers ...\n"); |
|
104 |
|
105 // SHOULD SUCCEED: |
|
106 rvs.AppendElement(TestSuccess("max-age=100", false, 100, false, sss)); |
|
107 rvs.AppendElement(TestSuccess("max-age =100", false, 100, false, sss)); |
|
108 rvs.AppendElement(TestSuccess(" max-age=100", false, 100, false, sss)); |
|
109 rvs.AppendElement(TestSuccess("max-age = 100 ", false, 100, false, sss)); |
|
110 rvs.AppendElement(TestSuccess("max-age = \"100\" ", false, 100, false, sss)); |
|
111 rvs.AppendElement(TestSuccess("max-age=\"100\"", false, 100, false, sss)); |
|
112 rvs.AppendElement(TestSuccess(" max-age =\"100\" ", false, 100, false, sss)); |
|
113 rvs.AppendElement(TestSuccess("\tmax-age\t=\t\"100\"\t", false, 100, false, sss)); |
|
114 rvs.AppendElement(TestSuccess("max-age = 100 ", false, 100, false, sss)); |
|
115 |
|
116 rvs.AppendElement(TestSuccess("maX-aGe=100", false, 100, false, sss)); |
|
117 rvs.AppendElement(TestSuccess("MAX-age =100", false, 100, false, sss)); |
|
118 rvs.AppendElement(TestSuccess("max-AGE=100", false, 100, false, sss)); |
|
119 rvs.AppendElement(TestSuccess("Max-Age = 100 ", false, 100, false, sss)); |
|
120 rvs.AppendElement(TestSuccess("MAX-AGE = 100 ", false, 100, false, sss)); |
|
121 |
|
122 rvs.AppendElement(TestSuccess("max-age=100;includeSubdomains", false, 100, true, sss)); |
|
123 rvs.AppendElement(TestSuccess("max-age=100\t; includeSubdomains", false, 100, true, sss)); |
|
124 rvs.AppendElement(TestSuccess(" max-age=100; includeSubdomains", false, 100, true, sss)); |
|
125 rvs.AppendElement(TestSuccess("max-age = 100 ; includeSubdomains", false, 100, true, sss)); |
|
126 rvs.AppendElement(TestSuccess("max-age = 100 ; includeSubdomains", false, 100, true, sss)); |
|
127 |
|
128 rvs.AppendElement(TestSuccess("maX-aGe=100; includeSUBDOMAINS", false, 100, true, sss)); |
|
129 rvs.AppendElement(TestSuccess("MAX-age =100; includeSubDomains", false, 100, true, sss)); |
|
130 rvs.AppendElement(TestSuccess("max-AGE=100; iNcLuDeSuBdoMaInS", false, 100, true, sss)); |
|
131 rvs.AppendElement(TestSuccess("Max-Age = 100; includesubdomains ", false, 100, true, sss)); |
|
132 rvs.AppendElement(TestSuccess("INCLUDESUBDOMAINS;MaX-AgE = 100 ", false, 100, true, sss)); |
|
133 // Turns out, the actual directive is entirely optional (hence the |
|
134 // trailing semicolon) |
|
135 rvs.AppendElement(TestSuccess("max-age=100;includeSubdomains;", true, 100, true, sss)); |
|
136 |
|
137 // these are weird tests, but are testing that some extended syntax is |
|
138 // still allowed (but it is ignored) |
|
139 rvs.AppendElement(TestSuccess("max-age=100 ; includesubdomainsSomeStuff", true, 100, false, sss)); |
|
140 rvs.AppendElement(TestSuccess("\r\n\t\t \tcompletelyUnrelated = foobar; max-age= 34520103 \t \t; alsoUnrelated;asIsThis;\tincludeSubdomains\t\t \t", true, 34520103, true, sss)); |
|
141 rvs.AppendElement(TestSuccess("max-age=100; unrelated=\"quoted \\\"thingy\\\"\"", true, 100, false, sss)); |
|
142 |
|
143 rv0 = rvs.Contains(false) ? 1 : 0; |
|
144 if (rv0 == 0) |
|
145 passed("Successfully Parsed STS headers with mixed case and LWS"); |
|
146 |
|
147 rvs.Clear(); |
|
148 |
|
149 // SHOULD FAIL: |
|
150 printf("*** Attempting to parse invalid STS headers (should not parse)...\n"); |
|
151 // invalid max-ages |
|
152 rvs.AppendElement(TestFailure("max-age", sss)); |
|
153 rvs.AppendElement(TestFailure("max-age ", sss)); |
|
154 rvs.AppendElement(TestFailure("max-age=p", sss)); |
|
155 rvs.AppendElement(TestFailure("max-age=*1p2", sss)); |
|
156 rvs.AppendElement(TestFailure("max-age=.20032", sss)); |
|
157 rvs.AppendElement(TestFailure("max-age=!20032", sss)); |
|
158 rvs.AppendElement(TestFailure("max-age==20032", sss)); |
|
159 |
|
160 // invalid headers |
|
161 rvs.AppendElement(TestFailure("foobar", sss)); |
|
162 rvs.AppendElement(TestFailure("maxage=100", sss)); |
|
163 rvs.AppendElement(TestFailure("maxa-ge=100", sss)); |
|
164 rvs.AppendElement(TestFailure("max-ag=100", sss)); |
|
165 rvs.AppendElement(TestFailure("includesubdomains", sss)); |
|
166 rvs.AppendElement(TestFailure(";", sss)); |
|
167 rvs.AppendElement(TestFailure("max-age=\"100", sss)); |
|
168 // The max-age directive here doesn't conform to the spec, so it MUST |
|
169 // be ignored. Consequently, the REQUIRED max-age directive is not |
|
170 // present in this header, and so it is invalid. |
|
171 rvs.AppendElement(TestFailure("max-age=100, max-age=200; includeSubdomains", sss)); |
|
172 rvs.AppendElement(TestFailure("max-age=100 includesubdomains", sss)); |
|
173 rvs.AppendElement(TestFailure("max-age=100 bar foo", sss)); |
|
174 rvs.AppendElement(TestFailure("max-age=100randomstuffhere", sss)); |
|
175 // All directives MUST appear only once in an STS header field. |
|
176 rvs.AppendElement(TestFailure("max-age=100; max-age=200", sss)); |
|
177 rvs.AppendElement(TestFailure("includeSubdomains; max-age=200; includeSubdomains", sss)); |
|
178 rvs.AppendElement(TestFailure("max-age=200; includeSubdomains; includeSubdomains", sss)); |
|
179 // The includeSubdomains directive is valueless. |
|
180 rvs.AppendElement(TestFailure("max-age=100; includeSubdomains=unexpected", sss)); |
|
181 // LWS must have at least one space or horizontal tab |
|
182 rvs.AppendElement(TestFailure("\r\nmax-age=200", sss)); |
|
183 |
|
184 rv1 = rvs.Contains(false) ? 1 : 0; |
|
185 if (rv1 == 0) |
|
186 passed("Avoided parsing invalid STS headers"); |
|
187 |
|
188 return (rv0 + rv1); |
|
189 } |