From 73d74c66f3ddd62e9cdd32d9dd439d95c2c3268d Mon Sep 17 00:00:00 2001 From: kgchinthana Date: Wed, 19 Mar 2025 23:57:08 +0530 Subject: [PATCH 1/3] SCG:1.0.1: #4 #4 fixed a floating-point underflow vulnerability --- src/__init__.py | 0 src/main.py | 34 +++++++++++++++++++++++----------- tests/__init__.py | 0 3 files changed, 23 insertions(+), 11 deletions(-) delete mode 100644 src/__init__.py delete mode 100644 tests/__init__.py diff --git a/src/__init__.py b/src/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/main.py b/src/main.py index 1df8c46..3b876cb 100644 --- a/src/main.py +++ b/src/main.py @@ -1,21 +1,33 @@ from collections import namedtuple +from decimal import Decimal -Order = namedtuple("Order", "id, items") -Item = namedtuple("Item", "type, description, amount, quantity") +Order = namedtuple('Order', 'id, items') +Item = namedtuple('Item', 'type, description, amount, quantity') +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: Order): - net = 0 +def validorder(order): + payments = Decimal('0') + expenses = Decimal('0') for item in order.items: - if item.type == "payment": - net += item.amount - elif item.type == "product": - net -= item.amount * item.quantity + if item.type == 'payment': + # Sets a reasonable min & max value for the invoice amounts + 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 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 payable for an order exceeded" - if net != 0: - return "Order ID: %s - Payment imbalance: $%0.2f" % (order.id, net) + if payments != expenses: + return "Order ID: %s - Payment imbalance: $%0.2f" % (order.id, payments - expenses) else: - return "Order ID: %s - Full payment received!" % order.id + return "Order ID: %s - Full payment received!" % order.id \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29..0000000 From 8a8727c24c6f71509b688cf477d5cb4589df738c Mon Sep 17 00:00:00 2001 From: kgchinthana Date: Thu, 20 Mar 2025 15:57:26 +0530 Subject: [PATCH 2/3] SCG: 1.0.0 : NA #4 fixed issue --- .env.example | 0 docker-compose.yml | 0 requirements.txt | 0 src/main.py | 4 ++++ 4 files changed, 4 insertions(+) delete mode 100644 .env.example delete mode 100644 docker-compose.yml delete mode 100644 requirements.txt diff --git a/.env.example b/.env.example deleted file mode 100644 index e69de29..0000000 diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index e69de29..0000000 diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index e69de29..0000000 diff --git a/src/main.py b/src/main.py index 3b876cb..c87caa0 100644 --- a/src/main.py +++ b/src/main.py @@ -1,18 +1,22 @@ from collections import namedtuple from decimal import Decimal + Order = namedtuple('Order', 'id, items') Item = namedtuple('Item', 'type, description, amount, quantity') + 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): payments = Decimal('0') expenses = Decimal('0') + for item in order.items: if item.type == 'payment': # Sets a reasonable min & max value for the invoice amounts From 6a68ccca0b196dc453c87b7187ed2c38271ac191 Mon Sep 17 00:00:00 2001 From: dakshina99 Date: Thu, 27 Mar 2025 15:20:49 +0530 Subject: [PATCH 3/3] Modify the codebase --- .github/workflows/cd.yml | 0 .github/workflows/ci.yml | 4 ++-- .gitignore | 2 ++ CHANGELOG.md | 4 ++-- Dockerfile | 18 ++++++++++++++ LICENSE.md | 22 +++++++++++++++++ README.md | 51 +++++++++++++++++++++++++++++++++++++++- src/main.py | 38 +++++++++--------------------- tests/test_main.py | 1 - 9 files changed, 107 insertions(+), 33 deletions(-) delete mode 100644 .github/workflows/cd.yml diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml deleted file mode 100644 index e69de29..0000000 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 61b42cf..9f6d342 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,7 @@ name: Run Tests on: pull_request: # Runs on pull requests branches: - - dev + - main jobs: test: @@ -26,4 +26,4 @@ jobs: run: | python -m unittest discover -s tests -p "test_*.py" -v env: - PYTHONPATH: src # Adds src/ to the module search path \ No newline at end of file + PYTHONPATH: src # Adds src/ to the module search path diff --git a/.gitignore b/.gitignore index e69de29..c784eca 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +*.DS_Store \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ead95c..e801870 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. ## [Unreleased] - Upcoming features and bug fixes. -## [1.0.0] - YYYY-MM-DD +## [1.0.0] - 2025-03-19 ### Added - Initial project structure with source code. - Basic functionality in `main.py`. @@ -16,6 +16,6 @@ All notable changes to this project will be documented in this file. - Issue and Pull Request templates. - Open-source license (`LICENSE`). -## [0.1.0] - YYYY-MM-DD +## [1.0.1] - 2025-03-20 ### Added - Project initialization with Git and GitHub setup. diff --git a/Dockerfile b/Dockerfile index e69de29..db4c161 100644 --- a/Dockerfile +++ b/Dockerfile @@ -0,0 +1,18 @@ +# Use official Python image +FROM python:3.11 + +# Set the working directory inside the container +WORKDIR /app + +# Copy dependencies file and install dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy the rest of the application files +COPY . . + +# Run tests before starting the application +RUN pytest tests/ + +# Command to run the main application after successful tests +CMD ["python", "src/main.py"] diff --git a/LICENSE.md b/LICENSE.md index e69de29..cc947e0 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -0,0 +1,22 @@ +# MIT License + +Copyright (c) 2025 CodeX + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/README.md b/README.md index 94a6cea..cd629bb 100644 --- a/README.md +++ b/README.md @@ -1 +1,50 @@ -"# secure-code-game" +# Secure Code Game +## Welcome to Secure Code Game - CodeX! 👋 +To get started, please follow the 🛠️ [set up guide](#setup-guide) (if you haven't already). +Refer to the [Hints](#hints) for helpful information and [Tasks](#time-to-start) to head to the challenge directly. + +## Overview +This program validates transactions in systems where orders consist of multiple items and payments. It ensures that the financial records are consistent and highlights any discrepancies. + +## 📝 Storyline +In another part of the world, a quaint local bookstore was buzzing with excitement as the annual Book Fair approached. Eager to expand their reach, they hastily launched an online platform, hoping to attract book lovers far and wide. However, in their rush, they overlooked critical security measures, leaving their website vulnerable. Now, with hackers lurking in the shadows, the fate of their online store hangs in the balance. Can you uncover and fix the vulnerabilities before it's too late? + +## ⌨️ What's in the repo? +For each level, you will find the same file structure: +- `src/` includes the vulnerable code to be reviewed. +- `tests/` contains the unit tests that should still pass after you have implemented your fix. + +## 🚦 Time to start! +- [ ] Observe the **Github Workflow** logs, and identify the failing test cases. +- [ ] Open a new **Github Issue** to address the problem and your observations. +- [ ] Create a new **Git Branch** and work on the fix. + 1. Review the code in `src/main.py`. Can you spot the bug(s)? + 2. Try to fix the bug(s). Ensure that unit tests are still passing 🟢. +- [ ] Make a new **Pull Request** with a description of what you fixed. +- [ ] Observe the GitHub Workflow logs, and ensure that the code is error free. +- [ ] Once all workflows pass, **merge** the pull request. + +> [!NOTE] +> You successfully completed the level when the Github Workflow passes 🟢. + +## 💡 Hints +The program currently has vulnerabilities related to floating-point arithmetic. Pay close attention to how decimal values are handled during transaction validation. + + + +# Setup Guide +## Local Installation +```bash +git clone https://github.com/kgchinthana/secure-code-game.git +cd secure-code-game +``` + +## Usage +```bash +python src/main.py [arguments] +``` + +## Running Tests +```bash +python -m pytest +``` diff --git a/src/main.py b/src/main.py index c87caa0..1df8c46 100644 --- a/src/main.py +++ b/src/main.py @@ -1,37 +1,21 @@ from collections import namedtuple -from decimal import Decimal +Order = namedtuple("Order", "id, items") +Item = namedtuple("Item", "type, description, amount, quantity") -Order = namedtuple('Order', 'id, items') -Item = namedtuple('Item', 'type, description, amount, quantity') - - -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): - payments = Decimal('0') - expenses = Decimal('0') +def validorder(order: Order): + net = 0 for item in order.items: - if item.type == 'payment': - # Sets a reasonable min & max value for the invoice amounts - 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 MIN_QUANTITY < item.quantity <= MAX_QUANTITY and MIN_QUANTITY < item.amount <= MAX_ITEM_AMOUNT: - expenses += Decimal(str(item.amount)) * item.quantity + if item.type == "payment": + net += item.amount + elif item.type == "product": + net -= item.amount * item.quantity else: return "Invalid item type: %s" % item.type - - if abs(payments) > MAX_TOTAL or expenses > MAX_TOTAL: - return "Total amount payable for an order exceeded" - if payments != expenses: - return "Order ID: %s - Payment imbalance: $%0.2f" % (order.id, payments - expenses) + if net != 0: + return "Order ID: %s - Payment imbalance: $%0.2f" % (order.id, net) else: - return "Order ID: %s - Full payment received!" % order.id \ No newline at end of file + return "Order ID: %s - Full payment received!" % order.id diff --git a/tests/test_main.py b/tests/test_main.py index 9050603..eefb10e 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -51,6 +51,5 @@ def test_5(self): order_1 = c.Order(id="1", items=[service]) self.assertEqual(c.validorder(order_1), "Invalid item type: service") - if __name__ == "__main__": unittest.main()