diff --git a/request_decision_graph.yml b/request_decision_graph.yml
index 06dffd7..fa13eb5 100644
--- a/request_decision_graph.yml
+++ b/request_decision_graph.yml
@@ -3,6 +3,9 @@ start:
garbage:
- '{random 10-3000 printable}'
- '{random 10-1000 binary}'
+ garbage_no_whitespace:
+ - '{random 10-3000 printable_no_whitespace}'
+ - '{random 10-1000 binary_no_whitespace}'
choices:
- bucket
@@ -25,7 +28,7 @@ bucket_garbage_method:
- '{bucket_not_readable}'
- '{bucket_writable}'
- '{bucket_not_writable}'
- - '2 {garbage}'
+ - '2 {garbage_no_whitespace}'
choices:
- bucket_get_simple
- bucket_get_filtered
@@ -40,12 +43,12 @@ bucket_delete:
bucket:
- '{bucket_writable}'
- '{bucket_not_writable}'
- - '2 {garbage}'
+ - '2 {garbage_no_whitespace}'
query:
- null
- policy
- website
- - '2 {garbage}'
+ - '2 {garbage_no_whitespace}'
choices: []
bucket_get:
@@ -54,7 +57,7 @@ bucket_get:
bucket:
- '{bucket_readable}'
- '{bucket_not_readable}'
- - '2 {garbage}'
+ - '2 {garbage_no_whitespace}'
choices:
- 11 bucket_get_simple
- bucket_get_filtered
@@ -72,56 +75,56 @@ bucket_get_simple:
- requestPayment
- versioning
- website
- - '2 {garbage}'
+ - '2 {garbage_no_whitespace}'
choices: []
bucket_get_uploads:
set:
delimiter:
- null
- - '3 delimiter={garbage}'
+ - '3 delimiter={garbage_no_whitespace}'
prefix:
- null
- - '3 prefix={garbage}'
+ - '3 prefix={garbage_no_whitespace}'
key_marker:
- null
- 'key-marker={object_readable}'
- 'key-marker={object_not_readable}'
- 'key-marker={invalid_key}'
- - 'key-marker={random 100-1000 printable}'
+ - 'key-marker={random 100-1000 printable_no_whitespace}'
max_uploads:
- null
- - 'max-uploads={random 1-5 binary}'
+ - 'max-uploads={random 1-5 binary_no_whitespace}'
- 'max-uploads={random 1-1000 digits}'
upload_id_marker:
- null
- - '3 upload-id-marker={random}'
+ - '3 upload-id-marker={random 0-1000 printable_no_whitespace}'
query:
- 'uploads'
- 'uploads&{delimiter}&{prefix}'
- 'uploads&{max_uploads}&{key_marker}&{upload_id_marker}'
- - '2 {garbage}'
+ - '2 {garbage_no_whitespace}'
choices: []
bucket_get_filtered:
set:
delimiter:
- - 'delimiter={garbage}'
+ - 'delimiter={garbage_no_whitespace}'
prefix:
- - 'prefix={garbage}'
+ - 'prefix={garbage_no_whitespace}'
marker:
- 'marker={object_readable}'
- 'marker={object_not_readable}'
- 'marker={invalid_key}'
- - 'marker={random 100-1000 printable}'
+ - 'marker={random 100-1000 printable_no_whitespace}'
max_keys:
- - 'max-keys={random 1-5 binary}'
+ - 'max-keys={random 1-5 binary_no_whitespace}'
- 'max-keys={random 1-1000 digits}'
query:
- null
- '{delimiter}&{prefix}'
- '{max-keys}&{marker}'
- - '2 {garbage}'
+ - '2 {garbage_no_whitespace}'
choices: []
bucket_put:
@@ -129,7 +132,7 @@ bucket_put:
bucket:
- '{bucket_writable}'
- '{bucket_not_writable}'
- - '2 {garbage}'
+ - '2 {garbage_no_whitespace}'
method: PUT
choices:
- bucket_put_simple
@@ -142,9 +145,14 @@ bucket_put_create:
- '2 {garbage}'
- '{random 2-10 binary}'
acl:
- - private
+ - 'private'
+ - 'public-read'
+ - 'public-read-write'
+ - 'authenticated-read'
+ - 'bucket-owner-read'
+ - 'bucket-owner-full-control'
- '{random 3000 letters}'
- - '{random 100-1000 binary}'
+ - '{random 100-1000 binary_no_whitespace}'
headers:
- ['0-1', 'x-amz-acl', '{acl}']
choices: []
@@ -163,7 +171,7 @@ bucket_put_versioning:
- '{random 2-10 binary}'
- '{random 2000-3000 printable}'
mfa_header:
- - '{random 10-1000 printable} {random 10-1000 printable}'
+ - '{random 10-1000 printable_no_whitespace} {random 10-1000 printable_no_whitespace}'
headers:
- ['0-1', 'x-amz-mfa', '{mfa_header}']
choices: []
@@ -225,7 +233,7 @@ bucket_put_simple:
notification_body:
- null
- ''
- - '2 {topic}{event}'
+ - '2 {topic}{event}'
topic:
- null
- '2 {garbage}'
diff --git a/s3tests/functional/test_fuzzer.py b/s3tests/functional/test_fuzzer.py
index eb2ab1f..1039b55 100644
--- a/s3tests/functional/test_fuzzer.py
+++ b/s3tests/functional/test_fuzzer.py
@@ -158,6 +158,20 @@ def test_expand_random_binary():
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():
prng = random.Random(1)
for _ in xrange(1000):
diff --git a/s3tests/fuzz_headers.py b/s3tests/fuzz_headers.py
index 79a8d47..6e581b4 100644
--- a/s3tests/fuzz_headers.py
+++ b/s3tests/fuzz_headers.py
@@ -136,6 +136,7 @@ def expand(decision, value, prng):
class RepeatExpandingFormatter(string.Formatter):
charsets = {
+ 'printable_no_whitespace': string.printable.translate(None, string.whitespace),
'printable': string.printable,
'punctuation': string.punctuation,
'whitespace': string.whitespace,
@@ -161,8 +162,7 @@ class RepeatExpandingFormatter(string.Formatter):
if self._recursion > 5:
raise RecursionError(key)
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)
return n
@@ -185,10 +185,12 @@ class RepeatExpandingFormatter(string.Formatter):
except IndexError:
charset_arg = 'printable'
- if charset_arg == 'binary':
+ if charset_arg == 'binary' or charset_arg == 'binary_no_whitespace':
num_bytes = length + 8
tmplist = [self.prng.getrandbits(64) for _ in xrange(num_bytes / 8)]
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]
else:
charset = self.charsets[charset_arg]
@@ -198,14 +200,16 @@ class RepeatExpandingFormatter(string.Formatter):
def parse_options():
parser = OptionParser()
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('-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('-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(cleanup=True)
parser.set_defaults(graph_filename='request_decision_graph.yml')
return parser.parse_args()
@@ -317,10 +321,10 @@ def _main():
except KeyError:
headers = {}
- print>>VERBOSE, "%s %s" %(method[:100], path[:100])
+ print>>VERBOSE, "%r %r" %(method[:100], path[:100])
for h, v in headers.iteritems():
- print>>VERBOSE, "%s: %s" %(h[:50], v[:50])
- print>>VERBOSE, "%s\n" % body[:100]
+ print>>VERBOSE, "%r: %r" %(h[:50], v[:50])
+ print>>VERBOSE, "%r\n" % body[:100]
print>>DEBUG, 'FULL REQUEST'
print>>DEBUG, 'Method: %r' %method
@@ -333,13 +337,22 @@ 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)
+ 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>>DEBUG, 'Body:\n%s' %response.read()
- if response.status == 500 or response.status == 503:
- print>>OUT, 'FAILED:\n%s' %request
print>>VERBOSE, '='*80
+ if failed:
+ VERBOSE = OLD_VERBOSE
+ DEBUG = OLD_DEBUG
print>>OUT, '...done fuzzing'
- common.teardown()
+
+ if options.cleanup:
+ common.teardown()
def main():