CYBER-0 initial concept ready
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Convert the existing Ansible compliance report to the common report schema."""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from jsonschema import Draft202012Validator, FormatChecker
|
||||
|
||||
|
||||
STATUS_MAP = {True: "passed", False: "failed", "review": "review", "skipped": "skipped"}
|
||||
|
||||
|
||||
def environment(name: str, fallback: str = "") -> str:
|
||||
return os.environ.get(name, fallback)
|
||||
|
||||
|
||||
def normalize(source: dict[str, Any], raw_path: Path) -> dict[str, Any]:
|
||||
source_meta = source["meta"]
|
||||
results = []
|
||||
for result in source.get("results", []):
|
||||
requirement = str(result.get("requirement", ""))
|
||||
standards = []
|
||||
if requirement:
|
||||
standards.append(
|
||||
{
|
||||
"framework": source_meta.get("standard", "IEC 62443-3-3"),
|
||||
"version": source_meta.get("security_level", ""),
|
||||
"control": requirement,
|
||||
}
|
||||
)
|
||||
results.append(
|
||||
{
|
||||
"id": str(result["test_id"]),
|
||||
"title": str(result.get("description", result["test_id"])),
|
||||
"description": requirement,
|
||||
"status": STATUS_MAP.get(result.get("passed"), "error"),
|
||||
"severity": str(result.get("severity", "info")).lower(),
|
||||
"category": str(result.get("category", "")),
|
||||
"expected": str(result.get("expected", "")),
|
||||
"observed": str(result.get("actual", "")),
|
||||
"remediation": str(result.get("remediation", "")),
|
||||
"standards": standards,
|
||||
"evidence": [{"type": "text", "name": "Ansible observation", "value": str(result.get("actual", ""))}],
|
||||
}
|
||||
)
|
||||
|
||||
counts = dict.fromkeys(("passed", "failed", "errors", "skipped", "review"), 0)
|
||||
for result in results:
|
||||
counter = "errors" if result["status"] == "error" else result["status"]
|
||||
counts[counter] += 1
|
||||
scored = counts["passed"] + counts["failed"]
|
||||
|
||||
return {
|
||||
"schema_version": "1.0.0",
|
||||
"run": {
|
||||
"id": environment("CI_PIPELINE_ID", source_meta.get("timestamp", "local")),
|
||||
"started_at": source_meta["timestamp"],
|
||||
"source": "gitlab" if environment("CI") else "local",
|
||||
"pipeline_url": environment("CI_PIPELINE_URL"),
|
||||
"commit_sha": environment("CI_COMMIT_SHA"),
|
||||
},
|
||||
"project": {
|
||||
"id": environment("TEST_PROJECT_ID", "local"),
|
||||
"name": environment("TEST_PROJECT_NAME", "Local test project"),
|
||||
"environment": environment("TEST_ENVIRONMENT", "test"),
|
||||
"customer": environment("TEST_CUSTOMER"),
|
||||
"location": environment("TEST_LOCATION"),
|
||||
},
|
||||
"tool": {"id": "ansible", "name": "Ansible", "adapter_version": "1.0.0"},
|
||||
"target": {"id": source_meta["target"], "type": "managed_host", "groups": []},
|
||||
"summary": {
|
||||
"total": len(results),
|
||||
**counts,
|
||||
"score": round(counts["passed"] / scored * 100, 2) if scored else 0,
|
||||
},
|
||||
"results": results,
|
||||
"raw_artifacts": [str(raw_path)],
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("input", type=Path)
|
||||
parser.add_argument("output", type=Path)
|
||||
parser.add_argument("--schema", type=Path, default=Path("schemas/test-report.schema.json"))
|
||||
args = parser.parse_args()
|
||||
|
||||
source = json.loads(args.input.read_text(encoding="utf-8"))
|
||||
report = normalize(source, args.input)
|
||||
schema = json.loads(args.schema.read_text(encoding="utf-8"))
|
||||
Draft202012Validator(schema, format_checker=FormatChecker()).validate(report)
|
||||
|
||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.output.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
|
||||
print(f"Normalized {len(report['results'])} Ansible results to {args.output}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user