proofscan 0.10.61 → 0.11.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.ja.md +1 -0
- package/README.md +2 -0
- package/dist/a2a/agent-card.d.ts +40 -0
- package/dist/a2a/agent-card.d.ts.map +1 -0
- package/dist/a2a/agent-card.js +227 -0
- package/dist/a2a/agent-card.js.map +1 -0
- package/dist/a2a/client.d.ts +169 -0
- package/dist/a2a/client.d.ts.map +1 -0
- package/dist/a2a/client.js +854 -0
- package/dist/a2a/client.js.map +1 -0
- package/dist/a2a/config.d.ts +35 -0
- package/dist/a2a/config.d.ts.map +1 -0
- package/dist/a2a/config.js +474 -0
- package/dist/a2a/config.js.map +1 -0
- package/dist/a2a/index.d.ts +11 -0
- package/dist/a2a/index.d.ts.map +1 -0
- package/dist/a2a/index.js +11 -0
- package/dist/a2a/index.js.map +1 -0
- package/dist/a2a/normalizer.d.ts +66 -0
- package/dist/a2a/normalizer.d.ts.map +1 -0
- package/dist/a2a/normalizer.js +146 -0
- package/dist/a2a/normalizer.js.map +1 -0
- package/dist/a2a/session-manager.d.ts +81 -0
- package/dist/a2a/session-manager.d.ts.map +1 -0
- package/dist/a2a/session-manager.js +176 -0
- package/dist/a2a/session-manager.js.map +1 -0
- package/dist/a2a/types.d.ts +249 -0
- package/dist/a2a/types.d.ts.map +1 -0
- package/dist/a2a/types.js +8 -0
- package/dist/a2a/types.js.map +1 -0
- package/dist/cli.d.ts +2 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +8 -3
- package/dist/cli.js.map +1 -1
- package/dist/commands/agent.d.ts +12 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +339 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +12 -10
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/connectors.js +2 -2
- package/dist/commands/connectors.js.map +1 -1
- package/dist/commands/index.d.ts +2 -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/plans.js +1 -1
- package/dist/commands/plans.js.map +1 -1
- package/dist/commands/record.js +5 -4
- package/dist/commands/record.js.map +1 -1
- package/dist/commands/rpc.d.ts.map +1 -1
- package/dist/commands/rpc.js +220 -3
- package/dist/commands/rpc.js.map +1 -1
- package/dist/commands/scan.d.ts.map +1 -1
- package/dist/commands/scan.js +8 -10
- package/dist/commands/scan.js.map +1 -1
- package/dist/commands/secrets.d.ts.map +1 -1
- package/dist/commands/secrets.js +11 -10
- package/dist/commands/secrets.js.map +1 -1
- package/dist/commands/sessions.js +2 -2
- package/dist/commands/sessions.js.map +1 -1
- package/dist/commands/summary.d.ts.map +1 -1
- package/dist/commands/summary.js +4 -2
- package/dist/commands/summary.js.map +1 -1
- package/dist/commands/task.d.ts +14 -0
- package/dist/commands/task.d.ts.map +1 -0
- package/dist/commands/task.js +520 -0
- package/dist/commands/task.js.map +1 -0
- package/dist/db/agent-cache-store.d.ts +57 -0
- package/dist/db/agent-cache-store.d.ts.map +1 -0
- package/dist/db/agent-cache-store.js +99 -0
- package/dist/db/agent-cache-store.js.map +1 -0
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +86 -1
- package/dist/db/connection.js.map +1 -1
- package/dist/db/events-store.d.ts +321 -7
- package/dist/db/events-store.d.ts.map +1 -1
- package/dist/db/events-store.js +659 -31
- package/dist/db/events-store.js.map +1 -1
- package/dist/db/index.d.ts +2 -0
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +2 -0
- package/dist/db/index.js.map +1 -1
- package/dist/db/proofs-store.d.ts +8 -1
- package/dist/db/proofs-store.d.ts.map +1 -1
- package/dist/db/proofs-store.js +18 -8
- package/dist/db/proofs-store.js.map +1 -1
- package/dist/db/schema.d.ts +27 -3
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +201 -5
- package/dist/db/schema.js.map +1 -1
- package/dist/db/targets-store.d.ts +79 -0
- package/dist/db/targets-store.d.ts.map +1 -0
- package/dist/db/targets-store.js +150 -0
- package/dist/db/targets-store.js.map +1 -0
- package/dist/db/tool-analysis.d.ts +15 -3
- package/dist/db/tool-analysis.d.ts.map +1 -1
- package/dist/db/tool-analysis.js +35 -17
- package/dist/db/tool-analysis.js.map +1 -1
- package/dist/db/types.d.ts +86 -2
- package/dist/db/types.d.ts.map +1 -1
- package/dist/db/types.js +1 -1
- package/dist/filter/fields.d.ts.map +1 -1
- package/dist/filter/fields.js +22 -0
- package/dist/filter/fields.js.map +1 -1
- package/dist/filter/parser.js +2 -2
- package/dist/filter/parser.js.map +1 -1
- package/dist/filter/types.d.ts +1 -1
- package/dist/filter/types.d.ts.map +1 -1
- package/dist/html/analytics.test.ts +682 -0
- package/dist/html/analytics.ts +499 -0
- package/dist/html/browser.ts +39 -0
- package/dist/html/index.ts +97 -0
- package/dist/html/rpc-inspector.test.ts +529 -0
- package/dist/html/rpc-inspector.ts +1700 -0
- package/dist/html/templates.js +4 -4
- package/dist/html/templates.js.map +1 -1
- package/dist/html/templates.test.ts +861 -0
- package/dist/html/templates.ts +3163 -0
- package/dist/html/trace-viewer.html +624 -0
- package/dist/html/types.d.ts +3 -3
- package/dist/html/types.d.ts.map +1 -1
- package/dist/html/types.ts +491 -0
- package/dist/html/utils.ts +107 -0
- package/dist/monitor/data/connectors.d.ts.map +1 -1
- package/dist/monitor/data/connectors.js +113 -8
- package/dist/monitor/data/connectors.js.map +1 -1
- package/dist/monitor/data/popl.js +2 -2
- package/dist/monitor/data/popl.js.map +1 -1
- package/dist/monitor/routes/api.js +2 -2
- package/dist/monitor/routes/api.js.map +1 -1
- package/dist/monitor/routes/connectors.js +15 -15
- package/dist/monitor/routes/connectors.js.map +1 -1
- package/dist/monitor/routes/popl.js +5 -5
- package/dist/monitor/routes/popl.js.map +1 -1
- package/dist/monitor/templates/components.js +2 -2
- package/dist/monitor/templates/components.js.map +1 -1
- package/dist/monitor/templates/popl.js +4 -4
- package/dist/monitor/templates/popl.js.map +1 -1
- package/dist/monitor/types.d.ts +2 -2
- package/dist/monitor/types.d.ts.map +1 -1
- package/dist/proxy/bridge-utils.d.ts +41 -0
- package/dist/proxy/bridge-utils.d.ts.map +1 -0
- package/dist/proxy/bridge-utils.js +60 -0
- package/dist/proxy/bridge-utils.js.map +1 -0
- package/dist/proxy/ipc-client.d.ts.map +1 -1
- package/dist/proxy/ipc-client.js +1 -2
- package/dist/proxy/ipc-client.js.map +1 -1
- package/dist/proxy/ipc-server.d.ts.map +1 -1
- package/dist/proxy/ipc-server.js +4 -2
- package/dist/proxy/ipc-server.js.map +1 -1
- package/dist/proxy/mcp-server.d.ts +31 -0
- package/dist/proxy/mcp-server.d.ts.map +1 -1
- package/dist/proxy/mcp-server.js +393 -4
- package/dist/proxy/mcp-server.js.map +1 -1
- package/dist/proxy/types.d.ts +95 -0
- package/dist/proxy/types.d.ts.map +1 -1
- package/dist/secrets/management.d.ts +2 -2
- package/dist/secrets/management.d.ts.map +1 -1
- package/dist/secrets/management.js +7 -7
- package/dist/secrets/management.js.map +1 -1
- package/dist/shell/completer.d.ts.map +1 -1
- package/dist/shell/completer.js +16 -0
- package/dist/shell/completer.js.map +1 -1
- package/dist/shell/context-applicator.d.ts.map +1 -1
- package/dist/shell/context-applicator.js +32 -0
- package/dist/shell/context-applicator.js.map +1 -1
- package/dist/shell/filter-mappers.d.ts +5 -1
- package/dist/shell/filter-mappers.d.ts.map +1 -1
- package/dist/shell/filter-mappers.js +12 -0
- package/dist/shell/filter-mappers.js.map +1 -1
- package/dist/shell/find-command.js +13 -13
- package/dist/shell/find-command.js.map +1 -1
- package/dist/shell/inscribe-commands.js +5 -5
- package/dist/shell/inscribe-commands.js.map +1 -1
- package/dist/shell/pager/less-pager.d.ts +1 -1
- package/dist/shell/pager/less-pager.d.ts.map +1 -1
- package/dist/shell/pager/less-pager.js +5 -2
- package/dist/shell/pager/less-pager.js.map +1 -1
- package/dist/shell/pager/more-pager.d.ts +1 -1
- package/dist/shell/pager/more-pager.d.ts.map +1 -1
- package/dist/shell/pager/more-pager.js +3 -2
- package/dist/shell/pager/more-pager.js.map +1 -1
- package/dist/shell/pager/renderer.d.ts.map +1 -1
- package/dist/shell/pager/renderer.js +66 -15
- package/dist/shell/pager/renderer.js.map +1 -1
- package/dist/shell/pager/types.d.ts +5 -2
- package/dist/shell/pager/types.d.ts.map +1 -1
- package/dist/shell/pager/utils.d.ts +5 -2
- package/dist/shell/pager/utils.d.ts.map +1 -1
- package/dist/shell/pager/utils.js +14 -17
- package/dist/shell/pager/utils.js.map +1 -1
- package/dist/shell/pipeline-types.d.ts +12 -4
- package/dist/shell/pipeline-types.d.ts.map +1 -1
- package/dist/shell/ref-commands.js +7 -7
- package/dist/shell/ref-commands.js.map +1 -1
- package/dist/shell/ref-resolver.d.ts +15 -15
- package/dist/shell/ref-resolver.d.ts.map +1 -1
- package/dist/shell/ref-resolver.js +34 -20
- package/dist/shell/ref-resolver.js.map +1 -1
- package/dist/shell/repl.d.ts +25 -0
- package/dist/shell/repl.d.ts.map +1 -1
- package/dist/shell/repl.js +285 -51
- package/dist/shell/repl.js.map +1 -1
- package/dist/shell/router-commands.d.ts +30 -0
- package/dist/shell/router-commands.d.ts.map +1 -1
- package/dist/shell/router-commands.js +1011 -62
- package/dist/shell/router-commands.js.map +1 -1
- package/dist/shell/selector.d.ts +1 -1
- package/dist/shell/selector.d.ts.map +1 -1
- package/dist/shell/selector.js +1 -1
- package/dist/shell/selector.js.map +1 -1
- package/dist/shell/types.d.ts.map +1 -1
- package/dist/shell/types.js +3 -1
- package/dist/shell/types.js.map +1 -1
- package/dist/shell/where-command.d.ts.map +1 -1
- package/dist/shell/where-command.js +19 -3
- package/dist/shell/where-command.js.map +1 -1
- package/dist/utils/output.d.ts.map +1 -1
- package/dist/utils/output.js +7 -1
- package/dist/utils/output.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RPC Inspector Tests (Phase 11.5)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from 'vitest';
|
|
6
|
+
import {
|
|
7
|
+
escapeJsonPointer,
|
|
8
|
+
renderJsonWithPaths,
|
|
9
|
+
renderMethodSummary,
|
|
10
|
+
renderSummaryRowsHtml,
|
|
11
|
+
detectSensitiveKeys,
|
|
12
|
+
hasSensitiveContent,
|
|
13
|
+
} from './rpc-inspector.js';
|
|
14
|
+
|
|
15
|
+
describe('escapeJsonPointer', () => {
|
|
16
|
+
it('should escape tilde characters', () => {
|
|
17
|
+
expect(escapeJsonPointer('foo~bar')).toBe('foo~0bar');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should escape forward slash characters', () => {
|
|
21
|
+
expect(escapeJsonPointer('foo/bar')).toBe('foo~1bar');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should escape both tilde and slash', () => {
|
|
25
|
+
expect(escapeJsonPointer('foo~/bar')).toBe('foo~0~1bar');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should handle empty string', () => {
|
|
29
|
+
expect(escapeJsonPointer('')).toBe('');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should handle string without special characters', () => {
|
|
33
|
+
expect(escapeJsonPointer('foobar')).toBe('foobar');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should handle multiple special characters', () => {
|
|
37
|
+
expect(escapeJsonPointer('a/b~c/d~e')).toBe('a~1b~0c~1d~0e');
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('renderJsonWithPaths', () => {
|
|
42
|
+
it('should render null value', () => {
|
|
43
|
+
const html = renderJsonWithPaths(null);
|
|
44
|
+
expect(html).toContain('data-path="#"');
|
|
45
|
+
expect(html).toContain('json-null');
|
|
46
|
+
expect(html).toContain('null');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should render boolean value', () => {
|
|
50
|
+
const html = renderJsonWithPaths(true);
|
|
51
|
+
expect(html).toContain('data-path="#"');
|
|
52
|
+
expect(html).toContain('json-bool');
|
|
53
|
+
expect(html).toContain('true');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should render number value', () => {
|
|
57
|
+
const html = renderJsonWithPaths(42);
|
|
58
|
+
expect(html).toContain('data-path="#"');
|
|
59
|
+
expect(html).toContain('json-number');
|
|
60
|
+
expect(html).toContain('42');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should render string value', () => {
|
|
64
|
+
const html = renderJsonWithPaths('hello');
|
|
65
|
+
expect(html).toContain('data-path="#"');
|
|
66
|
+
expect(html).toContain('json-string');
|
|
67
|
+
expect(html).toContain('"hello"');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should render simple object with paths', () => {
|
|
71
|
+
const json = { name: 'test', value: 123 };
|
|
72
|
+
const html = renderJsonWithPaths(json);
|
|
73
|
+
|
|
74
|
+
expect(html).toContain('data-path="#"');
|
|
75
|
+
expect(html).toContain('data-path="#/name"');
|
|
76
|
+
expect(html).toContain('data-path="#/value"');
|
|
77
|
+
expect(html).toContain('"test"');
|
|
78
|
+
expect(html).toContain('123');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should render nested object with correct paths', () => {
|
|
82
|
+
const json = { outer: { inner: 'value' } };
|
|
83
|
+
const html = renderJsonWithPaths(json);
|
|
84
|
+
|
|
85
|
+
expect(html).toContain('data-path="#"');
|
|
86
|
+
expect(html).toContain('data-path="#/outer"');
|
|
87
|
+
expect(html).toContain('data-path="#/outer/inner"');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should render array with index paths', () => {
|
|
91
|
+
const json = ['a', 'b', 'c'];
|
|
92
|
+
const html = renderJsonWithPaths(json);
|
|
93
|
+
|
|
94
|
+
expect(html).toContain('data-path="#"');
|
|
95
|
+
expect(html).toContain('data-path="#/0"');
|
|
96
|
+
expect(html).toContain('data-path="#/1"');
|
|
97
|
+
expect(html).toContain('data-path="#/2"');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should render tools/list response with correct paths', () => {
|
|
101
|
+
const json = {
|
|
102
|
+
result: {
|
|
103
|
+
tools: [
|
|
104
|
+
{ name: 'read_file', description: 'Read a file' },
|
|
105
|
+
{ name: 'write_file', description: 'Write a file' },
|
|
106
|
+
],
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
const html = renderJsonWithPaths(json);
|
|
110
|
+
|
|
111
|
+
expect(html).toContain('data-path="#/result"');
|
|
112
|
+
expect(html).toContain('data-path="#/result/tools"');
|
|
113
|
+
expect(html).toContain('data-path="#/result/tools/0"');
|
|
114
|
+
expect(html).toContain('data-path="#/result/tools/0/name"');
|
|
115
|
+
expect(html).toContain('data-path="#/result/tools/1"');
|
|
116
|
+
expect(html).toContain('data-path="#/result/tools/1/name"');
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('should escape special characters in keys', () => {
|
|
120
|
+
const json = { 'key/with/slash': 'value', 'key~with~tilde': 'value' };
|
|
121
|
+
const html = renderJsonWithPaths(json);
|
|
122
|
+
|
|
123
|
+
expect(html).toContain('data-path="#/key~1with~1slash"');
|
|
124
|
+
expect(html).toContain('data-path="#/key~0with~0tilde"');
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should handle undefined/null input gracefully', () => {
|
|
128
|
+
const html = renderJsonWithPaths(undefined);
|
|
129
|
+
expect(html).toContain('(no data)');
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe('renderMethodSummary', () => {
|
|
134
|
+
describe('tools/list method', () => {
|
|
135
|
+
it('should render tools table for tools/list response', () => {
|
|
136
|
+
const response = {
|
|
137
|
+
result: {
|
|
138
|
+
tools: [
|
|
139
|
+
{ name: 'read_file', description: 'Read a file' },
|
|
140
|
+
{ name: 'write_file', description: 'Write a file' },
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const rows = renderMethodSummary('tools/list', {}, response);
|
|
146
|
+
|
|
147
|
+
// Should have method header + tools header + 2 tool rows
|
|
148
|
+
expect(rows.length).toBe(4);
|
|
149
|
+
expect(rows[0].type).toBe('header');
|
|
150
|
+
expect(rows[0].label).toBe('Method: tools/list');
|
|
151
|
+
expect(rows[1].type).toBe('header');
|
|
152
|
+
expect(rows[1].label).toBe('Tools (2)');
|
|
153
|
+
|
|
154
|
+
// First tool
|
|
155
|
+
expect(rows[2].type).toBe('item');
|
|
156
|
+
expect(rows[2].label).toBe('read_file');
|
|
157
|
+
expect(rows[2].value).toBe('Read a file');
|
|
158
|
+
expect(rows[2].pointer).toEqual({
|
|
159
|
+
target: 'response',
|
|
160
|
+
path: '#/result/tools/0',
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Second tool
|
|
164
|
+
expect(rows[3].type).toBe('item');
|
|
165
|
+
expect(rows[3].label).toBe('write_file');
|
|
166
|
+
expect(rows[3].pointer).toEqual({
|
|
167
|
+
target: 'response',
|
|
168
|
+
path: '#/result/tools/1',
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('should render inputSchema properties as children', () => {
|
|
173
|
+
const response = {
|
|
174
|
+
result: {
|
|
175
|
+
tools: [
|
|
176
|
+
{
|
|
177
|
+
name: 'read_file',
|
|
178
|
+
description: 'Read a file',
|
|
179
|
+
inputSchema: {
|
|
180
|
+
type: 'object',
|
|
181
|
+
properties: {
|
|
182
|
+
path: { type: 'string', description: 'File path' },
|
|
183
|
+
encoding: { type: 'string', description: 'Encoding' },
|
|
184
|
+
},
|
|
185
|
+
required: ['path'],
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const rows = renderMethodSummary('tools/list', {}, response);
|
|
193
|
+
|
|
194
|
+
// Tool row should have children (row[2] is first tool after 2 headers)
|
|
195
|
+
expect(rows[2].children).toBeDefined();
|
|
196
|
+
expect(rows[2].children!.length).toBe(2);
|
|
197
|
+
|
|
198
|
+
// path property (required)
|
|
199
|
+
const pathProp = rows[2].children![0];
|
|
200
|
+
expect(pathProp.label).toBe('path');
|
|
201
|
+
expect(pathProp.value).toBe('string (required)');
|
|
202
|
+
expect(pathProp.cssClass).toBe('schema-required');
|
|
203
|
+
expect(pathProp.pointer).toEqual({
|
|
204
|
+
target: 'response',
|
|
205
|
+
path: '#/result/tools/0/inputSchema/properties/path',
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// encoding property (optional)
|
|
209
|
+
const encodingProp = rows[2].children![1];
|
|
210
|
+
expect(encodingProp.label).toBe('encoding');
|
|
211
|
+
expect(encodingProp.value).toBe('string');
|
|
212
|
+
expect(encodingProp.cssClass).toBe('schema-optional');
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('should handle empty tools array', () => {
|
|
216
|
+
const response = { result: { tools: [] } };
|
|
217
|
+
const rows = renderMethodSummary('tools/list', {}, response);
|
|
218
|
+
|
|
219
|
+
// Method header + empty message
|
|
220
|
+
expect(rows.length).toBe(2);
|
|
221
|
+
expect(rows[0].label).toBe('Method: tools/list');
|
|
222
|
+
expect(rows[1].label).toBe('(no tools available)');
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('should handle missing tools in response', () => {
|
|
226
|
+
const response = { result: {} };
|
|
227
|
+
const rows = renderMethodSummary('tools/list', {}, response);
|
|
228
|
+
|
|
229
|
+
// Method header + empty message
|
|
230
|
+
expect(rows.length).toBe(2);
|
|
231
|
+
expect(rows[0].label).toBe('Method: tools/list');
|
|
232
|
+
expect(rows[1].label).toBe('(no tools available)');
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
describe('generic method fallback', () => {
|
|
237
|
+
it('should render generic summary for unknown methods', () => {
|
|
238
|
+
const request = { params: { query: 'test' } };
|
|
239
|
+
const response = { result: { data: [1, 2, 3] } };
|
|
240
|
+
|
|
241
|
+
const rows = renderMethodSummary('custom/method', request, response);
|
|
242
|
+
|
|
243
|
+
// Should have method header (appears twice: once for request, once for response)
|
|
244
|
+
expect(rows[0].type).toBe('header');
|
|
245
|
+
expect(rows[0].label).toBe('Method: custom/method');
|
|
246
|
+
|
|
247
|
+
// Should have parameters section
|
|
248
|
+
const paramHeader = rows.find(
|
|
249
|
+
(r) => r.label === 'Parameters' && r.type === 'header'
|
|
250
|
+
);
|
|
251
|
+
expect(paramHeader).toBeDefined();
|
|
252
|
+
|
|
253
|
+
// Should have query param row
|
|
254
|
+
const queryRow = rows.find((r) => r.label === 'query');
|
|
255
|
+
expect(queryRow).toBeDefined();
|
|
256
|
+
expect(queryRow!.pointer?.target).toBe('request');
|
|
257
|
+
expect(queryRow!.pointer?.path).toBe('#/params/query');
|
|
258
|
+
|
|
259
|
+
// Should have response result section
|
|
260
|
+
const resultHeader = rows.find(
|
|
261
|
+
(r) => r.label === 'Result' && r.type === 'header'
|
|
262
|
+
);
|
|
263
|
+
expect(resultHeader).toBeDefined();
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it('should render error section for error responses', () => {
|
|
267
|
+
const response = {
|
|
268
|
+
error: { code: -32600, message: 'Invalid Request' },
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const rows = renderMethodSummary('custom/method', {}, response);
|
|
272
|
+
|
|
273
|
+
const errorHeader = rows.find(
|
|
274
|
+
(r) => r.label === 'Error' && r.type === 'header'
|
|
275
|
+
);
|
|
276
|
+
expect(errorHeader).toBeDefined();
|
|
277
|
+
|
|
278
|
+
// Error object properties are shown as separate rows (code, message)
|
|
279
|
+
const codeRow = rows.find((r) => r.label === 'code');
|
|
280
|
+
expect(codeRow).toBeDefined();
|
|
281
|
+
expect(codeRow!.pointer?.target).toBe('response');
|
|
282
|
+
expect(codeRow!.pointer?.path).toBe('#/error/code');
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
describe('renderSummaryRowsHtml', () => {
|
|
288
|
+
it('should render header row', () => {
|
|
289
|
+
const rows = [{ type: 'header' as const, label: 'Tools (3)' }];
|
|
290
|
+
const html = renderSummaryRowsHtml(rows);
|
|
291
|
+
|
|
292
|
+
expect(html).toContain('summary-row');
|
|
293
|
+
expect(html).toContain('summary-header');
|
|
294
|
+
expect(html).toContain('Tools (3)');
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('should render item row with pointer attributes', () => {
|
|
298
|
+
const rows = [
|
|
299
|
+
{
|
|
300
|
+
type: 'item' as const,
|
|
301
|
+
label: 'read_file',
|
|
302
|
+
value: 'Read a file',
|
|
303
|
+
pointer: { target: 'response' as const, path: '#/result/tools/0' },
|
|
304
|
+
},
|
|
305
|
+
];
|
|
306
|
+
const html = renderSummaryRowsHtml(rows);
|
|
307
|
+
|
|
308
|
+
expect(html).toContain('summary-item');
|
|
309
|
+
expect(html).toContain('clickable');
|
|
310
|
+
expect(html).toContain('data-pointer-target="response"');
|
|
311
|
+
expect(html).toContain('data-pointer-path="#/result/tools/0"');
|
|
312
|
+
expect(html).toContain('read_file');
|
|
313
|
+
expect(html).toContain('Read a file');
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('should render property row', () => {
|
|
317
|
+
const rows = [
|
|
318
|
+
{
|
|
319
|
+
type: 'property' as const,
|
|
320
|
+
label: 'path',
|
|
321
|
+
value: 'string (required)',
|
|
322
|
+
cssClass: 'schema-required',
|
|
323
|
+
pointer: {
|
|
324
|
+
target: 'response' as const,
|
|
325
|
+
path: '#/result/tools/0/inputSchema/properties/path',
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
];
|
|
329
|
+
const html = renderSummaryRowsHtml(rows);
|
|
330
|
+
|
|
331
|
+
expect(html).toContain('summary-property');
|
|
332
|
+
expect(html).toContain('schema-required');
|
|
333
|
+
expect(html).toContain('summary-prop-name');
|
|
334
|
+
expect(html).toContain('path');
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it('should render children recursively', () => {
|
|
338
|
+
const rows = [
|
|
339
|
+
{
|
|
340
|
+
type: 'item' as const,
|
|
341
|
+
label: 'tool',
|
|
342
|
+
value: 'desc',
|
|
343
|
+
children: [
|
|
344
|
+
{
|
|
345
|
+
type: 'property' as const,
|
|
346
|
+
label: 'prop1',
|
|
347
|
+
value: 'string',
|
|
348
|
+
},
|
|
349
|
+
],
|
|
350
|
+
},
|
|
351
|
+
];
|
|
352
|
+
const html = renderSummaryRowsHtml(rows);
|
|
353
|
+
|
|
354
|
+
expect(html).toContain('summary-children');
|
|
355
|
+
expect(html).toContain('prop1');
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it('should escape HTML in labels and values', () => {
|
|
359
|
+
const rows = [
|
|
360
|
+
{
|
|
361
|
+
type: 'item' as const,
|
|
362
|
+
label: '<script>alert("xss")</script>',
|
|
363
|
+
value: '<img src=x onerror=alert(1)>',
|
|
364
|
+
},
|
|
365
|
+
];
|
|
366
|
+
const html = renderSummaryRowsHtml(rows);
|
|
367
|
+
|
|
368
|
+
// Should not contain raw HTML tags (which would be executable)
|
|
369
|
+
expect(html).not.toContain('<script>');
|
|
370
|
+
expect(html).not.toContain('<img src=x');
|
|
371
|
+
// Should contain escaped versions
|
|
372
|
+
expect(html).toContain('<script>');
|
|
373
|
+
expect(html).toContain('<img');
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
describe('detectSensitiveKeys', () => {
|
|
378
|
+
it('should detect authorization header', () => {
|
|
379
|
+
const json = { headers: { authorization: 'Bearer xxx' } };
|
|
380
|
+
const keys = detectSensitiveKeys(json);
|
|
381
|
+
expect(keys).toContain('headers.authorization');
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('should detect api_key and api-key variations', () => {
|
|
385
|
+
const json = { api_key: 'xxx', config: { 'api-key': 'yyy' } };
|
|
386
|
+
const keys = detectSensitiveKeys(json);
|
|
387
|
+
expect(keys).toContain('api_key');
|
|
388
|
+
expect(keys).toContain('config.api-key');
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
it('should detect token and access_token', () => {
|
|
392
|
+
const json = { token: 'xxx', auth: { access_token: 'yyy', refresh_token: 'zzz' } };
|
|
393
|
+
const keys = detectSensitiveKeys(json);
|
|
394
|
+
expect(keys).toContain('token');
|
|
395
|
+
expect(keys).toContain('auth.access_token');
|
|
396
|
+
expect(keys).toContain('auth.refresh_token');
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it('should detect password and secret', () => {
|
|
400
|
+
const json = { password: 'xxx', db: { secret: 'yyy' } };
|
|
401
|
+
const keys = detectSensitiveKeys(json);
|
|
402
|
+
expect(keys).toContain('password');
|
|
403
|
+
expect(keys).toContain('db.secret');
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
it('should detect private_key and credential', () => {
|
|
407
|
+
const json = { private_key: 'xxx', credential: 'yyy' };
|
|
408
|
+
const keys = detectSensitiveKeys(json);
|
|
409
|
+
expect(keys).toContain('private_key');
|
|
410
|
+
expect(keys).toContain('credential');
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
it('should detect bearer and signature', () => {
|
|
414
|
+
const json = { bearer: 'xxx', signature: 'yyy' };
|
|
415
|
+
const keys = detectSensitiveKeys(json);
|
|
416
|
+
expect(keys).toContain('bearer');
|
|
417
|
+
expect(keys).toContain('signature');
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it('should detect session_id and cookie', () => {
|
|
421
|
+
const json = { session_id: 'xxx', cookie: 'yyy' };
|
|
422
|
+
const keys = detectSensitiveKeys(json);
|
|
423
|
+
expect(keys).toContain('session_id');
|
|
424
|
+
expect(keys).toContain('cookie');
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
it('should detect exact auth key but not author', () => {
|
|
428
|
+
const json = { auth: 'xxx', author: 'John', auth_token: 'yyy' };
|
|
429
|
+
const keys = detectSensitiveKeys(json);
|
|
430
|
+
expect(keys).toContain('auth');
|
|
431
|
+
expect(keys).toContain('auth_token');
|
|
432
|
+
expect(keys).not.toContain('author'); // Should NOT match 'author'
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
it('should detect client_secret, jwt, oauth, x-api-key', () => {
|
|
436
|
+
const json = {
|
|
437
|
+
client_secret: 'xxx',
|
|
438
|
+
jwt: 'yyy',
|
|
439
|
+
oauth: 'zzz',
|
|
440
|
+
'x-api-key': 'aaa',
|
|
441
|
+
'x-auth-token': 'bbb',
|
|
442
|
+
};
|
|
443
|
+
const keys = detectSensitiveKeys(json);
|
|
444
|
+
expect(keys).toContain('client_secret');
|
|
445
|
+
expect(keys).toContain('jwt');
|
|
446
|
+
expect(keys).toContain('oauth');
|
|
447
|
+
expect(keys).toContain('x-api-key');
|
|
448
|
+
expect(keys).toContain('x-auth-token');
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it('should detect auth-related keys case-insensitively', () => {
|
|
452
|
+
const json = { Authorization: 'xxx', API_KEY: 'yyy', Token: 'zzz' };
|
|
453
|
+
const keys = detectSensitiveKeys(json);
|
|
454
|
+
expect(keys).toContain('Authorization');
|
|
455
|
+
expect(keys).toContain('API_KEY');
|
|
456
|
+
expect(keys).toContain('Token');
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
it('should traverse nested objects', () => {
|
|
460
|
+
const json = {
|
|
461
|
+
level1: {
|
|
462
|
+
level2: {
|
|
463
|
+
level3: {
|
|
464
|
+
secret: 'hidden',
|
|
465
|
+
},
|
|
466
|
+
},
|
|
467
|
+
},
|
|
468
|
+
};
|
|
469
|
+
const keys = detectSensitiveKeys(json);
|
|
470
|
+
expect(keys).toContain('level1.level2.level3.secret');
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
it('should traverse arrays', () => {
|
|
474
|
+
const json = {
|
|
475
|
+
items: [
|
|
476
|
+
{ name: 'safe' },
|
|
477
|
+
{ password: 'hidden' },
|
|
478
|
+
{ api_key: 'xxx' },
|
|
479
|
+
],
|
|
480
|
+
};
|
|
481
|
+
const keys = detectSensitiveKeys(json);
|
|
482
|
+
expect(keys).toContain('items[1].password');
|
|
483
|
+
expect(keys).toContain('items[2].api_key');
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
it('should return empty array for safe data', () => {
|
|
487
|
+
const json = { name: 'test', value: 123, items: ['a', 'b'] };
|
|
488
|
+
const keys = detectSensitiveKeys(json);
|
|
489
|
+
expect(keys).toEqual([]);
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
it('should handle null and undefined', () => {
|
|
493
|
+
expect(detectSensitiveKeys(null)).toEqual([]);
|
|
494
|
+
expect(detectSensitiveKeys(undefined)).toEqual([]);
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
it('should handle primitive values', () => {
|
|
498
|
+
expect(detectSensitiveKeys('string')).toEqual([]);
|
|
499
|
+
expect(detectSensitiveKeys(123)).toEqual([]);
|
|
500
|
+
expect(detectSensitiveKeys(true)).toEqual([]);
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
it('should handle empty object and array', () => {
|
|
504
|
+
expect(detectSensitiveKeys({})).toEqual([]);
|
|
505
|
+
expect(detectSensitiveKeys([])).toEqual([]);
|
|
506
|
+
});
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
describe('hasSensitiveContent', () => {
|
|
510
|
+
it('should return true when sensitive keys exist', () => {
|
|
511
|
+
const json = { authorization: 'Bearer xxx' };
|
|
512
|
+
expect(hasSensitiveContent(json)).toBe(true);
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
it('should return false when no sensitive keys exist', () => {
|
|
516
|
+
const json = { name: 'test', value: 123 };
|
|
517
|
+
expect(hasSensitiveContent(json)).toBe(false);
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
it('should return false for null/undefined', () => {
|
|
521
|
+
expect(hasSensitiveContent(null)).toBe(false);
|
|
522
|
+
expect(hasSensitiveContent(undefined)).toBe(false);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
it('should detect nested sensitive content', () => {
|
|
526
|
+
const json = { config: { db: { password: 'xxx' } } };
|
|
527
|
+
expect(hasSensitiveContent(json)).toBe(true);
|
|
528
|
+
});
|
|
529
|
+
});
|