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

Report when XHR are issued before viewer is visible #9350

Merged
merged 9 commits into from May 25, 2017

Conversation

jridgewell
Copy link
Contributor

Fixes #9048.

this.ampdoc_ = ampdocServiceFor(win).getAmpDoc();
} catch (e) {
this.ampdoc_ = null;
dev().error('XHR', e, 'XHR service failed to find ampdoc');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The probability of failure in PWA case here is about 100% :).

I think we just need to restrict this reporting/warning to single-doc mode. The real issue is only in this mode anyway. See AmpDocService.isSingleDoc()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I was thinking. Though I don't agree that it's only needed in single-doc mode. Is there a way to get the parent amp-doc while in PWA?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only way to pull the parent ampdoc is by passing around the context node (element). The reason why I think PWA mode is less relevant b/c this is a privacy feature, since iframes can pull documents from different origins. PWA on the other hand is built to serve "own" same-origin documents. If PWA decides to request cross-origin docs, it still needs to support CORS, etc - which essentially nullify our protection here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation, done!

input);
}
} else {
dev().error('XHR', 'attempted to fetch %s without knowing if viewer is' +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above

// TODO(@jridgewell, #9048): We can alternatively _always_ wait on
// #whenFirstVisible.
if (!viewerForDoc(this.ampdoc_).hasBeenVisible()) {
dev().error('XHR', 'attempted to fetch %s before viewer was visible',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To start with, we should sample it at 1% and then go higher. Otherwise we take a risk of over-spamming our pantheon which is quota-limited. Once confirmed low volume - we can go to 100%.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this already sampled by the error reporting server?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, we don't want to overwhelm that service either. But basically, error transformer can only sample randomly regardless of error. So if we think this could have too big a rate, we should do it here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -94,6 +96,11 @@ export class Xhr {
constructor(win) {
/** @const {!Window} */
this.win = win;

const ampdocService = ampdocServiceFor(win);
this.ampdoc_ = ampdocService.isSingleDoc() ?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: ampdocSingle_

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 and jsdoc

if (this.ampdoc_) {
// TODO(@jridgewell, #9048): We can alternatively _always_ wait on
// #whenFirstVisible.
if (Math.random() < .01 && !viewerForDoc(this.ampdoc_).hasBeenVisible()) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this will also trigger for legitimate pre-render XHRs, e.g. same-origin.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's intended for now.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of reporting those? Won't it just muddy the signal?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To follow up on this from above. I definitely agree with @choumx - this signal will get super noisy and even with 1% error rate it would be overwhelming. Sorry for missing it at first. We can already define which requests are "good" or "bad" . The only requests we need to report here are URLs with url.origin != window.location.origin. Same-origin requests are explicitly allowed and have 0 risk of any kind associated with them since we just pulled the document itself from that origin.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eventually, I believe, we'll use this condition and throw an actual error == deny the request in prerender with different origin.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -106,6 +113,15 @@ export class Xhr {
* @private
*/
fetch_(input, init) {
if (this.ampdoc_) {
// TODO(@jridgewell, #9048): We can alternatively _always_ wait on
// #whenFirstVisible.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be nice as a conservative mitigation technique. As is, this PR won't prevent the next privacy issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, just reports it. I think once we understand all the valid use cases, we can move to this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We definitely can't always wait on first-visible, not w/o additional rules. We can try to define these rules right now - it's generally possible. But starting with reporting is likely better. I'll make a comment below.

@jridgewell jridgewell force-pushed the xhr-viewer-visible branch 2 times, most recently from d8fddf2 to 9b89509 Compare May 17, 2017 17:40
@@ -154,7 +171,7 @@ export class Xhr {
}
// For some same origin requests, add AMP-Same-Origin: true header to allow
// publishers to validate that this request came from their own origin.
const currentOrigin = parseUrl(this.win.location.href).origin;
const currentOrigin = this.win.location.origin;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Safe to use? MDN is unclear.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WTF.

@jridgewell
Copy link
Contributor Author

Ping.

return false;
},
};
});

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks weird. Can we do without?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'll complain that it can't find the service.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hidden side effect would cause subsequent ampdoc registrations to silently fail. Perhaps we can either allow injection of the ampdoc instance or just don't call that code in test mode.

The rest LGTM.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to resolve this. I'm ok with not calling this code in the test code as well. Otherwise this will be asking for a trouble.

Ditto: otherwise LGTM.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have to, we can even test for the presence of the service and if it out if not.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

return false;
},
};
});

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hidden side effect would cause subsequent ampdoc registrations to silently fail. Perhaps we can either allow injection of the ampdoc instance or just don't call that code in test mode.

The rest LGTM.

Copy link
Contributor

@dvoytenko dvoytenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, but one quick confirmation: this won't have any issues with our workers, will it?

@jridgewell
Copy link
Contributor Author

Workers?

@dvoytenko
Copy link
Contributor

Web workers or service workers? Do we reuse xhr-impl for any of those?

@jridgewell
Copy link
Contributor Author

Oh no, they don't use any of this.

@dreamofabear
Copy link

Well, the web worker is fetched via fetchText() but that happens after viewer is visible.

@jridgewell jridgewell merged commit a70dcf8 into ampproject:master May 25, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Make Malte, Dima, Justin Happy
  
Privacy Leaks Caught
Development

Successfully merging this pull request may close these issues.

None yet

5 participants