sambal 0.2.2 → 0.2.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0a50e0a519bdbc0ab8cccb0116250c2b073e5e305a906d9f46fac5012dc11814
4
- data.tar.gz: 71ac3f8493eae53b548a0e58a207e38034615af0f0d915956d50ab338b5bc16e
3
+ metadata.gz: 6c823f0d8de2bf8123f6eca3b6b30b5886a73e525319abe6293c84e326d57751
4
+ data.tar.gz: 5b37db63b852ee0ee9ae04b0ff2cd547a5e002ae7ce46b4bff0c93643a2f099e
5
5
  SHA512:
6
- metadata.gz: 5e14f217b9d83deceae6bd11a17d94ce17ca59800850031a0d57d416fe5f1f1431a0de3b8c9d9e8a3f4b4695f9d4e16b15fa2e2cbb808cf16ba409ad60dc67d3
7
- data.tar.gz: ba2eb773c39c1dccfd4b71dbe2b184f453b17cf02690124d010d30d18d3456e4976a7d33f5b11bacda7c192882d1336b686f859ca0147a736ac0a9ff8191ede4
6
+ metadata.gz: 3d980269e1dbe721c4cd6915e2999dc0e7c86827798302400a35ce2c4ff9bf8e95c59465eb30113fb34e4c150f468334175d9a90ed2eaf370dc65fc0540f9717
7
+ data.tar.gz: b8d926be2d6e28c2429625e2a576994e459968d096b50cf4af822c3a0e31db11ee1129d236f6e271e8473a1378fba4c8083368a976d9348425e7da108ed41c74
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Build Status](https://ci.insane.se/buildStatus/icon?job=sambal/master)](https://ci.insane.se/job/sambal/job/master/)
1
+ ![Test status](https://github.com/johnae/sambal/actions/workflows/ci.yaml/badge.svg)
2
2
 
3
3
  # Sambal
4
4
 
@@ -34,7 +34,11 @@ On Linux (Ubuntu) it's as easy as:
34
34
  It should be available in a similar way on all major Linux distributions.
35
35
 
36
36
  If you happen to be running [NixOS](https://nixos.org/) or at least the [Nix package manager](https://nixos.org/nix/download.html) you could just
37
- use the `default.nix` file in this repo. That should also set you up for running the tests (which require samba).
37
+ use the `flake.nix` file in this repo. That should also set you up for running the tests (which require samba). Something like this:
38
+
39
+ nix develop
40
+ bundle install
41
+ bundle exec rspec
38
42
 
39
43
  ## Usage
40
44
 
data/lib/sambal/client.rb CHANGED
@@ -18,7 +18,8 @@ module Sambal
18
18
  password: false,
19
19
  port: 445,
20
20
  timeout: 10,
21
- columns: 80
21
+ columns: 80,
22
+ smbclient_command: 'smbclient'
22
23
  }
23
24
 
24
25
  options = default_options.merge(user_options)
@@ -33,17 +34,17 @@ module Sambal
33
34
 
34
35
  password =
35
36
  if options[:authfile]
36
- "--authentication-file #{options[:authfile]}"
37
+ ['--authentication-file', options[:authfile]]
37
38
  elsif options[:password]
38
- options[:password]
39
+ [options[:password]]
39
40
  else
40
- '--no-pass'
41
+ ['--no-pass']
41
42
  end
42
- command = "COLUMNS=#{options[:columns]} smbclient \"//#{options[:host]}/#{options[:share]}\" #{password}"
43
+ command = ['env', "COLUMNS=#{options[:columns]}", options[:smbclient_command], "//#{options[:host]}/#{options[:share]}", password, option_flags(options)].flatten
43
44
 
44
- @output, @input, @pid = PTY.spawn("#{command} #{option_flags(options)}")
45
+ @output, @input, @pid = PTY.spawn(command[0], *command[1..-1])
45
46
 
46
- res = @output.expect(/(.*\n)?smb:.*\\>/, @timeout)[0] rescue nil
47
+ res = @output.expect(/smb:.*\\>/, @timeout)[0] rescue nil
47
48
  @connected = case res
48
49
  when nil
49
50
  false
@@ -104,7 +105,7 @@ module Sambal
104
105
 
105
106
  def cd(dir)
106
107
  response = ask("cd \"#{dir}\"")
107
- if response.split("\r\n").join('') =~ /NT_STATUS_OBJECT_NAME_NOT_FOUND/
108
+ if response.split("\r\n").join('') =~ /NT_STATUS_OBJECT_(NAME|PATH)_NOT_FOUND/
108
109
  Response.new(response, false)
109
110
  else
110
111
  Response.new(response, true)
@@ -115,7 +116,7 @@ module Sambal
115
116
  begin
116
117
  file_context(filename) do |file|
117
118
  response = ask_wrapped 'get', [file, output]
118
- if response =~ /^getting\sfile.*$/
119
+ if response =~ /getting\sfile.*$/
119
120
  Response.new(response, true)
120
121
  else
121
122
  Response.new(response, false)
@@ -139,7 +140,7 @@ module Sambal
139
140
 
140
141
  def put(file, destination)
141
142
  response = ask_wrapped 'put', [file, destination]
142
- if response =~ /^putting\sfile.*$/
143
+ if response =~ /putting\sfile.*$/
143
144
  Response.new(response, true)
144
145
  else
145
146
  Response.new(response, false)
@@ -154,7 +155,7 @@ module Sambal
154
155
  f << content
155
156
  end
156
157
  response = ask_wrapped 'put', [t.path, destination]
157
- if response =~ /^putting\sfile.*$/
158
+ if response =~ /putting\sfile.*$/
158
159
  Response.new(response, true)
159
160
  else
160
161
  Response.new(response, false)
@@ -190,7 +191,7 @@ module Sambal
190
191
  cd '..'
191
192
  response = ask_wrapped 'rmdir', dir
192
193
  next_line = response.split("\n")[1]
193
- if next_line =~ /^smb:.*\\>/
194
+ if next_line =~ /smb:.*\\>/
194
195
  Response.new(response, true)
195
196
  else
196
197
  Response.new(response, false)
@@ -205,7 +206,7 @@ module Sambal
205
206
  file_context(filename) do |file|
206
207
  response = ask_wrapped 'del', file
207
208
  next_line = response.split("\n")[1]
208
- if next_line =~ /^smb:.*\\>/
209
+ if next_line =~ /smb:.*\\>/
209
210
  Response.new(response, true)
210
211
  #elsif next_line =~ /^NT_STATUS_NO_SUCH_FILE.*$/
211
212
  # Response.new(response, false)
@@ -253,7 +254,13 @@ module Sambal
253
254
 
254
255
  def ask(cmd)
255
256
  @input.print("#{cmd}\n")
256
- response = @output.expect(/^smb:.*\\>/,@timeout)[0] rescue nil
257
+ response = begin
258
+ @output.expect(/smb:.*\\>/,@timeout)[0]
259
+ rescue => e
260
+ $stderr.puts e
261
+ nil
262
+ end
263
+
257
264
  if response.nil?
258
265
  $stderr.puts "Failed to do #{cmd}"
259
266
  raise "Failed to do #{cmd}"
@@ -266,9 +273,13 @@ module Sambal
266
273
  ask wrap_filenames(cmd,filenames)
267
274
  end
268
275
 
276
+ def sanitize_filename(filename)
277
+ filename.to_s.gsub(/[[:^print:]"]/,'')
278
+ end
279
+
269
280
  def wrap_filenames(cmd,filenames)
270
281
  filenames = [filenames] unless filenames.kind_of?(Array)
271
- filenames.map!{ |filename| "\"#{filename}\"" }
282
+ filenames.map!{ |filename| "\"#{sanitize_filename(filename)}\"" }
272
283
  [cmd,filenames].flatten.join(' ')
273
284
  end
274
285
 
@@ -305,19 +316,20 @@ module Sambal
305
316
 
306
317
  def option_flags(options)
307
318
  flags = []
308
- flags << "--workgroup \"#{options[:domain]}\"" if options[:domain] && !options[:authfile]
309
- flags << "--user \"#{options[:user]}\"" if options[:user] && !options[:authfile]
310
- flags << "--ip-address #{options[:ip_address]}" if options[:ip_address]
311
- flags << "--send-buffer #{options[:buffer_size]}" if options[:buffer_size]
312
- flags << "--debuglevel #{options[:debug_level]}" if options[:debug_level]
313
- flags << '--encrypt' if options[:encrypt]
314
- flags << "--max-protocol #{options[:max_protocol]}" if options[:max_protocol]
315
- flags << '--use-ccache' if options[:use_ccache]
316
- flags << "--socket-options #{options[:socket_options]}" if options[:socket_options]
317
- flags << "--port #{options[:port]}" if options[:port]
318
- flags << "--name-resolve #{options[:name_resolve]}" if options[:name_resolve]
319
- flags << (options[:configfile] ? "--configfile #{options[:configfile]}" : '--configfile /dev/null')
320
- flags.join(' ')
319
+ flags += ['--workgroup', options[:domain]] if options[:domain] && !options[:authfile]
320
+ flags += ['--user', options[:user]] if options[:user] && !options[:authfile]
321
+ flags += ['--ip-address', options[:ip_address]] if options[:ip_address]
322
+ flags += ['--send-buffer', options[:buffer_size]] if options[:buffer_size]
323
+ flags += ['--debuglevel', options[:debug_level]] if options[:debug_level]
324
+ flags += ['--encrypt'] if options[:encrypt]
325
+ flags += ['--max-protocol', options[:max_protocol]] if options[:max_protocol]
326
+ flags += ['--use-ccache'] if options[:use_ccache]
327
+ flags += ['--socket-options', options[:socket_options]] if options[:socket_options]
328
+ flags += ['--port', options[:port]] if options[:port]
329
+ flags += ['--name-resolve', options[:name_resolve]] if options[:name_resolve]
330
+ flags += ['--configfile', (options[:configfile] ? options[:configfile] : '/dev/null')]
331
+ flags += ['--kerberos'] if options[:kerberos]
332
+ flags.map(&:to_s)
321
333
  end
322
334
  end
323
335
  end
@@ -71,11 +71,11 @@ module Sambal
71
71
  def start
72
72
  if RUBY_PLATFORM=="java"
73
73
  @smb_server_pid = Thread.new do
74
- `smbd -S -F -s #{@config_path} -p #{@port} --option="lockdir"=#{@lock_path} --option="pid directory"=#{@pid_dir} --option="private directory"=#{@private_dir} --option="cache directory"=#{@cache_dir} --option="state directory"=#{@state_dir} < /dev/null > #{@log_path}/smb.log`
74
+ `smbd -F -s #{@config_path} -p #{@port} --option="lockdir"=#{@lock_path} --option="pid directory"=#{@pid_dir} --option="private directory"=#{@private_dir} --option="cache directory"=#{@cache_dir} --option="state directory"=#{@state_dir} < /dev/null > #{@log_path}/smb.log`
75
75
  end
76
76
  else
77
77
  @smb_server_pid = fork do
78
- exec "smbd -S -F -s #{@config_path} -p #{@port} --option=\"lockdir\"=#{@lock_path} --option=\"pid directory\"=#{@pid_dir} --option=\"private directory\"=#{@private_dir} --option=\"cache directory\"=#{@cache_dir} --option=\"state directory\"=#{@state_dir} < /dev/null > #{@log_path}/smb.log"
78
+ exec "smbd -F -s #{@config_path} -p #{@port} --option=\"lockdir\"=#{@lock_path} --option=\"pid directory\"=#{@pid_dir} --option=\"private directory\"=#{@private_dir} --option=\"cache directory\"=#{@cache_dir} --option=\"state directory\"=#{@state_dir} < /dev/null > #{@log_path}/smb.log"
79
79
  end
80
80
  end
81
81
  sleep 2 ## takes a short time to start up
@@ -1,5 +1,5 @@
1
1
  # coding: UTF-8
2
2
 
3
3
  module Sambal
4
- VERSION = "0.2.2"
4
+ VERSION = "0.2.4"
5
5
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
  require 'tempfile'
5
+ require 'tmpdir'
5
6
 
6
7
  describe Sambal::Client do
7
8
 
@@ -257,4 +258,44 @@ describe Sambal::Client do
257
258
  expect(@sambal_client.wrap_filenames('cmd',[Pathname.new('file1'), Pathname.new('file2')])).to eq('cmd "file1" "file2"')
258
259
  end
259
260
 
261
+ it 'should prevent smb command injection by malicious filename' do
262
+ expect(@sambal_client.exists?('evil.txt')).to be_falsy
263
+ @sambal_client.ls("\b\b\b\bput \"#{file_to_upload.path}\" \"evil.txt")
264
+ expect(@sambal_client.exists?('evil.txt')).to be_falsy
265
+ end
266
+
267
+ describe 'sanitize_filename' do
268
+ it 'should remove unprintable character' do
269
+ expect(@sambal_client.sanitize_filename("fi\b\ble\n name\r\n")).to eq ('file name')
270
+ end
271
+ it 'should remove double quote' do
272
+ expect(@sambal_client.sanitize_filename('double"quote')).to eq ('doublequote')
273
+ end
274
+ end
275
+
276
+ describe 'malicious flags' do
277
+ it 'should not inject command by hostname' do
278
+ Dir.mktmpdir do |dir|
279
+ begin
280
+ client = described_class.new(host: "\"; touch #{dir}/evil.txt;\"", share: test_server.share_name, port: test_server.port)
281
+ rescue
282
+ ensure
283
+ client.close if client
284
+ end
285
+ expect(File.exists?("#{dir}/evil.txt")).to be_falsy
286
+ end
287
+ end
288
+
289
+ it 'should not inject command by domain' do
290
+ Dir.mktmpdir do |dir|
291
+ begin
292
+ client = described_class.new(host: test_server.host, share: test_server.share_name, port: test_server.share_name, domain: "\"; touch #{dir}/evil.txt; ls \"")
293
+ rescue
294
+ ensure
295
+ client.close if client
296
+ end
297
+ expect(File.exists?("#{dir}/evil.txt")).to be_falsy
298
+ end
299
+ end
300
+ end
260
301
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sambal
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Axel Eriksson
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-09-22 00:00:00.000000000 Z
11
+ date: 2022-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -31,28 +31,20 @@ executables: []
31
31
  extensions: []
32
32
  extra_rdoc_files: []
33
33
  files:
34
- - ".envrc"
35
- - ".gitignore"
36
- - Gemfile
37
- - Jenkinsfile
38
34
  - LICENSE
39
35
  - README.md
40
- - Rakefile
41
- - Spookfile
42
- - default.nix
43
36
  - lib/sambal.rb
44
37
  - lib/sambal/client.rb
45
38
  - lib/sambal/response.rb
46
39
  - lib/sambal/smb.conf.erb
47
40
  - lib/sambal/test_server.rb
48
41
  - lib/sambal/version.rb
49
- - sambal.gemspec
50
42
  - spec/sambal/client_spec.rb
51
43
  - spec/spec_helper.rb
52
44
  homepage: https://github.com/johnae/sambal
53
45
  licenses: []
54
46
  metadata: {}
55
- post_install_message:
47
+ post_install_message:
56
48
  rdoc_options: []
57
49
  require_paths:
58
50
  - lib
@@ -67,9 +59,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
67
59
  - !ruby/object:Gem::Version
68
60
  version: '0'
69
61
  requirements: []
70
- rubyforge_project:
71
- rubygems_version: 2.7.7
72
- signing_key:
62
+ rubygems_version: 3.2.26
63
+ signing_key:
73
64
  specification_version: 4
74
65
  summary: Ruby Samba Client
75
66
  test_files:
data/.envrc DELETED
@@ -1 +0,0 @@
1
- use_nix
data/.gitignore DELETED
@@ -1,19 +0,0 @@
1
- *.gem
2
- *.rbc
3
- .bundle
4
- .config
5
- .yardoc
6
- Gemfile.lock
7
- InstalledFiles
8
- _yardoc
9
- coverage
10
- doc/
11
- lib/bundler/man
12
- pkg
13
- rdoc
14
- spec/reports
15
- spec/sambashare
16
- spec/smb.conf
17
- test/tmp
18
- test/version_tmp
19
- tmp
data/Gemfile DELETED
@@ -1,6 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in sambal.gemspec
4
- gemspec
5
-
6
- gem 'rspec_junit_formatter', '0.2.2'
data/Jenkinsfile DELETED
@@ -1,34 +0,0 @@
1
- #!/usr/bin/groovy
2
-
3
- def hasCmd(cmd) { "command -v ${cmd} >/dev/null 2>&1" }
4
-
5
- def shell(cmd) {
6
- def nixInitPath = '$HOME/.nix-profile/etc/profile.d/nix.sh'
7
- sh """
8
- if ! ${hasCmd('nix-shell')}; then
9
- if [ -e ${nixInitPath} ]; then
10
- . ${nixInitPath}
11
- else
12
- curl https://nixos.org/nix/install | sh
13
- . ${nixInitPath}
14
- fi
15
- fi
16
- ${cmd}
17
- """
18
- }
19
-
20
- def nixShell(cmd) { shell """ nix-shell --run "${cmd}" """ }
21
-
22
- node('linux') {
23
- stage("Prerequisites") { shell """ nix-env -iA nixpkgs.git """ }
24
-
25
- stage("Checkout") { checkout scm }
26
-
27
- stage("Setup") { nixShell '''
28
- bundle install
29
- mkdir -p /tmp/sambal-temp-path
30
- '''
31
- }
32
-
33
- stage("Test") { nixShell "SAMBAL_TEMP_PATH=/tmp/sambal-temp-path bundle exec rspec" }
34
- }
data/Rakefile DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env rake
2
- require "bundler/gem_tasks"
data/Spookfile DELETED
@@ -1,45 +0,0 @@
1
- -- vim: syntax=moon
2
- -- see: https//github.com/johnae/spook
3
- log_level "INFO"
4
- fs = require 'fs'
5
- execute = require('process').execute
6
- -- Adds the built-in terminal_notifier
7
- notify.add 'terminal_notifier'
8
- -- Add "notifier" in path if any, otherwise
9
- -- fail silently.
10
- pcall notify.add, 'notifier'
11
-
12
- {
13
- :until_success
14
- :command
15
- :task_filter
16
- :notifies
17
- } = require 'spookfile_helpers'
18
-
19
- test = (event, tasks) ->
20
- until_success ->
21
- notifies event.path, event, tasks
22
-
23
- task_list = task_filter fs.is_present
24
- rspec = command "bundle exec rspec -f d"
25
-
26
- watch '.', ->
27
-
28
- on_changed "^(spec)/(spec_helper%.rb)", (event) ->
29
- test event, task_list(
30
- rspec, "spec"
31
- )
32
-
33
- on_changed "^spec/(.*)_spec%.rb", (event, name) ->
34
- test event, task_list(
35
- rspec, "spec/#{name}_spec.rb"
36
- )
37
-
38
- on_changed "^lib/.*%.rb", (event) ->
39
- test event, task_list(
40
- rspec, "spec/sambal/client_spec.rb"
41
- )
42
-
43
- on_changed '^Spookfile$', ->
44
- notify.info 'Re-executing spook...'
45
- reload_spook!
data/default.nix DELETED
@@ -1,6 +0,0 @@
1
- with import <nixpkgs> {};
2
-
3
- stdenv.mkDerivation {
4
- name = "sambal";
5
- buildInputs = [ ruby rake samba ];
6
- }
data/sambal.gemspec DELETED
@@ -1,19 +0,0 @@
1
- # -*- encoding: utf-8 -*-
2
- require File.expand_path('../lib/sambal/version', __FILE__)
3
-
4
- Gem::Specification.new do |gem|
5
- gem.authors = ["John Axel Eriksson"]
6
- gem.email = ["john@insane.se"]
7
- gem.description = %q{Ruby Samba Client using the cmdline smbclient}
8
- gem.summary = %q{Ruby Samba Client}
9
- gem.homepage = "https://github.com/johnae/sambal"
10
-
11
- gem.files = `git ls-files`.split($\)
12
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
- gem.name = "sambal"
15
- gem.require_paths = ["lib"]
16
- gem.version = Sambal::VERSION
17
-
18
- gem.add_development_dependency "rspec", '>=3.4.0'
19
- end