github-issue-tower-defence-management 1.148.6 → 1.148.7
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 +7 -0
- package/bin/domain/usecases/DailySecurityScanUseCase.js +65 -41
- package/bin/domain/usecases/DailySecurityScanUseCase.js.map +1 -1
- package/package.json +1 -1
- package/src/domain/usecases/DailySecurityScanUseCase.test.ts +299 -237
- package/src/domain/usecases/DailySecurityScanUseCase.ts +106 -60
- package/types/domain/usecases/DailySecurityScanUseCase.d.ts +0 -1
- package/types/domain/usecases/DailySecurityScanUseCase.d.ts.map +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [1.148.7](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.148.6...v1.148.7) (2026-08-09)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **security-scan:** select CISA KEV additions by scanned package versions ([#1481](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/1481)) ([550a788](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/commit/550a788bf940d08c53e3ff5120dc6c589573f731))
|
|
7
|
+
|
|
1
8
|
## [1.148.6](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.148.5...v1.148.6) (2026-08-09)
|
|
2
9
|
|
|
3
10
|
|
|
@@ -20,6 +20,48 @@ const isKevCatalog = (value) => {
|
|
|
20
20
|
return (Array.isArray(record.vulnerabilities) &&
|
|
21
21
|
record.vulnerabilities.every(isKevVulnerability));
|
|
22
22
|
};
|
|
23
|
+
const parseScannerVulnerabilities = (repositoryName, scannerOutput) => {
|
|
24
|
+
let parsed;
|
|
25
|
+
try {
|
|
26
|
+
parsed = JSON.parse(scannerOutput);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
console.error(`Unparsable osv-scanner output for ${repositoryName}: ${String(error)}`);
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
const toRecord = (value) => typeof value === 'object' && value !== null ? { ...value } : {};
|
|
33
|
+
const readArray = (value, key) => {
|
|
34
|
+
const entry = toRecord(value)[key];
|
|
35
|
+
return Array.isArray(entry) ? entry : [];
|
|
36
|
+
};
|
|
37
|
+
const readString = (value, key) => {
|
|
38
|
+
const entry = toRecord(value)[key];
|
|
39
|
+
return typeof entry === 'string' ? entry : '';
|
|
40
|
+
};
|
|
41
|
+
return readArray(parsed, 'results').flatMap((result) => readArray(result, 'packages').flatMap((scannedPackage) => {
|
|
42
|
+
const packageDetail = toRecord(scannedPackage).package;
|
|
43
|
+
return readArray(scannedPackage, 'vulnerabilities').map((vulnerability) => {
|
|
44
|
+
const vulnerabilityId = readString(vulnerability, 'id');
|
|
45
|
+
const aliases = readArray(vulnerability, 'aliases').filter((alias) => typeof alias === 'string');
|
|
46
|
+
return {
|
|
47
|
+
repositoryName,
|
|
48
|
+
ecosystem: readString(packageDetail, 'ecosystem'),
|
|
49
|
+
packageName: readString(packageDetail, 'name'),
|
|
50
|
+
packageVersion: readString(packageDetail, 'version'),
|
|
51
|
+
vulnerabilityId,
|
|
52
|
+
vulnerabilityIdentifiers: [vulnerabilityId, ...aliases],
|
|
53
|
+
summary: readString(vulnerability, 'summary'),
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
}));
|
|
57
|
+
};
|
|
58
|
+
const renderScannerFindings = (today, vulnerablePackages) => [
|
|
59
|
+
'## OSV-Scanner findings',
|
|
60
|
+
'',
|
|
61
|
+
`### ${today}`,
|
|
62
|
+
'',
|
|
63
|
+
...vulnerablePackages.map((vulnerablePackage) => `- ${vulnerablePackage.ecosystem} ${vulnerablePackage.packageName} ${vulnerablePackage.packageVersion} ${vulnerablePackage.vulnerabilityIdentifiers.join(' ')} ${vulnerablePackage.summary}`),
|
|
64
|
+
].join('\n');
|
|
23
65
|
const KEV_CATALOG_URL = 'https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json';
|
|
24
66
|
class DailySecurityScanUseCase {
|
|
25
67
|
constructor(localCommandRunner, issueRepository, httpRepository) {
|
|
@@ -34,8 +76,8 @@ class DailySecurityScanUseCase {
|
|
|
34
76
|
}
|
|
35
77
|
const lastTargetDate = input.targetDates[input.targetDates.length - 1];
|
|
36
78
|
const today = lastTargetDate.toISOString().slice(0, 10);
|
|
37
|
-
await this.scanRepositories(input.org, input.manager, today, input.dailySecurityScan);
|
|
38
|
-
await this.reportKevAdditions(input.org, input.manager, lastTargetDate, input.dailySecurityScan);
|
|
79
|
+
const scannedVulnerablePackages = await this.scanRepositories(input.org, input.manager, today, input.dailySecurityScan);
|
|
80
|
+
await this.reportKevAdditions(input.org, input.manager, lastTargetDate, input.dailySecurityScan, scannedVulnerablePackages);
|
|
39
81
|
};
|
|
40
82
|
this.scanRepositories = async (org, manager, today, config) => {
|
|
41
83
|
const { stdout: findOutput } = await this.localCommandRunner.runCommand('find', [
|
|
@@ -53,8 +95,9 @@ class DailySecurityScanUseCase {
|
|
|
53
95
|
.map((gitDirectory) => gitDirectory.replace(/\/\.git$/, ''));
|
|
54
96
|
if (repositoryDirectories.length === 0) {
|
|
55
97
|
console.error(`No repositories found in scan base directory: ${config.scanBaseDirectory}`);
|
|
56
|
-
return;
|
|
98
|
+
return [];
|
|
57
99
|
}
|
|
100
|
+
const scannedVulnerablePackages = [];
|
|
58
101
|
for (const repositoryDirectory of repositoryDirectories) {
|
|
59
102
|
const { stdout: remoteUrl, exitCode: remoteExitCode } = await this.localCommandRunner.runCommand('git', [
|
|
60
103
|
'-C',
|
|
@@ -79,6 +122,8 @@ class DailySecurityScanUseCase {
|
|
|
79
122
|
'source',
|
|
80
123
|
'-r',
|
|
81
124
|
repositoryDirectory,
|
|
125
|
+
'--format',
|
|
126
|
+
'json',
|
|
82
127
|
]);
|
|
83
128
|
if (scanExitCode === 0) {
|
|
84
129
|
continue;
|
|
@@ -87,7 +132,9 @@ class DailySecurityScanUseCase {
|
|
|
87
132
|
console.error(`osv-scanner failed with exit code ${scanExitCode} for ${repositoryDirectory}: ${scanStderr}`);
|
|
88
133
|
continue;
|
|
89
134
|
}
|
|
90
|
-
const
|
|
135
|
+
const vulnerablePackages = parseScannerVulnerabilities(repositoryName, scanOutput);
|
|
136
|
+
scannedVulnerablePackages.push(...vulnerablePackages);
|
|
137
|
+
const findingsBody = renderScannerFindings(today, vulnerablePackages);
|
|
91
138
|
const existingIssues = await this.issueRepository.searchIssue({
|
|
92
139
|
owner: repositoryOrg,
|
|
93
140
|
repositoryName,
|
|
@@ -103,8 +150,9 @@ class DailySecurityScanUseCase {
|
|
|
103
150
|
await this.issueRepository.createNewIssue(repositoryOrg, repositoryName, 'Daily security scan findings', findingsBody, [manager], []);
|
|
104
151
|
}
|
|
105
152
|
}
|
|
153
|
+
return scannedVulnerablePackages;
|
|
106
154
|
};
|
|
107
|
-
this.reportKevAdditions = async (org, manager, lastTargetDate, config) => {
|
|
155
|
+
this.reportKevAdditions = async (org, manager, lastTargetDate, config, scannedVulnerablePackages) => {
|
|
108
156
|
if (!config.enableKevNvdReport || !config.kevReportRepo) {
|
|
109
157
|
return;
|
|
110
158
|
}
|
|
@@ -117,46 +165,22 @@ class DailySecurityScanUseCase {
|
|
|
117
165
|
throw new Error(`Unexpected CISA KEV catalog format from ${KEV_CATALOG_URL}`);
|
|
118
166
|
}
|
|
119
167
|
const newKevEntries = parsedKev.vulnerabilities.filter((vulnerability) => vulnerability.dateAdded >= yesterdayYmd);
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (
|
|
168
|
+
const affectingKevEntries = newKevEntries
|
|
169
|
+
.map((vulnerability) => ({
|
|
170
|
+
vulnerability,
|
|
171
|
+
affectedPackages: scannedVulnerablePackages.filter((scannedPackage) => scannedPackage.vulnerabilityIdentifiers.includes(vulnerability.cveID)),
|
|
172
|
+
}))
|
|
173
|
+
.filter((entry) => entry.affectedPackages.length > 0);
|
|
174
|
+
if (affectingKevEntries.length === 0) {
|
|
127
175
|
return;
|
|
128
176
|
}
|
|
129
|
-
await this.issueRepository.createNewIssue(org, config.kevReportRepo, `CISA KEV new additions since ${yesterdayYmd}`,
|
|
130
|
-
.map((
|
|
177
|
+
await this.issueRepository.createNewIssue(org, config.kevReportRepo, `CISA KEV new additions since ${yesterdayYmd}`, affectingKevEntries
|
|
178
|
+
.map((entry) => [
|
|
179
|
+
`- ${entry.vulnerability.dateAdded} ${entry.vulnerability.cveID} ${entry.vulnerability.vulnerabilityName}`,
|
|
180
|
+
...entry.affectedPackages.map((affectedPackage) => ` - ${affectedPackage.repositoryName} ${affectedPackage.ecosystem} ${affectedPackage.packageName} ${affectedPackage.packageVersion}`),
|
|
181
|
+
].join('\n'))
|
|
131
182
|
.join('\n'), [manager], []);
|
|
132
183
|
};
|
|
133
|
-
this.isProductPresentInScannedWorkspace = async (scanBaseDirectory, product) => {
|
|
134
|
-
const { stdout: findOutput } = await this.localCommandRunner.runCommand('find', [scanBaseDirectory, '-maxdepth', '3', '-name', '.git', '-type', 'd']);
|
|
135
|
-
const repositoryDirectories = findOutput
|
|
136
|
-
.split('\n')
|
|
137
|
-
.filter((line) => line.length > 0)
|
|
138
|
-
.map((gitDirectory) => gitDirectory.replace(/\/\.git$/, ''));
|
|
139
|
-
for (const repositoryDirectory of repositoryDirectories) {
|
|
140
|
-
const { stderr, exitCode } = await this.localCommandRunner.runCommand('git', [
|
|
141
|
-
'-C',
|
|
142
|
-
repositoryDirectory,
|
|
143
|
-
'grep',
|
|
144
|
-
'-I',
|
|
145
|
-
'-i',
|
|
146
|
-
'-q',
|
|
147
|
-
'-F',
|
|
148
|
-
'-e',
|
|
149
|
-
product,
|
|
150
|
-
]);
|
|
151
|
-
if (exitCode === 0) {
|
|
152
|
-
return true;
|
|
153
|
-
}
|
|
154
|
-
if (exitCode !== 1) {
|
|
155
|
-
console.error(`Failed to search ${repositoryDirectory} for ${product}: ${stderr}`);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
return false;
|
|
159
|
-
};
|
|
160
184
|
}
|
|
161
185
|
}
|
|
162
186
|
exports.DailySecurityScanUseCase = DailySecurityScanUseCase;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DailySecurityScanUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/DailySecurityScanUseCase.ts"],"names":[],"mappings":";;;AAwBA,MAAM,kBAAkB,GAAG,CAAC,KAAc,EAA6B,EAAE;IACvE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,MAAM,GAA4B,EAAE,GAAG,KAAK,EAAE,CAAC;IACrD,OAAO,CACL,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ;QAChC,OAAO,MAAM,CAAC,aAAa,KAAK,QAAQ;QACxC,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ;QAClC,OAAO,MAAM,CAAC,iBAAiB,KAAK,QAAQ;QAC5C,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,CACrC,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,YAAY,GAAG,CAAC,KAAc,EAAuB,EAAE;IAC3D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,MAAM,GAA4B,EAAE,GAAG,KAAK,EAAE,CAAC;IACrD,OAAO,CACL,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC;QACrC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,kBAAkB,CAAC,CACjD,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,eAAe,GACnB,qFAAqF,CAAC;AAExF,MAAa,wBAAwB;IACnC,YACW,kBAAsC,EACtC,eAGR,EACQ,cAA8B;QAL9B,uBAAkB,GAAlB,kBAAkB,CAAoB;QACtC,oBAAe,GAAf,eAAe,CAGvB;QACQ,mBAAc,GAAd,cAAc,CAAgB;QAGzC,QAAG,GAAG,KAAK,EAAE,KAKZ,EAAiB,EAAE;YAClB,MAAM,SAAS,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,CACtC,CAAC,UAAU,EAAE,EAAE,CACb,UAAU,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,iBAAiB,CAAC,aAAa;gBAClE,UAAU,CAAC,aAAa,EAAE,KAAK,CAAC,CACnC,CAAC;YACF,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,OAAO;YACT,CAAC;YAED,MAAM,cAAc,GAAG,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YACvE,MAAM,KAAK,GAAG,cAAc,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAExD,MAAM,IAAI,CAAC,gBAAgB,
|
|
1
|
+
{"version":3,"file":"DailySecurityScanUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/DailySecurityScanUseCase.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -311,14 +311,53 @@ describe('DailySecurityScanUseCase', () => {
|
|
|
311
311
|
mockHttpRepository,
|
|
312
312
|
} = buildUseCase();
|
|
313
313
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
314
|
+
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
|
+
],
|
|
350
|
+
},
|
|
351
|
+
],
|
|
352
|
+
},
|
|
353
|
+
],
|
|
354
|
+
}),
|
|
355
|
+
stderr: '',
|
|
356
|
+
exitCode: 1,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
return { stdout: '', stderr: '', exitCode: 0 };
|
|
360
|
+
});
|
|
322
361
|
mockHttpRepository.get.mockResolvedValue(
|
|
323
362
|
JSON.stringify({
|
|
324
363
|
vulnerabilities: [
|
|
@@ -354,22 +393,17 @@ describe('DailySecurityScanUseCase', () => {
|
|
|
354
393
|
|
|
355
394
|
expect(mockHttpRepository.get.mock.calls).toHaveLength(1);
|
|
356
395
|
expect(mockHttpRepository.get.mock.calls[0][0]).toBe(KEV_CATALOG_URL);
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
expect(
|
|
362
|
-
|
|
363
|
-
)
|
|
364
|
-
expect(mockIssueRepository.createNewIssue.mock.calls[0][2]).toBe(
|
|
396
|
+
const kevIssueCalls =
|
|
397
|
+
mockIssueRepository.createNewIssue.mock.calls.filter(
|
|
398
|
+
(call) => call[1] === 'security-reports',
|
|
399
|
+
);
|
|
400
|
+
expect(kevIssueCalls).toHaveLength(1);
|
|
401
|
+
expect(kevIssueCalls[0][0]).toBe('example-org');
|
|
402
|
+
expect(kevIssueCalls[0][2]).toBe(
|
|
365
403
|
'CISA KEV new additions since 2024-01-01',
|
|
366
404
|
);
|
|
367
|
-
expect(
|
|
368
|
-
|
|
369
|
-
);
|
|
370
|
-
expect(mockIssueRepository.createNewIssue.mock.calls[0][3]).not.toContain(
|
|
371
|
-
'CVE-2023-9999',
|
|
372
|
-
);
|
|
405
|
+
expect(kevIssueCalls[0][3]).toContain('CVE-2024-0001');
|
|
406
|
+
expect(kevIssueCalls[0][3]).not.toContain('CVE-2023-9999');
|
|
373
407
|
});
|
|
374
408
|
|
|
375
409
|
it('does not create a KEV report issue when there are no new additions', async () => {
|
|
@@ -428,218 +462,6 @@ describe('DailySecurityScanUseCase', () => {
|
|
|
428
462
|
expect(mockIssueRepository.createNewIssue.mock.calls).toHaveLength(0);
|
|
429
463
|
});
|
|
430
464
|
|
|
431
|
-
it('does not create a KEV report issue when the product is absent from the scanned workspace', async () => {
|
|
432
|
-
const {
|
|
433
|
-
useCase,
|
|
434
|
-
mockLocalCommandRunner,
|
|
435
|
-
mockIssueRepository,
|
|
436
|
-
mockHttpRepository,
|
|
437
|
-
} = buildUseCase();
|
|
438
|
-
|
|
439
|
-
mockLocalCommandRunner.runCommand.mockImplementation(
|
|
440
|
-
async (program, args) => {
|
|
441
|
-
if (program === 'find' && args.includes('-maxdepth')) {
|
|
442
|
-
return {
|
|
443
|
-
stdout: '/repos/app/.git\n/repos/site/.git\n',
|
|
444
|
-
stderr: '',
|
|
445
|
-
exitCode: 0,
|
|
446
|
-
};
|
|
447
|
-
}
|
|
448
|
-
if (program === 'git' && args.includes('grep')) {
|
|
449
|
-
return { stdout: '', stderr: '', exitCode: 1 };
|
|
450
|
-
}
|
|
451
|
-
return { stdout: '', stderr: '', exitCode: 0 };
|
|
452
|
-
},
|
|
453
|
-
);
|
|
454
|
-
mockHttpRepository.get.mockResolvedValue(
|
|
455
|
-
JSON.stringify({
|
|
456
|
-
vulnerabilities: [
|
|
457
|
-
{
|
|
458
|
-
cveID: 'CVE-2024-0001',
|
|
459
|
-
vendorProject: 'Progress',
|
|
460
|
-
product: 'LoadMaster',
|
|
461
|
-
vulnerabilityName: 'Progress LoadMaster Command Injection',
|
|
462
|
-
dateAdded: '2024-01-02',
|
|
463
|
-
},
|
|
464
|
-
],
|
|
465
|
-
}),
|
|
466
|
-
);
|
|
467
|
-
|
|
468
|
-
await useCase.run({
|
|
469
|
-
targetDates: [new Date('2024-01-02T05:00:00Z')],
|
|
470
|
-
org: 'example-org',
|
|
471
|
-
manager: 'manager-name',
|
|
472
|
-
dailySecurityScan: {
|
|
473
|
-
scanBaseDirectory: '/repos',
|
|
474
|
-
targetHourUtc: 5,
|
|
475
|
-
enableKevNvdReport: true,
|
|
476
|
-
kevReportRepo: 'security-reports',
|
|
477
|
-
},
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
const gitGrepCalls = mockLocalCommandRunner.runCommand.mock.calls.filter(
|
|
481
|
-
(call) => call[0] === 'git' && call[1].includes('grep'),
|
|
482
|
-
);
|
|
483
|
-
expect(gitGrepCalls.map((call) => call[1])).toEqual([
|
|
484
|
-
[
|
|
485
|
-
'-C',
|
|
486
|
-
'/repos/app',
|
|
487
|
-
'grep',
|
|
488
|
-
'-I',
|
|
489
|
-
'-i',
|
|
490
|
-
'-q',
|
|
491
|
-
'-F',
|
|
492
|
-
'-e',
|
|
493
|
-
'LoadMaster',
|
|
494
|
-
],
|
|
495
|
-
[
|
|
496
|
-
'-C',
|
|
497
|
-
'/repos/site',
|
|
498
|
-
'grep',
|
|
499
|
-
'-I',
|
|
500
|
-
'-i',
|
|
501
|
-
'-q',
|
|
502
|
-
'-F',
|
|
503
|
-
'-e',
|
|
504
|
-
'LoadMaster',
|
|
505
|
-
],
|
|
506
|
-
]);
|
|
507
|
-
expect(mockIssueRepository.createNewIssue.mock.calls).toHaveLength(0);
|
|
508
|
-
});
|
|
509
|
-
|
|
510
|
-
it('reports only the new KEV entries whose product is present in the scanned workspace', async () => {
|
|
511
|
-
const {
|
|
512
|
-
useCase,
|
|
513
|
-
mockLocalCommandRunner,
|
|
514
|
-
mockIssueRepository,
|
|
515
|
-
mockHttpRepository,
|
|
516
|
-
} = buildUseCase();
|
|
517
|
-
|
|
518
|
-
mockLocalCommandRunner.runCommand.mockImplementation(
|
|
519
|
-
async (program, args) => {
|
|
520
|
-
if (program === 'find' && args.includes('-maxdepth')) {
|
|
521
|
-
return { stdout: '/repos/app/.git\n', stderr: '', exitCode: 0 };
|
|
522
|
-
}
|
|
523
|
-
if (program === 'git' && args.includes('grep')) {
|
|
524
|
-
return {
|
|
525
|
-
stdout: '',
|
|
526
|
-
stderr: '',
|
|
527
|
-
exitCode: args.includes('LoadMaster') ? 1 : 0,
|
|
528
|
-
};
|
|
529
|
-
}
|
|
530
|
-
return { stdout: '', stderr: '', exitCode: 0 };
|
|
531
|
-
},
|
|
532
|
-
);
|
|
533
|
-
mockHttpRepository.get.mockResolvedValue(
|
|
534
|
-
JSON.stringify({
|
|
535
|
-
vulnerabilities: [
|
|
536
|
-
{
|
|
537
|
-
cveID: 'CVE-2024-0001',
|
|
538
|
-
vendorProject: 'Progress',
|
|
539
|
-
product: 'LoadMaster',
|
|
540
|
-
vulnerabilityName: 'Progress LoadMaster Command Injection',
|
|
541
|
-
dateAdded: '2024-01-02',
|
|
542
|
-
},
|
|
543
|
-
{
|
|
544
|
-
cveID: 'CVE-2024-0002',
|
|
545
|
-
vendorProject: 'Example',
|
|
546
|
-
product: 'ExampleLibrary',
|
|
547
|
-
vulnerabilityName: 'Example Library Deserialization',
|
|
548
|
-
dateAdded: '2024-01-02',
|
|
549
|
-
},
|
|
550
|
-
],
|
|
551
|
-
}),
|
|
552
|
-
);
|
|
553
|
-
|
|
554
|
-
await useCase.run({
|
|
555
|
-
targetDates: [new Date('2024-01-02T05:00:00Z')],
|
|
556
|
-
org: 'example-org',
|
|
557
|
-
manager: 'manager-name',
|
|
558
|
-
dailySecurityScan: {
|
|
559
|
-
scanBaseDirectory: '/repos',
|
|
560
|
-
targetHourUtc: 5,
|
|
561
|
-
enableKevNvdReport: true,
|
|
562
|
-
kevReportRepo: 'security-reports',
|
|
563
|
-
},
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
expect(mockIssueRepository.createNewIssue.mock.calls).toHaveLength(1);
|
|
567
|
-
expect(mockIssueRepository.createNewIssue.mock.calls[0][3]).toContain(
|
|
568
|
-
'CVE-2024-0002',
|
|
569
|
-
);
|
|
570
|
-
expect(mockIssueRepository.createNewIssue.mock.calls[0][3]).not.toContain(
|
|
571
|
-
'CVE-2024-0001',
|
|
572
|
-
);
|
|
573
|
-
});
|
|
574
|
-
|
|
575
|
-
it('logs a failed repository search and keeps searching the remaining repositories', async () => {
|
|
576
|
-
const {
|
|
577
|
-
useCase,
|
|
578
|
-
mockLocalCommandRunner,
|
|
579
|
-
mockIssueRepository,
|
|
580
|
-
mockHttpRepository,
|
|
581
|
-
} = buildUseCase();
|
|
582
|
-
const errorSpy = jest
|
|
583
|
-
.spyOn(console, 'error')
|
|
584
|
-
.mockImplementation(() => undefined);
|
|
585
|
-
|
|
586
|
-
mockLocalCommandRunner.runCommand.mockImplementation(
|
|
587
|
-
async (program, args) => {
|
|
588
|
-
if (program === 'find' && args.includes('-maxdepth')) {
|
|
589
|
-
return {
|
|
590
|
-
stdout: '/repos/broken/.git\n/repos/app/.git\n',
|
|
591
|
-
stderr: '',
|
|
592
|
-
exitCode: 0,
|
|
593
|
-
};
|
|
594
|
-
}
|
|
595
|
-
if (program === 'git' && args.includes('grep')) {
|
|
596
|
-
return args.includes('/repos/broken')
|
|
597
|
-
? {
|
|
598
|
-
stdout: '',
|
|
599
|
-
stderr: 'fatal: not a git repository',
|
|
600
|
-
exitCode: 128,
|
|
601
|
-
}
|
|
602
|
-
: { stdout: '', stderr: '', exitCode: 0 };
|
|
603
|
-
}
|
|
604
|
-
return { stdout: '', stderr: '', exitCode: 0 };
|
|
605
|
-
},
|
|
606
|
-
);
|
|
607
|
-
mockHttpRepository.get.mockResolvedValue(
|
|
608
|
-
JSON.stringify({
|
|
609
|
-
vulnerabilities: [
|
|
610
|
-
{
|
|
611
|
-
cveID: 'CVE-2024-0001',
|
|
612
|
-
vendorProject: 'Progress',
|
|
613
|
-
product: 'LoadMaster',
|
|
614
|
-
vulnerabilityName: 'Progress LoadMaster Command Injection',
|
|
615
|
-
dateAdded: '2024-01-02',
|
|
616
|
-
},
|
|
617
|
-
],
|
|
618
|
-
}),
|
|
619
|
-
);
|
|
620
|
-
|
|
621
|
-
await useCase.run({
|
|
622
|
-
targetDates: [new Date('2024-01-02T05:00:00Z')],
|
|
623
|
-
org: 'example-org',
|
|
624
|
-
manager: 'manager-name',
|
|
625
|
-
dailySecurityScan: {
|
|
626
|
-
scanBaseDirectory: '/repos',
|
|
627
|
-
targetHourUtc: 5,
|
|
628
|
-
enableKevNvdReport: true,
|
|
629
|
-
kevReportRepo: 'security-reports',
|
|
630
|
-
},
|
|
631
|
-
});
|
|
632
|
-
|
|
633
|
-
expect(errorSpy).toHaveBeenCalledWith(
|
|
634
|
-
'Failed to search /repos/broken for LoadMaster: fatal: not a git repository',
|
|
635
|
-
);
|
|
636
|
-
expect(mockIssueRepository.createNewIssue.mock.calls).toHaveLength(1);
|
|
637
|
-
expect(mockIssueRepository.createNewIssue.mock.calls[0][3]).toContain(
|
|
638
|
-
'CVE-2024-0001',
|
|
639
|
-
);
|
|
640
|
-
errorSpy.mockRestore();
|
|
641
|
-
});
|
|
642
|
-
|
|
643
465
|
it('throws when the KEV catalog format is unexpected', async () => {
|
|
644
466
|
const { useCase, mockLocalCommandRunner, mockHttpRepository } =
|
|
645
467
|
buildUseCase();
|
|
@@ -741,15 +563,41 @@ describe('DailySecurityScanUseCase', () => {
|
|
|
741
563
|
};
|
|
742
564
|
}
|
|
743
565
|
if (program === 'osv-scanner') {
|
|
744
|
-
const
|
|
745
|
-
if (
|
|
566
|
+
const scannedDirectory = args[args.indexOf('-r') + 1];
|
|
567
|
+
if (scannedDirectory.includes('broken')) {
|
|
746
568
|
return {
|
|
747
569
|
stdout: '',
|
|
748
570
|
stderr: 'osv-scanner: command not found',
|
|
749
571
|
exitCode: 127,
|
|
750
572
|
};
|
|
751
573
|
}
|
|
752
|
-
return {
|
|
574
|
+
return {
|
|
575
|
+
stdout: JSON.stringify({
|
|
576
|
+
results: [
|
|
577
|
+
{
|
|
578
|
+
source: { path: scannedDirectory, type: 'lockfile' },
|
|
579
|
+
packages: [
|
|
580
|
+
{
|
|
581
|
+
package: {
|
|
582
|
+
name: 'example-library',
|
|
583
|
+
version: '1.2.3',
|
|
584
|
+
ecosystem: 'npm',
|
|
585
|
+
},
|
|
586
|
+
vulnerabilities: [
|
|
587
|
+
{
|
|
588
|
+
id: 'GHSA-1111-2222-3333',
|
|
589
|
+
aliases: [],
|
|
590
|
+
summary: 'Example Library Deserialization',
|
|
591
|
+
},
|
|
592
|
+
],
|
|
593
|
+
},
|
|
594
|
+
],
|
|
595
|
+
},
|
|
596
|
+
],
|
|
597
|
+
}),
|
|
598
|
+
stderr: '',
|
|
599
|
+
exitCode: 1,
|
|
600
|
+
};
|
|
753
601
|
}
|
|
754
602
|
return { stdout: '', stderr: '', exitCode: 0 };
|
|
755
603
|
},
|
|
@@ -869,5 +717,219 @@ describe('DailySecurityScanUseCase', () => {
|
|
|
869
717
|
);
|
|
870
718
|
expect(mockIssueRepository.createCommentByUrl.mock.calls).toHaveLength(0);
|
|
871
719
|
});
|
|
720
|
+
|
|
721
|
+
const osvScanOutput = (
|
|
722
|
+
vulnerablePackages: {
|
|
723
|
+
name: string;
|
|
724
|
+
version: string;
|
|
725
|
+
ecosystem: string;
|
|
726
|
+
id: string;
|
|
727
|
+
aliases: string[];
|
|
728
|
+
summary: string;
|
|
729
|
+
}[],
|
|
730
|
+
): string =>
|
|
731
|
+
JSON.stringify({
|
|
732
|
+
results: [
|
|
733
|
+
{
|
|
734
|
+
source: { path: '/repos/example-org/app', type: 'lockfile' },
|
|
735
|
+
packages: vulnerablePackages.map((vulnerablePackage) => ({
|
|
736
|
+
package: {
|
|
737
|
+
name: vulnerablePackage.name,
|
|
738
|
+
version: vulnerablePackage.version,
|
|
739
|
+
ecosystem: vulnerablePackage.ecosystem,
|
|
740
|
+
},
|
|
741
|
+
vulnerabilities: [
|
|
742
|
+
{
|
|
743
|
+
id: vulnerablePackage.id,
|
|
744
|
+
aliases: vulnerablePackage.aliases,
|
|
745
|
+
summary: vulnerablePackage.summary,
|
|
746
|
+
},
|
|
747
|
+
],
|
|
748
|
+
})),
|
|
749
|
+
},
|
|
750
|
+
],
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
const buildScanEnvironment = (scanOutput: string, kevCatalog: unknown) => {
|
|
754
|
+
const built = buildUseCase();
|
|
755
|
+
built.mockIssueRepository.searchIssue.mockResolvedValue([]);
|
|
756
|
+
built.mockLocalCommandRunner.runCommand.mockImplementation(
|
|
757
|
+
async (program) => {
|
|
758
|
+
if (program === 'find') {
|
|
759
|
+
return {
|
|
760
|
+
stdout: '/repos/example-org/app/.git\n',
|
|
761
|
+
stderr: '',
|
|
762
|
+
exitCode: 0,
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
if (program === 'git') {
|
|
766
|
+
return {
|
|
767
|
+
stdout: 'git@github.com:example-org/app.git\n',
|
|
768
|
+
stderr: '',
|
|
769
|
+
exitCode: 0,
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
if (program === 'osv-scanner') {
|
|
773
|
+
return { stdout: scanOutput, stderr: '', exitCode: 1 };
|
|
774
|
+
}
|
|
775
|
+
return { stdout: '', stderr: '', exitCode: 0 };
|
|
776
|
+
},
|
|
777
|
+
);
|
|
778
|
+
built.mockHttpRepository.get.mockResolvedValue(
|
|
779
|
+
JSON.stringify(kevCatalog),
|
|
780
|
+
);
|
|
781
|
+
return built;
|
|
782
|
+
};
|
|
783
|
+
|
|
784
|
+
const kevReportCalls = (
|
|
785
|
+
calls: [string, string, string, string, string[], string[]][],
|
|
786
|
+
) => calls.filter((call) => call[1] === 'security-reports');
|
|
787
|
+
|
|
788
|
+
const runWithKevReporting = async (useCase: DailySecurityScanUseCase) =>
|
|
789
|
+
useCase.run({
|
|
790
|
+
targetDates: [new Date('2024-01-02T05:00:00Z')],
|
|
791
|
+
org: 'example-org',
|
|
792
|
+
manager: 'manager-name',
|
|
793
|
+
dailySecurityScan: {
|
|
794
|
+
scanBaseDirectory: '/repos',
|
|
795
|
+
targetHourUtc: 5,
|
|
796
|
+
enableKevNvdReport: true,
|
|
797
|
+
kevReportRepo: 'security-reports',
|
|
798
|
+
},
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
it('reports a KEV addition whose CVE the scanner found at an installed version', async () => {
|
|
802
|
+
const { useCase, mockIssueRepository } = buildScanEnvironment(
|
|
803
|
+
osvScanOutput([
|
|
804
|
+
{
|
|
805
|
+
name: 'example-library',
|
|
806
|
+
version: '1.2.3',
|
|
807
|
+
ecosystem: 'npm',
|
|
808
|
+
id: 'GHSA-1111-2222-3333',
|
|
809
|
+
aliases: ['CVE-2024-0001'],
|
|
810
|
+
summary: 'Example Library Deserialization',
|
|
811
|
+
},
|
|
812
|
+
]),
|
|
813
|
+
{
|
|
814
|
+
vulnerabilities: [
|
|
815
|
+
{
|
|
816
|
+
cveID: 'CVE-2024-0001',
|
|
817
|
+
vendorProject: 'Example',
|
|
818
|
+
product: 'ExampleLibrary',
|
|
819
|
+
vulnerabilityName: 'Example Library Deserialization',
|
|
820
|
+
dateAdded: '2024-01-02',
|
|
821
|
+
},
|
|
822
|
+
],
|
|
823
|
+
},
|
|
824
|
+
);
|
|
825
|
+
|
|
826
|
+
await runWithKevReporting(useCase);
|
|
827
|
+
|
|
828
|
+
const kevCalls = kevReportCalls(
|
|
829
|
+
mockIssueRepository.createNewIssue.mock.calls,
|
|
830
|
+
);
|
|
831
|
+
expect(kevCalls).toHaveLength(1);
|
|
832
|
+
expect(kevCalls[0][3]).toContain('CVE-2024-0001');
|
|
833
|
+
expect(kevCalls[0][3]).toContain('app');
|
|
834
|
+
expect(kevCalls[0][3]).toContain('example-library');
|
|
835
|
+
expect(kevCalls[0][3]).toContain('1.2.3');
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
it('does not report a KEV addition that the scanner did not find in any repository', async () => {
|
|
839
|
+
const { useCase, mockIssueRepository } = buildScanEnvironment(
|
|
840
|
+
osvScanOutput([
|
|
841
|
+
{
|
|
842
|
+
name: 'example-library',
|
|
843
|
+
version: '1.2.3',
|
|
844
|
+
ecosystem: 'npm',
|
|
845
|
+
id: 'GHSA-1111-2222-3333',
|
|
846
|
+
aliases: ['CVE-2024-0001'],
|
|
847
|
+
summary: 'Example Library Deserialization',
|
|
848
|
+
},
|
|
849
|
+
]),
|
|
850
|
+
{
|
|
851
|
+
vulnerabilities: [
|
|
852
|
+
{
|
|
853
|
+
cveID: 'CVE-2024-9999',
|
|
854
|
+
vendorProject: 'Progress',
|
|
855
|
+
product: 'LoadMaster',
|
|
856
|
+
vulnerabilityName: 'Progress LoadMaster Command Injection',
|
|
857
|
+
dateAdded: '2024-01-02',
|
|
858
|
+
},
|
|
859
|
+
],
|
|
860
|
+
},
|
|
861
|
+
);
|
|
862
|
+
|
|
863
|
+
await runWithKevReporting(useCase);
|
|
864
|
+
|
|
865
|
+
expect(
|
|
866
|
+
kevReportCalls(mockIssueRepository.createNewIssue.mock.calls),
|
|
867
|
+
).toHaveLength(0);
|
|
868
|
+
});
|
|
869
|
+
|
|
870
|
+
it('reports a KEV addition whose CVE the scanner returned as the vulnerability id rather than an alias', async () => {
|
|
871
|
+
const { useCase, mockIssueRepository } = buildScanEnvironment(
|
|
872
|
+
osvScanOutput([
|
|
873
|
+
{
|
|
874
|
+
name: 'example-library',
|
|
875
|
+
version: '1.2.3',
|
|
876
|
+
ecosystem: 'npm',
|
|
877
|
+
id: 'CVE-2024-0001',
|
|
878
|
+
aliases: [],
|
|
879
|
+
summary: 'Example Library Deserialization',
|
|
880
|
+
},
|
|
881
|
+
]),
|
|
882
|
+
{
|
|
883
|
+
vulnerabilities: [
|
|
884
|
+
{
|
|
885
|
+
cveID: 'CVE-2024-0001',
|
|
886
|
+
vendorProject: 'Example',
|
|
887
|
+
product: 'ExampleLibrary',
|
|
888
|
+
vulnerabilityName: 'Example Library Deserialization',
|
|
889
|
+
dateAdded: '2024-01-02',
|
|
890
|
+
},
|
|
891
|
+
],
|
|
892
|
+
},
|
|
893
|
+
);
|
|
894
|
+
|
|
895
|
+
await runWithKevReporting(useCase);
|
|
896
|
+
|
|
897
|
+
expect(
|
|
898
|
+
kevReportCalls(mockIssueRepository.createNewIssue.mock.calls),
|
|
899
|
+
).toHaveLength(1);
|
|
900
|
+
});
|
|
901
|
+
|
|
902
|
+
it('does not search the repositories for the KEV product name', async () => {
|
|
903
|
+
const { useCase, mockLocalCommandRunner } = buildScanEnvironment(
|
|
904
|
+
osvScanOutput([
|
|
905
|
+
{
|
|
906
|
+
name: 'example-library',
|
|
907
|
+
version: '1.2.3',
|
|
908
|
+
ecosystem: 'npm',
|
|
909
|
+
id: 'GHSA-1111-2222-3333',
|
|
910
|
+
aliases: ['CVE-2024-0001'],
|
|
911
|
+
summary: 'Example Library Deserialization',
|
|
912
|
+
},
|
|
913
|
+
]),
|
|
914
|
+
{
|
|
915
|
+
vulnerabilities: [
|
|
916
|
+
{
|
|
917
|
+
cveID: 'CVE-2024-0001',
|
|
918
|
+
vendorProject: 'Example',
|
|
919
|
+
product: 'ExampleLibrary',
|
|
920
|
+
vulnerabilityName: 'Example Library Deserialization',
|
|
921
|
+
dateAdded: '2024-01-02',
|
|
922
|
+
},
|
|
923
|
+
],
|
|
924
|
+
},
|
|
925
|
+
);
|
|
926
|
+
|
|
927
|
+
await runWithKevReporting(useCase);
|
|
928
|
+
|
|
929
|
+
const grepCalls = mockLocalCommandRunner.runCommand.mock.calls.filter(
|
|
930
|
+
(call) => call[0] === 'git' && call[1].includes('grep'),
|
|
931
|
+
);
|
|
932
|
+
expect(grepCalls).toHaveLength(0);
|
|
933
|
+
});
|
|
872
934
|
});
|
|
873
935
|
});
|
|
@@ -47,6 +47,78 @@ const isKevCatalog = (value: unknown): value is KevCatalog => {
|
|
|
47
47
|
);
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
+
type ScannedVulnerablePackage = {
|
|
51
|
+
repositoryName: string;
|
|
52
|
+
ecosystem: string;
|
|
53
|
+
packageName: string;
|
|
54
|
+
packageVersion: string;
|
|
55
|
+
vulnerabilityId: string;
|
|
56
|
+
vulnerabilityIdentifiers: string[];
|
|
57
|
+
summary: string;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const parseScannerVulnerabilities = (
|
|
61
|
+
repositoryName: string,
|
|
62
|
+
scannerOutput: string,
|
|
63
|
+
): ScannedVulnerablePackage[] => {
|
|
64
|
+
let parsed: unknown;
|
|
65
|
+
try {
|
|
66
|
+
parsed = JSON.parse(scannerOutput);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error(
|
|
69
|
+
`Unparsable osv-scanner output for ${repositoryName}: ${String(error)}`,
|
|
70
|
+
);
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
const toRecord = (value: unknown): Record<string, unknown> =>
|
|
74
|
+
typeof value === 'object' && value !== null ? { ...value } : {};
|
|
75
|
+
const readArray = (value: unknown, key: string): unknown[] => {
|
|
76
|
+
const entry = toRecord(value)[key];
|
|
77
|
+
return Array.isArray(entry) ? entry : [];
|
|
78
|
+
};
|
|
79
|
+
const readString = (value: unknown, key: string): string => {
|
|
80
|
+
const entry = toRecord(value)[key];
|
|
81
|
+
return typeof entry === 'string' ? entry : '';
|
|
82
|
+
};
|
|
83
|
+
return readArray(parsed, 'results').flatMap((result) =>
|
|
84
|
+
readArray(result, 'packages').flatMap((scannedPackage) => {
|
|
85
|
+
const packageDetail = toRecord(scannedPackage).package;
|
|
86
|
+
return readArray(scannedPackage, 'vulnerabilities').map(
|
|
87
|
+
(vulnerability) => {
|
|
88
|
+
const vulnerabilityId = readString(vulnerability, 'id');
|
|
89
|
+
const aliases = readArray(vulnerability, 'aliases').filter(
|
|
90
|
+
(alias): alias is string => typeof alias === 'string',
|
|
91
|
+
);
|
|
92
|
+
return {
|
|
93
|
+
repositoryName,
|
|
94
|
+
ecosystem: readString(packageDetail, 'ecosystem'),
|
|
95
|
+
packageName: readString(packageDetail, 'name'),
|
|
96
|
+
packageVersion: readString(packageDetail, 'version'),
|
|
97
|
+
vulnerabilityId,
|
|
98
|
+
vulnerabilityIdentifiers: [vulnerabilityId, ...aliases],
|
|
99
|
+
summary: readString(vulnerability, 'summary'),
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
);
|
|
103
|
+
}),
|
|
104
|
+
);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const renderScannerFindings = (
|
|
108
|
+
today: string,
|
|
109
|
+
vulnerablePackages: ScannedVulnerablePackage[],
|
|
110
|
+
): string =>
|
|
111
|
+
[
|
|
112
|
+
'## OSV-Scanner findings',
|
|
113
|
+
'',
|
|
114
|
+
`### ${today}`,
|
|
115
|
+
'',
|
|
116
|
+
...vulnerablePackages.map(
|
|
117
|
+
(vulnerablePackage) =>
|
|
118
|
+
`- ${vulnerablePackage.ecosystem} ${vulnerablePackage.packageName} ${vulnerablePackage.packageVersion} ${vulnerablePackage.vulnerabilityIdentifiers.join(' ')} ${vulnerablePackage.summary}`,
|
|
119
|
+
),
|
|
120
|
+
].join('\n');
|
|
121
|
+
|
|
50
122
|
const KEV_CATALOG_URL =
|
|
51
123
|
'https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json';
|
|
52
124
|
|
|
@@ -78,7 +150,7 @@ export class DailySecurityScanUseCase {
|
|
|
78
150
|
const lastTargetDate = input.targetDates[input.targetDates.length - 1];
|
|
79
151
|
const today = lastTargetDate.toISOString().slice(0, 10);
|
|
80
152
|
|
|
81
|
-
await this.scanRepositories(
|
|
153
|
+
const scannedVulnerablePackages = await this.scanRepositories(
|
|
82
154
|
input.org,
|
|
83
155
|
input.manager,
|
|
84
156
|
today,
|
|
@@ -90,6 +162,7 @@ export class DailySecurityScanUseCase {
|
|
|
90
162
|
input.manager,
|
|
91
163
|
lastTargetDate,
|
|
92
164
|
input.dailySecurityScan,
|
|
165
|
+
scannedVulnerablePackages,
|
|
93
166
|
);
|
|
94
167
|
};
|
|
95
168
|
|
|
@@ -98,7 +171,7 @@ export class DailySecurityScanUseCase {
|
|
|
98
171
|
manager: Member['name'],
|
|
99
172
|
today: string,
|
|
100
173
|
config: DailySecurityScanConfig,
|
|
101
|
-
): Promise<
|
|
174
|
+
): Promise<ScannedVulnerablePackage[]> => {
|
|
102
175
|
const { stdout: findOutput } = await this.localCommandRunner.runCommand(
|
|
103
176
|
'find',
|
|
104
177
|
[
|
|
@@ -121,9 +194,10 @@ export class DailySecurityScanUseCase {
|
|
|
121
194
|
console.error(
|
|
122
195
|
`No repositories found in scan base directory: ${config.scanBaseDirectory}`,
|
|
123
196
|
);
|
|
124
|
-
return;
|
|
197
|
+
return [];
|
|
125
198
|
}
|
|
126
199
|
|
|
200
|
+
const scannedVulnerablePackages: ScannedVulnerablePackage[] = [];
|
|
127
201
|
for (const repositoryDirectory of repositoryDirectories) {
|
|
128
202
|
const { stdout: remoteUrl, exitCode: remoteExitCode } =
|
|
129
203
|
await this.localCommandRunner.runCommand('git', [
|
|
@@ -155,6 +229,8 @@ export class DailySecurityScanUseCase {
|
|
|
155
229
|
'source',
|
|
156
230
|
'-r',
|
|
157
231
|
repositoryDirectory,
|
|
232
|
+
'--format',
|
|
233
|
+
'json',
|
|
158
234
|
]);
|
|
159
235
|
if (scanExitCode === 0) {
|
|
160
236
|
continue;
|
|
@@ -166,7 +242,13 @@ export class DailySecurityScanUseCase {
|
|
|
166
242
|
continue;
|
|
167
243
|
}
|
|
168
244
|
|
|
169
|
-
const
|
|
245
|
+
const vulnerablePackages = parseScannerVulnerabilities(
|
|
246
|
+
repositoryName,
|
|
247
|
+
scanOutput,
|
|
248
|
+
);
|
|
249
|
+
scannedVulnerablePackages.push(...vulnerablePackages);
|
|
250
|
+
|
|
251
|
+
const findingsBody = renderScannerFindings(today, vulnerablePackages);
|
|
170
252
|
const existingIssues = await this.issueRepository.searchIssue({
|
|
171
253
|
owner: repositoryOrg,
|
|
172
254
|
repositoryName,
|
|
@@ -193,6 +275,7 @@ export class DailySecurityScanUseCase {
|
|
|
193
275
|
);
|
|
194
276
|
}
|
|
195
277
|
}
|
|
278
|
+
return scannedVulnerablePackages;
|
|
196
279
|
};
|
|
197
280
|
|
|
198
281
|
private reportKevAdditions = async (
|
|
@@ -200,6 +283,7 @@ export class DailySecurityScanUseCase {
|
|
|
200
283
|
manager: Member['name'],
|
|
201
284
|
lastTargetDate: Date,
|
|
202
285
|
config: DailySecurityScanConfig,
|
|
286
|
+
scannedVulnerablePackages: ScannedVulnerablePackage[],
|
|
203
287
|
): Promise<void> => {
|
|
204
288
|
if (!config.enableKevNvdReport || !config.kevReportRepo) {
|
|
205
289
|
return;
|
|
@@ -220,18 +304,15 @@ export class DailySecurityScanUseCase {
|
|
|
220
304
|
const newKevEntries = parsedKev.vulnerabilities.filter(
|
|
221
305
|
(vulnerability) => vulnerability.dateAdded >= yesterdayYmd,
|
|
222
306
|
);
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
)
|
|
231
|
-
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
if (usedKevEntries.length === 0) {
|
|
307
|
+
const affectingKevEntries = newKevEntries
|
|
308
|
+
.map((vulnerability) => ({
|
|
309
|
+
vulnerability,
|
|
310
|
+
affectedPackages: scannedVulnerablePackages.filter((scannedPackage) =>
|
|
311
|
+
scannedPackage.vulnerabilityIdentifiers.includes(vulnerability.cveID),
|
|
312
|
+
),
|
|
313
|
+
}))
|
|
314
|
+
.filter((entry) => entry.affectedPackages.length > 0);
|
|
315
|
+
if (affectingKevEntries.length === 0) {
|
|
235
316
|
return;
|
|
236
317
|
}
|
|
237
318
|
|
|
@@ -239,54 +320,19 @@ export class DailySecurityScanUseCase {
|
|
|
239
320
|
org,
|
|
240
321
|
config.kevReportRepo,
|
|
241
322
|
`CISA KEV new additions since ${yesterdayYmd}`,
|
|
242
|
-
|
|
243
|
-
.map(
|
|
244
|
-
|
|
245
|
-
`- ${vulnerability.dateAdded} ${vulnerability.cveID} ${vulnerability.vulnerabilityName}`,
|
|
323
|
+
affectingKevEntries
|
|
324
|
+
.map((entry) =>
|
|
325
|
+
[
|
|
326
|
+
`- ${entry.vulnerability.dateAdded} ${entry.vulnerability.cveID} ${entry.vulnerability.vulnerabilityName}`,
|
|
327
|
+
...entry.affectedPackages.map(
|
|
328
|
+
(affectedPackage) =>
|
|
329
|
+
` - ${affectedPackage.repositoryName} ${affectedPackage.ecosystem} ${affectedPackage.packageName} ${affectedPackage.packageVersion}`,
|
|
330
|
+
),
|
|
331
|
+
].join('\n'),
|
|
246
332
|
)
|
|
247
333
|
.join('\n'),
|
|
248
334
|
[manager],
|
|
249
335
|
[],
|
|
250
336
|
);
|
|
251
337
|
};
|
|
252
|
-
|
|
253
|
-
private isProductPresentInScannedWorkspace = async (
|
|
254
|
-
scanBaseDirectory: string,
|
|
255
|
-
product: string,
|
|
256
|
-
): Promise<boolean> => {
|
|
257
|
-
const { stdout: findOutput } = await this.localCommandRunner.runCommand(
|
|
258
|
-
'find',
|
|
259
|
-
[scanBaseDirectory, '-maxdepth', '3', '-name', '.git', '-type', 'd'],
|
|
260
|
-
);
|
|
261
|
-
const repositoryDirectories = findOutput
|
|
262
|
-
.split('\n')
|
|
263
|
-
.filter((line) => line.length > 0)
|
|
264
|
-
.map((gitDirectory) => gitDirectory.replace(/\/\.git$/, ''));
|
|
265
|
-
|
|
266
|
-
for (const repositoryDirectory of repositoryDirectories) {
|
|
267
|
-
const { stderr, exitCode } = await this.localCommandRunner.runCommand(
|
|
268
|
-
'git',
|
|
269
|
-
[
|
|
270
|
-
'-C',
|
|
271
|
-
repositoryDirectory,
|
|
272
|
-
'grep',
|
|
273
|
-
'-I',
|
|
274
|
-
'-i',
|
|
275
|
-
'-q',
|
|
276
|
-
'-F',
|
|
277
|
-
'-e',
|
|
278
|
-
product,
|
|
279
|
-
],
|
|
280
|
-
);
|
|
281
|
-
if (exitCode === 0) {
|
|
282
|
-
return true;
|
|
283
|
-
}
|
|
284
|
-
if (exitCode !== 1) {
|
|
285
|
-
console.error(
|
|
286
|
-
`Failed to search ${repositoryDirectory} for ${product}: ${stderr}`,
|
|
287
|
-
);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
return false;
|
|
291
|
-
};
|
|
292
338
|
}
|
|
@@ -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;
|
|
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"}
|