Skip to content

Commit

Permalink
Ensure that the response-origin of range requests match the full requ…
Browse files Browse the repository at this point in the history
…est (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.
  • Loading branch information
Snuffleupagus committed Nov 12, 2024
1 parent 7a96203 commit 9c38dd5
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 1 deletion.
15 changes: 14 additions & 1 deletion src/display/fetch_stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
createHeaders,
createResponseStatusError,
extractFilenameFromHeader,
getResponseOrigin,
validateRangeRequestCapabilities,
validateResponseStatus,
} from "./network_utils.js";
Expand Down Expand Up @@ -52,6 +53,8 @@ function getArrayBuffer(val) {

/** @implements {IPDFStream} */
class PDFFetchStream {
_responseOrigin = Promise.withResolvers();

constructor(source) {
this.source = source;
this.isHttp = /^https?:/i.test(source.url);
Expand Down Expand Up @@ -121,6 +124,8 @@ class PDFFetchStreamReader {
createFetchOptions(headers, this._withCredentials, this._abortController)
)
.then(response => {
stream._responseOrigin.resolve(getResponseOrigin(response.url));

if (!validateResponseStatus(response.status)) {
throw createResponseStatusError(response.status, url);
}
Expand Down Expand Up @@ -216,7 +221,15 @@ class PDFFetchStreamRangeReader {
url,
createFetchOptions(headers, this._withCredentials, this._abortController)
)
.then(response => {
.then(async response => {
const responseOrigin = getResponseOrigin(response.url),
fullResponseOrigin = await stream._responseOrigin.promise;

if (responseOrigin !== fullResponseOrigin) {
throw new Error(
`Expected range response-origin "${responseOrigin}" to match "${fullResponseOrigin}".`
);
}
if (!validateResponseStatus(response.status)) {
throw createResponseStatusError(response.status, url);
}
Expand Down
28 changes: 28 additions & 0 deletions src/display/network.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
createHeaders,
createResponseStatusError,
extractFilenameFromHeader,
getResponseOrigin,
validateRangeRequestCapabilities,
} from "./network_utils.js";

Expand All @@ -39,6 +40,8 @@ function getArrayBuffer(xhr) {
}

class NetworkManager {
_responseOrigin = Promise.withResolvers();

constructor({ url, httpHeaders, withCredentials }) {
this.url = url;
this.isHttp = /^https?:/i.test(url);
Expand Down Expand Up @@ -273,6 +276,10 @@ class PDFNetworkStreamFullRequestReader {
const fullRequestXhrId = this._fullRequestId;
const fullRequestXhr = this._manager.getRequestXhr(fullRequestXhrId);

this._manager._responseOrigin.resolve(
getResponseOrigin(fullRequestXhr.responseURL)
);

const rawResponseHeaders = fullRequestXhr.getAllResponseHeaders();
const responseHeaders = new Headers(
rawResponseHeaders
Expand Down Expand Up @@ -401,10 +408,13 @@ class PDFNetworkStreamFullRequestReader {

/** @implements {IPDFStreamRangeReader} */
class PDFNetworkStreamRangeRequestReader {
#responseOrigin = Promise.withResolvers();

constructor(manager, begin, end) {
this._manager = manager;

const args = {
onHeadersReceived: this._onHeadersReceived.bind(this),
onDone: this._onDone.bind(this),
onError: this._onError.bind(this),
onProgress: this._onProgress.bind(this),
Expand All @@ -420,6 +430,14 @@ class PDFNetworkStreamRangeRequestReader {
this.onClosed = null;
}

_onHeadersReceived() {
this.#responseOrigin.resolve(
getResponseOrigin(
this._manager.getRequestXhr(this._requestId)?.responseURL
)
);
}

_close() {
this.onClosed?.(this);
}
Expand Down Expand Up @@ -460,6 +478,16 @@ class PDFNetworkStreamRangeRequestReader {
}

async read() {
const [responseOrigin, fullResponseOrigin] = await Promise.all([
this.#responseOrigin.promise,
this._manager._responseOrigin.promise,
]);

if (responseOrigin !== fullResponseOrigin) {
throw new Error(
`Expected range response-origin "${responseOrigin}" to match "${fullResponseOrigin}".`
);
}
if (this._storedError) {
throw this._storedError;
}
Expand Down
10 changes: 10 additions & 0 deletions src/display/network_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@ function createHeaders(isHttp, httpHeaders) {
return headers;
}

function getResponseOrigin(url) {
try {
return new URL(url).origin;
} catch {
// `new URL()` will throw on incorrect data.
}
return null;
}

function validateRangeRequestCapabilities({
responseHeaders,
isHttp,
Expand Down Expand Up @@ -116,6 +125,7 @@ export {
createHeaders,
createResponseStatusError,
extractFilenameFromHeader,
getResponseOrigin,
validateRangeRequestCapabilities,
validateResponseStatus,
};

0 comments on commit 9c38dd5

Please sign in to comment.