github-issue-tower-defence-management 1.148.21 → 1.148.23

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 (25) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/bin/adapter/entry-points/console/consoleReadApi.js +1 -11
  3. package/bin/adapter/entry-points/console/consoleReadApi.js.map +1 -1
  4. package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js +15 -3
  5. package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js.map +1 -1
  6. package/bin/domain/usecases/DailySecurityScanUseCase.js +46 -5
  7. package/bin/domain/usecases/DailySecurityScanUseCase.js.map +1 -1
  8. package/package.json +1 -1
  9. package/src/adapter/entry-points/console/consoleReadApi.test.ts +15 -47
  10. package/src/adapter/entry-points/console/consoleReadApi.ts +2 -20
  11. package/src/adapter/entry-points/console/ui/e2e/consoleTestHarness.ts +10 -1
  12. package/src/adapter/entry-points/handlers/inTmuxByHumanSessionReconciler.test.ts +1 -0
  13. package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.test.ts +87 -4
  14. package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.ts +24 -4
  15. package/src/domain/usecases/DailySecurityScanUseCase.test.ts +502 -195
  16. package/src/domain/usecases/DailySecurityScanUseCase.ts +60 -5
  17. package/src/domain/usecases/adapter-interfaces/IssueRepository.ts +1 -0
  18. package/src/domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase.test.ts +1 -0
  19. package/types/adapter/entry-points/console/consoleReadApi.d.ts.map +1 -1
  20. package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts +1 -0
  21. package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts.map +1 -1
  22. package/types/domain/usecases/DailySecurityScanUseCase.d.ts +1 -0
  23. package/types/domain/usecases/DailySecurityScanUseCase.d.ts.map +1 -1
  24. package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts +1 -0
  25. package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts.map +1 -1
@@ -75,6 +75,13 @@ describe('DailySecurityScanUseCase', () => {
75
75
  exitCode: 0,
76
76
  };
77
77
  }
78
+ if (program === 'mktemp') {
79
+ return {
80
+ stdout: `/tmp/${args[args.length - 1].replace('XXXXXX', 'abc123')}\n`,
81
+ stderr: '',
82
+ exitCode: 0,
83
+ };
84
+ }
78
85
  if (program === 'git') {
79
86
  return {
80
87
  stdout: 'git@github.com:example-org/app.git\n',
@@ -116,14 +123,40 @@ describe('DailySecurityScanUseCase', () => {
116
123
  ]);
117
124
  });
118
125
 
119
- it('does not create an issue when osv-scanner reports no vulnerabilities', async () => {
120
- const { useCase, mockLocalCommandRunner, mockIssueRepository } =
121
- buildUseCase();
122
-
123
- mockLocalCommandRunner.runCommand.mockImplementation(async (program) => {
126
+ const buildDefaultBranchScanRunner = () => {
127
+ const scannerFindings = JSON.stringify({
128
+ results: [
129
+ {
130
+ packages: [
131
+ {
132
+ package: {
133
+ name: 'example-library',
134
+ version: '1.2.3',
135
+ ecosystem: 'npm',
136
+ },
137
+ vulnerabilities: [
138
+ {
139
+ id: 'GHSA-1111-2222-3333',
140
+ aliases: [],
141
+ summary: 'Example Library Deserialization',
142
+ },
143
+ ],
144
+ },
145
+ ],
146
+ },
147
+ ],
148
+ });
149
+ return async (program: string, args: string[]) => {
124
150
  if (program === 'find') {
125
151
  return {
126
- stdout: '/repos/example-org/app/.git\n',
152
+ stdout: '/repos/workspace1/app/.git\n/repos/workspace2/app/.git\n',
153
+ stderr: '',
154
+ exitCode: 0,
155
+ };
156
+ }
157
+ if (program === 'mktemp') {
158
+ return {
159
+ stdout: '/tmp/tdpm-daily-security-scan-app-abc123\n',
127
160
  stderr: '',
128
161
  exitCode: 0,
129
162
  };
@@ -135,9 +168,100 @@ describe('DailySecurityScanUseCase', () => {
135
168
  exitCode: 0,
136
169
  };
137
170
  }
138
- return { stdout: 'no vulnerabilities', stderr: '', exitCode: 0 };
171
+ if (program === 'osv-scanner') {
172
+ return { stdout: scannerFindings, stderr: '', exitCode: 1 };
173
+ }
174
+ if (program === 'rm') {
175
+ return { stdout: args.join(' '), stderr: '', exitCode: 0 };
176
+ }
177
+ return { stdout: '', stderr: '', exitCode: 0 };
178
+ };
179
+ };
180
+
181
+ it('scans one fresh checkout of the default branch instead of the working copies on disk', async () => {
182
+ const { useCase, mockLocalCommandRunner, mockIssueRepository } =
183
+ buildUseCase();
184
+
185
+ mockIssueRepository.searchIssue.mockResolvedValue([]);
186
+ mockLocalCommandRunner.runCommand.mockImplementation(
187
+ buildDefaultBranchScanRunner(),
188
+ );
189
+
190
+ await useCase.run({
191
+ targetDates: [new Date('2024-01-02T05:00:00Z')],
192
+ org: 'example-org',
193
+ manager: 'manager-name',
194
+ dailySecurityScan: {
195
+ scanBaseDirectory: '/repos',
196
+ targetHourUtc: 5,
197
+ },
139
198
  });
140
199
 
200
+ const cloneCalls = mockLocalCommandRunner.runCommand.mock.calls.filter(
201
+ (call) => call[0] === 'git' && call[1][0] === 'clone',
202
+ );
203
+ expect(cloneCalls).toHaveLength(1);
204
+ expect(cloneCalls[0][1]).toEqual([
205
+ 'clone',
206
+ '--depth',
207
+ '1',
208
+ 'git@github.com:example-org/app.git',
209
+ '/tmp/tdpm-daily-security-scan-app-abc123',
210
+ ]);
211
+
212
+ const scanCalls = mockLocalCommandRunner.runCommand.mock.calls.filter(
213
+ (call) => call[0] === 'osv-scanner',
214
+ );
215
+ expect(scanCalls).toHaveLength(1);
216
+ expect(scanCalls[0][1][scanCalls[0][1].indexOf('-r') + 1]).toBe(
217
+ '/tmp/tdpm-daily-security-scan-app-abc123',
218
+ );
219
+ expect(mockIssueRepository.createNewIssue.mock.calls).toHaveLength(1);
220
+ expect(mockIssueRepository.createNewIssue.mock.calls[0][1]).toBe('app');
221
+ });
222
+
223
+ it('logs an error and scans nothing for a repository whose default branch clone fails', async () => {
224
+ const { useCase, mockLocalCommandRunner, mockIssueRepository } =
225
+ buildUseCase();
226
+ const errorSpy = jest
227
+ .spyOn(console, 'error')
228
+ .mockImplementation(() => undefined);
229
+
230
+ mockIssueRepository.searchIssue.mockResolvedValue([]);
231
+ mockLocalCommandRunner.runCommand.mockImplementation(
232
+ async (program, args) => {
233
+ if (program === 'find') {
234
+ return {
235
+ stdout: '/repos/workspace1/app/.git\n',
236
+ stderr: '',
237
+ exitCode: 0,
238
+ };
239
+ }
240
+ if (program === 'mktemp') {
241
+ return {
242
+ stdout: '/tmp/tdpm-daily-security-scan-app-abc123\n',
243
+ stderr: '',
244
+ exitCode: 0,
245
+ };
246
+ }
247
+ if (program === 'git' && args[0] === 'clone') {
248
+ return {
249
+ stdout: '',
250
+ stderr: 'fatal: repository not found',
251
+ exitCode: 128,
252
+ };
253
+ }
254
+ if (program === 'git') {
255
+ return {
256
+ stdout: 'git@github.com:example-org/app.git\n',
257
+ stderr: '',
258
+ exitCode: 0,
259
+ };
260
+ }
261
+ return { stdout: '', stderr: '', exitCode: 0 };
262
+ },
263
+ );
264
+
141
265
  await useCase.run({
142
266
  targetDates: [new Date('2024-01-02T05:00:00Z')],
143
267
  org: 'example-org',
@@ -148,31 +272,125 @@ describe('DailySecurityScanUseCase', () => {
148
272
  },
149
273
  });
150
274
 
275
+ expect(errorSpy).toHaveBeenCalledWith(
276
+ expect.stringContaining('Failed to clone the default branch of app'),
277
+ );
278
+ const scanCalls = mockLocalCommandRunner.runCommand.mock.calls.filter(
279
+ (call) => call[0] === 'osv-scanner',
280
+ );
281
+ expect(scanCalls).toHaveLength(0);
282
+ const removeCalls = mockLocalCommandRunner.runCommand.mock.calls.filter(
283
+ (call) => call[0] === 'rm',
284
+ );
285
+ expect(removeCalls).toHaveLength(1);
151
286
  expect(mockIssueRepository.createNewIssue.mock.calls).toHaveLength(0);
287
+ errorSpy.mockRestore();
152
288
  });
153
289
 
154
- it('skips repositories whose origin remote does not belong to the org', async () => {
290
+ it('removes the temporary checkout after scanning it', async () => {
155
291
  const { useCase, mockLocalCommandRunner, mockIssueRepository } =
156
292
  buildUseCase();
157
293
 
158
- mockLocalCommandRunner.runCommand.mockImplementation(async (program) => {
159
- if (program === 'find') {
160
- return {
161
- stdout: '/repos/other-org/app/.git\n',
162
- stderr: '',
163
- exitCode: 0,
164
- };
165
- }
166
- if (program === 'git') {
167
- return {
168
- stdout: 'git@github.com:other-org/app.git\n',
169
- stderr: '',
170
- exitCode: 0,
171
- };
172
- }
173
- return { stdout: '', stderr: '', exitCode: 1 };
294
+ mockIssueRepository.searchIssue.mockResolvedValue([]);
295
+ mockLocalCommandRunner.runCommand.mockImplementation(
296
+ buildDefaultBranchScanRunner(),
297
+ );
298
+
299
+ await useCase.run({
300
+ targetDates: [new Date('2024-01-02T05:00:00Z')],
301
+ org: 'example-org',
302
+ manager: 'manager-name',
303
+ dailySecurityScan: {
304
+ scanBaseDirectory: '/repos',
305
+ targetHourUtc: 5,
306
+ },
307
+ });
308
+
309
+ const removeCalls = mockLocalCommandRunner.runCommand.mock.calls.filter(
310
+ (call) => call[0] === 'rm',
311
+ );
312
+ expect(removeCalls).toHaveLength(1);
313
+ expect(removeCalls[0][1]).toEqual([
314
+ '-rf',
315
+ '/tmp/tdpm-daily-security-scan-app-abc123',
316
+ ]);
317
+ });
318
+
319
+ it('does not create an issue when osv-scanner reports no vulnerabilities', async () => {
320
+ const { useCase, mockLocalCommandRunner, mockIssueRepository } =
321
+ buildUseCase();
322
+
323
+ mockLocalCommandRunner.runCommand.mockImplementation(
324
+ async (program, args) => {
325
+ if (program === 'find') {
326
+ return {
327
+ stdout: '/repos/example-org/app/.git\n',
328
+ stderr: '',
329
+ exitCode: 0,
330
+ };
331
+ }
332
+ if (program === 'mktemp') {
333
+ return {
334
+ stdout: `/tmp/${args[args.length - 1].replace('XXXXXX', 'abc123')}\n`,
335
+ stderr: '',
336
+ exitCode: 0,
337
+ };
338
+ }
339
+ if (program === 'git') {
340
+ return {
341
+ stdout: 'git@github.com:example-org/app.git\n',
342
+ stderr: '',
343
+ exitCode: 0,
344
+ };
345
+ }
346
+ return { stdout: 'no vulnerabilities', stderr: '', exitCode: 0 };
347
+ },
348
+ );
349
+
350
+ await useCase.run({
351
+ targetDates: [new Date('2024-01-02T05:00:00Z')],
352
+ org: 'example-org',
353
+ manager: 'manager-name',
354
+ dailySecurityScan: {
355
+ scanBaseDirectory: '/repos',
356
+ targetHourUtc: 5,
357
+ },
174
358
  });
175
359
 
360
+ expect(mockIssueRepository.createNewIssue.mock.calls).toHaveLength(0);
361
+ });
362
+
363
+ it('skips repositories whose origin remote does not belong to the org', async () => {
364
+ const { useCase, mockLocalCommandRunner, mockIssueRepository } =
365
+ buildUseCase();
366
+
367
+ mockLocalCommandRunner.runCommand.mockImplementation(
368
+ async (program, args) => {
369
+ if (program === 'find') {
370
+ return {
371
+ stdout: '/repos/other-org/app/.git\n',
372
+ stderr: '',
373
+ exitCode: 0,
374
+ };
375
+ }
376
+ if (program === 'mktemp') {
377
+ return {
378
+ stdout: `/tmp/${args[args.length - 1].replace('XXXXXX', 'abc123')}\n`,
379
+ stderr: '',
380
+ exitCode: 0,
381
+ };
382
+ }
383
+ if (program === 'git') {
384
+ return {
385
+ stdout: 'git@github.com:other-org/app.git\n',
386
+ stderr: '',
387
+ exitCode: 0,
388
+ };
389
+ }
390
+ return { stdout: '', stderr: '', exitCode: 1 };
391
+ },
392
+ );
393
+
176
394
  await useCase.run({
177
395
  targetDates: [new Date('2024-01-02T05:00:00Z')],
178
396
  org: 'example-org',
@@ -195,23 +413,32 @@ describe('DailySecurityScanUseCase', () => {
195
413
  const { useCase, mockLocalCommandRunner, mockIssueRepository } =
196
414
  buildUseCase();
197
415
 
198
- mockLocalCommandRunner.runCommand.mockImplementation(async (program) => {
199
- if (program === 'find') {
200
- return {
201
- stdout: '/repos/example-org/app/.git\n',
202
- stderr: '',
203
- exitCode: 0,
204
- };
205
- }
206
- if (program === 'git') {
207
- return {
208
- stdout: '',
209
- stderr: 'fatal: No such remote',
210
- exitCode: 2,
211
- };
212
- }
213
- return { stdout: '', stderr: '', exitCode: 1 };
214
- });
416
+ mockLocalCommandRunner.runCommand.mockImplementation(
417
+ async (program, args) => {
418
+ if (program === 'find') {
419
+ return {
420
+ stdout: '/repos/example-org/app/.git\n',
421
+ stderr: '',
422
+ exitCode: 0,
423
+ };
424
+ }
425
+ if (program === 'mktemp') {
426
+ return {
427
+ stdout: `/tmp/${args[args.length - 1].replace('XXXXXX', 'abc123')}\n`,
428
+ stderr: '',
429
+ exitCode: 0,
430
+ };
431
+ }
432
+ if (program === 'git') {
433
+ return {
434
+ stdout: '',
435
+ stderr: 'fatal: No such remote',
436
+ exitCode: 2,
437
+ };
438
+ }
439
+ return { stdout: '', stderr: '', exitCode: 1 };
440
+ },
441
+ );
215
442
 
216
443
  await useCase.run({
217
444
  targetDates: [new Date('2024-01-02T05:00:00Z')],
@@ -235,23 +462,32 @@ describe('DailySecurityScanUseCase', () => {
235
462
  const { useCase, mockLocalCommandRunner, mockHttpRepository } =
236
463
  buildUseCase();
237
464
 
238
- mockLocalCommandRunner.runCommand.mockImplementation(async (program) => {
239
- if (program === 'find') {
240
- return {
241
- stdout: '/repos/example-org/app/.git\n',
242
- stderr: '',
243
- exitCode: 0,
244
- };
245
- }
246
- if (program === 'git') {
247
- return {
248
- stdout: 'git@github.com:example-org/app.git\n',
249
- stderr: '',
250
- exitCode: 0,
251
- };
252
- }
253
- return { stdout: '', stderr: '', exitCode: 0 };
254
- });
465
+ mockLocalCommandRunner.runCommand.mockImplementation(
466
+ async (program, args) => {
467
+ if (program === 'find') {
468
+ return {
469
+ stdout: '/repos/example-org/app/.git\n',
470
+ stderr: '',
471
+ exitCode: 0,
472
+ };
473
+ }
474
+ if (program === 'mktemp') {
475
+ return {
476
+ stdout: `/tmp/${args[args.length - 1].replace('XXXXXX', 'abc123')}\n`,
477
+ stderr: '',
478
+ exitCode: 0,
479
+ };
480
+ }
481
+ if (program === 'git') {
482
+ return {
483
+ stdout: 'git@github.com:example-org/app.git\n',
484
+ stderr: '',
485
+ exitCode: 0,
486
+ };
487
+ }
488
+ return { stdout: '', stderr: '', exitCode: 0 };
489
+ },
490
+ );
255
491
 
256
492
  await useCase.run({
257
493
  targetDates: [new Date('2024-01-02T05:00:00Z')],
@@ -271,23 +507,32 @@ describe('DailySecurityScanUseCase', () => {
271
507
  const { useCase, mockLocalCommandRunner, mockHttpRepository } =
272
508
  buildUseCase();
273
509
 
274
- mockLocalCommandRunner.runCommand.mockImplementation(async (program) => {
275
- if (program === 'find') {
276
- return {
277
- stdout: '/repos/example-org/app/.git\n',
278
- stderr: '',
279
- exitCode: 0,
280
- };
281
- }
282
- if (program === 'git') {
283
- return {
284
- stdout: 'git@github.com:example-org/app.git\n',
285
- stderr: '',
286
- exitCode: 0,
287
- };
288
- }
289
- return { stdout: '', stderr: '', exitCode: 0 };
290
- });
510
+ mockLocalCommandRunner.runCommand.mockImplementation(
511
+ async (program, args) => {
512
+ if (program === 'find') {
513
+ return {
514
+ stdout: '/repos/example-org/app/.git\n',
515
+ stderr: '',
516
+ exitCode: 0,
517
+ };
518
+ }
519
+ if (program === 'mktemp') {
520
+ return {
521
+ stdout: `/tmp/${args[args.length - 1].replace('XXXXXX', 'abc123')}\n`,
522
+ stderr: '',
523
+ exitCode: 0,
524
+ };
525
+ }
526
+ if (program === 'git') {
527
+ return {
528
+ stdout: 'git@github.com:example-org/app.git\n',
529
+ stderr: '',
530
+ exitCode: 0,
531
+ };
532
+ }
533
+ return { stdout: '', stderr: '', exitCode: 0 };
534
+ },
535
+ );
291
536
 
292
537
  await useCase.run({
293
538
  targetDates: [new Date('2024-01-02T05:00:00Z')],
@@ -312,52 +557,64 @@ describe('DailySecurityScanUseCase', () => {
312
557
  } = buildUseCase();
313
558
 
314
559
  mockIssueRepository.searchIssue.mockResolvedValue([]);
315
- mockLocalCommandRunner.runCommand.mockImplementation(async (program) => {
316
- if (program === 'find') {
317
- return {
318
- stdout: '/repos/example-org/app/.git\n',
319
- stderr: '',
320
- exitCode: 0,
321
- };
322
- }
323
- if (program === 'git') {
324
- return {
325
- stdout: 'git@github.com:example-org/app.git\n',
326
- stderr: '',
327
- exitCode: 0,
328
- };
329
- }
330
- if (program === 'osv-scanner') {
331
- return {
332
- stdout: JSON.stringify({
333
- results: [
334
- {
335
- source: { path: '/repos/example-org/app', type: 'lockfile' },
336
- packages: [
337
- {
338
- package: {
339
- name: 'example-library',
340
- version: '1.2.3',
341
- ecosystem: 'npm',
342
- },
343
- vulnerabilities: [
344
- {
345
- id: 'GHSA-1111-2222-3333',
346
- aliases: ['CVE-2024-0001', 'CVE-2023-9999'],
347
- summary: 'New Vulnerability',
348
- },
349
- ],
560
+ mockLocalCommandRunner.runCommand.mockImplementation(
561
+ async (program, args) => {
562
+ if (program === 'find') {
563
+ return {
564
+ stdout: '/repos/example-org/app/.git\n',
565
+ stderr: '',
566
+ exitCode: 0,
567
+ };
568
+ }
569
+ if (program === 'mktemp') {
570
+ return {
571
+ stdout: `/tmp/${args[args.length - 1].replace('XXXXXX', 'abc123')}\n`,
572
+ stderr: '',
573
+ exitCode: 0,
574
+ };
575
+ }
576
+ if (program === 'git') {
577
+ return {
578
+ stdout: 'git@github.com:example-org/app.git\n',
579
+ stderr: '',
580
+ exitCode: 0,
581
+ };
582
+ }
583
+ if (program === 'osv-scanner') {
584
+ return {
585
+ stdout: JSON.stringify({
586
+ results: [
587
+ {
588
+ source: {
589
+ path: '/repos/example-org/app',
590
+ type: 'lockfile',
350
591
  },
351
- ],
352
- },
353
- ],
354
- }),
355
- stderr: '',
356
- exitCode: 1,
357
- };
358
- }
359
- return { stdout: '', stderr: '', exitCode: 0 };
360
- });
592
+ packages: [
593
+ {
594
+ package: {
595
+ name: 'example-library',
596
+ version: '1.2.3',
597
+ ecosystem: 'npm',
598
+ },
599
+ vulnerabilities: [
600
+ {
601
+ id: 'GHSA-1111-2222-3333',
602
+ aliases: ['CVE-2024-0001', 'CVE-2023-9999'],
603
+ summary: 'New Vulnerability',
604
+ },
605
+ ],
606
+ },
607
+ ],
608
+ },
609
+ ],
610
+ }),
611
+ stderr: '',
612
+ exitCode: 1,
613
+ };
614
+ }
615
+ return { stdout: '', stderr: '', exitCode: 0 };
616
+ },
617
+ );
361
618
  mockHttpRepository.get.mockResolvedValue(
362
619
  JSON.stringify({
363
620
  vulnerabilities: [
@@ -414,23 +671,32 @@ describe('DailySecurityScanUseCase', () => {
414
671
  mockHttpRepository,
415
672
  } = buildUseCase();
416
673
 
417
- mockLocalCommandRunner.runCommand.mockImplementation(async (program) => {
418
- if (program === 'find') {
419
- return {
420
- stdout: '/repos/example-org/app/.git\n',
421
- stderr: '',
422
- exitCode: 0,
423
- };
424
- }
425
- if (program === 'git') {
426
- return {
427
- stdout: 'git@github.com:example-org/app.git\n',
428
- stderr: '',
429
- exitCode: 0,
430
- };
431
- }
432
- return { stdout: '', stderr: '', exitCode: 0 };
433
- });
674
+ mockLocalCommandRunner.runCommand.mockImplementation(
675
+ async (program, args) => {
676
+ if (program === 'find') {
677
+ return {
678
+ stdout: '/repos/example-org/app/.git\n',
679
+ stderr: '',
680
+ exitCode: 0,
681
+ };
682
+ }
683
+ if (program === 'mktemp') {
684
+ return {
685
+ stdout: `/tmp/${args[args.length - 1].replace('XXXXXX', 'abc123')}\n`,
686
+ stderr: '',
687
+ exitCode: 0,
688
+ };
689
+ }
690
+ if (program === 'git') {
691
+ return {
692
+ stdout: 'git@github.com:example-org/app.git\n',
693
+ stderr: '',
694
+ exitCode: 0,
695
+ };
696
+ }
697
+ return { stdout: '', stderr: '', exitCode: 0 };
698
+ },
699
+ );
434
700
  mockHttpRepository.get.mockResolvedValue(
435
701
  JSON.stringify({
436
702
  vulnerabilities: [
@@ -466,23 +732,32 @@ describe('DailySecurityScanUseCase', () => {
466
732
  const { useCase, mockLocalCommandRunner, mockHttpRepository } =
467
733
  buildUseCase();
468
734
 
469
- mockLocalCommandRunner.runCommand.mockImplementation(async (program) => {
470
- if (program === 'find') {
471
- return {
472
- stdout: '/repos/example-org/app/.git\n',
473
- stderr: '',
474
- exitCode: 0,
475
- };
476
- }
477
- if (program === 'git') {
478
- return {
479
- stdout: 'git@github.com:example-org/app.git\n',
480
- stderr: '',
481
- exitCode: 0,
482
- };
483
- }
484
- return { stdout: '', stderr: '', exitCode: 0 };
485
- });
735
+ mockLocalCommandRunner.runCommand.mockImplementation(
736
+ async (program, args) => {
737
+ if (program === 'find') {
738
+ return {
739
+ stdout: '/repos/example-org/app/.git\n',
740
+ stderr: '',
741
+ exitCode: 0,
742
+ };
743
+ }
744
+ if (program === 'mktemp') {
745
+ return {
746
+ stdout: `/tmp/${args[args.length - 1].replace('XXXXXX', 'abc123')}\n`,
747
+ stderr: '',
748
+ exitCode: 0,
749
+ };
750
+ }
751
+ if (program === 'git') {
752
+ return {
753
+ stdout: 'git@github.com:example-org/app.git\n',
754
+ stderr: '',
755
+ exitCode: 0,
756
+ };
757
+ }
758
+ return { stdout: '', stderr: '', exitCode: 0 };
759
+ },
760
+ );
486
761
  mockHttpRepository.get.mockResolvedValue(
487
762
  JSON.stringify({ unexpected: 'structure' }),
488
763
  );
@@ -553,6 +828,13 @@ describe('DailySecurityScanUseCase', () => {
553
828
  exitCode: 0,
554
829
  };
555
830
  }
831
+ if (program === 'mktemp') {
832
+ return {
833
+ stdout: `/tmp/${args[args.length - 1].replace('XXXXXX', 'abc123')}\n`,
834
+ stderr: '',
835
+ exitCode: 0,
836
+ };
837
+ }
556
838
  if (program === 'git') {
557
839
  const dir = args[1];
558
840
  const repo = dir.split('/').pop() ?? '';
@@ -633,26 +915,35 @@ describe('DailySecurityScanUseCase', () => {
633
915
  },
634
916
  ]);
635
917
 
636
- mockLocalCommandRunner.runCommand.mockImplementation(async (program) => {
637
- if (program === 'find') {
638
- return {
639
- stdout: '/repos/example-org/app/.git\n',
640
- stderr: '',
641
- exitCode: 0,
642
- };
643
- }
644
- if (program === 'git') {
645
- return {
646
- stdout: 'git@github.com:example-org/app.git\n',
647
- stderr: '',
648
- exitCode: 0,
649
- };
650
- }
651
- if (program === 'osv-scanner') {
652
- return { stdout: 'vulnerability found', stderr: '', exitCode: 1 };
653
- }
654
- return { stdout: '', stderr: '', exitCode: 0 };
655
- });
918
+ mockLocalCommandRunner.runCommand.mockImplementation(
919
+ async (program, args) => {
920
+ if (program === 'find') {
921
+ return {
922
+ stdout: '/repos/example-org/app/.git\n',
923
+ stderr: '',
924
+ exitCode: 0,
925
+ };
926
+ }
927
+ if (program === 'mktemp') {
928
+ return {
929
+ stdout: `/tmp/${args[args.length - 1].replace('XXXXXX', 'abc123')}\n`,
930
+ stderr: '',
931
+ exitCode: 0,
932
+ };
933
+ }
934
+ if (program === 'git') {
935
+ return {
936
+ stdout: 'git@github.com:example-org/app.git\n',
937
+ stderr: '',
938
+ exitCode: 0,
939
+ };
940
+ }
941
+ if (program === 'osv-scanner') {
942
+ return { stdout: 'vulnerability found', stderr: '', exitCode: 1 };
943
+ }
944
+ return { stdout: '', stderr: '', exitCode: 0 };
945
+ },
946
+ );
656
947
 
657
948
  await useCase.run({
658
949
  targetDates: [new Date('2024-01-02T05:00:00Z')],
@@ -680,26 +971,35 @@ describe('DailySecurityScanUseCase', () => {
680
971
 
681
972
  mockIssueRepository.searchIssue.mockResolvedValue([]);
682
973
 
683
- mockLocalCommandRunner.runCommand.mockImplementation(async (program) => {
684
- if (program === 'find') {
685
- return {
686
- stdout: '/repos/example-org/app/.git\n',
687
- stderr: '',
688
- exitCode: 0,
689
- };
690
- }
691
- if (program === 'git') {
692
- return {
693
- stdout: 'git@github.com:example-org/app.git\n',
694
- stderr: '',
695
- exitCode: 0,
696
- };
697
- }
698
- if (program === 'osv-scanner') {
699
- return { stdout: 'vulnerability found', stderr: '', exitCode: 1 };
700
- }
701
- return { stdout: '', stderr: '', exitCode: 0 };
702
- });
974
+ mockLocalCommandRunner.runCommand.mockImplementation(
975
+ async (program, args) => {
976
+ if (program === 'find') {
977
+ return {
978
+ stdout: '/repos/example-org/app/.git\n',
979
+ stderr: '',
980
+ exitCode: 0,
981
+ };
982
+ }
983
+ if (program === 'mktemp') {
984
+ return {
985
+ stdout: `/tmp/${args[args.length - 1].replace('XXXXXX', 'abc123')}\n`,
986
+ stderr: '',
987
+ exitCode: 0,
988
+ };
989
+ }
990
+ if (program === 'git') {
991
+ return {
992
+ stdout: 'git@github.com:example-org/app.git\n',
993
+ stderr: '',
994
+ exitCode: 0,
995
+ };
996
+ }
997
+ if (program === 'osv-scanner') {
998
+ return { stdout: 'vulnerability found', stderr: '', exitCode: 1 };
999
+ }
1000
+ return { stdout: '', stderr: '', exitCode: 0 };
1001
+ },
1002
+ );
703
1003
 
704
1004
  await useCase.run({
705
1005
  targetDates: [new Date('2024-01-02T05:00:00Z')],
@@ -754,7 +1054,7 @@ describe('DailySecurityScanUseCase', () => {
754
1054
  const built = buildUseCase();
755
1055
  built.mockIssueRepository.searchIssue.mockResolvedValue([]);
756
1056
  built.mockLocalCommandRunner.runCommand.mockImplementation(
757
- async (program) => {
1057
+ async (program, args) => {
758
1058
  if (program === 'find') {
759
1059
  return {
760
1060
  stdout: '/repos/example-org/app/.git\n',
@@ -762,6 +1062,13 @@ describe('DailySecurityScanUseCase', () => {
762
1062
  exitCode: 0,
763
1063
  };
764
1064
  }
1065
+ if (program === 'mktemp') {
1066
+ return {
1067
+ stdout: `/tmp/${args[args.length - 1].replace('XXXXXX', 'abc123')}\n`,
1068
+ stderr: '',
1069
+ exitCode: 0,
1070
+ };
1071
+ }
765
1072
  if (program === 'git') {
766
1073
  return {
767
1074
  stdout: 'git@github.com:example-org/app.git\n',