mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-16 20:02:45 +03:00
fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 perms (#12572) (#13242)
Merged as part of the 39-PR owner batch of 2026-09-11, validated as a unit. Boarded into one consolidated worktree cut from `release/v3.8.51` with the other 38 — zero conflicts between them. - ESLint over every changed file: no errors (the only finding was one suppression entry the batch emptied, pruned on #13243) - `typecheck:core` clean; `check:dashboard-typecheck` OK (206 pre-existing, within baseline); `check:changelog-integrity` OK - complexity 2821 / baseline 3218 and cognitive-complexity 1272 / baseline 1437 — both under baseline - 256 assertions green: 246 under node:test and 10 under vitest, which is where `tests/unit/**/*.test.tsx` actually runs - `check-file-size`: `chatCore.ts` rebaselined 6144 → 6146 for #13278 and #13276, annotated and landed on #13243 ⚠️ base-red inherited: #12732 — the provider count (356 in the docs vs the 358 the modules define) and `open-sse/utils/stream.ts` at 3115 > frozen 3098 both reproduce on the pure tip with zero contribution from this batch.
This commit is contained in:
committed by
GitHub
parent
bc28bb06b1
commit
d0b22ddf86
@@ -0,0 +1 @@
|
||||
- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572)
|
||||
@@ -14,10 +14,11 @@
|
||||
*/
|
||||
import { spawn, type ChildProcess } from "node:child_process";
|
||||
import { createHash } from "node:crypto";
|
||||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
||||
import http from "node:http";
|
||||
import { createServer } from "node:net";
|
||||
import { join } from "node:path";
|
||||
import { ensureSecureDir, writeSecureFile } from "../utils/secureFileWrite.ts";
|
||||
import {
|
||||
decodeAdobeJwtPayload,
|
||||
isAdobeUserAccessToken,
|
||||
@@ -410,7 +411,7 @@ export function filterAdobeBrowserCookies(cookies: CdpCookie[]): AdobeBrowserCoo
|
||||
|
||||
function adobeBrowserCookieJarPath(sessionKey: string): string {
|
||||
const dir = join(resolveAdobeFireflyDataRoot(), "adobe-browser-sessions");
|
||||
mkdirSync(dir, { recursive: true });
|
||||
ensureSecureDir(dir);
|
||||
return join(dir, `${adobeFireflyBrowserSessionKey(sessionKey)}.json`);
|
||||
}
|
||||
|
||||
@@ -427,10 +428,9 @@ function loadAdobeBrowserCookies(sessionKey: string): AdobeBrowserCookie[] {
|
||||
|
||||
function saveAdobeBrowserCookies(sessionKey: string, cookies: CdpCookie[]): void {
|
||||
try {
|
||||
writeFileSync(
|
||||
writeSecureFile(
|
||||
adobeBrowserCookieJarPath(sessionKey),
|
||||
JSON.stringify(filterAdobeBrowserCookies(cookies)),
|
||||
"utf8"
|
||||
JSON.stringify(filterAdobeBrowserCookies(cookies))
|
||||
);
|
||||
} catch {
|
||||
// Best-effort: login still returns the portable JWT + Firefly risk cookies.
|
||||
|
||||
@@ -13,8 +13,9 @@
|
||||
*/
|
||||
|
||||
import { createHash, randomUUID } from "node:crypto";
|
||||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { ensureSecureDir, writeSecureFile } from "../utils/secureFileWrite.ts";
|
||||
import {
|
||||
AdobeFireflyError,
|
||||
buildAdobeArpSessionId,
|
||||
@@ -147,7 +148,7 @@ function dataDir(): string {
|
||||
function sessionFilePath(fingerprint: string): string {
|
||||
const dir = join(dataDir(), SESSION_DIR_NAME);
|
||||
try {
|
||||
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
||||
ensureSecureDir(dir);
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
@@ -232,7 +233,7 @@ export function markAdobeFireflyArpSuccess(fingerprint: string, arpSessionId: st
|
||||
const obj = JSON.parse(readFileSync(path, "utf8")) as AdobeFireflySession;
|
||||
obj.arpSessionId = arp;
|
||||
obj.updatedAt = Date.now();
|
||||
writeFileSync(path, JSON.stringify(obj, null, 2), "utf8");
|
||||
writeSecureFile(path, JSON.stringify(obj, null, 2));
|
||||
sessionCache.set(fp, { ...obj, fingerprint: fp });
|
||||
}
|
||||
} catch {
|
||||
@@ -455,7 +456,7 @@ function saveDiskSession(session: AdobeFireflySession): void {
|
||||
if (!diskSessionsEnabled()) return;
|
||||
try {
|
||||
const path = sessionFilePath(session.fingerprint);
|
||||
writeFileSync(path, JSON.stringify(session, null, 2), "utf8");
|
||||
writeSecureFile(path, JSON.stringify(session, null, 2));
|
||||
} catch {
|
||||
/* best-effort */
|
||||
}
|
||||
|
||||
25
open-sse/utils/secureFileWrite.ts
Normal file
25
open-sse/utils/secureFileWrite.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Shared helpers for persisting credential/session material (tokens, cookie jars) to disk
|
||||
* with restrictive permissions — 0700 directories, 0600 files — instead of inheriting the
|
||||
* process umask (typically 0755/0644).
|
||||
*
|
||||
* Mirrors the established pattern in src/lib/vncSession/service.ts::createProfileDir.
|
||||
* `chmodSync` is applied even on an already-existing directory so a dir created before this
|
||||
* hardening (or by any looser writer) is tightened rather than silently trusted.
|
||||
*/
|
||||
|
||||
import { chmodSync, mkdirSync, writeFileSync } from "node:fs";
|
||||
|
||||
const SECURE_DIR_MODE = 0o700;
|
||||
const SECURE_FILE_MODE = 0o600;
|
||||
|
||||
/** Create `dir` (recursively) with 0700 permissions, tightening it if it already exists. */
|
||||
export function ensureSecureDir(dir: string): void {
|
||||
mkdirSync(dir, { recursive: true, mode: SECURE_DIR_MODE });
|
||||
chmodSync(dir, SECURE_DIR_MODE);
|
||||
}
|
||||
|
||||
/** Write `data` to `path` as utf8 with 0600 permissions. */
|
||||
export function writeSecureFile(path: string, data: string): void {
|
||||
writeFileSync(path, data, { encoding: "utf8", mode: SECURE_FILE_MODE });
|
||||
}
|
||||
73
tests/unit/adobe-firefly-session-file-perms.test.ts
Normal file
73
tests/unit/adobe-firefly-session-file-perms.test.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import { chmodSync, mkdirSync, mkdtempSync, rmSync, statSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
// Point DATA_DIR at a throwaway tmp dir BEFORE importing the module under test, since
|
||||
// adobeFireflySession.ts reads process.env.DATA_DIR lazily via dataDir().
|
||||
const probeDataDir = mkdtempSync(join(tmpdir(), "adobe-firefly-perm-"));
|
||||
process.env.DATA_DIR = probeDataDir;
|
||||
|
||||
test.after(() => {
|
||||
try {
|
||||
rmSync(probeDataDir, { recursive: true, force: true });
|
||||
} catch {
|
||||
/* best-effort cleanup */
|
||||
}
|
||||
});
|
||||
|
||||
test("Adobe Firefly session dir/file are created with restrictive permissions (0700/0600)", async () => {
|
||||
const { markAdobeFireflyArpSuccess, fingerprintAdobeCredential } = await import(
|
||||
"../../open-sse/services/adobeFireflySession.ts"
|
||||
);
|
||||
|
||||
const fp = fingerprintAdobeCredential("probe-credential-blob");
|
||||
|
||||
// Triggers sessionFilePath() -> ensureSecureDir(dir) with no cached session and no
|
||||
// pre-existing file on disk (mirrors first-touch creation of the adobe-firefly-sessions
|
||||
// directory in production).
|
||||
markAdobeFireflyArpSuccess(fp, "arp-probe-1");
|
||||
|
||||
const sessionDir = join(probeDataDir, "adobe-firefly-sessions");
|
||||
const dirMode = statSync(sessionDir).mode & 0o777;
|
||||
|
||||
assert.equal(
|
||||
dirMode & 0o077,
|
||||
0,
|
||||
`expected adobe-firefly-sessions dir to be 0700 (no group/other access), got mode ${dirMode.toString(8)}`
|
||||
);
|
||||
});
|
||||
|
||||
test("ensureSecureDir tightens an already-existing looser directory to 0700", async () => {
|
||||
const { ensureSecureDir } = await import("../../open-sse/utils/secureFileWrite.ts");
|
||||
const dir = join(probeDataDir, "already-loose-dir");
|
||||
mkdirSync(dir, { recursive: true, mode: 0o777 });
|
||||
chmodSync(dir, 0o777);
|
||||
|
||||
ensureSecureDir(dir);
|
||||
|
||||
const dirMode = statSync(dir).mode & 0o777;
|
||||
assert.equal(
|
||||
dirMode & 0o077,
|
||||
0,
|
||||
`expected already-loose-dir to be tightened to 0700, got mode ${dirMode.toString(8)}`
|
||||
);
|
||||
});
|
||||
|
||||
test("writeSecureFile writes files with 0600 permissions", async () => {
|
||||
const { writeSecureFile, ensureSecureDir } = await import(
|
||||
"../../open-sse/utils/secureFileWrite.ts"
|
||||
);
|
||||
const dir = join(probeDataDir, "secure-file-write-probe");
|
||||
ensureSecureDir(dir);
|
||||
const filePath = join(dir, "probe.json");
|
||||
writeSecureFile(filePath, JSON.stringify({ hello: "world" }));
|
||||
|
||||
const fileMode = statSync(filePath).mode & 0o777;
|
||||
assert.equal(
|
||||
fileMode & 0o177,
|
||||
0,
|
||||
`expected probe.json to be 0600 (no group/other access), got mode ${fileMode.toString(8)}`
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user