-
Notifications
You must be signed in to change notification settings - Fork 110
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
Handle missing Web Crypto API in non-HTTPS contexts when generating the already-submitted sessionStorage
key
#1911
Handle missing Web Crypto API in non-HTTPS contexts when generating the already-submitted sessionStorage
key
#1911
Conversation
Signed-off-by: Shyamsundar Gadde <[email protected]>
// eslint-disable-next-line no-console | ||
console.warn( | ||
'[Optimization Detective] Web Crypto API is not available. This API is only available in secure contexts (HTTPS). Detection cannot proceed. If you are testing locally, ensure you use HTTPS or run on localhost.' | ||
); | ||
throw new Error( | ||
'Web Crypto API is unavailable in this context. Try using HTTPS or localhost.' | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this reuse the logger?
// eslint-disable-next-line no-console | |
console.warn( | |
'[Optimization Detective] Web Crypto API is not available. This API is only available in secure contexts (HTTPS). Detection cannot proceed. If you are testing locally, ensure you use HTTPS or run on localhost.' | |
); | |
throw new Error( | |
'Web Crypto API is unavailable in this context. Try using HTTPS or localhost.' | |
); | |
error( | |
'[Optimization Detective] Web Crypto API is not available. This API is only available in secure contexts (HTTPS). Detection cannot proceed. If you are testing locally, ensure you use HTTPS or run on localhost.' | |
); | |
return; |
) { | ||
log( | ||
'The current client session already submitted a fresh URL Metric for this URL so a new one will not be collected now.' | ||
let alreadySubmittedSessionStorageKey; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this change needed? The above check for crypto.subtle
should mean this change won't be needed, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For example, if I go to http://httpforever.com/ then I see that crypto.subtle
is undefined
in the console.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, although we check for crypto.subtle
, I think the try/catch is still necessary.
If we were to just return early from getAlreadySubmittedSessionStorageKey
when crypto.subtle
is unavailable (instead of throwing an error), the function would return undefined
and the key would be undefined
for all URLs the user visits. This could lead to misleading messages like "The current client session already submitted a fresh URL Metric..." before the freshnessTTL expires.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another option would be to just throw a simple error when Web Crypto API is not available in the getAlreadySubmittedSessionStorageKey
function (without logging):
throw new Error( 'Web Crypto API is unavailable' );
And then handle the detailed messaging in the catch block inside detect
:
catch (err) {
if ( err.message === 'Web Crypto API is unavailable' ) {
error(
'Unable to create session storage key: Web Crypto API is not available. This API is only available in secure contexts (HTTPS). Detection cannot proceed. If you are testing locally, ensure you use HTTPS or run on localhost.'
);
} else {
error('Unable to create session storage key: ' + err.message);
}
return;
}
This approach also avoids duplicate error messages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, but why would crypto.subtle
need the try/catch?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, but why would crypto.subtle need the try/catch?
I'm sorry for not being clear. I wasn't suggesting adding a try/catch in the getAlreadySubmittedSessionStorageKey
function itself. Instead, I was referring to the try/catch block I added in the detect function where we call await getAlreadySubmittedSessionStorageKey()
.
The check for crypto.subtle
is in the getAlreadySubmittedSessionStorageKey
function, which throws an error when it's not available. Then, that error is caught in the detect
function's try/catch block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've implemented this approach in a66325a to illustrate what I was describing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for that. Here's another iteration on top of your suggestion: what if we have it so that the function just returns a constant string in the case of an error? This would essentially be bringing the functionality back to how it behaved before the URL hashing was introduced. Oh wait, actually that functionality is still in place:
performance/plugins/optimization-detective/detect.js
Lines 475 to 478 in a66325a
if ( isStorageLocked( getCurrentTime(), storageLockTTL ) ) { | |
warn( 'Aborted detection due to storage being locked.' ); | |
return; | |
} |
So how about it just return null
to indicate that this isn't available? There is already storage locking going on with a session storage key, so this is basically a secondary (redundant?) lock.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's what I have in mind: d5679ec
This will ensure that URL Metrics can continue to be collected on HTTP sites. The already-submitted "lock" is just intended to increase the diversity of the URL Metrics being collected, so if it's not available it's not a big deal. Better to skip it altogether in favor of collecting URL Metrics as opposed to doing nothing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for iterating on this! That approach makes a lot of sense—returning null
instead of blocking detection entirely keeps things functional while still maintaining some level of session tracking. I hadn't fully considered that before.
Signed-off-by: Shyamsundar Gadde <[email protected]>
) { | ||
log( | ||
'The current client session already submitted a fresh URL Metric for this URL so a new one will not be collected now.' | ||
let alreadySubmittedSessionStorageKey; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for that. Here's another iteration on top of your suggestion: what if we have it so that the function just returns a constant string in the case of an error? This would essentially be bringing the functionality back to how it behaved before the URL hashing was introduced. Oh wait, actually that functionality is still in place:
performance/plugins/optimization-detective/detect.js
Lines 475 to 478 in a66325a
if ( isStorageLocked( getCurrentTime(), storageLockTTL ) ) { | |
warn( 'Aborted detection due to storage being locked.' ); | |
return; | |
} |
So how about it just return null
to indicate that this isn't available? There is already storage locking going on with a session storage key, so this is basically a secondary (redundant?) lock.
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.
To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for iterating on this with me!
urlMetricGroupStatus.minimumViewportWidth, | ||
urlMetricGroupStatus.maximumViewportWidth || '', | ||
].join( '-' ); | ||
if ( ! window.crypto || ! window.crypto.subtle ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: only checking window.crypto
should suffice. There's no combination where the former is available but the latter isn't.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So like so?
if ( ! window.crypto || ! window.crypto.subtle ) { | |
if ( ! window.crypto ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nevertheless someone could try to polyfill crypto.getRandomValues()
but not define crypto.subtle
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So like so?
Yes that's what I meant.
Nevertheless someone could try to polyfill
crypto.getRandomValues()
but not definecrypto.subtle
.
Sounds unlikely to me but if we are concerned about that then we should keep the check.
sessionStorage
key
Summary
Fixes #1904
Relevant technical choices
null
instead of blocking detection when the Web Crypto API is unavailable, allowing the script to continue running while still tracking session data when possible.