Skip to content

Commit 3e5e34f

Browse files
authored
Fix issues #54 and #53 (#57)
1 parent 6a89fdc commit 3e5e34f

File tree

3 files changed

+48
-14
lines changed

3 files changed

+48
-14
lines changed

Season-1/Level-1/hack.py

+18-4
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,35 @@
33

44
class TestOnlineStore(unittest.TestCase):
55

6-
# Tricks the system and walks away with 1 television, despite valid payment & reimbursement
7-
def test_4(self):
6+
# Tricks the system and walks away with 1 television, despite valid payment & reimbursement.
7+
def test_6(self):
88
tv_item = c.Item(type='product', description='tv', amount=1000.00, quantity=1)
99
payment = c.Item(type='payment', description='invoice_4', amount=1e19, quantity=1)
1010
payback = c.Item(type='payment', description='payback_4', amount=-1e19, quantity=1)
1111
order_4 = c.Order(id='4', items=[payment, tv_item, payback])
1212
self.assertEqual(c.validorder(order_4), 'Order ID: 4 - Payment imbalance: $-1000.00')
1313

14-
# Valid payments that should add up correctly, but do not
15-
def test_5(self):
14+
# Valid payments that should add up correctly, but do not.
15+
def test_7(self):
1616
small_item = c.Item(type='product', description='accessory', amount=3.3, quantity=1)
1717
payment_1 = c.Item(type='payment', description='invoice_5_1', amount=1.1, quantity=1)
1818
payment_2 = c.Item(type='payment', description='invoice_5_2', amount=2.2, quantity=1)
1919
order_5 = c.Order(id='5', items=[small_item, payment_1, payment_2])
2020
self.assertEqual(c.validorder(order_5), 'Order ID: 5 - Full payment received!')
2121

22+
# The total amount of an order must be limited. Order validation shouldn't depend on ordering of items.
23+
def test_8(self):
24+
num_items = 12
25+
items = [c.Item(type='product', description='tv', amount=99999, quantity=num_items)]
26+
for i in range(num_items):
27+
items.append(c.Item(type='payment', description='invoice_' + str(i), amount=99999, quantity=1))
28+
order_1 = c.Order(id='1', items=items)
29+
self.assertEqual(c.validorder(order_1), 'Total amount exceeded')
30+
31+
# Put payments before products
32+
items = items[1:] + [items[0]]
33+
order_2 = c.Order(id='2', items=items)
34+
self.assertEqual(c.validorder(order_2), 'Total amount exceeded')
35+
2236
if __name__ == '__main__':
2337
unittest.main()

Season-1/Level-1/solution.py

+15-10
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,25 @@
99
MAX_TOTAL = 1e6 # maximum total amount accepted for an order
1010

1111
def validorder(order):
12-
net = Decimal('0')
12+
payments = Decimal('0')
13+
expenses = Decimal('0')
1314

1415
for item in order.items:
1516
if item.type == 'payment':
16-
# sets a reasonable min & max value for the invoice amounts
17-
if item.amount > -1*MAX_ITEM_AMOUNT and item.amount < MAX_ITEM_AMOUNT:
18-
net += Decimal(str(item.amount))
17+
# Sets a reasonable min & max value for the invoice amounts
18+
if -MAX_ITEM_AMOUNT <= item.amount <= MAX_ITEM_AMOUNT:
19+
payments += Decimal(str(item.amount))
1920
elif item.type == 'product':
20-
if item.quantity > 0 and item.quantity <= MAX_QUANTITY and item.amount > 0 and item.amount <= MAX_ITEM_AMOUNT:
21-
net -= Decimal(str(item.amount)) * item.quantity
22-
if net > MAX_TOTAL or net < -1*MAX_TOTAL:
23-
return "Total amount exceeded"
21+
if type(item.quantity) is int and 0 < item.quantity <= MAX_QUANTITY and 0 < item.amount <= MAX_ITEM_AMOUNT:
22+
expenses += Decimal(str(item.amount)) * item.quantity
2423
else:
2524
return "Invalid item type: %s" % item.type
25+
26+
if abs(payments) > MAX_TOTAL or expenses > MAX_TOTAL:
27+
return "Total amount exceeded"
2628

27-
if net != 0:
28-
return "Order ID: %s - Payment imbalance: $%0.2f" % (order.id, net)
29+
if payments != expenses:
30+
return "Order ID: %s - Payment imbalance: $%0.2f" % (order.id, payments - expenses)
2931
else:
3032
return "Order ID: %s - Full payment received!" % order.id
3133

@@ -67,6 +69,9 @@ def validorder(order):
6769
# >>> Decimal('0.3')
6870
# Decimal('0.3')
6971

72+
# Input validation should be expanded to also check data types besides testing allowed range of values.
73+
# This specific bug, caused by using a non-integer quantity, is due to insufficient attention to requirements engineering.
74+
# In some systems it is OK to buy half of something, but here it was assumed to be a positive integer.
7075

7176
# Contribute new levels to the game in 3 simple steps!
7277
# Read our Contribution Guideline at github.com/skills/secure-code-game/blob/main/CONTRIBUTING.md

Season-1/Level-1/tests.py

+15
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,20 @@ def test_3(self):
2424
order_3 = c.Order(id='3', items=[payment, tv_item, payback])
2525
self.assertEqual(c.validorder(order_3), 'Order ID: 3 - Payment imbalance: $-1000.00')
2626

27+
# Example 4 - invalid input should not blow up the system
28+
def test_4(self):
29+
tv = c.Item(type='product', description='tv', amount=1000, quantity=1.5)
30+
order_1 = c.Order(id='1', items=[tv])
31+
try:
32+
c.validorder(order_1)
33+
except:
34+
self.fail("Unhandled exception occured in the target system!")
35+
36+
# Example 5 - successfully detects an invalid item type
37+
def test_5(self):
38+
service = c.Item(type='service', description='shippment of goods', amount=100, quantity=1)
39+
order_1 = c.Order(id='1', items=[service])
40+
self.assertEqual(c.validorder(order_1), 'Invalid item type: service')
41+
2742
if __name__ == '__main__':
2843
unittest.main()

0 commit comments

Comments
 (0)