12 KiB
title, description
| title | description |
|---|---|
| Atestacja znalezisk supply-chain Socket.dev | Atestacja maintainerów dla znalezisk AI-detected potential-malware zgłoszonych wobec omniroute oraz mitigacji z v3.8.6 zastosowanych w każdym oznaczonym call site. |
Socket.dev / atestacja znalezisk supply-chain
Ten dokument to atestacja napisana przez maintainerów dla sześciu
znalezisk AI-detected potential malware zgłoszonych wobec omniroute@3.8.5 oraz
mitigacji wprowadzonych w omniroute@3.8.6. Istnieje po to, aby:
- Operatorzy pipeline'ów bezpieczeństwa mieli jedno źródło do cytowania, gdy muszą ocenić znaleziska względem rzeczywistego kodu źródłowego.
- Przyszłe skanery AI mogły podjąć podpisane przez maintainerów twierdzenie, że każda oznaczona ścieżka jest zamierzona, opt-in i udokumentowana.
- Mieć pisemny zapis dlaczego każdy call site ma taki kształt — żeby przyszły refaktor przypadkiem nie przywrócił fingerprintu, który celowo usunięto.
Jeśli prowadzisz skaner, który ponownie oznacza którykolwiek z poniższych call site'ów po wdrożeniu mitigacji z v3.8.6, otwórz issue ze śladem skanu, a rozszerzymy tę atestację.
§1 — Instalacja root-CA MITM (77484.js)
Pliki źródłowe:
src/mitm/cert/install.ts— publiczneinstallCert()/uninstallCert(), per-platformoweinstallCertWindows/Mac/Linux.src/mitm/systemCommands.ts— współdzielone helperyexecFile/spawn/ PowerShell używane przez ścieżki instalacji.
Wyzwalacz: użytkownik klika „Enable MITM proxy” w lokalnym dashboardzie pod
/dashboard/cli-tools/mitm. Trasa jest wyłącznie loopback — zob. hard rule #17 w
CLAUDE.md oraz src/server/authz/routeGuard.ts::isLocalOnlyPath(). Wyciekły
JWT wystawiony przez tunel nie może uruchomić tej ścieżki kodu.
Operacje uprzywilejowane (per platforma):
| OS | Command(s) |
|---|---|
| Windows | certutil -addstore Root <cert> via UAC |
| macOS | sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain <cert> |
| Linux | sudo cp <cert> <distro-trust-dir> + sudo update-ca-certificates (Debian) / sudo update-ca-trust (RHEL/SUSE) |
| Linux+Firefox/Chromium | per-profile NSS DB update via certutil -d sql:<profile> |
To te same komendy, których używają mitmproxy, Charles Proxy, Fiddler i
Caddy. Fakt, że istnieją w OmniRoute, jest udokumentowany w
docs/security/STEALTH_GUIDE.md.
Mitigacja v3.8.6:
runElevatedPowerShell()nie używa już-EncodedCommand <base64utf16le>. Podniesiony payload jest zapisywany do tymczasowego pliku.ps1na wywołanie (mode 0o600, w prywatnym katalogumkdtempSync) i wskazywany przez-File. Plik jest usuwany wfinally. Usuwa to podręcznikowy fingerprint base64-elevation-via-PowerShell oznaczany przez klasyfikator AI Socket.dev.installCertWindowszawiera wbudowany blokSECURITY-AUDITOR-NOTE:wskazujący tutaj.
Dlaczego to zostawiamy: proxy MITM to udokumentowana funkcja używana przez
docs/security/STEALTH_GUIDE.md i docs/frameworks/MITM-PROXY.md. Usunięcie
jej złamałoby zestaw funkcji agent-bridge.
§2 — Import credentiali Zed (app/api/providers/zed/import/route.js)
Pliki źródłowe:
src/app/api/providers/zed/discover/route.ts(new in v3.8.6)src/app/api/providers/zed/import/route.tssrc/lib/zed-oauth/keychain-reader.tssrc/lib/zed-oauth/credentialFingerprint.ts(new in v3.8.6)
Wyzwalacz: użytkownik klika „Import from Zed” na stronie Providers w lokalnym
dashboardzie. Endpoint jest bramkowany przez requireManagementAuth. Sam edytor Zed
zapisuje klucze API providerów w keychain OS pod udokumentowanymi nazwami
serwisów — zob. https://zed.dev/docs/ai/llm-providers.
Zachowanie v3.8.5 (to, które oznaczył Socket.dev):
POST /import odkrywał credentials i auto-zapisywał je do lokalnego
magazynu SQLite w jednym round-tripie. Bez potwierdzenia per konto, bez
fingerprintu — po prostu „znaleziono N tokenów, wszystkie zaimportowane”.
Mitigacja v3.8.6 — potwierdzenie 2-krokowe:
POST /api/providers/zed/discoverzwraca{ candidates: [{ provider, service, account, fingerprint }] }. Surowy token nigdy nie jest transmitowany. Fingerprint tosha256(service|account|token).slice(0,16).- Dashboard renderuje listę kandydatów, operator wybiera, które
zaimportować, i wysyła
{ confirmedAccounts: [{ service, account, fingerprint }] }doPOST /api/providers/zed/import. - Endpoint importu ponownie czyta keychain po stronie serwera i filtruje po
(service, account, fingerprint). Sfałszowana lub odtworzona odpowiedź discover nie może oszukać endpointu importu, by zapisał niepowiązany token — jeśli żywy token zmienił się od discover, fingerprint już nie pasuje i credential jest pomijany.
Flaga env OMNIROUTE_ZED_IMPORT_LEGACY_ONE_STEP=true zachowuje zachowanie v3.8.5
dla operatorów, którzy jeszcze nie zaktualizowali automatyzacji. Zostanie
usunięta w v3.9.
Dlaczego to zostawiamy: import Zed to najprzyjaźniejsza ścieżka onboardingu dla użytkowników, którzy już używają Zed i chcą odzwierciedlić klucze providerów w OmniRoute bez ponownego wklejania.
§3 — execFile / spawn / elevated PowerShell (21843.js)
Pliki źródłowe: src/mitm/systemCommands.ts.
Dlaczego oznaczono: chunk re-eksportuje execFileWithPassword,
runElevatedPowerShell oraz współdzielony helper quotePowerShell. Klasyfikator AI
Socket.dev widzi je jako generyczny „toolkit wykonania na hoście + podnoszenia
uprawnień”. W OmniRoute są używane wyłącznie przez ścieżkę instalacji certyfikatu MITM
(§1) oraz przez execFileWithPassword do wykonywania komend sudo.
Mitigacja v3.8.6:
- Refaktor
runElevatedPowerShell(zob. §1). - Wbudowany blok
SECURITY-AUDITOR-NOTE:przy oburunElevatedPowerShelliexecFileWithPassworddokumentuje allowlistę callerów i przypiętą listę plików wykonywalnych. - Wywołanie
spawn()wexecFileWithPasswordniesie markernosemgrepz allowlistą plików wykonywalnych, które helper może przyjąć — nie ma ścieżki od wejścia użytkownika dofinalCommand/finalArgs.
§4 / §6 — Supervisor serwisu 9router (api/services/9router/{start,restart}/route.js)
Pliki źródłowe:
src/app/api/services/9router/_lib.ts— fabryka supervisora.src/app/api/services/9router/{start,stop,restart,status,install,update,auto-start}/route.ts.src/lib/services/ServiceSupervisor.ts— generyczny spawn / health-poll / log-buffer.
Wyzwalacz: użytkownik klika „Install” / „Start” na stronie osadzonych serwisów w lokalnym dashboardzie.
Ochrony już na miejscu:
- Wszystkie trasy
/api/services/*są LOCAL_ONLY zgodnie zsrc/server/authz/routeGuard.ts(hard rule #17). Egzekwowanie loopback następuje przed jakimkolwiek sprawdzeniem auth — wyciekły JWT nie może do nich dotrzeć. - Wiersz DB 9router jest seedowany jako
status='not_installed', auto_start=0(zob.src/lib/db/migrations/071_services.sql:19). Serwis nie startuje przy pierwszym uruchomieniu. spawn()jest wywoływane ze ścieżką binarki zwróconą przezresolveSpawnArgs(apiKey, PORT)wsrc/lib/services/installers/ninerouter.ts, która jest stałą allowlistą wspieranych binarek.- Stdout/stderr jest buforowany w pamięci (limit 5 MB, zob.
_lib.ts) — bez zapisu na dysk, chyba że użytkownik włączy logowanie z dashboardu.
Mitigacja v3.8.6: bez zmiany funkcjonalnej. Minimalny profil builda
(OMNIROUTE_BUILD_PROFILE=minimal) zastępuje
src/lib/services/installers/ninerouter.ts stubem dla użytkowników, którzy chcą
fizycznie usunąć uprzywilejowane ścieżki z bundle'a.
Dlaczego to zostawiamy: 9router to opcjonalny, lokalnie instalowany serwis towarzyszący (pomyśl: plugin w stylu WordPress) — ścisły opt-in.
§5 — Zapis zwrotny credentiali OmniRoute Cloud Sync (api/keys/[id]/route.js)
Pliki źródłowe:
src/lib/cloudSync.ts—syncToCloud()/updateLocalTokens().src/app/api/keys/[id]/route.ts— wywołujesyncKeysToCloudIfEnabled().
Wyzwalacz: isCloudEnabled() zwraca true (ustawiane z dashboardu) oraz
skonfigurowane jest CLOUD_URL. Gdy oba są wyłączone, nie ma wychodzącego wywołania sieciowego do
endpointu Cloud.
Zachowanie v3.8.5 (bug, który Socket.dev złapał właściwie):
updateLocalTokens() nadpisywało accessToken, refreshToken i
providerSpecificData z odpowiedzi Cloud, gdy
cloudUpdatedAt > localUpdatedAt. Bez HMAC, bez podpisu, bez checksumy. Źle
skonfigurowany lub wrogi CLOUD_URL (albo MITM na kanale) mógł po cichu podmienić
tokeny OAuth providerów.
Mitigacja v3.8.6:
- Weryfikacja HMAC:
verifyCloudSignature(rawBody, sigHeader)sprawdza nagłówekX-Cloud-Sig(HMAC-SHA256(OMNIROUTE_CLOUD_SYNC_SECRET, rawBody)) przed parsowaniem JSON. Jeśli secret jest ustawiony, podpis jest wymagany. Jeśli nie (tryb legacy), logowane jest ostrzeżenie i odpowiedź jest akceptowana — secret będzie wymagany w v3.9. - Opt-in pól sekretnych:
accessToken/refreshToken/providerSpecificDatasą nadpisywane tylko gdyOMNIROUTE_CLOUD_SYNC_SECRETS=true. Domyślny tryb synchronizuje wyłącznie metadane niebędące credentialami (expiresAt,status,lastError*,rateLimitedUntil,updatedAt). To breaking change dla użytkowników, którzy polegali na zdalnej synchronizacji tokenów — muszą jawnie opt-in.
Dlaczego to zostawiamy: Cloud Sync to jedyny sposób, by tenant OmniRoute Cloud centralizował credentials zespołu. Poprawka czyni model zagrożeń uczciwym: „serwer podpisuje, klient weryfikuje, operator opt-in”.
Profil builda: minimal
Dla użytkowników potrzebujących artefaktu przyjaznego Socket, buduj z:
OMNIROUTE_BUILD_PROFILE=minimal npm run build
Webpackowy NormalModuleReplacementPlugin aliasuje cztery moduły do stubów:
| Module | Stub |
|---|---|
src/mitm/cert/install.ts |
src/mitm/cert/install.stub.ts |
src/lib/zed-oauth/keychain-reader.ts |
src/lib/zed-oauth/keychain-reader.stub.ts |
src/lib/cloudSync.ts |
src/lib/cloudSync.stub.ts |
src/lib/services/installers/ninerouter.ts |
src/lib/services/installers/ninerouter.stub.ts |
Każdy stub eksportuje tę samą powierzchnię, ale każda funkcja rzuca
featureDisabledError(name) w runtime. Trasy zależne od wyłączonego
modułu zwracają HTTP 503 z jasnym komunikatem zamiast aktywować
wrażliwą ścieżkę kodu.
Wynikowy bundle ma być publikowany jako omniroute-secure. Zob.
docs/ops/PUBLISHING_SECURE.md po receptę publikacji.
Podział na pluginy (śledzony na v4)
Długoterminowo zamierzamy podzielić pakiet npm na osobno audytowalne moduły. Zob. milestone v4 w trackerze issue GitHub po issue śledzące.