Everything you need to install, configure, and operate logSguarDian — plus the pipeline that decides every verdict.
npm install logsguardian
npx logsguardian config init
This writes logsguardian.config.js in the current directory. Mount the middleware after your body parsers — express.urlencoded, express.json, and any multer instance if your app accepts file uploads.
const express = require('express');
const { logsguardian } = require('logsguardian');
const config = require('./logsguardian.config.js');
const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(logsguardian(config));
Every request past that point is inspected. Attacks get an HTTP 403; everything else is forwarded unchanged. Requires Node.js ≥ 20 and Express 4 or 5.
Docker note: onnxruntime-node requires glibc. Alpine images (musl) silently fail to load the native binary, and the worker-spawn try/catch swallows the error — meaning 100% silent fail-open with no visible warning. Use node:20-slim, not -alpine.
Feature extraction happens once, inside worker threads — not on the main thread — and produces a 73-feature vector. RF uses 67 of them; IF uses 61 (6 fewer: features confirmed zero/near-zero variance on benign traffic). 6 features are excluded from both models entirely: status_code (unknown at intercept time) and five temporal features that need cross-request state the extractor doesn't keep.
rf_classes = ['benign', 'cmdi', 'path_traversal', 'sqli', 'xss']
RF_THRESHOLD = 0.35
IF_THRESHOLD = 0.002486040118540811 // if_v9
predicted_class = rf_classes[argmax(rf_probs)]
confidence = max(rf_probs)
is_attack = predicted_class != 'benign'
is_anomaly = if_score < IF_THRESHOLD
if is_attack AND confidence >= RF_THRESHOLD: verdict = 'block'
else if is_anomaly: verdict = 'pass_anomaly'
else: verdict = 'pass'
Bodies and query strings are serialized with URLSearchParams, not JSON.stringify — chosen specifically to avoid introducing structural characters ({ } : ") that caused false positives on plain form POSTs. A JSON-encoded login body once scored xss @ 0.40; the same body URL-encoded scored benign @ 0.90.
logsguardian.config.jsmodule.exports = {
mode: 'block', // 'block' (403 on attacks) or 'monitor' (log only, never blocks)
threshold: 0.35, // RF confidence above which a request is blocked
model: 'hybrid', // 'rf' (blocking only), 'if' (anomaly logging only), or 'hybrid'
timeoutMs: 50, // fail-open timeout — if RF doesn't answer in time, request passes
dbPath: './logsguardian.db', // SQLite event log + webhook registry
webhookUrl: undefined, // optional static webhook, called on block/anomaly
};
| Field | Type | Default | Notes |
|---|---|---|---|
| mode | 'block' | 'monitor' | 'block' | monitor is a dark-launch mode — logs what would have blocked, never blocks |
| threshold | number 0–1 | 0.35 | lower = more attacks caught, more false positives |
| model | 'rf' | 'if' | 'hybrid' | 'hybrid' | which model(s) run |
| timeoutMs | number | 50 | RF only — IF is never on the blocking path |
| dbPath | string | ./logsguardian.db | use :memory: in tests |
| webhookUrl | string | unset | single static webhook; HTTPS not enforced here (unlike CLI-registered ones) |
Start in mode: 'monitor' to see what logSguarDian would have blocked before turning on enforcement.
logsguardian --help14 subcommands across four groups. All except config init require a logsguardian.config.js in the current directory.
| Group | Commands |
|---|---|
config | init, show [--format table|json], set <key> <value>, validate |
attacks | list, summary [--from] [--to] [--endpoint], inspect <type> — sqli, xss, path_traversal, or cmdi |
endpoints | top [--limit], profile <route> [--method], report [--format json|csv] [--output] |
webhooks | add <url> (HTTPS required), remove <id>, list, test <id> — no restart needed |
{ "error": "Forbidden", "class": "sqli" }
timestamp, method, path, query_string, user_agent, client_ip, verdict, predicted_class, confidence, if_score, is_anomaly, webhook_sent, elapsed_ms
Webhooks fire a fire-and-forget POST of the detection event JSON, 3s timeout, and never affect the response on failure. A verdict can flip retroactively: a pass becomes pass_anomaly once IF's async result lands, firing a webhook at that point if one is configured.