From 8e8e236b3046c7124ba41ade594003c98421634a Mon Sep 17 00:00:00 2001 From: RissRIce Date: Tue, 28 Jul 2026 13:22:37 -0600 Subject: [PATCH] fix(auth): tighten stored credential permissions --- src/auth.mjs | 3 ++- test/auth.test.mjs | 13 +++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/auth.mjs b/src/auth.mjs index c86154d..9b862d3 100644 --- a/src/auth.mjs +++ b/src/auth.mjs @@ -20,9 +20,10 @@ const b64url = (buf) => Buffer.from(buf).toString("base64url"); export function loadCreds() { try { return JSON.parse(fs.readFileSync(credsPath, "utf8")); } catch { return null; } } -function saveCreds(creds) { +export function saveCreds(creds) { fs.mkdirSync(CREDS_DIR, { recursive: true }); fs.writeFileSync(credsPath, JSON.stringify(creds, null, 2), { mode: 0o600 }); + fs.chmodSync(credsPath, 0o600); } function openBrowser(url) { diff --git a/test/auth.test.mjs b/test/auth.test.mjs index 86ce4da..a37dfd1 100644 --- a/test/auth.test.mjs +++ b/test/auth.test.mjs @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs"; +import { chmodSync, mkdirSync, mkdtempSync, statSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import test from "node:test"; @@ -15,7 +15,8 @@ writeFileSync( process.env.HOME = home; process.env.USERPROFILE = home; -const { whoami } = await import("../src/auth.mjs"); +const { saveCreds, whoami } = await import("../src/auth.mjs"); +const posixMode = process.platform === "win32" ? { skip: "POSIX permission bits" } : {}; /** Run whoami against a canned app response and collect what it printed. */ async function whoamiAgainst({ status, body }) { @@ -55,3 +56,11 @@ test("whoami still calls out an expired session on 401", async () => { const out = await whoamiAgainst({ status: 401, body: { error: "unauthorized" } }); assert.match(out, /session expired/); }); + +test("saving credentials tightens a world-readable existing file", posixMode, () => { + chmodSync(join(home, ".moshcode", "credentials.json"), 0o644); + + saveCreds({ api: "https://app.example.test", token: "tok_fresh", email: "me@example.test" }); + + assert.equal(statSync(join(home, ".moshcode", "credentials.json")).mode & 0o777, 0o600); +});