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.
- data/.gitignore +19 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/Guardfile +24 -0
- data/LICENSE.txt +22 -0
- data/README.md +43 -0
- data/Rakefile +25 -0
- data/conpar.gemspec +30 -0
- data/lib/conpar.rb +18 -0
- data/lib/conpar/_all.rb +9 -0
- data/lib/conpar/configuration.rb +19 -0
- data/lib/conpar/directive.rb +30 -0
- data/lib/conpar/directive/_all.rb +9 -0
- data/lib/conpar/directive/access_list.rb +33 -0
- data/lib/conpar/directive/access_list/_all.rb +11 -0
- data/lib/conpar/directive/access_list/base.rb +20 -0
- data/lib/conpar/directive/access_list/ether_type.rb +43 -0
- data/lib/conpar/directive/access_list/extended.rb +56 -0
- data/lib/conpar/directive/access_list/standard.rb +42 -0
- data/lib/conpar/directive/access_list/unknown_type.rb +16 -0
- data/lib/conpar/directive/access_list/web_type.rb +42 -0
- data/lib/conpar/directive/base.rb +52 -0
- data/lib/conpar/directive/comment.rb +13 -0
- data/lib/conpar/directive/empty.rb +12 -0
- data/lib/conpar/document.rb +24 -0
- data/lib/conpar/version.rb +3 -0
- data/spec/conpar_spec.rb +20 -0
- data/spec/lib/.keep +0 -0
- data/spec/lib/directive/access_list/base_spec.rb +5 -0
- data/spec/lib/directive/access_list/ether_type_spec.rb +32 -0
- data/spec/lib/directive/access_list/extended_spec.rb +71 -0
- data/spec/lib/directive/access_list/standard_spec.rb +32 -0
- data/spec/lib/directive/access_list/unknown_spec.rb +8 -0
- data/spec/lib/directive/access_list/web_type_spec.rb +27 -0
- data/spec/lib/directive/access_list_spec.rb +17 -0
- data/spec/lib/directive/base_spec.rb +46 -0
- data/spec/lib/directive/comment_spec.rb +13 -0
- data/spec/lib/directive/empty_spec.rb +13 -0
- data/spec/lib/directive_spec.rb +9 -0
- data/spec/lib/document_spec.rb +98 -0
- data/spec/samples/basic +3 -0
- data/spec/samples/sample2 +3 -0
- data/spec/samples/sample3 +4 -0
- data/spec/samples/sample4 +5 -0
- data/spec/samples/sample5 +8 -0
- data/spec/spec_helper.rb +7 -0
- 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,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
|
data/spec/conpar_spec.rb
ADDED
@@ -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,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
|