1
0
Fork 0
mirror of https://github.com/mozilla/pdf.js.git synced 2025-04-19 14:48:08 +02:00

Merge pull request #19028 from Snuffleupagus/issue-12744

Ensure that the response-origin of range requests match the full request (issue 12744)
This commit is contained in:
Jonas Jenwald 2024-11-24 10:37:07 +01:00 committed by GitHub
commit f911635b58
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 57 additions and 9 deletions

View file

@ -18,6 +18,7 @@ import {
createHeaders,
createResponseStatusError,
extractFilenameFromHeader,
getResponseOrigin,
validateRangeRequestCapabilities,
validateResponseStatus,
} from "./network_utils.js";
@ -52,6 +53,8 @@ function getArrayBuffer(val) {
/** @implements {IPDFStream} */
class PDFFetchStream {
_responseOrigin = null;
constructor(source) {
this.source = source;
this.isHttp = /^https?:/i.test(source.url);
@ -121,6 +124,8 @@ class PDFFetchStreamReader {
createFetchOptions(headers, this._withCredentials, this._abortController)
)
.then(response => {
stream._responseOrigin = getResponseOrigin(response.url);
if (!validateResponseStatus(response.status)) {
throw createResponseStatusError(response.status, url);
}
@ -217,6 +222,13 @@ class PDFFetchStreamRangeReader {
createFetchOptions(headers, this._withCredentials, this._abortController)
)
.then(response => {
const responseOrigin = getResponseOrigin(response.url);
if (responseOrigin !== stream._responseOrigin) {
throw new Error(
`Expected range response-origin "${responseOrigin}" to match "${stream._responseOrigin}".`
);
}
if (!validateResponseStatus(response.status)) {
throw createResponseStatusError(response.status, url);
}

View file

@ -18,6 +18,7 @@ import {
createHeaders,
createResponseStatusError,
extractFilenameFromHeader,
getResponseOrigin,
validateRangeRequestCapabilities,
} from "./network_utils.js";
@ -39,6 +40,8 @@ function getArrayBuffer(xhr) {
}
class NetworkManager {
_responseOrigin = null;
constructor({ url, httpHeaders, withCredentials }) {
this.url = url;
this.isHttp = /^https?:/i.test(url);
@ -273,6 +276,10 @@ class PDFNetworkStreamFullRequestReader {
const fullRequestXhrId = this._fullRequestId;
const fullRequestXhr = this._manager.getRequestXhr(fullRequestXhrId);
this._manager._responseOrigin = getResponseOrigin(
fullRequestXhr.responseURL
);
const rawResponseHeaders = fullRequestXhr.getAllResponseHeaders();
const responseHeaders = new Headers(
rawResponseHeaders
@ -370,6 +377,8 @@ class PDFNetworkStreamFullRequestReader {
}
async read() {
await this._headersCapability.promise;
if (this._storedError) {
throw this._storedError;
}
@ -405,6 +414,7 @@ class PDFNetworkStreamRangeRequestReader {
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),
@ -420,6 +430,19 @@ class PDFNetworkStreamRangeRequestReader {
this.onClosed = null;
}
_onHeadersReceived() {
const responseOrigin = getResponseOrigin(
this._manager.getRequestXhr(this._requestId)?.responseURL
);
if (responseOrigin !== this._manager._responseOrigin) {
this._storedError = new Error(
`Expected range response-origin "${responseOrigin}" to match "${this._manager._responseOrigin}".`
);
this._onError(0);
}
}
_close() {
this.onClosed?.(this);
}
@ -441,7 +464,7 @@ class PDFNetworkStreamRangeRequestReader {
}
_onError(status) {
this._storedError = createResponseStatusError(status, this._url);
this._storedError ??= createResponseStatusError(status, this._url);
for (const requestCapability of this._requests) {
requestCapability.reject(this._storedError);
}

View file

@ -36,6 +36,16 @@ function createHeaders(isHttp, httpHeaders) {
return headers;
}
function getResponseOrigin(url) {
try {
return new URL(url).origin;
} catch {
// `new URL()` will throw on incorrect data.
}
// Notably, null is distinct from "null" string (e.g. from file:-URLs).
return null;
}
function validateRangeRequestCapabilities({
responseHeaders,
isHttp,
@ -116,6 +126,7 @@ export {
createHeaders,
createResponseStatusError,
extractFilenameFromHeader,
getResponseOrigin,
validateRangeRequestCapabilities,
validateResponseStatus,
};

View file

@ -30,6 +30,10 @@ class IPDFStream {
/**
* Gets a reader for the range of the PDF data.
*
* NOTE: Currently this method is only expected to be invoked *after*
* the `IPDFStreamReader.prototype.headersReady` promise has resolved.
*
* @param {number} begin - the start offset of the data.
* @param {number} end - the end offset of the data.
* @returns {IPDFStreamRangeReader}

View file

@ -54,7 +54,7 @@ describe("fetch_stream", function () {
const fullReader = stream.getFullReader();
let isStreamingSupported, isRangeSupported;
const promise = fullReader.headersReady.then(function () {
await fullReader.headersReady.then(function () {
isStreamingSupported = fullReader.isStreamingSupported;
isRangeSupported = fullReader.isRangeSupported;
});
@ -71,7 +71,7 @@ describe("fetch_stream", function () {
});
};
await Promise.all([read(), promise]);
await read();
expect(len).toEqual(pdfLength);
expect(isStreamingSupported).toEqual(true);
@ -90,7 +90,7 @@ describe("fetch_stream", function () {
const fullReader = stream.getFullReader();
let isStreamingSupported, isRangeSupported, fullReaderCancelled;
const promise = fullReader.headersReady.then(function () {
await fullReader.headersReady.then(function () {
isStreamingSupported = fullReader.isStreamingSupported;
isRangeSupported = fullReader.isRangeSupported;
// We shall be able to close full reader without any issue.
@ -121,7 +121,6 @@ describe("fetch_stream", function () {
await Promise.all([
read(rangeReader1, result1),
read(rangeReader2, result2),
promise,
]);
expect(isStreamingSupported).toEqual(true);

View file

@ -31,7 +31,7 @@ describe("network", function () {
const fullReader = stream.getFullReader();
let isStreamingSupported, isRangeSupported;
const promise = fullReader.headersReady.then(function () {
await fullReader.headersReady.then(function () {
isStreamingSupported = fullReader.isStreamingSupported;
isRangeSupported = fullReader.isRangeSupported;
});
@ -49,7 +49,7 @@ describe("network", function () {
});
};
await Promise.all([read(), promise]);
await read();
expect(len).toEqual(pdf1Length);
expect(count).toEqual(1);
@ -72,7 +72,7 @@ describe("network", function () {
const fullReader = stream.getFullReader();
let isStreamingSupported, isRangeSupported, fullReaderCancelled;
const promise = fullReader.headersReady.then(function () {
await fullReader.headersReady.then(function () {
isStreamingSupported = fullReader.isStreamingSupported;
isRangeSupported = fullReader.isRangeSupported;
// we shall be able to close the full reader without issues
@ -107,7 +107,6 @@ describe("network", function () {
await Promise.all([
read(range1Reader, result1),
read(range2Reader, result2),
promise,
]);
expect(result1.value).toEqual(rangeSize);