require "helper" require "inspec/objects" describe "Objects" do describe "Inspec::Test" do let(:obj) { Inspec::Test.new } it 'constructs a simple resource + its("argument")' do obj.qualifier = [["resource"], ["version"]] obj.matcher = "cmp >=" obj.expectation = "2.4.2" _(obj.to_ruby).must_equal ' describe resource do its("version") { should cmp >= "2.4.2" } end '.strip end # same as the above test but with it it "constructs a simple resource.argument" do # [''] forces an 'it' instead of 'its': obj.qualifier = [["resource"], ["version"], [""]] obj.matcher = "cmp >=" obj.expectation = "2.4.2" _(obj.to_ruby).must_equal ' describe resource.version do it { should cmp >= "2.4.2" } end '.strip end it "constructs a simple resource+argument with to_s" do obj.qualifier = [["resource"], ["to_s"]] obj.matcher = "cmp" obj.expectation = Regexp.new("^Desc.+$") _(obj.to_ruby).must_equal ' describe resource.to_s do it { should cmp(/^Desc.+$/) } end '.strip end it "constructs a simple resource+argument with to_i" do obj.qualifier = [["resource"], ["to_i"]] obj.matcher = "cmp >" obj.expectation = 3 _(obj.to_ruby).must_equal ' describe resource.to_i do it { should cmp > 3 } end '.strip end it "constructs a simple resource+argument with array accessors" do obj.qualifier = [["resource"], ["name[2]"]] obj.matcher = "exist" obj.matcher = "eq" obj.expectation = "mytest" _(obj.to_ruby).must_equal ' describe resource.name[2] do it { should eq "mytest" } end '.strip end it "constructs a simple resource+argument with method calls" do obj.qualifier = [["resource"], %w{hello world}] obj.matcher = "eq" obj.expectation = "mytest" _(obj.to_ruby).must_equal ' describe resource.hello("world") do it { should eq "mytest" } end '.strip end it "constructs a simple resource+argument with method calls" do obj.qualifier = [["resource"], %w{hello world}] obj.matcher = "be_in" obj.expectation = %w{mytest mytest2 mytest3} _(obj.to_ruby).must_equal ' describe resource.hello("world") do it { should be_in ["mytest", "mytest2", "mytest3"] } end '.strip end it "constructs a simple resource+argument with method calls" do obj.qualifier = [["resource"], %w{hello world}] obj.matcher = "be_in" obj.negate! obj.expectation = %w{mytest2 mytest3 mytest4} _(obj.to_ruby).must_equal ' describe resource.hello("world") do it { should_not be_in ["mytest2", "mytest3", "mytest4"] } end '.strip end it "constructs a simple resource+argument with method calls" do obj.qualifier = [['["item1","item2","item3"]']] obj.matcher = "be_in" obj.expectation = %w{item1 item2 item3 item4 item5} _(obj.to_ruby).must_equal ' describe ["item1","item2","item3"] do it { should be_in ["item1", "item2", "item3", "item4", "item5"] } end '.strip end it "constructs a simple resource+argument with method calls" do obj.qualifier = [["resource"], [:mode]] obj.matcher = "cmp" obj.expectation = "0755" _(obj.to_ruby).must_equal ' describe resource do its("mode") { should cmp "0755" } end '.strip end it "constructs a resource+argument block with method call, matcher and expectation" do obj.qualifier = [["command", "ls /etc"], ["exit_status"]] obj.matcher = "eq" obj.expectation = 0 _(obj.to_ruby).must_equal ' describe command("ls /etc") do its("exit_status") { should eq 0 } end '.strip end it "constructs a simple describe with static data, negated regex matcher and expectation" do obj.qualifier = [['"aaa"']] obj.matcher = "match" obj.negate! obj.expectation = Regexp.new("^aa.*") _(obj.to_ruby).must_equal ' describe "aaa" do it { should_not match(/^aa.*/) } end '.strip end it "constructs a resource+argument block without a property call" do obj.qualifier = [%w{service avahi-daemon}] obj.qualifier.push(["info['properties']['UnitFileState']"]) obj.expectation = "enabled" obj.matcher = "eq" _(obj.to_ruby).must_equal ' describe service("avahi-daemon").info[\'properties\'][\'UnitFileState\'] do it { should eq "enabled" } end '.strip end it "constructs a simple resource + only_if" do obj.qualifier = [["resource"], ["version"]] obj.matcher = "cmp >=" obj.expectation = "2.4.2" obj.only_if = "package('ntp').installed?" _(obj.to_ruby).must_equal ' only_if { package(\'ntp\').installed? } describe resource do its("version") { should cmp >= "2.4.2" } end '.strip end end describe "Inspec::EachLoop, each_loop" do it "constructs an each loop to match listening addresses" do loop_obj = Inspec::EachLoop.new loop_obj.qualifier = [["port", 25]] loop_obj.qualifier.push(["addresses"]) obj = Inspec::Test.new obj.matcher = "match" obj.negate! obj.expectation = "0.0.0.0" loop_obj.add_test(obj) _(loop_obj.to_ruby).must_equal ' port(25).addresses.each do |entry| describe entry do it { should_not match "0.0.0.0" } end end '.strip end end describe "Inspec::List" do it "constructs a list filtering test" do list_obj = Inspec::List.new([["passwd"]]) list_obj.qualifier.push(["where { user =~ /^(?!root|sync|shutdown|halt).*$/ }"]) obj = Inspec::Test.new obj.qualifier = list_obj.qualifier obj.matcher = "be_empty" obj.qualifier.push(["entries"]) obj.negate! _(obj.to_ruby).must_equal ' describe passwd.where { user =~ /^(?!root|sync|shutdown|halt).*$/ } do its("entries") { should_not be_empty } end '.strip end end describe "Inspec::OrTest and Inspec::Control" do let(:obj1) do obj1 = Inspec::Test.new obj1.qualifier = [["command", "ls /etc"], ["exit_status"]] obj1.matcher = "eq" obj1.expectation = 0 obj1 end let(:obj2) do obj2 = obj1.dup obj2.negate! obj2.expectation = 100 obj2 end it "constructs a simple describe.one block wrapping two tests" do or_obj = Inspec::OrTest.new([obj1, obj2]) _(or_obj.to_ruby).must_equal ' describe.one do describe command("ls /etc") do its("exit_status") { should eq 0 } end describe command("ls /etc") do its("exit_status") { should_not eq 100 } end end '.strip end it "negates a describe.one block, wow!" do or_obj = Inspec::OrTest.new([obj1, obj2]) or_obj.negate! _(or_obj.to_ruby).must_equal ' describe command("ls /etc") do its("exit_status") { should_not eq 0 } end describe command("ls /etc") do its("exit_status") { should eq 100 } end '.strip end it "loops a describe.one block, ooooooo!" do res = Inspec::EachLoop.new res.qualifier.push(["(1..5)"]) # already defined in the let block: obj1.matcher = "eq entity" obj2.matcher = "eq entity" obj1.remove_expectation obj2.remove_expectation or_obj = Inspec::OrTest.new([obj1, obj2]) res.tests = [or_obj] _(res.to_ruby).must_equal ' (1..5).each do |entry| describe.one do describe command("ls /etc") do its("exit_status") { should eq entity } end describe command("ls /etc") do its("exit_status") { should_not eq entity } end end end '.strip end it "constructs a control" do control = Inspec::Control.new control.add_test(obj1) control.id = "sample.control.id" control.title = "Sample Control Important Title" control.descriptions = { default: "The most critical control the world has ever seen", rationale: "It is needed to save the planet", 'more info': "Insert clever joke here", } control.refs = ["simple ref", { ref: "title", url: "my url" }] control.impact = 1.0 _(control.to_ruby).must_equal ' control "sample.control.id" do title "Sample Control Important Title" desc "The most critical control the world has ever seen" desc "rationale", "It is needed to save the planet" desc "more info", "Insert clever joke here" impact 1.0 ref "simple ref" ref ({:ref=>"title", :url=>"my url"}) describe command("ls /etc") do its("exit_status") { should eq 0 } end end '.strip end it "constructs a control with only_if" do control = Inspec::Control.new control.add_test(obj1) control.only_if = "package('ntp').installed?" control.id = "sample.control.id" control.title = "Sample Control Important Title" control.descriptions = { default: "The most critical control the world has ever seen", rationale: "It is needed to save the planet", 'more info': "Insert clever joke here", } control.refs = ["simple ref", { ref: "title", url: "my url" }] control.impact = 1.0 _(control.to_ruby).must_equal ' control "sample.control.id" do title "Sample Control Important Title" desc "The most critical control the world has ever seen" desc "rationale", "It is needed to save the planet" desc "more info", "Insert clever joke here" impact 1.0 ref "simple ref" ref ({:ref=>"title", :url=>"my url"}) only_if { package(\'ntp\').installed? } describe command("ls /etc") do its("exit_status") { should eq 0 } end end '.strip end it "constructs a multiline desc in a control with indentation" do control = Inspec::Control.new control.descriptions[:default] = "Multiline\n control" _(control.to_ruby).must_equal ' control nil do desc " Multiline control " end '.strip end it "ignores empty control descriptions" do control = Inspec::Control.new x = ' control nil do end '.strip control.descriptions[:default] = "" _(control.to_ruby).must_equal x control.descriptions[:default] = nil _(control.to_ruby).must_equal x end it "handles non-string descriptions" do control = Inspec::Control.new control.descriptions[:default] = 123 _(control.to_ruby).must_equal ' control nil do desc "123" end '.strip end end describe "Inspec::Variable, take #1" do it "constructs a control with variable to instantiate a resource only once" do control = Inspec::Control.new variable = Inspec::Value.new([["command", "which grep"]]) variable_id = variable.name_variable.to_s obj1 = Inspec::Test.new obj1.variables.push(variable) obj1.qualifier.push([variable_id]) obj1.qualifier.push(["exit_status"]) obj1.matcher = "eq" obj1.expectation = 0 control.add_test(obj1) obj2 = Inspec::Test.new obj2.qualifier.push([variable_id.to_s]) obj2.qualifier.push(["stdout"]) obj2.matcher = "contain" obj2.expectation = "grep" control.add_test(obj2) control.id = "variable.control.id" control.title = "Variable Control Important Title" control.descriptions[:default] = "The most variable control the world has ever seen" control.impact = 1.0 _(control.to_ruby).must_equal ' control "variable.control.id" do title "Variable Control Important Title" desc "The most variable control the world has ever seen" impact 1.0 a = command("which grep") describe a do its("exit_status") { should eq 0 } end describe a do its("stdout") { should contain "grep" } end end '.strip end end describe "Inspec::Variable, take #2" do it "constructs a control with variable, loop and var reference" do control = Inspec::Control.new command_value = %r{^/usr/bin/chrony} pid_filter = ">" pid_value = 0 loopy = Inspec::EachLoop.new loopy.qualifier = [["processes", command_value]] loopy.qualifier.push(["where { pid #{pid_filter} #{pid_value} }.entries"]) obj = loopy.add_test variable = Inspec::Value.new([['passwd.where { user == "_chrony" }.uids.first']]) variable_id = variable.name_variable.to_s obj.variables.push(variable) obj.qualifier = [["user(entry.user)"], ["uid"]] obj.matcher = "cmp #{variable_id}" control.add_test(obj) control.id = "variable.control.id" control.impact = 0.1 _(control.to_ruby).must_equal ' control "variable.control.id" do impact 0.1 a = passwd.where { user == "_chrony" }.uids.first describe user(entry.user) do its("uid") { should cmp a } end end '.strip end end describe "Inspec::Tag" do it "constructs a tag with key and value" do control = Inspec::Control.new res1 = { name: "key", value: "value" } tag1 = Inspec::Tag.new(res1[:name], res1[:value]) _(tag1.to_hash).must_equal res1 control.add_tag(tag1) res2 = { name: "key2", value: %w{a b} } tag2 = Inspec::Tag.new(res2[:name], res2[:value]) _(tag2.to_hash).must_equal res2 control.add_tag(tag2) control.id = "tag.control.id" _(control.to_ruby).must_equal ' control "tag.control.id" do tag key: "value" tag key2: ["a", "b"] end '.strip control_hash = { id: "tag.control.id", title: nil, descriptions: {}, impact: nil, tests: [], tags: [{ name: "key", value: "value", }, { name: "key2", value: %w{a b}, }], } _(control.to_hash).must_equal control_hash end end describe "Inspec::Input" do describe "to_ruby method" do it "generates the code for the input" do input = Inspec::Input.new("application_port", description: "The port my application uses", value: 80) ruby_code = input.to_ruby _(ruby_code).must_include "attr_application_port = " # Should assign to a var # Should have the DSL call. This should be attribute(), not input(), for the # foreseeable future, to maintain backwards compatibility. _(ruby_code).must_include "attribute('application_port'" _(ruby_code).must_include "value: 80" _(ruby_code).must_include "default: 80" _(ruby_code).must_include "description: 'The port my application uses'" # Try to eval the code to verify that the generated code was valid ruby. # Note that the input() method is part of the DSL, so we need to # alter the call into something that can respond - the constructor will do ruby_code_for_eval = ruby_code.sub(/attribute\(/, "Inspec::Input.new(") # This will throw exceptions if there is a problem new_attr = eval(ruby_code_for_eval) # rubocop:disable Security/Eval # Could use ripper! _(new_attr.value).must_equal 80 end end # TODO - deprecate this, not sure it is used describe "to_hash method" do it "generates a similar hash" do ipt = Inspec::Input.new( "some_attr", description: "The port my application uses", value: 80, identifier: "app_port", required: false, type: "numeric" ) expected = { name: "some_attr", options: { description: "The port my application uses", value: 80, identifier: "app_port", required: false, type: "Numeric", # This gets normalized }, } _(ipt.to_hash).must_equal expected end end end end