android-foss/ensure_sorted.py
Trần Bách bfb7d0d758
fix(security): potential indexerror when parsing malformed readme (#632)
If a line starting with `*` (app entry) appears before any category header (`### •` or `## –`), `categories[-1]` will raise an `IndexError` because the `categories` list will be empty. A malformed or tampered README.md could cause the CI script to crash with an unhandled exception.

Affected files: ensure_sorted.py

Signed-off-by: Trần Bách <45133811+barttran2k@users.noreply.github.com>
2026-04-07 18:15:24 +00:00

105 lines
3.5 KiB
Python
Executable file
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/python3
import re
class bcolors:
HEADER = '\033[95m'
BLUE = '\033[94m'
CYAN = '\033[96m'
GREEN = '\033[92m'
YELLOW = '\033[93m'
RED = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
class Category:
def __init__(self, name):
self.name = name
# a list of apps
self.apps = []
def add_app(self, app_str: str):
matches = re.findall("(?<=\\[\\*\\*).*(?=\\*\\*\\])", app_str)
if len(matches) != 1:
raise RuntimeError("These should be only one match")
app_name = matches[0]
# make it lower case and append it
self.apps.append(app_name.lower())
def is_sorted(self):
# I know not effiencent
return sorted(self.apps) == self.apps
# Tell exactly where it's unsorted
def where_unsorted(self):
for i in range(1, len(self.apps)):
if self.apps[i] < self.apps[i-1]:
return f'App {bcolors.RED}{self.apps[i-1]}{bcolors.ENDC} is not in the correct order'
def how_to_sort(self):
sorted_apps = sorted(self.apps)
unsorted_apps = self.apps.copy()
for i in range(len(sorted_apps)):
if sorted_apps[i] != unsorted_apps[i]:
# Color it
sorted_apps[i] = f'{sorted_apps[i]}'
unsorted_apps[i] = f'{unsorted_apps[i]}'
return sorted_apps, unsorted_apps
def __str__(self):
return str(self.apps)
def __repr__(self):
return self.__str__()
def main():
# start of the Apps section
APPS_LINE_START = '## Apps \n'
with open('README.md', 'r') as readme_file:
lines = readme_file.readlines()
index: int
try:
index = lines.index(APPS_LINE_START)
except ValueError:
print(f'String "{APPS_LINE_START}" was not found in README.md it\'s needed to determine the start of the app categories')
exit(1)
categories = []
for i in range(index+1, len(lines)):
# This is a category
if lines[i].startswith("### •"):
category = Category(lines[i][6:-1])
categories.append(category)
# This is also a category
elif lines[i].startswith("## "):
category_name = re.findall("(?<=##\\s\\s).*?(?=\\s)", lines[i])[0]
category = Category(category_name)
categories.append(category)
# This is an app
elif lines[i].startswith("*"):
if not categories:
raise RuntimeError("App entry found before any category header")
# The last category in the categories list is the one we're working on
category = categories[-1]
category.add_app(lines[i])
all_sorted = True
for i in categories:
if not i.is_sorted():
print(f'Category {bcolors.BLUE}{i.name}{bcolors.ENDC} is not sorted')
print(' ' + i.where_unsorted())
print(' Should be sorted as follows:')
sorted, unsorted = i.how_to_sort()
longest_str = len(max(unsorted, key=len))
for j in range(len(sorted)):
color = sorted[j] != unsorted[j]
print(f' {bcolors.RED if color else ""}{unsorted[j]}{bcolors.ENDC if color else ""}{((longest_str-len(unsorted[j]))+2) * " "}{bcolors.GREEN if color else ""}{sorted[j]}{bcolors.ENDC if color else ""}')
all_sorted = False
if not all_sorted:
exit(2)
if __name__ == "__main__":
main()