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
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Severity Classification Engine
|
|
3
|
-
*
|
|
4
|
-
* Maps injection pattern categories to standardized severity levels.
|
|
5
|
-
* Used for threat reporting and compliance documentation.
|
|
6
|
-
*/
|
|
7
|
-
/**
|
|
8
|
-
* Pattern category to severity mapping
|
|
9
|
-
* Aligned with NIST AI 600-1 and OWASP LLM Top 10 risk levels
|
|
10
|
-
*/
|
|
11
|
-
const SEVERITY_MAP = {
|
|
12
|
-
// CRITICAL - Immediate threat, block-level
|
|
13
|
-
direct_instruction_injection: 'CRITICAL',
|
|
14
|
-
role_hijacking: 'CRITICAL',
|
|
15
|
-
system_prompt_extraction: 'CRITICAL',
|
|
16
|
-
privilege_escalation: 'CRITICAL',
|
|
17
|
-
data_exfiltration: 'CRITICAL',
|
|
18
|
-
code_execution_requests: 'CRITICAL',
|
|
19
|
-
memory_manipulation: 'CRITICAL',
|
|
20
|
-
jailbreak_keywords: 'CRITICAL',
|
|
21
|
-
ethical_override: 'CRITICAL',
|
|
22
|
-
credential_harvesting: 'CRITICAL',
|
|
23
|
-
html_script_injection: 'CRITICAL',
|
|
24
|
-
// HIGH - Significant threat
|
|
25
|
-
context_poisoning: 'HIGH',
|
|
26
|
-
base64_obfuscation: 'HIGH',
|
|
27
|
-
zero_width_characters: 'HIGH',
|
|
28
|
-
data_uri_injection: 'HIGH',
|
|
29
|
-
markdown_link_injection: 'HIGH',
|
|
30
|
-
instruction_delimiter_injection: 'HIGH',
|
|
31
|
-
token_smuggling: 'HIGH',
|
|
32
|
-
system_message_injection: 'HIGH',
|
|
33
|
-
file_system_access: 'HIGH',
|
|
34
|
-
training_data_extraction: 'HIGH',
|
|
35
|
-
nested_encoding: 'HIGH',
|
|
36
|
-
authority_impersonation: 'HIGH',
|
|
37
|
-
callback_url_injection: 'HIGH',
|
|
38
|
-
// MEDIUM - Moderate threat
|
|
39
|
-
comment_injection: 'MEDIUM',
|
|
40
|
-
unicode_lookalikes: 'MEDIUM',
|
|
41
|
-
url_fragment_hashjack: 'MEDIUM',
|
|
42
|
-
social_engineering_urgency: 'MEDIUM',
|
|
43
|
-
multi_language_obfuscation: 'MEDIUM',
|
|
44
|
-
reverse_text_obfuscation: 'MEDIUM',
|
|
45
|
-
conversation_reset: 'MEDIUM',
|
|
46
|
-
chain_of_thought_manipulation: 'MEDIUM',
|
|
47
|
-
hypothetical_scenario_injection: 'MEDIUM',
|
|
48
|
-
output_format_manipulation: 'MEDIUM',
|
|
49
|
-
simulator_mode: 'MEDIUM',
|
|
50
|
-
payload_splitting: 'MEDIUM',
|
|
51
|
-
css_hiding: 'MEDIUM',
|
|
52
|
-
testing_debugging_claims: 'MEDIUM',
|
|
53
|
-
// LOW - Low threat, flagged for awareness
|
|
54
|
-
leetspeak_obfuscation: 'LOW',
|
|
55
|
-
capability_probing: 'LOW',
|
|
56
|
-
negative_instruction: 'LOW',
|
|
57
|
-
time_based_triggers: 'LOW',
|
|
58
|
-
whitespace_steganography: 'LOW'
|
|
59
|
-
};
|
|
60
|
-
/**
|
|
61
|
-
* Classify severity for a single pattern category
|
|
62
|
-
*/
|
|
63
|
-
export function classifySeverity(patternCategory) {
|
|
64
|
-
return SEVERITY_MAP[patternCategory] || 'LOW';
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Aggregate severity across multiple findings
|
|
68
|
-
* Returns the highest severity level found, or CLEAN if no findings
|
|
69
|
-
*/
|
|
70
|
-
export function aggregateSeverity(findings) {
|
|
71
|
-
if (findings.length === 0) {
|
|
72
|
-
return 'CLEAN';
|
|
73
|
-
}
|
|
74
|
-
const severities = findings.map(f => f.severity);
|
|
75
|
-
if (severities.includes('CRITICAL'))
|
|
76
|
-
return 'CRITICAL';
|
|
77
|
-
if (severities.includes('HIGH'))
|
|
78
|
-
return 'HIGH';
|
|
79
|
-
if (severities.includes('MEDIUM'))
|
|
80
|
-
return 'MEDIUM';
|
|
81
|
-
if (severities.includes('LOW'))
|
|
82
|
-
return 'LOW';
|
|
83
|
-
return 'CLEAN';
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Count findings by severity level
|
|
87
|
-
*/
|
|
88
|
-
export function countBySeverity(findings) {
|
|
89
|
-
const counts = {
|
|
90
|
-
CRITICAL: 0,
|
|
91
|
-
HIGH: 0,
|
|
92
|
-
MEDIUM: 0,
|
|
93
|
-
LOW: 0
|
|
94
|
-
};
|
|
95
|
-
for (const finding of findings) {
|
|
96
|
-
counts[finding.severity]++;
|
|
97
|
-
}
|
|
98
|
-
return counts;
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Get emoji for severity level (for Markdown reports)
|
|
102
|
-
*/
|
|
103
|
-
export function getSeverityEmoji(severity) {
|
|
104
|
-
switch (severity) {
|
|
105
|
-
case 'CRITICAL': return '🔴';
|
|
106
|
-
case 'HIGH': return '🟠';
|
|
107
|
-
case 'MEDIUM': return '🟡';
|
|
108
|
-
case 'LOW': return '🟢';
|
|
109
|
-
case 'CLEAN': return '✅';
|
|
110
|
-
default: return '⚪';
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
//# sourceMappingURL=severity-classifier.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"severity-classifier.js","sourceRoot":"","sources":["../../src/sanitizer/severity-classifier.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Threat Reporter
|
|
3
|
-
*
|
|
4
|
-
* Generates structured threat reports when prompt injection or PII is detected.
|
|
5
|
-
* Two output layers:
|
|
6
|
-
* 1. TOON-formatted findings array (token-efficient, machine-readable)
|
|
7
|
-
* 2. Markdown compliance report block (human-readable, renders in Claude Desktop)
|
|
8
|
-
*
|
|
9
|
-
* Aligned with:
|
|
10
|
-
* - OWASP LLM Top 10 (2025)
|
|
11
|
-
* - NIST AI 600-1 (Generative AI Profile)
|
|
12
|
-
* - MITRE ATLAS (Adversarial Threat Landscape)
|
|
13
|
-
*/
|
|
14
|
-
import { type Severity, type OverallSeverity } from './severity-classifier.js';
|
|
15
|
-
/**
|
|
16
|
-
* Threat finding with compliance framework mappings
|
|
17
|
-
*/
|
|
18
|
-
export interface ThreatFinding {
|
|
19
|
-
id: number;
|
|
20
|
-
pattern_id: string;
|
|
21
|
-
category: string;
|
|
22
|
-
severity: Severity;
|
|
23
|
-
confidence: number;
|
|
24
|
-
owasp_llm: string;
|
|
25
|
-
nist_ai_600_1: string;
|
|
26
|
-
mitre_atlas: string;
|
|
27
|
-
iso_42001: string;
|
|
28
|
-
remediation: string;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Threat report structure
|
|
32
|
-
*/
|
|
33
|
-
export interface ThreatReport {
|
|
34
|
-
generated: string;
|
|
35
|
-
source_url: string;
|
|
36
|
-
overall_severity: OverallSeverity;
|
|
37
|
-
total_findings: number;
|
|
38
|
-
by_severity: Record<Severity, number>;
|
|
39
|
-
pii_redacted: number;
|
|
40
|
-
sanitization_applied: boolean;
|
|
41
|
-
frameworks: string[];
|
|
42
|
-
findings_toon: string;
|
|
43
|
-
report_markdown: string;
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Input to threat reporter
|
|
47
|
-
*/
|
|
48
|
-
export interface ThreatReportInput {
|
|
49
|
-
patterns_detected: string[];
|
|
50
|
-
pii_redacted: number;
|
|
51
|
-
source_url: string;
|
|
52
|
-
timestamp?: string;
|
|
53
|
-
detections_by_severity?: {
|
|
54
|
-
critical: number;
|
|
55
|
-
high: number;
|
|
56
|
-
medium: number;
|
|
57
|
-
low: number;
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Generate threat report (main entry point)
|
|
62
|
-
*
|
|
63
|
-
* Returns null if no findings (injections_removed === 0 AND pii_redacted === 0)
|
|
64
|
-
*/
|
|
65
|
-
export declare function generateThreatReport(input: ThreatReportInput): ThreatReport | null;
|
|
66
|
-
//# sourceMappingURL=threat-reporter.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"threat-reporter.d.ts","sourceRoot":"","sources":["../../src/sanitizer/threat-reporter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAKL,KAAK,QAAQ,EACb,KAAK,eAAe,EAErB,MAAM,0BAA0B,CAAC;AAGlC;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,eAAe,CAAC;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sBAAsB,CAAC,EAAE;QACvB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;CACH;AAsID;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,iBAAiB,GAAG,YAAY,GAAG,IAAI,CAsDlF"}
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Threat Reporter
|
|
3
|
-
*
|
|
4
|
-
* Generates structured threat reports when prompt injection or PII is detected.
|
|
5
|
-
* Two output layers:
|
|
6
|
-
* 1. TOON-formatted findings array (token-efficient, machine-readable)
|
|
7
|
-
* 2. Markdown compliance report block (human-readable, renders in Claude Desktop)
|
|
8
|
-
*
|
|
9
|
-
* Aligned with:
|
|
10
|
-
* - OWASP LLM Top 10 (2025)
|
|
11
|
-
* - NIST AI 600-1 (Generative AI Profile)
|
|
12
|
-
* - MITRE ATLAS (Adversarial Threat Landscape)
|
|
13
|
-
*/
|
|
14
|
-
import { classifySeverity, aggregateSeverity, countBySeverity, getSeverityEmoji } from './severity-classifier.js';
|
|
15
|
-
import { getFrameworkMappings } from './framework-mapper.js';
|
|
16
|
-
/**
|
|
17
|
-
* Generate pattern ID from category name
|
|
18
|
-
* Format: PI-XXX where XXX is a zero-padded number based on hash
|
|
19
|
-
*/
|
|
20
|
-
function generatePatternId(category) {
|
|
21
|
-
// Simple hash to generate consistent IDs
|
|
22
|
-
let hash = 0;
|
|
23
|
-
for (let i = 0; i < category.length; i++) {
|
|
24
|
-
hash = ((hash << 5) - hash) + category.charCodeAt(i);
|
|
25
|
-
hash = hash & hash;
|
|
26
|
-
}
|
|
27
|
-
const id = Math.abs(hash) % 1000;
|
|
28
|
-
return `PI-${String(id).padStart(3, '0')}`;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Build findings data structure from pattern detections
|
|
32
|
-
*/
|
|
33
|
-
function buildFindings(patternsDetected) {
|
|
34
|
-
return patternsDetected.map((category, index) => {
|
|
35
|
-
const severity = classifySeverity(category);
|
|
36
|
-
const frameworks = getFrameworkMappings(category);
|
|
37
|
-
const patternId = generatePatternId(category);
|
|
38
|
-
return {
|
|
39
|
-
id: index + 1,
|
|
40
|
-
pattern_id: patternId,
|
|
41
|
-
category,
|
|
42
|
-
severity,
|
|
43
|
-
confidence: 0.95, // Default confidence; can be enhanced later
|
|
44
|
-
owasp_llm: frameworks.owasp_llm,
|
|
45
|
-
nist_ai_600_1: frameworks.nist_ai_600_1,
|
|
46
|
-
mitre_atlas: frameworks.mitre_atlas,
|
|
47
|
-
iso_42001: frameworks.iso_42001,
|
|
48
|
-
remediation: `Content sanitized. ${category.replace(/_/g, ' ')} removed.`
|
|
49
|
-
};
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Generate TOON-encoded findings string
|
|
54
|
-
* Using manual TOON format to avoid Jest ESM compatibility issues
|
|
55
|
-
*/
|
|
56
|
-
function generateToonFindings(findings) {
|
|
57
|
-
if (findings.length === 0) {
|
|
58
|
-
return '';
|
|
59
|
-
}
|
|
60
|
-
return generateManualToonFormat(findings);
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Fallback manual TOON format generation
|
|
64
|
-
*/
|
|
65
|
-
function generateManualToonFormat(findings) {
|
|
66
|
-
const header = `findings[${findings.length}]{id,pattern_id,category,severity,confidence,owasp_llm,nist_ai_600_1,mitre_atlas,iso_42001,remediation}:`;
|
|
67
|
-
const rows = findings.map(f => `${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}`);
|
|
68
|
-
return `${header}\n${rows.join('\n')}`;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Generate Markdown report block
|
|
72
|
-
*/
|
|
73
|
-
function generateMarkdownReport(findings, overallSeverity, bySeverity, piiRedacted, sourceUrl, timestamp) {
|
|
74
|
-
const emoji = getSeverityEmoji(overallSeverity);
|
|
75
|
-
let markdown = '---\n';
|
|
76
|
-
markdown += `## ${emoji} Visus Threat Report\n`;
|
|
77
|
-
markdown += `**Generated:** ${timestamp}\n`;
|
|
78
|
-
markdown += `**Source:** ${sourceUrl}\n`;
|
|
79
|
-
markdown += `**Overall Severity:** ${overallSeverity}\n`;
|
|
80
|
-
markdown += `**Framework:** OWASP LLM Top 10 | NIST AI 600-1 | MITRE ATLAS | ISO/IEC 42001\n\n`;
|
|
81
|
-
// Findings Summary
|
|
82
|
-
markdown += '### Findings Summary\n';
|
|
83
|
-
markdown += '| Severity | Count |\n';
|
|
84
|
-
markdown += '|---|---|\n';
|
|
85
|
-
markdown += `| ${getSeverityEmoji('CRITICAL')} CRITICAL | ${bySeverity.CRITICAL} |\n`;
|
|
86
|
-
markdown += `| ${getSeverityEmoji('HIGH')} HIGH | ${bySeverity.HIGH} |\n`;
|
|
87
|
-
markdown += `| ${getSeverityEmoji('MEDIUM')} MEDIUM | ${bySeverity.MEDIUM} |\n`;
|
|
88
|
-
markdown += `| ${getSeverityEmoji('LOW')} LOW | ${bySeverity.LOW} |\n\n`;
|
|
89
|
-
// Findings Detail (only if we have findings)
|
|
90
|
-
if (findings.length > 0) {
|
|
91
|
-
markdown += '### Findings Detail\n';
|
|
92
|
-
markdown += '| # | Category | Severity | Confidence | OWASP | MITRE | ISO 42001 |\n';
|
|
93
|
-
markdown += '|---|---|---|---|---|---|---|\n';
|
|
94
|
-
for (const finding of findings.slice(0, 10)) { // Limit to first 10 for readability
|
|
95
|
-
const confidencePct = Math.round(finding.confidence * 100);
|
|
96
|
-
const owaspShort = finding.owasp_llm.split(' - ')[0]; // e.g., "LLM01:2025"
|
|
97
|
-
const mitreShort = finding.mitre_atlas.split(' - ')[0]; // e.g., "AML.T0051.000"
|
|
98
|
-
const isoShort = finding.iso_42001.split(' - ')[0]; // e.g., "A.6.1.5"
|
|
99
|
-
markdown += `| ${finding.id} | ${finding.category} | ${finding.severity} | ${confidencePct}% | ${owaspShort} | ${mitreShort} | ${isoShort} |\n`;
|
|
100
|
-
}
|
|
101
|
-
if (findings.length > 10) {
|
|
102
|
-
markdown += `\n*...and ${findings.length - 10} more findings*\n`;
|
|
103
|
-
}
|
|
104
|
-
markdown += '\n';
|
|
105
|
-
}
|
|
106
|
-
// PII Redaction
|
|
107
|
-
if (piiRedacted > 0) {
|
|
108
|
-
markdown += '### PII Redaction\n';
|
|
109
|
-
markdown += `- **Items Redacted:** ${piiRedacted}\n`;
|
|
110
|
-
markdown += `- **Standard:** NIST AI 600-1 MS-2.6\n\n`;
|
|
111
|
-
}
|
|
112
|
-
// Remediation Status
|
|
113
|
-
markdown += '### Remediation Status\n';
|
|
114
|
-
markdown += '✅ All findings sanitized. Content delivered clean.\n\n';
|
|
115
|
-
// TODO: PDF export hook for future visus_report tool
|
|
116
|
-
// This is where the PDF generation would be triggered in Phase 3
|
|
117
|
-
markdown += '*Report generated by Visus MCP — Security-first web access for Claude*\n';
|
|
118
|
-
markdown += '---\n';
|
|
119
|
-
return markdown;
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Generate threat report (main entry point)
|
|
123
|
-
*
|
|
124
|
-
* Returns null if no findings (injections_removed === 0 AND pii_redacted === 0)
|
|
125
|
-
*/
|
|
126
|
-
export function generateThreatReport(input) {
|
|
127
|
-
const { patterns_detected, pii_redacted, source_url, timestamp = new Date().toISOString() } = input;
|
|
128
|
-
// Omit threat report if nothing was found
|
|
129
|
-
if (patterns_detected.length === 0 && pii_redacted === 0) {
|
|
130
|
-
return null;
|
|
131
|
-
}
|
|
132
|
-
// Build findings from detected patterns
|
|
133
|
-
const findings = buildFindings(patterns_detected);
|
|
134
|
-
// Calculate severity
|
|
135
|
-
const severityFindings = findings.map(f => ({
|
|
136
|
-
pattern_category: f.category,
|
|
137
|
-
severity: f.severity
|
|
138
|
-
}));
|
|
139
|
-
const overallSeverity = aggregateSeverity(severityFindings);
|
|
140
|
-
const bySeverity = countBySeverity(severityFindings);
|
|
141
|
-
// Generate TOON findings
|
|
142
|
-
const toonFindings = generateToonFindings(findings);
|
|
143
|
-
// Generate Markdown report
|
|
144
|
-
const markdownReport = generateMarkdownReport(findings, overallSeverity, bySeverity, pii_redacted, source_url, timestamp);
|
|
145
|
-
return {
|
|
146
|
-
generated: timestamp,
|
|
147
|
-
source_url,
|
|
148
|
-
overall_severity: overallSeverity,
|
|
149
|
-
total_findings: findings.length,
|
|
150
|
-
by_severity: bySeverity,
|
|
151
|
-
pii_redacted,
|
|
152
|
-
sanitization_applied: true,
|
|
153
|
-
frameworks: [
|
|
154
|
-
'OWASP LLM Top 10',
|
|
155
|
-
'NIST AI 600-1',
|
|
156
|
-
'MITRE ATLAS',
|
|
157
|
-
'ISO/IEC 42001'
|
|
158
|
-
],
|
|
159
|
-
findings_toon: toonFindings,
|
|
160
|
-
report_markdown: markdownReport
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
//# sourceMappingURL=threat-reporter.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"threat-reporter.js","sourceRoot":"","sources":["../../src/sanitizer/threat-reporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* visus_fetch_structured MCP Tool
|
|
3
|
-
*
|
|
4
|
-
* Fetches a web page and extracts structured data according to a schema.
|
|
5
|
-
* All extracted data is sanitized before being returned.
|
|
6
|
-
*
|
|
7
|
-
* CRITICAL: ALL content MUST pass through the sanitizer. This cannot be bypassed.
|
|
8
|
-
*/
|
|
9
|
-
import type { VisusFetchStructuredInput, VisusFetchStructuredOutput, Result } from '../types.js';
|
|
10
|
-
/**
|
|
11
|
-
* visus_fetch_structured tool implementation
|
|
12
|
-
*
|
|
13
|
-
* @param input Tool input parameters
|
|
14
|
-
* @returns Extracted and sanitized structured data
|
|
15
|
-
*/
|
|
16
|
-
export declare function visusFetchStructured(input: VisusFetchStructuredInput): Promise<Result<VisusFetchStructuredOutput, Error>>;
|
|
17
|
-
/**
|
|
18
|
-
* MCP tool definition for registration
|
|
19
|
-
*/
|
|
20
|
-
export declare const visusFetchStructuredToolDefinition: {
|
|
21
|
-
name: string;
|
|
22
|
-
title: string;
|
|
23
|
-
description: string;
|
|
24
|
-
inputSchema: {
|
|
25
|
-
type: string;
|
|
26
|
-
properties: {
|
|
27
|
-
url: {
|
|
28
|
-
type: string;
|
|
29
|
-
description: string;
|
|
30
|
-
};
|
|
31
|
-
schema: {
|
|
32
|
-
type: string;
|
|
33
|
-
description: string;
|
|
34
|
-
additionalProperties: {
|
|
35
|
-
type: string;
|
|
36
|
-
};
|
|
37
|
-
};
|
|
38
|
-
timeout_ms: {
|
|
39
|
-
type: string;
|
|
40
|
-
description: string;
|
|
41
|
-
default: number;
|
|
42
|
-
};
|
|
43
|
-
};
|
|
44
|
-
required: string[];
|
|
45
|
-
};
|
|
46
|
-
readOnlyHint: boolean;
|
|
47
|
-
destructiveHint: boolean;
|
|
48
|
-
idempotentHint: boolean;
|
|
49
|
-
openWorldHint: boolean;
|
|
50
|
-
};
|
|
51
|
-
//# sourceMappingURL=fetch-structured.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-structured.d.ts","sourceRoot":"","sources":["../../src/tools/fetch-structured.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,KAAK,EAAE,yBAAyB,EAAE,0BAA0B,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AA2EjG;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,yBAAyB,GAC/B,OAAO,CAAC,MAAM,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAC,CA4IpD;AAED;;GAEG;AACH,eAAO,MAAM,kCAAkC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8B9C,CAAC"}
|
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* visus_fetch_structured MCP Tool
|
|
3
|
-
*
|
|
4
|
-
* Fetches a web page and extracts structured data according to a schema.
|
|
5
|
-
* All extracted data is sanitized before being returned.
|
|
6
|
-
*
|
|
7
|
-
* CRITICAL: ALL content MUST pass through the sanitizer. This cannot be bypassed.
|
|
8
|
-
*/
|
|
9
|
-
import * as cheerio from 'cheerio';
|
|
10
|
-
import { renderPage } from '../browser/playwright-renderer.js';
|
|
11
|
-
import { sanitize } from '../sanitizer/index.js';
|
|
12
|
-
import { truncateContent } from '../utils/truncate.js';
|
|
13
|
-
import { generateThreatReport } from '../sanitizer/threat-reporter.js';
|
|
14
|
-
import { Err } from '../types.js';
|
|
15
|
-
/**
|
|
16
|
-
* Extract structured data from HTML using cheerio
|
|
17
|
-
*
|
|
18
|
-
* Phase 1: cheerio-based semantic HTML extraction
|
|
19
|
-
* Phase 2+: LLM-powered extraction with Bedrock for complex schemas
|
|
20
|
-
*/
|
|
21
|
-
function extractStructuredData(html, schema) {
|
|
22
|
-
const $ = cheerio.load(html);
|
|
23
|
-
const extracted = {};
|
|
24
|
-
for (const [fieldName, description] of Object.entries(schema)) {
|
|
25
|
-
const descLower = description.toLowerCase();
|
|
26
|
-
let value = null;
|
|
27
|
-
// Pattern: main heading, title, h1
|
|
28
|
-
if (descLower.includes('heading') || descLower.includes('title') || descLower.includes('h1')) {
|
|
29
|
-
const h1 = $('h1').first().text().trim();
|
|
30
|
-
if (h1)
|
|
31
|
-
value = h1;
|
|
32
|
-
}
|
|
33
|
-
// Pattern: subheading, subtitle, h2, h3
|
|
34
|
-
else if (descLower.includes('subheading') || descLower.includes('subtitle') || descLower.includes('h2') || descLower.includes('h3')) {
|
|
35
|
-
const h2 = $('h2, h3').first().text().trim();
|
|
36
|
-
if (h2)
|
|
37
|
-
value = h2;
|
|
38
|
-
}
|
|
39
|
-
// Pattern: paragraph, body text, description
|
|
40
|
-
else if (descLower.includes('paragraph') || descLower.includes('body') || descLower.includes('description')) {
|
|
41
|
-
const p = $('p').first().text().trim();
|
|
42
|
-
if (p)
|
|
43
|
-
value = p;
|
|
44
|
-
}
|
|
45
|
-
// Pattern: link, url, href
|
|
46
|
-
else if (descLower.includes('link') || descLower.includes('url') || descLower.includes('href')) {
|
|
47
|
-
const link = $('a').first();
|
|
48
|
-
const href = link.attr('href');
|
|
49
|
-
if (href)
|
|
50
|
-
value = href;
|
|
51
|
-
}
|
|
52
|
-
// Pattern: link text, anchor text
|
|
53
|
-
else if (descLower.includes('link text') || descLower.includes('anchor')) {
|
|
54
|
-
const linkText = $('a').first().text().trim();
|
|
55
|
-
if (linkText)
|
|
56
|
-
value = linkText;
|
|
57
|
-
}
|
|
58
|
-
// Pattern: page title (from <title> tag)
|
|
59
|
-
else if (descLower.includes('page title') || descLower.includes('document title')) {
|
|
60
|
-
const title = $('title').text().trim();
|
|
61
|
-
if (title)
|
|
62
|
-
value = title;
|
|
63
|
-
}
|
|
64
|
-
// Fallback: try to find text containing the field name
|
|
65
|
-
else {
|
|
66
|
-
const elements = $('*').filter((_, el) => {
|
|
67
|
-
const text = $(el).text().toLowerCase();
|
|
68
|
-
return text.includes(fieldName.toLowerCase()) || text.includes(descLower);
|
|
69
|
-
});
|
|
70
|
-
if (elements.length > 0) {
|
|
71
|
-
value = $(elements.first()).text().trim();
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
extracted[fieldName] = value;
|
|
75
|
-
}
|
|
76
|
-
return extracted;
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* visus_fetch_structured tool implementation
|
|
80
|
-
*
|
|
81
|
-
* @param input Tool input parameters
|
|
82
|
-
* @returns Extracted and sanitized structured data
|
|
83
|
-
*/
|
|
84
|
-
export async function visusFetchStructured(input) {
|
|
85
|
-
const { url, schema, timeout_ms = 10000 } = input;
|
|
86
|
-
// Validate inputs
|
|
87
|
-
if (!url || typeof url !== 'string') {
|
|
88
|
-
return Err(new Error('Invalid input: url must be a non-empty string'));
|
|
89
|
-
}
|
|
90
|
-
if (!schema || typeof schema !== 'object' || Object.keys(schema).length === 0) {
|
|
91
|
-
return Err(new Error('Invalid input: schema must be a non-empty object'));
|
|
92
|
-
}
|
|
93
|
-
try {
|
|
94
|
-
// Step 1: Render the page (use default format to get HTML)
|
|
95
|
-
const renderResult = await renderPage(url, {
|
|
96
|
-
timeout_ms
|
|
97
|
-
});
|
|
98
|
-
if (!renderResult.ok) {
|
|
99
|
-
return Err(renderResult.error);
|
|
100
|
-
}
|
|
101
|
-
const { title, html } = renderResult.value;
|
|
102
|
-
// Step 2: Extract structured data from HTML using cheerio
|
|
103
|
-
const extractedData = extractStructuredData(html, schema);
|
|
104
|
-
// Step 3: CRITICAL - Sanitize each extracted field (with allowlisting)
|
|
105
|
-
// This step CANNOT be skipped or bypassed
|
|
106
|
-
const sanitizedData = {};
|
|
107
|
-
const allPatternsDetected = new Set();
|
|
108
|
-
const allPIITypesRedacted = new Set();
|
|
109
|
-
const allPIIAllowlisted = [];
|
|
110
|
-
let anyContentModified = false;
|
|
111
|
-
for (const [fieldName, value] of Object.entries(extractedData)) {
|
|
112
|
-
if (value === null) {
|
|
113
|
-
sanitizedData[fieldName] = null;
|
|
114
|
-
continue;
|
|
115
|
-
}
|
|
116
|
-
const sanitizationResult = sanitize(value, url);
|
|
117
|
-
sanitizedData[fieldName] = sanitizationResult.content;
|
|
118
|
-
// Collect all patterns detected across fields
|
|
119
|
-
sanitizationResult.sanitization.patterns_detected.forEach(p => allPatternsDetected.add(p));
|
|
120
|
-
sanitizationResult.sanitization.pii_types_redacted.forEach(p => allPIITypesRedacted.add(p));
|
|
121
|
-
allPIIAllowlisted.push(...sanitizationResult.sanitization.pii_allowlisted);
|
|
122
|
-
if (sanitizationResult.sanitization.content_modified) {
|
|
123
|
-
anyContentModified = true;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
// Step 4: Apply token ceiling truncation to combined data (AFTER sanitization)
|
|
127
|
-
// Combine all field values to check total content size
|
|
128
|
-
const combinedData = Object.entries(sanitizedData)
|
|
129
|
-
.map(([key, value]) => `${key}: ${value || 'null'}`)
|
|
130
|
-
.join('\n');
|
|
131
|
-
const truncationResult = truncateContent(combinedData);
|
|
132
|
-
// If truncated, we need to reconstruct sanitizedData from truncated content
|
|
133
|
-
let finalData = sanitizedData;
|
|
134
|
-
if (truncationResult.truncated) {
|
|
135
|
-
// Parse truncated content back into fields
|
|
136
|
-
// This is a simple approach - in production you might want more sophisticated handling
|
|
137
|
-
const lines = truncationResult.content.split('\n');
|
|
138
|
-
finalData = {};
|
|
139
|
-
for (const line of lines) {
|
|
140
|
-
if (line.includes(':')) {
|
|
141
|
-
const [key, ...valueParts] = line.split(':');
|
|
142
|
-
const value = valueParts.join(':').trim();
|
|
143
|
-
if (key.trim() in sanitizedData) {
|
|
144
|
-
finalData[key.trim()] = value === 'null' ? null : value;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
// Preserve any missing fields as null
|
|
149
|
-
for (const key of Object.keys(sanitizedData)) {
|
|
150
|
-
if (!(key in finalData)) {
|
|
151
|
-
finalData[key] = null;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
// Step 5: Generate aggregated threat report
|
|
156
|
-
const threatReport = generateThreatReport({
|
|
157
|
-
patterns_detected: Array.from(allPatternsDetected),
|
|
158
|
-
pii_redacted: Array.from(allPIITypesRedacted).length,
|
|
159
|
-
source_url: url
|
|
160
|
-
});
|
|
161
|
-
// Step 6: Build output
|
|
162
|
-
const output = {
|
|
163
|
-
url,
|
|
164
|
-
data: finalData,
|
|
165
|
-
sanitization: {
|
|
166
|
-
patterns_detected: Array.from(allPatternsDetected),
|
|
167
|
-
pii_types_redacted: Array.from(allPIITypesRedacted),
|
|
168
|
-
pii_allowlisted: allPIIAllowlisted,
|
|
169
|
-
content_modified: anyContentModified
|
|
170
|
-
},
|
|
171
|
-
metadata: {
|
|
172
|
-
title: title || 'Untitled',
|
|
173
|
-
fetched_at: new Date().toISOString(),
|
|
174
|
-
content_length_original: html.length,
|
|
175
|
-
content_length_sanitized: Object.values(sanitizedData)
|
|
176
|
-
.filter(v => v !== null)
|
|
177
|
-
.join(' ')
|
|
178
|
-
.length,
|
|
179
|
-
...(truncationResult.truncated && {
|
|
180
|
-
truncated: true,
|
|
181
|
-
truncated_at_chars: truncationResult.truncated_at_chars
|
|
182
|
-
})
|
|
183
|
-
},
|
|
184
|
-
// Include threat_report only if findings exist
|
|
185
|
-
...(threatReport && { threat_report: threatReport })
|
|
186
|
-
};
|
|
187
|
-
// Log to stderr if threats detected
|
|
188
|
-
if (allPatternsDetected.size > 0) {
|
|
189
|
-
console.error(JSON.stringify({
|
|
190
|
-
timestamp: new Date().toISOString(),
|
|
191
|
-
event: 'structured_extraction_threats',
|
|
192
|
-
url,
|
|
193
|
-
patterns: Array.from(allPatternsDetected),
|
|
194
|
-
fields: Object.keys(schema)
|
|
195
|
-
}));
|
|
196
|
-
}
|
|
197
|
-
return { ok: true, value: output };
|
|
198
|
-
}
|
|
199
|
-
catch (error) {
|
|
200
|
-
return Err(error instanceof Error ? error : new Error(String(error)));
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* MCP tool definition for registration
|
|
205
|
-
*/
|
|
206
|
-
export const visusFetchStructuredToolDefinition = {
|
|
207
|
-
name: 'visus_fetch_structured',
|
|
208
|
-
title: 'Fetch Structured Data (Sanitized)',
|
|
209
|
-
description: 'Fetch a web page and extract structured data according to a schema. SECURITY: All extracted fields pass through prompt injection sanitization (43 pattern categories) and PII redaction BEFORE being returned to the LLM. Each field is independently sanitized to ensure safe consumption of untrusted web content.',
|
|
210
|
-
inputSchema: {
|
|
211
|
-
type: 'object',
|
|
212
|
-
properties: {
|
|
213
|
-
url: {
|
|
214
|
-
type: 'string',
|
|
215
|
-
description: 'The URL to fetch (must be http:// or https://)'
|
|
216
|
-
},
|
|
217
|
-
schema: {
|
|
218
|
-
type: 'object',
|
|
219
|
-
description: 'Field extraction schema: { fieldName: "field description", ... }',
|
|
220
|
-
additionalProperties: {
|
|
221
|
-
type: 'string'
|
|
222
|
-
}
|
|
223
|
-
},
|
|
224
|
-
timeout_ms: {
|
|
225
|
-
type: 'number',
|
|
226
|
-
description: 'Request timeout in milliseconds (default: 10000)',
|
|
227
|
-
default: 10000
|
|
228
|
-
}
|
|
229
|
-
},
|
|
230
|
-
required: ['url', 'schema']
|
|
231
|
-
},
|
|
232
|
-
readOnlyHint: true,
|
|
233
|
-
destructiveHint: false,
|
|
234
|
-
idempotentHint: true,
|
|
235
|
-
openWorldHint: true
|
|
236
|
-
};
|
|
237
|
-
//# sourceMappingURL=fetch-structured.js.map
|