Skip to content
This repository has been archived by the owner on May 3, 2020. It is now read-only.

Improved multi language support #455

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion helpers/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ def url_escape_hash(hash)
end

v = new_v
elsif (k == 'remediation') || (k == 'overview') || (k == 'poc') || (k == 'affected_hosts') || (k == 'references')
elsif k.start_with?('remediation', 'overview', 'poc', 'affected_hosts', 'references')
new_v = "<paragraph>#{v}</paragraph>"
v = new_v
end
Expand Down
16 changes: 15 additions & 1 deletion model/master.rb
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,22 @@ class TemplateFindings
property :nist_rating, String, :required => false

property :language, String, required: false
has n, :translations, 'TemplateFindingsTranslation'
end

class TemplateFindingsTranslation
include DataMapper::Resource

property :id, Serial
property :language, String, required: true, length: 100
property :title, String, length: 200
property :overview, String, length: 20_000, required: false
property :poc, String, length: 20_000, required: false
property :remediation, String, length: 20_000, required: false
property :references, String, length: 20_000, required: false

belongs_to :finding, 'TemplateFindings', required: true
end
class Findings
include DataMapper::Resource

Expand Down Expand Up @@ -353,7 +367,7 @@ class Reports
property :user_defined_variables, String, length: 10_000
property :scoring, String, length: 100

property :language, String, required: false
property :language, String, required: true, default: "undefined" ,length: 50
end

class Attachments
Expand Down
93 changes: 90 additions & 3 deletions routes/master.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
post '/master/findings/new' do
data = url_escape_hash(request.POST)

@available_languages = config_options['languages']
return "Language \"#{data["language"]}\" is absent from the config file" if !@available_languages.include?(data["language"])

if config_options['dread']
data['dread_total'] = data['damage'].to_i + data['reproducability'].to_i + data['exploitability'].to_i + data['affected_users'].to_i + data['discoverability'].to_i
end
Expand All @@ -63,6 +66,9 @@
data['risk'] = severity_val + likelihood_val
end

# split out language
language = data['language']
data.delete('language')
# Create NIST800 finding in the main database
if(config_options["nist800"])
# call nist800 helper function
Expand All @@ -85,6 +91,8 @@
# find the id of the newly created finding so we can link mappings to it
@newfinding = TemplateFindings.first(title: data['title'], order: [:id.desc], limit: 1)

# save translation
@finding.translations.create(:finding => @finding, :language => language, :title => data['title'], :poc => data['poc'], :overview => data['overview'], :remediation => data['remediation'])
# save mapping data
if config_options['nessusmap'] && nessusdata['pluginid']
nessusdata['templatefindings_id'] = @finding.id
Expand All @@ -111,7 +119,6 @@
get '/master/findings/:id/edit' do
@master = true

@languages = config_options['languages']
@dread = config_options['dread']
@cvss = config_options['cvss']
@cvssv3 = config_options['cvssv3']
Expand All @@ -121,6 +128,7 @@
@burpmap = config_options['burpmap']
@vulnmap = config_options['vulnmap']

@available_languages = config_options['languages']
# Check for kosher name in report name
id = params[:id]

Expand Down Expand Up @@ -154,7 +162,9 @@
data['approved'] = data['approved'] == 'on'

# to prevent title's from degenerating with &gt;, etc. [issue 237]
data['title'] = data['title'].gsub('&amp;', '&')
if data.has_key? 'title'
data['title'] = data['title'].gsub('&amp;', '&')
end

if config_options['dread']
data['dread_total'] = data['damage'].to_i + data['reproducability'].to_i + data['exploitability'].to_i + data['affected_users'].to_i + data['discoverability'].to_i
Expand Down Expand Up @@ -208,8 +218,33 @@
data.delete('msf_ref')
vulnmappingdata['templatefindings_id'] = id

# split out and save any translation data
translated_parts_from_findings = @finding.translations.all(:finding_id => id)
translated_parts_from_findings.each do |translated_parts_from_finding|
translated_finding_data = {}
if data["title_#{translated_parts_from_finding.language}"].empty?
status 500
return "Title can't be empty"
end
# to prevent title's from degenerating with &gt;, etc. [issue 237]
translated_finding_data['title'] = data["title_#{translated_parts_from_finding.language}"].to_s.gsub('&amp;', '&')
data.delete("title_#{translated_parts_from_finding.language}")
translated_finding_data["overview"] = data["overview_#{translated_parts_from_finding.language}"]
data.delete("overview_#{translated_parts_from_finding.language}")
translated_finding_data["poc"] = data["poc_#{translated_parts_from_finding.language}"]
data.delete("poc_#{translated_parts_from_finding.language}")
translated_finding_data["remediation"] = data["remediation_#{translated_parts_from_finding.language}"]
data.delete("remediation_#{translated_parts_from_finding.language}")
translated_finding_data["references"] = data["references_#{translated_parts_from_finding.language}"]
data.delete("references_#{translated_parts_from_finding.language}")
if !translated_parts_from_finding.update(translated_finding_data)
return "<p>The following error(s) were found while trying to update translated finding data: </p><p>#{translated_parts_from_finding.errors.full_messages.flatten.join(', ')}<p>"
end
end
# Update the finding with templated finding stuff
@finding.update(data)
if !@finding.update(data)
return "<p>The following error(s) were found while trying to update finding: </p><p>#{@finding.errors.full_messages.flatten.join(', ')}<p>"
end

# save nessus mapping data to db
if config_options['nessusmap']
Expand All @@ -232,6 +267,53 @@
redirect to('/master/findings')
end

post '/master/findings/:id/add_language' do
id = params[:id]

#check if finding exist
finding = TemplateFindings.first(:id => id)
return "No Such Finding #{id}" if finding.nil?

error = mm_verify(request.POST)
return error if error.size > 1
data = url_escape_hash(request.POST)

@available_languages = config_options['languages']
return "Language \"#{data["language"]}\" is absent from the config file" if !@available_languages.include?(data["language"])


finding_translation = finding.translations.first(:language => data["language"])
return 'The translation already exists' if !finding_translation.nil?

#if the finding language is undefined, we update the language "undefined"
if finding.translations.empty?
translation = finding.translations.create(:finding => finding, :language => data["language"], :title => finding.title, :overview => finding.overview, :poc => finding.poc, :remediation => finding.remediation, :references => finding.references)
else
translation = finding.translations.create(:finding => finding, :language => data["language"])
end
return "Error while creating translation : #{translation.errors.full_messages.flatten.join(', ')}" if !translation.saved?
redirect to("/master/findings/#{id}/edit")
end

get '/master/findings/:id/define_language/:language' do
id = params[:id]
language = params[:language]

finding = TemplateFindings.first(:id => id)
return "No Such Finding #{id}" if finding.nil?

@available_languages = config_options['languages']
return "Language \"#{data["language"]}\" is absent from the config file" if !@available_languages.include?(language)

finding_translation = finding.translations.first(:language => language)
return 'The translation already exists' if !finding_translation.nil?

finding_translation = finding.translations.first(:language => "undefined")
finding_translation.update(:language => language)
return "Error while updating translations : #{translation.errors.full_messages.flatten.join(', ')}" if !translation.saved?

redirect to("/master/findings/#{id}/edit")
end
# Delete a template finding
get '/master/findings/delete/:id' do
id = params[:id]
Expand All @@ -240,6 +322,11 @@
finding = TemplateFindings.first(id: current_id)
return "No Such Finding : #{current_id}" if finding.nil?

# delete the entries
finding.translations.each do |translation|
translation.destroy
end

# delete the entries
finding.destroy

Expand Down
38 changes: 28 additions & 10 deletions routes/report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,13 @@
# Create a report
get '/report/new' do
@templates = Xslt.all
@assessment_types = config_options['report_assessment_types']
@languages = config_options['languages']
haml :new_report, encode_html: true
if config_options['languages']
@languages = config_options['languages']
else
return "No language found in your config.json file. Please, add your langagues in the config.json file."
end
@assessment_types = config_options["report_assessment_types"]
haml :new_report, :encode_html => true
end

# Create a report
Expand Down Expand Up @@ -373,7 +377,11 @@
@templates = Xslt.all(order: [:report_type.asc])
@plugin_side_menu = get_plugin_list('user')
@assessment_types = config_options['report_assessment_types']
@languages = config_options['languages']
if config_options['languages']
@languages = config_options['languages']
else
return "No language found in your config.json file. Please, add your langagues in the config.json file."
end
@risk_scores = %w[Risk DREAD CVSS CVSSv3 RiskMatrix NIST800]

return 'No Such Report' if @report.nil?
Expand Down Expand Up @@ -754,7 +762,7 @@
@languages = config_options['languages']

# Query for all Findings
@findings = TemplateFindings.all(approved: true, order: [:title.asc])
@findings = TemplateFindings.all(approved: true, order: [:title.asc], :translations => {:language => [@report.language]}) + TemplateFindings.all(order: [:title.asc], :translations => nil )

haml :findings_add, encode_html: true
end
Expand All @@ -776,12 +784,22 @@
params[:finding].each do |finding|
templated_finding = TemplateFindings.first(id: finding.to_i)

templated_finding.id = nil
attr = templated_finding.attributes
attr.delete(:approved)
# Set translation in approriate attributes
if !templated_finding.translations.first(:language => @report.language).nil?
templated_finding.title = templated_finding.translations.first(:language => @report.language).title
templated_finding.overview = templated_finding.translations.first(:language => @report.language).overview
templated_finding.poc = templated_finding.translations.first(:language => @report.language).poc
templated_finding.remediation = templated_finding.translations.first(:language => @report.language).remediation
templated_finding.references = templated_finding.translations.first(:language => @report.language).references
end

templated_finding.id = nil
attr = templated_finding.attributes
attr.delete(:approved)
attr.delete(:translations)
attr['master_id'] = finding.to_i
@newfinding = Findings.new(attr)
@newfinding.report_id = id
@newfinding = Findings.new(attr)
@newfinding.report_id = id

# because of multiple scores we need to make sure all are set
# => leave it up to the user to make the calculation if they switch mid report
Expand Down
9 changes: 9 additions & 0 deletions serpico.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,13 @@

end

# Replace old language field for a translation class
if !Pathname("verify/language_db").exist?
@findings = TemplateFindings.all(:language.not => nil)
@findings.each do |finding|
finding.translations.create(:finding => finding, :language => finding.language, :title => finding.title, :overview =>finding.overview, :poc => finding.poc, :remediation => finding.remediation, :references => finding.references)
end
File.open("verify/language_db", "w") {}
puts "|+| [#{DateTime.now.strftime('%d/%m/%Y %H:%M')}] Updated database"
end
Rack::Handler::WEBrick.run Server, server_options
Empty file added verify/blank
Empty file.
38 changes: 35 additions & 3 deletions views/findings_add.haml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@
%table{ :style => "width: 100%" }
- @findings.each do |finding|
- if finding.type == type
- if @languages and @languages.size > 1
- next unless (finding.language == @report.language) or (@report.language == "English" and finding.language == nil)
%tr
- if finding.translations.first.nil?
%td.searchable{ :style => "width: 80%", :"data-index" => "#{finding.title.downcase}" }
- if @autoadd
- if @autoadd_findings.include?(finding.id.to_s) and not @dup_findings.include?(finding.id)
Expand Down Expand Up @@ -68,9 +67,42 @@
%a.btn.btn-info{ :href => "/master/findings/#{finding.id}/preview" }
%i.icon-play-circle.icon-white{ :title => "Preview" }
- else
%a{ :class => "btn btn-info", :href => "/master/findings/#{finding.id}/preview"}
%i{:class => 'icon-play-circle icon-white', :title => 'Preview'}
- elsif !finding.translations.first(:language => @report.language).nil?
%td.searchable{ :style => "width: 80%", :"data-index" => "#{finding.translations.first(:language => @report.language).title.downcase}" }
- if @autoadd
- if @autoadd_findings.include?(finding.id.to_s) and not @dup_findings.include?(finding.id)
%input{ :id => "finding_#{finding.id}", :type => "checkbox", :name => "finding[]", :value => "#{finding.id}", :checked => "" }
- else
%input{ :id => "finding_#{finding.id}", :type => "checkbox", :name => "finding[]", :value => "#{finding.id}" }
- else
%input{ :id => "finding_#{finding.id}", :type => "checkbox", :name => "finding[]", :value => "#{finding.id}" }
%label.control-label{ :for => "finding_#{finding.id}" }
#{finding.translations.first(:language => @report.language).title}
- if @dup_findings
- if @dup_findings.include?(finding.id)
.label.label-warning Duplicate
- if finding.translations.first(:language => @report.language).overview
%i.icon-chevron-down#actionButton{ "data-toggle" => "collapse", "data-target" => "#info_#{finding.id}" }
.info.collapse.out{ :id => "info_#{finding.id}" }
#{finding.translations.first(:language => @report.language).overview.gsub("<paragraph>","<br />").gsub("</paragraph>","").gsub("<bullet>","&#x2022;").gsub("</bullet>","")}
- if @autoadd_hosts
- @autoadd_hosts.keys.each do |x|
- if finding.id == x.to_i
- @autoadd_hosts[x].each do |ip|
.label.label-default #{ip}
- iplist = @autoadd_hosts[x].join(",")
%input{ :type => "hidden", :name => "finding#{finding.id.to_s}", :value => "#{iplist}" }
%td.searchable{ :style => "width: 20%", :"data-index" => "#{finding.title.downcase}" }
- if @master
%a.btn.btn-warning{ :href => "/master/findings/#{finding.id}/edit" }
%i.icon-pencil.icon-white{ :title => "Edit" }
%a.btn.btn-info{ :href => "/master/findings/#{finding.id}/preview" }
%i.icon-play-circle.icon-white{ :title => "Preview" }

- else
%a{ :class => "btn btn-info", :href => "/master/findings/#{finding.id}/preview"}
%i{:class => 'icon-play-circle icon-white', :title => 'Preview'}
%br
%input.btn.btn-default{ :type => "submit", :value => "Add" }
%a.btn.btn-default{ :href => "/report/#{@report.id}/findings" }
Expand Down