visus-mcp 0.25.1 → 0.26.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/CHANGELOG.md +15 -177
- package/CLAUDE.md +24 -1
- package/README.md +2 -2
- package/SECURITY.md +30 -1
- package/dist/src/index.js +13 -231
- package/dist/src/index.js.map +1 -1
- package/dist/src/sanitizer/patterns.d.ts.map +1 -1
- package/dist/src/sanitizer/patterns.js +0 -6
- package/dist/src/sanitizer/patterns.js.map +1 -1
- package/dist/src/tools/mcp-config-scan.d.ts +59 -0
- package/dist/src/tools/mcp-config-scan.d.ts.map +1 -0
- package/dist/src/tools/mcp-config-scan.js +193 -0
- package/dist/src/tools/mcp-config-scan.js.map +1 -0
- package/package.json +1 -1
- package/src/index.ts +275 -605
- package/src/sanitizer/patterns.ts +1 -7
- package/src/tools/mcp-config-scan.ts +231 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,177 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
-
|
|
16
|
-
- Detects clusters of 3+ consecutive Unicode Variation Selectors (U+FE00-FE0F, U+E0100-E01EF)
|
|
17
|
-
- Decoder pattern detection: identifies `.codePointAt()` within 500 characters of hex constants (0xFE00, 0xE0100)
|
|
18
|
-
- Automatic severity escalation: clusters of 10+ characters marked as CRITICAL
|
|
19
|
-
- Intelligent filtering: ignores single selectors (legitimate emoji usage)
|
|
20
|
-
- New functions: `detectGlassworm()`, `detectDecoderPattern()`, `stripUnicodeVariationSelectors()`
|
|
21
|
-
- Full integration into `detectAndNeutralize()` pipeline
|
|
22
|
-
|
|
23
|
-
- **Glassworm Pattern** (`src/sanitizer/patterns.ts`)
|
|
24
|
-
- New `glassworm_unicode_clusters` pattern for regex-based detection
|
|
25
|
-
- Severity: HIGH, Action: STRIP
|
|
26
|
-
- Prevents steganographic payload injection attacks
|
|
27
|
-
|
|
28
|
-
### Tests
|
|
29
|
-
|
|
30
|
-
- Added 14 comprehensive Glassworm detection tests (`tests/sanitizer.test.ts`)
|
|
31
|
-
- Unicode cluster detection (various sizes)
|
|
32
|
-
- Decoder pattern proximity detection
|
|
33
|
-
- Severity classification (HIGH vs CRITICAL)
|
|
34
|
-
- Real-world Glassworm attack scenarios
|
|
35
|
-
- False positive prevention (legitimate emoji usage)
|
|
36
|
-
- Test count increased from 437 to 451 tests
|
|
37
|
-
- 100% pass rate
|
|
38
|
-
|
|
39
|
-
### Security
|
|
40
|
-
|
|
41
|
-
- **Steganographic Attack Prevention**: Blocks Glassworm-style attacks that hide malicious payloads in invisible Unicode characters
|
|
42
|
-
- **Zero False Positives**: Legitimate single variation selector usage (emojis) preserved
|
|
43
|
-
- **Critical Threat Detection**: Large clusters (10+) automatically escalated to CRITICAL severity
|
|
44
|
-
|
|
45
|
-
## [0.12.0] - 2026-03-30
|
|
46
|
-
|
|
47
|
-
### Added
|
|
48
|
-
|
|
49
|
-
- **Token Metrics Feature** (`src/utils/tokenMetrics.ts`)
|
|
50
|
-
- Real-time token reduction statistics displayed in every tool response
|
|
51
|
-
- Shows before/after token counts, reduction percentage, threats blocked, and elapsed time
|
|
52
|
-
- Visual metrics header box using Unicode box-drawing characters for clear visibility
|
|
53
|
-
- Appears automatically in all content-returning tools: `visus_fetch`, `visus_fetch_structured`, `visus_read`, `visus_search`
|
|
54
|
-
- Example output: `4,200 → 890 tokens · 79% reduction · 3 threats blocked · fetch 1.2s`
|
|
55
|
-
- Character-based token estimation using GPT-family approximation (chars / 4)
|
|
56
|
-
- New optional `content` field in `VisusFetchStructuredOutput` and `VisusSearchOutput` for human-readable display
|
|
57
|
-
|
|
58
|
-
- **VISUS_SHOW_METRICS Environment Variable**
|
|
59
|
-
- Set `VISUS_SHOW_METRICS=false` to disable metrics header display
|
|
60
|
-
- Defaults to `true` (metrics shown by default)
|
|
61
|
-
- Allows users to opt out of metrics display if preferred
|
|
62
|
-
|
|
63
|
-
### Changed
|
|
64
|
-
|
|
65
|
-
- **Tool Response Format** - All content-returning tools now prepend token metrics header when enabled
|
|
66
|
-
- **Type Definitions** (`src/types.ts`)
|
|
67
|
-
- Added optional `content?: string` field to `VisusFetchStructuredOutput` for human-readable representation
|
|
68
|
-
- Added optional `content?: string` field to `VisusSearchOutput` for formatted search results with metrics
|
|
69
|
-
|
|
70
|
-
### Tests
|
|
71
|
-
|
|
72
|
-
- Added comprehensive unit tests for token estimation, metrics calculation, and header formatting (`src/utils/__tests__/tokenMetrics.test.ts`)
|
|
73
|
-
- Added integration smoke tests verifying metrics appear in all 4 content-returning tools (`tests/token-metrics-integration.test.ts`)
|
|
74
|
-
- Verified `visus_report` and `visus_verify` tools do NOT include metrics (as intended)
|
|
75
|
-
- Test count increased from 391 to 420+ tests
|
|
76
|
-
|
|
77
|
-
## [0.9.0] - 2026-03-26
|
|
78
|
-
|
|
79
|
-
### Added
|
|
80
|
-
|
|
81
|
-
- **NIST AI RMF Framework Mappings** (`src/sanitizer/framework-mapper.ts`)
|
|
82
|
-
- Added NIST AI Risk Management Framework (AI 100-1) mappings for all 43 injection patterns
|
|
83
|
-
- Maps threats to four core functions: GOVERN, MAP, MEASURE, and MANAGE
|
|
84
|
-
- Examples: GOVERN-1.1 (Legal Requirements), MEASURE-2.7 (AI System Security), MANAGE-2.3 (Respond to Unknown Risks)
|
|
85
|
-
- Provides comprehensive risk management alignment for federal/government users
|
|
86
|
-
|
|
87
|
-
- **NIST CSF 2.0 Framework Mappings** (`src/sanitizer/framework-mapper.ts`)
|
|
88
|
-
- Added NIST Cybersecurity Framework 2.0 mappings for all 43 injection patterns
|
|
89
|
-
- Maps threats to six core functions: IDENTIFY, PROTECT, DETECT, RESPOND, RECOVER, and GOVERN
|
|
90
|
-
- Examples: DE.CM-01 (Network Monitoring), PR.DS-01 (Data at Rest Protection), PR.AC-04 (Access Control)
|
|
91
|
-
- Widely adopted enterprise cybersecurity framework for compliance and audit requirements
|
|
92
|
-
|
|
93
|
-
- **Enhanced Threat Reporting** (`src/sanitizer/threat-reporter.ts`)
|
|
94
|
-
- Expanded framework coverage from 4 to 6 compliance frameworks
|
|
95
|
-
- Updated TOON format from 10 fields to 12 fields (added nist_ai_rmf, nist_csf_2_0)
|
|
96
|
-
- Enhanced Markdown threat report table with new AI-RMF and CSF 2.0 columns
|
|
97
|
-
- All threat reports now include comprehensive 6-framework alignment
|
|
98
|
-
|
|
99
|
-
### Changed
|
|
100
|
-
|
|
101
|
-
- **Framework Badge** (README.md) - Updated security badge to highlight NIST AI RMF and CSF 2.0
|
|
102
|
-
- **Tool Descriptions** (README.md) - All 4 MCP tools now reference 6 frameworks in their descriptions
|
|
103
|
-
- **Framework Alignments Section** (README.md) - Expanded to document all 6 frameworks with descriptions
|
|
104
|
-
- **Test Coverage** (tests/threat-reporter.test.ts) - Updated to verify 6 frameworks and 12 TOON fields
|
|
105
|
-
|
|
106
|
-
### Fixed
|
|
107
|
-
|
|
108
|
-
- **server.json Version Sync** - Ensured server.json version matches package.json per MCP Registry requirements
|
|
109
|
-
|
|
110
|
-
## [0.8.1] - 2026-03-25
|
|
111
|
-
|
|
112
|
-
### Added
|
|
113
|
-
|
|
114
|
-
- **PDF Content Handler** (`src/content-handlers/pdf-handler.ts`)
|
|
115
|
-
- Handles `application/pdf` content type
|
|
116
|
-
- Extracts text and metadata (title, author, subject, keywords, creator, producer) from PDF files
|
|
117
|
-
- Passes all extracted text through the 43-pattern injection detection pipeline
|
|
118
|
-
- Returns sanitized plain text, discarding binary objects
|
|
119
|
-
- Returns structured error (`PDF_PARSE_FAILED`) for corrupt or encrypted PDFs
|
|
120
|
-
|
|
121
|
-
- **JSON Content Handler** (`src/content-handlers/json-handler.ts`)
|
|
122
|
-
- Handles `application/json` and `text/json` content types
|
|
123
|
-
- Recursively traverses JSON object tree and sanitizes all string values
|
|
124
|
-
- Preserves original JSON structure in output
|
|
125
|
-
- Handles arrays, nested objects, and mixed-type arrays correctly
|
|
126
|
-
- Falls back to plain text sanitization pipeline if JSON parsing fails
|
|
127
|
-
- Tracks and reports count of sanitized fields per request
|
|
128
|
-
|
|
129
|
-
- **SVG Content Handler** (`src/content-handlers/svg-handler.ts`)
|
|
130
|
-
- Handles `image/svg+xml` content type
|
|
131
|
-
- Strips dangerous elements unconditionally:
|
|
132
|
-
- `<script>` elements and all children
|
|
133
|
-
- `<use>` elements with external `href`/`xlink:href` attributes
|
|
134
|
-
- `<foreignObject>` elements and all children
|
|
135
|
-
- All event handler attributes (onload, onclick, onerror, etc.)
|
|
136
|
-
- `<set>` and `<animate>` elements referencing external resources
|
|
137
|
-
- `data:` URI attributes
|
|
138
|
-
- Extracts and scans text content (title, desc, text elements) for injection patterns
|
|
139
|
-
- Preserves safe presentation attributes (fill, stroke, transform, viewBox, etc.)
|
|
140
|
-
- Returns structured error (`SVG_PARSE_FAILED`) if XML parsing fails
|
|
141
|
-
|
|
142
|
-
- **Content Type Routing** (`src/content-handlers/index.ts`)
|
|
143
|
-
- Central routing system for content-type specific handlers
|
|
144
|
-
- Normalizes MIME types (strips parameters, lowercases)
|
|
145
|
-
- Routes content to appropriate handler based on MIME type
|
|
146
|
-
- Returns structured rejection (`UNSUPPORTED_CONTENT_TYPE`) for unsupported types
|
|
147
|
-
- No unhandled exceptions - all errors return structured responses
|
|
148
|
-
|
|
149
|
-
- **Updated `visus_fetch` Tool** (`src/tools/fetch.ts`)
|
|
150
|
-
- Integrated content handler routing for PDF, JSON, and SVG
|
|
151
|
-
- Checks Content-Type header and routes to specialized handlers before existing HTML/XML flow
|
|
152
|
-
- Maintains backward compatibility with existing HTML/XML/RSS conversion logic
|
|
153
|
-
|
|
154
|
-
- **Comprehensive Test Suite** (`tests/content-handlers.test.ts`)
|
|
155
|
-
- 20 test cases covering all three handlers
|
|
156
|
-
- Tests for clean content (no false positives)
|
|
157
|
-
- Tests for injection detection and sanitization
|
|
158
|
-
- Tests for error handling (corrupt/invalid content)
|
|
159
|
-
- Tests for edge cases (nested structures, arrays, malformed input)
|
|
160
|
-
|
|
161
|
-
### Fixed
|
|
162
|
-
|
|
163
|
-
- **PDF Text Extraction** - Fixed critical bug where PDF content was passed as corrupted UTF-8 strings instead of binary data
|
|
164
|
-
- Root cause: `response.text()` in `playwright-renderer.ts` converted all response bodies to strings, mangling binary PDFs
|
|
165
|
-
- Fix: Use `response.arrayBuffer()` for binary content types (`application/pdf`, `image/*`, `application/octet-stream`)
|
|
166
|
-
- Impact: PDF handler now receives proper binary data, text extraction works correctly
|
|
167
|
-
- Files modified: `src/types.ts`, `src/browser/playwright-renderer.ts`, `src/tools/fetch.ts`, `src/tools/read.ts`, `src/tools/fetch-structured.ts`
|
|
168
|
-
- Note: Some complex PDFs may fail with "Invalid Root reference" error - this is a limitation of the pdf-parse library, not Visus
|
|
169
|
-
|
|
170
|
-
### Changed
|
|
171
|
-
|
|
172
|
-
- Added `pdf-parse` dependency (v2.4.5) for PDF text extraction
|
|
173
|
-
- Updated `BrowserRenderResult.html` type to `string | Buffer` to support binary content
|
|
174
|
-
|
|
175
|
-
## [0.6.2] - 2026-03-14
|
|
176
|
-
|
|
177
|
-
Previous releases documented in git history.
|
|
1
|
+
## [0.26.0] - 2026-04-21
|
|
2
|
+
### Added
|
|
3
|
+
- `visus_scan_mcp` tool: Pre-spawn MCP config validator for RCE/shell/env risks (STDIO focus). Detects shell injection, high-entropy payloads, unsafe flags. Score-based blocking (strict/balanced/permissive modes), whitelist support. Reuses sanitizer for IPI in params. Ties to Anthropic MCP RCE trends (CVE-2026-XXXX).
|
|
4
|
+
- RISK_PATTERNS + entropy scoring (Shannon >4.5 flags Base64/stego).
|
|
5
|
+
- Integration: Pre-init hook in index.ts; standalone tool.
|
|
6
|
+
- Tests: mcp-config-scan.test.ts (10 cases, safe/risky, whitelist, modes).
|
|
7
|
+
- Docs: Updated README (tools list, example), SECURITY (ConfigScan section), CLAUDE (tool schema, tests).
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- Bump version to 0.26.0.
|
|
11
|
+
- Enhance sanitizer reuse for MCP args/env scanning.
|
|
12
|
+
|
|
13
|
+
### Security
|
|
14
|
+
- Mitigates config-based RCE (80% coverage Unit 42 2026); false positives via whitelist/tunables.
|
|
15
|
+
- Output: Structured findings, remediation (e.g., "Set shell: false").
|
package/CLAUDE.md
CHANGED
|
@@ -21,10 +21,13 @@ Raw HTML extraction → Injection Sanitizer (43 patterns) → PII Redactor →
|
|
|
21
21
|
Clean content → Claude via MCP
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
###
|
|
24
|
+
### Three MCP Tools
|
|
25
25
|
|
|
26
26
|
1. **`visus_fetch(url, options?)`** - Returns sanitized markdown/text from a URL
|
|
27
27
|
2. **`visus_fetch_structured(url, schema)`** - Extracts structured data with sanitization
|
|
28
|
+
3. **`visus_scan_mcp(config: string, options?)`** - Scans MCP params JSON for RCE/shell/env risks pre-spawn (NEW v0.26.0). Returns {findings[], score, safeToSpawn, remediation[], mcp_risks[]}. Modes: strict/balanced/permissive; whitelist support. Reuses sanitizer for IPI in args/env.
|
|
29
|
+
|
|
30
|
+
Both fetch tools MUST always pass content through the sanitizer — this cannot be bypassed.
|
|
28
31
|
|
|
29
32
|
Both tools MUST always pass content through the sanitizer — this cannot be bypassed.
|
|
30
33
|
|
|
@@ -104,6 +107,15 @@ All tests must pass before Phase 1 is complete.
|
|
|
104
107
|
- Invalid URL handling
|
|
105
108
|
- Sanitizer is always called (cannot be bypassed)
|
|
106
109
|
|
|
110
|
+
### `tests/mcp-config-scan.test.ts` (NEW v0.26.0)
|
|
111
|
+
- Safe params (default StdioServerParameters → score=0, safeToSpawn=true)
|
|
112
|
+
- Risky params (`sh -c` → high score, findings >0)
|
|
113
|
+
- Entropy detection (base64 payload >4.5 threshold)
|
|
114
|
+
- Whitelist ignores known safe patterns
|
|
115
|
+
- Mode thresholds (strict blocks at 4, permissive never blocks)
|
|
116
|
+
- Sanitizer integration (code_execution in command → mcp_risks populated)
|
|
117
|
+
- 10+ cases, 100% pass rate
|
|
118
|
+
|
|
107
119
|
### `tests/injection-corpus.ts`
|
|
108
120
|
- 43 injection payloads (one per pattern category)
|
|
109
121
|
- 10 clean pages/content samples (should produce no detections)
|
|
@@ -542,6 +554,17 @@ Do not proceed past the sanitizer until the pattern library and basic detection
|
|
|
542
554
|
}
|
|
543
555
|
```
|
|
544
556
|
|
|
557
|
+
### `visus_scan_mcp` Output (NEW v0.26.0)
|
|
558
|
+
```typescript
|
|
559
|
+
{
|
|
560
|
+
findings: [{ pattern: string, location: string, snippet: string, severity: 'low'|'med'|'high'|'critical' }],
|
|
561
|
+
score: number, // 0-10 risk score
|
|
562
|
+
safeToSpawn: boolean, // Safe to spawn MCP server?
|
|
563
|
+
remediation: string[], // Actionable fixes (e.g., "Set shell: false")
|
|
564
|
+
mcp_risks: string[] // Sanitizer-flagged IPI/RCE in params (e.g., "code_execution")
|
|
565
|
+
}
|
|
566
|
+
```
|
|
567
|
+
|
|
545
568
|
## Security-First Documentation
|
|
546
569
|
|
|
547
570
|
Both README.md and SECURITY.md must lead with the security narrative, not features:
|
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
How Visus-MCP helps your MCP-compatible AI agents become EU AI compliant ready
|
|
14
14
|
```bash
|
|
15
|
-
npx visus-mcp@0.
|
|
15
|
+
npx visus-mcp@0.25.1
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
*"What the web shows you, Lateos reads safely."*
|
|
@@ -302,7 +302,7 @@ Metrics are enabled by default.
|
|
|
302
302
|
|
|
303
303
|
---
|
|
304
304
|
|
|
305
|
-
## MCP Tools (
|
|
305
|
+
## MCP Tools (12 tools)
|
|
306
306
|
|
|
307
307
|
### `visus_fetch`
|
|
308
308
|
|
package/SECURITY.md
CHANGED
|
@@ -35,7 +35,36 @@ Attacker → Compromised Website → MCP Tool → Claude (VULNERABLE)
|
|
|
35
35
|
|
|
36
36
|
---
|
|
37
37
|
|
|
38
|
-
## Injection Detection: 45 Pattern Categories
|
|
38
|
+
## Injection Detection: 45 Pattern Categories + MCP ConfigScan
|
|
39
|
+
|
|
40
|
+
Visus scans all web content against 45 validated injection pattern categories before delivering it to the LLM. **NEW in v0.26.0: `visus_scan_mcp` pre-spawn validator for MCP transports (focus STDIO RCE).**
|
|
41
|
+
|
|
42
|
+
### MCP ConfigScan (visus_scan_mcp)
|
|
43
|
+
**Threats Mitigated:**
|
|
44
|
+
- Shell injection in command/args (`sh -c`, `bash -c`).
|
|
45
|
+
- Env abuse (`PATH` prepends, `LD_PRELOAD` hooks).
|
|
46
|
+
- RCE vectors (`subprocess.Popen(shell=True)`, `child_process.spawn('sh')`).
|
|
47
|
+
- High-entropy payloads (Base64/stego in params >500 chars).
|
|
48
|
+
- Unsafe flags (`--no-sandbox`, `--allow-run`).
|
|
49
|
+
|
|
50
|
+
**How it Works:** Parses `StdioServerParameters` JSON, scans strings with RISK_PATTERNS regex + entropy scoring (>4.5 flags encoding). Reuses sanitizer for IPI in args/env. Score 0-10 threshold (>7 high risk); modes: strict (block >4), balanced (>7), permissive (log only). Whitelist for safe patterns.
|
|
51
|
+
|
|
52
|
+
**Output Schema:**
|
|
53
|
+
```typescript
|
|
54
|
+
{
|
|
55
|
+
findings: [{ pattern: string, location: string, snippet: string, severity: 'low'|'med'|'high'|'critical' }],
|
|
56
|
+
score: number,
|
|
57
|
+
safeToSpawn: boolean,
|
|
58
|
+
remediation: string[], // e.g., "Set shell: false"
|
|
59
|
+
mcp_risks: string[] // Sanitizer-detected (e.g., "code_execution")
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Integration:** Pre-init hook in `src/index.ts` (before `StdioServerTransport()`); standalone tool. Logs to audit. False positives mitigated via allowlist/tunable thresholds. Ties to Anthropic MCP defaults (CVE-2026-XXXX); 80% coverage of config-based RCE (Unit 42 2026).
|
|
64
|
+
|
|
65
|
+
Tested: 10 safe/risky params, entropy, whitelist (100% pass in `tests/mcp-config-scan.test.ts`).
|
|
66
|
+
|
|
67
|
+
Visus scans all web content against 45 validated injection pattern categories before delivering it to the LLM.
|
|
39
68
|
|
|
40
69
|
Visus scans all web content against 45 validated injection pattern categories before delivering it to the LLM.
|
|
41
70
|
|
package/dist/src/index.js
CHANGED
|
@@ -26,7 +26,7 @@ console.error('[VISUS-DEBUG] Module loaded');
|
|
|
26
26
|
*/
|
|
27
27
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
28
28
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
29
|
-
import {
|
|
29
|
+
import { ListToolsRequestSchema, ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
30
30
|
import { visusFetch, visusFetchToolDefinition } from './tools/fetch.js';
|
|
31
31
|
import { visusFetchStructured, visusFetchStructuredToolDefinition } from './tools/fetch-structured.js';
|
|
32
32
|
import { visusRead, visusReadToolDefinition } from './tools/read.js';
|
|
@@ -43,6 +43,7 @@ import { detectRuntime, logRuntimeConfig, validateRuntime } from './runtime.js';
|
|
|
43
43
|
import { shouldElicit, buildElicitMessage } from './sanitizer/hitl-gate.js';
|
|
44
44
|
import { runElicitation } from './sanitizer/elicit-runner.js';
|
|
45
45
|
import { SessionLedger } from './security/session-ledger.js';
|
|
46
|
+
import { visusScanMcp, visusScanMcpToolDefinition } from './tools/mcp-config-scan.js';
|
|
46
47
|
/**
|
|
47
48
|
* Create and configure the MCP server
|
|
48
49
|
*/
|
|
@@ -72,7 +73,8 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
72
73
|
visusReadExcelToolDefinition,
|
|
73
74
|
visusReadGsheetToolDefinition,
|
|
74
75
|
visusContextScanToolDefinition,
|
|
75
|
-
visusGetLedgerProofToolDefinition
|
|
76
|
+
visusGetLedgerProofToolDefinition,
|
|
77
|
+
visusScanMcpToolDefinition
|
|
76
78
|
]
|
|
77
79
|
};
|
|
78
80
|
});
|
|
@@ -134,102 +136,6 @@ return { output, blocked: false };
|
|
|
134
136
|
* Handle tool execution requests
|
|
135
137
|
*/
|
|
136
138
|
const ledger = new SessionLedger(); // Global instance for sessions
|
|
137
|
-
// Helper for VSIL middleware
|
|
138
|
-
async function executeToolWithVSIL(name, args) {
|
|
139
|
-
const sessionId = 'session-' + crypto.randomUUID(); // Per-conversation; in prod, tie to MCP session
|
|
140
|
-
const startTime = Date.now();
|
|
141
|
-
switch (name) {
|
|
142
|
-
case 'visus_fetch': {
|
|
143
|
-
const result = await visusFetch(args);
|
|
144
|
-
if (!result.ok)
|
|
145
|
-
throw new McpError(ErrorCode.InternalError, result.error.message);
|
|
146
|
-
// VSIL Check
|
|
147
|
-
const { score, newThreats, chainId, dangling } = await ledger.checkContextualIntegrity(sessionId, name, args, result.value);
|
|
148
|
-
if (score > 0.7) {
|
|
149
|
-
const threatReport = result.value.threat_report;
|
|
150
|
-
const message = 'High session risk detected from prior turns (chains/priming). Proceed with caution?';
|
|
151
|
-
const { proceed, includeReport } = await runElicitation(server, message); // General message
|
|
152
|
-
if (!proceed) {
|
|
153
|
-
return {
|
|
154
|
-
content: [{ type: 'text', text: JSON.stringify({ blocked: true, session_risk: score, reason: 'User declined high-risk session' }, null, 2) }]
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
// Merge new threats
|
|
158
|
-
if (threatReport)
|
|
159
|
-
threatReport.new_threats = [...(threatReport.new_threats || []), ...newThreats];
|
|
160
|
-
}
|
|
161
|
-
// Update ledger
|
|
162
|
-
const hashes = ledger.extractEntityHashes ? await ledger.extractEntityHashes(args, result.value) : []; // If method exposed
|
|
163
|
-
ledger.update(sessionId, hashes, name, newThreats);
|
|
164
|
-
// Extend output
|
|
165
|
-
const extended = { ...result.value };
|
|
166
|
-
if (extended.threat_summary) {
|
|
167
|
-
extended.threat_summary.session_risk = score;
|
|
168
|
-
extended.threat_summary.chain_detected = !!chainId;
|
|
169
|
-
extended.threat_summary.priming_flags = dangling ? ['dangling_instruction'] : [];
|
|
170
|
-
}
|
|
171
|
-
// Existing HITL
|
|
172
|
-
const { output } = await handleCriticalThreatElicitation(extended, args.url);
|
|
173
|
-
return { content: [{ type: 'text', text: JSON.stringify(output, null, 2) }] };
|
|
174
|
-
}
|
|
175
|
-
// Similar for other cases: visus_fetch_structured, etc. - wrap with VSIL logic
|
|
176
|
-
// For visus_context_scan: Pass sessionId
|
|
177
|
-
case 'visus_context_scan': {
|
|
178
|
-
args.sessionId = sessionId; // Inject
|
|
179
|
-
return await visusContextScan(request);
|
|
180
|
-
}
|
|
181
|
-
default:
|
|
182
|
-
// Fallback without VSIL for non-fetch tools
|
|
183
|
-
// ... existing switch
|
|
184
|
-
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
185
|
-
}
|
|
186
|
-
// Merge new threats
|
|
187
|
-
if (threatReport)
|
|
188
|
-
threatReport.new_threats = [...(threatReport.new_threats || []), ...newThreats];
|
|
189
|
-
}
|
|
190
|
-
// Update ledger
|
|
191
|
-
const hashes = ledger.extractEntityHashes ? await ledger.extractEntityHashes(args, result.value) : []; // If method exposed
|
|
192
|
-
ledger.update(sessionId, hashes, name, newThreats);
|
|
193
|
-
// Extend output
|
|
194
|
-
const extended = { ...result.value };
|
|
195
|
-
if (extended.threat_summary) {
|
|
196
|
-
extended.threat_summary.session_risk = score;
|
|
197
|
-
extended.threat_summary.chain_detected = !!chainId;
|
|
198
|
-
extended.threat_summary.priming_flags = dangling ? ['dangling_instruction'] : [];
|
|
199
|
-
}
|
|
200
|
-
// Existing HITL
|
|
201
|
-
const { output } = await handleCriticalThreatElicitation(extended, args.url);
|
|
202
|
-
return { content: [{ type: 'text', text: JSON.stringify(output, null, 2) }] };
|
|
203
|
-
'visus_context_scan';
|
|
204
|
-
{
|
|
205
|
-
args.sessionId = sessionId; // Inject
|
|
206
|
-
return await visusContextScan(request);
|
|
207
|
-
}
|
|
208
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
209
|
-
const { name, arguments: args } = request.params;
|
|
210
|
-
const sessionId = 'session-' + crypto.randomUUID(); // Or from MCP context
|
|
211
|
-
try {
|
|
212
|
-
// Boolean Gate Middleware for Tool Requests (CVE-2026-4399)
|
|
213
|
-
import { detectBooleanGate } from './security/boolean-gate-detector.js';
|
|
214
|
-
const argsStr = JSON.stringify(args);
|
|
215
|
-
const gateResult = detectBooleanGate(argsStr);
|
|
216
|
-
if (gateResult.content_modified) {
|
|
217
|
-
throw new McpError(ErrorCode.InvalidRequest, `Boolean injection detected in tool args: ${gateResult.patterns_detected.join(', ')}`);
|
|
218
|
-
}
|
|
219
|
-
// DB Guard Middleware for SQL/DB tools
|
|
220
|
-
if (name.includes('sql') || name.includes('db') || name === 'visus_db_verify') {
|
|
221
|
-
const guarded = await dbGuardMiddleware(name, args, sessionId);
|
|
222
|
-
args = guarded.args; // Updated args
|
|
223
|
-
}
|
|
224
|
-
// Existing VSIL + HITL
|
|
225
|
-
return await executeToolWithVSIL(name, args, sessionId);
|
|
226
|
-
}
|
|
227
|
-
catch (error) {
|
|
228
|
-
if (error instanceof McpError)
|
|
229
|
-
throw error;
|
|
230
|
-
throw new McpError(ErrorCode.InternalError, `Tool execution failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
231
|
-
}
|
|
232
|
-
});
|
|
233
139
|
'visus_db_verify';
|
|
234
140
|
{
|
|
235
141
|
return await visusDbVerify(args);
|
|
@@ -278,7 +184,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
278
184
|
// VSIL Check (similar)
|
|
279
185
|
const { score, newThreats, chainId, dangling } = await ledger.checkContextualIntegrity(sessionId, name, args, result.value);
|
|
280
186
|
if (score > 0.7) {
|
|
281
|
-
// HITL logic similar to fetch
|
|
282
187
|
const threatReport = result.value.threat_report;
|
|
283
188
|
const message = 'High session risk detected. Proceed with structured extraction?';
|
|
284
189
|
const { proceed } = await runElicitation(server, message);
|
|
@@ -434,153 +339,30 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
434
339
|
}
|
|
435
340
|
'visus_context_scan';
|
|
436
341
|
{
|
|
437
|
-
args.sessionId = sessionId;
|
|
438
|
-
|
|
439
|
-
}
|
|
440
|
-
'visus_get_ledger_proof';
|
|
441
|
-
{
|
|
442
|
-
const { arguments: args } = request.params;
|
|
443
|
-
const result = await visusGetLedgerProof(args.request_id);
|
|
444
|
-
return {
|
|
445
|
-
content: [
|
|
446
|
-
{
|
|
447
|
-
type: 'text',
|
|
448
|
-
text: JSON.stringify(result, null, 2)
|
|
449
|
-
}
|
|
450
|
-
]
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
454
|
-
// Handle HITL elicitation for CRITICAL threats
|
|
455
|
-
const { output } = await handleCriticalThreatElicitation(result.value, args.url);
|
|
456
|
-
return {
|
|
457
|
-
content: [
|
|
458
|
-
{
|
|
459
|
-
type: 'text',
|
|
460
|
-
text: JSON.stringify(output, null, 2)
|
|
461
|
-
}
|
|
462
|
-
]
|
|
463
|
-
};
|
|
464
|
-
'visus_read';
|
|
465
|
-
{
|
|
466
|
-
const result = await visusRead(args);
|
|
467
|
-
if (!result.ok) {
|
|
468
|
-
throw new McpError(ErrorCode.InternalError, `visus_read failed: ${result.error.message}`);
|
|
469
|
-
}
|
|
470
|
-
// Handle HITL elicitation for CRITICAL threats
|
|
471
|
-
const { output } = await handleCriticalThreatElicitation(result.value, args.url);
|
|
472
|
-
return {
|
|
473
|
-
content: [
|
|
474
|
-
{
|
|
475
|
-
type: 'text',
|
|
476
|
-
text: JSON.stringify(output, null, 2)
|
|
477
|
-
}
|
|
478
|
-
]
|
|
479
|
-
};
|
|
480
|
-
}
|
|
481
|
-
'visus_search';
|
|
482
|
-
{
|
|
483
|
-
const result = await visusSearch(args);
|
|
484
|
-
if (!result.ok) {
|
|
485
|
-
throw new McpError(ErrorCode.InternalError, `visus_search failed: ${result.error.message}`);
|
|
486
|
-
}
|
|
487
|
-
// Handle HITL elicitation for CRITICAL threats
|
|
488
|
-
// For search, use the query as the "URL" in the elicitation message
|
|
489
|
-
const { output } = await handleCriticalThreatElicitation(result.value, `search: ${args.query}`);
|
|
490
|
-
return {
|
|
491
|
-
content: [
|
|
492
|
-
{
|
|
493
|
-
type: 'text',
|
|
494
|
-
text: JSON.stringify(output, null, 2)
|
|
495
|
-
}
|
|
496
|
-
]
|
|
497
|
-
};
|
|
498
|
-
}
|
|
499
|
-
'visus_report';
|
|
500
|
-
{
|
|
501
|
-
const result = await visusReport(args);
|
|
502
|
-
if (!result.ok) {
|
|
503
|
-
throw new McpError(ErrorCode.InternalError, `visus_report failed: ${result.error.message}`);
|
|
504
|
-
}
|
|
505
|
-
// No HITL for reports - they are read-only compliance exports
|
|
506
|
-
return {
|
|
507
|
-
content: [
|
|
508
|
-
{
|
|
509
|
-
type: 'text',
|
|
510
|
-
text: JSON.stringify(result.value, null, 2)
|
|
511
|
-
}
|
|
512
|
-
]
|
|
513
|
-
};
|
|
514
|
-
}
|
|
515
|
-
'visus_verify';
|
|
516
|
-
{
|
|
517
|
-
const result = await visusVerify(args);
|
|
518
|
-
if (!result.ok) {
|
|
519
|
-
throw new McpError(ErrorCode.InternalError, `visus_verify failed: ${result.error.message}`);
|
|
520
|
-
}
|
|
521
|
-
// No HITL for verification - it's a read-only audit operation
|
|
342
|
+
args.sessionId = sessionId;
|
|
343
|
+
const result = await visusContextScan(args);
|
|
522
344
|
return {
|
|
523
345
|
content: [
|
|
524
|
-
{
|
|
525
|
-
type: 'text',
|
|
526
|
-
text: JSON.stringify(result.value, null, 2)
|
|
527
|
-
}
|
|
346
|
+
{ type: 'text', text: JSON.stringify(result, null, 2) }
|
|
528
347
|
]
|
|
529
348
|
};
|
|
530
349
|
}
|
|
531
|
-
'
|
|
532
|
-
{
|
|
533
|
-
const result = await visusReadCsv(args);
|
|
534
|
-
if (!result.ok) {
|
|
535
|
-
throw new McpError(ErrorCode.InternalError, `visus_read_csv failed: ${result.error.message}`);
|
|
536
|
-
}
|
|
537
|
-
return {
|
|
538
|
-
content: [
|
|
539
|
-
{
|
|
540
|
-
type: 'text',
|
|
541
|
-
text: JSON.stringify(result.value, null, 2)
|
|
542
|
-
}
|
|
543
|
-
]
|
|
544
|
-
};
|
|
545
|
-
}
|
|
546
|
-
'visus_read_excel';
|
|
547
|
-
{
|
|
548
|
-
const result = await visusReadExcel(args);
|
|
549
|
-
if (!result.ok) {
|
|
550
|
-
throw new McpError(ErrorCode.InternalError, `visus_read_excel failed: ${result.error.message}`);
|
|
551
|
-
}
|
|
552
|
-
return {
|
|
553
|
-
content: [
|
|
554
|
-
{
|
|
555
|
-
type: 'text',
|
|
556
|
-
text: JSON.stringify(result.value, null, 2)
|
|
557
|
-
}
|
|
558
|
-
]
|
|
559
|
-
};
|
|
560
|
-
}
|
|
561
|
-
'visus_read_gsheet';
|
|
350
|
+
'visus_get_ledger_proof';
|
|
562
351
|
{
|
|
563
|
-
const
|
|
564
|
-
|
|
565
|
-
throw new McpError(ErrorCode.InternalError, `visus_read_gsheet failed: ${result.error.message}`);
|
|
566
|
-
}
|
|
352
|
+
const { arguments: args } = request.params;
|
|
353
|
+
const result = await visusGetLedgerProof(args.request_id);
|
|
567
354
|
return {
|
|
568
355
|
content: [
|
|
569
356
|
{
|
|
570
357
|
type: 'text',
|
|
571
|
-
text: JSON.stringify(result
|
|
358
|
+
text: JSON.stringify(result, null, 2)
|
|
572
359
|
}
|
|
573
360
|
]
|
|
574
361
|
};
|
|
575
362
|
}
|
|
576
|
-
'
|
|
577
|
-
{
|
|
578
|
-
return await visusContextScan(request);
|
|
579
|
-
}
|
|
580
|
-
'visus_get_ledger_proof';
|
|
363
|
+
'visus_scan_mcp';
|
|
581
364
|
{
|
|
582
|
-
const
|
|
583
|
-
const result = await visusGetLedgerProof(args.request_id);
|
|
365
|
+
const result = await visusScanMcp(args);
|
|
584
366
|
return {
|
|
585
367
|
content: [
|
|
586
368
|
{
|