Hlavní příčiny
Předchozí editor nahrával svůj pracovní soubor do nadřazeného adresáře cíle,
do kterého účet SSH pod /etc nebo /var často nemůže zapisovat. Přejmenování
přes SFTP neposkytovalo autorizovanou cestu přes sudo, kontrolu konfliktu vůči
výchozímu stavu, ověření bezpečnostních metadat ani spolehlivou sémantiku přepsání.
Obsluha chyb mazala dočasnou kopii, a tím odstraňovala materiál pro obnovu i tehdy,
když dokončení nebylo jisté.
Stav relace dříve ukazoval pouze stav spojení. Indikátor terminálu nedostával žádné měření ICMP ani měření odezvy autentizovaného SSH.
Protokol ukládání
- Pevný pomocný program SSH otevře běžný soubor s jediným pevným odkazem pomocí
operací relativních k deskriptoru a
O_NOFOLLOWpro každou složku cesty. Zaznamená obsah, hash SHA-256, zařízení/inode, UID, GID, všechny bity oprávnění, nanosekundové časy změny obsahu a stavu a všechny čitelné rozšířené atributy. Core ověří hash a dekóduje striktní UTF-8 se zachováním BOM. Každý dokument dostane neprůhlednéeditIdomezené na svůj plugin, relaci terminálu a identitu serveru. - Neprivilegovaná přípravná operace zjistí domovský adresář autentizovaného účtu
přes
pwd.getpwuid(os.getuid()), vytvoří$HOME/ch-j-sm/s režimem0700a vytvoří soukromý výhradní deník obnovy. Nebezpečný domovský adresář, pracovní prostor jako symbolický odkaz nebo pracovní prostor vlastněný jiným účtem je odmítnut. - Core nahraje přes SFTP skrytý náhodný 128bitový pracovní soubor
.tmps výhradním vytvořením (wx), režimem0600a šedesátisekundovým limitem kanálu/nahrávání. Deník obsahuje cíl, původní výchozí stav a očekávaný hash pracovní kopie; neobsahuje žádné přihlašovací údaje. - Celé dokončení provede jediné spuštění přes SSH exec, volitelně přes stávající vykonavatel sudo. Pomocný program bezpečně otevře soukromou pracovní kopii, spočítá její hash a zapíše ji na disk. Uzamkne původní inode, zkontroluje výchozí stav, vytvoří výhradní cílový soubor vedle cíle, zapíše celý obsah, nastaví a ověří metadata, znovu zkontroluje originál a zapíše náhradu na disk.
os.replaceve stejném adresáři atomicky nahradí cíl. Nadřazený adresář se zapíše na disk a ověří se konečná cesta, hash, inode a metadata. Původní cesta zůstává dostupná až do nahrazení. Pracovní soubor se nikdy nepřesouvá mezi souborovými systémy a dokončení jej nikdy neodstraňuje.- Jakmile Core obdrží ověřený úspěch, samostatný úklid znovu zkontroluje konečný
obsah a bezpečnostní metadata a teprve poté odstraní pracovní soubor a deník.
Selhání úklidu stále hlásí potvrzené uložení s
recoveryAvailable: true.
Neúspěšné nahrání, neúspěšné sudo, konflikt, chyba metadat nebo neúspěšné nahrazení zachová originál i veškerý připravený materiál. Neúplná nahrání se jako taková označí a nelze je obnovit jako úplný upravený dokument. Ztracená komunikace SSH, chybějící návratový stav, vypršení časových limitů a neočekávané výsledky dokončení se hlásí jako neznámé, nikdy jako uložené. Core u nejistého dokumentu blokuje další uložení; automatické opakování nahrazení neexistuje.
Metadata, oprávnění a bezpečnostní hranice
Pomocný program zachovává a ověřuje UID, GID a všechny bity oprávnění POSIX, včetně bitů spustitelnosti, setuid, setgid a sticky. Rozšířené atributy Linux reprodukuje bajt po bajtu, včetně POSIX ACL, capabilities a značek SELinux. Vlastnictví a obsah souboru se nastavují před bezpečnostními atributy, protože zápis/chown může vymazat capabilities nebo speciální bity oprávnění. Zděděné atributy cíle, které originál nemá, se odstraní. Čas změny odpovídá novému zápisu a nekopíruje se z originálu.
Nečitelný nebo nepřenositelný atribut operaci přeruší; metadata se nikdy tiše nezahazují. Podpisy IMA/EVM jsou výslovně odmítnuty, protože kopírování podpisů integrity vázaných na obsah/inode by je zneplatnilo. Symbolické odkazy v cílové nebo domovské cestě, soubory s více pevnými odkazy a jiné než běžné soubory jsou odmítnuty. Nepoužívá se žádné přepsání na místě ani slabší náhradní přepsání přes SFTP.
Běžná zapisovatelná uložení běží pod účtem SSH. Chráněná uložení vyžadují výslovné
{ sudo: true }; heslo pro sudo je u sudo bez hesla volitelné. Hesla se
ověřují a předávají pouze přes stdin kanálu, nikdy v příkazu, deníku,
pracovním souboru, logu ani ve vzdálené chybě. Relace SSH jako root používají stávající přímý
vykonavatel root. Neúspěšná autorizace ponechá originál i pracovní kopii beze změny.
Všechna uložení prováděná jako root (přímé SSH jako root nebo sudo) navíc odmítají cílové
adresáře/předky vlastněné jinými účty nebo jimi zapisovatelné či opatřené přístupovým
ACL, místo aby důvěřovala privilegované cestě ovládané jiným účtem.
Pevný pomocný program běží s python3 -I -B: ignoruje uživatelské cesty Pythonu,
pracovní adresář a uživatelské site packages a nezapisuje bytecode. Cesty a argumenty JSON
se v Core uvozují pro shell. Pluginy nikdy nedostávají libovolný SSH exec ani
handly SFTP. Stávající sandbox, CSP, oprávnění manifestu, ověřování klíče hostitele
a ověřování aktualizací zůstávají v platnosti.
Plugin API a zbývající integrace Monaco
Samostatně distribuované UI Monaco/File Manager v tomto checkoutu chybí. Jeho integraci do UI, výzvu sudo, práci s neuloženým bufferem a překlady zde nebylo možné implementovat ani otestovat. Přibaleným zdrojem pluginu je plugin Hash & Checksum. Žádný náhradní zdroj File Manageru nebyl vymyšlen.
Stávající metody zůstávají dostupné. Doplňkové metody/volby zpřístupňuje preload v sandboxu a běhové prostředí s kontrolou oprávnění:
const opened = await chjPlugin.files.readText(sessionId, path);
// For a protected read, explicitly pass { sudo: true, sudoPassword }.
const result = await chjPlugin.files.saveText(sessionId, path, editor.getValue(), {
editId: opened.editId,
sudo: true, // Include only after the user authorizes elevation.
sudoPassword // Omit for passwordless sudo; never persist this value.
});
if (result.ok && result.value.status === "saved") {
// Mark this exact submitted editor version as saved. Later edits stay dirty.
} else {
// Keep the Monaco model dirty. Translate result.error.code/status.
// result.error.recoveryId/recoveryPath identify retained working material.
}
await chjPlugin.files.closeText(opened.editId);
const recovery = await chjPlugin.files.listRecovery(sessionId);
const copy = await chjPlugin.files.readRecovery(sessionId, recoveryId);
// Review copy.status and copy.text; open a fresh document before another save.
await chjPlugin.files.cleanupRecovery(sessionId, recoveryId);
// Cleanup rejects anything whose target is not currently confirmed.
Metody obnovy přijímají volitelný poslední argument { sudo: true, sudoPassword },
pokud kontrola cíle vyžaduje zvýšení oprávnění. Ponechávají pracovní prostor
autentizovaného uživatele i při spuštění přes sudo. Deníky přežijí restart aplikace i nové
připojení SSH; obnova nikdy automaticky neopakuje dokončení. Obnovený
buffer před výslovným uložením porovnejte s nově otevřeným cílem.
files.read chrání čtení, kontrolu a uvolnění handlů. files.write chrání
uložení a úklid obnovy. writeText u stávajících pluginů při selhání nadále odmítá;
saveText vrací strukturovanou obálku { ok, value/error }, protože
Electron u odmítnutých objektů Error zahazuje vlastní vlastnosti. Starší tříargumentové
volání writeText může použít výchozí stav jediného otevřeného dokumentu. Více živých dokumentů
pro stejnou cestu vyžaduje editId; nejednoznačná starší volání bezpečně selžou, místo aby
použila výchozí stav jiného dokumentu. Pluginy by měly nepoužívané handly uvolňovat.
Externí UI musí ukládání zobrazit okamžitě, při selhání ponechat buffery jako neuložené, nabízet sudo až po selhání oprávnění/autentizace, zobrazit konflikty bez přepsání a po ztrátě spojení nebo neznámém výsledku nabídnout obnovu. Tyto stavy musí překládat do češtiny, němčiny a angličtiny. Navržené texty stavů:
| Stav | Čeština | Němčina | Angličtina |
|---|---|---|---|
| Ukládání | Ukládání… | Wird gespeichert… | Saving… |
| Potvrzeno | Uloženo | Gespeichert | Saved |
| Selhání | Uložení selhalo | Speichern fehlgeschlagen | Save failed |
| Vyžadováno sudo | Vyžadováno oprávnění sudo | sudo-Autorisierung erforderlich | Sudo authorization required |
| Konflikt | Původní soubor byl změněn | Originaldatei wurde geändert | Original file changed |
| Odpojeno | Spojení bylo přerušeno | Verbindung unterbrochen | Connection lost |
| Neznámé | Výsledek uložení není znám | Speicherergebnis unbekannt | Save result unknown |
| Obnova | Obnova je dostupná | Wiederherstellung verfügbar | Recovery available |
Definice latence a životní cyklus
Ping je latence hlášená skutečnou odpovědí ICMP echo na straně klienta
na přeloženou IP adresu, kterou používá SSH. Core spouští systémový program ping asynchronně
s pevným polem argumentů, bez shellu a bez nutnosti spouštět aplikaci jako správce.
Argumenty pro Linux, macOS (včetně ping6) a Windows se volí zvlášť.
Blokované ICMP, chybějící program, vypršení času, neplatný výstup nebo odpověď Windows
udávající jen horní mez, například <1ms, vedou k nedostupné hodnotě, nikoli k vymyšlenému číslu.
SSH RTT je monotónní doba performance.now() pevného požadavku exec true
přes stávajícího autentizovaného klienta SSH. Zahrnuje vytvoření kanálu,
zpracování na serveru a doručení odpovědi; nejde o čisté síťové RTT ani o dobu
přihlášení SSH. Přeložená nápověda indikátoru tento rozdíl vysvětluje.
Sledování začíná, jakmile se terminál připojí. Sondy běží souběžně a asynchronně s pětisekundovým limitem požadavku/procesu. Další vzorek se plánuje dvanáct sekund po dokončení předchozího, takže se nepřekrývají. Odpojení přeruší ICMP, vymaže časovače, zruší probíhající práci SSH a vymaže hodnoty. Kontroly identity záznamu brání tomu, aby předchozí spojení publikovalo hodnoty po novém připojení. Selhání ICMP nikdy nemění stav spojení SSH.
Latence se přenáší uvnitř stávající události state / datové části IPC ssh:state:
latency: { pingMs: 24.1, sshRttMs: 31.25, measuredAt: "…" }
Nedostupná čísla jsou null. Kompaktní indikátor terminálu používá milisekundy,
lokalizovaný text pro nedostupnost a popisky v češtině/němčině/angličtině. Po odpojení se skryje.
Nebyla potřeba žádná další schopnost spouštění v rendereru ani požadavek IPC.
Ověření a provozní omezení
- Spusťte z
app/:npm ci, poténpm test. - Konečný místní výsledek: 179 testů; 163 prošlo, 0 selhalo, 16 přeskočeno. Všechna přeskočení
jsou případy souborového systému Linux na tomto hostiteli macOS.
npm ci, kontrola syntaxe Pythonu agit diff --checkproběhly úspěšně. - Testy pokrývají ukládání/obnovu v Core, protokol sudo a skrývání hesel, skutečné uvozování nebezpečných cest pro shell POSIX, konflikty, nahrávání, neúspěšný úklid, nejisté výsledky, souběžné dokumenty, oprávnění IPC a životní cyklus latence.
- Testy souborového systému Linux spouštějí dodávaný vstupní bod v Pythonu se soukromým
testovacím vyhledáním domovského adresáře účtu: vlastnictví/režim/xattrs, POSIX ACL, symbolické/pevné odkazy,
FIFO, selhání nahrávání a metadat, selhání nahrazení, souběhy na cíli, neznámé
výsledky po nahrazení a obnovu. Příprava napříč souborovými systémy používá
/dev/shma další souborový systém, pokud je dostupný. Na jiných hostitelích než Linux se tyto testy přeskočí; chybějící podpora ACL/samostatného souborového systému se výslovně přeskočí. - Přidaná úloha GitHub Actions pro Linux spouští celou sadu a opakuje případy souborového systému jako root. Tento workflow z tohoto pracovního stromu spuštěn nebyl.
- Místní ověření proběhlo na macOS. Syntaxe zdrojů v Pythonu byla zkontrolována přímo
spustitelným souborem Pythonu z Command Line Tools. Skutečná smoke kontrola ICMP na loopbacku v macOS
vrátila
0.071 ms. Místně nebyl testován žádný živý vzdálený server SSH, běhové prostředí Windows ani Linux, nasazení SELinux ani externí UI Monaco. - Vzdálené úpravy textu nyní vyžadují Linux a Python 3 s podporou souborového systému/xattr relativní k deskriptoru. Chybějící interpret selže s výslovnou chybou. Core záměrně odmítá nebezpečná metadata, odkazy a konfigurace adresářů.
- Zamykání je pouze doporučující: nespolupracující procesy mohou stále soupeřit v úzkém intervalu mezi poslední kontrolou výchozího stavu a atomickým přejmenováním. Linux neposkytuje obecnou atomickou operaci porovnání hashe obsahu a nahrazení. Kontroly odhalí změny před tímto intervalem; ponechané pracovní kopie umožňují pozdější obnovu.
- Data pro obnovu zůstávají ve vzdáleném účtu až do potvrzeného úklidu. Neúplné, konfliktní a nekontrolovatelné deníky se nikdy tiše nezahazují. Neexistuje automatická expirace souborů obnovy ani možnost vynuceného přepsání.
npm cihlásil 17 nálezů auditu závislostí (9 středních, 8 vysokých). Tato změna nemění závislosti ani model důvěry aktualizací aplikace.
Uživatel požadoval zachovat změny ve stávajícím pracovním stromu, nyní
feature/hash-checksum-plugin; nedošlo k přepnutí větve ani k nesouvisejícímu úklidu.
Soubory změněné v tomto úkolu
src/main/files/remoteFileService.jssrc/main/files/remoteEditor.py(nový)src/main/sessions/sessionManager.jssrc/main/sessions/latencyMonitor.js(nový)src/main/plugins/pluginRuntime.jssrc/preload/pluginPreload.jssrc/renderer/app.jssrc/renderer/index.htmlsrc/renderer/styles.csssrc/renderer/i18n.jstest/remoteFileService.test.jstest/sessionManager.test.jstest/pluginRuntime.test.jstest/remoteEditor.test.js(nový)test/remoteEditorProtocol.test.js(nový)test/remoteEditorLinux.test.js(nový)test/fixtures/remoteEditorLinux.py(nový)test/latencyMonitor.test.js(nový)README.mddocs/remote-editor-latency.md(tento soubor)../.github/workflows/remote-editor-tests.yml(nový)