partnercore-proxy 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +54 -0
- package/LICENSE +22 -0
- package/README.md +234 -0
- package/dist/al/extension-manager.d.ts +52 -0
- package/dist/al/extension-manager.d.ts.map +1 -0
- package/dist/al/extension-manager.js +348 -0
- package/dist/al/extension-manager.js.map +1 -0
- package/dist/al/index.d.ts +3 -0
- package/dist/al/index.d.ts.map +1 -0
- package/dist/al/index.js +19 -0
- package/dist/al/index.js.map +1 -0
- package/dist/al/language-server.d.ts +134 -0
- package/dist/al/language-server.d.ts.map +1 -0
- package/dist/al/language-server.js +431 -0
- package/dist/al/language-server.js.map +1 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +207 -0
- package/dist/cli.js.map +1 -0
- package/dist/cloud/index.d.ts +2 -0
- package/dist/cloud/index.d.ts.map +1 -0
- package/dist/cloud/index.js +18 -0
- package/dist/cloud/index.js.map +1 -0
- package/dist/cloud/relay-client.d.ts +84 -0
- package/dist/cloud/relay-client.d.ts.map +1 -0
- package/dist/cloud/relay-client.js +211 -0
- package/dist/cloud/relay-client.js.map +1 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +19 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/loader.d.ts +20 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +136 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/types.d.ts +51 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +33 -0
- package/dist/config/types.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +18 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.d.ts +33 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +98 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/router/index.d.ts +2 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/index.js +18 -0
- package/dist/router/index.js.map +1 -0
- package/dist/router/tool-router.d.ts +87 -0
- package/dist/router/tool-router.d.ts.map +1 -0
- package/dist/router/tool-router.js +557 -0
- package/dist/router/tool-router.js.map +1 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +19 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +20 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +99 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/security.d.ts +66 -0
- package/dist/utils/security.d.ts.map +1 -0
- package/dist/utils/security.js +358 -0
- package/dist/utils/security.js.map +1 -0
- package/mcp.json +125 -0
- package/package.json +107 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Logger utility for PartnerCore Proxy
|
|
4
|
+
*
|
|
5
|
+
* Security: This logger automatically masks sensitive data like API keys,
|
|
6
|
+
* passwords, and tokens to prevent accidental exposure in logs.
|
|
7
|
+
*/
|
|
8
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.createLogger = createLogger;
|
|
13
|
+
exports.getLogger = getLogger;
|
|
14
|
+
exports.setLogLevel = setLogLevel;
|
|
15
|
+
const winston_1 = __importDefault(require("winston"));
|
|
16
|
+
let loggerInstance = null;
|
|
17
|
+
/**
|
|
18
|
+
* Patterns that indicate sensitive data in log messages
|
|
19
|
+
*/
|
|
20
|
+
const SENSITIVE_PATTERNS = [
|
|
21
|
+
// API keys and tokens
|
|
22
|
+
/api[_-]?key[=:]\s*['"]?([a-zA-Z0-9_-]{20,})['"]?/gi,
|
|
23
|
+
/token[=:]\s*['"]?([a-zA-Z0-9_.-]{20,})['"]?/gi,
|
|
24
|
+
/bearer\s+([a-zA-Z0-9_.-]{20,})/gi,
|
|
25
|
+
/authorization[=:]\s*['"]?([a-zA-Z0-9_.-]{20,})['"]?/gi,
|
|
26
|
+
// Passwords
|
|
27
|
+
/password[=:]\s*['"]?([^\s'"]+)['"]?/gi,
|
|
28
|
+
/secret[=:]\s*['"]?([^\s'"]+)['"]?/gi,
|
|
29
|
+
];
|
|
30
|
+
/**
|
|
31
|
+
* Mask sensitive data in a string
|
|
32
|
+
*/
|
|
33
|
+
function maskSensitiveString(str) {
|
|
34
|
+
let masked = str;
|
|
35
|
+
for (const pattern of SENSITIVE_PATTERNS) {
|
|
36
|
+
masked = masked.replace(pattern, (match, group) => {
|
|
37
|
+
const capturedGroup = group ?? '';
|
|
38
|
+
if (capturedGroup.length > 8) {
|
|
39
|
+
return match.replace(capturedGroup, `${capturedGroup.slice(0, 4)}****${capturedGroup.slice(-4)}`);
|
|
40
|
+
}
|
|
41
|
+
return match.replace(capturedGroup || match, '****');
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return masked;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Format for masking sensitive data
|
|
48
|
+
*/
|
|
49
|
+
const maskSensitiveFormat = winston_1.default.format((info) => {
|
|
50
|
+
if (typeof info.message === 'string') {
|
|
51
|
+
info.message = maskSensitiveString(info.message);
|
|
52
|
+
}
|
|
53
|
+
return info;
|
|
54
|
+
});
|
|
55
|
+
/**
|
|
56
|
+
* Create and configure the logger
|
|
57
|
+
*/
|
|
58
|
+
function createLogger(level = 'info') {
|
|
59
|
+
if (loggerInstance) {
|
|
60
|
+
return loggerInstance;
|
|
61
|
+
}
|
|
62
|
+
loggerInstance = winston_1.default.createLogger({
|
|
63
|
+
level,
|
|
64
|
+
format: winston_1.default.format.combine(maskSensitiveFormat(), winston_1.default.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston_1.default.format.errors({ stack: true }), winston_1.default.format.printf(({ level, message, timestamp, ...meta }) => {
|
|
65
|
+
// Mask sensitive data in metadata too
|
|
66
|
+
let metaStr = '';
|
|
67
|
+
if (Object.keys(meta).length) {
|
|
68
|
+
const maskedMeta = maskSensitiveString(JSON.stringify(meta));
|
|
69
|
+
metaStr = ` ${maskedMeta}`;
|
|
70
|
+
}
|
|
71
|
+
const ts = String(timestamp ?? '');
|
|
72
|
+
const msg = String(message ?? '');
|
|
73
|
+
return `[${ts}] ${level.toUpperCase()}: ${msg}${metaStr}`;
|
|
74
|
+
})),
|
|
75
|
+
transports: [
|
|
76
|
+
new winston_1.default.transports.Console({
|
|
77
|
+
stderrLevels: ['error', 'warn'],
|
|
78
|
+
}),
|
|
79
|
+
],
|
|
80
|
+
});
|
|
81
|
+
return loggerInstance;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get the logger instance (creates one if not exists)
|
|
85
|
+
*/
|
|
86
|
+
function getLogger() {
|
|
87
|
+
if (!loggerInstance) {
|
|
88
|
+
return createLogger();
|
|
89
|
+
}
|
|
90
|
+
return loggerInstance;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Set log level dynamically
|
|
94
|
+
*/
|
|
95
|
+
function setLogLevel(level) {
|
|
96
|
+
const logger = getLogger();
|
|
97
|
+
logger.level = level;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security utilities for PartnerCore Proxy
|
|
3
|
+
*
|
|
4
|
+
* Provides path sanitization, input validation, and secret masking
|
|
5
|
+
* to prevent common security vulnerabilities.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Mask sensitive values in objects for safe logging
|
|
9
|
+
*/
|
|
10
|
+
export declare function maskSensitiveData(obj: unknown, depth?: number): unknown;
|
|
11
|
+
/**
|
|
12
|
+
* Validate and sanitize a file path to prevent directory traversal
|
|
13
|
+
*
|
|
14
|
+
* @param filePath - The path to validate
|
|
15
|
+
* @param workspaceRoot - The allowed root directory
|
|
16
|
+
* @returns The sanitized absolute path
|
|
17
|
+
* @throws SecurityError if path is outside workspace or contains suspicious patterns
|
|
18
|
+
*/
|
|
19
|
+
export declare function sanitizePath(filePath: string, workspaceRoot: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Validate that a path exists and is within the workspace
|
|
22
|
+
*/
|
|
23
|
+
export declare function validatePathExists(filePath: string, workspaceRoot: string, type?: 'file' | 'directory' | 'any'): string;
|
|
24
|
+
/**
|
|
25
|
+
* Validate tool arguments against expected schema
|
|
26
|
+
*/
|
|
27
|
+
export declare function validateToolArgs(args: Record<string, unknown>, required: string[], types: Record<string, 'string' | 'number' | 'boolean' | 'object' | 'array'>): void;
|
|
28
|
+
/**
|
|
29
|
+
* Sanitize string input to prevent injection
|
|
30
|
+
*/
|
|
31
|
+
export declare function sanitizeString(input: string, maxLength?: number): string;
|
|
32
|
+
/**
|
|
33
|
+
* Security error with code for categorization
|
|
34
|
+
*/
|
|
35
|
+
export declare class SecurityError extends Error {
|
|
36
|
+
code: string;
|
|
37
|
+
constructor(message: string, code: string);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Validation error for invalid input
|
|
41
|
+
*/
|
|
42
|
+
export declare class ValidationError extends Error {
|
|
43
|
+
constructor(message: string);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Rate limiter for API calls
|
|
47
|
+
*/
|
|
48
|
+
export declare class RateLimiter {
|
|
49
|
+
private requests;
|
|
50
|
+
private readonly maxRequests;
|
|
51
|
+
private readonly windowMs;
|
|
52
|
+
constructor(maxRequests: number, windowMs: number);
|
|
53
|
+
/**
|
|
54
|
+
* Check if a request is allowed
|
|
55
|
+
*/
|
|
56
|
+
isAllowed(): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Get remaining requests in current window
|
|
59
|
+
*/
|
|
60
|
+
remaining(): number;
|
|
61
|
+
/**
|
|
62
|
+
* Get time until window resets (ms)
|
|
63
|
+
*/
|
|
64
|
+
resetIn(): number;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=security.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../src/utils/security.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA6CH;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,SAAI,GAAG,OAAO,CA8ClE;AAwCD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAwF5E;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,IAAI,GAAE,MAAM,GAAG,WAAW,GAAG,KAAa,GACzC,MAAM,CA2BR;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,QAAQ,EAAE,MAAM,EAAE,EAClB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC,GAC1E,IAAI,CA2BN;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,SAAQ,GAAG,MAAM,CAcvE;AAED;;GAEG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACtC,IAAI,EAAE,MAAM,CAAC;gBAED,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAK1C;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAEtB,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IAKjD;;OAEG;IACH,SAAS,IAAI,OAAO;IAmBpB;;OAEG;IACH,SAAS,IAAI,MAAM;IAMnB;;OAEG;IACH,OAAO,IAAI,MAAM;CAKlB"}
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Security utilities for PartnerCore Proxy
|
|
4
|
+
*
|
|
5
|
+
* Provides path sanitization, input validation, and secret masking
|
|
6
|
+
* to prevent common security vulnerabilities.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.RateLimiter = exports.ValidationError = exports.SecurityError = void 0;
|
|
43
|
+
exports.maskSensitiveData = maskSensitiveData;
|
|
44
|
+
exports.sanitizePath = sanitizePath;
|
|
45
|
+
exports.validatePathExists = validatePathExists;
|
|
46
|
+
exports.validateToolArgs = validateToolArgs;
|
|
47
|
+
exports.sanitizeString = sanitizeString;
|
|
48
|
+
const path = __importStar(require("path"));
|
|
49
|
+
const fs = __importStar(require("fs"));
|
|
50
|
+
/**
|
|
51
|
+
* Sensitive patterns that should never be logged
|
|
52
|
+
* Expanded to catch more credential types
|
|
53
|
+
*/
|
|
54
|
+
const SENSITIVE_KEY_PATTERNS = [
|
|
55
|
+
/api[_-]?key/i,
|
|
56
|
+
/password/i,
|
|
57
|
+
/secret/i,
|
|
58
|
+
/token/i,
|
|
59
|
+
/bearer/i,
|
|
60
|
+
/authorization/i,
|
|
61
|
+
/credential/i,
|
|
62
|
+
/access[_-]?key/i, // AWS access keys
|
|
63
|
+
/private[_-]?key/i, // Private keys
|
|
64
|
+
/connection[_-]?string/i, // Connection strings
|
|
65
|
+
/^auth$/i, // Simple 'auth' key
|
|
66
|
+
/session[_-]?id/i, // Session identifiers
|
|
67
|
+
/refresh[_-]?token/i, // Refresh tokens
|
|
68
|
+
/client[_-]?secret/i, // OAuth client secrets
|
|
69
|
+
/signing[_-]?key/i, // Signing keys
|
|
70
|
+
/encryption[_-]?key/i, // Encryption keys
|
|
71
|
+
/cert(?:ificate)?/i, // Certificates
|
|
72
|
+
];
|
|
73
|
+
/**
|
|
74
|
+
* Patterns that indicate a value is likely a secret
|
|
75
|
+
* (regardless of key name)
|
|
76
|
+
*/
|
|
77
|
+
const SECRET_VALUE_PATTERNS = [
|
|
78
|
+
/^-----BEGIN.*KEY-----/, // PEM format keys
|
|
79
|
+
/^sk_live_/, // Stripe live keys
|
|
80
|
+
/^sk_test_/, // Stripe test keys
|
|
81
|
+
/^AKIA[A-Z0-9]{16}$/, // AWS access key IDs
|
|
82
|
+
/^ghp_[a-zA-Z0-9]{36}$/, // GitHub personal access tokens
|
|
83
|
+
/^gho_[a-zA-Z0-9]{36}$/, // GitHub OAuth tokens
|
|
84
|
+
/^github_pat_/, // GitHub PATs (new format)
|
|
85
|
+
/^xox[bpras]-/, // Slack tokens
|
|
86
|
+
/^eyJ[a-zA-Z0-9_-]*\./, // JWT tokens
|
|
87
|
+
];
|
|
88
|
+
/**
|
|
89
|
+
* Mask sensitive values in objects for safe logging
|
|
90
|
+
*/
|
|
91
|
+
function maskSensitiveData(obj, depth = 0) {
|
|
92
|
+
if (depth > 10)
|
|
93
|
+
return '[MAX_DEPTH]';
|
|
94
|
+
if (obj === null || obj === undefined)
|
|
95
|
+
return obj;
|
|
96
|
+
if (typeof obj === 'string') {
|
|
97
|
+
// Check if the value looks like a known secret format
|
|
98
|
+
for (const pattern of SECRET_VALUE_PATTERNS) {
|
|
99
|
+
if (pattern.test(obj)) {
|
|
100
|
+
return '[REDACTED]';
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Mask strings that look like keys/tokens (long alphanumeric strings)
|
|
104
|
+
if (obj.length > 20 && /^[a-zA-Z0-9+/=_-]+$/.test(obj)) {
|
|
105
|
+
return `[MASKED:${obj.slice(0, 4)}...${obj.slice(-4)}]`;
|
|
106
|
+
}
|
|
107
|
+
return obj;
|
|
108
|
+
}
|
|
109
|
+
if (Array.isArray(obj)) {
|
|
110
|
+
return obj.map(item => maskSensitiveData(item, depth + 1));
|
|
111
|
+
}
|
|
112
|
+
if (typeof obj === 'object') {
|
|
113
|
+
const masked = {};
|
|
114
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
115
|
+
// Skip prototype pollution vectors
|
|
116
|
+
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const isSensitiveKey = SENSITIVE_KEY_PATTERNS.some(pattern => pattern.test(key));
|
|
120
|
+
if (isSensitiveKey && typeof value === 'string' && value.length > 0) {
|
|
121
|
+
masked[key] = '[REDACTED]';
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
masked[key] = maskSensitiveData(value, depth + 1);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return masked;
|
|
128
|
+
}
|
|
129
|
+
return obj;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Decode URL-encoded strings recursively to catch bypass attempts
|
|
133
|
+
*/
|
|
134
|
+
function decodeUrlRecursive(input, maxIterations = 3) {
|
|
135
|
+
let decoded = input;
|
|
136
|
+
let prev = '';
|
|
137
|
+
let iterations = 0;
|
|
138
|
+
// Keep decoding until no change or max iterations
|
|
139
|
+
while (decoded !== prev && iterations < maxIterations) {
|
|
140
|
+
prev = decoded;
|
|
141
|
+
try {
|
|
142
|
+
decoded = decodeURIComponent(decoded);
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// Invalid encoding, return as-is
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
iterations++;
|
|
149
|
+
}
|
|
150
|
+
return decoded;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Check for path traversal patterns in a string
|
|
154
|
+
*/
|
|
155
|
+
function containsTraversalPatterns(input) {
|
|
156
|
+
const patterns = [
|
|
157
|
+
/\.\.[/\\]/, // ../ or ..\
|
|
158
|
+
/[/\\]\.\./, // /.. or \..
|
|
159
|
+
/^\.\./, // starts with ..
|
|
160
|
+
/\.\.$/, // ends with ..
|
|
161
|
+
/^\.\.$/, // just ..
|
|
162
|
+
];
|
|
163
|
+
return patterns.some(p => p.test(input));
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Validate and sanitize a file path to prevent directory traversal
|
|
167
|
+
*
|
|
168
|
+
* @param filePath - The path to validate
|
|
169
|
+
* @param workspaceRoot - The allowed root directory
|
|
170
|
+
* @returns The sanitized absolute path
|
|
171
|
+
* @throws SecurityError if path is outside workspace or contains suspicious patterns
|
|
172
|
+
*/
|
|
173
|
+
function sanitizePath(filePath, workspaceRoot) {
|
|
174
|
+
// Check for null bytes first (before any other processing)
|
|
175
|
+
if (filePath.includes('\0')) {
|
|
176
|
+
throw new SecurityError('Null byte detected in path', 'NULL_BYTE_INJECTION');
|
|
177
|
+
}
|
|
178
|
+
// Decode URL encoding recursively to catch bypass attempts
|
|
179
|
+
const decoded = decodeUrlRecursive(filePath);
|
|
180
|
+
// Check for traversal in both original and decoded versions
|
|
181
|
+
if (containsTraversalPatterns(filePath) || containsTraversalPatterns(decoded)) {
|
|
182
|
+
throw new SecurityError('Path traversal pattern detected', 'PATH_TRAVERSAL');
|
|
183
|
+
}
|
|
184
|
+
// Check for absolute paths (platform-aware)
|
|
185
|
+
if (path.isAbsolute(decoded)) {
|
|
186
|
+
throw new SecurityError('Absolute paths are not allowed', 'ABSOLUTE_PATH');
|
|
187
|
+
}
|
|
188
|
+
// Check for Windows drive letters even on non-Windows
|
|
189
|
+
if (/^[a-zA-Z]:/.test(decoded)) {
|
|
190
|
+
throw new SecurityError('Windows drive paths are not allowed', 'ABSOLUTE_PATH');
|
|
191
|
+
}
|
|
192
|
+
// Check for paths that start with / (Unix absolute)
|
|
193
|
+
if (decoded.startsWith('/') && process.platform !== 'win32') {
|
|
194
|
+
// On Windows, /something becomes relative, but on Unix it's absolute
|
|
195
|
+
throw new SecurityError('Absolute paths are not allowed', 'ABSOLUTE_PATH');
|
|
196
|
+
}
|
|
197
|
+
// Additional suspicious patterns that might indicate attack attempts
|
|
198
|
+
const suspiciousPatterns = [
|
|
199
|
+
/%2e/i, // Any remaining URL-encoded dots
|
|
200
|
+
/%5c/i, // URL-encoded backslash
|
|
201
|
+
/%2f/i, // URL-encoded forward slash
|
|
202
|
+
/%00/i, // URL-encoded null
|
|
203
|
+
/\uff0e/, // Full-width period
|
|
204
|
+
/\u2024/, // One dot leader
|
|
205
|
+
/\u2025/, // Two dot leader
|
|
206
|
+
/\u2026/, // Horizontal ellipsis
|
|
207
|
+
/\ufe52/, // Small full stop
|
|
208
|
+
/\uff0f/, // Full-width solidus
|
|
209
|
+
/\uff3c/, // Full-width reverse solidus
|
|
210
|
+
/%c0%ae/i, // Overlong UTF-8 encoding of .
|
|
211
|
+
/%c1%1c/i, // Overlong UTF-8 encoding of /
|
|
212
|
+
/%c0%af/i, // Overlong UTF-8 encoding of /
|
|
213
|
+
];
|
|
214
|
+
for (const pattern of suspiciousPatterns) {
|
|
215
|
+
if (pattern.test(filePath)) {
|
|
216
|
+
throw new SecurityError('Suspicious encoding pattern detected', 'SUSPICIOUS_ENCODING');
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// Normalize both paths
|
|
220
|
+
const normalizedRoot = path.resolve(workspaceRoot);
|
|
221
|
+
const normalizedPath = path.resolve(workspaceRoot, decoded);
|
|
222
|
+
// Check for path traversal after resolution
|
|
223
|
+
if (!normalizedPath.startsWith(normalizedRoot)) {
|
|
224
|
+
throw new SecurityError('Path resolves outside workspace', 'PATH_TRAVERSAL');
|
|
225
|
+
}
|
|
226
|
+
// Ensure the path doesn't equal root exactly when input was empty
|
|
227
|
+
// This is fine - empty string resolves to workspace root
|
|
228
|
+
return normalizedPath;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Validate that a path exists and is within the workspace
|
|
232
|
+
*/
|
|
233
|
+
function validatePathExists(filePath, workspaceRoot, type = 'any') {
|
|
234
|
+
const sanitized = sanitizePath(filePath, workspaceRoot);
|
|
235
|
+
if (!fs.existsSync(sanitized)) {
|
|
236
|
+
throw new SecurityError('Path does not exist', 'PATH_NOT_FOUND');
|
|
237
|
+
}
|
|
238
|
+
const stat = fs.statSync(sanitized);
|
|
239
|
+
if (type === 'file' && !stat.isFile()) {
|
|
240
|
+
throw new SecurityError('Expected file but found directory', 'NOT_A_FILE');
|
|
241
|
+
}
|
|
242
|
+
if (type === 'directory' && !stat.isDirectory()) {
|
|
243
|
+
throw new SecurityError('Expected directory but found file', 'NOT_A_DIRECTORY');
|
|
244
|
+
}
|
|
245
|
+
return sanitized;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Validate tool arguments against expected schema
|
|
249
|
+
*/
|
|
250
|
+
function validateToolArgs(args, required, types) {
|
|
251
|
+
// Check required fields
|
|
252
|
+
for (const field of required) {
|
|
253
|
+
if (!(field in args) || args[field] === undefined || args[field] === null) {
|
|
254
|
+
throw new ValidationError(`Missing required argument: ${field}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// Check types
|
|
258
|
+
for (const [field, expectedType] of Object.entries(types)) {
|
|
259
|
+
if (!(field in args))
|
|
260
|
+
continue;
|
|
261
|
+
const value = args[field];
|
|
262
|
+
let actualType;
|
|
263
|
+
if (Array.isArray(value)) {
|
|
264
|
+
actualType = 'array';
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
actualType = typeof value;
|
|
268
|
+
}
|
|
269
|
+
if (actualType !== expectedType) {
|
|
270
|
+
throw new ValidationError(`Invalid type for ${field}: expected ${expectedType}, got ${actualType}`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Sanitize string input to prevent injection
|
|
276
|
+
*/
|
|
277
|
+
function sanitizeString(input, maxLength = 10000) {
|
|
278
|
+
if (typeof input !== 'string') {
|
|
279
|
+
throw new ValidationError('Expected string input');
|
|
280
|
+
}
|
|
281
|
+
// Truncate if too long
|
|
282
|
+
if (input.length > maxLength) {
|
|
283
|
+
input = input.slice(0, maxLength);
|
|
284
|
+
}
|
|
285
|
+
// Remove null bytes
|
|
286
|
+
input = input.replace(/\0/g, '');
|
|
287
|
+
return input;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Security error with code for categorization
|
|
291
|
+
*/
|
|
292
|
+
class SecurityError extends Error {
|
|
293
|
+
code;
|
|
294
|
+
constructor(message, code) {
|
|
295
|
+
super(message);
|
|
296
|
+
this.name = 'SecurityError';
|
|
297
|
+
this.code = code;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
exports.SecurityError = SecurityError;
|
|
301
|
+
/**
|
|
302
|
+
* Validation error for invalid input
|
|
303
|
+
*/
|
|
304
|
+
class ValidationError extends Error {
|
|
305
|
+
constructor(message) {
|
|
306
|
+
super(message);
|
|
307
|
+
this.name = 'ValidationError';
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
exports.ValidationError = ValidationError;
|
|
311
|
+
/**
|
|
312
|
+
* Rate limiter for API calls
|
|
313
|
+
*/
|
|
314
|
+
class RateLimiter {
|
|
315
|
+
requests = [];
|
|
316
|
+
maxRequests;
|
|
317
|
+
windowMs;
|
|
318
|
+
constructor(maxRequests, windowMs) {
|
|
319
|
+
this.maxRequests = maxRequests;
|
|
320
|
+
this.windowMs = windowMs;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Check if a request is allowed
|
|
324
|
+
*/
|
|
325
|
+
isAllowed() {
|
|
326
|
+
// Handle edge cases
|
|
327
|
+
if (this.maxRequests <= 0) {
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
const now = Date.now();
|
|
331
|
+
// Remove old requests outside the window
|
|
332
|
+
this.requests = this.requests.filter(time => now - time < this.windowMs);
|
|
333
|
+
if (this.requests.length >= this.maxRequests) {
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
this.requests.push(now);
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Get remaining requests in current window
|
|
341
|
+
*/
|
|
342
|
+
remaining() {
|
|
343
|
+
const now = Date.now();
|
|
344
|
+
this.requests = this.requests.filter(time => now - time < this.windowMs);
|
|
345
|
+
return Math.max(0, this.maxRequests - this.requests.length);
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Get time until window resets (ms)
|
|
349
|
+
*/
|
|
350
|
+
resetIn() {
|
|
351
|
+
if (this.requests.length === 0)
|
|
352
|
+
return 0;
|
|
353
|
+
const oldest = Math.min(...this.requests);
|
|
354
|
+
return Math.max(0, this.windowMs - (Date.now() - oldest));
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
exports.RateLimiter = RateLimiter;
|
|
358
|
+
//# sourceMappingURL=security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../src/utils/security.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDH,8CA8CC;AAgDD,oCAwFC;AAKD,gDA+BC;AAKD,4CA+BC;AAKD,wCAcC;AA/TD,2CAA6B;AAC7B,uCAAyB;AAEzB;;;GAGG;AACH,MAAM,sBAAsB,GAAG;IAC7B,cAAc;IACd,WAAW;IACX,SAAS;IACT,QAAQ;IACR,SAAS;IACT,gBAAgB;IAChB,aAAa;IACb,iBAAiB,EAAM,kBAAkB;IACzC,kBAAkB,EAAK,eAAe;IACtC,wBAAwB,EAAE,qBAAqB;IAC/C,SAAS,EAAc,oBAAoB;IAC3C,iBAAiB,EAAM,sBAAsB;IAC7C,oBAAoB,EAAG,iBAAiB;IACxC,oBAAoB,EAAG,uBAAuB;IAC9C,kBAAkB,EAAK,eAAe;IACtC,qBAAqB,EAAE,kBAAkB;IACzC,mBAAmB,EAAI,eAAe;CACvC,CAAC;AAEF;;;GAGG;AACH,MAAM,qBAAqB,GAAG;IAC5B,uBAAuB,EAAM,kBAAkB;IAC/C,WAAW,EAAkB,mBAAmB;IAChD,WAAW,EAAkB,mBAAmB;IAChD,oBAAoB,EAAS,qBAAqB;IAClD,uBAAuB,EAAM,gCAAgC;IAC7D,uBAAuB,EAAM,sBAAsB;IACnD,cAAc,EAAe,2BAA2B;IACxD,cAAc,EAAe,eAAe;IAC5C,sBAAsB,EAAO,aAAa;CAC3C,CAAC;AAEF;;GAEG;AACH,SAAgB,iBAAiB,CAAC,GAAY,EAAE,KAAK,GAAG,CAAC;IACvD,IAAI,KAAK,GAAG,EAAE;QAAE,OAAO,aAAa,CAAC;IAErC,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC;IAElD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,sDAAsD;QACtD,KAAK,MAAM,OAAO,IAAI,qBAAqB,EAAE,CAAC;YAC5C,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtB,OAAO,YAAY,CAAC;YACtB,CAAC;QACH,CAAC;QAED,sEAAsE;QACtE,IAAI,GAAG,CAAC,MAAM,GAAG,EAAE,IAAI,qBAAqB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACvD,OAAO,WAAW,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAC1D,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,iBAAiB,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;IAC7D,CAAC;IAED,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,MAAM,GAA4B,EAAE,CAAC;QAE3C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/C,mCAAmC;YACnC,IAAI,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,aAAa,IAAI,GAAG,KAAK,WAAW,EAAE,CAAC;gBACxE,SAAS;YACX,CAAC;YAED,MAAM,cAAc,GAAG,sBAAsB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YAEjF,IAAI,cAAc,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACpE,MAAM,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC;YAC7B,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,GAAG,CAAC,GAAG,iBAAiB,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;YACpD,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB,CAAC,KAAa,EAAE,aAAa,GAAG,CAAC;IAC1D,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,IAAI,UAAU,GAAG,CAAC,CAAC;IAEnB,kDAAkD;IAClD,OAAO,OAAO,KAAK,IAAI,IAAI,UAAU,GAAG,aAAa,EAAE,CAAC;QACtD,IAAI,GAAG,OAAO,CAAC;QACf,IAAI,CAAC;YACH,OAAO,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,iCAAiC;YACjC,MAAM;QACR,CAAC;QACD,UAAU,EAAE,CAAC;IACf,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,SAAS,yBAAyB,CAAC,KAAa;IAC9C,MAAM,QAAQ,GAAG;QACf,WAAW,EAAa,cAAc;QACtC,WAAW,EAAa,cAAc;QACtC,OAAO,EAAiB,iBAAiB;QACzC,OAAO,EAAiB,eAAe;QACvC,QAAQ,EAAgB,UAAU;KACnC,CAAC;IAEF,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,YAAY,CAAC,QAAgB,EAAE,aAAqB;IAClE,2DAA2D;IAC3D,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,aAAa,CACrB,4BAA4B,EAC5B,qBAAqB,CACtB,CAAC;IACJ,CAAC;IAED,2DAA2D;IAC3D,MAAM,OAAO,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAE7C,4DAA4D;IAC5D,IAAI,yBAAyB,CAAC,QAAQ,CAAC,IAAI,yBAAyB,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9E,MAAM,IAAI,aAAa,CACrB,iCAAiC,EACjC,gBAAgB,CACjB,CAAC;IACJ,CAAC;IAED,4CAA4C;IAC5C,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,aAAa,CACrB,gCAAgC,EAChC,eAAe,CAChB,CAAC;IACJ,CAAC;IAED,sDAAsD;IACtD,IAAI,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,aAAa,CACrB,qCAAqC,EACrC,eAAe,CAChB,CAAC;IACJ,CAAC;IAED,oDAAoD;IACpD,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QAC5D,qEAAqE;QACrE,MAAM,IAAI,aAAa,CACrB,gCAAgC,EAChC,eAAe,CAChB,CAAC;IACJ,CAAC;IAED,qEAAqE;IACrE,MAAM,kBAAkB,GAAG;QACzB,MAAM,EAAkB,iCAAiC;QACzD,MAAM,EAAkB,wBAAwB;QAChD,MAAM,EAAkB,4BAA4B;QACpD,MAAM,EAAkB,mBAAmB;QAC3C,QAAQ,EAAgB,oBAAoB;QAC5C,QAAQ,EAAgB,iBAAiB;QACzC,QAAQ,EAAgB,iBAAiB;QACzC,QAAQ,EAAgB,sBAAsB;QAC9C,QAAQ,EAAgB,kBAAkB;QAC1C,QAAQ,EAAgB,qBAAqB;QAC7C,QAAQ,EAAgB,6BAA6B;QACrD,SAAS,EAAe,+BAA+B;QACvD,SAAS,EAAe,+BAA+B;QACvD,SAAS,EAAe,+BAA+B;KACxD,CAAC;IAEF,KAAK,MAAM,OAAO,IAAI,kBAAkB,EAAE,CAAC;QACzC,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,aAAa,CACrB,sCAAsC,EACtC,qBAAqB,CACtB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,uBAAuB;IACvB,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACnD,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IAE5D,4CAA4C;IAC5C,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,aAAa,CACrB,iCAAiC,EACjC,gBAAgB,CACjB,CAAC;IACJ,CAAC;IAED,kEAAkE;IAClE,yDAAyD;IAEzD,OAAO,cAAc,CAAC;AACxB,CAAC;AAED;;GAEG;AACH,SAAgB,kBAAkB,CAChC,QAAgB,EAChB,aAAqB,EACrB,OAAqC,KAAK;IAE1C,MAAM,SAAS,GAAG,YAAY,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;IAExD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,aAAa,CACrB,qBAAqB,EACrB,gBAAgB,CACjB,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IAEpC,IAAI,IAAI,KAAK,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,aAAa,CACrB,mCAAmC,EACnC,YAAY,CACb,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,KAAK,WAAW,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;QAChD,MAAM,IAAI,aAAa,CACrB,mCAAmC,EACnC,iBAAiB,CAClB,CAAC;IACJ,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,SAAgB,gBAAgB,CAC9B,IAA6B,EAC7B,QAAkB,EAClB,KAA2E;IAE3E,wBAAwB;IACxB,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,SAAS,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;YAC1E,MAAM,IAAI,eAAe,CAAC,8BAA8B,KAAK,EAAE,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;IAED,cAAc;IACd,KAAK,MAAM,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1D,IAAI,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC;YAAE,SAAS;QAE/B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1B,IAAI,UAAkB,CAAC;QAEvB,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,UAAU,GAAG,OAAO,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,UAAU,GAAG,OAAO,KAAK,CAAC;QAC5B,CAAC;QAED,IAAI,UAAU,KAAK,YAAY,EAAE,CAAC;YAChC,MAAM,IAAI,eAAe,CACvB,oBAAoB,KAAK,cAAc,YAAY,SAAS,UAAU,EAAE,CACzE,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAgB,cAAc,CAAC,KAAa,EAAE,SAAS,GAAG,KAAK;IAC7D,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,eAAe,CAAC,uBAAuB,CAAC,CAAC;IACrD,CAAC;IAED,uBAAuB;IACvB,IAAI,KAAK,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;QAC7B,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACpC,CAAC;IAED,oBAAoB;IACpB,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAEjC,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAa,aAAc,SAAQ,KAAK;IACtC,IAAI,CAAS;IAEb,YAAY,OAAe,EAAE,IAAY;QACvC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AARD,sCAQC;AAED;;GAEG;AACH,MAAa,eAAgB,SAAQ,KAAK;IACxC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AALD,0CAKC;AAED;;GAEG;AACH,MAAa,WAAW;IACd,QAAQ,GAAa,EAAE,CAAC;IACf,WAAW,CAAS;IACpB,QAAQ,CAAS;IAElC,YAAY,WAAmB,EAAE,QAAgB;QAC/C,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,SAAS;QACP,oBAAoB;QACpB,IAAI,IAAI,CAAC,WAAW,IAAI,CAAC,EAAE,CAAC;YAC1B,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEvB,yCAAyC;QACzC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;QAEzE,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YAC7C,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,SAAS;QACP,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC9D,CAAC;IAED;;OAEG;IACH,OAAO;QACL,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC1C,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC;IAC5D,CAAC;CACF;AAjDD,kCAiDC"}
|
package/mcp.json
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/modelcontextprotocol/registry/main/mcp-registry.schema.json",
|
|
3
|
+
"name": "@ciellos/partnercore-proxy",
|
|
4
|
+
"displayName": "PartnerCore AL Proxy",
|
|
5
|
+
"description": "Local MCP proxy for Microsoft Dynamics 365 Business Central AL development. Provides real-time AL code intelligence via Language Server Protocol and connects to PartnerCore Cloud for AI-powered code review, best practices, and knowledge base access.",
|
|
6
|
+
"version": "0.1.0",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Ciellos",
|
|
9
|
+
"url": "https://ciellos.com"
|
|
10
|
+
},
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"repository": "https://github.com/ciellos-dev/partnercore-proxy",
|
|
13
|
+
"homepage": "https://partnercore.ai",
|
|
14
|
+
"categories": [
|
|
15
|
+
"developer-tools",
|
|
16
|
+
"code-analysis",
|
|
17
|
+
"language-support"
|
|
18
|
+
],
|
|
19
|
+
"tags": [
|
|
20
|
+
"business-central",
|
|
21
|
+
"dynamics-365",
|
|
22
|
+
"al-language",
|
|
23
|
+
"erp",
|
|
24
|
+
"code-review"
|
|
25
|
+
],
|
|
26
|
+
"runtime": "node",
|
|
27
|
+
"transport": "stdio",
|
|
28
|
+
"command": {
|
|
29
|
+
"npx": ["@ciellos/partnercore-proxy", "start"],
|
|
30
|
+
"global": ["partnercore-proxy", "start"]
|
|
31
|
+
},
|
|
32
|
+
"configuration": {
|
|
33
|
+
"required": [],
|
|
34
|
+
"optional": [
|
|
35
|
+
{
|
|
36
|
+
"name": "PARTNERCORE_API_KEY",
|
|
37
|
+
"description": "API key for PartnerCore Cloud features (code review, KB access, templates). Get yours at https://partnercore.ai",
|
|
38
|
+
"type": "string",
|
|
39
|
+
"secret": true
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"name": "AL_WORKSPACE_ROOT",
|
|
43
|
+
"description": "Path to AL project root (containing app.json). Defaults to current directory.",
|
|
44
|
+
"type": "string"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"name": "LOG_LEVEL",
|
|
48
|
+
"description": "Logging level: debug, info, warn, error",
|
|
49
|
+
"type": "string",
|
|
50
|
+
"default": "info"
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
"tools": {
|
|
55
|
+
"local": [
|
|
56
|
+
{
|
|
57
|
+
"name": "al_get_symbols",
|
|
58
|
+
"description": "Get all symbols (tables, pages, codeunits, procedures, fields) in an AL file"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"name": "al_find_symbol",
|
|
62
|
+
"description": "Search for AL symbols by name across the workspace"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"name": "al_find_references",
|
|
66
|
+
"description": "Find all references to a symbol at a specific position"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"name": "al_get_diagnostics",
|
|
70
|
+
"description": "Get real compiler diagnostics (errors, warnings) for an AL file"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"name": "al_go_to_definition",
|
|
74
|
+
"description": "Navigate to the definition of a symbol"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"name": "al_hover",
|
|
78
|
+
"description": "Get type information and documentation for a symbol"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"name": "al_completion",
|
|
82
|
+
"description": "Get intelligent code completion suggestions"
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
"cloud": [
|
|
86
|
+
{
|
|
87
|
+
"name": "partnercore_review",
|
|
88
|
+
"description": "AI-powered code review with 252+ AL patterns and best practices",
|
|
89
|
+
"requiresApiKey": true
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"name": "partnercore_kb_search",
|
|
93
|
+
"description": "Search PartnerCore knowledge base for AL/BC solutions and guidance",
|
|
94
|
+
"requiresApiKey": true
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"name": "partnercore_template",
|
|
98
|
+
"description": "Get production-ready AL code templates",
|
|
99
|
+
"requiresApiKey": true
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
"requirements": {
|
|
104
|
+
"node": ">=18.0.0"
|
|
105
|
+
},
|
|
106
|
+
"security": {
|
|
107
|
+
"network": {
|
|
108
|
+
"outbound": [
|
|
109
|
+
{
|
|
110
|
+
"host": "marketplace.visualstudio.com",
|
|
111
|
+
"purpose": "Download Microsoft AL Language extension"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"host": "partnercore-mcp.azurewebsites.net",
|
|
115
|
+
"purpose": "PartnerCore Cloud MCP API (optional, requires API key)"
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
},
|
|
119
|
+
"filesystem": {
|
|
120
|
+
"read": ["AL_WORKSPACE_ROOT"],
|
|
121
|
+
"write": ["~/.partnercore/al-extension"]
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|