-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathapp.py
More file actions
139 lines (121 loc) · 4.76 KB
/
Copy pathapp.py
File metadata and controls
139 lines (121 loc) · 4.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
from flask import Flask, request, jsonify
from flask_cors import CORS # Import CORS
from cvss import CVSS2, CVSS3, CVSS4
import os
app = Flask(__name__)
CORS(app) # Enable CORS for all routes and origins
# Function to fix missing CVSS 4.0 Subsequent System Impact Metrics (SC, SI, SA)
def fix_cvss4_vector(vector):
if "SC:" not in vector:
vector += "/SC:N"
if "SI:" not in vector:
vector += "/SI:N"
if "SA:" not in vector:
vector += "/SA:N"
return vector
# CVSS risk severity label (CVSS v3/v4 rating scale)
def _cvss_risk_label(score):
if score == 0.0: return 'NONE'
if score < 4.0: return 'LOW'
if score < 7.0: return 'MEDIUM'
if score < 9.0: return 'HIGH'
return 'CRITICAL'
# OWASP Risk Rating Methodology helpers
# Vector format: (SL:5/M:5/O:5/S:5/ED:5/EE:5/A:5/ID:5/LC:5/LI:5/LAV:5/LAC:5/FD:5/RD:5/NC:5/PV:5)
_OWASP_LIKELIHOOD_KEYS = ['SL', 'M', 'O', 'S', 'ED', 'EE', 'A', 'ID']
_OWASP_IMPACT_KEYS = ['LC', 'LI', 'LAV', 'LAC', 'FD', 'RD', 'NC', 'PV']
_OWASP_ALL_KEYS = _OWASP_LIKELIHOOD_KEYS + _OWASP_IMPACT_KEYS
# Risk level label for a 0-9 score (matches OWASP Risk Rating Methodology)
_OWASP_CRITICALITY_MATRIX = {
('NOTE', 'NOTE'): 'NOTE',
('NOTE', 'LOW'): 'NOTE',
('NOTE', 'MEDIUM'): 'LOW',
('NOTE', 'HIGH'): 'MEDIUM',
('LOW', 'NOTE'): 'NOTE',
('LOW', 'LOW'): 'NOTE',
('LOW', 'MEDIUM'): 'LOW',
('LOW', 'HIGH'): 'MEDIUM',
('MEDIUM', 'NOTE'): 'LOW',
('MEDIUM', 'LOW'): 'LOW',
('MEDIUM', 'MEDIUM'): 'MEDIUM',
('MEDIUM', 'HIGH'): 'HIGH',
('HIGH', 'NOTE'): 'MEDIUM',
('HIGH', 'LOW'): 'MEDIUM',
('HIGH', 'MEDIUM'): 'HIGH',
('HIGH', 'HIGH'): 'CRITICAL',
}
def _owasp_risk_label(score):
if score == 0:
return 'NOTE'
if score < 3:
return 'LOW'
if score < 6:
return 'MEDIUM'
return 'HIGH'
def _parse_owasp_vector(vector):
cleaned = vector.strip().lstrip('(').rstrip(')')
parts = cleaned.split('/')
if len(parts) != 16:
raise ValueError(f"Expected 16 factors, got {len(parts)}")
values = {}
for part in parts:
if ':' not in part:
raise ValueError(f"Invalid factor format: '{part}'")
key, raw = part.split(':', 1)
key = key.upper()
try:
num = float(raw)
except ValueError:
raise ValueError(f"Non-numeric value for factor {key}: '{raw}'")
if not (0 <= num <= 9):
raise ValueError(f"Value for {key} must be between 0 and 9, got {num}")
values[key] = num
for key in _OWASP_ALL_KEYS:
if key not in values:
raise ValueError(f"Missing required factor: {key}")
return values
@app.route('/owasp', methods=['GET'])
def calculate_owasp():
vector = request.args.get('vector', '')
version = os.getenv('VERSION', 'unknown')
if not vector:
return jsonify({"error": "No OWASP vector provided"}), 400
try:
values = _parse_owasp_vector(vector)
except ValueError as e:
return jsonify({"error": str(e)}), 400
likelihood_score = round(sum(values[k] for k in _OWASP_LIKELIHOOD_KEYS) / 8, 3)
impact_score = round(sum(values[k] for k in _OWASP_IMPACT_KEYS) / 8, 3)
likelihood_label = _owasp_risk_label(likelihood_score)
impact_label = _owasp_risk_label(impact_score)
risk_severity = _OWASP_CRITICALITY_MATRIX.get((likelihood_label, impact_label), 'NOTE')
return jsonify({
"owasp_vector": vector,
"likelihood_score": likelihood_score,
"impact_score": impact_score,
"likelihood_label": likelihood_label,
"impact_label": impact_label,
"risk_severity": risk_severity,
"api_version": version,
})
@app.route('/cvss', methods=['GET'])
def calculate_cvss():
vector = request.args.get('vector', '')
version = os.getenv('VERSION', 'unknown')
if not vector:
return jsonify({"error": "No CVSS vector provided"}), 400
try:
if vector.startswith("CVSS:2."):
score = CVSS2(vector).scores()[0] # Base Score
elif vector.startswith("CVSS:3."):
score = CVSS3(vector).scores()[0] # Base Score
elif vector.startswith("CVSS:4."):
vector = fix_cvss4_vector(vector) # Add SC/SI/SA if not provided
score = CVSS4(vector).scores()[0] # Base Score
else:
return jsonify({"error": "Unsupported or invalid CVSS vector string"}), 400
return jsonify({"cvss_vector": vector, "cvss_score": score, "risk_severity": _cvss_risk_label(score), "api_version": version})
except Exception as e:
return jsonify({"error": str(e)}), 400
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=False)