Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 53 additions & 14 deletions odfuzz/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@
from odfuzz.config import Config

START_DATE = datetime.datetime(1970, 1, 1, 0, 0, 0)
END_DATE = datetime.datetime(9999, 12, 31, 23, 59, 59)
END_DATE = datetime.datetime(3000, 12, 31, 23, 59, 59)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

9999 is valid SAP input sadly..

But we can go in a way that the START-END range will be 1970 - 3000 + the timestamp value for 9999-12-31-23-59-59 (= "Infinity" in SAP world, for example validity period end) will be hardcoded String.. it is in the end used in GET/POST string anyway.

Then the generator do not need to tackle real DateTime object with such high value and go OK on LInux and fail on Windows.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented.


'''The END_DATE is reduced to 23:59:59 31st DEC 3000 as that is the highest supported timestamp possible on Windows x64 platforms.
https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/localtime-localtime32-localtime64 '''

DATE_INTERVAL = (END_DATE - START_DATE).total_seconds()


Expand Down Expand Up @@ -72,10 +76,20 @@ def generate(generator_format='uri'):
The format of Edm.DateTime is defined as datetime'yyyy-mm-ddThh:mm[:ss[.fffffff]]'. The attribute Precision,
which is used for declaring a microsecond as a decimal number, is ignored.
"""
body_value = random.randint(0, DATE_INTERVAL)
random_date = START_DATE + datetime.timedelta(seconds=body_value)
uri_value = 'datetime\'{0}\''.format(datetime.datetime.strftime(random_date, '%Y-%m-%dT%I:%M:%S'))
body_value = "/Date({})/".format(body_value)
'''
Once in roughly 10 attempts the generator will return values for 31 DEC 9999, 23:59:59.
This value represents infinity. Windows x64 systems cannot generate this timestamp so its hardcoded.
'''
chance_of_infinity = random.randint(1,10)
if chance_of_infinity == 10:
uri_value = "datetime'9999-12-31T23:59:59'"
body_value = "/Date(253402300799)/"
else:
body_value = random.randint(0, DATE_INTERVAL)
random_date = START_DATE + datetime.timedelta(seconds=body_value)
uri_value = 'datetime\'{0}\''.format(datetime.datetime.strftime(random_date, '%Y-%m-%dT%H:%M:%S'))
body_value = "/Date({})/".format(body_value)

if generator_format == 'uri':
return uri_value
elif generator_format == 'body':
Expand Down Expand Up @@ -258,16 +272,41 @@ class EdmDateTimeOffset:

@staticmethod
def generate(generator_format='uri'):
random_date = START_DATE + datetime.timedelta(seconds=random.randint(0, DATE_INTERVAL))
formatted_datetime = datetime.datetime.strftime(random_date, '%Y-%m-%dT%I:%M:%S')
offset = random.choice(['Z', '']) or ''.join(['-', str(random.randint(0, 24)), ':00'])
value = 'datetimeoffset\'{0}{1}\''.format(formatted_datetime, offset)
if generator_format == 'body' or generator_format == 'uri':
return value
elif generator_format == 'key':
return value, value
'''
Once in roughly 10 attempts the generator will return values for 31 DEC 9999, 23:59:59.
This value represents infinity. Windows x64 systems cannot generate this timestamp so its hardcoded.
'''
chance_of_infinity = random.randint(1,10)
if chance_of_infinity == 10:
generic_value = "datetimeoffset'9999-12-31T23:59:59+00:00'"
sap_value = "/Date(253402300799+0000)/"
else:
raise ValueError
random_date = START_DATE + datetime.timedelta(seconds=random.randint(0, DATE_INTERVAL))
formatted_datetime = datetime.datetime.strftime(random_date, '%Y-%m-%dT%H:%M:%S')
offset = f'{random.randint(0, 1439):04d}'
offset_operator = random.choice('+-')
sap_offset = ''.join([offset_operator,offset])
offset_hrs, offset_mins = divmod(int(offset),60)
generic_offset = ''.join([offset_operator, f'{offset_hrs:02d}',':', f'{offset_mins:02d}'])
generic_value = 'datetimeoffset\'{0}{1}\''.format(formatted_datetime, generic_offset)
sap_value = "/Date({0}{1})/".format(int(random_date.timestamp()),sap_offset)

if Config.fuzzer.sap_vendor_enabled == True:
if generator_format == 'uri':
return generic_value
elif generator_format == 'body':
return sap_value
elif generator_format == 'key':
return generic_value, sap_value
else:
raise ValueError
else:
if generator_format == 'body' or generator_format == 'uri':
return generic_value
elif generator_format == 'key':
return generic_value, generic_value
else:
raise ValueError

class RandomGenerator(EncoderMixin):
@staticmethod
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def test_direct_builder_http_put_url():
queries_list.append(queries.query_string)
queries_list=queries_list
choice = random.choice(queries_list)
assert "Employees(EmployeeID=-1187303908)?sap-client=500" == choice
assert "Order_Details(OrderID=2080823154,ProductID=-477501033)?sap-client=500" == choice

def test_direct_builder_http_post_url():
random.seed(20)
Expand All @@ -120,7 +120,7 @@ def test_direct_builder_http_post_url():
queries_list.append(queries.query_string)
queries_list=queries_list
choice = random.choice(queries_list)
assert "Sales_Totals_by_Amounts?sap-client=500" == choice
assert "Suppliers(SupplierID=1127307038)/Products?sap-client=500" == choice

def test_direct_builder_body_generation():
random.seed(20)
Expand All @@ -133,7 +133,7 @@ def test_direct_builder_body_generation():
q = queryable_factory(queryable, logger, 1)
queries,body = q.generate()
body_list.append(body)
assert random.choice(body_list) == "{\"EmployeeID\": -1187303908, \"LastName\": \"\\u2021\\u0152\\u0153\\u00e3d\\u00a9\\u00dd\\u00a7\\u00ab\\u00d4\\u00f56\", \"FirstName\": \"\\u00e1(P\\u2122\\u00a9\\u00a5d\\u00d3}n\", \"Title\": \"\\u00d7\\u2020\\u00e6 \\u2018\\u00f6V\\u00c1\\u00e5\\u00db\\u2013\\u00cc\\u00cc\", \"TitleOfCourtesy\": \"\\u00ba\\u2026\", \"BirthDate\": \"/Date(251388392975)/\", \"HireDate\": \"/Date(78762567516)/\", \"Address\": \"\\u00eefI\\u00cd\\u009dJ\\u00f2\\u00fa\\u201d\\u00da\\u00dc\\u00b4sn>\\u00da\\u00d0\\u008f\\u00b23\\u00ac]=\\u009d[\\u00abQ}7\\u00a6\\u201ct\\u00acT\\u0090\\u00f3vt\\u00db\\u00fbQE8B|34\\u00c5\\u00c4\\u009d}\\u00b5:\\u00d9\\u00a1nR\", \"City\": \"C\\u00e7\\u00a3\\u00a2\\u00bc\", \"Region\": \"L-\\u00d4E\\u00db\\u00f7)4`Ef\", \"PostalCode\": \"\\u00dem\", \"Country\": \"E\", \"HomePhone\": \"\\u00b2\\u00a6OR\\u00fbLrcI\\u00bc\\u00f6\\u00efy6\\u00ab\", \"Extension\": \"k\\u00d1\", \"Photo\": \"WCdiRic=\", \"Notes\": \"\\u00f6\\u00b5[V\\u20209n\\u00b1\\u00fcf\\u00a3\\u00ec\\u00daf\\u00ce\\u00ec`\\u2026\\u00ae\\u2021\\u00cf9A\\u00b3\\u00f1_\\u00ce\\u008f\\u00db\\u00b6\\u00f1\\u0153\\u00b5e.\\u2030\\u00b9\\u2014K\\u00f7kz-\", \"ReportsTo\": 14526380, \"PhotoPath\": \"c\\u008dY\\u00c2ah\\u00db0\\u00e3.<\\u00fa\\u00da\\u00a6xdM$7\\u00a5\\u2026 GN:r5+u\\u00db\\u00d2!\\u00bc\\u00c8\\u00cf\\u2019\\u00b6{\\u00c3\\u00cf9\\u20192\\u00cc\\u2020O\\u00d4\\u00df\\u00ed\\u00f1\\u00dc\\u00ee.\\u00f8\\u00eb\\u00da\\u00d5\\u00c4f\\u00e8\\u00calR\\u00da\\u00bf\\u00b3\\u00c1\\u00ca*\\u00a8\\u0081\\u00d7t\\u00b7-@\\u00da\\u00c1n\\u00d6EXF\\u2018\\u00dc1\\u00c7\\u00f6\\u00ec\\u00efIp\\u00dd\\u201dq\\u00cb1\\u00c8O\\u00d5\\u00c8\\u2122\\u2022\\u00a5>\\u00a4\\u00c9O\\u00cf@P=4.\\u00ac\\u00dd\\u00f3\\u00a9\\u00cflu\\u00aa\\u00a5:\\u00b7\\u00f6VhE\\u00f0\\u00f6u\\u00a7-\\u2022\\u00aap\\u2019\\u00ea\\u00f1@\\u00c3xvp\\u00c2O0$F\\u00bf\\u00ae\\u00df\\u00d4N\\u0081\\u00feD\\u00cb\\u00f9\\u00d6\\u00d4!\\u00f7v\\u00e9\\u00c3Z\\u00a1\\u00eb\\u00de5--\\u00bd9S|\\u00c7\\u00f4\\u00f3O\\u00a95.\\u00dc\\u00e2\\u00aau.\\u00b1\\u00fb\\u00f8y\\u00f5\\u00da\\u00d3\\u00fa\\u00a6(\\u00c3i\\u00c1=\\u00b0\\u201c\\u00b5r\\u00dd\\u00bcU\\u00fd\\u00eaTAK\\u00ab\\u00d1u\\u00ec\\u00d9\\u00f1\\u00c1\\u00b8@\\u00d7\\u00d2S\\u00a2\\u00ccDg=|\\u00c1\\u201d\\u00dcmXaJuN_\\u00aa\"}"
assert random.choice(body_list) == "{\"OrderID\": 2080823154, \"ProductID\": -477501033, \"UnitPrice\": \"5644108454722995m\", \"Quantity\": 9551, \"Discount\": \"1.6719705272889652e+20f\"}"

def test_direct_builder_http_merge_body():
random.seed(20)
Expand Down