Skip to content
Open
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
38 changes: 25 additions & 13 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,35 @@
import os
import json
import sys

def main():
github_event_name = os.getenv("GITHUB_EVENT_NAME")
github_event_path = os.getenv("GITHUB_EVENT_PATH")
def load_github_event():
event_name = os.getenv("GITHUB_EVENT_NAME", "UNKNOWN_EVENT")
event_path = os.getenv("GITHUB_EVENT_PATH")

print(f"📦 Received GitHub event: {event_name}")

print(f"Received GitHub event: {github_event_name}")
if not event_path:
print("⚠️ Environment variable GITHUB_EVENT_PATH is not set. Cannot read event data.")
sys.exit(1)
Comment on lines +12 to +13

Choose a reason for hiding this comment

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

medium

For better separation of concerns, error messages and diagnostic output should be written to standard error (sys.stderr) instead of standard output (sys.stdout). This allows consumers of your script to redirect normal output and error output independently.

This principle should be applied to all error-related print statements in this function (e.g., on lines 16, 23, and 25).

print("⚠️  Environment variable GITHUB_EVENT_PATH is not set. Cannot read event data.", file=sys.stderr)
sys.exit(1)


if not github_event_path:
print("GITHUB_EVENT_PATH not set, cannot read event data.")
return
if not os.path.isfile(event_path):
print(f"❌ Event file not found at: {event_path}")
sys.exit(1)

try:
with open(github_event_path, "r") as file:
event_data = json.load(file)
print("Event JSON Payload:")
print(json.dumps(event_data, indent=2))
with open(event_path, "r", encoding="utf-8") as f:
return json.load(f)
except json.JSONDecodeError as e:
Comment on lines 18 to +22
Copy link

Choose a reason for hiding this comment

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

Uncaught JSONDecodeError in load_github_event Function

The function catches the JSONDecodeError and prints an error message, but doesn't exit the program or return a value. This will cause the function to implicitly return None, which will then be used in the main function when it tries to access event_data. This will lead to an AttributeError or TypeError when the code attempts to use json.dumps on None, causing the program to crash with an uncaught exception. This violates ISO/IEC 25010 Reliability requirements for proper error handling and fault tolerance.

    try:
        with open(event_path, "r", encoding="utf-8") as f:
            return json.load(f)
    except json.JSONDecodeError as e:
        print(f"❌ Failed to parse event JSON: {e}")
        sys.exit(1)

References

Standard: ISO/IEC 25010 Reliability - Fault Tolerance

print(f"❌ Failed to parse event JSON: {e}")
except Exception as e:
Comment on lines +20 to 24
Copy link

Choose a reason for hiding this comment

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

Resource Cleanup Missing

File resource not properly closed in error path. JSONDecodeError catch doesn't close file handle, potentially leading to resource leaks under error conditions.

Suggested change
with open(event_path, "r", encoding="utf-8") as f:
return json.load(f)
except json.JSONDecodeError as e:
print(f"❌ Failed to parse event JSON: {e}")
except Exception as e:
try:
with open(event_path, "r", encoding="utf-8") as f:
try:
return json.load(f)
except json.JSONDecodeError as e:
print(f"❌ Failed to parse event JSON: {e}")
sys.exit(1)
except Exception as e:
print(f"❌ Unexpected error reading event file: {e}")
sys.exit(1)
Standards
  • ISO-IEC-25010-Reliability-Recoverability
  • ISO-IEC-25010-Functional-Correctness-Appropriateness
  • SRE-Resource-Management

print(f"Error reading event data: {e}")
print(f"❌ Unexpected error reading event file: {e}")

sys.exit(1)

def main():
event_data = load_github_event()
Comment on lines +29 to +30
Copy link

Choose a reason for hiding this comment

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

Potential JSON Injection via Untrusted Event Data

The application loads potentially untrusted JSON data from an external source (GitHub event) and prints it directly to the console without proper validation or sanitization. If the JSON contains malicious content designed to exploit console vulnerabilities (e.g., ANSI escape sequences that can manipulate the terminal), it could lead to command injection or interface manipulation attacks.

    print("✅ Event JSON Payload:")
    # Sanitize and limit output of potentially untrusted data
    safe_output = json.dumps(event_data, indent=2)
    print(f"Length: {len(safe_output)} characters. First 1000 chars: {safe_output[:1000]}")

References

Standard: CWE-94
Standard: OWASP Top 10 2021: A03 - Injection

print("✅ Event JSON Payload:")
print(json.dumps(event_data, indent=2))

if __name__ == "__main__":
main()
main()
121 changes: 121 additions & 0 deletions sorting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
def bubble_sort(arr):
"""
Performs in-place Bubble Sort on a list.
Stops early if no swaps occur in a pass.
"""
n = len(arr)
for end in range(n - 1, 0, -1):
swapped = False
for i in range(end):
if arr[i] > arr[i + 1]:
arr[i], arr[i + 1] = arr[i + 1], arr[i]
swapped = True
if not swapped:
break


def selection_sort(arr):
"""
Performs in-place Selection Sort.
Repeatedly selects the minimum element and puts it in place.
"""
n = len(arr)
for i in range(n):
min_idx = i
for j in range(i + 1, n):
if arr[j] < arr[min_idx]:
min_idx = j
arr[i], arr[min_idx] = arr[min_idx], arr[i]


def insertion_sort(arr):
"""
Performs in-place Insertion Sort.
Builds the sorted array one item at a time.
"""
for i in range(1, len(arr)):
key = arr[i]
j = i - 1
while j >= 0 and arr[j] > key:
arr[j + 1] = arr[j]
j -= 1
arr[j + 1] = key


def merge_sort(arr):
"""
Performs Merge Sort (not in-place).
Recursively divides the list and merges sorted halves.
"""
if len(arr) <= 1:
return arr

mid = len(arr) // 2
left = merge_sort(arr[:mid])
right = merge_sort(arr[mid:])

Comment on lines +48 to +56
Copy link

Choose a reason for hiding this comment

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

Inefficient Merge Sort Implementation with Excessive Memory Usage

The current merge_sort implementation creates new arrays on each recursive call through list slicing (arr[:mid] and arr[mid:]). This leads to O(n log n) extra memory allocation beyond the O(n) that merge sort typically requires. For large arrays, this can cause excessive memory consumption and potentially stack overflow errors due to deep recursion. This violates ISO/IEC 25010 Reliability requirements for resource utilization efficiency and could lead to application crashes when sorting large datasets.

def merge_sort(arr):
    """
    Performs Merge Sort with reduced memory overhead.
    Uses an auxiliary array but avoids creating new arrays on each recursive call.
    """
    # Create a single auxiliary array once
    aux = arr.copy()
    # Call the recursive helper function
    _merge_sort_helper(arr, aux, 0, len(arr) - 1)
    return arr


def _merge_sort_helper(arr, aux, low, high):
    """Helper function that performs the recursive merge sort"""
    if low >= high:
        return
        
    mid = (low + high) // 2
    # Recursively sort both halves
    _merge_sort_helper(aux, arr, low, mid)
    _merge_sort_helper(aux, arr, mid + 1, high)
    # Merge the sorted halves
    _merge_in_place(arr, aux, low, mid, high)

References

Standard: ISO/IEC 25010 Reliability - Resource Utilization

return merge(left, right)
Comment on lines +45 to +57
Copy link

Choose a reason for hiding this comment

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

Inefficient Merge Sort

The merge sort implementation creates unnecessary copies of arrays with slice operations (arr[:mid] and arr[mid:]) on each recursive call, leading to O(n log n) extra space complexity. This reduces performance for large arrays due to excessive memory allocation.

Suggested change
def merge_sort(arr):
"""
Performs Merge Sort (not in-place).
Recursively divides the list and merges sorted halves.
"""
if len(arr) <= 1:
return arr
mid = len(arr) // 2
left = merge_sort(arr[:mid])
right = merge_sort(arr[mid:])
return merge(left, right)
def merge_sort(arr):
"""
Performs Merge Sort (not in-place).
Recursively divides the list and merges sorted halves.
"""
# Create a copy to avoid modifying the original
result = arr.copy()
temp = [0] * len(arr) # Auxiliary array
_merge_sort_helper(result, 0, len(result) - 1, temp)
return result
def _merge_sort_helper(arr, left, right, temp):
"""Helper function that performs the recursive merge sort"""
if left < right:
mid = left + (right - left) // 2
_merge_sort_helper(arr, left, mid, temp)
_merge_sort_helper(arr, mid + 1, right, temp)
_merge(arr, left, mid, right, temp)
def _merge(arr, left, mid, right, temp):
"""Merges two sorted subarrays using indices instead of slices"""
# Copy data to temp arrays
for i in range(left, right + 1):
temp[i] = arr[i]
i = left # Initial index of first subarray
j = mid + 1 # Initial index of second subarray
k = left # Initial index of merged subarray
# Merge temp arrays back into arr[left..right]
while i <= mid and j <= right:
if temp[i] <= temp[j]:
arr[k] = temp[i]
i += 1
else:
arr[k] = temp[j]
j += 1
k += 1
# Copy the remaining elements of left subarray, if any
while i <= mid:
arr[k] = temp[i]
i += 1
k += 1
# No need to copy remaining elements of right subarray
# as they are already in the correct position
Standards
  • Algorithm-Correctness-Space-Complexity
  • Mathematical-Accuracy-Recursive-Algorithms
  • Logic-Verification-Performance-Optimization



def merge(left, right):
"""
Helper function to merge two sorted lists.
"""
result = []
i = j = 0

# Merge the two halves
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1

# Append any remaining elements
result.extend(left[i:])
Comment on lines +59 to +77
Copy link

Choose a reason for hiding this comment

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

Missing Corresponding In-Place Merge Function

The current merge function creates a new result list and appends elements to it, which is inefficient for memory usage. This implementation doesn't match the new in-place merge sort approach from the previous finding. Without a corresponding in-place merge function, the merge sort algorithm will not work correctly after the previous modification, leading to functional correctness issues and potential runtime errors.

def _merge_in_place(arr, aux, low, mid, high):
    """
    Helper function to merge two sorted subarrays in-place.
    Merges aux[low:mid+1] and aux[mid+1:high+1] into arr[low:high+1].
    """
    # Copy elements to merge
    for k in range(low, high + 1):
        arr[k] = aux[k]
        
    # Merge the two halves
    i, j = low, mid + 1
    for k in range(low, high + 1):
        if i > mid:
            arr[k] = aux[j]
            j += 1
        elif j > high:
            arr[k] = aux[i]
            i += 1
        elif aux[i] <= aux[j]:
            arr[k] = aux[i]
            i += 1
        else:
            arr[k] = aux[j]
            j += 1

References

Standard: ISO/IEC 25010 Functional Suitability - Functional Correctness

result.extend(right[j:])
return result
Comment on lines +60 to +79
Copy link

Choose a reason for hiding this comment

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

Inefficient Merge Implementation

Multiple append operations in the merge function cause O(n) individual memory allocations. This creates unnecessary GC pressure and reduces performance, especially for large arrays.

Suggested change
def merge(left, right):
"""
Helper function to merge two sorted lists.
"""
result = []
i = j = 0
# Merge the two halves
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
# Append any remaining elements
result.extend(left[i:])
result.extend(right[j:])
return result
def merge(left, right):
"""
Helper function to merge two sorted lists.
"""
result = [None] * (len(left) + len(right))
i = j = k = 0
# Merge the two halves
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result[k] = left[i]
i += 1
else:
result[k] = right[j]
j += 1
k += 1
# Copy remaining elements
while i < len(left):
result[k] = left[i]
i += 1
k += 1
while j < len(right):
result[k] = right[j]
j += 1
k += 1
return result
Standards
  • ISO-IEC-25010-Performance-Resource-Utilization
  • Algorithm-Opt-Memory-Allocation
  • Google-Performance-Best-Practices



# Sample input
original = [6, 6, 2, 3, 2]
Copy link

Choose a reason for hiding this comment

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

Command Injection Risk

Sample data is hardcoded in source. In production, if this code accepts user input for sorting, it could enable command injection. Attackers could inject malicious data leading to code execution.

Suggested change
original = [6, 6, 2, 3, 2]
def validate_input(arr):
"""Validate that input is a list of numbers only"""
if not isinstance(arr, list):
raise TypeError("Input must be a list")
for item in arr:
if not isinstance(item, (int, float)):
raise ValueError("List must contain only numbers")
return arr
# Replace hardcoded sample with validated input
original = [6, 6, 2, 3, 2] # For demo purposes
try:
original = validate_input(original)
except (TypeError, ValueError) as e:
print(f"Input validation error: {e}")
exit(1)
Standards
  • CWE-78
  • OWASP-A03


print("Original list:")
print(original)
Comment on lines +83 to +86
Copy link

Choose a reason for hiding this comment

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

Exception Handling Missing

Sample code lacks exception handling for sorting operations. Errors during sorting would cause uncaught exceptions, crashing the program without graceful recovery.

Suggested change
original = [6, 6, 2, 3, 2]
print("Original list:")
print(original)
# Sample input
original = [6, 6, 2, 3, 2]
print("Original list:")
print(original)
try:
# Bubble Sort
arr1 = original.copy()
bubble_sort(arr1)
print("\nBubble Sort result:")
print(arr1)
# Selection Sort
arr2 = original.copy()
selection_sort(arr2)
print("\nSelection Sort result:")
print(arr2)
# Insertion Sort
arr3 = original.copy()
insertion_sort(arr3)
print("\nInsertion Sort result:")
print(arr3)
# Merge Sort (not in-place)
arr4 = original.copy()
arr4_sorted = merge_sort(arr4)
print("\nMerge Sort result:")
print(arr4_sorted)
# Python built-in sort (in-place)
arr5 = original.copy()
arr5.sort()
print("\nPython .sort() result:")
print(arr5)
# Python built-in sorted() (returns new list)
arr6 = sorted(original)
print("\nPython sorted() result (non-in-place):")
print(arr6)
except Exception as e:
print(f"Error during sorting operation: {e}")
Standards
  • ISO-IEC-25010-Reliability-Fault-Tolerance
  • ISO-IEC-25010-Functional-Correctness-Appropriateness
  • SRE-Error-Handling


# Bubble Sort
arr1 = original.copy()
bubble_sort(arr1)
print("\nBubble Sort result:")
print(arr1)

Comment on lines +83 to +93
Copy link

Choose a reason for hiding this comment

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

Hardcoded Test Data

Sorting algorithm testing code is mixed with implementation, violating separation of concerns. This makes the module less reusable as a library and complicates maintenance by coupling implementation with testing.

Suggested change
original = [6, 6, 2, 3, 2]
print("Original list:")
print(original)
# Bubble Sort
arr1 = original.copy()
bubble_sort(arr1)
print("\nBubble Sort result:")
print(arr1)
def bubble_sort(arr):
"""
Performs in-place Bubble Sort on a list.
Stops early if no swaps occur in a pass.
"""
n = len(arr)
for end in range(n - 1, 0, -1):
swapped = False
for i in range(end):
if arr[i] > arr[i + 1]:
arr[i], arr[i + 1] = arr[i + 1], arr[i]
swapped = True
if not swapped:
break
def selection_sort(arr):
"""
Performs in-place Selection Sort.
Repeatedly selects the minimum element and puts it in place.
"""
n = len(arr)
for i in range(n):
min_idx = i
for j in range(i + 1, n):
if arr[j] < arr[min_idx]:
min_idx = j
arr[i], arr[min_idx] = arr[min_idx], arr[i]
def insertion_sort(arr):
"""
Performs in-place Insertion Sort.
Builds the sorted array one item at a time.
"""
for i in range(1, len(arr)):
key = arr[i]
j = i - 1
while j >= 0 and arr[j] > key:
arr[j + 1] = arr[j]
j -= 1
arr[j + 1] = key
def merge_sort(arr):
"""
Performs Merge Sort (not in-place).
Recursively divides the list and merges sorted halves.
"""
if len(arr) <= 1:
return arr
mid = len(arr) // 2
left = merge_sort(arr[:mid])
right = merge_sort(arr[mid:])
return merge(left, right)
def merge(left, right):
"""
Helper function to merge two sorted lists.
"""
result = []
i = j = 0
# Merge the two halves
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
# Append any remaining elements
result.extend(left[i:])
result.extend(right[j:])
return result
if __name__ == "__main__":
# Sample input
original = [6, 6, 2, 3, 2]
print("Original list:")
print(original)
# Bubble Sort
arr1 = original.copy()
bubble_sort(arr1)
print("\nBubble Sort result:")
print(arr1)
# Selection Sort
arr2 = original.copy()
selection_sort(arr2)
print("\nSelection Sort result:")
print(arr2)
# Insertion Sort
arr3 = original.copy()
insertion_sort(arr3)
print("\nInsertion Sort result:")
print(arr3)
# Merge Sort (not in-place)
arr4 = original.copy()
arr4_sorted = merge_sort(arr4)
print("\nMerge Sort result:")
print(arr4_sorted)
# Python built-in sort (in-place)
arr5 = original.copy()
arr5.sort()
print("\nPython .sort() result:")
print(arr5)
# Python built-in sorted() (returns new list)
arr6 = sorted(original)
print("\nPython sorted() result (non-in-place):")
print(arr6)
Standards
  • Clean-Code-Separation-Of-Concerns
  • Clean-Code-Module-Organization
  • SOLID-SRP

# Selection Sort
arr2 = original.copy()
selection_sort(arr2)
print("\nSelection Sort result:")
print(arr2)

# Insertion Sort
arr3 = original.copy()
insertion_sort(arr3)
print("\nInsertion Sort result:")
print(arr3)

# Merge Sort (not in-place)
arr4 = original.copy()
arr4_sorted = merge_sort(arr4)
print("\nMerge Sort result:")
print(arr4_sorted)

# Python built-in sort (in-place)
arr5 = original.copy()
arr5.sort()
print("\nPython .sort() result:")
print(arr5)

# Python built-in sorted() (returns new list)
arr6 = sorted(original)
print("\nPython sorted() result (non-in-place):")
Comment on lines +83 to +120
Copy link

Choose a reason for hiding this comment

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

Test Code Mixed with Implementation Code

The sorting.py file contains both implementation code (sorting algorithms) and test/demonstration code. This violates the separation of concerns principle and makes the module less maintainable. Production code should be separate from test code to improve modularity, reusability, and to prevent accidental execution of tests in production environments.

# In a new file: test_sorting.py
from sorting import bubble_sort, selection_sort, insertion_sort, merge_sort

def test_sorting_algorithms():
    # Sample input
    original = [6, 6, 2, 3, 2]
    expected = [2, 2, 3, 6, 6]

    # Test Bubble Sort
    arr1 = original.copy()
    bubble_sort(arr1)
    assert arr1 == expected

    # Test Selection Sort
    arr2 = original.copy()
    selection_sort(arr2)
    assert arr2 == expected

    # Test Insertion Sort
    arr3 = original.copy()
    insertion_sort(arr3)
    assert arr3 == expected

    # Test Merge Sort
    arr4 = original.copy()
    arr4_sorted = merge_sort(arr4)
    assert arr4_sorted == expected

if __name__ == "__main__":
    test_sorting_algorithms()
    print("All tests passed!")

References

Standard: Single Responsibility Principle (SRP), Python Testing Best Practices

print(arr6)
Comment on lines +82 to +121

Choose a reason for hiding this comment

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

high

The sample code that demonstrates the sorting functions is currently at the module's top level. This means it will execute every time this file is imported by another module. To make these sorting functions reusable, it's a best practice to place the demonstration code inside an if __name__ == "__main__": block. This ensures the code only runs when the script is executed directly.

if __name__ == "__main__":
    # Sample input
    original = [6, 6, 2, 3, 2]

    print("Original list:")
    print(original)

    # Bubble Sort
    arr1 = original.copy()
    bubble_sort(arr1)
    print("\nBubble Sort result:")
    print(arr1)

    # Selection Sort
    arr2 = original.copy()
    selection_sort(arr2)
    print("\nSelection Sort result:")
    print(arr2)

    # Insertion Sort
    arr3 = original.copy()
    insertion_sort(arr3)
    print("\nInsertion Sort result:")
    print(arr3)

    # Merge Sort (not in-place)
    arr4 = original.copy()
    arr4_sorted = merge_sort(arr4)
    print("\nMerge Sort result:")
    print(arr4_sorted)

    # Python built-in sort (in-place)
    arr5 = original.copy()
    arr5.sort()
    print("\nPython .sort() result:")
    print(arr5)

    # Python built-in sorted() (returns new list)
    arr6 = sorted(original)
    print("\nPython sorted() result (non-in-place):")
    print(arr6)