guardian-risk-logger 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,9 +6,7 @@
6
6
  npm install guardian-risk guardian-risk-logger
7
7
  ```
8
8
 
9
- > **Stub package** API may change before `1.0.0`.
10
-
11
- Audit logging for [guardian-risk](https://www.npmjs.com/package/guardian-risk). Records risk reports, matched rules, and scores for compliance and debugging.
9
+ Audit logging for [guardian-risk](https://www.npmjs.com/package/guardian-risk) analysis results.
12
10
 
13
11
  ## What gets logged
14
12
 
@@ -16,43 +14,34 @@ Audit logging for [guardian-risk](https://www.npmjs.com/package/guardian-risk).
16
14
  |-------|--------|
17
15
  | `score` | `report.score` |
18
16
  | `riskLevel` | `report.level` |
19
- | `matchedRules` | Count and details from `report.matchedRules` |
17
+ | `matchedRules` | Rule names from `report.matchedRules` |
20
18
  | `reasons` | `report.reasons` |
21
- | `analyzedAt` | `report.analyzedAt` |
19
+ | `context` | Sanitized request metadata (method, path, IP only) |
22
20
 
23
- ## Usage (stub)
21
+ ## Production usage
24
22
 
25
23
  ```typescript
26
24
  import { Guardian } from 'guardian-risk';
27
- import { loggerPlugin, analyzeAndLog } from 'guardian-risk-logger';
28
- import { vpnPlugin } from 'guardian-risk-vpn';
25
+ import { loggerPlugin } from 'guardian-risk-logger';
29
26
 
30
- const guardian = new Guardian()
27
+ const template = new Guardian()
31
28
  .use(loggerPlugin({ level: 'info', minScore: 20 }))
32
- .use(vpnPlugin());
33
-
34
- guardian.signal('vpn', true);
29
+ .rule({ name: 'Bot', when: (s) => s.headlessUA === true, score: 30 });
35
30
 
36
- const report = analyzeAndLog(guardian, { minScore: 0 });
31
+ // Middleware runs analyzeAsync afterAnalyze logs automatically
37
32
  ```
38
33
 
39
- ## Custom sink
34
+ ## Security notes
40
35
 
41
- ```typescript
42
- import { loggerPlugin, logReport, type LogSink } from 'guardian-risk-logger';
43
-
44
- const sink: LogSink = {
45
- write(entry) {
46
- // Send to Datadog, CloudWatch, file, etc.
47
- myAuditService.record(entry);
48
- },
49
- };
50
-
51
- const guardian = new Guardian().use(loggerPlugin({ sink }));
52
- const report = guardian.analyze();
53
- logReport(report, { sink });
54
- ```
36
+ - Request **headers are redacted** from log context — only method, path, and validated IP are kept.
37
+ - Do not log raw cookies, auth tokens, or PII in custom sinks.
38
+ - Use `minScore` to reduce noise in high-traffic apps.
39
+
40
+ ## API
55
41
 
56
- ## Status
42
+ - `loggerPlugin(options)` — `afterAnalyze` auto-logging
43
+ - `analyzeAndLog(guardian, options)` — one-shot analyze + log
44
+ - `logReport(report, options)` — log an existing report
45
+ - `LogSink` — custom destination (Datadog, CloudWatch, etc.)
57
46
 
58
- Not yet published. Implementation in progress.
47
+ See [SECURITY.md](../../SECURITY.md).
package/dist/index.cjs CHANGED
@@ -15,37 +15,74 @@ var defaultSink = {
15
15
  score: entry.report.score,
16
16
  riskLevel: entry.report.level,
17
17
  matchedRules: entry.report.matchedRules.length,
18
- timestamp: entry.timestamp
18
+ timestamp: entry.timestamp,
19
+ ...entry.context !== void 0 ? { context: entry.context } : {}
19
20
  });
20
21
  console.log(`[guardian-risk] ${payload}`);
21
22
  }
22
23
  };
23
24
  function loggerPlugin(options = {}) {
24
- const { level = "info", sink = defaultSink, minScore = 0 } = options;
25
+ const resolved = {
26
+ level: "info",
27
+ sink: defaultSink,
28
+ minScore: 0,
29
+ includeContext: true,
30
+ ...options
31
+ };
25
32
  return {
26
33
  name: "guardian-risk-logger",
27
- install(_guardian) {
34
+ install(guardian) {
35
+ guardian.afterAnalyze(({ report, data }) => {
36
+ logReport(report, {
37
+ ...resolved,
38
+ context: resolved.includeContext ? sanitizeLogContext(data) : void 0
39
+ });
40
+ });
28
41
  }
29
42
  };
30
43
  }
31
44
  function logReport(report, options = {}) {
32
- const { level = "info", sink = defaultSink, minScore = 0 } = options;
45
+ const {
46
+ level = "info",
47
+ sink = defaultSink,
48
+ minScore = 0,
49
+ context,
50
+ includeContext = true
51
+ } = options;
33
52
  if (report.score < minScore) {
34
53
  return;
35
54
  }
36
55
  const entryLevel = resolveLogLevel(report, level);
56
+ if (!shouldLog(level, entryLevel)) {
57
+ return;
58
+ }
37
59
  sink.write({
38
60
  level: entryLevel,
39
- message: `Risk analysis complete: ${report.level} (${report.score})`,
61
+ message: `Risk analysis: ${report.level} (score ${report.score})`,
40
62
  report,
41
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
63
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
64
+ context: includeContext ? sanitizeLogContext(context) : void 0
42
65
  });
43
66
  }
44
- function analyzeAndLog(guardian, options = {}) {
45
- const report = guardian.analyze();
46
- logReport(report, options);
67
+ async function analyzeAndLog(guardian, context, options = {}) {
68
+ const report = await guardian.analyzeAsync(context);
69
+ logReport(report, { ...options, context });
47
70
  return report;
48
71
  }
72
+ function sanitizeLogContext(context) {
73
+ if (context === null || context === void 0) {
74
+ return void 0;
75
+ }
76
+ if (typeof context !== "object") {
77
+ return void 0;
78
+ }
79
+ const record = context;
80
+ return {
81
+ method: typeof record.method === "string" ? record.method : void 0,
82
+ path: typeof record.path === "string" ? record.path : typeof record.originalUrl === "string" ? record.originalUrl : void 0,
83
+ ip: typeof record.ip === "string" ? record.ip : void 0
84
+ };
85
+ }
49
86
  function resolveLogLevel(report, configured) {
50
87
  if (report.level === "CRITICAL") {
51
88
  return "error";
@@ -62,4 +99,5 @@ function shouldLog(configured, actual) {
62
99
  exports.analyzeAndLog = analyzeAndLog;
63
100
  exports.logReport = logReport;
64
101
  exports.loggerPlugin = loggerPlugin;
102
+ exports.sanitizeLogContext = sanitizeLogContext;
65
103
  exports.shouldLog = shouldLog;
package/dist/index.d.cts CHANGED
@@ -13,31 +13,23 @@ interface LogEntry {
13
13
  readonly message: string;
14
14
  readonly report: RiskReport;
15
15
  readonly timestamp: string;
16
+ readonly context?: unknown;
16
17
  }
17
- /** Options for the logger plugin (stub). */
18
+ /** Options for the logger plugin. */
18
19
  interface LoggerPluginOptions {
19
- /** Minimum level to emit. */
20
20
  readonly level?: LogLevel;
21
- /** Custom sink. Defaults to console. */
22
21
  readonly sink?: LogSink;
23
- /** Log only when score exceeds this threshold. */
24
22
  readonly minScore?: number;
23
+ /** Include redacted request metadata in logs (default: true). */
24
+ readonly includeContext?: boolean;
25
25
  }
26
- /**
27
- * Logger plugin for guardian-risk.
28
- *
29
- * @stub Future versions will hook into analyze() automatically.
30
- * For now, use `logReport()` after `guardian.analyze()`.
31
- */
32
26
  declare function loggerPlugin(options?: LoggerPluginOptions): Plugin;
33
- /**
34
- * Log a risk report to the configured sink.
35
- */
36
- declare function logReport(report: RiskReport, options?: LoggerPluginOptions): void;
37
- /**
38
- * Analyze and log in one step.
39
- */
40
- declare function analyzeAndLog(guardian: guardian_risk.Guardian, options?: LoggerPluginOptions): RiskReport;
27
+ declare function logReport(report: RiskReport, options?: LoggerPluginOptions & {
28
+ context?: unknown;
29
+ }): void;
30
+ declare function analyzeAndLog(guardian: guardian_risk.Guardian, context?: unknown, options?: LoggerPluginOptions): Promise<RiskReport>;
31
+ /** Redact sensitive request fields before logging. */
32
+ declare function sanitizeLogContext(context: unknown): unknown;
41
33
  declare function shouldLog(configured: LogLevel, actual: LogLevel): boolean;
42
34
 
43
- export { type LogEntry, type LogLevel, type LogSink, type LoggerPluginOptions, analyzeAndLog, logReport, loggerPlugin, shouldLog };
35
+ export { type LogEntry, type LogLevel, type LogSink, type LoggerPluginOptions, analyzeAndLog, logReport, loggerPlugin, sanitizeLogContext, shouldLog };
package/dist/index.d.ts CHANGED
@@ -13,31 +13,23 @@ interface LogEntry {
13
13
  readonly message: string;
14
14
  readonly report: RiskReport;
15
15
  readonly timestamp: string;
16
+ readonly context?: unknown;
16
17
  }
17
- /** Options for the logger plugin (stub). */
18
+ /** Options for the logger plugin. */
18
19
  interface LoggerPluginOptions {
19
- /** Minimum level to emit. */
20
20
  readonly level?: LogLevel;
21
- /** Custom sink. Defaults to console. */
22
21
  readonly sink?: LogSink;
23
- /** Log only when score exceeds this threshold. */
24
22
  readonly minScore?: number;
23
+ /** Include redacted request metadata in logs (default: true). */
24
+ readonly includeContext?: boolean;
25
25
  }
26
- /**
27
- * Logger plugin for guardian-risk.
28
- *
29
- * @stub Future versions will hook into analyze() automatically.
30
- * For now, use `logReport()` after `guardian.analyze()`.
31
- */
32
26
  declare function loggerPlugin(options?: LoggerPluginOptions): Plugin;
33
- /**
34
- * Log a risk report to the configured sink.
35
- */
36
- declare function logReport(report: RiskReport, options?: LoggerPluginOptions): void;
37
- /**
38
- * Analyze and log in one step.
39
- */
40
- declare function analyzeAndLog(guardian: guardian_risk.Guardian, options?: LoggerPluginOptions): RiskReport;
27
+ declare function logReport(report: RiskReport, options?: LoggerPluginOptions & {
28
+ context?: unknown;
29
+ }): void;
30
+ declare function analyzeAndLog(guardian: guardian_risk.Guardian, context?: unknown, options?: LoggerPluginOptions): Promise<RiskReport>;
31
+ /** Redact sensitive request fields before logging. */
32
+ declare function sanitizeLogContext(context: unknown): unknown;
41
33
  declare function shouldLog(configured: LogLevel, actual: LogLevel): boolean;
42
34
 
43
- export { type LogEntry, type LogLevel, type LogSink, type LoggerPluginOptions, analyzeAndLog, logReport, loggerPlugin, shouldLog };
35
+ export { type LogEntry, type LogLevel, type LogSink, type LoggerPluginOptions, analyzeAndLog, logReport, loggerPlugin, sanitizeLogContext, shouldLog };
package/dist/index.js CHANGED
@@ -13,37 +13,74 @@ var defaultSink = {
13
13
  score: entry.report.score,
14
14
  riskLevel: entry.report.level,
15
15
  matchedRules: entry.report.matchedRules.length,
16
- timestamp: entry.timestamp
16
+ timestamp: entry.timestamp,
17
+ ...entry.context !== void 0 ? { context: entry.context } : {}
17
18
  });
18
19
  console.log(`[guardian-risk] ${payload}`);
19
20
  }
20
21
  };
21
22
  function loggerPlugin(options = {}) {
22
- const { level = "info", sink = defaultSink, minScore = 0 } = options;
23
+ const resolved = {
24
+ level: "info",
25
+ sink: defaultSink,
26
+ minScore: 0,
27
+ includeContext: true,
28
+ ...options
29
+ };
23
30
  return {
24
31
  name: "guardian-risk-logger",
25
- install(_guardian) {
32
+ install(guardian) {
33
+ guardian.afterAnalyze(({ report, data }) => {
34
+ logReport(report, {
35
+ ...resolved,
36
+ context: resolved.includeContext ? sanitizeLogContext(data) : void 0
37
+ });
38
+ });
26
39
  }
27
40
  };
28
41
  }
29
42
  function logReport(report, options = {}) {
30
- const { level = "info", sink = defaultSink, minScore = 0 } = options;
43
+ const {
44
+ level = "info",
45
+ sink = defaultSink,
46
+ minScore = 0,
47
+ context,
48
+ includeContext = true
49
+ } = options;
31
50
  if (report.score < minScore) {
32
51
  return;
33
52
  }
34
53
  const entryLevel = resolveLogLevel(report, level);
54
+ if (!shouldLog(level, entryLevel)) {
55
+ return;
56
+ }
35
57
  sink.write({
36
58
  level: entryLevel,
37
- message: `Risk analysis complete: ${report.level} (${report.score})`,
59
+ message: `Risk analysis: ${report.level} (score ${report.score})`,
38
60
  report,
39
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
61
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
62
+ context: includeContext ? sanitizeLogContext(context) : void 0
40
63
  });
41
64
  }
42
- function analyzeAndLog(guardian, options = {}) {
43
- const report = guardian.analyze();
44
- logReport(report, options);
65
+ async function analyzeAndLog(guardian, context, options = {}) {
66
+ const report = await guardian.analyzeAsync(context);
67
+ logReport(report, { ...options, context });
45
68
  return report;
46
69
  }
70
+ function sanitizeLogContext(context) {
71
+ if (context === null || context === void 0) {
72
+ return void 0;
73
+ }
74
+ if (typeof context !== "object") {
75
+ return void 0;
76
+ }
77
+ const record = context;
78
+ return {
79
+ method: typeof record.method === "string" ? record.method : void 0,
80
+ path: typeof record.path === "string" ? record.path : typeof record.originalUrl === "string" ? record.originalUrl : void 0,
81
+ ip: typeof record.ip === "string" ? record.ip : void 0
82
+ };
83
+ }
47
84
  function resolveLogLevel(report, configured) {
48
85
  if (report.level === "CRITICAL") {
49
86
  return "error";
@@ -57,4 +94,4 @@ function shouldLog(configured, actual) {
57
94
  return LEVEL_ORDER[actual] >= LEVEL_ORDER[configured];
58
95
  }
59
96
 
60
- export { analyzeAndLog, logReport, loggerPlugin, shouldLog };
97
+ export { analyzeAndLog, logReport, loggerPlugin, sanitizeLogContext, shouldLog };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guardian-risk-logger",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Audit logging plugin for guardian-risk — logs reports and matched rules",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -55,12 +55,12 @@
55
55
  "access": "public"
56
56
  },
57
57
  "peerDependencies": {
58
- "guardian-risk": "^0.2.0"
58
+ "guardian-risk": "^0.3.0"
59
59
  },
60
60
  "devDependencies": {
61
61
  "tsup": "^8.3.5",
62
62
  "typescript": "^5.7.2",
63
- "guardian-risk": "0.2.1"
63
+ "guardian-risk": "0.3.0"
64
64
  },
65
65
  "scripts": {
66
66
  "build": "tsup",