Skip to content

Commit 95f51dd

Browse files
committed
feat: check at launch of container,
+ other security fixes against xss or some exploits
1 parent f19b975 commit 95f51dd

4 files changed

Lines changed: 79 additions & 30 deletions

File tree

src/compare_json.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,18 @@ def load_notes_json(filepath):
66
if not os.path.exists(filepath):
77
print(f"Le fichier {filepath} n'existe pas. Retourne une liste vide.")
88
return []
9-
with open(filepath, "r", encoding="utf-8") as f:
10-
return json.load(f)
9+
try:
10+
with open(filepath, "r", encoding="utf-8") as f:
11+
data = json.load(f)
12+
if not isinstance(data, list):
13+
raise ValueError("Le fichier JSON doit contenir une liste")
14+
return data
15+
except json.JSONDecodeError as e:
16+
print(f"Erreur lors de la lecture du JSON {filepath}: {e}")
17+
return []
18+
except Exception as e:
19+
print(f"Erreur inattendue lors de la lecture de {filepath}: {e}")
20+
return []
1121

1222
def save_notes_json(data, filepath):
1323
data.replace('�', 'é').replace('é', 'é').replace('è', 'è').replace('�', 'Á')

src/env.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,23 @@ def validate_ntfy_url(url):
2525

2626
#* Ajout de la gestion du timezone
2727
TZ = os.getenv("TZ", "Europe/Paris")
28+
# Validation simple du fuseau horaire
29+
try:
30+
from zoneinfo import ZoneInfo
31+
ZoneInfo(TZ) # Vérifie que le TZ est valide
32+
except Exception:
33+
print(f"Erreur: Fuseau horaire '{TZ}' invalide. Utilisation de Europe/Paris par défaut.")
34+
TZ = "Europe/Paris"
35+
36+
#* Validation de CHECK_INTERVAL
37+
try:
38+
CHECK_INTERVAL = int(os.getenv("CHECK_INTERVAL", "1800"))
39+
if CHECK_INTERVAL < 30 or CHECK_INTERVAL > 86400:
40+
print(f"Avertissement: CHECK_INTERVAL doit être entre 30s et 24h. Utilisation de 1800s.")
41+
CHECK_INTERVAL = 1800
42+
except ValueError:
43+
print("Erreur: CHECK_INTERVAL doit être un nombre. Utilisation de 1800s.")
44+
CHECK_INTERVAL = 1800
2845

2946
#! Chargement des variables d'environnement importantes
3047
#? URL des notes à surveiller

src/main.py

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ def get_notes_content():
3838
'Sec-Fetch-User': '?1',
3939
'Cache-Control': 'max-age=0'
4040
}
41-
response = requests.get(URL, headers=headers, timeout=30)
41+
response = requests.get(URL, headers=headers, timeout=30, verify=True)
4242

4343
if response.status_code != 200:
44-
raise Exception(f"Erreur lors de la récupération des notes: {response.status_code} - {response.text}")
44+
error_text = response.text[:500] # Limiter la longueur du message d'erreur
45+
raise Exception(f"Erreur lors de la récupération des notes: {response.status_code} - {error_text}")
4546
response.raise_for_status()
4647
return response.text
4748

@@ -139,26 +140,9 @@ def send_notification(change):
139140
print()
140141
print("DEBUG : {e}")
141142

142-
def main():
143-
while True:
144-
now = datetime.datetime.now()
145-
# Mode DEBUG : exécution toutes les 30 secondes, sans contrainte d'heure
146-
if LOG_LEVEL == "DEBUG":
147-
interval = 30
148-
else:
149-
# Si on est hors de la plage minuit-7h, on attend jusqu'à minuit
150-
if not (0 <= now.hour < 3):
151-
next_midnight = (now + datetime.timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0)
152-
sleep_seconds = (next_midnight - now).total_seconds()
153-
print(f"Hors plage horaire, dodo jusqu'à minuit ({next_midnight.strftime('%Y-%m-%d %H:%M:%S')})")
154-
time.sleep(sleep_seconds)
155-
continue
156-
interval = CHECK_INTERVAL
157-
# Si on est entre 1h20 et 1h40, on attend 2 minutes
158-
now_tz = get_tz_time()
159-
if now_tz.hour == 1 and 20 <= now_tz.minute < 40:
160-
interval = 120
161-
143+
def check_notes():
144+
"""Fonction pour vérifier et traiter les notes"""
145+
try:
162146
# Récupérer le contenu des notes
163147
content = get_notes_content()
164148
current_time = get_tz_time().strftime("%Y-%m-%d %H:%M:%S")
@@ -171,7 +155,7 @@ def main():
171155
parse.convert_notes_to_json(content, STORAGE_NOTES_JSON_2)
172156
if not os.path.exists(STORAGE_NOTES_JSON_2):
173157
print(f"Erreur : le fichier {STORAGE_NOTES_JSON_2} n'a pas été créé.")
174-
continue
158+
return
175159

176160
new_notes = comparator.load_notes_json(STORAGE_NOTES_JSON_2)
177161

@@ -194,6 +178,35 @@ def main():
194178
if os.path.exists(STORAGE_NOTES_JSON):
195179
os.remove(STORAGE_NOTES_JSON)
196180
shutil.move(STORAGE_NOTES_JSON_2, STORAGE_NOTES_JSON)
181+
except Exception as e:
182+
print(f"Erreur lors de la vérification des notes: {e}")
183+
184+
def main():
185+
# Premier check automatique au lancement
186+
print("🚀 Premier check automatique au lancement du conteneur...")
187+
check_notes()
188+
print()
189+
190+
while True:
191+
now = datetime.datetime.now()
192+
# Mode DEBUG : exécution toutes les 30 secondes, sans contrainte d'heure
193+
if LOG_LEVEL == "DEBUG":
194+
interval = 30
195+
else:
196+
# Si on est hors de la plage minuit-7h, on attend jusqu'à minuit
197+
if not (0 <= now.hour < 3):
198+
next_midnight = (now + datetime.timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0)
199+
sleep_seconds = (next_midnight - now).total_seconds()
200+
print(f"Hors plage horaire, dodo jusqu'à minuit ({next_midnight.strftime('%Y-%m-%d %H:%M:%S')})")
201+
time.sleep(sleep_seconds)
202+
continue
203+
interval = CHECK_INTERVAL
204+
# Si on est entre 1h20 et 1h40, on attend 2 minutes
205+
now_tz = get_tz_time()
206+
if now_tz.hour == 1 and 20 <= now_tz.minute < 40:
207+
interval = 120
208+
209+
check_notes()
197210

198211
next_time = get_tz_time() + datetime.timedelta(seconds=interval)
199212
print("Prochain check à", next_time.strftime("%Y-%m-%d %H:%M:%S"))

src/parse.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,24 +46,33 @@ def convert_notes_to_json(url_response, json_file):
4646
thead = soup.find("thead")
4747
if thead is None:
4848
print("Avertissement : balise <thead> non trouvée dans la réponse, le serveur est probablement en train de se reload, attente 1 minutes avant relance...")
49-
# Sauvegarder en debug uniquement si LOG_LEVEL == DEBUG
5049
import os
5150
if os.getenv("LOG_LEVEL", "INFO").upper() == "DEBUG":
5251
try:
5352
with open("debug_last_notes.html", "w", encoding="utf-8") as f:
54-
f.write(html_content[:10000]) # Limiter à 10KB pour éviter les gros fichiers
55-
os.chmod("debug_last_notes.html", 0o600) # Restreindre les permissions
53+
f.write(html_content[:10000])
54+
os.chmod("debug_last_notes.html", 0o600)
5655
except Exception as e:
5756
print(f"Impossible de sauvegarder le fichier de debug: {e}")
5857
time.sleep(60)
5958
print("Redémarrage du script...")
6059
sys.exit(1)
61-
header_row = thead.find_all("tr")[1]
60+
61+
# Vérifier que la structure est valide
62+
tr_elements = thead.find_all("tr")
63+
if len(tr_elements) < 2:
64+
raise ValueError("Structure HTML invalide: <thead> ne contient pas assez de <tr>")
65+
66+
header_row = tr_elements[1]
6267
headers = [fix_encoding_accents(th.get_text(separator=" ", strip=True).split("\n")[0]) for th in header_row.find_all("th")]
6368

69+
tbody = soup.find("tbody")
70+
if tbody is None:
71+
raise ValueError("Structure HTML invalide: <tbody> introuvable")
72+
6473
rows = [
6574
[fix_encoding_accents(td.get_text(separator=" ", strip=True)) for td in row.find_all("td")]
66-
for row in soup.find("tbody").find_all("tr")
75+
for row in tbody.find_all("tr")
6776
if "master-1" in row.get("class", [])
6877
]
6978

0 commit comments

Comments
 (0)