@ouro.bot/cli 0.1.0-alpha.286 → 0.1.0-alpha.287

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/changelog.json CHANGED
@@ -1,6 +1,12 @@
1
1
  {
2
2
  "_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
3
3
  "versions": [
4
+ {
5
+ "version": "0.1.0-alpha.287",
6
+ "changes": [
7
+ "feat(nerves): add two-layer log redaction to the NDJSON sink. Structured key-based redaction strips sensitive fields (passwords, tokens, API keys, auth headers) from event meta objects before serialization. Regex fallback catches secrets in serialized strings that bypass structured checks (Anthropic keys, OpenAI keys, Bearer tokens, URL token params). Redacted values are replaced with `[REDACTED:key_name]` markers that preserve debugging context without exposing secrets. Redaction happens at the sink level only — in-memory events retain full data for runtime use. `OURO_LOG_VERBOSE=1` env var disables redaction for active debugging sessions. New `src/nerves/redact.ts` module with 38 tests across 3 test files including a 10-case golden corpus covering nested objects, mixed safe/secret fields, and realistic key patterns."
8
+ ]
9
+ },
4
10
  {
5
11
  "version": "0.1.0-alpha.286",
6
12
  "changes": [
@@ -69,6 +69,8 @@ const DISPATCH_EXEMPT_PATTERNS = [
69
69
  "mind/embedding-provider",
70
70
  // Commerce utility module: error classes and pure helpers (no independent side effects).
71
71
  "repertoire/commerce-errors",
72
+ // Log redaction: pure utility consumed by the NDJSON sink (no independent side effects).
73
+ "nerves/redact",
72
74
  ];
73
75
  function isDispatchExempt(filePath) {
74
76
  return DISPATCH_EXEMPT_PATTERNS.some((pattern) => filePath.includes(pattern));
@@ -54,6 +54,7 @@ const zlib = __importStar(require("zlib"));
54
54
  // lazily inside `getRuntimeLogger()`, and we only call `emitNervesEvent` from
55
55
  // inside `rotateIfNeeded` — never at module top level.
56
56
  const runtime_1 = require("./runtime");
57
+ const redact_1 = require("./redact");
57
58
  const LEVEL_PRIORITY = {
58
59
  debug: 10,
59
60
  info: 20,
@@ -293,6 +294,7 @@ function createNdjsonFileSink(filePath, optionsOrMaxSize) {
293
294
  ? { maxSizeBytes: optionsOrMaxSize }
294
295
  : optionsOrMaxSize ?? {};
295
296
  const rotationCheckInterval = options.rotationCheckIntervalBytes ?? DEFAULT_ROTATION_CHECK_INTERVAL_BYTES;
297
+ const verbose = process.env.OURO_LOG_VERBOSE === "1";
296
298
  const queue = [];
297
299
  let flushing = false;
298
300
  let bytesSinceCheck = 0;
@@ -316,7 +318,9 @@ function createNdjsonFileSink(filePath, optionsOrMaxSize) {
316
318
  });
317
319
  }
318
320
  return (entry) => {
319
- const line = `${JSON.stringify(entry)}\n`;
321
+ const line = verbose
322
+ ? `${JSON.stringify(entry)}\n`
323
+ : `${(0, redact_1.redactString)(JSON.stringify((0, redact_1.redactLogEntry)(entry)))}\n`;
320
324
  bytesSinceCheck += line.length;
321
325
  queue.push(line);
322
326
  flush();
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SENSITIVE_PATTERNS = exports.SENSITIVE_KEYS = void 0;
4
+ exports.redactMeta = redactMeta;
5
+ exports.redactString = redactString;
6
+ exports.redactLogEntry = redactLogEntry;
7
+ /**
8
+ * Lowercase key names that trigger structured redaction.
9
+ * Matched case-insensitively against meta object keys.
10
+ */
11
+ exports.SENSITIVE_KEYS = new Set([
12
+ "password",
13
+ "secret",
14
+ "token",
15
+ "accesstoken",
16
+ "refreshtoken",
17
+ "clientsecret",
18
+ "apikey",
19
+ "oauthaccesstoken",
20
+ "authorization",
21
+ "cookie",
22
+ "set-cookie",
23
+ ]);
24
+ /**
25
+ * Regex patterns for string-level fallback redaction.
26
+ * Applied in order; each match is replaced with `[REDACTED:name]`.
27
+ */
28
+ exports.SENSITIVE_PATTERNS = [
29
+ { name: "anthropic_key", pattern: /sk-ant-[A-Za-z0-9_-]+/g },
30
+ { name: "openai_key", pattern: /sk-proj-[A-Za-z0-9_-]+/g },
31
+ { name: "bearer_token", pattern: /Bearer [A-Za-z0-9\-_.]+/g },
32
+ { name: "api_key_assignment", pattern: /api[_-]?key[=:]\s*["']?[A-Za-z0-9\-_.]+/g },
33
+ { name: "url_token", pattern: /[?&](?:token|key|api_key|access_token)=[A-Za-z0-9\-_.%]+/g },
34
+ ];
35
+ /**
36
+ * Deep-clone meta and replace values at sensitive keys with `[REDACTED:key_name]`.
37
+ * Recurses into nested objects. Case-insensitive key matching.
38
+ */
39
+ function redactMeta(meta) {
40
+ const result = {};
41
+ for (const key of Object.keys(meta)) {
42
+ const value = meta[key];
43
+ if (exports.SENSITIVE_KEYS.has(key.toLowerCase())) {
44
+ result[key] = `[REDACTED:${key}]`;
45
+ }
46
+ else if (value !== null && typeof value === "object" && !Array.isArray(value)) {
47
+ result[key] = redactMeta(value);
48
+ }
49
+ else {
50
+ result[key] = value;
51
+ }
52
+ }
53
+ return result;
54
+ }
55
+ /**
56
+ * Apply all regex patterns to a string, replacing matches with `[REDACTED:pattern_name]`.
57
+ */
58
+ function redactString(text) {
59
+ let result = text;
60
+ for (const { name, pattern } of exports.SENSITIVE_PATTERNS) {
61
+ // Reset lastIndex for global regexes reused across calls
62
+ pattern.lastIndex = 0;
63
+ result = result.replace(pattern, `[REDACTED:${name}]`);
64
+ }
65
+ return result;
66
+ }
67
+ /**
68
+ * Full redaction pipeline: redact meta (structured keys), then redact message (regex patterns).
69
+ * Returns a new LogEvent -- never mutates the input.
70
+ */
71
+ function redactLogEntry(entry) {
72
+ const redactedMeta = redactMeta(entry.meta);
73
+ const redactedMessage = redactString(entry.message);
74
+ return {
75
+ ...entry,
76
+ meta: redactedMeta,
77
+ message: redactedMessage,
78
+ };
79
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.286",
3
+ "version": "0.1.0-alpha.287",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",