|
1 | 1 | #!/usr/bin/env python3
|
| 2 | +# /// script |
| 3 | +# requires-python = ">=3.12" |
| 4 | +# dependencies = [ |
| 5 | +# "ghastoolkit" |
| 6 | +# ] |
| 7 | +# /// |
| 8 | + |
2 | 9 | import os
|
3 | 10 | import csv
|
4 | 11 | import json
|
@@ -35,20 +42,16 @@ def generateCsv(csvfile, rules, src, src_suite, display: bool = True):
|
35 | 42 |
|
36 | 43 | def generateMarkdown(rules, suite: str = "code-scanning") -> str:
|
37 | 44 | markdown = """\
|
38 |
| -# Code Scanning Coverage Report |
39 |
| -
|
40 |
| -This report shows the coverage of Code Scanning rules for the current repository. |
41 |
| -
|
42 |
| -| Suite | Query ID | Severity | |
43 |
| -| ------------- | ------------------------------------------ | -------- | |
| 45 | +| Suite | Query ID | Severity | |
| 46 | +| ------------- | ---------------------------------------------------- | -------- | |
44 | 47 | """
|
45 | 48 |
|
46 | 49 | for rule in rules:
|
47 | 50 | id = rule.get("id")
|
48 | 51 | props = rule.get("properties", {})
|
49 | 52 | severity = props.get("security-severity", "NA")
|
50 | 53 |
|
51 |
| - markdown += f"| {suite} | {id:<42} | {severity:<8} |\n" |
| 54 | + markdown += f"| {suite} | {id:<52} | {severity:<8} |\n" |
52 | 55 | return markdown
|
53 | 56 |
|
54 | 57 | @dataclass
|
@@ -87,6 +90,7 @@ def arguments(self):
|
87 | 90 | parser.add_argument("--pull-request", help="Output to pull_request")
|
88 | 91 | parser.add_argument("--csv", action="store_true", help="Output as csv")
|
89 | 92 | parser.add_argument("--markdown", action="store_true", help="Output as markdown")
|
| 93 | + parser.add_argument("-o", "--output", help="Output file") |
90 | 94 |
|
91 | 95 | parser.add_argument("--rules", default="./scripts/rules", help="Path to query rules folder")
|
92 | 96 |
|
@@ -116,7 +120,28 @@ def runReport(self, arguments):
|
116 | 120 | generateCsv("coverage.csv", rules, src, src_suite, display=not arguments.markdown)
|
117 | 121 |
|
118 | 122 | markdown = generateMarkdown(rules)
|
119 |
| - print(markdown) |
| 123 | + |
| 124 | + if arguments.output: |
| 125 | + if not os.path.exists(arguments.output): |
| 126 | + raise Exception(f"Markdown output file not found: {arguments.output}") |
| 127 | + with open(arguments.output, "r") as handle: |
| 128 | + content = handle.read() |
| 129 | + |
| 130 | + start_marker = "<!-- coverage-start -->" |
| 131 | + end_marker = "<!-- coverage-end -->" |
| 132 | + start_index = content.find(start_marker) |
| 133 | + end_index = content.find(end_marker) |
| 134 | + if start_index == -1 or end_index == -1: |
| 135 | + raise Exception("Markers not found in markdown file") |
| 136 | + |
| 137 | + # Replace the content between the markers |
| 138 | + new_content = content[:start_index + len(start_marker)] + "\n" + markdown + "\n" + content[end_index:] |
| 139 | + with open(arguments.output, "w") as handle: |
| 140 | + handle.write(new_content) |
| 141 | + |
| 142 | + else: |
| 143 | + print("""# Code Scanning Coverage Report\n\nThis report shows the coverage of Code Scanning rules for the current repository.\n\n""") |
| 144 | + print(markdown) |
120 | 145 |
|
121 | 146 | def runRules(self, arguments):
|
122 | 147 | """Run and generate the queries."""
|
|
0 commit comments