2015-07-28 13:52:22 -05:00
# Deprecation notice!
Please see [CONTRIBUTING.md ](https://github.com/rapid7/metasploit-framework/blob/master/CONTRIBUTING.md ) for an authoritative coding guide. This document has fallen out of date. We don't write bad code any more! Hooray!
2015-07-28 13:47:56 -05:00
2012-05-22 08:37:15 -07:00
This is a collection of all the bad code we often see in Metasploit modules. You should avoid them, too.
Note: Some of these examples use puts() for demo purposes, but you should always use print_status / print_error when writing a module.
2015-07-28 13:52:22 -05:00
### Bad Examples You Should NOT Follow:
2012-05-22 08:37:15 -07:00
1. Not checking the return value of a Metasploit API
2. Ruby 1.9.3 vs 1.8.7... gotcha!
3. Not checking the return value when using match()
2012-12-13 09:08:15 -08:00
4. Not checking nil before accessing a method
2012-05-22 08:37:15 -07:00
5. Using exception handling to shut an error up
6. Not taking advantage of the 'ensure' block
7. Adding the 'VERBOSE' option
2013-01-31 00:53:14 -08:00
8. Avoid using 'vars_post' for send_request_cgi() when crafting a POST request
2012-05-22 08:37:15 -07:00
9. Bad variable naming style
10. Using global variables
2015-02-11 13:56:47 -06:00
11. Modifying the datastore during execution
2012-05-22 08:37:15 -07:00
**1. Not checking the return value of a Metasploit API **
2015-02-11 13:56:47 -06:00
2012-05-22 08:37:15 -07:00
``` ruby
2015-02-11 14:46:18 -06:00
res = send_request_cgi ( {
'method' = > 'GET' ,
'uri' = > '/app/index.php'
} )
# There's a bug here, because res can return nil (due to a timeout or other reasons)
# If that happens, you will hit a "undefined method `code' for nil:NilClass" error.
2015-07-28 14:58:40 -05:00
# The correct way should be: if res && res.code == 200
2015-02-11 14:46:18 -06:00
if res . code == 200
print_status ( " Response looks good " )
else
print_error ( " Unexpected response " )
end
2012-05-22 08:37:15 -07:00
```
2015-02-11 13:56:47 -06:00
2012-05-22 08:37:15 -07:00
**2. Ruby 1.9.3 vs 1.8.7... gotcha! **
2015-02-11 13:56:47 -06:00
2012-05-22 08:37:15 -07:00
``` ruby
2015-02-11 14:46:18 -06:00
some_string = " ABC "
2012-05-22 08:37:15 -07:00
2015-02-11 14:46:18 -06:00
# This can cause unexpected results to your module.
# Better to always do: char = some_string[1, 1]
char = some_string [ 1 ]
2012-05-22 08:37:15 -07:00
2015-02-11 14:46:18 -06:00
if char == 'B'
puts " You will see this message in Ruby 1.9.3 "
elsif char == 66
puts " You will see this message in Ruby 1.8.7 "
end
2012-05-22 08:37:15 -07:00
```
2015-02-11 13:56:47 -06:00
2012-05-22 08:37:15 -07:00
``` ruby
2015-02-11 14:46:18 -06:00
# 1.9 allows a comma after the last argument when calling
# a method while 1.8 does not. The most common place to
# see this error is in the update_info() section in a
# module's constructor.
some_method (
" arg1 " ,
" arg2 " , # <-- This comma is a syntax error on 1.8.x
)
2012-05-22 08:37:15 -07:00
```
**3. Not checking the return value when using match() **
2015-02-11 13:56:47 -06:00
2012-05-22 08:37:15 -07:00
``` ruby
2015-02-11 14:46:18 -06:00
str = " dragon! drag on! Not lizard, I don't do that tongue thing "
2012-05-22 08:37:15 -07:00
2015-02-11 14:46:18 -06:00
# This tries to print "Not snake", but it's not in the string,
# so you'll get this error: "undefined method `[]' for nil:NilClass"
puts str . match ( / (Not snake) / ) [ 0 ]
2012-05-22 08:37:15 -07:00
```
2015-02-11 13:56:47 -06:00
2012-05-22 08:37:15 -07:00
``` ruby
2015-02-11 14:46:18 -06:00
# The above is better written as:
if ( str =~ / (Not snake) / )
puts $1
end
2012-05-22 08:37:15 -07:00
```
2012-12-13 09:06:21 -08:00
**4. Not checking nil first before accessing a method **
2015-02-11 13:56:47 -06:00
2012-05-22 08:37:15 -07:00
``` ruby
2015-02-11 14:46:18 -06:00
str = " These things are round and tasty, let's call them... tastycles! "
2012-05-22 08:37:15 -07:00
2015-02-11 14:46:18 -06:00
food = str . scan ( / donut holes / ) [ 0 ]
2012-05-22 08:37:15 -07:00
2015-02-11 14:46:18 -06:00
# food is nil, and nil has no method called "empty".
# This will throw an error: "undefined method `empty?' for nil:NilClass"
if food . empty? or food . nil?
puts " I don't know what it's called "
end
2012-05-22 08:37:15 -07:00
```
2015-02-11 13:56:47 -06:00
2012-05-22 08:37:15 -07:00
**5. Using exception handling to shut an error up **
2015-02-11 13:56:47 -06:00
2012-05-22 08:37:15 -07:00
``` ruby
2015-02-11 14:46:18 -06:00
begin
# This block has 2 issues:
# Issue #1: sample() is not a method in 1.8.7
# Issue #2: Divided by 0 (race condition)
n = [ 0 , 1 , 2 , 3 , 4 , 5 ] . sample
1 / n
rescue
# If the user reports a bug saying this code isn't
# working, it can be hard to debug exactly what went
# wrong for the user without a backtrace.
# When you do this, the error also won't be logged in
# framework.log, either.
# Note that rescuing ::Exception is especially harmful
# because it can even hide syntax errors.
end
2012-05-22 08:37:15 -07:00
```
2015-02-11 13:56:47 -06:00
2012-05-22 08:37:15 -07:00
**6. Not taking advantage of the 'ensure' block **
2015-02-11 13:56:47 -06:00
2012-05-22 08:37:15 -07:00
``` ruby
2015-02-11 14:46:18 -06:00
# You should use the ensure block to make sure x always has a value,
# which also avoids repeating code
begin
n = [ 0 , 1 , 2 ] . sample
x = 1 / n
rescue ZeroDivisionError = > e
puts " Are you smarter than a 5th grader? #{ e . message } "
x = 0 # Can put this in the ensure block
rescue NoMethodError
puts " You must be using an older Ruby "
x = 0 # Can put this in the ensure block
end
puts " Value is #{ x . to_s } "
2012-05-22 08:37:15 -07:00
```
2015-02-11 13:56:47 -06:00
2012-05-22 08:37:15 -07:00
**7. Adding the 'VERBOSE' option **
2015-02-11 13:56:47 -06:00
2012-05-22 08:37:15 -07:00
``` ruby
2015-02-11 14:46:18 -06:00
register_options (
[
# You already have this. Just type 'show advanced' and you'll see it.
# So no need to register again
OptBool . new ( " VERBOSE " , [ false , 'Enable detailed status messages' , false ] )
] , self . class )
2012-05-22 08:37:15 -07:00
```
2015-02-11 13:56:47 -06:00
2012-05-22 09:37:35 -07:00
**8. Avoid using send_request_cgi()'s vars_get or vars_get when crafting a POST/GET request **
2015-02-11 13:56:47 -06:00
2012-05-22 08:37:15 -07:00
``` ruby
2015-02-11 14:46:18 -06:00
data_post = 'user=jsmith&pass=hello123'
# You should use the 'vars_post' key instead of 'data',
# unless you're trying to avoid the API escaping your
# parameter names
send_request_cgi ( {
'method' = > 'POST' ,
'uri' = > '/' ,
'data' = > data_post
} )
2012-05-22 08:37:15 -07:00
```
2015-02-11 13:56:47 -06:00
2012-05-22 08:37:15 -07:00
**9. Bad variable naming style **
2015-02-11 13:56:47 -06:00
2012-05-22 08:37:15 -07:00
``` ruby
2015-02-11 14:46:18 -06:00
# What's this, Java?
# The proper naming style in this case should be: my_string
myString = " hello, world "
2012-05-22 08:37:15 -07:00
```
**10. Using global variables **
2015-02-11 13:56:47 -06:00
2012-05-22 08:37:15 -07:00
``` ruby
2015-02-11 14:46:18 -06:00
# $msg is a global variable that can be accessed anywhere within the program.
# This can induce bugs to other modules or mixins that are hard to debug.
# Use @instance variables instead.
# This is also mentioned in your HACKING file :-)
class Opinion
def initialize
# This variable shouldn't be shared with other classes
$msg = " It's called the Freedom of Information Act. The Hippies finally got something right. "
end
end
class Metasploit3
def initialize
puts $msg
end
end
Opinion . new
Metasploit3 . new
2015-02-11 13:56:47 -06:00
```
**11. Modifying the datastore during execution **
``` ruby
2015-02-11 13:59:22 -06:00
# https://github.com/rapid7/metasploit-framework/issues/3853
2015-02-11 13:56:47 -06:00
datastore [ 'BAD' ] = 'This is bad.'
2015-07-28 13:47:56 -05:00
```