/**
 * auditLog.ts — Security audit trail for tool execution.
 *
 * Logs all security-relevant events (path checks, command execution,
 * URL validation, credential redactions) to SQLite.
 */
import * as path from "node:path";
import * as os from "node:os";
import * as fs from "node:fs";
import { getSecurityConfig } from "./config.js";
// In-memory buffer for batch writes
let _buffer = [];
let _flushTimer = null;
let _db = null;
let _initialized = false;
const DB_PATH = path.join(os.homedir(), ".nodebench", "security_audit.db");
function genId() {
    return `audit_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
}
function getDb() {
    if (_db)
        return _db;
    try {
        // Try to use better-sqlite3 if available
        const Database = require("better-sqlite3");
        const dir = path.dirname(DB_PATH);
        if (!fs.existsSync(dir))
            fs.mkdirSync(dir, { recursive: true });
        _db = new Database(DB_PATH);
        _db.pragma("journal_mode = WAL");
        _db.exec(`
      CREATE TABLE IF NOT EXISTS audit_log (
        id         TEXT PRIMARY KEY,
        timestamp  TEXT NOT NULL,
        category   TEXT NOT NULL,
        tool_name  TEXT NOT NULL,
        args_preview TEXT NOT NULL DEFAULT '',
        allowed    INTEGER NOT NULL DEFAULT 1,
        reason     TEXT,
        metadata   TEXT
      );
      CREATE INDEX IF NOT EXISTS idx_audit_category ON audit_log(category);
      CREATE INDEX IF NOT EXISTS idx_audit_tool ON audit_log(tool_name);
      CREATE INDEX IF NOT EXISTS idx_audit_timestamp ON audit_log(timestamp);
    `);
        // Auto-prune entries older than 30 days
        _db
            .prepare("DELETE FROM audit_log WHERE timestamp < datetime('now', '-30 days')")
            .run();
        _initialized = true;
        return _db;
    }
    catch {
        // better-sqlite3 not available — use in-memory only
        _initialized = false;
        return null;
    }
}
function flushBuffer() {
    if (_buffer.length === 0)
        return;
    const db = getDb();
    if (!db) {
        // No SQLite — just discard (entries were already returned from auditLog)
        _buffer = [];
        return;
    }
    const insert = db.prepare(`
    INSERT OR IGNORE INTO audit_log (id, timestamp, category, tool_name, args_preview, allowed, reason, metadata)
    VALUES (?, ?, ?, ?, ?, ?, ?, ?)
  `);
    const insertMany = db.transaction((entries) => {
        for (const e of entries) {
            insert.run(e.id, e.timestamp, e.category, e.toolName, e.argsPreview, e.allowed ? 1 : 0, e.reason ?? null, e.metadata ? JSON.stringify(e.metadata) : null);
        }
    });
    try {
        insertMany(_buffer);
    }
    catch {
        // SQLite write failed — discard silently
    }
    _buffer = [];
}
/**
 * Log a security audit event.
 *
 * Non-blocking — buffers writes and flushes every 100ms.
 */
export function auditLog(category, toolName, argsPreview, allowed, reason, metadata) {
    const config = getSecurityConfig();
    if (!config.auditEnabled)
        return;
    const entry = {
        id: genId(),
        timestamp: new Date().toISOString(),
        category,
        toolName,
        argsPreview: argsPreview.substring(0, 200),
        allowed,
        reason,
        metadata,
    };
    _buffer.push(entry);
    // Batch flush every 100ms
    if (!_flushTimer) {
        _flushTimer = setTimeout(() => {
            flushBuffer();
            _flushTimer = null;
        }, 100);
    }
}
/**
 * Query the audit log.
 */
export function getAuditLog(opts) {
    // Flush pending writes first
    flushBuffer();
    const db = getDb();
    if (!db)
        return [];
    const conditions = [];
    const params = [];
    if (opts?.category) {
        conditions.push("category = ?");
        params.push(opts.category);
    }
    if (opts?.toolName) {
        conditions.push("tool_name = ?");
        params.push(opts.toolName);
    }
    if (opts?.since) {
        conditions.push("timestamp >= ?");
        params.push(opts.since);
    }
    if (opts?.onlyBlocked) {
        conditions.push("allowed = 0");
    }
    const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
    const limit = opts?.limit ?? 100;
    try {
        const rows = db
            .prepare(`SELECT * FROM audit_log ${where} ORDER BY timestamp DESC LIMIT ?`)
            .all(...params, limit);
        return rows.map((r) => ({
            id: r.id,
            timestamp: r.timestamp,
            category: r.category,
            toolName: r.tool_name,
            argsPreview: r.args_preview,
            allowed: r.allowed === 1,
            reason: r.reason,
            metadata: r.metadata ? JSON.parse(r.metadata) : undefined,
        }));
    }
    catch {
        return [];
    }
}
/** Force flush — use before process exit */
export function flushAuditLog() {
    if (_flushTimer) {
        clearTimeout(_flushTimer);
        _flushTimer = null;
    }
    flushBuffer();
}
/** Test helper */
export function _resetAuditForTesting() {
    _buffer = [];
    if (_flushTimer) {
        clearTimeout(_flushTimer);
        _flushTimer = null;
    }
    _db = null;
    _initialized = false;
}
//# sourceMappingURL=auditLog.js.map