|
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