github-issue-tower-defence-management 1.148.4 → 1.148.5

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.5](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.148.4...v1.148.5) (2026-08-09)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **security-scan:** report KEV additions only for products present in the workspace ([#1475](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/1475)) ([70b9442](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/commit/70b9442f21c46948319a32f8f296b1e290f450ec))
7
+
1
8
  ## [1.148.4](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.148.3...v1.148.4) (2026-08-09)
2
9
 
3
10
 
@@ -7,6 +7,8 @@ const isKevVulnerability = (value) => {
7
7
  }
8
8
  const record = { ...value };
9
9
  return (typeof record.cveID === 'string' &&
10
+ typeof record.vendorProject === 'string' &&
11
+ typeof record.product === 'string' &&
10
12
  typeof record.vulnerabilityName === 'string' &&
11
13
  typeof record.dateAdded === 'string');
12
14
  };
@@ -94,13 +96,46 @@ class DailySecurityScanUseCase {
94
96
  if (!isKevCatalog(parsedKev)) {
95
97
  throw new Error(`Unexpected CISA KEV catalog format from ${KEV_CATALOG_URL}`);
96
98
  }
97
- const newKevEntries = parsedKev.vulnerabilities
98
- .filter((vulnerability) => vulnerability.dateAdded >= yesterdayYmd)
99
- .map((vulnerability) => `- ${vulnerability.dateAdded} ${vulnerability.cveID} ${vulnerability.vulnerabilityName}`);
100
- if (newKevEntries.length === 0) {
99
+ const newKevEntries = parsedKev.vulnerabilities.filter((vulnerability) => vulnerability.dateAdded >= yesterdayYmd);
100
+ const usedKevEntries = [];
101
+ for (const vulnerability of newKevEntries) {
102
+ if (await this.isProductPresentInScannedWorkspace(config.scanBaseDirectory, vulnerability.product)) {
103
+ usedKevEntries.push(vulnerability);
104
+ }
105
+ }
106
+ if (usedKevEntries.length === 0) {
101
107
  return;
102
108
  }
103
- await this.issueRepository.createNewIssue(org, config.kevReportRepo, `CISA KEV new additions since ${yesterdayYmd}`, newKevEntries.join('\n'), [manager], []);
109
+ await this.issueRepository.createNewIssue(org, config.kevReportRepo, `CISA KEV new additions since ${yesterdayYmd}`, usedKevEntries
110
+ .map((vulnerability) => `- ${vulnerability.dateAdded} ${vulnerability.cveID} ${vulnerability.vulnerabilityName}`)
111
+ .join('\n'), [manager], []);
112
+ };
113
+ this.isProductPresentInScannedWorkspace = async (scanBaseDirectory, product) => {
114
+ const { stdout: findOutput } = await this.localCommandRunner.runCommand('find', [scanBaseDirectory, '-maxdepth', '3', '-name', '.git', '-type', 'd']);
115
+ const repositoryDirectories = findOutput
116
+ .split('\n')
117
+ .filter((line) => line.length > 0)
118
+ .map((gitDirectory) => gitDirectory.replace(/\/\.git$/, ''));
119
+ for (const repositoryDirectory of repositoryDirectories) {
120
+ const { stderr, exitCode } = await this.localCommandRunner.runCommand('git', [
121
+ '-C',
122
+ repositoryDirectory,
123
+ 'grep',
124
+ '-I',
125
+ '-i',
126
+ '-q',
127
+ '-F',
128
+ '-e',
129
+ product,
130
+ ]);
131
+ if (exitCode === 0) {
132
+ return true;
133
+ }
134
+ if (exitCode !== 1) {
135
+ console.error(`Failed to search ${repositoryDirectory} for ${product}: ${stderr}`);
136
+ }
137
+ }
138
+ return false;
104
139
  };
105
140
  }
106
141
  }
@@ -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.4",
3
+ "version": "1.148.5",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "scripts": {
@@ -285,21 +285,28 @@ describe('DailySecurityScanUseCase', () => {
285
285
  mockHttpRepository,
286
286
  } = buildUseCase();
287
287
 
288
- mockLocalCommandRunner.runCommand.mockResolvedValue({
289
- stdout: '',
290
- stderr: '',
291
- exitCode: 0,
292
- });
288
+ mockLocalCommandRunner.runCommand.mockImplementation(
289
+ async (program, args) => {
290
+ if (program === 'find' && args.includes('-maxdepth')) {
291
+ return { stdout: '/repos/app/.git\n', stderr: '', exitCode: 0 };
292
+ }
293
+ return { stdout: '', stderr: '', exitCode: 0 };
294
+ },
295
+ );
293
296
  mockHttpRepository.get.mockResolvedValue(
294
297
  JSON.stringify({
295
298
  vulnerabilities: [
296
299
  {
297
300
  cveID: 'CVE-2024-0001',
301
+ vendorProject: 'Example',
302
+ product: 'NewProduct',
298
303
  vulnerabilityName: 'New Vulnerability',
299
304
  dateAdded: '2024-01-02',
300
305
  },
301
306
  {
302
307
  cveID: 'CVE-2023-9999',
308
+ vendorProject: 'Example',
309
+ product: 'OldProduct',
303
310
  vulnerabilityName: 'Old Vulnerability',
304
311
  dateAdded: '2023-12-31',
305
312
  },
@@ -357,6 +364,8 @@ describe('DailySecurityScanUseCase', () => {
357
364
  vulnerabilities: [
358
365
  {
359
366
  cveID: 'CVE-2023-9999',
367
+ vendorProject: 'Example',
368
+ product: 'OldProduct',
360
369
  vulnerabilityName: 'Old Vulnerability',
361
370
  dateAdded: '2023-12-31',
362
371
  },
@@ -381,6 +390,218 @@ describe('DailySecurityScanUseCase', () => {
381
390
  expect(mockIssueRepository.createNewIssue.mock.calls).toHaveLength(0);
382
391
  });
383
392
 
393
+ it('does not create a KEV report issue when the product is absent from the scanned workspace', async () => {
394
+ const {
395
+ useCase,
396
+ mockLocalCommandRunner,
397
+ mockIssueRepository,
398
+ mockHttpRepository,
399
+ } = buildUseCase();
400
+
401
+ mockLocalCommandRunner.runCommand.mockImplementation(
402
+ async (program, args) => {
403
+ if (program === 'find' && args.includes('-maxdepth')) {
404
+ return {
405
+ stdout: '/repos/app/.git\n/repos/site/.git\n',
406
+ stderr: '',
407
+ exitCode: 0,
408
+ };
409
+ }
410
+ if (program === 'git' && args.includes('grep')) {
411
+ return { stdout: '', stderr: '', exitCode: 1 };
412
+ }
413
+ return { stdout: '', stderr: '', exitCode: 0 };
414
+ },
415
+ );
416
+ mockHttpRepository.get.mockResolvedValue(
417
+ JSON.stringify({
418
+ vulnerabilities: [
419
+ {
420
+ cveID: 'CVE-2024-0001',
421
+ vendorProject: 'Progress',
422
+ product: 'LoadMaster',
423
+ vulnerabilityName: 'Progress LoadMaster Command Injection',
424
+ dateAdded: '2024-01-02',
425
+ },
426
+ ],
427
+ }),
428
+ );
429
+
430
+ await useCase.run({
431
+ targetDates: [new Date('2024-01-02T05:00:00Z')],
432
+ org: 'example-org',
433
+ manager: 'manager-name',
434
+ dailySecurityScan: {
435
+ scanBaseDirectory: '/repos',
436
+ targetHourUtc: 5,
437
+ enableKevNvdReport: true,
438
+ kevReportRepo: 'security-reports',
439
+ },
440
+ });
441
+
442
+ const gitGrepCalls = mockLocalCommandRunner.runCommand.mock.calls.filter(
443
+ (call) => call[0] === 'git' && call[1].includes('grep'),
444
+ );
445
+ expect(gitGrepCalls.map((call) => call[1])).toEqual([
446
+ [
447
+ '-C',
448
+ '/repos/app',
449
+ 'grep',
450
+ '-I',
451
+ '-i',
452
+ '-q',
453
+ '-F',
454
+ '-e',
455
+ 'LoadMaster',
456
+ ],
457
+ [
458
+ '-C',
459
+ '/repos/site',
460
+ 'grep',
461
+ '-I',
462
+ '-i',
463
+ '-q',
464
+ '-F',
465
+ '-e',
466
+ 'LoadMaster',
467
+ ],
468
+ ]);
469
+ expect(mockIssueRepository.createNewIssue.mock.calls).toHaveLength(0);
470
+ });
471
+
472
+ it('reports only the new KEV entries whose product is present in the scanned workspace', async () => {
473
+ const {
474
+ useCase,
475
+ mockLocalCommandRunner,
476
+ mockIssueRepository,
477
+ mockHttpRepository,
478
+ } = buildUseCase();
479
+
480
+ mockLocalCommandRunner.runCommand.mockImplementation(
481
+ async (program, args) => {
482
+ if (program === 'find' && args.includes('-maxdepth')) {
483
+ return { stdout: '/repos/app/.git\n', stderr: '', exitCode: 0 };
484
+ }
485
+ if (program === 'git' && args.includes('grep')) {
486
+ return {
487
+ stdout: '',
488
+ stderr: '',
489
+ exitCode: args.includes('LoadMaster') ? 1 : 0,
490
+ };
491
+ }
492
+ return { stdout: '', stderr: '', exitCode: 0 };
493
+ },
494
+ );
495
+ mockHttpRepository.get.mockResolvedValue(
496
+ JSON.stringify({
497
+ vulnerabilities: [
498
+ {
499
+ cveID: 'CVE-2024-0001',
500
+ vendorProject: 'Progress',
501
+ product: 'LoadMaster',
502
+ vulnerabilityName: 'Progress LoadMaster Command Injection',
503
+ dateAdded: '2024-01-02',
504
+ },
505
+ {
506
+ cveID: 'CVE-2024-0002',
507
+ vendorProject: 'Example',
508
+ product: 'ExampleLibrary',
509
+ vulnerabilityName: 'Example Library Deserialization',
510
+ dateAdded: '2024-01-02',
511
+ },
512
+ ],
513
+ }),
514
+ );
515
+
516
+ await useCase.run({
517
+ targetDates: [new Date('2024-01-02T05:00:00Z')],
518
+ org: 'example-org',
519
+ manager: 'manager-name',
520
+ dailySecurityScan: {
521
+ scanBaseDirectory: '/repos',
522
+ targetHourUtc: 5,
523
+ enableKevNvdReport: true,
524
+ kevReportRepo: 'security-reports',
525
+ },
526
+ });
527
+
528
+ expect(mockIssueRepository.createNewIssue.mock.calls).toHaveLength(1);
529
+ expect(mockIssueRepository.createNewIssue.mock.calls[0][3]).toContain(
530
+ 'CVE-2024-0002',
531
+ );
532
+ expect(mockIssueRepository.createNewIssue.mock.calls[0][3]).not.toContain(
533
+ 'CVE-2024-0001',
534
+ );
535
+ });
536
+
537
+ it('logs a failed repository search and keeps searching the remaining repositories', async () => {
538
+ const {
539
+ useCase,
540
+ mockLocalCommandRunner,
541
+ mockIssueRepository,
542
+ mockHttpRepository,
543
+ } = buildUseCase();
544
+ const errorSpy = jest
545
+ .spyOn(console, 'error')
546
+ .mockImplementation(() => undefined);
547
+
548
+ mockLocalCommandRunner.runCommand.mockImplementation(
549
+ async (program, args) => {
550
+ if (program === 'find' && args.includes('-maxdepth')) {
551
+ return {
552
+ stdout: '/repos/broken/.git\n/repos/app/.git\n',
553
+ stderr: '',
554
+ exitCode: 0,
555
+ };
556
+ }
557
+ if (program === 'git' && args.includes('grep')) {
558
+ return args.includes('/repos/broken')
559
+ ? {
560
+ stdout: '',
561
+ stderr: 'fatal: not a git repository',
562
+ exitCode: 128,
563
+ }
564
+ : { stdout: '', stderr: '', exitCode: 0 };
565
+ }
566
+ return { stdout: '', stderr: '', exitCode: 0 };
567
+ },
568
+ );
569
+ mockHttpRepository.get.mockResolvedValue(
570
+ JSON.stringify({
571
+ vulnerabilities: [
572
+ {
573
+ cveID: 'CVE-2024-0001',
574
+ vendorProject: 'Progress',
575
+ product: 'LoadMaster',
576
+ vulnerabilityName: 'Progress LoadMaster Command Injection',
577
+ dateAdded: '2024-01-02',
578
+ },
579
+ ],
580
+ }),
581
+ );
582
+
583
+ await useCase.run({
584
+ targetDates: [new Date('2024-01-02T05:00:00Z')],
585
+ org: 'example-org',
586
+ manager: 'manager-name',
587
+ dailySecurityScan: {
588
+ scanBaseDirectory: '/repos',
589
+ targetHourUtc: 5,
590
+ enableKevNvdReport: true,
591
+ kevReportRepo: 'security-reports',
592
+ },
593
+ });
594
+
595
+ expect(errorSpy).toHaveBeenCalledWith(
596
+ 'Failed to search /repos/broken for LoadMaster: fatal: not a git repository',
597
+ );
598
+ expect(mockIssueRepository.createNewIssue.mock.calls).toHaveLength(1);
599
+ expect(mockIssueRepository.createNewIssue.mock.calls[0][3]).toContain(
600
+ 'CVE-2024-0001',
601
+ );
602
+ errorSpy.mockRestore();
603
+ });
604
+
384
605
  it('throws when the KEV catalog format is unexpected', async () => {
385
606
  const { useCase, mockLocalCommandRunner, mockHttpRepository } =
386
607
  buildUseCase();
@@ -12,6 +12,8 @@ export type DailySecurityScanConfig = {
12
12
 
13
13
  type KevVulnerability = {
14
14
  cveID: string;
15
+ vendorProject: string;
16
+ product: string;
15
17
  vulnerabilityName: string;
16
18
  dateAdded: string;
17
19
  };
@@ -27,6 +29,8 @@ const isKevVulnerability = (value: unknown): value is KevVulnerability => {
27
29
  const record: Record<string, unknown> = { ...value };
28
30
  return (
29
31
  typeof record.cveID === 'string' &&
32
+ typeof record.vendorProject === 'string' &&
33
+ typeof record.product === 'string' &&
30
34
  typeof record.vulnerabilityName === 'string' &&
31
35
  typeof record.dateAdded === 'string'
32
36
  );
@@ -178,13 +182,21 @@ export class DailySecurityScanUseCase {
178
182
  );
179
183
  }
180
184
 
181
- const newKevEntries = parsedKev.vulnerabilities
182
- .filter((vulnerability) => vulnerability.dateAdded >= yesterdayYmd)
183
- .map(
184
- (vulnerability) =>
185
- `- ${vulnerability.dateAdded} ${vulnerability.cveID} ${vulnerability.vulnerabilityName}`,
186
- );
187
- if (newKevEntries.length === 0) {
185
+ const newKevEntries = parsedKev.vulnerabilities.filter(
186
+ (vulnerability) => vulnerability.dateAdded >= yesterdayYmd,
187
+ );
188
+ const usedKevEntries: KevVulnerability[] = [];
189
+ for (const vulnerability of newKevEntries) {
190
+ if (
191
+ await this.isProductPresentInScannedWorkspace(
192
+ config.scanBaseDirectory,
193
+ vulnerability.product,
194
+ )
195
+ ) {
196
+ usedKevEntries.push(vulnerability);
197
+ }
198
+ }
199
+ if (usedKevEntries.length === 0) {
188
200
  return;
189
201
  }
190
202
 
@@ -192,9 +204,54 @@ export class DailySecurityScanUseCase {
192
204
  org,
193
205
  config.kevReportRepo,
194
206
  `CISA KEV new additions since ${yesterdayYmd}`,
195
- newKevEntries.join('\n'),
207
+ usedKevEntries
208
+ .map(
209
+ (vulnerability) =>
210
+ `- ${vulnerability.dateAdded} ${vulnerability.cveID} ${vulnerability.vulnerabilityName}`,
211
+ )
212
+ .join('\n'),
196
213
  [manager],
197
214
  [],
198
215
  );
199
216
  };
217
+
218
+ private isProductPresentInScannedWorkspace = async (
219
+ scanBaseDirectory: string,
220
+ product: string,
221
+ ): Promise<boolean> => {
222
+ const { stdout: findOutput } = await this.localCommandRunner.runCommand(
223
+ 'find',
224
+ [scanBaseDirectory, '-maxdepth', '3', '-name', '.git', '-type', 'd'],
225
+ );
226
+ const repositoryDirectories = findOutput
227
+ .split('\n')
228
+ .filter((line) => line.length > 0)
229
+ .map((gitDirectory) => gitDirectory.replace(/\/\.git$/, ''));
230
+
231
+ for (const repositoryDirectory of repositoryDirectories) {
232
+ const { stderr, exitCode } = await this.localCommandRunner.runCommand(
233
+ 'git',
234
+ [
235
+ '-C',
236
+ repositoryDirectory,
237
+ 'grep',
238
+ '-I',
239
+ '-i',
240
+ '-q',
241
+ '-F',
242
+ '-e',
243
+ product,
244
+ ],
245
+ );
246
+ if (exitCode === 0) {
247
+ return true;
248
+ }
249
+ if (exitCode !== 1) {
250
+ console.error(
251
+ `Failed to search ${repositoryDirectory} for ${product}: ${stderr}`,
252
+ );
253
+ }
254
+ }
255
+ return false;
256
+ };
200
257
  }
@@ -21,5 +21,6 @@ export declare class DailySecurityScanUseCase {
21
21
  }) => Promise<void>;
22
22
  private scanRepositories;
23
23
  private reportKevAdditions;
24
+ private isProductPresentInScannedWorkspace;
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;AAsCF,qBAAa,wBAAwB;IAEjC,QAAQ,CAAC,kBAAkB,EAAE,kBAAkB;IAC/C,QAAQ,CAAC,eAAe,EAAE,IAAI,CAAC,eAAe,EAAE,gBAAgB,CAAC;IACjE,QAAQ,CAAC,cAAc,EAAE,cAAc;gBAF9B,kBAAkB,EAAE,kBAAkB,EACtC,eAAe,EAAE,IAAI,CAAC,eAAe,EAAE,gBAAgB,CAAC,EACxD,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,CA0Bf;IAEF,OAAO,CAAC,gBAAgB,CAoEtB;IAEF,OAAO,CAAC,kBAAkB,CAwCxB;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;AA0CF,qBAAa,wBAAwB;IAEjC,QAAQ,CAAC,kBAAkB,EAAE,kBAAkB;IAC/C,QAAQ,CAAC,eAAe,EAAE,IAAI,CAAC,eAAe,EAAE,gBAAgB,CAAC;IACjE,QAAQ,CAAC,cAAc,EAAE,cAAc;gBAF9B,kBAAkB,EAAE,kBAAkB,EACtC,eAAe,EAAE,IAAI,CAAC,eAAe,EAAE,gBAAgB,CAAC,EACxD,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,CA0Bf;IAEF,OAAO,CAAC,gBAAgB,CAoEtB;IAEF,OAAO,CAAC,kBAAkB,CAqDxB;IAEF,OAAO,CAAC,kCAAkC,CAsCxC;CACH"}