From 0421e13f0420fc2d10c736fdf21daac5ee7fc33f Mon Sep 17 00:00:00 2001 From: Nikita Pinaev Date: Thu, 8 Oct 2026 20:46:28 +0300 Subject: [PATCH 1/2] STG-5397: prefer iOS 16 and prepare CLI 2026.10.4 --- README.md | 2 +- mdast_cli/__init__.py | 2 +- mdast_cli/helpers/const.py | 3 +- mdast_cli/mdast_scan.py | 6 +- mdast_cli/ms_flow.py | 11 ++- setup.py | 128 +++++++++++++++++----------------- specs/ios-default-priority.md | 35 ++++++++++ tests/test_ms_architecture.py | 18 +++++ tests/test_smoke_flows.py | 40 +++++++++++ 9 files changed, 169 insertions(+), 76 deletions(-) create mode 100644 specs/ios-default-priority.md diff --git a/README.md b/README.md index 88a8c10..6cd68e6 100644 --- a/README.md +++ b/README.md @@ -693,7 +693,7 @@ mdast_cli \ **Parameter:** `--architecture_id ` Select the target architecture/OS version for scanning: -- If not specified, defaults to Android 11 or iOS 14 (depending on file type) +- If not specified, defaults to Android 11 or prefers iOS 16, then iOS 15, then iOS 14 (depending on file type and the server catalogue) - Use specific architecture ID for testing on different OS versions - Available architectures depend on your DAST platform configuration diff --git a/mdast_cli/__init__.py b/mdast_cli/__init__.py index 3e46509..31a6091 100644 --- a/mdast_cli/__init__.py +++ b/mdast_cli/__init__.py @@ -1 +1 @@ -__version__ = '2026.9.2' +__version__ = '2026.10.4' diff --git a/mdast_cli/helpers/const.py b/mdast_cli/helpers/const.py index 356a234..0ea1321 100644 --- a/mdast_cli/helpers/const.py +++ b/mdast_cli/helpers/const.py @@ -32,7 +32,8 @@ class DastState: # Default architecture names for auto-selection DEFAULT_ANDROID_ARCHITECTURE = 'Android 11' -DEFAULT_IOS_ARCHITECTURE = 'iOS 14' +DEFAULT_IOS_ARCHITECTURE = 'iOS 16' +IOS_ARCHITECTURE_PRIORITY = (DEFAULT_IOS_ARCHITECTURE, 'iOS 15', 'iOS 14') # Timeout constants (in seconds) TRY = 360 diff --git a/mdast_cli/mdast_scan.py b/mdast_cli/mdast_scan.py index 0a3e8de..f046712 100644 --- a/mdast_cli/mdast_scan.py +++ b/mdast_cli/mdast_scan.py @@ -23,7 +23,7 @@ from mdast_cli.distribution_systems.nexus2 import Nexus2Repository from mdast_cli.distribution_systems.rumarket import rumarket_download_app from mdast_cli.distribution_systems.rustore import rustore_download_app -from mdast_cli.helpers.const import (ANDROID_EXTENSIONS, DEFAULT_ANDROID_ARCHITECTURE, DEFAULT_IOS_ARCHITECTURE, +from mdast_cli.helpers.const import (ANDROID_EXTENSIONS, DEFAULT_ANDROID_ARCHITECTURE, IOS_ARCHITECTURE_PRIORITY, END_SCAN_TIMEOUT, LONG_TRY, REPORT_TIMEOUT, SLEEP_TIMEOUT, TRY, DastState, DastStateDict) from mdast_cli.helpers.exit_codes import ExitCode @@ -542,8 +542,8 @@ def run_monolith_flow(arguments, url, token, company_id, app_file, appstore_app_ architecture = next((arch.get('id') for arch in architectures if isinstance(arch, dict) and arch.get('name') == DEFAULT_ANDROID_ARCHITECTURE), None) if file_extension == '.ipa': - architecture = next((arch.get('id') for arch in architectures - if isinstance(arch, dict) and arch.get('name') == DEFAULT_IOS_ARCHITECTURE), None) + architecture = next((arch.get('id') for name in IOS_ARCHITECTURE_PRIORITY for arch in architectures + if isinstance(arch, dict) and arch.get('name') == name), None) if architecture is None: logger.error("Cannot create scan - no suitable architecture for this app, try to set it manually with --architecture_id") sys.exit(ExitCode.INVALID_ARGS) diff --git a/mdast_cli/ms_flow.py b/mdast_cli/ms_flow.py index 6e11117..e121cf1 100644 --- a/mdast_cli/ms_flow.py +++ b/mdast_cli/ms_flow.py @@ -20,7 +20,7 @@ import requests from mdast_cli.helpers.const import (ACTIVE_STAGES, ANDROID_EXTENSIONS, DEFAULT_ANDROID_ARCHITECTURE, - DEFAULT_IOS_ARCHITECTURE, END_SCAN_TIMEOUT, LONG_TRY, OS_ANDROID, + IOS_ARCHITECTURE_PRIORITY, END_SCAN_TIMEOUT, LONG_TRY, OS_ANDROID, OS_IOS, PRE_START_STAGES, REPORT_TIMEOUT, SLEEP_TIMEOUT, TERMINAL_SCAN_PAIRS, TRY, UPLOAD_TIMEOUT_ENV_VAR, UPLOAD_TIMEOUT_MAX, UPLOAD_TIMEOUT_MIN, ENGINE_ACTIVE_STATUS, ScanStage, ScanStageStatus) @@ -92,12 +92,11 @@ def resolve_ms_os_version(architectures, platform): if not candidates: return None - preferred_name = { - OS_ANDROID: DEFAULT_ANDROID_ARCHITECTURE, - OS_IOS: DEFAULT_IOS_ARCHITECTURE, - }.get(platform) + preferred_names = (IOS_ARCHITECTURE_PRIORITY if platform == OS_IOS + else (DEFAULT_ANDROID_ARCHITECTURE,)) selected = next( - (architecture for architecture in candidates if architecture.get('name') == preferred_name), + (architecture for name in preferred_names for architecture in candidates + if architecture.get('name') == name), candidates[0], ) return str(selected['os_version']).strip() diff --git a/setup.py b/setup.py index 3bc0b73..5b1e578 100644 --- a/setup.py +++ b/setup.py @@ -1,64 +1,64 @@ -from setuptools import find_packages, setup - -with open("README.md", "r") as fh: - long_description = fh.read() - -setup( - name="mdast_cli", - - version='2026.10.3', - - python_requires='>=3.12', - - author="Dynamic-Mobile-Security", - description="Dynamic-Mobile-Security", - long_description=long_description, - long_description_content_type="text/markdown", - url="https://github.com/Dynamic-Mobile-Security/mdast-cli", - packages=find_packages(), - include_package_data=True, - package_data={'': ['device.properties'], - 'mdast_cli': ['bin/apkeep-*', 'bin/APKEEP-LICENSE', 'bin/GPAPI-LICENSE'], - 'mdast_cli.distribution_systems.appstore_client': ['bin/mdast-sap-*', 'IPATOOL-LICENSE']}, - install_requires=[ - 'altgraph==0.17', - 'beautifulsoup4==4.10.0', - 'cachetools==4.1.1', - 'certifi>=2023.7.22', - 'cffi>=1.15.0', - 'chardet>=3.0.4', - 'charset-normalizer==2.0.12', - 'cryptography>=37.0.2', - 'google==3.0.0', - 'google-auth>=2.23.0', - 'idna>=3.7', - 'pyasn1==0.4.8', - 'pyasn1-modules==0.2.8', - 'pycparser==2.21', - 'pyparsing==3.0.7', - 'pytz>=2021.1', - 'requests>=2.23.0', - 'rsa==4.8', - 'six>=1.15.0', - 'soupsieve==2.3.1', - 'uritemplate==3.0.1', - 'urllib3>=1.26.13', - 'tqdm>=4.66.0' - ], - entry_points={ - 'console_scripts': [ - 'mdast_cli=mdast_cli.mdast_scan:main' - ] - }, - classifiers=[ - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "License :: OSI Approved :: MIT License", - "Operating System :: Unix", - ], -) - - +from setuptools import find_packages, setup + +with open("README.md", "r") as fh: + long_description = fh.read() + +setup( + name="mdast_cli", + + version='2026.10.4', + + python_requires='>=3.12', + + author="Dynamic-Mobile-Security", + description="Dynamic-Mobile-Security", + long_description=long_description, + long_description_content_type="text/markdown", + url="https://github.com/Dynamic-Mobile-Security/mdast-cli", + packages=find_packages(), + include_package_data=True, + package_data={'': ['device.properties'], + 'mdast_cli': ['bin/apkeep-*', 'bin/APKEEP-LICENSE', 'bin/GPAPI-LICENSE'], + 'mdast_cli.distribution_systems.appstore_client': ['bin/mdast-sap-*', 'IPATOOL-LICENSE']}, + install_requires=[ + 'altgraph==0.17', + 'beautifulsoup4==4.10.0', + 'cachetools==4.1.1', + 'certifi>=2023.7.22', + 'cffi>=1.15.0', + 'chardet>=3.0.4', + 'charset-normalizer==2.0.12', + 'cryptography>=37.0.2', + 'google==3.0.0', + 'google-auth>=2.23.0', + 'idna>=3.7', + 'pyasn1==0.4.8', + 'pyasn1-modules==0.2.8', + 'pycparser==2.21', + 'pyparsing==3.0.7', + 'pytz>=2021.1', + 'requests>=2.23.0', + 'rsa==4.8', + 'six>=1.15.0', + 'soupsieve==2.3.1', + 'uritemplate==3.0.1', + 'urllib3>=1.26.13', + 'tqdm>=4.66.0' + ], + entry_points={ + 'console_scripts': [ + 'mdast_cli=mdast_cli.mdast_scan:main' + ] + }, + classifiers=[ + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "License :: OSI Approved :: MIT License", + "Operating System :: Unix", + ], +) + + diff --git a/specs/ios-default-priority.md b/specs/ios-default-priority.md new file mode 100644 index 0000000..56d57f3 --- /dev/null +++ b/specs/ios-default-priority.md @@ -0,0 +1,35 @@ +# Приоритет iOS при автоматическом выборе архитектуры + +## Требование + +По решению пользователя от 08.10.2026 порядок выбора: iOS 16, затем iOS 15, +затем iOS 14. Сейчас CLI предпочитает iOS 14 независимо от порядка каталога. + +## Границы + +- Общий дефолт iOS становится 16. +- Kubernetes и монолит выбирают первую присутствующую архитектуру из порядка 16/15/14. +- Порядок записей каталога не влияет на этот приоритет. +- Явный architecture_id в монолите сохраняет приоритет над автоматическим выбором. +- Android, обработка движков и контракт API не меняются. +- Если ни одной из трёх архитектур нет, сохраняется прежнее поведение: + Kubernetes берёт первую подходящую iOS-запись, монолит просит явный architecture_id. +- Изменение не добавляет выбор ОС по доступности устройств организации. + +## Приёмка + +Тесты покрывают разные порядки каталога, fallback 16 → 15 → 14, +пустой/неподходящий каталог, прежний Android-дефолт и явный выбор в монолите. +По подтверждению пользователя публикуется релиз 2026.10.4 в GitHub, PyPI и Docker. +Версии setup.py и mdast_cli/__init__.py синхронизируются. +Перед публикацией должен пройти полный CI; после публикации проверяется версия пакета и образа. + +## Результаты проверки 08.10.2026 + +- 128 тестов прошли, git diff --check без ошибок. +- Изменённая локальная CLI выбрала iOS 16 на dev, скан 20399. +- Скан дошёл до SUCCESS/PARTIAL_COMPLETE, exit code 0, JSON и PDF получены. +- Неполнота вызвана отсутствием зарегистрированного сервиса app_dump. + Также зафиксированы падения контрольного приложения. Эти проблемы + не исправляются изменением порядка выбора ОС. +- Полный локальный набор: 410 passed, 1 skipped. Публикация 2026.10.4 выполняется после CI. diff --git a/tests/test_ms_architecture.py b/tests/test_ms_architecture.py index 7e38a80..4782237 100644 --- a/tests/test_ms_architecture.py +++ b/tests/test_ms_architecture.py @@ -1,5 +1,7 @@ """Unit tests for microservices OS-version selection (STG-4588).""" +from itertools import permutations + import pytest from mdast_cli.helpers.const import OS_ANDROID, OS_IOS @@ -44,3 +46,19 @@ def test_selects_version_from_paginated_scanyon_catalogue(): @pytest.mark.parametrize('architectures', [None, {}, [], [{'type': 'ANDROID', 'os_version': ''}]]) def test_missing_platform_version_returns_none(architectures): assert resolve_ms_os_version(architectures, OS_ANDROID) is None + + +@pytest.mark.parametrize('versions,expected', [ + *[(versions, '16') for versions in permutations(('14', '15', '16'))], + (('14', '15'), '15'), + (('15', '14'), '15'), + (('14',), '14'), + (('26.1', '14', '15', '16'), '16'), + (('26.1',), '26.1'), + ((), None), +]) +@pytest.mark.parametrize('paginated', [False, True]) +def test_ios_priority(versions, expected, paginated): + catalogue = [{'type': 'IOS', 'name': f'iOS {v}', 'os_version': v} for v in versions] + payload = {'items': catalogue} if paginated else catalogue + assert resolve_ms_os_version(payload, OS_IOS) == expected diff --git a/tests/test_smoke_flows.py b/tests/test_smoke_flows.py index d978f79..df5d034 100644 --- a/tests/test_smoke_flows.py +++ b/tests/test_smoke_flows.py @@ -7,6 +7,7 @@ """ import json import os +from itertools import permutations import pytest import responses @@ -16,6 +17,45 @@ pytestmark = pytest.mark.smoke +@pytest.mark.parametrize('versions,explicit,expected', [ + *[(versions, None, 16) for versions in permutations((14, 15, 16))], + ((14, 15), None, 15), + ((15, 14), None, 15), + ((14,), None, 14), + ((14, 15, 16), 14, 14), + ((26,), None, None), + ((), None, None), +]) +def test_monolith_ios_priority(mocked_responses, monkeypatch, tmp_path, + monolith_mode, versions, explicit, expected): + ipa = tmp_path / 'app.ipa' + ipa.write_bytes(b'local test payload') + mocked_responses.add(responses.GET, f'{REST_URL}/architectures/', json=[ + {'id': v, 'name': f'iOS {v}', 'type': 2} for v in versions + ]) + mocked_responses.add(responses.GET, f'{REST_URL}/organizations/1/engines/', json=[ + {'architecture': expected, 'state': 3}, + ]) + mocked_responses.add(responses.GET, f'{REST_URL}/organizations/1/applications/', + json=[{'id': 10}]) + mocked_responses.add(responses.POST, f'{REST_URL}/organizations/1/dasts/', + json={'id': 77}, status=201) + mocked_responses.add(responses.POST, f'{REST_URL}/dasts/77/start/', json={}) + argv = ['--distribution_system', 'file', '--file_path', str(ipa), + '--url', BASE_URL, '--company_id', '1', '--token', TOKEN, '--nowait'] + if explicit is not None: + argv += ['--architecture_id', str(explicit)] + result = run_main(monkeypatch, argv) + if expected is None: + assert result == 2 + assert len(mocked_responses.calls) == 1 + return + assert result == 0 + created = next(call for call in mocked_responses.calls + if call.request.url.endswith('/organizations/1/dasts/')) + assert json.loads(created.request.body)['architecture_id'] == expected + + def register_ms_happy_path(rsps, apk_md5, with_testcase=True, paginated_architectures=False): architectures = [ {'id': 1, 'type': 'ANDROID', 'os_version': '11', 'name': 'Android 11', 'description': 'API 30'}, From 048154f360f85c8bc85d4cd4dc96fdc85953cee6 Mon Sep 17 00:00:00 2001 From: Nikita Pinaev Date: Thu, 8 Oct 2026 20:46:47 +0300 Subject: [PATCH 2/2] STG-5397: preserve setup.py line endings --- setup.py | 126 +++++++++++++++++++++++++++---------------------------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/setup.py b/setup.py index 5b1e578..699a2b9 100644 --- a/setup.py +++ b/setup.py @@ -1,64 +1,64 @@ -from setuptools import find_packages, setup - -with open("README.md", "r") as fh: - long_description = fh.read() - -setup( - name="mdast_cli", - +from setuptools import find_packages, setup + +with open("README.md", "r") as fh: + long_description = fh.read() + +setup( + name="mdast_cli", + version='2026.10.4', - - python_requires='>=3.12', - - author="Dynamic-Mobile-Security", - description="Dynamic-Mobile-Security", - long_description=long_description, - long_description_content_type="text/markdown", - url="https://github.com/Dynamic-Mobile-Security/mdast-cli", - packages=find_packages(), - include_package_data=True, - package_data={'': ['device.properties'], - 'mdast_cli': ['bin/apkeep-*', 'bin/APKEEP-LICENSE', 'bin/GPAPI-LICENSE'], - 'mdast_cli.distribution_systems.appstore_client': ['bin/mdast-sap-*', 'IPATOOL-LICENSE']}, - install_requires=[ - 'altgraph==0.17', - 'beautifulsoup4==4.10.0', - 'cachetools==4.1.1', - 'certifi>=2023.7.22', - 'cffi>=1.15.0', - 'chardet>=3.0.4', - 'charset-normalizer==2.0.12', - 'cryptography>=37.0.2', - 'google==3.0.0', - 'google-auth>=2.23.0', - 'idna>=3.7', - 'pyasn1==0.4.8', - 'pyasn1-modules==0.2.8', - 'pycparser==2.21', - 'pyparsing==3.0.7', - 'pytz>=2021.1', - 'requests>=2.23.0', - 'rsa==4.8', - 'six>=1.15.0', - 'soupsieve==2.3.1', - 'uritemplate==3.0.1', - 'urllib3>=1.26.13', - 'tqdm>=4.66.0' - ], - entry_points={ - 'console_scripts': [ - 'mdast_cli=mdast_cli.mdast_scan:main' - ] - }, - classifiers=[ - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "License :: OSI Approved :: MIT License", - "Operating System :: Unix", - ], -) - - + + python_requires='>=3.12', + + author="Dynamic-Mobile-Security", + description="Dynamic-Mobile-Security", + long_description=long_description, + long_description_content_type="text/markdown", + url="https://github.com/Dynamic-Mobile-Security/mdast-cli", + packages=find_packages(), + include_package_data=True, + package_data={'': ['device.properties'], + 'mdast_cli': ['bin/apkeep-*', 'bin/APKEEP-LICENSE', 'bin/GPAPI-LICENSE'], + 'mdast_cli.distribution_systems.appstore_client': ['bin/mdast-sap-*', 'IPATOOL-LICENSE']}, + install_requires=[ + 'altgraph==0.17', + 'beautifulsoup4==4.10.0', + 'cachetools==4.1.1', + 'certifi>=2023.7.22', + 'cffi>=1.15.0', + 'chardet>=3.0.4', + 'charset-normalizer==2.0.12', + 'cryptography>=37.0.2', + 'google==3.0.0', + 'google-auth>=2.23.0', + 'idna>=3.7', + 'pyasn1==0.4.8', + 'pyasn1-modules==0.2.8', + 'pycparser==2.21', + 'pyparsing==3.0.7', + 'pytz>=2021.1', + 'requests>=2.23.0', + 'rsa==4.8', + 'six>=1.15.0', + 'soupsieve==2.3.1', + 'uritemplate==3.0.1', + 'urllib3>=1.26.13', + 'tqdm>=4.66.0' + ], + entry_points={ + 'console_scripts': [ + 'mdast_cli=mdast_cli.mdast_scan:main' + ] + }, + classifiers=[ + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "License :: OSI Approved :: MIT License", + "Operating System :: Unix", + ], +) + +