conpar 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. data/.gitignore +19 -0
  2. data/.travis.yml +7 -0
  3. data/Gemfile +4 -0
  4. data/Guardfile +24 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +43 -0
  7. data/Rakefile +25 -0
  8. data/conpar.gemspec +30 -0
  9. data/lib/conpar.rb +18 -0
  10. data/lib/conpar/_all.rb +9 -0
  11. data/lib/conpar/configuration.rb +19 -0
  12. data/lib/conpar/directive.rb +30 -0
  13. data/lib/conpar/directive/_all.rb +9 -0
  14. data/lib/conpar/directive/access_list.rb +33 -0
  15. data/lib/conpar/directive/access_list/_all.rb +11 -0
  16. data/lib/conpar/directive/access_list/base.rb +20 -0
  17. data/lib/conpar/directive/access_list/ether_type.rb +43 -0
  18. data/lib/conpar/directive/access_list/extended.rb +56 -0
  19. data/lib/conpar/directive/access_list/standard.rb +42 -0
  20. data/lib/conpar/directive/access_list/unknown_type.rb +16 -0
  21. data/lib/conpar/directive/access_list/web_type.rb +42 -0
  22. data/lib/conpar/directive/base.rb +52 -0
  23. data/lib/conpar/directive/comment.rb +13 -0
  24. data/lib/conpar/directive/empty.rb +12 -0
  25. data/lib/conpar/document.rb +24 -0
  26. data/lib/conpar/version.rb +3 -0
  27. data/spec/conpar_spec.rb +20 -0
  28. data/spec/lib/.keep +0 -0
  29. data/spec/lib/directive/access_list/base_spec.rb +5 -0
  30. data/spec/lib/directive/access_list/ether_type_spec.rb +32 -0
  31. data/spec/lib/directive/access_list/extended_spec.rb +71 -0
  32. data/spec/lib/directive/access_list/standard_spec.rb +32 -0
  33. data/spec/lib/directive/access_list/unknown_spec.rb +8 -0
  34. data/spec/lib/directive/access_list/web_type_spec.rb +27 -0
  35. data/spec/lib/directive/access_list_spec.rb +17 -0
  36. data/spec/lib/directive/base_spec.rb +46 -0
  37. data/spec/lib/directive/comment_spec.rb +13 -0
  38. data/spec/lib/directive/empty_spec.rb +13 -0
  39. data/spec/lib/directive_spec.rb +9 -0
  40. data/spec/lib/document_spec.rb +98 -0
  41. data/spec/samples/basic +3 -0
  42. data/spec/samples/sample2 +3 -0
  43. data/spec/samples/sample3 +4 -0
  44. data/spec/samples/sample4 +5 -0
  45. data/spec/samples/sample5 +8 -0
  46. data/spec/spec_helper.rb +7 -0
  47. metadata +247 -0
@@ -0,0 +1,42 @@
1
+ module Conpar
2
+ module Directive
3
+ module AccessList
4
+ # Class that maps directly to Cisco standard ACL definition
5
+ # See http://www.cisco.com/c/en/us/td/docs/security/asa/asa91/configuration/general/asa_91_general_config/acl_standard.html
6
+ class Standard < Base
7
+ SIGNATURE = /^(access-list)\b.*\b(standard)\b/i
8
+
9
+ def initialize(content="", options={})
10
+ super
11
+
12
+ @sub_ilk = "standard"
13
+
14
+ parse_regex = %r/
15
+ (access-list)\s* # Directive signature
16
+ (?<name>[\-\w]+)\s* # ACL Name
17
+ (?<type>(standard))\s* # Standard ACL Type
18
+ (?<permission>(permit|deny))?\s* # permit or deny
19
+ (?<rule>.+) # Everything else on line
20
+ $/x
21
+ @match_data = parse_regex.match(@content)
22
+
23
+ self
24
+ end#initialize
25
+
26
+ # Method for each match name in the parsed match data (see regex above)
27
+ [ :name,
28
+ :permission,
29
+ :rule
30
+ ].each do |m|
31
+ define_method(m) do
32
+ begin
33
+ @match_data[m]
34
+ rescue IndexError
35
+ nil
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,16 @@
1
+ module Conpar
2
+ module Directive
3
+ module AccessList
4
+ # Class that matches a basic signature of an AccessList but is not a recognized type
5
+ class UnknownType < Base
6
+
7
+ def initialize(content="", options={})
8
+ super
9
+ @sub_ilk = "unknown"
10
+ self
11
+ end#initialize
12
+
13
+ end
14
+ end#AccessList
15
+ end#Directive
16
+ end#Conpar
@@ -0,0 +1,42 @@
1
+ module Conpar
2
+ module Directive
3
+ module AccessList
4
+ # Class that maps directly to Cisco webtype ACL definition
5
+ # See http://www.cisco.com/c/en/us/td/docs/security/asa/asa91/configuration/general/asa_91_general_config/acl_webtype.html
6
+ class WebType < Base
7
+ SIGNATURE = /^(access-list)\b.*\b(webtype)\b/i
8
+
9
+ def initialize(content="", options={})
10
+ super
11
+
12
+ @sub_ilk = "webtype"
13
+
14
+ parse_regex = %r/
15
+ (access-list)\s* # Directive Signature
16
+ (?<name>[\-\w]+)\s* # ACL name
17
+ (?<type>(webtype))\s* # Webtype ACL Type
18
+ (?<permission>(permit|deny))?\s* # permit or deny
19
+ (?<rule>.+) # Everything else on line
20
+ /x
21
+ @match_data = parse_regex.match(@content)
22
+
23
+ self
24
+ end#initialize
25
+
26
+ # Method for each match name in the parsed match data (see regex above)
27
+ [ :name,
28
+ :permission,
29
+ :rule
30
+ ].each do |m|
31
+ define_method(m) do
32
+ begin
33
+ @match_data[m]
34
+ rescue IndexError
35
+ nil
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end#AccessList
41
+ end#Directive
42
+ end#Conpar
@@ -0,0 +1,52 @@
1
+ module Conpar
2
+ module Directive
3
+ # Base class for all Directives
4
+ class Base
5
+ SIGNATURE = /^(.*)$/ # Matches any non-empty string
6
+
7
+ # @!attribute [rw] line_number
8
+ # @return [Integer]
9
+ # 1-based line number within the configuration
10
+ attr_accessor :line_number
11
+
12
+ # @!attribute [rw] line_span
13
+ # @return [Integer]
14
+ # Number of lines this directive spans within the configuration
15
+ attr_accessor :line_span
16
+
17
+ # @!attribute [r] content
18
+ # @return [String]
19
+ # directive content
20
+ attr_reader :content
21
+
22
+ # @!attribute [r] ilk
23
+ # @return [Symbol]
24
+ # shorthand type of directive
25
+ attr_reader :ilk
26
+
27
+ # @!attribute [r] sub_ilk
28
+ # @return [String]
29
+ # a.k.a. "sub type"
30
+ attr_reader :sub_ilk
31
+
32
+ # @!attribute [r] match_data
33
+ # @return [MatchData]
34
+ # This value is to be set internally by subclasses
35
+ attr_reader :match_data
36
+
37
+ # @!attribute [r] rule
38
+ # @return [String]
39
+ attr_reader :rule
40
+
41
+
42
+ def initialize(content="", options={})
43
+ @line_number = options[:line_number]
44
+ @line_span = options.fetch(:line_span, 1)
45
+ @content = content
46
+ @ilk = :directive
47
+ @sub_ilk = ""
48
+ self
49
+ end#initialize
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,13 @@
1
+ module Conpar
2
+ module Directive
3
+ # Class for a commented line
4
+ class Comment < Base
5
+ SIGNATURE = /^\:(.*)?$/
6
+
7
+ def initialize(content="", options={})
8
+ super
9
+ @ilk = :comment
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ module Conpar
2
+ module Directive
3
+ # Class for line that contains only whitespace
4
+ class Empty < Base
5
+ SIGNATURE = /^\s*$/
6
+ def initialize(content, options={})
7
+ super
8
+ @ilk = :empty
9
+ end
10
+ end
11
+ end#Directive
12
+ end#Conpar
@@ -0,0 +1,24 @@
1
+ module Conpar
2
+ class Document
3
+ # @param [String] fw_config
4
+ # Firewall configuration as string
5
+ # @return [Array]
6
+ # Array of Directive objects with which to perform logic
7
+ def self.parse(fw_config)
8
+ if fw_config.class != String
9
+ raise ArgumentError, "fw_config must be string"
10
+ end
11
+
12
+ parsed = []
13
+ lines = fw_config.split("\n")
14
+
15
+ lines.each_with_index do |line, i|
16
+ human_line = i+1
17
+ parsed << Conpar.config.parser.new( line, line_number:human_line )
18
+ end
19
+
20
+ return parsed
21
+ end#self.parse
22
+
23
+ end
24
+ end
@@ -0,0 +1,3 @@
1
+ module Conpar
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe Conpar do
4
+ describe ".configure" do
5
+ context "yielded value" do
6
+ it "should be the same object asa .config()" do
7
+ yielded = nil
8
+ Conpar.configure { |c| yielded = c }
9
+ expect(yielded).to eq(Conpar.config)
10
+ end
11
+ end
12
+ end
13
+
14
+ describe ".config" do
15
+ subject { Conpar.config }
16
+ it "should be a Conpar::Configuration object" do
17
+ expect(subject).to be_a_kind_of(Conpar::Configuration)
18
+ end
19
+ end
20
+ end
data/spec/lib/.keep ADDED
File without changes
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe Conpar::Directive::AccessList::Base do
4
+ let(:klass) { Conpar::Directive::AccessList::Base }
5
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe Conpar::Directive::AccessList::EtherType do
4
+ let(:klass) { Conpar::Directive::AccessList::EtherType }
5
+ {
6
+ "access-list NONIP ethertype permit bpdu" => {
7
+ name: "NONIP",
8
+ permission: "permit",
9
+ rule: "bpdu"
10
+ },
11
+ "access-list ETHER ethertype permit ipx" => {
12
+ name: "ETHER",
13
+ permission: "permit",
14
+ rule: "ipx",
15
+ },
16
+ }.each do |conf,attrs|
17
+ it "::SIGNATURE should match '#{conf}'" do
18
+ expect(conf).to match(klass::SIGNATURE)
19
+ end
20
+ context "for config line '#{conf}'" do
21
+ subject { klass.new(conf) }
22
+ it ".sub_ilk should be 'ethertype'" do
23
+ expect(subject.sub_ilk).to eq("ethertype")
24
+ end
25
+ attrs.each do |k,v|
26
+ it ".#{k} should be '#{v || 'nil'}'" do
27
+ expect(subject.send(k)).to eq(v)
28
+ end
29
+ end#attrs
30
+ end#context
31
+ end
32
+ end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ describe Conpar::Directive::AccessList::Extended do
4
+ let(:klass) { Conpar::Directive::AccessList::Extended }
5
+ {
6
+ # example from cisco documenation
7
+ # rule any any
8
+ "access-list ACL_IN extended permit ip any any" => {
9
+ name: "ACL_IN", line: nil,
10
+ permission: "permit", protocol: "ip",
11
+ rule:"any any"
12
+ },
13
+ # Rule contains more than two object-group statements
14
+ "access-list foo extended permit tcp object-group OBJGRP1 object-group OBJGRP2 object-group OBJGRP3" => {
15
+ name: "foo", line: nil,
16
+ permission: "permit", protocol: "tcp",
17
+ rule:"object-group OBJGRP1 object-group OBJGRP2 object-group OBJGRP3"
18
+ },
19
+ # rule contains object-group BLAH to any
20
+ "access-list bar extended permit ip object-group OBJGRP1 any" => {
21
+ name: "bar", line: nil,
22
+ permission: "permit", protocol: "ip",
23
+ rule:"object-group OBJGRP1 any"
24
+ },
25
+ # rule ip mask any
26
+ "access-list bang extended permit ip 10.1.1.0 255.255.255.240 any" => {
27
+ name: "bang", line: nil,
28
+ permission: "permit", protocol: "ip",
29
+ rule:"10.1.1.0 255.255.255.240 any"
30
+ },
31
+ # rule host ip any
32
+ "access-list biz extended permit ip host 192.168.1.50 any" => {
33
+ name: "biz", line: nil,
34
+ permission: "permit", protocol: "ip",
35
+ rule:"host 192.168.1.50 any"
36
+ },
37
+ # rule ip mask to ip mask
38
+ "access-list nonat extended deny ip 10.9.8.7 255.255.255.0 7.8.9.10 255.255.255.0" => {
39
+ name: "nonat", line: nil,
40
+ permission: "deny", protocol: "ip",
41
+ rule:"10.9.8.7 255.255.255.0 7.8.9.10 255.255.255.0"
42
+ },
43
+ # rule with line number
44
+ "access-list foo line 16 extended permit ip any any" => {
45
+ name: "foo", line: "16",
46
+ permission: "permit", protocol: "ip",
47
+ rule: "any any"
48
+ }
49
+ }.each do |conf, attrs|
50
+ it "::SIGNATURE should match '#{conf}'" do
51
+ expect(conf).to match(klass::SIGNATURE)
52
+ end
53
+ context "for config def '#{conf}'" do
54
+ subject { klass.new(conf) }
55
+ it ".sub_ilk should be 'extended'" do
56
+ expect(subject.sub_ilk).to eq("extended")
57
+ end
58
+ attrs.each do |k,v|
59
+ it ".#{k} should be '#{v || 'nil'}'" do
60
+ expect(subject.send(k)).to eq(v)
61
+ end
62
+ end#attrs
63
+ context "with explicit line_number as #new argument" do
64
+ subject { klass.new(conf, line_number: 42) }
65
+ it ".line_number should be 42" do
66
+ expect(subject.line_number).to eq(42)
67
+ end
68
+ end
69
+ end#context
70
+ end#hash
71
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe Conpar::Directive::AccessList::Standard do
4
+ let(:klass) { Conpar::Directive::AccessList::Standard }
5
+ {
6
+ "access-list OSPF standard permit 192.168.1.0 255.255.255.0" => {
7
+ name: "OSPF",
8
+ permission: "permit",
9
+ rule: "192.168.1.0 255.255.255.0"
10
+ },
11
+ "access-list foo standard permit 192.168.1.50 255.255.255.0" => {
12
+ name: "foo",
13
+ permission: "permit",
14
+ rule: "192.168.1.50 255.255.255.0"
15
+ }
16
+ }.each do |conf, attrs|
17
+ it "::SIGNATURE should match '#{conf}'" do
18
+ expect(conf).to match(klass::SIGNATURE)
19
+ end
20
+ context "for config line '#{conf}'" do
21
+ subject { klass.new(conf) }
22
+ it ".sub_ilk should be 'standard'" do
23
+ expect(subject.sub_ilk).to eq('standard')
24
+ end
25
+ attrs.each do |k,v|
26
+ it ".#{k} should be '#{v || 'nil'}'" do
27
+ expect(subject.send(k)).to eq(v)
28
+ end
29
+ end#attrs
30
+ end#context
31
+ end#hash
32
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe Conpar::Directive::AccessList::UnknownType do
4
+ let(:klass) { Conpar::Directive::AccessList::UnknownType }
5
+ it "should have correct sub_ilk" do
6
+ expect(klass.new("access-list foo unknowntype permit all").sub_ilk).to eq("unknown")
7
+ end
8
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Conpar::Directive::AccessList::WebType do
4
+ let(:klass) { Conpar::Directive::AccessList::WebType }
5
+ {
6
+ "access-list acl_company webtype deny url http://*.cisco.example" => {
7
+ name: "acl_company",
8
+ permission: "deny",
9
+ rule: "url http://*.cisco.example"
10
+ }
11
+ }.each do |conf,attrs|
12
+ it "::SIGNATURE should match '#{conf}'" do
13
+ expect(conf).to match(klass::SIGNATURE)
14
+ end
15
+ context "for config line '#{conf}'" do
16
+ subject { klass.new(conf) }
17
+ it ".sub_ilk should be 'webtype'" do
18
+ expect(subject.sub_ilk).to eq('webtype')
19
+ end
20
+ attrs.each do |k,v|
21
+ it ".#{k} should be '#{v || 'nil'}'" do
22
+ expect(subject.send(k)).to eq(v)
23
+ end
24
+ end#attrs
25
+ end#context
26
+ end
27
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe Conpar::Directive::AccessList do
4
+ context ".new" do
5
+ {
6
+ "access-list foo standard deny all" => Conpar::Directive::AccessList::Standard,
7
+ "access-list foo extended deny all" => Conpar::Directive::AccessList::Extended,
8
+ "access-list foo webtype deny all" => Conpar::Directive::AccessList::WebType,
9
+ "access-list foo ethertype deny all" => Conpar::Directive::AccessList::EtherType,
10
+ "access-list foo unknowntype deny all" => Conpar::Directive::AccessList::Base
11
+ }.each do |line, klass_output|
12
+ it "for '#{line}' should return a #{klass_output.name}" do
13
+ expect(subject.new(line)).to be_a_kind_of(klass_output)
14
+ end
15
+ end
16
+ end
17
+ end