Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: properly replace integrity tags in script resources when experimentalModifyObstructiveThirdPartyCode is true #23820

Merged
merged 10 commits into from
Sep 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions packages/proxy/lib/http/util/regex-rewriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,18 @@ const topOrParentLocationOrFramesRe = /([^\da-zA-Z\(\)])?(\btop\b|\bparent\b)([.

const jiraTopWindowGetterRe = /(!function\s*\((\w{1})\)\s*{\s*return\s*\w{1}\s*(?:={2,})\s*\w{1}\.parent)(\s*}\(\w{1}\))/g
const jiraTopWindowGetterUnMinifiedRe = /(function\s*\w{1,}\s*\((\w{1})\)\s*{\s*return\s*\w{1}\s*(?:={2,})\s*\w{1}\.parent)(\s*;\s*})/g

const integrityTagReplacementRe = new RegExp(`(${STRIPPED_INTEGRITY_TAG}|integrity)(=(?:\"|\')sha(?:256|384|512)-.*?(?:\"|\'))`, 'g')
/**
* Matches the word integrity if being set on an object, such as foo.integrity. This MUST be followed by a valid hash to match. This is replaced with
* foo['cypress-stripped-integrity']
*/
const javaScriptIntegrityReplacementRe = new RegExp(`[\\.](${STRIPPED_INTEGRITY_TAG}|integrity)((\\s?=\\s?)(?:"|')sha(?:256|384|512)-.*?(?:"|'))`, 'g')
/**
* Does a negative lookback to see a variable is being declared, such as var let or const (the 'nst' is back end of 'const' since lookbacks need a fixed width). This can then
* be followed by any optional character that isn't a period, space, single or double quote. This MUST be followed by a valid hash to match. A space preceding the integrity tag can still be matched,
* but the match only starts at the word integrity, and not the character preceding it. In these cases, we always replace the word integrity with cypress-stripped-integrity.
* The match for cypress-stripped-integrity is if we are replacing in the stripStream, and the replaced text is rematched to essentially complete a no op
*/
const generalIntegrityReplacementRe = new RegExp(`(?:(?<!(var|let|nst)\\s)[^\\.\\s'"]?)(${STRIPPED_INTEGRITY_TAG}|integrity)((?:'|")?\\]?(\\s?=|["|'],)\\s?(?:"|')sha(?:256|384|512)-.*?(?:"|'))`, 'g')

export function strip (html: string, { modifyObstructiveThirdPartyCode }: Partial<SecurityOpts> = {
modifyObstructiveThirdPartyCode: false,
Expand All @@ -30,7 +40,8 @@ export function strip (html: string, { modifyObstructiveThirdPartyCode }: Partia
.replace(jiraTopWindowGetterUnMinifiedRe, '$1 || $2.parent.__Cypress__$3')

if (modifyObstructiveThirdPartyCode) {
rewrittenHTML = rewrittenHTML.replace(integrityTagReplacementRe, `${STRIPPED_INTEGRITY_TAG}$2`)
rewrittenHTML = rewrittenHTML.replace(javaScriptIntegrityReplacementRe, `['${STRIPPED_INTEGRITY_TAG}']$2`)
rewrittenHTML = rewrittenHTML.replace(generalIntegrityReplacementRe, `${STRIPPED_INTEGRITY_TAG}$3`)
}

return rewrittenHTML
Expand All @@ -49,7 +60,8 @@ export function stripStream ({ modifyObstructiveThirdPartyCode }: Partial<Securi
jiraTopWindowGetterRe,
jiraTopWindowGetterUnMinifiedRe,
...(modifyObstructiveThirdPartyCode ? [
integrityTagReplacementRe,
javaScriptIntegrityReplacementRe,
generalIntegrityReplacementRe,
] : []),
],
[
Expand All @@ -59,7 +71,8 @@ export function stripStream ({ modifyObstructiveThirdPartyCode }: Partial<Securi
'$1 || $2.parent.__Cypress__$3',
'$1 || $2.parent.__Cypress__$3',
...(modifyObstructiveThirdPartyCode ? [
`${STRIPPED_INTEGRITY_TAG}$2`,
`['${STRIPPED_INTEGRITY_TAG}']$2`,
`${STRIPPED_INTEGRITY_TAG}$3`,
] : []),
],
),
Expand Down
28 changes: 25 additions & 3 deletions packages/proxy/test/unit/http/util/regex-rewriter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ const originalWithModifyObstructiveThirdPartyCode = `\
dynamicIntegrityScript.src = 'integrity.js'
dynamicIntegrityScript.setAttribute('crossorigin', "anonymous")
dynamicIntegrityScript.setAttribute('data-script-type', 'dynamic')
dynamicIntegrityScript.setAttribute('integrity', "sha384-XiV6bRRw9OEpsWSumtD1J7rElgTrNQro4MY/O4IYjhH+YGCf1dHaNGZ3A2kzYi/C"
dynamicIntegrityScript.setAttribute('integrity', "sha384-XiV6bRRw9OEpsWSumtD1J7rElgTrNQro4MY/O4IYjhH+YGCf1dHaNGZ3A2kzYi/C")
document.querySelector('head').appendChild(dynamicIntegrityScript)
</script>
<link id="static-set-integrity-link" rel="stylesheet" href="integrity.css" integrity="sha256-MGkilwijzWAi/LutxKC+CWhsXXc6t1tXTMqY1zakP8c=">
Expand All @@ -279,6 +279,17 @@ const originalWithModifyObstructiveThirdPartyCode = `\
dynamicIntegrityScript.setAttribute('integrity', "sha384-XiV6bRRw9OEpsWSumtD1J7rElgTrNQro4MY/O4IYjhH+YGCf1dHaNGZ3A2kzYi/C")
document.querySelector('head').appendChild(dynamicIntegrityScript)
</script>
<script>
(function(){var d=document,po=d.createElement('script');po.type='text/javascript';po.async=true;po.src='https://www.foobar.com/foobar.js';po.crossOrigin='anonymous';po.integrity='sha384-XiV6bRRw9OEpsWSumtD1J7rElgTrNQro4MY/O4IYjhH+YGCf1dHaNGZ3A2kzYi/C';var e=d.querySelector('script[nonce]'),n=e&&(e['nonce']||e.getAttribute('nonce'));if(n){po.setAttribute('nonce',n);}var s=d.getElementsByTagName('script')[0];s.parentNode.insertBefore(po, s);})();
var integrity = 'sha384-XiV6bRRw9OEpsWSumtD1J7rElgTrNQro4MY/O4IYjhH+YGCf1dHaNGZ3A2kzYi/C'
foo.integrity = 'foo-bar'
foo.integrity = 'sha384-XiV6bRRw9OEpsWSumtD1J7rElgTrNQro4MY/O4IYjhH+YGCf1dHaNGZ3A2kzYi/C'
foo['integrity'] = 'sha256-XiV6bRRw9OEpsWSumtD1J7rElgTrNQro4MY/O4IYjhH+YGCf1dHaNGZ3A2kzYi/C'
var integrity='sha256-XiV6bRRw9OEpsWSumtD1J7rElgTrNQro4MY/O4IYjhH+YGCf1dHaNGZ3A2kzYi/C'
foo.integrity='foo-bar'
foo.integrity='sha256-XiV6bRRw9OEpsWSumtD1J7rElgTrNQro4MY/O4IYjhH+YGCf1dHaNGZ3A2kzYi/C'
foo['integrity']='sha256-XiV6bRRw9OEpsWSumtD1J7rElgTrNQro4MY/O4IYjhH+YGCf1dHaNGZ3A2kzYi/C'
</script>
</body>
</html>\
`
Expand Down Expand Up @@ -380,7 +391,7 @@ const expectedWithModifyObstructiveThirdPartyCode = `\
dynamicIntegrityScript.src = 'integrity.js'
dynamicIntegrityScript.setAttribute('crossorigin', "anonymous")
dynamicIntegrityScript.setAttribute('data-script-type', 'dynamic')
dynamicIntegrityScript.setAttribute('integrity', "sha384-XiV6bRRw9OEpsWSumtD1J7rElgTrNQro4MY/O4IYjhH+YGCf1dHaNGZ3A2kzYi/C"
dynamicIntegrityScript.setAttribute('cypress-stripped-integrity', "sha384-XiV6bRRw9OEpsWSumtD1J7rElgTrNQro4MY/O4IYjhH+YGCf1dHaNGZ3A2kzYi/C")
document.querySelector('head').appendChild(dynamicIntegrityScript)
</script>
<link id="static-set-integrity-link" rel="stylesheet" href="integrity.css" cypress-stripped-integrity="sha256-MGkilwijzWAi/LutxKC+CWhsXXc6t1tXTMqY1zakP8c=">
Expand All @@ -391,9 +402,20 @@ const expectedWithModifyObstructiveThirdPartyCode = `\
dynamicIntegrityScript.rel = "stylesheet"
dynamicIntegrityScript.href = 'integrity.css'
dynamicIntegrityScript.setAttribute('crossorigin', "anonymous")
dynamicIntegrityScript.setAttribute('integrity', "sha384-XiV6bRRw9OEpsWSumtD1J7rElgTrNQro4MY/O4IYjhH+YGCf1dHaNGZ3A2kzYi/C")
dynamicIntegrityScript.setAttribute('cypress-stripped-integrity', "sha384-XiV6bRRw9OEpsWSumtD1J7rElgTrNQro4MY/O4IYjhH+YGCf1dHaNGZ3A2kzYi/C")
document.querySelector('head').appendChild(dynamicIntegrityScript)
</script>
<script>
(function(){var d=document,po=d.createElement('script');po.type='text/javascript';po.async=true;po.src='https://www.foobar.com/foobar.js';po.crossOrigin='anonymous';po['cypress-stripped-integrity']='sha384-XiV6bRRw9OEpsWSumtD1J7rElgTrNQro4MY/O4IYjhH+YGCf1dHaNGZ3A2kzYi/C';var e=d.querySelector('script[nonce]'),n=e&&(e['nonce']||e.getAttribute('nonce'));if(n){po.setAttribute('nonce',n);}var s=d.getElementsByTagName('script')[0];s.parentNode.insertBefore(po, s);})();
var integrity = 'sha384-XiV6bRRw9OEpsWSumtD1J7rElgTrNQro4MY/O4IYjhH+YGCf1dHaNGZ3A2kzYi/C'
foo.integrity = 'foo-bar'
foo['cypress-stripped-integrity'] = 'sha384-XiV6bRRw9OEpsWSumtD1J7rElgTrNQro4MY/O4IYjhH+YGCf1dHaNGZ3A2kzYi/C'
foo['cypress-stripped-integrity'] = 'sha256-XiV6bRRw9OEpsWSumtD1J7rElgTrNQro4MY/O4IYjhH+YGCf1dHaNGZ3A2kzYi/C'
var integrity='sha256-XiV6bRRw9OEpsWSumtD1J7rElgTrNQro4MY/O4IYjhH+YGCf1dHaNGZ3A2kzYi/C'
foo.integrity='foo-bar'
foo['cypress-stripped-integrity']='sha256-XiV6bRRw9OEpsWSumtD1J7rElgTrNQro4MY/O4IYjhH+YGCf1dHaNGZ3A2kzYi/C'
foo['cypress-stripped-integrity']='sha256-XiV6bRRw9OEpsWSumtD1J7rElgTrNQro4MY/O4IYjhH+YGCf1dHaNGZ3A2kzYi/C'
</script>
</body>
</html>\
`
Expand Down