S3 Fuzzer: Output and garbage data tweaks.

- Output tweaks
- added support for printable_no_whitespace and binary_no_whitespace
This commit is contained in:
Kyle Marsh 2011-08-23 15:08:18 -07:00
parent f45d28765d
commit 18c3fe53c2
3 changed files with 67 additions and 32 deletions

View file

@ -3,6 +3,9 @@ start:
garbage: garbage:
- '{random 10-3000 printable}' - '{random 10-3000 printable}'
- '{random 10-1000 binary}' - '{random 10-1000 binary}'
garbage_no_whitespace:
- '{random 10-3000 printable_no_whitespace}'
- '{random 10-1000 binary_no_whitespace}'
choices: choices:
- bucket - bucket
@ -25,7 +28,7 @@ bucket_garbage_method:
- '{bucket_not_readable}' - '{bucket_not_readable}'
- '{bucket_writable}' - '{bucket_writable}'
- '{bucket_not_writable}' - '{bucket_not_writable}'
- '2 {garbage}' - '2 {garbage_no_whitespace}'
choices: choices:
- bucket_get_simple - bucket_get_simple
- bucket_get_filtered - bucket_get_filtered
@ -40,12 +43,12 @@ bucket_delete:
bucket: bucket:
- '{bucket_writable}' - '{bucket_writable}'
- '{bucket_not_writable}' - '{bucket_not_writable}'
- '2 {garbage}' - '2 {garbage_no_whitespace}'
query: query:
- null - null
- policy - policy
- website - website
- '2 {garbage}' - '2 {garbage_no_whitespace}'
choices: [] choices: []
bucket_get: bucket_get:
@ -54,7 +57,7 @@ bucket_get:
bucket: bucket:
- '{bucket_readable}' - '{bucket_readable}'
- '{bucket_not_readable}' - '{bucket_not_readable}'
- '2 {garbage}' - '2 {garbage_no_whitespace}'
choices: choices:
- 11 bucket_get_simple - 11 bucket_get_simple
- bucket_get_filtered - bucket_get_filtered
@ -72,56 +75,56 @@ bucket_get_simple:
- requestPayment - requestPayment
- versioning - versioning
- website - website
- '2 {garbage}' - '2 {garbage_no_whitespace}'
choices: [] choices: []
bucket_get_uploads: bucket_get_uploads:
set: set:
delimiter: delimiter:
- null - null
- '3 delimiter={garbage}' - '3 delimiter={garbage_no_whitespace}'
prefix: prefix:
- null - null
- '3 prefix={garbage}' - '3 prefix={garbage_no_whitespace}'
key_marker: key_marker:
- null - null
- 'key-marker={object_readable}' - 'key-marker={object_readable}'
- 'key-marker={object_not_readable}' - 'key-marker={object_not_readable}'
- 'key-marker={invalid_key}' - 'key-marker={invalid_key}'
- 'key-marker={random 100-1000 printable}' - 'key-marker={random 100-1000 printable_no_whitespace}'
max_uploads: max_uploads:
- null - null
- 'max-uploads={random 1-5 binary}' - 'max-uploads={random 1-5 binary_no_whitespace}'
- 'max-uploads={random 1-1000 digits}' - 'max-uploads={random 1-1000 digits}'
upload_id_marker: upload_id_marker:
- null - null
- '3 upload-id-marker={random}' - '3 upload-id-marker={random 0-1000 printable_no_whitespace}'
query: query:
- 'uploads' - 'uploads'
- 'uploads&{delimiter}&{prefix}' - 'uploads&{delimiter}&{prefix}'
- 'uploads&{max_uploads}&{key_marker}&{upload_id_marker}' - 'uploads&{max_uploads}&{key_marker}&{upload_id_marker}'
- '2 {garbage}' - '2 {garbage_no_whitespace}'
choices: [] choices: []
bucket_get_filtered: bucket_get_filtered:
set: set:
delimiter: delimiter:
- 'delimiter={garbage}' - 'delimiter={garbage_no_whitespace}'
prefix: prefix:
- 'prefix={garbage}' - 'prefix={garbage_no_whitespace}'
marker: marker:
- 'marker={object_readable}' - 'marker={object_readable}'
- 'marker={object_not_readable}' - 'marker={object_not_readable}'
- 'marker={invalid_key}' - 'marker={invalid_key}'
- 'marker={random 100-1000 printable}' - 'marker={random 100-1000 printable_no_whitespace}'
max_keys: max_keys:
- 'max-keys={random 1-5 binary}' - 'max-keys={random 1-5 binary_no_whitespace}'
- 'max-keys={random 1-1000 digits}' - 'max-keys={random 1-1000 digits}'
query: query:
- null - null
- '{delimiter}&{prefix}' - '{delimiter}&{prefix}'
- '{max-keys}&{marker}' - '{max-keys}&{marker}'
- '2 {garbage}' - '2 {garbage_no_whitespace}'
choices: [] choices: []
bucket_put: bucket_put:
@ -129,7 +132,7 @@ bucket_put:
bucket: bucket:
- '{bucket_writable}' - '{bucket_writable}'
- '{bucket_not_writable}' - '{bucket_not_writable}'
- '2 {garbage}' - '2 {garbage_no_whitespace}'
method: PUT method: PUT
choices: choices:
- bucket_put_simple - bucket_put_simple
@ -142,9 +145,14 @@ bucket_put_create:
- '2 {garbage}' - '2 {garbage}'
- '<CreateBucketConfiguration><LocationConstraint>{random 2-10 binary}</LocationConstraint></CreateBucketConfiguration>' - '<CreateBucketConfiguration><LocationConstraint>{random 2-10 binary}</LocationConstraint></CreateBucketConfiguration>'
acl: acl:
- private - 'private'
- 'public-read'
- 'public-read-write'
- 'authenticated-read'
- 'bucket-owner-read'
- 'bucket-owner-full-control'
- '{random 3000 letters}' - '{random 3000 letters}'
- '{random 100-1000 binary}' - '{random 100-1000 binary_no_whitespace}'
headers: headers:
- ['0-1', 'x-amz-acl', '{acl}'] - ['0-1', 'x-amz-acl', '{acl}']
choices: [] choices: []
@ -163,7 +171,7 @@ bucket_put_versioning:
- '<MfaDelete>{random 2-10 binary}</MfaDelete>' - '<MfaDelete>{random 2-10 binary}</MfaDelete>'
- '<MfaDelete>{random 2000-3000 printable}</MfaDelete>' - '<MfaDelete>{random 2000-3000 printable}</MfaDelete>'
mfa_header: mfa_header:
- '{random 10-1000 printable} {random 10-1000 printable}' - '{random 10-1000 printable_no_whitespace} {random 10-1000 printable_no_whitespace}'
headers: headers:
- ['0-1', 'x-amz-mfa', '{mfa_header}'] - ['0-1', 'x-amz-mfa', '{mfa_header}']
choices: [] choices: []
@ -225,7 +233,7 @@ bucket_put_simple:
notification_body: notification_body:
- null - null
- '<NotificationConfiguration />' - '<NotificationConfiguration />'
- '2 <NotificationConfiguration><TopicConfiguration>{topic}{event}</TopicConfiguration}</NotificationConfiguration>' - '2 <NotificationConfiguration><TopicConfiguration>{topic}{event}</TopicConfiguration></NotificationConfiguration>'
topic: topic:
- null - null
- '2 <Topic>{garbage}</Topic>' - '2 <Topic>{garbage}</Topic>'

View file

@ -158,6 +158,20 @@ def test_expand_random_binary():
eq(got, '\xdfj\xf1\xd80>a\xcd\xc4\xbb') eq(got, '\xdfj\xf1\xd80>a\xcd\xc4\xbb')
def test_expand_random_printable_no_whitespace():
prng = random.Random(1)
for _ in xrange(1000):
got = expand({}, '{random 500 printable_no_whitespace}', prng)
assert_true(reduce(lambda x, y: x and y, [x not in string.whitespace and x in string.printable for x in got]))
def test_expand_random_binary():
prng = random.Random(1)
for _ in xrange(1000):
got = expand({}, '{random 500 binary_no_whitespace}', prng)
assert_true(reduce(lambda x, y: x and y, [x not in string.whitespace for x in got]))
def test_expand_random_no_args(): def test_expand_random_no_args():
prng = random.Random(1) prng = random.Random(1)
for _ in xrange(1000): for _ in xrange(1000):

View file

@ -136,6 +136,7 @@ def expand(decision, value, prng):
class RepeatExpandingFormatter(string.Formatter): class RepeatExpandingFormatter(string.Formatter):
charsets = { charsets = {
'printable_no_whitespace': string.printable.translate(None, string.whitespace),
'printable': string.printable, 'printable': string.printable,
'punctuation': string.punctuation, 'punctuation': string.punctuation,
'whitespace': string.whitespace, 'whitespace': string.whitespace,
@ -161,8 +162,7 @@ class RepeatExpandingFormatter(string.Formatter):
if self._recursion > 5: if self._recursion > 5:
raise RecursionError(key) raise RecursionError(key)
fmt = self.__class__(self.prng, _recursion=self._recursion+1) fmt = self.__class__(self.prng, _recursion=self._recursion+1)
# must use vformat not **kwargs so our SpecialVariables is not
# downgraded to just a dict
n = fmt.vformat(val, args, kwargs) n = fmt.vformat(val, args, kwargs)
return n return n
@ -185,10 +185,12 @@ class RepeatExpandingFormatter(string.Formatter):
except IndexError: except IndexError:
charset_arg = 'printable' charset_arg = 'printable'
if charset_arg == 'binary': if charset_arg == 'binary' or charset_arg == 'binary_no_whitespace':
num_bytes = length + 8 num_bytes = length + 8
tmplist = [self.prng.getrandbits(64) for _ in xrange(num_bytes / 8)] tmplist = [self.prng.getrandbits(64) for _ in xrange(num_bytes / 8)]
tmpstring = struct.pack((num_bytes / 8) * 'Q', *tmplist) tmpstring = struct.pack((num_bytes / 8) * 'Q', *tmplist)
if charset_arg == 'binary_no_whitespace':
tmpstring = ''.join(c for c in tmpstring if c not in string.whitespace)
return tmpstring[0:length] return tmpstring[0:length]
else: else:
charset = self.charsets[charset_arg] charset = self.charsets[charset_arg]
@ -198,14 +200,16 @@ class RepeatExpandingFormatter(string.Formatter):
def parse_options(): def parse_options():
parser = OptionParser() parser = OptionParser()
parser.add_option('-O', '--outfile', help='write output to FILE. Defaults to STDOUT', metavar='FILE') parser.add_option('-O', '--outfile', help='write output to FILE. Defaults to STDOUT', metavar='FILE')
parser.add_option('--seed', dest='seed', type='int', help='initial seed for the random number generator', metavar='SEED') parser.add_option('--seed', dest='seed', type='int', help='initial seed for the random number generator')
parser.add_option('--seed-file', dest='seedfile', help='read seeds for specific requests from FILE', metavar='FILE') parser.add_option('--seed-file', dest='seedfile', help='read seeds for specific requests from FILE', metavar='FILE')
parser.add_option('-n', dest='num_requests', type='int', help='issue NUM requests before stopping', metavar='NUM') parser.add_option('-n', dest='num_requests', type='int', help='issue NUM requests before stopping', metavar='NUM')
parser.add_option('-v', '--verbose', dest='verbose', action="store_true", help='turn on verbose output') parser.add_option('-v', '--verbose', dest='verbose', action="store_true", help='turn on verbose output')
parser.add_option('-d', '--debug', dest='debug', action="store_true", help='turn on debugging (very verbose) output') parser.add_option('-d', '--debug', dest='debug', action="store_true", help='turn on debugging (very verbose) output')
parser.add_option('--decision-graph', dest='graph_filename', help='file in which to find the request decision graph', metavar='NUM') parser.add_option('--decision-graph', dest='graph_filename', help='file in which to find the request decision graph')
parser.add_option('--no-cleanup', dest='cleanup', action="store_false", help='turn off teardown so you can peruse the state of buckets after testing')
parser.set_defaults(num_requests=5) parser.set_defaults(num_requests=5)
parser.set_defaults(cleanup=True)
parser.set_defaults(graph_filename='request_decision_graph.yml') parser.set_defaults(graph_filename='request_decision_graph.yml')
return parser.parse_args() return parser.parse_args()
@ -317,10 +321,10 @@ def _main():
except KeyError: except KeyError:
headers = {} headers = {}
print>>VERBOSE, "%s %s" %(method[:100], path[:100]) print>>VERBOSE, "%r %r" %(method[:100], path[:100])
for h, v in headers.iteritems(): for h, v in headers.iteritems():
print>>VERBOSE, "%s: %s" %(h[:50], v[:50]) print>>VERBOSE, "%r: %r" %(h[:50], v[:50])
print>>VERBOSE, "%s\n" % body[:100] print>>VERBOSE, "%r\n" % body[:100]
print>>DEBUG, 'FULL REQUEST' print>>DEBUG, 'FULL REQUEST'
print>>DEBUG, 'Method: %r' %method print>>DEBUG, 'Method: %r' %method
@ -333,12 +337,21 @@ def _main():
#response = s3_connection.make_request(method, path, data=body, headers=headers, override_num_retries=0) #response = s3_connection.make_request(method, path, data=body, headers=headers, override_num_retries=0)
response = s3_connection.make_request(method, path, data=body, headers=headers) response = s3_connection.make_request(method, path, data=body, headers=headers)
failed = True if response.status in [500, 503] else False
if failed:
print>>OUT, 'FAILED:'
OLD_VERBOSE = VERBOSE
OLD_DEBUG = DEBUG
VERBOSE = DEBUG = OUT
print>>VERBOSE, 'Response status code: %d %s' %(response.status, response.reason) print>>VERBOSE, 'Response status code: %d %s' %(response.status, response.reason)
print>>DEBUG, 'Body:\n%s' %response.read() print>>DEBUG, 'Body:\n%s' %response.read()
if response.status == 500 or response.status == 503:
print>>OUT, 'FAILED:\n%s' %request
print>>VERBOSE, '='*80 print>>VERBOSE, '='*80
if failed:
VERBOSE = OLD_VERBOSE
DEBUG = OLD_DEBUG
print>>OUT, '...done fuzzing' print>>OUT, '...done fuzzing'
if options.cleanup:
common.teardown() common.teardown()