# Odebranie dostępu w aktywnej sesji MCP

Własny test wykonany 12 października 2026. Badany system to rzeczywisty,
niezmieniony pakiet npm `@modelcontextprotocol/server-filesystem@1.0.0`.
Własny jest klient testowy JSON-RPC, nie serwer. Nie uruchamialiśmy modelu AI.

## Wynik i zakres

W jednej aktywnej sesji wykonano dziewięć odczytów i osiem sprawdzeń listy
dozwolonych katalogów. Cztery odczyty kontrolne zwróciły znacznik; pięć prób
bez uprawnienia zwróciło odmowę i nie zwróciło znacznika. Są to różne,
zaplanowane przypadki; nie należy liczyć z nich procentu bezpieczeństwa.

| Etykieta w logu | Odczyt | Wynik |
| --- | --- | --- |
| A-positive-control | Plik wewnątrz przyznanego root | znacznik |
| A-outside-control | Plik spoza root | odmowa |
| B-after-revocation | Ten sam plik po pustej liście roots | odmowa |
| C-restored-read | Ten sam plik po przywróceniu root | znacznik |
| D-read-during-refresh | Odczyt wysłany po żądaniu roots/list, przed odpowiedzią | oczekiwanie, potem odmowa |
| E-positive-before-overlap | Kontrola przed nakładającymi się aktualizacjami | znacznik |
| E-denied-before-stale | Po nowszym wycofaniu, starsza odpowiedź wstrzymana | odmowa |
| E-denied-after-stale | Po zwolnieniu starszej odpowiedzi z przyznanym root | odmowa |
| E-final-positive | Końcowe ponowne przyznanie root | znacznik |

W przypadku D klient obserwował oczekujący odczyt przez 201,465 ms
(planowane okno 200 ms). Nie jest to pomiar szybkości unieważnienia.
Klient celowo wstrzymał odpowiedź roots/list. Jej zwolnienie zakończyło się
odmową odczytu. W przypadku E stderr rzeczywistego serwera potwierdził
odrzucenie starszej odpowiedzi przed końcowym sprawdzeniem stanu i odczytem.

## Metoda i potwierdzenie aktualizacji

Serwer działa przez stdio, bez katalogów CLI, w jednym procesie.
Klient deklaruje `roots.listChanged: true`. Dwa pliki powstają w świeżym
katalogu `/private/tmp/mcp-dostep-probe-*`; tylko jeden katalog zostaje
udostępniony jako root. Test usuwa wyłącznie własny katalog po zakończeniu.
Treść obu plików jest syntetyczna i zapisana także w zdarzeniu `fixtures`.

Każde wycofanie jest powiadomieniem `notifications/roots/list_changed`,
po którym serwer wysyła `roots/list`. Klient odpowiada pustą listą.
Przed zwykłą próbą odczytu sprawdzamy listę narzędziem
`list_allowed_directories`. Jego odpowiedź potwierdza stan, a odczyt
oddzielnie sprawdza egzekwowanie. Nie uznajemy samego wysłania powiadomienia
ani odpowiedzi klienta za potwierdzenie zastosowania zmiany.

Wyjątkiem jest celowy przypadek D: odczyt trafia dopiero po odebraniu
żądania roots/list od serwera, a przed odpowiedzią klienta. W E starsza
odpowiedź z dostępem jest zwalniana dopiero po potwierdzeniu nowszej pustej
listy i odmowy. Kolejne sprawdzenie następuje po komunikacie stderr
`Discarded a stale roots/list answer`. Pełną kolejność zachowuje `log.jsonl`.

Weryfikator odczytuje surowy log niezależnie od pól `passed` podsumowania.
Łączy wywołania i odpowiedzi po identyfikatorach oraz kierunku komunikacji,
sprawdza znaczniki, odmowy, stany, kolejność i liczbę rzeczywistych odczytów.
Porównuje obliczony wynik z `results.json` oraz sprawdza SHA-256.

## Środowisko

- macOS, Darwin 25.6.0, arm64; Node.js 26.3.0; npm 11.16.0.
- Pakiet serwera 1.0.0, faktycznie zainstalowany SDK 1.32.1.
- Protokół wynegocjowany przez MCP: 2025-06-18. To osobny numer od wersji pakietu.
- Zależności przypięte plikiem package-lock.json; pełne drzewo w dependencies.json.
- Serwer nie otrzymuje argumentów katalogów ani odziedziczonych sekretów środowiska.
- Brak LLM, GPU, API, przeglądarki, ruchu sieciowego w skrypcie audytu i danych klientów.
  Nie wykonywano osobnego przechwytywania sieci. Instalacja wymaga pobrania z npm.

## Odtworzenie

Po rozpakowaniu paczki przejdź do katalogu zawierającego README i package.json.
Skrypt używa `/private/tmp`, więc podana konfiguracja jest przygotowana dla macOS.
Inny system wymaga zmiany lokalizacji katalogu tymczasowego i osobnego oznaczenia przebiegu.

```bash
node verify.mjs raw/run-001
npm ci --ignore-scripts --no-audit --no-fund
node audit.mjs raw/run-002
node verify.mjs raw/run-002
```

Pierwsza komenda weryfikuje zapisane wyniki bez instalacji zależności i bez
sieci. Kolejne uruchamiają oficjalny pakiet. Użyj nowej nazwy katalogu
przebiegu; skrypt odmawia nadpisania istniejącego. Weryfikator również przy
nowym przebiegu sprawdza integralność pierwotnych plików z manifestu.
Nowego przebiegu nie dopisuje automatycznie do manifestu.

## Pliki

- `protokol.md`: zapis planu przed uruchomieniem oraz dziennik przebiegu.
- `audit.mjs`: własny klient testowy używający bibliotek standardowych Node.js.
- `verify.mjs`: niezależne przeliczenie i kontrola kolejności z logu.
- `package.json`, `package-lock.json`: przypięty pakiet i zależności.
- `raw/run-001/log.jsonl`: pełna wymiana JSON-RPC, stderr i zdarzenia klienta;
  numery `seq` są wspólne dla obu kierunków i zdarzeń sterujących.
- `raw/run-001/stdout.jsonl`, `stderr.txt`: oryginalne linie z procesu serwera.
- `raw/run-001/results.json`: wyniki poszczególnych sprawdzeń, także ścieżki i treść odpowiedzi.
- `raw/run-001/versions.json`, `dependencies.json`: środowisko, wersje, sumy plików kodu serwera.
- `sources/`: metadane publicznego wydania, trzech poprawek i paczki npm.
- `manifest-sha256.json`: sumy wszystkich plików paczki poza samym manifestem.

## Dziennik wykonania

Przed testem zapisano protokół. Pierwsze pobranie npm w piaskownicy nie
przeszło z powodu ENOTFOUND; zależności pobrano następnie z dostępem do
publicznego rejestru, bez skryptów instalacyjnych. To nie był przebieg testu.
Jedyny pomiar serwera, `run-001`, rozpoczął się
2026-10-12T17:55:36.781Z i zakończył powodzeniem. Po pierwszym przebiegu
nie zmieniano skryptu `audit.mjs` ani kryteriów. Następnie powstał
weryfikator, dokumentacja i paczka publikacyjna. Nie wykonywano porównania
poprzedniej wersji; jej zachowanie opisujemy wyłącznie za dokumentacją poprawki.

## Ograniczenia

Test dotyczy tej wersji, konfiguracji i harmonogramu jednej sesji. Nie
sprawdza błędów roots/list, katalogów podanych przez CLI, zapisów, symlinków,
OAuth, izolacji procesu, wielu użytkowników ani wszystkich przeplotów
aktualizacji. Nie sprawdza anulowania operacji, które rozpoczęły się przed
powiadomieniem. Nie usuwa danych odczytanych wcześniej. Nie ocenia zachowania
modelu ani innych narzędzi agenta. Roots nie są tu przedstawiane jako
systemowy sandbox. Wynik nie certyfikuje bezpieczeństwa integracji.

## Pochodzenie i licencje

Własne pliki skryptów, protokołu, danych i dokumentacji: Robert Marczyński,
CC BY 4.0, https://creativecommons.org/licenses/by/4.0/.
Nie redystrybuujemy node_modules ani kodu pakietu serwera. Pakiet npm ma
deklarację `SEE LICENSE IN LICENSE`; źródłem warunków projektu jest
https://github.com/modelcontextprotocol/servers/blob/v1.0.0/LICENSE.
Metadane i opisy w `sources/` pochodzą z GitHub oraz npm, zachowują wskazane
tam adresy i autorstwo. Nie obejmujemy treści osób trzecich własną licencją.
