Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions _dev/src/ts/appUI/api/ShopRequestsGuard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/

/**
* While we run a process of update or restore, we alter the store in many ways.
* We try to control when to initialize the Core, or emptying the cache when necessary.
*
* If an ajax request is sent to the shop in parallel, we encounter side effects
* of cache partially generated, HTTP 500 in the logs caused by PrestaShop being modified etc.
*
* To avoid these issues, Update Assistant will prevent requests to reach the shop.
*
* @see https://github.com/PrestaShop/PrestaShop/issues/39509
*/
const PREFIX = "Update Assistant's guard:";
let filterRequestsToShop = false;

(function (): void {
// Save references to the original methods
const origOpen = XMLHttpRequest.prototype.open;

XMLHttpRequest.prototype.open = function (...args: [method: string, url: string | URL]) {
const url = args[1];
const filtered = !isRequestAllowed(String(url));

if (filterRequestsToShop && filtered) {
console.debug(`${PREFIX} Request filtered (${url})`);
this.abort();
} else {
// @ts-expect-error because we're sending the same params as we receive them. Typescript is lost becaused of the overloads.
origOpen.apply(this, args);
}
};
})();

export const isRequestAllowed = (url: string): boolean => {
// Block all requests targeting the shop unless they are for Update Assistant
if (!url.startsWith('/') && !url.includes(window.location.host)) {
return true;
}
return url.includes('autoupgrade');
};

export function disableFilteringOfRequestsToShop(): void {
console.debug(`${PREFIX} Requests to the shop are allowed.`);

filterRequestsToShop = false;
}

export function enableFilteringOfRequestsToShop(): void {
console.debug(`${PREFIX} Requests to the shop are now filtered.`);

filterRequestsToShop = true;
}
7 changes: 7 additions & 0 deletions _dev/src/ts/appUI/components/ProcessContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ import { ProcessContainerCallbacks } from '../types/Process';
import Process from '../utils/Process';
import ProgressTracker from './ProgressTracker';
import BrowserTab from './BrowserTab';
import {
disableFilteringOfRequestsToShop,
enableFilteringOfRequestsToShop
} from '../api/ShopRequestsGuard';

export default class ProcessContainer implements DomLifecycle {
#progressTracker: ProgressTracker = new ProgressTracker(this.#progressTrackerContainer);
Expand All @@ -44,6 +48,7 @@ export default class ProcessContainer implements DomLifecycle {
});

this.#enableExitConfirmation();
enableFilteringOfRequestsToShop();

await process.startProcess(this.initialAction);
};
Expand Down Expand Up @@ -72,6 +77,7 @@ export default class ProcessContainer implements DomLifecycle {

#onProcessEnd = async (response: ApiResponseAction): Promise<void> => {
this.#disableExitConfirmation();
disableFilteringOfRequestsToShop();
if (response.error) {
this.#onError(response);
} else {
Expand All @@ -82,6 +88,7 @@ export default class ProcessContainer implements DomLifecycle {

#onError = (response: ApiResponseAction): void => {
this.#disableExitConfirmation();
disableFilteringOfRequestsToShop();
this.#progressTracker.updateProgress(response);
this.#progressTracker.endProgress();
this.#browserTab.setError();
Expand Down
70 changes: 70 additions & 0 deletions _dev/tests/api/ShopRequestsGuard.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/

import { isRequestAllowed } from '../../src/ts/appUI/api/ShopRequestsGuard';

describe('ShopRequestsGuard', () => {
describe('isRequestAllowed', () => {
const dataSet = [
{
urlRequest: '/admin-dev/common/notifications/ack?_token=',
urlShop:
'http://localhost:8001/admin-dev/?controller=AdminSelfUpgrade&token=&route=restore-page-post-restore',
expectedValue: false
},
{
urlRequest: 'http://localhost:8001/admin-dev/common/notifications?_token=',
urlShop:
'http://localhost:8001/admin-dev/?controller=AdminSelfUpgrade&token=&route=restore-page-post-restore',
expectedValue: false
},
{
urlRequest:
'/admin-dev/autoupgrade/ajax-upgradetab.php?route=restore-page-backup-selection',
urlShop:
'http://localhost:8001/admin-dev/?controller=AdminSelfUpgrade&token=&route=restore-page-post-restore',
expectedValue: true
},
{
urlRequest: 'https://api.prestashop-project.org/prestashop/stable',
urlShop:
'http://localhost:8001/admin-dev/?controller=AdminSelfUpgrade&token=&route=restore-page-post-restore',
expectedValue: true
}
];

it.each(dataSet)(
'checks the URLs can be called',
({
urlRequest,
urlShop,
expectedValue
}: {
urlRequest: string;
urlShop: string;
expectedValue: boolean;
}) => {
(window as Window).location = urlShop;
const result = isRequestAllowed(urlRequest);

expect(result).toBe(expectedValue);
}
);
});
});