PageRenderTime 35ms CodeModel.GetById 7ms RepoModel.GetById 1ms app.codeStats 0ms

/graphingwiki/graphingwiki/plugin/macro/D3CVSS.py

https://bitbucket.org/clarifiednetworks/graphingwiki/
Python | 286 lines | 231 code | 17 blank | 38 comment | 11 complexity | 802af33c46b834405dda57c401282fff MD5 | raw file
  1. # -*- coding: utf-8 -*-"
  2. """
  3. CVSS base score macro plugin to MoinMoin
  4. - Return the value of a calculated base score.
  5. @copyright: 2007-2010 by Juhani Eronen <exec@iki.fi>, Lari Huttunen <debian@huttu.net>
  6. @license: MIT <http://www.opensource.org/licenses/mit-license.php>
  7. """
  8. Dependencies = ['metadata']
  9. from MoinMoin.macro.Include import _sysmsg
  10. from graphingwiki.editing import get_metas
  11. from graphingwiki.util import format_wikitext
  12. vector_keys_str = {'AV': 'AccessVector',
  13. 'AC': 'AccessComplexity',
  14. 'Au': 'Authentication',
  15. 'C': 'ConfImpact',
  16. 'I': 'IntegImpact',
  17. 'A': 'AvailImpact',
  18. 'E': 'Exploitability',
  19. 'RL': 'RemediationLevel',
  20. 'RC': 'ReportConfidence',
  21. 'CD': 'CollateralDamagePotential',
  22. 'TD': 'TargetDistribution',
  23. 'CR': 'SystemConfidentialityRequirement',
  24. 'IR': 'SystemIntegrityRequirement',
  25. 'AR': 'SystemAvailabilityRequirement'}
  26. vector_str = {'AV': {'L': "Local access",
  27. 'A': "Adjacent network",
  28. 'N': "Network"},
  29. 'AC': {'H': "High",
  30. 'M': "Medium",
  31. 'L': "Low"},
  32. 'Au': {'N': "None required",
  33. 'S': "Requires single instance",
  34. 'M': "Requires multiple instances"},
  35. 'C': {'N': "None",
  36. 'P': "Partial",
  37. 'C': "Complete"},
  38. 'I': {'N': "None",
  39. 'P': "Partial",
  40. 'C': "Complete"},
  41. 'A': {'N': "None",
  42. 'P': "Partial",
  43. 'C': "Complete"},
  44. 'E': {'U': 'Unproven',
  45. 'P': 'Proof-of-concept',
  46. 'F': 'Functional',
  47. 'W': 'Widespread'},
  48. 'RL': {'O': 'Official-fix',
  49. 'T': 'Temporary-fix',
  50. 'W': 'Workaround',
  51. 'U': 'Unavailable'},
  52. 'RC': {'N': 'Not confirmed',
  53. 'U': 'Uncorroborated',
  54. 'C': 'Confirmed'},
  55. 'CD': {'N': 'None',
  56. 'L': 'Low',
  57. 'LM': 'Low-Medium',
  58. 'MH': 'Medium-High',
  59. 'H': 'High'},
  60. 'TD': {'N': 'None',
  61. 'L': 'Low',
  62. 'M': 'Medium',
  63. 'H': 'High'},
  64. 'CR': {'L': 'Low',
  65. 'M': 'Medium',
  66. 'H': 'High'},
  67. 'IR': {'L': 'Low',
  68. 'M': 'Medium',
  69. 'H': 'High'},
  70. 'AR': {'L': 'Low',
  71. 'M': 'Medium',
  72. 'H': 'High'}
  73. }
  74. vector_val = {'AV': {'L': 0.395,
  75. 'A': 0.646,
  76. 'N': 1.0},
  77. 'AC': {'H': 0.35,
  78. 'M': 0.61,
  79. 'L': 0.71},
  80. 'Au': {'N': 0.704,
  81. 'S': 0.56,
  82. 'M': 0.25},
  83. 'C': {'N': 0.0,
  84. 'P': 0.275,
  85. 'C': 0.660},
  86. 'I': {'N': 0.0,
  87. 'P': 0.275,
  88. 'C': 0.660},
  89. 'A': {'N': 0.0,
  90. 'P': 0.275,
  91. 'C': 0.660},
  92. 'E': {'U': 0.85,
  93. 'P': 0.9,
  94. 'F': 0.95,
  95. 'W': 1.0},
  96. 'RL': {'O': 0.87,
  97. 'T': 0.9,
  98. 'W': 0.95,
  99. 'U': 1.0},
  100. 'RC': {'N': 0.9,
  101. 'U': 0.95,
  102. 'C': 1.0},
  103. 'CD': {'N': 0.0,
  104. 'L': 0.1,
  105. 'LM': 0.3,
  106. 'MH': 0.4,
  107. 'H': 0.5},
  108. 'TD': {'N': 0,
  109. 'L': 0.25,
  110. 'M': 0.75,
  111. 'H': 1.0},
  112. 'CR': {'L': 0.5,
  113. 'M': 1.0,
  114. 'H': 1.51},
  115. 'IR': {'L': 0.5,
  116. 'M': 1.0,
  117. 'H': 1.51},
  118. 'AR': {'L': 0.5,
  119. 'M': 1.0,
  120. 'H': 1.51}
  121. }
  122. def parse_cvss(cvss_raw):
  123. cvss = dict()
  124. for asp in cvss_raw.split('/'):
  125. vec, val = asp.split(':')
  126. cvss[vec] = val
  127. return cvss
  128. def cvssval(cvss, vec):
  129. # If it does not exist, always return 1.0 (except with CollateralDamagePotential)
  130. if vec == 'CD':
  131. val = cvss.get(vec, None)
  132. if not val:
  133. return 0.0
  134. return vector_val.get(vec, {}).get(val, 0.0)
  135. else:
  136. val = cvss.get(vec, None)
  137. if not val:
  138. return 1.0
  139. return vector_val.get(vec, {}).get(val, 1.0)
  140. def calcimpact(impact):
  141. return impact == 0.0 and 0.0 or 1.176
  142. # CVSS2
  143. # BaseScore = round_to_1_decimal(((0.6*Impact)+(0.4*Exploitability)-1.5)*f(Impact))
  144. # Impact = 10.41*(1-(1-ConfImpact)*(1-IntegImpact)*(1-AvailImpact))
  145. # Exploitability = 20* AccessVector*AccessComplexity*Authentication
  146. # f(impact)= 0 if Impact=0, 1.176 otherwise
  147. # AccessVector = case AccessVector of
  148. # requires local access: 0.395
  149. # adjacent network accessible: 0.646
  150. # network accessible: 1.0
  151. # AccessComplexity = case AccessComplexity of
  152. # high: 0.35
  153. # medium: 0.61
  154. # low: 0.71
  155. # Authentication = case Authentication of
  156. # requires multiple instances of authentication: 0.45
  157. # requires single instance of authentication: 0.56
  158. # requires no authentication: 0.704
  159. # ConfImpact = case ConfidentialityImpact of
  160. # none: 0.0
  161. # partial: 0.275
  162. # complete: 0.660
  163. # IntegImpact = case IntegrityImpact of
  164. # none: 0.0
  165. # partial: 0.275
  166. # complete: 0.660
  167. # AvailImpact = case AvailabilityImpact of
  168. # none: 0.0
  169. # partial: 0.275
  170. # complete: 0.660
  171. def basescore(cvss, impact=None):
  172. if not impact:
  173. impact = 10.41 * (1-(1-cvssval(cvss, 'C')) *
  174. (1-cvssval(cvss, 'I')) *
  175. (1-cvssval(cvss, 'A')))
  176. exploitability = 20 * (cvssval(cvss, 'AV') *
  177. cvssval(cvss, 'AC') *
  178. cvssval(cvss, 'Au'))
  179. base = ((0.6 * impact) +
  180. (0.4 * exploitability - 1.5)) * \
  181. calcimpact(impact)
  182. return round(base, 1)
  183. def buildVector(base_metas):
  184. vector = ""
  185. avset = set(['L', 'A', 'N'])
  186. acset = set(['H', 'M', 'L'])
  187. auset = set(['M', 'S', 'N'])
  188. ciaset = set(['C', 'P', 'N'])
  189. av = base_metas["Option cvss-AV"]
  190. avs = set(av)
  191. if len(avs) > 0 and avs <= avset:
  192. vector += "AV:" + av.pop() + "/"
  193. else:
  194. return None
  195. ac = base_metas["Option cvss-AC"]
  196. acs = set(ac)
  197. if len(acs) > 0 and acs <= acset:
  198. vector += "AC:" + ac.pop() + "/"
  199. else:
  200. return None
  201. au = base_metas["Option cvss-Au"]
  202. aus = set(au)
  203. if len(aus) > 0 and aus <= auset:
  204. vector += "Au:" + au.pop() + "/"
  205. else:
  206. return None
  207. c = base_metas["Option cvss-C"]
  208. cs = set(c)
  209. if len(cs) > 0 and cs <= ciaset:
  210. vector += "C:" + c.pop() + "/"
  211. else:
  212. return None
  213. i = base_metas["Option cvss-I"]
  214. ise = set(i)
  215. if len(ise) > 0 and ise <= ciaset:
  216. vector += "I:" + i.pop() + "/"
  217. else:
  218. return None
  219. a = base_metas["Option cvss-A"]
  220. ase = set(a)
  221. if len(ase) > 0 and ase <= ciaset:
  222. vector += "A:" + a.pop()
  223. else:
  224. return None
  225. return vector
  226. def execute(macro, args):
  227. tset = set(['score', 'vector'])
  228. request = macro.request
  229. _ = request.getText
  230. if args:
  231. args = [x.strip() for x in args.split(',')]
  232. # Wrong number of arguments
  233. if len(args) not in [0,1,2]:
  234. return _sysmsg % ('error',
  235. _("CVSS: Need to specify a page or page and type (score|vector)."))
  236. # Get all non-empty args
  237. args = [x for x in args if x]
  238. # If page is not specified, defaulting to current page
  239. if len(args) == 0:
  240. page = request.page.page_name
  241. type = "score"
  242. elif len(args) == 1:
  243. page = args[0]
  244. type = "score"
  245. else:
  246. page = args[0]
  247. type = args[1]
  248. if type not in tset:
  249. return _sysmsg % ('error',
  250. _("CVSS: The type needs to be either score or vector."))
  251. base_metas = get_metas(request, page, ["Option cvss-AV", "Option cvss-AC", "Option cvss-Au", "Option cvss-C", "Option cvss-I", "Option cvss-A"])
  252. vector = buildVector(base_metas)
  253. if vector is not None:
  254. if type == "vector":
  255. return format_wikitext(request, vector)
  256. cvss = parse_cvss(vector)
  257. bscore = basescore(cvss)
  258. bstring = "%s" % bscore
  259. return format_wikitext(request, bstring)
  260. else:
  261. return _sysmsg % ('error',
  262. _("CVSS: Invalid value(s) in Base Metrics."))