@frontmcp/plugins 0.4.1 → 0.5.1

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 (98) hide show
  1. package/package.json +8 -3
  2. package/src/cache/cache.plugin.js +27 -25
  3. package/src/cache/cache.plugin.js.map +1 -1
  4. package/src/cache/providers/cache-memory.provider.js +2 -1
  5. package/src/cache/providers/cache-memory.provider.js.map +1 -1
  6. package/src/cache/providers/cache-redis.provider.js +1 -0
  7. package/src/cache/providers/cache-redis.provider.js.map +1 -1
  8. package/src/codecall/README.md +999 -0
  9. package/src/codecall/codecall.plugin.d.ts +41 -0
  10. package/src/codecall/codecall.plugin.js +152 -0
  11. package/src/codecall/codecall.plugin.js.map +1 -0
  12. package/src/codecall/codecall.symbol.d.ts +106 -0
  13. package/src/codecall/codecall.symbol.js +4 -0
  14. package/src/codecall/codecall.symbol.js.map +1 -0
  15. package/src/codecall/codecall.types.d.ts +289 -0
  16. package/src/codecall/codecall.types.js +258 -0
  17. package/src/codecall/codecall.types.js.map +1 -0
  18. package/src/codecall/errors/index.d.ts +1 -0
  19. package/src/codecall/errors/index.js +6 -0
  20. package/src/codecall/errors/index.js.map +1 -0
  21. package/src/codecall/errors/tool-call.errors.d.ts +79 -0
  22. package/src/codecall/errors/tool-call.errors.js +119 -0
  23. package/src/codecall/errors/tool-call.errors.js.map +1 -0
  24. package/src/codecall/index.d.ts +2 -0
  25. package/src/codecall/index.js +8 -0
  26. package/src/codecall/index.js.map +1 -0
  27. package/src/codecall/providers/code-call.config.d.ts +29 -0
  28. package/src/codecall/providers/code-call.config.js +120 -0
  29. package/src/codecall/providers/code-call.config.js.map +1 -0
  30. package/src/codecall/security/index.d.ts +2 -0
  31. package/src/codecall/security/index.js +7 -0
  32. package/src/codecall/security/index.js.map +1 -0
  33. package/src/codecall/security/self-reference-guard.d.ts +32 -0
  34. package/src/codecall/security/self-reference-guard.js +70 -0
  35. package/src/codecall/security/self-reference-guard.js.map +1 -0
  36. package/src/codecall/security/tool-access-control.service.d.ts +104 -0
  37. package/src/codecall/security/tool-access-control.service.js +170 -0
  38. package/src/codecall/security/tool-access-control.service.js.map +1 -0
  39. package/src/codecall/services/audit-logger.service.d.ts +186 -0
  40. package/src/codecall/services/audit-logger.service.js +322 -0
  41. package/src/codecall/services/audit-logger.service.js.map +1 -0
  42. package/src/codecall/services/enclave.service.d.ts +62 -0
  43. package/src/codecall/services/enclave.service.js +214 -0
  44. package/src/codecall/services/enclave.service.js.map +1 -0
  45. package/src/codecall/services/error-enrichment.service.d.ts +94 -0
  46. package/src/codecall/services/error-enrichment.service.js +387 -0
  47. package/src/codecall/services/error-enrichment.service.js.map +1 -0
  48. package/src/codecall/services/index.d.ts +6 -0
  49. package/src/codecall/services/index.js +13 -0
  50. package/src/codecall/services/index.js.map +1 -0
  51. package/src/codecall/services/output-sanitizer.d.ts +86 -0
  52. package/src/codecall/services/output-sanitizer.js +260 -0
  53. package/src/codecall/services/output-sanitizer.js.map +1 -0
  54. package/src/codecall/services/synonym-expansion.service.d.ts +66 -0
  55. package/src/codecall/services/synonym-expansion.service.js +374 -0
  56. package/src/codecall/services/synonym-expansion.service.js.map +1 -0
  57. package/src/codecall/services/tool-search.service.d.ts +175 -0
  58. package/src/codecall/services/tool-search.service.js +587 -0
  59. package/src/codecall/services/tool-search.service.js.map +1 -0
  60. package/src/codecall/tools/describe.schema.d.ts +28 -0
  61. package/src/codecall/tools/describe.schema.js +67 -0
  62. package/src/codecall/tools/describe.schema.js.map +1 -0
  63. package/src/codecall/tools/describe.tool.d.ts +35 -0
  64. package/src/codecall/tools/describe.tool.js +207 -0
  65. package/src/codecall/tools/describe.tool.js.map +1 -0
  66. package/src/codecall/tools/execute.schema.d.ts +115 -0
  67. package/src/codecall/tools/execute.schema.js +116 -0
  68. package/src/codecall/tools/execute.schema.js.map +1 -0
  69. package/src/codecall/tools/execute.tool.d.ts +5 -0
  70. package/src/codecall/tools/execute.tool.js +238 -0
  71. package/src/codecall/tools/execute.tool.js.map +1 -0
  72. package/src/codecall/tools/index.d.ts +4 -0
  73. package/src/codecall/tools/index.js +13 -0
  74. package/src/codecall/tools/index.js.map +1 -0
  75. package/src/codecall/tools/invoke.schema.d.ts +99 -0
  76. package/src/codecall/tools/invoke.schema.js +27 -0
  77. package/src/codecall/tools/invoke.schema.js.map +1 -0
  78. package/src/codecall/tools/invoke.tool.d.ts +13 -0
  79. package/src/codecall/tools/invoke.tool.js +70 -0
  80. package/src/codecall/tools/invoke.tool.js.map +1 -0
  81. package/src/codecall/tools/search.schema.d.ts +30 -0
  82. package/src/codecall/tools/search.schema.js +60 -0
  83. package/src/codecall/tools/search.schema.js.map +1 -0
  84. package/src/codecall/tools/search.tool.d.ts +5 -0
  85. package/src/codecall/tools/search.tool.js +108 -0
  86. package/src/codecall/tools/search.tool.js.map +1 -0
  87. package/src/codecall/utils/describe.utils.d.ts +86 -0
  88. package/src/codecall/utils/describe.utils.js +531 -0
  89. package/src/codecall/utils/describe.utils.js.map +1 -0
  90. package/src/codecall/utils/index.d.ts +2 -0
  91. package/src/codecall/utils/index.js +7 -0
  92. package/src/codecall/utils/index.js.map +1 -0
  93. package/src/codecall/utils/mcp-result.d.ts +6 -0
  94. package/src/codecall/utils/mcp-result.js +36 -0
  95. package/src/codecall/utils/mcp-result.js.map +1 -0
  96. package/src/index.d.ts +2 -0
  97. package/src/index.js +3 -1
  98. package/src/index.js.map +1 -1
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ // file: libs/plugins/src/codecall/providers/code-call.config.ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const tslib_1 = require("tslib");
5
+ const sdk_1 = require("@frontmcp/sdk");
6
+ const codecall_types_1 = require("../codecall.types");
7
+ /**
8
+ * CodeCall configuration provider with convict-like API
9
+ * Extends BaseConfig to provide type-safe dotted path access
10
+ *
11
+ * @example
12
+ * // Get values with dotted path notation
13
+ * config.get('vm.preset') // returns 'secure'
14
+ * config.get('embedding.strategy') // returns 'tfidf'
15
+ * config.get('directCalls.enabled') // returns true
16
+ *
17
+ * // Get with default value
18
+ * config.get('vm.timeoutMs', 5000) // returns value or 5000
19
+ *
20
+ * // Get entire sections
21
+ * config.getSection('vm') // returns entire vm config
22
+ * config.getAll() // returns complete config
23
+ *
24
+ * // Require values (throws if undefined)
25
+ * config.getOrThrow('mode') // throws if undefined
26
+ * config.getRequired('topK') // same as getOrThrow
27
+ */
28
+ let CodeCallConfig = class CodeCallConfig extends sdk_1.BaseConfig {
29
+ constructor(options = {}) {
30
+ // Parse and validate options with Zod schema to apply all defaults
31
+ const parsedConfig = codecall_types_1.codeCallPluginOptionsSchema.parse(options);
32
+ // Resolve VM options with preset defaults
33
+ const resolvedVm = resolveVmOptions(parsedConfig.vm);
34
+ super({ ...parsedConfig, resolvedVm });
35
+ }
36
+ };
37
+ CodeCallConfig = tslib_1.__decorate([
38
+ (0, sdk_1.Provider)({
39
+ name: 'codecall:config',
40
+ description: 'CodeCall plugin configuration with validated defaults',
41
+ scope: sdk_1.ProviderScope.GLOBAL,
42
+ }),
43
+ tslib_1.__metadata("design:paramtypes", [Object])
44
+ ], CodeCallConfig);
45
+ exports.default = CodeCallConfig;
46
+ // ---- VM Options Resolution ----
47
+ function resolveVmOptions(vmOptions) {
48
+ const preset = vmOptions?.preset ?? 'secure';
49
+ const base = presetDefaults(preset);
50
+ return {
51
+ ...base,
52
+ ...vmOptions,
53
+ disabledBuiltins: vmOptions?.disabledBuiltins ?? base.disabledBuiltins,
54
+ disabledGlobals: vmOptions?.disabledGlobals ?? base.disabledGlobals,
55
+ };
56
+ }
57
+ function presetDefaults(preset) {
58
+ switch (preset) {
59
+ case 'locked_down':
60
+ return {
61
+ preset,
62
+ timeoutMs: 2000,
63
+ allowLoops: false,
64
+ allowConsole: false,
65
+ maxSteps: 2000,
66
+ disabledBuiltins: ['eval', 'Function', 'AsyncFunction'],
67
+ disabledGlobals: [
68
+ 'require',
69
+ 'process',
70
+ 'fetch',
71
+ 'setTimeout',
72
+ 'setInterval',
73
+ 'setImmediate',
74
+ 'global',
75
+ 'globalThis',
76
+ ],
77
+ };
78
+ case 'balanced':
79
+ return {
80
+ preset,
81
+ timeoutMs: 5000,
82
+ allowLoops: true,
83
+ allowConsole: true,
84
+ maxSteps: 10000,
85
+ disabledBuiltins: ['eval', 'Function', 'AsyncFunction'],
86
+ disabledGlobals: ['require', 'process', 'fetch'],
87
+ };
88
+ case 'experimental':
89
+ return {
90
+ preset,
91
+ timeoutMs: 10000,
92
+ allowLoops: true,
93
+ allowConsole: true,
94
+ maxSteps: 20000,
95
+ disabledBuiltins: ['eval', 'Function', 'AsyncFunction'],
96
+ disabledGlobals: ['require', 'process'],
97
+ };
98
+ case 'secure':
99
+ default:
100
+ return {
101
+ preset: 'secure',
102
+ timeoutMs: 3500,
103
+ allowLoops: false,
104
+ allowConsole: true,
105
+ maxSteps: 5000,
106
+ disabledBuiltins: ['eval', 'Function', 'AsyncFunction'],
107
+ disabledGlobals: [
108
+ 'require',
109
+ 'process',
110
+ 'fetch',
111
+ 'setTimeout',
112
+ 'setInterval',
113
+ 'setImmediate',
114
+ 'global',
115
+ 'globalThis',
116
+ ],
117
+ };
118
+ }
119
+ }
120
+ //# sourceMappingURL=code-call.config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-call.config.js","sourceRoot":"","sources":["../../../../src/codecall/providers/code-call.config.ts"],"names":[],"mappings":";AAAA,gEAAgE;;;AAEhE,uCAAoE;AACpE,sDAK2B;AAG3B;;;;;;;;;;;;;;;;;;;;GAoBG;AAMY,IAAM,cAAc,GAApB,MAAM,cAAe,SAAQ,gBAE3C;IACC,YAAY,UAA0C,EAAE;QACtD,mEAAmE;QACnE,MAAM,YAAY,GAAG,4CAA2B,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAEhE,0CAA0C;QAC1C,MAAM,UAAU,GAAG,gBAAgB,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QAErD,KAAK,CAAC,EAAE,GAAG,YAAY,EAAE,UAAU,EAAE,CAAC,CAAC;IACzC,CAAC;CACF,CAAA;AAZoB,cAAc;IALlC,IAAA,cAAQ,EAAC;QACR,IAAI,EAAE,iBAAiB;QACvB,WAAW,EAAE,uDAAuD;QACpE,KAAK,EAAE,mBAAa,CAAC,MAAM;KAC5B,CAAC;;GACmB,cAAc,CAYlC;kBAZoB,cAAc;AAcnC,kCAAkC;AAElC,SAAS,gBAAgB,CAAC,SAA6B;IACrD,MAAM,MAAM,GAAqB,SAAS,EAAE,MAAM,IAAI,QAAQ,CAAC;IAE/D,MAAM,IAAI,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IAEpC,OAAO;QACL,GAAG,IAAI;QACP,GAAG,SAAS;QACZ,gBAAgB,EAAE,SAAS,EAAE,gBAAgB,IAAI,IAAI,CAAC,gBAAgB;QACtE,eAAe,EAAE,SAAS,EAAE,eAAe,IAAI,IAAI,CAAC,eAAe;KACpE,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,MAAwB;IAC9C,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,aAAa;YAChB,OAAO;gBACL,MAAM;gBACN,SAAS,EAAE,IAAI;gBACf,UAAU,EAAE,KAAK;gBACjB,YAAY,EAAE,KAAK;gBACnB,QAAQ,EAAE,IAAI;gBACd,gBAAgB,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,eAAe,CAAC;gBACvD,eAAe,EAAE;oBACf,SAAS;oBACT,SAAS;oBACT,OAAO;oBACP,YAAY;oBACZ,aAAa;oBACb,cAAc;oBACd,QAAQ;oBACR,YAAY;iBACb;aACF,CAAC;QAEJ,KAAK,UAAU;YACb,OAAO;gBACL,MAAM;gBACN,SAAS,EAAE,IAAI;gBACf,UAAU,EAAE,IAAI;gBAChB,YAAY,EAAE,IAAI;gBAClB,QAAQ,EAAE,KAAK;gBACf,gBAAgB,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,eAAe,CAAC;gBACvD,eAAe,EAAE,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC;aACjD,CAAC;QAEJ,KAAK,cAAc;YACjB,OAAO;gBACL,MAAM;gBACN,SAAS,EAAE,KAAK;gBAChB,UAAU,EAAE,IAAI;gBAChB,YAAY,EAAE,IAAI;gBAClB,QAAQ,EAAE,KAAK;gBACf,gBAAgB,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,eAAe,CAAC;gBACvD,eAAe,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC;aACxC,CAAC;QAEJ,KAAK,QAAQ,CAAC;QACd;YACE,OAAO;gBACL,MAAM,EAAE,QAAQ;gBAChB,SAAS,EAAE,IAAI;gBACf,UAAU,EAAE,KAAK;gBACjB,YAAY,EAAE,IAAI;gBAClB,QAAQ,EAAE,IAAI;gBACd,gBAAgB,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,eAAe,CAAC;gBACvD,eAAe,EAAE;oBACf,SAAS;oBACT,SAAS;oBACT,OAAO;oBACP,YAAY;oBACZ,aAAa;oBACb,cAAc;oBACd,QAAQ;oBACR,YAAY;iBACb;aACF,CAAC;IACN,CAAC;AACH,CAAC","sourcesContent":["// file: libs/plugins/src/codecall/providers/code-call.config.ts\n\nimport { Provider, ProviderScope, BaseConfig } from '@frontmcp/sdk';\nimport {\n CodeCallPluginOptions,\n CodeCallVmOptions,\n CodeCallVmPreset,\n codeCallPluginOptionsSchema,\n} from '../codecall.types';\nimport { ResolvedCodeCallVmOptions } from '../codecall.symbol';\n\n/**\n * CodeCall configuration provider with convict-like API\n * Extends BaseConfig to provide type-safe dotted path access\n *\n * @example\n * // Get values with dotted path notation\n * config.get('vm.preset') // returns 'secure'\n * config.get('embedding.strategy') // returns 'tfidf'\n * config.get('directCalls.enabled') // returns true\n *\n * // Get with default value\n * config.get('vm.timeoutMs', 5000) // returns value or 5000\n *\n * // Get entire sections\n * config.getSection('vm') // returns entire vm config\n * config.getAll() // returns complete config\n *\n * // Require values (throws if undefined)\n * config.getOrThrow('mode') // throws if undefined\n * config.getRequired('topK') // same as getOrThrow\n */\n@Provider({\n name: 'codecall:config',\n description: 'CodeCall plugin configuration with validated defaults',\n scope: ProviderScope.GLOBAL,\n})\nexport default class CodeCallConfig extends BaseConfig<\n CodeCallPluginOptions & { resolvedVm: ResolvedCodeCallVmOptions }\n> {\n constructor(options: Partial<CodeCallPluginOptions> = {}) {\n // Parse and validate options with Zod schema to apply all defaults\n const parsedConfig = codeCallPluginOptionsSchema.parse(options);\n\n // Resolve VM options with preset defaults\n const resolvedVm = resolveVmOptions(parsedConfig.vm);\n\n super({ ...parsedConfig, resolvedVm });\n }\n}\n\n// ---- VM Options Resolution ----\n\nfunction resolveVmOptions(vmOptions?: CodeCallVmOptions): ResolvedCodeCallVmOptions {\n const preset: CodeCallVmPreset = vmOptions?.preset ?? 'secure';\n\n const base = presetDefaults(preset);\n\n return {\n ...base,\n ...vmOptions,\n disabledBuiltins: vmOptions?.disabledBuiltins ?? base.disabledBuiltins,\n disabledGlobals: vmOptions?.disabledGlobals ?? base.disabledGlobals,\n };\n}\n\nfunction presetDefaults(preset: CodeCallVmPreset): ResolvedCodeCallVmOptions {\n switch (preset) {\n case 'locked_down':\n return {\n preset,\n timeoutMs: 2000,\n allowLoops: false,\n allowConsole: false,\n maxSteps: 2000,\n disabledBuiltins: ['eval', 'Function', 'AsyncFunction'],\n disabledGlobals: [\n 'require',\n 'process',\n 'fetch',\n 'setTimeout',\n 'setInterval',\n 'setImmediate',\n 'global',\n 'globalThis',\n ],\n };\n\n case 'balanced':\n return {\n preset,\n timeoutMs: 5000,\n allowLoops: true,\n allowConsole: true,\n maxSteps: 10000,\n disabledBuiltins: ['eval', 'Function', 'AsyncFunction'],\n disabledGlobals: ['require', 'process', 'fetch'],\n };\n\n case 'experimental':\n return {\n preset,\n timeoutMs: 10000,\n allowLoops: true,\n allowConsole: true,\n maxSteps: 20000,\n disabledBuiltins: ['eval', 'Function', 'AsyncFunction'],\n disabledGlobals: ['require', 'process'],\n };\n\n case 'secure':\n default:\n return {\n preset: 'secure',\n timeoutMs: 3500,\n allowLoops: false,\n allowConsole: true,\n maxSteps: 5000,\n disabledBuiltins: ['eval', 'Function', 'AsyncFunction'],\n disabledGlobals: [\n 'require',\n 'process',\n 'fetch',\n 'setTimeout',\n 'setInterval',\n 'setImmediate',\n 'global',\n 'globalThis',\n ],\n };\n }\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export * from './self-reference-guard';
2
+ export * from './tool-access-control.service';
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ // file: libs/plugins/src/codecall/security/index.ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const tslib_1 = require("tslib");
5
+ tslib_1.__exportStar(require("./self-reference-guard"), exports);
6
+ tslib_1.__exportStar(require("./tool-access-control.service"), exports);
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/codecall/security/index.ts"],"names":[],"mappings":";AAAA,oDAAoD;;;AAEpD,iEAAuC;AACvC,wEAA8C","sourcesContent":["// file: libs/plugins/src/codecall/security/index.ts\n\nexport * from './self-reference-guard';\nexport * from './tool-access-control.service';\n"]}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Check if a tool name is a CodeCall plugin tool that should be blocked.
3
+ *
4
+ * SECURITY: This is the FIRST check in callTool before ANY other processing.
5
+ * It prevents:
6
+ * - Recursive execution attacks (codecall:execute calling itself)
7
+ * - Privilege escalation via nested tool calls
8
+ * - Resource exhaustion through self-invocation loops
9
+ *
10
+ * @param toolName - The name of the tool being called
11
+ * @returns true if the tool is a blocked CodeCall tool
12
+ */
13
+ export declare function isBlockedSelfReference(toolName: string): boolean;
14
+ /**
15
+ * Assert that a tool call is not a self-reference.
16
+ * Throws SelfReferenceError if the tool is blocked.
17
+ *
18
+ * SECURITY: This function MUST be called at the very start of callTool,
19
+ * before any other validation or processing.
20
+ *
21
+ * @param toolName - The name of the tool being called
22
+ * @throws SelfReferenceError if the tool is a blocked CodeCall tool
23
+ */
24
+ export declare function assertNotSelfReference(toolName: string): void;
25
+ /**
26
+ * Get the list of blocked tool patterns for documentation/testing.
27
+ * This is informational only - the actual blocking uses isBlockedSelfReference().
28
+ */
29
+ export declare function getBlockedPatterns(): {
30
+ prefix: string;
31
+ explicit: readonly string[];
32
+ };
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ // file: libs/plugins/src/codecall/security/self-reference-guard.ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.isBlockedSelfReference = isBlockedSelfReference;
5
+ exports.assertNotSelfReference = assertNotSelfReference;
6
+ exports.getBlockedPatterns = getBlockedPatterns;
7
+ const tool_call_errors_1 = require("../errors/tool-call.errors");
8
+ /**
9
+ * HARDCODED list of CodeCall plugin tool names that MUST be blocked from self-reference.
10
+ *
11
+ * SECURITY: This list is NOT configurable to prevent bypass attempts.
12
+ * Any tool with the 'codecall:' prefix is blocked.
13
+ */
14
+ const CODECALL_TOOL_PREFIX = 'codecall:';
15
+ /**
16
+ * Explicitly blocked tool names for additional safety.
17
+ * Even if a tool doesn't have the prefix, these are always blocked.
18
+ */
19
+ const BLOCKED_CODECALL_TOOLS = Object.freeze(new Set(['codecall:search', 'codecall:describe', 'codecall:execute', 'codecall:invoke']));
20
+ /**
21
+ * Check if a tool name is a CodeCall plugin tool that should be blocked.
22
+ *
23
+ * SECURITY: This is the FIRST check in callTool before ANY other processing.
24
+ * It prevents:
25
+ * - Recursive execution attacks (codecall:execute calling itself)
26
+ * - Privilege escalation via nested tool calls
27
+ * - Resource exhaustion through self-invocation loops
28
+ *
29
+ * @param toolName - The name of the tool being called
30
+ * @returns true if the tool is a blocked CodeCall tool
31
+ */
32
+ function isBlockedSelfReference(toolName) {
33
+ // Normalize tool name (case-insensitive check for prefix)
34
+ const normalizedName = toolName.toLowerCase().trim();
35
+ // Check for codecall: prefix
36
+ if (normalizedName.startsWith(CODECALL_TOOL_PREFIX)) {
37
+ return true;
38
+ }
39
+ // Check explicit blocklist
40
+ if (BLOCKED_CODECALL_TOOLS.has(toolName)) {
41
+ return true;
42
+ }
43
+ return false;
44
+ }
45
+ /**
46
+ * Assert that a tool call is not a self-reference.
47
+ * Throws SelfReferenceError if the tool is blocked.
48
+ *
49
+ * SECURITY: This function MUST be called at the very start of callTool,
50
+ * before any other validation or processing.
51
+ *
52
+ * @param toolName - The name of the tool being called
53
+ * @throws SelfReferenceError if the tool is a blocked CodeCall tool
54
+ */
55
+ function assertNotSelfReference(toolName) {
56
+ if (isBlockedSelfReference(toolName)) {
57
+ throw new tool_call_errors_1.SelfReferenceError(toolName);
58
+ }
59
+ }
60
+ /**
61
+ * Get the list of blocked tool patterns for documentation/testing.
62
+ * This is informational only - the actual blocking uses isBlockedSelfReference().
63
+ */
64
+ function getBlockedPatterns() {
65
+ return {
66
+ prefix: CODECALL_TOOL_PREFIX,
67
+ explicit: Array.from(BLOCKED_CODECALL_TOOLS),
68
+ };
69
+ }
70
+ //# sourceMappingURL=self-reference-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"self-reference-guard.js","sourceRoot":"","sources":["../../../../src/codecall/security/self-reference-guard.ts"],"names":[],"mappings":";AAAA,mEAAmE;;AAgCnE,wDAeC;AAYD,wDAIC;AAMD,gDAKC;AAxED,iEAAgE;AAEhE;;;;;GAKG;AACH,MAAM,oBAAoB,GAAG,WAAW,CAAC;AAEzC;;;GAGG;AACH,MAAM,sBAAsB,GAAwB,MAAM,CAAC,MAAM,CAC/D,IAAI,GAAG,CAAC,CAAC,iBAAiB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,iBAAiB,CAAC,CAAC,CACzF,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,SAAgB,sBAAsB,CAAC,QAAgB;IACrD,0DAA0D;IAC1D,MAAM,cAAc,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;IAErD,6BAA6B;IAC7B,IAAI,cAAc,CAAC,UAAU,CAAC,oBAAoB,CAAC,EAAE,CAAC;QACpD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,2BAA2B;IAC3B,IAAI,sBAAsB,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;GASG;AACH,SAAgB,sBAAsB,CAAC,QAAgB;IACrD,IAAI,sBAAsB,CAAC,QAAQ,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,qCAAkB,CAAC,QAAQ,CAAC,CAAC;IACzC,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,kBAAkB;IAChC,OAAO;QACL,MAAM,EAAE,oBAAoB;QAC5B,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC;KAC7C,CAAC;AACJ,CAAC","sourcesContent":["// file: libs/plugins/src/codecall/security/self-reference-guard.ts\n\nimport { SelfReferenceError } from '../errors/tool-call.errors';\n\n/**\n * HARDCODED list of CodeCall plugin tool names that MUST be blocked from self-reference.\n *\n * SECURITY: This list is NOT configurable to prevent bypass attempts.\n * Any tool with the 'codecall:' prefix is blocked.\n */\nconst CODECALL_TOOL_PREFIX = 'codecall:';\n\n/**\n * Explicitly blocked tool names for additional safety.\n * Even if a tool doesn't have the prefix, these are always blocked.\n */\nconst BLOCKED_CODECALL_TOOLS: ReadonlySet<string> = Object.freeze(\n new Set(['codecall:search', 'codecall:describe', 'codecall:execute', 'codecall:invoke']),\n);\n\n/**\n * Check if a tool name is a CodeCall plugin tool that should be blocked.\n *\n * SECURITY: This is the FIRST check in callTool before ANY other processing.\n * It prevents:\n * - Recursive execution attacks (codecall:execute calling itself)\n * - Privilege escalation via nested tool calls\n * - Resource exhaustion through self-invocation loops\n *\n * @param toolName - The name of the tool being called\n * @returns true if the tool is a blocked CodeCall tool\n */\nexport function isBlockedSelfReference(toolName: string): boolean {\n // Normalize tool name (case-insensitive check for prefix)\n const normalizedName = toolName.toLowerCase().trim();\n\n // Check for codecall: prefix\n if (normalizedName.startsWith(CODECALL_TOOL_PREFIX)) {\n return true;\n }\n\n // Check explicit blocklist\n if (BLOCKED_CODECALL_TOOLS.has(toolName)) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Assert that a tool call is not a self-reference.\n * Throws SelfReferenceError if the tool is blocked.\n *\n * SECURITY: This function MUST be called at the very start of callTool,\n * before any other validation or processing.\n *\n * @param toolName - The name of the tool being called\n * @throws SelfReferenceError if the tool is a blocked CodeCall tool\n */\nexport function assertNotSelfReference(toolName: string): void {\n if (isBlockedSelfReference(toolName)) {\n throw new SelfReferenceError(toolName);\n }\n}\n\n/**\n * Get the list of blocked tool patterns for documentation/testing.\n * This is informational only - the actual blocking uses isBlockedSelfReference().\n */\nexport function getBlockedPatterns(): { prefix: string; explicit: readonly string[] } {\n return {\n prefix: CODECALL_TOOL_PREFIX,\n explicit: Array.from(BLOCKED_CODECALL_TOOLS),\n };\n}\n"]}
@@ -0,0 +1,104 @@
1
+ import type { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';
2
+ /**
3
+ * Tool access control modes:
4
+ * - whitelist: Only explicitly allowed tools can be called (most secure)
5
+ * - blacklist: All tools allowed except explicitly blocked (default, more flexible)
6
+ * - dynamic: Evaluate access per-call based on custom function
7
+ */
8
+ export type ToolAccessMode = 'whitelist' | 'blacklist' | 'dynamic';
9
+ /**
10
+ * Tool access policy configuration.
11
+ */
12
+ export interface ToolAccessPolicy {
13
+ /**
14
+ * Access control mode.
15
+ * @default 'blacklist'
16
+ */
17
+ mode: ToolAccessMode;
18
+ /**
19
+ * Tools explicitly allowed (for whitelist mode).
20
+ * Supports exact names and glob patterns (e.g., 'users:*').
21
+ */
22
+ whitelist?: string[];
23
+ /**
24
+ * Tools explicitly blocked (for blacklist mode).
25
+ * Supports exact names and glob patterns (e.g., 'admin:*').
26
+ */
27
+ blacklist?: string[];
28
+ /**
29
+ * Pattern-based rules that apply in addition to whitelist/blacklist.
30
+ */
31
+ patterns?: {
32
+ allow?: string[];
33
+ deny?: string[];
34
+ };
35
+ /**
36
+ * Custom evaluator function for dynamic mode.
37
+ * Called for each tool access request.
38
+ */
39
+ evaluator?: (context: ToolAccessContext) => Promise<ToolAccessDecision>;
40
+ }
41
+ /**
42
+ * Context provided to the access evaluator.
43
+ */
44
+ export interface ToolAccessContext {
45
+ toolName: string;
46
+ authInfo?: AuthInfo;
47
+ executionId?: string;
48
+ callDepth: number;
49
+ timestamp: number;
50
+ }
51
+ /**
52
+ * Decision from the access control check.
53
+ */
54
+ export interface ToolAccessDecision {
55
+ allowed: boolean;
56
+ reason?: string;
57
+ }
58
+ /**
59
+ * Tool Access Control Service
60
+ *
61
+ * Provides centralized access control for tool calls within CodeCall.
62
+ * Implements a layered security model:
63
+ *
64
+ * 1. Self-reference blocking (handled separately in self-reference-guard.ts)
65
+ * 2. Default blacklist (always enforced)
66
+ * 3. User-configured whitelist/blacklist
67
+ * 4. Pattern matching (glob patterns)
68
+ * 5. Dynamic evaluation (if configured)
69
+ */
70
+ export declare class ToolAccessControlService {
71
+ private readonly policy;
72
+ private readonly whitelistSet;
73
+ private readonly blacklistSet;
74
+ private readonly allowPatterns;
75
+ private readonly denyPatterns;
76
+ constructor(policy?: ToolAccessPolicy);
77
+ /**
78
+ * Check if a tool can be accessed.
79
+ *
80
+ * @param toolName - The tool being accessed
81
+ * @param context - Optional access context for dynamic evaluation
82
+ * @returns Decision indicating if access is allowed
83
+ */
84
+ checkAccess(toolName: string, context?: Partial<ToolAccessContext>): Promise<ToolAccessDecision>;
85
+ private checkWhitelistMode;
86
+ private checkBlacklistMode;
87
+ private checkDynamicMode;
88
+ /**
89
+ * Check if a tool name matches any entry in a set (supports glob patterns).
90
+ */
91
+ private isInSet;
92
+ /**
93
+ * Convert a glob pattern to a RegExp.
94
+ * Supports: * (any characters), ? (single character)
95
+ *
96
+ * Security: Prevents ReDoS by limiting pattern complexity and using non-greedy matching.
97
+ */
98
+ private globToRegex;
99
+ /**
100
+ * Get the current policy configuration (for debugging/testing).
101
+ */
102
+ getPolicy(): Readonly<ToolAccessPolicy>;
103
+ }
104
+ export default ToolAccessControlService;
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+ // file: libs/plugins/src/codecall/security/tool-access-control.service.ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.ToolAccessControlService = void 0;
5
+ const tslib_1 = require("tslib");
6
+ const sdk_1 = require("@frontmcp/sdk");
7
+ /**
8
+ * Default blacklist - tools that are always blocked regardless of configuration.
9
+ * These are security-sensitive and should never be accessible via CodeCall.
10
+ */
11
+ const DEFAULT_BLACKLIST = Object.freeze(new Set([
12
+ // Internal system tools
13
+ 'system:*',
14
+ 'internal:*',
15
+ '__*', // Any tool starting with __
16
+ ]));
17
+ /**
18
+ * Tool Access Control Service
19
+ *
20
+ * Provides centralized access control for tool calls within CodeCall.
21
+ * Implements a layered security model:
22
+ *
23
+ * 1. Self-reference blocking (handled separately in self-reference-guard.ts)
24
+ * 2. Default blacklist (always enforced)
25
+ * 3. User-configured whitelist/blacklist
26
+ * 4. Pattern matching (glob patterns)
27
+ * 5. Dynamic evaluation (if configured)
28
+ */
29
+ let ToolAccessControlService = class ToolAccessControlService {
30
+ policy;
31
+ whitelistSet;
32
+ blacklistSet;
33
+ allowPatterns;
34
+ denyPatterns;
35
+ constructor(policy) {
36
+ this.policy = policy || { mode: 'blacklist' };
37
+ // Pre-compile sets and patterns for fast lookup
38
+ this.whitelistSet = new Set(this.policy.whitelist || []);
39
+ this.blacklistSet = new Set([...(this.policy.blacklist || []), ...Array.from(DEFAULT_BLACKLIST)]);
40
+ this.allowPatterns = (this.policy.patterns?.allow || []).map(this.globToRegex);
41
+ this.denyPatterns = (this.policy.patterns?.deny || []).map(this.globToRegex);
42
+ }
43
+ /**
44
+ * Check if a tool can be accessed.
45
+ *
46
+ * @param toolName - The tool being accessed
47
+ * @param context - Optional access context for dynamic evaluation
48
+ * @returns Decision indicating if access is allowed
49
+ */
50
+ async checkAccess(toolName, context) {
51
+ const fullContext = {
52
+ toolName,
53
+ authInfo: context?.authInfo,
54
+ executionId: context?.executionId,
55
+ callDepth: context?.callDepth ?? 0,
56
+ timestamp: Date.now(),
57
+ };
58
+ // Step 1: Check deny patterns first (highest priority)
59
+ for (const pattern of this.denyPatterns) {
60
+ if (pattern.test(toolName)) {
61
+ return { allowed: false, reason: `Tool matches deny pattern` };
62
+ }
63
+ }
64
+ // Step 2: Check explicit blacklist
65
+ if (this.isInSet(toolName, this.blacklistSet)) {
66
+ return { allowed: false, reason: `Tool is explicitly blocked` };
67
+ }
68
+ // Step 3: Mode-specific checks
69
+ switch (this.policy.mode) {
70
+ case 'whitelist':
71
+ return this.checkWhitelistMode(toolName, fullContext);
72
+ case 'blacklist':
73
+ return this.checkBlacklistMode(toolName, fullContext);
74
+ case 'dynamic':
75
+ return this.checkDynamicMode(toolName, fullContext);
76
+ default:
77
+ // Default to deny for unknown modes (fail-secure)
78
+ return { allowed: false, reason: 'Unknown access control mode' };
79
+ }
80
+ }
81
+ async checkWhitelistMode(toolName, context) {
82
+ // In whitelist mode, tool must be explicitly allowed
83
+ if (this.isInSet(toolName, this.whitelistSet)) {
84
+ return { allowed: true };
85
+ }
86
+ // Check allow patterns
87
+ for (const pattern of this.allowPatterns) {
88
+ if (pattern.test(toolName)) {
89
+ return { allowed: true };
90
+ }
91
+ }
92
+ // Check dynamic evaluator as fallback
93
+ if (this.policy.evaluator) {
94
+ return this.policy.evaluator(context);
95
+ }
96
+ return { allowed: false, reason: 'Tool not in whitelist' };
97
+ }
98
+ async checkBlacklistMode(toolName, context) {
99
+ // In blacklist mode, tool is allowed unless blocked
100
+ // (blacklist already checked above)
101
+ // Check dynamic evaluator for additional restrictions
102
+ if (this.policy.evaluator) {
103
+ const dynamicDecision = await this.policy.evaluator(context);
104
+ if (!dynamicDecision.allowed) {
105
+ return dynamicDecision;
106
+ }
107
+ }
108
+ return { allowed: true };
109
+ }
110
+ async checkDynamicMode(toolName, context) {
111
+ if (!this.policy.evaluator) {
112
+ // No evaluator configured - fail-secure
113
+ return { allowed: false, reason: 'No dynamic evaluator configured' };
114
+ }
115
+ return this.policy.evaluator(context);
116
+ }
117
+ /**
118
+ * Check if a tool name matches any entry in a set (supports glob patterns).
119
+ */
120
+ isInSet(toolName, set) {
121
+ // Direct match
122
+ if (set.has(toolName)) {
123
+ return true;
124
+ }
125
+ // Check glob patterns in the set
126
+ for (const pattern of set) {
127
+ if (pattern.includes('*')) {
128
+ const regex = this.globToRegex(pattern);
129
+ if (regex.test(toolName)) {
130
+ return true;
131
+ }
132
+ }
133
+ }
134
+ return false;
135
+ }
136
+ /**
137
+ * Convert a glob pattern to a RegExp.
138
+ * Supports: * (any characters), ? (single character)
139
+ *
140
+ * Security: Prevents ReDoS by limiting pattern complexity and using non-greedy matching.
141
+ */
142
+ globToRegex(pattern) {
143
+ // Prevent ReDoS: limit pattern complexity
144
+ const wildcardCount = (pattern.match(/\*/g) || []).length;
145
+ if (wildcardCount > 3 || pattern.length > 100) {
146
+ throw new Error(`Pattern too complex: ${pattern}`);
147
+ }
148
+ const escaped = pattern
149
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&') // Escape special regex chars
150
+ .replace(/\*/g, '.*?') // * -> .*? (non-greedy to prevent backtracking)
151
+ .replace(/\?/g, '.'); // ? -> .
152
+ return new RegExp(`^${escaped}$`, 'i');
153
+ }
154
+ /**
155
+ * Get the current policy configuration (for debugging/testing).
156
+ */
157
+ getPolicy() {
158
+ return Object.freeze({ ...this.policy });
159
+ }
160
+ };
161
+ exports.ToolAccessControlService = ToolAccessControlService;
162
+ exports.ToolAccessControlService = ToolAccessControlService = tslib_1.__decorate([
163
+ (0, sdk_1.Provider)({
164
+ name: 'codecall:tool-access-control',
165
+ scope: sdk_1.ProviderScope.GLOBAL,
166
+ }),
167
+ tslib_1.__metadata("design:paramtypes", [Object])
168
+ ], ToolAccessControlService);
169
+ exports.default = ToolAccessControlService;
170
+ //# sourceMappingURL=tool-access-control.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-access-control.service.js","sourceRoot":"","sources":["../../../../src/codecall/security/tool-access-control.service.ts"],"names":[],"mappings":"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","sourcesContent":["// file: libs/plugins/src/codecall/security/tool-access-control.service.ts\n\nimport { Provider, ProviderScope } from '@frontmcp/sdk';\nimport type { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';\n\n/**\n * Tool access control modes:\n * - whitelist: Only explicitly allowed tools can be called (most secure)\n * - blacklist: All tools allowed except explicitly blocked (default, more flexible)\n * - dynamic: Evaluate access per-call based on custom function\n */\nexport type ToolAccessMode = 'whitelist' | 'blacklist' | 'dynamic';\n\n/**\n * Tool access policy configuration.\n */\nexport interface ToolAccessPolicy {\n /**\n * Access control mode.\n * @default 'blacklist'\n */\n mode: ToolAccessMode;\n\n /**\n * Tools explicitly allowed (for whitelist mode).\n * Supports exact names and glob patterns (e.g., 'users:*').\n */\n whitelist?: string[];\n\n /**\n * Tools explicitly blocked (for blacklist mode).\n * Supports exact names and glob patterns (e.g., 'admin:*').\n */\n blacklist?: string[];\n\n /**\n * Pattern-based rules that apply in addition to whitelist/blacklist.\n */\n patterns?: {\n allow?: string[];\n deny?: string[];\n };\n\n /**\n * Custom evaluator function for dynamic mode.\n * Called for each tool access request.\n */\n evaluator?: (context: ToolAccessContext) => Promise<ToolAccessDecision>;\n}\n\n/**\n * Context provided to the access evaluator.\n */\nexport interface ToolAccessContext {\n toolName: string;\n authInfo?: AuthInfo;\n executionId?: string;\n callDepth: number;\n timestamp: number;\n}\n\n/**\n * Decision from the access control check.\n */\nexport interface ToolAccessDecision {\n allowed: boolean;\n reason?: string;\n}\n\n/**\n * Default blacklist - tools that are always blocked regardless of configuration.\n * These are security-sensitive and should never be accessible via CodeCall.\n */\nconst DEFAULT_BLACKLIST: ReadonlySet<string> = Object.freeze(\n new Set([\n // Internal system tools\n 'system:*',\n 'internal:*',\n '__*', // Any tool starting with __\n ]),\n);\n\n/**\n * Tool Access Control Service\n *\n * Provides centralized access control for tool calls within CodeCall.\n * Implements a layered security model:\n *\n * 1. Self-reference blocking (handled separately in self-reference-guard.ts)\n * 2. Default blacklist (always enforced)\n * 3. User-configured whitelist/blacklist\n * 4. Pattern matching (glob patterns)\n * 5. Dynamic evaluation (if configured)\n */\n@Provider({\n name: 'codecall:tool-access-control',\n scope: ProviderScope.GLOBAL,\n})\nexport class ToolAccessControlService {\n private readonly policy: ToolAccessPolicy;\n private readonly whitelistSet: Set<string>;\n private readonly blacklistSet: Set<string>;\n private readonly allowPatterns: RegExp[];\n private readonly denyPatterns: RegExp[];\n\n constructor(policy?: ToolAccessPolicy) {\n this.policy = policy || { mode: 'blacklist' };\n\n // Pre-compile sets and patterns for fast lookup\n this.whitelistSet = new Set(this.policy.whitelist || []);\n this.blacklistSet = new Set([...(this.policy.blacklist || []), ...Array.from(DEFAULT_BLACKLIST)]);\n\n this.allowPatterns = (this.policy.patterns?.allow || []).map(this.globToRegex);\n this.denyPatterns = (this.policy.patterns?.deny || []).map(this.globToRegex);\n }\n\n /**\n * Check if a tool can be accessed.\n *\n * @param toolName - The tool being accessed\n * @param context - Optional access context for dynamic evaluation\n * @returns Decision indicating if access is allowed\n */\n async checkAccess(toolName: string, context?: Partial<ToolAccessContext>): Promise<ToolAccessDecision> {\n const fullContext: ToolAccessContext = {\n toolName,\n authInfo: context?.authInfo,\n executionId: context?.executionId,\n callDepth: context?.callDepth ?? 0,\n timestamp: Date.now(),\n };\n\n // Step 1: Check deny patterns first (highest priority)\n for (const pattern of this.denyPatterns) {\n if (pattern.test(toolName)) {\n return { allowed: false, reason: `Tool matches deny pattern` };\n }\n }\n\n // Step 2: Check explicit blacklist\n if (this.isInSet(toolName, this.blacklistSet)) {\n return { allowed: false, reason: `Tool is explicitly blocked` };\n }\n\n // Step 3: Mode-specific checks\n switch (this.policy.mode) {\n case 'whitelist':\n return this.checkWhitelistMode(toolName, fullContext);\n\n case 'blacklist':\n return this.checkBlacklistMode(toolName, fullContext);\n\n case 'dynamic':\n return this.checkDynamicMode(toolName, fullContext);\n\n default:\n // Default to deny for unknown modes (fail-secure)\n return { allowed: false, reason: 'Unknown access control mode' };\n }\n }\n\n private async checkWhitelistMode(toolName: string, context: ToolAccessContext): Promise<ToolAccessDecision> {\n // In whitelist mode, tool must be explicitly allowed\n if (this.isInSet(toolName, this.whitelistSet)) {\n return { allowed: true };\n }\n\n // Check allow patterns\n for (const pattern of this.allowPatterns) {\n if (pattern.test(toolName)) {\n return { allowed: true };\n }\n }\n\n // Check dynamic evaluator as fallback\n if (this.policy.evaluator) {\n return this.policy.evaluator(context);\n }\n\n return { allowed: false, reason: 'Tool not in whitelist' };\n }\n\n private async checkBlacklistMode(toolName: string, context: ToolAccessContext): Promise<ToolAccessDecision> {\n // In blacklist mode, tool is allowed unless blocked\n // (blacklist already checked above)\n\n // Check dynamic evaluator for additional restrictions\n if (this.policy.evaluator) {\n const dynamicDecision = await this.policy.evaluator(context);\n if (!dynamicDecision.allowed) {\n return dynamicDecision;\n }\n }\n\n return { allowed: true };\n }\n\n private async checkDynamicMode(toolName: string, context: ToolAccessContext): Promise<ToolAccessDecision> {\n if (!this.policy.evaluator) {\n // No evaluator configured - fail-secure\n return { allowed: false, reason: 'No dynamic evaluator configured' };\n }\n\n return this.policy.evaluator(context);\n }\n\n /**\n * Check if a tool name matches any entry in a set (supports glob patterns).\n */\n private isInSet(toolName: string, set: Set<string>): boolean {\n // Direct match\n if (set.has(toolName)) {\n return true;\n }\n\n // Check glob patterns in the set\n for (const pattern of set) {\n if (pattern.includes('*')) {\n const regex = this.globToRegex(pattern);\n if (regex.test(toolName)) {\n return true;\n }\n }\n }\n\n return false;\n }\n\n /**\n * Convert a glob pattern to a RegExp.\n * Supports: * (any characters), ? (single character)\n *\n * Security: Prevents ReDoS by limiting pattern complexity and using non-greedy matching.\n */\n private globToRegex(pattern: string): RegExp {\n // Prevent ReDoS: limit pattern complexity\n const wildcardCount = (pattern.match(/\\*/g) || []).length;\n if (wildcardCount > 3 || pattern.length > 100) {\n throw new Error(`Pattern too complex: ${pattern}`);\n }\n\n const escaped = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&') // Escape special regex chars\n .replace(/\\*/g, '.*?') // * -> .*? (non-greedy to prevent backtracking)\n .replace(/\\?/g, '.'); // ? -> .\n\n return new RegExp(`^${escaped}$`, 'i');\n }\n\n /**\n * Get the current policy configuration (for debugging/testing).\n */\n getPolicy(): Readonly<ToolAccessPolicy> {\n return Object.freeze({ ...this.policy });\n }\n}\n\nexport default ToolAccessControlService;\n"]}