@skillsmith/core 0.4.9 → 0.4.11

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 (197) hide show
  1. package/README.md +1 -1
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/src/analysis/__tests__/incremental.test.d.ts +1 -1
  4. package/dist/src/analysis/__tests__/incremental.test.js +1 -1
  5. package/dist/src/analysis/__tests__/integration.test.d.ts +1 -1
  6. package/dist/src/analysis/__tests__/integration.test.js +1 -1
  7. package/dist/src/analysis/__tests__/performance.test.d.ts +1 -1
  8. package/dist/src/analysis/__tests__/performance.test.js +1 -1
  9. package/dist/src/analysis/adapters/__tests__/python.test.d.ts +1 -1
  10. package/dist/src/analysis/adapters/__tests__/python.test.js +1 -1
  11. package/dist/src/analysis/adapters/__tests__/typescript.test.d.ts +1 -1
  12. package/dist/src/analysis/adapters/__tests__/typescript.test.js +1 -1
  13. package/dist/src/analysis/adapters/base.d.ts +1 -1
  14. package/dist/src/analysis/adapters/base.js +1 -1
  15. package/dist/src/analysis/adapters/factory.d.ts +1 -1
  16. package/dist/src/analysis/adapters/factory.js +1 -1
  17. package/dist/src/analysis/adapters/go.d.ts +1 -1
  18. package/dist/src/analysis/adapters/go.js +1 -1
  19. package/dist/src/analysis/adapters/index.d.ts +1 -1
  20. package/dist/src/analysis/adapters/index.js +1 -1
  21. package/dist/src/analysis/adapters/java-parsers.d.ts +1 -1
  22. package/dist/src/analysis/adapters/java-parsers.d.ts.map +1 -1
  23. package/dist/src/analysis/adapters/java-parsers.js +10 -3
  24. package/dist/src/analysis/adapters/java-parsers.js.map +1 -1
  25. package/dist/src/analysis/adapters/java.d.ts +1 -1
  26. package/dist/src/analysis/adapters/java.js +1 -1
  27. package/dist/src/analysis/adapters/python-frameworks.d.ts +1 -1
  28. package/dist/src/analysis/adapters/python-frameworks.js +1 -1
  29. package/dist/src/analysis/adapters/python.d.ts +1 -1
  30. package/dist/src/analysis/adapters/python.js +1 -1
  31. package/dist/src/analysis/adapters/rust-parsers.d.ts +1 -1
  32. package/dist/src/analysis/adapters/rust-parsers.js +1 -1
  33. package/dist/src/analysis/adapters/rust.d.ts +1 -1
  34. package/dist/src/analysis/adapters/rust.js +1 -1
  35. package/dist/src/analysis/adapters/typescript.d.ts +1 -1
  36. package/dist/src/analysis/adapters/typescript.js +1 -1
  37. package/dist/src/analysis/aggregator.d.ts +1 -1
  38. package/dist/src/analysis/aggregator.js +1 -1
  39. package/dist/src/analysis/cache.d.ts +1 -1
  40. package/dist/src/analysis/cache.js +1 -1
  41. package/dist/src/analysis/file-streamer.d.ts +1 -1
  42. package/dist/src/analysis/file-streamer.js +1 -1
  43. package/dist/src/analysis/incremental-parser.d.ts +1 -1
  44. package/dist/src/analysis/incremental-parser.js +1 -1
  45. package/dist/src/analysis/incremental.d.ts +1 -1
  46. package/dist/src/analysis/incremental.js +1 -1
  47. package/dist/src/analysis/index.d.ts +1 -1
  48. package/dist/src/analysis/index.js +1 -1
  49. package/dist/src/analysis/language-detector.d.ts +1 -1
  50. package/dist/src/analysis/language-detector.js +1 -1
  51. package/dist/src/analysis/memory-monitor.d.ts +1 -1
  52. package/dist/src/analysis/memory-monitor.js +1 -1
  53. package/dist/src/analysis/metrics.d.ts +1 -1
  54. package/dist/src/analysis/metrics.js +1 -1
  55. package/dist/src/analysis/router.d.ts +1 -1
  56. package/dist/src/analysis/router.js +1 -1
  57. package/dist/src/analysis/tree-cache.d.ts +1 -1
  58. package/dist/src/analysis/tree-cache.js +1 -1
  59. package/dist/src/analysis/tree-sitter/manager.d.ts +1 -1
  60. package/dist/src/analysis/tree-sitter/manager.js +1 -1
  61. package/dist/src/analysis/types.d.ts +1 -1
  62. package/dist/src/analysis/types.js +1 -1
  63. package/dist/src/analysis/worker-pool.d.ts +1 -1
  64. package/dist/src/analysis/worker-pool.js +1 -1
  65. package/dist/src/analysis/worker-types.d.ts +1 -1
  66. package/dist/src/analysis/worker-types.js +1 -1
  67. package/dist/src/analysis/worker-utils.d.ts +1 -1
  68. package/dist/src/analysis/worker-utils.js +1 -1
  69. package/dist/src/api/client.d.ts +1 -0
  70. package/dist/src/api/client.d.ts.map +1 -1
  71. package/dist/src/api/client.js.map +1 -1
  72. package/dist/src/api/schemas.d.ts +11 -4
  73. package/dist/src/api/schemas.d.ts.map +1 -1
  74. package/dist/src/api/schemas.js +8 -1
  75. package/dist/src/api/schemas.js.map +1 -1
  76. package/dist/src/config/index.d.ts +49 -1
  77. package/dist/src/config/index.d.ts.map +1 -1
  78. package/dist/src/config/index.js +166 -3
  79. package/dist/src/config/index.js.map +1 -1
  80. package/dist/src/config/index.test.d.ts +11 -0
  81. package/dist/src/config/index.test.d.ts.map +1 -0
  82. package/dist/src/config/index.test.js +288 -0
  83. package/dist/src/config/index.test.js.map +1 -0
  84. package/dist/src/db/drivers/betterSqlite3Driver.d.ts.map +1 -1
  85. package/dist/src/db/drivers/betterSqlite3Driver.js +5 -3
  86. package/dist/src/db/drivers/betterSqlite3Driver.js.map +1 -1
  87. package/dist/src/db/quarantine-approvals-schema.d.ts +37 -0
  88. package/dist/src/db/quarantine-approvals-schema.d.ts.map +1 -0
  89. package/dist/src/db/quarantine-approvals-schema.js +71 -0
  90. package/dist/src/db/quarantine-approvals-schema.js.map +1 -0
  91. package/dist/src/exports/services.d.ts +1 -0
  92. package/dist/src/exports/services.d.ts.map +1 -1
  93. package/dist/src/exports/services.js +4 -0
  94. package/dist/src/exports/services.js.map +1 -1
  95. package/dist/src/index.d.ts +4 -1
  96. package/dist/src/index.d.ts.map +1 -1
  97. package/dist/src/index.js +8 -2
  98. package/dist/src/index.js.map +1 -1
  99. package/dist/src/indexer/SkillParser.d.ts +20 -0
  100. package/dist/src/indexer/SkillParser.d.ts.map +1 -1
  101. package/dist/src/indexer/SkillParser.js +58 -0
  102. package/dist/src/indexer/SkillParser.js.map +1 -1
  103. package/dist/src/repositories/quarantine/ApprovalRepository.d.ts +148 -0
  104. package/dist/src/repositories/quarantine/ApprovalRepository.d.ts.map +1 -0
  105. package/dist/src/repositories/quarantine/ApprovalRepository.js +212 -0
  106. package/dist/src/repositories/quarantine/ApprovalRepository.js.map +1 -0
  107. package/dist/src/repositories/quarantine/QuarantineRepository.d.ts.map +1 -1
  108. package/dist/src/repositories/quarantine/QuarantineRepository.js +4 -1
  109. package/dist/src/repositories/quarantine/QuarantineRepository.js.map +1 -1
  110. package/dist/src/repositories/quarantine/index.d.ts +2 -0
  111. package/dist/src/repositories/quarantine/index.d.ts.map +1 -1
  112. package/dist/src/repositories/quarantine/index.js +1 -0
  113. package/dist/src/repositories/quarantine/index.js.map +1 -1
  114. package/dist/src/scripts/validation/types.d.ts +2 -2
  115. package/dist/src/security/audit-types.d.ts +2 -2
  116. package/dist/src/security/audit-types.d.ts.map +1 -1
  117. package/dist/src/security/audit-types.js.map +1 -1
  118. package/dist/src/security/sanitization.d.ts.map +1 -1
  119. package/dist/src/security/sanitization.js +25 -17
  120. package/dist/src/security/sanitization.js.map +1 -1
  121. package/dist/src/security/scanner/SecurityScanner.formatters.js +1 -1
  122. package/dist/src/security/scanner/SecurityScanner.formatters.js.map +1 -1
  123. package/dist/src/services/index.d.ts +9 -0
  124. package/dist/src/services/index.d.ts.map +1 -0
  125. package/dist/src/services/index.js +10 -0
  126. package/dist/src/services/index.js.map +1 -0
  127. package/dist/src/services/quarantine/QuarantineService.d.ts +157 -0
  128. package/dist/src/services/quarantine/QuarantineService.d.ts.map +1 -0
  129. package/dist/src/services/quarantine/QuarantineService.js +464 -0
  130. package/dist/src/services/quarantine/QuarantineService.js.map +1 -0
  131. package/dist/src/services/quarantine/index.d.ts +10 -0
  132. package/dist/src/services/quarantine/index.d.ts.map +1 -0
  133. package/dist/src/services/quarantine/index.js +14 -0
  134. package/dist/src/services/quarantine/index.js.map +1 -0
  135. package/dist/src/services/quarantine/types.d.ts +127 -0
  136. package/dist/src/services/quarantine/types.d.ts.map +1 -0
  137. package/dist/src/services/quarantine/types.js +59 -0
  138. package/dist/src/services/quarantine/types.js.map +1 -0
  139. package/dist/src/types/skill.d.ts +6 -1
  140. package/dist/src/types/skill.d.ts.map +1 -1
  141. package/dist/src/types/skill.js.map +1 -1
  142. package/dist/src/types.d.ts +1 -1
  143. package/dist/src/types.d.ts.map +1 -1
  144. package/dist/src/utils/index.d.ts +1 -0
  145. package/dist/src/utils/index.d.ts.map +1 -1
  146. package/dist/src/utils/index.js +2 -0
  147. package/dist/src/utils/index.js.map +1 -1
  148. package/dist/src/utils/safe-fs.d.ts +63 -0
  149. package/dist/src/utils/safe-fs.d.ts.map +1 -0
  150. package/dist/src/utils/safe-fs.js +119 -0
  151. package/dist/src/utils/safe-fs.js.map +1 -0
  152. package/dist/src/validation/input-validators.d.ts.map +1 -1
  153. package/dist/src/validation/input-validators.js +11 -4
  154. package/dist/src/validation/input-validators.js.map +1 -1
  155. package/dist/tests/QuarantineRepository.test.js +39 -2
  156. package/dist/tests/QuarantineRepository.test.js.map +1 -1
  157. package/dist/tests/RawUrlSourceAdapter.security.test.js +2 -1
  158. package/dist/tests/RawUrlSourceAdapter.security.test.js.map +1 -1
  159. package/dist/tests/SecurityScanner.test.js +2 -2
  160. package/dist/tests/SecurityScanner.test.js.map +1 -1
  161. package/dist/tests/adapters-factory.test.d.ts +1 -1
  162. package/dist/tests/adapters-factory.test.js +1 -1
  163. package/dist/tests/edge-cases/EdgeCases.test.js +5 -2
  164. package/dist/tests/edge-cases/EdgeCases.test.js.map +1 -1
  165. package/dist/tests/integration/QuarantineService.test.d.ts +11 -0
  166. package/dist/tests/integration/QuarantineService.test.d.ts.map +1 -0
  167. package/dist/tests/integration/QuarantineService.test.js +426 -0
  168. package/dist/tests/integration/QuarantineService.test.js.map +1 -0
  169. package/dist/tests/integration/neural/e2e-learning.test.d.ts +1 -1
  170. package/dist/tests/integration/neural/e2e-learning.test.js +1 -1
  171. package/dist/tests/integration/neural/personalization.test.d.ts +1 -1
  172. package/dist/tests/integration/neural/personalization.test.js +1 -1
  173. package/dist/tests/integration/neural/preference-learner.test.d.ts +1 -1
  174. package/dist/tests/integration/neural/preference-learner.test.js +1 -1
  175. package/dist/tests/integration/neural/privacy.test.d.ts +1 -1
  176. package/dist/tests/integration/neural/privacy.test.js +1 -1
  177. package/dist/tests/integration/neural/signal-collection.test.d.ts +1 -1
  178. package/dist/tests/integration/neural/signal-collection.test.js +1 -1
  179. package/dist/tests/language-detector.test.d.ts +1 -1
  180. package/dist/tests/language-detector.test.js +1 -1
  181. package/dist/tests/unit/approval-repository.test.d.ts +9 -0
  182. package/dist/tests/unit/approval-repository.test.d.ts.map +1 -0
  183. package/dist/tests/unit/approval-repository.test.js +509 -0
  184. package/dist/tests/unit/approval-repository.test.js.map +1 -0
  185. package/dist/tests/unit/check-references.test.d.ts +2 -0
  186. package/dist/tests/unit/check-references.test.d.ts.map +1 -0
  187. package/dist/tests/unit/check-references.test.js +118 -0
  188. package/dist/tests/unit/check-references.test.js.map +1 -0
  189. package/dist/tests/utils/safe-fs.test.d.ts +12 -0
  190. package/dist/tests/utils/safe-fs.test.d.ts.map +1 -0
  191. package/dist/tests/utils/safe-fs.test.js +116 -0
  192. package/dist/tests/utils/safe-fs.test.js.map +1 -0
  193. package/package.json +14 -10
  194. package/dist/tests/db/driver-parity.integration.test.d.ts +0 -16
  195. package/dist/tests/db/driver-parity.integration.test.d.ts.map +0 -1
  196. package/dist/tests/db/driver-parity.integration.test.js +0 -555
  197. package/dist/tests/db/driver-parity.integration.test.js.map +0 -1
@@ -0,0 +1,426 @@
1
+ /**
2
+ * SMI-2269: QuarantineService Integration Tests
3
+ *
4
+ * Tests for authenticated quarantine review operations including:
5
+ * - Session validation
6
+ * - Permission checks (security_reviewer role)
7
+ * - Multi-approval workflow for MALICIOUS severity
8
+ * - Audit logging with verified identities
9
+ */
10
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
11
+ import { QuarantineService } from '../../src/services/quarantine/QuarantineService.js';
12
+ import { QuarantineServiceError } from '../../src/services/quarantine/types.js';
13
+ import { QuarantineRepository } from '../../src/repositories/quarantine/index.js';
14
+ import { ApprovalRepository } from '../../src/repositories/quarantine/index.js';
15
+ import { AuditLogger } from '../../src/security/AuditLogger.js';
16
+ import { createDatabaseSync } from '../../src/db/createDatabase.js';
17
+ // ============================================================================
18
+ // Test Helpers
19
+ // ============================================================================
20
+ /**
21
+ * Create a mock authenticated session for testing
22
+ */
23
+ function createMockSession(overrides = {}) {
24
+ return {
25
+ userId: 'user-123',
26
+ email: 'reviewer@example.com',
27
+ displayName: 'Test Reviewer',
28
+ permissions: ['quarantine:read', 'quarantine:review'],
29
+ sessionId: 'session-456',
30
+ expiresAt: new Date(Date.now() + 3600000), // 1 hour from now
31
+ ...overrides,
32
+ };
33
+ }
34
+ /**
35
+ * Create a session with specific permissions
36
+ */
37
+ function createSessionWithPermissions(permissions) {
38
+ return createMockSession({ permissions });
39
+ }
40
+ /**
41
+ * Create an expired session
42
+ */
43
+ function createExpiredSession() {
44
+ return createMockSession({
45
+ expiresAt: new Date(Date.now() - 1000), // Expired 1 second ago
46
+ });
47
+ }
48
+ // ============================================================================
49
+ // Test Suite
50
+ // ============================================================================
51
+ describe('SMI-2269: QuarantineService Authentication', () => {
52
+ let db;
53
+ let repository;
54
+ let approvalRepository;
55
+ let auditLogger;
56
+ let service;
57
+ beforeEach(() => {
58
+ // Create in-memory database for testing
59
+ db = createDatabaseSync(':memory:');
60
+ auditLogger = new AuditLogger(db);
61
+ repository = new QuarantineRepository(db, auditLogger);
62
+ approvalRepository = new ApprovalRepository(db);
63
+ service = new QuarantineService(repository, approvalRepository, auditLogger);
64
+ });
65
+ afterEach(() => {
66
+ db.close();
67
+ });
68
+ // ==========================================================================
69
+ // Session Validation Tests
70
+ // ==========================================================================
71
+ describe('Session Validation', () => {
72
+ it('should reject expired sessions', () => {
73
+ const session = createExpiredSession();
74
+ expect(() => service.findById(session, 'some-id')).toThrow(QuarantineServiceError);
75
+ expect(() => service.findById(session, 'some-id')).toThrow('Session has expired');
76
+ });
77
+ it('should accept valid sessions', () => {
78
+ const session = createMockSession();
79
+ // Should not throw
80
+ expect(() => service.findById(session, 'nonexistent-id')).not.toThrow();
81
+ });
82
+ it('should include session expiry in error details', () => {
83
+ const session = createExpiredSession();
84
+ try {
85
+ service.findById(session, 'some-id');
86
+ expect.fail('Should have thrown');
87
+ }
88
+ catch (error) {
89
+ expect(error).toBeInstanceOf(QuarantineServiceError);
90
+ expect(error.code).toBe('SESSION_EXPIRED');
91
+ expect(error.details?.expiresAt).toBeDefined();
92
+ }
93
+ });
94
+ });
95
+ // ==========================================================================
96
+ // Permission Tests
97
+ // ==========================================================================
98
+ describe('Permission Enforcement', () => {
99
+ it('should reject users without quarantine:read permission', () => {
100
+ const session = createSessionWithPermissions([]);
101
+ expect(() => service.findById(session, 'some-id')).toThrow(QuarantineServiceError);
102
+ expect(() => service.findById(session, 'some-id')).toThrow('Permission denied');
103
+ });
104
+ it('should allow read with quarantine:read permission', () => {
105
+ const session = createSessionWithPermissions(['quarantine:read']);
106
+ // Should not throw
107
+ expect(() => service.findById(session, 'nonexistent-id')).not.toThrow();
108
+ });
109
+ it('should reject review without quarantine:review permission', () => {
110
+ const session = createSessionWithPermissions(['quarantine:read']);
111
+ // Create a quarantine entry first
112
+ const entry = repository.create({
113
+ skillId: 'test/skill',
114
+ source: 'test',
115
+ quarantineReason: 'Test reason',
116
+ severity: 'SUSPICIOUS',
117
+ });
118
+ expect(() => service.review(session, entry.id, {
119
+ reviewStatus: 'approved',
120
+ })).toThrow('Permission denied');
121
+ });
122
+ it('should allow admin users to bypass permission checks', () => {
123
+ const session = createSessionWithPermissions(['quarantine:admin']);
124
+ // Admin should be able to read without explicit read permission
125
+ expect(() => service.findById(session, 'nonexistent-id')).not.toThrow();
126
+ });
127
+ it('should include required permission in error details', () => {
128
+ const session = createSessionWithPermissions([]);
129
+ try {
130
+ service.findById(session, 'some-id');
131
+ expect.fail('Should have thrown');
132
+ }
133
+ catch (error) {
134
+ expect(error).toBeInstanceOf(QuarantineServiceError);
135
+ expect(error.code).toBe('INSUFFICIENT_PERMISSIONS');
136
+ expect(error.details?.required).toBe('quarantine:read');
137
+ }
138
+ });
139
+ });
140
+ // ==========================================================================
141
+ // Review Tests
142
+ // ==========================================================================
143
+ describe('Authenticated Review', () => {
144
+ it('should include verified reviewer identity in result', () => {
145
+ const session = createMockSession({
146
+ userId: 'verified-user-123',
147
+ email: 'verified@example.com',
148
+ displayName: 'Verified Reviewer',
149
+ });
150
+ const entry = repository.create({
151
+ skillId: 'test/skill',
152
+ source: 'test',
153
+ quarantineReason: 'Test reason',
154
+ severity: 'SUSPICIOUS',
155
+ });
156
+ const result = service.review(session, entry.id, {
157
+ reviewStatus: 'approved',
158
+ reviewNotes: 'Verified safe',
159
+ });
160
+ expect(result.reviewedBy.userId).toBe('verified-user-123');
161
+ expect(result.reviewedBy.email).toBe('verified@example.com');
162
+ expect(result.reviewedBy.displayName).toBe('Verified Reviewer');
163
+ });
164
+ it('should reject review of non-existent entry', () => {
165
+ const session = createMockSession();
166
+ expect(() => service.review(session, 'nonexistent-id', {
167
+ reviewStatus: 'approved',
168
+ })).toThrow('Quarantine entry not found');
169
+ });
170
+ it('should reject review of already-reviewed entry', () => {
171
+ const session = createMockSession();
172
+ const entry = repository.create({
173
+ skillId: 'test/skill',
174
+ source: 'test',
175
+ quarantineReason: 'Test reason',
176
+ severity: 'SUSPICIOUS',
177
+ });
178
+ // First review should succeed
179
+ service.review(session, entry.id, {
180
+ reviewStatus: 'approved',
181
+ });
182
+ // Second review should fail
183
+ expect(() => service.review(session, entry.id, {
184
+ reviewStatus: 'rejected',
185
+ })).toThrow('already reviewed');
186
+ });
187
+ it('should log audit event with session details', () => {
188
+ const session = createMockSession({
189
+ sessionId: 'audit-test-session',
190
+ });
191
+ const entry = repository.create({
192
+ skillId: 'test/audit-skill',
193
+ source: 'test',
194
+ quarantineReason: 'Test reason',
195
+ severity: 'SUSPICIOUS',
196
+ });
197
+ // Spy on audit logger
198
+ const logSpy = vi.spyOn(auditLogger, 'log');
199
+ service.review(session, entry.id, {
200
+ reviewStatus: 'approved',
201
+ });
202
+ // Check audit was logged
203
+ expect(logSpy).toHaveBeenCalledWith(expect.objectContaining({
204
+ event_type: 'quarantine_authenticated_review',
205
+ actor: 'reviewer',
206
+ resource: 'test/audit-skill',
207
+ action: 'review',
208
+ result: 'success',
209
+ metadata: expect.objectContaining({
210
+ sessionId: 'audit-test-session',
211
+ reviewer: expect.objectContaining({
212
+ userId: session.userId,
213
+ email: session.email,
214
+ }),
215
+ }),
216
+ }));
217
+ });
218
+ });
219
+ // ==========================================================================
220
+ // MALICIOUS Severity Multi-Approval Tests
221
+ // ==========================================================================
222
+ describe('Multi-Approval Workflow (MALICIOUS Severity)', () => {
223
+ it('should require quarantine:review_malicious permission for MALICIOUS reviews', () => {
224
+ const session = createSessionWithPermissions(['quarantine:read', 'quarantine:review']);
225
+ const entry = repository.create({
226
+ skillId: 'test/malicious-skill',
227
+ source: 'test',
228
+ quarantineReason: 'Malicious code detected',
229
+ severity: 'MALICIOUS',
230
+ });
231
+ expect(() => service.review(session, entry.id, {
232
+ reviewStatus: 'approved',
233
+ })).toThrow('quarantine:review_malicious required');
234
+ });
235
+ it('should start multi-approval workflow for MALICIOUS approval', () => {
236
+ const session = createSessionWithPermissions([
237
+ 'quarantine:read',
238
+ 'quarantine:review',
239
+ 'quarantine:review_malicious',
240
+ ]);
241
+ const entry = repository.create({
242
+ skillId: 'test/malicious-skill',
243
+ source: 'test',
244
+ quarantineReason: 'Malicious code detected',
245
+ severity: 'MALICIOUS',
246
+ });
247
+ const result = service.review(session, entry.id, {
248
+ reviewStatus: 'approved',
249
+ reviewNotes: 'First approval',
250
+ });
251
+ expect(result.approved).toBe(false); // Not yet approved (needs 2 approvals)
252
+ expect(result.multiApprovalStatus).toBeDefined();
253
+ expect(result.multiApprovalStatus?.currentApprovals.length).toBe(1);
254
+ expect(result.multiApprovalStatus?.requiredApprovals).toBe(2);
255
+ });
256
+ it('should complete approval when enough reviewers approve', () => {
257
+ const session1 = createSessionWithPermissions([
258
+ 'quarantine:read',
259
+ 'quarantine:review',
260
+ 'quarantine:review_malicious',
261
+ ]);
262
+ const session2 = createMockSession({
263
+ userId: 'user-456',
264
+ email: 'reviewer2@example.com',
265
+ permissions: ['quarantine:read', 'quarantine:review', 'quarantine:review_malicious'],
266
+ });
267
+ const entry = repository.create({
268
+ skillId: 'test/malicious-skill',
269
+ source: 'test',
270
+ quarantineReason: 'Malicious code detected',
271
+ severity: 'MALICIOUS',
272
+ });
273
+ // First approval
274
+ const result1 = service.review(session1, entry.id, {
275
+ reviewStatus: 'approved',
276
+ reviewNotes: 'First approval',
277
+ });
278
+ expect(result1.approved).toBe(false);
279
+ // Second approval
280
+ const result2 = service.review(session2, entry.id, {
281
+ reviewStatus: 'approved',
282
+ reviewNotes: 'Second approval',
283
+ });
284
+ expect(result2.approved).toBe(true);
285
+ expect(result2.multiApprovalStatus?.isComplete).toBe(true);
286
+ });
287
+ it('should prevent same user from approving twice', () => {
288
+ const session = createSessionWithPermissions([
289
+ 'quarantine:read',
290
+ 'quarantine:review',
291
+ 'quarantine:review_malicious',
292
+ ]);
293
+ const entry = repository.create({
294
+ skillId: 'test/malicious-skill',
295
+ source: 'test',
296
+ quarantineReason: 'Malicious code detected',
297
+ severity: 'MALICIOUS',
298
+ });
299
+ // First approval should work
300
+ service.review(session, entry.id, {
301
+ reviewStatus: 'approved',
302
+ });
303
+ // Same user trying to approve again should fail
304
+ expect(() => service.review(session, entry.id, {
305
+ reviewStatus: 'approved',
306
+ })).toThrow('already approved');
307
+ });
308
+ it('should allow MALICIOUS rejection without multi-approval', () => {
309
+ const session = createSessionWithPermissions([
310
+ 'quarantine:read',
311
+ 'quarantine:review',
312
+ 'quarantine:review_malicious',
313
+ ]);
314
+ const entry = repository.create({
315
+ skillId: 'test/malicious-skill',
316
+ source: 'test',
317
+ quarantineReason: 'Malicious code detected',
318
+ severity: 'MALICIOUS',
319
+ });
320
+ // Rejection should work immediately (no multi-approval needed)
321
+ const result = service.review(session, entry.id, {
322
+ reviewStatus: 'rejected',
323
+ reviewNotes: 'Confirmed malicious',
324
+ });
325
+ expect(result.approved).toBe(false);
326
+ expect(result.multiApprovalStatus).toBeUndefined();
327
+ });
328
+ it('should track multi-approval status', () => {
329
+ const session = createSessionWithPermissions([
330
+ 'quarantine:read',
331
+ 'quarantine:review',
332
+ 'quarantine:review_malicious',
333
+ ]);
334
+ const entry = repository.create({
335
+ skillId: 'test/malicious-skill',
336
+ source: 'test',
337
+ quarantineReason: 'Malicious code detected',
338
+ severity: 'MALICIOUS',
339
+ });
340
+ // First approval
341
+ service.review(session, entry.id, {
342
+ reviewStatus: 'approved',
343
+ });
344
+ // Check status
345
+ const status = service.getMultiApprovalStatus(session, entry.id);
346
+ expect(status).not.toBeNull();
347
+ expect(status?.currentApprovals.length).toBe(1);
348
+ expect(status?.isComplete).toBe(false);
349
+ });
350
+ });
351
+ // ==========================================================================
352
+ // Timeout Tests
353
+ // ==========================================================================
354
+ describe('Multi-Approval Timeout', () => {
355
+ it('should reset workflow and log audit event when approval times out', () => {
356
+ const session1 = createSessionWithPermissions([
357
+ 'quarantine:read',
358
+ 'quarantine:review',
359
+ 'quarantine:review_malicious',
360
+ ]);
361
+ const session2 = createMockSession({
362
+ userId: 'user-456',
363
+ email: 'reviewer2@example.com',
364
+ permissions: ['quarantine:read', 'quarantine:review', 'quarantine:review_malicious'],
365
+ });
366
+ const entry = repository.create({
367
+ skillId: 'test/malicious-skill',
368
+ source: 'test',
369
+ quarantineReason: 'Malicious code detected',
370
+ severity: 'MALICIOUS',
371
+ });
372
+ // First approval
373
+ service.review(session1, entry.id, {
374
+ reviewStatus: 'approved',
375
+ reviewNotes: 'First approval',
376
+ });
377
+ // Manipulate the created_at timestamp to simulate 25 hours ago
378
+ db.prepare("UPDATE quarantine_approvals SET created_at = datetime('now', '-25 hours') || 'Z' WHERE skill_id = ?").run(entry.id);
379
+ // Spy on audit logger
380
+ const logSpy = vi.spyOn(auditLogger, 'log');
381
+ // Second reviewer triggers timeout
382
+ expect(() => service.review(session2, entry.id, {
383
+ reviewStatus: 'approved',
384
+ })).toThrow('timed out');
385
+ // Verify timeout was audit-logged
386
+ expect(logSpy).toHaveBeenCalledWith(expect.objectContaining({
387
+ event_type: 'quarantine_multi_approval_timeout',
388
+ actor: 'system',
389
+ action: 'timeout',
390
+ }));
391
+ // Verify approvals were cleared
392
+ const status = service.getMultiApprovalStatus(session1, entry.id);
393
+ expect(status).toBeNull();
394
+ });
395
+ });
396
+ // ==========================================================================
397
+ // Error Handling Tests
398
+ // ==========================================================================
399
+ describe('Error Handling', () => {
400
+ it('should return typed error codes', () => {
401
+ const session = createExpiredSession();
402
+ try {
403
+ service.findById(session, 'some-id');
404
+ expect.fail('Should have thrown');
405
+ }
406
+ catch (error) {
407
+ expect(error).toBeInstanceOf(QuarantineServiceError);
408
+ expect(error.code).toBe('SESSION_EXPIRED');
409
+ expect(error.name).toBe('QuarantineServiceError');
410
+ }
411
+ });
412
+ it('should include error details for debugging', () => {
413
+ const session = createSessionWithPermissions([]);
414
+ try {
415
+ service.findById(session, 'some-id');
416
+ expect.fail('Should have thrown');
417
+ }
418
+ catch (error) {
419
+ expect(error).toBeInstanceOf(QuarantineServiceError);
420
+ expect(error.details).toBeDefined();
421
+ expect(error.details?.available).toEqual([]);
422
+ }
423
+ });
424
+ });
425
+ });
426
+ //# sourceMappingURL=QuarantineService.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QuarantineService.test.js","sourceRoot":"","sources":["../../../tests/integration/QuarantineService.test.ts"],"names":[],"mappings":"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"}
@@ -11,7 +11,7 @@
11
11
  * 4. Learning persists across session restart
12
12
  *
13
13
  * @see packages/core/src/learning/interfaces.ts
14
- * @see docs/execution/phase5-testing-execution.md
14
+ * @see docs/internal/execution/phase5-testing-execution.md
15
15
  */
16
16
  export {};
17
17
  //# sourceMappingURL=e2e-learning.test.d.ts.map
@@ -11,7 +11,7 @@
11
11
  * 4. Learning persists across session restart
12
12
  *
13
13
  * @see packages/core/src/learning/interfaces.ts
14
- * @see docs/execution/phase5-testing-execution.md
14
+ * @see docs/internal/execution/phase5-testing-execution.md
15
15
  */
16
16
  import { describe, it, expect, beforeEach, afterEach } from 'vitest';
17
17
  import { createNeuralTestContext, cleanupNeuralTestContext, createDefaultProfile, } from './setup.js';
@@ -15,7 +15,7 @@
15
15
  * 8. Personalization disabled by user preference
16
16
  *
17
17
  * @see packages/core/src/learning/interfaces.ts
18
- * @see docs/execution/phase5-testing-execution.md
18
+ * @see docs/internal/execution/phase5-testing-execution.md
19
19
  */
20
20
  export {};
21
21
  //# sourceMappingURL=personalization.test.d.ts.map
@@ -15,7 +15,7 @@
15
15
  * 8. Personalization disabled by user preference
16
16
  *
17
17
  * @see packages/core/src/learning/interfaces.ts
18
- * @see docs/execution/phase5-testing-execution.md
18
+ * @see docs/internal/execution/phase5-testing-execution.md
19
19
  */
20
20
  import { describe, it, expect, beforeEach, afterEach } from 'vitest';
21
21
  import { createNeuralTestContext, cleanupNeuralTestContext, createDefaultProfile, } from './setup.js';
@@ -17,7 +17,7 @@
17
17
  * 10. Profile persistence across sessions
18
18
  *
19
19
  * @see packages/core/src/learning/interfaces.ts
20
- * @see docs/execution/phase5-testing-execution.md
20
+ * @see docs/internal/execution/phase5-testing-execution.md
21
21
  */
22
22
  export {};
23
23
  //# sourceMappingURL=preference-learner.test.d.ts.map
@@ -17,7 +17,7 @@
17
17
  * 10. Profile persistence across sessions
18
18
  *
19
19
  * @see packages/core/src/learning/interfaces.ts
20
- * @see docs/execution/phase5-testing-execution.md
20
+ * @see docs/internal/execution/phase5-testing-execution.md
21
21
  */
22
22
  import { describe, it, expect, beforeEach, afterEach } from 'vitest';
23
23
  import { createNeuralTestContext, cleanupNeuralTestContext, createDefaultProfile, } from './setup.js';
@@ -13,7 +13,7 @@
13
13
  * 6. Privacy audit log records all operations
14
14
  *
15
15
  * @see packages/core/src/learning/interfaces.ts
16
- * @see docs/execution/phase5-testing-execution.md
16
+ * @see docs/internal/execution/phase5-testing-execution.md
17
17
  */
18
18
  export {};
19
19
  //# sourceMappingURL=privacy.test.d.ts.map
@@ -13,7 +13,7 @@
13
13
  * 6. Privacy audit log records all operations
14
14
  *
15
15
  * @see packages/core/src/learning/interfaces.ts
16
- * @see docs/execution/phase5-testing-execution.md
16
+ * @see docs/internal/execution/phase5-testing-execution.md
17
17
  */
18
18
  import { describe, it, expect, beforeEach, afterEach } from 'vitest';
19
19
  import { createNeuralTestContext, cleanupNeuralTestContext, createDefaultProfile, } from './setup.js';
@@ -15,7 +15,7 @@
15
15
  * 8. Query signals by date range
16
16
  *
17
17
  * @see packages/core/src/learning/interfaces.ts
18
- * @see docs/execution/phase5-testing-execution.md
18
+ * @see docs/internal/execution/phase5-testing-execution.md
19
19
  */
20
20
  export {};
21
21
  //# sourceMappingURL=signal-collection.test.d.ts.map
@@ -15,7 +15,7 @@
15
15
  * 8. Query signals by date range
16
16
  *
17
17
  * @see packages/core/src/learning/interfaces.ts
18
- * @see docs/execution/phase5-testing-execution.md
18
+ * @see docs/internal/execution/phase5-testing-execution.md
19
19
  */
20
20
  import { describe, it, expect, beforeEach, afterEach } from 'vitest';
21
21
  import { createNeuralTestContext, cleanupNeuralTestContext, } from './setup.js';
@@ -7,7 +7,7 @@
7
7
  * - Statistical keyword analysis
8
8
  * - Confidence scoring
9
9
  *
10
- * @see docs/architecture/multi-language-analysis.md
10
+ * @see docs/internal/architecture/multi-language-analysis.md
11
11
  */
12
12
  export {};
13
13
  //# sourceMappingURL=language-detector.test.d.ts.map
@@ -7,7 +7,7 @@
7
7
  * - Statistical keyword analysis
8
8
  * - Confidence scoring
9
9
  *
10
- * @see docs/architecture/multi-language-analysis.md
10
+ * @see docs/internal/architecture/multi-language-analysis.md
11
11
  */
12
12
  import { describe, it, expect } from 'vitest';
13
13
  import { LanguageDetector, detectLanguage } from '../src/analysis/language-detector.js';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * SMI-2277: ApprovalRepository Unit Tests
3
+ *
4
+ * Tests for the database-persisted multi-approval workflow state.
5
+ * Verifies that approval state survives across repository instances
6
+ * (simulating service restarts).
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=approval-repository.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval-repository.test.d.ts","sourceRoot":"","sources":["../../../tests/unit/approval-repository.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}