From ffef2d00138649153043ee915353836237e61922 Mon Sep 17 00:00:00 2001 From: Stephen Kapp Date: Thu, 20 Dec 2012 11:58:10 +0000 Subject: [PATCH] Rewrite of API parsing and handling to eliminate the use of roxml --- lib/veracode/api/builds.rb | 107 +++++++++----- lib/veracode/api/detailed.rb | 197 +++++++++++++++++++------- lib/veracode/api/flaws.rb | 176 ++++++++++++++++------- lib/veracode/api/summary.rb | 114 +++++++++------ lib/veracode/api/types.rb | 216 +++++++++++++++++++++-------- lib/veracode/parser/parser.rb | 78 +++++++++++ lib/veracode/results.rb | 13 +- lib/veracode/version.rb | 2 +- spec/lib/veracode/detailed_spec.rb | 14 ++ spec/lib/veracode/summary_spec.rb | 14 ++ veracode-api.gemspec | 2 + 11 files changed, 694 insertions(+), 239 deletions(-) create mode 100644 lib/veracode/parser/parser.rb diff --git a/lib/veracode/api/builds.rb b/lib/veracode/api/builds.rb index 37b87e4..c1003c7 100644 --- a/lib/veracode/api/builds.rb +++ b/lib/veracode/api/builds.rb @@ -2,46 +2,89 @@ module Veracode module Result - module Builds - class AnalysisUnit < Base - xml_reader :analysis_type, :from => "@analysis_type" - xml_reader :status, :from => "@status" - xml_reader :published_date, :from => "@published_date" + module Builds + class AnalysisUnit < Base + api_field :analysis_type, :tag => :analysis_type + api_field :status, :tag => :status + api_field :published_date, :tag => :published_date end - class Build < Base - xml_reader :version, :from => "@version" - xml_reader :build_id, :from => "@build_id" - xml_reader :submitter, :from => "@submitter" - xml_reader :platform, :from => "@platform" - xml_reader :lifecycle_stage, :from => "@lifecycle_stage" - xml_reader :results_ready, :from => "@results_ready" - xml_reader :policy_name, :from => "@policy_name" - xml_reader :policy_version, :from => "@policy_version" - xml_reader :policy_compliance_status, :from => "@policy_compliance_status" - xml_reader :rules_status, :from => "@rules_status" - xml_reader :grace_period_expired, :from => "@grace_period_expired" - xml_reader :scan_overdue, :from => "@scan_overdue" - - xml_reader :analysis_units, :as => [AnalysisUnit] + class Build < Base + api_field :version, :tag => :version + api_field :build_id, :tag => :build_id + api_field :submitter, :tag => :submitter + api_field :platform, :tag => :platform + api_field :lifecycle_stage, :tag => :lifecycle_stage + api_field :policy_name, :tag => :policy_name + api_field :policy_version, :tag => :policy_version + api_field :policy_compliance_status, :tag => :policy_compliance_status + api_field :rules_status, :tag => :rules_status + + def grace_period_expired? + @grace_period_expired ||= @xml_hash.grace_period_expired.to_bool + end + + def scan_overdue? + @scan_overdue ||= @xml_hash.scan_overdue.to_bool + end + + def results_ready? + @results_ready ||= @xml_hash.results_ready.to_bool + end + + def analysis_units + @analysis_units ||= [] + if @analysis_units.empty? + if @xml_hash.analysis_unit.class == Array + @analysis_units = @xml_hash.analysis_unit.map do |analysis_unit| + AnalysisUnit.new(analysis_unit) + end + else + @analysis_units << AnalysisUnit.new(@xml_hash.analysis_unit) + end + end + return @analysis_units + end end class Application < Base - xml_reader :app_name, :from => "@app_name" - xml_reader :app_id, :from => "@app_id" - xml_reader :industry_vertical, :from => "@industry_vertical" - xml_reader :assurance_level, :from => "@assurance_level" - xml_reader :business_criticality, :from => "business_criticality" - xml_reader :origin, :from => "@origin" - xml_reader :cots, :from => "@cots" - xml_reader :business_unit, :from => "@business_unit" - xml_reader :tags, :from => "@tags" - xml_reader :builds, :as => [Build] - + api_field :app_name, :tag => :app_name + api_field :app_id, :tag => :app_id + api_field :industry_vertical, :tag => :industry_vertical + api_field :assurance_level, :tag => :assurance_level + api_field :business_criticality, :tag => :business_criticality + api_field :origin, :tag => :origin + api_field :business_unit, :tag => :business_unit + api_field :tags, :tag => :tags + + def cots? + @cots ||= @xml_hash.cots.to_bool + end + + def builds + @builds ||= [] + if @builds.empty? + if @xml_hash.build.class == Array + @builds = @xml_hash.build.map do |build| + Build.new(build) + end + else + @builds << Build.new(@xml_hash.build) + end + end + return @builds + end end class Applications < Base - xml_reader :applications, :as => [Application] + def applications + @applications ||= [] + if @applications.empty? + @applications = @xml_hash.applicationbuilds.application.map do |application| + Application.new(application) + end + end + end end end diff --git a/lib/veracode/api/detailed.rb b/lib/veracode/api/detailed.rb index deebb0d..26637d9 100644 --- a/lib/veracode/api/detailed.rb +++ b/lib/veracode/api/detailed.rb @@ -4,74 +4,165 @@ module Veracode module Result class CWE < Base - xml_reader :cweid, :from => "@cweid" - xml_reader :cwename, :from => "@cwename" - xml_reader :pcirelated?, :from => "@pcirelated" + api_field :cweid, :tag => :cweid + api_field :cwename, :tag => :cwename - xml_reader :description, :as => [TextType] - xml_reader :staticflaws, :as => Flaws - xml_reader :dynamicflaws, :as => Flaws - xml_reader :manualflaws, :as => Flaws + def pcirelated? + @pcirelated ||= @xml_hash.pcirelated.to_bool + end + + def description + @xml_hash.description.text.text + end + + api_type_field :manualflaws, :tag => :manualflaws, :as => Flaws + api_type_field :dynamicflaws, :tag => :dynamicflaws, :as => Flaws + api_type_field :staticflaws, :tag => :staticflaws, :as => Flaws + end - class Category < Base - xml_reader :categoryid, :from => "@categoryid" - xml_reader :categoryname, :from => "@categoryname" - xml_reader :pcirelated?, :from => "pcirelated" + class Category < Base + api_field :categoryid, :tag => :categoryid + api_field :categoryname, :tag => :categoryname + api_type_field :desc, :tag => :desc, :as => Para + api_type_field :recommendations, :tag => :recommendations, :as => Para + + def pcirelated? + @pcirelated ||= @xml_hash.pcirelated.to_bool + end - xml_reader :desc, :as => Para - xml_reader :recommendations, :as => Para - xml_reader :cwe, :as => [CWE] + def description + temp = self.desc.para.map do |para| + para.text + end + + self.desc.para.map do |para| + if !para.bulletitem.nil? + x = para.bulletitem.each.map do |item| + "* " + item.text + "\r\n" + end + end + temp << x.join + end + + return temp.join("\r\n\r\n").strip + end + + def recommendation + temp = self.recommendations.para.map do |para| + para.text + end + + self.recommendations.para.map do |para| + if !para.bulletitem.nil? + x = para.bulletitem.each.map do |item| + "* " + item.text + "\r\n" + end + end + temp << x.join + end + + return temp.join("\r\n\r\n").strip + end + + def cwe + @cwe ||= [] + begin + if @cwe.empty? + if @xml_hash.cwe.class == Array + @cwe = @xml_hash.cwe.map do |c| + CWE.new(c) + end + else + @cwe << CWE.new(@xml_hash.cwe) + end + end + rescue NoMethodError + end + + return @cwe + end end class Severity < Base - xml_reader :level, :from => "@level" + api_field :level, :tag => :level - xml_reader :categories, :as => [Category] + def categories + @categories ||= [] + begin + if @categories.empty? + if @xml_hash.category.class == Array + @categories = @xml_hash.category.map do |sev| + Category.new(sev) + end + else + @categories << Category.new(@xml_hash.category) + end + end + rescue NoMethodError + end + + return @categories + end end class DetailedReport < Base - xml_convention :dasherize - xml_reader :report_format_version, :from => "@report_format_version" - xml_reader :app_name, :from => "@app_name" - xml_reader :app_id, :from => "@app_id" - xml_reader :first_build_submitted_date, :from => "@first_build_submitted_date" - xml_reader :version, :from => "@version" - xml_reader :build_id, :from => "@build_id" - xml_reader :submitter, :from => "@submitter" - xml_reader :vendor, :from => "@vendor" - xml_reader :platform, :from => "@platform" - xml_reader :assurance_level, :from => "@assurance_level" - xml_reader :business_criticality, :from => "@business_criticality" - xml_reader :generation_date, :from => "@generation_date" - xml_reader :veracode_level, :from => "@veracode_level" - xml_reader :total_flaws, :from => "@total_flaws" - xml_reader :flaws_not_mitigated, :from => "@flaws_not_mitigated" - xml_reader :teams, :from => "@teams" - xml_reader :life_cycle_stage, :from => "@life_cycle_stage" - xml_reader :planned_deployment_date, :from => "@planned_deployment_date" - xml_reader :last_update_time, :from => "@last_update_time" - xml_reader :is_latest_build?, :from => "@is_latest_build" - xml_reader :policy_name, :from => "@policy_name" - xml_reader :policy_version, :from => "@policy_version" - xml_reader :policy_compliance_status, :from => "@policy_compliance_status" - xml_reader :policy_rules_status, :from => "@policy_rules_status" - xml_reader :scan_overdue, :from => "@scan_overdue" - xml_reader :any_type_scan_due, :from => "@any_type_scan_due" - xml_reader :business_owner, :from => "@business_owner" - xml_reader :business_unit, :from => "@business_unit" - xml_reader :tags, :from => "@tags" - xml_reader :grace_period_expired?, :from => "@grace_period_expired" - + api_field :report_format_version, :tag => :report_format_version + api_field :app_name, :tag => :app_name + api_field :app_id, :tag => :app_id + api_field :first_build_submitted_date, :tag => :first_build_submitted_date + api_field :version, :tag => :version + api_field :build_id, :tag => :build_id + api_field :submitter, :tag => :submitter + api_field :vendor, :tag => :vendor + api_field :platform, :tag => :platform + api_field :assurance_level, :tag => :assurance_level + api_field :business_criticality, :tag => :business_criticality + api_field :generation_date, :tag => :generation_date + api_field :veracode_level, :tag => :veracode_level + api_field :total_flaws, :tag => :total_flaws + api_field :flaws_not_mitigated, :tag => :flaws_not_mitigated + api_field :teams, :tag => :teams + api_field :life_cycle_stage, :tag => :life_cycle_stage + api_field :planned_deployment_date, :tag => :planned_deployment_date + api_field :last_update_time, :tag => :last_update_time + api_field :policy_name, :tag => :policy_name + api_field :policy_version, :tag => :policy_version + api_field :policy_compliance_status, :tag => :policy_compliance_status + api_field :policy_rules_status, :tag => :policy_rules_status + api_field :scan_overdue, :tag => :scan_overdue + api_field :any_type_scan_due, :tag => :any_type_scan_due + api_field :business_owner, :tag => :business_owner + api_field :business_unit, :tag => :business_unit + api_field :tags, :tag => :tags + + api_type_field :static_analysis, :tag => :static_analysis, :as => Analysis + api_type_field :dynamic_analysis, :tag => :dynamic_analysis, :as => Analysis + api_type_field :manual_analysis, :tag => :manual_analysis, :as => ManualAnalysis + api_type_field :flaw_status, :tag => :flaw_status, :as => FlawStatus - xml_reader :static_analysis, :as => Analysis - xml_reader :dynamic_analysis, :as => Analysis - xml_reader :manual_analysis, :as => ManualAnalysis + def is_latest_build? + @is_latest_build ||= @xml_hash.is_latest_build.to_bool + end - xml_reader :severity, :as => [Severity] + def grace_period_expired? + @grace_period_expired ||= @xml_hash.grace_period_expired.to_bool + end - xml_reader :flaw_status, :as => FlawStatus + def severity + @severity ||= [] + if @severity.empty? + if @xml_hash.severity.class == Array + @severity = @xml_hash.severity.map do |sev| + Severity.new(sev) + end + else + @severity << Severity.new(@xml_hash.severity) + end + end + return @severity + end end end diff --git a/lib/veracode/api/flaws.rb b/lib/veracode/api/flaws.rb index 250beca..61fb4c8 100644 --- a/lib/veracode/api/flaws.rb +++ b/lib/veracode/api/flaws.rb @@ -3,80 +3,152 @@ module Veracode module Result class AnnotationType < Base - xml_reader :action, :from => :attr - xml_reader :description, :from => :attr - xml_reader :user, :from => :attr - xml_reader :date, :from => :attr + api_field :action, :tag => :action + api_field :description, :tag => :description + api_field :user, :tag => :user + api_field :date, :tag => :date end class Annotations < Base - xml_reader :annotation, :as => [AnnotationType] + def annotation + @annotations ||= [] + begin + if @annotations.empty? + if @xml_hash.annotation.class == Array + @annotations = @xml_hash.annotation.map do |annotation| + AnnotationType.new(annotation) + end + else + @annotations << AnnotationType.new(@xml_hash.annotation) + end + end + rescue NoMethodError + end + + return @annotations + end end class MitigationType < Base - xml_reader :action, :from => :attr - xml_reader :description, :from => :attr - xml_reader :user, :from => :attr - xml_reader :date, :from => :attr + api_field :action, :tag => :action + api_field :description, :tag => :description + api_field :user, :tag => :user + api_field :date, :tag => :date end class Mitigations < Base - xml_reader :mitigation, :as => [MitigationType] + def mitigation + @mitigations ||= [] + begin + if @mitigations.empty? + if @xml_hash.mitigation.class == Array + @mitigations = @xml_hash.mitigation.map do |mitigation| + MitigationType.new(mitigation) + end + else + @mitigations << MitigationType.new(@xml_hash.mitigation) + end + end + rescue NoMethodError + end + + return @mitigations + end end class ExploitabilityAdjustment < Base - xml_reader :note - xml_reader :score_adjustment, :from => :attr + api_field :note, :tag => :note + api_field :score_adjustment, :tag => :score_adjustment end class ExploitAdjustment < Base - xml_reader :exploitability_adjustment, :as => ExploitabilityAdjustment + def exploitability_adjustment + @exploitability_adjustments ||= [] + begin + if @exploitability_adjustments.empty? + if @xml_hash.exploitability_adjustment.class == Array + @exploitability_adjustments = @xml_hash.exploitability_adjustment.map do |exploitability_adjustment| + ExploitabilityAdjustment.new(exploitability_adjustment) + end + else + @exploitability_adjustments << ExploitabilityAdjustment.new(@xml_hash.exploitability_adjustment) + end + end + rescue NoMethodError + end + + return @exploitability_adjustments + end end class Flaw < Base - xml_reader :severity, :from => :attr - xml_reader :categoryname, :from => :attr - xml_reader :count, :from => :attr - xml_reader :issueid, :from => :attr - xml_reader :module, :from => :attr - xml_reader :type, :from => :attr - xml_reader :description, :from => :attr - xml_reader :note, :from => :attr - xml_reader :cweid, :from => :attr - xml_reader :remediationeffort, :from => :attr - xml_reader :exploitLevel, :from => :attr - xml_reader :categoryid, :from => :attr - xml_reader :pcirelated?, :from => :attr - xml_reader :date_first_occurrence, :from => :attr - xml_reader :remediation_status, :from => :attr - xml_reader :sourcefile, :from => :attr - xml_reader :line, :from => :attr - xml_reader :sourcefilepath, :from => :attr - xml_reader :scope, :from => :attr - xml_reader :functionprototype, :from => :attr - xml_reader :functionrelativelocation, :from => :attr - xml_reader :url, :from => :attr - xml_reader :vuln_parameter, :from => :attr - xml_reader :location, :from => :attr - xml_reader :cvss, :from => :attr - xml_reader :capecid, :from => :attr - xml_reader :exploitdifficulty, :from => :attr - xml_reader :inputvector, :from => :attr - xml_reader :cia_impact, :from => :attr - xml_reader :grace_period_expires, :from => :attr - xml_reader :affects_policy_compliance?, :from => :attr + api_field :severity, :tag => :severity + api_field :categoryname, :tag => :categoryname + api_field :count, :tag => :count + api_field :issueid, :tag => :issueid + api_field :module, :tag => :module + api_field :type, :tag => :type + api_field :description, :tag => :description + api_field :note, :tag => :note + api_field :cweid, :tag => :cweid + api_field :remediationeffort, :tag => :remediationeffort + api_field :exploitlevel, :tag => :exploitLevel + api_field :categoryid, :tag => :categoryid + api_field :date_first_occurrence, :tag => :date_first_occurrence + api_field :remediation_status, :tag => :remediation_status + api_field :sourcefile, :tag => :sourcefile + api_field :line, :tag => :line + api_field :sourcefilepath, :tag => :sourcefilepath + api_field :scope, :tag => :scope + api_field :functionprototype, :tag => :functionprototype + api_field :functionrelativelocation, :tag => :functionrelativelocation + api_field :url, :tag => :url + api_field :vuln_parameter, :tag => :vuln_parameter + api_field :location, :tag => :location + api_field :cvss, :tag => :cvss + api_field :capecid, :tag => :capecid + api_field :exploitdifficulty, :tag => :exploitdifficulty + api_field :inputvector, :tag => :inputvector + api_field :cia_impact, :tag => :cia_impact + api_field :grace_period_expires, :tag => :grace_period_expires - xml_reader :exploit_desc - xml_reader :severity_desc - xml_reader :remediation_desc - xml_reader :exploitability_adjustments, :as => ExploitAdjustment - xml_reader :appendix, :as => AppendixType - xml_reader :mitigations, :as => Mitigations - xml_reader :annotations, :as => Annotations + def pcirelated? + @pcirelated ||= @xml_hash.pcirelated.to_bool + end + + def affects_policy_compliance? + @affects_policy_compliance ||= @xml_hash.affects_policy_compliance.to_bool + end + + api_field :exploit_desc, :tag => :exploit_desc + api_field :severity_desc, :tag => :severity_desc + api_field :remediation_desc, :tag => :remediation_desc + + api_type_field :exploitability_adjustments, :tag => :exploitability_adjustments, :as => ExploitAdjustment + api_type_field :appendix, :tag => :appendix, :as => AppendixType + api_type_field :mitigations, :tag => :mitigations, :as => Mitigations + api_type_field :annotations, :tag => :annotations, :as => Annotations end class Flaws < Base - xml_reader :flaws, :as => [Flaw] + def flaws + @flaws ||= [] + begin + if @flaws.empty? + if @xml_hash.flaw.class == Array + @flaws = @xml_hash.flaw.map do |flaw| + Flaw.new(flaw) + end + else + @flaws << Flaw.new(@xml_hash.flaw) + end + end + rescue NoMethodError + end + + return @flaws + end end end end + \ No newline at end of file diff --git a/lib/veracode/api/summary.rb b/lib/veracode/api/summary.rb index 60d5a8e..052556e 100644 --- a/lib/veracode/api/summary.rb +++ b/lib/veracode/api/summary.rb @@ -5,59 +5,89 @@ module Veracode module Result class SummaryCategory < Base - xml_reader :categoryname, :from => :attr - xml_reader :severity, :from => :attr - xml_reader :count, :from => :attr + api_field :categoryname, :tag => :categoryname + api_field :severity, :tag => :severity + api_field :count, :tag => :count end class SummarySeverity < Base - xml_reader :level, :from => "@level" + api_field :level, :tag => :level + + def categories + @categories ||= [] + begin + if @categories.empty? + if @xml_hash.category.class == Array + @categories = @xml_hash.category.map do |sev| + SummaryCategory.new(sev) + end + else + @categories << SummaryCategory.new(@xml_hash.category) + end + end + rescue NoMethodError + end - xml_reader :categories, :as => [SummaryCategory] + return @categories + end end class SummaryReport < Base - xml_convention :dasherize + api_field :report_format_version, :tag => :report_format_version + api_field :app_name, :tag => :app_name + api_field :app_id, :tag => :app_id + api_field :first_build_submitted_date, :tag => :first_build_submitted_date + api_field :version, :tag => :version + api_field :build_id, :tag => :build_id + api_field :vendor, :tag => :vendor + api_field :submitter, :tag => :submitter + api_field :platform, :tag => :platform + api_field :assurance_level, :tag => :assurance_level + api_field :business_criticality, :tag => :business_criticality + api_field :generation_date, :tag => :generation_date + api_field :veracode_level, :tag => :veracode_level + api_field :total_flaws, :tag => :total_flaws + api_field :flaws_not_mitigated, :tag => :flaws_not_mitigated + api_field :teams, :tag => :teams + api_field :life_cycle_stage, :tag => :life_cycle_stage + api_field :planned_deployment_date, :tag => :planned_deployment_date + api_field :last_update_time, :tag => :last_update_time + api_field :policy_name, :tag => :policy_name + api_field :policy_version, :tag => :policy_version + api_field :policy_compliance_status, :tag => :policy_compliance_status + api_field :policy_rules_status, :tag => :policy_rules_status + api_field :scan_overdue, :tag => :scan_overdue + api_field :any_type_scan_due, :tag => :any_type_scan_due + api_field :business_owner, :tag => :business_owner + api_field :business_unit, :tag => :business_unit + api_field :tags, :tag => :tags - xml_reader :report_format_version, :from => "@report_format_version" - xml_reader :app_name, :from => "@app_name" - xml_reader :app_id, :from => "@app_id" - xml_reader :first_build_submitted_date, :from => "@first_build_submitted_date" - xml_reader :version, :from => "@version" - xml_reader :build_id, :from => "@build_id" - xml_reader :vendor, :from => "@vendor" - xml_reader :submitter, :from => "@submitter" - xml_reader :platform, :from => "@platform" - xml_reader :assurance_level, :from => "@assurance_level" - xml_reader :business_criticality, :from => "@business_criticality" - xml_reader :generation_date, :from => "@generation_date" - xml_reader :veracode_level, :from => "@veracode_level" - xml_reader :total_flaws, :from => "@total_flaws" - xml_reader :flaws_not_mitigated, :from => "@flaws_not_mitigated" - xml_reader :teams, :from => "@teams" - xml_reader :life_cycle_stage, :from => "@life_cycle_stage" - xml_reader :planned_deployment_date, :from => "@planned_deployment_date" - xml_reader :last_update_time, :from => "@last_update_time" - xml_reader :is_latest_build?, :from => "@is_latest_build" - xml_reader :policy_name, :from => "@policy_name" - xml_reader :policy_version, :from => "@policy_version" - xml_reader :policy_compliance_status, :from => "@policy_compliance_status" - xml_reader :policy_rules_status, :from => "@policy_rules_status" - xml_reader :scan_overdue, :from => "@scan_overdue" - xml_reader :any_type_scan_due, :from => "@any_type_scan_due" - xml_reader :business_owner, :from => "@business_owner" - xml_reader :business_unit, :from => "@business_unit" - xml_reader :tags, :from => "@tags" - xml_reader :grace_period_expired?, :from => "@grace_period_expired" - + api_type_field :static_analysis, :tag => :static_analysis, :as => Analysis + api_type_field :dynamic_analysis, :tag => :dynamic_analysis, :as => Analysis + api_type_field :manual_analysis, :tag => :manual_analysis, :as => ManualAnalysis + api_type_field :flaw_status, :tag => :flaw_status, :as => FlawStatus - xml_reader :static_analysis, :as => Analysis - xml_reader :dynamic_analysis, :as => Analysis - xml_reader :manual_analysis, :as => ManualAnalysis + def is_latest_build? + @is_latest_build ||= @xml_hash.is_latest_build.to_bool + end - xml_reader :severity, :as => [SummarySeverity] + def grace_period_expired? + @grace_period_expired ||= @xml_hash.grace_period_expired.to_bool + end - xml_reader :flaw_status, :as => FlawStatus + def severity + @severity ||= [] + if @severity.empty? + if @xml_hash.severity.class == Array + @severity = @xml_hash.severity.map do |sev| + SummarySeverity.new(sev) + end + else + @severity << SummarySeverity.new(@xml_hash.severity) + end + end + return @severity + end end end end \ No newline at end of file diff --git a/lib/veracode/api/types.rb b/lib/veracode/api/types.rb index 45b1b7a..96e2820 100644 --- a/lib/veracode/api/types.rb +++ b/lib/veracode/api/types.rb @@ -1,94 +1,198 @@ + require 'base64' -require 'roxml' +# Veracode API General Types used by Summary and Detailed results as well as Application Build API +# module Veracode - module Result + module Result + # Base Class for result class Base - include ROXML + + def self.api_field(name, args) + send(:define_method, name) do + return @xml_hash.send(args[:tag].to_sym) + end + end + + def self.api_type_field(name, args) + send(:define_method, name) do + begin + tmp = eval("@" + name.to_s) + tmp ||= args[:as].new(@xml_hash.send(args[:tag].to_sym)) + instance_variable_set("@#{name}", tmp) + return tmp + rescue NoMethodError + end + end + end + # Takes Hash of XML stores, hash is has addition to allow dot access to components + def initialize(xml_hash) + @xml_hash = xml_hash + end end class Screenshot < Base - xml_reader :format, :from => "@format" + api_field :format, :tag => :format - xml_reader(:data) {|b64data| Base64.decode64(b64data) } + def data + @scr_data ||= Base64.decode64(@xml_hash.data) + + return @scr_data + end + #xml_reader(:data) {|b64data| Base64.decode64(b64data) } end class BulletType < Base - xml_reader :text, :from => "@text" + api_field :text, :tag => :text end class ParaType < Base - xml_reader :bulletitem, :as => [BulletType] - xml_reader :text, :from => "@text" + #xml_reader :bulletitem, :as => [BulletType] + api_field :text, :tag => :text + + def bulletitem + @bulletitems ||= [] + begin + if @bulletitems.empty? + if @xml_hash.bulletitem.class == Array + @bulletitems = @xml_hash.bulletitem.map do |item| + BulletType.new(item) + end + else + @bulletitems << BulletType.new(@xml_hash.bulletitem) + end + end + rescue NoMethodError + end + return @bulletitems + end end class TextType < Base - xml_reader :text, :from => "text/@text" + #xml_reader :text, :from => "text/@text" end class Para < Base - xml_reader :para, :as => [ParaType] + #xml_reader :para, :as => [ParaType] + def para + @paras ||= [] + if @paras.empty? + if @xml_hash.para.class == Array + @paras = @xml_hash.para.map do |para| + ParaType.new(para) + end + else + @paras << ParaType.new(@xml_hash.para) + end + end + return @paras + end end class AppendixType < Base - xml_reader :description - xml_reader :screenshot, :as => [Screenshot] - xml_reader :code + api_field :description, :tag => :description + #xml_reader :screenshot, :as => [Screenshot] + def screenshot + @screenshots ||= [] + begin + if @screenshots.empty? + if @xml_hash.screenshot.class == Array + @screenshots = @xml_hash.screenshot.map do |screenshot| + Screenshot.new(screenshot) + end + else + @screenshots << Screenshot.new(@xml_hash.screenshot) + end + end + rescue NoMethodError + end + return @screenshots + end + api_field :code, :tag => :code end - class Modules < Base - xml_reader :name, :from => "@name" - xml_reader :compiler, :from => "@compiler" - xml_reader :os, :from => "@os" - xml_reader :architecture, :from => "@architecture" - xml_reader :score, :from => "@score" - xml_reader :numflawssev0, :from => "@numflawssev0" - xml_reader :numflawssev1, :from => "@numflawssev1" - xml_reader :numflawssev2, :from => "@numflawssev2" - xml_reader :numflawssev3, :from => "@numflawssev3" - xml_reader :numflawssev4, :from => "@numflawssev4" - xml_reader :numflawssev5, :from => "@numflawssev5" + class Module < Base + api_field :name, :tag => :name + api_field :compiler, :tag => :compiler + api_field :os, :tag => :os + api_field :architecture, :tag => :architecture + api_field :score, :tag => :score + api_field :numflawssev0, :tag => :numflawssev0 + api_field :numflawssev1, :tag => :numflawssev1 + api_field :numflawssev2, :tag => :numflawssev2 + api_field :numflawssev3, :tag => :numflawssev3 + api_field :numflawssev4, :tag => :numflawssev4 + api_field :numflawssev5, :tag => :numflawssev5 end class Analysis < Base - xml_reader :rating, :from => "@rating" - xml_reader :score, :from => "@score" - xml_reader :submitted_date, :from => "@submitted_date" - xml_reader :published_date, :from => "@published_date" - xml_reader :mitigated_rating, :from => "@mitigated_rating" - xml_reader :mitigated_score, :from => "@mitigated_score" - xml_reader :analysis_size_bytes, :from => "@analysis_size_bytes" - xml_reader :next_scan_due, :from => "@next_scan_due" + + api_field :analysis_size_bytes, :tag => :analysis_size_bytes + api_field :rating, :tag => :rating + api_field :score, :tag => :score + api_field :mitigated_rating, :tag => :mitigated_rating + api_field :mitigated_score, :tag => :mitigated_score + api_field :submitted_date, :tag => :submitted_date + api_field :published_date, :tag => :published_date + api_field :next_scan_due, :tag => :next_scan_due - xml_reader :modules, :as => [Modules] + def modules + @modules ||= [] + if @modules.empty? + if @xml_hash.modules.class == Array + @modules = @xml_hash.modules.map do |modules| + Module.new(modules.module) + end + else + @modules << Module.new(@xml_hash.modules.module) + end + end + return @modules + end end class ManualAnalysis < Base - xml_reader :rating, :from => "@rating" - xml_reader :score, :from => "@score" - xml_reader :mitigated_rating, :from => "@mitigated_rating" - xml_reader :mitigated_score, :from => "@mitigated_score" - xml_reader :submitted_date, :from => "@submitted_date" - xml_reader :published_date, :from => "@published_date" - xml_reader :next_scan_due, :from => "@next_scan_due" - - xml_reader :modules, :as => [Modules] - xml_reader :cia_adjustment - xml_reader :delivery_consultant + api_field :rating, :tag => :rating + api_field :score, :tag => :score + api_field :mitigated_rating, :tag => :mitigated_rating + api_field :mitigated_score, :tag => :mitigated_score + api_field :submitted_date, :tag => :submitted_date + api_field :published_date, :tag => :published_date + api_field :next_scan_due, :tag => :next_scan_due + api_field :cia_adjustment, :tag => :cia_adjustment + api_field :delivery_consultant, :tag => :delivery_consultant + + def modules + @modules ||= [] + if @modules.empty? + if @xml_hash.modules.class == Array + @modules = @xml_hash.modules.map do |modules| + Module.new(modules.module) + end + else + @modules << Module.new(@xml_hash.modules.module) + end + end + return @modules + end end class FlawStatus < Base - xml_reader :new, :from => :attr - xml_reader :reopen, :from => :attr - xml_reader :open, :from => :attr - xml_reader :fixed, :from => :attr - xml_reader :total, :from => :attr - xml_reader :not_mitigated, :from => :attr - xml_reader :sev_1_change, :from => :attr - xml_reader :sev_2_change, :from => :attr - xml_reader :sev_3_change, :from => :attr - xml_reader :sev_4_change, :from => :attr - xml_reader :sev_5_change, :from => :attr + api_field :new_flaws, :tag => :new + api_field :reopen_flaws, :tag => :reopen + #api_field :open_flaws, :tag => :open + api_field :fixed_flaws, :tag => :fixed + api_field :total_flaws, :tag => :total + api_field :not_mitigated, :tag => :not_mitigated + api_field :sev_1_change, :tag => :sev_1_change + api_field :sev_2_change, :tag => :sev_2_change + api_field :sev_3_change, :tag => :sev_3_change + api_field :sev_4_change, :tag => :sev_4_change + api_field :sev_5_change, :tag => :sev_5_change + + def open_flaws + return @xml_hash['open'] + end end end end \ No newline at end of file diff --git a/lib/veracode/parser/parser.rb b/lib/veracode/parser/parser.rb new file mode 100644 index 0000000..ccedd8e --- /dev/null +++ b/lib/veracode/parser/parser.rb @@ -0,0 +1,78 @@ +require 'nori' + +require 'rexml/document' + +class ::String + def to_bool + return true if self == true || self =~ (/(true|t|yes|y|1)$/i) + return false if self == false || self =~ (/(false|f|no|n|0)$/i) + raise ArgumentError.new("invalid value for Boolean: \"#{self}\"") + end +end + +class ::Hash + def method_missing(name) + return self[name] if key? name + self.each { |k,v| return v if k.to_s.to_sym == name } + super.method_missing name + end +end + +class Nori + class XMLUtilityNode + def prefixed_attributes + attributes.inject({}) do |memo, (key, value)| + memo[prefixed_attribute_name("#{key}")] = value + memo + end + end + end +end + +module Veracode + class Parser + def self.parse(xml) + parser = XML::SAXParser.new() + parser.options = {} + REXML::Document.parse_stream(xml, parser) + + parser.stack.length > 0 ? parser.stack.pop.to_hash : {} + + end + end + + module XML + + class SAXParser + attr_accessor :options + def stack + @stack ||= [] + end + + def tag_start(name, attrs) + stack.push Nori::XMLUtilityNode.new(options, name, Hash[*attrs.flatten]) + end + + def tag_end(name) + if stack.size > 1 + last = stack.pop + stack.last.add_node last + end + end + + def text(string) + stack.last.add_node(string) unless string.strip.length == 0 || stack.empty? + end + + # Treat CDATA sections just like text + alias_method :cdata, :text + + def method_missing(sym, *args) + end + + def initialize(callback=nil) + + end + end # SAXParser + end # XML +end \ No newline at end of file diff --git a/lib/veracode/results.rb b/lib/veracode/results.rb index 7f07472..62140c5 100644 --- a/lib/veracode/results.rb +++ b/lib/veracode/results.rb @@ -1,4 +1,5 @@ require 'nokogiri' +require 'veracode/parser/parser' require 'veracode/api/builds' require 'veracode/api/detailed' require 'veracode/api/summary' @@ -18,7 +19,9 @@ def get_application_builds xml = getXML(GET_APP_BUILDS_URI) case xml.code when 200 - builds = Veracode::Result::Builds::Applications.from_xml(xml.body) + clean_xml = xml.body.strip + parsed = Veracode::Parser.parse(clean_xml) + builds = Veracode::Result::Builds::Applications.new(parsed) else xml.error! end @@ -28,7 +31,9 @@ def get_summary_report(build_id) xml = getXML(SUMMARY_REPORT_URI + "?build_id=" + build_id) case xml.code when 200 - report = Veracode::Result::SummaryReport.from_xml(xml.body) + clean_xml = xml.body.strip + parsed = Veracode::Parser.parse(clean_xml) + report = Veracode::Result::SummaryReport.new(parsed.summaryreport) else xml.error! end @@ -38,7 +43,9 @@ def get_detailed_report(build_id) xml = getXML(DETAILED_REPORT_URI + "?build_id=" + build_id) case xml.code when 200 - report = Veracode::Result::DetailedReport.from_xml(xml.body) + clean_xml = xml.body.strip + parsed = Veracode::Parser.parse(clean_xml) + report = Veracode::Result::DetailedReport.new(parsed.detailedreport) else xml.error! end diff --git a/lib/veracode/version.rb b/lib/veracode/version.rb index cc86af6..b0db50b 100644 --- a/lib/veracode/version.rb +++ b/lib/veracode/version.rb @@ -1,5 +1,5 @@ module Veracode module API - VERSION = "0.0.1" + VERSION = "0.2.1" end end diff --git a/spec/lib/veracode/detailed_spec.rb b/spec/lib/veracode/detailed_spec.rb index 77fe6d2..a64135c 100644 --- a/spec/lib/veracode/detailed_spec.rb +++ b/spec/lib/veracode/detailed_spec.rb @@ -43,6 +43,20 @@ @result.manual_analysis.must_be_instance_of Veracode::Result::ManualAnalysis end + it "must be an instance of Veracode::Result::FlawStatus" do + @result.flaw_status.must_be_instance_of Veracode::Result::FlawStatus + end + + it "must be an instance of Veracode::Result::Severity" do + @result.severity.each do |sev| + sev.must_be_instance_of Veracode::Result::Severity + end + end + + it "must be an instance of TrueClass" do + @result.is_latest_build?.must_be_instance_of TrueClass + end + it "must raise method missing if attribute is not present" do lambda { @result.foo_attribute }.must_raise NoMethodError end diff --git a/spec/lib/veracode/summary_spec.rb b/spec/lib/veracode/summary_spec.rb index 7019647..09f618e 100644 --- a/spec/lib/veracode/summary_spec.rb +++ b/spec/lib/veracode/summary_spec.rb @@ -42,6 +42,20 @@ it "must be an instance of Veracode::Result::ManualAnalysis" do @result.manual_analysis.must_be_instance_of Veracode::Result::ManualAnalysis end + + it "must be an instance of Veracode::Result::FlawStatus" do + @result.flaw_status.must_be_instance_of Veracode::Result::FlawStatus + end + + it "must be an instance of Veracode::Result::SummarySeverity" do + @result.severity.each do |sev| + sev.must_be_instance_of Veracode::Result::SummarySeverity + end + end + + it "must be an instance of TrueClass" do + @result.is_latest_build?.must_be_instance_of TrueClass + end it "must raise method missing if attribute is not present" do lambda { @result.foo_attribute }.must_raise NoMethodError diff --git a/veracode-api.gemspec b/veracode-api.gemspec index 56531c6..6d021ed 100644 --- a/veracode-api.gemspec +++ b/veracode-api.gemspec @@ -19,6 +19,8 @@ Gem::Specification.new do |s| s.require_paths = ["lib"] s.add_dependency "httparty" + s.add_dependency "rexml" + s.add_dependency "nori" s.add_dependency "nokogiri" s.add_dependency "xml-simple" s.add_dependency "roxml"