patchman 4.0.6__tar.gz → 4.0.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {patchman-4.0.6 → patchman-4.0.8}/PKG-INFO +1 -1
- patchman-4.0.8/VERSION.txt +1 -0
- {patchman-4.0.6 → patchman-4.0.8}/arch/models.py +1 -0
- {patchman-4.0.6 → patchman-4.0.8}/domains/models.py +3 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/sources/distros/alma.py +3 -1
- {patchman-4.0.6 → patchman-4.0.8}/errata/sources/distros/centos.py +2 -1
- {patchman-4.0.6 → patchman-4.0.8}/errata/sources/repos/yum.py +27 -9
- {patchman-4.0.6 → patchman-4.0.8}/errata/tests/test_integration.py +4 -3
- {patchman-4.0.6 → patchman-4.0.8}/etc/patchman/local_settings.py +2 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/models.py +1 -0
- patchman-4.0.8/operatingsystems/tests/test_models.py +241 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/utils.py +38 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/models.py +1 -0
- {patchman-4.0.6 → patchman-4.0.8}/patchman.egg-info/PKG-INFO +1 -1
- {patchman-4.0.6 → patchman-4.0.8}/reports/models.py +6 -1
- {patchman-4.0.6 → patchman-4.0.8}/reports/tests/test_utils.py +164 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/utils.py +7 -7
- {patchman-4.0.6 → patchman-4.0.8}/reports/views.py +2 -1
- {patchman-4.0.6 → patchman-4.0.8}/repos/models.py +5 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/models.py +4 -0
- patchman-4.0.6/VERSION.txt +0 -1
- patchman-4.0.6/operatingsystems/tests/test_models.py +0 -102
- {patchman-4.0.6 → patchman-4.0.8}/AUTHORS +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/COPYING +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/INSTALL.md +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/MANIFEST.in +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/README.md +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/arch/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/arch/admin.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/arch/apps.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/arch/migrations/0001_initial.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/arch/migrations/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/arch/serializers.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/arch/tests/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/arch/tests/test_api.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/arch/tests/test_models.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/arch/utils.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/arch/views.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/domains/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/domains/admin.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/domains/apps.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/domains/migrations/0001_initial.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/domains/migrations/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/domains/serializers.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/domains/tests/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/domains/tests/test_api.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/domains/tests/test_models.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/domains/views.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/admin.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/apps.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/managers.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/migrations/0001_initial.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/migrations/0002_alter_erratumreference_unique_together.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/migrations/0003_delete_erratumreference_alter_erratum_references.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/migrations/0004_rename_packages_erratum_fixed_packages.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/migrations/0005_erratum_affected_packages_and_more.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/migrations/0006_alter_erratum_options.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/migrations/0007_alter_erratum_fixed_packages.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/migrations/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/models.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/serializers.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/sources/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/sources/distros/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/sources/distros/arch.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/sources/distros/debian.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/sources/distros/rocky.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/sources/distros/ubuntu.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/sources/repos/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/tables.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/tasks.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/templates/errata/erratum_detail.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/templates/errata/erratum_list.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/tests/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/tests/test_api.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/tests/test_models.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/urls.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/utils.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/errata/views.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/etc/patchman/apache.conf.example +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/etc/patchman/celery.conf +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/etc/systemd/system/patchman-celery-beat.service +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/etc/systemd/system/patchman-celery-worker.service +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/admin.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/apps.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/forms.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/managers.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/migrations/0001_initial.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/migrations/0002_initial.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/migrations/0003_host_modules.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/migrations/0004_remove_host_tags_host_tags.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/migrations/0005_rename_os_host_osvariant.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/migrations/0006_migrate_to_tz_aware.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/migrations/0007_alter_host_tags.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/migrations/0008_alter_host_options.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/migrations/0009_host_errata.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/migrations/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/serializers.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/tables.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/tasks.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/templates/hosts/host_delete.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/templates/hosts/host_detail.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/templates/hosts/host_edit.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/templates/hosts/host_list.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/templatetags/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/templatetags/report_alert.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/tests/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/tests/test_api.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/tests/test_find_updates.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/tests/test_managers.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/tests/test_models.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/urls.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/utils.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/hosts/views.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/modules/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/modules/admin.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/modules/apps.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/modules/managers.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/modules/migrations/0001_initial.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/modules/migrations/0002_auto_20240204_2214.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/modules/migrations/0003_alter_module_options.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/modules/migrations/0004_alter_module_options.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/modules/migrations/0005_alter_module_unique_together.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/modules/migrations/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/modules/models.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/modules/serializers.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/modules/tables.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/modules/templates/modules/module_detail.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/modules/templates/modules/module_list.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/modules/urls.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/modules/utils.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/modules/views.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/admin.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/apps.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/fixtures/os.json +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/fixtures/osgroup.json +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/forms.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/managers.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/migrations/0001_initial.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/migrations/0002_initial.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/migrations/0003_os_arch.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/migrations/0003_osgroup_codename.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/migrations/0004_alter_osgroup_unique_together.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/migrations/0005_rename_osgroup_osrelease_rename_os_osvariant_and_more.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/migrations/0006_osrelease_cpe_name_osvariant_codename.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/migrations/0007_alter_osrelease_unique_together.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/migrations/0008_alter_osrelease_options_alter_osvariant_options.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/migrations/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/models.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/serializers.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/tables.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/templates/operatingsystems/os_landing.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/templates/operatingsystems/osrelease_delete.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/templates/operatingsystems/osrelease_detail.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/templates/operatingsystems/osrelease_list.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/templates/operatingsystems/osvariant_delete.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/templates/operatingsystems/osvariant_delete_multiple.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/templates/operatingsystems/osvariant_detail.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/templates/operatingsystems/osvariant_list.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/tests/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/tests/test_api.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/urls.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/operatingsystems/views.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/admin.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/apps.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/managers.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/migrations/0001_initial.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/migrations/0002_auto_20250207_1319.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/migrations/0002_delete_erratum_delete_erratumreference.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/migrations/0003_auto_20250207_1746.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/migrations/0004_alter_package_options_alter_packagecategory_options_and_more.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/migrations/0005_alter_package_packagetype.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/migrations/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/serializers.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/tables.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/templates/packages/package_detail.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/templates/packages/package_list.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/templates/packages/package_name_detail.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/templates/packages/package_name_list.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/tests/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/tests/test_api.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/tests/test_models.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/tests/test_version_compare.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/urls.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/utils.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/packages/views.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/patchman/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/patchman/celery.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/patchman/receivers.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/patchman/settings.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/patchman/signals.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/patchman/sqlite3/base.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/patchman/static/css/base.css +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/patchman/static/js/expandable-text.js +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/patchman/static/js/sidebar.js +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/patchman/urls.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/patchman/wsgi.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/patchman.egg-info/SOURCES.txt +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/patchman.egg-info/dependency_links.txt +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/patchman.egg-info/requires.txt +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/patchman.egg-info/top_level.txt +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/admin.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/apps.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/migrations/0001_initial.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/migrations/0002_report_modules.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/migrations/0003_remove_report_accessed.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/migrations/0004_migrate_to_tz_aware.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/migrations/0005_alter_report_options.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/migrations/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/serializers.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/tables.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/tasks.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/templates/reports/report.txt +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/templates/reports/report_delete.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/templates/reports/report_detail.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/templates/reports/report_list.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/tests/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/tests/test_api.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/tests/test_edge_cases.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/tests/test_integration.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/tests/test_models.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/tests/test_parsing.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/tests/test_serializers.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/tests/test_tasks.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/reports/urls.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/admin.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/apps.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/forms.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/managers.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/migrations/0001_initial.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/migrations/0002_alter_repository_repotype.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/migrations/0003_migrate_to_tz_aware.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/migrations/0004_rename_file_checksum_mirror_package_checksum.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/migrations/0005_rename_package_checksum_mirror_packages_checksum.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/migrations/0006_mirror_errata_checksum_mirror_modules_checksum.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/migrations/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/repo_types/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/repo_types/arch.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/repo_types/deb.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/repo_types/gentoo.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/repo_types/rpm.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/repo_types/yast.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/repo_types/yum.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/serializers.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/tables.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/tasks.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/templates/repos/mirror_delete.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/templates/repos/mirror_detail.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/templates/repos/mirror_edit.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/templates/repos/mirror_list.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/templates/repos/mirror_with_repo_list.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/templates/repos/repo_delete.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/templates/repos/repo_detail.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/templates/repos/repo_edit.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/templates/repos/repo_list.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/templatetags/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/tests/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/tests/test_api.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/tests/test_managers.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/tests/test_mirror_sync.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/tests/test_models.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/urls.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/utils.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/repos/views.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/requirements.txt +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/sbin/patchman +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/sbin/patchman-manage +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/sbin/patchman-set-secret-key +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/scripts/clear-django-logs.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/scripts/create_graph.sh +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/scripts/rpm-install.sh +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/scripts/rpm-post-install.sh +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/admin.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/apps.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/managers.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/migrations/0001_initial.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/migrations/0002_alter_cve_options.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/migrations/0003_alter_cve_description_alter_cwe_description.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/migrations/0004_alter_cve_options.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/migrations/0005_reference_cve_references.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/migrations/0006_alter_cve_options_alter_cvss_unique_together.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/migrations/0007_remove_cve_title.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/migrations/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/serializers.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/tables.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/tasks.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/templates/security/cve_detail.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/templates/security/cve_list.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/templates/security/cwe_detail.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/templates/security/cwe_list.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/templates/security/reference_list.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/templates/security/security_landing.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/tests/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/tests/test_api.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/tests/test_models.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/urls.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/utils.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/security/views.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/setup.cfg +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/setup.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/apps.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/context_processors.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/filterspecs.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/logging.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/management/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/management/commands/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/management/commands/create_api_key.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/management/commands/list_api_keys.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/management/commands/revoke_api_key.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/tables.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/tasks.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/templates/404.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/templates/500.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/templates/base.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/templates/bulk_actions.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/templates/dashboard.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/templates/navbar.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/templates/objectlist.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/templates/registration/login.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/templates/searchbar.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/templates/table.html +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/templatetags/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/templatetags/common.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/tests/__init__.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/tests/test_commands.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/tests/test_utils.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/urls.py +0 -0
- {patchman-4.0.6 → patchman-4.0.8}/util/views.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
4.0.8
|
|
@@ -110,7 +110,9 @@ def process_alma_erratum(release, advisory):
|
|
|
110
110
|
def add_alma_erratum_osreleases(e, release):
|
|
111
111
|
""" Update OS Release for Alma Linux errata
|
|
112
112
|
"""
|
|
113
|
-
|
|
113
|
+
from operatingsystems.utils import normalize_el_osrelease
|
|
114
|
+
osrelease_name = normalize_el_osrelease(f'Alma Linux {release}')
|
|
115
|
+
osrelease = get_or_create_osrelease(name=osrelease_name)
|
|
114
116
|
e.osreleases.add(osrelease)
|
|
115
117
|
|
|
116
118
|
|
|
@@ -117,13 +117,14 @@ def add_centos_erratum_references(e, references):
|
|
|
117
117
|
def parse_centos_errata_children(e, children):
|
|
118
118
|
""" Parse errata children to obtain architecture, release and packages
|
|
119
119
|
"""
|
|
120
|
+
from operatingsystems.utils import normalize_el_osrelease
|
|
120
121
|
fixed_packages = set()
|
|
121
122
|
for c in children:
|
|
122
123
|
if c.tag == 'os_arch':
|
|
123
124
|
pass
|
|
124
125
|
elif c.tag == 'os_release':
|
|
125
126
|
if accepted_centos_release([c.text]):
|
|
126
|
-
osrelease_name = f'CentOS {c.text}'
|
|
127
|
+
osrelease_name = normalize_el_osrelease(f'CentOS {c.text}')
|
|
127
128
|
osrelease = get_or_create_osrelease(name=osrelease_name)
|
|
128
129
|
e.osreleases.add(osrelease)
|
|
129
130
|
elif c.tag == 'packages':
|
|
@@ -20,7 +20,9 @@ from io import BytesIO
|
|
|
20
20
|
from defusedxml import ElementTree
|
|
21
21
|
from django.db import connections
|
|
22
22
|
|
|
23
|
-
from operatingsystems.utils import
|
|
23
|
+
from operatingsystems.utils import (
|
|
24
|
+
get_or_create_osrelease, normalize_el_osrelease,
|
|
25
|
+
)
|
|
24
26
|
from packages.models import Package
|
|
25
27
|
from packages.utils import get_or_create_package
|
|
26
28
|
from patchman.signals import pbar_start, pbar_update
|
|
@@ -184,9 +186,25 @@ def get_osrelease_names(e, update):
|
|
|
184
186
|
return osreleases
|
|
185
187
|
|
|
186
188
|
|
|
189
|
+
def get_existing_el_osreleases(major_version):
|
|
190
|
+
""" Returns existing OSReleases for EL-based distros matching the major version
|
|
191
|
+
"""
|
|
192
|
+
from operatingsystems.models import OSRelease
|
|
193
|
+
el_patterns = [
|
|
194
|
+
f'Red Hat Enterprise Linux {major_version}',
|
|
195
|
+
f'CentOS Stream {major_version}',
|
|
196
|
+
f'CentOS {major_version}',
|
|
197
|
+
f'Rocky Linux {major_version}',
|
|
198
|
+
f'Alma Linux {major_version}',
|
|
199
|
+
f'Oracle Linux {major_version}',
|
|
200
|
+
]
|
|
201
|
+
return list(OSRelease.objects.filter(name__in=el_patterns))
|
|
202
|
+
|
|
203
|
+
|
|
187
204
|
def add_updateinfo_osreleases(e, collection, osrelease_names):
|
|
188
205
|
""" Adds OSRelease objects to an Erratum
|
|
189
206
|
rocky and alma need some renaming
|
|
207
|
+
EPEL maps to existing EL-based OSReleases only
|
|
190
208
|
"""
|
|
191
209
|
if not osrelease_names:
|
|
192
210
|
collection_name = collection.find('name')
|
|
@@ -194,14 +212,14 @@ def add_updateinfo_osreleases(e, collection, osrelease_names):
|
|
|
194
212
|
osrelease_name = collection_name.text
|
|
195
213
|
osrelease_names.append(osrelease_name)
|
|
196
214
|
for osrelease_name in osrelease_names:
|
|
197
|
-
if osrelease_name.startswith('
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
215
|
+
if osrelease_name.startswith('Fedora EPEL'):
|
|
216
|
+
# "Fedora EPEL 10.0" → map to existing EL 10 OSReleases
|
|
217
|
+
version_str = osrelease_name.split()[-1] # "10.0"
|
|
218
|
+
major_version = version_str.split('.')[0] # "10"
|
|
219
|
+
for osrelease in get_existing_el_osreleases(major_version):
|
|
220
|
+
e.osreleases.add(osrelease)
|
|
221
|
+
continue
|
|
222
|
+
osrelease_name = normalize_el_osrelease(osrelease_name)
|
|
205
223
|
osrelease = get_or_create_osrelease(name=osrelease_name)
|
|
206
224
|
e.osreleases.add(osrelease)
|
|
207
225
|
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
# along with Patchman. If not, see <http://www.gnu.org/licenses/>
|
|
16
16
|
|
|
17
17
|
from django.test import TestCase, override_settings
|
|
18
|
+
from django.utils import timezone
|
|
18
19
|
|
|
19
20
|
from errata.models import Erratum
|
|
20
21
|
from operatingsystems.models import OSRelease
|
|
@@ -37,7 +38,7 @@ class ErrataIntegrationTests(TestCase):
|
|
|
37
38
|
name='RHSA-2024:1234',
|
|
38
39
|
e_type='Security Advisory',
|
|
39
40
|
synopsis='Important: curl security update',
|
|
40
|
-
issue_date=
|
|
41
|
+
issue_date=timezone.now(),
|
|
41
42
|
)
|
|
42
43
|
erratum.cves.add(cve1, cve2)
|
|
43
44
|
|
|
@@ -54,7 +55,7 @@ class ErrataIntegrationTests(TestCase):
|
|
|
54
55
|
name='RHSA-2024:1235',
|
|
55
56
|
e_type='Security Advisory',
|
|
56
57
|
synopsis='Important: openssl security update',
|
|
57
|
-
issue_date=
|
|
58
|
+
issue_date=timezone.now(),
|
|
58
59
|
)
|
|
59
60
|
erratum.osreleases.add(osrelease1, osrelease2)
|
|
60
61
|
|
|
@@ -66,7 +67,7 @@ class ErrataIntegrationTests(TestCase):
|
|
|
66
67
|
name='RHSA-2024:1236',
|
|
67
68
|
e_type='Bug Fix',
|
|
68
69
|
synopsis='Bug fix: httpd update',
|
|
69
|
-
issue_date=
|
|
70
|
+
issue_date=timezone.now(),
|
|
70
71
|
)
|
|
71
72
|
|
|
72
73
|
# Verify erratum can store package references
|
|
@@ -111,5 +111,7 @@ LOGGING = {
|
|
|
111
111
|
'loggers': {
|
|
112
112
|
'urllib3': {'level': 'WARNING', 'handlers': ['console'], 'propagate': False},
|
|
113
113
|
'git': {'level': 'WARNING', 'handlers': ['console'], 'propagate': False},
|
|
114
|
+
'version_utils': {'level': 'WARNING', 'handlers': ['console'], 'propagate': False},
|
|
115
|
+
'celery': {'level': 'WARNING', 'handlers': ['console'], 'propagate': False},
|
|
114
116
|
}
|
|
115
117
|
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# Copyright 2025 Marcus Furlong <furlongm@gmail.com>
|
|
2
|
+
#
|
|
3
|
+
# This file is part of Patchman.
|
|
4
|
+
#
|
|
5
|
+
# Patchman is free software: you can redistribute it and/or modify
|
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
|
7
|
+
# the Free Software Foundation, version 3 only.
|
|
8
|
+
#
|
|
9
|
+
# Patchman is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with Patchman. If not, see <http://www.gnu.org/licenses/>
|
|
16
|
+
|
|
17
|
+
from django.test import TestCase, override_settings
|
|
18
|
+
|
|
19
|
+
from operatingsystems.models import OSRelease, OSVariant
|
|
20
|
+
from operatingsystems.utils import normalize_el_osrelease
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@override_settings(
|
|
24
|
+
CELERY_TASK_ALWAYS_EAGER=True,
|
|
25
|
+
CACHES={'default': {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'}}
|
|
26
|
+
)
|
|
27
|
+
class NormalizeELOSReleaseTests(TestCase):
|
|
28
|
+
"""Tests for normalize_el_osrelease() function.
|
|
29
|
+
|
|
30
|
+
This function normalizes EL-based distro names to major version only,
|
|
31
|
+
ensuring consistent OSRelease naming across errata and reports.
|
|
32
|
+
|
|
33
|
+
Regression notes - OLD behavior that caused duplicate OSReleases:
|
|
34
|
+
- 'rocky-linux-10.1' -> 'Rocky Linux 10.1' (should be 'Rocky Linux 10')
|
|
35
|
+
- 'almalinux-10.1' -> 'Alma Linux 10.1' (should be 'Alma Linux 10')
|
|
36
|
+
- 'Rocky Linux 10.1' -> passed through unchanged (should be 'Rocky Linux 10')
|
|
37
|
+
- 'CentOS 7.9' -> passed through unchanged (should be 'CentOS 7')
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
# ===========================================
|
|
41
|
+
# REGRESSION TESTS - These were bugs before
|
|
42
|
+
# ===========================================
|
|
43
|
+
|
|
44
|
+
def test_regression_rocky_dash_format_minor_stripped(self):
|
|
45
|
+
"""REGRESSION: rocky-linux-10.1 was creating 'Rocky Linux 10.1'
|
|
46
|
+
|
|
47
|
+
Old behavior: version = osrelease_name.split('-')[2] -> '10.1'
|
|
48
|
+
result = 'Rocky Linux 10.1'
|
|
49
|
+
New behavior: major_version = '10.1'.split('.')[0] -> '10'
|
|
50
|
+
result = 'Rocky Linux 10'
|
|
51
|
+
"""
|
|
52
|
+
# OLD (wrong): 'Rocky Linux 10.1'
|
|
53
|
+
# NEW (correct): 'Rocky Linux 10'
|
|
54
|
+
self.assertEqual(normalize_el_osrelease('rocky-linux-10.1'), 'Rocky Linux 10')
|
|
55
|
+
self.assertNotEqual(normalize_el_osrelease('rocky-linux-10.1'), 'Rocky Linux 10.1')
|
|
56
|
+
|
|
57
|
+
def test_regression_alma_dash_format_minor_stripped(self):
|
|
58
|
+
"""REGRESSION: almalinux-10.1 was creating 'Alma Linux 10.1'
|
|
59
|
+
|
|
60
|
+
Old behavior: version = osrelease_name.split('-')[1] -> '10.1'
|
|
61
|
+
result = 'Alma Linux 10.1'
|
|
62
|
+
New behavior: major_version = '10.1'.split('.')[0] -> '10'
|
|
63
|
+
result = 'Alma Linux 10'
|
|
64
|
+
"""
|
|
65
|
+
# OLD (wrong): 'Alma Linux 10.1'
|
|
66
|
+
# NEW (correct): 'Alma Linux 10'
|
|
67
|
+
self.assertEqual(normalize_el_osrelease('almalinux-10.1'), 'Alma Linux 10')
|
|
68
|
+
self.assertNotEqual(normalize_el_osrelease('almalinux-10.1'), 'Alma Linux 10.1')
|
|
69
|
+
|
|
70
|
+
def test_regression_rocky_human_format_minor_stripped(self):
|
|
71
|
+
"""REGRESSION: 'Rocky Linux 10.1' was passed through unchanged
|
|
72
|
+
|
|
73
|
+
Old behavior: no handling, passed through as 'Rocky Linux 10.1'
|
|
74
|
+
New behavior: normalized to 'Rocky Linux 10'
|
|
75
|
+
"""
|
|
76
|
+
# OLD (wrong): 'Rocky Linux 10.1'
|
|
77
|
+
# NEW (correct): 'Rocky Linux 10'
|
|
78
|
+
self.assertEqual(normalize_el_osrelease('Rocky Linux 10.1'), 'Rocky Linux 10')
|
|
79
|
+
self.assertNotEqual(normalize_el_osrelease('Rocky Linux 10.1'), 'Rocky Linux 10.1')
|
|
80
|
+
|
|
81
|
+
def test_regression_centos_minor_stripped(self):
|
|
82
|
+
"""REGRESSION: 'CentOS 7.9' was passed through unchanged
|
|
83
|
+
|
|
84
|
+
Old behavior: no handling for human-readable format
|
|
85
|
+
New behavior: normalized to 'CentOS 7'
|
|
86
|
+
"""
|
|
87
|
+
# OLD (wrong): 'CentOS 7.9'
|
|
88
|
+
# NEW (correct): 'CentOS 7'
|
|
89
|
+
self.assertEqual(normalize_el_osrelease('CentOS 7.9'), 'CentOS 7')
|
|
90
|
+
self.assertNotEqual(normalize_el_osrelease('CentOS 7.9'), 'CentOS 7.9')
|
|
91
|
+
|
|
92
|
+
# ===========================================
|
|
93
|
+
# STANDARD TESTS - Expected behavior
|
|
94
|
+
# ===========================================
|
|
95
|
+
|
|
96
|
+
def test_rocky_linux_with_minor_version(self):
|
|
97
|
+
"""Test Rocky Linux X.Y -> Rocky Linux X"""
|
|
98
|
+
self.assertEqual(normalize_el_osrelease('Rocky Linux 10.1'), 'Rocky Linux 10')
|
|
99
|
+
self.assertEqual(normalize_el_osrelease('Rocky Linux 9.3'), 'Rocky Linux 9')
|
|
100
|
+
|
|
101
|
+
def test_rocky_linux_dash_format(self):
|
|
102
|
+
"""Test rocky-linux-X.Y -> Rocky Linux X"""
|
|
103
|
+
self.assertEqual(normalize_el_osrelease('rocky-linux-10.1'), 'Rocky Linux 10')
|
|
104
|
+
self.assertEqual(normalize_el_osrelease('rocky-linux-9.3'), 'Rocky Linux 9')
|
|
105
|
+
|
|
106
|
+
def test_alma_linux_with_minor_version(self):
|
|
107
|
+
"""Test Alma Linux X.Y -> Alma Linux X"""
|
|
108
|
+
self.assertEqual(normalize_el_osrelease('Alma Linux 10.1'), 'Alma Linux 10')
|
|
109
|
+
self.assertEqual(normalize_el_osrelease('Alma Linux 9.3'), 'Alma Linux 9')
|
|
110
|
+
|
|
111
|
+
def test_almalinux_dash_format(self):
|
|
112
|
+
"""Test almalinux-X.Y -> Alma Linux X"""
|
|
113
|
+
self.assertEqual(normalize_el_osrelease('almalinux-10.1'), 'Alma Linux 10')
|
|
114
|
+
self.assertEqual(normalize_el_osrelease('almalinux-9.3'), 'Alma Linux 9')
|
|
115
|
+
|
|
116
|
+
def test_almalinux_no_space(self):
|
|
117
|
+
"""Test AlmaLinux X.Y -> AlmaLinux X"""
|
|
118
|
+
self.assertEqual(normalize_el_osrelease('AlmaLinux 10.1'), 'Alma Linux 10')
|
|
119
|
+
|
|
120
|
+
def test_centos_with_minor_version(self):
|
|
121
|
+
"""Test CentOS X.Y -> CentOS X"""
|
|
122
|
+
self.assertEqual(normalize_el_osrelease('CentOS 7.9'), 'CentOS 7')
|
|
123
|
+
self.assertEqual(normalize_el_osrelease('CentOS 8.5'), 'CentOS 8')
|
|
124
|
+
|
|
125
|
+
def test_red_hat_enterprise_linux_with_minor_version(self):
|
|
126
|
+
"""Test Red Hat Enterprise Linux X.Y -> Red Hat Enterprise Linux X"""
|
|
127
|
+
self.assertEqual(
|
|
128
|
+
normalize_el_osrelease('Red Hat Enterprise Linux 8.2'),
|
|
129
|
+
'Red Hat Enterprise Linux 8'
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
def test_oracle_linux_with_minor_version(self):
|
|
133
|
+
"""Test Oracle Linux X.Y -> Oracle Linux X"""
|
|
134
|
+
self.assertEqual(normalize_el_osrelease('Oracle Linux 8.1'), 'Oracle Linux 8')
|
|
135
|
+
|
|
136
|
+
def test_amazon_linux_normalization(self):
|
|
137
|
+
"""Test Amazon Linux -> Amazon Linux 1"""
|
|
138
|
+
self.assertEqual(normalize_el_osrelease('Amazon Linux'), 'Amazon Linux 1')
|
|
139
|
+
self.assertEqual(normalize_el_osrelease('Amazon Linux AMI'), 'Amazon Linux 1')
|
|
140
|
+
|
|
141
|
+
# ===========================================
|
|
142
|
+
# NO-OP TESTS - Should remain unchanged
|
|
143
|
+
# ===========================================
|
|
144
|
+
|
|
145
|
+
def test_major_version_only_unchanged(self):
|
|
146
|
+
"""Test that major-version-only names are unchanged (no regression)"""
|
|
147
|
+
self.assertEqual(normalize_el_osrelease('Rocky Linux 10'), 'Rocky Linux 10')
|
|
148
|
+
self.assertEqual(normalize_el_osrelease('CentOS 7'), 'CentOS 7')
|
|
149
|
+
self.assertEqual(normalize_el_osrelease('RHEL 9'), 'RHEL 9')
|
|
150
|
+
self.assertEqual(normalize_el_osrelease('Alma Linux 9'), 'Alma Linux 9')
|
|
151
|
+
|
|
152
|
+
def test_non_el_distros_unchanged(self):
|
|
153
|
+
"""Test that non-EL distros are unchanged (no false positives)"""
|
|
154
|
+
self.assertEqual(normalize_el_osrelease('Ubuntu 22.04'), 'Ubuntu 22.04')
|
|
155
|
+
self.assertEqual(normalize_el_osrelease('Debian 12'), 'Debian 12')
|
|
156
|
+
self.assertEqual(normalize_el_osrelease('Fedora 39'), 'Fedora 39')
|
|
157
|
+
self.assertEqual(normalize_el_osrelease('Arch Linux'), 'Arch Linux')
|
|
158
|
+
self.assertEqual(normalize_el_osrelease('openSUSE Leap 15.5'), 'openSUSE Leap 15.5')
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@override_settings(
|
|
162
|
+
CELERY_TASK_ALWAYS_EAGER=True,
|
|
163
|
+
CACHES={'default': {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'}}
|
|
164
|
+
)
|
|
165
|
+
class OSReleaseMethodTests(TestCase):
|
|
166
|
+
"""Tests for OSRelease model methods."""
|
|
167
|
+
|
|
168
|
+
def test_osrelease_creation(self):
|
|
169
|
+
"""Test creating an OSRelease."""
|
|
170
|
+
release = OSRelease.objects.create(name='Ubuntu 22.04')
|
|
171
|
+
self.assertEqual(release.name, 'Ubuntu 22.04')
|
|
172
|
+
|
|
173
|
+
def test_osrelease_str(self):
|
|
174
|
+
"""Test OSRelease __str__ method."""
|
|
175
|
+
release = OSRelease.objects.create(name='Ubuntu 22.04')
|
|
176
|
+
self.assertEqual(str(release), 'Ubuntu 22.04')
|
|
177
|
+
|
|
178
|
+
def test_osrelease_get_absolute_url(self):
|
|
179
|
+
"""Test OSRelease.get_absolute_url()."""
|
|
180
|
+
release = OSRelease.objects.create(name='Rocky Linux 9')
|
|
181
|
+
url = release.get_absolute_url()
|
|
182
|
+
self.assertIn(str(release.id), url)
|
|
183
|
+
|
|
184
|
+
def test_osrelease_unique_name(self):
|
|
185
|
+
"""Test OSRelease name is unique."""
|
|
186
|
+
OSRelease.objects.create(name='Ubuntu 22.04')
|
|
187
|
+
from django.db import IntegrityError
|
|
188
|
+
with self.assertRaises(IntegrityError):
|
|
189
|
+
OSRelease.objects.create(name='Ubuntu 22.04')
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@override_settings(
|
|
193
|
+
CELERY_TASK_ALWAYS_EAGER=True,
|
|
194
|
+
CACHES={'default': {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'}}
|
|
195
|
+
)
|
|
196
|
+
class OSVariantMethodTests(TestCase):
|
|
197
|
+
"""Tests for OSVariant model methods."""
|
|
198
|
+
|
|
199
|
+
def setUp(self):
|
|
200
|
+
"""Set up test data."""
|
|
201
|
+
self.release = OSRelease.objects.create(name='Ubuntu 22.04')
|
|
202
|
+
|
|
203
|
+
def test_osvariant_creation(self):
|
|
204
|
+
"""Test creating an OSVariant."""
|
|
205
|
+
variant = OSVariant.objects.create(
|
|
206
|
+
name='Ubuntu 22.04.3 LTS',
|
|
207
|
+
osrelease=self.release,
|
|
208
|
+
)
|
|
209
|
+
self.assertEqual(variant.name, 'Ubuntu 22.04.3 LTS')
|
|
210
|
+
self.assertEqual(variant.osrelease, self.release)
|
|
211
|
+
|
|
212
|
+
def test_osvariant_str(self):
|
|
213
|
+
"""Test OSVariant __str__ method."""
|
|
214
|
+
variant = OSVariant.objects.create(
|
|
215
|
+
name='Ubuntu 22.04.3 LTS',
|
|
216
|
+
osrelease=self.release,
|
|
217
|
+
)
|
|
218
|
+
# __str__ returns 'name arch' format
|
|
219
|
+
self.assertIn('Ubuntu 22.04.3 LTS', str(variant))
|
|
220
|
+
|
|
221
|
+
def test_osvariant_get_absolute_url(self):
|
|
222
|
+
"""Test OSVariant.get_absolute_url()."""
|
|
223
|
+
variant = OSVariant.objects.create(
|
|
224
|
+
name='Ubuntu 22.04.3 LTS',
|
|
225
|
+
osrelease=self.release,
|
|
226
|
+
)
|
|
227
|
+
url = variant.get_absolute_url()
|
|
228
|
+
self.assertIn(str(variant.id), url)
|
|
229
|
+
|
|
230
|
+
def test_osvariant_unique_name(self):
|
|
231
|
+
"""Test OSVariant name is unique."""
|
|
232
|
+
OSVariant.objects.create(
|
|
233
|
+
name='Ubuntu 22.04.3 LTS',
|
|
234
|
+
osrelease=self.release,
|
|
235
|
+
)
|
|
236
|
+
from django.db import IntegrityError
|
|
237
|
+
with self.assertRaises(IntegrityError):
|
|
238
|
+
OSVariant.objects.create(
|
|
239
|
+
name='Ubuntu 22.04.3 LTS',
|
|
240
|
+
osrelease=self.release,
|
|
241
|
+
)
|
|
@@ -17,6 +17,44 @@
|
|
|
17
17
|
from django.db import IntegrityError
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
def normalize_el_osrelease(osrelease_name):
|
|
21
|
+
"""Normalize EL-based distros to major version only.
|
|
22
|
+
e.g. 'Rocky Linux 10.1' -> 'Rocky Linux 10'
|
|
23
|
+
'rocky-linux-10.1' -> 'Rocky Linux 10'
|
|
24
|
+
'almalinux-10.1' -> 'Alma Linux 10'
|
|
25
|
+
"""
|
|
26
|
+
if osrelease_name.startswith('rocky-linux-'):
|
|
27
|
+
major_version = osrelease_name.split('-')[2].split('.')[0]
|
|
28
|
+
return f'Rocky Linux {major_version}'
|
|
29
|
+
elif osrelease_name.startswith('almalinux-'):
|
|
30
|
+
major_version = osrelease_name.split('-')[1].split('.')[0]
|
|
31
|
+
return f'Alma Linux {major_version}'
|
|
32
|
+
elif osrelease_name.startswith('AlmaLinux'):
|
|
33
|
+
version_part = osrelease_name[len('AlmaLinux'):].strip()
|
|
34
|
+
major_version = version_part.split('.')[0]
|
|
35
|
+
return f'Alma Linux {major_version}'
|
|
36
|
+
elif osrelease_name.startswith('rhel-'):
|
|
37
|
+
major_version = osrelease_name.split('-')[1]
|
|
38
|
+
return f'Red Hat Enterprise Linux {major_version}'
|
|
39
|
+
elif osrelease_name in ['Amazon Linux', 'Amazon Linux AMI']:
|
|
40
|
+
return 'Amazon Linux 1'
|
|
41
|
+
|
|
42
|
+
el_distro_prefixes = [
|
|
43
|
+
'Rocky Linux',
|
|
44
|
+
'Alma Linux',
|
|
45
|
+
'CentOS',
|
|
46
|
+
'Red Hat Enterprise Linux',
|
|
47
|
+
'Oracle Linux',
|
|
48
|
+
]
|
|
49
|
+
for prefix in el_distro_prefixes:
|
|
50
|
+
if osrelease_name.startswith(prefix):
|
|
51
|
+
version_part = osrelease_name[len(prefix):].strip()
|
|
52
|
+
if '.' in version_part:
|
|
53
|
+
major_version = version_part.split('.')[0]
|
|
54
|
+
return f'{prefix} {major_version}'
|
|
55
|
+
return osrelease_name
|
|
56
|
+
|
|
57
|
+
|
|
20
58
|
def get_or_create_osrelease(name, cpe_name=None, codename=None):
|
|
21
59
|
""" Get or create OSRelease from OS details
|
|
22
60
|
"""
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
# along with Patchman. If not, see <http://www.gnu.org/licenses/>
|
|
17
17
|
|
|
18
18
|
import json
|
|
19
|
+
from urllib.parse import unquote
|
|
19
20
|
|
|
20
21
|
from django.db import models
|
|
21
22
|
from django.urls import reverse
|
|
@@ -169,7 +170,11 @@ class Report(models.Model):
|
|
|
169
170
|
|
|
170
171
|
for attr in attrs:
|
|
171
172
|
if data.get(attr):
|
|
172
|
-
|
|
173
|
+
value = data.get(attr)
|
|
174
|
+
# Decode URL-encoded kernel (e.g., %2b -> +)
|
|
175
|
+
if attr == 'kernel':
|
|
176
|
+
value = unquote(value)
|
|
177
|
+
setattr(self, attr, value)
|
|
173
178
|
else:
|
|
174
179
|
setattr(self, attr, '')
|
|
175
180
|
|