I'd like to call a command line utility from a ruby script like so:
#!/env/ruby
json = {"key" => "value that \"has\" quotes"}.to_json
`aws events put-targets --cli-input-json #{json}`
Such that the resultant call should look like:
aws events put-targets --cli-input-json "{\"key\": \"value that \"has\" quotes\"}"
However, the result in this string interpolation results in a clean looking json structure without the quotes escaped and so results in error at the command line. Eg.
aws events put-targets --cli-input-json {"key": "value that \"has\" quotes"}
I need all the quotes properly escaped so that a string to the command line can be parsed as proper json.
I've tried doing string manipulation to manually escape quotes with things like:
json.gsub(/\"/,'\"')
But that doesn't work either.
This seems like it's harder than it should be. How can I render a properly escaped json string to the command line call?
I do have a rails environment that I can run this through if there are utilities that ActiveSupport provides that would facilitate this.
In this case it is simpler and more effective to make a system call without a shell. If you use the multi-argument form of Kernel#system to invoke the external command directly:
system('aws', 'events', 'puts-targets', '--cli-input-json', json)
No shell, no quoting or escaping problems with json.
If you need to do more complicated things (such as capture output or errors), look into the various methods in Open3.
If you absolutely must go through a shell there's always Shellwords.shellescape. But really, when you use the shell, you're:
Building a command line with a bunch of Ruby string operations.
Invoking a shell.
Letting the shell parse the command line (i.e. undo everything you did in (1)).
Letting the shell invoke the command with your arguments.
Why not go straight to (4) yourself?
Thanks to #mu-is-too-short, I came across Shellwords which is a neat utility. This didn't solve the problem however, but led me to search for "shell escape json" which in turn led me to: Best way to escape and unescape strings in Ruby?
So:
json = {"key" => "value that \"has\" quotes"}.to_json.dump
Properly gets the escaped string that bash will understand. Tada.
UPDATE: Don't use this in production code. You're better off following #mu-is-too-short's advice in the comments or using a higher level library.
Related
I have a rails app with an interface like this:
<%= form_tag 'sound/speak', remote: true do %>
<input type='text' name='phrase'>
<%= submit_tag 'Say' %>
<% end %>
Which sends a request to a controller:
def speak
`espeak "#{params['phrase']}"`
end
It works but is this a bad idea in terms of safety? I'm just trying to make my server (raspberry pi) to speak whatever I write into the textbox. However in future this might be used by a group of friends. In this case I need to make sure it's impossible to, say, escape the quotes and the "say" command and execute a malicious script/command on the server. So, am I safe with this code?
What you're doing is dangerous. Consider things like this:
danger = 'antisocial tendencies" $(ls) "'
`echo "#{danger}"`
and think about what else you could do besides running ls.
You could use Shellwords (as in How to escape strings for terminal in Ruby?) but that would mean doing this:
Use Shellwords to escape params['phrase'].
Use string interpolation to build a command line.
Hand that command line to the backticks.
Execute a shell.
Let the shell parse the command line you just built.
Get the shell to run espeak with the desired argument.
You could avoid that by using the tools in Open3 or the multi-argument form of Kernel#system to jump directly to step (6) without involving a shell at all.
The shell is where the quoting problems come in so not involving a shell is a convenient way to cut out a whole class of errors and vulnerabilities. Avoiding the shell is also more efficient (although you'd be hard pressed to notice) and flexible as a bonus.
Thanks to sakurashinken for finding the sort-of-duplicate I linked to above, shame you deleted your answer.
I have a lua REPL, and would like to run a lua script file stored as plain text at HTTPS://URL. I understand os.execute() can run OS commands so we can use curl etc. to grab the script then load(). Is that something possible to do in lua REPL with a single line?
Note: If you're going to run source code directly from the web, use https at least, to avoid easy MitM attacks.
To give this question an answer, since Egor will probably not post it as such:
(loadstring or load)(io.popen("wget -qO- https://i.imgur.com/91HtaFp.gif"):read"*a")()
For why this prints Hello world:
loadstring or load is to be compatible with different Lua versions, as the functions loadstring and load were merged at some point (5.2 I believe). io.popen executes its first argument in the shell and returns a file pointer to its stdout.
The "gif" from Egor is not really a GIF (open this in your browser: view-source:https://i.imgur.com/91HtaFp.gif) but a plain text file that contains this text:
GIF89a=GIF89a
print'Hello world'
Basically a GIF starts with GIF89a and the =GIF89a afterwards is just to produce valid Lua, meaning you don't have to use imgur or gifs, you can just as well use raw gists or github.
Now, it's rather unlikely that os.execute is available in a sandbox when io.popen is not, but if it is, you can achieve a one-liner (though drastically longer) using os.execute and temporary files
Lets first write this out because in a single line it will be a bit complex:
(function(u,f)
-- get a temp file name, Windows prefixes those with a \, so remove that
f=f or os.tmpname():gsub('^\\','')
-- run curl, make it output into our temp file
os.execute(('curl -s "%s" -o "%s"'):format(u,f))
-- load/run temp file
loadfile(f)()
os.remove(f)
end)("https://i.imgur.com/91HtaFp.gif");
And you can easily condense that into a single line by removing comments, tabs and newlines:
(function(u,f)f=f or os.tmpname():gsub('^\\','')os.execute(('curl -s "%s" -o "%s"'):format(u,f))loadfile(f)()os.remove(f)end)("https://i.imgur.com/91HtaFp.gif");
I am very new to ruby on rails and I have a very simple question.
I am able to read the user input from a text field in ruby on rails
#user = params[:user]
I have to pass this value as an argument to a shell script
I tried a number of things, but nothing works
run_command "./file.sh #{#user}"
and
%x(./file.sh #{#user})
The script receives "{user="
Any help is appreciated!
First, make sure you escape any parameters you pass to command line. You may be easily attacked via command line injection this way. Try this instead:
system "/path/to/file.sh #{#user.shellescape}"
You may also want to try exec, depending on how you want to track output. Please see http://ruby-doc.org/core-2.3.0/Kernel.html for more details on both
I'm trying to run an executable using Lua's os.execute() function. If I do something like the following it does not work:
os.execute("C:\\\Program Files\\\Movie Maker\\\moviemk.exe")
However, if I put my lua file in the same path that moviemk.exe is in then it can call it.
Any ideas why this may be?
P.S. I'm using Windows XP SP3
This is a classic problem with the command shell. It isn't really a Windows specific problem, except that on *nix, people never really got into the habit of putting spaces in file names, and Windows puts spaces in several default system places such as C:\Program Files.
What is happening is that os.execute(str) is implemented in terms of the ANSI C function system(str), which on Windows tries to duplicate the effect of typing "cmd /C "..str to the command prompt. (On *nix, it uses /bin/sh -c instead of cmd /C.)
The classic problem is that this has to split the complete command string at whitespace to decide what program to run, and what its arguments are.
Your original example: os.execute("C:\\Program Files\\Movie Maker\\moviemk.exe") effectively became cmd /c c:\program files\movie maker\moviemk.exe, which after splitting it up on whitespace, CMD tried to find a program named c:\program to execute with arguments named files\movie and maker\moviemk.exe. This isn't what you intended.
The solution is to be much more defensive about quoting.
I would write this as:
os.execute [["C:\Program Files\Movie Maker\Moviemk.exe"]]
If there were additional command line arguments to be supplied, I would use double-quotes around each, and a single space between arguments. Using the long string syntax [[...]] has the advantage that backslash isn't a special character, so you don't need extra leaning toothpicks making it harder to read the string literal.
Using double quotes around each argument should work on both Windows and *nix, although it is harder to find commands that are the same on both platforms, of course.
One other detail to be aware of is that \Programs Files might not be on C:. There might not even be a disk named C:. (My work PC boots from E: and I find more buggy programs that way.) The easiest way to learn the correct pathname is to just use the environment variable ProgramFiles. There are many other ways.
Try:
os.execute("C:\\Program Files\\Movie Maker\\moviemk.exe")
or:
os.execute("C:/Program Files/Movie Maker/moviemk.exe")
The '\' character is used for escape characters in Lua.
I have a question about ruby and wsdl soap.
I couldn't find a way to get each method's params and their type.
For example, if I found out that a soap has a methods called "get_user_information" (using wsdlDriver) is there a way to know if this method requires some params and what type of params does it require (int, string, complex type, ecc..)?
I'd like to be able to build html forms from a remote wsdl for each method...
Sorry for my horrible English :D
Are you using soapr4?
Soap4r comes with a command line client to build proxies for accessing web services via SOAP. This is preferable to using the wsdlDriver which has to build the proxy dynamically every time it runs.
To build a "permanent" proxy then you need to run the following command
wsdl2ruby.rb --type client --wsdl http://some/path/to/the/wsdl
When this command runs then you should end up with a bunch of ruby files one of which (probably default.rb) will call each method in turn and document the necessary inputs and outputs.
Alternatively you may find the Wsdl Analyser useful. This will allow you to enter the URL for a WSDL which it will then analyse and list all of the operations and (sometimes) the paramaters required
Thank you for the very quick response!
I'll try to explain myself a little better :D
I've tried soap4r, and I'm able to get soap's methods with something like this:
require "soap/wsdlDriver"
client = SOAP::WSDLDriverFactory.new(my-wsdl-url).create_rpc_driver
puts client.singleton_methods
What I'd like to know is:
If, for example, my soap has a method called "get_some_params_and_sum_them", is there a way to know how many params it takes and which type they should be?
Something like
puts client.method("get_some_params_and_sum_them").params
Wsdl Analyser does it, and I'd like to know if this is possible also in a ruby script without tons of code lines :D