@objectstack/core 0.8.2 → 0.9.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.
- package/API_REGISTRY.md +392 -0
- package/CHANGELOG.md +8 -0
- package/README.md +36 -0
- package/dist/api-registry-plugin.d.ts +54 -0
- package/dist/api-registry-plugin.d.ts.map +1 -0
- package/dist/api-registry-plugin.js +53 -0
- package/dist/api-registry-plugin.test.d.ts +2 -0
- package/dist/api-registry-plugin.test.d.ts.map +1 -0
- package/dist/api-registry-plugin.test.js +332 -0
- package/dist/api-registry.d.ts +259 -0
- package/dist/api-registry.d.ts.map +1 -0
- package/dist/api-registry.js +599 -0
- package/dist/api-registry.test.d.ts +2 -0
- package/dist/api-registry.test.d.ts.map +1 -0
- package/dist/api-registry.test.js +957 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/logger.d.ts +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +35 -11
- package/dist/plugin-loader.d.ts +3 -2
- package/dist/plugin-loader.d.ts.map +1 -1
- package/dist/plugin-loader.js +13 -11
- package/dist/qa/adapter.d.ts +14 -0
- package/dist/qa/adapter.d.ts.map +1 -0
- package/dist/qa/adapter.js +1 -0
- package/dist/qa/http-adapter.d.ts +16 -0
- package/dist/qa/http-adapter.d.ts.map +1 -0
- package/dist/qa/http-adapter.js +107 -0
- package/dist/qa/index.d.ts +4 -0
- package/dist/qa/index.d.ts.map +1 -0
- package/dist/qa/index.js +3 -0
- package/dist/qa/runner.d.ts +27 -0
- package/dist/qa/runner.d.ts.map +1 -0
- package/dist/qa/runner.js +157 -0
- package/dist/security/index.d.ts +14 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +13 -0
- package/dist/security/plugin-config-validator.d.ts +79 -0
- package/dist/security/plugin-config-validator.d.ts.map +1 -0
- package/dist/security/plugin-config-validator.js +166 -0
- package/dist/security/plugin-config-validator.test.d.ts +2 -0
- package/dist/security/plugin-config-validator.test.d.ts.map +1 -0
- package/dist/security/plugin-config-validator.test.js +223 -0
- package/dist/security/plugin-permission-enforcer.d.ts +154 -0
- package/dist/security/plugin-permission-enforcer.d.ts.map +1 -0
- package/dist/security/plugin-permission-enforcer.js +323 -0
- package/dist/security/plugin-permission-enforcer.test.d.ts +2 -0
- package/dist/security/plugin-permission-enforcer.test.d.ts.map +1 -0
- package/dist/security/plugin-permission-enforcer.test.js +205 -0
- package/dist/security/plugin-signature-verifier.d.ts +96 -0
- package/dist/security/plugin-signature-verifier.d.ts.map +1 -0
- package/dist/security/plugin-signature-verifier.js +250 -0
- package/examples/api-registry-example.ts +557 -0
- package/package.json +2 -2
- package/src/api-registry-plugin.test.ts +391 -0
- package/src/api-registry-plugin.ts +86 -0
- package/src/api-registry.test.ts +1089 -0
- package/src/api-registry.ts +736 -0
- package/src/index.ts +6 -0
- package/src/logger.ts +36 -11
- package/src/plugin-loader.ts +17 -13
- package/src/qa/adapter.ts +14 -0
- package/src/qa/http-adapter.ts +114 -0
- package/src/qa/index.ts +3 -0
- package/src/qa/runner.ts +179 -0
- package/src/security/index.ts +29 -0
- package/src/security/plugin-config-validator.test.ts +276 -0
- package/src/security/plugin-config-validator.ts +191 -0
- package/src/security/plugin-permission-enforcer.test.ts +251 -0
- package/src/security/plugin-permission-enforcer.ts +408 -0
- package/src/security/plugin-signature-verifier.ts +359 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Plugin Configuration Validator
|
|
4
|
+
*
|
|
5
|
+
* Validates plugin configurations against Zod schemas to ensure:
|
|
6
|
+
* 1. Type safety - all config values have correct types
|
|
7
|
+
* 2. Business rules - values meet constraints (min/max, regex, etc.)
|
|
8
|
+
* 3. Required fields - all mandatory configuration is provided
|
|
9
|
+
* 4. Default values - missing optional fields get defaults
|
|
10
|
+
*
|
|
11
|
+
* Architecture:
|
|
12
|
+
* - Uses Zod for runtime validation
|
|
13
|
+
* - Provides detailed error messages with field paths
|
|
14
|
+
* - Supports nested configuration objects
|
|
15
|
+
* - Allows partial validation for incremental updates
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const validator = new PluginConfigValidator(logger);
|
|
20
|
+
* const validConfig = validator.validatePluginConfig(plugin, userConfig);
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export class PluginConfigValidator {
|
|
24
|
+
constructor(logger) {
|
|
25
|
+
this.logger = logger;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Validate plugin configuration against its Zod schema
|
|
29
|
+
*
|
|
30
|
+
* @param plugin - Plugin metadata with configSchema
|
|
31
|
+
* @param config - User-provided configuration
|
|
32
|
+
* @returns Validated and typed configuration
|
|
33
|
+
* @throws Error with detailed validation errors
|
|
34
|
+
*/
|
|
35
|
+
validatePluginConfig(plugin, config) {
|
|
36
|
+
if (!plugin.configSchema) {
|
|
37
|
+
this.logger.debug(`Plugin ${plugin.name} has no config schema - skipping validation`);
|
|
38
|
+
return config;
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
// Use Zod to parse and validate
|
|
42
|
+
const validatedConfig = plugin.configSchema.parse(config);
|
|
43
|
+
this.logger.debug(`✅ Plugin config validated: ${plugin.name}`, {
|
|
44
|
+
plugin: plugin.name,
|
|
45
|
+
configKeys: Object.keys(config || {}).length,
|
|
46
|
+
});
|
|
47
|
+
return validatedConfig;
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
if (error instanceof z.ZodError) {
|
|
51
|
+
const formattedErrors = this.formatZodErrors(error);
|
|
52
|
+
const errorMessage = [
|
|
53
|
+
`Plugin ${plugin.name} configuration validation failed:`,
|
|
54
|
+
...formattedErrors.map(e => ` - ${e.path}: ${e.message}`),
|
|
55
|
+
].join('\n');
|
|
56
|
+
this.logger.error(errorMessage, undefined, {
|
|
57
|
+
plugin: plugin.name,
|
|
58
|
+
errors: formattedErrors,
|
|
59
|
+
});
|
|
60
|
+
throw new Error(errorMessage);
|
|
61
|
+
}
|
|
62
|
+
// Re-throw other errors
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Validate partial configuration (for incremental updates)
|
|
68
|
+
*
|
|
69
|
+
* @param plugin - Plugin metadata
|
|
70
|
+
* @param partialConfig - Partial configuration to validate
|
|
71
|
+
* @returns Validated partial configuration
|
|
72
|
+
*/
|
|
73
|
+
validatePartialConfig(plugin, partialConfig) {
|
|
74
|
+
if (!plugin.configSchema) {
|
|
75
|
+
return partialConfig;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
// Use Zod's partial() method for partial validation
|
|
79
|
+
// Cast to ZodObject to access partial() method
|
|
80
|
+
const partialSchema = plugin.configSchema.partial();
|
|
81
|
+
const validatedConfig = partialSchema.parse(partialConfig);
|
|
82
|
+
this.logger.debug(`✅ Partial config validated: ${plugin.name}`);
|
|
83
|
+
return validatedConfig;
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
if (error instanceof z.ZodError) {
|
|
87
|
+
const formattedErrors = this.formatZodErrors(error);
|
|
88
|
+
const errorMessage = [
|
|
89
|
+
`Plugin ${plugin.name} partial configuration validation failed:`,
|
|
90
|
+
...formattedErrors.map(e => ` - ${e.path}: ${e.message}`),
|
|
91
|
+
].join('\n');
|
|
92
|
+
throw new Error(errorMessage);
|
|
93
|
+
}
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Get default configuration from schema
|
|
99
|
+
*
|
|
100
|
+
* @param plugin - Plugin metadata
|
|
101
|
+
* @returns Default configuration object
|
|
102
|
+
*/
|
|
103
|
+
getDefaultConfig(plugin) {
|
|
104
|
+
if (!plugin.configSchema) {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
// Parse empty object to get defaults
|
|
109
|
+
const defaults = plugin.configSchema.parse({});
|
|
110
|
+
this.logger.debug(`Default config extracted: ${plugin.name}`);
|
|
111
|
+
return defaults;
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
// Schema may require some fields - return undefined
|
|
115
|
+
this.logger.debug(`No default config available: ${plugin.name}`);
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Check if configuration is valid without throwing
|
|
121
|
+
*
|
|
122
|
+
* @param plugin - Plugin metadata
|
|
123
|
+
* @param config - Configuration to check
|
|
124
|
+
* @returns True if valid, false otherwise
|
|
125
|
+
*/
|
|
126
|
+
isConfigValid(plugin, config) {
|
|
127
|
+
if (!plugin.configSchema) {
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
const result = plugin.configSchema.safeParse(config);
|
|
131
|
+
return result.success;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Get configuration errors without throwing
|
|
135
|
+
*
|
|
136
|
+
* @param plugin - Plugin metadata
|
|
137
|
+
* @param config - Configuration to check
|
|
138
|
+
* @returns Array of validation errors, or empty array if valid
|
|
139
|
+
*/
|
|
140
|
+
getConfigErrors(plugin, config) {
|
|
141
|
+
if (!plugin.configSchema) {
|
|
142
|
+
return [];
|
|
143
|
+
}
|
|
144
|
+
const result = plugin.configSchema.safeParse(config);
|
|
145
|
+
if (result.success) {
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
148
|
+
return this.formatZodErrors(result.error);
|
|
149
|
+
}
|
|
150
|
+
// Private methods
|
|
151
|
+
formatZodErrors(error) {
|
|
152
|
+
return error.issues.map((e) => ({
|
|
153
|
+
path: e.path.join('.') || 'root',
|
|
154
|
+
message: e.message,
|
|
155
|
+
}));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Create a plugin config validator
|
|
160
|
+
*
|
|
161
|
+
* @param logger - Logger instance
|
|
162
|
+
* @returns Plugin config validator
|
|
163
|
+
*/
|
|
164
|
+
export function createPluginConfigValidator(logger) {
|
|
165
|
+
return new PluginConfigValidator(logger);
|
|
166
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-config-validator.test.d.ts","sourceRoot":"","sources":["../../src/security/plugin-config-validator.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { PluginConfigValidator } from './plugin-config-validator.js';
|
|
4
|
+
import { createLogger } from '../logger.js';
|
|
5
|
+
describe('PluginConfigValidator', () => {
|
|
6
|
+
let validator;
|
|
7
|
+
let logger;
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
logger = createLogger({ level: 'error' });
|
|
10
|
+
validator = new PluginConfigValidator(logger);
|
|
11
|
+
});
|
|
12
|
+
describe('validatePluginConfig', () => {
|
|
13
|
+
it('should validate valid configuration', () => {
|
|
14
|
+
const configSchema = z.object({
|
|
15
|
+
port: z.number().min(1000).max(65535),
|
|
16
|
+
host: z.string(),
|
|
17
|
+
debug: z.boolean().default(false),
|
|
18
|
+
});
|
|
19
|
+
const plugin = {
|
|
20
|
+
name: 'com.test.plugin',
|
|
21
|
+
version: '1.0.0',
|
|
22
|
+
configSchema,
|
|
23
|
+
init: async () => { },
|
|
24
|
+
};
|
|
25
|
+
const config = {
|
|
26
|
+
port: 3000,
|
|
27
|
+
host: 'localhost',
|
|
28
|
+
debug: true,
|
|
29
|
+
};
|
|
30
|
+
const validatedConfig = validator.validatePluginConfig(plugin, config);
|
|
31
|
+
expect(validatedConfig).toEqual(config);
|
|
32
|
+
});
|
|
33
|
+
it('should apply defaults for missing optional fields', () => {
|
|
34
|
+
const configSchema = z.object({
|
|
35
|
+
port: z.number().default(3000),
|
|
36
|
+
host: z.string().default('localhost'),
|
|
37
|
+
debug: z.boolean().default(false),
|
|
38
|
+
});
|
|
39
|
+
const plugin = {
|
|
40
|
+
name: 'com.test.plugin',
|
|
41
|
+
version: '1.0.0',
|
|
42
|
+
configSchema,
|
|
43
|
+
init: async () => { },
|
|
44
|
+
};
|
|
45
|
+
const config = {
|
|
46
|
+
port: 8080,
|
|
47
|
+
};
|
|
48
|
+
const validatedConfig = validator.validatePluginConfig(plugin, config);
|
|
49
|
+
expect(validatedConfig).toEqual({
|
|
50
|
+
port: 8080,
|
|
51
|
+
host: 'localhost',
|
|
52
|
+
debug: false,
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
it('should throw error for invalid configuration', () => {
|
|
56
|
+
const configSchema = z.object({
|
|
57
|
+
port: z.number().min(1000).max(65535),
|
|
58
|
+
host: z.string(),
|
|
59
|
+
});
|
|
60
|
+
const plugin = {
|
|
61
|
+
name: 'com.test.plugin',
|
|
62
|
+
version: '1.0.0',
|
|
63
|
+
configSchema,
|
|
64
|
+
init: async () => { },
|
|
65
|
+
};
|
|
66
|
+
const config = {
|
|
67
|
+
port: 100, // Invalid: < 1000
|
|
68
|
+
host: 'localhost',
|
|
69
|
+
};
|
|
70
|
+
expect(() => validator.validatePluginConfig(plugin, config)).toThrow();
|
|
71
|
+
});
|
|
72
|
+
it('should provide detailed error messages', () => {
|
|
73
|
+
const configSchema = z.object({
|
|
74
|
+
port: z.number().min(1000),
|
|
75
|
+
host: z.string().min(1),
|
|
76
|
+
});
|
|
77
|
+
const plugin = {
|
|
78
|
+
name: 'com.test.plugin',
|
|
79
|
+
version: '1.0.0',
|
|
80
|
+
configSchema,
|
|
81
|
+
init: async () => { },
|
|
82
|
+
};
|
|
83
|
+
const config = {
|
|
84
|
+
port: 100,
|
|
85
|
+
host: '',
|
|
86
|
+
};
|
|
87
|
+
try {
|
|
88
|
+
validator.validatePluginConfig(plugin, config);
|
|
89
|
+
expect.fail('Should have thrown validation error');
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
const errorMessage = error.message;
|
|
93
|
+
expect(errorMessage).toContain('com.test.plugin');
|
|
94
|
+
expect(errorMessage).toContain('port');
|
|
95
|
+
expect(errorMessage).toContain('host');
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
it('should skip validation when no schema is provided', () => {
|
|
99
|
+
const plugin = {
|
|
100
|
+
name: 'com.test.plugin',
|
|
101
|
+
version: '1.0.0',
|
|
102
|
+
init: async () => { },
|
|
103
|
+
};
|
|
104
|
+
const config = { anything: 'goes' };
|
|
105
|
+
const validatedConfig = validator.validatePluginConfig(plugin, config);
|
|
106
|
+
expect(validatedConfig).toEqual(config);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
describe('validatePartialConfig', () => {
|
|
110
|
+
it('should validate partial configuration', () => {
|
|
111
|
+
const configSchema = z.object({
|
|
112
|
+
port: z.number().min(1000),
|
|
113
|
+
host: z.string(),
|
|
114
|
+
debug: z.boolean(),
|
|
115
|
+
});
|
|
116
|
+
const plugin = {
|
|
117
|
+
name: 'com.test.plugin',
|
|
118
|
+
version: '1.0.0',
|
|
119
|
+
configSchema,
|
|
120
|
+
init: async () => { },
|
|
121
|
+
};
|
|
122
|
+
const partialConfig = {
|
|
123
|
+
port: 8080,
|
|
124
|
+
};
|
|
125
|
+
const validatedConfig = validator.validatePartialConfig(plugin, partialConfig);
|
|
126
|
+
expect(validatedConfig).toEqual({ port: 8080 });
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
describe('getDefaultConfig', () => {
|
|
130
|
+
it('should extract default configuration', () => {
|
|
131
|
+
const configSchema = z.object({
|
|
132
|
+
port: z.number().default(3000),
|
|
133
|
+
host: z.string().default('localhost'),
|
|
134
|
+
debug: z.boolean().default(false),
|
|
135
|
+
});
|
|
136
|
+
const plugin = {
|
|
137
|
+
name: 'com.test.plugin',
|
|
138
|
+
version: '1.0.0',
|
|
139
|
+
configSchema,
|
|
140
|
+
init: async () => { },
|
|
141
|
+
};
|
|
142
|
+
const defaults = validator.getDefaultConfig(plugin);
|
|
143
|
+
expect(defaults).toEqual({
|
|
144
|
+
port: 3000,
|
|
145
|
+
host: 'localhost',
|
|
146
|
+
debug: false,
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
it('should return undefined when schema requires fields', () => {
|
|
150
|
+
const configSchema = z.object({
|
|
151
|
+
port: z.number(),
|
|
152
|
+
host: z.string(),
|
|
153
|
+
});
|
|
154
|
+
const plugin = {
|
|
155
|
+
name: 'com.test.plugin',
|
|
156
|
+
version: '1.0.0',
|
|
157
|
+
configSchema,
|
|
158
|
+
init: async () => { },
|
|
159
|
+
};
|
|
160
|
+
const defaults = validator.getDefaultConfig(plugin);
|
|
161
|
+
expect(defaults).toBeUndefined();
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
describe('isConfigValid', () => {
|
|
165
|
+
it('should return true for valid config', () => {
|
|
166
|
+
const configSchema = z.object({
|
|
167
|
+
port: z.number(),
|
|
168
|
+
});
|
|
169
|
+
const plugin = {
|
|
170
|
+
name: 'com.test.plugin',
|
|
171
|
+
version: '1.0.0',
|
|
172
|
+
configSchema,
|
|
173
|
+
init: async () => { },
|
|
174
|
+
};
|
|
175
|
+
const isValid = validator.isConfigValid(plugin, { port: 3000 });
|
|
176
|
+
expect(isValid).toBe(true);
|
|
177
|
+
});
|
|
178
|
+
it('should return false for invalid config', () => {
|
|
179
|
+
const configSchema = z.object({
|
|
180
|
+
port: z.number(),
|
|
181
|
+
});
|
|
182
|
+
const plugin = {
|
|
183
|
+
name: 'com.test.plugin',
|
|
184
|
+
version: '1.0.0',
|
|
185
|
+
configSchema,
|
|
186
|
+
init: async () => { },
|
|
187
|
+
};
|
|
188
|
+
const isValid = validator.isConfigValid(plugin, { port: 'invalid' });
|
|
189
|
+
expect(isValid).toBe(false);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
describe('getConfigErrors', () => {
|
|
193
|
+
it('should return errors for invalid config', () => {
|
|
194
|
+
const configSchema = z.object({
|
|
195
|
+
port: z.number().min(1000),
|
|
196
|
+
host: z.string().min(1),
|
|
197
|
+
});
|
|
198
|
+
const plugin = {
|
|
199
|
+
name: 'com.test.plugin',
|
|
200
|
+
version: '1.0.0',
|
|
201
|
+
configSchema,
|
|
202
|
+
init: async () => { },
|
|
203
|
+
};
|
|
204
|
+
const errors = validator.getConfigErrors(plugin, { port: 100, host: '' });
|
|
205
|
+
expect(errors).toHaveLength(2);
|
|
206
|
+
expect(errors[0].path).toBe('port');
|
|
207
|
+
expect(errors[1].path).toBe('host');
|
|
208
|
+
});
|
|
209
|
+
it('should return empty array for valid config', () => {
|
|
210
|
+
const configSchema = z.object({
|
|
211
|
+
port: z.number(),
|
|
212
|
+
});
|
|
213
|
+
const plugin = {
|
|
214
|
+
name: 'com.test.plugin',
|
|
215
|
+
version: '1.0.0',
|
|
216
|
+
configSchema,
|
|
217
|
+
init: async () => { },
|
|
218
|
+
};
|
|
219
|
+
const errors = validator.getConfigErrors(plugin, { port: 3000 });
|
|
220
|
+
expect(errors).toEqual([]);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
});
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import type { Logger } from '@objectstack/spec/contracts';
|
|
2
|
+
import type { PluginCapability } from '@objectstack/spec/system';
|
|
3
|
+
import type { PluginContext } from '../types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Plugin Permissions
|
|
6
|
+
* Defines what actions a plugin is allowed to perform
|
|
7
|
+
*/
|
|
8
|
+
export interface PluginPermissions {
|
|
9
|
+
canAccessService(serviceName: string): boolean;
|
|
10
|
+
canTriggerHook(hookName: string): boolean;
|
|
11
|
+
canReadFile(path: string): boolean;
|
|
12
|
+
canWriteFile(path: string): boolean;
|
|
13
|
+
canNetworkRequest(url: string): boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Permission Check Result
|
|
17
|
+
*/
|
|
18
|
+
export interface PermissionCheckResult {
|
|
19
|
+
allowed: boolean;
|
|
20
|
+
reason?: string;
|
|
21
|
+
capability?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Plugin Permission Enforcer
|
|
25
|
+
*
|
|
26
|
+
* Implements capability-based security model to enforce:
|
|
27
|
+
* 1. Service access control - which services a plugin can use
|
|
28
|
+
* 2. Hook restrictions - which hooks a plugin can trigger
|
|
29
|
+
* 3. File system permissions - what files a plugin can read/write
|
|
30
|
+
* 4. Network permissions - what URLs a plugin can access
|
|
31
|
+
*
|
|
32
|
+
* Architecture:
|
|
33
|
+
* - Uses capability declarations from plugin manifest
|
|
34
|
+
* - Checks permissions before allowing operations
|
|
35
|
+
* - Logs all permission denials for security audit
|
|
36
|
+
* - Supports allowlist and denylist patterns
|
|
37
|
+
*
|
|
38
|
+
* Security Model:
|
|
39
|
+
* - Principle of least privilege - plugins get minimal permissions
|
|
40
|
+
* - Explicit declaration - all capabilities must be declared
|
|
41
|
+
* - Runtime enforcement - checks happen at operation time
|
|
42
|
+
* - Audit trail - all denials are logged
|
|
43
|
+
*
|
|
44
|
+
* Usage:
|
|
45
|
+
* ```typescript
|
|
46
|
+
* const enforcer = new PluginPermissionEnforcer(logger);
|
|
47
|
+
* enforcer.registerPluginPermissions(pluginName, capabilities);
|
|
48
|
+
* enforcer.enforceServiceAccess(pluginName, 'database');
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare class PluginPermissionEnforcer {
|
|
52
|
+
private logger;
|
|
53
|
+
private permissionRegistry;
|
|
54
|
+
private capabilityRegistry;
|
|
55
|
+
constructor(logger: Logger);
|
|
56
|
+
/**
|
|
57
|
+
* Register plugin capabilities and build permission set
|
|
58
|
+
*
|
|
59
|
+
* @param pluginName - Plugin identifier
|
|
60
|
+
* @param capabilities - Array of capability declarations
|
|
61
|
+
*/
|
|
62
|
+
registerPluginPermissions(pluginName: string, capabilities: PluginCapability[]): void;
|
|
63
|
+
/**
|
|
64
|
+
* Enforce service access permission
|
|
65
|
+
*
|
|
66
|
+
* @param pluginName - Plugin requesting access
|
|
67
|
+
* @param serviceName - Service to access
|
|
68
|
+
* @throws Error if permission denied
|
|
69
|
+
*/
|
|
70
|
+
enforceServiceAccess(pluginName: string, serviceName: string): void;
|
|
71
|
+
/**
|
|
72
|
+
* Enforce hook trigger permission
|
|
73
|
+
*
|
|
74
|
+
* @param pluginName - Plugin requesting access
|
|
75
|
+
* @param hookName - Hook to trigger
|
|
76
|
+
* @throws Error if permission denied
|
|
77
|
+
*/
|
|
78
|
+
enforceHookTrigger(pluginName: string, hookName: string): void;
|
|
79
|
+
/**
|
|
80
|
+
* Enforce file read permission
|
|
81
|
+
*
|
|
82
|
+
* @param pluginName - Plugin requesting access
|
|
83
|
+
* @param path - File path to read
|
|
84
|
+
* @throws Error if permission denied
|
|
85
|
+
*/
|
|
86
|
+
enforceFileRead(pluginName: string, path: string): void;
|
|
87
|
+
/**
|
|
88
|
+
* Enforce file write permission
|
|
89
|
+
*
|
|
90
|
+
* @param pluginName - Plugin requesting access
|
|
91
|
+
* @param path - File path to write
|
|
92
|
+
* @throws Error if permission denied
|
|
93
|
+
*/
|
|
94
|
+
enforceFileWrite(pluginName: string, path: string): void;
|
|
95
|
+
/**
|
|
96
|
+
* Enforce network request permission
|
|
97
|
+
*
|
|
98
|
+
* @param pluginName - Plugin requesting access
|
|
99
|
+
* @param url - URL to access
|
|
100
|
+
* @throws Error if permission denied
|
|
101
|
+
*/
|
|
102
|
+
enforceNetworkRequest(pluginName: string, url: string): void;
|
|
103
|
+
/**
|
|
104
|
+
* Get plugin capabilities
|
|
105
|
+
*
|
|
106
|
+
* @param pluginName - Plugin identifier
|
|
107
|
+
* @returns Array of capabilities or undefined
|
|
108
|
+
*/
|
|
109
|
+
getPluginCapabilities(pluginName: string): PluginCapability[] | undefined;
|
|
110
|
+
/**
|
|
111
|
+
* Get plugin permissions
|
|
112
|
+
*
|
|
113
|
+
* @param pluginName - Plugin identifier
|
|
114
|
+
* @returns Permissions object or undefined
|
|
115
|
+
*/
|
|
116
|
+
getPluginPermissions(pluginName: string): PluginPermissions | undefined;
|
|
117
|
+
/**
|
|
118
|
+
* Revoke all permissions for a plugin
|
|
119
|
+
*
|
|
120
|
+
* @param pluginName - Plugin identifier
|
|
121
|
+
*/
|
|
122
|
+
revokePermissions(pluginName: string): void;
|
|
123
|
+
private checkPermission;
|
|
124
|
+
private checkServiceAccess;
|
|
125
|
+
private checkHookAccess;
|
|
126
|
+
private checkFileRead;
|
|
127
|
+
private checkFileWrite;
|
|
128
|
+
private checkNetworkAccess;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Secure Plugin Context
|
|
132
|
+
* Wraps PluginContext with permission checks
|
|
133
|
+
*/
|
|
134
|
+
export declare class SecurePluginContext implements PluginContext {
|
|
135
|
+
private pluginName;
|
|
136
|
+
private permissionEnforcer;
|
|
137
|
+
private baseContext;
|
|
138
|
+
constructor(pluginName: string, permissionEnforcer: PluginPermissionEnforcer, baseContext: PluginContext);
|
|
139
|
+
registerService(name: string, service: any): void;
|
|
140
|
+
getService<T>(name: string): T;
|
|
141
|
+
getServices(): Map<string, any>;
|
|
142
|
+
hook(name: string, handler: (...args: any[]) => void | Promise<void>): void;
|
|
143
|
+
trigger(name: string, ...args: any[]): Promise<void>;
|
|
144
|
+
get logger(): Logger;
|
|
145
|
+
getKernel(): import("../kernel.js").ObjectKernel;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Create a plugin permission enforcer
|
|
149
|
+
*
|
|
150
|
+
* @param logger - Logger instance
|
|
151
|
+
* @returns Plugin permission enforcer
|
|
152
|
+
*/
|
|
153
|
+
export declare function createPluginPermissionEnforcer(logger: Logger): PluginPermissionEnforcer;
|
|
154
|
+
//# sourceMappingURL=plugin-permission-enforcer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-permission-enforcer.d.ts","sourceRoot":"","sources":["../../src/security/plugin-permission-enforcer.ts"],"names":[],"mappings":"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"}
|