ssh-allow 0.6.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.
@@ -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