-
Notifications
You must be signed in to change notification settings - Fork 35
/
dangerfile.ts
116 lines (103 loc) · 5.25 KB
/
dangerfile.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// eslint-disable-next-line import/no-extraneous-dependencies
import { danger, warn, fail } from 'danger';
//RA Summary: eslint-plugin-security - detect-child-process -
//RA Executing commands from an untrusted source or in an untrusted environment can cause an application to execute malicious commands on behalf of an attacker.
//RA: Locates usages of child process.
//RA: This usage checks for any critical or high vulnerabilities and upgrades in our dependencies to alert the Github user.
//RA: This usage does not utilize any user input and there is no opening for command injection.
//RA Developer Status: Mitigated
//RA Validator Status: Mitigated
//RA Validator: leodis.f.scott.civ@mail.mil
//RA Modified Severity: CAT III
// eslint-disable-next-line security/detect-child-process
const child = require('child_process');
// eslint-disable-next-line import/no-extraneous-dependencies
const jiraIssue = require('danger-plugin-jira-issue').default;
const githubChecks = () => {
if (danger.github) {
// No PR is too small to include a description of why you made a change
if (danger.github.pr.body.length < 10) {
warn('Please include a description of your PR changes.');
}
}
};
const fileChecks = () => {
// load all modified and new files
const allFiles = danger.git.modified_files.concat(danger.git.created_files);
const legacyFiles = danger.git.fileMatch('src/shared/**/*', 'src/scenes/**/*');
if (legacyFiles.created) {
warn(`New files have been created under one of the legacy directories
(src/shared or src/scenes). Please relocate them according to the file structure described [here](https://transcom.github.io/mymove-docs/docs/dev/contributing/frontend/frontend#file-layout--naming).
View the [frontend file org ADR](https://github.com/transcom/mymove/blob/main/docs/adr/0048-frontend-file-org.md) for more information`);
}
if (legacyFiles.modified) {
warn(`Files located in legacy directories (src/shared or src/scenes) have
been edited. Are you sure you don’t want to also relocate them to the new [file structure](https://transcom.github.io/mymove-docs/docs/dev/contributing/frontend/frontend#file-layout--naming)?
View the [frontend file org ADR](https://github.com/transcom/mymove/blob/main/docs/adr/0048-frontend-file-org.md) for more information`);
}
// Request changes to app code to also include changes to tests.
const hasAppChanges = allFiles.some((path) => !!path.match(/src\/.*\.jsx?/));
const hasTestChanges = allFiles.some((path) => !!path.match(/src\/.*\.test\.jsx?/));
if (hasAppChanges && !hasTestChanges) {
warn('This PR does not include changes to unit tests, even though it affects app code.');
}
// Require new src/components files to include changes to storybook
const hasComponentChanges = danger.git.created_files.some((path) => path.includes('src/components'));
const hasStorybookChanges = allFiles.some(
(path) => path.includes('src/stories') || !!path.match(/src\/.*\.stories.jsx?/),
);
if (hasComponentChanges && !hasStorybookChanges) {
warn('This PR does not include changes to storybook, even though it affects component code.');
}
// Request update of yarn.lock if package.json changed but yarn.lock isn't
const packageChanged = allFiles.includes('package.json');
const lockfileChanged = allFiles.includes('yarn.lock');
if (packageChanged && !lockfileChanged) {
const message = 'Changes were made to package.json, but not to yarn.lock';
const idea = 'Perhaps you need to run `yarn install`?';
warn(`${message} - <i>${idea}</i>`);
}
};
const checkYarnAudit = () => {
const result = child.spawnSync('yarn', ['audit', '--groups=dependencies', '--level=high', '--json']);
const output = result.stdout.toString().split('\n');
const summary = JSON.parse(output[output.length - 2]);
if (
'data' in summary &&
'vulnerabilities' in summary.data &&
'high' in summary.data.vulnerabilities &&
'critical' in summary.data.vulnerabilities
) {
if (summary.data.vulnerabilities.critical > 0) {
let issuesFound = 'Yarn Audit Issues Found:\n';
output.forEach((rawAudit) => {
try {
const audit = JSON.parse(rawAudit);
if (audit.type === 'auditAdvisory') {
issuesFound +=
`${audit.data.advisory.severity} - ${audit.data.advisory.title}\n` +
`Package ${audit.data.advisory.module_name}\n` +
`Patched in ${audit.data.advisory.patched_versions}\n` +
`Dependency of ${audit.data.resolution.path.split('>')[0]}\n` +
`Path ${audit.data.resolution.path.replace(/>/g, ' > ')}\n` +
`More info ${audit.data.advisory.url}\n\n`;
}
} catch {
// not all outputs maybe json and that's okay
}
});
fail(
`${issuesFound}${summary.data.vulnerabilities.high} high vulnerabilities and ` +
`${summary.data.vulnerabilities.critical} critical vulnerabilities found`,
);
}
} else {
warn(`Couldn't find summary of vulnerabilities from yarn audit`);
}
};
// skip these checks if PR is by dependabot, if we don't have a github object let it run also since we are local
if (!danger.github || (danger.github && danger.github.pr.user.login !== 'dependabot[bot]')) {
githubChecks();
fileChecks();
checkYarnAudit();
}