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.
Files changed (203) hide show
  1. package/.claude/settings.local.json +15 -1
  2. package/.env.status +7 -0
  3. package/CHANGELOG.md +110 -0
  4. package/CLAUDE.md +3 -0
  5. package/README.md +29 -19
  6. package/SECURITY.md +2 -0
  7. package/STATUS.md +320 -12
  8. package/dist/browser/playwright-renderer.d.ts.map +1 -1
  9. package/dist/browser/playwright-renderer.js +27 -5
  10. package/dist/browser/playwright-renderer.js.map +1 -1
  11. package/dist/content-handlers/index.d.ts +36 -0
  12. package/dist/content-handlers/index.d.ts.map +1 -0
  13. package/dist/content-handlers/index.js +59 -0
  14. package/dist/content-handlers/index.js.map +1 -0
  15. package/dist/content-handlers/json-handler.d.ts +28 -0
  16. package/dist/content-handlers/json-handler.d.ts.map +1 -0
  17. package/dist/content-handlers/json-handler.js +116 -0
  18. package/dist/content-handlers/json-handler.js.map +1 -0
  19. package/dist/content-handlers/pdf-handler.d.ts +29 -0
  20. package/dist/content-handlers/pdf-handler.d.ts.map +1 -0
  21. package/dist/content-handlers/pdf-handler.js +77 -0
  22. package/dist/content-handlers/pdf-handler.js.map +1 -0
  23. package/dist/content-handlers/svg-handler.d.ts +35 -0
  24. package/dist/content-handlers/svg-handler.d.ts.map +1 -0
  25. package/dist/content-handlers/svg-handler.js +206 -0
  26. package/dist/content-handlers/svg-handler.js.map +1 -0
  27. package/dist/content-handlers/types.d.ts +42 -0
  28. package/dist/content-handlers/types.d.ts.map +1 -0
  29. package/dist/content-handlers/types.js +7 -0
  30. package/dist/content-handlers/types.js.map +1 -0
  31. package/dist/sanitizer/framework-mapper.d.ts +4 -0
  32. package/dist/sanitizer/framework-mapper.d.ts.map +1 -1
  33. package/dist/sanitizer/framework-mapper.js +92 -0
  34. package/dist/sanitizer/framework-mapper.js.map +1 -1
  35. package/dist/sanitizer/threat-reporter.d.ts +5 -0
  36. package/dist/sanitizer/threat-reporter.d.ts.map +1 -1
  37. package/dist/sanitizer/threat-reporter.js +15 -6
  38. package/dist/sanitizer/threat-reporter.js.map +1 -1
  39. package/dist/tools/fetch-structured.d.ts.map +1 -1
  40. package/dist/tools/fetch-structured.js +4 -0
  41. package/dist/tools/fetch-structured.js.map +1 -1
  42. package/dist/tools/fetch.d.ts.map +1 -1
  43. package/dist/tools/fetch.js +68 -4
  44. package/dist/tools/fetch.js.map +1 -1
  45. package/dist/tools/read.d.ts.map +1 -1
  46. package/dist/tools/read.js +4 -0
  47. package/dist/tools/read.js.map +1 -1
  48. package/dist/types.d.ts +9 -1
  49. package/dist/types.d.ts.map +1 -1
  50. package/dist/types.js.map +1 -1
  51. package/package.json +2 -1
  52. package/server.json +25 -14
  53. package/src/browser/playwright-renderer.ts +29 -6
  54. package/src/content-handlers/index.ts +72 -0
  55. package/src/content-handlers/json-handler.ts +137 -0
  56. package/src/content-handlers/pdf-handler.ts +91 -0
  57. package/src/content-handlers/svg-handler.ts +243 -0
  58. package/src/content-handlers/types.ts +44 -0
  59. package/src/sanitizer/framework-mapper.ts +94 -0
  60. package/src/sanitizer/threat-reporter.ts +17 -6
  61. package/src/tools/fetch-structured.ts +5 -0
  62. package/src/tools/fetch.ts +76 -4
  63. package/src/tools/read.ts +5 -0
  64. package/src/types.ts +9 -1
  65. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -47
  66. package/.github/ISSUE_TEMPLATE/false_positive.md +0 -43
  67. package/.github/ISSUE_TEMPLATE/new_pattern.md +0 -49
  68. package/.github/ISSUE_TEMPLATE/security_report.md +0 -31
  69. package/.github/PULL_REQUEST_TEMPLATE.md +0 -39
  70. package/.mcpregistry_github_token +0 -1
  71. package/.mcpregistry_registry_token +0 -1
  72. package/CONTRIBUTING.md +0 -329
  73. package/LINKEDIN-STRATEGY.md +0 -367
  74. package/ROADMAP.md +0 -221
  75. package/SECURITY-AUDIT-v1.md +0 -277
  76. package/SUBMISSION.md +0 -66
  77. package/TROUBLESHOOT-AUTH-20260322-2019.md +0 -291
  78. package/TROUBLESHOOT-BUILD-20260319-1450.md +0 -546
  79. package/TROUBLESHOOT-COGNITO-AUTH-20260324-2029.md +0 -415
  80. package/TROUBLESHOOT-COGNITO-JWT-20260324.md +0 -592
  81. package/TROUBLESHOOT-FETCH-20260320-1150.md +0 -168
  82. package/TROUBLESHOOT-JEST-20260323-1357.md +0 -139
  83. package/TROUBLESHOOT-LAMBDA-20260322-1945.md +0 -183
  84. package/TROUBLESHOOT-PLAYWRIGHT-20260321-1549.md +0 -217
  85. package/TROUBLESHOOT-SSL-20260320-1138.md +0 -171
  86. package/TROUBLESHOOT-STRUCTURED-20260320-1200.md +0 -246
  87. package/TROUBLESHOOT-TEST-20260320-0942.md +0 -281
  88. package/VISUS-CLAUDE-CODE-PROMPT.md +0 -324
  89. package/VISUS-PROJECT-PLAN.md +0 -205
  90. package/cdk.json +0 -73
  91. package/infrastructure/app.ts +0 -39
  92. package/infrastructure/stack.ts +0 -298
  93. package/jest.config.js +0 -33
  94. package/jest.setup.js +0 -9
  95. package/lambda-deploy/index.js +0 -81512
  96. package/lambda-deploy/index.js.map +0 -7
  97. package/lambda-package/browser/__mocks__/playwright-renderer.d.ts +0 -25
  98. package/lambda-package/browser/__mocks__/playwright-renderer.d.ts.map +0 -1
  99. package/lambda-package/browser/__mocks__/playwright-renderer.js +0 -119
  100. package/lambda-package/browser/__mocks__/playwright-renderer.js.map +0 -1
  101. package/lambda-package/browser/playwright-renderer.d.ts +0 -40
  102. package/lambda-package/browser/playwright-renderer.d.ts.map +0 -1
  103. package/lambda-package/browser/playwright-renderer.js +0 -214
  104. package/lambda-package/browser/playwright-renderer.js.map +0 -1
  105. package/lambda-package/browser/reader.d.ts +0 -31
  106. package/lambda-package/browser/reader.d.ts.map +0 -1
  107. package/lambda-package/browser/reader.js +0 -98
  108. package/lambda-package/browser/reader.js.map +0 -1
  109. package/lambda-package/index.d.ts +0 -18
  110. package/lambda-package/index.d.ts.map +0 -1
  111. package/lambda-package/index.js +0 -238
  112. package/lambda-package/index.js.map +0 -1
  113. package/lambda-package/lambda-handler.d.ts +0 -28
  114. package/lambda-package/lambda-handler.d.ts.map +0 -1
  115. package/lambda-package/lambda-handler.js +0 -257
  116. package/lambda-package/lambda-handler.js.map +0 -1
  117. package/lambda-package/package-lock.json +0 -7435
  118. package/lambda-package/package.json +0 -74
  119. package/lambda-package/runtime.d.ts +0 -50
  120. package/lambda-package/runtime.d.ts.map +0 -1
  121. package/lambda-package/runtime.js +0 -86
  122. package/lambda-package/runtime.js.map +0 -1
  123. package/lambda-package/sanitizer/elicit-runner.d.ts +0 -48
  124. package/lambda-package/sanitizer/elicit-runner.d.ts.map +0 -1
  125. package/lambda-package/sanitizer/elicit-runner.js +0 -100
  126. package/lambda-package/sanitizer/elicit-runner.js.map +0 -1
  127. package/lambda-package/sanitizer/framework-mapper.d.ts +0 -24
  128. package/lambda-package/sanitizer/framework-mapper.d.ts.map +0 -1
  129. package/lambda-package/sanitizer/framework-mapper.js +0 -342
  130. package/lambda-package/sanitizer/framework-mapper.js.map +0 -1
  131. package/lambda-package/sanitizer/hitl-gate.d.ts +0 -69
  132. package/lambda-package/sanitizer/hitl-gate.d.ts.map +0 -1
  133. package/lambda-package/sanitizer/hitl-gate.js +0 -101
  134. package/lambda-package/sanitizer/hitl-gate.js.map +0 -1
  135. package/lambda-package/sanitizer/index.d.ts +0 -63
  136. package/lambda-package/sanitizer/index.d.ts.map +0 -1
  137. package/lambda-package/sanitizer/index.js +0 -105
  138. package/lambda-package/sanitizer/index.js.map +0 -1
  139. package/lambda-package/sanitizer/injection-detector.d.ts +0 -34
  140. package/lambda-package/sanitizer/injection-detector.d.ts.map +0 -1
  141. package/lambda-package/sanitizer/injection-detector.js +0 -89
  142. package/lambda-package/sanitizer/injection-detector.js.map +0 -1
  143. package/lambda-package/sanitizer/patterns.d.ts +0 -30
  144. package/lambda-package/sanitizer/patterns.d.ts.map +0 -1
  145. package/lambda-package/sanitizer/patterns.js +0 -372
  146. package/lambda-package/sanitizer/patterns.js.map +0 -1
  147. package/lambda-package/sanitizer/pii-allowlist.d.ts +0 -49
  148. package/lambda-package/sanitizer/pii-allowlist.d.ts.map +0 -1
  149. package/lambda-package/sanitizer/pii-allowlist.js +0 -231
  150. package/lambda-package/sanitizer/pii-allowlist.js.map +0 -1
  151. package/lambda-package/sanitizer/pii-redactor.d.ts +0 -41
  152. package/lambda-package/sanitizer/pii-redactor.d.ts.map +0 -1
  153. package/lambda-package/sanitizer/pii-redactor.js +0 -213
  154. package/lambda-package/sanitizer/pii-redactor.js.map +0 -1
  155. package/lambda-package/sanitizer/severity-classifier.d.ts +0 -33
  156. package/lambda-package/sanitizer/severity-classifier.d.ts.map +0 -1
  157. package/lambda-package/sanitizer/severity-classifier.js +0 -113
  158. package/lambda-package/sanitizer/severity-classifier.js.map +0 -1
  159. package/lambda-package/sanitizer/threat-reporter.d.ts +0 -66
  160. package/lambda-package/sanitizer/threat-reporter.d.ts.map +0 -1
  161. package/lambda-package/sanitizer/threat-reporter.js +0 -163
  162. package/lambda-package/sanitizer/threat-reporter.js.map +0 -1
  163. package/lambda-package/tools/fetch-structured.d.ts +0 -51
  164. package/lambda-package/tools/fetch-structured.d.ts.map +0 -1
  165. package/lambda-package/tools/fetch-structured.js +0 -237
  166. package/lambda-package/tools/fetch-structured.js.map +0 -1
  167. package/lambda-package/tools/fetch.d.ts +0 -49
  168. package/lambda-package/tools/fetch.d.ts.map +0 -1
  169. package/lambda-package/tools/fetch.js +0 -131
  170. package/lambda-package/tools/fetch.js.map +0 -1
  171. package/lambda-package/tools/read.d.ts +0 -51
  172. package/lambda-package/tools/read.d.ts.map +0 -1
  173. package/lambda-package/tools/read.js +0 -127
  174. package/lambda-package/tools/read.js.map +0 -1
  175. package/lambda-package/tools/search.d.ts +0 -45
  176. package/lambda-package/tools/search.d.ts.map +0 -1
  177. package/lambda-package/tools/search.js +0 -220
  178. package/lambda-package/tools/search.js.map +0 -1
  179. package/lambda-package/types.d.ts +0 -167
  180. package/lambda-package/types.d.ts.map +0 -1
  181. package/lambda-package/types.js +0 -16
  182. package/lambda-package/types.js.map +0 -1
  183. package/lambda-package/utils/format-converter.d.ts +0 -39
  184. package/lambda-package/utils/format-converter.d.ts.map +0 -1
  185. package/lambda-package/utils/format-converter.js +0 -191
  186. package/lambda-package/utils/format-converter.js.map +0 -1
  187. package/lambda-package/utils/truncate.d.ts +0 -26
  188. package/lambda-package/utils/truncate.d.ts.map +0 -1
  189. package/lambda-package/utils/truncate.js +0 -54
  190. package/lambda-package/utils/truncate.js.map +0 -1
  191. package/lambda.zip +0 -0
  192. package/test-output.txt +0 -4
  193. package/tests/auth-smoke.test.ts +0 -480
  194. package/tests/elicit-runner.test.ts +0 -232
  195. package/tests/fetch-tool.test.ts +0 -922
  196. package/tests/hitl-gate.test.ts +0 -267
  197. package/tests/injection-corpus.ts +0 -338
  198. package/tests/pii-allowlist.test.ts +0 -282
  199. package/tests/reader.test.ts +0 -353
  200. package/tests/sanitizer.test.ts +0 -358
  201. package/tests/search.test.ts +0 -456
  202. package/tests/threat-reporter.test.ts +0 -334
  203. 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 | Confidence | OWASP | MITRE | ISO 42001 |\n';
169
- markdown += '|---|---|---|---|---|---|---|\n';
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
 
@@ -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 format and apply format-appropriate conversion
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 3: CRITICAL - Sanitize content (injection detection + PII redaction with allowlisting)
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 3: Apply token ceiling truncation (AFTER sanitization)
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 4: Build output
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}