Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
IT Security DownloadsGitHub Release: ollama/ollama v0.34.4-rc0 (23.09.2026)(23.09.2026 um 02:53 Uhr)
Sichere ProgrammierungMy fact-checker said CONFIRMED about a group that doesn't exist(23.09.2026 um 02:13 Uhr)
Sichere ProgrammierungZero-Friction Payment Architectures for Global Scalability(23.09.2026 um 02:20 Uhr)
IT Security DownloadsGitHub Release: ollama/ollama v0.34.4-rc0 (23.09.2026)(23.09.2026 um 02:53 Uhr)
Sichere ProgrammierungMy fact-checker said CONFIRMED about a group that doesn't exist(23.09.2026 um 02:13 Uhr)
Sichere ProgrammierungZero-Friction Payment Architectures for Global Scalability(23.09.2026 um 02:20 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

Make Your CLI Config Write Survive Ctrl-C Without Leaving Truncated JSON

A small CLI reads config.json, changes one field, and writes the file back. The code looks harmless: await writeFile(path, JSON.stringify(next, null, 2)); Then the process is interrupted during the write. The next run finds 347…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

A small CLI reads config.json, changes one field, and writes the file back. The code looks harmless:




await writeFile(path, JSON.stringify(next, null, 2));






Then the process is interrupted during the write. The next run finds 347 bytes of what used to be a 2 KB file, JSON parsing fails, and a one-line settings command has become a recovery exercise.



The useful contract is not “the write usually succeeds.” It is:




After an interruption, the config is either the complete previous version or the complete next version—never a partial mixture.




Here is a small Node.js implementation and a way to test its failure path without pulling in a database.






Why direct overwrite is the wrong primitive



Opening an existing file for writing commonly truncates it before replacement bytes are complete. If the process exits, the machine loses power, or the filesystem reports an error, the old valid state is already gone.



A safer sequence is:




serialize fully
-> write sibling temporary file
-> fsync temporary file
-> rename temporary over destination
-> fsync parent directory






Rename on the same filesystem is the commit point. Readers observe the old name before commit and the new file after commit.






The 50-line writer






import { open, rename, rm } from "node:fs/promises";
import { dirname, basename, join } from "node:path";
import { randomUUID } from "node:crypto";

export async function writeJsonAtomic(path, value) {
const directory = dirname(path);
const temporary = join(directory, `.${basename(path)}.${randomUUID()}.tmp`);
const body = `${JSON.stringify(value, null, 2)}\n`;
let handle;

try {
handle = await open(temporary, "wx", 0o600);
await handle.writeFile(body, "utf8");
await handle.sync();
await handle.close();
handle = undefined;

await rename(temporary, path);

const directoryHandle = await open(directory, "r");
try {
await directoryHandle.sync();
} finally {
await directoryHandle.close();
}
} catch (error) {
if (handle) await handle.close().catch(() => {});
await rm(temporary, { force: true }).catch(() => {});
throw error;
}
}






The temporary file is created beside the destination so rename() does not cross filesystems. wx prevents accidental reuse. Mode 0600 avoids briefly creating a world-readable file for configs that may contain secrets.



The directory sync matters for durability across sudden power loss on filesystems where the directory entry can lag behind file data. Some platforms or filesystems do not support directory fsync; decide whether your CLI requires crash durability or only process-interruption safety, then test on supported targets instead of swallowing every error.






Do not mutate the in-memory object first



Separate parsing, validation, and commit:




const current = JSON.parse(await readFile(configPath, "utf8"));
const candidate = { ...current, output: requestedOutput };
validateConfig(candidate);
await writeJsonAtomic(configPath, candidate);






If validation fails, no write starts. If serialization fails, no temporary file is committed. If rename fails, the old destination remains.



Also reject non-finite numbers or values your schema cannot round-trip. JSON silently turns NaN and Infinity into null, which is atomic corruption: complete, durable, and wrong.






Test the interruption window



Unit tests that mock writeFile only prove call order. A stronger fixture starts a child process, pauses it after the temporary file is durable, kills it, and verifies that the destination is still valid.



Add a test-only synchronization hook:




if (process.env.ATOMIC_WRITE_PAUSE === "after-sync") {
process.stdout.write("PAUSED\n");
await new Promise(() => {});
}






Place it after handle.sync() and before rename(). The parent test can then:




  1. Write a known old config.

  2. Spawn the update command with the hook enabled.

  3. Wait for PAUSED.

  4. Send SIGKILL (or terminate the process using the platform's supported mechanism).

  5. Parse the destination and confirm it equals the old config.

  6. Run normally and confirm it equals the new config.

  7. Clean stale .*.tmp files created by the test.



A second hook after rename tests a different state: the destination must parse as the complete new config, even if cleanup or directory sync did not finish.



Expected evidence:




kill-before-rename: destination=old, parse=ok
kill-after-rename: destination=new, parse=ok
normal-run: destination=new, parse=ok









Concurrency is a separate bug



Atomic replacement prevents torn files. It does not prevent lost updates:




process A reads version 7
process B reads version 7
A writes output=table
B writes color=false
A's change disappears






For a single-user CLI, a lock file with owner PID, creation time, bounded wait, and stale-lock recovery may be enough. Another option is optimistic concurrency: store a revision, re-read immediately before commit, and refuse if it changed.



Do not claim the atomic writer solves concurrency. It solves one narrower and valuable failure: incomplete replacement.






Recovery policy



Temporary files can remain after a hard kill. On startup, do not automatically promote the newest temp file. Its presence does not prove the user intended to commit it, and wall-clock ordering can lie.



A conservative policy is:




  • keep the named destination authoritative;

  • validate it before use;

  • remove stale temp files only when no writer lock is active;

  • show a repair command if the destination itself is invalid;

  • never print secret config contents in the error.






Exit criteria



This pattern is worth keeping when the CLI owns a small local state file and needs zero extra services. Move to SQLite or another transactional store when updates involve multiple records, cross-process coordination, queries, or migrations. A clever file protocol is not a tiny database replacement.



The implementation is small, but the important part is the failure fixture. If the test cannot kill the writer on both sides of the commit point, “atomic” is still an assumption.

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Make Your CLI Config Write Survive Ctrl-C Without Leaving Truncated JSON

Thematisch verwandte Begriffe: Make, Your, Config, Write · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-17636 | IBM Financial Transaction Manager (FTM) for RedHat OpenShift could allow…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick