Create markdown summary file for sharing MRVA results
This commit is contained in:
@@ -10,11 +10,20 @@ export type MarkdownFile = string[];
|
||||
*/
|
||||
export function generateMarkdown(query: RemoteQuery, analysesResults: AnalysisResults[]): MarkdownFile[] {
|
||||
const files: MarkdownFile[] = [];
|
||||
// Generate summary file with links to individual files
|
||||
const summaryLines: MarkdownFile = generateMarkdownSummary(query);
|
||||
for (const analysisResult of analysesResults) {
|
||||
if (analysisResult.interpretedResults.length === 0) {
|
||||
// TODO: We'll add support for non-interpreted results later.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Append nwo and results count to the summary table
|
||||
summaryLines.push(
|
||||
`| ${analysisResult.nwo} | [${analysisResult.interpretedResults.length} result(s)](${createGistRelativeLink(analysisResult.nwo)}) |`
|
||||
);
|
||||
|
||||
// Generate individual markdown file for each repository
|
||||
const lines = [
|
||||
`### ${analysisResult.nwo}`,
|
||||
''
|
||||
@@ -25,8 +34,37 @@ export function generateMarkdown(query: RemoteQuery, analysesResults: AnalysisRe
|
||||
}
|
||||
files.push(lines);
|
||||
}
|
||||
files.push(summaryLines);
|
||||
return files;
|
||||
}
|
||||
|
||||
export function generateMarkdownSummary(query: RemoteQuery): MarkdownFile {
|
||||
const lines: MarkdownFile = [];
|
||||
// Title
|
||||
lines.push(`## Results for "${query.queryName}"`);
|
||||
lines.push('');
|
||||
|
||||
// Expandable section containing query text
|
||||
const queryCodeBlock = [
|
||||
'```ql',
|
||||
...query.queryText.split('\n'),
|
||||
'```',
|
||||
];
|
||||
lines.push(
|
||||
...buildExpandableMarkdownSection('Query', queryCodeBlock)
|
||||
);
|
||||
|
||||
// Summary table
|
||||
lines.push(
|
||||
'### Summary',
|
||||
''
|
||||
);
|
||||
lines.push(
|
||||
'| Repository | Results |',
|
||||
'| --- | --- |',
|
||||
);
|
||||
// nwo and result count will be appended to this table
|
||||
return lines;
|
||||
}
|
||||
|
||||
function generateMarkdownForInterpretedResult(interpretedResult: AnalysisAlert, language: string): MarkdownFile {
|
||||
@@ -97,3 +135,39 @@ export function createMarkdownRemoteFileRef(
|
||||
const markdownLink = `[${linkText || fileLink.filePath}](${createRemoteFileRef(fileLink, startLine, endLine)})`;
|
||||
return markdownLink;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an expandable markdown section of the form:
|
||||
* <details>
|
||||
* <summary>title</summary>
|
||||
*
|
||||
* contents
|
||||
*
|
||||
* </details>
|
||||
*/
|
||||
function buildExpandableMarkdownSection(title: string, contents: MarkdownFile): MarkdownFile {
|
||||
const expandableLines: MarkdownFile = [];
|
||||
expandableLines.push(
|
||||
'<details>',
|
||||
`<summary>${title}</summary>`,
|
||||
'',
|
||||
);
|
||||
expandableLines.push(...contents);
|
||||
expandableLines.push(
|
||||
'',
|
||||
'</details>',
|
||||
''
|
||||
);
|
||||
return expandableLines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates anchor link to a file in the gist. This is of the form:
|
||||
* '#file-<name>-<file-extension>'
|
||||
*
|
||||
* TODO: Make sure these names align with the actual file names once we upload them to a gist.
|
||||
*/
|
||||
function createGistRelativeLink(nwo: string): string {
|
||||
const [owner, repo] = nwo.split('/');
|
||||
return `#file-${owner}-${repo}-md`;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
## Results for "Shell command built from environment values"
|
||||
|
||||
<details>
|
||||
<summary>Query</summary>
|
||||
|
||||
```ql
|
||||
/**
|
||||
* @name Shell command built from environment values
|
||||
* @description Building a shell command string with values from the enclosing
|
||||
* environment may cause subtle bugs or vulnerabilities.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 6.3
|
||||
* @precision high
|
||||
* @id js/shell-command-injection-from-environment
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-078
|
||||
* external/cwe/cwe-088
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow::PathGraph
|
||||
import semmle.javascript.security.dataflow.ShellCommandInjectionFromEnvironmentQuery
|
||||
|
||||
from
|
||||
Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::Node highlight,
|
||||
Source sourceNode
|
||||
where
|
||||
sourceNode = source.getNode() and
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
if cfg.isSinkWithHighlight(sink.getNode(), _)
|
||||
then cfg.isSinkWithHighlight(sink.getNode(), highlight)
|
||||
else highlight = sink.getNode()
|
||||
select highlight, source, sink, "This shell command depends on an uncontrolled $@.", sourceNode,
|
||||
sourceNode.getSourceType()
|
||||
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Summary
|
||||
|
||||
| Repository | Results |
|
||||
| --- | --- |
|
||||
| github/codeql | [4 result(s)](#file-github-codeql-md) |
|
||||
| meteor/meteor | [1 result(s)](#file-meteor-meteor-md) |
|
||||
@@ -14,18 +14,21 @@ describe('markdown generation', async function() {
|
||||
);
|
||||
const markdownFiles = generateMarkdown(problemQuery, analysesResults);
|
||||
|
||||
// Check that query has results for two repositories
|
||||
expect(markdownFiles.length).to.equal(2);
|
||||
// Check that query has results for two repositories, plus a summary file
|
||||
expect(markdownFiles.length).to.equal(3);
|
||||
|
||||
const markdownFile1 = markdownFiles[0]; // results for github/codeql repo
|
||||
const markdownFile2 = markdownFiles[1]; // results for meteor/meteor repo
|
||||
const markdownFile3 = markdownFiles[2]; // summary file
|
||||
|
||||
const expectedTestOutput1 = await readTestOutputFile('data/results-repo1.md');
|
||||
const expectedTestOutput2 = await readTestOutputFile('data/results-repo2.md');
|
||||
const expectedSummaryFile = await readTestOutputFile('data/summary.md');
|
||||
|
||||
// Check that markdown output is correct, after making line endings consistent
|
||||
expect(markdownFile1.join('\n')).to.equal(expectedTestOutput1);
|
||||
expect(markdownFile2.join('\n')).to.equal(expectedTestOutput2);
|
||||
expect(markdownFile3.join('\n')).to.equal(expectedSummaryFile);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user