@skillsmith/core 0.4.9 → 0.4.10

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 (90) hide show
  1. package/README.md +1 -1
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/src/analysis/adapters/java-parsers.d.ts.map +1 -1
  4. package/dist/src/analysis/adapters/java-parsers.js +9 -2
  5. package/dist/src/analysis/adapters/java-parsers.js.map +1 -1
  6. package/dist/src/api/client.d.ts +1 -0
  7. package/dist/src/api/client.d.ts.map +1 -1
  8. package/dist/src/api/client.js.map +1 -1
  9. package/dist/src/api/schemas.d.ts +11 -4
  10. package/dist/src/api/schemas.d.ts.map +1 -1
  11. package/dist/src/api/schemas.js +8 -1
  12. package/dist/src/api/schemas.js.map +1 -1
  13. package/dist/src/db/drivers/betterSqlite3Driver.d.ts.map +1 -1
  14. package/dist/src/db/drivers/betterSqlite3Driver.js +5 -3
  15. package/dist/src/db/drivers/betterSqlite3Driver.js.map +1 -1
  16. package/dist/src/exports/services.d.ts +1 -0
  17. package/dist/src/exports/services.d.ts.map +1 -1
  18. package/dist/src/exports/services.js +4 -0
  19. package/dist/src/exports/services.js.map +1 -1
  20. package/dist/src/index.d.ts +3 -0
  21. package/dist/src/index.d.ts.map +1 -1
  22. package/dist/src/index.js +6 -0
  23. package/dist/src/index.js.map +1 -1
  24. package/dist/src/indexer/SkillParser.d.ts +20 -0
  25. package/dist/src/indexer/SkillParser.d.ts.map +1 -1
  26. package/dist/src/indexer/SkillParser.js +58 -0
  27. package/dist/src/indexer/SkillParser.js.map +1 -1
  28. package/dist/src/repositories/quarantine/QuarantineRepository.d.ts.map +1 -1
  29. package/dist/src/repositories/quarantine/QuarantineRepository.js +4 -1
  30. package/dist/src/repositories/quarantine/QuarantineRepository.js.map +1 -1
  31. package/dist/src/scripts/validation/types.d.ts +2 -2
  32. package/dist/src/security/audit-types.d.ts +2 -2
  33. package/dist/src/security/audit-types.d.ts.map +1 -1
  34. package/dist/src/security/audit-types.js.map +1 -1
  35. package/dist/src/security/sanitization.d.ts.map +1 -1
  36. package/dist/src/security/sanitization.js +25 -17
  37. package/dist/src/security/sanitization.js.map +1 -1
  38. package/dist/src/services/index.d.ts +9 -0
  39. package/dist/src/services/index.d.ts.map +1 -0
  40. package/dist/src/services/index.js +10 -0
  41. package/dist/src/services/index.js.map +1 -0
  42. package/dist/src/services/quarantine/QuarantineService.d.ts +149 -0
  43. package/dist/src/services/quarantine/QuarantineService.d.ts.map +1 -0
  44. package/dist/src/services/quarantine/QuarantineService.js +406 -0
  45. package/dist/src/services/quarantine/QuarantineService.js.map +1 -0
  46. package/dist/src/services/quarantine/index.d.ts +10 -0
  47. package/dist/src/services/quarantine/index.d.ts.map +1 -0
  48. package/dist/src/services/quarantine/index.js +14 -0
  49. package/dist/src/services/quarantine/index.js.map +1 -0
  50. package/dist/src/services/quarantine/types.d.ts +127 -0
  51. package/dist/src/services/quarantine/types.d.ts.map +1 -0
  52. package/dist/src/services/quarantine/types.js +59 -0
  53. package/dist/src/services/quarantine/types.js.map +1 -0
  54. package/dist/src/types/skill.d.ts +6 -1
  55. package/dist/src/types/skill.d.ts.map +1 -1
  56. package/dist/src/types/skill.js.map +1 -1
  57. package/dist/src/types.d.ts +1 -1
  58. package/dist/src/types.d.ts.map +1 -1
  59. package/dist/src/utils/index.d.ts +1 -0
  60. package/dist/src/utils/index.d.ts.map +1 -1
  61. package/dist/src/utils/index.js +2 -0
  62. package/dist/src/utils/index.js.map +1 -1
  63. package/dist/src/utils/safe-fs.d.ts +63 -0
  64. package/dist/src/utils/safe-fs.d.ts.map +1 -0
  65. package/dist/src/utils/safe-fs.js +119 -0
  66. package/dist/src/utils/safe-fs.js.map +1 -0
  67. package/dist/src/validation/input-validators.d.ts.map +1 -1
  68. package/dist/src/validation/input-validators.js +11 -4
  69. package/dist/src/validation/input-validators.js.map +1 -1
  70. package/dist/tests/QuarantineRepository.test.js +39 -2
  71. package/dist/tests/QuarantineRepository.test.js.map +1 -1
  72. package/dist/tests/edge-cases/EdgeCases.test.js +5 -2
  73. package/dist/tests/edge-cases/EdgeCases.test.js.map +1 -1
  74. package/dist/tests/integration/QuarantineService.test.d.ts +11 -0
  75. package/dist/tests/integration/QuarantineService.test.d.ts.map +1 -0
  76. package/dist/tests/integration/QuarantineService.test.js +378 -0
  77. package/dist/tests/integration/QuarantineService.test.js.map +1 -0
  78. package/dist/tests/unit/check-references.test.d.ts +2 -0
  79. package/dist/tests/unit/check-references.test.d.ts.map +1 -0
  80. package/dist/tests/unit/check-references.test.js +118 -0
  81. package/dist/tests/unit/check-references.test.js.map +1 -0
  82. package/dist/tests/utils/safe-fs.test.d.ts +12 -0
  83. package/dist/tests/utils/safe-fs.test.d.ts.map +1 -0
  84. package/dist/tests/utils/safe-fs.test.js +116 -0
  85. package/dist/tests/utils/safe-fs.test.js.map +1 -0
  86. package/package.json +14 -10
  87. package/dist/tests/db/driver-parity.integration.test.d.ts +0 -16
  88. package/dist/tests/db/driver-parity.integration.test.d.ts.map +0 -1
  89. package/dist/tests/db/driver-parity.integration.test.js +0 -555
  90. package/dist/tests/db/driver-parity.integration.test.js.map +0 -1
@@ -0,0 +1,406 @@
1
+ /**
2
+ * SMI-2269: Quarantine Service with Authentication
3
+ *
4
+ * Service layer for quarantine operations that enforces authentication
5
+ * and authorization. Wraps QuarantineRepository with security controls.
6
+ *
7
+ * VP Engineering Guidance:
8
+ * - Auth belongs in service/handler layer, not repository
9
+ * - Repositories should be pure data access
10
+ *
11
+ * Security Controls:
12
+ * - QUA-002: Requires authenticated session for review operations
13
+ * - Enforces security_reviewer permission for review access
14
+ * - Multi-approval workflow for MALICIOUS severity
15
+ * - Audit logs include verified reviewer identity
16
+ *
17
+ * @module @skillsmith/core/services/quarantine/QuarantineService
18
+ */
19
+ import { QuarantineServiceError, requirePermission } from './types.js';
20
+ // ============================================================================
21
+ // Configuration
22
+ // ============================================================================
23
+ /**
24
+ * Number of approvals required for MALICIOUS severity reviews
25
+ */
26
+ const MALICIOUS_APPROVAL_COUNT = 2;
27
+ /**
28
+ * Multi-approval timeout in milliseconds (24 hours)
29
+ */
30
+ const MULTI_APPROVAL_TIMEOUT_MS = 24 * 60 * 60 * 1000;
31
+ // ============================================================================
32
+ // Service Implementation
33
+ // ============================================================================
34
+ /**
35
+ * Quarantine Service with Authentication
36
+ *
37
+ * Provides authenticated access to quarantine operations with:
38
+ * - Session validation
39
+ * - Permission checks (security_reviewer role)
40
+ * - Multi-approval workflow for MALICIOUS severity
41
+ * - Audit logging with verified identities
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * const service = new QuarantineService(repository, auditLogger)
46
+ *
47
+ * // Review a quarantined skill (requires authentication)
48
+ * const result = await service.review(
49
+ * session,
50
+ * quarantineId,
51
+ * { reviewStatus: 'approved', reviewNotes: 'Verified safe' }
52
+ * )
53
+ * ```
54
+ */
55
+ export class QuarantineService {
56
+ repository;
57
+ auditLogger;
58
+ /**
59
+ * In-memory store for pending multi-approvals
60
+ * Key: quarantineId, Value: MultiApprovalStatus
61
+ *
62
+ * Note: In production, this should be persisted to database
63
+ */
64
+ pendingApprovals = new Map();
65
+ constructor(repository, auditLogger) {
66
+ this.repository = repository;
67
+ this.auditLogger = auditLogger;
68
+ }
69
+ // ==========================================================================
70
+ // Read Operations (require quarantine:read permission)
71
+ // ==========================================================================
72
+ /**
73
+ * Find a quarantine entry by ID
74
+ *
75
+ * @param session - Authenticated session
76
+ * @param id - Quarantine entry ID
77
+ * @returns Quarantine entry or null
78
+ */
79
+ findById(session, id) {
80
+ requirePermission(session, 'quarantine:read');
81
+ return this.repository.findById(id);
82
+ }
83
+ /**
84
+ * Find quarantine entries for a skill
85
+ *
86
+ * @param session - Authenticated session
87
+ * @param skillId - Skill ID
88
+ * @returns Array of quarantine entries
89
+ */
90
+ findBySkillId(session, skillId) {
91
+ requirePermission(session, 'quarantine:read');
92
+ return this.repository.findBySkillId(skillId);
93
+ }
94
+ /**
95
+ * Find all quarantine entries with optional filtering
96
+ *
97
+ * @param session - Authenticated session
98
+ * @param filter - Query filters
99
+ * @returns Paginated quarantine results
100
+ */
101
+ findAll(session, filter) {
102
+ requirePermission(session, 'quarantine:read');
103
+ return this.repository.findAll(filter);
104
+ }
105
+ /**
106
+ * Get quarantine statistics
107
+ *
108
+ * @param session - Authenticated session
109
+ * @returns Quarantine statistics
110
+ */
111
+ getStats(session) {
112
+ requirePermission(session, 'quarantine:read');
113
+ return this.repository.getStats();
114
+ }
115
+ // ==========================================================================
116
+ // Review Operations (require quarantine:review permission)
117
+ // ==========================================================================
118
+ /**
119
+ * Review a quarantine entry with authentication
120
+ *
121
+ * This is the secure replacement for QuarantineRepository.review().
122
+ * It enforces:
123
+ * - Valid authenticated session
124
+ * - security_reviewer permission (quarantine:review)
125
+ * - Multi-approval for MALICIOUS severity (quarantine:review_malicious)
126
+ * - Audit logging with verified reviewer identity
127
+ *
128
+ * @param session - Authenticated session (verified by auth layer)
129
+ * @param quarantineId - Quarantine entry ID to review
130
+ * @param input - Review decision and notes
131
+ * @returns Review result with verified reviewer identity
132
+ * @throws QuarantineServiceError on auth/permission failure
133
+ */
134
+ review(session, quarantineId, input) {
135
+ // Validate session and require review permission
136
+ requirePermission(session, 'quarantine:review');
137
+ // Get the quarantine entry
138
+ const entry = this.repository.findById(quarantineId);
139
+ if (!entry) {
140
+ throw new QuarantineServiceError(`Quarantine entry not found: ${quarantineId}`, 'NOT_FOUND', {
141
+ quarantineId,
142
+ });
143
+ }
144
+ // Check if already reviewed
145
+ if (entry.reviewStatus !== 'pending') {
146
+ throw new QuarantineServiceError(`Quarantine entry already reviewed: ${entry.reviewStatus}`, 'ALREADY_REVIEWED', { quarantineId, currentStatus: entry.reviewStatus });
147
+ }
148
+ // For MALICIOUS severity, require multi-approval workflow
149
+ if (entry.severity === 'MALICIOUS' && input.reviewStatus === 'approved') {
150
+ return this.handleMaliciousApproval(session, quarantineId, entry.skillId, input);
151
+ }
152
+ // For MALICIOUS severity rejection or non-MALICIOUS, check elevated permission
153
+ if (entry.severity === 'MALICIOUS') {
154
+ requirePermission(session, 'quarantine:review_malicious');
155
+ }
156
+ // Perform the review with verified identity
157
+ const reviewResult = this.repository.review(quarantineId, {
158
+ reviewedBy: session.email, // Verified email from session
159
+ reviewStatus: input.reviewStatus,
160
+ reviewNotes: input.reviewNotes,
161
+ });
162
+ if (!reviewResult) {
163
+ throw new QuarantineServiceError('Failed to review quarantine entry', 'INVALID_INPUT', {
164
+ quarantineId,
165
+ });
166
+ }
167
+ // Log audit event with full session details
168
+ this.auditLogger.log({
169
+ event_type: 'quarantine_authenticated_review',
170
+ actor: 'reviewer',
171
+ resource: entry.skillId,
172
+ action: 'review',
173
+ result: 'success',
174
+ metadata: {
175
+ quarantineId,
176
+ reviewStatus: input.reviewStatus,
177
+ reviewer: {
178
+ userId: session.userId,
179
+ email: session.email,
180
+ displayName: session.displayName,
181
+ },
182
+ sessionId: session.sessionId,
183
+ severity: entry.severity,
184
+ canImport: reviewResult.canImport,
185
+ },
186
+ });
187
+ return {
188
+ success: true,
189
+ approved: reviewResult.approved,
190
+ skillId: reviewResult.skillId,
191
+ severity: reviewResult.severity,
192
+ canImport: reviewResult.canImport,
193
+ warnings: reviewResult.warnings,
194
+ reviewedBy: {
195
+ userId: session.userId,
196
+ email: session.email,
197
+ displayName: session.displayName,
198
+ },
199
+ };
200
+ }
201
+ // ==========================================================================
202
+ // Multi-Approval Workflow (MALICIOUS Severity)
203
+ // ==========================================================================
204
+ /**
205
+ * Handle approval for MALICIOUS severity skills
206
+ *
207
+ * MALICIOUS severity requires multiple reviewers to approve
208
+ * before a skill can be unquarantined. This prevents single
209
+ * reviewer compromise from allowing malicious skills.
210
+ *
211
+ * @param session - Authenticated session
212
+ * @param quarantineId - Quarantine entry ID
213
+ * @param skillId - Skill ID
214
+ * @param input - Review input
215
+ * @returns Review result with multi-approval status
216
+ */
217
+ handleMaliciousApproval(session, quarantineId, skillId, input) {
218
+ // Require elevated permission for MALICIOUS review
219
+ requirePermission(session, 'quarantine:review_malicious');
220
+ // Get or create pending approval status
221
+ let approvalStatus = this.pendingApprovals.get(quarantineId);
222
+ if (!approvalStatus) {
223
+ // Start new multi-approval workflow
224
+ approvalStatus = {
225
+ quarantineId,
226
+ requiredApprovals: MALICIOUS_APPROVAL_COUNT,
227
+ currentApprovals: [],
228
+ isComplete: false,
229
+ startedAt: new Date(),
230
+ };
231
+ this.pendingApprovals.set(quarantineId, approvalStatus);
232
+ }
233
+ // Check if this reviewer already approved
234
+ const existingApproval = approvalStatus.currentApprovals.find((a) => a.reviewerId === session.userId);
235
+ if (existingApproval) {
236
+ throw new QuarantineServiceError('You have already approved this entry', 'ALREADY_REVIEWED', {
237
+ quarantineId,
238
+ previousApprovalAt: existingApproval.approvedAt.toISOString(),
239
+ });
240
+ }
241
+ // Check for approval timeout
242
+ const timeSinceStart = Date.now() - approvalStatus.startedAt.getTime();
243
+ if (timeSinceStart > MULTI_APPROVAL_TIMEOUT_MS) {
244
+ // Reset approval workflow
245
+ this.pendingApprovals.delete(quarantineId);
246
+ throw new QuarantineServiceError('Multi-approval workflow timed out. Please start again.', 'INVALID_INPUT', { quarantineId, timeoutMs: MULTI_APPROVAL_TIMEOUT_MS });
247
+ }
248
+ // Add this approval
249
+ const approval = {
250
+ reviewerId: session.userId,
251
+ reviewerEmail: session.email,
252
+ approvedAt: new Date(),
253
+ notes: input.reviewNotes,
254
+ };
255
+ approvalStatus.currentApprovals.push(approval);
256
+ // Log the approval
257
+ this.auditLogger.log({
258
+ event_type: 'quarantine_multi_approval',
259
+ actor: 'reviewer',
260
+ resource: skillId,
261
+ action: 'approve',
262
+ result: 'success',
263
+ metadata: {
264
+ quarantineId,
265
+ approvalNumber: approvalStatus.currentApprovals.length,
266
+ requiredApprovals: approvalStatus.requiredApprovals,
267
+ reviewer: {
268
+ userId: session.userId,
269
+ email: session.email,
270
+ },
271
+ },
272
+ });
273
+ // Check if we have enough approvals
274
+ if (approvalStatus.currentApprovals.length >= approvalStatus.requiredApprovals) {
275
+ // Complete the approval workflow
276
+ approvalStatus.isComplete = true;
277
+ approvalStatus.completedAt = new Date();
278
+ // Perform the actual review
279
+ const reviewResult = this.repository.review(quarantineId, {
280
+ reviewedBy: approvalStatus.currentApprovals.map((a) => a.reviewerEmail).join(', '),
281
+ reviewStatus: 'approved',
282
+ reviewNotes: `Multi-approval complete: ${approvalStatus.currentApprovals.length} reviewers approved. ${input.reviewNotes || ''}`,
283
+ });
284
+ // Clean up pending approval
285
+ this.pendingApprovals.delete(quarantineId);
286
+ // Log completion
287
+ this.auditLogger.log({
288
+ event_type: 'quarantine_multi_approval_complete',
289
+ actor: 'reviewer',
290
+ resource: skillId,
291
+ action: 'complete',
292
+ result: 'success',
293
+ metadata: {
294
+ quarantineId,
295
+ approvals: approvalStatus.currentApprovals.map((a) => ({
296
+ reviewerId: a.reviewerId,
297
+ email: a.reviewerEmail,
298
+ approvedAt: a.approvedAt.toISOString(),
299
+ })),
300
+ },
301
+ });
302
+ return {
303
+ success: true,
304
+ approved: true,
305
+ skillId,
306
+ severity: 'MALICIOUS',
307
+ canImport: reviewResult?.canImport ?? false,
308
+ warnings: [
309
+ 'MALICIOUS skill approved through multi-approval workflow',
310
+ `Approved by: ${approvalStatus.currentApprovals.map((a) => a.reviewerEmail).join(', ')}`,
311
+ ],
312
+ reviewedBy: {
313
+ userId: session.userId,
314
+ email: session.email,
315
+ displayName: session.displayName,
316
+ },
317
+ multiApprovalStatus: approvalStatus,
318
+ };
319
+ }
320
+ // Need more approvals
321
+ return {
322
+ success: true,
323
+ approved: false,
324
+ skillId,
325
+ severity: 'MALICIOUS',
326
+ canImport: false,
327
+ warnings: [
328
+ `Multi-approval in progress: ${approvalStatus.currentApprovals.length}/${approvalStatus.requiredApprovals} approvals received`,
329
+ `Requires ${approvalStatus.requiredApprovals - approvalStatus.currentApprovals.length} more approval(s)`,
330
+ ],
331
+ reviewedBy: {
332
+ userId: session.userId,
333
+ email: session.email,
334
+ displayName: session.displayName,
335
+ },
336
+ multiApprovalStatus: approvalStatus,
337
+ };
338
+ }
339
+ /**
340
+ * Get pending multi-approval status for a quarantine entry
341
+ *
342
+ * @param session - Authenticated session
343
+ * @param quarantineId - Quarantine entry ID
344
+ * @returns Multi-approval status or null
345
+ */
346
+ getMultiApprovalStatus(session, quarantineId) {
347
+ requirePermission(session, 'quarantine:read');
348
+ return this.pendingApprovals.get(quarantineId) ?? null;
349
+ }
350
+ /**
351
+ * Cancel a pending multi-approval workflow
352
+ *
353
+ * @param session - Authenticated session (requires admin)
354
+ * @param quarantineId - Quarantine entry ID
355
+ * @returns Whether the cancellation was successful
356
+ */
357
+ cancelMultiApproval(session, quarantineId) {
358
+ requirePermission(session, 'quarantine:admin');
359
+ const status = this.pendingApprovals.get(quarantineId);
360
+ if (!status) {
361
+ return false;
362
+ }
363
+ this.pendingApprovals.delete(quarantineId);
364
+ this.auditLogger.log({
365
+ event_type: 'quarantine_multi_approval_cancelled',
366
+ actor: 'reviewer',
367
+ resource: quarantineId,
368
+ action: 'cancel',
369
+ result: 'success',
370
+ metadata: {
371
+ quarantineId,
372
+ cancelledBy: session.email,
373
+ pendingApprovals: status.currentApprovals.length,
374
+ },
375
+ });
376
+ return true;
377
+ }
378
+ // ==========================================================================
379
+ // Admin Operations (require quarantine:admin permission)
380
+ // ==========================================================================
381
+ /**
382
+ * Create a quarantine entry (admin only)
383
+ *
384
+ * @param session - Authenticated session
385
+ * @param input - Quarantine creation input
386
+ * @returns Created quarantine entry
387
+ */
388
+ create(session, input) {
389
+ requirePermission(session, 'quarantine:create');
390
+ return this.repository.create(input);
391
+ }
392
+ /**
393
+ * Delete a quarantine entry (admin only)
394
+ *
395
+ * @param session - Authenticated session
396
+ * @param id - Quarantine entry ID
397
+ * @returns Whether the entry was deleted
398
+ */
399
+ delete(session, id) {
400
+ requirePermission(session, 'quarantine:delete');
401
+ // Cancel any pending multi-approval
402
+ this.pendingApprovals.delete(id);
403
+ return this.repository.delete(id);
404
+ }
405
+ }
406
+ //# sourceMappingURL=QuarantineService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QuarantineService.js","sourceRoot":"","sources":["../../../../src/services/quarantine/QuarantineService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * SMI-2269: Quarantine Service Module
3
+ *
4
+ * Authenticated quarantine operations with multi-approval workflow.
5
+ *
6
+ * @module @skillsmith/core/services/quarantine
7
+ */
8
+ export { QuarantineService } from './QuarantineService.js';
9
+ export { type QuarantinePermission, type AuthenticatedSession, type ApprovalRecord, type MultiApprovalStatus, type AuthenticatedReviewInput, type AuthenticatedReviewResult, type QuarantineServiceErrorCode, QuarantineServiceError, hasPermission, isSessionValid, requirePermission, } from './types.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/services/quarantine/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC1D,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAE/B,sBAAsB,EAEtB,aAAa,EACb,cAAc,EACd,iBAAiB,GAClB,MAAM,YAAY,CAAA"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * SMI-2269: Quarantine Service Module
3
+ *
4
+ * Authenticated quarantine operations with multi-approval workflow.
5
+ *
6
+ * @module @skillsmith/core/services/quarantine
7
+ */
8
+ export { QuarantineService } from './QuarantineService.js';
9
+ export {
10
+ // Error class
11
+ QuarantineServiceError,
12
+ // Helper functions
13
+ hasPermission, isSessionValid, requirePermission, } from './types.js';
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/services/quarantine/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC1D,OAAO;AASL,cAAc;AACd,sBAAsB;AACtB,mBAAmB;AACnB,aAAa,EACb,cAAc,EACd,iBAAiB,GAClB,MAAM,YAAY,CAAA"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * SMI-2269: Quarantine Service Types
3
+ *
4
+ * Authentication and authorization types for quarantine review operations.
5
+ * Part of QUA-002 security fix: No authentication on quarantine review.
6
+ *
7
+ * @module @skillsmith/core/services/quarantine/types
8
+ */
9
+ import type { QuarantineSeverity, QuarantineReviewStatus } from '../../db/quarantine-schema.js';
10
+ /**
11
+ * User permissions for quarantine operations
12
+ */
13
+ export type QuarantinePermission = 'quarantine:read' | 'quarantine:create' | 'quarantine:review' | 'quarantine:review_malicious' | 'quarantine:delete' | 'quarantine:admin';
14
+ /**
15
+ * Authenticated session for quarantine operations
16
+ *
17
+ * All quarantine review operations require a valid session with
18
+ * appropriate permissions. This ensures audit trails contain
19
+ * verified reviewer identities.
20
+ */
21
+ export interface AuthenticatedSession {
22
+ /** Unique user identifier (from auth provider) */
23
+ userId: string;
24
+ /** User's email address (verified) */
25
+ email: string;
26
+ /** User's display name */
27
+ displayName: string;
28
+ /** User's assigned permissions */
29
+ permissions: QuarantinePermission[];
30
+ /** Session token ID for audit logging */
31
+ sessionId: string;
32
+ /** Session expiration timestamp */
33
+ expiresAt: Date;
34
+ /** Organization/team ID (for team-based permissions) */
35
+ organizationId?: string;
36
+ }
37
+ /**
38
+ * Approval record for multi-approval workflow
39
+ */
40
+ export interface ApprovalRecord {
41
+ /** Reviewer user ID */
42
+ reviewerId: string;
43
+ /** Reviewer email */
44
+ reviewerEmail: string;
45
+ /** Timestamp of approval */
46
+ approvedAt: Date;
47
+ /** Optional notes from reviewer */
48
+ notes?: string;
49
+ }
50
+ /**
51
+ * Multi-approval status for MALICIOUS severity reviews
52
+ */
53
+ export interface MultiApprovalStatus {
54
+ /** Quarantine entry ID */
55
+ quarantineId: string;
56
+ /** Required number of approvals */
57
+ requiredApprovals: number;
58
+ /** Current approvals received */
59
+ currentApprovals: ApprovalRecord[];
60
+ /** Whether all required approvals have been received */
61
+ isComplete: boolean;
62
+ /** Timestamp when first approval was received */
63
+ startedAt: Date;
64
+ /** Timestamp when approval completed (if complete) */
65
+ completedAt?: Date;
66
+ }
67
+ /**
68
+ * Input for authenticated review operation
69
+ */
70
+ export interface AuthenticatedReviewInput {
71
+ /** Review status decision */
72
+ reviewStatus: QuarantineReviewStatus;
73
+ /** Optional review notes */
74
+ reviewNotes?: string;
75
+ }
76
+ /**
77
+ * Result from authenticated review operation
78
+ */
79
+ export interface AuthenticatedReviewResult {
80
+ /** Whether the review was successful */
81
+ success: boolean;
82
+ /** Whether the skill is approved for import */
83
+ approved: boolean;
84
+ /** Skill ID that was reviewed */
85
+ skillId: string;
86
+ /** Severity of the quarantined skill */
87
+ severity: QuarantineSeverity;
88
+ /** Whether the skill can be imported */
89
+ canImport: boolean;
90
+ /** Any warnings about the skill */
91
+ warnings: string[];
92
+ /** Verified reviewer identity */
93
+ reviewedBy: {
94
+ userId: string;
95
+ email: string;
96
+ displayName: string;
97
+ };
98
+ /** For MALICIOUS severity: multi-approval status */
99
+ multiApprovalStatus?: MultiApprovalStatus;
100
+ }
101
+ /**
102
+ * Error codes for quarantine service operations
103
+ */
104
+ export type QuarantineServiceErrorCode = 'UNAUTHORIZED' | 'FORBIDDEN' | 'SESSION_EXPIRED' | 'INSUFFICIENT_PERMISSIONS' | 'MULTI_APPROVAL_REQUIRED' | 'ALREADY_REVIEWED' | 'NOT_FOUND' | 'INVALID_INPUT';
105
+ /**
106
+ * Service error with typed error codes
107
+ */
108
+ export declare class QuarantineServiceError extends Error {
109
+ readonly code: QuarantineServiceErrorCode;
110
+ readonly details?: Record<string, unknown> | undefined;
111
+ constructor(message: string, code: QuarantineServiceErrorCode, details?: Record<string, unknown> | undefined);
112
+ }
113
+ /**
114
+ * Check if session has a specific permission
115
+ */
116
+ export declare function hasPermission(session: AuthenticatedSession, permission: QuarantinePermission): boolean;
117
+ /**
118
+ * Check if session is valid (not expired)
119
+ */
120
+ export declare function isSessionValid(session: AuthenticatedSession): boolean;
121
+ /**
122
+ * Validate session and check permission
123
+ *
124
+ * @throws QuarantineServiceError if session is invalid or lacks permission
125
+ */
126
+ export declare function requirePermission(session: AuthenticatedSession, permission: QuarantinePermission): void;
127
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/services/quarantine/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * SMI-2269: Quarantine Service Types
3
+ *
4
+ * Authentication and authorization types for quarantine review operations.
5
+ * Part of QUA-002 security fix: No authentication on quarantine review.
6
+ *
7
+ * @module @skillsmith/core/services/quarantine/types
8
+ */
9
+ /**
10
+ * Service error with typed error codes
11
+ */
12
+ export class QuarantineServiceError extends Error {
13
+ code;
14
+ details;
15
+ constructor(message, code, details) {
16
+ super(message);
17
+ this.code = code;
18
+ this.details = details;
19
+ this.name = 'QuarantineServiceError';
20
+ }
21
+ }
22
+ // ============================================================================
23
+ // Permission Helpers
24
+ // ============================================================================
25
+ /**
26
+ * Check if session has a specific permission
27
+ */
28
+ export function hasPermission(session, permission) {
29
+ // Admin has all permissions
30
+ if (session.permissions.includes('quarantine:admin')) {
31
+ return true;
32
+ }
33
+ return session.permissions.includes(permission);
34
+ }
35
+ /**
36
+ * Check if session is valid (not expired)
37
+ */
38
+ export function isSessionValid(session) {
39
+ return session.expiresAt > new Date();
40
+ }
41
+ /**
42
+ * Validate session and check permission
43
+ *
44
+ * @throws QuarantineServiceError if session is invalid or lacks permission
45
+ */
46
+ export function requirePermission(session, permission) {
47
+ if (!isSessionValid(session)) {
48
+ throw new QuarantineServiceError('Session has expired', 'SESSION_EXPIRED', {
49
+ expiresAt: session.expiresAt.toISOString(),
50
+ });
51
+ }
52
+ if (!hasPermission(session, permission)) {
53
+ throw new QuarantineServiceError(`Permission denied: ${permission} required`, 'INSUFFICIENT_PERMISSIONS', {
54
+ required: permission,
55
+ available: session.permissions,
56
+ });
57
+ }
58
+ }
59
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/services/quarantine/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA8JH;;GAEG;AACH,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IAG7B;IACA;IAHlB,YACE,OAAe,EACC,IAAgC,EAChC,OAAiC;QAEjD,KAAK,CAAC,OAAO,CAAC,CAAA;QAHE,SAAI,GAAJ,IAAI,CAA4B;QAChC,YAAO,GAAP,OAAO,CAA0B;QAGjD,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAA;IACtC,CAAC;CACF;AAED,+EAA+E;AAC/E,qBAAqB;AACrB,+EAA+E;AAE/E;;GAEG;AACH,MAAM,UAAU,aAAa,CAC3B,OAA6B,EAC7B,UAAgC;IAEhC,4BAA4B;IAC5B,IAAI,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;QACrD,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAA;AACjD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,OAA6B;IAC1D,OAAO,OAAO,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAA;AACvC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAA6B,EAC7B,UAAgC;IAEhC,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,sBAAsB,CAAC,qBAAqB,EAAE,iBAAiB,EAAE;YACzE,SAAS,EAAE,OAAO,CAAC,SAAS,CAAC,WAAW,EAAE;SAC3C,CAAC,CAAA;IACJ,CAAC;IAED,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,sBAAsB,CAC9B,sBAAsB,UAAU,WAAW,EAC3C,0BAA0B,EAC1B;YACE,QAAQ,EAAE,UAAU;YACpB,SAAS,EAAE,OAAO,CAAC,WAAW;SAC/B,CACF,CAAA;IACH,CAAC;AACH,CAAC"}
@@ -3,8 +3,13 @@
3
3
  */
4
4
  /**
5
5
  * SMI-1809: Added 'local' for local skills from ~/.claude/skills/
6
+ *
7
+ * NOTE: 'local' is a client-only tier for skills discovered on disk.
8
+ * It is NOT stored in the database — the skills table CHECK constraint
9
+ * only allows: verified, curated, community, experimental, unknown.
10
+ * Never pass 'local' to database upsert operations.
6
11
  */
7
- export type TrustTier = 'verified' | 'community' | 'experimental' | 'unknown' | 'local';
12
+ export type TrustTier = 'verified' | 'curated' | 'community' | 'experimental' | 'unknown' | 'local';
8
13
  /**
9
14
  * SMI-1631: Skill roles for role-based recommendations
10
15
  * Used to filter and prioritize skills based on their primary purpose