@secfn/core 0.0.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 (64) hide show
  1. package/README.md +334 -0
  2. package/dist/access-control/rbac.d.ts +48 -0
  3. package/dist/access-control/rbac.d.ts.map +1 -0
  4. package/dist/access-control/rbac.js +203 -0
  5. package/dist/access-control/rbac.js.map +1 -0
  6. package/dist/core/secfn.d.ts +28 -0
  7. package/dist/core/secfn.d.ts.map +1 -0
  8. package/dist/core/secfn.js +73 -0
  9. package/dist/core/secfn.js.map +1 -0
  10. package/dist/index.d.ts +12 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +12 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/monitoring/security-monitor.d.ts +46 -0
  15. package/dist/monitoring/security-monitor.d.ts.map +1 -0
  16. package/dist/monitoring/security-monitor.js +172 -0
  17. package/dist/monitoring/security-monitor.js.map +1 -0
  18. package/dist/rate-limiting/rate-limiter.d.ts +31 -0
  19. package/dist/rate-limiting/rate-limiter.d.ts.map +1 -0
  20. package/dist/rate-limiting/rate-limiter.js +140 -0
  21. package/dist/rate-limiting/rate-limiter.js.map +1 -0
  22. package/dist/scanning/patterns.d.ts +3 -0
  23. package/dist/scanning/patterns.d.ts.map +1 -0
  24. package/dist/scanning/patterns.js +139 -0
  25. package/dist/scanning/patterns.js.map +1 -0
  26. package/dist/scanning/scanner.d.ts +36 -0
  27. package/dist/scanning/scanner.d.ts.map +1 -0
  28. package/dist/scanning/scanner.js +152 -0
  29. package/dist/scanning/scanner.js.map +1 -0
  30. package/dist/secrets/encryption.d.ts +17 -0
  31. package/dist/secrets/encryption.d.ts.map +1 -0
  32. package/dist/secrets/encryption.js +47 -0
  33. package/dist/secrets/encryption.js.map +1 -0
  34. package/dist/secrets/vault.d.ts +43 -0
  35. package/dist/secrets/vault.d.ts.map +1 -0
  36. package/dist/secrets/vault.js +126 -0
  37. package/dist/secrets/vault.js.map +1 -0
  38. package/dist/storage/indexeddb-storage.d.ts +37 -0
  39. package/dist/storage/indexeddb-storage.d.ts.map +1 -0
  40. package/dist/storage/indexeddb-storage.js +284 -0
  41. package/dist/storage/indexeddb-storage.js.map +1 -0
  42. package/dist/types/index.d.ts +189 -0
  43. package/dist/types/index.d.ts.map +1 -0
  44. package/dist/types/index.js +2 -0
  45. package/dist/types/index.js.map +1 -0
  46. package/dist/utils/helpers.d.ts +8 -0
  47. package/dist/utils/helpers.d.ts.map +1 -0
  48. package/dist/utils/helpers.js +73 -0
  49. package/dist/utils/helpers.js.map +1 -0
  50. package/examples/basic-usage.ts +143 -0
  51. package/package.json +68 -0
  52. package/src/access-control/rbac.ts +271 -0
  53. package/src/core/secfn.ts +90 -0
  54. package/src/index.ts +27 -0
  55. package/src/monitoring/security-monitor.ts +245 -0
  56. package/src/rate-limiting/rate-limiter.ts +181 -0
  57. package/src/scanning/patterns.ts +140 -0
  58. package/src/scanning/scanner.ts +199 -0
  59. package/src/secrets/encryption.ts +74 -0
  60. package/src/secrets/vault.ts +187 -0
  61. package/src/storage/indexeddb-storage.ts +355 -0
  62. package/src/types/index.ts +213 -0
  63. package/src/utils/helpers.ts +84 -0
  64. package/tsconfig.json +26 -0
package/README.md ADDED
@@ -0,0 +1,334 @@
1
+ # SecFn
2
+
3
+ > Comprehensive self-hosted security platform for developers
4
+
5
+ SecFn is a developer-first security solution that combines vulnerability scanning, access control, secrets management, and security monitoring into a single self-hosted platform.
6
+
7
+ ## Features
8
+
9
+ - 🔐 **Secrets Vault** - AES-256-GCM encrypted secret storage with rotation and access logging
10
+ - 🔍 **Secret Scanning** - Pattern-based detection of 20+ secret types in code and git history
11
+ - 🛡️ **Access Control** - Role-based permissions (RBAC) with caching and inheritance
12
+ - ⏱️ **Rate Limiting** - Token bucket algorithm with multi-level limits
13
+ - 📊 **Security Monitoring** - Event logging, metrics, and anomaly detection
14
+ - 🗄️ **Self-Hosted** - IndexedDB storage, no external dependencies
15
+ - 🚀 **Developer-Friendly** - Simple API, TypeScript support, works locally
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install secfn
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```typescript
26
+ import { createSecFn } from 'secfn';
27
+
28
+ const secFn = createSecFn({
29
+ namespace: 'my-app',
30
+ masterKey: process.env.MASTER_KEY,
31
+ });
32
+
33
+ await secFn.initialize();
34
+
35
+ const vault = secFn.createSecretsVault();
36
+ await vault.set('api_key', 'secret-value', {
37
+ tags: ['production'],
38
+ environment: 'production',
39
+ });
40
+
41
+ const secret = await vault.get('api_key');
42
+ console.log('Retrieved:', secret);
43
+
44
+ await secFn.destroy();
45
+ ```
46
+
47
+ ## Core Components
48
+
49
+ ### 1. Secrets Vault
50
+
51
+ Encrypted storage and management of application secrets.
52
+
53
+ ```typescript
54
+ const vault = secFn.createSecretsVault();
55
+
56
+ await vault.set('stripe_api_key', 'sk_live_...', {
57
+ tags: ['payment', 'production'],
58
+ environment: 'production',
59
+ expiresAt: Date.now() + 90 * 24 * 60 * 60 * 1000,
60
+ rotateEvery: 90 * 24 * 60 * 60 * 1000,
61
+ });
62
+
63
+ const key = await vault.get('stripe_api_key');
64
+
65
+ const secrets = await vault.list({ environment: 'production' });
66
+
67
+ await vault.rotate('stripe_api_key', 'sk_live_new...');
68
+
69
+ const accessLog = await vault.getAccessLog('stripe_api_key', 100);
70
+ ```
71
+
72
+ ### 2. Secret Scanner
73
+
74
+ Detect hardcoded secrets in source code with 20+ built-in patterns.
75
+
76
+ ```typescript
77
+ const scanner = secFn.createSecretScanner({
78
+ excludePaths: ['node_modules/**', 'dist/**'],
79
+ minEntropy: 3.5,
80
+ });
81
+
82
+ const results = await scanner.scanDirectory('./src');
83
+
84
+ results.forEach(result => {
85
+ console.log(`${result.severity}: ${result.pattern} in ${result.file}:${result.line}`);
86
+ console.log(` Match: ${result.redactedMatch}`);
87
+ });
88
+
89
+ const gitResults = await scanner.scanGitHistory({
90
+ branch: 'main',
91
+ commits: 100,
92
+ });
93
+
94
+ scanner.addPattern({
95
+ name: 'Custom Secret',
96
+ pattern: /custom_secret_[a-zA-Z0-9]{32}/g,
97
+ entropy: 4.0,
98
+ description: 'Custom application secret',
99
+ severity: 'high',
100
+ });
101
+ ```
102
+
103
+ **Built-in Secret Patterns:**
104
+ - AWS Access Keys, Secret Keys
105
+ - GitHub Tokens
106
+ - Stripe API Keys
107
+ - OpenAI & Anthropic API Keys
108
+ - Google API Keys
109
+ - Slack Tokens
110
+ - NPM Tokens
111
+ - JWT Tokens
112
+ - Database Connection Strings
113
+ - Private Keys (RSA, OpenSSH, DSA, EC, PGP)
114
+ - And many more...
115
+
116
+ ### 3. Access Control (RBAC)
117
+
118
+ Fine-grained role-based access control with permission caching.
119
+
120
+ ```typescript
121
+ const access = secFn.createAccessControl();
122
+
123
+ await access.createRole({
124
+ name: 'editor',
125
+ description: 'Can edit projects and files',
126
+ permissions: ['project:read', 'project:write', 'file:read', 'file:write'],
127
+ });
128
+
129
+ await access.createRole({
130
+ name: 'admin',
131
+ description: 'Full system access',
132
+ permissions: ['*:*'],
133
+ });
134
+
135
+ await access.assignRole('user_123', 'editor', {
136
+ resourceIds: ['project_abc', 'project_def'],
137
+ });
138
+
139
+ const allowed = await access.check({
140
+ userId: 'user_123',
141
+ action: 'project:write',
142
+ resourceId: 'project_abc',
143
+ });
144
+
145
+ const permissions = await access.getUserPermissions('user_123');
146
+ ```
147
+
148
+ ### 4. Rate Limiting
149
+
150
+ Protect against brute force and API abuse with token bucket algorithm.
151
+
152
+ ```typescript
153
+ const limiter = secFn.createRateLimiter({
154
+ rules: {
155
+ global: { requests: 1000, window: 60000 },
156
+ perUser: { requests: 100, window: 60000 },
157
+ perIP: { requests: 50, window: 60000, blockDuration: 300000 },
158
+ perEndpoint: {
159
+ '/api/login': { requests: 5, window: 300000 },
160
+ '/api/search': { requests: 100, window: 60000 },
161
+ },
162
+ },
163
+ onLimitExceeded: async (violation) => {
164
+ console.warn(`Rate limit exceeded for ${violation.ip}`);
165
+ },
166
+ });
167
+
168
+ const result = await limiter.check({
169
+ userId: 'user_123',
170
+ ip: '192.168.1.1',
171
+ endpoint: '/api/search',
172
+ });
173
+
174
+ if (!result.allowed) {
175
+ throw new Error(`Rate limit exceeded. Retry after ${result.retryAfter}ms`);
176
+ }
177
+
178
+ await limiter.resetUser('user_123');
179
+ await limiter.unblock('ip:192.168.1.1');
180
+ ```
181
+
182
+ ### 5. Security Monitoring
183
+
184
+ Comprehensive event logging with metrics and anomaly detection.
185
+
186
+ ```typescript
187
+ const monitoring = secFn.createMonitoring({
188
+ retention: 90 * 24 * 60 * 60 * 1000,
189
+ anomalyDetection: {
190
+ enabled: true,
191
+ sensitivity: 0.8,
192
+ },
193
+ });
194
+
195
+ await monitoring.logEvent({
196
+ type: 'auth_failure',
197
+ severity: 'medium',
198
+ userId: 'user_123',
199
+ ip: '192.168.1.1',
200
+ resource: '/api/login',
201
+ action: 'login',
202
+ metadata: {
203
+ reason: 'invalid_password',
204
+ attemptCount: 3,
205
+ },
206
+ });
207
+
208
+ const events = await monitoring.queryEvents({
209
+ type: 'auth_failure',
210
+ severity: ['high', 'critical'],
211
+ startDate: Date.now() - 24 * 60 * 60 * 1000,
212
+ });
213
+
214
+ const metrics = await monitoring.getMetrics({
215
+ start: Date.now() - 7 * 24 * 60 * 60 * 1000,
216
+ end: Date.now(),
217
+ });
218
+
219
+ console.log('Total events:', metrics.totalEvents);
220
+ console.log('Events by type:', metrics.eventsByType);
221
+ console.log('Anomalies:', metrics.anomalies);
222
+
223
+ await monitoring.resolveEvent('event_123', {
224
+ resolvedBy: 'admin_456',
225
+ notes: 'False positive',
226
+ });
227
+ ```
228
+
229
+ ## Storage
230
+
231
+ SecFn uses IndexedDB for browser/local storage with no external dependencies.
232
+
233
+ ```typescript
234
+ const secFn = createSecFn({
235
+ storage: {
236
+ type: 'indexeddb',
237
+ dbName: 'my-app-security',
238
+ retentionDays: 90,
239
+ },
240
+ });
241
+ ```
242
+
243
+ **Storage Schema:**
244
+ - `secfn_secrets` - Encrypted secrets
245
+ - `secfn_secret_access` - Access logs
246
+ - `secfn_roles` - Role definitions
247
+ - `secfn_user_roles` - User-role assignments
248
+ - `secfn_security_events` - Security events
249
+ - `secfn_rate_limits` - Rate limit tracking
250
+ - `secfn_scan_results` - Secret scan results
251
+
252
+ ## Performance
253
+
254
+ **V0 Benchmarks:**
255
+ - Scan 10,000 files for secrets: <10s
256
+ - Rate limit check: <5ms
257
+ - Access control check: <10ms (with cache)
258
+ - Log 1,000 security events: <500ms
259
+ - Query 10,000 events: <100ms
260
+ - Encrypt/decrypt secret: <10ms
261
+
262
+ ## Examples
263
+
264
+ See `examples/basic-usage.ts` for a comprehensive demonstration of all features.
265
+
266
+ ```bash
267
+ npm run build
268
+ node dist/examples/basic-usage.js
269
+ ```
270
+
271
+ ## Development
272
+
273
+ ```bash
274
+ npm install
275
+
276
+ npm run build
277
+
278
+ npm run build:watch
279
+
280
+ npm test
281
+
282
+ npm run test:watch
283
+
284
+ npm run test:coverage
285
+
286
+ npm run typecheck
287
+
288
+ npm run lint
289
+ ```
290
+
291
+ ## Architecture
292
+
293
+ ```
294
+ secfn/
295
+ ├── storage/ # IndexedDB persistence
296
+ ├── secrets/ # Vault & encryption
297
+ ├── scanning/ # Secret detection
298
+ ├── access-control/ # RBAC system
299
+ ├── rate-limiting/ # Request throttling
300
+ ├── monitoring/ # Event logging
301
+ └── core/ # Main orchestrator
302
+ ```
303
+
304
+ ## Security Best Practices
305
+
306
+ 1. **Master Key**: Always use a strong, randomly generated master key in production
307
+ 2. **Rotation**: Enable automatic secret rotation for sensitive credentials
308
+ 3. **Monitoring**: Review security events and anomalies regularly
309
+ 4. **Access Control**: Apply principle of least privilege
310
+ 5. **Rate Limiting**: Configure appropriate limits for your use case
311
+ 6. **Scanning**: Integrate secret scanning into your CI/CD pipeline
312
+
313
+ ## Roadmap
314
+
315
+ - [ ] File system storage backend
316
+ - [ ] Database adapter support (@superfunctions/db)
317
+ - [ ] Vulnerability scanning (SQL injection, XSS, etc.)
318
+ - [ ] Security middleware for Express/Hono/Fastify
319
+ - [ ] HTML security dashboard
320
+ - [ ] CLI tools
321
+ - [ ] Real-time alert notifications
322
+ - [ ] Advanced anomaly detection with ML
323
+
324
+ ## License
325
+
326
+ MIT
327
+
328
+ ## Contributing
329
+
330
+ Contributions welcome! This is part of the super-functions ecosystem.
331
+
332
+ ## Support
333
+
334
+ For issues and questions, please open an issue on GitHub.
@@ -0,0 +1,48 @@
1
+ import type { Role, AccessRequest } from '../types/index.js';
2
+ import type { IndexedDBStorage } from '../storage/indexeddb-storage.js';
3
+ export interface AccessControlConfig {
4
+ storage: IndexedDBStorage;
5
+ cache?: {
6
+ ttl: number;
7
+ maxSize: number;
8
+ };
9
+ }
10
+ export interface CreateRoleOptions {
11
+ name: string;
12
+ description: string;
13
+ permissions: string[];
14
+ inherits?: string[];
15
+ metadata?: Record<string, unknown>;
16
+ }
17
+ export interface AssignRoleOptions {
18
+ resourceIds?: string[];
19
+ expiresAt?: number;
20
+ }
21
+ export declare class AccessControl {
22
+ private storage;
23
+ private cache;
24
+ private cacheTTL;
25
+ private maxCacheSize;
26
+ constructor(config: AccessControlConfig);
27
+ createRole(options: CreateRoleOptions): Promise<string>;
28
+ getRole(id: string): Promise<Role | null>;
29
+ getRoleByName(name: string): Promise<Role | null>;
30
+ listRoles(): Promise<Role[]>;
31
+ updateRole(id: string, updates: Partial<CreateRoleOptions>): Promise<void>;
32
+ deleteRole(id: string): Promise<void>;
33
+ assignRole(userId: string, roleId: string, options?: AssignRoleOptions): Promise<void>;
34
+ removeRole(userId: string, roleId: string): Promise<void>;
35
+ getUserRoles(userId: string): Promise<Role[]>;
36
+ getUserPermissions(userId: string): Promise<string[]>;
37
+ check(request: AccessRequest): Promise<boolean>;
38
+ private evaluateAccess;
39
+ private checkRolePermission;
40
+ private collectPermissions;
41
+ private matchesPermission;
42
+ private getCacheKey;
43
+ private getCached;
44
+ private setCached;
45
+ private invalidateCache;
46
+ private invalidateCacheForUser;
47
+ }
48
+ //# sourceMappingURL=rbac.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rbac.d.ts","sourceRoot":"","sources":["../../src/access-control/rbac.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAY,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAGxE,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,gBAAgB,CAAC;IAC1B,KAAK,CAAC,EAAE;QACN,GAAG,EAAE,MAAM,CAAC;QACZ,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAOD,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,KAAK,CAA0B;IACvC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,YAAY,CAAS;gBAEjB,MAAM,EAAE,mBAAmB;IAOjC,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC;IAgBvD,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAIzC,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAKjD,SAAS,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;IAI5B,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB1E,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKrC,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB1F,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKzD,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAkB7C,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAWrD,KAAK,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;YAcvC,cAAc;YA0Bd,mBAAmB;YAanB,kBAAkB;IAehC,OAAO,CAAC,iBAAiB;IAgBzB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,SAAS;IAajB,OAAO,CAAC,SAAS;IAYjB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,sBAAsB;CAO/B"}
@@ -0,0 +1,203 @@
1
+ import { generateId } from '../utils/helpers.js';
2
+ export class AccessControl {
3
+ storage;
4
+ cache;
5
+ cacheTTL;
6
+ maxCacheSize;
7
+ constructor(config) {
8
+ this.storage = config.storage;
9
+ this.cache = new Map();
10
+ this.cacheTTL = config.cache?.ttl || 300000;
11
+ this.maxCacheSize = config.cache?.maxSize || 1000;
12
+ }
13
+ async createRole(options) {
14
+ const role = {
15
+ id: generateId('role'),
16
+ name: options.name,
17
+ description: options.description,
18
+ permissions: options.permissions,
19
+ inherits: options.inherits,
20
+ metadata: options.metadata,
21
+ createdAt: Date.now(),
22
+ updatedAt: Date.now(),
23
+ };
24
+ await this.storage.saveRole(role);
25
+ return role.id;
26
+ }
27
+ async getRole(id) {
28
+ return await this.storage.getRole(id);
29
+ }
30
+ async getRoleByName(name) {
31
+ const roles = await this.storage.listRoles();
32
+ return roles.find(r => r.name === name) || null;
33
+ }
34
+ async listRoles() {
35
+ return await this.storage.listRoles();
36
+ }
37
+ async updateRole(id, updates) {
38
+ const role = await this.storage.getRole(id);
39
+ if (!role) {
40
+ throw new Error(`Role ${id} not found`);
41
+ }
42
+ if (updates.name)
43
+ role.name = updates.name;
44
+ if (updates.description)
45
+ role.description = updates.description;
46
+ if (updates.permissions)
47
+ role.permissions = updates.permissions;
48
+ if (updates.inherits)
49
+ role.inherits = updates.inherits;
50
+ if (updates.metadata)
51
+ role.metadata = updates.metadata;
52
+ role.updatedAt = Date.now();
53
+ await this.storage.saveRole(role);
54
+ this.invalidateCache();
55
+ }
56
+ async deleteRole(id) {
57
+ await this.storage.deleteRole(id);
58
+ this.invalidateCache();
59
+ }
60
+ async assignRole(userId, roleId, options = {}) {
61
+ const role = await this.storage.getRole(roleId);
62
+ if (!role) {
63
+ throw new Error(`Role ${roleId} not found`);
64
+ }
65
+ const userRole = {
66
+ userId,
67
+ roleId,
68
+ resourceIds: options.resourceIds,
69
+ expiresAt: options.expiresAt,
70
+ assignedAt: Date.now(),
71
+ };
72
+ await this.storage.assignUserRole(userRole);
73
+ this.invalidateCacheForUser(userId);
74
+ }
75
+ async removeRole(userId, roleId) {
76
+ await this.storage.removeUserRole(userId, roleId);
77
+ this.invalidateCacheForUser(userId);
78
+ }
79
+ async getUserRoles(userId) {
80
+ const userRoles = await this.storage.getUserRoles(userId);
81
+ const roles = [];
82
+ for (const userRole of userRoles) {
83
+ if (userRole.expiresAt && Date.now() > userRole.expiresAt) {
84
+ continue;
85
+ }
86
+ const role = await this.storage.getRole(userRole.roleId);
87
+ if (role) {
88
+ roles.push(role);
89
+ }
90
+ }
91
+ return roles;
92
+ }
93
+ async getUserPermissions(userId) {
94
+ const roles = await this.getUserRoles(userId);
95
+ const permissions = new Set();
96
+ for (const role of roles) {
97
+ await this.collectPermissions(role, permissions);
98
+ }
99
+ return Array.from(permissions);
100
+ }
101
+ async check(request) {
102
+ const cacheKey = this.getCacheKey(request);
103
+ const cached = this.getCached(cacheKey);
104
+ if (cached !== null) {
105
+ return cached;
106
+ }
107
+ const allowed = await this.evaluateAccess(request);
108
+ this.setCached(cacheKey, allowed);
109
+ return allowed;
110
+ }
111
+ async evaluateAccess(request) {
112
+ const userRoles = await this.storage.getUserRoles(request.userId);
113
+ for (const userRole of userRoles) {
114
+ if (userRole.expiresAt && Date.now() > userRole.expiresAt) {
115
+ continue;
116
+ }
117
+ if (userRole.resourceIds && request.resourceId) {
118
+ if (!userRole.resourceIds.includes(request.resourceId)) {
119
+ continue;
120
+ }
121
+ }
122
+ const role = await this.storage.getRole(userRole.roleId);
123
+ if (!role)
124
+ continue;
125
+ const hasPermission = await this.checkRolePermission(role, request);
126
+ if (hasPermission) {
127
+ return true;
128
+ }
129
+ }
130
+ return false;
131
+ }
132
+ async checkRolePermission(role, request) {
133
+ const allPermissions = new Set();
134
+ await this.collectPermissions(role, allPermissions);
135
+ for (const permission of allPermissions) {
136
+ if (this.matchesPermission(permission, request.action)) {
137
+ return true;
138
+ }
139
+ }
140
+ return false;
141
+ }
142
+ async collectPermissions(role, collected) {
143
+ for (const permission of role.permissions) {
144
+ collected.add(permission);
145
+ }
146
+ if (role.inherits) {
147
+ for (const inheritedRoleId of role.inherits) {
148
+ const inheritedRole = await this.storage.getRole(inheritedRoleId);
149
+ if (inheritedRole) {
150
+ await this.collectPermissions(inheritedRole, collected);
151
+ }
152
+ }
153
+ }
154
+ }
155
+ matchesPermission(permission, action) {
156
+ if (permission === '*:*')
157
+ return true;
158
+ const [permResource, permAction] = permission.split(':');
159
+ const [reqResource, reqAction] = action.split(':');
160
+ if (!permResource || !permAction || !reqResource || !reqAction) {
161
+ return false;
162
+ }
163
+ const resourceMatch = permResource === '*' || permResource === reqResource;
164
+ const actionMatch = permAction === '*' || permAction === reqAction;
165
+ return resourceMatch && actionMatch;
166
+ }
167
+ getCacheKey(request) {
168
+ return `${request.userId}:${request.action}:${request.resourceId || '*'}`;
169
+ }
170
+ getCached(key) {
171
+ const entry = this.cache.get(key);
172
+ if (!entry)
173
+ return null;
174
+ const age = Date.now() - entry.timestamp;
175
+ if (age > this.cacheTTL) {
176
+ this.cache.delete(key);
177
+ return null;
178
+ }
179
+ return entry.allowed;
180
+ }
181
+ setCached(key, allowed) {
182
+ if (this.cache.size >= this.maxCacheSize) {
183
+ const firstKey = this.cache.keys().next().value;
184
+ if (firstKey)
185
+ this.cache.delete(firstKey);
186
+ }
187
+ this.cache.set(key, {
188
+ allowed,
189
+ timestamp: Date.now(),
190
+ });
191
+ }
192
+ invalidateCache() {
193
+ this.cache.clear();
194
+ }
195
+ invalidateCacheForUser(userId) {
196
+ for (const [key] of this.cache) {
197
+ if (key.startsWith(`${userId}:`)) {
198
+ this.cache.delete(key);
199
+ }
200
+ }
201
+ }
202
+ }
203
+ //# sourceMappingURL=rbac.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rbac.js","sourceRoot":"","sources":["../../src/access-control/rbac.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import { IndexedDBStorage } from '../storage/indexeddb-storage.js';
2
+ import { SecretsVault, type SecretsVaultConfig } from '../secrets/vault.js';
3
+ import { SecretScanner, type SecretScannerConfig } from '../scanning/scanner.js';
4
+ import { AccessControl, type AccessControlConfig } from '../access-control/rbac.js';
5
+ import { RateLimiter, type RateLimiterConfig } from '../rate-limiting/rate-limiter.js';
6
+ import { SecurityMonitor, type MonitoringConfig } from '../monitoring/security-monitor.js';
7
+ import type { StorageConfig } from '../types/index.js';
8
+ export interface SecFnConfig {
9
+ storage?: StorageConfig;
10
+ masterKey?: string;
11
+ namespace?: string;
12
+ }
13
+ export declare class SecFn {
14
+ private storage;
15
+ private masterKey?;
16
+ private initialized;
17
+ constructor(config?: SecFnConfig);
18
+ initialize(): Promise<void>;
19
+ createSecretsVault(config?: Partial<Omit<SecretsVaultConfig, 'storage'>>): SecretsVault;
20
+ createSecretScanner(config?: Partial<Omit<SecretScannerConfig, 'storage'>>): SecretScanner;
21
+ createAccessControl(config?: Partial<Omit<AccessControlConfig, 'storage'>>): AccessControl;
22
+ createRateLimiter(config: Omit<RateLimiterConfig, 'storage'>): RateLimiter;
23
+ createMonitoring(config?: Partial<Omit<MonitoringConfig, 'storage'>>): SecurityMonitor;
24
+ destroy(): Promise<void>;
25
+ getStorage(): IndexedDBStorage;
26
+ }
27
+ export declare function createSecFn(config?: SecFnConfig): SecFn;
28
+ //# sourceMappingURL=secfn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secfn.d.ts","sourceRoot":"","sources":["../../src/core/secfn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC5E,OAAO,EAAE,aAAa,EAAE,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,KAAK,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACpF,OAAO,EAAE,WAAW,EAAE,KAAK,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACvF,OAAO,EAAE,eAAe,EAAE,KAAK,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAC3F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,KAAK;IAChB,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,WAAW,CAAkB;gBAEzB,MAAM,GAAE,WAAgB;IAM9B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAMjC,kBAAkB,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAC,GAAG,YAAY;IAgBvF,mBAAmB,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,mBAAmB,EAAE,SAAS,CAAC,CAAC,GAAG,aAAa;IAO1F,mBAAmB,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,mBAAmB,EAAE,SAAS,CAAC,CAAC,GAAG,aAAa;IAO1F,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC,GAAG,WAAW;IAQ1E,gBAAgB,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAC,GAAG,eAAe;IAQhF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAK9B,UAAU,IAAI,gBAAgB;CAG/B;AAED,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,KAAK,CAEvD"}