fix: harden logging, memory sync, config merge, and input validation
- Redact sensitive text in file log transport before writing to disk - Validate Nextcloud Talk room tokens and message IDs against path traversal - Propagate real errors from ensureDir instead of silently swallowing - Use Promise.allSettled in memory sync so one bad file does not abort indexing - Block __proto__/constructor/prototype keys in deepMerge (config includes + voice-call TTS)
This commit is contained in:
parent
19823c5498
commit
d5be6aa3f4
@ -48,6 +48,9 @@ function normalizeRoomToken(to: string): string {
|
||||
}
|
||||
|
||||
if (!normalized) throw new Error("Room token is required for Nextcloud Talk sends");
|
||||
if (!/^[a-zA-Z0-9_-]+$/.test(normalized)) {
|
||||
throw new Error(`Invalid room token: contains disallowed characters`);
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
@ -177,6 +180,9 @@ export async function sendReactionNextcloudTalk(
|
||||
account,
|
||||
);
|
||||
const normalizedToken = normalizeRoomToken(roomToken);
|
||||
if (!/^[a-zA-Z0-9_-]+$/.test(messageId)) {
|
||||
throw new Error("Invalid message ID: contains disallowed characters");
|
||||
}
|
||||
|
||||
const body = JSON.stringify({ reaction });
|
||||
const { random, signature } = generateNextcloudTalkSignature({
|
||||
|
||||
@ -80,6 +80,7 @@ function deepMerge<T>(base: T, override: T): T {
|
||||
const result: Record<string, unknown> = { ...base };
|
||||
for (const [key, value] of Object.entries(override)) {
|
||||
if (value === undefined) continue;
|
||||
if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
|
||||
const existing = (base as Record<string, unknown>)[key];
|
||||
if (isPlainObject(existing) && isPlainObject(value)) {
|
||||
result[key] = deepMerge(existing, value);
|
||||
|
||||
@ -70,6 +70,7 @@ export function deepMerge(target: unknown, source: unknown): unknown {
|
||||
if (isPlainObject(target) && isPlainObject(source)) {
|
||||
const result: Record<string, unknown> = { ...target };
|
||||
for (const key of Object.keys(source)) {
|
||||
if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
|
||||
result[key] = key in result ? deepMerge(result[key], source[key]) : source[key];
|
||||
}
|
||||
return result;
|
||||
|
||||
@ -8,6 +8,7 @@ import type { MoltbotConfig } from "../config/types.js";
|
||||
import type { ConsoleStyle } from "./console.js";
|
||||
import { type LogLevel, levelToMinLevel, normalizeLogLevel } from "./levels.js";
|
||||
import { readLoggingConfig } from "./config.js";
|
||||
import { redactSensitiveText } from "./redact.js";
|
||||
import { loggingState } from "./state.js";
|
||||
|
||||
// Pin to /tmp so mac Debug UI and docs match; os.tmpdir() can be a per-user
|
||||
@ -96,7 +97,8 @@ function buildLogger(settings: ResolvedSettings): TsLogger<LogObj> {
|
||||
logger.attachTransport((logObj: LogObj) => {
|
||||
try {
|
||||
const time = logObj.date?.toISOString?.() ?? new Date().toISOString();
|
||||
const line = JSON.stringify({ ...logObj, time });
|
||||
const raw = JSON.stringify({ ...logObj, time });
|
||||
const line = redactSensitiveText(raw);
|
||||
fs.appendFileSync(settings.file, `${line}\n`, { encoding: "utf8" });
|
||||
} catch {
|
||||
// never block on logging failures
|
||||
|
||||
@ -19,9 +19,7 @@ export type MemoryChunk = {
|
||||
};
|
||||
|
||||
export function ensureDir(dir: string): string {
|
||||
try {
|
||||
fsSync.mkdirSync(dir, { recursive: true });
|
||||
} catch {}
|
||||
fsSync.mkdirSync(dir, { recursive: true });
|
||||
return dir;
|
||||
}
|
||||
|
||||
|
||||
@ -976,9 +976,17 @@ export class MemoryIndexManager {
|
||||
progress?: MemorySyncProgressState;
|
||||
}) {
|
||||
const files = await listMemoryFiles(this.workspaceDir);
|
||||
const fileEntries = await Promise.all(
|
||||
const settled = await Promise.allSettled(
|
||||
files.map(async (file) => buildFileEntry(file, this.workspaceDir)),
|
||||
);
|
||||
const fileEntries = settled.flatMap((result, i) => {
|
||||
if (result.status === "fulfilled") return [result.value];
|
||||
log.warn("memory sync: skipping unreadable file", {
|
||||
file: files[i],
|
||||
error: String(result.reason),
|
||||
});
|
||||
return [];
|
||||
});
|
||||
log.debug("memory sync: indexing memory files", {
|
||||
files: fileEntries.length,
|
||||
needsFullReindex: params.needsFullReindex,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user