rai-check-core 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rai_check_core-0.0.1/.gitignore +55 -0
- rai_check_core-0.0.1/LICENSE +21 -0
- rai_check_core-0.0.1/PKG-INFO +64 -0
- rai_check_core-0.0.1/README.md +20 -0
- rai_check_core-0.0.1/examples/ci-gate.yml +94 -0
- rai_check_core-0.0.1/pyproject.toml +39 -0
- rai_check_core-0.0.1/src/rai_audit/core/__init__.py +43 -0
- rai_check_core-0.0.1/src/rai_audit/core/ci_formats.py +114 -0
- rai_check_core-0.0.1/src/rai_audit/core/cli.py +374 -0
- rai_check_core-0.0.1/src/rai_audit/core/config.py +458 -0
- rai_check_core-0.0.1/src/rai_audit/core/engine.py +22 -0
- rai_check_core-0.0.1/src/rai_audit/core/evidence.py +148 -0
- rai_check_core-0.0.1/src/rai_audit/core/export_commands.py +60 -0
- rai_check_core-0.0.1/src/rai_audit/core/findings.py +164 -0
- rai_check_core-0.0.1/src/rai_audit/core/history.py +317 -0
- rai_check_core-0.0.1/src/rai_audit/core/model_card.py +208 -0
- rai_check_core-0.0.1/src/rai_audit/core/monitoring.py +170 -0
- rai_check_core-0.0.1/src/rai_audit/core/privacy.py +74 -0
- rai_check_core-0.0.1/src/rai_audit/core/registry.py +35 -0
- rai_check_core-0.0.1/src/rai_audit/core/report.py +355 -0
- rai_check_core-0.0.1/src/rai_audit/core/reproducibility.py +173 -0
- rai_check_core-0.0.1/src/rai_audit/core/schemas.py +254 -0
- rai_check_core-0.0.1/src/rai_audit/core/scoring.py +74 -0
- rai_check_core-0.0.1/src/rai_audit/core/standards.py +223 -0
- rai_check_core-0.0.1/tests/test_config.py +154 -0
- rai_check_core-0.0.1/tests/test_evidence.py +37 -0
- rai_check_core-0.0.1/tests/test_findings.py +122 -0
- rai_check_core-0.0.1/tests/test_history.py +115 -0
- rai_check_core-0.0.1/tests/test_model_card.py +118 -0
- rai_check_core-0.0.1/tests/test_monitoring.py +100 -0
- rai_check_core-0.0.1/tests/test_schemas.py +61 -0
- rai_check_core-0.0.1/tests/test_scoring.py +59 -0
- rai_check_core-0.0.1/tests/test_standards.py +91 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
|
|
8
|
+
# Virtual environments
|
|
9
|
+
.venv/
|
|
10
|
+
venv/
|
|
11
|
+
env/
|
|
12
|
+
|
|
13
|
+
# Packaging / build
|
|
14
|
+
build/
|
|
15
|
+
dist/
|
|
16
|
+
*.egg-info/
|
|
17
|
+
*.egg
|
|
18
|
+
pip-wheel-metadata/
|
|
19
|
+
*.whl
|
|
20
|
+
|
|
21
|
+
# Test / coverage / type checking
|
|
22
|
+
.pytest_cache/
|
|
23
|
+
.coverage
|
|
24
|
+
.coverage.*
|
|
25
|
+
htmlcov/
|
|
26
|
+
.mypy_cache/
|
|
27
|
+
.ruff_cache/
|
|
28
|
+
.hypothesis/
|
|
29
|
+
|
|
30
|
+
# Audit runtime artifacts
|
|
31
|
+
.rai-check/
|
|
32
|
+
*_audit_report.html
|
|
33
|
+
*_audit_report.md
|
|
34
|
+
*_audit_report.json
|
|
35
|
+
loan_audit_report.*
|
|
36
|
+
|
|
37
|
+
# Environment / secrets
|
|
38
|
+
.env
|
|
39
|
+
.env.*
|
|
40
|
+
!.env.example
|
|
41
|
+
|
|
42
|
+
# IDE / editor
|
|
43
|
+
.vscode/
|
|
44
|
+
.idea/
|
|
45
|
+
*.swp
|
|
46
|
+
*.swo
|
|
47
|
+
*~
|
|
48
|
+
|
|
49
|
+
# OS
|
|
50
|
+
.DS_Store
|
|
51
|
+
Thumbs.db
|
|
52
|
+
Desktop.ini
|
|
53
|
+
|
|
54
|
+
# Typical dev data
|
|
55
|
+
dev-data/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sai Teja Erukude
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: rai-check-core
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Shared audit engine, findings, reports, and CLI for the RAI Check Kit
|
|
5
|
+
Author: Sai Teja Erukude
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Sai Teja Erukude
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Keywords: audit,fairness,responsible-ai,trustworthy-ai
|
|
29
|
+
Classifier: Development Status :: 4 - Beta
|
|
30
|
+
Classifier: Intended Audience :: Developers
|
|
31
|
+
Classifier: Intended Audience :: Science/Research
|
|
32
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
33
|
+
Classifier: Programming Language :: Python :: 3
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
37
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
38
|
+
Requires-Python: >=3.10
|
|
39
|
+
Requires-Dist: jsonschema>=4.0
|
|
40
|
+
Requires-Dist: pyyaml>=6.0
|
|
41
|
+
Requires-Dist: rich>=13.0
|
|
42
|
+
Requires-Dist: typer>=0.12
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
|
|
45
|
+
# rai-check-core
|
|
46
|
+
|
|
47
|
+
Shared audit findings, evidence manifests, history, and reporting APIs.
|
|
48
|
+
|
|
49
|
+
Configured runs can include `standards-coverage` in `audit.report_formats` to
|
|
50
|
+
write a standalone JSON evidence-coverage artifact. History exports are available
|
|
51
|
+
from the CLI:
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
rai-check export standards-coverage audit-run.json
|
|
55
|
+
rai-check export history-dashboard --directory .rai-check/history
|
|
56
|
+
rai-check export eu-post-market --directory .rai-check/history
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Standards coverage and EU AI Act-oriented post-market reports summarize recorded
|
|
60
|
+
evidence. They do not make a compliance claim.
|
|
61
|
+
|
|
62
|
+
Configs, audit reports, evidence manifests, agent traces, and LLM suites use
|
|
63
|
+
versioned JSON Schemas. Existing unversioned inputs are migrated to schema version
|
|
64
|
+
`1.0` during loading.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# rai-check-core
|
|
2
|
+
|
|
3
|
+
Shared audit findings, evidence manifests, history, and reporting APIs.
|
|
4
|
+
|
|
5
|
+
Configured runs can include `standards-coverage` in `audit.report_formats` to
|
|
6
|
+
write a standalone JSON evidence-coverage artifact. History exports are available
|
|
7
|
+
from the CLI:
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
rai-check export standards-coverage audit-run.json
|
|
11
|
+
rai-check export history-dashboard --directory .rai-check/history
|
|
12
|
+
rai-check export eu-post-market --directory .rai-check/history
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Standards coverage and EU AI Act-oriented post-market reports summarize recorded
|
|
16
|
+
evidence. They do not make a compliance claim.
|
|
17
|
+
|
|
18
|
+
Configs, audit reports, evidence manifests, agent traces, and LLM suites use
|
|
19
|
+
versioned JSON Schemas. Existing unversioned inputs are migrated to schema version
|
|
20
|
+
`1.0` during loading.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Example: RAI Check Gate in GitHub Actions
|
|
2
|
+
#
|
|
3
|
+
# Copy this file into your repository at .github/workflows/rai-check-gate.yml
|
|
4
|
+
# and adjust the paths and thresholds to match your project.
|
|
5
|
+
#
|
|
6
|
+
# This workflow:
|
|
7
|
+
# 1. Installs rai-check-ml
|
|
8
|
+
# 2. Runs your audit script to produce an audit-run.json
|
|
9
|
+
# 3. Calls `rai-check gate` — exits 1 (blocking deployment) on failure
|
|
10
|
+
# 4. Exports a model card for the audit artifact
|
|
11
|
+
# 5. Uploads the report as a workflow artifact
|
|
12
|
+
|
|
13
|
+
name: RAI Check Gate
|
|
14
|
+
|
|
15
|
+
on:
|
|
16
|
+
push:
|
|
17
|
+
branches: [main]
|
|
18
|
+
pull_request:
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
rai-check:
|
|
22
|
+
name: Responsible AI Audit
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
|
|
28
|
+
- uses: actions/setup-python@v5
|
|
29
|
+
with:
|
|
30
|
+
python-version: "3.12"
|
|
31
|
+
|
|
32
|
+
- name: Install rai-check-ml
|
|
33
|
+
run: pip install rai-check-ml
|
|
34
|
+
|
|
35
|
+
# ── Run your audit script ───────────────────────────────────────────────
|
|
36
|
+
# Replace this step with your actual audit script.
|
|
37
|
+
# The script must produce a JSON file via report.to_json("audit-run.json").
|
|
38
|
+
#
|
|
39
|
+
# Minimal example (inline):
|
|
40
|
+
- name: Run audit
|
|
41
|
+
run: |
|
|
42
|
+
python - <<'EOF'
|
|
43
|
+
import numpy as np
|
|
44
|
+
import pandas as pd
|
|
45
|
+
from rai_audit.ml import ClassificationAudit
|
|
46
|
+
|
|
47
|
+
rng = np.random.default_rng(42)
|
|
48
|
+
n = 500
|
|
49
|
+
y_true = rng.integers(0, 2, n)
|
|
50
|
+
y_pred = y_true.copy()
|
|
51
|
+
# Inject synthetic bias: flip 30 % of predictions for group B
|
|
52
|
+
group = pd.DataFrame({"gender": rng.choice(["A", "B"], n)})
|
|
53
|
+
flip_mask = (group["gender"] == "B") & (rng.random(n) < 0.30)
|
|
54
|
+
y_pred[flip_mask] = 1 - y_pred[flip_mask]
|
|
55
|
+
|
|
56
|
+
report = ClassificationAudit(
|
|
57
|
+
y_true=y_true,
|
|
58
|
+
y_pred=y_pred,
|
|
59
|
+
sensitive_features=group,
|
|
60
|
+
project_name="Example Classifier",
|
|
61
|
+
persist=False,
|
|
62
|
+
).run()
|
|
63
|
+
report.to_json("audit-run.json")
|
|
64
|
+
EOF
|
|
65
|
+
|
|
66
|
+
# ── Gate check ─────────────────────────────────────────────────────────
|
|
67
|
+
# Exit code 1 if any CRITICAL findings exist.
|
|
68
|
+
# Remove --fail-on-critical to allow critical findings through.
|
|
69
|
+
- name: RAI Check gate
|
|
70
|
+
run: |
|
|
71
|
+
rai-check gate audit-run.json \
|
|
72
|
+
--fail-on-critical \
|
|
73
|
+
--output-json gate-result.json
|
|
74
|
+
|
|
75
|
+
# ── Export model card (always, even on gate failure) ───────────────────
|
|
76
|
+
- name: Export model card
|
|
77
|
+
if: always()
|
|
78
|
+
run: |
|
|
79
|
+
rai-check export model-card audit-run.json \
|
|
80
|
+
--model-name "Example Classifier" \
|
|
81
|
+
--model-version "${{ github.sha }}" \
|
|
82
|
+
--output model-card.md
|
|
83
|
+
|
|
84
|
+
# ── Upload artifacts ───────────────────────────────────────────────────
|
|
85
|
+
- name: Upload audit artifacts
|
|
86
|
+
if: always()
|
|
87
|
+
uses: actions/upload-artifact@v4
|
|
88
|
+
with:
|
|
89
|
+
name: rai-check-report-${{ github.run_number }}
|
|
90
|
+
path: |
|
|
91
|
+
audit-run.json
|
|
92
|
+
gate-result.json
|
|
93
|
+
model-card.md
|
|
94
|
+
retention-days: 90
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "rai-check-core"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
description = "Shared audit engine, findings, reports, and CLI for the RAI Check Kit"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { file = "LICENSE" }
|
|
11
|
+
authors = [{ name = "Sai Teja Erukude" }]
|
|
12
|
+
requires-python = ">=3.10"
|
|
13
|
+
keywords = ["responsible-ai", "audit", "fairness", "trustworthy-ai"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Intended Audience :: Science/Research",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
24
|
+
]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"typer>=0.12",
|
|
27
|
+
"rich>=13.0",
|
|
28
|
+
"pyyaml>=6.0",
|
|
29
|
+
"jsonschema>=4.0",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.scripts]
|
|
33
|
+
rai-check = "rai_audit.core.cli:app"
|
|
34
|
+
|
|
35
|
+
[tool.hatch.build.targets.wheel]
|
|
36
|
+
packages = ["src/rai_audit"]
|
|
37
|
+
|
|
38
|
+
[tool.hatch.build.targets.wheel.sources]
|
|
39
|
+
"src" = ""
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from rai_audit.core.evidence import build_evidence_manifest, load_evidence_manifest
|
|
2
|
+
from rai_audit.core.findings import (
|
|
3
|
+
AuditFinding,
|
|
4
|
+
AuditReport,
|
|
5
|
+
CategoryRisk,
|
|
6
|
+
RemediationEffort,
|
|
7
|
+
RiskLevel,
|
|
8
|
+
Severity,
|
|
9
|
+
)
|
|
10
|
+
from rai_audit.core.history import build_history_summary, write_history_dashboard
|
|
11
|
+
from rai_audit.core.monitoring import build_eu_ai_act_post_market_report
|
|
12
|
+
from rai_audit.core.schemas import (
|
|
13
|
+
SCHEMA_VERSION,
|
|
14
|
+
SchemaDocumentError,
|
|
15
|
+
get_schema,
|
|
16
|
+
migrate_document,
|
|
17
|
+
prepare_document,
|
|
18
|
+
validate_document,
|
|
19
|
+
)
|
|
20
|
+
from rai_audit.core.scoring import compute_risk_matrix
|
|
21
|
+
from rai_audit.core.standards import build_standards_coverage_report
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"AuditFinding",
|
|
25
|
+
"AuditReport",
|
|
26
|
+
"CategoryRisk",
|
|
27
|
+
"RemediationEffort",
|
|
28
|
+
"RiskLevel",
|
|
29
|
+
"Severity",
|
|
30
|
+
"SCHEMA_VERSION",
|
|
31
|
+
"SchemaDocumentError",
|
|
32
|
+
"build_evidence_manifest",
|
|
33
|
+
"build_eu_ai_act_post_market_report",
|
|
34
|
+
"build_history_summary",
|
|
35
|
+
"build_standards_coverage_report",
|
|
36
|
+
"compute_risk_matrix",
|
|
37
|
+
"get_schema",
|
|
38
|
+
"load_evidence_manifest",
|
|
39
|
+
"migrate_document",
|
|
40
|
+
"prepare_document",
|
|
41
|
+
"validate_document",
|
|
42
|
+
"write_history_dashboard",
|
|
43
|
+
]
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import xml.etree.ElementTree as ET
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from rai_audit.core.findings import AuditReport, Severity
|
|
8
|
+
|
|
9
|
+
_SARIF_LEVEL = {
|
|
10
|
+
Severity.CRITICAL: "error",
|
|
11
|
+
Severity.HIGH: "error",
|
|
12
|
+
Severity.MEDIUM: "warning",
|
|
13
|
+
Severity.LOW: "note",
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def render_sarif(report: AuditReport) -> str:
|
|
18
|
+
"""Render active findings as SARIF 2.1.0 for CI and code-scanning systems."""
|
|
19
|
+
active = [
|
|
20
|
+
finding
|
|
21
|
+
for finding in report.findings
|
|
22
|
+
if finding.severity not in {Severity.PASSED, Severity.INFO}
|
|
23
|
+
]
|
|
24
|
+
rules = {
|
|
25
|
+
finding.check_id: {
|
|
26
|
+
"id": finding.check_id,
|
|
27
|
+
"name": finding.title,
|
|
28
|
+
"shortDescription": {"text": finding.description},
|
|
29
|
+
"properties": {
|
|
30
|
+
"category": finding.category,
|
|
31
|
+
"standards_refs": finding.standards_refs,
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
for finding in active
|
|
35
|
+
}
|
|
36
|
+
results = [
|
|
37
|
+
{
|
|
38
|
+
"ruleId": finding.check_id,
|
|
39
|
+
"level": _SARIF_LEVEL.get(finding.severity, "note"),
|
|
40
|
+
"message": {"text": finding.description},
|
|
41
|
+
"properties": {
|
|
42
|
+
"category": finding.category,
|
|
43
|
+
"severity": finding.severity.value,
|
|
44
|
+
"evidence": finding.evidence,
|
|
45
|
+
"recommendation": finding.recommendation,
|
|
46
|
+
"standards_refs": finding.standards_refs,
|
|
47
|
+
},
|
|
48
|
+
}
|
|
49
|
+
for finding in active
|
|
50
|
+
]
|
|
51
|
+
document: dict[str, Any] = {
|
|
52
|
+
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
|
|
53
|
+
"version": "2.1.0",
|
|
54
|
+
"runs": [
|
|
55
|
+
{
|
|
56
|
+
"tool": {
|
|
57
|
+
"driver": {
|
|
58
|
+
"name": "rai-check",
|
|
59
|
+
"informationUri": "https://pypi.org/project/rai-check-kit/",
|
|
60
|
+
"rules": list(rules.values()),
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"results": results,
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
}
|
|
67
|
+
return json.dumps(document, indent=2)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def render_junit(report: AuditReport) -> str:
|
|
71
|
+
"""Render findings as JUnit XML for CI test-report systems."""
|
|
72
|
+
active = [
|
|
73
|
+
finding
|
|
74
|
+
for finding in report.findings
|
|
75
|
+
if finding.severity not in {Severity.PASSED, Severity.INFO}
|
|
76
|
+
]
|
|
77
|
+
suite = ET.Element(
|
|
78
|
+
"testsuite",
|
|
79
|
+
{
|
|
80
|
+
"name": report.project_name,
|
|
81
|
+
"tests": str(len(report.findings)),
|
|
82
|
+
"failures": str(len(active)),
|
|
83
|
+
},
|
|
84
|
+
)
|
|
85
|
+
for finding in report.findings:
|
|
86
|
+
case = ET.SubElement(
|
|
87
|
+
suite,
|
|
88
|
+
"testcase",
|
|
89
|
+
{
|
|
90
|
+
"classname": finding.category or "General",
|
|
91
|
+
"name": finding.check_id,
|
|
92
|
+
},
|
|
93
|
+
)
|
|
94
|
+
if finding.severity not in {Severity.PASSED, Severity.INFO}:
|
|
95
|
+
failure = ET.SubElement(
|
|
96
|
+
case,
|
|
97
|
+
"failure",
|
|
98
|
+
{
|
|
99
|
+
"message": finding.title,
|
|
100
|
+
"type": finding.severity.value,
|
|
101
|
+
},
|
|
102
|
+
)
|
|
103
|
+
failure.text = finding.description
|
|
104
|
+
output = ET.SubElement(case, "system-out")
|
|
105
|
+
output.text = json.dumps(
|
|
106
|
+
{
|
|
107
|
+
"evidence": finding.evidence,
|
|
108
|
+
"recommendation": finding.recommendation,
|
|
109
|
+
"standards_refs": finding.standards_refs,
|
|
110
|
+
},
|
|
111
|
+
default=str,
|
|
112
|
+
)
|
|
113
|
+
ET.indent(suite)
|
|
114
|
+
return ET.tostring(suite, encoding="unicode")
|