stelligent / cfn_nag

Linting tool for CloudFormation templates
MIT License
1.26k stars 212 forks source link

Cannot run custom rule from local directory #520

Closed morgantho closed 3 years ago

morgantho commented 3 years ago

I have been able to successfully run this custom rule from an S3 repository, custom gem, and by copying to the custom_rules directory. When I try to use it from a custom directory the rule does not seem to ever be loaded. This is confirmed by running cfn_nag_rules --rule-directory lib/rules/.

CFHTTPSRedirect.rb

require 'cfn-nag/violation'
require 'cfn-nag/custom_rules/base'

class CFHTTPSRedirectRule < BaseRule

  def rule_text
    'Cloudfront distribution does not have an HTTPS redirect configured'
  end

  def rule_type
    Violation::FAILING_VIOLATION
  end

  def rule_id
    'CF500004'
  end

  def audit_impl(cfn_model)
    violating_distributions = cfn_model.resources_by_type('AWS::CloudFront::Distribution')
                                  .select do |distribution|
      distribution.distributionConfig['DefaultCacheBehavior'].nil? || viewer_protocol?(distribution.distributionConfig['DefaultCacheBehavior'])
    end

    violating_distributions.map(&:logical_resource_id)
  end

  private

  def viewer_protocol?(viewer_protocol_policy)
    viewer_protocol_policy['ViewerProtocolPolicy'].nil? || viewer_protocol_policy['ViewerProtocolPolicy'] != 'redirect-to-https'
  end
end

I thought the issue may be with the filename, but changing to CFHTTPSRedirectRule.rb then generates this error.

$ cfn_nag_rules --rule-directory lib/rules/
Traceback (most recent call last):
        10: from /usr/local/rvm/gems/ruby-2.6.6@dir1/bin/ruby_executable_hooks:24:in `<main>'
         9: from /usr/local/rvm/gems/ruby-2.6.6@dir1/bin/ruby_executable_hooks:24:in `eval'
         8: from /usr/local/rvm/gems/ruby-2.6.6@dir1/bin/cfn_nag_rules:23:in `<main>'
         7: from /usr/local/rvm/gems/ruby-2.6.6@dir1/bin/cfn_nag_rules:23:in `load'
         6: from /usr/local/rvm/gems/ruby-2.6.6@dir1/gems/cfn-nag-0.7.0/bin/cfn_nag_rules:43:in `<top (required)>'
         5: from /usr/local/rvm/gems/ruby-2.6.6@dir1/gems/cfn-nag-0.7.0/lib/cfn-nag/rule_dumper.rb:20:in `dump_rules'
         4: from /usr/local/rvm/gems/ruby-2.6.6@dir1/gems/cfn-nag-0.7.0/lib/cfn-nag/rule_repos/file_based_rule_repo.rb:22:in `discover_rules'
         3: from /usr/local/rvm/gems/ruby-2.6.6@dir1/gems/cfn-nag-0.7.0/lib/cfn-nag/rule_repos/file_based_rule_repo.rb:55:in `discover_rule_classes'
         2: from /usr/local/rvm/gems/ruby-2.6.6@dir1/gems/cfn-nag-0.7.0/lib/cfn-nag/rule_repos/file_based_rule_repo.rb:55:in `each'
         1: from /usr/local/rvm/gems/ruby-2.6.6@dir1/gems/cfn-nag-0.7.0/lib/cfn-nag/rule_repos/file_based_rule_repo.rb:56:in `block in discover_rule_classes'
/usr/local/rvm/gems/ruby-2.6.6@dir1/gems/cfn-nag-0.7.0/lib/cfn-nag/rule_repos/file_based_rule_repo.rb:56:in `require': cannot load such file -- lib/rules/CFHTTPSRedirectRule.rb (LoadError)

I have tried this on Mac OS 10.15.7 with cfn-nag version 0.3.55 and on a Ruby 2.6 docker container with cfn-nag version 0.7.0.

arothian commented 3 years ago

Hi @morgantho , I think the issue might be with the base class in use:

Any custom rules should derive from CfnNag::BaseRule in cfn-nag/base_rule (not cfn-nag/custom-rules/base). If the rule must derive from something else, defining a method cfn_nag_rule? that returns true will also cause it to be loaded as a rule.

In addition, you are on the right track. The rule filename should end with Rule.rb.

morgantho commented 3 years ago

I was following the instructions in Custom Rule Development but changing the code to derive from CfnNag::BaseRule in cfn-nag/base_rule still does not cause the rule to be loaded.

CFHTTPSRedirect.rb

require 'cfn-nag/violation'
require 'cfn-nag/base_rule'

class CFHTTPSRedirectRule < CfnNag::BaseRule

  def rule_text
    'Cloudfront distribution does not have an HTTPS redirect configured'
  end

  def rule_type
    Violation::FAILING_VIOLATION
  end

  def rule_id
    'CF500004'
  end

  def audit_impl(cfn_model)
    violating_distributions = cfn_model.resources_by_type('AWS::CloudFront::Distribution')
                                  .select do |distribution|
      distribution.distributionConfig['DefaultCacheBehavior'].nil? || viewer_protocol?(distribution.distributionConfig['DefaultCacheBehavior'])
    end

    violating_distributions.map(&:logical_resource_id)
  end

  private

  def viewer_protocol?(viewer_protocol_policy)
    viewer_protocol_policy['ViewerProtocolPolicy'].nil? || viewer_protocol_policy['ViewerProtocolPolicy'] != 'redirect-to-https'
  end
end

Changing the name to CFHTTPSRedirectRule.rb also still errors out after the code change.