@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,11 @@
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
+ export {};
11
+ //# sourceMappingURL=QuarantineService.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QuarantineService.test.d.ts","sourceRoot":"","sources":["../../../tests/integration/QuarantineService.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
@@ -0,0 +1,378 @@
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 { AuditLogger } from '../../src/security/AuditLogger.js';
15
+ import { createDatabaseSync } from '../../src/db/createDatabase.js';
16
+ // ============================================================================
17
+ // Test Helpers
18
+ // ============================================================================
19
+ /**
20
+ * Create a mock authenticated session for testing
21
+ */
22
+ function createMockSession(overrides = {}) {
23
+ return {
24
+ userId: 'user-123',
25
+ email: 'reviewer@example.com',
26
+ displayName: 'Test Reviewer',
27
+ permissions: ['quarantine:read', 'quarantine:review'],
28
+ sessionId: 'session-456',
29
+ expiresAt: new Date(Date.now() + 3600000), // 1 hour from now
30
+ ...overrides,
31
+ };
32
+ }
33
+ /**
34
+ * Create a session with specific permissions
35
+ */
36
+ function createSessionWithPermissions(permissions) {
37
+ return createMockSession({ permissions });
38
+ }
39
+ /**
40
+ * Create an expired session
41
+ */
42
+ function createExpiredSession() {
43
+ return createMockSession({
44
+ expiresAt: new Date(Date.now() - 1000), // Expired 1 second ago
45
+ });
46
+ }
47
+ // ============================================================================
48
+ // Test Suite
49
+ // ============================================================================
50
+ describe('SMI-2269: QuarantineService Authentication', () => {
51
+ let db;
52
+ let repository;
53
+ let auditLogger;
54
+ let service;
55
+ beforeEach(() => {
56
+ // Create in-memory database for testing
57
+ db = createDatabaseSync(':memory:');
58
+ auditLogger = new AuditLogger(db);
59
+ repository = new QuarantineRepository(db, auditLogger);
60
+ service = new QuarantineService(repository, auditLogger);
61
+ });
62
+ afterEach(() => {
63
+ db.close();
64
+ });
65
+ // ==========================================================================
66
+ // Session Validation Tests
67
+ // ==========================================================================
68
+ describe('Session Validation', () => {
69
+ it('should reject expired sessions', () => {
70
+ const session = createExpiredSession();
71
+ expect(() => service.findById(session, 'some-id')).toThrow(QuarantineServiceError);
72
+ expect(() => service.findById(session, 'some-id')).toThrow('Session has expired');
73
+ });
74
+ it('should accept valid sessions', () => {
75
+ const session = createMockSession();
76
+ // Should not throw
77
+ expect(() => service.findById(session, 'nonexistent-id')).not.toThrow();
78
+ });
79
+ it('should include session expiry in error details', () => {
80
+ const session = createExpiredSession();
81
+ try {
82
+ service.findById(session, 'some-id');
83
+ expect.fail('Should have thrown');
84
+ }
85
+ catch (error) {
86
+ expect(error).toBeInstanceOf(QuarantineServiceError);
87
+ expect(error.code).toBe('SESSION_EXPIRED');
88
+ expect(error.details?.expiresAt).toBeDefined();
89
+ }
90
+ });
91
+ });
92
+ // ==========================================================================
93
+ // Permission Tests
94
+ // ==========================================================================
95
+ describe('Permission Enforcement', () => {
96
+ it('should reject users without quarantine:read permission', () => {
97
+ const session = createSessionWithPermissions([]);
98
+ expect(() => service.findById(session, 'some-id')).toThrow(QuarantineServiceError);
99
+ expect(() => service.findById(session, 'some-id')).toThrow('Permission denied');
100
+ });
101
+ it('should allow read with quarantine:read permission', () => {
102
+ const session = createSessionWithPermissions(['quarantine:read']);
103
+ // Should not throw
104
+ expect(() => service.findById(session, 'nonexistent-id')).not.toThrow();
105
+ });
106
+ it('should reject review without quarantine:review permission', () => {
107
+ const session = createSessionWithPermissions(['quarantine:read']);
108
+ // Create a quarantine entry first
109
+ const entry = repository.create({
110
+ skillId: 'test/skill',
111
+ source: 'test',
112
+ quarantineReason: 'Test reason',
113
+ severity: 'SUSPICIOUS',
114
+ });
115
+ expect(() => service.review(session, entry.id, {
116
+ reviewStatus: 'approved',
117
+ })).toThrow('Permission denied');
118
+ });
119
+ it('should allow admin users to bypass permission checks', () => {
120
+ const session = createSessionWithPermissions(['quarantine:admin']);
121
+ // Admin should be able to read without explicit read permission
122
+ expect(() => service.findById(session, 'nonexistent-id')).not.toThrow();
123
+ });
124
+ it('should include required permission in error details', () => {
125
+ const session = createSessionWithPermissions([]);
126
+ try {
127
+ service.findById(session, 'some-id');
128
+ expect.fail('Should have thrown');
129
+ }
130
+ catch (error) {
131
+ expect(error).toBeInstanceOf(QuarantineServiceError);
132
+ expect(error.code).toBe('INSUFFICIENT_PERMISSIONS');
133
+ expect(error.details?.required).toBe('quarantine:read');
134
+ }
135
+ });
136
+ });
137
+ // ==========================================================================
138
+ // Review Tests
139
+ // ==========================================================================
140
+ describe('Authenticated Review', () => {
141
+ it('should include verified reviewer identity in result', () => {
142
+ const session = createMockSession({
143
+ userId: 'verified-user-123',
144
+ email: 'verified@example.com',
145
+ displayName: 'Verified Reviewer',
146
+ });
147
+ const entry = repository.create({
148
+ skillId: 'test/skill',
149
+ source: 'test',
150
+ quarantineReason: 'Test reason',
151
+ severity: 'SUSPICIOUS',
152
+ });
153
+ const result = service.review(session, entry.id, {
154
+ reviewStatus: 'approved',
155
+ reviewNotes: 'Verified safe',
156
+ });
157
+ expect(result.reviewedBy.userId).toBe('verified-user-123');
158
+ expect(result.reviewedBy.email).toBe('verified@example.com');
159
+ expect(result.reviewedBy.displayName).toBe('Verified Reviewer');
160
+ });
161
+ it('should reject review of non-existent entry', () => {
162
+ const session = createMockSession();
163
+ expect(() => service.review(session, 'nonexistent-id', {
164
+ reviewStatus: 'approved',
165
+ })).toThrow('Quarantine entry not found');
166
+ });
167
+ it('should reject review of already-reviewed entry', () => {
168
+ const session = createMockSession();
169
+ const entry = repository.create({
170
+ skillId: 'test/skill',
171
+ source: 'test',
172
+ quarantineReason: 'Test reason',
173
+ severity: 'SUSPICIOUS',
174
+ });
175
+ // First review should succeed
176
+ service.review(session, entry.id, {
177
+ reviewStatus: 'approved',
178
+ });
179
+ // Second review should fail
180
+ expect(() => service.review(session, entry.id, {
181
+ reviewStatus: 'rejected',
182
+ })).toThrow('already reviewed');
183
+ });
184
+ it('should log audit event with session details', () => {
185
+ const session = createMockSession({
186
+ sessionId: 'audit-test-session',
187
+ });
188
+ const entry = repository.create({
189
+ skillId: 'test/audit-skill',
190
+ source: 'test',
191
+ quarantineReason: 'Test reason',
192
+ severity: 'SUSPICIOUS',
193
+ });
194
+ // Spy on audit logger
195
+ const logSpy = vi.spyOn(auditLogger, 'log');
196
+ service.review(session, entry.id, {
197
+ reviewStatus: 'approved',
198
+ });
199
+ // Check audit was logged
200
+ expect(logSpy).toHaveBeenCalledWith(expect.objectContaining({
201
+ event_type: 'quarantine_authenticated_review',
202
+ actor: 'reviewer',
203
+ resource: 'test/audit-skill',
204
+ action: 'review',
205
+ result: 'success',
206
+ metadata: expect.objectContaining({
207
+ sessionId: 'audit-test-session',
208
+ reviewer: expect.objectContaining({
209
+ userId: session.userId,
210
+ email: session.email,
211
+ }),
212
+ }),
213
+ }));
214
+ });
215
+ });
216
+ // ==========================================================================
217
+ // MALICIOUS Severity Multi-Approval Tests
218
+ // ==========================================================================
219
+ describe('Multi-Approval Workflow (MALICIOUS Severity)', () => {
220
+ it('should require quarantine:review_malicious permission for MALICIOUS reviews', () => {
221
+ const session = createSessionWithPermissions(['quarantine:read', 'quarantine:review']);
222
+ const entry = repository.create({
223
+ skillId: 'test/malicious-skill',
224
+ source: 'test',
225
+ quarantineReason: 'Malicious code detected',
226
+ severity: 'MALICIOUS',
227
+ });
228
+ expect(() => service.review(session, entry.id, {
229
+ reviewStatus: 'approved',
230
+ })).toThrow('quarantine:review_malicious required');
231
+ });
232
+ it('should start multi-approval workflow for MALICIOUS approval', () => {
233
+ const session = createSessionWithPermissions([
234
+ 'quarantine:read',
235
+ 'quarantine:review',
236
+ 'quarantine:review_malicious',
237
+ ]);
238
+ const entry = repository.create({
239
+ skillId: 'test/malicious-skill',
240
+ source: 'test',
241
+ quarantineReason: 'Malicious code detected',
242
+ severity: 'MALICIOUS',
243
+ });
244
+ const result = service.review(session, entry.id, {
245
+ reviewStatus: 'approved',
246
+ reviewNotes: 'First approval',
247
+ });
248
+ expect(result.approved).toBe(false); // Not yet approved (needs 2 approvals)
249
+ expect(result.multiApprovalStatus).toBeDefined();
250
+ expect(result.multiApprovalStatus?.currentApprovals.length).toBe(1);
251
+ expect(result.multiApprovalStatus?.requiredApprovals).toBe(2);
252
+ });
253
+ it('should complete approval when enough reviewers approve', () => {
254
+ const session1 = createSessionWithPermissions([
255
+ 'quarantine:read',
256
+ 'quarantine:review',
257
+ 'quarantine:review_malicious',
258
+ ]);
259
+ const session2 = createMockSession({
260
+ userId: 'user-456',
261
+ email: 'reviewer2@example.com',
262
+ permissions: ['quarantine:read', 'quarantine:review', 'quarantine:review_malicious'],
263
+ });
264
+ const entry = repository.create({
265
+ skillId: 'test/malicious-skill',
266
+ source: 'test',
267
+ quarantineReason: 'Malicious code detected',
268
+ severity: 'MALICIOUS',
269
+ });
270
+ // First approval
271
+ const result1 = service.review(session1, entry.id, {
272
+ reviewStatus: 'approved',
273
+ reviewNotes: 'First approval',
274
+ });
275
+ expect(result1.approved).toBe(false);
276
+ // Second approval
277
+ const result2 = service.review(session2, entry.id, {
278
+ reviewStatus: 'approved',
279
+ reviewNotes: 'Second approval',
280
+ });
281
+ expect(result2.approved).toBe(true);
282
+ expect(result2.multiApprovalStatus?.isComplete).toBe(true);
283
+ });
284
+ it('should prevent same user from approving twice', () => {
285
+ const session = createSessionWithPermissions([
286
+ 'quarantine:read',
287
+ 'quarantine:review',
288
+ 'quarantine:review_malicious',
289
+ ]);
290
+ const entry = repository.create({
291
+ skillId: 'test/malicious-skill',
292
+ source: 'test',
293
+ quarantineReason: 'Malicious code detected',
294
+ severity: 'MALICIOUS',
295
+ });
296
+ // First approval should work
297
+ service.review(session, entry.id, {
298
+ reviewStatus: 'approved',
299
+ });
300
+ // Same user trying to approve again should fail
301
+ expect(() => service.review(session, entry.id, {
302
+ reviewStatus: 'approved',
303
+ })).toThrow('already approved');
304
+ });
305
+ it('should allow MALICIOUS rejection without multi-approval', () => {
306
+ const session = createSessionWithPermissions([
307
+ 'quarantine:read',
308
+ 'quarantine:review',
309
+ 'quarantine:review_malicious',
310
+ ]);
311
+ const entry = repository.create({
312
+ skillId: 'test/malicious-skill',
313
+ source: 'test',
314
+ quarantineReason: 'Malicious code detected',
315
+ severity: 'MALICIOUS',
316
+ });
317
+ // Rejection should work immediately (no multi-approval needed)
318
+ const result = service.review(session, entry.id, {
319
+ reviewStatus: 'rejected',
320
+ reviewNotes: 'Confirmed malicious',
321
+ });
322
+ expect(result.approved).toBe(false);
323
+ expect(result.multiApprovalStatus).toBeUndefined();
324
+ });
325
+ it('should track multi-approval status', () => {
326
+ const session = createSessionWithPermissions([
327
+ 'quarantine:read',
328
+ 'quarantine:review',
329
+ 'quarantine:review_malicious',
330
+ ]);
331
+ const entry = repository.create({
332
+ skillId: 'test/malicious-skill',
333
+ source: 'test',
334
+ quarantineReason: 'Malicious code detected',
335
+ severity: 'MALICIOUS',
336
+ });
337
+ // First approval
338
+ service.review(session, entry.id, {
339
+ reviewStatus: 'approved',
340
+ });
341
+ // Check status
342
+ const status = service.getMultiApprovalStatus(session, entry.id);
343
+ expect(status).not.toBeNull();
344
+ expect(status?.currentApprovals.length).toBe(1);
345
+ expect(status?.isComplete).toBe(false);
346
+ });
347
+ });
348
+ // ==========================================================================
349
+ // Error Handling Tests
350
+ // ==========================================================================
351
+ describe('Error Handling', () => {
352
+ it('should return typed error codes', () => {
353
+ const session = createExpiredSession();
354
+ try {
355
+ service.findById(session, 'some-id');
356
+ expect.fail('Should have thrown');
357
+ }
358
+ catch (error) {
359
+ expect(error).toBeInstanceOf(QuarantineServiceError);
360
+ expect(error.code).toBe('SESSION_EXPIRED');
361
+ expect(error.name).toBe('QuarantineServiceError');
362
+ }
363
+ });
364
+ it('should include error details for debugging', () => {
365
+ const session = createSessionWithPermissions([]);
366
+ try {
367
+ service.findById(session, 'some-id');
368
+ expect.fail('Should have thrown');
369
+ }
370
+ catch (error) {
371
+ expect(error).toBeInstanceOf(QuarantineServiceError);
372
+ expect(error.details).toBeDefined();
373
+ expect(error.details?.available).toEqual([]);
374
+ }
375
+ });
376
+ });
377
+ });
378
+ //# 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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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=check-references.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-references.test.d.ts","sourceRoot":"","sources":["../../../tests/unit/check-references.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,118 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { SkillParser } from '../../src/indexer/SkillParser.js';
3
+ describe('SkillParser.checkReferences', () => {
4
+ describe('default patterns', () => {
5
+ it('detects Docker container names (e.g., myproject-dev-1)', () => {
6
+ const content = 'Run: docker exec myproject-dev-1 npm test';
7
+ const result = SkillParser.checkReferences(content);
8
+ expect(result.matches).toHaveLength(1);
9
+ expect(result.matches[0]).toMatchObject({
10
+ line: 1,
11
+ text: 'myproject-dev-1',
12
+ pattern: 'Docker container name',
13
+ });
14
+ });
15
+ it('detects npm package scopes (e.g., @my-org/)', () => {
16
+ const content = 'import { foo } from "@my-org/utils"';
17
+ const result = SkillParser.checkReferences(content);
18
+ expect(result.matches).toHaveLength(1);
19
+ expect(result.matches[0]).toMatchObject({
20
+ text: '@my-org/',
21
+ pattern: 'npm package scope',
22
+ });
23
+ });
24
+ it('detects project URLs (e.g., https://myapp.app/)', () => {
25
+ const content = 'Visit https://myapp.app/ for more info';
26
+ const result = SkillParser.checkReferences(content);
27
+ expect(result.matches).toHaveLength(1);
28
+ expect(result.matches[0]).toMatchObject({
29
+ text: 'https://myapp.app/',
30
+ pattern: 'Project URL',
31
+ });
32
+ });
33
+ it('detects GitHub repo references (e.g., github.com/org/repo)', () => {
34
+ const content = 'Source: github.com/smith-horn/skillsmith';
35
+ const result = SkillParser.checkReferences(content);
36
+ expect(result.matches).toHaveLength(1);
37
+ expect(result.matches[0]).toMatchObject({
38
+ text: 'github.com/smith-horn/skillsmith',
39
+ pattern: 'GitHub repo reference',
40
+ });
41
+ });
42
+ it('detects specific line counts (e.g., 1212 lines)', () => {
43
+ const content = 'This file has 1212 lines of code';
44
+ const result = SkillParser.checkReferences(content);
45
+ expect(result.matches).toHaveLength(1);
46
+ expect(result.matches[0]).toMatchObject({
47
+ text: '1212 lines',
48
+ pattern: 'Specific line count',
49
+ });
50
+ });
51
+ });
52
+ describe('custom patterns', () => {
53
+ it('merges custom patterns with defaults', () => {
54
+ const content = 'Use ACME_SECRET_KEY for auth\ndocker exec acme-dev-1 test';
55
+ const custom = [/ACME_[A-Z_]+/g];
56
+ const result = SkillParser.checkReferences(content, custom);
57
+ const patterns = result.matches.map((m) => m.pattern);
58
+ expect(patterns).toContain('Custom pattern');
59
+ expect(patterns).toContain('Docker container name');
60
+ });
61
+ it('labels custom pattern matches as "Custom pattern"', () => {
62
+ const content = 'token: xoxb-abc-123';
63
+ const custom = [/xoxb-[a-z]+-\d+/g];
64
+ const result = SkillParser.checkReferences(content, custom);
65
+ expect(result.matches).toHaveLength(1);
66
+ expect(result.matches[0].pattern).toBe('Custom pattern');
67
+ });
68
+ });
69
+ describe('edge cases', () => {
70
+ it('returns empty results for content with no matches', () => {
71
+ const content = 'A simple skill that formats code.';
72
+ const result = SkillParser.checkReferences(content);
73
+ expect(result.warnings).toHaveLength(0);
74
+ expect(result.matches).toHaveLength(0);
75
+ });
76
+ it('handles empty string content', () => {
77
+ const result = SkillParser.checkReferences('');
78
+ expect(result.warnings).toHaveLength(0);
79
+ expect(result.matches).toHaveLength(0);
80
+ });
81
+ it('truncates matched text to 80 characters', () => {
82
+ // GitHub repo pattern matches [a-zA-Z-]+ for org and repo segments
83
+ const longRepo = 'github.com/' + 'a-'.repeat(50) + 'a/' + 'b-'.repeat(50) + 'b';
84
+ const result = SkillParser.checkReferences(longRepo);
85
+ const ghMatch = result.matches.find((m) => m.pattern === 'GitHub repo reference');
86
+ expect(ghMatch).toBeDefined();
87
+ expect(ghMatch.text).toHaveLength(83); // 80 + '...'
88
+ expect(ghMatch.text).toMatch(/\.\.\.$/u);
89
+ });
90
+ it('reports correct line numbers (1-based)', () => {
91
+ const content = 'line one\nline two\ngithub.com/org/repo on line three';
92
+ const result = SkillParser.checkReferences(content);
93
+ expect(result.matches).toHaveLength(1);
94
+ expect(result.matches[0].line).toBe(3);
95
+ });
96
+ it('handles multiple matches on the same line', () => {
97
+ const content = 'See github.com/org/repo and github.com/other/lib';
98
+ const result = SkillParser.checkReferences(content);
99
+ const ghMatches = result.matches.filter((m) => m.pattern === 'GitHub repo reference');
100
+ expect(ghMatches).toHaveLength(2);
101
+ expect(ghMatches[0].line).toBe(1);
102
+ expect(ghMatches[1].line).toBe(1);
103
+ });
104
+ });
105
+ describe('false positive awareness', () => {
106
+ it('matches legitimate npm scopes (documents false positive risk)', () => {
107
+ // The pattern /@[a-z]+-[a-z]+\// matches any hyphenated scope,
108
+ // including legitimate ones like @babel-core/ or @my-lib/.
109
+ // This is by design: the method flags for review, not rejection.
110
+ const content = 'import { x } from "@some-lib/core"';
111
+ const result = SkillParser.checkReferences(content);
112
+ expect(result.matches).toHaveLength(1);
113
+ expect(result.matches[0].pattern).toBe('npm package scope');
114
+ expect(result.warnings[0]).toContain('project-specific reference');
115
+ });
116
+ });
117
+ });
118
+ //# sourceMappingURL=check-references.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-references.test.js","sourceRoot":"","sources":["../../../tests/unit/check-references.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * SMI-2274: Safe filesystem operations tests
3
+ *
4
+ * Tests for symlink-safe file write operations.
5
+ * Covers:
6
+ * - Normal file writes
7
+ * - Symlink detection and rejection
8
+ * - Non-existent file creation
9
+ * - Explicit file permissions
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=safe-fs.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safe-fs.test.d.ts","sourceRoot":"","sources":["../../../tests/utils/safe-fs.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}