From 55a930d98df8b300f5159b91e94630094bfba847 Mon Sep 17 00:00:00 2001 From: qlwang Date: Sun, 3 Aug 2025 13:12:49 -0400 Subject: [PATCH 01/13] enable markdown --- .github/workflows/nightvision-evidence.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/nightvision-evidence.yml b/.github/workflows/nightvision-evidence.yml index c4627ed..e2f4a03 100644 --- a/.github/workflows/nightvision-evidence.yml +++ b/.github/workflows/nightvision-evidence.yml @@ -70,6 +70,13 @@ jobs: nightvision scan ${NIGHTVISION_TARGET} --auth ${NIGHTVISION_AUTH} > scan-results.txt nightvision export sarif -s "$(head -n 1 scan-results.txt)" --swagger-file openapi-spec.yml -o ${NIGHTVISION_SCAN_RESULT} + - name: Install sarif-to-markdown + run: npm ci && npm install --no-save @security-alert/sarif-to-markdown + + - name: Convert sarif to markdown + run: | + sarif-to-markdown ${NIGHTVISION_SCAN_RESULT} > nightvision_scan_result.md + - name: Upload evidence to the docker package run: | jf evd create \ @@ -80,6 +87,7 @@ jobs: --key-alias nightvision_evidence_key \ --predicate ${{ env.NIGHTVISION_SCAN_RESULT }} \ --predicate-type https://in-toto.io/attestation/vulns + --markdown nightvision_scan_result.md - name: Publish build info run: jfrog rt build-publish ${{ vars.BUILD_NAME }} ${{ github.run_number }} \ No newline at end of file From 38a12689f87a6e9bfe042b2545b0fd904c427d2f Mon Sep 17 00:00:00 2001 From: qlwang Date: Sun, 3 Aug 2025 13:35:07 -0400 Subject: [PATCH 02/13] removed the npm install step with npx --- .github/workflows/nightvision-evidence.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/nightvision-evidence.yml b/.github/workflows/nightvision-evidence.yml index e2f4a03..2f56086 100644 --- a/.github/workflows/nightvision-evidence.yml +++ b/.github/workflows/nightvision-evidence.yml @@ -70,12 +70,9 @@ jobs: nightvision scan ${NIGHTVISION_TARGET} --auth ${NIGHTVISION_AUTH} > scan-results.txt nightvision export sarif -s "$(head -n 1 scan-results.txt)" --swagger-file openapi-spec.yml -o ${NIGHTVISION_SCAN_RESULT} - - name: Install sarif-to-markdown - run: npm ci && npm install --no-save @security-alert/sarif-to-markdown - - name: Convert sarif to markdown run: | - sarif-to-markdown ${NIGHTVISION_SCAN_RESULT} > nightvision_scan_result.md + npx @security-alert/sarif-to-markdown ${NIGHTVISION_SCAN_RESULT} > nightvision_scan_result.md - name: Upload evidence to the docker package run: | From 0a6e7730aacc9e0fd1603bded1f68b0eda1ce04b Mon Sep 17 00:00:00 2001 From: qlwang Date: Tue, 12 Aug 2025 15:11:35 -0400 Subject: [PATCH 03/13] use python script to convert sarif to markdown --- .github/workflows/nightvision-evidence.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nightvision-evidence.yml b/.github/workflows/nightvision-evidence.yml index 2f56086..7ac9a8b 100644 --- a/.github/workflows/nightvision-evidence.yml +++ b/.github/workflows/nightvision-evidence.yml @@ -72,7 +72,7 @@ jobs: - name: Convert sarif to markdown run: | - npx @security-alert/sarif-to-markdown ${NIGHTVISION_SCAN_RESULT} > nightvision_scan_result.md + python sarif_to_markdown.py ${NIGHTVISION_SCAN_RESULT} nightvision_scan_result.md - name: Upload evidence to the docker package run: | From f4c48be67d08bc0da97fc7e8443e1dc3fc18f367 Mon Sep 17 00:00:00 2001 From: qlwang Date: Tue, 12 Aug 2025 15:13:02 -0400 Subject: [PATCH 04/13] use python script to convert sarif to markdown --- sarif_to_markdown.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 sarif_to_markdown.py diff --git a/sarif_to_markdown.py b/sarif_to_markdown.py new file mode 100644 index 0000000..e69de29 From 9c64466dc83f06f8212410d772e9aa84d37577a5 Mon Sep 17 00:00:00 2001 From: qlwang Date: Tue, 12 Aug 2025 16:30:51 -0400 Subject: [PATCH 05/13] added provider id and missing backslash --- .github/workflows/nightvision-evidence.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/nightvision-evidence.yml b/.github/workflows/nightvision-evidence.yml index 7ac9a8b..1670b88 100644 --- a/.github/workflows/nightvision-evidence.yml +++ b/.github/workflows/nightvision-evidence.yml @@ -82,8 +82,9 @@ jobs: --package-repo-name ${{ vars.DOCKER_REPO }} \ --key "${{ secrets.PRIVATE_KEY }}" \ --key-alias nightvision_evidence_key \ + --provider-id nightvision \ --predicate ${{ env.NIGHTVISION_SCAN_RESULT }} \ - --predicate-type https://in-toto.io/attestation/vulns + --predicate-type https://in-toto.io/attestation/vulns \ --markdown nightvision_scan_result.md - name: Publish build info From 8d9a9dc63c518a24fe738b2d616513b426820e85 Mon Sep 17 00:00:00 2001 From: qlwang Date: Tue, 12 Aug 2025 17:36:45 -0400 Subject: [PATCH 06/13] updated sarif converter --- sarif_to_markdown.py | 255 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+) diff --git a/sarif_to_markdown.py b/sarif_to_markdown.py index e69de29..15c4f6e 100644 --- a/sarif_to_markdown.py +++ b/sarif_to_markdown.py @@ -0,0 +1,255 @@ +import json +import re +import sys +from os import path + + +def parse_sarif_file(sarif_data: dict[str]) -> dict[str]: + """Parse SARIF file and extract relevant information""" + + # Extract tool information + tool_info = sarif_data["runs"][0]["tool"]["driver"] + tool_name = tool_info.get("name", "Unknown") + tool_version = tool_info.get("version", "Unknown") + scan_url = tool_info.get("informationUri", "") + + # Extract rules + rules = {} + for rule in tool_info.get("rules", []): + rules[rule["id"]] = { + "name": rule.get("name", ""), + "description": rule.get("fullDescription", {}).get("text", ""), + "short_description": rule.get("shortDescription", {}).get("text", ""), + } + + # Extract results + results = [] + for result in sarif_data["runs"][0].get("results", []): + rule_id = result.get("ruleId", "") + rule_info = rules.get(rule_id, {}) + result_data = _convert_result(result, rule_info) + results.append(result_data) + + return { + "tool_name": tool_name, + "tool_version": tool_version, + "scan_url": scan_url, + "results": results, + } + + +def _convert_result(result, rule_info: dict) -> dict: + """Convert a SARIF result to a simplified dictionary format""" + + rule_description = rule_info.get("description", "") + + # Extract basic information + vulnerability_name = result.get("message", {}).get("text", "") + properties = result.get("properties", {}) + + # Extract endpoint information + endpoint = properties.get("path", "") + if not endpoint or endpoint == "-": + url = properties.get("url", "") + endpoint = __extract_endpoint_from_url(url) + if not endpoint or endpoint == "-": + endpoint = __extract_endpoint_from_description(rule_description) + + method = __extract_method_from_description(rule_description) + + # Extract location information + file_location = "-" + locations = result.get("locations", []) + if locations and "physicalLocation" in locations[0]: + phys_loc = locations[0]["physicalLocation"] + if "artifactLocation" in phys_loc: + file_path = phys_loc["artifactLocation"].get("uri", "") + if "region" in phys_loc and "startLine" in phys_loc["region"]: + line_num = phys_loc["region"]["startLine"] + file_location = f"{file_path}:{line_num}" if file_path != "-" else "-" + elif file_path: + file_location = file_path + + result_data = { + "vulnerability": vulnerability_name, + "risk": properties.get("nightvision-risk", "-"), + "confidence": properties.get("nightvision-confidence", "-"), + "security_severity": properties.get("security-severity", "-"), + "endpoint": endpoint, + "method": method, + "parameter": ( + ", ".join(properties.get("parameter", [])) + if properties.get("parameter") + else "-" + ), + "payload": properties.get("payload", "-"), + "file_location": file_location, + "issue_id": result.get("partialFingerprints", {}).get( + "nightvisionIssueID/v1", "" + ), + } + return result_data + + +def __extract_endpoint_from_url(url: str) -> str: + """Extract endpoint path from URL""" + + if not url or url == "-": + return "-" + # Extract path component from URL + path_match = re.search(r"(?:https?://[^/]+)?(/[^\s?#]*)", url) + return path_match.group(1) if path_match else "-" + + +def __extract_endpoint_from_description(description: str) -> str: + """Extract endpoint path from vulnerability description""" + + # Look for patterns like "The `/path/` URL path is vulnerable" + pattern = r"The `([^`]+)` URL path is vulnerable" + match = re.search(pattern, description) + if match: + return match.group(1) + return "-" + + +def __extract_method_from_description(description: str) -> str: + """Extract HTTP method from vulnerability description""" + + # Look for patterns like "via a METHOD request." + pattern = r"via a (\S+) request" + match = re.search(pattern, description) + if match: + return match.group(1).upper() + return "-" + + +def generate_markdown_report(parsed_data: dict[str]) -> str: + """Generate Markdown report from parsed SARIF data""" + + tool_name = parsed_data["tool_name"] + tool_version = parsed_data["tool_version"] + scan_url = parsed_data["scan_url"] + results = parsed_data["results"] + + # Start building the markdown + markdown = f"""# Security Vulnerability Report + +**Tool:** {tool_name} v{tool_version} + +**Scan URL:** {scan_url} + +## Vulnerability Findings + +| Vulnerability | Risk | Confidence | Endpoint | Method | Parameter | Payload | File Location | +|---------------|------|------------|----------|--------|-----------|---------|---------------| +""" + + # Add each vulnerability as a table row + for result in results: + payload = result["payload"] + # Escape pipe characters and backticks in payload for markdown table + if payload != "-": + payload = f"`{payload}`" + + markdown += f"| {result['vulnerability']} | {result['risk']} | {result['confidence']} | {result['endpoint']} | {result['method']} | {result['parameter']} | {payload} | {result['file_location']} |\n" + + # Generate summary statistics + risk_counts = {} + for result in results: + risk = result["risk"] + risk_counts[risk] = risk_counts.get(risk, 0) + 1 + + markdown += f""" +## Summary Statistics + +| Risk Level | Count | +|------------|-------| +""" + + for risk, count in sorted(risk_counts.items()): + markdown += f"| {risk} | {count} |\n" + + # Generate endpoint summary + endpoint_stats = {} + for result in results: + endpoint = result["endpoint"] + if endpoint not in endpoint_stats: + endpoint_stats[endpoint] = {"count": 0, "highest_risk": "-"} + endpoint_stats[endpoint]["count"] += 1 + + # Determine highest risk (simple priority: CRITICAL > HIGH > MEDIUM > LOW) + current_risk = result["risk"] + current_highest = endpoint_stats[endpoint]["highest_risk"] + + risk_priority = {"CRITICAL": 4, "HIGH": 3, "MEDIUM": 2, "LOW": 1, "-": 0} + if risk_priority.get(current_risk, 0) > risk_priority.get(current_highest, 0): + endpoint_stats[endpoint]["highest_risk"] = current_risk + + markdown += f""" +## Affected Endpoints Summary + +| Endpoint | Vulnerability Count | Highest Risk | +|----------|-------------------|--------------| +""" + + for endpoint, stats in sorted(endpoint_stats.items()): + markdown += f"| {endpoint} | {stats['count']} | {stats['highest_risk']} |\n" + + # Generate file location summary + file_stats = {} + for result in results: + file_loc = result["file_location"] + if file_loc != "-" and ":" in file_loc: + file_path, line = file_loc.split(":", 1) + if file_path not in file_stats: + file_stats[file_path] = {} + if line not in file_stats[file_path]: + file_stats[file_path][line] = 0 + file_stats[file_path][line] += 1 + + if file_stats: + markdown += f""" +## File Locations Summary + +| File | Line | Vulnerability Count | +|------|------|-------------------| +""" + + for file_path, lines in sorted(file_stats.items()): + for line, count in sorted(lines.items()): + markdown += f"| {file_path} | {line} | {count} |\n" + + return markdown + + +if __name__ == "__main__": + if len(sys.argv) != 3: + script_name = path.basename(__file__) + print(f"Usage: python {script_name} ") + sys.exit(1) + + input_file = sys.argv[1] + output_file = sys.argv[2] + + try: + with open(input_file, "r", encoding="utf-8") as f: + sarif_data = json.load(f) + + parsed_data = parse_sarif_file(sarif_data) + markdown_report = generate_markdown_report(parsed_data) + + with open(output_file, "w", encoding="utf-8") as f: + f.write(markdown_report) + + print(f"Successfully converted {input_file} to {output_file}") + print(f"Found {len(parsed_data['results'])} vulnerabilities") + + except FileNotFoundError: + print(f"Error: Could not find input file '{input_file}'") + sys.exit(1) + except json.JSONDecodeError as e: + print(f"Error: Invalid JSON in SARIF file - {e}") + sys.exit(1) + except Exception as e: + print(f"Error: {e}") + sys.exit(1) From 00ad319ec4a68e9409ad28c746294b30652cdf72 Mon Sep 17 00:00:00 2001 From: qlwang Date: Tue, 12 Aug 2025 18:42:03 -0400 Subject: [PATCH 07/13] update predicate type --- .github/workflows/nightvision-evidence.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nightvision-evidence.yml b/.github/workflows/nightvision-evidence.yml index 1670b88..9931544 100644 --- a/.github/workflows/nightvision-evidence.yml +++ b/.github/workflows/nightvision-evidence.yml @@ -84,7 +84,7 @@ jobs: --key-alias nightvision_evidence_key \ --provider-id nightvision \ --predicate ${{ env.NIGHTVISION_SCAN_RESULT }} \ - --predicate-type https://in-toto.io/attestation/vulns \ + --predicate-type https://nightvision.net/evidence/vulnscan/v1 \ --markdown nightvision_scan_result.md - name: Publish build info From 9cebb57d4c5e5b2884e0265fbd9381d488c86cba Mon Sep 17 00:00:00 2001 From: qlwang Date: Tue, 12 Aug 2025 19:00:21 -0400 Subject: [PATCH 08/13] print out markdown file --- .github/workflows/nightvision-evidence.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/nightvision-evidence.yml b/.github/workflows/nightvision-evidence.yml index 9931544..86d46ac 100644 --- a/.github/workflows/nightvision-evidence.yml +++ b/.github/workflows/nightvision-evidence.yml @@ -74,6 +74,11 @@ jobs: run: | python sarif_to_markdown.py ${NIGHTVISION_SCAN_RESULT} nightvision_scan_result.md + - name: Print scan result markdown + run: | + echo "=== NightVision Scan Result ===" + cat nightvision_scan_result.md + - name: Upload evidence to the docker package run: | jf evd create \ From c8199100dc27382a538ffb8855b141f25f06098c Mon Sep 17 00:00:00 2001 From: qlwang Date: Thu, 14 Aug 2025 09:48:08 -0400 Subject: [PATCH 09/13] upload OpenAPI spec as evidence --- .github/workflows/nightvision-evidence.yml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/nightvision-evidence.yml b/.github/workflows/nightvision-evidence.yml index 86d46ac..c3cbf8c 100644 --- a/.github/workflows/nightvision-evidence.yml +++ b/.github/workflows/nightvision-evidence.yml @@ -74,11 +74,6 @@ jobs: run: | python sarif_to_markdown.py ${NIGHTVISION_SCAN_RESULT} nightvision_scan_result.md - - name: Print scan result markdown - run: | - echo "=== NightVision Scan Result ===" - cat nightvision_scan_result.md - - name: Upload evidence to the docker package run: | jf evd create \ @@ -92,5 +87,17 @@ jobs: --predicate-type https://nightvision.net/evidence/vulnscan/v1 \ --markdown nightvision_scan_result.md + - name: Upload OpenAPI spec to the docker package + run: | + jf evd create \ + --package-name ${{ vars.IMAGE_NAME }} \ + --package-version "${{ env.IMAGE_TAG }}" \ + --package-repo-name ${{ vars.DOCKER_REPO }} \ + --key "${{ secrets.PRIVATE_KEY }}" \ + --key-alias nightvision_evidence_key \ + --provider-id nightvision \ + --predicate openapi-spec.yml \ + --predicate-type https://nightvision.net/evidence/vulnscan/v1 + - name: Publish build info run: jfrog rt build-publish ${{ vars.BUILD_NAME }} ${{ github.run_number }} \ No newline at end of file From f43b2e8e5ec56dbf4273b0eb1ffb11e245b21f27 Mon Sep 17 00:00:00 2001 From: qlwang Date: Thu, 14 Aug 2025 10:42:18 -0400 Subject: [PATCH 10/13] output swagger file in json format --- .github/workflows/nightvision-evidence.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/nightvision-evidence.yml b/.github/workflows/nightvision-evidence.yml index c3cbf8c..eb5bdeb 100644 --- a/.github/workflows/nightvision-evidence.yml +++ b/.github/workflows/nightvision-evidence.yml @@ -57,9 +57,9 @@ jobs: - name: Extract API documentation from code run: | - nightvision swagger extract . --target ${NIGHTVISION_TARGET} --lang java || true - if [ ! -e openapi-spec.yml ]; then - cp backup-openapi-spec.yml openapi-spec.yml + nightvision swagger extract . --target ${NIGHTVISION_TARGET} --lang java --file-format json || true + if [ ! -e openapi-spec.json ]; then + cp backup-openapi-spec.json openapi-spec.json fi - name: Start the app @@ -68,7 +68,7 @@ jobs: - name: Scan the app run: | nightvision scan ${NIGHTVISION_TARGET} --auth ${NIGHTVISION_AUTH} > scan-results.txt - nightvision export sarif -s "$(head -n 1 scan-results.txt)" --swagger-file openapi-spec.yml -o ${NIGHTVISION_SCAN_RESULT} + nightvision export sarif -s "$(head -n 1 scan-results.txt)" --swagger-file openapi-spec.json -o ${NIGHTVISION_SCAN_RESULT} - name: Convert sarif to markdown run: | @@ -96,7 +96,7 @@ jobs: --key "${{ secrets.PRIVATE_KEY }}" \ --key-alias nightvision_evidence_key \ --provider-id nightvision \ - --predicate openapi-spec.yml \ + --predicate openapi-spec.json \ --predicate-type https://nightvision.net/evidence/vulnscan/v1 - name: Publish build info From 8918fa8db7232b075af2fed71b5a845416ffabe5 Mon Sep 17 00:00:00 2001 From: qlwang Date: Thu, 14 Aug 2025 10:56:46 -0400 Subject: [PATCH 11/13] updated predicate type for openapi spec --- .github/workflows/nightvision-evidence.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nightvision-evidence.yml b/.github/workflows/nightvision-evidence.yml index eb5bdeb..fdf472e 100644 --- a/.github/workflows/nightvision-evidence.yml +++ b/.github/workflows/nightvision-evidence.yml @@ -97,7 +97,7 @@ jobs: --key-alias nightvision_evidence_key \ --provider-id nightvision \ --predicate openapi-spec.json \ - --predicate-type https://nightvision.net/evidence/vulnscan/v1 + --predicate-type https://nightvision.net/evidence/openapi-spec/v1 - name: Publish build info run: jfrog rt build-publish ${{ vars.BUILD_NAME }} ${{ github.run_number }} \ No newline at end of file From 802c91d1e6afb0f3bda3a96ad45b1cab3174def8 Mon Sep 17 00:00:00 2001 From: qlwang Date: Thu, 21 Aug 2025 05:38:57 -0400 Subject: [PATCH 12/13] replace hardcoded values to variables --- .github/workflows/nightvision-evidence.yml | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/.github/workflows/nightvision-evidence.yml b/.github/workflows/nightvision-evidence.yml index fdf472e..5371886 100644 --- a/.github/workflows/nightvision-evidence.yml +++ b/.github/workflows/nightvision-evidence.yml @@ -54,12 +54,14 @@ jobs: echo "NIGHTVISION_AUTH=javaspringvulny-api" >> $GITHUB_ENV echo "NIGHTVISION_TOKEN=${{ secrets.NIGHTVISION_TOKEN }}" >> $GITHUB_ENV echo "NIGHTVISION_SCAN_RESULT=scan_result.sarif" >> $GITHUB_ENV + echo "NIGHTVISION_SCAN_RESULT_MARKDOWN=scan_result.md" >> $GITHUB_ENV + echo "NIGHTVISION_OPENAPI_SPEC=openapi-spec.json" >> $GITHUB_ENV - name: Extract API documentation from code run: | nightvision swagger extract . --target ${NIGHTVISION_TARGET} --lang java --file-format json || true - if [ ! -e openapi-spec.json ]; then - cp backup-openapi-spec.json openapi-spec.json + if [ ! -e ${NIGHTVISION_OPENAPI_SPEC} ]; then + cp backup-openapi-spec.json ${NIGHTVISION_OPENAPI_SPEC} fi - name: Start the app @@ -72,7 +74,7 @@ jobs: - name: Convert sarif to markdown run: | - python sarif_to_markdown.py ${NIGHTVISION_SCAN_RESULT} nightvision_scan_result.md + python sarif_to_markdown.py ${NIGHTVISION_SCAN_RESULT} ${NIGHTVISION_SCAN_RESULT_MARKDOWN} - name: Upload evidence to the docker package run: | @@ -82,10 +84,10 @@ jobs: --package-repo-name ${{ vars.DOCKER_REPO }} \ --key "${{ secrets.PRIVATE_KEY }}" \ --key-alias nightvision_evidence_key \ - --provider-id nightvision \ + --provider-id ${{ vars.NIGHTVISION_PROVIDER_ID }} \ --predicate ${{ env.NIGHTVISION_SCAN_RESULT }} \ - --predicate-type https://nightvision.net/evidence/vulnscan/v1 \ - --markdown nightvision_scan_result.md + --predicate-type ${{ vars.NIGHTVISION_SCAN_RESULT_PREDICATE_TYPE }} \ + --markdown ${{ env.NIGHTVISION_SCAN_RESULT_MARKDOWN }} - name: Upload OpenAPI spec to the docker package run: | @@ -95,9 +97,9 @@ jobs: --package-repo-name ${{ vars.DOCKER_REPO }} \ --key "${{ secrets.PRIVATE_KEY }}" \ --key-alias nightvision_evidence_key \ - --provider-id nightvision \ - --predicate openapi-spec.json \ - --predicate-type https://nightvision.net/evidence/openapi-spec/v1 + --provider-id ${{ vars.NIGHTVISION_PROVIDER_ID }} \ + --predicate ${{ env.NIGHTVISION_OPENAPI_SPEC }} \ + --predicate-type ${{ vars.NIGHTVISION_OPENAPI_SPEC_PREDICATE_TYPE }} - name: Publish build info run: jfrog rt build-publish ${{ vars.BUILD_NAME }} ${{ github.run_number }} \ No newline at end of file From 7b380fceb4b1753c96e1da3b90089ee8ff43c18f Mon Sep 17 00:00:00 2001 From: qlwang Date: Thu, 21 Aug 2025 07:04:23 -0400 Subject: [PATCH 13/13] update README --- .github/workflows/nightvision-evidence.yml | 2 +- README.md | 47 ++++++++++++++++++---- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/.github/workflows/nightvision-evidence.yml b/.github/workflows/nightvision-evidence.yml index 5371886..c2e8aaa 100644 --- a/.github/workflows/nightvision-evidence.yml +++ b/.github/workflows/nightvision-evidence.yml @@ -70,7 +70,7 @@ jobs: - name: Scan the app run: | nightvision scan ${NIGHTVISION_TARGET} --auth ${NIGHTVISION_AUTH} > scan-results.txt - nightvision export sarif -s "$(head -n 1 scan-results.txt)" --swagger-file openapi-spec.json -o ${NIGHTVISION_SCAN_RESULT} + nightvision export sarif -s "$(head -n 1 scan-results.txt)" --swagger-file ${NIGHTVISION_OPENAPI_SPEC} -o ${NIGHTVISION_SCAN_RESULT} - name: Convert sarif to markdown run: | diff --git a/README.md b/README.md index 4ce19cd..2a054fa 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,9 @@ Refer to [nightvision-evidence.yml](https://github.com/nvsecurity/jfrog-integrat * `BUILD_NAME` (name of the docker image build) * `DOCKER_REPO` (name of the JFrog docker repository) * `IMAGE_NAME` (name of the docker image) + * `NIGHTVISION_PROVIDER_ID` (name of the provider that created the evidence) + * `NIGHTVISION_SCAN_RESULT_PREDICATE_TYPE` (predicate type for the DAST scan result) + * `NIGHTVISION_OPENAPI_SPEC_PREDICATE_TYPE` (predicate type for the OpenAPI spec) * Configure the following repository secrets in GitHub * `ARTIFACTORY_ACCESS_TOKEN` (access token for the JFrog artifactory server) * `JF_USER` (user name of the JFrog artifactory server) @@ -97,8 +100,8 @@ Scan the API source code automatically generate the Swagger file - name: Extract API documentation from code run: | nightvision swagger extract . --target ${NIGHTVISION_TARGET} --lang java || true - if [ ! -e openapi-spec.yml ]; then - cp backup-openapi-spec.yml openapi-spec.yml + if [ ! -e ${NIGHTVISION_OPENAPI_SPEC} ]; then + cp backup-openapi-spec.json ${NIGHTVISION_OPENAPI_SPEC} fi ``` @@ -117,7 +120,17 @@ Scan the application using the auto-generated Swagger file - name: Scan the app run: | nightvision scan ${NIGHTVISION_TARGET} --auth ${NIGHTVISION_AUTH} > scan-results.txt - nightvision export sarif -s "$(head -n 1 scan-results.txt)" --swagger-file openapi-spec.yml -o ${NIGHTVISION_SCAN_RESULT} + nightvision export sarif -s "$(head -n 1 scan-results.txt)" --swagger-file ${NIGHTVISION_OPENAPI_SPEC} -o ${NIGHTVISION_SCAN_RESULT} +``` + +### Markdown Conversion + +Convert the scan result from Sarif to Markdown + +```yaml +- name: Convert sarif to markdown + run: | + python sarif_to_markdown.py ${NIGHTVISION_SCAN_RESULT} ${NIGHTVISION_SCAN_RESULT_MARKDOWN} ``` ## Attach DAST Scan Evidence @@ -133,13 +146,31 @@ Sign the DAST scan result using the private key and upload it to the docker repo --package-repo-name ${{ vars.DOCKER_REPO }} \ --key ${{ secrets.PRIVATE_KEY }} \ --key-alias nightvision_evidence_key \ - --predicate ${NIGHTVISION_SCAN_RESULT} \ - --predicate-type https://in-toto.io/attestation/vulns + --provider-id ${{ vars.NIGHTVISION_PROVIDER_ID }} \ + --predicate ${{ env.NIGHTVISION_SCAN_RESULT }} \ + --predicate-type ${{ vars.NIGHTVISION_SCAN_RESULT_PREDICATE_TYPE }} \ + --markdown ${{ env.NIGHTVISION_SCAN_RESULT_MARKDOWN }} +``` + +## Attach OpenAPI Spec Evidence + +Sign the auto-generated OpenAPI spec using the private key and upload it to the docker repository + +```yaml +- name: Upload OpenAPI spec to the docker package + run: | + jf evd create \ + --package-name ${{ vars.IMAGE_NAME }} \ + --package-version "${{ env.IMAGE_TAG }}" \ + --package-repo-name ${{ vars.DOCKER_REPO }} \ + --key "${{ secrets.PRIVATE_KEY }}" \ + --key-alias nightvision_evidence_key \ + --provider-id ${{ vars.NIGHTVISION_PROVIDER_ID }} \ + --predicate ${{ env.NIGHTVISION_OPENAPI_SPEC }} \ + --predicate-type ${{ vars.NIGHTVISION_OPENAPI_SPEC_PREDICATE_TYPE }} ``` ## Note -1. It appears the `jf rt build-docker-create` command will create two different package versions: a version using the GitHub run number and a version using the `sha256` digest. The evidence will be attached to the version using the GitHub run number. -2. NightVision currently uses the `https://in-toto.io/attestation/vulns` predicate type when attaching the evidence. We can switch to a different predicate type if required. -3. In addition to attaching the DAST scan evidence, NightVision can attach the auto-generated Swagger file as evidence as well. +1. It appears the `jf rt build-docker-create` command will create two different package versions: a version using the GitHub run number and a version using the `sha256` digest. The evidence will be attached to the version using the GitHub run number.