Class: WPScan::Vulnerability

Inherits:
Object
  • Object
show all
Includes:
References
Defined in:
lib/wpscan/vulnerability.rb

Overview

Vulnerability model.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from References

#cve_url, #cve_urls, #cves, #exploitdb_ids, #exploitdb_url, #exploitdb_urls, #msf_modules, #msf_url, #msf_urls, #packetstorm_ids, #packetstorm_url, #packetstorm_urls, #references, #references=, #references_urls, #securityfocus_ids, #securityfocus_url, #securityfocus_urls, #urls, #wpvulndb_ids, #wpvulndb_url, #wpvulndb_urls, #youtube_url, #youtube_urls

Constructor Details

#initialize(title, references: {}, type: nil, affected_versions: nil, cvss: nil, poc: nil, uuid: nil) ⇒ Vulnerability

rubocop:disable Metrics/ParameterLists

Parameters:

  • title (String)
  • references (Hash) (defaults to: {})
  • type (String) (defaults to: nil)
  • affected_versions (Array<Hash>) (defaults to: nil)

    One entry per backported fix range, each with :fixed_in and :introduced_in keys (either may be nil).

  • cvss (HashSymbol) (defaults to: nil)

Options Hash (references:):

  • :cve (Array<String>, String)
  • :secunia (Array<String>, String)
  • :osvdb (Array<String>, String)
  • :exploitdb (Array<String>, String)
  • :url (Array<String>)

    URL(s) to related advisories etc

  • :metasploit (Array<String>, String)

    The related metasploit module(s)

  • :youtube (Array<String>)

Options Hash (cvss:):

  • :score (String)
  • :vector (String)


31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/wpscan/vulnerability.rb', line 31

def initialize(title, references: {}, type: nil, affected_versions: nil, cvss: nil, poc: nil, uuid: nil)
  # rubocop:enable Metrics/ParameterLists
  @title             = title
  @type              = type
  @affected_versions = normalize_affected_versions(affected_versions)
  @cvss              = { score: cvss[:score], vector: cvss[:vector] } if cvss
  @poc               = poc
  @uuid              = uuid
  @detected_version  = nil

  self.references = references
end

Instance Attribute Details

#affected_versionsObject (readonly)

Returns the value of attribute affected_versions.



8
9
10
# File 'lib/wpscan/vulnerability.rb', line 8

def affected_versions
  @affected_versions
end

#cvssObject (readonly)

Returns the value of attribute cvss.



8
9
10
# File 'lib/wpscan/vulnerability.rb', line 8

def cvss
  @cvss
end

#detected_versionObject

Version of the scanned item (plugin/theme/WordPress), used to resolve which backported range to surface as the scalar fixed_in / introduced_in. Left nil/false when the version could not be detected.



13
14
15
# File 'lib/wpscan/vulnerability.rb', line 13

def detected_version
  @detected_version
end

#pocObject (readonly)

Returns the value of attribute poc.



8
9
10
# File 'lib/wpscan/vulnerability.rb', line 8

def poc
  @poc
end

#titleObject (readonly)

Returns the value of attribute title.



8
9
10
# File 'lib/wpscan/vulnerability.rb', line 8

def title
  @title
end

#typeObject (readonly)

Returns the value of attribute type.



8
9
10
# File 'lib/wpscan/vulnerability.rb', line 8

def type
  @type
end

#uuidObject (readonly)

Returns the value of attribute uuid.



8
9
10
# File 'lib/wpscan/vulnerability.rb', line 8

def uuid
  @uuid
end

Class Method Details

.load_from_json(json_data) ⇒ Vulnerability

Parameters:

  • json_data (Hash)

Returns:



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/wpscan/vulnerability.rb', line 46

def self.load_from_json(json_data)
  references = { wpvulndb: json_data['id'].to_s }

  if json_data['references']
    references_keys.each do |key|
      references[key] = json_data['references'][key.to_s] if json_data['references'].key?(key.to_s)
    end
  end

  new(
    json_data['title'],
    references: references,
    type: json_data['vuln_type'],
    affected_versions: json_data['affected_versions'],
    cvss: json_data['cvss']&.symbolize_keys,
    poc: json_data['poc'],
    uuid: json_data['id'].to_s # The 'id' field IS the UUID in WPScan API
  )
end

.range_covers?(range, version) ⇒ Boolean

Whether a single range covers the version. A range matches when introduced_in is missing or <= version AND fixed_in is missing or > version.

Parameters:

  • range (Hash{Symbol => String, nil})
  • version (#<)

Returns:

  • (Boolean)


121
122
123
124
125
126
127
128
129
# File 'lib/wpscan/vulnerability.rb', line 121

def self.range_covers?(range, version)
  introduced = range[:introduced_in]
  fixed      = range[:fixed_in]

  return false if introduced && !introduced.to_s.empty? && version < introduced
  return true if fixed.nil? || fixed.to_s.empty?

  version < fixed
end

Instance Method Details

#==(other) ⇒ Boolean

Parameters:

Returns:

  • (Boolean)


134
135
136
137
138
139
140
141
142
# File 'lib/wpscan/vulnerability.rb', line 134

def ==(other)
  title == other.title &&
    type == other.type &&
    references == other.references &&
    affected_versions == other.affected_versions &&
    cvss == other.cvss &&
    poc == other.poc &&
    uuid == other.uuid
end

#affects?(version) ⇒ Boolean

Whether this vulnerability covers a given version. True when no affected_versions are declared (advisory with no fix metadata) or when at least one backported range covers the version.

Parameters:

  • version (#<)

Returns:

  • (Boolean)


73
74
75
76
77
# File 'lib/wpscan/vulnerability.rb', line 73

def affects?(version)
  return true if affected_versions.empty?

  affected_versions.any? { |range| self.class.range_covers?(range, version) }
end

#fixed_inString?

Backward-compatible scalar: the fixed_in of the range relevant to the detected version (the branch the scanned site is actually on). nil when there is no fix metadata.

Returns:

  • (String, nil)


84
85
86
# File 'lib/wpscan/vulnerability.rb', line 84

def fixed_in
  relevant_range && relevant_range[:fixed_in]
end

#introduced_inString?

Backward-compatible scalar counterpart of #fixed_in.

Returns:

  • (String, nil)


91
92
93
# File 'lib/wpscan/vulnerability.rb', line 91

def introduced_in
  relevant_range && relevant_range[:introduced_in]
end

#relevant_rangeHash{Symbol => String, nil}?

The single affected-version range whose fixed_in / introduced_in we surface in the output. Picks the range covering the detected version so a site on an older but still-supported branch is matched against the right fix. Falls back to the highest (most recent) range when the version is unknown or no range covers it, mirroring the pre-v4 single-value output.

Returns:

  • (Hash{Symbol => String, nil}, nil)


102
103
104
105
106
107
108
109
110
111
# File 'lib/wpscan/vulnerability.rb', line 102

def relevant_range
  return nil if affected_versions.empty?

  if detected_version
    covering = affected_versions.find { |range| self.class.range_covers?(range, detected_version) }
    return covering if covering
  end

  highest_range
end