OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "config.h" | 5 #include "config.h" |
6 #include "core/frame/SubresourceIntegrity.h" | 6 #include "core/frame/SubresourceIntegrity.h" |
7 | 7 |
8 #include "core/HTMLNames.h" | 8 #include "core/HTMLNames.h" |
9 #include "core/dom/Document.h" | 9 #include "core/dom/Document.h" |
10 #include "core/dom/Element.h" | 10 #include "core/dom/Element.h" |
(...skipping 22 matching lines...) Expand all Loading... | |
33 // Check if it's a base64 encoded value. We're pretty loose here, as there's | 33 // Check if it's a base64 encoded value. We're pretty loose here, as there's |
34 // not much risk in it, and it'll make it simpler for developers. | 34 // not much risk in it, and it'll make it simpler for developers. |
35 return isASCIIAlphanumeric(c) || c == '_' || c == '-' || c == '+' || c == '/ ' || c == '='; | 35 return isASCIIAlphanumeric(c) || c == '_' || c == '-' || c == '+' || c == '/ ' || c == '='; |
36 } | 36 } |
37 | 37 |
38 static bool isTypeCharacter(UChar c) | 38 static bool isTypeCharacter(UChar c) |
39 { | 39 { |
40 return isASCIIAlphanumeric(c) || c == '+' || c == '.' || c == '-'; | 40 return isASCIIAlphanumeric(c) || c == '+' || c == '.' || c == '-'; |
41 } | 41 } |
42 | 42 |
43 static bool isOptionNameCharacter(UChar c) | |
44 { | |
45 return isASCIIAlphanumeric(c) || c == '-'; | |
46 } | |
47 | |
48 static bool isOptionValueCharacter(UChar c) | |
49 { | |
50 return isASCIIAlphanumeric(c) || c == '!' || c == '#' || c == '$' || c == '% ' || c == '&' || c == '\'' || c == '*' || c == '+' || c == '-' || c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~' || c == '/'; | |
51 } | |
52 | |
43 static void logErrorToConsole(const String& message, Document& document) | 53 static void logErrorToConsole(const String& message, Document& document) |
44 { | 54 { |
45 document.addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, Err orMessageLevel, message)); | 55 document.addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, Err orMessageLevel, message)); |
46 } | 56 } |
47 | 57 |
48 static bool DigestsEqual(const DigestValue& digest1, const DigestValue& digest2) | 58 static bool DigestsEqual(const DigestValue& digest1, const DigestValue& digest2) |
49 { | 59 { |
50 if (digest1.size() != digest2.size()) | 60 if (digest1.size() != digest2.size()) |
51 return false; | 61 return false; |
52 | 62 |
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
196 if (position == begin || (position != end && *position != '?')) { | 206 if (position == begin || (position != end && *position != '?')) { |
197 digest = emptyString(); | 207 digest = emptyString(); |
198 return false; | 208 return false; |
199 } | 209 } |
200 | 210 |
201 // We accept base64url encoding, but normalize to "normal" base64 internally : | 211 // We accept base64url encoding, but normalize to "normal" base64 internally : |
202 digest = normalizeToBase64(String(begin, position - begin)); | 212 digest = normalizeToBase64(String(begin, position - begin)); |
203 return true; | 213 return true; |
204 } | 214 } |
205 | 215 |
216 bool SubresourceIntegrity::isValidMimeTypeValue(const String& value) | |
217 { | |
218 Vector<UChar> characters; | |
219 value.appendTo(characters); | |
220 const UChar* position = characters.data(); | |
221 const UChar* end = characters.end(); | |
206 | 222 |
207 // Before: | |
208 // | |
209 // [algorithm]-[hash] OR [algorithm]-[hash]?[options] | |
210 // ^ ^ ^ | |
211 // position/end position end | |
212 // | |
213 // After (if successful: if the method returns false, we make no promises and th e caller should exit early): | |
214 // | |
215 // [algorithm]-[hash] OR [algorithm]-[hash]?[options] | |
216 // ^ ^ | |
217 // position/end position/end | |
218 bool SubresourceIntegrity::parseMimeType(const UChar*& position, const UChar* en d, String& type) | |
219 { | |
220 type = emptyString(); | |
221 | |
222 if (position == end) | |
223 return true; | |
224 | |
225 if (!skipToken<UChar>(position, end, "?ct=")) | |
226 return false; | |
227 | |
228 const UChar* begin = position; | |
229 skipWhile<UChar, isASCIIAlpha>(position, end); | 223 skipWhile<UChar, isASCIIAlpha>(position, end); |
230 if (position == end) | 224 if (position == end) |
231 return false; | 225 return false; |
232 | 226 |
233 if (!skipExactly<UChar>(position, end, '/')) | 227 if (!skipExactly<UChar>(position, end, '/')) |
234 return false; | 228 return false; |
235 | 229 |
236 if (position == end) | |
237 return false; | |
238 | |
239 skipWhile<UChar, isTypeCharacter>(position, end); | 230 skipWhile<UChar, isTypeCharacter>(position, end); |
240 if (position != end) | 231 if (position != end) |
241 return false; | 232 return false; |
242 | 233 |
243 type = String(begin, position - begin); | |
244 return true; | 234 return true; |
245 } | 235 } |
246 | 236 |
237 | |
238 // Before: | |
239 // | |
240 // [algorithm]-[hash] OR [algorithm]-[hash]?[name]=[value] OR [algorithm]-[hash]?[name]=[value]?[rest of options] | |
241 // ^ ^ ^ ^ ^ | |
242 // position/end position end position end | |
243 // | |
244 // After (if successful: if the method returns false, we make no promises and th e caller should exit early): | |
245 // | |
246 // [algorithm]-[hash] OR [algorithm]-[hash]?[name]=[value] OR [algorithm]-[hash]?[name]=[value]?[rest of options] | |
Mike West
2015/05/11 03:25:28
This is such a weird syntax.
| |
247 // ^ ^ ^ ^ | |
248 // position/end position/end position end | |
249 bool SubresourceIntegrity::parseOption(const UChar*& position, const UChar* end, String& name, String& value) | |
250 { | |
251 name = emptyString(); | |
252 value = emptyString(); | |
253 | |
254 if (position == end) | |
255 return true; | |
256 | |
257 if (!skipExactly<UChar>(position, end, '?')) | |
258 return false; | |
259 | |
260 const UChar* begin = position; | |
261 skipWhile<UChar, isOptionNameCharacter>(position, end); | |
262 if (position == end) | |
263 return false; | |
264 | |
265 name = String(begin, position - begin); | |
266 | |
267 if (!skipExactly<UChar>(position, end, '=') || position == end) | |
268 return false; | |
269 | |
270 begin = position; | |
271 skipWhile<UChar, isOptionValueCharacter>(position, end); | |
272 | |
273 value = String(begin, position - begin); | |
274 | |
275 if (position == end || *position == '?') | |
Mike West
2015/05/11 03:25:28
Would you mind asserting that `position < end` bef
| |
276 return true; | |
277 | |
278 return false; | |
279 } | |
280 | |
247 SubresourceIntegrity::IntegrityParseResult SubresourceIntegrity::parseIntegrityA ttribute(const WTF::String& attribute, WTF::Vector<IntegrityMetadata>& metadataL ist, Document& document) | 281 SubresourceIntegrity::IntegrityParseResult SubresourceIntegrity::parseIntegrityA ttribute(const WTF::String& attribute, WTF::Vector<IntegrityMetadata>& metadataL ist, Document& document) |
248 { | 282 { |
249 Vector<UChar> characters; | 283 Vector<UChar> characters; |
250 attribute.stripWhiteSpace().appendTo(characters); | 284 attribute.stripWhiteSpace().appendTo(characters); |
251 const UChar* position = characters.data(); | 285 const UChar* position = characters.data(); |
252 const UChar* end = characters.end(); | 286 const UChar* end = characters.end(); |
253 const UChar* currentIntegrityEnd; | 287 const UChar* currentIntegrityEnd; |
254 | 288 |
255 metadataList.clear(); | 289 metadataList.clear(); |
256 bool error = false; | 290 bool error = false; |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
293 ASSERT(parseResult == AlgorithmValid); | 327 ASSERT(parseResult == AlgorithmValid); |
294 | 328 |
295 if (!parseDigest(position, currentIntegrityEnd, digest)) { | 329 if (!parseDigest(position, currentIntegrityEnd, digest)) { |
296 logErrorToConsole("Error parsing 'integrity' attribute ('" + attribu te + "'). The digest must be a valid, base64-encoded value.", document); | 330 logErrorToConsole("Error parsing 'integrity' attribute ('" + attribu te + "'). The digest must be a valid, base64-encoded value.", document); |
297 error = true; | 331 error = true; |
298 skipUntil<UChar, isASCIISpace>(position, end); | 332 skipUntil<UChar, isASCIISpace>(position, end); |
299 UseCounter::count(document, UseCounter::SRIElementWithUnparsableInte grityAttribute); | 333 UseCounter::count(document, UseCounter::SRIElementWithUnparsableInte grityAttribute); |
300 continue; | 334 continue; |
301 } | 335 } |
302 | 336 |
303 if (!parseMimeType(position, currentIntegrityEnd, type)) { | 337 bool skipToNextIntegrity = false; |
304 logErrorToConsole("Error parsing 'integrity' attribute ('" + attribu te + "'). The content type could not be parsed.", document); | 338 while (*position == '?') { |
305 error = true; | 339 String name, value; |
306 skipUntil<UChar, isASCIISpace>(position, end); | 340 if (!parseOption(position, currentIntegrityEnd, name, value)) { |
307 UseCounter::count(document, UseCounter::SRIElementWithUnparsableInte grityAttribute); | 341 logErrorToConsole("Error parsing 'integrity' attribute ('" + att ribute + "'). The options could not be parsed.", document); |
342 skipToNextIntegrity = true; | |
343 error = true; | |
344 skipUntil<UChar, isASCIISpace>(position, end); | |
345 UseCounter::count(document, UseCounter::SRIElementWithUnparsable IntegrityAttribute); | |
346 break; | |
347 } | |
348 | |
349 if (name == "ct") { | |
350 if (!isValidMimeTypeValue(value)) { | |
351 logErrorToConsole("Error parsing 'integrity' attribute ('" + attribute + "'). The content type could not be parsed.", document); | |
352 skipToNextIntegrity = true; | |
353 error = true; | |
354 skipUntil<UChar, isASCIISpace>(position, end); | |
355 UseCounter::count(document, UseCounter::SRIElementWithUnpars ableIntegrityAttribute); | |
356 break; | |
357 } | |
358 | |
359 type = value; | |
360 } else { | |
361 logErrorToConsole("Ignoring unrecogized 'integrity' attribute op tion '" + name + "' with value '" + value + "'.", document); | |
362 } | |
363 } | |
364 | |
365 if (skipToNextIntegrity) | |
308 continue; | 366 continue; |
309 } | |
310 | 367 |
311 IntegrityMetadata integrityMetadata = { | 368 IntegrityMetadata integrityMetadata = { |
312 digest, | 369 digest, |
313 algorithm, | 370 algorithm, |
314 type | 371 type |
315 }; | 372 }; |
316 metadataList.append(integrityMetadata); | 373 metadataList.append(integrityMetadata); |
317 } | 374 } |
318 | 375 |
319 if (metadataList.size() == 0 && error) | 376 if (metadataList.size() == 0 && error) |
320 return IntegrityParseNoValidResult; | 377 return IntegrityParseNoValidResult; |
321 | 378 |
322 return IntegrityParseValidResult; | 379 return IntegrityParseValidResult; |
323 } | 380 } |
324 | 381 |
325 } // namespace blink | 382 } // namespace blink |
OLD | NEW |