diff --git a/.expeditor/config.yml b/.expeditor/config.yml index 3ec092a..43b6c4d 100644 --- a/.expeditor/config.yml +++ b/.expeditor/config.yml @@ -1,12 +1,12 @@ --- project: - alias: inspec-objects + alias: mitre-inspec-objects slack: notify_channel: "compliance-profile" rubygems: - - inspec-objects + - mitre-inspec-objects github: # This deletes the GitHub PR branch after successfully merged into the release branch diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..ed1485f --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,43 @@ +name: Release Management + +on: + release: + types: [published] + +jobs: + release: + name: Release to gem hosts + runs-on: ubuntu-latest + steps: + - name: Setup ruby + uses: actions/setup-ruby@v1 + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + lfs: true + - run: git fetch origin +refs/tags/*:refs/tags/* + - name: Setup credentials and versioning + run: | + gem install git-lite-version-bump keycutter roo + mkdir -p $HOME/.gem + touch $HOME/.gem/credentials + chmod 0600 $HOME/.gem/credentials + printf -- "---\n:push_api_key: ${RUBYGEMS_API_KEY}\n" > $HOME/.gem/credentials + printf -- ":github_api_key: Bearer ${GPR_API_KEY}\n" >> $HOME/.gem/credentials + env: + RUBYGEMS_API_KEY: ${{secrets.RUBYGEMS_AUTH_TOKEN}} + GPR_API_KEY: ${{secrets.GITHUB_TOKEN}} + - name: Build gem + run: gem build ./mitre-inspec-objects.gemspec + - name: Get gem version + run: echo "::set-env name=VERSION::$(ls *.gem | grep -Po '(\d+.)+\d+')" + env: + ACTIONS_ALLOW_UNSECURE_COMMANDS: true + - name: Publish to RubyGems + run: | + gem push -k push_api_key --host https://rubygems.org *.gem + - name: Publish to GPR + run: | + gem push -k github_api_key --host https://rubygems.pkg.github.com/${OWNER} *.gem + env: + OWNER: mitre \ No newline at end of file diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml new file mode 100644 index 0000000..f7f7158 --- /dev/null +++ b/.github/workflows/ruby.yml @@ -0,0 +1,36 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake +# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby + +name: Ruby + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + test: + + runs-on: ubuntu-latest + strategy: + matrix: + ruby-version: ['2.6', '2.7'] + + steps: + - uses: actions/checkout@v2 + - name: Set up Ruby + # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, + # change this to (see https://github.com/ruby/setup-ruby#versioning): + # uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e + with: + ruby-version: ${{ matrix.ruby-version }} + bundler-cache: true # runs 'bundle install' and caches installed gems automatically + - name: Run tests + run: | + bundle exec rake diff --git a/.gitignore b/.gitignore index 6c0498a..d6fcb9a 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,4 @@ Gemfile.lock # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: .rvmrc +.vscode/* diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..bede519 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,14 @@ +# This configuration was generated by +# `rubocop --auto-gen-config` +# on 2022-02-07 20:29:42 -0500 using RuboCop version 0.72.0. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 4 +# Cop supports --auto-correct. +# Configuration parameters: AllowInHeredoc. +Layout/TrailingWhitespace: + Exclude: + - 'test/unit/objects_test.rb' \ No newline at end of file diff --git a/Gemfile b/Gemfile index 54e69ca..53c7138 100644 --- a/Gemfile +++ b/Gemfile @@ -1,10 +1,12 @@ +# frozen_string_literal: true + source "https://rubygems.org" -gem "inspec-objects", path: "." +gem "mitre-inspec-objects", path: "." group :test do + gem "chef-utils", "~> 16.6.14" gem "chefstyle", "0.13.0" gem "minitest", "~> 5.5" gem "rake", ">= 10" - gem "chef-utils", "~> 16.6.14" end diff --git a/README.md b/README.md index 8392c7c..2629e89 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # inspec-objects -[![Gem Version](https://badge.fury.io/rb/inspec-objects.svg)](https://badge.fury.io/rb/inspec-objects) +[![Gem Version](https://badge.fury.io/rb/mitre-inspec-objects.svg)](https://badge.fury.io/rb/mitre-inspec-objects) **Umbrella Project**: [InSpec](https://github.com/chef/chef-oss-practices/blob/master/projects/inspec.md) diff --git a/Rakefile b/Rakefile index 485de7c..cb1657a 100644 --- a/Rakefile +++ b/Rakefile @@ -1,13 +1,15 @@ +# frozen_string_literal: true + require "bundler" require "rake/testtask" -Bundler::GemHelper.install_tasks name: "inspec-objects" +Bundler::GemHelper.install_tasks name: "mitre-inspec-objects" Rake::TestTask.new(:unit) do |t| t.libs << "test" t.test_files = Dir.glob([ - "test/unit/**/*_test.rb", - ]) + "test/unit/**/*_test.rb", + ]) end begin diff --git a/VERSION b/VERSION index 6e8bf73..87a0871 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0 +0.3.3 \ No newline at end of file diff --git a/lib/inspec-objects/version.rb b/lib/inspec-objects/version.rb deleted file mode 100644 index d3b404b..0000000 --- a/lib/inspec-objects/version.rb +++ /dev/null @@ -1,3 +0,0 @@ -module InspecObjects - VERSION = "0.1.0".freeze -end diff --git a/lib/inspec/objects.rb b/lib/inspec/objects.rb index ed77d52..65a5b34 100644 --- a/lib/inspec/objects.rb +++ b/lib/inspec/objects.rb @@ -1,14 +1,18 @@ +# frozen_string_literal: true + module Inspec module Object - autoload :Tag, "inspec/objects/tag" - autoload :Control, "inspec/objects/control" - autoload :Describe, "inspec/objects/describe" - autoload :EachLoop, "inspec/objects/each_loop" - autoload :List, "inspec/objects/list" - autoload :OrTest, "inspec/objects/or_test" - autoload :RubyHelper, "inspec/objects/ruby_helper" - autoload :Test, "inspec/objects/test" - autoload :Value, "inspec/objects/value" - autoload :Input, "inspec/objects/input" + require_relative "objects/tag" + require_relative "objects/control" + require_relative "objects/ruby_helper" + require_relative "objects/describe" + require_relative "objects/value" + require_relative "objects/list" + require_relative "objects/each_loop" + require_relative "objects/or_test" + require_relative "objects/test" + require_relative "objects/input" + require_relative "objects/post_body" + require_relative "objects/header" end end diff --git a/lib/inspec/objects/control.rb b/lib/inspec/objects/control.rb index 0a83e23..639be07 100644 --- a/lib/inspec/objects/control.rb +++ b/lib/inspec/objects/control.rb @@ -1,11 +1,19 @@ +# frozen_string_literal: true + module Inspec::Object class Control - attr_accessor :id, :title, :descriptions, :impact, :tests, :tags, :refs, :only_if + attr_accessor :header, :id, :title, :descriptions, :impact, :tests, :post_body, :tags, :refs, :only_if def initialize + @header = "" @tests = [] @tags = [] @refs = [] @descriptions = {} + @post_body = "" + end + + def add_header(header) + @header = header end def add_test(t) @@ -16,19 +24,27 @@ def add_tag(t) @tags.push(t) end + def add_post_body(post_body) + @post_body = post_body + end + def to_hash { + header: header, id: id, title: title, descriptions: descriptions, impact: impact, tests: tests.map(&:to_hash), tags: tags.map(&:to_hash), + post_body: post_body, } end - def to_ruby # rubocop:disable Metrics/AbcSize - res = ["control #{id.inspect} do"] + def to_ruby + res = [] + res.push header unless header.nil? || header.empty? + res.push "control #{id.inspect} do" res.push " title #{title.inspect}" unless title.to_s.empty? descriptions.each do |label, text| if label == :default @@ -44,6 +60,7 @@ def to_ruby # rubocop:disable Metrics/AbcSize refs.each { |t| res.push(" ref #{print_ref(t)}") } res.push " only_if { #{only_if} }" if only_if tests.each { |t| res.push(indent(t.to_ruby, 2)) } + res.push(indent(post_body, 2)) unless post_body.nil? || post_body.empty? res.push "end" res.join("\n") end diff --git a/lib/inspec/objects/describe.rb b/lib/inspec/objects/describe.rb index 673cb5e..2125aad 100644 --- a/lib/inspec/objects/describe.rb +++ b/lib/inspec/objects/describe.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Inspec::Object class Describe # Internal helper to structure test objects. @@ -11,11 +13,11 @@ def negate! def to_ruby itsy = "it" unless its.nil? - if its.is_a? Array - itsy = "its(" + its.inspect + ")" - else - itsy = "its(" + its.to_s.inspect + ")" - end + itsy = if its.is_a? Array + "its(" + its.inspect + ")" + else + "its(" + its.to_s.inspect + ")" + end end naughty = negated ? "_not" : "" xpect = if expectation.nil? @@ -72,7 +74,7 @@ def to_hash end def resource - return nil if qualifier.empty? || qualifier[0].empty? || qualifier[0][0].empty? + return if qualifier.empty? || qualifier[0].empty? || qualifier[0][0].empty? qualifier[0][0] end diff --git a/lib/inspec/objects/each_loop.rb b/lib/inspec/objects/each_loop.rb index a483138..5b8920f 100644 --- a/lib/inspec/objects/each_loop.rb +++ b/lib/inspec/objects/each_loop.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Inspec::Object class EachLoop < List attr_reader :variables diff --git a/lib/inspec/objects/header.rb b/lib/inspec/objects/header.rb new file mode 100644 index 0000000..1f0ebde --- /dev/null +++ b/lib/inspec/objects/header.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Inspec::Object + class Header + attr_accessor :header + + def initialize(header) + @header = header + end + + def to_hash + { + header: header, + } + end + + def to_ruby + header.inspect.to_s + end + + def to_s + "Header #{header}" + end + end +end diff --git a/lib/inspec/objects/input.rb b/lib/inspec/objects/input.rb index 1640c7d..07fa045 100644 --- a/lib/inspec/objects/input.rb +++ b/lib/inspec/objects/input.rb @@ -1,8 +1,9 @@ +# frozen_string_literal: true + require "inspec/input" module Inspec::Object class Input < ::Inspec::Input - # NOTE: No initialize method or accessors for the reasons listed above #--------------------------------------------------------------------------# diff --git a/lib/inspec/objects/list.rb b/lib/inspec/objects/list.rb index 230f5d9..c6f88fd 100644 --- a/lib/inspec/objects/list.rb +++ b/lib/inspec/objects/list.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Inspec::Object class List < Value def map diff --git a/lib/inspec/objects/or_test.rb b/lib/inspec/objects/or_test.rb index a3ed9a5..80bc9c5 100644 --- a/lib/inspec/objects/or_test.rb +++ b/lib/inspec/objects/or_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Inspec::Object class OrTest attr_reader :tests @@ -18,12 +20,11 @@ def to_ruby if @negated # We don't use the describe.one wrapper when negated because: # !(test1 || test2) same as (!test1 && !test2) where && is implicit in inspec - all_tests = @tests.map do |test| + @tests.map do |test| test.negate! test end.map(&:to_ruby).join("\n") - all_tests else all_tests = @tests.map(&:to_ruby).join("\n").gsub("\n", "\n ") diff --git a/lib/inspec/objects/post_body.rb b/lib/inspec/objects/post_body.rb new file mode 100644 index 0000000..8f8f214 --- /dev/null +++ b/lib/inspec/objects/post_body.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Inspec::Object + class PostBody + attr_accessor :post_body + + def initialize(post_body) + @post_body = post_body + end + + def to_hash + { + post_body: post_body, + } + end + + def to_ruby + post_body.inspect.to_s + end + + def to_s + "Post Body #{post_body}" + end + end +end diff --git a/lib/inspec/objects/ruby_helper.rb b/lib/inspec/objects/ruby_helper.rb index 51c8c1a..1912b6d 100644 --- a/lib/inspec/objects/ruby_helper.rb +++ b/lib/inspec/objects/ruby_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Inspec::Object module RubyHelper def ruby_qualifier(q) diff --git a/lib/inspec/objects/tag.rb b/lib/inspec/objects/tag.rb index 753cbac..8d9394a 100644 --- a/lib/inspec/objects/tag.rb +++ b/lib/inspec/objects/tag.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Inspec::Object class Tag attr_accessor :key, :value diff --git a/lib/inspec/objects/test.rb b/lib/inspec/objects/test.rb index 8d96031..2952102 100644 --- a/lib/inspec/objects/test.rb +++ b/lib/inspec/objects/test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Inspec::Object class Test attr_accessor :qualifier, :matcher, :expectation, :skip, :negated, :variables, :only_if @@ -39,7 +41,7 @@ def remove_expectation private def describe_chain - return nil if @qualifier.empty? + return if @qualifier.empty? resource = @qualifier.length > 1 ? @qualifier[0..-2] : [@qualifier[0]] res = resource.map { |q| ruby_qualifier(q) }.join(".") @@ -75,7 +77,7 @@ def rb_describe " " + expectation.inspect end format("%s%sdescribe %s do\n %s { should%s %s%s }\nend", - only_if_clause, vars, res, itsy, naughty, matcher, xpect) + only_if_clause, vars, res, itsy, naughty, matcher, xpect) end def rb_skip diff --git a/lib/inspec/objects/value.rb b/lib/inspec/objects/value.rb index 4a35eda..c1932f4 100644 --- a/lib/inspec/objects/value.rb +++ b/lib/inspec/objects/value.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Inspec::Object class Value include ::Inspec::Object::RubyHelper diff --git a/lib/mitre-inspec-objects.rb b/lib/mitre-inspec-objects.rb new file mode 100644 index 0000000..cf379d7 --- /dev/null +++ b/lib/mitre-inspec-objects.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +require "mitre-inspec-objects/version" +require_relative "inspec/objects" diff --git a/lib/mitre-inspec-objects/version.rb b/lib/mitre-inspec-objects/version.rb new file mode 100644 index 0000000..59a63c2 --- /dev/null +++ b/lib/mitre-inspec-objects/version.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +module InspecObjects + VERSION = "0.3.3" +end diff --git a/inspec-objects.gemspec b/mitre-inspec-objects.gemspec similarity index 60% rename from inspec-objects.gemspec rename to mitre-inspec-objects.gemspec index e19b39f..c80034d 100644 --- a/inspec-objects.gemspec +++ b/mitre-inspec-objects.gemspec @@ -1,20 +1,24 @@ +# frozen_string_literal: true + lib = File.expand_path("lib", __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require "inspec-objects/version" +require "mitre-inspec-objects/version" Gem::Specification.new do |spec| - spec.name = "inspec-objects" + spec.name = "mitre-inspec-objects" spec.version = InspecObjects::VERSION - spec.authors = ["Dominik Richter", "Christoph Hartmann"] + spec.authors = ["Dominik Richter", "Christoph Hartmann", "Aaron Lippold"] spec.email = ["dominik.richter@gmail.com", "chris@lollyrock.com"] spec.summary = "InSpec Objects library" spec.description = "Library that provides an API for programmatically creating InSpec profiles" - spec.homepage = "https://github.com/inspec/inspec-objects" + spec.homepage = "https://github.com/mitre/inspec-objects" spec.license = "Apache-2.0" + spec.metadata = { "github_repo" => "ssh://github.com/mitre/inspec-objects" } + spec.files = %w{ - README.md inspec-objects.gemspec Gemfile + README.md mitre-inspec-objects.gemspec Gemfile } + Dir.glob("{lib}/**/*", File::FNM_DOTMATCH).reject { |f| File.directory?(f) } spec.require_paths = ["lib"] diff --git a/test/spec_helper.rb b/test/spec_helper.rb index 9a4ca35..a93651f 100644 --- a/test/spec_helper.rb +++ b/test/spec_helper.rb @@ -1 +1,3 @@ +# frozen_string_literal: true + require "minitest/autorun" diff --git a/test/unit/objects_test.rb b/test/unit/objects_test.rb index bae197f..9d2b066 100644 --- a/test/unit/objects_test.rb +++ b/test/unit/objects_test.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + require "spec_helper" -require "inspec/objects" +require "mitre-inspec-objects" describe "Objects" do describe "Inspec::Object::Test" do @@ -468,6 +470,7 @@ '.strip control_hash = { + header: "", id: "tag.control.id", title: nil, descriptions: {}, @@ -480,6 +483,107 @@ name: "key2", value: %w{a b}, }], + post_body: "", + } + _(control.to_hash).must_equal control_hash + end + end + + describe "Inspec::Object::PostBody" do + it "constructs a control with a simple post body" do + control = Inspec::Object::Control.new + control.id = "tag.control.id" + control.add_post_body("my body") + _(control.to_ruby).must_equal ' +control "tag.control.id" do + my body +end + '.strip + + control_hash = { + header: "", + id: "tag.control.id", + title: nil, + descriptions: {}, + impact: nil, + tests: [], + tags: [], + post_body: "my body", + } + _(control.to_hash).must_equal control_hash + end + end + + describe "Inspec::Object::PostBody" do + it "constructs a control with a complex post body" do + control = Inspec::Object::Control.new + control.id = "tag.control.id" + control.add_post_body(%q(unless package('gnome-settings-daemon').installed? + impact 0.0 + describe "The system does not have GNOME installed" do + skip "The system does not have GNOME installed, this requirement is Not Applicable." + end +else + describe command("gsettings get org.gnome.settings-daemon.media-keys logout") do + its('stdout.strip') { should cmp "''" } + end +end)) + _(control.to_ruby).must_equal %q( +control "tag.control.id" do + unless package('gnome-settings-daemon').installed? + impact 0.0 + describe "The system does not have GNOME installed" do + skip "The system does not have GNOME installed, this requirement is Not Applicable." + end + else + describe command("gsettings get org.gnome.settings-daemon.media-keys logout") do + its('stdout.strip') { should cmp "''" } + end + end +end +).strip + control_hash = { + header: "", + id: "tag.control.id", + title: nil, + descriptions: {}, + impact: nil, + tests: [], + tags: [], + post_body: "unless package('gnome-settings-daemon').installed?\n impact 0.0\n describe \"The system does not have GNOME installed\" do\n skip \"The system does not have GNOME installed, this requirement is Not Applicable.\"\n end\nelse \n describe command(\"gsettings get org.gnome.settings-daemon.media-keys logout\") do\n its('stdout.strip') { should cmp \"''\" }\n end \nend", + } + _(control.to_hash).must_equal control_hash + end + end + + describe "Inspec::Object::Tag" do + it "constructs a control with a company header" do + control = Inspec::Object::Control.new + res1 = { name: "key", value: "value" } + tag1 = Inspec::Object::Tag.new(res1[:name], res1[:value]) + _(tag1.to_hash).must_equal res1 + control.add_header("# my company header") + control.id = "tag.control.id" + control.add_tag(tag1) + _(control.to_ruby).must_equal ' +# my company header +control "tag.control.id" do + tag key: "value" +end +'.strip + + control_hash = { + header: "# my company header", + id: "tag.control.id", + title: nil, + descriptions: {}, + impact: nil, + tests: [], + tags: [{ + name: "key", + value: "value", + }], + post_body: "", } _(control.to_hash).must_equal control_hash end @@ -510,7 +614,7 @@ end end - # TODO - deprecate this, not sure it is used + # TODO: - deprecate this, not sure it is used describe "to_hash method" do it "generates a similar hash" do ipt = Inspec::Object::Input.new(