Replace inventory-counting risk with bounded workload scoring and separate confidence
This commit is contained in:
55
samsara/analyzers/policy.py
Normal file
55
samsara/analyzers/policy.py
Normal file
@@ -0,0 +1,55 @@
|
||||
"""Data-only scoring policy. No executable expressions or arbitrary regex rules."""
|
||||
import copy
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
DEFAULT_POLICY = Path(__file__).resolve().parents[1] / 'assets' / 'scoring_policy.json'
|
||||
RULES = {'installed_workload_role', 'running_application_service', 'attributed_application_listener',
|
||||
'observed_application_clients', 'configured_business_share', 'observed_smb_transport',
|
||||
'observed_business_share_clients', 'configured_custom_schedule', 'verified_domain_controller'}
|
||||
|
||||
|
||||
def load_policy(source=None):
|
||||
if source is not None and not isinstance(source,(dict,str,Path)):
|
||||
raise ValueError('A scoring policy must be a JSON object or file path.')
|
||||
if isinstance(source, dict):
|
||||
policy = copy.deepcopy(source)
|
||||
else:
|
||||
path = Path(source) if source is not None else DEFAULT_POLICY
|
||||
try:
|
||||
if path.stat().st_size > 65536:
|
||||
raise ValueError('Scoring policy exceeds 64 KiB.')
|
||||
policy = json.loads(path.read_text(encoding='utf-8'))
|
||||
except (OSError, UnicodeError, json.JSONDecodeError, RecursionError):
|
||||
raise ValueError('Unable to read a valid UTF-8 scoring policy JSON file.') from None
|
||||
try:
|
||||
limits, weights = policy['thresholds'], policy['coverage_weights']
|
||||
valid = (
|
||||
set(policy) == {'model_version','name','scores','thresholds','coverage_weights','partial_check_credit'}
|
||||
and policy['model_version'] == '2.0' and isinstance(policy['name'],str) and 0<len(policy['name'])<=120
|
||||
and set(policy['scores']) == RULES
|
||||
and all(type(value) is int and 0<=value<=100 for value in policy['scores'].values())
|
||||
and set(limits) == {'low','medium','high','critical'}
|
||||
and all(type(value) is int for value in limits.values())
|
||||
and 1 == limits['low'] < limits['medium'] < limits['high'] < limits['critical'] <= 100
|
||||
and set(weights) == {'system','roles','services','network','tasks','software','storage','shares','certificates'}
|
||||
and all(type(weight) is int and 1<=weight<=10 for weight in weights.values())
|
||||
and type(policy['partial_check_credit']) in (int,float) and 0<=policy['partial_check_credit']<1
|
||||
)
|
||||
except (KeyError,TypeError,AttributeError):
|
||||
valid = False
|
||||
if not valid:
|
||||
raise ValueError('Invalid scoring policy. Use the bundled v2 policy structure, integer scores 0–100, ordered thresholds and positive coverage weights.')
|
||||
return policy
|
||||
|
||||
|
||||
def policy_id(policy):
|
||||
return hashlib.sha256(json.dumps(policy, sort_keys=True).encode('utf-8')).hexdigest()
|
||||
|
||||
|
||||
def risk_level(score, policy):
|
||||
for name in ('critical','high','medium','low'):
|
||||
if score >= policy['thresholds'][name]:
|
||||
return name
|
||||
return 'informational'
|
||||
Reference in New Issue
Block a user