# Не дайте юзерскриптам і розширенням патчити ваш вебзастосунок

Юзерскрипт, патч для Tampermonkey чи розширення мусить за щось зачепитися у вашому JavaScript: за ім'я, рядок або впізнавану форму. Що структурна обфускація та нова форма кожної збірки роблять із цими якорями.

Source: https://www.afterpack.dev/uk/docs/use-cases/patching-tools

Юзерскрипт, патч від спільноти чи розширення змінює ваш застосунок, знаходячи текст у JavaScript, який ви постачаєте, і переписуючи його. Змінюйте те, що можна знайти, у кожному релізі, і патч перестане застосовуватися.

## За що чіпляється патч

Авторові патча потрібен стабільний якір: ім'я функції, рядковий літерал, впізнавана форма, яку можна знову знайти в наступному завантаженому файлі. Мініфікація залишає всі три на місці. У мініфікованому бандлі все ще є імена функцій, скорочені, але однакові від збірки до збірки, ті самі рядкові літерали й та сама загальна структура, тож патч, написаний під один реліз, і далі спрацьовує на наступному.

Саме тому хороший патч живе місяцями. Автор тестує його на вашій поточній збірці, роздає своїм користувачам, і він працює в кожному релізі, який не зачіпає його якорів.

## Структурні зміни прибирають форму, яку шукає патч

Одне лише перейменування просто перемістило б якір. Починаючи з `medium`, AfterPack переписує саму програму: потік керування сплощується й перевпорядковується, значення обчислюються заново при кожному використанні замість зберігання, а код, який був незалежним, виходить переплетеним. Постачається функціональний еквівалент того, що ви написали: інша програма, яка [поводиться так само](https://www.afterpack.dev/uk/docs/semantic-contract) і яку обирає [`seed`](https://www.afterpack.dev/uk/docs/config#seed) цієї збірки.

Тож патч, який раніше вставляв рядок у `checkAccess`, мусить спершу знайти код, що робить те, що робила `checkAccess`. Це вже не функція з таким ім'ям і не функція з такою формою. Механізм описано в розділі [Як працює AfterPack](https://www.afterpack.dev/uk/docs/concepts#a-new-program-every-build).

## Рядки, які шукає автор патча, зникають

[`strings.encode`](https://www.afterpack.dev/uk/docs/config#strings-encode) увімкнено на будь-якому додатному цільовому значенні [`complexity`](https://www.afterpack.dev/uk/docs/config#complexity), тож рядкові літерали вашого JavaScript не є текстом в артефакті. Назва функції застосунку, тип повідомлення, шлях API, рядок прав доступу, текст, який показується, коли доступ закрито: `grep` опублікованого файлу не знаходить жодного з них, як і пошук із заміною, написаний під будь-який із них.

Ці літерали є найдешевшим якорем для патча, бо переживають мініфікацію без змін і читаються як документація до коду навколо них. Кодування — це частина збірки, яка їх прибирає.

## Кожен реліз знову переміщує якорі

[`seed`](https://www.afterpack.dev/uk/docs/config#seed) новий у кожній збірці, якщо ви його не закріпили, тож імена, кодування та форма відрізняються від вашого попереднього релізу. Тоді патч стає постійною статтею витрат для того, хто його підтримує: роботу доводиться повторювати для кожної збірки, яку ви випускаєте, а скрипт, уже розданий користувачам, перестає працювати в момент, коли ви розгортаєте нову версію.

## Посильте захист там, де працює автор патча

`medium` підвищує захист у всій збірці:

```json
{
  "preset": "medium"
}
```

Для коду, на який патч справді націлений, наприклад перевірки прав доступу, підніміться до `hard`:

```bash
npx afterpack@latest dist/ --preset=hard
```

Залишайте в продакшні випадковий сід. Саме сталий сід дає одному патчу спрацьовувати реліз за релізом; [Стратегія сіду](https://www.afterpack.dev/uk/docs/best-practices#seed-strategy) описує випадки, коли його безпечно закріпити. Якщо ви випускаєте релізи рідко, перезбирайте за розкладом, щоб форма все одно змінювалася; див. [Ротація за розкладом](https://www.afterpack.dev/uk/docs/builds#rotate-on-a-schedule).

## Переконайтеся, що якорів немає

Пошукайте в артефакті, який збираєтеся розгорнути, імена й рядки, на які націлився б патч. Нічого не має знайтися.

Відкрийте [Protection Map](https://www.afterpack.dev/uk/docs/protection-map#how-to-read-it), яка записується, коли увімкнено [`protectionMap.enabled`](https://www.afterpack.dev/uk/docs/config#protectionMap-enabled), і переконайтеся, що код, за яким полюватиме патч, покрито з очікуваною складністю. Потім запустіть [`verify`](https://www.afterpack.dev/uk/docs/cli#afterpack-verify-dir) як останній крок перед розгортанням: збірка записує у свій результат квитанцію захисту, а `verify` заново хешує кожен файл, названий у квитанції, і завершується помилкою, якщо якийсь із них більше не збігається.

## Чого це не зупиняє

Патчинг триває. Той, хто прочитає нову збірку, може написати під неї новий патч, і ніщо в збірці не заважає розширенню змінювати вашу сторінку в браузері, який контролює його власник.

Юзерскрипт, що чіпляється за DOM, а не за ваш JavaScript, тобто за CSS-класи, структуру елементів чи видимий текст, не залежить від того, що AfterPack робить із бандлом. Змінюється кожен патч, якому потрібно знайти код: він коштує авторові роботи заново в кожному релізі, а його користувачі працюють зі зламаним скриптом, доки не отримають оновлення.

## Далі

- [Збірки та CI](https://www.afterpack.dev/uk/docs/builds): перезбирання за розкладом і випадковий сід у продакшні.
- [Найкращі практики](https://www.afterpack.dev/uk/docs/best-practices#seed-strategy): повна стратегія сіду, зокрема коли його безпечно закріпити.
- [Захист перевірки пейволу](https://www.afterpack.dev/uk/docs/use-cases/paywall-checks): та сама механіка, застосована до перевірки прав доступу.
