@skillsmith/core 0.10.0 → 0.11.1
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.md +9 -0
- package/README.md +7 -5
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/analysis/tree-sitter/pythonIncremental.d.ts +23 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.d.ts.map +1 -1
- package/dist/src/analysis/tree-sitter/pythonIncremental.hardening.test.js +137 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.hardening.test.js.map +1 -1
- package/dist/src/analysis/tree-sitter/pythonIncremental.js +83 -10
- package/dist/src/analysis/tree-sitter/pythonIncremental.js.map +1 -1
- package/dist/src/analysis/tree-sitter/queryExtractionMatchesOrExceedsRegex.test.js +6 -1
- package/dist/src/analysis/tree-sitter/queryExtractionMatchesOrExceedsRegex.test.js.map +1 -1
- package/dist/src/config/audit-notify-state.d.ts +46 -0
- package/dist/src/config/audit-notify-state.d.ts.map +1 -0
- package/dist/src/config/audit-notify-state.js +55 -0
- package/dist/src/config/audit-notify-state.js.map +1 -0
- package/dist/src/config/audit-notify-state.test.d.ts +9 -0
- package/dist/src/config/audit-notify-state.test.d.ts.map +1 -0
- package/dist/src/config/audit-notify-state.test.js +63 -0
- package/dist/src/config/audit-notify-state.test.js.map +1 -0
- package/dist/src/config/index.d.ts +10 -0
- package/dist/src/config/index.d.ts.map +1 -1
- package/dist/src/config/index.js.map +1 -1
- package/dist/src/exports/services.d.ts +1 -1
- package/dist/src/exports/services.d.ts.map +1 -1
- package/dist/src/exports/services.js +3 -1
- package/dist/src/exports/services.js.map +1 -1
- package/dist/src/index.d.ts +4 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +7 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/logging/context.d.ts +58 -0
- package/dist/src/logging/context.d.ts.map +1 -0
- package/dist/src/logging/context.js +62 -0
- package/dist/src/logging/context.js.map +1 -0
- package/dist/src/logging/context.test.d.ts +14 -0
- package/dist/src/logging/context.test.d.ts.map +1 -0
- package/dist/src/logging/context.test.js +88 -0
- package/dist/src/logging/context.test.js.map +1 -0
- package/dist/src/logging/index.d.ts +13 -0
- package/dist/src/logging/index.d.ts.map +1 -0
- package/dist/src/logging/index.js +12 -0
- package/dist/src/logging/index.js.map +1 -0
- package/dist/src/logging/logger.d.ts +71 -0
- package/dist/src/logging/logger.d.ts.map +1 -0
- package/dist/src/logging/logger.js +264 -0
- package/dist/src/logging/logger.js.map +1 -0
- package/dist/src/logging/logger.test.d.ts +9 -0
- package/dist/src/logging/logger.test.d.ts.map +1 -0
- package/dist/src/logging/logger.test.js +320 -0
- package/dist/src/logging/logger.test.js.map +1 -0
- package/dist/src/logging/redact.d.ts +35 -0
- package/dist/src/logging/redact.d.ts.map +1 -0
- package/dist/src/logging/redact.js +152 -0
- package/dist/src/logging/redact.js.map +1 -0
- package/dist/src/logging/redact.test.d.ts +8 -0
- package/dist/src/logging/redact.test.d.ts.map +1 -0
- package/dist/src/logging/redact.test.js +330 -0
- package/dist/src/logging/redact.test.js.map +1 -0
- package/dist/src/logging/rotation.d.ts +75 -0
- package/dist/src/logging/rotation.d.ts.map +1 -0
- package/dist/src/logging/rotation.js +284 -0
- package/dist/src/logging/rotation.js.map +1 -0
- package/dist/src/logging/rotation.test.d.ts +11 -0
- package/dist/src/logging/rotation.test.d.ts.map +1 -0
- package/dist/src/logging/rotation.test.js +132 -0
- package/dist/src/logging/rotation.test.js.map +1 -0
- package/dist/src/logging/types.d.ts +69 -0
- package/dist/src/logging/types.d.ts.map +1 -0
- package/dist/src/logging/types.js +9 -0
- package/dist/src/logging/types.js.map +1 -0
- package/dist/src/security/index.d.ts +2 -0
- package/dist/src/security/index.d.ts.map +1 -1
- package/dist/src/security/index.js +4 -0
- package/dist/src/security/index.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.hostile-update.d.ts +46 -0
- package/dist/src/security/scanner/SecurityScanner.hostile-update.d.ts.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.hostile-update.js +209 -0
- package/dist/src/security/scanner/SecurityScanner.hostile-update.js.map +1 -0
- package/dist/src/security/scanner/index.d.ts +2 -1
- package/dist/src/security/scanner/index.d.ts.map +1 -1
- package/dist/src/security/scanner/index.js +2 -0
- package/dist/src/security/scanner/index.js.map +1 -1
- package/dist/src/security/scanner/types.d.ts +21 -0
- package/dist/src/security/scanner/types.d.ts.map +1 -1
- package/dist/src/services/skill-installation.helpers.d.ts +13 -1
- package/dist/src/services/skill-installation.helpers.d.ts.map +1 -1
- package/dist/src/services/skill-installation.helpers.js +14 -1
- package/dist/src/services/skill-installation.helpers.js.map +1 -1
- package/dist/src/services/skill-installation.io.d.ts.map +1 -1
- package/dist/src/services/skill-installation.io.js +19 -2
- package/dist/src/services/skill-installation.io.js.map +1 -1
- package/dist/src/sync/BackgroundSyncService.d.ts +10 -2
- package/dist/src/sync/BackgroundSyncService.d.ts.map +1 -1
- package/dist/src/sync/BackgroundSyncService.js +46 -4
- package/dist/src/sync/BackgroundSyncService.js.map +1 -1
- package/dist/src/sync/SyncEngine.d.ts +9 -0
- package/dist/src/sync/SyncEngine.d.ts.map +1 -1
- package/dist/src/sync/SyncEngine.js +23 -4
- package/dist/src/sync/SyncEngine.js.map +1 -1
- package/dist/src/sync/access-token.d.ts +27 -0
- package/dist/src/sync/access-token.d.ts.map +1 -0
- package/dist/src/sync/access-token.js +40 -0
- package/dist/src/sync/access-token.js.map +1 -0
- package/dist/src/sync/audit-notify-client.d.ts +83 -0
- package/dist/src/sync/audit-notify-client.d.ts.map +1 -0
- package/dist/src/sync/audit-notify-client.js +126 -0
- package/dist/src/sync/audit-notify-client.js.map +1 -0
- package/dist/src/sync/audit-notify-client.test.d.ts +14 -0
- package/dist/src/sync/audit-notify-client.test.d.ts.map +1 -0
- package/dist/src/sync/audit-notify-client.test.js +133 -0
- package/dist/src/sync/audit-notify-client.test.js.map +1 -0
- package/dist/src/sync/index.d.ts +1 -0
- package/dist/src/sync/index.d.ts.map +1 -1
- package/dist/src/sync/index.js +2 -0
- package/dist/src/sync/index.js.map +1 -1
- package/dist/src/sync/inventory-client.d.ts.map +1 -1
- package/dist/src/sync/inventory-client.js +5 -16
- package/dist/src/sync/inventory-client.js.map +1 -1
- package/dist/src/telemetry/posthog.d.ts +15 -0
- package/dist/src/telemetry/posthog.d.ts.map +1 -1
- package/dist/src/telemetry/posthog.js +6 -1
- package/dist/src/telemetry/posthog.js.map +1 -1
- package/dist/src/telemetry/wrap.bench.js +5 -0
- package/dist/src/telemetry/wrap.bench.js.map +1 -1
- package/dist/src/telemetry/wrap.correlation.test.d.ts +20 -0
- package/dist/src/telemetry/wrap.correlation.test.d.ts.map +1 -0
- package/dist/src/telemetry/wrap.correlation.test.js +174 -0
- package/dist/src/telemetry/wrap.correlation.test.js.map +1 -0
- package/dist/src/telemetry/wrap.d.ts.map +1 -1
- package/dist/src/telemetry/wrap.gate.test.js +5 -0
- package/dist/src/telemetry/wrap.gate.test.js.map +1 -1
- package/dist/src/telemetry/wrap.js +111 -52
- package/dist/src/telemetry/wrap.js.map +1 -1
- package/dist/src/telemetry/wrap.marker.test.js +5 -0
- package/dist/src/telemetry/wrap.marker.test.js.map +1 -1
- package/dist/src/telemetry/wrap.test.js +10 -0
- package/dist/src/telemetry/wrap.test.js.map +1 -1
- package/dist/src/types.d.ts +6 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/tests/security/scanner-regression-guard.test.js +179 -1
- package/dist/tests/security/scanner-regression-guard.test.js.map +1 -1
- package/dist/tests/sync/BackgroundSyncService.abort.test.d.ts +17 -0
- package/dist/tests/sync/BackgroundSyncService.abort.test.d.ts.map +1 -0
- package/dist/tests/sync/BackgroundSyncService.abort.test.js +182 -0
- package/dist/tests/sync/BackgroundSyncService.abort.test.js.map +1 -0
- package/dist/tests/sync/BackgroundSyncService.test.d.ts +9 -1
- package/dist/tests/sync/BackgroundSyncService.test.d.ts.map +1 -1
- package/dist/tests/sync/BackgroundSyncService.test.js +8 -3
- package/dist/tests/sync/BackgroundSyncService.test.js.map +1 -1
- package/dist/tests/sync/SyncEngine.abort.test.d.ts +15 -0
- package/dist/tests/sync/SyncEngine.abort.test.d.ts.map +1 -0
- package/dist/tests/sync/SyncEngine.abort.test.js +100 -0
- package/dist/tests/sync/SyncEngine.abort.test.js.map +1 -0
- package/dist/tests/sync/SyncEngine.status.test.d.ts +10 -0
- package/dist/tests/sync/SyncEngine.status.test.d.ts.map +1 -0
- package/dist/tests/sync/SyncEngine.status.test.js +97 -0
- package/dist/tests/sync/SyncEngine.status.test.js.map +1 -0
- package/dist/tests/sync/SyncEngine.test.d.ts +22 -1
- package/dist/tests/sync/SyncEngine.test.d.ts.map +1 -1
- package/dist/tests/sync/SyncEngine.test.js +3 -69
- package/dist/tests/sync/SyncEngine.test.js.map +1 -1
- package/package.json +7 -2
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-5615: Tests for the shared `logger.ts` factory — crash-proofing,
|
|
3
|
+
* `SKILLSMITH_ERROR_LOG_DISABLE`, `SKILLSMITH_LOG_LEVEL` filtering,
|
|
4
|
+
* redaction-on-disk, and the correlation-ID call-site invariant (F3). All
|
|
5
|
+
* tests point `SKILLSMITH_LOG_DIR` at a temp directory — never the real
|
|
6
|
+
* `~/.skillsmith/logs/`.
|
|
7
|
+
*/
|
|
8
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
9
|
+
import { mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
10
|
+
import { tmpdir } from 'node:os';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { runWithCorrelationId } from './context.js';
|
|
13
|
+
import { createLogger } from './logger.js';
|
|
14
|
+
import { __resetLoggingStateForTests } from './rotation.js';
|
|
15
|
+
let tempDir;
|
|
16
|
+
let originalLogDir;
|
|
17
|
+
let originalDisable;
|
|
18
|
+
let originalLevel;
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
tempDir = mkdtempSync(join(tmpdir(), 'skillsmith-logger-'));
|
|
21
|
+
originalLogDir = process.env.SKILLSMITH_LOG_DIR;
|
|
22
|
+
originalDisable = process.env.SKILLSMITH_ERROR_LOG_DISABLE;
|
|
23
|
+
originalLevel = process.env.SKILLSMITH_LOG_LEVEL;
|
|
24
|
+
process.env.SKILLSMITH_LOG_DIR = tempDir;
|
|
25
|
+
delete process.env.SKILLSMITH_ERROR_LOG_DISABLE;
|
|
26
|
+
delete process.env.SKILLSMITH_LOG_LEVEL;
|
|
27
|
+
});
|
|
28
|
+
afterEach(async () => {
|
|
29
|
+
await __resetLoggingStateForTests();
|
|
30
|
+
vi.restoreAllMocks();
|
|
31
|
+
const restore = (key, value) => {
|
|
32
|
+
if (value === undefined)
|
|
33
|
+
delete process.env[key];
|
|
34
|
+
else
|
|
35
|
+
process.env[key] = value;
|
|
36
|
+
};
|
|
37
|
+
restore('SKILLSMITH_LOG_DIR', originalLogDir);
|
|
38
|
+
restore('SKILLSMITH_ERROR_LOG_DISABLE', originalDisable);
|
|
39
|
+
restore('SKILLSMITH_LOG_LEVEL', originalLevel);
|
|
40
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
41
|
+
});
|
|
42
|
+
function findLogFile(surfacePrefix) {
|
|
43
|
+
const files = readdirSync(tempDir).filter((f) => f.startsWith(surfacePrefix));
|
|
44
|
+
expect(files.length).toBeGreaterThan(0);
|
|
45
|
+
return join(tempDir, files[0]);
|
|
46
|
+
}
|
|
47
|
+
async function waitForLogFile(surfacePrefix) {
|
|
48
|
+
return vi.waitFor(() => {
|
|
49
|
+
const files = readdirSync(tempDir).filter((f) => f.startsWith(surfacePrefix));
|
|
50
|
+
expect(files.length).toBeGreaterThan(0);
|
|
51
|
+
return files;
|
|
52
|
+
}, { timeout: 2000, interval: 20 });
|
|
53
|
+
}
|
|
54
|
+
describe('logger.ts — redaction on disk', () => {
|
|
55
|
+
it('writes a redacted message and never persists the raw secret', async () => {
|
|
56
|
+
const logger = createLogger('mcp');
|
|
57
|
+
logger.error('Failed with token sk_live_abcdefghijklmnopqrstuvwx');
|
|
58
|
+
await waitForLogFile('skillsmith-mcp-');
|
|
59
|
+
const content = await vi.waitFor(() => {
|
|
60
|
+
const text = readFileSync(findLogFile('skillsmith-mcp-'), 'utf8');
|
|
61
|
+
expect(text.length).toBeGreaterThan(0);
|
|
62
|
+
return text;
|
|
63
|
+
}, { timeout: 2000, interval: 20 });
|
|
64
|
+
expect(content).toContain('sk_live_[REDACTED]');
|
|
65
|
+
expect(content).not.toContain('abcdefghijklmnopqrstuvwx');
|
|
66
|
+
const record = JSON.parse(content.trim().split('\n')[0]);
|
|
67
|
+
expect(record.level).toBe('error');
|
|
68
|
+
expect(record.surface).toBe('mcp');
|
|
69
|
+
expect(record.msg).toContain('sk_live_[REDACTED]');
|
|
70
|
+
});
|
|
71
|
+
it('redacts secrets inside the `err` detail as well as the message', async () => {
|
|
72
|
+
const logger = createLogger('cli');
|
|
73
|
+
const err = new Error('token=ghp_1234567890abcdefghijklmnopqrstuvwxyz');
|
|
74
|
+
logger.error('tool call failed', { err });
|
|
75
|
+
await waitForLogFile('skillsmith-cli-');
|
|
76
|
+
const content = await vi.waitFor(() => {
|
|
77
|
+
const text = readFileSync(findLogFile('skillsmith-cli-'), 'utf8');
|
|
78
|
+
expect(text.length).toBeGreaterThan(0);
|
|
79
|
+
return text;
|
|
80
|
+
}, { timeout: 2000, interval: 20 });
|
|
81
|
+
const record = JSON.parse(content.trim().split('\n')[0]);
|
|
82
|
+
expect(record.err.message).toContain('ghp_[REDACTED]');
|
|
83
|
+
expect(record.err.message).not.toContain('1234567890abcdefghijklmnopqrstuvwxyz');
|
|
84
|
+
});
|
|
85
|
+
it('strips chalk/ANSI codes from the disk record and redacts a secret that immediately follows one (Mode-B Wave 2 finding)', async () => {
|
|
86
|
+
const logger = createLogger('cli');
|
|
87
|
+
const secret = 'sk_live_' + 'd'.repeat(24);
|
|
88
|
+
// Realistic CLI shape: chalk.red('Error:') directly adjacent to a secret,
|
|
89
|
+
// no space — the exact pattern the diff-audit flagged as a latent gap.
|
|
90
|
+
logger.error(`\x1b[31mError:\x1b[39m${secret} leaked`);
|
|
91
|
+
await waitForLogFile('skillsmith-cli-');
|
|
92
|
+
const content = readFileSync(findLogFile('skillsmith-cli-'), 'utf8');
|
|
93
|
+
const record = JSON.parse(content.trim().split('\n')[0]);
|
|
94
|
+
expect(record.msg).not.toContain('\x1b[');
|
|
95
|
+
expect(record.msg).not.toContain(secret);
|
|
96
|
+
expect(record.msg).toContain('sk_live_[REDACTED]');
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
describe('logger.ts — warn/error always mirror to console (plan §1, not just on write failure)', () => {
|
|
100
|
+
// Regression coverage for a Mode-B-adjacent finding caught during Wave 2
|
|
101
|
+
// wire-in: an earlier version of `persistRecord` only called
|
|
102
|
+
// `consoleFallback` when the disk write FAILED. That silently broke the
|
|
103
|
+
// whole point of swapping `console.error`/`warn` call sites in the MCP
|
|
104
|
+
// server and CLI for `logger.error`/`warn` — on the (normal) successful
|
|
105
|
+
// write path, callers would see NOTHING on screen, a regression from
|
|
106
|
+
// today's always-visible `console.error`. The plan is explicit: "the
|
|
107
|
+
// logger writes to disk AND still emits to stderr for warn/error" —
|
|
108
|
+
// unconditionally, not as a failure fallback.
|
|
109
|
+
it('mirrors error to console.error even when the disk write succeeds (writable dir)', async () => {
|
|
110
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
111
|
+
const logger = createLogger('mcp');
|
|
112
|
+
logger.error('boom');
|
|
113
|
+
// console mirror must be synchronous/immediate — a CLI user reading a
|
|
114
|
+
// terminal cannot wait for an async disk write to decide whether to print.
|
|
115
|
+
expect(errorSpy).toHaveBeenCalledWith('boom');
|
|
116
|
+
// ...and the disk record still lands too (both, not either/or).
|
|
117
|
+
await waitForLogFile('skillsmith-mcp-');
|
|
118
|
+
});
|
|
119
|
+
it('mirrors warn to console.warn (not console.error) even when the disk write succeeds', async () => {
|
|
120
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined);
|
|
121
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
122
|
+
const logger = createLogger('cli');
|
|
123
|
+
logger.warn('careful');
|
|
124
|
+
expect(warnSpy).toHaveBeenCalledWith('careful');
|
|
125
|
+
expect(errorSpy).not.toHaveBeenCalled();
|
|
126
|
+
await waitForLogFile('skillsmith-cli-');
|
|
127
|
+
});
|
|
128
|
+
it('redacts the console-mirrored message too, not just the disk record', () => {
|
|
129
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
130
|
+
const logger = createLogger('mcp');
|
|
131
|
+
const secret = 'sk_live_' + 'b'.repeat(24);
|
|
132
|
+
logger.error(`auth failed with ${secret}`);
|
|
133
|
+
const calledWith = errorSpy.mock.calls.map((call) => call[0]).join('\n');
|
|
134
|
+
expect(calledWith).not.toContain(secret);
|
|
135
|
+
expect(calledWith).toContain('sk_live_[REDACTED]');
|
|
136
|
+
});
|
|
137
|
+
it('does NOT mirror info/debug to console on the successful-write path (disk-only by design)', async () => {
|
|
138
|
+
process.env.SKILLSMITH_LOG_LEVEL = 'debug';
|
|
139
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
140
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined);
|
|
141
|
+
const logger = createLogger('mcp');
|
|
142
|
+
logger.info('routine status');
|
|
143
|
+
logger.debug('verbose detail');
|
|
144
|
+
await waitForLogFile('skillsmith-mcp-');
|
|
145
|
+
expect(errorSpy).not.toHaveBeenCalled();
|
|
146
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
describe('logger.ts — SKILLSMITH_ERROR_LOG_DISABLE', () => {
|
|
150
|
+
it('accepts "1" and skips writing entirely', async () => {
|
|
151
|
+
process.env.SKILLSMITH_ERROR_LOG_DISABLE = '1';
|
|
152
|
+
const logger = createLogger('mcp');
|
|
153
|
+
logger.error('should not be written');
|
|
154
|
+
// Give any accidental async write a chance to land before asserting absence.
|
|
155
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
156
|
+
expect(readdirSync(tempDir)).toHaveLength(0);
|
|
157
|
+
});
|
|
158
|
+
it('accepts "true" (case-insensitive)', async () => {
|
|
159
|
+
process.env.SKILLSMITH_ERROR_LOG_DISABLE = 'TRUE';
|
|
160
|
+
const logger = createLogger('mcp');
|
|
161
|
+
logger.error('should not be written');
|
|
162
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
163
|
+
expect(readdirSync(tempDir)).toHaveLength(0);
|
|
164
|
+
});
|
|
165
|
+
it('logs normally when unset', async () => {
|
|
166
|
+
const logger = createLogger('mcp');
|
|
167
|
+
logger.error('should be written');
|
|
168
|
+
await waitForLogFile('skillsmith-mcp-');
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
describe('logger.ts — SKILLSMITH_LOG_LEVEL filtering', () => {
|
|
172
|
+
it('defaults to warn — info/debug are suppressed, warn/error are not', async () => {
|
|
173
|
+
const logger = createLogger('mcp');
|
|
174
|
+
logger.debug('debug message');
|
|
175
|
+
logger.info('info message');
|
|
176
|
+
logger.warn('warn message');
|
|
177
|
+
await waitForLogFile('skillsmith-mcp-');
|
|
178
|
+
const content = readFileSync(findLogFile('skillsmith-mcp-'), 'utf8');
|
|
179
|
+
const lines = content.trim().split('\n').filter(Boolean);
|
|
180
|
+
expect(lines).toHaveLength(1);
|
|
181
|
+
expect(JSON.parse(lines[0]).msg).toBe('warn message');
|
|
182
|
+
});
|
|
183
|
+
it('SKILLSMITH_LOG_LEVEL=debug allows every level through', async () => {
|
|
184
|
+
process.env.SKILLSMITH_LOG_LEVEL = 'debug';
|
|
185
|
+
const logger = createLogger('mcp');
|
|
186
|
+
logger.debug('debug message');
|
|
187
|
+
logger.info('info message');
|
|
188
|
+
logger.warn('warn message');
|
|
189
|
+
logger.error('error message');
|
|
190
|
+
await vi.waitFor(() => {
|
|
191
|
+
const files = readdirSync(tempDir).filter((f) => f.startsWith('skillsmith-mcp-'));
|
|
192
|
+
expect(files.length).toBeGreaterThan(0);
|
|
193
|
+
const content = readFileSync(join(tempDir, files[0]), 'utf8');
|
|
194
|
+
const lines = content.trim().split('\n').filter(Boolean);
|
|
195
|
+
expect(lines).toHaveLength(4);
|
|
196
|
+
return lines;
|
|
197
|
+
}, { timeout: 2000, interval: 20 });
|
|
198
|
+
});
|
|
199
|
+
it('SKILLSMITH_LOG_LEVEL=error suppresses everything but error', async () => {
|
|
200
|
+
process.env.SKILLSMITH_LOG_LEVEL = 'error';
|
|
201
|
+
const logger = createLogger('mcp');
|
|
202
|
+
logger.warn('warn message');
|
|
203
|
+
logger.error('error message');
|
|
204
|
+
await waitForLogFile('skillsmith-mcp-');
|
|
205
|
+
const content = await vi.waitFor(() => {
|
|
206
|
+
const text = readFileSync(findLogFile('skillsmith-mcp-'), 'utf8');
|
|
207
|
+
const lines = text.trim().split('\n').filter(Boolean);
|
|
208
|
+
expect(lines).toHaveLength(1);
|
|
209
|
+
return text;
|
|
210
|
+
}, { timeout: 2000, interval: 20 });
|
|
211
|
+
expect(JSON.parse(content.trim())).toMatchObject({ level: 'error', msg: 'error message' });
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
describe('logger.ts — crash-proofing', () => {
|
|
215
|
+
it('never throws and falls back to console when the log directory is unwritable', async () => {
|
|
216
|
+
// A regular FILE at the path where a directory is expected makes
|
|
217
|
+
// `mkdir(..., { recursive: true })` fail with ENOTDIR — portable and
|
|
218
|
+
// permission-independent (unlike relying on running as non-root).
|
|
219
|
+
const blockerFile = join(tempDir, 'blocker');
|
|
220
|
+
writeFileSync(blockerFile, '');
|
|
221
|
+
process.env.SKILLSMITH_LOG_DIR = join(blockerFile, 'logs');
|
|
222
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
223
|
+
const logger = createLogger('mcp');
|
|
224
|
+
expect(() => logger.error('this must not throw')).not.toThrow();
|
|
225
|
+
await vi.waitFor(() => {
|
|
226
|
+
expect(errorSpy).toHaveBeenCalled();
|
|
227
|
+
}, { timeout: 2000, interval: 20 });
|
|
228
|
+
expect(errorSpy.mock.calls.some((call) => call[0] === 'this must not throw')).toBe(true);
|
|
229
|
+
});
|
|
230
|
+
it('routes the warn-level fallback through console.warn, not console.error', async () => {
|
|
231
|
+
const blockerFile = join(tempDir, 'blocker');
|
|
232
|
+
writeFileSync(blockerFile, '');
|
|
233
|
+
process.env.SKILLSMITH_LOG_DIR = join(blockerFile, 'logs');
|
|
234
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined);
|
|
235
|
+
const logger = createLogger('cli');
|
|
236
|
+
expect(() => logger.warn('warn fallback')).not.toThrow();
|
|
237
|
+
await vi.waitFor(() => {
|
|
238
|
+
expect(warnSpy).toHaveBeenCalledWith('warn fallback');
|
|
239
|
+
}, { timeout: 2000, interval: 20 });
|
|
240
|
+
});
|
|
241
|
+
it('never throws even when `details` contains a circular reference, and still persists the record (NEW-2)', async () => {
|
|
242
|
+
const logger = createLogger('mcp');
|
|
243
|
+
const circular = {};
|
|
244
|
+
circular.self = circular;
|
|
245
|
+
expect(() => logger.error('circular details', { blob: circular })).not.toThrow();
|
|
246
|
+
// Before the cycle guard, `redactSensitiveObject` stack-overflowed on
|
|
247
|
+
// `circular`, which aborted the whole record and silently dropped it to
|
|
248
|
+
// the console-only fallback. With the guard, the record — including the
|
|
249
|
+
// correlationId/msg that have nothing to do with the cycle — must still
|
|
250
|
+
// reach disk, with only the offending branch marked `'[Circular]'`.
|
|
251
|
+
await waitForLogFile('skillsmith-mcp-');
|
|
252
|
+
const content = readFileSync(findLogFile('skillsmith-mcp-'), 'utf8');
|
|
253
|
+
const record = JSON.parse(content.trim().split('\n')[0]);
|
|
254
|
+
expect(record.msg).toBe('circular details');
|
|
255
|
+
expect(record.details.blob.self).toBe('[Circular]');
|
|
256
|
+
});
|
|
257
|
+
it('redacts the message before the console fallback, not just the disk record (NEW-1)', async () => {
|
|
258
|
+
const blockerFile = join(tempDir, 'blocker');
|
|
259
|
+
writeFileSync(blockerFile, '');
|
|
260
|
+
process.env.SKILLSMITH_LOG_DIR = join(blockerFile, 'logs');
|
|
261
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
262
|
+
const logger = createLogger('mcp');
|
|
263
|
+
const secret = 'sk_live_' + 'a'.repeat(24);
|
|
264
|
+
expect(() => logger.error(`auth failed with ${secret}`)).not.toThrow();
|
|
265
|
+
await vi.waitFor(() => {
|
|
266
|
+
expect(errorSpy).toHaveBeenCalled();
|
|
267
|
+
}, { timeout: 2000, interval: 20 });
|
|
268
|
+
const calledWith = errorSpy.mock.calls.map((call) => call[0]).join('\n');
|
|
269
|
+
expect(calledWith).not.toContain(secret);
|
|
270
|
+
expect(calledWith).toContain('sk_live_[REDACTED]');
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
describe('logger.ts — correlation ID (F3)', () => {
|
|
274
|
+
it('stamps the correlation ID installed for the calling async continuation', async () => {
|
|
275
|
+
const logger = createLogger('mcp');
|
|
276
|
+
await runWithCorrelationId('corr-abc-123', async () => {
|
|
277
|
+
logger.error('inside scope');
|
|
278
|
+
});
|
|
279
|
+
await waitForLogFile('skillsmith-mcp-');
|
|
280
|
+
const content = readFileSync(findLogFile('skillsmith-mcp-'), 'utf8');
|
|
281
|
+
const record = JSON.parse(content.trim().split('\n')[0]);
|
|
282
|
+
expect(record.correlationId).toBe('corr-abc-123');
|
|
283
|
+
});
|
|
284
|
+
it('omits correlationId when called outside any runWithCorrelationId scope', async () => {
|
|
285
|
+
const logger = createLogger('cli');
|
|
286
|
+
logger.error('outside scope');
|
|
287
|
+
await waitForLogFile('skillsmith-cli-');
|
|
288
|
+
const content = readFileSync(findLogFile('skillsmith-cli-'), 'utf8');
|
|
289
|
+
const record = JSON.parse(content.trim().split('\n')[0]);
|
|
290
|
+
expect(record.correlationId).toBeUndefined();
|
|
291
|
+
});
|
|
292
|
+
it('keeps two concurrent, overlapping scopes from observing each others correlation ID', async () => {
|
|
293
|
+
const logger = createLogger('vscode');
|
|
294
|
+
const seen = {};
|
|
295
|
+
await Promise.all([
|
|
296
|
+
runWithCorrelationId('corr-A', async () => {
|
|
297
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
298
|
+
logger.error('call A');
|
|
299
|
+
}),
|
|
300
|
+
runWithCorrelationId('corr-B', async () => {
|
|
301
|
+
logger.error('call B');
|
|
302
|
+
}),
|
|
303
|
+
]);
|
|
304
|
+
await vi.waitFor(() => {
|
|
305
|
+
const files = readdirSync(tempDir).filter((f) => f.startsWith('skillsmith-vscode-'));
|
|
306
|
+
expect(files.length).toBeGreaterThan(0);
|
|
307
|
+
const content = readFileSync(join(tempDir, files[0]), 'utf8');
|
|
308
|
+
const lines = content.trim().split('\n').filter(Boolean);
|
|
309
|
+
expect(lines).toHaveLength(2);
|
|
310
|
+
for (const line of lines) {
|
|
311
|
+
const record = JSON.parse(line);
|
|
312
|
+
seen[record.msg] = record.correlationId;
|
|
313
|
+
}
|
|
314
|
+
return lines;
|
|
315
|
+
}, { timeout: 2000, interval: 20 });
|
|
316
|
+
expect(seen['call A']).toBe('corr-A');
|
|
317
|
+
expect(seen['call B']).toBe('corr-B');
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
//# sourceMappingURL=logger.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.test.js","sourceRoot":"","sources":["../../../src/logging/logger.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-883 / SMI-5615: Sensitive-data redaction, ported VERBATIM from the dead
|
|
3
|
+
* `packages/mcp-server/src/logger.ts` (lines 11-107) into the shared
|
|
4
|
+
* `@skillsmith/core` logging module. Same regex patterns, same behavior,
|
|
5
|
+
* same test coverage (see `redact.test.ts`) — this is tested, working code
|
|
6
|
+
* relocated, not rewritten.
|
|
7
|
+
*/
|
|
8
|
+
export declare function stripAnsi(text: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* SMI-883: Redact sensitive data from text before logging
|
|
11
|
+
* Exported for testing purposes
|
|
12
|
+
*/
|
|
13
|
+
export declare function redactSensitiveData(text: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* SMI-883: Recursively redact sensitive data from objects
|
|
16
|
+
* Exported for testing purposes
|
|
17
|
+
*
|
|
18
|
+
* SMI-5615 Mode-B diff-audit NEW-2: `seen` guards against circular
|
|
19
|
+
* references. The original (dead) `mcp-server/src/logger.ts` version this was
|
|
20
|
+
* ported from had no cycle guard — harmless there since it was never called
|
|
21
|
+
* in production, but this module IS the production write path now, so a
|
|
22
|
+
* circular `details`/`err` object (e.g. a thrown object with a `cause` cycle)
|
|
23
|
+
* must degrade to `'[Circular]'` rather than stack-overflowing, which would
|
|
24
|
+
* otherwise abort the whole record (including the redacted `msg` and
|
|
25
|
+
* `correlationId`) and fall through to the raw-message console fallback.
|
|
26
|
+
*
|
|
27
|
+
* `seen` tracks only the CURRENT recursion path (ancestors), not every object
|
|
28
|
+
* visited overall — each object is removed again once its own subtree
|
|
29
|
+
* finishes (the `finally` below). This is deliberate: the same object
|
|
30
|
+
* appearing twice at sibling positions (a shared, non-cyclic reference — a
|
|
31
|
+
* DAG, not a cycle) must redact normally both times, not get falsely flagged
|
|
32
|
+
* `'[Circular]'` just because it was visited once already elsewhere.
|
|
33
|
+
*/
|
|
34
|
+
export declare function redactSensitiveObject(obj: unknown, seen?: WeakSet<object>): unknown;
|
|
35
|
+
//# sourceMappingURL=redact.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../../src/logging/redact.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAqBH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG9C;AAmED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASxD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,OAAO,EACZ,IAAI,GAAE,OAAO,CAAC,MAAM,CAAiB,GACpC,OAAO,CAgBT"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-883 / SMI-5615: Sensitive-data redaction, ported VERBATIM from the dead
|
|
3
|
+
* `packages/mcp-server/src/logger.ts` (lines 11-107) into the shared
|
|
4
|
+
* `@skillsmith/core` logging module. Same regex patterns, same behavior,
|
|
5
|
+
* same test coverage (see `redact.test.ts`) — this is tested, working code
|
|
6
|
+
* relocated, not rewritten.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Strips ANSI/SGR escape sequences (`\x1b[...m` — the `chalk`-style color
|
|
10
|
+
* codes CLI call sites wrap error text in). SMI-5615 Mode-B diff-audit
|
|
11
|
+
* (Wave 2 pass): the disk-persisted record must not embed raw escape codes
|
|
12
|
+
* as log noise, AND a secret sitting immediately after an escape code's
|
|
13
|
+
* trailing letter (e.g. `\x1b[31msk_live_...`) can defeat
|
|
14
|
+
* `redactSensitiveData`'s leading `\b` word-boundary check — the `m`→`s`
|
|
15
|
+
* transition is word-to-word, not a boundary. Stripping ANSI BEFORE redacting
|
|
16
|
+
* closes that gap. Only applied on the DISK-persisted path (`logger.ts`'s
|
|
17
|
+
* `buildRecord`/`normalizeError`) — deliberately NOT applied to the
|
|
18
|
+
* console-mirrored path, which must keep chalk's colors for CLI terminal UX.
|
|
19
|
+
* Covers chalk's actual output (SGR codes only); not a full ANSI/VT100 strip.
|
|
20
|
+
*/
|
|
21
|
+
// \x1b is the intentional ESC byte an SGR sequence always starts with; this
|
|
22
|
+
// is the same pattern the widely-used `strip-ansi` npm package uses (also
|
|
23
|
+
// eslint-disabled there).
|
|
24
|
+
// eslint-disable-next-line no-control-regex
|
|
25
|
+
const ANSI_SGR_PATTERN = /\x1b\[[0-9;]*m/g;
|
|
26
|
+
export function stripAnsi(text) {
|
|
27
|
+
if (!text)
|
|
28
|
+
return text;
|
|
29
|
+
return text.replace(ANSI_SGR_PATTERN, '');
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* SMI-883: Sensitive data patterns to redact before logging
|
|
33
|
+
* Prevents API keys, tokens, passwords, and secrets from being written to disk
|
|
34
|
+
*/
|
|
35
|
+
const SENSITIVE_PATTERNS = [
|
|
36
|
+
// GitHub tokens
|
|
37
|
+
{ pattern: /\b(ghp_[a-zA-Z0-9]{36})\b/g, replacement: 'ghp_[REDACTED]' },
|
|
38
|
+
{ pattern: /\b(github_pat_[a-zA-Z0-9_]{22,})\b/g, replacement: 'github_pat_[REDACTED]' },
|
|
39
|
+
{ pattern: /\b(gho_[a-zA-Z0-9]{36})\b/g, replacement: 'gho_[REDACTED]' },
|
|
40
|
+
{ pattern: /\b(ghs_[a-zA-Z0-9]{36})\b/g, replacement: 'ghs_[REDACTED]' },
|
|
41
|
+
{ pattern: /\b(ghu_[a-zA-Z0-9]{36})\b/g, replacement: 'ghu_[REDACTED]' },
|
|
42
|
+
{ pattern: /\b(ghr_[a-zA-Z0-9]{36})\b/g, replacement: 'ghr_[REDACTED]' },
|
|
43
|
+
// Linear API keys
|
|
44
|
+
{ pattern: /\b(lin_api_[a-zA-Z0-9]{32,})\b/g, replacement: 'lin_api_[REDACTED]' },
|
|
45
|
+
// Stripe keys
|
|
46
|
+
{ pattern: /\b(sk_live_[a-zA-Z0-9]{24,})\b/g, replacement: 'sk_live_[REDACTED]' },
|
|
47
|
+
{ pattern: /\b(sk_test_[a-zA-Z0-9]{24,})\b/g, replacement: 'sk_test_[REDACTED]' },
|
|
48
|
+
{ pattern: /\b(pk_live_[a-zA-Z0-9]{24,})\b/g, replacement: 'pk_live_[REDACTED]' },
|
|
49
|
+
{ pattern: /\b(pk_test_[a-zA-Z0-9]{24,})\b/g, replacement: 'pk_test_[REDACTED]' },
|
|
50
|
+
// OpenAI API keys
|
|
51
|
+
{ pattern: /\b(sk-[a-zA-Z0-9]{48,})\b/g, replacement: 'sk-[REDACTED]' },
|
|
52
|
+
// Anthropic API keys
|
|
53
|
+
{ pattern: /\b(sk-ant-[a-zA-Z0-9-]{32,})\b/g, replacement: 'sk-ant-[REDACTED]' },
|
|
54
|
+
// AWS keys
|
|
55
|
+
{ pattern: /\b(AKIA[A-Z0-9]{16})\b/g, replacement: 'AKIA[REDACTED]' },
|
|
56
|
+
// Slack tokens
|
|
57
|
+
{ pattern: /\b(xox[boaprs]-[a-zA-Z0-9-]{10,})\b/g, replacement: 'xox*-[REDACTED]' },
|
|
58
|
+
// npm tokens
|
|
59
|
+
{ pattern: /\b(npm_[a-zA-Z0-9]{36})\b/g, replacement: 'npm_[REDACTED]' },
|
|
60
|
+
// Bearer tokens
|
|
61
|
+
{ pattern: /\bBearer\s+([a-zA-Z0-9_\-.]{20,})/gi, replacement: 'Bearer [REDACTED]' },
|
|
62
|
+
// Basic auth
|
|
63
|
+
{ pattern: /\bBasic\s+([a-zA-Z0-9+/=]{20,})/gi, replacement: 'Basic [REDACTED]' },
|
|
64
|
+
// JWT tokens
|
|
65
|
+
{
|
|
66
|
+
pattern: /\beyJ[a-zA-Z0-9_-]*\.eyJ[a-zA-Z0-9_-]*\.[a-zA-Z0-9_-]+\b/g,
|
|
67
|
+
replacement: '[JWT_REDACTED]',
|
|
68
|
+
},
|
|
69
|
+
// Generic API key patterns
|
|
70
|
+
{
|
|
71
|
+
pattern: /\b(api[_-]?key|apikey)\s*[=:]\s*["']?([a-zA-Z0-9_-]{8,})["']?/gi,
|
|
72
|
+
replacement: 'api_key=[REDACTED]',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
pattern: /\b(token|auth[_-]?token)\s*[=:]\s*["']?([a-zA-Z0-9_-]{8,})["']?/gi,
|
|
76
|
+
replacement: 'token=[REDACTED]',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
pattern: /\b(password|passwd|pwd)\s*[=:]\s*["']?([^"'\s]{4,})["']?/gi,
|
|
80
|
+
replacement: 'password=[REDACTED]',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
pattern: /\b(secret|client[_-]?secret)\s*[=:]\s*["']?([a-zA-Z0-9_-]{8,})["']?/gi,
|
|
84
|
+
replacement: 'secret=[REDACTED]',
|
|
85
|
+
},
|
|
86
|
+
// Connection strings with passwords
|
|
87
|
+
{ pattern: /(:\/\/[^:]+:)([^@]+)(@)/gi, replacement: '$1[REDACTED]$3' },
|
|
88
|
+
// Private keys
|
|
89
|
+
{
|
|
90
|
+
pattern: /-----BEGIN\s+(RSA\s+)?PRIVATE\s+KEY-----[\s\S]*?-----END\s+(RSA\s+)?PRIVATE\s+KEY-----/gi,
|
|
91
|
+
replacement: '-----[PRIVATE KEY REDACTED]-----',
|
|
92
|
+
},
|
|
93
|
+
];
|
|
94
|
+
/**
|
|
95
|
+
* SMI-883: Redact sensitive data from text before logging
|
|
96
|
+
* Exported for testing purposes
|
|
97
|
+
*/
|
|
98
|
+
export function redactSensitiveData(text) {
|
|
99
|
+
if (!text)
|
|
100
|
+
return text;
|
|
101
|
+
let redacted = text;
|
|
102
|
+
for (const { pattern, replacement } of SENSITIVE_PATTERNS) {
|
|
103
|
+
// Reset lastIndex for global regex patterns to ensure all matches are found
|
|
104
|
+
pattern.lastIndex = 0;
|
|
105
|
+
redacted = redacted.replace(pattern, replacement);
|
|
106
|
+
}
|
|
107
|
+
return redacted;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* SMI-883: Recursively redact sensitive data from objects
|
|
111
|
+
* Exported for testing purposes
|
|
112
|
+
*
|
|
113
|
+
* SMI-5615 Mode-B diff-audit NEW-2: `seen` guards against circular
|
|
114
|
+
* references. The original (dead) `mcp-server/src/logger.ts` version this was
|
|
115
|
+
* ported from had no cycle guard — harmless there since it was never called
|
|
116
|
+
* in production, but this module IS the production write path now, so a
|
|
117
|
+
* circular `details`/`err` object (e.g. a thrown object with a `cause` cycle)
|
|
118
|
+
* must degrade to `'[Circular]'` rather than stack-overflowing, which would
|
|
119
|
+
* otherwise abort the whole record (including the redacted `msg` and
|
|
120
|
+
* `correlationId`) and fall through to the raw-message console fallback.
|
|
121
|
+
*
|
|
122
|
+
* `seen` tracks only the CURRENT recursion path (ancestors), not every object
|
|
123
|
+
* visited overall — each object is removed again once its own subtree
|
|
124
|
+
* finishes (the `finally` below). This is deliberate: the same object
|
|
125
|
+
* appearing twice at sibling positions (a shared, non-cyclic reference — a
|
|
126
|
+
* DAG, not a cycle) must redact normally both times, not get falsely flagged
|
|
127
|
+
* `'[Circular]'` just because it was visited once already elsewhere.
|
|
128
|
+
*/
|
|
129
|
+
export function redactSensitiveObject(obj, seen = new WeakSet()) {
|
|
130
|
+
if (obj === null || obj === undefined)
|
|
131
|
+
return obj;
|
|
132
|
+
if (typeof obj === 'string')
|
|
133
|
+
return redactSensitiveData(obj);
|
|
134
|
+
if (typeof obj !== 'object')
|
|
135
|
+
return obj;
|
|
136
|
+
if (seen.has(obj))
|
|
137
|
+
return '[Circular]';
|
|
138
|
+
seen.add(obj);
|
|
139
|
+
try {
|
|
140
|
+
if (Array.isArray(obj))
|
|
141
|
+
return obj.map((item) => redactSensitiveObject(item, seen));
|
|
142
|
+
const result = {};
|
|
143
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
144
|
+
result[key] = redactSensitiveObject(value, seen);
|
|
145
|
+
}
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
seen.delete(obj);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=redact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.js","sourceRoot":"","sources":["../../../src/logging/redact.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-883 / SMI-5615: Tests for logger sensitive data redaction, ported from
|
|
3
|
+
* `packages/mcp-server/tests/unit/logger.test.ts` — same coverage, new
|
|
4
|
+
* location (`@skillsmith/core`'s `logging/redact.ts`).
|
|
5
|
+
* Ensures API keys, tokens, passwords, and secrets are never written to disk.
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=redact.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.test.d.ts","sourceRoot":"","sources":["../../../src/logging/redact.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|