├── .github
└── workflows
│ ├── build_and_test.yml
│ ├── build_executables.yml
│ ├── manual.yml
│ └── manual_test_repo.yml
├── .gitignore
├── .npmignore
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── action.yml
├── dist
├── fsevents.node
├── index.js
├── index.js.map
├── sourcemap-register.js
└── templates
│ ├── executive_summary.html
│ ├── summary.html
│ ├── summary_old.html
│ └── vulnerability.html
├── package-lock.json
├── package.json
├── samples
├── __old_to_remove
│ ├── cplusplus_scan.json
│ ├── cpp_scan.json
│ ├── java_result.json
│ ├── javascript_scan.json
│ ├── report.json
│ ├── sample_data.json
│ ├── summary
│ │ └── small.json
│ └── vulnerabilities.json
├── reportJson
│ ├── octodemo
│ │ └── ghas-reporting
│ │ │ ├── payload.json
│ │ │ └── summary.html
│ └── peter-murray
│ │ └── advanced-security-java
│ │ └── payload.json
└── sarif
│ ├── java
│ ├── basic
│ │ └── java.sarif
│ └── detailed
│ │ └── java.sarif
│ └── peter-murray
│ └── advanced-security-java
│ ├── java-builtin.sarif
│ └── javascript-builtin.sarif
├── src
├── DataCollector.ts
├── ReportGenerator.test.ts
├── ReportGenerator.ts
├── codeScanning
│ ├── CodeScanningAlert.ts
│ ├── CodeScanningResults.ts
│ ├── GitHubCodeScanning.test.ts
│ └── GitHubCodeScanning.ts
├── dependencies
│ ├── Dependency.ts
│ ├── DependencySet.ts
│ ├── DependencyTypes.ts
│ ├── GitHubDependencies.test.ts
│ ├── GitHubDependencies.ts
│ └── Vulnerability.ts
├── executable.ts
├── index.ts
├── pdf
│ ├── pdfWriter.test.ts
│ └── pdfWriter.ts
├── sarif
│ ├── CodeScanningResult.ts
│ ├── CodeScanningRule.ts
│ ├── SarifDataTypes.ts
│ ├── SarifReport.ts
│ └── SarifReportFinder.ts
├── templating
│ ├── ReportData.ts
│ ├── ReportTypes.ts
│ ├── Template.test.ts
│ └── Template.ts
└── testUtils.ts
├── summary_report_example.png
├── templates
├── executive_summary.html
├── summary.html
├── summary_old.html
└── vulnerability.html
└── tsconfig.json
/.github/workflows/build_and_test.yml:
--------------------------------------------------------------------------------
1 | name: Build and Test
2 |
3 | on:
4 | push:
5 | workflow_dispatch:
6 |
7 | jobs:
8 | test:
9 | runs-on: ubuntu-latest
10 |
11 | steps:
12 | - name: Checkout Sources
13 | uses: actions/checkout@v2
14 |
15 | - name: Install Node.js
16 | uses: actions/setup-node@v1
17 | with:
18 | node-version: 12
19 |
20 | - name: Install Dependencies
21 | run: npm ci
22 |
23 | - name: Build
24 | run: npm run build
25 |
26 | - name: Test
27 | env:
28 | GH_TOKEN: ${{ secrets.SECURITY_TOKEN }}
29 | run: npm test
30 |
--------------------------------------------------------------------------------
/.github/workflows/build_executables.yml:
--------------------------------------------------------------------------------
1 | name: Build Bundle Executables
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | build:
8 | strategy:
9 | matrix:
10 | include:
11 | - type: linux-x64
12 | - type: mac-x64
13 | - type: windows-x64
14 |
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - name: Checkout Sources
19 | uses: actions/checkout@v2
20 |
21 | - name: Install Node.js
22 | uses: actions/setup-node@v1
23 | with:
24 | node-version: 12
25 |
26 | - name: Install Dependencies
27 | run: npm ci
28 |
29 | - name: Build Executable
30 | run: npm run build-exe-${{ matrix.type }}
31 |
32 | - name: npm pack
33 | run: npm pack
34 |
35 | - name: Attach artifact
36 | uses: actions/upload-artifact@v2
37 | with:
38 | name: github-security-report-bundle-${{ matrix.type }}
39 | path: github-security-report-bundle.zip
40 |
41 |
--------------------------------------------------------------------------------
/.github/workflows/manual.yml:
--------------------------------------------------------------------------------
1 | name: Manual Test
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | test:
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 |
12 | - name: Checkout Sources
13 | uses: actions/checkout@v2
14 |
15 | - name: Invoke Action
16 | uses: ./
17 | with:
18 | token: ${{ secrets.SECURITY_TOKEN }}
19 | sarifReportDir: ./samples/sarif/peter-murray/advanced-security-java
20 | outputDir: .
21 |
22 | - name: Upload Artifacts
23 | uses: actions/upload-artifact@v2
24 | with:
25 | name: reports
26 | path: ./*.pdf
27 |
--------------------------------------------------------------------------------
/.github/workflows/manual_test_repo.yml:
--------------------------------------------------------------------------------
1 | name: Manual Test Repository
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | test:
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 |
12 | - name: Checkout Sources
13 | uses: actions/checkout@v2
14 |
15 | - name: Invoke Action
16 | uses: ./
17 | with:
18 | token: ${{ secrets.TEST_TOKEN }}
19 | sarifReportDir: ./samples/sarif/java/detailed
20 | outputDir: .
21 | repository: octodemo/ghas-reporting
22 |
23 | - name: Upload Artifacts
24 | uses: actions/upload-artifact@v2
25 | with:
26 | name: reports
27 | path: ./*.pdf
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.tgz
3 |
4 | .DS_Store
5 |
6 | # Intermeidate directory to TS to JS
7 | lib
8 |
9 | # IntelliJ IDEA
10 | .idea
11 |
12 | # Binary executables
13 | github-security-report*
14 | .local-chromium/
15 | runtime/
16 | tmp/
17 | *.zip
18 |
19 | # Output directory for tests generating reports or other temp data
20 | _tmp/
21 |
22 | # Any PDF files generated from testing
23 | *.pdf
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .local-chromium
2 | .github/
3 | _tmp/
4 | lib/
5 | dist/
6 | samples/
7 | src/
8 | templates/
9 | package.json
10 | summary_report_example.png
11 | *.tgz
12 | *.zip
13 | tsconfig.json
14 | .idea
15 | action.yml
16 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "debug.onTaskErrors": "showErrors",
3 | "files.trimTrailingWhitespace": true,
4 | "editor.tabSize": 2,
5 | "editor.insertSpaces": true,
6 | "editor.detectIndentation": false,
7 | "[javascript]": {
8 | "editor.tabSize": 2
9 | },
10 | "[json]": {
11 | "editor.tabSize": 2
12 | },
13 | "[yaml]": {
14 | "editor.tabSize": 2
15 | },
16 | "[html]": {
17 | "editor.tabSize": 2
18 | },
19 | // GitHub Codespace Theme
20 | "workbench.colorTheme": "GitHub Dark Dimmed"
21 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Peter Murray]
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # github-security-report-action
2 |
3 | A GitHub Action for generating PDF reports for GitHub Advanced Security Code Scan Results and Dependency Vulnerabilities.
4 |
5 | The action comes with some predefined HTML templates using [Nunjucks](https://mozilla.github.io/nunjucks/templating.html),
6 | along with the ability to in the future provide your own templates to the renderer.
7 |
8 | Due to the nature of CodeQL Analysis this action ideally should be executed after the `github/codeql-action/analyze`
9 | action step, as this will generate the SARIF files on the runner which can be used to identify ALL the rules that were
10 | applied during the analysis. The results stored on your repository will only contain the results that generated an alert.
11 |
12 | ## Processing
13 |
14 | The action will use the provided token to load all the dependencies, dependency vulnerabilities and the Code Scanning
15 | results for the specified repository. It will then look in the directory specified for any SARIF reports.
16 |
17 | With this data it will construct a JSON payload that it then passes into the template system (using Nunjucks a Jinja
18 | like templating system for JavaScript) and will generate a Summary Report (with more of these to come in the future)
19 | providing a roll up summary security report in HTML.
20 |
21 | Using this HTML, it then passes it over to Puppeteer to render this in a headless Chromium before generating a PDF and
22 | saving it in the specified directory.
23 |
24 | ## Parameters
25 |
26 | * `token`: A GitHub Personal Access Token with access to `repo` scope
27 | * `sarifReportDir`: The directory to look for SARIF reports (from the CodeQL analyze action this defaults to `../results`)
28 | * `outputDir`: The output directory for the PDF reports, defaults to `github.workspace`
29 | * `repository`: The repository in `/` form, defaults to `github.repository`
30 |
31 |
32 | ## Templates
33 |
34 | Currently the templates are hard coded into the action. There are extension points built into the action that will allow
35 | a future release to provide customization of these templates, via an ability to specify your own.
36 |
37 |
38 | ## Examples
39 |
40 | ```
41 | name: Generate Security Report
42 | uses: peter-murray/github-security-report-action@v2
43 | with:
44 | token: ${{ secrets.SECURITY_TOKEN }}
45 | ```
46 |
47 | Example summary report output:
48 | 
49 |
50 |
51 | ## Standalone execution
52 |
53 | For the v2 version, there are bundles that can be used to provide a command line client executable for Linux, MacOS and Windows platforms. The bundles can be downloaded from the [v2 Release](https://github.com/peter-murray/github-security-report-action/releases/tag/v2) assets.
54 |
55 | * [Linux bundle](https://github.com/peter-murray/github-security-report-action/releases/download/v2/github-security-report-bundle-linux-x64.zip)
56 | * [MacOS bundle](https://github.com/peter-murray/github-security-report-action/releases/download/v2/github-security-report-bundle-mac-x64.zip)
57 | * [Windows bundle](https://github.com/peter-murray/github-security-report-action/releases/download/v2/github-security-report-bundle-windows-x64.zip)
58 |
59 | ### Installation
60 | Just download and extract the zip bundle for your target platform. Inside there is a file starting with `github-security-report` with a target platform suffix or .exe extension in the case of Windows.
61 |
62 | ### Running
63 | Just call the platform executable and pass in the arguments as required. The arguments are the same as that of the GitHub Action, and you can get the full details from invoking the `--help` option on the executable as it will output detailed help
64 |
65 | Options:
66 | * `-t`, `--token`: The GitHub Personal Access Token that has the necessary access for security and dependency API endpoints.
67 | * `-r`, `--repository`: The repository that contains the source code, in `/` form, e.g. `peter-murray/node-hue-api`
68 | * `-s`, `--sarif-directory`: The directory containing the SARIF report files
69 | * `-o`, `--output-directory`: The directory to output the PDF report to. This will be created if it does not exist.
70 |
71 | An example of running the MacOS command line executable from the un:
72 | ```
73 | $ ./github-security-report-mac-x64 -t -r peter-murray/node-hue-api -s
74 | ```
75 | The above command would output a `summary.pdf` file in the current working directory.
76 |
77 | ## Future improvements
78 |
79 | * Add support for selecting reporting templates to the parameters
80 | * Example of extending html templates and using them
81 |
82 |
--------------------------------------------------------------------------------
/action.yml:
--------------------------------------------------------------------------------
1 | name: GitHub Security Report Action
2 | description: Generates security reports for a GitHub repository
3 | author: Peter Murray
4 |
5 | inputs:
6 | token:
7 | description: GitHub Access Token with permissions for Dependencies and Security API access on the repository.
8 | required: true
9 |
10 | sarifReportDir:
11 | description: The CodeQL output directory for SARIF report(s).
12 | required: true
13 | default: "../results"
14 |
15 | outputDir:
16 | description: The output directory for the generated report(s).
17 | required: true
18 | default: ${{ github.workspace }}
19 |
20 | repository:
21 | description: Repository name with owner. For example, peter-murray/github-security-report
22 | required: true
23 | default: ${{ github.repository }}
24 |
25 | runs:
26 | using: node12
27 | main: dist/index.js
28 |
29 | branding:
30 | icon: shield
31 | color: green
--------------------------------------------------------------------------------
/dist/fsevents.node:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peter-murray/github-security-report-action/42aa5945bd5b688f14868891df0ecae0024b3c60/dist/fsevents.node
--------------------------------------------------------------------------------
/dist/templates/executive_summary.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | GitHub Advanced Security - Executive Summary
6 |
7 |
126 |
127 |
128 |
129 |
130 |
143 |
144 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 | Open: 0
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
--------------------------------------------------------------------------------
/dist/templates/summary.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | GitHub Advanced Security Report
6 |
7 |
35 |
36 |
37 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |

50 |
GitHub Advanced Security
Summary Report
51 |
52 |
53 |
54 |
55 |
GitHub Repository:
56 | {{ github.owner }}/{{ github.repo }}
57 |
58 | Generated:
59 | {{ metadata.created }}
60 |
61 |
62 |
63 | {% if sca %}
64 |
65 |
70 |
71 |
72 |
73 |
74 |
78 |
79 |
80 |
83 |
87 |
88 |
89 |
90 |
91 | {% if sca.vulnerabilities %}
92 |
93 |
94 |
98 |
99 |
100 |
103 |
106 |
109 |
112 |
113 |
114 |
115 | {% endif %}
116 |
117 |
118 | {% endif %}
119 |
120 | {% if scanning %}
121 |
122 |
127 |
128 |
140 |
141 |
142 |
143 |
144 |
148 |
149 |
150 |
151 | {% for cwe in scanning.cwe.cwes | sort %}
152 | - {{ cwe }}
153 | {% endfor %}
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 | {% if scanning.results.open %}
164 |
165 |
169 |
170 | {% if scanning.results.open.total > 0 %}
171 |
174 |
177 | {% else %}
178 |
179 | {% endif %}
180 |
181 |
182 | {% endif %}
183 |
184 |
185 |
186 | {% if scanning.results.closed %}
187 |
188 |
192 |
193 | {% if scanning.results.closed.total > 0 %}
194 |
197 |
200 | {% else %}
201 |
202 | {% endif %}
203 |
204 |
205 | {% endif %}
206 |
207 |
208 |
209 | {% endif %}
210 |
211 |
212 |
216 |
217 |
218 |
--------------------------------------------------------------------------------
/dist/templates/summary_old.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | GitHub Advanced Security Summary
6 |
7 |
8 |
9 | GitHub Advanced Security Summary
10 |
11 | Software Composition Analysis (SCA)
12 |
13 | Number of Dependencies: {{dependencies.deps.totalDependencies}}
14 |
15 | SCA Dependency Vulnerability Summary:
16 |
17 |
18 | - Critical: {{dependencies.vulnerabilities.CRITICAL.length}}
19 | - High: {{dependencies.vulnerabilities.HIGH.length}}
20 | - Moderate: {{dependencies.vulnerabilities.MODERATE.length}}
21 | - Low: {{dependencies.vulnerabilities.LOW.length}}
22 |
23 |
24 |
25 | Code Scaning
26 |
27 | Total CWE Vulnerabilities Checked: {{codeScanning.cwes.length}}
28 |
29 |
30 | {{#each codeScanning.cwes}}
31 | {{this}}
32 | {{/each}}
33 |
34 |
35 |
36 |
37 | Open Violations
38 |
39 | - Errors: {{codeScanning.open.error.length}}
40 | - Warnings: {{codeScanning.open.warning.length}}
41 |
42 |
43 | Errors
44 |
45 |
46 | Tool |
47 | Name |
48 | Created |
49 | Tags |
50 | CWE |
51 | Link |
52 |
53 | {{#each codeScanning.open.error}}
54 |
55 | {{this.tool}} |
56 | {{this.name}} |
57 | {{this.created}} |
58 | {{this.rule.details.tags}} |
59 | {{this.rule.details.cwes}} |
60 | {{this.url}} |
61 |
62 | {{/each}}
63 |
64 |
65 | Warnings
66 |
67 |
68 | Tool |
69 | Name |
70 | Created |
71 | Tags |
72 | CWE |
73 | Link |
74 |
75 | {{#each codeScanning.open.warning}}
76 |
77 | {{this.tool}} |
78 | {{this.name}} |
79 | {{this.created}} |
80 | {{this.rule.tags}} |
81 | {{this.rule.cwes}} |
82 | {{this.url}} |
83 |
84 | {{/each}}
85 |
86 |
87 | Dependencies Detail
88 |
89 | Vulnerabilities
90 |
91 | {{#if dependencies.vulnerabilities.CRITICAL.length}}
92 | Critical
93 |
94 |
95 | Package |
96 | Ecosystem |
97 | Version |
98 | Manifest |
99 | Severity |
100 | Advisory Id |
101 | Advisory Summary |
102 |
103 | {{#each dependencies.vulnerabilities.CRITICAL}}
104 |
105 | {{this._data.securityVulnerability.package.name}} |
106 | {{this._data.securityVulnerability.package.ecosystem}} |
107 | {{this._data.vulnerableRequirements}} |
108 | {{this._data.vulnerableManifestPath}} |
109 | {{this._data.securityVulnerability.severity}} |
110 | {{this._data.securityAdvisory.ghsaId}} |
111 | {{this._data.securityAdvisory.summary}} |
112 |
113 | {{/each}}
114 |
115 | {{/if}}
116 |
117 | {{#if dependencies.vulnerabilities.HIGH.length}}
118 | High
119 |
120 |
121 | Package |
122 | Ecosystem |
123 | Version |
124 | Manifest |
125 | Severity |
126 | Advisory Id |
127 | Advisory Summary |
128 |
129 | {{#each dependencies.vulnerabilities.HIGH}}
130 |
131 | {{this._data.securityVulnerability.package.name}} |
132 | {{this._data.securityVulnerability.package.ecosystem}} |
133 | {{this._data.vulnerableRequirements}} |
134 | {{this._data.vulnerableManifestPath}} |
135 | {{this._data.securityVulnerability.severity}} |
136 | {{this._data.securityAdvisory.ghsaId}} |
137 | {{this._data.securityAdvisory.summary}} |
138 |
139 | {{/each}}
140 |
141 | {{/if}}
142 |
143 | {{#if dependencies.vulnerabilities.LOW.length}}
144 | Low
145 |
146 |
147 | Package |
148 | Ecosystem |
149 | Version |
150 | Manifest |
151 | Severity |
152 | Advisory Id |
153 | Advisory Summary |
154 |
155 | {{#each dependencies.vulnerabilities.LOW}}
156 |
157 | {{this._data.securityVulnerability.package.name}} |
158 | {{this._data.securityVulnerability.package.ecosystem}} |
159 | {{this._data.vulnerableRequirements}} |
160 | {{this._data.vulnerableManifestPath}} |
161 | {{this._data.securityVulnerability.severity}} |
162 | {{this._data.securityAdvisory.ghsaId}} |
163 | {{this._data.securityAdvisory.summary}} |
164 |
165 | {{/each}}
166 |
167 | {{/if}}
168 |
169 | {{#if dependencies.vulnerabilities.MODERATE.length}}
170 | Moderate
171 |
172 |
173 | Package |
174 | Ecosystem |
175 | Version |
176 | Manifest |
177 | Severity |
178 | Advisory Id |
179 | Advisory Summary |
180 |
181 | {{#each dependencies.vulnerabilities.MODERATE}}
182 |
183 | {{this._data.securityVulnerability.package.name}} |
184 | {{this._data.securityVulnerability.package.ecosystem}} |
185 | {{this._data.vulnerableRequirements}} |
186 | {{this._data.vulnerableManifestPath}} |
187 | {{this._data.securityVulnerability.severity}} |
188 | {{this._data.securityAdvisory.ghsaId}} |
189 | {{this._data.securityAdvisory.summary}} |
190 |
191 | {{/each}}
192 |
193 | {{/if}}
194 |
195 | Detected Dependencies
196 |
197 | Maven Dependencies:
198 |
199 |
200 | {{#each dependencies.deps.dependencies.maven}}
201 |
202 | {{this.name}} |
203 | {{this.version}} |
204 |
205 | {{/each}}
206 |
207 |
208 |
209 | NPM Dependencies:
210 |
211 |
212 | {{#each dependencies.deps.dependencies.npm}}
213 |
214 | {{this.name}} |
215 | {{this.version}} |
216 |
217 | {{/each}}
218 |
219 |
220 |
221 |
222 |
--------------------------------------------------------------------------------
/dist/templates/vulnerability.html:
--------------------------------------------------------------------------------
1 | {% for severity, vulnerabilities in sca.vulnerabilities %}
2 |
3 |
{{ severity }}
4 |
5 | {% for vuln in vulnerabilities %}
6 | -
7 |
8 |
{{ vuln.advisory.summary }}
9 |
{{ vuln.created }}
10 |
{{ vuln.published }}
11 |
12 | {% for identifier in vuln.advisory.identifiers %}
13 |
{{ identifier.value }}
14 | {% endfor %}
15 |
{{ vuln.advisory.permalink }}
16 |
{{ vuln.advisory.description }}
17 |
18 |
19 |
20 | {% endfor %}
21 |
22 |
23 | {% endfor %}
24 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "github-security-report",
3 | "version": "2.0.0",
4 | "description": "Generates a report from GitHub CodeQL and Dependency information",
5 | "scripts": {
6 | "build": "tsc",
7 | "pre-build-exe": "npm run build && ncc build lib/executable.js -o runtime",
8 | "build-exe-linux-x64": "npm run pre-build-exe && nexe -i runtime/index.js -t linux-x64-12.16.2 -o github-security-report-linux-x64",
9 | "build-exe-mac-x64": "npm run pre-build-exe && nexe -i runtime/index.js -t mac-x64-12.16.2 -o github-security-report-mac-x64",
10 | "build-exe-windows-x64": "npm run pre-build-exe && nexe -i runtime/index.js -t windows-x64-12.16.2 -o github-security-report",
11 | "package": "npm run build && ncc build --source-map",
12 | "postpack": "tarball=$(npm list - depth 0 | sed 's/@/-/g; s/ .*/.tgz/g; 1q;'); tar -tf $tarball | sed 's/^package\\///' | zip -@r github-security-report-bundle",
13 | "test": "mocha --recursive -r ts-node/register \"src/**/*.test.ts\""
14 | },
15 | "keywords": [],
16 | "author": "Peter Murray",
17 | "license": "MIT",
18 | "dependencies": {
19 | "@actions/core": "^1.2.6",
20 | "@actions/io": "^1.0.2",
21 | "@octokit/rest": "^18.0.15",
22 | "commander": "^7.0.0",
23 | "nunjucks": "^3.2.2",
24 | "puppeteer-core": "^5.5.0"
25 | },
26 | "devDependencies": {
27 | "@octokit/types": "^5.5.0",
28 | "@types/chai": "^4.2.14",
29 | "@types/mocha": "^8.2.0",
30 | "@types/node": "^12.19.15",
31 | "@types/puppeteer-core": "^2.1.0",
32 | "@vercel/ncc": "^0.27.0",
33 | "chai": "^4.2.0",
34 | "mocha": "^8.2.1",
35 | "nexe": "^4.0.0-beta.17",
36 | "ts-node": "^9.1.1",
37 | "typescript": "^4.1.3"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/samples/__old_to_remove/java_result.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema" : "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
3 | "version" : "2.1.0",
4 | "runs" : [ {
5 | "tool" : {
6 | "driver" : {
7 | "name" : "CodeQL",
8 | "organization" : "GitHub",
9 | "semanticVersion" : "2.2.5",
10 | "rules" : [
11 | {
12 | "id" : "java/unsafe-deserialization",
13 | "name" : "java/unsafe-deserialization",
14 | "shortDescription" : {
15 | "text" : "Deserialization of user-controlled data"
16 | },
17 | "fullDescription" : {
18 | "text" : "Deserializing user-controlled data may allow attackers to execute arbitrary code."
19 | },
20 | "defaultConfiguration" : {
21 | "level" : "error"
22 | },
23 | "properties" : {
24 | "tags" : [ "security", "external/cwe/cwe-502" ],
25 | "kind" : "path-problem",
26 | "precision" : "high",
27 | "name" : "Deserialization of user-controlled data",
28 | "description" : "Deserializing user-controlled data may allow attackers to\n execute arbitrary code.",
29 | "id" : "java/unsafe-deserialization",
30 | "problem.severity" : "error"
31 | }
32 | }, {
33 | "id" : "java/ldap-injection",
34 | "name" : "java/ldap-injection",
35 | "shortDescription" : {
36 | "text" : "LDAP query built from user-controlled sources"
37 | },
38 | "fullDescription" : {
39 | "text" : "Building an LDAP query from user-controlled sources is vulnerable to insertion of malicious LDAP code by the user."
40 | },
41 | "defaultConfiguration" : {
42 | "level" : "error"
43 | },
44 | "properties" : {
45 | "tags" : [ "security", "external/cwe/cwe-090" ],
46 | "kind" : "path-problem",
47 | "precision" : "high",
48 | "name" : "LDAP query built from user-controlled sources",
49 | "description" : "Building an LDAP query from user-controlled sources is vulnerable to insertion of\n malicious LDAP code by the user.",
50 | "id" : "java/ldap-injection",
51 | "problem.severity" : "error"
52 | }
53 | }, {
54 | "id" : "java/xss",
55 | "name" : "java/xss",
56 | "shortDescription" : {
57 | "text" : "Cross-site scripting"
58 | },
59 | "fullDescription" : {
60 | "text" : "Writing user input directly to a web page allows for a cross-site scripting vulnerability."
61 | },
62 | "defaultConfiguration" : {
63 | "level" : "error"
64 | },
65 | "properties" : {
66 | "tags" : [ "security", "external/cwe/cwe-079" ],
67 | "kind" : "path-problem",
68 | "precision" : "high",
69 | "name" : "Cross-site scripting",
70 | "description" : "Writing user input directly to a web page\n allows for a cross-site scripting vulnerability.",
71 | "id" : "java/xss",
72 | "problem.severity" : "error"
73 | }
74 | }, {
75 | "id" : "java/insecure-cookie",
76 | "name" : "java/insecure-cookie",
77 | "shortDescription" : {
78 | "text" : "Failure to use secure cookies"
79 | },
80 | "fullDescription" : {
81 | "text" : "Insecure cookies may be sent in cleartext, which makes them vulnerable to interception."
82 | },
83 | "defaultConfiguration" : {
84 | "level" : "error"
85 | },
86 | "properties" : {
87 | "tags" : [ "security", "external/cwe/cwe-614" ],
88 | "kind" : "problem",
89 | "precision" : "high",
90 | "name" : "Failure to use secure cookies",
91 | "description" : "Insecure cookies may be sent in cleartext, which makes them vulnerable to\n interception.",
92 | "id" : "java/insecure-cookie",
93 | "problem.severity" : "error"
94 | }
95 | }, {
96 | "id" : "java/maven/non-https-url",
97 | "name" : "java/maven/non-https-url",
98 | "shortDescription" : {
99 | "text" : "Failure to use HTTPS or SFTP URL in Maven artifact upload/download"
100 | },
101 | "fullDescription" : {
102 | "text" : "Non-HTTPS connections can be intercepted by third parties."
103 | },
104 | "defaultConfiguration" : {
105 | "level" : "error"
106 | },
107 | "properties" : {
108 | "tags" : [ "security", "external/cwe/cwe-300", "external/cwe/cwe-319", "external/cwe/cwe-494", "external/cwe/cwe-829" ],
109 | "kind" : "problem",
110 | "precision" : "very-high",
111 | "name" : "Failure to use HTTPS or SFTP URL in Maven artifact upload/download",
112 | "description" : "Non-HTTPS connections can be intercepted by third parties.",
113 | "id" : "java/maven/non-https-url",
114 | "problem.severity" : "error"
115 | }
116 | }, {
117 | "id" : "java/tainted-format-string",
118 | "name" : "java/tainted-format-string",
119 | "shortDescription" : {
120 | "text" : "Use of externally-controlled format string"
121 | },
122 | "fullDescription" : {
123 | "text" : "Using external input in format strings can lead to exceptions or information leaks."
124 | },
125 | "defaultConfiguration" : {
126 | "level" : "error"
127 | },
128 | "properties" : {
129 | "tags" : [ "security", "external/cwe/cwe-134" ],
130 | "kind" : "path-problem",
131 | "precision" : "high",
132 | "name" : "Use of externally-controlled format string",
133 | "description" : "Using external input in format strings can lead to exceptions or information leaks.",
134 | "id" : "java/tainted-format-string",
135 | "problem.severity" : "error"
136 | }
137 | }, {
138 | "id" : "java/sql-injection",
139 | "name" : "java/sql-injection",
140 | "shortDescription" : {
141 | "text" : "Query built from user-controlled sources"
142 | },
143 | "fullDescription" : {
144 | "text" : "Building a SQL or Java Persistence query from user-controlled sources is vulnerable to insertion of malicious code by the user."
145 | },
146 | "defaultConfiguration" : {
147 | "level" : "error"
148 | },
149 | "properties" : {
150 | "tags" : [ "security", "external/cwe/cwe-089" ],
151 | "kind" : "path-problem",
152 | "precision" : "high",
153 | "name" : "Query built from user-controlled sources",
154 | "description" : "Building a SQL or Java Persistence query from user-controlled sources is vulnerable to insertion of\n malicious code by the user.",
155 | "id" : "java/sql-injection",
156 | "problem.severity" : "error"
157 | }
158 | }, {
159 | "id" : "java/concatenated-sql-query",
160 | "name" : "java/concatenated-sql-query",
161 | "shortDescription" : {
162 | "text" : "Query built without neutralizing special characters"
163 | },
164 | "fullDescription" : {
165 | "text" : "Building a SQL or Java Persistence query without escaping or otherwise neutralizing any special characters is vulnerable to insertion of malicious code."
166 | },
167 | "defaultConfiguration" : {
168 | "level" : "error"
169 | },
170 | "properties" : {
171 | "tags" : [ "security", "external/cwe/cwe-089" ],
172 | "kind" : "problem",
173 | "precision" : "high",
174 | "name" : "Query built without neutralizing special characters",
175 | "description" : "Building a SQL or Java Persistence query without escaping or otherwise neutralizing any special\n characters is vulnerable to insertion of malicious code.",
176 | "id" : "java/concatenated-sql-query",
177 | "problem.severity" : "error"
178 | }
179 | }, {
180 | "id" : "java/tainted-numeric-cast",
181 | "name" : "java/tainted-numeric-cast",
182 | "shortDescription" : {
183 | "text" : "User-controlled data in numeric cast"
184 | },
185 | "fullDescription" : {
186 | "text" : "Casting user-controlled numeric data to a narrower type without validation can cause unexpected truncation."
187 | },
188 | "defaultConfiguration" : {
189 | "level" : "error"
190 | },
191 | "properties" : {
192 | "tags" : [ "security", "external/cwe/cwe-197", "external/cwe/cwe-681" ],
193 | "kind" : "path-problem",
194 | "precision" : "high",
195 | "name" : "User-controlled data in numeric cast",
196 | "description" : "Casting user-controlled numeric data to a narrower type without validation\n can cause unexpected truncation.",
197 | "id" : "java/tainted-numeric-cast",
198 | "problem.severity" : "error"
199 | }
200 | }, {
201 | "id" : "java/tainted-permissions-check",
202 | "name" : "java/tainted-permissions-check",
203 | "shortDescription" : {
204 | "text" : "User-controlled data used in permissions check"
205 | },
206 | "fullDescription" : {
207 | "text" : "Using user-controlled data in a permissions check may result in inappropriate permissions being granted."
208 | },
209 | "defaultConfiguration" : {
210 | "level" : "error"
211 | },
212 | "properties" : {
213 | "tags" : [ "security", "external/cwe/cwe-807", "external/cwe/cwe-290" ],
214 | "kind" : "path-problem",
215 | "precision" : "high",
216 | "name" : "User-controlled data used in permissions check",
217 | "description" : "Using user-controlled data in a permissions check may result in inappropriate\n permissions being granted.",
218 | "id" : "java/tainted-permissions-check",
219 | "problem.severity" : "error"
220 | }
221 | }, {
222 | "id" : "java/spring-disabled-csrf-protection",
223 | "name" : "java/spring-disabled-csrf-protection",
224 | "shortDescription" : {
225 | "text" : "Disabled Spring CSRF protection"
226 | },
227 | "fullDescription" : {
228 | "text" : "Disabling CSRF protection makes the application vulnerable to a Cross-Site Request Forgery (CSRF) attack."
229 | },
230 | "defaultConfiguration" : {
231 | "level" : "error"
232 | },
233 | "properties" : {
234 | "tags" : [ "security", "external/cwe/cwe-352" ],
235 | "kind" : "problem",
236 | "precision" : "high",
237 | "name" : "Disabled Spring CSRF protection",
238 | "description" : "Disabling CSRF protection makes the application vulnerable to\n a Cross-Site Request Forgery (CSRF) attack.",
239 | "id" : "java/spring-disabled-csrf-protection",
240 | "problem.severity" : "error"
241 | }
242 | }, {
243 | "id" : "java/cleartext-storage-in-cookie",
244 | "name" : "java/cleartext-storage-in-cookie",
245 | "shortDescription" : {
246 | "text" : "Cleartext storage of sensitive information in cookie"
247 | },
248 | "fullDescription" : {
249 | "text" : "Storing sensitive information in cleartext can expose it to an attacker."
250 | },
251 | "defaultConfiguration" : {
252 | "level" : "error"
253 | },
254 | "properties" : {
255 | "tags" : [ "security", "external/cwe/cwe-315" ],
256 | "kind" : "problem",
257 | "precision" : "high",
258 | "name" : "Cleartext storage of sensitive information in cookie",
259 | "description" : "Storing sensitive information in cleartext can expose it to an attacker.",
260 | "id" : "java/cleartext-storage-in-cookie",
261 | "problem.severity" : "error"
262 | }
263 | }, {
264 | "id" : "java/unvalidated-url-redirection",
265 | "name" : "java/unvalidated-url-redirection",
266 | "shortDescription" : {
267 | "text" : "URL redirection from remote source"
268 | },
269 | "fullDescription" : {
270 | "text" : "URL redirection based on unvalidated user-input may cause redirection to malicious web sites."
271 | },
272 | "defaultConfiguration" : {
273 | "level" : "error"
274 | },
275 | "properties" : {
276 | "tags" : [ "security", "external/cwe/cwe-601" ],
277 | "kind" : "path-problem",
278 | "precision" : "high",
279 | "name" : "URL redirection from remote source",
280 | "description" : "URL redirection based on unvalidated user-input\n may cause redirection to malicious web sites.",
281 | "id" : "java/unvalidated-url-redirection",
282 | "problem.severity" : "error"
283 | }
284 | }, {
285 | "id" : "java/stack-trace-exposure",
286 | "name" : "java/stack-trace-exposure",
287 | "shortDescription" : {
288 | "text" : "Information exposure through a stack trace"
289 | },
290 | "fullDescription" : {
291 | "text" : "Information from a stack trace propagates to an external user. Stack traces can unintentionally reveal implementation details that are useful to an attacker for developing a subsequent exploit."
292 | },
293 | "defaultConfiguration" : {
294 | "level" : "error"
295 | },
296 | "properties" : {
297 | "tags" : [ "security", "external/cwe/cwe-209", "external/cwe/cwe-497" ],
298 | "kind" : "problem",
299 | "precision" : "high",
300 | "name" : "Information exposure through a stack trace",
301 | "description" : "Information from a stack trace propagates to an external user.\n Stack traces can unintentionally reveal implementation details\n that are useful to an attacker for developing a subsequent exploit.",
302 | "id" : "java/stack-trace-exposure",
303 | "problem.severity" : "error"
304 | }
305 | }, {
306 | "id" : "java/netty-http-response-splitting",
307 | "name" : "java/netty-http-response-splitting",
308 | "shortDescription" : {
309 | "text" : "Disabled Netty HTTP header validation"
310 | },
311 | "fullDescription" : {
312 | "text" : "Disabling HTTP header validation makes code vulnerable to attack by header splitting if user input is written directly to an HTTP header."
313 | },
314 | "defaultConfiguration" : {
315 | "level" : "error"
316 | },
317 | "properties" : {
318 | "tags" : [ "security", "external/cwe/cwe-113" ],
319 | "kind" : "problem",
320 | "precision" : "high",
321 | "name" : "Disabled Netty HTTP header validation",
322 | "description" : "Disabling HTTP header validation makes code vulnerable to\n attack by header splitting if user input is written directly to\n an HTTP header.",
323 | "id" : "java/netty-http-response-splitting",
324 | "problem.severity" : "error"
325 | }
326 | }, {
327 | "id" : "java/http-response-splitting",
328 | "name" : "java/http-response-splitting",
329 | "shortDescription" : {
330 | "text" : "HTTP response splitting"
331 | },
332 | "fullDescription" : {
333 | "text" : "Writing user input directly to an HTTP header makes code vulnerable to attack by header splitting."
334 | },
335 | "defaultConfiguration" : {
336 | "level" : "error"
337 | },
338 | "properties" : {
339 | "tags" : [ "security", "external/cwe/cwe-113" ],
340 | "kind" : "path-problem",
341 | "precision" : "high",
342 | "name" : "HTTP response splitting",
343 | "description" : "Writing user input directly to an HTTP header\n makes code vulnerable to attack by header splitting.",
344 | "id" : "java/http-response-splitting",
345 | "problem.severity" : "error"
346 | }
347 | }, {
348 | "id" : "java/zipslip",
349 | "name" : "java/zipslip",
350 | "shortDescription" : {
351 | "text" : "Arbitrary file write during archive extraction (\"Zip Slip\")"
352 | },
353 | "fullDescription" : {
354 | "text" : "Extracting files from a malicious archive without validating that the destination file path is within the destination directory can cause files outside the destination directory to be overwritten."
355 | },
356 | "defaultConfiguration" : {
357 | "level" : "error"
358 | },
359 | "properties" : {
360 | "tags" : [ "security", "external/cwe/cwe-022" ],
361 | "kind" : "path-problem",
362 | "precision" : "high",
363 | "name" : "Arbitrary file write during archive extraction (\"Zip Slip\")",
364 | "description" : "Extracting files from a malicious archive without validating that the\n destination file path is within the destination directory can cause files outside\n the destination directory to be overwritten.",
365 | "id" : "java/zipslip",
366 | "problem.severity" : "error"
367 | }
368 | }, {
369 | "id" : "java/path-injection",
370 | "name" : "java/path-injection",
371 | "shortDescription" : {
372 | "text" : "Uncontrolled data used in path expression"
373 | },
374 | "fullDescription" : {
375 | "text" : "Accessing paths influenced by users can allow an attacker to access unexpected resources."
376 | },
377 | "defaultConfiguration" : {
378 | "level" : "error"
379 | },
380 | "properties" : {
381 | "tags" : [ "security", "external/cwe/cwe-022", "external/cwe/cwe-023", "external/cwe/cwe-036", "external/cwe/cwe-073" ],
382 | "kind" : "path-problem",
383 | "precision" : "high",
384 | "name" : "Uncontrolled data used in path expression",
385 | "description" : "Accessing paths influenced by users can allow an attacker to access unexpected resources.",
386 | "id" : "java/path-injection",
387 | "problem.severity" : "error"
388 | }
389 | }, {
390 | "id" : "java/predictable-seed",
391 | "name" : "java/predictable-seed",
392 | "shortDescription" : {
393 | "text" : "Use of a predictable seed in a secure random number generator"
394 | },
395 | "fullDescription" : {
396 | "text" : "Using a predictable seed in a pseudo-random number generator can lead to predictability of the numbers generated by it."
397 | },
398 | "defaultConfiguration" : {
399 | "level" : "error"
400 | },
401 | "properties" : {
402 | "tags" : [ "security" ],
403 | "kind" : "problem",
404 | "precision" : "high",
405 | "name" : "Use of a predictable seed in a secure random number generator",
406 | "description" : "Using a predictable seed in a pseudo-random number generator can lead to predictability of the numbers generated by it.",
407 | "id" : "java/predictable-seed",
408 | "problem.severity" : "error"
409 | }
410 | }, {
411 | "id" : "java/command-line-injection",
412 | "name" : "java/command-line-injection",
413 | "shortDescription" : {
414 | "text" : "Uncontrolled command line"
415 | },
416 | "fullDescription" : {
417 | "text" : "Using externally controlled strings in a command line is vulnerable to malicious changes in the strings."
418 | },
419 | "defaultConfiguration" : {
420 | "level" : "error"
421 | },
422 | "properties" : {
423 | "tags" : [ "security", "external/cwe/cwe-078", "external/cwe/cwe-088" ],
424 | "kind" : "path-problem",
425 | "precision" : "high",
426 | "name" : "Uncontrolled command line",
427 | "description" : "Using externally controlled strings in a command line is vulnerable to malicious\n changes in the strings.",
428 | "id" : "java/command-line-injection",
429 | "problem.severity" : "error"
430 | }
431 | }, {
432 | "id" : "java/concatenated-command-line",
433 | "name" : "java/concatenated-command-line",
434 | "shortDescription" : {
435 | "text" : "Building a command line with string concatenation"
436 | },
437 | "fullDescription" : {
438 | "text" : "Using concatenated strings in a command line is vulnerable to malicious insertion of special characters in the strings."
439 | },
440 | "defaultConfiguration" : {
441 | "level" : "error"
442 | },
443 | "properties" : {
444 | "tags" : [ "security", "external/cwe/cwe-078", "external/cwe/cwe-088" ],
445 | "kind" : "problem",
446 | "precision" : "high",
447 | "name" : "Building a command line with string concatenation",
448 | "description" : "Using concatenated strings in a command line is vulnerable to malicious\n insertion of special characters in the strings.",
449 | "id" : "java/concatenated-command-line",
450 | "problem.severity" : "error"
451 | }
452 | }, {
453 | "id" : "java/world-writable-file-read",
454 | "name" : "java/world-writable-file-read",
455 | "shortDescription" : {
456 | "text" : "Reading from a world writable file"
457 | },
458 | "fullDescription" : {
459 | "text" : "Reading from a file which is set as world writable is dangerous because the file may be modified or removed by external actors."
460 | },
461 | "defaultConfiguration" : {
462 | "level" : "error"
463 | },
464 | "properties" : {
465 | "tags" : [ "security", "external/cwe/cwe-732" ],
466 | "kind" : "problem",
467 | "precision" : "high",
468 | "name" : "Reading from a world writable file",
469 | "description" : "Reading from a file which is set as world writable is dangerous because\n the file may be modified or removed by external actors.",
470 | "id" : "java/world-writable-file-read",
471 | "problem.severity" : "error"
472 | }
473 | }, {
474 | "id" : "java/xxe",
475 | "name" : "java/xxe",
476 | "shortDescription" : {
477 | "text" : "Resolving XML external entity in user-controlled data"
478 | },
479 | "fullDescription" : {
480 | "text" : "Parsing user-controlled XML documents and allowing expansion of external entity references may lead to disclosure of confidential data or denial of service."
481 | },
482 | "defaultConfiguration" : {
483 | "level" : "error"
484 | },
485 | "properties" : {
486 | "tags" : [ "security", "external/cwe/cwe-611" ],
487 | "kind" : "path-problem",
488 | "precision" : "high",
489 | "name" : "Resolving XML external entity in user-controlled data",
490 | "description" : "Parsing user-controlled XML documents and allowing expansion of external entity\n references may lead to disclosure of confidential data or denial of service.",
491 | "id" : "java/xxe",
492 | "problem.severity" : "error"
493 | }
494 | }, {
495 | "id" : "java/implicit-cast-in-compound-assignment",
496 | "name" : "java/implicit-cast-in-compound-assignment",
497 | "shortDescription" : {
498 | "text" : "Implicit narrowing conversion in compound assignment"
499 | },
500 | "fullDescription" : {
501 | "text" : "Compound assignment statements (for example 'intvar += longvar') that implicitly cast a value of a wider type to a narrower type may result in information loss and numeric errors such as overflows."
502 | },
503 | "defaultConfiguration" : { },
504 | "properties" : {
505 | "tags" : [ "reliability", "security", "external/cwe/cwe-190", "external/cwe/cwe-192", "external/cwe/cwe-197", "external/cwe/cwe-681" ],
506 | "kind" : "problem",
507 | "precision" : "very-high",
508 | "name" : "Implicit narrowing conversion in compound assignment",
509 | "description" : "Compound assignment statements (for example 'intvar += longvar') that implicitly\n cast a value of a wider type to a narrower type may result in information loss and\n numeric errors such as overflows.",
510 | "id" : "java/implicit-cast-in-compound-assignment",
511 | "problem.severity" : "warning"
512 | }
513 | }, {
514 | "id" : "java/integer-multiplication-cast-to-long",
515 | "name" : "java/integer-multiplication-cast-to-long",
516 | "shortDescription" : {
517 | "text" : "Result of multiplication cast to wider type"
518 | },
519 | "fullDescription" : {
520 | "text" : "Casting the result of a multiplication to a wider type instead of casting before the multiplication may cause overflow."
521 | },
522 | "defaultConfiguration" : { },
523 | "properties" : {
524 | "tags" : [ "reliability", "security", "correctness", "types", "external/cwe/cwe-190", "external/cwe/cwe-192", "external/cwe/cwe-197", "external/cwe/cwe-681" ],
525 | "kind" : "problem",
526 | "precision" : "very-high",
527 | "name" : "Result of multiplication cast to wider type",
528 | "description" : "Casting the result of a multiplication to a wider type instead of casting\n before the multiplication may cause overflow.",
529 | "id" : "java/integer-multiplication-cast-to-long",
530 | "problem.severity" : "warning"
531 | }
532 | } ]
533 | }
534 | },
535 | "results" : [ ],
536 | "columnKind" : "utf16CodeUnits",
537 | "properties" : {
538 | "semmle.formatSpecifier" : "sarif-latest"
539 | }
540 | } ]
541 | }
--------------------------------------------------------------------------------
/samples/__old_to_remove/sample_data.json:
--------------------------------------------------------------------------------
1 | {
2 | "repository": {
3 | "owner": "",
4 | "name": ""
5 | },
6 | "vulnerabilities": [
7 |
8 | ],
9 | "codeScanning": {
10 | "open": [],
11 | "closed": [],
12 | "rules": []
13 | }
14 | }
--------------------------------------------------------------------------------
/samples/__old_to_remove/summary/small.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "deps": {
4 | "manifests": {
5 | "processed": [],
6 | "unprocessed": []
7 | },
8 | "totalDependencies": 0,
9 | "dependencies": {}
10 | },
11 | "vulnerabilities": {
12 | "CRITICAL": [],
13 | "HIGH": [],
14 | "LOW": [],
15 | "MODERATE": []
16 | }
17 | },
18 | "codeScanning": {
19 | "cwes": [
20 | "cwe-022",
21 | "cwe-023",
22 | "cwe-036",
23 | "cwe-073",
24 | "cwe-078",
25 | "cwe-079",
26 | "cwe-088",
27 | "cwe-089",
28 | "cwe-090",
29 | "cwe-113",
30 | "cwe-129",
31 | "cwe-134",
32 | "cwe-190"
33 | ],
34 | "rules": [
35 | {
36 | "name": "java/unsafe-deserialization",
37 | "severity": "error",
38 | "precision": "high",
39 | "kind": "path-problem",
40 | "shortDescription": "Deserialization of user-controlled data",
41 | "description": "Deserializing user-controlled data may allow attackers to execute arbitrary code.",
42 | "tags": [
43 | "security",
44 | "external/cwe/cwe-502"
45 | ],
46 | "cwe": [
47 | "cwe-502"
48 | ]
49 | },
50 | {
51 | "name": "java/call-to-object-tostring",
52 | "severity": "recommendation",
53 | "precision": "high",
54 | "kind": "problem",
55 | "shortDescription": "Use of default toString()",
56 | "description": "Calling the default implementation of 'toString' returns a value that is unlikely to be what you expect.",
57 | "tags": [
58 | "reliability",
59 | "maintainability"
60 | ],
61 | "cwe": []
62 | },
63 | {
64 | "name": "java/constant-comparison",
65 | "severity": "warning",
66 | "precision": "very-high",
67 | "kind": "problem",
68 | "shortDescription": "Useless comparison test",
69 | "description": "A comparison operation that always evaluates to true or always evaluates to false may indicate faulty logic and may result in dead code.",
70 | "tags": [
71 | "correctness",
72 | "logic",
73 | "external/cwe/cwe-570",
74 | "external/cwe/cwe-571"
75 | ],
76 | "cwe": [
77 | "cwe-570",
78 | "cwe-571"
79 | ]
80 | }
81 | ],
82 | "open": {
83 | "warning": [
84 | {
85 | "tool": "CodeQL",
86 | "name": "Unsafe use of getResource",
87 | "open": true,
88 | "created": "2020-09-04T13:39:16Z",
89 | "url": "https://api.github.com/repos/octodemo/demo-octopus/code-scanning/alerts/55",
90 | "rule": {
91 | "id": "java/unsafe-get-resource",
92 | "details": {
93 | "name": "java/unsafe-get-resource",
94 | "shortDescription": "Unsafe use of getResource",
95 | "description": "Calling 'this.getClass().getResource()' may yield unexpected results if called from a subclass in another package.",
96 | "tags": [
97 | "reliability",
98 | "maintainability"
99 | ],
100 | "cwes": []
101 | }
102 | }
103 | }
104 | ]
105 | }
106 | }
107 | }
--------------------------------------------------------------------------------
/samples/__old_to_remove/vulnerabilities.json:
--------------------------------------------------------------------------------
1 | {
2 | "data": {
3 | "repository": {
4 | "vulnerabilityAlerts": {
5 | "totalCount": 16,
6 | "pageInfo": {
7 | "hasNextPage": false,
8 | "endCursor": "Y3Vyc29yOnYyOpHOFkH69w=="
9 | },
10 | "nodes": [
11 | {
12 | "id": "MDI4OlJlcG9zaXRvcnlWdWxuZXJhYmlsaXR5QWxlcnQzNzM0MjI3NDY=",
13 | "createdAt": "2020-09-05T14:05:42Z",
14 | "dismisser": null,
15 | "dismissedAt": null,
16 | "dismissReason": null,
17 | "vulnerableManifestFilename": "package-lock.json",
18 | "vulnerableRequirements": "= 4.2.1",
19 | "vulnerableManifestPath": "docs/package-lock.json",
20 | "securityVulnerability": {
21 | "package": {
22 | "ecosystem": "NPM",
23 | "name": "bootstrap"
24 | },
25 | "severity": "MODERATE",
26 | "vulnerableVersionRange": ">= 4.0.0, < 4.3.1"
27 | },
28 | "securityAdvisory": {
29 | "databaseId": 1276,
30 | "id": "MDE2OlNlY3VyaXR5QWR2aXNvcnlHSFNBLXdoNzctM3g0bS00cTln",
31 | "summary": "Moderate severity vulnerability that affects bootstrap and bootstrap-sass",
32 | "severity": "MODERATE",
33 | "description": "In Bootstrap 4 before 4.3.1 and Bootstrap 3 before 3.4.1, XSS is possible in the tooltip or popover data-template attribute. For more information, see: https://blog.getbootstrap.com/2019/02/13/bootstrap-4-3-1-and-3-4-1/",
34 | "ghsaId": "GHSA-wh77-3x4m-4q9g",
35 | "identifiers": [
36 | {
37 | "type": "GHSA",
38 | "value": "GHSA-wh77-3x4m-4q9g"
39 | },
40 | {
41 | "type": "CVE",
42 | "value": "CVE-2019-8331"
43 | }
44 | ],
45 | "permalink": "https://github.com/advisories/GHSA-wh77-3x4m-4q9g",
46 | "publishedAt": "2019-02-22T20:54:27Z"
47 | }
48 | },
49 | {
50 | "id": "MDI4OlJlcG9zaXRvcnlWdWxuZXJhYmlsaXR5QWxlcnQzNzM0MjI3Njc=",
51 | "createdAt": "2020-09-05T14:05:42Z",
52 | "dismisser": null,
53 | "dismissedAt": null,
54 | "dismissReason": null,
55 | "vulnerableManifestFilename": "package-lock.json",
56 | "vulnerableRequirements": "= 0.17.1",
57 | "vulnerableManifestPath": "docs/package-lock.json",
58 | "securityVulnerability": {
59 | "package": {
60 | "ecosystem": "NPM",
61 | "name": "axios"
62 | },
63 | "severity": "MODERATE",
64 | "vulnerableVersionRange": "<= 0.18.0"
65 | },
66 | "securityAdvisory": {
67 | "databaseId": 1398,
68 | "id": "MDE2OlNlY3VyaXR5QWR2aXNvcnlHSFNBLTQyeHctMnh2Yy1xeDht",
69 | "summary": "Denial of Service in axios",
70 | "severity": "MODERATE",
71 | "description": "Versions of `axios` prior to 0.18.1 are vulnerable to Denial of Service. If a request exceeds the `maxContentLength` property, the package prints an error but does not stop the request. This may cause high CPU usage and lead to Denial of Service.\n\n\n## Recommendation\n\nUpgrade to 0.18.1 or later.",
72 | "ghsaId": "GHSA-42xw-2xvc-qx8m",
73 | "identifiers": [
74 | {
75 | "type": "GHSA",
76 | "value": "GHSA-42xw-2xvc-qx8m"
77 | },
78 | {
79 | "type": "CVE",
80 | "value": "CVE-2019-10742"
81 | }
82 | ],
83 | "permalink": "https://github.com/advisories/GHSA-42xw-2xvc-qx8m",
84 | "publishedAt": "2019-05-29T18:04:45Z"
85 | }
86 | },
87 | {
88 | "id": "MDI4OlJlcG9zaXRvcnlWdWxuZXJhYmlsaXR5QWxlcnQzNzM0MjI3NzU=",
89 | "createdAt": "2020-09-05T14:05:43Z",
90 | "dismisser": null,
91 | "dismissedAt": null,
92 | "dismissReason": null,
93 | "vulnerableManifestFilename": "package-lock.json",
94 | "vulnerableRequirements": "= 2.2.1",
95 | "vulnerableManifestPath": "docs/package-lock.json",
96 | "securityVulnerability": {
97 | "package": {
98 | "ecosystem": "NPM",
99 | "name": "tar"
100 | },
101 | "severity": "HIGH",
102 | "vulnerableVersionRange": "< 2.2.2"
103 | },
104 | "securityAdvisory": {
105 | "databaseId": 1383,
106 | "id": "MDE2OlNlY3VyaXR5QWR2aXNvcnlHSFNBLWo0NG0tcW02cC1ocDdt",
107 | "summary": "Arbitrary File Overwrite in tar",
108 | "severity": "HIGH",
109 | "description": "Versions of `tar` prior to 4.4.2 for 4.x and 2.2.2 for 2.x are vulnerable to Arbitrary File Overwrite. Extracting tarballs containing a hardlink to a file that already exists in the system, and a file that matches the hardlink will overwrite the system's file with the contents of the extracted file.\n\n\n## Recommendation\n\nFor tar 4.x, upgrade to version 4.4.2 or later.\nFor tar 2.x, upgrade to version 2.2.2 or later.",
110 | "ghsaId": "GHSA-j44m-qm6p-hp7m",
111 | "identifiers": [
112 | {
113 | "type": "GHSA",
114 | "value": "GHSA-j44m-qm6p-hp7m"
115 | },
116 | {
117 | "type": "CVE",
118 | "value": "CVE-2018-20834"
119 | }
120 | ],
121 | "permalink": "https://github.com/advisories/GHSA-j44m-qm6p-hp7m",
122 | "publishedAt": "2019-05-01T18:37:31Z"
123 | }
124 | },
125 | {
126 | "id": "MDI4OlJlcG9zaXRvcnlWdWxuZXJhYmlsaXR5QWxlcnQzNzM0MjI3ODI=",
127 | "createdAt": "2020-09-05T14:05:43Z",
128 | "dismisser": null,
129 | "dismissedAt": null,
130 | "dismissReason": null,
131 | "vulnerableManifestFilename": "package-lock.json",
132 | "vulnerableRequirements": "= 1.0.11",
133 | "vulnerableManifestPath": "docs/package-lock.json",
134 | "securityVulnerability": {
135 | "package": {
136 | "ecosystem": "NPM",
137 | "name": "fstream"
138 | },
139 | "severity": "HIGH",
140 | "vulnerableVersionRange": "< 1.0.12"
141 | },
142 | "securityAdvisory": {
143 | "databaseId": 1431,
144 | "id": "MDE2OlNlY3VyaXR5QWR2aXNvcnlHSFNBLXhmN3ctcjQ1My1tNTZj",
145 | "summary": "Arbitrary File Overwrite in fstream",
146 | "severity": "HIGH",
147 | "description": "Versions of `fstream` prior to 1.0.12 are vulnerable to Arbitrary File Overwrite. Extracting tarballs containing a hardlink to a file that already exists in the system and a file that matches the hardlink will overwrite the system's file with the contents of the extracted file. The `fstream.DirWriter()` function is vulnerable.\n\n\n## Recommendation\n\nUpgrade to version 1.0.12 or later.",
148 | "ghsaId": "GHSA-xf7w-r453-m56c",
149 | "identifiers": [
150 | {
151 | "type": "GHSA",
152 | "value": "GHSA-xf7w-r453-m56c"
153 | },
154 | {
155 | "type": "CVE",
156 | "value": "CVE-2019-13173"
157 | }
158 | ],
159 | "permalink": "https://github.com/advisories/GHSA-xf7w-r453-m56c",
160 | "publishedAt": "2019-05-30T17:19:34Z"
161 | }
162 | },
163 | {
164 | "id": "MDI4OlJlcG9zaXRvcnlWdWxuZXJhYmlsaXR5QWxlcnQzNzM0MjI3ODg=",
165 | "createdAt": "2020-09-05T14:05:43Z",
166 | "dismisser": null,
167 | "dismissedAt": null,
168 | "dismissReason": null,
169 | "vulnerableManifestFilename": "package-lock.json",
170 | "vulnerableRequirements": "= 4.17.11",
171 | "vulnerableManifestPath": "docs/package-lock.json",
172 | "securityVulnerability": {
173 | "package": {
174 | "ecosystem": "NPM",
175 | "name": "lodash"
176 | },
177 | "severity": "HIGH",
178 | "vulnerableVersionRange": "< 4.17.12"
179 | },
180 | "securityAdvisory": {
181 | "databaseId": 1579,
182 | "id": "MDE2OlNlY3VyaXR5QWR2aXNvcnlHSFNBLWpmODUtY3BjcC1qNjk1",
183 | "summary": "Prototype Pollution in lodash",
184 | "severity": "HIGH",
185 | "description": "Versions of `lodash` before 4.17.12 are vulnerable to Prototype Pollution. The function `defaultsDeep` allows a malicious user to modify the prototype of `Object` via `{constructor: {prototype: {...}}}` causing the addition or modification of an existing property that will exist on all objects.\n\n\n\n\n## Recommendation\n\nUpdate to version 4.17.12 or later.",
186 | "ghsaId": "GHSA-jf85-cpcp-j695",
187 | "identifiers": [
188 | {
189 | "type": "GHSA",
190 | "value": "GHSA-jf85-cpcp-j695"
191 | },
192 | {
193 | "type": "CVE",
194 | "value": "CVE-2019-10744"
195 | }
196 | ],
197 | "permalink": "https://github.com/advisories/GHSA-jf85-cpcp-j695",
198 | "publishedAt": "2019-07-10T19:45:23Z"
199 | }
200 | },
201 | {
202 | "id": "MDI4OlJlcG9zaXRvcnlWdWxuZXJhYmlsaXR5QWxlcnQzNzM0MjI3OTM=",
203 | "createdAt": "2020-09-05T14:05:43Z",
204 | "dismisser": null,
205 | "dismissedAt": null,
206 | "dismissReason": null,
207 | "vulnerableManifestFilename": "package-lock.json",
208 | "vulnerableRequirements": "= 4.4.0",
209 | "vulnerableManifestPath": "docs/package-lock.json",
210 | "securityVulnerability": {
211 | "package": {
212 | "ecosystem": "NPM",
213 | "name": "lodash.template"
214 | },
215 | "severity": "HIGH",
216 | "vulnerableVersionRange": "< 4.5.0"
217 | },
218 | "securityAdvisory": {
219 | "databaseId": 1579,
220 | "id": "MDE2OlNlY3VyaXR5QWR2aXNvcnlHSFNBLWpmODUtY3BjcC1qNjk1",
221 | "summary": "Prototype Pollution in lodash",
222 | "severity": "HIGH",
223 | "description": "Versions of `lodash` before 4.17.12 are vulnerable to Prototype Pollution. The function `defaultsDeep` allows a malicious user to modify the prototype of `Object` via `{constructor: {prototype: {...}}}` causing the addition or modification of an existing property that will exist on all objects.\n\n\n\n\n## Recommendation\n\nUpdate to version 4.17.12 or later.",
224 | "ghsaId": "GHSA-jf85-cpcp-j695",
225 | "identifiers": [
226 | {
227 | "type": "GHSA",
228 | "value": "GHSA-jf85-cpcp-j695"
229 | },
230 | {
231 | "type": "CVE",
232 | "value": "CVE-2019-10744"
233 | }
234 | ],
235 | "permalink": "https://github.com/advisories/GHSA-jf85-cpcp-j695",
236 | "publishedAt": "2019-07-10T19:45:23Z"
237 | }
238 | },
239 | {
240 | "id": "MDI4OlJlcG9zaXRvcnlWdWxuZXJhYmlsaXR5QWxlcnQzNzM0MjI3OTc=",
241 | "createdAt": "2020-09-05T14:05:43Z",
242 | "dismisser": null,
243 | "dismissedAt": null,
244 | "dismissReason": null,
245 | "vulnerableManifestFilename": "package-lock.json",
246 | "vulnerableRequirements": "= 4.6.1",
247 | "vulnerableManifestPath": "docs/package-lock.json",
248 | "securityVulnerability": {
249 | "package": {
250 | "ecosystem": "NPM",
251 | "name": "lodash.mergewith"
252 | },
253 | "severity": "HIGH",
254 | "vulnerableVersionRange": "< 4.6.2"
255 | },
256 | "securityAdvisory": {
257 | "databaseId": 1579,
258 | "id": "MDE2OlNlY3VyaXR5QWR2aXNvcnlHSFNBLWpmODUtY3BjcC1qNjk1",
259 | "summary": "Prototype Pollution in lodash",
260 | "severity": "HIGH",
261 | "description": "Versions of `lodash` before 4.17.12 are vulnerable to Prototype Pollution. The function `defaultsDeep` allows a malicious user to modify the prototype of `Object` via `{constructor: {prototype: {...}}}` causing the addition or modification of an existing property that will exist on all objects.\n\n\n\n\n## Recommendation\n\nUpdate to version 4.17.12 or later.",
262 | "ghsaId": "GHSA-jf85-cpcp-j695",
263 | "identifiers": [
264 | {
265 | "type": "GHSA",
266 | "value": "GHSA-jf85-cpcp-j695"
267 | },
268 | {
269 | "type": "CVE",
270 | "value": "CVE-2019-10744"
271 | }
272 | ],
273 | "permalink": "https://github.com/advisories/GHSA-jf85-cpcp-j695",
274 | "publishedAt": "2019-07-10T19:45:23Z"
275 | }
276 | },
277 | {
278 | "id": "MDI4OlJlcG9zaXRvcnlWdWxuZXJhYmlsaXR5QWxlcnQzNzM0MjI4MDg=",
279 | "createdAt": "2020-09-05T14:05:43Z",
280 | "dismisser": null,
281 | "dismissedAt": null,
282 | "dismissReason": null,
283 | "vulnerableManifestFilename": "package-lock.json",
284 | "vulnerableRequirements": "= 1.3.1",
285 | "vulnerableManifestPath": "docs/package-lock.json",
286 | "securityVulnerability": {
287 | "package": {
288 | "ecosystem": "NPM",
289 | "name": "mixin-deep"
290 | },
291 | "severity": "HIGH",
292 | "vulnerableVersionRange": "< 1.3.2"
293 | },
294 | "securityAdvisory": {
295 | "databaseId": 1657,
296 | "id": "MDE2OlNlY3VyaXR5QWR2aXNvcnlHSFNBLWZoamYtODN3Zy1yMmo5",
297 | "summary": "Prototype Pollution in mixin-deep",
298 | "severity": "HIGH",
299 | "description": "Versions of `mixin-deep` prior to 2.0.1 or 1.3.2 are vulnerable to Prototype Pollution. The `mixinDeep` function fails to validate which Object properties it updates. This allows attackers to modify the prototype of Object, causing the addition or modification of an existing property on all objects.\n\n\n\n\n## Recommendation\n\nIf you are using `mixin-deep` 2.x, upgrade to version 2.0.1 or later.\nIf you are using `mixin-deep` 1.x, upgrade to version 1.3.2 or later.",
300 | "ghsaId": "GHSA-fhjf-83wg-r2j9",
301 | "identifiers": [
302 | {
303 | "type": "GHSA",
304 | "value": "GHSA-fhjf-83wg-r2j9"
305 | },
306 | {
307 | "type": "CVE",
308 | "value": "CVE-2019-10746"
309 | }
310 | ],
311 | "permalink": "https://github.com/advisories/GHSA-fhjf-83wg-r2j9",
312 | "publishedAt": "2019-08-27T17:42:33Z"
313 | }
314 | },
315 | {
316 | "id": "MDI4OlJlcG9zaXRvcnlWdWxuZXJhYmlsaXR5QWxlcnQzNzM0MjI4MTI=",
317 | "createdAt": "2020-09-05T14:05:43Z",
318 | "dismisser": null,
319 | "dismissedAt": null,
320 | "dismissReason": null,
321 | "vulnerableManifestFilename": "package-lock.json",
322 | "vulnerableRequirements": "= 2.0.0",
323 | "vulnerableManifestPath": "docs/package-lock.json",
324 | "securityVulnerability": {
325 | "package": {
326 | "ecosystem": "NPM",
327 | "name": "set-value"
328 | },
329 | "severity": "HIGH",
330 | "vulnerableVersionRange": "< 2.0.1"
331 | },
332 | "securityAdvisory": {
333 | "databaseId": 1658,
334 | "id": "MDE2OlNlY3VyaXR5QWR2aXNvcnlHSFNBLTRnODgtZnBwci01M3Bw",
335 | "summary": "Prototype Pollution in set-value",
336 | "severity": "HIGH",
337 | "description": "Versions of `set-value` prior to 3.0.1 or 2.0.1 are vulnerable to Prototype Pollution. The `set` function fails to validate which Object properties it updates. This allows attackers to modify the prototype of Object, causing the addition or modification of an existing property on all objects.\n\n\n\n\n## Recommendation\n\nIf you are using `set-value` 3.x, upgrade to version 3.0.1 or later.\nIf you are using `set-value` 2.x, upgrade to version 2.0.1 or later.\n",
338 | "ghsaId": "GHSA-4g88-fppr-53pp",
339 | "identifiers": [
340 | {
341 | "type": "GHSA",
342 | "value": "GHSA-4g88-fppr-53pp"
343 | },
344 | {
345 | "type": "CVE",
346 | "value": "CVE-2019-10747"
347 | }
348 | ],
349 | "permalink": "https://github.com/advisories/GHSA-4g88-fppr-53pp",
350 | "publishedAt": "2019-08-27T17:43:33Z"
351 | }
352 | },
353 | {
354 | "id": "MDI4OlJlcG9zaXRvcnlWdWxuZXJhYmlsaXR5QWxlcnQzNzM0MjI4MTc=",
355 | "createdAt": "2020-09-05T14:05:43Z",
356 | "dismisser": null,
357 | "dismissedAt": null,
358 | "dismissReason": null,
359 | "vulnerableManifestFilename": "package-lock.json",
360 | "vulnerableRequirements": "= 6.0.2",
361 | "vulnerableManifestPath": "docs/package-lock.json",
362 | "securityVulnerability": {
363 | "package": {
364 | "ecosystem": "NPM",
365 | "name": "kind-of"
366 | },
367 | "severity": "LOW",
368 | "vulnerableVersionRange": ">= 6.0.0, < 6.0.3"
369 | },
370 | "securityAdvisory": {
371 | "databaseId": 2015,
372 | "id": "MDE2OlNlY3VyaXR5QWR2aXNvcnlHSFNBLTZjOGYtcXBoZy1xamdw",
373 | "summary": "Validation Bypass in kind-of",
374 | "severity": "LOW",
375 | "description": "Versions of `kind-of` 6.x prior to 6.0.3 are vulnerable to a Validation Bypass. A maliciously crafted object can alter the result of the type check, allowing attackers to bypass the type checking validation. \n\n\n## Recommendation\n\nUpgrade to versions 6.0.3 or later.",
376 | "ghsaId": "GHSA-6c8f-qphg-qjgp",
377 | "identifiers": [
378 | {
379 | "type": "GHSA",
380 | "value": "GHSA-6c8f-qphg-qjgp"
381 | },
382 | {
383 | "type": "CVE",
384 | "value": "CVE-2019-20149"
385 | }
386 | ],
387 | "permalink": "https://github.com/advisories/GHSA-6c8f-qphg-qjgp",
388 | "publishedAt": "2020-03-31T15:59:54Z"
389 | }
390 | },
391 | {
392 | "id": "MDI4OlJlcG9zaXRvcnlWdWxuZXJhYmlsaXR5QWxlcnQzNzM0MjI4MjM=",
393 | "createdAt": "2020-09-05T14:05:43Z",
394 | "dismisser": null,
395 | "dismissedAt": null,
396 | "dismissReason": null,
397 | "vulnerableManifestFilename": "package-lock.json",
398 | "vulnerableRequirements": "= 1.2.0",
399 | "vulnerableManifestPath": "docs/package-lock.json",
400 | "securityVulnerability": {
401 | "package": {
402 | "ecosystem": "NPM",
403 | "name": "minimist"
404 | },
405 | "severity": "LOW",
406 | "vulnerableVersionRange": ">= 1.0.0, < 1.2.3"
407 | },
408 | "securityAdvisory": {
409 | "databaseId": 2026,
410 | "id": "MDE2OlNlY3VyaXR5QWR2aXNvcnlHSFNBLXZoOTUtcm1nci02dzRt",
411 | "summary": "Prototype Pollution in minimist",
412 | "severity": "LOW",
413 | "description": "Affected versions of `minimist` are vulnerable to prototype pollution. Arguments are not properly sanitized, allowing an attacker to modify the prototype of `Object`, causing the addition or modification of an existing property that will exist on all objects. \nParsing the argument `--__proto__.y=Polluted` adds a `y` property with value `Polluted` to all objects. The argument `--__proto__=Polluted` raises and uncaught error and crashes the application. \nThis is exploitable if attackers have control over the arguments being passed to `minimist`.\n\n\n\n## Recommendation\n\nUpgrade to versions 0.2.1, 1.2.3 or later.",
414 | "ghsaId": "GHSA-vh95-rmgr-6w4m",
415 | "identifiers": [
416 | {
417 | "type": "GHSA",
418 | "value": "GHSA-vh95-rmgr-6w4m"
419 | },
420 | {
421 | "type": "CVE",
422 | "value": "CVE-2020-7598"
423 | }
424 | ],
425 | "permalink": "https://github.com/advisories/GHSA-vh95-rmgr-6w4m",
426 | "publishedAt": "2020-04-03T21:48:32Z"
427 | }
428 | },
429 | {
430 | "id": "MDI4OlJlcG9zaXRvcnlWdWxuZXJhYmlsaXR5QWxlcnQzNzM0MjI4MzU=",
431 | "createdAt": "2020-09-05T14:05:43Z",
432 | "dismisser": null,
433 | "dismissedAt": null,
434 | "dismissReason": null,
435 | "vulnerableManifestFilename": "package-lock.json",
436 | "vulnerableRequirements": "= 4.17.11",
437 | "vulnerableManifestPath": "docs/package-lock.json",
438 | "securityVulnerability": {
439 | "package": {
440 | "ecosystem": "NPM",
441 | "name": "lodash"
442 | },
443 | "severity": "LOW",
444 | "vulnerableVersionRange": "< 4.17.19"
445 | },
446 | "securityAdvisory": {
447 | "databaseId": 2232,
448 | "id": "MDE2OlNlY3VyaXR5QWR2aXNvcnlHSFNBLXA2bWMtbTQ2OC04M2d3",
449 | "summary": "Prototype Pollution in lodash",
450 | "severity": "LOW",
451 | "description": "Versions of lodash prior to 4.17.19 are vulnerable to Prototype Pollution. The function zipObjectDeep allows a malicious user to modify the prototype of Object if the property identifiers are user-supplied. Being affected by this issue requires zipping objects based on user-provided property arrays.\n\nThis vulnerability causes the addition or modification of an existing property that will exist on all objects and may lead to Denial of Service or Code Execution under specific circumstances.",
452 | "ghsaId": "GHSA-p6mc-m468-83gw",
453 | "identifiers": [
454 | {
455 | "type": "GHSA",
456 | "value": "GHSA-p6mc-m468-83gw"
457 | },
458 | {
459 | "type": "CVE",
460 | "value": "CVE-2020-8203"
461 | }
462 | ],
463 | "permalink": "https://github.com/advisories/GHSA-p6mc-m468-83gw",
464 | "publishedAt": "2020-07-15T19:15:48Z"
465 | }
466 | },
467 | {
468 | "id": "MDI4OlJlcG9zaXRvcnlWdWxuZXJhYmlsaXR5QWxlcnQzNzM0MjI4MzY=",
469 | "createdAt": "2020-09-05T14:05:44Z",
470 | "dismisser": null,
471 | "dismissedAt": null,
472 | "dismissReason": null,
473 | "vulnerableManifestFilename": "package-lock.json",
474 | "vulnerableRequirements": "= 4.11.0",
475 | "vulnerableManifestPath": "docs/package-lock.json",
476 | "securityVulnerability": {
477 | "package": {
478 | "ecosystem": "NPM",
479 | "name": "node-sass"
480 | },
481 | "severity": "LOW",
482 | "vulnerableVersionRange": ">= 3.3.0, < 4.13.1"
483 | },
484 | "securityAdvisory": {
485 | "databaseId": 2655,
486 | "id": "MDE2OlNlY3VyaXR5QWR2aXNvcnlHSFNBLTl2NjItMjRjci01OGN4",
487 | "summary": "Denial of Service in node-sass",
488 | "severity": "LOW",
489 | "description": "Affected versions of `node-sass` are vulnerable to Denial of Service (DoS). Crafted objects passed to the `renderSync` function may trigger C++ assertions in `CustomImporterBridge::get_importer_entry` and `CustomImporterBridge::post_process_return_value` that crash the Node process. This may allow attackers to crash the system's running Node process and lead to Denial of Service.\n\n\n## Recommendation\n\nUpgrade to version 4.13.1 or later",
490 | "ghsaId": "GHSA-9v62-24cr-58cx",
491 | "identifiers": [
492 | {
493 | "type": "GHSA",
494 | "value": "GHSA-9v62-24cr-58cx"
495 | }
496 | ],
497 | "permalink": "https://github.com/advisories/GHSA-9v62-24cr-58cx",
498 | "publishedAt": "2020-09-03T15:27:25Z"
499 | }
500 | },
501 | {
502 | "id": "MDI4OlJlcG9zaXRvcnlWdWxuZXJhYmlsaXR5QWxlcnQzNzM0MjI4Mzc=",
503 | "createdAt": "2020-09-05T14:05:44Z",
504 | "dismisser": null,
505 | "dismissedAt": null,
506 | "dismissReason": null,
507 | "vulnerableManifestFilename": "package-lock.json",
508 | "vulnerableRequirements": "= 4.6.1",
509 | "vulnerableManifestPath": "docs/package-lock.json",
510 | "securityVulnerability": {
511 | "package": {
512 | "ecosystem": "NPM",
513 | "name": "lodash.mergewith"
514 | },
515 | "severity": "HIGH",
516 | "vulnerableVersionRange": "< 4.6.2"
517 | },
518 | "securityAdvisory": {
519 | "databaseId": 2726,
520 | "id": "MDE2OlNlY3VyaXR5QWR2aXNvcnlHSFNBLTc3OWYtd2d4Zy1xcjhm",
521 | "summary": "Prototype Pollution in lodash.mergewith",
522 | "severity": "HIGH",
523 | "description": "Versions of `lodash.mergewith` before 4.6.2 are vulnerable to prototype pollution. The function `mergeWith` may allow a malicious user to modify the prototype of `Object` via `{constructor: {prototype: {...}}}` causing the addition or modification of an existing property that will exist on all objects.\n\n\n\n\n## Recommendation\n\nUpdate to version 4.6.2 or later.",
524 | "ghsaId": "GHSA-779f-wgxg-qr8f",
525 | "identifiers": [
526 | {
527 | "type": "GHSA",
528 | "value": "GHSA-779f-wgxg-qr8f"
529 | }
530 | ],
531 | "permalink": "https://github.com/advisories/GHSA-779f-wgxg-qr8f",
532 | "publishedAt": "2020-09-03T18:10:22Z"
533 | }
534 | },
535 | {
536 | "id": "MDI4OlJlcG9zaXRvcnlWdWxuZXJhYmlsaXR5QWxlcnQzNzM0MjI4Mzg=",
537 | "createdAt": "2020-09-05T14:05:44Z",
538 | "dismisser": null,
539 | "dismissedAt": null,
540 | "dismissReason": null,
541 | "vulnerableManifestFilename": "package-lock.json",
542 | "vulnerableRequirements": "= 1.15.2",
543 | "vulnerableManifestPath": "docs/package-lock.json",
544 | "securityVulnerability": {
545 | "package": {
546 | "ecosystem": "NPM",
547 | "name": "http-proxy"
548 | },
549 | "severity": "HIGH",
550 | "vulnerableVersionRange": "< 1.18.1"
551 | },
552 | "securityAdvisory": {
553 | "databaseId": 3040,
554 | "id": "MDE2OlNlY3VyaXR5QWR2aXNvcnlHSFNBLTZ4MzMtcHc3cC1obXBx",
555 | "summary": "Denial of Service in http-proxy",
556 | "severity": "HIGH",
557 | "description": "Versions of `http-proxy` prior to 1.18.1 are vulnerable to Denial of Service. An HTTP request with a long body triggers an `ERR_HTTP_HEADERS_SENT` unhandled exception that crashes the proxy server. This is only possible when the proxy server sets headers in the proxy request using the `proxyReq.setHeader` function. \n\nFor a proxy server running on `http://localhost:3000`, the following curl request triggers the unhandled exception: \n```curl -XPOST http://localhost:3000 -d \"$(python -c 'print(\"x\"*1025)')\"```\n\n\n## Recommendation\n\nUpgrade to version 1.18.1 or later",
558 | "ghsaId": "GHSA-6x33-pw7p-hmpq",
559 | "identifiers": [
560 | {
561 | "type": "GHSA",
562 | "value": "GHSA-6x33-pw7p-hmpq"
563 | }
564 | ],
565 | "permalink": "https://github.com/advisories/GHSA-6x33-pw7p-hmpq",
566 | "publishedAt": "2020-09-04T17:59:49Z"
567 | }
568 | },
569 | {
570 | "id": "MDI4OlJlcG9zaXRvcnlWdWxuZXJhYmlsaXR5QWxlcnQzNzM0MjI4Mzk=",
571 | "createdAt": "2020-09-05T14:05:44Z",
572 | "dismisser": null,
573 | "dismissedAt": null,
574 | "dismissReason": null,
575 | "vulnerableManifestFilename": "package-lock.json",
576 | "vulnerableRequirements": "= 4.2.1",
577 | "vulnerableManifestPath": "docs/package-lock.json",
578 | "securityVulnerability": {
579 | "package": {
580 | "ecosystem": "NPM",
581 | "name": "yargs-parser"
582 | },
583 | "severity": "LOW",
584 | "vulnerableVersionRange": "< 13.1.2"
585 | },
586 | "securityAdvisory": {
587 | "databaseId": 3048,
588 | "id": "MDE2OlNlY3VyaXR5QWR2aXNvcnlHSFNBLXA5cGMtMjk5cC12eGdw",
589 | "summary": "Prototype Pollution in yargs-parser",
590 | "severity": "LOW",
591 | "description": "Affected versions of `yargs-parser` are vulnerable to prototype pollution. Arguments are not properly sanitized, allowing an attacker to modify the prototype of `Object`, causing the addition or modification of an existing property that will exist on all objects. \nParsing the argument `--foo.__proto__.bar baz'` adds a `bar` property with value `baz` to all objects. This is only exploitable if attackers have control over the arguments being passed to `yargs-parser`.\n\n\n\n## Recommendation\n\nUpgrade to versions 13.1.2, 15.0.1, 18.1.1 or later.",
592 | "ghsaId": "GHSA-p9pc-299p-vxgp",
593 | "identifiers": [
594 | {
595 | "type": "GHSA",
596 | "value": "GHSA-p9pc-299p-vxgp"
597 | }
598 | ],
599 | "permalink": "https://github.com/advisories/GHSA-p9pc-299p-vxgp",
600 | "publishedAt": "2020-09-04T18:00:54Z"
601 | }
602 | }
603 | ]
604 | }
605 | }
606 | }
607 | }
--------------------------------------------------------------------------------
/samples/sarif/java/basic/java.sarif:
--------------------------------------------------------------------------------
1 | {
2 | "$schema" : "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
3 | "version" : "2.1.0",
4 | "runs" : [ {
5 | "tool" : {
6 | "driver" : {
7 | "name" : "CodeQL",
8 | "organization" : "GitHub",
9 | "semanticVersion" : "2.2.5",
10 | "rules" : [ {
11 | "id" : "java/unsafe-deserialization",
12 | "name" : "java/unsafe-deserialization",
13 | "shortDescription" : {
14 | "text" : "Deserialization of user-controlled data"
15 | },
16 | "fullDescription" : {
17 | "text" : "Deserializing user-controlled data may allow attackers to execute arbitrary code."
18 | },
19 | "defaultConfiguration" : {
20 | "level" : "error"
21 | },
22 | "properties" : {
23 | "tags" : [ "security", "external/cwe/cwe-502" ],
24 | "kind" : "path-problem",
25 | "precision" : "high",
26 | "name" : "Deserialization of user-controlled data",
27 | "description" : "Deserializing user-controlled data may allow attackers to\n execute arbitrary code.",
28 | "id" : "java/unsafe-deserialization",
29 | "problem.severity" : "error"
30 | }
31 | }, {
32 | "id" : "java/ldap-injection",
33 | "name" : "java/ldap-injection",
34 | "shortDescription" : {
35 | "text" : "LDAP query built from user-controlled sources"
36 | },
37 | "fullDescription" : {
38 | "text" : "Building an LDAP query from user-controlled sources is vulnerable to insertion of malicious LDAP code by the user."
39 | },
40 | "defaultConfiguration" : {
41 | "level" : "error"
42 | },
43 | "properties" : {
44 | "tags" : [ "security", "external/cwe/cwe-090" ],
45 | "kind" : "path-problem",
46 | "precision" : "high",
47 | "name" : "LDAP query built from user-controlled sources",
48 | "description" : "Building an LDAP query from user-controlled sources is vulnerable to insertion of\n malicious LDAP code by the user.",
49 | "id" : "java/ldap-injection",
50 | "problem.severity" : "error"
51 | }
52 | }, {
53 | "id" : "java/xss",
54 | "name" : "java/xss",
55 | "shortDescription" : {
56 | "text" : "Cross-site scripting"
57 | },
58 | "fullDescription" : {
59 | "text" : "Writing user input directly to a web page allows for a cross-site scripting vulnerability."
60 | },
61 | "defaultConfiguration" : {
62 | "level" : "error"
63 | },
64 | "properties" : {
65 | "tags" : [ "security", "external/cwe/cwe-079" ],
66 | "kind" : "path-problem",
67 | "precision" : "high",
68 | "name" : "Cross-site scripting",
69 | "description" : "Writing user input directly to a web page\n allows for a cross-site scripting vulnerability.",
70 | "id" : "java/xss",
71 | "problem.severity" : "error"
72 | }
73 | }, {
74 | "id" : "java/insecure-cookie",
75 | "name" : "java/insecure-cookie",
76 | "shortDescription" : {
77 | "text" : "Failure to use secure cookies"
78 | },
79 | "fullDescription" : {
80 | "text" : "Insecure cookies may be sent in cleartext, which makes them vulnerable to interception."
81 | },
82 | "defaultConfiguration" : {
83 | "level" : "error"
84 | },
85 | "properties" : {
86 | "tags" : [ "security", "external/cwe/cwe-614" ],
87 | "kind" : "problem",
88 | "precision" : "high",
89 | "name" : "Failure to use secure cookies",
90 | "description" : "Insecure cookies may be sent in cleartext, which makes them vulnerable to\n interception.",
91 | "id" : "java/insecure-cookie",
92 | "problem.severity" : "error"
93 | }
94 | }, {
95 | "id" : "java/maven/non-https-url",
96 | "name" : "java/maven/non-https-url",
97 | "shortDescription" : {
98 | "text" : "Failure to use HTTPS or SFTP URL in Maven artifact upload/download"
99 | },
100 | "fullDescription" : {
101 | "text" : "Non-HTTPS connections can be intercepted by third parties."
102 | },
103 | "defaultConfiguration" : {
104 | "level" : "error"
105 | },
106 | "properties" : {
107 | "tags" : [ "security", "external/cwe/cwe-300", "external/cwe/cwe-319", "external/cwe/cwe-494", "external/cwe/cwe-829" ],
108 | "kind" : "problem",
109 | "precision" : "very-high",
110 | "name" : "Failure to use HTTPS or SFTP URL in Maven artifact upload/download",
111 | "description" : "Non-HTTPS connections can be intercepted by third parties.",
112 | "id" : "java/maven/non-https-url",
113 | "problem.severity" : "error"
114 | }
115 | }, {
116 | "id" : "java/tainted-format-string",
117 | "name" : "java/tainted-format-string",
118 | "shortDescription" : {
119 | "text" : "Use of externally-controlled format string"
120 | },
121 | "fullDescription" : {
122 | "text" : "Using external input in format strings can lead to exceptions or information leaks."
123 | },
124 | "defaultConfiguration" : {
125 | "level" : "error"
126 | },
127 | "properties" : {
128 | "tags" : [ "security", "external/cwe/cwe-134" ],
129 | "kind" : "path-problem",
130 | "precision" : "high",
131 | "name" : "Use of externally-controlled format string",
132 | "description" : "Using external input in format strings can lead to exceptions or information leaks.",
133 | "id" : "java/tainted-format-string",
134 | "problem.severity" : "error"
135 | }
136 | }, {
137 | "id" : "java/sql-injection",
138 | "name" : "java/sql-injection",
139 | "shortDescription" : {
140 | "text" : "Query built from user-controlled sources"
141 | },
142 | "fullDescription" : {
143 | "text" : "Building a SQL or Java Persistence query from user-controlled sources is vulnerable to insertion of malicious code by the user."
144 | },
145 | "defaultConfiguration" : {
146 | "level" : "error"
147 | },
148 | "properties" : {
149 | "tags" : [ "security", "external/cwe/cwe-089" ],
150 | "kind" : "path-problem",
151 | "precision" : "high",
152 | "name" : "Query built from user-controlled sources",
153 | "description" : "Building a SQL or Java Persistence query from user-controlled sources is vulnerable to insertion of\n malicious code by the user.",
154 | "id" : "java/sql-injection",
155 | "problem.severity" : "error"
156 | }
157 | }, {
158 | "id" : "java/concatenated-sql-query",
159 | "name" : "java/concatenated-sql-query",
160 | "shortDescription" : {
161 | "text" : "Query built without neutralizing special characters"
162 | },
163 | "fullDescription" : {
164 | "text" : "Building a SQL or Java Persistence query without escaping or otherwise neutralizing any special characters is vulnerable to insertion of malicious code."
165 | },
166 | "defaultConfiguration" : {
167 | "level" : "error"
168 | },
169 | "properties" : {
170 | "tags" : [ "security", "external/cwe/cwe-089" ],
171 | "kind" : "problem",
172 | "precision" : "high",
173 | "name" : "Query built without neutralizing special characters",
174 | "description" : "Building a SQL or Java Persistence query without escaping or otherwise neutralizing any special\n characters is vulnerable to insertion of malicious code.",
175 | "id" : "java/concatenated-sql-query",
176 | "problem.severity" : "error"
177 | }
178 | }, {
179 | "id" : "java/tainted-numeric-cast",
180 | "name" : "java/tainted-numeric-cast",
181 | "shortDescription" : {
182 | "text" : "User-controlled data in numeric cast"
183 | },
184 | "fullDescription" : {
185 | "text" : "Casting user-controlled numeric data to a narrower type without validation can cause unexpected truncation."
186 | },
187 | "defaultConfiguration" : {
188 | "level" : "error"
189 | },
190 | "properties" : {
191 | "tags" : [ "security", "external/cwe/cwe-197", "external/cwe/cwe-681" ],
192 | "kind" : "path-problem",
193 | "precision" : "high",
194 | "name" : "User-controlled data in numeric cast",
195 | "description" : "Casting user-controlled numeric data to a narrower type without validation\n can cause unexpected truncation.",
196 | "id" : "java/tainted-numeric-cast",
197 | "problem.severity" : "error"
198 | }
199 | }, {
200 | "id" : "java/tainted-permissions-check",
201 | "name" : "java/tainted-permissions-check",
202 | "shortDescription" : {
203 | "text" : "User-controlled data used in permissions check"
204 | },
205 | "fullDescription" : {
206 | "text" : "Using user-controlled data in a permissions check may result in inappropriate permissions being granted."
207 | },
208 | "defaultConfiguration" : {
209 | "level" : "error"
210 | },
211 | "properties" : {
212 | "tags" : [ "security", "external/cwe/cwe-807", "external/cwe/cwe-290" ],
213 | "kind" : "path-problem",
214 | "precision" : "high",
215 | "name" : "User-controlled data used in permissions check",
216 | "description" : "Using user-controlled data in a permissions check may result in inappropriate\n permissions being granted.",
217 | "id" : "java/tainted-permissions-check",
218 | "problem.severity" : "error"
219 | }
220 | }, {
221 | "id" : "java/spring-disabled-csrf-protection",
222 | "name" : "java/spring-disabled-csrf-protection",
223 | "shortDescription" : {
224 | "text" : "Disabled Spring CSRF protection"
225 | },
226 | "fullDescription" : {
227 | "text" : "Disabling CSRF protection makes the application vulnerable to a Cross-Site Request Forgery (CSRF) attack."
228 | },
229 | "defaultConfiguration" : {
230 | "level" : "error"
231 | },
232 | "properties" : {
233 | "tags" : [ "security", "external/cwe/cwe-352" ],
234 | "kind" : "problem",
235 | "precision" : "high",
236 | "name" : "Disabled Spring CSRF protection",
237 | "description" : "Disabling CSRF protection makes the application vulnerable to\n a Cross-Site Request Forgery (CSRF) attack.",
238 | "id" : "java/spring-disabled-csrf-protection",
239 | "problem.severity" : "error"
240 | }
241 | }, {
242 | "id" : "java/cleartext-storage-in-cookie",
243 | "name" : "java/cleartext-storage-in-cookie",
244 | "shortDescription" : {
245 | "text" : "Cleartext storage of sensitive information in cookie"
246 | },
247 | "fullDescription" : {
248 | "text" : "Storing sensitive information in cleartext can expose it to an attacker."
249 | },
250 | "defaultConfiguration" : {
251 | "level" : "error"
252 | },
253 | "properties" : {
254 | "tags" : [ "security", "external/cwe/cwe-315" ],
255 | "kind" : "problem",
256 | "precision" : "high",
257 | "name" : "Cleartext storage of sensitive information in cookie",
258 | "description" : "Storing sensitive information in cleartext can expose it to an attacker.",
259 | "id" : "java/cleartext-storage-in-cookie",
260 | "problem.severity" : "error"
261 | }
262 | }, {
263 | "id" : "java/unvalidated-url-redirection",
264 | "name" : "java/unvalidated-url-redirection",
265 | "shortDescription" : {
266 | "text" : "URL redirection from remote source"
267 | },
268 | "fullDescription" : {
269 | "text" : "URL redirection based on unvalidated user-input may cause redirection to malicious web sites."
270 | },
271 | "defaultConfiguration" : {
272 | "level" : "error"
273 | },
274 | "properties" : {
275 | "tags" : [ "security", "external/cwe/cwe-601" ],
276 | "kind" : "path-problem",
277 | "precision" : "high",
278 | "name" : "URL redirection from remote source",
279 | "description" : "URL redirection based on unvalidated user-input\n may cause redirection to malicious web sites.",
280 | "id" : "java/unvalidated-url-redirection",
281 | "problem.severity" : "error"
282 | }
283 | }, {
284 | "id" : "java/stack-trace-exposure",
285 | "name" : "java/stack-trace-exposure",
286 | "shortDescription" : {
287 | "text" : "Information exposure through a stack trace"
288 | },
289 | "fullDescription" : {
290 | "text" : "Information from a stack trace propagates to an external user. Stack traces can unintentionally reveal implementation details that are useful to an attacker for developing a subsequent exploit."
291 | },
292 | "defaultConfiguration" : {
293 | "level" : "error"
294 | },
295 | "properties" : {
296 | "tags" : [ "security", "external/cwe/cwe-209", "external/cwe/cwe-497" ],
297 | "kind" : "problem",
298 | "precision" : "high",
299 | "name" : "Information exposure through a stack trace",
300 | "description" : "Information from a stack trace propagates to an external user.\n Stack traces can unintentionally reveal implementation details\n that are useful to an attacker for developing a subsequent exploit.",
301 | "id" : "java/stack-trace-exposure",
302 | "problem.severity" : "error"
303 | }
304 | }, {
305 | "id" : "java/netty-http-response-splitting",
306 | "name" : "java/netty-http-response-splitting",
307 | "shortDescription" : {
308 | "text" : "Disabled Netty HTTP header validation"
309 | },
310 | "fullDescription" : {
311 | "text" : "Disabling HTTP header validation makes code vulnerable to attack by header splitting if user input is written directly to an HTTP header."
312 | },
313 | "defaultConfiguration" : {
314 | "level" : "error"
315 | },
316 | "properties" : {
317 | "tags" : [ "security", "external/cwe/cwe-113" ],
318 | "kind" : "problem",
319 | "precision" : "high",
320 | "name" : "Disabled Netty HTTP header validation",
321 | "description" : "Disabling HTTP header validation makes code vulnerable to\n attack by header splitting if user input is written directly to\n an HTTP header.",
322 | "id" : "java/netty-http-response-splitting",
323 | "problem.severity" : "error"
324 | }
325 | }, {
326 | "id" : "java/http-response-splitting",
327 | "name" : "java/http-response-splitting",
328 | "shortDescription" : {
329 | "text" : "HTTP response splitting"
330 | },
331 | "fullDescription" : {
332 | "text" : "Writing user input directly to an HTTP header makes code vulnerable to attack by header splitting."
333 | },
334 | "defaultConfiguration" : {
335 | "level" : "error"
336 | },
337 | "properties" : {
338 | "tags" : [ "security", "external/cwe/cwe-113" ],
339 | "kind" : "path-problem",
340 | "precision" : "high",
341 | "name" : "HTTP response splitting",
342 | "description" : "Writing user input directly to an HTTP header\n makes code vulnerable to attack by header splitting.",
343 | "id" : "java/http-response-splitting",
344 | "problem.severity" : "error"
345 | }
346 | }, {
347 | "id" : "java/zipslip",
348 | "name" : "java/zipslip",
349 | "shortDescription" : {
350 | "text" : "Arbitrary file write during archive extraction (\"Zip Slip\")"
351 | },
352 | "fullDescription" : {
353 | "text" : "Extracting files from a malicious archive without validating that the destination file path is within the destination directory can cause files outside the destination directory to be overwritten."
354 | },
355 | "defaultConfiguration" : {
356 | "level" : "error"
357 | },
358 | "properties" : {
359 | "tags" : [ "security", "external/cwe/cwe-022" ],
360 | "kind" : "path-problem",
361 | "precision" : "high",
362 | "name" : "Arbitrary file write during archive extraction (\"Zip Slip\")",
363 | "description" : "Extracting files from a malicious archive without validating that the\n destination file path is within the destination directory can cause files outside\n the destination directory to be overwritten.",
364 | "id" : "java/zipslip",
365 | "problem.severity" : "error"
366 | }
367 | }, {
368 | "id" : "java/path-injection",
369 | "name" : "java/path-injection",
370 | "shortDescription" : {
371 | "text" : "Uncontrolled data used in path expression"
372 | },
373 | "fullDescription" : {
374 | "text" : "Accessing paths influenced by users can allow an attacker to access unexpected resources."
375 | },
376 | "defaultConfiguration" : {
377 | "level" : "error"
378 | },
379 | "properties" : {
380 | "tags" : [ "security", "external/cwe/cwe-022", "external/cwe/cwe-023", "external/cwe/cwe-036", "external/cwe/cwe-073" ],
381 | "kind" : "path-problem",
382 | "precision" : "high",
383 | "name" : "Uncontrolled data used in path expression",
384 | "description" : "Accessing paths influenced by users can allow an attacker to access unexpected resources.",
385 | "id" : "java/path-injection",
386 | "problem.severity" : "error"
387 | }
388 | }, {
389 | "id" : "java/predictable-seed",
390 | "name" : "java/predictable-seed",
391 | "shortDescription" : {
392 | "text" : "Use of a predictable seed in a secure random number generator"
393 | },
394 | "fullDescription" : {
395 | "text" : "Using a predictable seed in a pseudo-random number generator can lead to predictability of the numbers generated by it."
396 | },
397 | "defaultConfiguration" : {
398 | "level" : "error"
399 | },
400 | "properties" : {
401 | "tags" : [ "security" ],
402 | "kind" : "problem",
403 | "precision" : "high",
404 | "name" : "Use of a predictable seed in a secure random number generator",
405 | "description" : "Using a predictable seed in a pseudo-random number generator can lead to predictability of the numbers generated by it.",
406 | "id" : "java/predictable-seed",
407 | "problem.severity" : "error"
408 | }
409 | }, {
410 | "id" : "java/command-line-injection",
411 | "name" : "java/command-line-injection",
412 | "shortDescription" : {
413 | "text" : "Uncontrolled command line"
414 | },
415 | "fullDescription" : {
416 | "text" : "Using externally controlled strings in a command line is vulnerable to malicious changes in the strings."
417 | },
418 | "defaultConfiguration" : {
419 | "level" : "error"
420 | },
421 | "properties" : {
422 | "tags" : [ "security", "external/cwe/cwe-078", "external/cwe/cwe-088" ],
423 | "kind" : "path-problem",
424 | "precision" : "high",
425 | "name" : "Uncontrolled command line",
426 | "description" : "Using externally controlled strings in a command line is vulnerable to malicious\n changes in the strings.",
427 | "id" : "java/command-line-injection",
428 | "problem.severity" : "error"
429 | }
430 | }, {
431 | "id" : "java/concatenated-command-line",
432 | "name" : "java/concatenated-command-line",
433 | "shortDescription" : {
434 | "text" : "Building a command line with string concatenation"
435 | },
436 | "fullDescription" : {
437 | "text" : "Using concatenated strings in a command line is vulnerable to malicious insertion of special characters in the strings."
438 | },
439 | "defaultConfiguration" : {
440 | "level" : "error"
441 | },
442 | "properties" : {
443 | "tags" : [ "security", "external/cwe/cwe-078", "external/cwe/cwe-088" ],
444 | "kind" : "problem",
445 | "precision" : "high",
446 | "name" : "Building a command line with string concatenation",
447 | "description" : "Using concatenated strings in a command line is vulnerable to malicious\n insertion of special characters in the strings.",
448 | "id" : "java/concatenated-command-line",
449 | "problem.severity" : "error"
450 | }
451 | }, {
452 | "id" : "java/world-writable-file-read",
453 | "name" : "java/world-writable-file-read",
454 | "shortDescription" : {
455 | "text" : "Reading from a world writable file"
456 | },
457 | "fullDescription" : {
458 | "text" : "Reading from a file which is set as world writable is dangerous because the file may be modified or removed by external actors."
459 | },
460 | "defaultConfiguration" : {
461 | "level" : "error"
462 | },
463 | "properties" : {
464 | "tags" : [ "security", "external/cwe/cwe-732" ],
465 | "kind" : "problem",
466 | "precision" : "high",
467 | "name" : "Reading from a world writable file",
468 | "description" : "Reading from a file which is set as world writable is dangerous because\n the file may be modified or removed by external actors.",
469 | "id" : "java/world-writable-file-read",
470 | "problem.severity" : "error"
471 | }
472 | }, {
473 | "id" : "java/xxe",
474 | "name" : "java/xxe",
475 | "shortDescription" : {
476 | "text" : "Resolving XML external entity in user-controlled data"
477 | },
478 | "fullDescription" : {
479 | "text" : "Parsing user-controlled XML documents and allowing expansion of external entity references may lead to disclosure of confidential data or denial of service."
480 | },
481 | "defaultConfiguration" : {
482 | "level" : "error"
483 | },
484 | "properties" : {
485 | "tags" : [ "security", "external/cwe/cwe-611" ],
486 | "kind" : "path-problem",
487 | "precision" : "high",
488 | "name" : "Resolving XML external entity in user-controlled data",
489 | "description" : "Parsing user-controlled XML documents and allowing expansion of external entity\n references may lead to disclosure of confidential data or denial of service.",
490 | "id" : "java/xxe",
491 | "problem.severity" : "error"
492 | }
493 | }, {
494 | "id" : "java/implicit-cast-in-compound-assignment",
495 | "name" : "java/implicit-cast-in-compound-assignment",
496 | "shortDescription" : {
497 | "text" : "Implicit narrowing conversion in compound assignment"
498 | },
499 | "fullDescription" : {
500 | "text" : "Compound assignment statements (for example 'intvar += longvar') that implicitly cast a value of a wider type to a narrower type may result in information loss and numeric errors such as overflows."
501 | },
502 | "defaultConfiguration" : { },
503 | "properties" : {
504 | "tags" : [ "reliability", "security", "external/cwe/cwe-190", "external/cwe/cwe-192", "external/cwe/cwe-197", "external/cwe/cwe-681" ],
505 | "kind" : "problem",
506 | "precision" : "very-high",
507 | "name" : "Implicit narrowing conversion in compound assignment",
508 | "description" : "Compound assignment statements (for example 'intvar += longvar') that implicitly\n cast a value of a wider type to a narrower type may result in information loss and\n numeric errors such as overflows.",
509 | "id" : "java/implicit-cast-in-compound-assignment",
510 | "problem.severity" : "warning"
511 | }
512 | }, {
513 | "id" : "java/integer-multiplication-cast-to-long",
514 | "name" : "java/integer-multiplication-cast-to-long",
515 | "shortDescription" : {
516 | "text" : "Result of multiplication cast to wider type"
517 | },
518 | "fullDescription" : {
519 | "text" : "Casting the result of a multiplication to a wider type instead of casting before the multiplication may cause overflow."
520 | },
521 | "defaultConfiguration" : { },
522 | "properties" : {
523 | "tags" : [ "reliability", "security", "correctness", "types", "external/cwe/cwe-190", "external/cwe/cwe-192", "external/cwe/cwe-197", "external/cwe/cwe-681" ],
524 | "kind" : "problem",
525 | "precision" : "very-high",
526 | "name" : "Result of multiplication cast to wider type",
527 | "description" : "Casting the result of a multiplication to a wider type instead of casting\n before the multiplication may cause overflow.",
528 | "id" : "java/integer-multiplication-cast-to-long",
529 | "problem.severity" : "warning"
530 | }
531 | } ]
532 | }
533 | },
534 | "results" : [ ],
535 | "columnKind" : "utf16CodeUnits",
536 | "properties" : {
537 | "semmle.formatSpecifier" : "sarif-latest"
538 | }
539 | } ]
540 | }
--------------------------------------------------------------------------------
/src/DataCollector.ts:
--------------------------------------------------------------------------------
1 | import {Octokit} from "@octokit/rest";
2 | import GitHubCodeScanning from './codeScanning/GitHubCodeScanning';
3 | import GitHubDependencies from './dependencies/GitHubDependencies';
4 | import SarifReportFinder from './sarif/SarifReportFinder';
5 | import ReportData from './templating/ReportData';
6 | import { CollectedData } from './templating/ReportTypes';
7 |
8 |
9 | type Repo = {
10 | owner: string,
11 | repo: string
12 | }
13 |
14 | export default class DataCollector {
15 |
16 | private readonly repo: Repo
17 |
18 | private readonly octokit
19 |
20 | constructor(octokit: Octokit, repo: string) {
21 | if (!octokit) {
22 | throw new Error('A GitHub Octokit client needs to be provided');
23 | }
24 | this.octokit = octokit;
25 |
26 | if (!repo) {
27 | throw new Error('A GitHub repository must be provided');
28 | }
29 |
30 | const parts = repo.split('/')
31 | this.repo = {
32 | owner: parts[0],
33 | repo: parts[1]
34 | }
35 | }
36 |
37 | getPayload(sarifReportDir: string): Promise {
38 | const ghDeps = new GitHubDependencies(this.octokit)
39 | , codeScanning = new GitHubCodeScanning(this.octokit)
40 | , sarifFinder = new SarifReportFinder(sarifReportDir)
41 | ;
42 |
43 | return Promise.all([
44 | sarifFinder.getSarifFiles(),
45 | ghDeps.getAllDependencies(this.repo),
46 | ghDeps.getAllVulnerabilities(this.repo),
47 | codeScanning.getOpenCodeScanningAlerts(this.repo),
48 | codeScanning.getClosedCodeScanningAlerts(this.repo),
49 | ]).then(results => {
50 |
51 | const data: CollectedData = {
52 | github: this.repo,
53 | sarifReports: results[0],
54 | dependencies: results[1],
55 | vulnerabilities: results[2],
56 | codeScanningOpen: results[3],
57 | codeScanningClosed: results[4],
58 | };
59 |
60 | return new ReportData(data);
61 | });
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/ReportGenerator.test.ts:
--------------------------------------------------------------------------------
1 | import { Octokit } from '@octokit/rest';
2 | import { expect } from 'chai';
3 | import ReportGenerator from './ReportGenerator';
4 | import { getGitHubToken, getSampleSarifDirectory, getTestDirectoryFilePath } from './testUtils';
5 |
6 | const TOKEN: string = getGitHubToken();
7 |
8 | const SIMPLE_TEST_REPOSITORY = {
9 | repository: 'octodemo/ghas-reporting',
10 | sarifReportDir: getSampleSarifDirectory('java', 'detailed')
11 | }
12 |
13 | const PM_AS_JAVA = {
14 | repository: 'peter-murray/advanced-security-java',
15 | sarifReportDir: getSampleSarifDirectory('peter-murray', 'advanced-security-java')
16 | }
17 |
18 | describe('ReportGenerator', function () {
19 |
20 | this.timeout(10 * 1000);
21 |
22 | [SIMPLE_TEST_REPOSITORY, PM_AS_JAVA].forEach(config => {
23 | it(`should generate a report for ${config.repository}`, async () => {
24 | const generatorConfig = {
25 | octokit: new Octokit({auth: TOKEN}),
26 | repository: config.repository,
27 |
28 | sarifReportDirectory: config.sarifReportDir,
29 | outputDirectory: getTestDirectoryFilePath(config.repository),
30 |
31 | templating: {
32 | name: 'summary'
33 | }
34 | }
35 |
36 | const generator = new ReportGenerator(generatorConfig);
37 | const file = await generator.run();
38 | expect(file).to.contain(generatorConfig.outputDirectory);
39 | //TODO need to store an expected result
40 | });
41 | })
42 |
43 |
44 | });
--------------------------------------------------------------------------------
/src/ReportGenerator.ts:
--------------------------------------------------------------------------------
1 | import { Octokit } from '@octokit/rest';
2 | import DataCollector from './DataCollector';
3 | import Template from './templating/Template';
4 | import { createPDF } from './pdf/pdfWriter';
5 | import * as path from 'path';
6 |
7 | import { mkdirP } from '@actions/io';
8 |
9 | export type ReportGeneratorConfig = {
10 | repository: string,
11 | octokit: Octokit,
12 |
13 | sarifReportDirectory: string,
14 | outputDirectory: string,
15 |
16 | templating: {
17 | directory?: string,
18 | name: string,
19 | }
20 | }
21 |
22 | export default class ReportGenerator {
23 |
24 | private readonly config: ReportGeneratorConfig;
25 |
26 | constructor(config: ReportGeneratorConfig) {
27 | this.config = config;
28 | }
29 |
30 | run(): Promise {
31 | const config = this.config;
32 | const collector = new DataCollector(config.octokit, config.repository);
33 |
34 | return collector.getPayload(config.sarifReportDirectory)
35 | .then(reportData => {
36 | const reportTemplate = new Template(config.templating.directory);
37 | return reportTemplate.render(reportData.getJSONPayload(), config.templating.name);
38 | })
39 | .then(html => {
40 | return mkdirP(config.outputDirectory)
41 | .then(() => {
42 | return createPDF(html, path.join(config.outputDirectory, 'summary.pdf'));
43 | });
44 | });
45 | }
46 | }
--------------------------------------------------------------------------------
/src/codeScanning/CodeScanningAlert.ts:
--------------------------------------------------------------------------------
1 | export type Rule = {
2 | id: string,
3 | severity: string,
4 | description: string
5 | }
6 |
7 | export type CodeScanningData = {
8 | number: number;
9 | /**
10 | * The time that the alert was created in ISO 8601 format: `YYYY-MM-DDTHH:MM:SSZ`.
11 | */
12 | created_at: string;
13 | /**
14 | * The REST API URL of the alert resource.
15 | */
16 | url: string;
17 | /**
18 | * The GitHub URL of the alert resource.
19 | */
20 | html_url: string;
21 | /**
22 | * State of a code scanning alert.
23 | */
24 | state: "open" | "dismissed" | "fixed";
25 | dismissed_by: {
26 | login?: string;
27 | id?: number;
28 | node_id?: string;
29 | avatar_url?: string;
30 | gravatar_id?: string;
31 | url?: string;
32 | html_url?: string;
33 | followers_url?: string;
34 | following_url?: string;
35 | gists_url?: string;
36 | starred_url?: string;
37 | subscriptions_url?: string;
38 | organizations_url?: string;
39 | repos_url?: string;
40 | events_url?: string;
41 | received_events_url?: string;
42 | type?: string;
43 | site_admin?: boolean;
44 | [k: string]: unknown;
45 | } | null;
46 | /**
47 | * The time that the alert was dismissed in ISO 8601 format: `YYYY-MM-DDTHH:MM:SSZ`.
48 | */
49 | dismissed_at: string;
50 | /**
51 | * **Required when the state is dismissed.** The reason for dismissing or closing the alert. Can be one of: `false positive`, `won't fix`, and `used in tests`.
52 | */
53 | dismissed_reason: ("false positive" | "won't fix" | "used in tests") | null;
54 | rule: {
55 | /**
56 | * A unique identifier for the rule used to detect the alert.
57 | */
58 | id: string;
59 | /**
60 | * The severity of the alert.
61 | */
62 | severity: "none" | "note" | "warning" | "error";
63 | /**
64 | * A short description of the rule used to detect the alert.
65 | */
66 | description: string;
67 | };
68 | tool: {
69 | /**
70 | * The name of the tool used to generate the code scanning analysis alert.
71 | */
72 | name: string;
73 | /**
74 | * The version of the tool used to detect the alert.
75 | */
76 | version: string;
77 | };
78 | }
79 |
80 | export type AlertDismissal = {
81 | at: string,
82 | reason: 'false positive' | 'won\'t fix' | 'used in tests' | null,
83 | by?: {
84 | login?: string,
85 | type?: string,
86 | id?: number,
87 | }
88 | }
89 |
90 | export default class CodeScanningAlert {
91 |
92 | private readonly data: CodeScanningData;
93 |
94 | constructor(data: CodeScanningData) {
95 | this.data = data;
96 | }
97 |
98 | get id(): number {
99 | return this.data.number;
100 | }
101 |
102 | get url(): string {
103 | return this.data.html_url;
104 | }
105 |
106 | get created(): string {
107 | return this.data.created_at;
108 | }
109 |
110 | get dismissed(): AlertDismissal | null {
111 | if (!this.data.dismissed_at) {
112 | return null;
113 | }
114 |
115 | const result: AlertDismissal = {
116 | at: this.data.dismissed_at,
117 | reason: this.data.dismissed_reason,
118 | };
119 |
120 | if (this.data.dismissed_by) {
121 | result.by = {
122 | login: this.data.dismissed_by.login,
123 | type: this.data.dismissed_by.type,
124 | id: this.data.dismissed_by.id,
125 | //TODO these were invalid
126 | // avatar: this._data.dismissed_at.avatar_url,
127 | // url: this._data.dismissed_at.html_url,
128 | };
129 | }
130 |
131 | return result;
132 | }
133 |
134 | get severity(): string {
135 | // return this.rule ? this.rule.severity : null;
136 | return this.rule.severity;
137 | }
138 |
139 | get state(): string {
140 | return this.data.state;
141 | }
142 |
143 | get rule(): Rule {
144 | return this.data.rule;
145 | }
146 |
147 | get ruleId(): string {
148 | return this.rule.id;
149 | }
150 |
151 | get ruleDescription(): string {
152 | return this.rule.description;
153 | }
154 |
155 | get toolName(): string | null {
156 | return this.data.tool ? this.data.tool.name : null;
157 | }
158 |
159 | get toolVersion(): string | null {
160 | return this.data.tool ? this.data.tool.version : null;
161 | }
162 | }
--------------------------------------------------------------------------------
/src/codeScanning/CodeScanningResults.ts:
--------------------------------------------------------------------------------
1 | import CodeScanningAlert from './CodeScanningAlert';
2 |
3 | export default class CodeScanningResults {
4 |
5 | private data: CodeScanningAlert[];
6 |
7 | constructor() {
8 | this.data = [];
9 | }
10 |
11 | addCodeScanningAlert(alert: CodeScanningAlert) {
12 | this.data.push(alert);
13 | }
14 |
15 | getTools(): string[] {
16 | const result: string[] = [];
17 |
18 | this.data.forEach(alert => {
19 | const toolName = alert.toolName;
20 |
21 | if (toolName && result.indexOf(toolName) === -1) {
22 | result.push(toolName);
23 | }
24 | })
25 |
26 | return result;
27 | }
28 |
29 | getCodeQLScanningAlerts(): CodeScanningAlert[] {
30 | return this.data.filter(value => {
31 | //TODO this is now reporting CodeQL command-line toolchain as the name of the tool!
32 | // Need to follow up on this with GHAS team on what to expect in the future.
33 | return `${value.toolName}`.toLowerCase().startsWith('codeql');
34 | });
35 | }
36 | }
--------------------------------------------------------------------------------
/src/codeScanning/GitHubCodeScanning.test.ts:
--------------------------------------------------------------------------------
1 | import { expect } from 'chai';
2 | import { Octokit } from '@octokit/rest';
3 | import GitHubCodeScanning from './GitHubCodeScanning';
4 | import { getGitHubToken } from '../testUtils';
5 |
6 | describe('GitHubDependencies', () => {
7 |
8 | const testRepo = {
9 | owner: 'octodemo',
10 | repo: 'demo-vulnerabilities-ghas'
11 | };
12 |
13 | const ghasReportingRepo = {
14 | owner: 'octodemo',
15 | repo: 'ghas-reporting'
16 | };
17 |
18 | const pmAdvanceSecurityJava = {
19 | owner: 'peter-murray',
20 | repo: 'advanced-security-java'
21 | };
22 |
23 | let codeScanning: GitHubCodeScanning;
24 |
25 | before(() => {
26 | const octokit = new Octokit({auth: getGitHubToken()});
27 | codeScanning = new GitHubCodeScanning(octokit);
28 | });
29 |
30 |
31 | describe('getOpenCodeScanningAlerts()', () => {
32 |
33 | it(`from ${JSON.stringify(testRepo)}`, async () => {
34 | const results = await codeScanning.getOpenCodeScanningAlerts(testRepo)
35 | , tools = results.getTools()
36 | ;
37 |
38 | expect(tools).to.have.length(1);
39 | expect(tools[0]).to.equal('CodeQL');
40 | });
41 |
42 | it(`from ${JSON.stringify(ghasReportingRepo)}`, async () => {
43 | const results = await codeScanning.getOpenCodeScanningAlerts(ghasReportingRepo)
44 | , tools = results.getTools()
45 | ;
46 |
47 | expect(tools).to.have.length(1);
48 | expect(tools[0]).to.equal('-CodeQL-');
49 | });
50 |
51 | it (`from ${JSON.stringify(pmAdvanceSecurityJava)}`, async () => {
52 | const results = await codeScanning.getOpenCodeScanningAlerts(pmAdvanceSecurityJava);
53 |
54 | expect(results.getCodeQLScanningAlerts()).to.have.length(26);//TODO flaky test, sort this out
55 | });
56 | });
57 |
58 | });
--------------------------------------------------------------------------------
/src/codeScanning/GitHubCodeScanning.ts:
--------------------------------------------------------------------------------
1 | import { Octokit } from '@octokit/rest';
2 | import { CodeScanningListAlertsForRepoResponseData, Endpoints } from '@octokit/types';
3 |
4 | import CodeScanningAlert, { CodeScanningData } from './CodeScanningAlert';
5 | import CodeScanningResults from './CodeScanningResults';
6 |
7 | type listCodeScanningAlertsParameters = Endpoints['GET /repos/:owner/:repo/code-scanning/alerts']['parameters'];
8 |
9 | type Repo = {
10 | owner: string,
11 | repo: string
12 | }
13 |
14 | export default class GitHubCodeScanning {
15 |
16 | private readonly octokit: Octokit;
17 |
18 | constructor(octokit) {
19 | this.octokit = octokit;
20 | }
21 |
22 | getOpenCodeScanningAlerts(repo: Repo): Promise {
23 | return getCodeScanning(this.octokit, repo, 'open');
24 | }
25 |
26 | getClosedCodeScanningAlerts(repo: Repo): Promise {
27 | return getCodeScanning(this.octokit, repo, 'dismissed');
28 | }
29 | }
30 |
31 | function getCodeScanning(octokit: Octokit,
32 | repo: Repo,
33 | state: 'open' | 'fixed' | 'dismissed'): Promise {
34 |
35 | const params: listCodeScanningAlertsParameters = {
36 | owner: repo.owner,
37 | repo: repo.repo,
38 | state: state
39 | };
40 |
41 | return octokit.paginate('GET /repos/:owner/:repo/code-scanning/alerts', params)
42 | //@ts-ignore
43 | .then((alerts: CodeScanningListAlertsForRepoResponseData) => {
44 | const results: CodeScanningResults = new CodeScanningResults();
45 |
46 | alerts.forEach((alert: CodeScanningData) => {
47 | results.addCodeScanningAlert(new CodeScanningAlert(alert));
48 | });
49 |
50 | return results;
51 | });
52 | }
--------------------------------------------------------------------------------
/src/dependencies/Dependency.ts:
--------------------------------------------------------------------------------
1 | import { DependencySetDependencyData } from './DependencyTypes';
2 |
3 | export default class Dependency {
4 |
5 | private readonly data: DependencySetDependencyData;
6 |
7 | constructor(data: DependencySetDependencyData) {
8 | this.data = data;
9 | }
10 |
11 | get name(): string {
12 | return this.data.node.packageName;
13 | }
14 |
15 | get packageType(): string {
16 | return this.data.node.packageManager;
17 | }
18 |
19 | get version(): string {
20 | return this.data.node.requirements;
21 | }
22 | }
--------------------------------------------------------------------------------
/src/dependencies/DependencySet.ts:
--------------------------------------------------------------------------------
1 | import Dependency from './Dependency';
2 |
3 | import { DependencySetData } from './DependencyTypes';
4 |
5 | export default class DependencySet {
6 |
7 | private readonly data: DependencySetData;
8 |
9 | constructor(data: DependencySetData) {
10 | this.data = data;
11 | }
12 |
13 | get filename(): string {
14 | return this.data.node.filename;
15 | }
16 |
17 | get count(): number {
18 | return this.data.node.dependenciesCount || 0;
19 | }
20 |
21 | get path(): string {
22 | return this.data.node.blobPath;
23 | }
24 |
25 | get isValid(): boolean {
26 | return this.parsable && !this.exceededMaxSize;
27 | }
28 |
29 | get parsable(): boolean {
30 | return this.data.node.parseable;
31 | }
32 |
33 | get exceededMaxSize(): boolean {
34 | return this.data.node.exceedsMaxSize;
35 | }
36 |
37 | get dependencies(): Dependency[] {
38 | const deps = this.data.node.dependencies.edges;
39 |
40 | if (deps) {
41 | return deps.map(dep => {
42 | return new Dependency(dep);
43 | });
44 | }
45 | return [];
46 | }
47 | }
--------------------------------------------------------------------------------
/src/dependencies/DependencyTypes.ts:
--------------------------------------------------------------------------------
1 | export const QUERY_SECURITY_VULNERABILITIES = `
2 | query users($organizationName: String!, $repositoryName: String!, $cursor: String) {
3 |
4 | repository(owner: $organizationName, name: $repositoryName) {
5 | vulnerabilityAlerts(first: 100, after: $cursor) {
6 | totalCount
7 | pageInfo {
8 | hasNextPage
9 | endCursor
10 | }
11 | nodes {
12 | id
13 | createdAt
14 | dismisser {
15 | login
16 | name
17 | }
18 | dismissedAt
19 | dismissReason
20 | vulnerableManifestFilename
21 | vulnerableRequirements
22 | vulnerableManifestPath
23 | securityVulnerability{
24 | package {
25 | ecosystem
26 | name
27 | }
28 | severity
29 | vulnerableVersionRange
30 | }
31 | securityAdvisory{
32 | databaseId
33 | id
34 | summary
35 | severity
36 | description
37 | ghsaId
38 | identifiers {
39 | type
40 | value
41 | }
42 | permalink
43 | publishedAt
44 | }
45 | }
46 | }
47 | }
48 | }
49 | `;
50 |
51 | export type RepositoryVulnerabilityAlerts = {
52 | repository: {
53 | vulnerabilityAlerts: {
54 | totalCount: number,
55 | pageInfo: {
56 | hasNextPage: boolean,
57 | endCursor: string,
58 | },
59 | nodes: VulnerabilityAlert[]
60 | }
61 | }
62 | }
63 |
64 | export type VulnerabilityAlert = {
65 | id: string,
66 | createdAt: string,
67 | dismisser: {
68 | login: string,
69 | name: string,
70 | },
71 | dismissedAt: string,
72 | dismissReason: string,
73 | vulnerableManifestFilename: string,
74 | vulnerableRequirements: string,
75 | vulnerableManifestPath
76 | securityVulnerability: SecurityVulnerability
77 | securityAdvisory: SecurityAdvisory
78 | }
79 |
80 | export type SecurityVulnerability = {
81 | package: {
82 | ecosystem: string,
83 | name: string
84 | }
85 | severity: string,
86 | vulnerableVersionRange: string
87 | }
88 |
89 | export type SecurityAdvisory = {
90 | databaseId: string,
91 | id: string,
92 | summary: string
93 | severity: string,
94 | description: string
95 | ghsaId: string,
96 | identifiers: {
97 | type: string,
98 | value: string,
99 | }
100 | permalink: string
101 | publishedAt: string
102 | }
103 |
104 | export const QUERY_DEPENDENCY_GRAPH = `
105 | query ($organizationName: String!, $repositoryName: String!, $cursor: String){
106 | repository(owner: $organizationName name: $repositoryName) {
107 | name
108 | dependencyGraphManifests(first: 100, after: $cursor) {
109 | pageInfo {
110 | hasNextPage
111 | endCursor
112 | }
113 | totalCount
114 | edges {
115 | node {
116 | filename
117 | dependenciesCount
118 | blobPath
119 | exceedsMaxSize
120 | parseable
121 | dependencies{
122 | edges {
123 | node {
124 | packageName
125 | packageManager
126 | requirements
127 | }
128 | }
129 | }
130 | }
131 | }
132 | }
133 | }
134 | }
135 | `;
136 |
137 | export type DependencyGraphResult = {
138 | repository: {
139 | name: string,
140 | dependencyGraphManifests: {
141 | pageInfo: {
142 | hasNextPage: boolean,
143 | endCursor: string,
144 | },
145 | totalCount: number,
146 | edges: DependencySetData[]
147 | }
148 | }
149 | }
150 |
151 | export type DependencySetData = {
152 | node: {
153 | filename: string
154 | dependenciesCount: number
155 | blobPath: string
156 | exceedsMaxSize: boolean
157 | parseable: boolean
158 | dependencies: {
159 | edges: DependencySetDependencyData []
160 | }
161 | }
162 | }
163 |
164 | export type DependencySetDependencyData = {
165 | node: {
166 | packageName: string
167 | packageManager: string
168 | requirements: string
169 | }
170 | }
171 |
172 |
--------------------------------------------------------------------------------
/src/dependencies/GitHubDependencies.test.ts:
--------------------------------------------------------------------------------
1 | import { expect } from 'chai';
2 | import GitHubDependencies from './GitHubDependencies';
3 |
4 | import { Octokit } from '@octokit/rest';
5 | import DependencySet from './DependencySet';
6 | import Dependency from './Dependency';
7 | import { getGitHubToken } from '../testUtils';
8 |
9 | describe('GitHubDependencies', function () {
10 |
11 | this.timeout(10 * 1000);
12 |
13 | const testRepo = {
14 | owner: 'octodemo',
15 | repo: 'demo-vulnerabilities-ghas'
16 | };
17 |
18 | let ghDeps: GitHubDependencies;
19 |
20 | before(() => {
21 | const octokit = new Octokit({auth: getGitHubToken()});
22 | ghDeps = new GitHubDependencies(octokit);
23 | });
24 |
25 | describe('#getAllDependencies()', () => {
26 |
27 | it(`from ${JSON.stringify(testRepo)}`, async () => {
28 | const results: DependencySet[] = await ghDeps.getAllDependencies(testRepo);
29 |
30 | expect(results).to.have.length.greaterThan(0);
31 | expect(results[0]).to.have.property('count').to.be.greaterThan(0);
32 |
33 | const dep: Dependency = results[0].dependencies[0];
34 | expect(dep.packageType).to.equal('MAVEN');
35 | });
36 | });
37 |
38 | describe('#getAllVulnerabilities()', () => {
39 |
40 | it(`from ${JSON.stringify(testRepo)}`, async () => {
41 | const results = await ghDeps.getAllVulnerabilities(testRepo);
42 |
43 | expect(results).to.have.length.greaterThan(10);
44 | });
45 | });
46 | });
--------------------------------------------------------------------------------
/src/dependencies/GitHubDependencies.ts:
--------------------------------------------------------------------------------
1 | import { Octokit } from '@octokit/rest';
2 | import { RequestHeaders, RequestParameters } from '@octokit/types';
3 |
4 | import {
5 | QUERY_SECURITY_VULNERABILITIES,
6 | QUERY_DEPENDENCY_GRAPH,
7 | VulnerabilityAlert,
8 | DependencySetData, RepositoryVulnerabilityAlerts, DependencyGraphResult
9 | } from './DependencyTypes';
10 |
11 | import Vulnerability from './Vulnerability';
12 | import DependencySet from './DependencySet';
13 |
14 | type Repo = {
15 | owner: string,
16 | repo: string,
17 | }
18 |
19 |
20 | export default class GitHubDependencies {
21 |
22 | private readonly octokit: Octokit;
23 |
24 | constructor(octokit) {
25 | this.octokit = octokit;
26 | }
27 |
28 | async getAllVulnerabilities(repo: Repo): Promise {
29 | function extractVulnerabilityAlerts(data: RepositoryVulnerabilityAlerts): VulnerabilityAlert[] {
30 | return data.repository.vulnerabilityAlerts.nodes;
31 | }
32 |
33 | const data: VulnerabilityAlert[] = await this.getPaginatedQuery(
34 | QUERY_SECURITY_VULNERABILITIES,
35 | {organizationName: repo.owner, repositoryName: repo.repo},
36 | 'repository.vulnerabilityAlerts.pageInfo',
37 | extractVulnerabilityAlerts
38 | );
39 |
40 | return data.map(val => {
41 | return new Vulnerability(val);
42 | });
43 | }
44 |
45 | async getAllDependencies(repo: Repo): Promise {
46 | function extractDependencySetData(data: DependencyGraphResult): DependencySetData[] {
47 | return data.repository.dependencyGraphManifests.edges;
48 | }
49 |
50 | const data = await this.getPaginatedQuery(
51 | QUERY_DEPENDENCY_GRAPH,
52 | {organizationName: repo.owner, repositoryName: repo.repo},
53 | 'repository.dependencyGraphManifests.pageInfo',
54 | extractDependencySetData,
55 | {accept: 'application/vnd.github.hawkgirl-preview+json'}
56 | );
57 |
58 | return data.map(node => {
59 | return new DependencySet(node);
60 | });
61 | }
62 |
63 | async getPaginatedQuery(query: string,
64 | parameters: Object,
65 | pageInfoPath: string,
66 | extractResultsFn: (val: T) => Y[],
67 | headers?): Promise {
68 | const octokit = this.octokit
69 | , results: Y[] = []
70 | , queryParameters = Object.assign({cursor: null}, parameters)
71 | ;
72 |
73 | let hasNextPage = false;
74 | do {
75 | const graphqlParameters = buildGraphQLParameters(query, parameters, headers)
76 | , queryResult = await octokit.graphql(graphqlParameters)
77 | ;
78 |
79 | // @ts-ignore
80 | const extracted: Y = extractResultsFn(queryResult);
81 | // @ts-ignore
82 | results.push(...extracted);
83 |
84 | const pageInfo = getObject(queryResult, ...pageInfoPath.split('.'));
85 | hasNextPage = pageInfo ? pageInfo.hasNextPage : false;
86 | if (hasNextPage) {
87 | queryParameters.cursor = pageInfo.endCursor;
88 | }
89 | } while (hasNextPage);
90 |
91 | return results;
92 | }
93 | }
94 |
95 | function buildGraphQLParameters(query: string, parameters?: Object, headers?: RequestHeaders): RequestParameters {
96 | const result: RequestParameters = {
97 | ...(parameters || {}),
98 | query: query,
99 | };
100 |
101 | if (headers) {
102 | result.headers = headers;
103 | }
104 |
105 | return result;
106 | }
107 |
108 | function getObject(target, ...path) {
109 | if (target !== null && target !== undefined) {
110 | const value = target[path[0]];
111 |
112 | if (path.length > 1) {
113 | return getObject(value, ...path.slice(1));
114 | } else {
115 | return value;
116 | }
117 | }
118 | return null;
119 | }
--------------------------------------------------------------------------------
/src/dependencies/Vulnerability.ts:
--------------------------------------------------------------------------------
1 | import { SecurityAdvisory, SecurityVulnerability, VulnerabilityAlert } from './DependencyTypes';
2 |
3 | export type DismissedBy = {
4 | user: {
5 | login: string,
6 | name: string,
7 | },
8 | reason: string,
9 | at: string
10 | };
11 |
12 | export type VulnerableSource = {
13 | manifest: string,
14 | version: string,
15 | path: string,
16 | }
17 |
18 | export default class Vulnerability {
19 |
20 | private readonly data: VulnerabilityAlert;
21 |
22 | constructor(data: VulnerabilityAlert) {
23 | this.data = data;
24 | }
25 |
26 | get created(): string {
27 | return this.data.createdAt;
28 | }
29 |
30 | get isDismissed(): boolean {
31 | return !!this.data.dismisser;
32 | }
33 |
34 | get dismissedBy(): DismissedBy {
35 | return {
36 | user: {
37 | login: this.data.dismisser.login,
38 | name: this.data.dismisser.name
39 | },
40 | reason: this.data.dismissReason,
41 | at: this.data.dismissedAt,
42 | };
43 | }
44 |
45 | get severity(): string {
46 | return this.data.securityVulnerability.severity;
47 | }
48 |
49 | get vulnerability(): SecurityVulnerability {
50 | return Object.assign({}, this.data.securityVulnerability);
51 | }
52 |
53 | get advisory(): SecurityAdvisory {
54 | return Object.assign({}, this.data.securityAdvisory);
55 | }
56 |
57 | get source(): VulnerableSource {
58 | return {
59 | manifest: this.data.vulnerableManifestFilename,
60 | version: this.data.vulnerableRequirements,
61 | path: this.data.vulnerableManifestPath
62 | };
63 | }
64 |
65 | get publishedAt(): string {
66 | return this.advisory.publishedAt;
67 | }
68 |
69 | get link(): string {
70 | return this.advisory.permalink;
71 | }
72 | }
--------------------------------------------------------------------------------
/src/executable.ts:
--------------------------------------------------------------------------------
1 | import ReportGenerator, { ReportGeneratorConfig } from './ReportGenerator';
2 | import { Octokit } from '@octokit/rest';
3 |
4 | import path from 'path';
5 |
6 | const {program} = require('commander');
7 | program.name('github-security-report');
8 | program.version(require('../package.json').version);
9 |
10 | program.requiredOption('-t, --token ', 'github access token');
11 | program.requiredOption('-r --repository ', 'github repository, owner/repo_name format');
12 | program.option('-s --sarif-directory ', 'the SARIF report directory to load reports from', '../results');
13 | program.option('-o --output-directory ', 'output directory for summary report', '.');
14 | program.option('--github-api-url ', 'GitHub API URL', 'https://api.github.com')
15 |
16 | program.parse(process.argv);
17 | const opts = program.opts();
18 |
19 | const reportGenerateConfig: ReportGeneratorConfig = {
20 | repository: opts.repository,
21 | octokit: new Octokit({auth: opts.token, baseUrl: opts.url}),
22 | sarifReportDirectory: getPath(opts.sarifDirectory),
23 | outputDirectory: getPath(opts.outputDirectory),
24 | templating: {
25 | name: 'summary'
26 | }
27 | }
28 |
29 | async function execute(reportGenerateConfig: ReportGeneratorConfig) {
30 | try {
31 | const generator = new ReportGenerator(reportGenerateConfig);
32 | console.log(`Generating Security report for ${reportGenerateConfig.repository}...`);
33 | const file = await generator.run();
34 | console.log(`Summary Report generated: ${file}`);
35 |
36 | } catch (err) {
37 | console.log(err.stack);
38 | console.error(err.message);
39 | console.error();
40 | program.help({error: true});
41 | }
42 | }
43 |
44 | execute(reportGenerateConfig);
45 |
46 |
47 | function getPath(value) {
48 | if (path.isAbsolute(value)) {
49 | return value;
50 | } else {
51 | return path.normalize(path.join(process.cwd(), value));
52 | }
53 | }
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import ReportGenerator from './ReportGenerator';
2 |
3 | import * as core from '@actions/core';
4 | import { Octokit } from '@octokit/rest';
5 |
6 | async function run(): Promise {
7 | try {
8 | const token = getRequiredInputValue('token');
9 |
10 | const generator = new ReportGenerator({
11 | repository: getRequiredInputValue('repository'),
12 | octokit: new Octokit({auth: token}),
13 |
14 | sarifReportDirectory: getRequiredInputValue('sarifReportDir'),
15 | outputDirectory: getRequiredInputValue('outputDir'),
16 |
17 | templating: {
18 | name: 'summary'
19 | }
20 | });
21 |
22 | const file = await generator.run();
23 | console.log(file);
24 | } catch (err) {
25 | core.setFailed(err.message);
26 | }
27 | }
28 |
29 | run();
30 |
31 | function getRequiredInputValue(key: string): string {
32 | return core.getInput(key, {required: true});
33 | }
34 |
--------------------------------------------------------------------------------
/src/pdf/pdfWriter.test.ts:
--------------------------------------------------------------------------------
1 | import { expect } from 'chai';
2 | import { mkdirP } from '@actions/io';
3 | import { createPDF } from './pdfWriter';
4 | import { getTestDirectoryFilePath } from '../testUtils';
5 |
6 | describe('pdfWriter', function () {
7 |
8 | this.timeout(30 * 1000);
9 |
10 | it('should generate a simple pdf', async () => {
11 | const html = 'Hello World
'
12 | , file = getTestDirectoryFilePath('test.pdf')
13 | ;
14 |
15 | // Ensure the directory exists
16 | await mkdirP(getTestDirectoryFilePath());
17 |
18 | const generatePdf = await createPDF(html, file)
19 | expect(generatePdf).to.equal(file);
20 | //TODO check size
21 | });
22 | });
--------------------------------------------------------------------------------
/src/pdf/pdfWriter.ts:
--------------------------------------------------------------------------------
1 | import * as os from 'os';
2 |
3 | const puppeteer = require('puppeteer-core');
4 |
5 | export function createPDF(html: string, file: string): Promise {
6 |
7 | const fetcher = puppeteer.createBrowserFetcher({path: os.tmpdir()});
8 |
9 | return fetcher.download('782078')//TODO need to store and inject this
10 | .then(revisionInfo => {
11 | return puppeteer.launch({executablePath: revisionInfo.executablePath})
12 | .then(browser => {
13 | return browser.newPage()
14 | .then(page => {
15 | return page.setContent(html)
16 | .then(() => {
17 | return page.pdf({path: file, format: 'A4'})
18 | });
19 | })
20 | .then(() => {
21 | return browser.close();
22 | });
23 | })
24 | .then(() => {
25 | return file;
26 | });
27 | });
28 | };
--------------------------------------------------------------------------------
/src/sarif/CodeScanningResult.ts:
--------------------------------------------------------------------------------
1 | interface Location {
2 | artifactLocation: {
3 | uri: string
4 | }
5 | region: string
6 | }
7 |
8 | export class PhysicalLocation {
9 |
10 | private _location: Location
11 |
12 | constructor(location: Location) {
13 | this._location = location;
14 | }
15 |
16 | get artifactLocation(): string {
17 | return this._location.artifactLocation.uri;
18 | }
19 |
20 | get region(): string {
21 | return this._location.region;
22 | }
23 | }
24 |
25 | interface Sarif {
26 | ruleId: string,
27 | ruleIndex: number,
28 | message: {
29 | text: string,
30 | },
31 | locations: Location[],
32 | }
33 |
34 | export default class CodeScanningResult {
35 |
36 | readonly locations: PhysicalLocation[] | null;
37 |
38 | private _sarif: Sarif
39 |
40 | constructor(sarifResult) {
41 | this._sarif = sarifResult;
42 | this.locations = extractLocations(sarifResult);
43 | }
44 |
45 | get ruleId() : string {
46 | return this._sarif.ruleId;
47 | }
48 |
49 | get ruleIndex(): number {
50 | return this._sarif.ruleIndex;
51 | }
52 |
53 | get message(): string {
54 | return this._sarif.message.text;
55 | }
56 | }
57 |
58 | function extractLocations(sarif: Sarif): PhysicalLocation[] | null {
59 | if (sarif && sarif.locations) {
60 | const results: PhysicalLocation[] = [];
61 |
62 | sarif.locations.forEach(location => {
63 | results.push(new PhysicalLocation(location));
64 | });
65 |
66 | return results;
67 | }
68 | return null;
69 | }
70 |
71 |
--------------------------------------------------------------------------------
/src/sarif/CodeScanningRule.ts:
--------------------------------------------------------------------------------
1 | import {SarifRule} from './SarifDataTypes';
2 |
3 | const CWE_REGEX = /external\/cwe\/(cwe-.*)/;
4 |
5 | export default class CodeScanningRule {
6 |
7 | private readonly sarifRule: SarifRule;
8 |
9 | readonly cwes: string[];
10 |
11 | constructor(sarifRule: SarifRule) {
12 | this.sarifRule = sarifRule;
13 | this.cwes = getCWEs(sarifRule.properties.tags);
14 | }
15 |
16 | get id(): string {
17 | return this.sarifRule.id;
18 | }
19 |
20 | get name() : string{
21 | return this.sarifRule.name;
22 | }
23 |
24 | get shortDescription(): string {
25 | return this.sarifRule.shortDescription.text;
26 | }
27 |
28 | get description(): string {
29 | return this.sarifRule.fullDescription.text;
30 | }
31 |
32 | get tags(): Array {
33 | return this.sarifRule.properties.tags;
34 | }
35 |
36 | get severity(): string {
37 | return this.sarifRule.properties['problem.severity'];
38 | }
39 |
40 | get precision() : string{
41 | return this.sarifRule.properties.precision;
42 | }
43 |
44 | get kind() : string{
45 | return this.sarifRule.properties.kind;
46 | }
47 |
48 | get defaultConfigurationLevel(): string {
49 | return this.sarifRule.defaultConfiguration.level;
50 | }
51 | }
52 |
53 | function getCWEs(tags: string[]): string[] {
54 | const cwes: string[] = [];
55 |
56 | if (tags) {
57 | tags.forEach(tag => {
58 | const match = CWE_REGEX.exec(tag);
59 |
60 | if (match) {
61 | // @ts-ignore
62 | cwes.push(match[1]);
63 | }
64 | });
65 | }
66 |
67 | return cwes.sort();
68 | }
--------------------------------------------------------------------------------
/src/sarif/SarifDataTypes.ts:
--------------------------------------------------------------------------------
1 | export type SarifReportData = {
2 | version: string,
3 | runs: SarifRun[],
4 | }
5 |
6 | export type SarifRun = {
7 | tool: {
8 | driver: {
9 | name: string,
10 | rules: SarifRule[]
11 | }
12 | }
13 | }
14 |
15 | export type SarifRule = {
16 | id: string,
17 | name: string,
18 | shortDescription: {
19 | text: string,
20 | },
21 | fullDescription: {
22 | text: string,
23 | },
24 | properties: {
25 | tags: string[],
26 | precision: string,
27 | kind: string,
28 |
29 | },
30 | defaultConfiguration: {
31 | level: string,
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/sarif/SarifReport.ts:
--------------------------------------------------------------------------------
1 | import CodeScanningRule from "./CodeScanningRule";
2 | import {SarifReportData, SarifRule} from './SarifDataTypes';
3 |
4 | export default class SarifReport {
5 |
6 | private readonly data: SarifReportData;
7 |
8 | readonly rules: CodeScanningRule[];
9 |
10 | constructor(data: SarifReportData) {
11 | this.data = data;
12 | this.rules = getRules(data) || [];
13 | }
14 |
15 | get cweList(): string[] {
16 | const result = this.rules.reduce((cwes: string[], rule) => {
17 | return cwes.concat(rule.cwes)
18 | }, []);
19 | return unique(result).sort();
20 | }
21 | }
22 |
23 |
24 | function getRules(report: SarifReportData) {
25 | let sarifRules: SarifRule[] | null = null;
26 |
27 | if (report.version === '2.1.0') {
28 | if (report.runs) {
29 | report.runs.forEach(run => {
30 | if (run.tool.driver.name === 'CodeQL') { //TODO could support other tools
31 | sarifRules = run.tool.driver.rules;
32 | }
33 | });
34 | }
35 | } else {
36 | throw new Error(`Unsupported version: ${report.version}`)
37 | }
38 |
39 | return getAppliedRuleDetails(sarifRules);
40 | }
41 |
42 |
43 | function getAppliedRuleDetails(sarifRules: SarifRule[] | null): CodeScanningRule[] | null {
44 | if (sarifRules) {
45 | return sarifRules.map(rule => {
46 | return new CodeScanningRule(rule)
47 | });
48 | }
49 |
50 | return null;
51 | }
52 |
53 |
54 | function unique(array: string[]): string[] {
55 | return array.filter((val, idx, self) => {
56 | return self.indexOf(val) === idx
57 | });
58 | }
--------------------------------------------------------------------------------
/src/sarif/SarifReportFinder.ts:
--------------------------------------------------------------------------------
1 | import * as path from 'path';
2 | import * as fs from 'fs';
3 | import SarifReport from './SarifReport';
4 |
5 | export type SarifFile = {
6 | file: string,
7 | payload: SarifReport
8 | }
9 |
10 | export default class SarifReportFinder {
11 |
12 | private readonly dir: string;
13 |
14 | constructor(dir: string) {
15 | this.dir = dir;
16 | }
17 |
18 | getSarifFiles(): Promise {
19 | const dir = this.dir
20 | , promises: Promise[] = []
21 | ;
22 |
23 | if (!fs.existsSync(dir)) {
24 | throw new Error(`SARIF Finder, path "${dir}", does not exist.`);
25 | }
26 |
27 | console.log(`SARIF File Finder, processing: ${dir}`);
28 | if (fs.lstatSync(dir).isDirectory()) {
29 | console.log(` is a directory, looking for files`);
30 |
31 | const files = fs.readdirSync(dir) // TODO use promises here
32 | .filter(f => f.endsWith('.sarif'))
33 | .map(f => path.resolve(dir, f));
34 |
35 | console.log(` SARIF files detected: ${JSON.stringify(files)}`);
36 | if (files) {
37 | files.forEach(f => {
38 | promises.push(loadFileContents(f));
39 | });
40 | }
41 | }
42 |
43 | if (promises.length > 0) {
44 | return Promise.all(promises);
45 | } else {
46 | return Promise.resolve([]);
47 | }
48 | }
49 | }
50 |
51 | function loadFileContents(file: string): Promise {
52 | return fs.promises.open(file, 'r')
53 | .then(fileHandle => {
54 | return fileHandle.readFile()
55 | .then(content => {
56 | fileHandle.close();
57 | try {
58 | return JSON.parse(content.toString('utf8'));
59 | } catch (err) {
60 | throw new Error(`Failed to parse JSON from SARIF file '${file}': ${err}`);
61 | }
62 | })
63 | .then(data => {
64 | return {
65 | file: file,
66 | payload: new SarifReport(data),
67 | };
68 | })
69 | });
70 | }
--------------------------------------------------------------------------------
/src/templating/ReportData.ts:
--------------------------------------------------------------------------------
1 | import Vulnerability from '../dependencies/Vulnerability';
2 | import DependencySet from '../dependencies/DependencySet';
3 | import { SarifFile } from '../sarif/SarifReportFinder';
4 | import CodeScanningResults from '../codeScanning/CodeScanningResults';
5 | import CodeScanningRule from '../sarif/CodeScanningRule';
6 | import {
7 | AlertSummary,
8 | CodeScanningRules, CodeScanResults, CodeScanSummary,
9 | CollectedData,
10 | CWECoverage, Dependencies,
11 | DependencySummary,
12 | JsonPayload, Manifest,
13 | Repo,
14 | RuleData, ServerityToVulnerabilities, SeverityToAlertSummary
15 | } from './ReportTypes';
16 |
17 | export default class ReportData {
18 |
19 | private readonly data: CollectedData;
20 |
21 | constructor(data: CollectedData) {
22 | this.data = data || {};
23 | }
24 |
25 | get githubRepo(): Repo {
26 | return this.data.github || {};
27 | }
28 |
29 | get vulnerabilities(): Vulnerability[] {
30 | return this.data.vulnerabilities || [];
31 | }
32 |
33 | get dependencies(): DependencySet[] {
34 | return this.data.dependencies || [];
35 | }
36 |
37 | get openDependencyVulnerabilities(): Vulnerability[] {
38 | return this.vulnerabilities.filter(vuln => {
39 | return !vuln.isDismissed;
40 | });
41 | }
42 |
43 | get closedDependencyVulnerabilities(): Vulnerability[] {
44 | return this.vulnerabilities.filter(vuln => {
45 | return vuln.isDismissed;
46 | });
47 | }
48 |
49 | get openCodeScanResults(): CodeScanningResults {
50 | return this.data.codeScanningOpen || {};
51 | }
52 |
53 | get closedCodeScanResults(): CodeScanningResults {
54 | return this.data.codeScanningClosed || {};
55 | }
56 |
57 | get sarifReports(): SarifFile[] {
58 | return this.data.sarifReports || [];
59 | }
60 |
61 | get codeScanningRules(): CodeScanningRules {
62 | const result = {};
63 |
64 | this.sarifReports.forEach(report => {
65 | // Each report is an object of {file, payload} keys
66 | const rules = report.payload.rules;
67 |
68 | if (rules) {
69 | rules.forEach(rule => {
70 | result[rule.id] = rule;
71 | });
72 | }
73 | });
74 |
75 | return result;
76 | }
77 |
78 | getJSONPayload(): JsonPayload {
79 | const data = {
80 | github: this.githubRepo,
81 | metadata: {
82 | created: new Date().toISOString(),
83 | },
84 | sca: {
85 | dependencies: this.getDependencySummary(),
86 | vulnerabilities: {
87 | total: this.openDependencyVulnerabilities.length,
88 | bySeverity: this.getVulnerabilitiesBySeverity()
89 | },
90 | },
91 | scanning: {
92 | rules: this.getAppliedCodeScanningRules(),
93 | cwe: this.getCWECoverage() || {},
94 | results: this.getCodeScanSummary(),
95 | }
96 | };
97 | return data;
98 | }
99 |
100 | getCWECoverage(): CWECoverage | null {
101 | const rules = this.getAppliedCodeScanningRules();
102 |
103 | if (rules) {
104 | const result: {[key: string]: RuleData[]} = {};
105 |
106 | rules.forEach(rule => {
107 | const cwes = rule.cwe;
108 |
109 | if (cwes) {
110 | cwes.forEach(cwe => {
111 | if (!result[cwe]) {
112 | result[cwe] = [];
113 | }
114 |
115 | result[cwe].push(rule);
116 | });
117 | }
118 | });
119 |
120 | return {
121 | cweToRules: result,
122 | cwes: Object.keys(result)
123 | };
124 | }
125 |
126 | return null;
127 | }
128 |
129 |
130 | getDependencySummary(): DependencySummary {
131 | const unprocessed: Manifest[] = []
132 | , processed: Manifest[] = []
133 | , dependencies: Dependencies = {}
134 | ;
135 |
136 | let totalDeps = 0;
137 |
138 | this.dependencies.forEach(depSet => {
139 | totalDeps += depSet.count;
140 |
141 | const manifest = {
142 | filename: depSet.filename,
143 | path: depSet.path
144 | };
145 |
146 | if (depSet.isValid) {
147 | processed.push(manifest);
148 | } else {
149 | unprocessed.push(manifest);
150 | }
151 |
152 | const identifiedDeps = depSet.dependencies;
153 | if (identifiedDeps) {
154 | identifiedDeps.forEach(dep => {
155 | const type = dep.packageType.toLowerCase();
156 |
157 | if (!dependencies[type]) {
158 | dependencies[type] = [];
159 | }
160 |
161 | dependencies[type].push({
162 | name: dep.name,
163 | type: dep.packageType,
164 | version: dep.version,
165 | });
166 | });
167 | }
168 | });
169 |
170 | return {
171 | manifests: {
172 | processed: processed,
173 | unprocessed: unprocessed,
174 | },
175 | totalDependencies: totalDeps,
176 | dependencies: dependencies
177 | };
178 | }
179 |
180 | getVulnerabilitiesBySeverity(): ServerityToVulnerabilities {
181 | const result = {};
182 |
183 | // Obtain third party artifacts ranked by severity
184 | const vulnerabilities = this.openDependencyVulnerabilities;
185 | vulnerabilities.forEach(vulnerability => {
186 | const severity = vulnerability.severity.toLowerCase();
187 |
188 | if (!result[severity]) {
189 | result[severity] = [];
190 | }
191 | result[severity].push(vulnerability);
192 | });
193 |
194 | return result;
195 | }
196 |
197 | getAppliedCodeScanningRules(): RuleData[] {
198 | const rules = this.codeScanningRules;
199 |
200 | if (rules) {
201 | return Object.values(rules).map(rule => {
202 | return getRuleData(rule);
203 | });
204 | }
205 |
206 | return [];
207 | }
208 |
209 | getCodeScanSummary(): CodeScanSummary {
210 | const open = this.openCodeScanResults
211 | , closed = this.closedCodeScanResults
212 | , rules = this.codeScanningRules
213 | ;
214 |
215 | const data = {
216 | open: generateAlertSummary(open, rules),
217 | closed: generateAlertSummary(closed, rules),
218 | };
219 |
220 | return data;
221 | }
222 | }
223 |
224 | function generateAlertSummary(open: CodeScanningResults, rules: CodeScanningRules): CodeScanResults {
225 | const result: SeverityToAlertSummary = {};
226 | let total = 0;
227 |
228 | open.getCodeQLScanningAlerts().forEach(codeScanAlert => {
229 | const severity = codeScanAlert.severity
230 | , matchedRule = rules ? rules[codeScanAlert.ruleId] : null
231 | ;
232 |
233 | const summary: AlertSummary = {
234 | tool: codeScanAlert.toolName,
235 | name: codeScanAlert.ruleDescription,
236 | state: codeScanAlert.state,
237 | created: codeScanAlert.created,
238 | url: codeScanAlert.url,
239 | rule: {
240 | id: codeScanAlert.ruleId,
241 | }
242 | };
243 |
244 | if (matchedRule) {
245 | summary.rule.details = matchedRule;
246 | }
247 |
248 | if (!result[severity]) {
249 | result[severity] = [];
250 | }
251 | result[severity].push(summary);
252 | total++;
253 | });
254 |
255 | return {
256 | total: total,
257 | scans: result
258 | };
259 | }
260 |
261 | function getRuleData(rule: CodeScanningRule): RuleData {
262 | return {
263 | name: rule.name,
264 | //TODO maybe id?
265 | severity: rule.severity,
266 | precision: rule.precision,
267 | kind: rule.kind,
268 | shortDescription: rule.shortDescription,
269 | description: rule.description,
270 | tags: rule.tags,
271 | cwe: rule.cwes,
272 | };
273 | }
274 |
275 | //TODO this was not used
276 | // function getVulnerability(vuln) {
277 | // if (!vuln) {
278 | // return null;
279 | // }
280 | //
281 | // const data = {
282 | // created: vuln.created,
283 | // published: vuln.publishedAt,
284 | // severity: vuln.severity,
285 | // vulnerability: vuln.vulnerability,
286 | // advisory: vuln.advisory,
287 | // source: vuln.source,
288 | // link: vuln.link,
289 | // };
290 | //
291 | // if (vuln.isDismissed()) {
292 | // data.dismissed = vuln.dismissedBy;
293 | // }
294 | //
295 | // return data;
296 | // }
--------------------------------------------------------------------------------
/src/templating/ReportTypes.ts:
--------------------------------------------------------------------------------
1 | import CodeScanningRule from '../sarif/CodeScanningRule';
2 | import Vulnerability from '../dependencies/Vulnerability';
3 | import DependencySet from '../dependencies/DependencySet';
4 | import { SarifFile } from '../sarif/SarifReportFinder';
5 | import CodeScanningResults from '../codeScanning/CodeScanningResults';
6 |
7 | export type RuleData = {
8 | name: string,
9 | severity: string,
10 | precision: string,
11 | kind: string,
12 | shortDescription: string,
13 | description: string,
14 | tags: string[],
15 | cwe: string[]
16 | }
17 |
18 | export type Repo = {
19 | owner: string,
20 | repo: string
21 | }
22 |
23 | export type CodeScanningRules = {
24 | [key: string]: CodeScanningRule
25 | }
26 |
27 | export type CollectedData = {
28 | github: Repo
29 | vulnerabilities: Vulnerability[],
30 | dependencies: DependencySet[],
31 | sarifReports: SarifFile[],
32 | codeScanningOpen: CodeScanningResults,
33 | codeScanningClosed: CodeScanningResults,
34 | }
35 |
36 | export type JsonPayload = {
37 | github: Repo,
38 | metadata: {
39 | created: string,
40 | }
41 | sca: {
42 | dependencies: DependencySummary
43 | vulnerabilities: {
44 | total: number
45 | bySeverity: ServerityToVulnerabilities
46 | }
47 | },
48 | scanning: {
49 | rules: RuleData[],
50 | cwe: CWECoverage | {},
51 | results: CodeScanSummary
52 | }
53 | }
54 |
55 | export type DependencySummary = {
56 | manifests: {
57 | processed: Manifest[],
58 | unprocessed: Manifest[],
59 | },
60 | totalDependencies: number,
61 | dependencies: Dependencies
62 | }
63 |
64 | export type Manifest = {
65 | filename: string,
66 | path: string,
67 | }
68 |
69 | export type Dependencies = {
70 | [key: string]: Dependency[]
71 | }
72 |
73 | export type Dependency = {
74 | name: string,
75 | type: string,
76 | version: string
77 | }
78 |
79 | export type ServerityToVulnerabilities = {
80 | [key: string]: Vulnerability[]
81 | }
82 |
83 | export type AlertSummary = {
84 | tool: string | null,
85 | name: string,
86 | state: string,
87 | created: string,
88 | url: string,
89 | rule: {
90 | id: string
91 | details?: CodeScanningRule
92 | }
93 | }
94 |
95 | export type SeverityToAlertSummary = {
96 | [key: string]: AlertSummary[]
97 | }
98 |
99 | export type CodeScanResults = {
100 | total: number,
101 | scans: SeverityToAlertSummary
102 | }
103 |
104 | export type CWECoverage = {
105 | cweToRules: {[key: string]: RuleData[]},
106 | cwes: string[]
107 | }
108 |
109 | export type CodeScanSummary = {
110 | open: CodeScanResults,
111 | closed: CodeScanResults
112 | }
--------------------------------------------------------------------------------
/src/templating/Template.test.ts:
--------------------------------------------------------------------------------
1 | import * as fs from 'fs';
2 | import { expect } from 'chai';
3 | import Template from './Template';
4 | import { getSampleReportJsonDirectory, getTestDirectoryFilePath } from '../testUtils';
5 |
6 |
7 | const OCTODEMO_GHAS_REPORTING = {
8 | directory: 'octodemo/ghas-reporting',
9 | json: 'payload.json',
10 | expectedSummary: 'summary.html'
11 | };
12 |
13 | describe('Template', () => {
14 |
15 | [OCTODEMO_GHAS_REPORTING].forEach(config => {
16 |
17 | it(`should render ${config.directory}`, () => {
18 | const reporting = new Template()
19 | , data = readSampleFileAsJson(config.directory, 'payload.json')
20 | , fileContent = reporting.render(data, 'summary')
21 | ;
22 |
23 | fs.writeFileSync(getTestDirectoryFilePath(config.directory, 'summary.html'), fileContent);
24 |
25 | const expectedContent = getExpectedContents(config);
26 | expect(fileContent).to.equal(expectedContent);
27 | });
28 | });
29 |
30 | });
31 |
32 |
33 | function getExpectedContents(config) {
34 | const content = fs.readFileSync(getSampleReportJsonDirectory(config.directory, config.expectedSummary));
35 | return content.toString('utf-8');
36 | }
37 |
38 |
39 | function readSampleFileAsJson(subDir, file) {
40 | const content = fs.readFileSync(getSampleReportJsonDirectory(...[subDir, file]));
41 | return JSON.parse(content.toString('utf-8'));
42 | }
43 |
--------------------------------------------------------------------------------
/src/templating/Template.ts:
--------------------------------------------------------------------------------
1 | import * as fs from 'fs';
2 |
3 | const path = require('path')
4 | , nunjucks = require('nunjucks')
5 | ;
6 |
7 | // Default templates as part of the action
8 | const EMBEDDED_TEMPLATES = path.join(__dirname, '..', '..', 'templates');
9 |
10 | export default class Template {
11 |
12 | private readonly renderer;
13 |
14 | private readonly templatesDir: string;
15 |
16 | constructor(templatesDir?: string) {
17 | if (!templatesDir) {
18 | this.templatesDir = EMBEDDED_TEMPLATES;
19 | } else {
20 | this.templatesDir = templatesDir;
21 | }
22 |
23 | this.renderer = nunjucks.configure(this.templatesDir, {autoescape: true})
24 | }
25 |
26 | render(data, template): string {
27 | const resolvedTemplateFilename = this.getValidatedTemplateFileName(template);
28 | const content = this.renderer.render(resolvedTemplateFilename, data);
29 | //TODO consider providing intermediate output
30 | return content;
31 | }
32 |
33 | getValidatedTemplateFileName(name): string {
34 | if (fs.existsSync(path.join(this.templatesDir, name))) {
35 | return name;
36 | } else {
37 | // Try our known supported extensions
38 | const found = ['html', 'j2'].filter(extension => {
39 | return fs.existsSync(path.join(this.templatesDir, `${name}.${extension}`));
40 | });
41 |
42 | if (found.length > 0) {
43 | return `${name}.${found[0]}`;
44 | }
45 | }
46 |
47 | throw new Error(`Failed to resolve a template file from directory ${this.templatesDir} with name "${name}"`);
48 | }
49 | }
--------------------------------------------------------------------------------
/src/testUtils.ts:
--------------------------------------------------------------------------------
1 | import * as path from 'path';
2 |
3 | export function getTestDirectoryFilePath(...filePath): string {
4 | const args = [__dirname, '..', '_tmp', ...filePath];
5 | return path.join(...args);
6 | }
7 |
8 | export function getSampleDataDirectory(...dir): string {
9 | const args = [__dirname, '..', 'samples', ...dir];
10 | return path.join(...args);
11 | }
12 |
13 | export function getSampleSarifDirectory(...dir): string {
14 | const args = [__dirname, '..', 'samples', 'sarif', ...dir];
15 | return path.join(...args);
16 | }
17 |
18 | export function getSampleReportJsonDirectory(...dir): string {
19 | const args = [__dirname, '..', 'samples', 'reportJson', ...dir];
20 | return path.join(...args);
21 | }
22 |
23 | export function getGitHubToken(): string {
24 | const token = process.env['GH_TOKEN'];
25 |
26 | if (!token) {
27 | throw new Error('GitHub Token was not set for environment variable "GH_TOKEN"');
28 | }
29 | return token;
30 | }
--------------------------------------------------------------------------------
/summary_report_example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peter-murray/github-security-report-action/42aa5945bd5b688f14868891df0ecae0024b3c60/summary_report_example.png
--------------------------------------------------------------------------------
/templates/executive_summary.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | GitHub Advanced Security - Executive Summary
6 |
7 |
126 |
127 |
128 |
129 |
130 |
143 |
144 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 | Open: 0
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
--------------------------------------------------------------------------------
/templates/summary.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | GitHub Advanced Security Report
6 |
7 |
35 |
36 |
37 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |

50 |
GitHub Advanced Security
Summary Report
51 |
52 |
53 |
54 |
55 |
GitHub Repository:
56 | {{ github.owner }}/{{ github.repo }}
57 |
58 | Generated:
59 | {{ metadata.created }}
60 |
61 |
62 |
63 | {% if sca %}
64 |
65 |
70 |
71 |
72 |
73 |
74 |
78 |
79 |
80 |
83 |
87 |
88 |
89 |
90 |
91 | {% if sca.vulnerabilities %}
92 |
93 |
94 |
98 |
99 |
100 |
103 |
106 |
109 |
112 |
113 |
114 |
115 | {% endif %}
116 |
117 |
118 | {% endif %}
119 |
120 | {% if scanning %}
121 |
122 |
127 |
128 |
140 |
141 |
142 |
143 |
144 |
148 |
149 |
150 |
151 | {% for cwe in scanning.cwe.cwes | sort %}
152 | - {{ cwe }}
153 | {% endfor %}
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 | {% if scanning.results.open %}
164 |
165 |
169 |
170 | {% if scanning.results.open.total > 0 %}
171 |
174 |
177 | {% else %}
178 |
179 | {% endif %}
180 |
181 |
182 | {% endif %}
183 |
184 |
185 |
186 | {% if scanning.results.closed %}
187 |
188 |
192 |
193 | {% if scanning.results.closed.total > 0 %}
194 |
197 |
200 | {% else %}
201 |
202 | {% endif %}
203 |
204 |
205 | {% endif %}
206 |
207 |
208 |
209 | {% endif %}
210 |
211 |
212 |
216 |
217 |
218 |
--------------------------------------------------------------------------------
/templates/summary_old.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | GitHub Advanced Security Summary
6 |
7 |
8 |
9 | GitHub Advanced Security Summary
10 |
11 | Software Composition Analysis (SCA)
12 |
13 | Number of Dependencies: {{dependencies.deps.totalDependencies}}
14 |
15 | SCA Dependency Vulnerability Summary:
16 |
17 |
18 | - Critical: {{dependencies.vulnerabilities.CRITICAL.length}}
19 | - High: {{dependencies.vulnerabilities.HIGH.length}}
20 | - Moderate: {{dependencies.vulnerabilities.MODERATE.length}}
21 | - Low: {{dependencies.vulnerabilities.LOW.length}}
22 |
23 |
24 |
25 | Code Scaning
26 |
27 | Total CWE Vulnerabilities Checked: {{codeScanning.cwes.length}}
28 |
29 |
30 | {{#each codeScanning.cwes}}
31 | {{this}}
32 | {{/each}}
33 |
34 |
35 |
36 |
37 | Open Violations
38 |
39 | - Errors: {{codeScanning.open.error.length}}
40 | - Warnings: {{codeScanning.open.warning.length}}
41 |
42 |
43 | Errors
44 |
45 |
46 | Tool |
47 | Name |
48 | Created |
49 | Tags |
50 | CWE |
51 | Link |
52 |
53 | {{#each codeScanning.open.error}}
54 |
55 | {{this.tool}} |
56 | {{this.name}} |
57 | {{this.created}} |
58 | {{this.rule.details.tags}} |
59 | {{this.rule.details.cwes}} |
60 | {{this.url}} |
61 |
62 | {{/each}}
63 |
64 |
65 | Warnings
66 |
67 |
68 | Tool |
69 | Name |
70 | Created |
71 | Tags |
72 | CWE |
73 | Link |
74 |
75 | {{#each codeScanning.open.warning}}
76 |
77 | {{this.tool}} |
78 | {{this.name}} |
79 | {{this.created}} |
80 | {{this.rule.tags}} |
81 | {{this.rule.cwes}} |
82 | {{this.url}} |
83 |
84 | {{/each}}
85 |
86 |
87 | Dependencies Detail
88 |
89 | Vulnerabilities
90 |
91 | {{#if dependencies.vulnerabilities.CRITICAL.length}}
92 | Critical
93 |
94 |
95 | Package |
96 | Ecosystem |
97 | Version |
98 | Manifest |
99 | Severity |
100 | Advisory Id |
101 | Advisory Summary |
102 |
103 | {{#each dependencies.vulnerabilities.CRITICAL}}
104 |
105 | {{this._data.securityVulnerability.package.name}} |
106 | {{this._data.securityVulnerability.package.ecosystem}} |
107 | {{this._data.vulnerableRequirements}} |
108 | {{this._data.vulnerableManifestPath}} |
109 | {{this._data.securityVulnerability.severity}} |
110 | {{this._data.securityAdvisory.ghsaId}} |
111 | {{this._data.securityAdvisory.summary}} |
112 |
113 | {{/each}}
114 |
115 | {{/if}}
116 |
117 | {{#if dependencies.vulnerabilities.HIGH.length}}
118 | High
119 |
120 |
121 | Package |
122 | Ecosystem |
123 | Version |
124 | Manifest |
125 | Severity |
126 | Advisory Id |
127 | Advisory Summary |
128 |
129 | {{#each dependencies.vulnerabilities.HIGH}}
130 |
131 | {{this._data.securityVulnerability.package.name}} |
132 | {{this._data.securityVulnerability.package.ecosystem}} |
133 | {{this._data.vulnerableRequirements}} |
134 | {{this._data.vulnerableManifestPath}} |
135 | {{this._data.securityVulnerability.severity}} |
136 | {{this._data.securityAdvisory.ghsaId}} |
137 | {{this._data.securityAdvisory.summary}} |
138 |
139 | {{/each}}
140 |
141 | {{/if}}
142 |
143 | {{#if dependencies.vulnerabilities.LOW.length}}
144 | Low
145 |
146 |
147 | Package |
148 | Ecosystem |
149 | Version |
150 | Manifest |
151 | Severity |
152 | Advisory Id |
153 | Advisory Summary |
154 |
155 | {{#each dependencies.vulnerabilities.LOW}}
156 |
157 | {{this._data.securityVulnerability.package.name}} |
158 | {{this._data.securityVulnerability.package.ecosystem}} |
159 | {{this._data.vulnerableRequirements}} |
160 | {{this._data.vulnerableManifestPath}} |
161 | {{this._data.securityVulnerability.severity}} |
162 | {{this._data.securityAdvisory.ghsaId}} |
163 | {{this._data.securityAdvisory.summary}} |
164 |
165 | {{/each}}
166 |
167 | {{/if}}
168 |
169 | {{#if dependencies.vulnerabilities.MODERATE.length}}
170 | Moderate
171 |
172 |
173 | Package |
174 | Ecosystem |
175 | Version |
176 | Manifest |
177 | Severity |
178 | Advisory Id |
179 | Advisory Summary |
180 |
181 | {{#each dependencies.vulnerabilities.MODERATE}}
182 |
183 | {{this._data.securityVulnerability.package.name}} |
184 | {{this._data.securityVulnerability.package.ecosystem}} |
185 | {{this._data.vulnerableRequirements}} |
186 | {{this._data.vulnerableManifestPath}} |
187 | {{this._data.securityVulnerability.severity}} |
188 | {{this._data.securityAdvisory.ghsaId}} |
189 | {{this._data.securityAdvisory.summary}} |
190 |
191 | {{/each}}
192 |
193 | {{/if}}
194 |
195 | Detected Dependencies
196 |
197 | Maven Dependencies:
198 |
199 |
200 | {{#each dependencies.deps.dependencies.maven}}
201 |
202 | {{this.name}} |
203 | {{this.version}} |
204 |
205 | {{/each}}
206 |
207 |
208 |
209 | NPM Dependencies:
210 |
211 |
212 | {{#each dependencies.deps.dependencies.npm}}
213 |
214 | {{this.name}} |
215 | {{this.version}} |
216 |
217 | {{/each}}
218 |
219 |
220 |
221 |
222 |
--------------------------------------------------------------------------------
/templates/vulnerability.html:
--------------------------------------------------------------------------------
1 | {% for severity, vulnerabilities in sca.vulnerabilities %}
2 |
3 |
{{ severity }}
4 |
5 | {% for vuln in vulnerabilities %}
6 | -
7 |
8 |
{{ vuln.advisory.summary }}
9 |
{{ vuln.created }}
10 |
{{ vuln.published }}
11 |
12 | {% for identifier in vuln.advisory.identifiers %}
13 |
{{ identifier.value }}
14 | {% endfor %}
15 |
{{ vuln.advisory.permalink }}
16 |
{{ vuln.advisory.description }}
17 |
18 |
19 |
20 | {% endfor %}
21 |
22 |
23 | {% endfor %}
24 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es6",
4 | "module": "commonjs",
5 | "outDir": "./lib",
6 | "rootDir": "./src",
7 | "strict": true,
8 | "noImplicitAny": false,
9 | "esModuleInterop": true,
10 | "skipLibCheck": true,
11 | "forceConsistentCasingInFileNames": true
12 | },
13 | "exclude": [
14 | "node_modules",
15 | "**/*.test.ts"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------