mirror of https://github.com/garrytan/gstack.git
fix(security): wire appendSecureFile at the four real log-append sites
file-permissions.ts carries a 24-line rationale for why POSIX mode bits are
insufficient on Windows and implements appendSecureFile (0600 at create,
Windows ACL on first write only) — but its single caller was the dead
logAttempt, while the four REAL page-content log writers (console/network/
dialog logs in server.ts, the command audit log) used raw fs.appendFileSync
with no mode. Page-content-derived logs now get owner-only permissions from
birth on every platform.
Verified before wiring: mode applies atomically at create via appendFileSync
{mode}, and the ACL pass runs only on first write — no per-append subprocess
cost on the hot console-log path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
5099637f95
commit
dd66e4cda2
|
|
@ -13,7 +13,7 @@
|
|||
* All writes are best-effort — audit failures never cause command failures.
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import { appendSecureFile } from './file-permissions';
|
||||
|
||||
export interface AuditEntry {
|
||||
ts: string;
|
||||
|
|
@ -62,7 +62,7 @@ export function writeAuditEntry(entry: AuditEntry): void {
|
|||
if (entry.aliasOf) record.aliasOf = entry.aliasOf;
|
||||
if (truncatedError) record.error = truncatedError;
|
||||
|
||||
fs.appendFileSync(auditPath, JSON.stringify(record) + '\n');
|
||||
appendSecureFile(auditPath, JSON.stringify(record) + '\n');
|
||||
} catch {
|
||||
// Audit write failures are silent — never block command execution
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import {
|
|||
} from './content-security';
|
||||
import { getStatus as getSecurityStatus } from './security';
|
||||
import { isSidecarAvailable, scanWithSidecar } from './security-sidecar-client';
|
||||
import { writeSecureFile, mkdirSecure } from './file-permissions';
|
||||
import { writeSecureFile, mkdirSecure, appendSecureFile } from './file-permissions';
|
||||
import { handleSnapshot, SNAPSHOT_FLAGS } from './snapshot';
|
||||
import {
|
||||
initRegistry, validateToken as validateScopedToken, checkScope, checkDomain,
|
||||
|
|
@ -586,7 +586,7 @@ async function flushBuffers() {
|
|||
const lines = entries.map(e =>
|
||||
`[${new Date(e.timestamp).toISOString()}] [${e.level}] ${e.text}`
|
||||
).join('\n') + '\n';
|
||||
fs.appendFileSync(CONSOLE_LOG_PATH, lines);
|
||||
appendSecureFile(CONSOLE_LOG_PATH, lines);
|
||||
lastConsoleFlushed = consoleBuffer.totalAdded;
|
||||
}
|
||||
|
||||
|
|
@ -597,7 +597,7 @@ async function flushBuffers() {
|
|||
const lines = entries.map(e =>
|
||||
`[${new Date(e.timestamp).toISOString()}] ${e.method} ${e.url} → ${e.status || 'pending'} (${e.duration || '?'}ms, ${e.size || '?'}B)`
|
||||
).join('\n') + '\n';
|
||||
fs.appendFileSync(NETWORK_LOG_PATH, lines);
|
||||
appendSecureFile(NETWORK_LOG_PATH, lines);
|
||||
lastNetworkFlushed = networkBuffer.totalAdded;
|
||||
}
|
||||
|
||||
|
|
@ -608,7 +608,7 @@ async function flushBuffers() {
|
|||
const lines = entries.map(e =>
|
||||
`[${new Date(e.timestamp).toISOString()}] [${e.type}] "${e.message}" → ${e.action}${e.response ? ` "${e.response}"` : ''}`
|
||||
).join('\n') + '\n';
|
||||
fs.appendFileSync(DIALOG_LOG_PATH, lines);
|
||||
appendSecureFile(DIALOG_LOG_PATH, lines);
|
||||
lastDialogFlushed = dialogBuffer.totalAdded;
|
||||
}
|
||||
} catch (err: any) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue