Passing shell commands parameters securely with %x[] in Ruby - ruby

Let's say I have in a Rails controller:
dir = params[:dir]
output = %x[ls #{dir}]
This is a HUGE security hole if somebody posts dir="foo; rm -rf /"
So I need to secure the parameter. I know I could do
system "ls", dir
But this method does not capture stdout !
So, how can I securely pass parameters to %x[] ?

The problem is that %x() basically hands a string to the shell to be parsed so you'd have to escape everything that the shell could possibly interpret. So %x is pretty much out the window if you need to deal with anything that you haven't built yourself (and event then it is suspect).
One solution is to use Open3.capture3:
out, err, status = Open3.capture3('/bin/ls', dir)
and then deal with the standard output (out) and standard error (err) returns as needed. There are a few other things in Open3 that might serve your needs better.

Have you looked at Ruby's safe levels?
http://www.ruby-doc.org/docs/ProgrammingRuby/html/taint.html
For levels >= 2 it says "Can't change, make, or remove directories, or use chroot."
There also used to be a sandbox gem, but I'm not sure if that's still active. You also could have a look at the source of "Try Ruby!" there has to be some kind of sandboxing in there.

Related

Configuration verification

ssh to an IP supplied via user input
Inject show running-config command
Search the output of the command and look for specific parameters like ports, QoS, VTY lines, SMTP settings, IP helpers etc.
Output only if the predefined parameters are not in place
If I store the output of the ssh run in a variable, is there a method I can use for parsing through it and just go with endless if, elsif, and else statements? How would that look?
It can be in Python bash or Perl, doesn't matter to me really but I don't know how to structure the thing and then fill in the gaps and expand the script.
Here's where I imagine starting from
use Net::SSH2
use File::Slurp;
print "\nEnter command list filename: ";
my $commandlist = <STDIN>;
chomp($commandlist);
my #commandfile = read_file($commandlist, chomp => 1);
my $commandsize = #commandfile;
How would I store the output of the commands in a variable or a temporary file and parse through it with conditions?
This is a very broad question, and it is unclear exactly what is puzzling you
The documentation for
Net::SSH2
and
Net::SSH2::Channel
describes clearly how to open a channel, send commands to it, and receive the response
You ask how you could store the results of the command in a variable, but it would be very awkward to do anything else. Again, the documentation describes this clearly
I suggest that you try writing some working code and experiment with it. It will be much easier to help you when you have a specific question

Programmatically test whether an arbitrary user has access to an arbitrary file

I'd like to be able to write tests or specs that state that user soandso can read or edit file such_and_such. Although I would ultimately like this to be in RSpec, I'm assuming it will either all boil down to Bash commands (neatly wrapped in Ruby methods, of course), since it doesn't seem to be available in Ruby's File class, or anything else I've looked at. I've been hunting around for something in Bash to do this, and can't find anything (and nothing in the File class looks useful either). (Note: File.owned? does not do what I want, as it only tests if the process running the Ruby code is the owner, not if any arbitrary user has rights to edit or read, so it's significantly different on two counts.)
Is there any way to do this, built into Bash? (Or Ruby, or some Ruby gem, that I missed?) Or do I need to build a system myself, by getting owner and group info for the file, as well as read, write, and execute bits for each, and then looking up members of the group (in case so_and_so is not the owner), and seeing if the desired permissions are available to soandso either through ownership or group membership?
I'm currently only concerned about doing this on Unix-like systems, although something not dependent on a Unix shell that would also run on Windows would be a nice bonus.
Also, I'm interested in testing actual files, so something like FakeFS is not (as far as I can see) useful to me. I'm not trying to test how my Ruby code will interact with a file system, I'm trying to verify that all the necessary operations can be performed on actual files and directories. (Again,) I want to be able to specify that soandso can edit (or read) file such_and_such, NOT specify that file such_and_such is owned by soandso. The whole point of this is not specifying ownership (since that's an implementation detail and may need to change to accommodate other requirements, such as security) but only specify what my application and users actually need to be able to do to/in the file system.
If the user running your Ruby script has the necessary permissions, you could try running a test with the specific user with sudo.
sudo -u soandso test -r such_and_such
-r tests readability, -w writability. The return code is 0 if the test is passed.
By the way, I think that since the feature you are looking for could only be available for the super-user (since figuring out whether someone else has enough permissions would need you to have read permissions at least), it makes sense that it is not readily available in Ruby File class.
I haven't tried it, but I wonder if the FileTest methods would work as they say they operate on the "effective user id".
File.readable?(file_name) → true or false Link
Returns true if the named file is readable by the effective user id of this process.
File.writable?(file_name) → true or false Link
Returns true if the named file is writable by the effective user id of this process.
That in conjunction with seteuid might do the trick.
Process::Sys.seteuid(integer) → nil Link
Set the effective user ID of the calling process to integer. Not available on all platforms.
There is also setegid for group stuff...
Parse Permissions with File::Stat and Etc Modules
While this isn't a pre-packaged solution, you should be able to inspect a given file where you have sufficient access for stat in order to get mode, uid, and gid information. You can then parse /etc/passwd and /etc/group in order to find out whether a given user would have sufficient permissions.
Some building blocks include:
# Get the file's octal mode.
File.stat('/etc/passwd').mode.to_s(8)
#=> "100644"
# Get the group ID assigned to the file.
File.stat('/etc/passwd').gid
#=> 0
# Get the username associated with the given GID.
Etc.getpwuid(0).name
#=> "root"
You could also use the id utility to get information about a given user. For example:
%x{id root}
#=> "uid=0(root) gid=0(root) groups=0(root)\n"
but I think it would be easier to use the standard libraries rather than parse the output from id. YMMV.

Extending tcsh completion

I must work with tcsh.
I am using an internal tool that provides basic completion for some of its commands.
I would like to extend the completion.
I mean that in future releases the default completion may evolve.
I tried something like this:
set def_cmpl = complete tool
complete tool $def_cmpl 'n/-l/(reg short long gui)/'
But I don't understand the result I get.
Indeed, the quotes inside $def_cmpl are doubled:
tcsh> complete tool
''n#-t#$script#'' n/-l/(reg short long gui)/'
I tried some tricks with echo, sed, etc. but I can't avoid those ''.
Could somebody help me?
Please don't say go on bash... The tool doesn't support it...
Finally, I did not find a solution to keep the data inside the script. So, the solution was to redirect the output of the complete command inside a file and then to append new lines to the file.

How to use cbreak mode in Ruby terminal application?

In a small Ruby application, I'd like user input to be accepted without having to wait for a carriage return. My understanding is that cbreak mode needs to be enabled in order for the terminal to feed user input directly into the script.
I tried simply running x%[cbreak()] at the top of my script but that didn't work. I've also seen that it's possible to use (n)curses to achieve the same results, although that seems like overkill.
Does anybody have a suggestion on how to implement this?
Thanks
cbreak is a curses function call, so %x definitely doesn't apply (that is for executing shell commands). cbreak is defined in the standard curses library, so that would probably be your best bet.
See:
http://ruby-doc.org/stdlib/libdoc/curses/rdoc/classes/Curses.html#M000280
Edit: you might also check out Curses.getch
One solution which avoids having to use curses (which I find difficult to implement) is to use the shell's read command via %x. It doesn't feel right dipping into the shell to do something that seems like Ruby's STDIN should be responsible for, but it's simple and it works.
#! /usr/bin/ruby
puts "Please enter your first initial"
str = %x[read -s -n1 keypress; echo $keypress]
puts "Your first inital is " + str

Extract DVD subtitles programmatically

I'm trying to extract subtitles from unencrypted DVDs with a program, so I can save them seperately. I know there are programs that do that (I found this page for example: http://www.bunkus.org/dvdripping4linux/en/separate/subtitles.html), but I would like to be able to do it with a library call or something like that (do libdvdread or libdvdnav support this), preferably using ruby.
You can have a look at Handbrake, it allows you to extract video, audio and subtitles.
There is also the Handbrake manual here, and the subtitles section here, that can provide more information.
This isn't in Ruby but you should be able to call the Handbrake CLI from Ruby without any problems.
I don't know of any library, which would be able to do this.
In ruby you can call programs. For example to get a directory listing you can do
files= `ls "#{dir}"`.to_a
The backtick variant gives you stdout of the calle program.
To know wheter a file exists
system("ls \"#{file}\"")
The system variant tells you whether the return value of the called program was 0.
Using this two methods, you can do almost anything with noninteracitve programs. The programs described in http://www.bunkus.org/dvdripping4linux/en/separate/subtitles.html seme to be suitable for this kind of control.
Be carefull with escaping arguments you give to external programs. If an argumend is "; rm -rf *; ls ". undesirable things may happen.

Resources