Skip to content

Commit 9c38dd5

Browse files
committed
Ensure that the response-origin of range requests match the full request (issue 12744)
The following cases are excluded in the patch: - The Firefox PDF Viewer, since it has been fixed on the platform side already; please see https://bugzilla.mozilla.org/show_bug.cgi?id=1683940 - The `PDFNodeStream`-implementation, used in Node.js environments, since after recent changes that code only supports `file://`-URLs.
1 parent 7a96203 commit 9c38dd5

File tree

3 files changed

+52
-1
lines changed

3 files changed

+52
-1
lines changed

src/display/fetch_stream.js

+14-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
createHeaders,
1919
createResponseStatusError,
2020
extractFilenameFromHeader,
21+
getResponseOrigin,
2122
validateRangeRequestCapabilities,
2223
validateResponseStatus,
2324
} from "./network_utils.js";
@@ -52,6 +53,8 @@ function getArrayBuffer(val) {
5253

5354
/** @implements {IPDFStream} */
5455
class PDFFetchStream {
56+
_responseOrigin = Promise.withResolvers();
57+
5558
constructor(source) {
5659
this.source = source;
5760
this.isHttp = /^https?:/i.test(source.url);
@@ -121,6 +124,8 @@ class PDFFetchStreamReader {
121124
createFetchOptions(headers, this._withCredentials, this._abortController)
122125
)
123126
.then(response => {
127+
stream._responseOrigin.resolve(getResponseOrigin(response.url));
128+
124129
if (!validateResponseStatus(response.status)) {
125130
throw createResponseStatusError(response.status, url);
126131
}
@@ -216,7 +221,15 @@ class PDFFetchStreamRangeReader {
216221
url,
217222
createFetchOptions(headers, this._withCredentials, this._abortController)
218223
)
219-
.then(response => {
224+
.then(async response => {
225+
const responseOrigin = getResponseOrigin(response.url),
226+
fullResponseOrigin = await stream._responseOrigin.promise;
227+
228+
if (responseOrigin !== fullResponseOrigin) {
229+
throw new Error(
230+
`Expected range response-origin "${responseOrigin}" to match "${fullResponseOrigin}".`
231+
);
232+
}
220233
if (!validateResponseStatus(response.status)) {
221234
throw createResponseStatusError(response.status, url);
222235
}

src/display/network.js

+28
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
createHeaders,
1919
createResponseStatusError,
2020
extractFilenameFromHeader,
21+
getResponseOrigin,
2122
validateRangeRequestCapabilities,
2223
} from "./network_utils.js";
2324

@@ -39,6 +40,8 @@ function getArrayBuffer(xhr) {
3940
}
4041

4142
class NetworkManager {
43+
_responseOrigin = Promise.withResolvers();
44+
4245
constructor({ url, httpHeaders, withCredentials }) {
4346
this.url = url;
4447
this.isHttp = /^https?:/i.test(url);
@@ -273,6 +276,10 @@ class PDFNetworkStreamFullRequestReader {
273276
const fullRequestXhrId = this._fullRequestId;
274277
const fullRequestXhr = this._manager.getRequestXhr(fullRequestXhrId);
275278

279+
this._manager._responseOrigin.resolve(
280+
getResponseOrigin(fullRequestXhr.responseURL)
281+
);
282+
276283
const rawResponseHeaders = fullRequestXhr.getAllResponseHeaders();
277284
const responseHeaders = new Headers(
278285
rawResponseHeaders
@@ -401,10 +408,13 @@ class PDFNetworkStreamFullRequestReader {
401408

402409
/** @implements {IPDFStreamRangeReader} */
403410
class PDFNetworkStreamRangeRequestReader {
411+
#responseOrigin = Promise.withResolvers();
412+
404413
constructor(manager, begin, end) {
405414
this._manager = manager;
406415

407416
const args = {
417+
onHeadersReceived: this._onHeadersReceived.bind(this),
408418
onDone: this._onDone.bind(this),
409419
onError: this._onError.bind(this),
410420
onProgress: this._onProgress.bind(this),
@@ -420,6 +430,14 @@ class PDFNetworkStreamRangeRequestReader {
420430
this.onClosed = null;
421431
}
422432

433+
_onHeadersReceived() {
434+
this.#responseOrigin.resolve(
435+
getResponseOrigin(
436+
this._manager.getRequestXhr(this._requestId)?.responseURL
437+
)
438+
);
439+
}
440+
423441
_close() {
424442
this.onClosed?.(this);
425443
}
@@ -460,6 +478,16 @@ class PDFNetworkStreamRangeRequestReader {
460478
}
461479

462480
async read() {
481+
const [responseOrigin, fullResponseOrigin] = await Promise.all([
482+
this.#responseOrigin.promise,
483+
this._manager._responseOrigin.promise,
484+
]);
485+
486+
if (responseOrigin !== fullResponseOrigin) {
487+
throw new Error(
488+
`Expected range response-origin "${responseOrigin}" to match "${fullResponseOrigin}".`
489+
);
490+
}
463491
if (this._storedError) {
464492
throw this._storedError;
465493
}

src/display/network_utils.js

+10
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,15 @@ function createHeaders(isHttp, httpHeaders) {
3636
return headers;
3737
}
3838

39+
function getResponseOrigin(url) {
40+
try {
41+
return new URL(url).origin;
42+
} catch {
43+
// `new URL()` will throw on incorrect data.
44+
}
45+
return null;
46+
}
47+
3948
function validateRangeRequestCapabilities({
4049
responseHeaders,
4150
isHttp,
@@ -116,6 +125,7 @@ export {
116125
createHeaders,
117126
createResponseStatusError,
118127
extractFilenameFromHeader,
128+
getResponseOrigin,
119129
validateRangeRequestCapabilities,
120130
validateResponseStatus,
121131
};

0 commit comments

Comments
 (0)