我是一名PHP开发人员,他试图熟练使用Ruby。我现在正在削减其中一个项目是源代码审计工具,它可以扫描webapp文件,查找多种Web编程语言中的潜在危险函数。找到匹配项后,脚本会将相关信息保存在poi
(兴趣点)类中,以便稍后显示。
该类的示例实例看起来像这样(在YAML中建模):
poi:
file_type: "php"
file: "the-scanned-file.php"
line_number: 100
match: "eval()"
snippet: "echo eval()"
在展示中,我想像这样组织这些兴趣点:
- file_type
-- file
--- match (the searched payload)
因此,在演示之前,我正在尝试将poi
个对象的平面数组构建为镜像上面结构的哈希。这将允许我简单地遍历散列中的项目以产生所需的屏幕上组织。 (或者至少,这是计划。)
现在,对于我的问题:我如何在Ruby中做到这一点?
在PHP中,我可以很容易地做到这样的事情:
<?php
$sorted_pois = array();
foreach($points_of_interest as $point){
$sorted_pois[$point->file_type][$point->file][$point->match][] = $point;
}
?>
我已经尝试过将这种想法从PHP转换为Ruby,但无济于事:
sorted_pois = {}
@points_of_interest.each_with_index do |point, index|
sorted_pois[point.file_type.to_sym][point.file.to_sym][point.match.to_sym].push point
end
我已经花了几个小时在这上面,此时我正好碰到我的头靠在墙上,所以我想我离开了基地。在Ruby中处理这个问题的正确方法是什么?
更新
作为参考,这是我定义的精确方法:
# sort the points of interest into a structured hash
def sort
sorted_pois = {}
@points_of_interest.each_with_index do |point, index|
sorted_pois[point.file_type.to_sym][point.file.to_sym][point.match.to_sym].push point
end
end
这是我运行代码时收到的错误:
./lib/models/vulnscanner.rb:63:in `sort': undefined method `[]' for nil:NilClass (NoMethodError)
from /usr/lib/ruby/1.8/rubygems/custom_require.rb:31:in `each_with_index'
from ./lib/models/vulnscanner.rb:62:in `each'
from ./lib/models/vulnscanner.rb:62:in `each_with_index'
from ./lib/models/vulnscanner.rb:62:in `sort'
from ./webapp-vulnscan:69
第62行(你可以推断)特别是这一行:
@points_of_interest.each_with_index do |point, index|
作为一个额外的参考,这里是@points_of_interest
在转换为YAML时的样子(<1}}:
- !ruby/object:PoI
file: models/couponkimoffer.php
file_type: php
group: :dangerous_functions
line_number: "472"
match: `
snippet: ORDER BY `created_at` DESC
- !ruby/object:PoI
file: models/couponkimoffer.php
file_type: php
group: :dangerous_functions
line_number: "818"
match: `
snippet: WHERE `company_slug` = '$company_slug'
- !ruby/object:PoI
file: models/couponkimoffer.php
file_type: php
group: :dangerous_functions
line_number: "819"
match: `
snippet: ORDER BY `created_at` DESC
答案 0 :(得分:28)
@ John的Enumerable#group_by
建议是解决您需求的好方法。另一种方法是创建一个自动生存的哈希(就像你似乎在PHP中一样):如下所示:
hash = Hash.new{ |h,k| h[k] = Hash.new(&h.default_proc) }
hash[:a][:b][:c] = 42
p hash
#=> {:a=>{:b=>{:c=>42}}}
请注意,如果您访问不存在的密钥,这种自动生存可能会“危险”,因为它会为您创建:
p hash["does this exist?"]
#=> {}
p hash
#=> {:a=>{:b=>{:c=>42}}, "does this exist?"=>{}}
如果您使用default_proc
首先测试密钥,您仍然可以使用活跃key?
而不会遇到此危险:
val = hash["OH NOES"] if hash.key?("OH NOES")
#=> nil
p hash
#=> {:a=>{:b=>{:c=>42}}, "does this exist?"=>{}}
FWIW,你得到的错误是,“嘿,你把[]
放到评估为nil
的内容之后,而nil
没有[]
方法。“具体来说,你的代码......
sorted_pois[point.file_type.to_sym]
评估为nil
(因为哈希值没有此键的值)然后您试图要求
nil[point.file.to_sym]
答案 1 :(得分:7)
您可能对group_by感兴趣。
样本用法:
birds = ["Golden Eagle", "Gyrfalcon", "American Robin",
"Mountain BlueBird", "Mountain-Hawk Eagle"]
grouped_by_first_letter = birds.group_by { |s| s[0] }
# { "G"=>["Golden Eagle", "Gyrfalcon"], "A"=>["American Robin"],
# "M"=>["Mountain BlueBird", "Mountain-Hawk Eagle"] }
答案 2 :(得分:2)
上面示例的明显问题是您尝试使用的嵌套哈希和数组不存在。试试这个:
sorted_pois = {}
pois.each do |point|
# sanitize data - convert to hash of symbolized keys and values
poi = Hash[ %w{file_type file match}.map do |key|
[key.to_sym, point.send(key).to_sym]
end ]
# create nested hash/array if it doesn't already exist
sorted_pois[ poi[:file_type] ] ||= {}
sorted_pois[ poi[:file_type] ][ poi[:file] ] ||= {}
sorted_pois[ poi[:file_type] ][ poi[:file] ][ poi[:match] ] ||= []
sorted_pois[ poi[:file_type] ][ poi[:file] ][ poi[:match] ] << point
end