inspec/lib/utils/nginx_parser.rb
Dominik Richter 56549aed82 add nginx_conf resource (#1889)
The resource itself only offers contents and params right now. It resolved
all include calls it can find and creates the aggregated config object.

This is limited in functionality. One last (set of) PR(s) is needed to
add an interface that makes querying this config file easier. It is due
to the file's inherent complexity that I want to explore which methods
are needed to be effective. In the meantime, this resource offers accessors
to the underlying data that are stable.

Signed-off-by: Dominik Richter <dominik.richter@gmail.com>
2017-06-26 06:37:41 -07:00

76 lines
2.1 KiB
Ruby

# encoding: utf-8
# author: Dominik Richter
# author: Christoph Hartmann
require 'parslet'
class NginxParser < Parslet::Parser
root :outermost
# only designed for rabbitmq config files for now:
rule(:outermost) { filler? >> exp.repeat }
rule(:filler?) { one_filler.repeat }
rule(:one_filler) { match('\s+') | match["\n"] | comment }
rule(:space) { match('\s+') }
rule(:comment) { str('#') >> (match["\n\r"].absent? >> any).repeat }
rule(:exp) {
section | assignment
}
rule(:assignment) {
(identifier >> values.maybe.as(:args)).as(:assignment) >> str(';') >> filler?
}
rule(:identifier) {
(match('[a-zA-Z]') >> match('\S').repeat).as(:identifier) >> space >> space.repeat
}
rule(:value) {
((match('[#;{]').absent? >> any) >> (
str('\\') >> any | match('[#;{]|\s').absent? >> any
).repeat).as(:value) >> space.repeat
}
rule(:values) {
value.repeat >> space.maybe
}
rule(:section) {
identifier.as(:section) >> values.maybe.as(:args) >> str('{') >> filler? >> exp.repeat.as(:expressions) >> str('}') >> filler?
}
end
class NginxTransform < Parslet::Transform
Group = Struct.new(:id, :args, :body)
Exp = Struct.new(:key, :vals)
def self.assemble_binary(seq)
b = ErlangBitstream.new
seq.each { |i| b.add(i) }
b.value
end
rule(section: { identifier: simple(:x) }, args: subtree(:y), expressions: subtree(:z)) { Group.new(x.to_s, y, z) }
rule(assignment: { identifier: simple(:x), args: subtree(:y) }) { Exp.new(x.to_s, y) }
rule(value: simple(:x)) { x.to_s }
end
class NginxConfig
def self.parse(content)
lex = NginxParser.new.parse(content)
tree = NginxTransform.new.apply(lex)
gtree = NginxTransform::Group.new(nil, '', tree)
read_nginx_group(gtree)
rescue Parslet::ParseFailed => err
raise "Failed to parse NginX config: #{err}"
end
def self.read_nginx_group(t)
agg_conf = Hash.new([])
agg_conf['_'] = t.args unless t.args == ''
groups, conf = t.body.partition { |i| i.is_a? NginxTransform::Group }
conf.each { |x| agg_conf[x.key] += [x.vals] }
groups.each { |x| agg_conf[x.id] += [read_nginx_group(x)] }
agg_conf
end
end