|
4562 | 4562 |
|
4563 | 4563 | function parseTrustedTypes(cspString) { |
4564 | 4564 | const policies = new Set(); |
| 4565 | + let allowDuplicates = false; |
| 4566 | + let ttDirectiveFound = false; |
4565 | 4567 | const ttRegex = /trusted-types\s+([^;]+)/gi; |
4566 | 4568 | let match; |
| 4569 | + |
4567 | 4570 | while ((match = ttRegex.exec(cspString)) !== null) { |
4568 | | - match[1].trim().split(/\s+/) |
4569 | | - .forEach(name => { |
4570 | | - if (name !== "'allow-duplicates'" && name !== "'none'") { |
4571 | | - policies.add(name.replace(/'/g, '')); |
4572 | | - } |
4573 | | - }); |
4574 | | - } |
4575 | | - return Array.from(policies); |
4576 | | - } |
| 4571 | + ttDirectiveFound = true; |
4577 | 4572 |
|
4578 | | - async function getAvailablePolicyNamesOptimized() { |
4579 | | - if (_unsafeWindow.trustedTypes && _unsafeWindow.trustedTypes.getPolicyNames) { |
4580 | | - const existingNames = _unsafeWindow.trustedTypes.getPolicyNames(); |
4581 | | - if (existingNames.length > 0) { |
4582 | | - return new Set(existingNames); |
| 4573 | + const policyNames = match[1].trim().split(/\s+/); |
| 4574 | + for (const name of policyNames) { |
| 4575 | + if (name === "'allow-duplicates'") { |
| 4576 | + allowDuplicates = true; |
| 4577 | + } else if (name !== "'none'") { |
| 4578 | + policies.add(name.replace(/'/g, '')); |
| 4579 | + } |
4583 | 4580 | } |
4584 | 4581 | } |
| 4582 | + return { names: policies, allowDuplicates: allowDuplicates, ttDirectiveFound: ttDirectiveFound }; |
| 4583 | + } |
| 4584 | + |
| 4585 | + async function getCspTrustedTypesInfo() { |
| 4586 | + const combinedPolicies = new Set(); |
| 4587 | + let combinedAllowDuplicates = false; |
| 4588 | + let combinedTtDirectiveFound = false; |
4585 | 4589 |
|
4586 | 4590 | const meta = document.querySelector('meta[http-equiv="Content-Security-Policy"]'); |
4587 | 4591 | if (meta) { |
4588 | | - const metaNames = parseTrustedTypes(meta.content); |
4589 | | - if (metaNames.length > 0) { |
4590 | | - return new Set(metaNames); |
| 4592 | + const metaResult = parseTrustedTypes(meta.content); |
| 4593 | + metaResult.names.forEach(name => combinedPolicies.add(name)); |
| 4594 | + if (metaResult.allowDuplicates) { |
| 4595 | + combinedAllowDuplicates = true; |
| 4596 | + } |
| 4597 | + if (metaResult.ttDirectiveFound) { |
| 4598 | + combinedTtDirectiveFound = true; |
4591 | 4599 | } |
4592 | 4600 | } |
4593 | 4601 |
|
|
4597 | 4605 | url: window.location.href, |
4598 | 4606 | onload: function(response) { |
4599 | 4607 | const cspHeader = response.responseHeaders.split('\r\n') |
4600 | | - .filter(h => h.toLowerCase().startsWith('content-security-policy:')) |
4601 | | - .map(h => h.substring(26).trim()) |
4602 | | - .join('; '); |
| 4608 | + .filter(h => h.toLowerCase().startsWith('content-security-policy:')) |
| 4609 | + .map(h => h.substring(26).trim()) |
| 4610 | + .join('; '); |
4603 | 4611 |
|
4604 | | - const headerNames = parseTrustedTypes(cspHeader); |
4605 | | - if (headerNames.length > 0) { |
4606 | | - resolve(new Set(headerNames)); |
4607 | | - } else { |
4608 | | - resolve(new Set()); |
| 4612 | + const headerResult = parseTrustedTypes(cspHeader); |
| 4613 | + headerResult.names.forEach(name => combinedPolicies.add(name)); |
| 4614 | + if (headerResult.allowDuplicates) { |
| 4615 | + combinedAllowDuplicates = true; |
4609 | 4616 | } |
| 4617 | + if (headerResult.ttDirectiveFound) { |
| 4618 | + combinedTtDirectiveFound = true; |
| 4619 | + } |
| 4620 | + |
| 4621 | + resolve({ |
| 4622 | + names: combinedPolicies, |
| 4623 | + allowDuplicates: combinedAllowDuplicates, |
| 4624 | + ttDirectiveFound: combinedTtDirectiveFound |
| 4625 | + }); |
4610 | 4626 | }, |
4611 | 4627 | onerror: function(error) { |
4612 | | - resolve(new Set()); |
| 4628 | + resolve({ |
| 4629 | + names: combinedPolicies, |
| 4630 | + allowDuplicates: combinedAllowDuplicates, |
| 4631 | + ttDirectiveFound: combinedTtDirectiveFound |
| 4632 | + }); |
4613 | 4633 | } |
4614 | 4634 | }); |
4615 | 4635 | }); |
|
4625 | 4645 | } |
4626 | 4646 |
|
4627 | 4647 | async function createPolicy() { |
4628 | | - if (_unsafeWindow.trustedTypes && _unsafeWindow.trustedTypes.createPolicy && isTrustedTypesEnforced()) { |
4629 | | - const allowedNames = await getAvailablePolicyNamesOptimized(); |
| 4648 | + if (!(_unsafeWindow.trustedTypes && _unsafeWindow.trustedTypes.createPolicy && isTrustedTypesEnforced())) { |
| 4649 | + return; |
| 4650 | + } |
4630 | 4651 |
|
4631 | | - if (allowedNames.size === 0) { |
4632 | | - escapeHTMLPolicy = _unsafeWindow.trustedTypes.createPolicy('pagetual_default', { |
4633 | | - createHTML: (string, sink) => string |
4634 | | - }); |
4635 | | - return; |
| 4652 | + const { names: allowedNames, allowDuplicates, ttDirectiveFound } = await getCspTrustedTypesInfo(); |
| 4653 | + |
| 4654 | + if (ttDirectiveFound && !allowDuplicates) { |
| 4655 | + debug("CSP Trusted Types is enforced without 'allow-duplicates'. " + |
| 4656 | + "Skipping policy creation to avoid conflicts with the page."); |
| 4657 | + return; |
| 4658 | + } |
| 4659 | + |
| 4660 | + const MY_POLICY_NAME = 'pvcep_default'; |
| 4661 | + |
| 4662 | + try { |
| 4663 | + escapeHTMLPolicy = _unsafeWindow.trustedTypes.createPolicy(MY_POLICY_NAME, { |
| 4664 | + createHTML: (string, sink) => string, |
| 4665 | + createScriptURL: string => string, |
| 4666 | + createScript: string => string |
| 4667 | + }); |
| 4668 | + return; |
| 4669 | + } catch (e) { |
| 4670 | + } |
| 4671 | + |
| 4672 | + const existingPolicies = new Set(_unsafeWindow.trustedTypes.getPolicyNames()); |
| 4673 | + for (const name of allowedNames) { |
| 4674 | + if (name === '*' || existingPolicies.has(name)) { |
| 4675 | + continue; |
4636 | 4676 | } |
4637 | 4677 |
|
4638 | | - for (const name of allowedNames) { |
4639 | | - if (name === '*') continue; |
4640 | | - try { |
4641 | | - escapeHTMLPolicy = _unsafeWindow.trustedTypes.createPolicy(name, { |
4642 | | - createHTML: (string, sink) => string |
4643 | | - }); |
4644 | | - break; |
4645 | | - } catch (e) { |
4646 | | - console.warn(`create '${name}' failed`); |
4647 | | - return; |
4648 | | - } |
| 4678 | + try { |
| 4679 | + escapeHTMLPolicy = _unsafeWindow.trustedTypes.createPolicy(name, { |
| 4680 | + createHTML: (string, sink) => string, |
| 4681 | + createScriptURL: string => string, |
| 4682 | + createScript: string => string |
| 4683 | + }); |
| 4684 | + return; |
| 4685 | + } catch (e) { |
| 4686 | + debug(`create '${name}' failed, trying next...`); |
4649 | 4687 | } |
4650 | 4688 | } |
| 4689 | + debug("Could not create any trusted types policy."); |
4651 | 4690 | } |
4652 | 4691 |
|
4653 | 4692 | let escapeHTMLPolicy = null; |
|
0 commit comments