visus-mcp 0.6.2 → 0.9.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/.claude/settings.local.json +15 -1
- package/.env.status +7 -0
- package/CHANGELOG.md +110 -0
- package/CLAUDE.md +3 -0
- package/README.md +29 -19
- package/SECURITY.md +2 -0
- package/STATUS.md +320 -12
- package/dist/browser/playwright-renderer.d.ts.map +1 -1
- package/dist/browser/playwright-renderer.js +27 -5
- package/dist/browser/playwright-renderer.js.map +1 -1
- package/dist/content-handlers/index.d.ts +36 -0
- package/dist/content-handlers/index.d.ts.map +1 -0
- package/dist/content-handlers/index.js +59 -0
- package/dist/content-handlers/index.js.map +1 -0
- package/dist/content-handlers/json-handler.d.ts +28 -0
- package/dist/content-handlers/json-handler.d.ts.map +1 -0
- package/dist/content-handlers/json-handler.js +116 -0
- package/dist/content-handlers/json-handler.js.map +1 -0
- package/dist/content-handlers/pdf-handler.d.ts +29 -0
- package/dist/content-handlers/pdf-handler.d.ts.map +1 -0
- package/dist/content-handlers/pdf-handler.js +77 -0
- package/dist/content-handlers/pdf-handler.js.map +1 -0
- package/dist/content-handlers/svg-handler.d.ts +35 -0
- package/dist/content-handlers/svg-handler.d.ts.map +1 -0
- package/dist/content-handlers/svg-handler.js +206 -0
- package/dist/content-handlers/svg-handler.js.map +1 -0
- package/dist/content-handlers/types.d.ts +42 -0
- package/dist/content-handlers/types.d.ts.map +1 -0
- package/dist/content-handlers/types.js +7 -0
- package/dist/content-handlers/types.js.map +1 -0
- package/dist/sanitizer/framework-mapper.d.ts +4 -0
- package/dist/sanitizer/framework-mapper.d.ts.map +1 -1
- package/dist/sanitizer/framework-mapper.js +92 -0
- package/dist/sanitizer/framework-mapper.js.map +1 -1
- package/dist/sanitizer/threat-reporter.d.ts +5 -0
- package/dist/sanitizer/threat-reporter.d.ts.map +1 -1
- package/dist/sanitizer/threat-reporter.js +15 -6
- package/dist/sanitizer/threat-reporter.js.map +1 -1
- package/dist/tools/fetch-structured.d.ts.map +1 -1
- package/dist/tools/fetch-structured.js +4 -0
- package/dist/tools/fetch-structured.js.map +1 -1
- package/dist/tools/fetch.d.ts.map +1 -1
- package/dist/tools/fetch.js +68 -4
- package/dist/tools/fetch.js.map +1 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +4 -0
- package/dist/tools/read.js.map +1 -1
- package/dist/types.d.ts +9 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +2 -1
- package/server.json +25 -14
- package/src/browser/playwright-renderer.ts +29 -6
- package/src/content-handlers/index.ts +72 -0
- package/src/content-handlers/json-handler.ts +137 -0
- package/src/content-handlers/pdf-handler.ts +91 -0
- package/src/content-handlers/svg-handler.ts +243 -0
- package/src/content-handlers/types.ts +44 -0
- package/src/sanitizer/framework-mapper.ts +94 -0
- package/src/sanitizer/threat-reporter.ts +17 -6
- package/src/tools/fetch-structured.ts +5 -0
- package/src/tools/fetch.ts +76 -4
- package/src/tools/read.ts +5 -0
- package/src/types.ts +9 -1
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -47
- package/.github/ISSUE_TEMPLATE/false_positive.md +0 -43
- package/.github/ISSUE_TEMPLATE/new_pattern.md +0 -49
- package/.github/ISSUE_TEMPLATE/security_report.md +0 -31
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -39
- package/.mcpregistry_github_token +0 -1
- package/.mcpregistry_registry_token +0 -1
- package/CONTRIBUTING.md +0 -329
- package/LINKEDIN-STRATEGY.md +0 -367
- package/ROADMAP.md +0 -221
- package/SECURITY-AUDIT-v1.md +0 -277
- package/SUBMISSION.md +0 -66
- package/TROUBLESHOOT-AUTH-20260322-2019.md +0 -291
- package/TROUBLESHOOT-BUILD-20260319-1450.md +0 -546
- package/TROUBLESHOOT-COGNITO-AUTH-20260324-2029.md +0 -415
- package/TROUBLESHOOT-COGNITO-JWT-20260324.md +0 -592
- package/TROUBLESHOOT-FETCH-20260320-1150.md +0 -168
- package/TROUBLESHOOT-JEST-20260323-1357.md +0 -139
- package/TROUBLESHOOT-LAMBDA-20260322-1945.md +0 -183
- package/TROUBLESHOOT-PLAYWRIGHT-20260321-1549.md +0 -217
- package/TROUBLESHOOT-SSL-20260320-1138.md +0 -171
- package/TROUBLESHOOT-STRUCTURED-20260320-1200.md +0 -246
- package/TROUBLESHOOT-TEST-20260320-0942.md +0 -281
- package/VISUS-CLAUDE-CODE-PROMPT.md +0 -324
- package/VISUS-PROJECT-PLAN.md +0 -205
- package/cdk.json +0 -73
- package/infrastructure/app.ts +0 -39
- package/infrastructure/stack.ts +0 -298
- package/jest.config.js +0 -33
- package/jest.setup.js +0 -9
- package/lambda-deploy/index.js +0 -81512
- package/lambda-deploy/index.js.map +0 -7
- package/lambda-package/browser/__mocks__/playwright-renderer.d.ts +0 -25
- package/lambda-package/browser/__mocks__/playwright-renderer.d.ts.map +0 -1
- package/lambda-package/browser/__mocks__/playwright-renderer.js +0 -119
- package/lambda-package/browser/__mocks__/playwright-renderer.js.map +0 -1
- package/lambda-package/browser/playwright-renderer.d.ts +0 -40
- package/lambda-package/browser/playwright-renderer.d.ts.map +0 -1
- package/lambda-package/browser/playwright-renderer.js +0 -214
- package/lambda-package/browser/playwright-renderer.js.map +0 -1
- package/lambda-package/browser/reader.d.ts +0 -31
- package/lambda-package/browser/reader.d.ts.map +0 -1
- package/lambda-package/browser/reader.js +0 -98
- package/lambda-package/browser/reader.js.map +0 -1
- package/lambda-package/index.d.ts +0 -18
- package/lambda-package/index.d.ts.map +0 -1
- package/lambda-package/index.js +0 -238
- package/lambda-package/index.js.map +0 -1
- package/lambda-package/lambda-handler.d.ts +0 -28
- package/lambda-package/lambda-handler.d.ts.map +0 -1
- package/lambda-package/lambda-handler.js +0 -257
- package/lambda-package/lambda-handler.js.map +0 -1
- package/lambda-package/package-lock.json +0 -7435
- package/lambda-package/package.json +0 -74
- package/lambda-package/runtime.d.ts +0 -50
- package/lambda-package/runtime.d.ts.map +0 -1
- package/lambda-package/runtime.js +0 -86
- package/lambda-package/runtime.js.map +0 -1
- package/lambda-package/sanitizer/elicit-runner.d.ts +0 -48
- package/lambda-package/sanitizer/elicit-runner.d.ts.map +0 -1
- package/lambda-package/sanitizer/elicit-runner.js +0 -100
- package/lambda-package/sanitizer/elicit-runner.js.map +0 -1
- package/lambda-package/sanitizer/framework-mapper.d.ts +0 -24
- package/lambda-package/sanitizer/framework-mapper.d.ts.map +0 -1
- package/lambda-package/sanitizer/framework-mapper.js +0 -342
- package/lambda-package/sanitizer/framework-mapper.js.map +0 -1
- package/lambda-package/sanitizer/hitl-gate.d.ts +0 -69
- package/lambda-package/sanitizer/hitl-gate.d.ts.map +0 -1
- package/lambda-package/sanitizer/hitl-gate.js +0 -101
- package/lambda-package/sanitizer/hitl-gate.js.map +0 -1
- package/lambda-package/sanitizer/index.d.ts +0 -63
- package/lambda-package/sanitizer/index.d.ts.map +0 -1
- package/lambda-package/sanitizer/index.js +0 -105
- package/lambda-package/sanitizer/index.js.map +0 -1
- package/lambda-package/sanitizer/injection-detector.d.ts +0 -34
- package/lambda-package/sanitizer/injection-detector.d.ts.map +0 -1
- package/lambda-package/sanitizer/injection-detector.js +0 -89
- package/lambda-package/sanitizer/injection-detector.js.map +0 -1
- package/lambda-package/sanitizer/patterns.d.ts +0 -30
- package/lambda-package/sanitizer/patterns.d.ts.map +0 -1
- package/lambda-package/sanitizer/patterns.js +0 -372
- package/lambda-package/sanitizer/patterns.js.map +0 -1
- package/lambda-package/sanitizer/pii-allowlist.d.ts +0 -49
- package/lambda-package/sanitizer/pii-allowlist.d.ts.map +0 -1
- package/lambda-package/sanitizer/pii-allowlist.js +0 -231
- package/lambda-package/sanitizer/pii-allowlist.js.map +0 -1
- package/lambda-package/sanitizer/pii-redactor.d.ts +0 -41
- package/lambda-package/sanitizer/pii-redactor.d.ts.map +0 -1
- package/lambda-package/sanitizer/pii-redactor.js +0 -213
- package/lambda-package/sanitizer/pii-redactor.js.map +0 -1
- package/lambda-package/sanitizer/severity-classifier.d.ts +0 -33
- package/lambda-package/sanitizer/severity-classifier.d.ts.map +0 -1
- package/lambda-package/sanitizer/severity-classifier.js +0 -113
- package/lambda-package/sanitizer/severity-classifier.js.map +0 -1
- package/lambda-package/sanitizer/threat-reporter.d.ts +0 -66
- package/lambda-package/sanitizer/threat-reporter.d.ts.map +0 -1
- package/lambda-package/sanitizer/threat-reporter.js +0 -163
- package/lambda-package/sanitizer/threat-reporter.js.map +0 -1
- package/lambda-package/tools/fetch-structured.d.ts +0 -51
- package/lambda-package/tools/fetch-structured.d.ts.map +0 -1
- package/lambda-package/tools/fetch-structured.js +0 -237
- package/lambda-package/tools/fetch-structured.js.map +0 -1
- package/lambda-package/tools/fetch.d.ts +0 -49
- package/lambda-package/tools/fetch.d.ts.map +0 -1
- package/lambda-package/tools/fetch.js +0 -131
- package/lambda-package/tools/fetch.js.map +0 -1
- package/lambda-package/tools/read.d.ts +0 -51
- package/lambda-package/tools/read.d.ts.map +0 -1
- package/lambda-package/tools/read.js +0 -127
- package/lambda-package/tools/read.js.map +0 -1
- package/lambda-package/tools/search.d.ts +0 -45
- package/lambda-package/tools/search.d.ts.map +0 -1
- package/lambda-package/tools/search.js +0 -220
- package/lambda-package/tools/search.js.map +0 -1
- package/lambda-package/types.d.ts +0 -167
- package/lambda-package/types.d.ts.map +0 -1
- package/lambda-package/types.js +0 -16
- package/lambda-package/types.js.map +0 -1
- package/lambda-package/utils/format-converter.d.ts +0 -39
- package/lambda-package/utils/format-converter.d.ts.map +0 -1
- package/lambda-package/utils/format-converter.js +0 -191
- package/lambda-package/utils/format-converter.js.map +0 -1
- package/lambda-package/utils/truncate.d.ts +0 -26
- package/lambda-package/utils/truncate.d.ts.map +0 -1
- package/lambda-package/utils/truncate.js +0 -54
- package/lambda-package/utils/truncate.js.map +0 -1
- package/lambda.zip +0 -0
- package/test-output.txt +0 -4
- package/tests/auth-smoke.test.ts +0 -480
- package/tests/elicit-runner.test.ts +0 -232
- package/tests/fetch-tool.test.ts +0 -922
- package/tests/hitl-gate.test.ts +0 -267
- package/tests/injection-corpus.ts +0 -338
- package/tests/pii-allowlist.test.ts +0 -282
- package/tests/reader.test.ts +0 -353
- package/tests/sanitizer.test.ts +0 -358
- package/tests/search.test.ts +0 -456
- package/tests/threat-reporter.test.ts +0 -334
- package/tsconfig.cdk.json +0 -35
|
@@ -9,7 +9,10 @@
|
|
|
9
9
|
* Aligned with:
|
|
10
10
|
* - OWASP LLM Top 10 (2025)
|
|
11
11
|
* - NIST AI 600-1 (Generative AI Profile)
|
|
12
|
+
* - NIST AI RMF (AI Risk Management Framework)
|
|
13
|
+
* - NIST CSF 2.0 (Cybersecurity Framework 2.0)
|
|
12
14
|
* - MITRE ATLAS (Adversarial Threat Landscape)
|
|
15
|
+
* - ISO/IEC 42001:2023 (AI Management System)
|
|
13
16
|
*/
|
|
14
17
|
|
|
15
18
|
import {
|
|
@@ -34,6 +37,8 @@ export interface ThreatFinding {
|
|
|
34
37
|
confidence: number;
|
|
35
38
|
owasp_llm: string;
|
|
36
39
|
nist_ai_600_1: string;
|
|
40
|
+
nist_ai_rmf: string;
|
|
41
|
+
nist_csf_2_0: string;
|
|
37
42
|
mitre_atlas: string;
|
|
38
43
|
iso_42001: string;
|
|
39
44
|
remediation: string;
|
|
@@ -103,6 +108,8 @@ function buildFindings(patternsDetected: string[]): ThreatFinding[] {
|
|
|
103
108
|
confidence: 0.95, // Default confidence; can be enhanced later
|
|
104
109
|
owasp_llm: frameworks.owasp_llm,
|
|
105
110
|
nist_ai_600_1: frameworks.nist_ai_600_1,
|
|
111
|
+
nist_ai_rmf: frameworks.nist_ai_rmf,
|
|
112
|
+
nist_csf_2_0: frameworks.nist_csf_2_0,
|
|
106
113
|
mitre_atlas: frameworks.mitre_atlas,
|
|
107
114
|
iso_42001: frameworks.iso_42001,
|
|
108
115
|
remediation: `Content sanitized. ${category.replace(/_/g, ' ')} removed.`
|
|
@@ -126,9 +133,9 @@ function generateToonFindings(findings: ThreatFinding[]): string {
|
|
|
126
133
|
* Fallback manual TOON format generation
|
|
127
134
|
*/
|
|
128
135
|
function generateManualToonFormat(findings: ThreatFinding[]): string {
|
|
129
|
-
const header = `findings[${findings.length}]{id,pattern_id,category,severity,confidence,owasp_llm,nist_ai_600_1,mitre_atlas,iso_42001,remediation}:`;
|
|
136
|
+
const header = `findings[${findings.length}]{id,pattern_id,category,severity,confidence,owasp_llm,nist_ai_600_1,nist_ai_rmf,nist_csf_2_0,mitre_atlas,iso_42001,remediation}:`;
|
|
130
137
|
const rows = findings.map(f =>
|
|
131
|
-
`${f.id},${f.pattern_id},${f.category},${f.severity},${f.confidence},${f.owasp_llm},${f.nist_ai_600_1},${f.mitre_atlas},${f.iso_42001},${f.remediation}`
|
|
138
|
+
`${f.id},${f.pattern_id},${f.category},${f.severity},${f.confidence},${f.owasp_llm},${f.nist_ai_600_1},${f.nist_ai_rmf},${f.nist_csf_2_0},${f.mitre_atlas},${f.iso_42001},${f.remediation}`
|
|
132
139
|
);
|
|
133
140
|
return `${header}\n${rows.join('\n')}`;
|
|
134
141
|
}
|
|
@@ -151,7 +158,7 @@ function generateMarkdownReport(
|
|
|
151
158
|
markdown += `**Generated:** ${timestamp}\n`;
|
|
152
159
|
markdown += `**Source:** ${sourceUrl}\n`;
|
|
153
160
|
markdown += `**Overall Severity:** ${overallSeverity}\n`;
|
|
154
|
-
markdown += `**Framework:** OWASP LLM Top 10 | NIST AI 600-1 | MITRE ATLAS | ISO/IEC 42001\n\n`;
|
|
161
|
+
markdown += `**Framework:** OWASP LLM Top 10 | NIST AI 600-1 | NIST AI RMF | NIST CSF 2.0 | MITRE ATLAS | ISO/IEC 42001\n\n`;
|
|
155
162
|
|
|
156
163
|
// Findings Summary
|
|
157
164
|
markdown += '### Findings Summary\n';
|
|
@@ -165,16 +172,18 @@ function generateMarkdownReport(
|
|
|
165
172
|
// Findings Detail (only if we have findings)
|
|
166
173
|
if (findings.length > 0) {
|
|
167
174
|
markdown += '### Findings Detail\n';
|
|
168
|
-
markdown += '| # | Category | Severity |
|
|
169
|
-
markdown += '
|
|
175
|
+
markdown += '| # | Category | Severity | Conf | OWASP | AI-RMF | CSF 2.0 | MITRE | ISO |\n';
|
|
176
|
+
markdown += '|---|---|---|---|---|---|---|---|---|\n';
|
|
170
177
|
|
|
171
178
|
for (const finding of findings.slice(0, 10)) { // Limit to first 10 for readability
|
|
172
179
|
const confidencePct = Math.round(finding.confidence * 100);
|
|
173
180
|
const owaspShort = finding.owasp_llm.split(' - ')[0]; // e.g., "LLM01:2025"
|
|
181
|
+
const nistRmfShort = finding.nist_ai_rmf.split(' - ')[0]; // e.g., "MEASURE-2.7"
|
|
182
|
+
const nistCsfShort = finding.nist_csf_2_0.split(' - ')[0]; // e.g., "DE.CM-01"
|
|
174
183
|
const mitreShort = finding.mitre_atlas.split(' - ')[0]; // e.g., "AML.T0051.000"
|
|
175
184
|
const isoShort = finding.iso_42001.split(' - ')[0]; // e.g., "A.6.1.5"
|
|
176
185
|
|
|
177
|
-
markdown += `| ${finding.id} | ${finding.category} | ${finding.severity} | ${confidencePct}% | ${owaspShort} | ${mitreShort} | ${isoShort} |\n`;
|
|
186
|
+
markdown += `| ${finding.id} | ${finding.category} | ${finding.severity} | ${confidencePct}% | ${owaspShort} | ${nistRmfShort} | ${nistCsfShort} | ${mitreShort} | ${isoShort} |\n`;
|
|
178
187
|
}
|
|
179
188
|
|
|
180
189
|
if (findings.length > 10) {
|
|
@@ -256,6 +265,8 @@ export function generateThreatReport(input: ThreatReportInput): ThreatReport | n
|
|
|
256
265
|
frameworks: [
|
|
257
266
|
'OWASP LLM Top 10',
|
|
258
267
|
'NIST AI 600-1',
|
|
268
|
+
'NIST AI RMF',
|
|
269
|
+
'NIST CSF 2.0',
|
|
259
270
|
'MITRE ATLAS',
|
|
260
271
|
'ISO/IEC 42001'
|
|
261
272
|
],
|
|
@@ -119,6 +119,11 @@ export async function visusFetchStructured(
|
|
|
119
119
|
|
|
120
120
|
const { title, html } = renderResult.value;
|
|
121
121
|
|
|
122
|
+
// Type guard: fetch-structured only works with HTML (string), not binary content
|
|
123
|
+
if (Buffer.isBuffer(html)) {
|
|
124
|
+
return Err(new Error('fetch-structured does not support binary content types (PDFs, images). Use visus_fetch instead.'));
|
|
125
|
+
}
|
|
126
|
+
|
|
122
127
|
// Step 2: Extract structured data from HTML using cheerio
|
|
123
128
|
const extractedData = extractStructuredData(html, schema);
|
|
124
129
|
|
package/src/tools/fetch.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { renderPage } from '../browser/playwright-renderer.js';
|
|
|
10
10
|
import { sanitize } from '../sanitizer/index.js';
|
|
11
11
|
import { truncateContent } from '../utils/truncate.js';
|
|
12
12
|
import { detectFormat, convertJson, convertXml, convertRss } from '../utils/format-converter.js';
|
|
13
|
+
import { routeContentHandler, normalizeMimeType } from '../content-handlers/index.js';
|
|
13
14
|
import type { VisusFetchInput, VisusFetchOutput, Result } from '../types.js';
|
|
14
15
|
import { Err } from '../types.js';
|
|
15
16
|
|
|
@@ -39,10 +40,81 @@ export async function visusFetch(input: VisusFetchInput): Promise<Result<VisusFe
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
const { html, title, contentType } = renderResult.value;
|
|
43
|
+
// rawContent can be string (HTML/JSON/SVG) or Buffer (PDF, binary)
|
|
42
44
|
const rawContent = html || '';
|
|
43
45
|
|
|
44
|
-
// Step 2: Detect
|
|
46
|
+
// Step 2: Detect content type and route to specialized handlers if applicable
|
|
45
47
|
const detectedContentType = contentType || 'text/html';
|
|
48
|
+
const normalizedMime = normalizeMimeType(detectedContentType);
|
|
49
|
+
|
|
50
|
+
// Check if content requires specialized handler (PDF, JSON, SVG)
|
|
51
|
+
if (normalizedMime === 'application/pdf' ||
|
|
52
|
+
normalizedMime === 'application/json' ||
|
|
53
|
+
normalizedMime === 'text/json' ||
|
|
54
|
+
normalizedMime === 'image/svg+xml') {
|
|
55
|
+
|
|
56
|
+
// Route to specialized content handler
|
|
57
|
+
// Note: rawContent may be Buffer for PDFs, string for JSON/SVG
|
|
58
|
+
const handlerResult = await routeContentHandler(rawContent, detectedContentType);
|
|
59
|
+
|
|
60
|
+
// Handle unsupported or error cases
|
|
61
|
+
if (handlerResult.status === 'rejected' || handlerResult.status === 'error') {
|
|
62
|
+
return Err(new Error(handlerResult.message));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Type guard: ensure we have a success result
|
|
66
|
+
if (handlerResult.status !== 'sanitized') {
|
|
67
|
+
return Err(new Error('Unexpected handler result status'));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Handler success - use the already-sanitized content
|
|
71
|
+
const sanitizedContent = handlerResult.sanitized_content;
|
|
72
|
+
const sanitization = handlerResult.sanitization;
|
|
73
|
+
const truncationResult = truncateContent(sanitizedContent);
|
|
74
|
+
|
|
75
|
+
// Determine format_detected based on MIME type
|
|
76
|
+
let formatDetected: 'html' | 'json' | 'xml' | 'rss' = 'html';
|
|
77
|
+
if (normalizedMime === 'application/json' || normalizedMime === 'text/json') {
|
|
78
|
+
formatDetected = 'json';
|
|
79
|
+
} else if (normalizedMime === 'image/svg+xml') {
|
|
80
|
+
formatDetected = 'xml'; // SVG is XML-based
|
|
81
|
+
} else if (normalizedMime === 'application/pdf') {
|
|
82
|
+
// PDF doesn't have a format_detected value in the current schema
|
|
83
|
+
// Leaving as 'html' for now
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const output: VisusFetchOutput = {
|
|
87
|
+
url,
|
|
88
|
+
content: truncationResult.content,
|
|
89
|
+
sanitization: {
|
|
90
|
+
patterns_detected: sanitization.patterns_detected,
|
|
91
|
+
pii_types_redacted: sanitization.pii_types_redacted,
|
|
92
|
+
pii_allowlisted: sanitization.pii_allowlisted,
|
|
93
|
+
content_modified: sanitization.sanitized_fields > 0
|
|
94
|
+
},
|
|
95
|
+
metadata: {
|
|
96
|
+
title: title || 'Untitled',
|
|
97
|
+
fetched_at: new Date().toISOString(),
|
|
98
|
+
content_length_original: rawContent.length,
|
|
99
|
+
content_length_sanitized: sanitizedContent.length,
|
|
100
|
+
format_detected: formatDetected,
|
|
101
|
+
content_type: detectedContentType,
|
|
102
|
+
...(truncationResult.truncated && {
|
|
103
|
+
truncated: true,
|
|
104
|
+
truncated_at_chars: truncationResult.truncated_at_chars
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
return { ok: true, value: output };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Step 3: For HTML/XML/RSS - use existing format conversion flow
|
|
113
|
+
// Type guard: rawContent should be string for non-binary types
|
|
114
|
+
if (Buffer.isBuffer(rawContent)) {
|
|
115
|
+
return Err(new Error('Unexpected binary content in HTML/XML/RSS path'));
|
|
116
|
+
}
|
|
117
|
+
|
|
46
118
|
const formatType = detectFormat(detectedContentType);
|
|
47
119
|
|
|
48
120
|
let processedContent = rawContent;
|
|
@@ -57,15 +129,15 @@ export async function visusFetch(input: VisusFetchInput): Promise<Result<VisusFe
|
|
|
57
129
|
}
|
|
58
130
|
// For 'html' format, processedContent remains as rawContent
|
|
59
131
|
|
|
60
|
-
// Step
|
|
132
|
+
// Step 4: CRITICAL - Sanitize content (injection detection + PII redaction with allowlisting)
|
|
61
133
|
// This step CANNOT be skipped or bypassed
|
|
62
134
|
const sanitizationResult = sanitize(processedContent, url);
|
|
63
135
|
|
|
64
|
-
// Step
|
|
136
|
+
// Step 5: Apply token ceiling truncation (AFTER sanitization)
|
|
65
137
|
// Anthropic MCP Directory enforces 25,000 token response limit
|
|
66
138
|
const truncationResult = truncateContent(sanitizationResult.content);
|
|
67
139
|
|
|
68
|
-
// Step
|
|
140
|
+
// Step 6: Build output
|
|
69
141
|
const output: VisusFetchOutput = {
|
|
70
142
|
url,
|
|
71
143
|
content: truncationResult.content,
|
package/src/tools/read.ts
CHANGED
|
@@ -48,6 +48,11 @@ export async function visusRead(input: VisusReadInput): Promise<Result<VisusRead
|
|
|
48
48
|
|
|
49
49
|
const { html, title: pageTitle } = renderResult.value;
|
|
50
50
|
|
|
51
|
+
// Type guard: visus_read only works with HTML (string), not binary content
|
|
52
|
+
if (Buffer.isBuffer(html)) {
|
|
53
|
+
return Err(new Error('visus_read does not support binary content types (PDFs, images). Use visus_fetch instead.'));
|
|
54
|
+
}
|
|
55
|
+
|
|
51
56
|
// Step 2: Extract article content using Readability
|
|
52
57
|
const readerResult = extractArticle(html, url);
|
|
53
58
|
|
package/src/types.ts
CHANGED
|
@@ -130,9 +130,17 @@ export interface VisusSearchOutput {
|
|
|
130
130
|
|
|
131
131
|
/**
|
|
132
132
|
* Result from browser rendering
|
|
133
|
+
*
|
|
134
|
+
* @property html - Response content as string (for text) or Buffer (for binary like PDFs)
|
|
135
|
+
* Use Buffer for application/pdf, image/*, and other binary types
|
|
136
|
+
* @property title - Page title extracted from response
|
|
137
|
+
* @property url - Final URL after redirects
|
|
138
|
+
* @property contentType - Content-Type header value (e.g., "application/pdf", "text/html")
|
|
139
|
+
* @property text - Optional text content (when available)
|
|
140
|
+
* @property error - Error message if rendering failed
|
|
133
141
|
*/
|
|
134
142
|
export interface BrowserRenderResult {
|
|
135
|
-
html: string;
|
|
143
|
+
html: string | Buffer;
|
|
136
144
|
title: string;
|
|
137
145
|
url: string;
|
|
138
146
|
contentType?: string;
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: Bug Report
|
|
3
|
-
about: Something isn't working correctly
|
|
4
|
-
title: '[BUG] '
|
|
5
|
-
labels: bug
|
|
6
|
-
assignees: ''
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Describe the bug
|
|
10
|
-
|
|
11
|
-
A clear description of what went wrong.
|
|
12
|
-
|
|
13
|
-
## Tool used
|
|
14
|
-
|
|
15
|
-
- [ ] visus_fetch
|
|
16
|
-
- [ ] visus_read
|
|
17
|
-
- [ ] visus_search
|
|
18
|
-
- [ ] visus_fetch_structured
|
|
19
|
-
|
|
20
|
-
## To Reproduce
|
|
21
|
-
|
|
22
|
-
1. Tool call (URL and parameters — remove any sensitive URLs)
|
|
23
|
-
2. Expected output
|
|
24
|
-
3. Actual output
|
|
25
|
-
|
|
26
|
-
## Sanitization metadata
|
|
27
|
-
|
|
28
|
-
Paste the `sanitization` block from the tool response if relevant.
|
|
29
|
-
|
|
30
|
-
```json
|
|
31
|
-
{
|
|
32
|
-
"patterns_detected": [],
|
|
33
|
-
"pii_types_redacted": [],
|
|
34
|
-
"content_modified": false
|
|
35
|
-
}
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Environment
|
|
39
|
-
|
|
40
|
-
- visus-mcp version: (run `npx visus-mcp --version`)
|
|
41
|
-
- Node.js version: (run `node --version`)
|
|
42
|
-
- OS: (macOS / Windows / Linux)
|
|
43
|
-
- Claude Desktop version (if applicable):
|
|
44
|
-
|
|
45
|
-
## Additional context
|
|
46
|
-
|
|
47
|
-
Any other relevant information.
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: False Positive Report
|
|
3
|
-
about: visus incorrectly flagged or redacted legitimate content
|
|
4
|
-
title: '[FALSE POSITIVE] '
|
|
5
|
-
labels: false-positive, sanitizer
|
|
6
|
-
assignees: ''
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## What was incorrectly flagged?
|
|
10
|
-
|
|
11
|
-
Describe the legitimate content that was redacted or blocked.
|
|
12
|
-
Do NOT include sensitive URLs or private content.
|
|
13
|
-
|
|
14
|
-
## Pattern category that fired
|
|
15
|
-
|
|
16
|
-
Which pattern triggered? (visible in the `patterns_detected` field of the sanitization metadata)
|
|
17
|
-
|
|
18
|
-
Pattern name: `pattern_name_here`
|
|
19
|
-
|
|
20
|
-
## Example of the content
|
|
21
|
-
|
|
22
|
-
A minimal example of the text that triggered the false positive.
|
|
23
|
-
Keep it short — just enough to reproduce the pattern match.
|
|
24
|
-
|
|
25
|
-
```
|
|
26
|
-
Example content here
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Expected behavior
|
|
30
|
-
|
|
31
|
-
What should visus have done with this content?
|
|
32
|
-
|
|
33
|
-
## Domain context
|
|
34
|
-
|
|
35
|
-
What type of site was this? (news, documentation, health info, government, etc.) — helps assess if a domain-scoped allowlist is appropriate.
|
|
36
|
-
|
|
37
|
-
Domain type: ___
|
|
38
|
-
|
|
39
|
-
## visus-mcp version
|
|
40
|
-
|
|
41
|
-
Run `npx visus-mcp --version`:
|
|
42
|
-
|
|
43
|
-
Version: ___
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: New Injection Pattern Request
|
|
3
|
-
about: Suggest a new prompt injection pattern category
|
|
4
|
-
title: '[PATTERN] '
|
|
5
|
-
labels: enhancement, sanitizer
|
|
6
|
-
assignees: ''
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Pattern description
|
|
10
|
-
|
|
11
|
-
What type of injection attack does this detect?
|
|
12
|
-
|
|
13
|
-
## Example of malicious content
|
|
14
|
-
|
|
15
|
-
A concrete example of what this pattern should catch.
|
|
16
|
-
(These examples are for security research — they will be added to the test corpus.)
|
|
17
|
-
|
|
18
|
-
**Example 1 (should be caught):**
|
|
19
|
-
```
|
|
20
|
-
[paste example here]
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
**Example 2 (should NOT be caught — negative case):**
|
|
24
|
-
```
|
|
25
|
-
[paste legitimate content that looks similar but is safe]
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Proposed severity level
|
|
29
|
-
|
|
30
|
-
- [ ] CRITICAL — Immediate threat, direct instruction manipulation
|
|
31
|
-
- [ ] HIGH — Significant obfuscation or context manipulation
|
|
32
|
-
- [ ] MEDIUM — Moderate risk, indirect attack vectors
|
|
33
|
-
- [ ] LOW — Low risk, informational flag
|
|
34
|
-
|
|
35
|
-
## Framework mapping (if known)
|
|
36
|
-
|
|
37
|
-
- OWASP LLM Top 10: (e.g. LLM01:2025)
|
|
38
|
-
- NIST AI 600-1: (e.g. MS-2.5)
|
|
39
|
-
- MITRE ATLAS: (e.g. AML.T0051)
|
|
40
|
-
- ISO/IEC 42001: (e.g. A.6.1.5)
|
|
41
|
-
|
|
42
|
-
## Real-world context
|
|
43
|
-
|
|
44
|
-
Where have you seen this pattern used? (CVE, research paper, red team exercise, etc.)
|
|
45
|
-
|
|
46
|
-
## Are you willing to submit a PR?
|
|
47
|
-
|
|
48
|
-
- [ ] Yes, I'll implement and test this pattern
|
|
49
|
-
- [ ] No, but I can provide more examples if needed
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: Security Vulnerability
|
|
3
|
-
about: Report a security vulnerability in visus-mcp
|
|
4
|
-
title: '[SECURITY] '
|
|
5
|
-
labels: security
|
|
6
|
-
assignees: ''
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# ⚠️ STOP — Do not file public security issues.
|
|
10
|
-
|
|
11
|
-
If you have found a security vulnerability in visus-mcp, please report it **privately**:
|
|
12
|
-
|
|
13
|
-
📧 **Email:** security@lateos.ai
|
|
14
|
-
|
|
15
|
-
🔒 **GitHub Security Advisories:**
|
|
16
|
-
https://github.com/visus-mcp/visus-mcp/security/advisories/new
|
|
17
|
-
|
|
18
|
-
## Include in your report:
|
|
19
|
-
|
|
20
|
-
- Description of the vulnerability
|
|
21
|
-
- Steps to reproduce
|
|
22
|
-
- Potential impact
|
|
23
|
-
- Suggested fix (optional)
|
|
24
|
-
|
|
25
|
-
We aim to respond within **48 hours** and will credit you in SECURITY.md once the issue is resolved.
|
|
26
|
-
|
|
27
|
-
Thank you for helping keep visus-mcp secure.
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
**Do not proceed with this public issue template for security vulnerabilities.**
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
## What does this PR do?
|
|
2
|
-
|
|
3
|
-
Brief description of the change.
|
|
4
|
-
|
|
5
|
-
## Type of change
|
|
6
|
-
|
|
7
|
-
- [ ] New injection pattern
|
|
8
|
-
- [ ] False positive fix
|
|
9
|
-
- [ ] Bug fix
|
|
10
|
-
- [ ] Feature addition
|
|
11
|
-
- [ ] Documentation update
|
|
12
|
-
- [ ] Performance improvement
|
|
13
|
-
|
|
14
|
-
## Test results
|
|
15
|
-
|
|
16
|
-
- [ ] npm test passes (all tests)
|
|
17
|
-
- [ ] npm run build compiles cleanly
|
|
18
|
-
- [ ] New tests added for new functionality
|
|
19
|
-
- [ ] Test count did not decrease
|
|
20
|
-
|
|
21
|
-
**Before:** X/X tests passing
|
|
22
|
-
**After:** X/X tests passing
|
|
23
|
-
|
|
24
|
-
## Security checklist
|
|
25
|
-
|
|
26
|
-
- [ ] All new content passes through the sanitizer pipeline
|
|
27
|
-
- [ ] No new `any` types introduced
|
|
28
|
-
- [ ] No console.log (use structured stderr logging)
|
|
29
|
-
- [ ] No hardcoded secrets or API keys
|
|
30
|
-
- [ ] CLAUDE.md security rules not violated
|
|
31
|
-
|
|
32
|
-
## Does this change affect sanitization coverage?
|
|
33
|
-
|
|
34
|
-
- [ ] No
|
|
35
|
-
- [ ] Yes — explain: ___
|
|
36
|
-
|
|
37
|
-
## Related issues
|
|
38
|
-
|
|
39
|
-
Closes #___
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
ghu_9xbQ1SXXZ8bh3BmoTNB21qUfmu31cJ0ly3mc
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"token":"eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJtY3AtcmVnaXN0cnkiLCJleHAiOjE3NzQzMDc2MzIsIm5iZiI6MTc3NDMwNzMzMiwiaWF0IjoxNzc0MzA3MzMyLCJhdXRoX21ldGhvZCI6ImdpdGh1Yi1hdCIsImF1dGhfbWV0aG9kX3N1YiI6Imxlb2Nob25nIiwicGVybWlzc2lvbnMiOlt7ImFjdGlvbiI6InB1Ymxpc2giLCJyZXNvdXJjZSI6ImlvLmdpdGh1Yi5sZW9jaG9uZy8qIn0seyJhY3Rpb24iOiJwdWJsaXNoIiwicmVzb3VyY2UiOiJpby5naXRodWIudmlzdXMtbWNwLyoifV19.qXNypXv1OF_l3ob4Kju46rvn-vs4s5x3JIU2ZH7jJiaBV5gy0sX60NJSKM9ybJvcXggnW9_Mp1L-JeZDPVd5BQ","expires_at":1774307632}
|