proofscan 0.10.1 → 0.10.3
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 +62 -0
- package/dist/cli.js +4 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/index.d.ts +1 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +2 -0
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/popl.d.ts +16 -0
- package/dist/commands/popl.d.ts.map +1 -0
- package/dist/commands/popl.js +284 -0
- package/dist/commands/popl.js.map +1 -0
- package/dist/popl/artifacts.d.ts +78 -0
- package/dist/popl/artifacts.d.ts.map +1 -0
- package/dist/popl/artifacts.js +205 -0
- package/dist/popl/artifacts.js.map +1 -0
- package/dist/popl/index.d.ts +10 -0
- package/dist/popl/index.d.ts.map +1 -0
- package/dist/popl/index.js +14 -0
- package/dist/popl/index.js.map +1 -0
- package/dist/popl/sanitizer.d.ts +86 -0
- package/dist/popl/sanitizer.d.ts.map +1 -0
- package/dist/popl/sanitizer.js +271 -0
- package/dist/popl/sanitizer.js.map +1 -0
- package/dist/popl/service.d.ts +46 -0
- package/dist/popl/service.d.ts.map +1 -0
- package/dist/popl/service.js +231 -0
- package/dist/popl/service.js.map +1 -0
- package/dist/popl/types.d.ts +195 -0
- package/dist/popl/types.d.ts.map +1 -0
- package/dist/popl/types.js +20 -0
- package/dist/popl/types.js.map +1 -0
- package/dist/proxy/mcp-server.d.ts +4 -0
- package/dist/proxy/mcp-server.d.ts.map +1 -1
- package/dist/proxy/mcp-server.js +37 -1
- package/dist/proxy/mcp-server.js.map +1 -1
- package/dist/proxy/tool-aggregator.d.ts +27 -1
- package/dist/proxy/tool-aggregator.d.ts.map +1 -1
- package/dist/proxy/tool-aggregator.js +62 -1
- package/dist/proxy/tool-aggregator.js.map +1 -1
- package/dist/shell/popl-commands.d.ts +26 -0
- package/dist/shell/popl-commands.d.ts.map +1 -0
- package/dist/shell/popl-commands.js +281 -0
- package/dist/shell/popl-commands.js.map +1 -0
- package/dist/shell/repl.d.ts.map +1 -1
- package/dist/shell/repl.js +6 -0
- package/dist/shell/repl.js.map +1 -1
- package/package.json +6 -4
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POPL Artifacts Generator (Phase 6.0)
|
|
3
|
+
*
|
|
4
|
+
* Generates sanitized artifacts for POPL entries:
|
|
5
|
+
* - status.json: Safe summary of session/connector state
|
|
6
|
+
* - logs.sanitized.jsonl: Sanitized proxy logs
|
|
7
|
+
* - rpc.sanitized.jsonl: Sanitized RPC events
|
|
8
|
+
* - validation-run.log: Validation/generation log
|
|
9
|
+
*/
|
|
10
|
+
import { EventsStore } from '../db/events-store.js';
|
|
11
|
+
import { getEventsDb } from '../db/connection.js';
|
|
12
|
+
import { sanitizeRpcEvent, hashFileContent, SANITIZER_RULESET_VERSION, } from './sanitizer.js';
|
|
13
|
+
/**
|
|
14
|
+
* Calculate latency percentiles from RPC calls
|
|
15
|
+
*/
|
|
16
|
+
function calculateLatencyPercentiles(rpcs) {
|
|
17
|
+
const latencies = [];
|
|
18
|
+
for (const rpc of rpcs) {
|
|
19
|
+
if (rpc.request_ts && rpc.response_ts) {
|
|
20
|
+
const reqTs = new Date(rpc.request_ts).getTime();
|
|
21
|
+
const resTs = new Date(rpc.response_ts).getTime();
|
|
22
|
+
const latency = resTs - reqTs;
|
|
23
|
+
if (latency >= 0) {
|
|
24
|
+
latencies.push(latency);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (latencies.length === 0) {
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
// Sort for percentile calculation
|
|
32
|
+
latencies.sort((a, b) => a - b);
|
|
33
|
+
const p50Index = Math.floor(latencies.length * 0.5);
|
|
34
|
+
const p95Index = Math.floor(latencies.length * 0.95);
|
|
35
|
+
return {
|
|
36
|
+
p50: latencies[p50Index],
|
|
37
|
+
p95: latencies[Math.min(p95Index, latencies.length - 1)],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Generate status.json artifact
|
|
42
|
+
*/
|
|
43
|
+
export function generateStatusArtifact(session, rpcs, events) {
|
|
44
|
+
// Calculate duration
|
|
45
|
+
let durationMs = null;
|
|
46
|
+
if (session.ended_at) {
|
|
47
|
+
const startMs = new Date(session.started_at).getTime();
|
|
48
|
+
const endMs = new Date(session.ended_at).getTime();
|
|
49
|
+
durationMs = endMs - startMs;
|
|
50
|
+
}
|
|
51
|
+
// Calculate error count
|
|
52
|
+
const errorCount = rpcs.filter((r) => r.success === 0).length;
|
|
53
|
+
// Calculate latency percentiles
|
|
54
|
+
const { p50, p95 } = calculateLatencyPercentiles(rpcs);
|
|
55
|
+
// Get unique RPC methods
|
|
56
|
+
const rpcMethods = [...new Set(rpcs.map((r) => r.method))].sort();
|
|
57
|
+
const status = {
|
|
58
|
+
generated_at: new Date().toISOString(),
|
|
59
|
+
sanitizer_version: SANITIZER_RULESET_VERSION,
|
|
60
|
+
session: {
|
|
61
|
+
session_id: session.session_id,
|
|
62
|
+
connector_id: session.connector_id,
|
|
63
|
+
started_at: session.started_at,
|
|
64
|
+
ended_at: session.ended_at,
|
|
65
|
+
exit_reason: session.exit_reason,
|
|
66
|
+
rpc_count: rpcs.length,
|
|
67
|
+
event_count: events.length,
|
|
68
|
+
duration_ms: durationMs,
|
|
69
|
+
actor_kind: session.actor_kind,
|
|
70
|
+
},
|
|
71
|
+
summary: {
|
|
72
|
+
rpc_total: rpcs.length,
|
|
73
|
+
errors: errorCount,
|
|
74
|
+
latency_ms_p50: p50,
|
|
75
|
+
latency_ms_p95: p95,
|
|
76
|
+
},
|
|
77
|
+
rpc_methods: rpcMethods,
|
|
78
|
+
};
|
|
79
|
+
const content = JSON.stringify(status, null, 2);
|
|
80
|
+
const sha256 = hashFileContent(content);
|
|
81
|
+
return {
|
|
82
|
+
artifact: {
|
|
83
|
+
name: 'status.json',
|
|
84
|
+
path: 'status.json',
|
|
85
|
+
sha256,
|
|
86
|
+
},
|
|
87
|
+
content,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Generate rpc.sanitized.jsonl artifact
|
|
92
|
+
*/
|
|
93
|
+
export function generateRpcArtifact(sessionId, configDir) {
|
|
94
|
+
const db = getEventsDb(configDir);
|
|
95
|
+
// Get all events for this session
|
|
96
|
+
const events = db
|
|
97
|
+
.prepare(`
|
|
98
|
+
SELECT * FROM events
|
|
99
|
+
WHERE session_id = ?
|
|
100
|
+
ORDER BY ts ASC
|
|
101
|
+
`)
|
|
102
|
+
.all(sessionId);
|
|
103
|
+
// Sanitize each event and write as JSONL
|
|
104
|
+
const lines = [];
|
|
105
|
+
for (const event of events) {
|
|
106
|
+
const sanitized = sanitizeRpcEvent(event);
|
|
107
|
+
lines.push(JSON.stringify(sanitized));
|
|
108
|
+
}
|
|
109
|
+
const content = lines.join('\n') + (lines.length > 0 ? '\n' : '');
|
|
110
|
+
const sha256 = hashFileContent(content);
|
|
111
|
+
return {
|
|
112
|
+
artifact: {
|
|
113
|
+
name: 'rpc.sanitized.jsonl',
|
|
114
|
+
path: 'rpc.sanitized.jsonl',
|
|
115
|
+
sha256,
|
|
116
|
+
},
|
|
117
|
+
content,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Generate logs.sanitized.jsonl artifact (if proxy logs exist)
|
|
122
|
+
*/
|
|
123
|
+
export function generateLogsArtifact(logsPath, sessionStarted, sessionEnded) {
|
|
124
|
+
// For now, return null - proxy logs are separate from session data
|
|
125
|
+
// This will be implemented when we have access to proxy logs for the session window
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Generate validation-run.log artifact
|
|
130
|
+
*/
|
|
131
|
+
export function generateValidationArtifact(sessionId, steps) {
|
|
132
|
+
const lines = [
|
|
133
|
+
`# POPL Entry Validation Log`,
|
|
134
|
+
`# Generated: ${new Date().toISOString()}`,
|
|
135
|
+
`# Session: ${sessionId}`,
|
|
136
|
+
`# Sanitizer Version: ${SANITIZER_RULESET_VERSION}`,
|
|
137
|
+
``,
|
|
138
|
+
`## Steps`,
|
|
139
|
+
...steps.map((step, i) => `${i + 1}. ${step}`),
|
|
140
|
+
``,
|
|
141
|
+
`## Result`,
|
|
142
|
+
`Entry generated successfully.`,
|
|
143
|
+
];
|
|
144
|
+
const content = lines.join('\n') + '\n';
|
|
145
|
+
const sha256 = hashFileContent(content);
|
|
146
|
+
return {
|
|
147
|
+
artifact: {
|
|
148
|
+
name: 'validation-run.log',
|
|
149
|
+
path: 'validation-run.log',
|
|
150
|
+
sha256,
|
|
151
|
+
},
|
|
152
|
+
content,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Generate all artifacts for a session POPL entry
|
|
157
|
+
*/
|
|
158
|
+
export async function generateSessionArtifacts(sessionId, configDir) {
|
|
159
|
+
const validationSteps = [];
|
|
160
|
+
// Get session data
|
|
161
|
+
validationSteps.push(`Loading session ${sessionId}`);
|
|
162
|
+
const eventsStore = new EventsStore(configDir);
|
|
163
|
+
const session = eventsStore.getSession(sessionId);
|
|
164
|
+
if (!session) {
|
|
165
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
166
|
+
}
|
|
167
|
+
validationSteps.push(`Found session for connector: ${session.connector_id}`);
|
|
168
|
+
// Get RPC calls
|
|
169
|
+
const rpcs = eventsStore.getRpcCallsBySession(sessionId);
|
|
170
|
+
validationSteps.push(`Found ${rpcs.length} RPC calls`);
|
|
171
|
+
// Get events
|
|
172
|
+
const db = getEventsDb(configDir);
|
|
173
|
+
const events = db
|
|
174
|
+
.prepare(`SELECT * FROM events WHERE session_id = ?`)
|
|
175
|
+
.all(sessionId);
|
|
176
|
+
validationSteps.push(`Found ${events.length} events`);
|
|
177
|
+
// Generate status.json
|
|
178
|
+
validationSteps.push('Generating status.json');
|
|
179
|
+
const statusResult = generateStatusArtifact(session, rpcs, events);
|
|
180
|
+
// Generate rpc.sanitized.jsonl
|
|
181
|
+
validationSteps.push('Generating rpc.sanitized.jsonl');
|
|
182
|
+
const rpcResult = generateRpcArtifact(sessionId, configDir);
|
|
183
|
+
// Generate validation log
|
|
184
|
+
validationSteps.push('Generating validation-run.log');
|
|
185
|
+
const validationResult = generateValidationArtifact(sessionId, validationSteps);
|
|
186
|
+
// Calculate summary for POPL.yml
|
|
187
|
+
const errorCount = rpcs.filter((r) => r.success === 0).length;
|
|
188
|
+
const { p50, p95 } = calculateLatencyPercentiles(rpcs);
|
|
189
|
+
const summary = {
|
|
190
|
+
rpc_total: rpcs.length,
|
|
191
|
+
errors: errorCount,
|
|
192
|
+
latency_ms_p50: p50,
|
|
193
|
+
latency_ms_p95: p95,
|
|
194
|
+
};
|
|
195
|
+
return {
|
|
196
|
+
artifacts: {
|
|
197
|
+
status: statusResult,
|
|
198
|
+
rpc: rpcResult,
|
|
199
|
+
validation: validationResult,
|
|
200
|
+
},
|
|
201
|
+
session: session,
|
|
202
|
+
summary,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=artifacts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../../src/popl/artifacts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POPL Module Exports (Phase 6.0)
|
|
3
|
+
*
|
|
4
|
+
* Public Observable Proof Ledger
|
|
5
|
+
*/
|
|
6
|
+
export { POPL_VERSION, TRUST_LABELS, type TrustLevel, type TargetKind, type RedactionPolicy, type PoplAuthor, type PoplTrust, type PoplTargetIds, type PoplTarget, type PoplCaptureSummary, type PoplMcpClient, type PoplMcpServer, type PoplMcp, type PoplCaptureWindow, type PoplCapture, type PoplArtifact, type PoplEvidencePolicy, type PoplEvidence, type PoplEntry, type PoplDocument, type CreatePoplOptions, type CreatePoplResult, type PoplConfig, } from './types.js';
|
|
7
|
+
export { SANITIZER_RULESET_VERSION, sanitize, sanitizeRpcPayload, sanitizeLogLine, sanitizeRpcEvent, hashValue, hashFileContent, type SanitizeResult, } from './sanitizer.js';
|
|
8
|
+
export { generateStatusArtifact, generateRpcArtifact, generateLogsArtifact, generateValidationArtifact, generateSessionArtifacts, type SessionStatus, type StatusJson, type ArtifactResult, type GeneratedArtifacts, } from './artifacts.js';
|
|
9
|
+
export { hasPoplDir, getPoplDir, getPoplEntriesDir, initPoplDir, loadPoplConfig, createSessionPoplEntry, listPoplEntries, readPoplEntry, } from './service.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/popl/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,OAAO,EACZ,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,UAAU,GAChB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,yBAAyB,EACzB,QAAQ,EACR,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,SAAS,EACT,eAAe,EACf,KAAK,cAAc,GACpB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,0BAA0B,EAC1B,wBAAwB,EACxB,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,kBAAkB,GACxB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,UAAU,EACV,UAAU,EACV,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,sBAAsB,EACtB,eAAe,EACf,aAAa,GACd,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POPL Module Exports (Phase 6.0)
|
|
3
|
+
*
|
|
4
|
+
* Public Observable Proof Ledger
|
|
5
|
+
*/
|
|
6
|
+
// Types
|
|
7
|
+
export { POPL_VERSION, TRUST_LABELS, } from './types.js';
|
|
8
|
+
// Sanitizer
|
|
9
|
+
export { SANITIZER_RULESET_VERSION, sanitize, sanitizeRpcPayload, sanitizeLogLine, sanitizeRpcEvent, hashValue, hashFileContent, } from './sanitizer.js';
|
|
10
|
+
// Artifacts
|
|
11
|
+
export { generateStatusArtifact, generateRpcArtifact, generateLogsArtifact, generateValidationArtifact, generateSessionArtifacts, } from './artifacts.js';
|
|
12
|
+
// Service
|
|
13
|
+
export { hasPoplDir, getPoplDir, getPoplEntriesDir, initPoplDir, loadPoplConfig, createSessionPoplEntry, listPoplEntries, readPoplEntry, } from './service.js';
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/popl/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,QAAQ;AACR,OAAO,EACL,YAAY,EACZ,YAAY,GAsBb,MAAM,YAAY,CAAC;AAEpB,YAAY;AACZ,OAAO,EACL,yBAAyB,EACzB,QAAQ,EACR,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,SAAS,EACT,eAAe,GAEhB,MAAM,gBAAgB,CAAC;AAExB,YAAY;AACZ,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,0BAA0B,EAC1B,wBAAwB,GAKzB,MAAM,gBAAgB,CAAC;AAExB,UAAU;AACV,OAAO,EACL,UAAU,EACV,UAAU,EACV,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,sBAAsB,EACtB,eAAe,EACf,aAAa,GACd,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POPL Sanitizer (Phase 6.0)
|
|
3
|
+
*
|
|
4
|
+
* Sanitization ruleset v1 for public disclosure safety.
|
|
5
|
+
*
|
|
6
|
+
* Rules:
|
|
7
|
+
* 1. Path removal - Windows and POSIX absolute paths
|
|
8
|
+
* 2. Secret token removal - Authorization, Bearer, api_key, token, secret
|
|
9
|
+
* 3. RPC payload handling - Replace values with hashes, keep key structure
|
|
10
|
+
*/
|
|
11
|
+
/** Current sanitization ruleset version */
|
|
12
|
+
export declare const SANITIZER_RULESET_VERSION = 1;
|
|
13
|
+
/**
|
|
14
|
+
* Result of sanitization
|
|
15
|
+
*/
|
|
16
|
+
export interface SanitizeResult {
|
|
17
|
+
/** Sanitized value */
|
|
18
|
+
value: unknown;
|
|
19
|
+
/** Number of items redacted */
|
|
20
|
+
redactedCount: number;
|
|
21
|
+
/** Categories of redactions made */
|
|
22
|
+
redactedCategories: Set<'path' | 'secret' | 'value'>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Compute SHA-256 hash of a value (first 16 chars)
|
|
26
|
+
*/
|
|
27
|
+
export declare function hashValue(value: unknown): string;
|
|
28
|
+
/**
|
|
29
|
+
* Sanitize a JSON-like value for public disclosure.
|
|
30
|
+
*
|
|
31
|
+
* @param value - Any JSON-serializable value
|
|
32
|
+
* @param options - Sanitization options
|
|
33
|
+
* @returns Sanitized value and statistics
|
|
34
|
+
*/
|
|
35
|
+
export declare function sanitize(value: unknown, options?: {
|
|
36
|
+
deep?: boolean;
|
|
37
|
+
context?: {
|
|
38
|
+
key?: string;
|
|
39
|
+
};
|
|
40
|
+
}): SanitizeResult;
|
|
41
|
+
/**
|
|
42
|
+
* Sanitize RPC payload for public disclosure.
|
|
43
|
+
*
|
|
44
|
+
* This is more aggressive than general sanitization:
|
|
45
|
+
* - Replaces all argument values with hashes
|
|
46
|
+
* - Keeps key structure for auditability
|
|
47
|
+
* - Stores original hash for verification
|
|
48
|
+
*
|
|
49
|
+
* @param payload - RPC arguments or result
|
|
50
|
+
* @returns Sanitized structure with hashes
|
|
51
|
+
*/
|
|
52
|
+
export declare function sanitizeRpcPayload(payload: Record<string, unknown> | null | undefined): {
|
|
53
|
+
sanitized: Record<string, unknown> | null;
|
|
54
|
+
payload_sha256: string;
|
|
55
|
+
keys: string[];
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Sanitize a log line for public disclosure.
|
|
59
|
+
*
|
|
60
|
+
* @param line - Log line object
|
|
61
|
+
* @returns Sanitized log line
|
|
62
|
+
*/
|
|
63
|
+
export declare function sanitizeLogLine(line: Record<string, unknown>): Record<string, unknown>;
|
|
64
|
+
/**
|
|
65
|
+
* Sanitize RPC event for public disclosure.
|
|
66
|
+
*
|
|
67
|
+
* @param event - RPC event object from events.db
|
|
68
|
+
* @returns Sanitized event
|
|
69
|
+
*/
|
|
70
|
+
export declare function sanitizeRpcEvent(event: {
|
|
71
|
+
event_id: string;
|
|
72
|
+
session_id: string;
|
|
73
|
+
rpc_id: string | null;
|
|
74
|
+
direction: string;
|
|
75
|
+
kind: string;
|
|
76
|
+
ts: string;
|
|
77
|
+
seq: number | null;
|
|
78
|
+
summary: string | null;
|
|
79
|
+
payload_hash: string | null;
|
|
80
|
+
raw_json: string | null;
|
|
81
|
+
}): Record<string, unknown>;
|
|
82
|
+
/**
|
|
83
|
+
* Compute SHA-256 hash of file contents
|
|
84
|
+
*/
|
|
85
|
+
export declare function hashFileContent(content: string | Buffer): string;
|
|
86
|
+
//# sourceMappingURL=sanitizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitizer.d.ts","sourceRoot":"","sources":["../../src/popl/sanitizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,2CAA2C;AAC3C,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAqE3C;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,sBAAsB;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,+BAA+B;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,oCAAoC;IACpC,kBAAkB,EAAE,GAAG,CAAC,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC,CAAC;CACtD;AAsCD;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAIhD;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CACtB,KAAK,EAAE,OAAO,EACd,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAO,GAC3D,cAAc,CAwDhB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,GAClD;IACD,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC1C,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB,CAyBA;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAGzB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAoD1B;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAEhE"}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POPL Sanitizer (Phase 6.0)
|
|
3
|
+
*
|
|
4
|
+
* Sanitization ruleset v1 for public disclosure safety.
|
|
5
|
+
*
|
|
6
|
+
* Rules:
|
|
7
|
+
* 1. Path removal - Windows and POSIX absolute paths
|
|
8
|
+
* 2. Secret token removal - Authorization, Bearer, api_key, token, secret
|
|
9
|
+
* 3. RPC payload handling - Replace values with hashes, keep key structure
|
|
10
|
+
*/
|
|
11
|
+
import { createHash } from 'crypto';
|
|
12
|
+
/** Current sanitization ruleset version */
|
|
13
|
+
export const SANITIZER_RULESET_VERSION = 1;
|
|
14
|
+
/** Maximum string length to process with regex (ReDoS prevention) */
|
|
15
|
+
const MAX_REGEX_INPUT_LENGTH = 1000;
|
|
16
|
+
/** Redacted placeholder for paths */
|
|
17
|
+
const REDACTED_PATH = '<redacted:path>';
|
|
18
|
+
/** Redacted placeholder for secrets */
|
|
19
|
+
const REDACTED_SECRET = '<redacted:secret>';
|
|
20
|
+
/** Redacted placeholder for values (RPC payloads) */
|
|
21
|
+
const REDACTED_VALUE = '<redacted:value>';
|
|
22
|
+
/**
|
|
23
|
+
* Patterns for detecting absolute paths
|
|
24
|
+
*/
|
|
25
|
+
const PATH_PATTERNS = [
|
|
26
|
+
// Windows: C:\Users\... or D:\...
|
|
27
|
+
/^[A-Za-z]:\\[^\s"']+/,
|
|
28
|
+
// Windows: \\server\share
|
|
29
|
+
/^\\\\[^\s"']+/,
|
|
30
|
+
// POSIX: /home/... /Users/... /var/... etc.
|
|
31
|
+
/^\/(?:home|Users|var|tmp|etc|opt|usr|root|mnt|media|srv|private)[^\s"']*/,
|
|
32
|
+
// Generic POSIX paths starting with / followed by word chars
|
|
33
|
+
/^\/[a-zA-Z0-9_-]+(?:\/[^\s"']*)?/,
|
|
34
|
+
];
|
|
35
|
+
/**
|
|
36
|
+
* Patterns for detecting secret-like keys
|
|
37
|
+
*/
|
|
38
|
+
const SECRET_KEY_PATTERNS = [
|
|
39
|
+
/^api[_-]?key$/i,
|
|
40
|
+
/^auth(?:orization)?$/i,
|
|
41
|
+
/^bearer$/i,
|
|
42
|
+
/^token$/i,
|
|
43
|
+
/^secret$/i,
|
|
44
|
+
/^password$/i,
|
|
45
|
+
/^passwd$/i,
|
|
46
|
+
/^credential/i,
|
|
47
|
+
/^private[_-]?key$/i,
|
|
48
|
+
/^access[_-]?token$/i,
|
|
49
|
+
/^refresh[_-]?token$/i,
|
|
50
|
+
/^session[_-]?(?:id|token)$/i,
|
|
51
|
+
/^cookie$/i,
|
|
52
|
+
/^x-api-key$/i,
|
|
53
|
+
/^x-auth/i,
|
|
54
|
+
];
|
|
55
|
+
/**
|
|
56
|
+
* Patterns for detecting secret-like values
|
|
57
|
+
*/
|
|
58
|
+
const SECRET_VALUE_PATTERNS = [
|
|
59
|
+
// Bearer tokens
|
|
60
|
+
/^Bearer\s+[A-Za-z0-9._-]+/i,
|
|
61
|
+
// Authorization header
|
|
62
|
+
/^Basic\s+[A-Za-z0-9+/=]+/i,
|
|
63
|
+
// JWT-like tokens (three base64 sections)
|
|
64
|
+
/^eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/,
|
|
65
|
+
// API keys (long alphanumeric strings)
|
|
66
|
+
/^[a-zA-Z0-9_-]{32,}$/,
|
|
67
|
+
// Hedera keys
|
|
68
|
+
/^302[a-fA-F0-9]{64,}/,
|
|
69
|
+
// secret:// references (not already masked)
|
|
70
|
+
/^secret:\/\/(?!\*\*\*$)/,
|
|
71
|
+
// dpapi: references
|
|
72
|
+
/^dpapi:[a-zA-Z0-9_-]+$/,
|
|
73
|
+
];
|
|
74
|
+
/**
|
|
75
|
+
* Check if a string looks like an absolute path
|
|
76
|
+
* Includes length check to prevent ReDoS attacks
|
|
77
|
+
*/
|
|
78
|
+
function isAbsolutePath(value) {
|
|
79
|
+
// Skip long strings to prevent ReDoS
|
|
80
|
+
if (value.length > MAX_REGEX_INPUT_LENGTH) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
return PATH_PATTERNS.some((pattern) => pattern.test(value));
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Check if a key name suggests it contains a secret
|
|
87
|
+
* Includes length check to prevent ReDoS attacks
|
|
88
|
+
*/
|
|
89
|
+
function isSecretKey(key) {
|
|
90
|
+
// Skip long strings to prevent ReDoS
|
|
91
|
+
if (key.length > MAX_REGEX_INPUT_LENGTH) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
return SECRET_KEY_PATTERNS.some((pattern) => pattern.test(key));
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Check if a value looks like a secret
|
|
98
|
+
* Includes length check to prevent ReDoS attacks
|
|
99
|
+
*/
|
|
100
|
+
function isSecretValue(value) {
|
|
101
|
+
// Skip long strings to prevent ReDoS
|
|
102
|
+
if (value.length > MAX_REGEX_INPUT_LENGTH) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
return SECRET_VALUE_PATTERNS.some((pattern) => pattern.test(value));
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Compute SHA-256 hash of a value (first 16 chars)
|
|
109
|
+
*/
|
|
110
|
+
export function hashValue(value) {
|
|
111
|
+
const str = typeof value === 'string' ? value : JSON.stringify(value);
|
|
112
|
+
const hash = createHash('sha256').update(str, 'utf8').digest('hex');
|
|
113
|
+
return hash.slice(0, 16);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Sanitize a JSON-like value for public disclosure.
|
|
117
|
+
*
|
|
118
|
+
* @param value - Any JSON-serializable value
|
|
119
|
+
* @param options - Sanitization options
|
|
120
|
+
* @returns Sanitized value and statistics
|
|
121
|
+
*/
|
|
122
|
+
export function sanitize(value, options = {}) {
|
|
123
|
+
let redactedCount = 0;
|
|
124
|
+
const redactedCategories = new Set();
|
|
125
|
+
function process(val, key) {
|
|
126
|
+
// Null/undefined pass through
|
|
127
|
+
if (val === null || val === undefined) {
|
|
128
|
+
return val;
|
|
129
|
+
}
|
|
130
|
+
// String processing
|
|
131
|
+
if (typeof val === 'string') {
|
|
132
|
+
// Check for paths
|
|
133
|
+
if (isAbsolutePath(val)) {
|
|
134
|
+
redactedCount++;
|
|
135
|
+
redactedCategories.add('path');
|
|
136
|
+
return REDACTED_PATH;
|
|
137
|
+
}
|
|
138
|
+
// Check for secret values
|
|
139
|
+
if (isSecretValue(val)) {
|
|
140
|
+
redactedCount++;
|
|
141
|
+
redactedCategories.add('secret');
|
|
142
|
+
return REDACTED_SECRET;
|
|
143
|
+
}
|
|
144
|
+
// Check if key suggests secret
|
|
145
|
+
if (key && isSecretKey(key) && val.length > 0) {
|
|
146
|
+
redactedCount++;
|
|
147
|
+
redactedCategories.add('secret');
|
|
148
|
+
return REDACTED_SECRET;
|
|
149
|
+
}
|
|
150
|
+
return val;
|
|
151
|
+
}
|
|
152
|
+
// Array processing
|
|
153
|
+
if (Array.isArray(val)) {
|
|
154
|
+
return val.map((item, i) => process(item, String(i)));
|
|
155
|
+
}
|
|
156
|
+
// Object processing
|
|
157
|
+
if (typeof val === 'object') {
|
|
158
|
+
const result = {};
|
|
159
|
+
for (const [k, v] of Object.entries(val)) {
|
|
160
|
+
result[k] = process(v, k);
|
|
161
|
+
}
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
164
|
+
// Primitives (number, boolean) pass through
|
|
165
|
+
return val;
|
|
166
|
+
}
|
|
167
|
+
const sanitized = process(value, options.context?.key);
|
|
168
|
+
return { value: sanitized, redactedCount, redactedCategories };
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Sanitize RPC payload for public disclosure.
|
|
172
|
+
*
|
|
173
|
+
* This is more aggressive than general sanitization:
|
|
174
|
+
* - Replaces all argument values with hashes
|
|
175
|
+
* - Keeps key structure for auditability
|
|
176
|
+
* - Stores original hash for verification
|
|
177
|
+
*
|
|
178
|
+
* @param payload - RPC arguments or result
|
|
179
|
+
* @returns Sanitized structure with hashes
|
|
180
|
+
*/
|
|
181
|
+
export function sanitizeRpcPayload(payload) {
|
|
182
|
+
if (payload === null || payload === undefined) {
|
|
183
|
+
return {
|
|
184
|
+
sanitized: null,
|
|
185
|
+
payload_sha256: hashValue(null),
|
|
186
|
+
keys: [],
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
// Get original hash before any processing
|
|
190
|
+
const payload_sha256 = hashValue(payload);
|
|
191
|
+
// Extract keys
|
|
192
|
+
const keys = Object.keys(payload);
|
|
193
|
+
// Create sanitized version with value hashes
|
|
194
|
+
const sanitized = {};
|
|
195
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
196
|
+
sanitized[key] = {
|
|
197
|
+
_type: typeof value,
|
|
198
|
+
_hash: hashValue(value),
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
return { sanitized, payload_sha256, keys };
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Sanitize a log line for public disclosure.
|
|
205
|
+
*
|
|
206
|
+
* @param line - Log line object
|
|
207
|
+
* @returns Sanitized log line
|
|
208
|
+
*/
|
|
209
|
+
export function sanitizeLogLine(line) {
|
|
210
|
+
const result = sanitize(line);
|
|
211
|
+
return result.value;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Sanitize RPC event for public disclosure.
|
|
215
|
+
*
|
|
216
|
+
* @param event - RPC event object from events.db
|
|
217
|
+
* @returns Sanitized event
|
|
218
|
+
*/
|
|
219
|
+
export function sanitizeRpcEvent(event) {
|
|
220
|
+
// Basic fields (safe to include)
|
|
221
|
+
const sanitized = {
|
|
222
|
+
event_id: event.event_id,
|
|
223
|
+
session_id: event.session_id,
|
|
224
|
+
rpc_id: event.rpc_id,
|
|
225
|
+
direction: event.direction,
|
|
226
|
+
kind: event.kind,
|
|
227
|
+
ts: event.ts,
|
|
228
|
+
seq: event.seq,
|
|
229
|
+
summary: event.summary ? sanitize(event.summary).value : null,
|
|
230
|
+
payload_hash: event.payload_hash,
|
|
231
|
+
};
|
|
232
|
+
// Process raw_json if present
|
|
233
|
+
if (event.raw_json) {
|
|
234
|
+
try {
|
|
235
|
+
const parsed = JSON.parse(event.raw_json);
|
|
236
|
+
// For request/response, sanitize params/result
|
|
237
|
+
if (parsed.params) {
|
|
238
|
+
const { sanitized: sanParams, payload_sha256, keys } = sanitizeRpcPayload(parsed.params);
|
|
239
|
+
sanitized.params_keys = keys;
|
|
240
|
+
sanitized.params_sha256 = payload_sha256;
|
|
241
|
+
// Don't include sanitized params - just keys and hash
|
|
242
|
+
}
|
|
243
|
+
if (parsed.result !== undefined) {
|
|
244
|
+
const resultHash = hashValue(parsed.result);
|
|
245
|
+
sanitized.result_sha256 = resultHash;
|
|
246
|
+
sanitized.result_type = typeof parsed.result;
|
|
247
|
+
}
|
|
248
|
+
if (parsed.error) {
|
|
249
|
+
// Error codes/messages are generally safe
|
|
250
|
+
sanitized.error_code = parsed.error.code;
|
|
251
|
+
sanitized.error_message = sanitize(parsed.error.message).value;
|
|
252
|
+
}
|
|
253
|
+
// Method name is safe
|
|
254
|
+
if (parsed.method) {
|
|
255
|
+
sanitized.method = parsed.method;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
// If parsing fails, just note that
|
|
260
|
+
sanitized.raw_json_parse_error = true;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return sanitized;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Compute SHA-256 hash of file contents
|
|
267
|
+
*/
|
|
268
|
+
export function hashFileContent(content) {
|
|
269
|
+
return createHash('sha256').update(content).digest('hex');
|
|
270
|
+
}
|
|
271
|
+
//# sourceMappingURL=sanitizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitizer.js","sourceRoot":"","sources":["../../src/popl/sanitizer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POPL Service Layer (Phase 6.0)
|
|
3
|
+
*
|
|
4
|
+
* Core service for POPL entry generation.
|
|
5
|
+
* Shared by CLI and shell - neither knows about @references.
|
|
6
|
+
*/
|
|
7
|
+
import { type PoplDocument, type PoplConfig, type CreatePoplOptions, type CreatePoplResult } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Check if .popl directory exists
|
|
10
|
+
*/
|
|
11
|
+
export declare function hasPoplDir(root: string): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Get path to .popl directory
|
|
14
|
+
*/
|
|
15
|
+
export declare function getPoplDir(root: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Get path to popl_entries directory
|
|
18
|
+
*/
|
|
19
|
+
export declare function getPoplEntriesDir(root: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Initialize .popl directory structure
|
|
22
|
+
*/
|
|
23
|
+
export declare function initPoplDir(root: string): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Load POPL config from .popl/config.json
|
|
26
|
+
* Returns empty config with warning on parse error.
|
|
27
|
+
*/
|
|
28
|
+
export declare function loadPoplConfig(root: string): Promise<PoplConfig>;
|
|
29
|
+
/**
|
|
30
|
+
* Create a POPL entry for a session
|
|
31
|
+
*
|
|
32
|
+
* This is the core service function called by both CLI and shell.
|
|
33
|
+
*/
|
|
34
|
+
export declare function createSessionPoplEntry(sessionId: string, configDir: string, options: Omit<CreatePoplOptions, 'kind' | 'ids'>): Promise<CreatePoplResult>;
|
|
35
|
+
/**
|
|
36
|
+
* List existing POPL entries
|
|
37
|
+
*/
|
|
38
|
+
export declare function listPoplEntries(root: string): Promise<{
|
|
39
|
+
id: string;
|
|
40
|
+
path: string;
|
|
41
|
+
}[]>;
|
|
42
|
+
/**
|
|
43
|
+
* Read a POPL entry document
|
|
44
|
+
*/
|
|
45
|
+
export declare function readPoplEntry(entryPath: string): Promise<PoplDocument | null>;
|
|
46
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/popl/service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,OAAO,EAGL,KAAK,YAAY,EAEjB,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EAGtB,MAAM,YAAY,CAAC;AAapB;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEhD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB7D;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAiBtE;AA8BD;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,KAAK,CAAC,GAC/C,OAAO,CAAC,gBAAgB,CAAC,CA2G3B;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,CAgBzC;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAanF"}
|