From 0177a0122a3b63e858c6591f7a715acae960abc8 Mon Sep 17 00:00:00 2001 From: Alix Lourme Date: Fri, 20 Feb 2026 23:36:43 +0100 Subject: [PATCH 1/6] Dot not store creds & improve git_askpass process --- .gitattributes | 1 + git_platforms_synchro.py | 15 +++++++++------ modules/git_askpass.py | 2 +- modules/utils.py | 23 +++++++++++++++++++++++ 4 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..211d886 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +modules/git_askpass.py text eol=lf \ No newline at end of file diff --git a/git_platforms_synchro.py b/git_platforms_synchro.py index f9ec6bb..39944f6 100644 --- a/git_platforms_synchro.py +++ b/git_platforms_synchro.py @@ -4,7 +4,7 @@ import modules.input_parser as input_parser from git import Repo from modules.git_clients import GitClientFactory, GitClient -from modules.utils import TMP_REPO_GIT_DIRECTORY, delete_temporary_repo_git_directory +from modules.utils import TMP_REPO_GIT_DIRECTORY, delete_temporary_repo_git_directory, test_git_ask_pass, get_git_ask_pass GIT_CONFIG_HTTP_PREFIX = 'http' GIT_REMOTE_TO = 'sync-to' @@ -29,13 +29,15 @@ def set_git_credentials(username: str, password: str): os.environ.pop('GIT_PASSWORD', None) return - working_dir = os.path.dirname(os.path.realpath(__file__)) - - os.environ['GIT_ASKPASS'] = os.path.join(working_dir, 'modules', 'git_askpass.py') + os.environ['GIT_ASKPASS'] = get_git_ask_pass() if username: os.environ['GIT_USERNAME'] = username + else: + os.environ.pop('GIT_USERNAME', None) if password: os.environ['GIT_PASSWORD'] = password + else: + os.environ.pop('GIT_PASSWORD', None) def git_clone(url: str, mirror: bool = False, disable_ssl_verify: bool = False, proxy: str = None) -> Repo: @@ -49,7 +51,7 @@ def git_clone(url: str, mirror: bool = False, disable_ssl_verify: bool = False, else: delete_temporary_repo_git_directory() logging.debug('Cloning repo %s', url) - options = [] + options = ['--config credential.helper=""'] if disable_ssl_verify: options += ['--config http.sslVerify=false'] if proxy: @@ -146,9 +148,10 @@ def repo_branches_sync(args, branches_commits_from: dict, branches_commits_to: d def main() -> int: - delete_temporary_repo_git_directory() args = input_parser.parse() + delete_temporary_repo_git_directory() log_init(args.log_level) + test_git_ask_pass() logger.info('Starting Git Platforms Synchronization...') input_parser.print_args(args) diff --git a/modules/git_askpass.py b/modules/git_askpass.py index 50920c4..0bd0614 100755 --- a/modules/git_askpass.py +++ b/modules/git_askpass.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 import sys from sys import argv diff --git a/modules/utils.py b/modules/utils.py index f5dc336..2f76d0a 100644 --- a/modules/utils.py +++ b/modules/utils.py @@ -1,4 +1,5 @@ import os +import stat import shutil import subprocess @@ -18,3 +19,25 @@ def delete_temporary_repo_git_directory(force_if_test_mode: bool = False): subprocess.run(['cmd', '/c', 'rmdir', '/s', '/q', TMP_REPO_GIT_DIRECTORY.rstrip('/')], shell=True) else: raise e + + +def set_file_execution_permission(file: str): + if not os.access(file, os.X_OK): + permissions = os.stat(file).st_mode + os.chmod(file, permissions | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) + + +def get_git_ask_pass() -> str: + working_dir = os.path.dirname(os.path.realpath(__file__)) + git_askpass = os.path.join(working_dir, 'git_askpass.py') + set_file_execution_permission(git_askpass) + return git_askpass + + +def test_git_ask_pass() -> None: + git_askpass = get_git_ask_pass() + custom_env = os.environ.copy() + custom_env['GIT_USERNAME'] = 'test42' + result = subprocess.run([git_askpass, 'Username'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, env=custom_env) + if result.returncode != 0 or not result.stdout.startswith('test42'): + raise ValueError('PROBLEM: The ' + git_askpass + ' cannot be executed, please verify Dos/Unix encoding') From 278997a26b25448856ac2332c2f7feefb6a5b54e Mon Sep 17 00:00:00 2001 From: Alix Lourme Date: Sun, 22 Feb 2026 00:46:08 +0100 Subject: [PATCH 2/6] Fix quality flaws --- modules/utils.py | 2 +- tests/test_utils.py | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/modules/utils.py b/modules/utils.py index 2f76d0a..61b4b21 100644 --- a/modules/utils.py +++ b/modules/utils.py @@ -24,7 +24,7 @@ def delete_temporary_repo_git_directory(force_if_test_mode: bool = False): def set_file_execution_permission(file: str): if not os.access(file, os.X_OK): permissions = os.stat(file).st_mode - os.chmod(file, permissions | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) + os.chmod(file, permissions | stat.S_IXUSR | stat.S_IXGRP) def get_git_ask_pass() -> str: diff --git a/tests/test_utils.py b/tests/test_utils.py index 24fcbc8..f04cf99 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,10 +1,11 @@ import os import json import tarfile +import tempfile import pytest from unittest.mock import patch from git import Repo -from modules.utils import ENV_TEST_MODE, TMP_REPO_GIT_DIRECTORY, delete_temporary_repo_git_directory +from modules.utils import ENV_TEST_MODE, TMP_REPO_GIT_DIRECTORY, delete_temporary_repo_git_directory, set_file_execution_permission from pytest_httpserver import HTTPServer @@ -74,3 +75,10 @@ def test_delete_temporary_repo_git_directory_permission_denied_windows(capsys): with patch('subprocess.run', return_value=mock_result) as mock_run: delete_temporary_repo_git_directory(True) mock_run.assert_called_once_with(['cmd', '/c', 'rmdir', '/s', '/q', 'tmp-git-repo'], shell=True) + + +def test_set_file_execution_permission(): + with tempfile.NamedTemporaryFile(mode='w', delete=True) as temp_file: + assert not os.access(temp_file.name, os.X_OK) + set_file_execution_permission(temp_file.name) + assert os.access(temp_file.name, os.X_OK) From 242873c6d474406ab5bf22f6258957ae456bb40f Mon Sep 17 00:00:00 2001 From: Alix Lourme Date: Sun, 22 Feb 2026 01:00:11 +0100 Subject: [PATCH 3/6] Windows OS --- modules/utils.py | 6 ++++++ tests/test_utils.py | 1 + 2 files changed, 7 insertions(+) diff --git a/modules/utils.py b/modules/utils.py index 61b4b21..35746a9 100644 --- a/modules/utils.py +++ b/modules/utils.py @@ -1,12 +1,15 @@ import os import stat import shutil +import logging import subprocess TMP_REPO_GIT_DIRECTORY = 'tmp-git-repo/' ENV_TEST_MODE = 'TEST_MODE' +logger = logging.getLogger(__name__) + def delete_temporary_repo_git_directory(force_if_test_mode: bool = False): if os.environ.get(ENV_TEST_MODE) != 'true' or force_if_test_mode: @@ -35,6 +38,9 @@ def get_git_ask_pass() -> str: def test_git_ask_pass() -> None: + if os.name != 'nt': + logger.warning('Windows is not supported for "git-askpass" process ; authentication will not work if not already in git credentials storage.') + return git_askpass = get_git_ask_pass() custom_env = os.environ.copy() custom_env['GIT_USERNAME'] = 'test42' diff --git a/tests/test_utils.py b/tests/test_utils.py index f04cf99..e8fe076 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -77,6 +77,7 @@ def test_delete_temporary_repo_git_directory_permission_denied_windows(capsys): mock_run.assert_called_once_with(['cmd', '/c', 'rmdir', '/s', '/q', 'tmp-git-repo'], shell=True) +@pytest.mark.skipif(os.name == 'nt', reason='Specific Linux test') def test_set_file_execution_permission(): with tempfile.NamedTemporaryFile(mode='w', delete=True) as temp_file: assert not os.access(temp_file.name, os.X_OK) From e90f2094a9cbdccd41bbc6a11bafdd035902ed06 Mon Sep 17 00:00:00 2001 From: Alix Lourme Date: Sun, 22 Feb 2026 01:02:35 +0100 Subject: [PATCH 4/6] Windows OS --- modules/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/utils.py b/modules/utils.py index 35746a9..4a641ca 100644 --- a/modules/utils.py +++ b/modules/utils.py @@ -38,7 +38,7 @@ def get_git_ask_pass() -> str: def test_git_ask_pass() -> None: - if os.name != 'nt': + if os.name == 'nt': logger.warning('Windows is not supported for "git-askpass" process ; authentication will not work if not already in git credentials storage.') return git_askpass = get_git_ask_pass() From 3932994cfbb127ffe999957927ee922dc2a6a59f Mon Sep 17 00:00:00 2001 From: Alix Lourme Date: Sun, 22 Feb 2026 18:59:06 +0100 Subject: [PATCH 5/6] git_askpass for Windows --- modules/git_askpass.bat | 29 +++++++++++++++++++++++++++++ modules/utils.py | 17 +++++++++-------- tests/test_utils.py | 6 +++++- 3 files changed, 43 insertions(+), 9 deletions(-) create mode 100644 modules/git_askpass.bat diff --git a/modules/git_askpass.bat b/modules/git_askpass.bat new file mode 100644 index 0000000..ed274fc --- /dev/null +++ b/modules/git_askpass.bat @@ -0,0 +1,29 @@ +@echo off +:: Script to read GIT_USERNAME or GIT_PASSWORD from environment variables + +:: Check if an argument is provided +if "%~1"=="" ( + echo "Usage: %~nx0 [Username|Password]" + exit /b 1 +) + +:: Convert the argument to lowercase for comparison +set "param=%~1" +set "param_lower=%param:~0,1%%param:~1%" + +:: Check if the argument contains "username" +echo %param_lower% | findstr /i "username" >nul +if %errorlevel% equ 0 ( + echo %GIT_USERNAME% + exit /b 0 +) + +:: Check if the argument contains "password" +echo %param_lower% | findstr /i "password" >nul +if %errorlevel% equ 0 ( + echo %GIT_PASSWORD% + exit /b 0 +) + +:: If none of the cases are found +exit /b 1 diff --git a/modules/utils.py b/modules/utils.py index 4a641ca..32f59ed 100644 --- a/modules/utils.py +++ b/modules/utils.py @@ -31,19 +31,20 @@ def set_file_execution_permission(file: str): def get_git_ask_pass() -> str: + os_windows = os.name == 'nt' working_dir = os.path.dirname(os.path.realpath(__file__)) - git_askpass = os.path.join(working_dir, 'git_askpass.py') - set_file_execution_permission(git_askpass) + git_askpass = os.path.join(working_dir, 'git_askpass.' + ('bat' if os_windows else 'py')) + if not os_windows: + set_file_execution_permission(git_askpass) return git_askpass def test_git_ask_pass() -> None: - if os.name == 'nt': - logger.warning('Windows is not supported for "git-askpass" process ; authentication will not work if not already in git credentials storage.') - return git_askpass = get_git_ask_pass() custom_env = os.environ.copy() custom_env['GIT_USERNAME'] = 'test42' - result = subprocess.run([git_askpass, 'Username'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, env=custom_env) - if result.returncode != 0 or not result.stdout.startswith('test42'): - raise ValueError('PROBLEM: The ' + git_askpass + ' cannot be executed, please verify Dos/Unix encoding') + custom_env['GIT_PASSWORD'] = '42test' # noqa: S2068 + result_user = subprocess.run([git_askpass, 'Username'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, env=custom_env) + result_pwd = subprocess.run([git_askpass, 'Password'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, env=custom_env) + if result_user.returncode != 0 or result_user.stdout != 'test42\n' or result_pwd.returncode != 0 or result_pwd.stdout != '42test\n': + raise ValueError('PROBLEM: The ' + git_askpass + ' cannot be executed, please verify Dos/Unix encoding and/or permissions.') diff --git a/tests/test_utils.py b/tests/test_utils.py index e8fe076..96b274a 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -5,7 +5,7 @@ import pytest from unittest.mock import patch from git import Repo -from modules.utils import ENV_TEST_MODE, TMP_REPO_GIT_DIRECTORY, delete_temporary_repo_git_directory, set_file_execution_permission +from modules.utils import ENV_TEST_MODE, TMP_REPO_GIT_DIRECTORY, delete_temporary_repo_git_directory, set_file_execution_permission, test_git_ask_pass from pytest_httpserver import HTTPServer @@ -83,3 +83,7 @@ def test_set_file_execution_permission(): assert not os.access(temp_file.name, os.X_OK) set_file_execution_permission(temp_file.name) assert os.access(temp_file.name, os.X_OK) + + +def test_test_git_ask_pass(): + test_git_ask_pass() From 59e0fd5665da8ff2d58247e3676eb0b53db12567 Mon Sep 17 00:00:00 2001 From: Alix Lourme Date: Sun, 22 Feb 2026 19:08:30 +0100 Subject: [PATCH 6/6] small improvments --- modules/utils.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/modules/utils.py b/modules/utils.py index 32f59ed..27a34b4 100644 --- a/modules/utils.py +++ b/modules/utils.py @@ -1,15 +1,12 @@ import os import stat import shutil -import logging import subprocess TMP_REPO_GIT_DIRECTORY = 'tmp-git-repo/' ENV_TEST_MODE = 'TEST_MODE' -logger = logging.getLogger(__name__) - def delete_temporary_repo_git_directory(force_if_test_mode: bool = False): if os.environ.get(ENV_TEST_MODE) != 'true' or force_if_test_mode: @@ -46,5 +43,10 @@ def test_git_ask_pass() -> None: custom_env['GIT_PASSWORD'] = '42test' # noqa: S2068 result_user = subprocess.run([git_askpass, 'Username'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, env=custom_env) result_pwd = subprocess.run([git_askpass, 'Password'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, env=custom_env) + os.environ.pop('GIT_USERNAME', None) + os.environ.pop('GIT_PASSWORD', None) if result_user.returncode != 0 or result_user.stdout != 'test42\n' or result_pwd.returncode != 0 or result_pwd.stdout != '42test\n': - raise ValueError('PROBLEM: The ' + git_askpass + ' cannot be executed, please verify Dos/Unix encoding and/or permissions.') + raise ValueError( + 'PROBLEM: The ' + + git_askpass + + ' used for Git authentications without credentials storage cannot be executed, please verify file Dos/Unix encoding and/or permissions.')