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.
Files changed (73) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/LICENSE +22 -0
  3. package/README.md +234 -0
  4. package/dist/al/extension-manager.d.ts +52 -0
  5. package/dist/al/extension-manager.d.ts.map +1 -0
  6. package/dist/al/extension-manager.js +348 -0
  7. package/dist/al/extension-manager.js.map +1 -0
  8. package/dist/al/index.d.ts +3 -0
  9. package/dist/al/index.d.ts.map +1 -0
  10. package/dist/al/index.js +19 -0
  11. package/dist/al/index.js.map +1 -0
  12. package/dist/al/language-server.d.ts +134 -0
  13. package/dist/al/language-server.d.ts.map +1 -0
  14. package/dist/al/language-server.js +431 -0
  15. package/dist/al/language-server.js.map +1 -0
  16. package/dist/cli.d.ts +8 -0
  17. package/dist/cli.d.ts.map +1 -0
  18. package/dist/cli.js +207 -0
  19. package/dist/cli.js.map +1 -0
  20. package/dist/cloud/index.d.ts +2 -0
  21. package/dist/cloud/index.d.ts.map +1 -0
  22. package/dist/cloud/index.js +18 -0
  23. package/dist/cloud/index.js.map +1 -0
  24. package/dist/cloud/relay-client.d.ts +84 -0
  25. package/dist/cloud/relay-client.d.ts.map +1 -0
  26. package/dist/cloud/relay-client.js +211 -0
  27. package/dist/cloud/relay-client.js.map +1 -0
  28. package/dist/config/index.d.ts +3 -0
  29. package/dist/config/index.d.ts.map +1 -0
  30. package/dist/config/index.js +19 -0
  31. package/dist/config/index.js.map +1 -0
  32. package/dist/config/loader.d.ts +20 -0
  33. package/dist/config/loader.d.ts.map +1 -0
  34. package/dist/config/loader.js +136 -0
  35. package/dist/config/loader.js.map +1 -0
  36. package/dist/config/types.d.ts +51 -0
  37. package/dist/config/types.d.ts.map +1 -0
  38. package/dist/config/types.js +33 -0
  39. package/dist/config/types.js.map +1 -0
  40. package/dist/index.d.ts +13 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +29 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/mcp/index.d.ts +2 -0
  45. package/dist/mcp/index.d.ts.map +1 -0
  46. package/dist/mcp/index.js +18 -0
  47. package/dist/mcp/index.js.map +1 -0
  48. package/dist/mcp/server.d.ts +33 -0
  49. package/dist/mcp/server.d.ts.map +1 -0
  50. package/dist/mcp/server.js +98 -0
  51. package/dist/mcp/server.js.map +1 -0
  52. package/dist/router/index.d.ts +2 -0
  53. package/dist/router/index.d.ts.map +1 -0
  54. package/dist/router/index.js +18 -0
  55. package/dist/router/index.js.map +1 -0
  56. package/dist/router/tool-router.d.ts +87 -0
  57. package/dist/router/tool-router.d.ts.map +1 -0
  58. package/dist/router/tool-router.js +557 -0
  59. package/dist/router/tool-router.js.map +1 -0
  60. package/dist/utils/index.d.ts +3 -0
  61. package/dist/utils/index.d.ts.map +1 -0
  62. package/dist/utils/index.js +19 -0
  63. package/dist/utils/index.js.map +1 -0
  64. package/dist/utils/logger.d.ts +20 -0
  65. package/dist/utils/logger.d.ts.map +1 -0
  66. package/dist/utils/logger.js +99 -0
  67. package/dist/utils/logger.js.map +1 -0
  68. package/dist/utils/security.d.ts +66 -0
  69. package/dist/utils/security.d.ts.map +1 -0
  70. package/dist/utils/security.js +358 -0
  71. package/dist/utils/security.js.map +1 -0
  72. package/mcp.json +125 -0
  73. 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":"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"}
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
+