ssh-allow 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ module SSH
2
+ module Allow
3
+ VERSION = "0.6.0"
4
+ end
5
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+ require 'aruba/api'
3
+
4
+ RSpec.configure do |config|
5
+ config.include Aruba::Api
6
+ config.include CLIHelpers
7
+
8
+ config.before(:all) do
9
+ @__aruba_original_paths = (ENV['PATH'] || '').split(File::PATH_SEPARATOR)
10
+ ENV['PATH'] = ([File.expand_path('bin')] + @__aruba_original_paths).join(File::PATH_SEPARATOR)
11
+ FileUtils.rm_rf(current_dir)
12
+ @aruba_io_wait_seconds = 1.5
13
+ end
14
+
15
+ config.after(:all) do
16
+ ENV['PATH'] = @__aruba_original_paths.join(File::PATH_SEPARATOR)
17
+ restore_env
18
+ end
19
+
20
+ end
21
+
@@ -0,0 +1,47 @@
1
+ require 'acceptance/acceptance_helper'
2
+
3
+ describe "Executing the ssh-allow CLI" do
4
+ context "GIVEN: a path-limited 'ls' config file" do
5
+ before(:each) do
6
+ @file = 'test.rules'
7
+ @dir_path = File.expand_path(current_dir + '/../')
8
+ @allow = %(
9
+ allow!("ls") do
10
+ opts "-ld"
11
+ args "#{@dir_path}/.*"
12
+ end
13
+ ).gsub(/^ {6}/, '')
14
+ write_file(@file, @allow)
15
+ @cmd = "ssh-allow guard --rules=#{File.expand_path(current_dir)}/#{@file} --echo"
16
+ end
17
+
18
+ context "WHEN: we run 'ssh-allow guard --echo' with an allowed path" do
19
+ before(:each) do
20
+ @ssh = ssh_command %(ls -ld #{@dir_path}/*)
21
+ run_simple(@cmd)
22
+ end
23
+
24
+ context "THEN: the output of the run" do
25
+ it "should contain the remote command" do
26
+ all_output.should include(@ssh)
27
+ end
28
+
29
+ it "should contain a listing for the 'tmp/aruba' directory" do
30
+ all_output.should match(/tmp\/aruba$/)
31
+ end
32
+ end
33
+ end
34
+
35
+ context "WHEN: we run 'ssh-allow guard --echo' with an disallowed path" do
36
+ before(:each) do
37
+ @ssh = ssh_command %(ls -ld /foo/bar/*)
38
+ run_simple(@cmd)
39
+ end
40
+
41
+ it "standard error indicates a bad command" do
42
+ all_stderr.should match(/Remote Command Not Allowed: #{@ssh}/)
43
+ end
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,197 @@
1
+ require 'spec_helper'
2
+
3
+ describe "The CommandLine parser" do
4
+ before(:each) do
5
+ @parser = CommandLineParser.new
6
+ end
7
+
8
+ context "with only the command" do
9
+ before(:each) do
10
+ @cmd = '/bin/ls'
11
+ end
12
+
13
+ context "when not quoted" do
14
+ before(:each) do
15
+ @parsed_cmd = @parser.parse(@cmd)
16
+ end
17
+
18
+ it "parses the command" do
19
+ @parsed_cmd.name.text_value.should == @cmd
20
+ end
21
+ end
22
+
23
+ context "when quoted" do
24
+ before(:each) do
25
+ @parsed_cmd = @parser.parse("\"#{@cmd}\"")
26
+ end
27
+
28
+ it "parses the quoted command" do
29
+ @parsed_cmd.text_value.should == "\"#{@cmd}\""
30
+ end
31
+ end
32
+ end
33
+
34
+ context "with just options" do
35
+ before(:each) do
36
+ @cmd = 'ls'
37
+ @opts = []
38
+ end
39
+
40
+ context "one short" do
41
+ before(:each) do
42
+ @opts << '-l'
43
+ @parsed_cmd = @parser.parse("#{@cmd} #{@opts.join(' ')}")
44
+ end
45
+
46
+ it "parses the list of options" do
47
+ @parsed_cmd.option_list.should == ['l']
48
+ end
49
+ end
50
+
51
+ context "one long" do
52
+ before(:each) do
53
+ @opts << '--long'
54
+ @parsed_cmd = @parser.parse("#{@cmd} #{@opts.join(' ')}")
55
+ end
56
+
57
+ it "parses the list of options" do
58
+ @parsed_cmd.option_list.should == ['long']
59
+ end
60
+ end
61
+
62
+ context "one long and one short" do
63
+ before(:each) do
64
+ @opts = ['--long', '-a']
65
+ @parsed_cmd = @parser.parse("#{@cmd} #{@opts.join(' ')}")
66
+ end
67
+
68
+ it "parses the list of options" do
69
+ @parsed_cmd.option_list.should == ['long', 'a']
70
+ end
71
+ end
72
+
73
+ context "one short w/argument" do
74
+ before(:each) do
75
+ @opts << '-l=foo/bar'
76
+ @parsed_cmd = @parser.parse("#{@cmd} #{@opts.join(' ')}")
77
+ end
78
+
79
+ it "parses the list of options" do
80
+ @parsed_cmd.option_list.should == ['l']
81
+ end
82
+
83
+ it "parses the list of arguments" do
84
+ @parsed_cmd.argument_list.should == ['foo/bar']
85
+ end
86
+ end
87
+
88
+ context "one long w/argument" do
89
+ before(:each) do
90
+ @opts << '--long=foo/bar'
91
+ @parsed_cmd = @parser.parse("#{@cmd} #{@opts.join(' ')}")
92
+ end
93
+
94
+ it "parses the list of options" do
95
+ @parsed_cmd.option_list.should == ['long']
96
+ end
97
+
98
+ it "parses the list of arguments" do
99
+ @parsed_cmd.argument_list.should == ['foo/bar']
100
+ end
101
+ end
102
+
103
+ context "one long and one short w/arguments" do
104
+ before(:each) do
105
+ @opts = ['--long=foo', '-a=bar']
106
+ @parsed_cmd = @parser.parse("#{@cmd} #{@opts.join(' ')}")
107
+ end
108
+
109
+ it "parses the list of options" do
110
+ @parsed_cmd.option_list.should == ['long', 'a']
111
+ end
112
+
113
+ it "parses the list of arguments" do
114
+ @parsed_cmd.argument_list.should == ['foo', 'bar']
115
+ end
116
+ end
117
+ end
118
+
119
+ context "with just arguments" do
120
+ before(:each) do
121
+ @cmd = 'cp'
122
+ @args = ['foo']
123
+ end
124
+
125
+ context "one argument" do
126
+ before(:each) do
127
+ @parsed_cmd = @parser.parse("#{@cmd} #{@args.join(' ')}")
128
+ end
129
+
130
+ it "parses the list of arguments" do
131
+ @parsed_cmd.argument_list.should == @args
132
+ end
133
+ end
134
+
135
+ context "two arguments" do
136
+ before(:each) do
137
+ @args << 'bar'
138
+ @parsed_cmd = @parser.parse("#{@cmd} #{@args.join(' ')}")
139
+ end
140
+
141
+ it "parses the list of arguments" do
142
+ @parsed_cmd.argument_list.should == @args
143
+ end
144
+ end
145
+ end
146
+
147
+ context "with arguments and options" do
148
+ before(:each) do
149
+ @cmd = 'cp'
150
+ @opts = ['r']
151
+ @args = ['foo', 'bar']
152
+ end
153
+
154
+ context "1 option followed by 2 arguments" do
155
+ before(:each) do
156
+ @parsed_cmd = @parser.parse("#{@cmd} -#{@opts} #{@args.join(' ')}")
157
+ end
158
+
159
+ it "parses the option" do
160
+ @parsed_cmd.option_list.should == @opts
161
+ end
162
+
163
+ it "parses the list of arguments" do
164
+ @parsed_cmd.argument_list.should == @args
165
+ end
166
+ end
167
+
168
+ context "2 arguments followed by 1 option" do
169
+ before(:each) do
170
+ @parsed_cmd = @parser.parse("#{@cmd} -#{@opts} #{@args.join(' ')}")
171
+ end
172
+
173
+ it "parses the option" do
174
+ @parsed_cmd.option_list.should == @opts
175
+ end
176
+
177
+ it "parses the list of arguments" do
178
+ @parsed_cmd.argument_list.should == @args
179
+ end
180
+ end
181
+
182
+ context "1 argument, 1 option, 1 argument" do
183
+ before(:each) do
184
+ @cmd << ' foo -r bar'
185
+ @parsed_cmd = @parser.parse(@cmd)
186
+ end
187
+
188
+ it "parses the option" do
189
+ @parsed_cmd.option_list.should == @opts
190
+ end
191
+
192
+ it "parses the list of arguments" do
193
+ @parsed_cmd.argument_list.should == @args
194
+ end
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+
3
+ describe SSH::Allow::Command do
4
+
5
+ context "for a simple command" do
6
+ before(:each) do
7
+ @cmd_text = 'git help'
8
+ @command = SSH::Allow::Command.new(@cmd_text)
9
+ end
10
+
11
+ it "returns the correct name" do
12
+ @command.name.should == 'git'
13
+ end
14
+
15
+ it "returns the array of arguments" do
16
+ @command.arguments.should == ['help']
17
+ end
18
+
19
+ it "returns an empty array for options" do
20
+ @command.options.should == []
21
+ end
22
+ end
23
+
24
+ context "for a complex command" do
25
+ before(:each) do
26
+ @cmd_text = %(git log --since="6 months ago" Gemfile)
27
+ @command = SSH::Allow::Command.new(@cmd_text)
28
+ end
29
+
30
+ it "returns the array of arguments" do
31
+ @command.arguments.should == ['log', '"6 months ago"', 'Gemfile']
32
+ end
33
+
34
+ it "returns the array of options" do
35
+ @command.options.should == ['since']
36
+ end
37
+ end
38
+
39
+ describe "#allowed?" do
40
+ context "with 1 'allow' rule, each, for cp and mv commands" do
41
+ before(:each) do
42
+ @cp = mock(:cp_rule)
43
+ @mv = mock(:mv_rule)
44
+ @cp.should_receive(:match?).once.and_return([false, false])
45
+ @rules = [@cp, @mv]
46
+ cmd_text = 'mv /foo/bar /baz/bar'
47
+ @command = SSH::Allow::Command.new(cmd_text)
48
+ end
49
+
50
+ context "when the mv command matches" do
51
+ before(:each) do
52
+ @mv.should_receive(:match?).once.and_return([true, true])
53
+ end
54
+
55
+ it "returns true" do
56
+ @command.should be_allowed(@rules)
57
+ end
58
+ end
59
+
60
+ context "when the mv command doesn't match" do
61
+ before(:each) do
62
+ @mv.should_receive(:match?).once.and_return([false, false])
63
+ end
64
+
65
+ it "returns false" do
66
+ @command.should_not be_allowed(@rules)
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ end
@@ -0,0 +1,95 @@
1
+ require 'spec_helper'
2
+
3
+ describe SSH::Allow::RuleSet do
4
+ before(:each) do
5
+ @rule_set = SSH::Allow::RuleSet.new
6
+ end
7
+
8
+ context "#new" do
9
+ it "has an empty rules array" do
10
+ @rule_set.rules.should be_empty
11
+ end
12
+ end
13
+
14
+ context "with a valid rule" do
15
+ before(:each) do
16
+ @rule = mock(:rule)
17
+ @rule_set.should_receive(:get_rule).once.and_return(@rule)
18
+ end
19
+
20
+ context "#allow" do
21
+ before(:each) do
22
+ @rule_set.allow(:rule)
23
+ end
24
+
25
+ it "adds the rule" do
26
+ @rule_set.rules.should == [@rule]
27
+ end
28
+ end
29
+
30
+ context "#allow!" do
31
+ it "does not raise an error" do
32
+ lambda { @rule_set.allow!(:rule) }.should_not raise_error
33
+ end
34
+ end
35
+ end
36
+
37
+ context "with an invalid rule" do
38
+ before(:each) do
39
+ @rule_set.should_receive(:get_rule).once.and_return(false)
40
+ end
41
+
42
+ context "#allow" do
43
+ before(:each) do
44
+ @allow = @rule_set.allow(:rule)
45
+ end
46
+
47
+ it "returns false" do
48
+ @allow.should == false
49
+ end
50
+
51
+ it "does not add the rule" do
52
+ @rule_set.rules.should be_empty
53
+ end
54
+ end
55
+
56
+ context "#allow!" do
57
+ it "raises an error" do
58
+ lambda { @rule_set.allow!(:rule) }.should raise_error(/Invalid rule: "rule"/)
59
+ end
60
+ end
61
+ end
62
+
63
+ context "with a config file with 2 valid rules" do
64
+ before(:each) do
65
+ @rule1 = mock(:foo)
66
+ @rule2 = mock(:bar)
67
+ @rule_set.should_receive(:get_rule).twice.and_return(@rule1, @rule2)
68
+ @rule_set.should_receive(:read_rules).once.and_return(sample_rules)
69
+ end
70
+
71
+ context "#read" do
72
+ before(:each) do
73
+ @rule_set.read('/my/fake/rules')
74
+ end
75
+
76
+ it "adds 2 rules" do
77
+ @rule_set.rules.should == [@rule1, @rule2]
78
+ end
79
+ end
80
+ end
81
+
82
+ context "with a config file with 1 valid and 1 invalid rule" do
83
+ before(:each) do
84
+ @rule1 = mock(:foo)
85
+ @rule_set.should_receive(:get_rule).twice.and_return(@rule1, false)
86
+ @rule_set.should_receive(:read_rules).once.and_return(sample_rules)
87
+ end
88
+
89
+ context "#read" do
90
+ it "raises an error on the second rule" do
91
+ lambda { @rule_set.read('/my/fake/rules') }.should raise_error(/Invalid rule: "bar"/)
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,219 @@
1
+ require 'spec_helper'
2
+
3
+ describe SSH::Allow::Rule do
4
+
5
+ context "creating an Allow rule, with a single command" do
6
+ before(:each) do
7
+ @cmd = "ls"
8
+ end
9
+
10
+ context "when specifying no options or arguments" do
11
+ before(:each) do
12
+ @rule = SSH::Allow::Rule.allow(@cmd)
13
+ end
14
+
15
+ it "returns [:none] for options" do
16
+ @rule.options.should == [:none]
17
+ end
18
+
19
+ it "match_command? matches 'ls'" do
20
+ @rule.should be_match_command(@cmd)
21
+ end
22
+
23
+ it "match_command? doesn't match 'ln'" do
24
+ @rule.should_not be_match_command('ln')
25
+ end
26
+
27
+ it "match_options? matches no options" do
28
+ @rule.should be_match_options([])
29
+ end
30
+
31
+ it "match_options? doesn't match a passed in option" do
32
+ @rule.should_not be_match_options(['foo'])
33
+ end
34
+
35
+ it "match_arguments? matches no arguments" do
36
+ @rule.should be_match_arguments([])
37
+ end
38
+
39
+ it "match_arguments? doesn't match a passed in argument" do
40
+ @rule.should_not be_match_arguments(['foo'])
41
+ end
42
+ end
43
+
44
+ context "when specifying :any for options and arguments " do
45
+ before(:each) do
46
+ @rule = SSH::Allow::Rule.allow(@cmd)
47
+ @rule.opts(:any)
48
+ @rule.args(:any)
49
+ end
50
+
51
+ it "match_options? matches no options" do
52
+ @rule.should be_match_options([])
53
+ end
54
+
55
+ it "match_options? matches any number of options" do
56
+ @rule.should be_match_options(['foo', 'bar', 'baz'])
57
+ end
58
+
59
+ it "match_arguments? matches no arguments" do
60
+ @rule.should be_match_arguments([])
61
+ end
62
+
63
+ it "match_arguments? matches any number of arguments" do
64
+ @rule.should be_match_arguments(['foo', 'bar', :baz])
65
+ end
66
+ end
67
+
68
+ context "when specifying strings for options and arguments, via a block" do
69
+ before(:each) do
70
+ @rule = SSH::Allow::Rule.allow(@cmd) do
71
+ opts '-ld'
72
+ args '^\/foo\/bar\/.*'
73
+ end
74
+ end
75
+
76
+ it "returns \"ld\" for the rule options" do
77
+ @rule.options.should == ['ld']
78
+ end
79
+
80
+ it "returns the correct pattern for the argument" do
81
+ @rule.arguments[0].should == Regexp.new('^\/foo\/bar\/.*')
82
+ end
83
+
84
+ it "match_options? matches the correct options" do
85
+ @rule.should be_match_options(['ld'])
86
+ end
87
+
88
+ it "match_options? doesn't match more than one option" do
89
+ @rule.should_not be_match_options(['ld', 'foo'])
90
+ end
91
+
92
+ it "match_arguments? matches an argument that starts with '/foo/bar/'" do
93
+ @rule.should be_match_arguments(['/foo/bar/*'])
94
+ end
95
+
96
+ it "match_arguments? doesn't match an argument that starts with '/foo/baz/'" do
97
+ @rule.should_not be_match_arguments(['/foo/baz/*'])
98
+ end
99
+
100
+ it "match_arguments? doesn't match more than one argument" do
101
+ @rule.should_not be_match_arguments(['/foo/bar/*', '/foo/baz/*'])
102
+ end
103
+ end
104
+
105
+ context "when sending an invalid block" do
106
+ before(:each) do
107
+ @rule = SSH::Allow::Rule.allow(@cmd) do
108
+ opts "-ld"
109
+ foo "bar"
110
+ end
111
+ end
112
+
113
+ it "returns false" do
114
+ @rule.should be(false)
115
+ end
116
+ end
117
+ end
118
+
119
+ context "creating a Deny rule" do
120
+ context "with a single command string" do
121
+ before(:each) do
122
+ @cmd = "ls"
123
+ @rule = SSH::Allow::Rule.deny(@cmd)
124
+ end
125
+
126
+ it "returns a Deny rule" do
127
+ @rule.should be_instance_of(SSH::Allow::Rule::Deny)
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ describe SSH::Allow::Rule::Allow do
134
+ context "A Command for 'ls -ld /foo/bar/*'" do
135
+ before(:each) do
136
+ @cmd = mock(:command)
137
+ @cmd.should_receive(:name).once.and_return('ls')
138
+ @cmd.should_receive(:options).once.and_return(['ld'])
139
+ @cmd.should_receive(:arguments).once.and_return(['/foo/bar/*'])
140
+ end
141
+
142
+ context "matched against an 'ls' rule with no options or arguments" do
143
+ before(:each) do
144
+ @rule = SSH::Allow::Rule.allow('ls')
145
+ @match, @allow = @rule.match?(@cmd)
146
+ end
147
+
148
+ it "is not a match" do
149
+ @match.should_not be(true)
150
+ end
151
+
152
+ it "would not be allowed" do
153
+ @allow.should_not be(true)
154
+ end
155
+ end
156
+
157
+ context "matched against an 'ls' rule with matching options and arguments" do
158
+ before(:each) do
159
+ @rule = SSH::Allow::Rule.allow('ls') do
160
+ opts '-ld'
161
+ args '^\/foo\/bar\/.*'
162
+ end
163
+ @match, @allow = @rule.match?(@cmd)
164
+ end
165
+
166
+ it "is a match" do
167
+ @match.should be(true)
168
+ end
169
+
170
+ it "would be allowed" do
171
+ @allow.should be(true)
172
+ end
173
+ end
174
+ end
175
+ end
176
+
177
+ describe SSH::Allow::Rule::Deny do
178
+ context "A Command for 'mv /foo/bar/file.txt /foo/baz/'" do
179
+ before(:each) do
180
+ @cmd = mock(:command)
181
+ @cmd.should_receive(:name).once.and_return('mv')
182
+ @cmd.should_receive(:options).once.and_return([])
183
+ @cmd.should_receive(:arguments).once.and_return(['/foo/bar/file.txt', '/foo/baz/'])
184
+ end
185
+
186
+ context "matched against an 'mv' rule with no options or arguments" do
187
+ before(:each) do
188
+ @rule = SSH::Allow::Rule.deny('mv')
189
+ @match, @allow = @rule.match?(@cmd)
190
+ end
191
+
192
+ it "is not a match" do
193
+ @match.should_not == true
194
+ end
195
+
196
+ it "would be allowed" do
197
+ @allow.should == true
198
+ end
199
+ end
200
+
201
+ context "matched against an 'mv' rule with matching options and arguments" do
202
+ before(:each) do
203
+ @rule = SSH::Allow::Rule.deny('mv') do
204
+ args '^\/foo\/bar\/.*'
205
+ args '^\/foo\/baz\/.*'
206
+ end
207
+ @match, @allow = @rule.match?(@cmd)
208
+ end
209
+
210
+ it "is a match" do
211
+ @match.should == true
212
+ end
213
+
214
+ it "would not be allowed" do
215
+ @allow.should_not == true
216
+ end
217
+ end
218
+ end
219
+ end