Skip to content

Commit 312124c

Browse files
authored
Merge branch 'AllDotPy:master' into master
2 parents a97b666 + d2d8afd commit 312124c

File tree

5 files changed

+151
-4
lines changed

5 files changed

+151
-4
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,4 +211,4 @@ __marimo__/
211211
docs/*
212212
rules/*
213213
tests/*
214-
# valkyrie/*
214+
valkyrie/repositories/*

pyproject.toml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@ maintainers = [{ name = "#Einswilli", email = "[email protected]" }]
99
requires-python = ">=3.10"
1010
keywords = [
1111
"valkyrie", "CI/CD", "security scaner", "guardian",
12-
"pipelines"
12+
"pipelines", "iam", "secrets"
13+
]
14+
dependencies = [
15+
"toml>=0.10.2",
16+
"tomli>=2.2.1",
17+
"yamllib>=0.0.1",
1318
]
14-
dependencies = []
1519

1620
[project.optional-dependencies]
1721
dev = [
@@ -38,4 +42,4 @@ packages = ["valkyrie"]
3842

3943
[build-system]
4044
requires = ["setuptools>=61.0", "wheel"]
41-
build-backend = "setuptools.build_meta"
45+
build-backend = "setuptools.build_meta"

valkyrie/core/formatters/base.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from abc import ABC, abstractmethod
2+
3+
from valkyrie.core.types import ScanResult
4+
5+
####
6+
## SCAN RESULT FORMATTERS BASE CLASS
7+
#####
8+
class ResultFormatter(ABC):
9+
"""Abstract base class for result formatting"""
10+
11+
@abstractmethod
12+
def format(self, result: ScanResult) -> str:
13+
"""Format scan result to string"""
14+
pass

valkyrie/core/formatters/sarif.py

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
"""
2+
Valkyrie - SARIF Scan result Foormatter
3+
"""
4+
5+
import json
6+
from typing import List, Dict, Any
7+
from valkyrie.core.types import (
8+
ScanResult, ScanStatus, SecurityFinding,
9+
SeverityLevel
10+
)
11+
from .base import ResultFormatter
12+
13+
14+
####
15+
## SARIF SCAN RESUT FORMATTER
16+
#####
17+
class SARIFFormatter(ResultFormatter):
18+
"""SARIF (Static Analysis Results Interchange Format) formatter"""
19+
20+
def format(self, result: ScanResult) -> str:
21+
"""Format results as SARIF JSON"""
22+
23+
sarif_report = {
24+
"version": "2.1.0",
25+
"$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0.json",
26+
"runs": [
27+
{
28+
"tool": {
29+
"driver": {
30+
"name": "Valkyrie",
31+
"version": "1.0.0",
32+
"informationUri": "https://github.com/valkyrie-scanner/valkyrie",
33+
"rules": self._generate_rules(result.findings)
34+
}
35+
},
36+
"results": self._generate_results(result.findings),
37+
"invocations": [
38+
{
39+
"executionSuccessful": result.status == ScanStatus.COMPLETED,
40+
"startTimeUtc": result.timestamp.isoformat() + "Z",
41+
"endTimeUtc": result.timestamp.isoformat() + "Z"
42+
}
43+
]
44+
}
45+
]
46+
}
47+
48+
return json.dumps(sarif_report, indent=2)
49+
50+
def _generate_rules(
51+
self,
52+
findings: List[SecurityFinding]
53+
) -> List[Dict[str, Any]]:
54+
"""Generate SARIF rules from findings"""
55+
56+
rules = {}
57+
58+
for finding in findings:
59+
if finding.rule_id not in rules:
60+
rules[finding.rule_id] = {
61+
"id": finding.rule_id,
62+
"shortDescription": {"text": finding.title},
63+
"fullDescription": {"text": finding.description},
64+
"help": {
65+
"text": finding.remediation_advice or "Review and fix the security issue"
66+
},
67+
"properties": {
68+
"security-severity": self._severity_to_score(finding.severity)
69+
}
70+
}
71+
72+
return list(rules.values())
73+
74+
def _generate_results(
75+
self,
76+
findings: List[SecurityFinding]
77+
) -> List[Dict[str, Any]]:
78+
"""Generate SARIF results from findings"""
79+
80+
results = []
81+
82+
for finding in findings:
83+
result = {
84+
"ruleId": finding.rule_id,
85+
"message": {"text": finding.description},
86+
"level": self._severity_to_level(finding.severity),
87+
"locations": [
88+
{
89+
"physicalLocation": {
90+
"artifactLocation": {
91+
"uri": str(finding.location.file_path)
92+
},
93+
"region": {
94+
"startLine": finding.location.line_number,
95+
"startColumn": finding.location.column_start,
96+
"endColumn": finding.location.column_end
97+
}
98+
}
99+
}
100+
]
101+
}
102+
results.append(result)
103+
104+
return results
105+
106+
def _severity_to_level(self, severity: SeverityLevel) -> str:
107+
"""Convert severity to SARIF level"""
108+
109+
mapping = {
110+
SeverityLevel.CRITICAL: "error",
111+
SeverityLevel.HIGH: "error",
112+
SeverityLevel.MEDIUM: "warning",
113+
SeverityLevel.LOW: "note",
114+
SeverityLevel.INFO: "note"
115+
}
116+
return mapping.get(severity, "note")
117+
118+
def _severity_to_score(self, severity: SeverityLevel) -> str:
119+
"""Convert severity to security score"""
120+
121+
mapping = {
122+
SeverityLevel.CRITICAL: "9.0",
123+
SeverityLevel.HIGH: "7.0",
124+
SeverityLevel.MEDIUM: "5.0",
125+
SeverityLevel.LOW: "3.0",
126+
SeverityLevel.INFO: "1.0"
127+
}
128+
return mapping.get(severity, "1.0")

valkyrie/plugins/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""
2+
Valkyrie - Plugin module.
23
"""
34
from pathlib import Path
45
from typing import List, Set, Dict, Any, Optional

0 commit comments

Comments
 (0)