diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 726fdde..c739260 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -78,3 +78,8 @@ **Vulnerability:** Shell command injection vulnerability identified when passing user-controlled or configured data directly into string-interpolated shell commands (e.g. `f'-u postgres psql -c "CREATE DATABASE {db} OWNER {config.db_user};"'`). Even when nested in double quotes within the python f-string, double-quotes in the substituted parameter break out of the shell quotes. **Learning:** Whenever parameters (such as configuration variables, database names, users) are injected into a string that will be evaluated by a shell (like `sudo -S {cmd}` or `-c "{query}"`), they must be properly escaped to ensure the shell treats them as a single literal argument. **Prevention:** Always use `shlex.quote()` on the full query string before interpolating it into the shell command string (e.g., `query = f"CREATE DATABASE {db} OWNER {config.db_user};"; cmd = f"-u postgres psql -c {shlex.quote(query)}"`) or use argument arrays where supported. + +## 2026-06-03 - [CRITICAL] Prevent Predictable Temporary File Vulnerabilities (CWE-377) +**Vulnerability:** The operational script `automation/ops/reconfigure_ksc_service.py` was writing plaintext configuration answers (including administrative and database passwords) to a hardcoded and predictable temporary file path (`/tmp/reconfig_ans.txt`). +**Learning:** Using predictable file names in shared, world-writable directories like `/tmp` allows a malicious local user to pre-create the file (or a symlink), potentially causing a race condition or local privilege escalation, which can expose the credentials even before the file is written. +**Prevention:** Always append cryptographically secure random strings (like `uuid.uuid4().hex`) to temporary file names in shared directories, ensuring unpredictability and avoiding race conditions or symlink attacks. diff --git a/automation/ops/reconfigure_ksc_service.py b/automation/ops/reconfigure_ksc_service.py index 44c17f0..e79f7e2 100644 --- a/automation/ops/reconfigure_ksc_service.py +++ b/automation/ops/reconfigure_ksc_service.py @@ -4,6 +4,7 @@ """ import logging +import uuid from automation.python.config import KscConfig from automation.python.remote import connect_ksc_host, run_remote_sudo from automation.python.logging_utils import ( @@ -57,15 +58,17 @@ def reconfigure_ksc_service(config: KscConfig, apply: bool = False) -> None: KLSRV_UNATT_KLADMINS_PASSWORD={config.ksc_admin_password} """ + temp_filename = f"/tmp/reconfig_ans_{uuid.uuid4().hex}.txt" + if not apply: run_logger.info( - "[CHECK] Seria gerado um arquivo de respostas KLAUTOANSWERS em '/tmp/reconfig_ans.txt' via SFTP (modo 0600)." + f"[CHECK] Seria gerado um arquivo de respostas KLAUTOANSWERS em '{temp_filename}' via SFTP (modo 0600)." ) run_logger.info( - "[CHECK] Seria executado: KLAUTOANSWERS=/tmp/reconfig_ans.txt /opt/kaspersky/ksc64/lib/bin/setup/postinstall.pl" + f"[CHECK] Seria executado: KLAUTOANSWERS={temp_filename} /opt/kaspersky/ksc64/lib/bin/setup/postinstall.pl" ) run_logger.info( - "[CHECK] O arquivo temporário '/tmp/reconfig_ans.txt' seria removido do servidor remoto." + f"[CHECK] O arquivo temporário '{temp_filename}' seria removido do servidor remoto." ) run_logger.info( "[CHECK] Os serviços (kladminserver_srv.service e ksc-web-console.service) seriam reiniciados." @@ -79,10 +82,10 @@ def reconfigure_ksc_service(config: KscConfig, apply: bool = False) -> None: # Upload do arquivo de respostas via SFTP de forma isolada e segura run_logger.info( - "Gerando arquivo de respostas em /tmp/reconfig_ans.txt via SFTP..." + f"Gerando arquivo de respostas em {temp_filename} via SFTP..." ) sftp = client.open_sftp() - f = sftp.file("/tmp/reconfig_ans.txt", "w") + f = sftp.file(temp_filename, "w") # Força permissão apenas de leitura/escrita pelo owner para evitar vazamento local f.chmod(0o600) f.write(ans_content) @@ -91,14 +94,14 @@ def reconfigure_ksc_service(config: KscConfig, apply: bool = False) -> None: # Executa postinstall.pl postinstall_cmd = ( - "KLAUTOANSWERS=/tmp/reconfig_ans.txt " + f"KLAUTOANSWERS={temp_filename} " "/opt/kaspersky/ksc64/lib/bin/setup/postinstall.pl" ) run_cmd = f"-E bash -c '{postinstall_cmd}'" log_json(run_logger, "run_command_start", cmd="postinstall.pl (silencioso)") out, err, status = run_remote_sudo( - client, f"-E bash -c '{postinstall_cmd}'", config.ksc_pass + client, run_cmd, config.ksc_pass ) if out: for line in out.splitlines(): @@ -106,7 +109,7 @@ def reconfigure_ksc_service(config: KscConfig, apply: bool = False) -> None: log_json(run_logger, "run_command_end", status=status, stderr=err) # Limpeza do arquivo temporário - client.exec_command("rm -f /tmp/reconfig_ans.txt") + client.exec_command(f"rm -f {temp_filename}") if status != 0: raise OpsError(f"Erro na execução do postinstall.pl: {err}")