Source code for synapse.lib.stormlib.infosec

import math
import decimal

import synapse.exc as s_exc
import synapse.data as s_data
import synapse.common as s_common

import synapse.lib.chop as s_chop
import synapse.lib.coro as s_coro
import synapse.lib.config as s_config
import synapse.lib.stormtypes as s_stormtypes

import synapse.lookup.cvss as s_cvss


# used as a reference implementation:
# https://www.first.org/cvss/calculator/cvsscalc31.js

CVSS31 = {
  'AV': {'N': 0.85, 'A': 0.62, 'L': 0.55, 'P': 0.2},
  'AC': {'H': 0.44, 'L': 0.77},
  'PR': {'U': {'N': 0.85, 'L': 0.62, 'H': 0.27},  # These values are used if Scope is Unchanged
         'C': {'N': 0.85, 'L': 0.68, 'H': 0.5}},  # These values are used if Scope is Changed
  'UI': {'N': 0.85, 'R': 0.62},
  'S': {'U': 6.42, 'C': 7.52},
  'C': {'N': 0, 'L': 0.22, 'H': 0.56},
  'I': {'N': 0, 'L': 0.22, 'H': 0.56},
  'A': {'N': 0, 'L': 0.22, 'H': 0.56},
  'E': {'X': 1, 'U': 0.91, 'P': 0.94, 'F': 0.97, 'H': 1},
  'RL': {'X': 1, 'O': 0.95, 'T': 0.96, 'W': 0.97, 'U': 1},
  'RC': {'X': 1, 'U': 0.92, 'R': 0.96, 'C': 1},
  'CR': {'X': 1, 'L': 0.5, 'M': 1, 'H': 1.5},
  'IR': {'X': 1, 'L': 0.5, 'M': 1, 'H': 1.5},
  'AR': {'X': 1, 'L': 0.5, 'M': 1, 'H': 1.5},
}

vect2prop = {
    'AV': 'cvss:av',
    'AC': 'cvss:ac',
    'PR': 'cvss:pr',
    'UI': 'cvss:ui',
    'S': 'cvss:s',
    'C': 'cvss:c',
    'I': 'cvss:i',
    'A': 'cvss:a',
    'E': 'cvss:e',
    'RL': 'cvss:rl',
    'RC': 'cvss:rc',
    'CR': 'cvss:cr',
    'IR': 'cvss:ir',
    'AR': 'cvss:ar',
    'MS': 'cvss:ms',
    'MC': 'cvss:mc',
    'MI': 'cvss:mi',
    'MA': 'cvss:ma',
    'MAV': 'cvss:mav',
    'MAC': 'cvss:mac',
    'MPR': 'cvss:mpr',
    'MUI': 'cvss:mui',
}

CTX = decimal.Context(rounding=decimal.ROUND_HALF_UP)

[docs] def CVSS2_round(x): d = decimal.Decimal(str(x)) return float(d.quantize(decimal.Decimal('0.1'), context=CTX))
[docs] def CVSS3_0_round(x): ''' Round up to the nearest one decimal place. From the JS reference implementation: https://www.first.org/cvss/calculator/cvsscalc30.js ''' return (math.ceil(x * 10) / 10.0)
[docs] def CVSS3_1_round(x): ''' Round up to the nearest one decimal place. From the JS reference implementation: https://www.first.org/cvss/calculator/cvsscalc31.js ''' i = int(x * 100000) if i % 10000 == 0: return i / 100000 return (math.floor(i / 10000) + 1) / 10.0
CVSS_ROUND = { s_cvss.cvss2: CVSS2_round, s_cvss.cvss3_0: CVSS3_0_round, s_cvss.cvss3_1: CVSS3_1_round, }
[docs] def CVSS_get_coefficients(vdict, vers): ret = {} metrics = s_cvss.metrics[vers] undefined = s_cvss.undefined[vers] Scope = None ModifiedScope = None if vers in (s_cvss.cvss3_0, s_cvss.cvss3_1): Scope = vdict['S'] ModifiedScope = vdict.get('MS', undefined) if ModifiedScope == undefined: ModifiedScope = Scope for metric in metrics: # These are special case lookups using the Scope/ModifiedScope value, # handle them later if metric in ('PR', 'MPR'): continue val = vdict.get(metric, undefined) ret[metric] = metrics[metric][2][val] # This block handles the special case lookups in CVSS3.0/CVSS3.1. Specifically: # - The extra Scope/ModifiedScope extra lookup for PR/MPR # - The "modified" values in the environment metrics that (if # present) modify the base metrics. if vers in (s_cvss.cvss3_0, s_cvss.cvss3_1): PR = vdict.get('PR') ret['PR'] = metrics['PR'][2][Scope][PR] MPR = vdict.get('MPR', undefined) if MPR == undefined: ret['MPR'] = ret['PR'] else: ret['MPR'] = metrics['MPR'][2][ModifiedScope][MPR] def set_modval(metric, base_metric): val = vdict.get(metric, undefined) if val == undefined: ret[metric] = ret[base_metric] else: ret[metric] = metrics[metric][2][val] set_modval('MAV', 'AV') set_modval('MAC', 'AC') set_modval('MUI', 'UI') set_modval('MC', 'C') set_modval('MI', 'I') set_modval('MA', 'A') return ret
[docs] def CVSS2_calc(vdict): round = CVSS_ROUND[s_cvss.cvss2] undefined = s_cvss.undefined[s_cvss.cvss2] coeffs = CVSS_get_coefficients(vdict, s_cvss.cvss2) OverallScore = None def _base(Impact): AccessVector = coeffs['AV'] AccessComplexity = coeffs['AC'] Authentication = coeffs['Au'] Exploitability = 20 * AccessVector * AccessComplexity * Authentication fImpact = 1.176 if Impact == 0: fImpact = 0.0 return ((0.6 * Impact) + (0.4 * Exploitability) - 1.5) * fImpact # Calculate the base score per the equation listed here: # https://www.first.org/cvss/v2/guide#3-2-1-Base-Equation ConfImpact = coeffs['C'] IntegImpact = coeffs['I'] AvailImpact = coeffs['A'] Impact = 10.41 * (1 - (1 - ConfImpact) * (1 - IntegImpact) * (1 - AvailImpact)) BaseScore = round(_base(Impact)) OverallScore = BaseScore # Calculate the temporal score per the equation listed here: # https://www.first.org/cvss/v2/guide#3-2-2-Temporal-Equation Exploitability = coeffs['E'] RemediationLevel = coeffs['RL'] ReportConfidence = coeffs['RC'] E = vdict.get('E', undefined) RL = vdict.get('RL', undefined) RC = vdict.get('RC', undefined) TemporalMetrics = (E, RL, RC) if TemporalMetrics.count(undefined) == len(TemporalMetrics): TemporalScore = None else: TemporalScore = round(BaseScore * Exploitability * RemediationLevel * ReportConfidence) ''' Calculate the environmental score per the equation listed here: https://www.first.org/cvss/v2/guide#3-2-3-Environmental-Equation ''' ConfImpact = coeffs['C'] IntegImpact = coeffs['I'] AvailImpact = coeffs['A'] ConfReq = coeffs['CR'] IntegReq = coeffs['IR'] AvailReq = coeffs['AR'] AdjustedImpact = min( 10.0, 10.41 * (1.0 - (1.0 - ConfImpact * ConfReq) * (1.0 - IntegImpact * IntegReq) * (1.0 - AvailImpact * AvailReq)) ) Exploitability = coeffs['E'] RemediationLevel = coeffs['RL'] ReportConfidence = coeffs['RC'] AdjustedTemporal = _base(AdjustedImpact) * Exploitability * RemediationLevel * ReportConfidence CollateralDamagePotential = coeffs['CDP'] TargetDistribution = coeffs['TD'] CDP = vdict.get('CDP', undefined) TD = vdict.get('TD', undefined) CR = vdict.get('CR', undefined) IR = vdict.get('IR', undefined) AR = vdict.get('AR', undefined) EnvironmentalMetrics = (CDP, TD, CR, IR, AR) if EnvironmentalMetrics.count(undefined) == len(EnvironmentalMetrics): EnvironmentalScore = None else: EnvironmentalScore = round((AdjustedTemporal + (10 - AdjustedTemporal) * CollateralDamagePotential) * TargetDistribution) if TemporalScore: OverallScore = TemporalScore if EnvironmentalScore: OverallScore = EnvironmentalScore return BaseScore, TemporalScore, EnvironmentalScore, OverallScore
def _CVSS3_calc(vdict, vers): round = CVSS_ROUND[vers] undefined = s_cvss.undefined[vers] coeffs = CVSS_get_coefficients(vdict, vers) # Calculate the base score per the equation listed here: # https://www.first.org/cvss/v3.1/specification-document#7-1-Base-Metrics-Equations # We use the CVSS3.1 spec because it's more clear than the CVSS3.0 spec # but the equations are the same. _Scope = vdict['S'] _ModifiedScope = vdict.get('MS', undefined) if _ModifiedScope == undefined: _ModifiedScope = _Scope Confidentiality = coeffs['C'] Integrity = coeffs['I'] Availability = coeffs['A'] ISS = 1 - ((1 - Confidentiality) * (1 - Integrity) * (1 - Availability)) if _Scope == 'U': Impact = 6.42 * ISS else: Impact = 7.52 * (ISS - 0.029) - 3.25 * (ISS - 0.02) ** 15 AttackVector = coeffs['AV'] AttackComplexity = coeffs['AC'] PrivilegesRequired = coeffs['PR'] UserInteraction = coeffs['UI'] Exploitability = 8.22 * AttackVector * AttackComplexity * PrivilegesRequired * UserInteraction if Impact <= 0: BaseScore = 0.0 else: if _Scope == 'U': BaseScore = round(min(Impact + Exploitability, 10.0)) else: BaseScore = round(min(1.08 * (Impact + Exploitability), 10.0)) OverallScore = BaseScore # Calculate the base score per the equation listed here: # https://www.first.org/cvss/v3.1/specification-document#7-2-Temporal-Metrics-Equations # We use the CVSS3.1 spec because it's more clear than the CVSS3.0 spec # but the equations are the same. E = vdict.get('E', undefined) RL = vdict.get('RL', undefined) RC = vdict.get('RC', undefined) TemporalMetrics = (E, RL, RC) if TemporalMetrics.count(undefined) == len(TemporalMetrics): TemporalScore = None else: ExploitCodeMaturity = coeffs['E'] RemediationLevel = coeffs['RL'] ReportConfidence = coeffs['RC'] TemporalScore = round(BaseScore * ExploitCodeMaturity * RemediationLevel * ReportConfidence) # Calculate the base score per the equation listed here: # https://www.first.org/cvss/v3.1/specification-document#7-3-Environmental-Metrics-Equations # We use the CVSS3.1 spec because it's more clear than the CVSS3.0 spec # but the equations are the same. ConfidentialityRequirement = coeffs['CR'] ModifiedConfidentiality = coeffs['MC'] IntegrityRequirement = coeffs['IR'] ModifiedIntegrity = coeffs['MI'] AvailabilityRequirement = coeffs['AR'] ModifiedAvailability = coeffs['MA'] MISS = min( 1.0 - ((1.0 - ConfidentialityRequirement * ModifiedConfidentiality) * (1.0 - IntegrityRequirement * ModifiedIntegrity) * (1.0 - AvailabilityRequirement * ModifiedAvailability) ), 0.915 ) if _ModifiedScope == 'U': ModifiedImpact = 6.42 * MISS else: ModifiedImpact = 7.52 * (MISS - 0.029) - 3.25 * (MISS * 0.9731 - 0.02) ** 13 ModifiedAttackVector = coeffs['MAV'] ModifiedAttackComplexity = coeffs['MAC'] ModifiedPrivilegesRequired = coeffs['MPR'] ModifiedUserInteraction = coeffs['MUI'] ModifiedExploitability = ( 8.22 * ModifiedAttackVector * ModifiedAttackComplexity * ModifiedPrivilegesRequired * ModifiedUserInteraction ) MAV = vdict.get('MAV', undefined) MAC = vdict.get('MAC', undefined) MPR = vdict.get('MPR', undefined) MUI = vdict.get('MUI', undefined) MS = vdict.get('MS', undefined) MC = vdict.get('MC', undefined) MI = vdict.get('MI', undefined) MA = vdict.get('MA', undefined) CR = vdict.get('CR', undefined) IR = vdict.get('IR', undefined) AR = vdict.get('AR', undefined) EnvironmentalMetrics = (MAV, MAC, MPR, MUI, MS, MC, MI, MA, CR, IR, AR) if EnvironmentalMetrics.count(undefined) == len(EnvironmentalMetrics): EnvironmentalScore = None elif ModifiedImpact <= 0: EnvironmentalScore = 0.0 else: ExploitCodeMaturity = coeffs['E'] RemediationLevel = coeffs['RL'] ReportConfidence = coeffs['RC'] if _ModifiedScope == 'U': EnvironmentalScore = round( round( min(ModifiedImpact + ModifiedExploitability, 10)) * ExploitCodeMaturity * RemediationLevel * ReportConfidence ) else: EnvironmentalScore = round( round( min(1.08 * (ModifiedImpact + ModifiedExploitability), 10)) * ExploitCodeMaturity * RemediationLevel * ReportConfidence ) if TemporalScore: OverallScore = TemporalScore if EnvironmentalScore: OverallScore = EnvironmentalScore return BaseScore, TemporalScore, EnvironmentalScore, OverallScore
[docs] def CVSS3_0_calc(vdict): return _CVSS3_calc(vdict, s_cvss.cvss3_0)
[docs] def CVSS3_1_calc(vdict): return _CVSS3_calc(vdict, s_cvss.cvss3_1)
CVSS_CALC = { s_cvss.cvss2: CVSS2_calc, s_cvss.cvss3_0: CVSS3_0_calc, s_cvss.cvss3_1: CVSS3_1_calc, }
[docs] def roundup(x): return (math.ceil(x * 10) / 10.0)
[docs] @s_stormtypes.registry.registerLib class MitreAttackFlowLib(s_stormtypes.Lib): ''' A Storm library which implements modeling MITRE ATT&CK Flow diagrams. ''' _storm_lib_path = ('infosec', 'mitre', 'attack', 'flow') _storm_locals = ( {'name': 'norm', 'desc': 'Normalize a MITRE ATT&CK Flow diagram in JSON format.', 'type': {'type': 'function', '_funcname': '_norm', 'args': ( {'name': 'flow', 'type': 'data', 'desc': 'The MITRE ATT&CK Flow diagram in JSON format to normalize (flatten and sort).'}, ), 'returns': {'type': 'dict', 'desc': 'The normalized MITRE ATT&CK Flow diagram.', } }}, {'name': 'ingest', 'desc': 'Ingest a MITRE ATT&CK Flow diagram in JSON format.', 'type': {'type': 'function', '_funcname': '_storm_query', 'args': ( {'name': 'flow', 'type': 'data', 'desc': 'The JSON data to ingest.'}, ), 'returns': {'type': ['node', 'null'], 'desc': 'The it:mitre:attack:flow node representing the ingested attack flow diagram.'}}}, ) _storm_query = ''' function ingest(flow) { // norm (validate, flatten, and sort) $flow = $lib.infosec.mitre.attack.flow.norm($flow) // Use the normed flow to generate the guid $guid = $lib.guid($flow) $objs_byid = ({}) $objs_bytype = ({}) for $obj in $flow.objects { $id = $obj.id $objs_byid.$id = $obj $type = $obj.type if (not $objs_bytype.$type) { $objs_bytype.$type = ([]) } $objs_bytype.$type.append($obj) } $attack_flow = $objs_bytype."attack-flow".0 $created_by = $objs_byid.($attack_flow.created_by_ref) ($ok, $name) = $lib.trycast(ps:name, $created_by.name) if (not $ok) { $lib.warn(`Error casting contact name to ou:name: {$created_by.name}`) return() } ($ok, $contact_information) = $lib.trycast(inet:email, $created_by.contact_information) if (not $ok) { $lib.warn(`Error casting contact information to inet:email: {$created_by.contact_information}`) return() } [ it:mitre:attack:flow = $guid :name ?= $attack_flow.name :data = $flow :created ?= $attack_flow.created :updated ?= $attack_flow.modified :author:user ?= $lib.user.iden :author:contact = {[ ps:contact = (attack-flow, $name, $contact_information) :name = $name :email = $contact_information ]} ] return($node) } ''' _validator = None
[docs] def getObjLocals(self): return { 'norm': self._norm, }
@s_stormtypes.stormfunc(readonly=True) async def _norm(self, flow): flow = await s_stormtypes.toprim(flow) if self.__class__._validator is None: schema = s_data.getJSON('attack-flow/attack-flow-schema-2.0.0') self.__class__._validator = s_config.getJsValidator(schema) flow = await s_coro.executor(self.__class__._validator, flow) flow = s_common.flatten(flow) flow['objects'] = sorted(flow.get('objects'), key=lambda x: x.get('id')) return flow
[docs] @s_stormtypes.registry.registerLib class CvssLib(s_stormtypes.Lib): ''' A Storm library which implements CVSS score calculations. ''' _storm_locals = ( {'name': 'calculate', 'desc': 'Calculate the CVSS score values for an input risk:vuln node.', 'type': {'type': 'function', '_funcname': 'calculate', 'args': ( {'name': 'node', 'type': 'node', 'desc': 'A risk:vuln node from the Storm runtime.'}, {'name': 'save', 'type': 'boolean', 'default': True, 'desc': 'If true, save the computed scores to the node properties.'}, {'name': 'vers', 'type': 'str', 'default': '3.1', 'desc': 'The version of CVSS calculations to execute.'}, ), 'returns': {'type': 'dict', 'desc': 'A dictionary containing the computed score and subscores.', } }}, {'name': 'calculateFromProps', 'desc': 'Calculate the CVSS score values from a props dict.', 'type': {'type': 'function', '_funcname': 'calculateFromProps', 'args': ( {'name': 'props', 'type': 'dict', 'desc': 'A props dictionary.'}, {'name': 'vers', 'type': 'str', 'default': '3.1', 'desc': 'The version of CVSS calculations to execute.'}, ), 'returns': {'type': 'dict', 'desc': 'A dictionary containing the computed score and subscores.', } }}, {'name': 'vectToProps', 'desc': 'Parse a CVSS v3.1 vector and return a dictionary of risk:vuln props.', 'deprecated': {'eolvers': 'v3.0.0'}, 'type': {'type': 'function', '_funcname': 'vectToProps', 'args': ( {'name': 'text', 'type': 'str', 'desc': 'A CVSS vector string.'}, ), 'returns': {'type': 'dict', 'desc': 'A dictionary of risk:vuln secondary props.', } }}, {'name': 'saveVectToNode', 'desc': 'Parse a CVSS v3.1 vector and record properties on a risk:vuln node.', 'deprecated': {'eolvers': 'v3.0.0'}, 'type': {'type': 'function', '_funcname': 'saveVectToNode', 'args': ( {'name': 'node', 'type': 'node', 'desc': 'A risk:vuln node to record the CVSS properties on.'}, {'name': 'text', 'type': 'str', 'desc': 'A CVSS vector string.'}, ), 'returns': {'type': 'null', } }}, {'name': 'vectToScore', 'desc': ''' Compute CVSS scores from a vector string. Takes a CVSS vector string, attempts to automatically detect the version (defaults to CVSS3.1 if it cannot), and calculates the base, temporal, and environmental scores. Raises: - BadArg: An invalid `vers` string is provided - BadDataValu: The vector string is invalid in some way. Possible reasons are malformed string, duplicated metrics, missing mandatory metrics, and invalid metric values.''', 'type': {'type': 'function', '_funcname': 'vectToScore', 'args': ( {'name': 'vect', 'type': 'str', 'desc': ''' A valid CVSS vector string. The following examples are valid formats: - CVSS 2 with version: `CVSS2#AV:L/AC:L/Au:M/C:P/I:C/A:N` - CVSS 2 with parentheses: `(AV:L/AC:L/Au:M/C:P/I:C/A:N)` - CVSS 2 without parentheses: `AV:L/AC:L/Au:M/C:P/I:C/A:N` - CVSS 3.0 with version: `CVSS:3.0/AV:N/AC:H/PR:L/UI:R/S:U/C:L/I:L/A:L` - CVSS 3.1 with version: `CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:L/I:L/A:L` - CVSS 3.0/3.1 with parentheses: `(AV:N/AC:H/PR:L/UI:R/S:U/C:L/I:L/A:L)` - CVSS 3.0/3.1 without parentheses: `AV:N/AC:H/PR:L/UI:R/S:U/C:L/I:L/A:L`'''}, {'name': 'vers', 'type': 'str', 'desc': f''' A valid version string or None to autodetect the version from the vector string. Accepted values are: { ', '.join(s_cvss.versions) }, None.''', 'default': None} ), 'returns': {'type': 'dict', 'desc': ''' A dictionary with the detected version, base score, temporal score, environmental score, overall score, and normalized vector string. The normalized vector string will have metrics ordered in specification order and metrics with undefined values will be removed. Example:: { 'version': '3.1', 'score': 4.3, 'base': 5.0, 'temporal': 4.4, 'environmental': 4.3, 'normalized': 'AV:N/AC:H/PR:L/UI:R/S:U/C:L/I:L/A:L' } '''} }}, ) _storm_lib_path = ('infosec', 'cvss',)
[docs] def getObjLocals(self): return { 'calculate': self.calculate, 'vectToProps': self.vectToProps, 'vectToScore': self.vectToScore, 'saveVectToNode': self.saveVectToNode, 'calculateFromProps': self.calculateFromProps, }
[docs] @s_stormtypes.stormfunc(readonly=True) async def vectToProps(self, text): s_common.deprecated('$lib.infosec.cvss.vectToProps()', '2.137.0', '3.0.0') await self.runt.snap.warnonce('$lib.infosec.cvss.vectToProps() is deprecated.') return await self._vectToProps(text)
async def _vectToProps(self, text): text = await s_stormtypes.tostr(text) props = {} try: for i, item in enumerate(text.split('/')): name, valu = item.split(':') if i == 0 and name == 'CVSS': if valu == '3.1': continue raise s_exc.BadArg(mesg='Currently only version 3.1 is supported.', vers=valu) prop = vect2prop.get(name) if prop is None: mesg = f'Invalid Vector Element: {name}' raise s_exc.BadArg(mesg=mesg) props[prop] = valu except ValueError: mesg = f'Invalid CVSS Vector: {text}' raise s_exc.BadArg(mesg=mesg) from None return props
[docs] async def saveVectToNode(self, node, text): s_common.deprecated('$lib.infosec.cvss.saveVectToNode()', '2.137.0', '3.0.0') await self.runt.snap.warnonce('$lib.infosec.cvss.saveVectToNode() is deprecated.') props = await self._vectToProps(text) for prop, valu in props.items(): await node.set(prop, valu)
[docs] async def calculate(self, node, save=True, vers='3.1'): save = await s_stormtypes.tobool(save) if node.ndef[0] != 'risk:vuln': mesg = '$lib.infosec.cvss.calculate() requires a risk:vuln node.' raise s_exc.BadArg(mesg=mesg) rval = await self.calculateFromProps(node.props, vers=vers) if save and rval.get('ok'): score = rval.get('score') if score is not None: await node.set('cvss:score', score) scores = rval.get('scores', {}) basescore = scores.get('base') if basescore is not None: await node.set('cvss:score:base', basescore) temporalscore = scores.get('temporal') if temporalscore is not None: await node.set('cvss:score:temporal', temporalscore) environmentalscore = scores.get('environmental') if environmentalscore is not None: await node.set('cvss:score:environmental', environmentalscore) return rval
[docs] @s_stormtypes.stormfunc(readonly=True) async def calculateFromProps(self, props, vers='3.1'): vers = await s_stormtypes.tostr(vers) if vers != '3.1': raise s_exc.BadArg(mesg='Currently only vers=3.1 is supported.') AV = props.get('cvss:av') AC = props.get('cvss:ac') PR = props.get('cvss:pr') UI = props.get('cvss:ui') S = props.get('cvss:s') C = props.get('cvss:c') _I = props.get('cvss:i') A = props.get('cvss:a') score = None impact = None basescore = None modimpact = None temporalscore = None exploitability = None environmentalscore = None if all((AV, AC, PR, UI, S, C, _I, A)): iscbase = 1.0 - ((1.0 - CVSS31['C'][C]) * (1.0 - CVSS31['I'][_I]) * (1.0 - CVSS31['A'][A])) if S == 'U': impact = 6.42 * iscbase else: impact = 7.52 * (iscbase - 0.029) - 3.25 * (iscbase - 0.02) ** 15 exploitability = 8.22 * CVSS31['AV'][AV] * CVSS31['AC'][AC] * CVSS31['PR'][S][PR] * CVSS31['UI'][UI] if impact <= 0: basescore = 0 elif S == 'U': basescore = min(roundup(impact + exploitability), 10.0) else: basescore = min(roundup(1.08 * (impact + exploitability)), 10.0) score = basescore if basescore: E = props.get('cvss:e') RL = props.get('cvss:rl') RC = props.get('cvss:rc') if all((E, RL, RC)): expfactor = CVSS31['E'][E] * CVSS31['RL'][RL] * CVSS31['RC'][RC] temporalscore = roundup(basescore * expfactor) score = temporalscore else: expfactor = CVSS31['E']['X'] * CVSS31['RL']['X'] * CVSS31['RC']['X'] MAV = props.get('cvss:mav') MAC = props.get('cvss:mac') MPR = props.get('cvss:mpr') MUI = props.get('cvss:mui') MS = props.get('cvss:ms') MC = props.get('cvss:mc') MI = props.get('cvss:mi') MA = props.get('cvss:ma') CR = props.get('cvss:cr') IR = props.get('cvss:ir') AR = props.get('cvss:ar') if all((MAV, MAC, MPR, MUI, MS, MC, MI, MA, CR, IR, AR)): if MAV == 'X': MAV = AV if MAC == 'X': MAC = AC if MPR == 'X': MPR = PR if MUI == 'X': MUI = UI if MS == 'X': MS = S if MC == 'X': MC = C if MI == 'X': MI = _I if MA == 'X': MA = A modiscbase = min(1.0 - ( (1.0 - (CVSS31['C'][MC] * CVSS31['CR'][CR])) * (1.0 - (CVSS31['I'][MI] * CVSS31['IR'][IR])) * (1.0 - (CVSS31['A'][MA] * CVSS31['AR'][AR])) ), 0.915) mav = CVSS31['AV'].get(MAV, 1) mac = CVSS31['AC'].get(MAC, 1) mpr = CVSS31['PR'][MS].get(MPR, 1) mui = CVSS31['UI'].get(MUI, 1) modexploit = 8.22 * mav * mac * mpr * mui if MS == 'U': modimpact = 6.42 * modiscbase else: modimpact = 7.52 * (modiscbase - 0.029) - 3.25 * (modiscbase * 0.9731 - 0.02) ** 13 if modimpact <= 0: environmentalscore = 0 elif MS == 'U': environmentalscore = roundup(roundup(min(modimpact + modexploit, 10.0)) * expfactor) else: environmentalscore = roundup(roundup(min(1.08 * (modimpact + modexploit), 10.0)) * expfactor) score = environmentalscore rval = { 'ok': True, 'version': '3.1', 'score': score, 'scores': { 'base': basescore, 'temporal': temporalscore, 'environmental': environmentalscore, }, } if impact: rval['scores']['impact'] = round(impact, 1) if modimpact: rval['scores']['modifiedimpact'] = round(modimpact, 1) if exploitability: rval['scores']['exploitability'] = round(exploitability, 1) return rval
[docs] @s_stormtypes.stormfunc(readonly=True) async def vectToScore(self, vect, vers=None): vers = await s_stormtypes.tostr(vers, noneok=True) if vers not in s_cvss.versions + [None]: raise s_exc.BadArg(mesg=f'Valid values for vers are: {s_cvss.versions + [None]}, got {vers}') detected = s_cvss.cvss3_1 if vers is None: if 'Au:' in vect or vect.startswith('CVSS#2'): detected = s_cvss.cvss2 elif vect.startswith('CVSS:3.0/'): detected = s_cvss.cvss3_0 elif vect.startswith('CVSS:3.1/'): detected = s_cvss.cvss3_1 else: detected = vers vdict = s_chop.cvss_validate(vect, detected) calc = CVSS_CALC[detected] base, temporal, environmental, overall = calc(vdict) return { 'version': detected, 'score': overall, 'base': base, 'temporal': temporal, 'environmental': environmental, 'normalized': s_chop.cvss_normalize(vdict, detected) }