Skip to content

Commit

Permalink
Improves solution and error messages for failing tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jkcso committed Jan 5, 2024
1 parent 3e5e34f commit 9e51dd6
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 12 deletions.
6 changes: 3 additions & 3 deletions Season-1/Level-1/hack.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,19 @@ def test_7(self):
order_5 = c.Order(id='5', items=[small_item, payment_1, payment_2])
self.assertEqual(c.validorder(order_5), 'Order ID: 5 - Full payment received!')

# The total amount of an order must be limited. Order validation shouldn't depend on ordering of items.
# The total amount payable in an order should be limited.
def test_8(self):
num_items = 12
items = [c.Item(type='product', description='tv', amount=99999, quantity=num_items)]
for i in range(num_items):
items.append(c.Item(type='payment', description='invoice_' + str(i), amount=99999, quantity=1))
order_1 = c.Order(id='1', items=items)
self.assertEqual(c.validorder(order_1), 'Total amount exceeded')
self.assertEqual(c.validorder(order_1), 'Total amount payable for an order exceeded')

# Put payments before products
items = items[1:] + [items[0]]
order_2 = c.Order(id='2', items=items)
self.assertEqual(c.validorder(order_2), 'Total amount exceeded')
self.assertEqual(c.validorder(order_2), 'Total amount payable for an order exceeded')

if __name__ == '__main__':
unittest.main()
15 changes: 10 additions & 5 deletions Season-1/Level-1/solution.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

MAX_ITEM_AMOUNT = 100000 # maximum price of item in the shop
MAX_QUANTITY = 100 # maximum quantity of an item in the shop
MIN_QUANTITY = 0 # minimum quantity of an item in the shop
MAX_TOTAL = 1e6 # maximum total amount accepted for an order

def validorder(order):
Expand All @@ -18,13 +19,13 @@ def validorder(order):
if -MAX_ITEM_AMOUNT <= item.amount <= MAX_ITEM_AMOUNT:
payments += Decimal(str(item.amount))
elif item.type == 'product':
if type(item.quantity) is int and 0 < item.quantity <= MAX_QUANTITY and 0 < item.amount <= MAX_ITEM_AMOUNT:
if type(item.quantity) is int and MIN_QUANTITY < item.quantity <= MAX_QUANTITY and MIN_QUANTITY < item.amount <= MAX_ITEM_AMOUNT:
expenses += Decimal(str(item.amount)) * item.quantity
else:
return "Invalid item type: %s" % item.type

if abs(payments) > MAX_TOTAL or expenses > MAX_TOTAL:
return "Total amount exceeded"
return "Total amount payable for an order exceeded"

if payments != expenses:
return "Order ID: %s - Payment imbalance: $%0.2f" % (order.id, payments - expenses)
Expand Down Expand Up @@ -69,9 +70,13 @@ def validorder(order):
# >>> Decimal('0.3')
# Decimal('0.3')

# Input validation should be expanded to also check data types besides testing allowed range of values.
# This specific bug, caused by using a non-integer quantity, is due to insufficient attention to requirements engineering.
# In some systems it is OK to buy half of something, but here it was assumed to be a positive integer.
# Input validation should be expanded to also check data types besides testing allowed range
# of values. This specific bug, caused by using a non-integer quantity, might occur due to
# insufficient attention to requirements engineering. While in certain contexts is acceptable
# to buy a non-integer amount of an item (e.g. buy a fractional share), in the context of our
# online shop we falsely placed trust to users for buying a positive integer of items only,
# without malicious intend.


# Contribute new levels to the game in 3 simple steps!
# Read our Contribution Guideline at github.com/skills/secure-code-game/blob/main/CONTRIBUTING.md
8 changes: 4 additions & 4 deletions Season-1/Level-1/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,18 @@ def test_3(self):
order_3 = c.Order(id='3', items=[payment, tv_item, payback])
self.assertEqual(c.validorder(order_3), 'Order ID: 3 - Payment imbalance: $-1000.00')

# Example 4 - invalid input should not blow up the system
# Example 4 - handles invalid input such as placing an invalid order for 1.5 device
def test_4(self):
tv = c.Item(type='product', description='tv', amount=1000, quantity=1.5)
order_1 = c.Order(id='1', items=[tv])
try:
c.validorder(order_1)
except:
self.fail("Unhandled exception occured in the target system!")
self.fail("Invalid order detected")

# Example 5 - successfully detects an invalid item type
# Example 5 - handles an invalid item type called 'service'
def test_5(self):
service = c.Item(type='service', description='shippment of goods', amount=100, quantity=1)
service = c.Item(type='service', description='order shipment', amount=100, quantity=1)
order_1 = c.Order(id='1', items=[service])
self.assertEqual(c.validorder(order_1), 'Invalid item type: service')

Expand Down

0 comments on commit 9e51dd6

Please sign in to comment.