I have a method that scans a URL for a website that contains an error:
def begin_vulnerability_check
info("Checking if sites are vulnerable.")
IO.read("#{PATH}/temp/SQL_sites_to_check.txt").each_line do |parse|
Timeout::timeout(10) do
parsing = Nokogiri::HTML(RestClient.get("#{parse.chomp}"))
info("Parsing page for SQL syntax error: #{parse.chomp}")
if parsing.css('html')[0].to_s[/You have an error in your SQL syntax/]
successful = parse
success("URL: #{parse.chomp} returned SQL syntax error, dumped to SQL_VULN.txt")
File.open("#{PATH}/lib/SQL_VULN.txt", "a+"){|s| s.puts(parse)}
sleep(1)
else
err("URL: #{parse.chomp} returned and error, dumped to non_exploitable.txt")
File.open("#{PATH}/lib/non_exploitable.txt", "a+"){|s| s.puts(parse)}
sleep(1)
end
end
end
end
During testing I'm scanning through this list of URLs:
http://www.bible.com/subcat.php?id=2'
http://www.cidko.com/pro_con.php?id=3'
http://www.slavsandtars.com/about.php?id=25'
http://www.police.gov/content.php?id=275'
http://www.icdprague.org/index.php?id=10'
http://huawei.com/en/plugin.php?id=hwdownload'
https://huawei.com/en/plugin.php?id=unlock'
https://facebook.com/profile.php?id'
http://www.footballclub.com.au/index.php?id=43'
http://www.mesrs.gouv/index.php?id=1525'
I also have a rescue block that is suppose to catch the exception Timeout::Error and move to the next URL in the list:
begin
begin_vulnerability_check
rescue Timeout::Error
if Timeout::Error
warn("Page timed out, this is usually cause by the page returning a white page, or being non-existent, skipping.")
next
end
end
However while attempting to run this program, I get the following error:
whitewidow.rb:130: Invalid next
whitewidow.rb: compile error (SyntaxError)
Line 130:
rescue Timeout::Error
if Timeout::Error
warn("Page timed out, this is usually cause by the page returning a white page, or being non-existent, skipping.")
next #<= HERE
end
end
My question being, am I using the next in the wrong sense? It seems to me like next would be, if this happens go to the next line, am I wrong for thinking like that? How can I refactor this to work?
You can use next to return from a block. You cannot use it outside a block like you're trying to do.
But you don't even need next, because when you rescue the timeout error the iteration will automatically continue with the next line. You just have to move the rescue inside the each_line iteration.
Your code should be something like this:
def begin_vulnerability_check
IO.read("#{PATH}/temp/SQL_sites_to_check.txt").each_line do |parse|
begin
Timeout::timeout(10) do
...
end
rescue Timeout::Error
# Will automatically continue with next line after this
end
end
end
Related
I want to catch the PuppetError::Error exception so it's not fatal, but a PuppetError::ExecError should be fatal.
My problem seems to be that the rescue clause is catching the PuppetError::Error then elevating it to ExecError status, so it isn't caught by the calling code. I need to limit the rescue clause to one line, the Open3.. line. But how?
module PuppetCert
def self.remove(instance_hostname)
begin
Open3::popen3("puppet cert clean #{instance_hostname}") do |stdin, stdout, stderr, wait_thr|
if wait_thr.value.success?
puts "success"
else
raise PuppetError::Error
return
end
end
rescue
raise PuppetError::ExecError
end
end
end
instances.each do |i|
begin
PuppetCert.remove(i)
rescue PuppetError::Error
logger.error("PuppetCert.remove failed. Not fatal")
end
end
Thanks in advance
EDIT: To clarify, both PuppetError type are custom types I have created (not shown here) as a way to identify different sorts of failure. PuppetError::Error is for when the system call to the puppet binary fails; PuppetError:ExecError is for when Open3::popen3 fails because it can't find the puppet binary.
You could use two rescue clauses, one to catch PuppetError::Error and raise it again, then on the other catch everything else:
module PuppetCert
def self.remove(instance_hostname)
begin
Open3::popen3("puppet cert clean #{instance_hostname}") do |stdin, stdout, stderr, wait_thr|
if wait_thr.value.success?
puts "success"
else
raise PuppetError::Error
return
end
end
rescue PuppetError::Error => error
raise error
rescue
raise PuppetError::ExecError
end
end
end
The first rescue clause (the order matters, so it must come first) rescues PuppetError::Error and simply raises it again, that is, it only intercepts it before getting to the more general rescue, so it won't get promoted. Every other exception will be rescued by the second rescue clause and promoted as PuppetError::ExecError.
I'm working on an application that I had been failing out of using raise, like this:
raise 'Some error happened!'
This caused caused an unsightly stack trace to be displayed, so I adjusted the code to use abort, like this:
abort 'Some error happened!'
Perfect! Now I can exit with a clear message and no stacktrace.
The problem comes in because, in one instance I need to rescue from this situation. I can do something like:
begin
abort 'Some error happened!'
rescue SystemExit
puts 'Rescued'
end
puts 'Moving on...'
# Outputs:
# Some error happened!
# Rescued
# Moving on...
This has the disadvantages of displaying the abort message despite being rescued and rescuing a fairly vague error. What I would really like to do, is something like this:
class MySuperFancyCustomError < StandardError
def initialize
super
abort 'Some error happened!'
end
end
begin
raise MySuperFancyCustomError
rescue MySuperFancyCustomError
puts 'Rescued'
end
puts 'Moving on...'
# Outputs:
# Some error happened!
But I haven't been able to figure out a way to set this up so that I can rescue from it. I just need it to keep running and output:
Rescued
Moving on...
Instead of failing with:
Some error happened!
Does anyone know of an elegant way to make this happen?
I'd strongly advise against using abort for flow control. Instead try this:
class CustomException < StandardError
end
class Example
def explode
raise CustomException, "Michael Bay here!"
end
end
begin
Example.new.explode
rescue CustomException => e
puts "This happened: %s" % e
end
Here you define your own exception type, and then explicitly catch it.
Now you'll get this output, but you have full control over how, if, and when it's displayed:
This happened: Michael Bay here!
I have the following code:
rescue Timeout::Error, StandardError => e
puts "Caught exception: #{e.message}".red
log.puts("#{e.backtrace}")
email_ids_all.each do |email_delete|
call= "/api/v2/emails/#{email_delete}/"
......
Before this rescue piece I have defined log and email_ids_all. However, neither of these are recognized within the ruby script. If i do this:
rescue Timeout::Error, StandardError => e
File.open(rescuelogfile, 'w') do |log| #setup log to write response codes.
puts "Caught exception: #{e.message}".red
log.puts("#{e.backtrace}")
email_ids_all.each do |email_delete|
call= "/api/v2/emails/#{email_delete}/"
....
log works fine, which makes sense. It would take a lot of writing to redefine the email_ids_all array and other variables contained inside my rescue block.
Is there anyway to allow variables to be recognized inside the rescue? Basically my code is laid out like this:
begin
#some code
rescue
#above code
end
I am using ruby 1.9.3.
EDIT----
log starts right after my begin statement :
begin
File.open(logfile, 'w') do |log| #setup log to write response codes.
log.puts works throughout the entire code except when an error is thrown, and then it runs the rescue script where log is not available.
The same goes for email_ids_all. There is an API call that generates about 10,000 emails and each of them is added to the array email_ids_all. The script is receiving an error about halfway through generating these emails, and so I need the rescue script to delete all email ids in the email_ids_all array. But for whatever reason, I get the following error:
FS_Test_Env.rb:762:in `block in <main>': undefined local variable or method `email_ids_all' for main:Object (NameError)
from FS_Test_Env.rb:759:in `open'
from FS_Test_Env.rb:759:in `rescue in <main>'
from FS_Test_Env.rb:7:in `<main>'
Any thoughts?
The way you put it, it should work, for example:
irb(main):001:0> begin
irb(main):002:1* x = 1
irb(main):003:1> x / 0
irb(main):004:1> rescue Exception => e
irb(main):005:1> p x
irb(main):006:1> end
1
=> 1
So it looks like the exception is thrown before your variables are defined.
The scope of the block parameter log is limited to that block. This is the whole point of the open with block.
Maybe you want to do:
begin
log = File.open('logfile', 'w')
...
rescue
...
ensure
log.close
end
Note that this does not cover errors when opening the logfile.
Regarding email_ids_all, I guess (!) you have the exception in a statement like:
email_ids_all = ... a long and complex calculation which raises an exception
If yes, the problem is that the assignment happens only after the whole right-hand side is calculated. The var email_ids_all is not yet created when the exception happens.
In order to access the elements created before the exception, you have to keep track of them, e.g.
begin
email_ids = []
10000.times do
email_ids << ... # create element eventually raising an exception
end
rescue
... # treat the already created elements
end
ie.link(:id, "ctl00_ContentPlaceHolder1_BtnSearch").click
rescue Timeout::Error
#sleep(5)
puts "timeout"
ie.close
#sleep(9)
retry #open new browser and go to begin
end`
when .click link gets time out , then output is = timeout, but ie.close does not work.
and the time out error comes
*i want to close the browser when time out error comes*
I do not believe that ie.link(:id, "ctl00_ContentPlaceHolder1_BtnSearch").click will ever throw a Timeout::Error. This would be why the rescue block is never executed.
The likely exceptions that are thrown are:
When you do ie.link(:id, "ctl00_ContentPlaceHolder1_BtnSearch").click and the element is not found, a Watir::Exception::UnknownObjectException will occur.
When you do ie.link(:id, "ctl00_ContentPlaceHolder1_BtnSearch").when_present.click and the element is not found within the required time frame, a Watir::Wait::TimeoutError will occur.
Your rescue likely needs to be catching one of these exceptions instead.
begin
ie = Watir::Browser.new
ie.goto 'www.yourpage.com'
ie.link(:id, "ctl00_ContentPlaceHolder1_BtnSearch").click
rescue Watir::Exception::UnknownObjectException
puts "element not found"
ie.close
retry #open new browser and go to begin
end
Or if you are using when_present on the element:
begin
ie = Watir::Browser.new
ie.goto 'www.yourpage.com'
ie.link(:id, "ctl00_ContentPlaceHolder1_BtnSearch").when_presentclick
rescue Watir::Wait::TimeoutError
puts "element did not appear in time"
ie.close
retry #open new browser and go to begin
end
I'm trying to understand exceptions in Ruby but I'm a little confused. The tutorial I'm using says that if an exception occurs that does not match any of the exceptions identified by the rescue statements, you can use an "else" to catch it:
begin
# -
rescue OneTypeOfException
# -
rescue AnotherTypeOfException
# -
else
# Other exceptions
ensure
# Always will be executed
end
However, I also saw later in the tutorial "rescue" being used without an exception specified:
begin
file = open("/unexistant_file")
if file
puts "File opened successfully"
end
rescue
file = STDIN
end
print file, "==", STDIN, "\n"
If you can do this, then do I ever need to use else? Or can I just use a generic rescue at the end like this?
begin
# -
rescue OneTypeOfException
# -
rescue AnotherTypeOfException
# -
rescue
# Other exceptions
ensure
# Always will be executed
end
The else is for when the block completes without an exception thrown. The ensure is run whether the block completes successfully or not. Example:
begin
puts "Hello, world!"
rescue
puts "rescue"
else
puts "else"
ensure
puts "ensure"
end
This will print Hello, world!, then else, then ensure.
Here's a concrete use-case for else in a begin expression. Suppose you're writing automated tests, and you want to write a method that returns the error raised by a block. But you also want the test to fail if the block doesn't raise an error. You can do this:
def get_error_from(&block)
begin
block.call
rescue => err
err # we want to return this
else
raise "No error was raised"
end
end
Note that you can't move the raise inside the begin block, because it'll get rescued. Of course, there are other ways without using else, like checking whether err is nil after the end, but that's not as succinct.
Personally, I rarely use else in this way because I think it's rarely needed, but it does come in handy in those rare cases.
EDIT
Another use case occurred to me. Here's a typical begin/rescue:
begin
do_something_that_may_raise_argument_error
do_something_else_when_the_previous_line_doesnt_raise
rescue ArgumentError => e
handle_the_error
end
Why is this less than ideal? Because the intent is to rescue when do_something_that_may_raise_argument_error raises ArgumentError, not when do_something_else_when_the_previous_line_doesnt_raise raises.
It's usually better to use begin/rescue to wrap the minimum code you want to protect from a raise, because otherwise:
you may mask bugs in the code that wasn't supposed to raise
the intention of rescue is harder to decipher. Someone (including your future self) may read the code and wonder "Which expression did I want to protect? It looks like expression ABC... but maybe expression DEF too???? What was the author intending?!" Refactoring becomes much more difficult.
You avoid those problems with this simple change:
begin
do_something_that_may_raise_argument_error
rescue ArgumentError => e
handle_the_error
else
do_something_else_when_the_previous_line_doesnt_raise
end
The else block in a begin rescue end block is used when you are perhaps expecting an exception of some sort to occur. If you run through all of your expected exceptions but still have nothing raised, then in your else block you can do whatever's needed now that you know that your original code ran error free.
The only reason I can see for the else block is if you want to execute something before the ensure block when the code in the begin block didn't raise any errors.
begin
puts "Hello"
rescue
puts "Error"
else
puts "Success"
ensure
puts "my old friend"
puts "I've come to talk with you again."
end
Thanks to else you sometimes can merge two nested begin end blocks.
So (simplified example from my current code) instead of:
begin
html = begin
NetHTTPUtils.request_data url
rescue NetHTTPUtils::Error => e
raise unless 503 == e.code
sleep 60
retry
end
redo unless html["market"]
end
you write:
begin
html = NetHTTPUtils.request_data url
rescue NetHTTPUtils::Error => e
raise unless 503 == e.code
sleep 60
retry
else
redo unless html["market"]
end