[FIX] execute: redact the secrets before printing a command
CodeQL flagged five clear-text logging alerts here, and they are real: todo.py and kdbx_manager.py build « --default_password_auth '<KeePass password>' » and db_restore.py « --master_password=… », while this file printed the command before and after every run and logged it on error. The « if "password" in command » guard covered one branch out of six. Redaction sits at the display point, not at construction: six outputs here against commands built all over the repository. Only the value goes, never the option name, and the RETURNED command stays clear — « [1] redo the command » needs it. Verified that PreferredAuthentications=password survives. --- FR --- CodeQL signalait ici cinq journalisations en clair, et elles sont réelles : todo.py et kdbx_manager.py construisent « --default_password_auth '<mot de passe KeePass>' » et db_restore.py « --master_password=… », tandis que ce fichier affichait la commande avant et après chaque exécution et la journalisait en erreur. Le garde « if "password" in command » couvrait une branche sur six. Le caviardage est au point d'affichage, non à la construction : six sorties ici, contre des commandes bâties partout dans le dépôt. Seule la valeur part, jamais le nom de l'option, et la commande RENVOYÉE reste en clair — « [1] refaire la commande » en dépend. Vérifié que PreferredAuthentications=password reste intact. Assisted-by: Claude Opus 5
This commit is contained in:
parent
67e902e416
commit
18a6f064f5
1 changed files with 42 additions and 11 deletions
|
|
@ -5,6 +5,7 @@
|
|||
import datetime
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
|
@ -17,6 +18,41 @@ except ModuleNotFoundError as e:
|
|||
|
||||
VENV_ERPLIBRE = ".venv.erplibre"
|
||||
|
||||
# Une commande construite ailleurs peut porter un secret en clair : todo.py et
|
||||
# kdbx_manager.py y mettent « --default_password_auth '<mot de passe KeePass>' »,
|
||||
# db_restore.py « --master_password=… ». Cette commande est affichée avant et
|
||||
# après l'exécution, et journalisée en erreur : le secret finissait donc dans le
|
||||
# terminal, dans les journaux et dans toute sortie CI qui les capture.
|
||||
#
|
||||
# On caviarde la VALEUR, jamais le nom de l'option : la commande reste lisible et
|
||||
# reproductible, il ne manque que ce qui ne doit pas être lu.
|
||||
_SECRET_OPTION = re.compile(
|
||||
r"(?P<opt>--?[\w-]*"
|
||||
r"(?:password|passwd|pwd|secret|token|api[-_]?key)[\w-]*"
|
||||
r"(?:\s+|=))"
|
||||
r"(?P<val>'[^']*'|\"[^\"]*\"|\S+)",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
_SECRET_ENV = re.compile(
|
||||
r"(?P<var>\b\w*(?:PASSWORD|PASSWD|SECRET|TOKEN)\w*=)"
|
||||
r"(?P<val>'[^']*'|\"[^\"]*\"|\S+)"
|
||||
)
|
||||
|
||||
|
||||
def redact_secrets(text):
|
||||
"""Remplace la valeur des options et variables porteuses de secret.
|
||||
|
||||
Appliqué à CHAQUE affichage d'une commande. Filtrer au point d'affichage
|
||||
plutôt qu'à la construction est ce qui rend la garantie tenable : il n'y a
|
||||
qu'une poignée de sorties ici, alors que les commandes se construisent
|
||||
partout dans le dépôt.
|
||||
"""
|
||||
if not text:
|
||||
return text
|
||||
text = _SECRET_OPTION.sub(lambda m: m.group("opt") + "'***'", text)
|
||||
return _SECRET_ENV.sub(lambda m: m.group("var") + "'***'", text)
|
||||
|
||||
|
||||
new_path = os.path.normpath(
|
||||
os.path.join(os.path.dirname(__file__), "..", "..")
|
||||
)
|
||||
|
|
@ -107,7 +143,8 @@ class Execute:
|
|||
source_odoo = f.read()
|
||||
if not source_odoo:
|
||||
_logger.error(
|
||||
f"You cannot execute Odoo command if no version is installed. Command : {command}"
|
||||
"You cannot execute Odoo command if no version is"
|
||||
f" installed. Command : {redact_secrets(command)}"
|
||||
)
|
||||
return -1
|
||||
command = f"source ./.venv.{source_odoo}/bin/activate && {command}"
|
||||
|
|
@ -116,7 +153,7 @@ class Execute:
|
|||
|
||||
if not quiet:
|
||||
print("🏠 ⬇ Execute command :\n")
|
||||
print(command)
|
||||
print(redact_secrets(command))
|
||||
output_lines = []
|
||||
|
||||
try:
|
||||
|
|
@ -156,16 +193,10 @@ class Execute:
|
|||
|
||||
except FileNotFoundError:
|
||||
if not quiet:
|
||||
if "password" in command:
|
||||
print(
|
||||
f"Error: Command '{command.split(' ')[0]}'[...]"
|
||||
" not found."
|
||||
)
|
||||
else:
|
||||
print(f"Error: Command '{command}' not found.")
|
||||
print(f"Error: Command '{redact_secrets(command)}' not found.")
|
||||
except Exception as e:
|
||||
if not quiet:
|
||||
print(f"An error occurred: {e}")
|
||||
print(f"An error occurred: {redact_secrets(str(e))}")
|
||||
process_end_time = time.time()
|
||||
duration_sec = process_end_time - process_start_time
|
||||
if humanize:
|
||||
|
|
@ -177,7 +208,7 @@ class Execute:
|
|||
if not quiet:
|
||||
print(f"🏠 ⬆ Executed ({duration_sec:.2f} sec.) :\n")
|
||||
if not quiet:
|
||||
print(command)
|
||||
print(redact_secrets(command))
|
||||
print()
|
||||
if return_status_and_output_and_command:
|
||||
return exit_code, command, output_lines
|
||||
|
|
|
|||
Loading…
Reference in a new issue