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

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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## [1.148.22](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.148.21...v1.148.22) (2026-08-13)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **security-scan:** scan a fresh checkout of the default branch instead of stale working copies ([#1509](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/1509)) ([f8033b8](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/commit/f8033b880ce3b1d2012ab6498c001f5c1c5a6388))
7
+
1
8
  ## [1.148.21](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.148.20...v1.148.21) (2026-08-13)
2
9
 
3
10
 
@@ -97,7 +97,7 @@ class DailySecurityScanUseCase {
97
97
  console.error(`No repositories found in scan base directory: ${config.scanBaseDirectory}`);
98
98
  return [];
99
99
  }
100
- const scannedVulnerablePackages = [];
100
+ const remoteUrlByRepositoryName = new Map();
101
101
  for (const repositoryDirectory of repositoryDirectories) {
102
102
  const { stdout: remoteUrl, exitCode: remoteExitCode } = await this.localCommandRunner.runCommand('git', [
103
103
  '-C',
@@ -115,21 +115,34 @@ class DailySecurityScanUseCase {
115
115
  if (!remoteMatch || remoteMatch[1] !== org) {
116
116
  continue;
117
117
  }
118
- const repositoryOrg = remoteMatch[1];
119
- const repositoryName = remoteMatch[2];
118
+ if (!remoteUrlByRepositoryName.has(remoteMatch[2])) {
119
+ remoteUrlByRepositoryName.set(remoteMatch[2], remoteUrl.trim());
120
+ }
121
+ }
122
+ const scannedVulnerablePackages = [];
123
+ for (const [repositoryName, remoteUrl] of remoteUrlByRepositoryName) {
124
+ const repositoryOrg = org;
125
+ const checkoutDirectory = await this.checkoutDefaultBranch(repositoryName, remoteUrl);
126
+ if (checkoutDirectory === null) {
127
+ continue;
128
+ }
120
129
  const { stdout: scanOutput, stderr: scanStderr, exitCode: scanExitCode, } = await this.localCommandRunner.runCommand('osv-scanner', [
121
130
  'scan',
122
131
  'source',
123
132
  '-r',
124
- repositoryDirectory,
133
+ checkoutDirectory,
125
134
  '--format',
126
135
  'json',
127
136
  ]);
137
+ await this.localCommandRunner.runCommand('rm', [
138
+ '-rf',
139
+ checkoutDirectory,
140
+ ]);
128
141
  if (scanExitCode === 0) {
129
142
  continue;
130
143
  }
131
144
  if (scanExitCode !== 1) {
132
- console.error(`osv-scanner failed with exit code ${scanExitCode} for ${repositoryDirectory}: ${scanStderr}`);
145
+ console.error(`osv-scanner failed with exit code ${scanExitCode} for ${repositoryName}: ${scanStderr}`);
133
146
  continue;
134
147
  }
135
148
  const vulnerablePackages = parseScannerVulnerabilities(repositoryName, scanOutput);
@@ -152,6 +165,34 @@ class DailySecurityScanUseCase {
152
165
  }
153
166
  return scannedVulnerablePackages;
154
167
  };
168
+ this.checkoutDefaultBranch = async (repositoryName, remoteUrl) => {
169
+ const { stdout: checkoutDirectoryOutput, exitCode: checkoutDirectoryExit } = await this.localCommandRunner.runCommand('mktemp', [
170
+ '-d',
171
+ '-t',
172
+ `tdpm-daily-security-scan-${repositoryName}-XXXXXX`,
173
+ ]);
174
+ const checkoutDirectory = checkoutDirectoryOutput.trim();
175
+ if (checkoutDirectoryExit !== 0 || checkoutDirectory.length === 0) {
176
+ console.error(`Failed to create a checkout directory for ${repositoryName}`);
177
+ return null;
178
+ }
179
+ const { stderr: cloneStderr, exitCode: cloneExitCode } = await this.localCommandRunner.runCommand('git', [
180
+ 'clone',
181
+ '--depth',
182
+ '1',
183
+ remoteUrl,
184
+ checkoutDirectory,
185
+ ]);
186
+ if (cloneExitCode !== 0) {
187
+ console.error(`Failed to clone the default branch of ${repositoryName}: ${cloneStderr}`);
188
+ await this.localCommandRunner.runCommand('rm', [
189
+ '-rf',
190
+ checkoutDirectory,
191
+ ]);
192
+ return null;
193
+ }
194
+ return checkoutDirectory;
195
+ };
155
196
  this.reportKevAdditions = async (org, manager, lastTargetDate, config, scannedVulnerablePackages) => {
156
197
  if (!config.enableKevNvdReport || !config.kevReportRepo) {
157
198
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"DailySecurityScanUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/DailySecurityScanUseCase.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DailySecurityScanUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/DailySecurityScanUseCase.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github-issue-tower-defence-management",
3
- "version": "1.148.21",
3
+ "version": "1.148.22",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "scripts": {
@@ -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',
@@ -197,7 +197,7 @@ export class DailySecurityScanUseCase {
197
197
  return [];
198
198
  }
199
199
 
200
- const scannedVulnerablePackages: ScannedVulnerablePackage[] = [];
200
+ const remoteUrlByRepositoryName = new Map<string, string>();
201
201
  for (const repositoryDirectory of repositoryDirectories) {
202
202
  const { stdout: remoteUrl, exitCode: remoteExitCode } =
203
203
  await this.localCommandRunner.runCommand('git', [
@@ -217,8 +217,21 @@ export class DailySecurityScanUseCase {
217
217
  if (!remoteMatch || remoteMatch[1] !== org) {
218
218
  continue;
219
219
  }
220
- const repositoryOrg = remoteMatch[1];
221
- const repositoryName = remoteMatch[2];
220
+ if (!remoteUrlByRepositoryName.has(remoteMatch[2])) {
221
+ remoteUrlByRepositoryName.set(remoteMatch[2], remoteUrl.trim());
222
+ }
223
+ }
224
+
225
+ const scannedVulnerablePackages: ScannedVulnerablePackage[] = [];
226
+ for (const [repositoryName, remoteUrl] of remoteUrlByRepositoryName) {
227
+ const repositoryOrg = org;
228
+ const checkoutDirectory = await this.checkoutDefaultBranch(
229
+ repositoryName,
230
+ remoteUrl,
231
+ );
232
+ if (checkoutDirectory === null) {
233
+ continue;
234
+ }
222
235
 
223
236
  const {
224
237
  stdout: scanOutput,
@@ -228,16 +241,20 @@ export class DailySecurityScanUseCase {
228
241
  'scan',
229
242
  'source',
230
243
  '-r',
231
- repositoryDirectory,
244
+ checkoutDirectory,
232
245
  '--format',
233
246
  'json',
234
247
  ]);
248
+ await this.localCommandRunner.runCommand('rm', [
249
+ '-rf',
250
+ checkoutDirectory,
251
+ ]);
235
252
  if (scanExitCode === 0) {
236
253
  continue;
237
254
  }
238
255
  if (scanExitCode !== 1) {
239
256
  console.error(
240
- `osv-scanner failed with exit code ${scanExitCode} for ${repositoryDirectory}: ${scanStderr}`,
257
+ `osv-scanner failed with exit code ${scanExitCode} for ${repositoryName}: ${scanStderr}`,
241
258
  );
242
259
  continue;
243
260
  }
@@ -278,6 +295,44 @@ export class DailySecurityScanUseCase {
278
295
  return scannedVulnerablePackages;
279
296
  };
280
297
 
298
+ private checkoutDefaultBranch = async (
299
+ repositoryName: string,
300
+ remoteUrl: string,
301
+ ): Promise<string | null> => {
302
+ const { stdout: checkoutDirectoryOutput, exitCode: checkoutDirectoryExit } =
303
+ await this.localCommandRunner.runCommand('mktemp', [
304
+ '-d',
305
+ '-t',
306
+ `tdpm-daily-security-scan-${repositoryName}-XXXXXX`,
307
+ ]);
308
+ const checkoutDirectory = checkoutDirectoryOutput.trim();
309
+ if (checkoutDirectoryExit !== 0 || checkoutDirectory.length === 0) {
310
+ console.error(
311
+ `Failed to create a checkout directory for ${repositoryName}`,
312
+ );
313
+ return null;
314
+ }
315
+ const { stderr: cloneStderr, exitCode: cloneExitCode } =
316
+ await this.localCommandRunner.runCommand('git', [
317
+ 'clone',
318
+ '--depth',
319
+ '1',
320
+ remoteUrl,
321
+ checkoutDirectory,
322
+ ]);
323
+ if (cloneExitCode !== 0) {
324
+ console.error(
325
+ `Failed to clone the default branch of ${repositoryName}: ${cloneStderr}`,
326
+ );
327
+ await this.localCommandRunner.runCommand('rm', [
328
+ '-rf',
329
+ checkoutDirectory,
330
+ ]);
331
+ return null;
332
+ }
333
+ return checkoutDirectory;
334
+ };
335
+
281
336
  private reportKevAdditions = async (
282
337
  org: string,
283
338
  manager: Member['name'],
@@ -20,6 +20,7 @@ export declare class DailySecurityScanUseCase {
20
20
  dailySecurityScan: DailySecurityScanConfig;
21
21
  }) => Promise<void>;
22
22
  private scanRepositories;
23
+ private checkoutDefaultBranch;
23
24
  private reportKevAdditions;
24
25
  }
25
26
  //# sourceMappingURL=DailySecurityScanUseCase.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DailySecurityScanUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/DailySecurityScanUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AACrE,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,MAAM,uBAAuB,GAAG;IACpC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAkHF,qBAAa,wBAAwB;IAEjC,QAAQ,CAAC,kBAAkB,EAAE,kBAAkB;IAC/C,QAAQ,CAAC,eAAe,EAAE,IAAI,CAC5B,eAAe,EACf,gBAAgB,GAAG,aAAa,GAAG,oBAAoB,CACxD;IACD,QAAQ,CAAC,cAAc,EAAE,cAAc;gBAL9B,kBAAkB,EAAE,kBAAkB,EACtC,eAAe,EAAE,IAAI,CAC5B,eAAe,EACf,gBAAgB,GAAG,aAAa,GAAG,oBAAoB,CACxD,EACQ,cAAc,EAAE,cAAc;IAGzC,GAAG,GAAU,OAAO;QAClB,WAAW,EAAE,IAAI,EAAE,CAAC;QACpB,GAAG,EAAE,MAAM,CAAC;QACZ,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QACxB,iBAAiB,EAAE,uBAAuB,CAAC;KAC5C,KAAG,OAAO,CAAC,IAAI,CAAC,CA2Bf;IAEF,OAAO,CAAC,gBAAgB,CA8GtB;IAEF,OAAO,CAAC,kBAAkB,CAwDxB;CACH"}
1
+ {"version":3,"file":"DailySecurityScanUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/DailySecurityScanUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AACrE,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,MAAM,uBAAuB,GAAG;IACpC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAkHF,qBAAa,wBAAwB;IAEjC,QAAQ,CAAC,kBAAkB,EAAE,kBAAkB;IAC/C,QAAQ,CAAC,eAAe,EAAE,IAAI,CAC5B,eAAe,EACf,gBAAgB,GAAG,aAAa,GAAG,oBAAoB,CACxD;IACD,QAAQ,CAAC,cAAc,EAAE,cAAc;gBAL9B,kBAAkB,EAAE,kBAAkB,EACtC,eAAe,EAAE,IAAI,CAC5B,eAAe,EACf,gBAAgB,GAAG,aAAa,GAAG,oBAAoB,CACxD,EACQ,cAAc,EAAE,cAAc;IAGzC,GAAG,GAAU,OAAO;QAClB,WAAW,EAAE,IAAI,EAAE,CAAC;QACpB,GAAG,EAAE,MAAM,CAAC;QACZ,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QACxB,iBAAiB,EAAE,uBAAuB,CAAC;KAC5C,KAAG,OAAO,CAAC,IAAI,CAAC,CA2Bf;IAEF,OAAO,CAAC,gBAAgB,CA+HtB;IAEF,OAAO,CAAC,qBAAqB,CAoC3B;IAEF,OAAO,CAAC,kBAAkB,CAwDxB;CACH"}