gitlabcis 1.3.2__py3-none-any.whl

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.
Files changed (218) hide show
  1. gitlabcis/__init__.py +12 -0
  2. gitlabcis/__main__.py +7 -0
  3. gitlabcis/benchmarks/__init__.py +8 -0
  4. gitlabcis/benchmarks/artifacts_4/__init__.py +4 -0
  5. gitlabcis/benchmarks/artifacts_4/access_to_artifacts_4_2.py +139 -0
  6. gitlabcis/benchmarks/artifacts_4/origin_traceability_4_4.py +11 -0
  7. gitlabcis/benchmarks/artifacts_4/package_registries_4_3.py +105 -0
  8. gitlabcis/benchmarks/artifacts_4/verification_4_1.py +83 -0
  9. gitlabcis/benchmarks/build_pipelines_2/__init__.py +4 -0
  10. gitlabcis/benchmarks/build_pipelines_2/build_environment_2_1.py +268 -0
  11. gitlabcis/benchmarks/build_pipelines_2/build_worker_2_2.py +129 -0
  12. gitlabcis/benchmarks/build_pipelines_2/pipeline_instructions_2_3.py +444 -0
  13. gitlabcis/benchmarks/build_pipelines_2/pipeline_integrity_2_4.py +146 -0
  14. gitlabcis/benchmarks/dependencies_3/__init__.py +2 -0
  15. gitlabcis/benchmarks/dependencies_3/third_party_packages_3_1.py +171 -0
  16. gitlabcis/benchmarks/dependencies_3/validate_packages_3_2.py +182 -0
  17. gitlabcis/benchmarks/deployment_5/__init__.py +2 -0
  18. gitlabcis/benchmarks/deployment_5/deployment_configuration_5_1.py +165 -0
  19. gitlabcis/benchmarks/deployment_5/deployment_environment_5_2.py +66 -0
  20. gitlabcis/benchmarks/source_code_1/__init__.py +6 -0
  21. gitlabcis/benchmarks/source_code_1/code_changes_1_1.py +665 -0
  22. gitlabcis/benchmarks/source_code_1/code_risks_1_5.py +506 -0
  23. gitlabcis/benchmarks/source_code_1/contribution_access_1_3.py +334 -0
  24. gitlabcis/benchmarks/source_code_1/repository_management_1_2.py +168 -0
  25. gitlabcis/benchmarks/source_code_1/third_party_1_4.py +139 -0
  26. gitlabcis/cli/__init__.py +0 -0
  27. gitlabcis/cli/log.py +30 -0
  28. gitlabcis/cli/main.py +541 -0
  29. gitlabcis/cli/output.py +151 -0
  30. gitlabcis/recommendations/artifacts_4/access_to_artifacts_4_2/external_auth_server.yml +51 -0
  31. gitlabcis/recommendations/artifacts_4/access_to_artifacts_4_2/limit_artifact_uploaders.yml +57 -0
  32. gitlabcis/recommendations/artifacts_4/access_to_artifacts_4_2/limit_certifying_artifacts.yml +53 -0
  33. gitlabcis/recommendations/artifacts_4/access_to_artifacts_4_2/minimum_package_registry_admins.yml +54 -0
  34. gitlabcis/recommendations/artifacts_4/access_to_artifacts_4_2/readme.md +14 -0
  35. gitlabcis/recommendations/artifacts_4/access_to_artifacts_4_2/require_mfa_to_package_registry.yml +52 -0
  36. gitlabcis/recommendations/artifacts_4/access_to_artifacts_4_2/restrict_anonymous_access.yml +67 -0
  37. gitlabcis/recommendations/artifacts_4/origin_traceability_4_4/artifact_origin_info.yml +56 -0
  38. gitlabcis/recommendations/artifacts_4/origin_traceability_4_4/readme.md +7 -0
  39. gitlabcis/recommendations/artifacts_4/package_registries_4_3/all_artifact_versions_signed.yml +70 -0
  40. gitlabcis/recommendations/artifacts_4/package_registries_4_3/audit_package_registry_config.yml +46 -0
  41. gitlabcis/recommendations/artifacts_4/package_registries_4_3/readme.md +12 -0
  42. gitlabcis/recommendations/artifacts_4/package_registries_4_3/secure_repo_webhooks.yml +50 -0
  43. gitlabcis/recommendations/artifacts_4/package_registries_4_3/validate_signed_artifacts_on_upload.yml +72 -0
  44. gitlabcis/recommendations/artifacts_4/readme.md +12 -0
  45. gitlabcis/recommendations/artifacts_4/verification_4_1/encrypt_artifacts_before_distribution.yml +47 -0
  46. gitlabcis/recommendations/artifacts_4/verification_4_1/only_authorized_platforms_can_decrypt_artifacts.yml +59 -0
  47. gitlabcis/recommendations/artifacts_4/verification_4_1/readme.md +11 -0
  48. gitlabcis/recommendations/artifacts_4/verification_4_1/sign_artifacts_in_build_pipeline.yml +40 -0
  49. gitlabcis/recommendations/build_pipelines_2/build_environment_2_1/authenticate_build_access.yml +55 -0
  50. gitlabcis/recommendations/build_pipelines_2/build_environment_2_1/build_automation.yml +54 -0
  51. gitlabcis/recommendations/build_pipelines_2/build_environment_2_1/build_env_admins.yml +55 -0
  52. gitlabcis/recommendations/build_pipelines_2/build_environment_2_1/build_logging.yml +49 -0
  53. gitlabcis/recommendations/build_pipelines_2/build_environment_2_1/disable_build_tools_default_passwords.yml +54 -0
  54. gitlabcis/recommendations/build_pipelines_2/build_environment_2_1/immutable_pipeline_infrastructure.yml +60 -0
  55. gitlabcis/recommendations/build_pipelines_2/build_environment_2_1/limit_build_access.yml +64 -0
  56. gitlabcis/recommendations/build_pipelines_2/build_environment_2_1/limit_build_secrets_scope.yml +56 -0
  57. gitlabcis/recommendations/build_pipelines_2/build_environment_2_1/readme.md +19 -0
  58. gitlabcis/recommendations/build_pipelines_2/build_environment_2_1/secure_build_env_webhooks.yml +43 -0
  59. gitlabcis/recommendations/build_pipelines_2/build_environment_2_1/single_responsibility_pipeline.yml +58 -0
  60. gitlabcis/recommendations/build_pipelines_2/build_environment_2_1/vuln_scanning.yml +64 -0
  61. gitlabcis/recommendations/build_pipelines_2/build_worker_2_2/build_worker_vuln_scanning.yml +58 -0
  62. gitlabcis/recommendations/build_pipelines_2/build_worker_2_2/monitor_worker_resource_consumption.yml +59 -0
  63. gitlabcis/recommendations/build_pipelines_2/build_worker_2_2/pass_worker_envs_and_commands.yml +48 -0
  64. gitlabcis/recommendations/build_pipelines_2/build_worker_2_2/readme.md +16 -0
  65. gitlabcis/recommendations/build_pipelines_2/build_worker_2_2/restrict_worker_connectivity.yml +61 -0
  66. gitlabcis/recommendations/build_pipelines_2/build_worker_2_2/segregate_worker_duties.yml +78 -0
  67. gitlabcis/recommendations/build_pipelines_2/build_worker_2_2/single_use_workers.yml +47 -0
  68. gitlabcis/recommendations/build_pipelines_2/build_worker_2_2/store_worker_config.yml +62 -0
  69. gitlabcis/recommendations/build_pipelines_2/build_worker_2_2/worker_runtime_security.yml +37 -0
  70. gitlabcis/recommendations/build_pipelines_2/pipeline_instructions_2_3/build_stage_io.yml +49 -0
  71. gitlabcis/recommendations/build_pipelines_2/pipeline_instructions_2_3/build_steps_as_code.yml +42 -0
  72. gitlabcis/recommendations/build_pipelines_2/pipeline_instructions_2_3/limit_pipeline_triggers.yml +76 -0
  73. gitlabcis/recommendations/build_pipelines_2/pipeline_instructions_2_3/pipeline_misconfiguration_scanning.yml +48 -0
  74. gitlabcis/recommendations/build_pipelines_2/pipeline_instructions_2_3/pipeline_secret_scanning.yml +56 -0
  75. gitlabcis/recommendations/build_pipelines_2/pipeline_instructions_2_3/pipeline_vuln_scanning.yml +44 -0
  76. gitlabcis/recommendations/build_pipelines_2/pipeline_instructions_2_3/readme.md +16 -0
  77. gitlabcis/recommendations/build_pipelines_2/pipeline_instructions_2_3/secure_pipeline_output.yml +52 -0
  78. gitlabcis/recommendations/build_pipelines_2/pipeline_instructions_2_3/track_pipeline_files.yml +48 -0
  79. gitlabcis/recommendations/build_pipelines_2/pipeline_integrity_2_4/create_reproducible_artifacts.yml +52 -0
  80. gitlabcis/recommendations/build_pipelines_2/pipeline_integrity_2_4/lock_dependencies.yml +59 -0
  81. gitlabcis/recommendations/build_pipelines_2/pipeline_integrity_2_4/pipeline_produces_sbom.yml +81 -0
  82. gitlabcis/recommendations/build_pipelines_2/pipeline_integrity_2_4/pipeline_signs_sbom.yml +38 -0
  83. gitlabcis/recommendations/build_pipelines_2/pipeline_integrity_2_4/readme.md +14 -0
  84. gitlabcis/recommendations/build_pipelines_2/pipeline_integrity_2_4/sign_artifacts.yml +35 -0
  85. gitlabcis/recommendations/build_pipelines_2/pipeline_integrity_2_4/validate_dependencies.yml +63 -0
  86. gitlabcis/recommendations/build_pipelines_2/readme.md +12 -0
  87. gitlabcis/recommendations/dependencies_3/readme.md +10 -0
  88. gitlabcis/recommendations/dependencies_3/third_party_packages_3_1/define_package_managers.yml +84 -0
  89. gitlabcis/recommendations/dependencies_3/third_party_packages_3_1/dependency_sbom.yml +84 -0
  90. gitlabcis/recommendations/dependencies_3/third_party_packages_3_1/monitor_dependencies.yml +61 -0
  91. gitlabcis/recommendations/dependencies_3/third_party_packages_3_1/packages_over_60_days_old.yml +95 -0
  92. gitlabcis/recommendations/dependencies_3/third_party_packages_3_1/pin_dependency_version.yml +48 -0
  93. gitlabcis/recommendations/dependencies_3/third_party_packages_3_1/readme.md +14 -0
  94. gitlabcis/recommendations/dependencies_3/third_party_packages_3_1/third_party_sbom_required.yml +70 -0
  95. gitlabcis/recommendations/dependencies_3/third_party_packages_3_1/verify_artifacts.yml +45 -0
  96. gitlabcis/recommendations/dependencies_3/third_party_packages_3_1/verify_signed_metadata.yml +41 -0
  97. gitlabcis/recommendations/dependencies_3/validate_packages_3_2/org_wide_dependency_policy.yml +47 -0
  98. gitlabcis/recommendations/dependencies_3/validate_packages_3_2/package_license_scanning.yml +47 -0
  99. gitlabcis/recommendations/dependencies_3/validate_packages_3_2/package_ownership_change.yml +42 -0
  100. gitlabcis/recommendations/dependencies_3/validate_packages_3_2/package_vuln_scanning.yml +62 -0
  101. gitlabcis/recommendations/dependencies_3/validate_packages_3_2/readme.md +10 -0
  102. gitlabcis/recommendations/deployment_5/deployment_configuration_5_1/audit_deployment_config.yml +46 -0
  103. gitlabcis/recommendations/deployment_5/deployment_configuration_5_1/limit_deployment_config_access.yml +51 -0
  104. gitlabcis/recommendations/deployment_5/deployment_configuration_5_1/pin_deployment_config_manifests.yml +59 -0
  105. gitlabcis/recommendations/deployment_5/deployment_configuration_5_1/readme.md +13 -0
  106. gitlabcis/recommendations/deployment_5/deployment_configuration_5_1/scan_iac.yml +72 -0
  107. gitlabcis/recommendations/deployment_5/deployment_configuration_5_1/secret_scan_deployment_config.yml +45 -0
  108. gitlabcis/recommendations/deployment_5/deployment_configuration_5_1/separate_deployment_config.yml +50 -0
  109. gitlabcis/recommendations/deployment_5/deployment_configuration_5_1/verify_deployment_config.yml +49 -0
  110. gitlabcis/recommendations/deployment_5/deployment_environment_5_2/automate_deployment.yml +47 -0
  111. gitlabcis/recommendations/deployment_5/deployment_environment_5_2/disable_default_passwords.yml +63 -0
  112. gitlabcis/recommendations/deployment_5/deployment_environment_5_2/limit_prod_access.yml +45 -0
  113. gitlabcis/recommendations/deployment_5/deployment_environment_5_2/readme.md +12 -0
  114. gitlabcis/recommendations/deployment_5/deployment_environment_5_2/reproducible_deployment.yml +50 -0
  115. gitlabcis/recommendations/deployment_5/readme.md +10 -0
  116. gitlabcis/recommendations/readme.md +24 -0
  117. gitlabcis/recommendations/source_code_1/code_changes_1_1/audit_branch_protections.yml +56 -0
  118. gitlabcis/recommendations/source_code_1/code_changes_1_1/auto_risk_scan_merges.yml +62 -0
  119. gitlabcis/recommendations/source_code_1/code_changes_1_1/branch_protections_for_admins.yml +60 -0
  120. gitlabcis/recommendations/source_code_1/code_changes_1_1/branches_updated_before_merging.yml +56 -0
  121. gitlabcis/recommendations/source_code_1/code_changes_1_1/checks_pass_before_merging.yml +57 -0
  122. gitlabcis/recommendations/source_code_1/code_changes_1_1/code_approval_dismissals.yml +62 -0
  123. gitlabcis/recommendations/source_code_1/code_changes_1_1/code_approvals.yml +65 -0
  124. gitlabcis/recommendations/source_code_1/code_changes_1_1/code_changes_require_code_owners.yml +68 -0
  125. gitlabcis/recommendations/source_code_1/code_changes_1_1/code_dismissal_restrictions.yml +69 -0
  126. gitlabcis/recommendations/source_code_1/code_changes_1_1/code_owners.yml +61 -0
  127. gitlabcis/recommendations/source_code_1/code_changes_1_1/code_tracing.yml +52 -0
  128. gitlabcis/recommendations/source_code_1/code_changes_1_1/comments_resolved_before_merging.yml +59 -0
  129. gitlabcis/recommendations/source_code_1/code_changes_1_1/commits_must_be_signed_before_merging.yml +63 -0
  130. gitlabcis/recommendations/source_code_1/code_changes_1_1/default_branch_protected.yml +85 -0
  131. gitlabcis/recommendations/source_code_1/code_changes_1_1/deny_branch_deletions.yml +76 -0
  132. gitlabcis/recommendations/source_code_1/code_changes_1_1/ensure_force_push_is_denied.yml +59 -0
  133. gitlabcis/recommendations/source_code_1/code_changes_1_1/linear_history_required.yml +56 -0
  134. gitlabcis/recommendations/source_code_1/code_changes_1_1/merging_restrictions.yml +65 -0
  135. gitlabcis/recommendations/source_code_1/code_changes_1_1/readme.md +26 -0
  136. gitlabcis/recommendations/source_code_1/code_changes_1_1/stale_branch_reviews.yml +72 -0
  137. gitlabcis/recommendations/source_code_1/code_changes_1_1/version_control.yml +45 -0
  138. gitlabcis/recommendations/source_code_1/code_risks_1_5/dast_api_scanning.yml +50 -0
  139. gitlabcis/recommendations/source_code_1/code_risks_1_5/dast_web_scanning.yml +51 -0
  140. gitlabcis/recommendations/source_code_1/code_risks_1_5/dependency_scanning.yml +84 -0
  141. gitlabcis/recommendations/source_code_1/code_risks_1_5/enable_secret_detection.yml +45 -0
  142. gitlabcis/recommendations/source_code_1/code_risks_1_5/license_scanning.yml +47 -0
  143. gitlabcis/recommendations/source_code_1/code_risks_1_5/readme.md +14 -0
  144. gitlabcis/recommendations/source_code_1/code_risks_1_5/secure_iac_instructions.yml +81 -0
  145. gitlabcis/recommendations/source_code_1/code_risks_1_5/secure_pipeline_instructions.yml +62 -0
  146. gitlabcis/recommendations/source_code_1/code_risks_1_5/vulnerability_scanning.yml +48 -0
  147. gitlabcis/recommendations/source_code_1/contribution_access_1_3/domain_verification.yml +65 -0
  148. gitlabcis/recommendations/source_code_1/contribution_access_1_3/ensure_2_admins_per_repo.yml +56 -0
  149. gitlabcis/recommendations/source_code_1/contribution_access_1_3/limit_top_level_group_creation.yml +61 -0
  150. gitlabcis/recommendations/source_code_1/contribution_access_1_3/limit_user_registration_domain.yml +58 -0
  151. gitlabcis/recommendations/source_code_1/contribution_access_1_3/minimum_number_of_admins.yml +56 -0
  152. gitlabcis/recommendations/source_code_1/contribution_access_1_3/org_provided_ssh_certs.yml +70 -0
  153. gitlabcis/recommendations/source_code_1/contribution_access_1_3/readme.md +21 -0
  154. gitlabcis/recommendations/source_code_1/contribution_access_1_3/require_mfa_at_org_level.yml +89 -0
  155. gitlabcis/recommendations/source_code_1/contribution_access_1_3/require_mfa_for_contributors.yml +76 -0
  156. gitlabcis/recommendations/source_code_1/contribution_access_1_3/restrict_ip_addresses.yml +84 -0
  157. gitlabcis/recommendations/source_code_1/contribution_access_1_3/review_and_remove_inactive_users.yml +62 -0
  158. gitlabcis/recommendations/source_code_1/contribution_access_1_3/scm_notification_restriction.yml +46 -0
  159. gitlabcis/recommendations/source_code_1/contribution_access_1_3/strict_permissions_for_repo.yml +62 -0
  160. gitlabcis/recommendations/source_code_1/contribution_access_1_3/track_code_anomalies.yml +43 -0
  161. gitlabcis/recommendations/source_code_1/readme.md +13 -0
  162. gitlabcis/recommendations/source_code_1/repository_management_1_2/limit_issue_deletions.yml +57 -0
  163. gitlabcis/recommendations/source_code_1/repository_management_1_2/limit_repo_creations.yml +64 -0
  164. gitlabcis/recommendations/source_code_1/repository_management_1_2/limit_repo_deletions.yml +57 -0
  165. gitlabcis/recommendations/source_code_1/repository_management_1_2/public_repos_have_security_file.yml +59 -0
  166. gitlabcis/recommendations/source_code_1/repository_management_1_2/readme.md +15 -0
  167. gitlabcis/recommendations/source_code_1/repository_management_1_2/review_and_archive_stale_repos.yml +65 -0
  168. gitlabcis/recommendations/source_code_1/repository_management_1_2/track_forks.yml +74 -0
  169. gitlabcis/recommendations/source_code_1/repository_management_1_2/track_project_visibility_status.yml +74 -0
  170. gitlabcis/recommendations/source_code_1/third_party_1_4/README.md +12 -0
  171. gitlabcis/recommendations/source_code_1/third_party_1_4/admin_approval_for_app_installs.yml +83 -0
  172. gitlabcis/recommendations/source_code_1/third_party_1_4/least_privilge_app_permissions.yml +103 -0
  173. gitlabcis/recommendations/source_code_1/third_party_1_4/secure_webhooks.yml +73 -0
  174. gitlabcis/recommendations/source_code_1/third_party_1_4/stale_app_reviews.yml +66 -0
  175. gitlabcis/recommendations/template.yml +30 -0
  176. gitlabcis/tests/__init__.py +0 -0
  177. gitlabcis/tests/input/__init__.py +0 -0
  178. gitlabcis/tests/input/conftest.py +29 -0
  179. gitlabcis/tests/input/no_input_test.py +82 -0
  180. gitlabcis/tests/input/switch_test.py +19 -0
  181. gitlabcis/tests/input/version_test.py +7 -0
  182. gitlabcis/tests/unit/__init__.py +0 -0
  183. gitlabcis/tests/unit/benchmarks/artifacts_4/access_to_artifacts_4_2_test.py +131 -0
  184. gitlabcis/tests/unit/benchmarks/artifacts_4/origin_traceability_4_4_test.py +15 -0
  185. gitlabcis/tests/unit/benchmarks/artifacts_4/package_registries_4_3_test.py +102 -0
  186. gitlabcis/tests/unit/benchmarks/artifacts_4/verification_4_1_test.py +78 -0
  187. gitlabcis/tests/unit/benchmarks/build_pipelines_2/build_environment_2_1_test.py +239 -0
  188. gitlabcis/tests/unit/benchmarks/build_pipelines_2/build_worker_2_2_test.py +105 -0
  189. gitlabcis/tests/unit/benchmarks/build_pipelines_2/pipeline_instructions_2_3_test.py +340 -0
  190. gitlabcis/tests/unit/benchmarks/build_pipelines_2/pipeline_integrity_2_4_test.py +115 -0
  191. gitlabcis/tests/unit/benchmarks/conftest.py +47 -0
  192. gitlabcis/tests/unit/benchmarks/dependencies_3/third_party_packages_3_1_test.py +135 -0
  193. gitlabcis/tests/unit/benchmarks/dependencies_3/validate_packages_3_2_test.py +171 -0
  194. gitlabcis/tests/unit/benchmarks/deployment_5/deployment_configuration_5_1_test.py +140 -0
  195. gitlabcis/tests/unit/benchmarks/deployment_5/deployment_environment_5_2_test.py +60 -0
  196. gitlabcis/tests/unit/benchmarks/function_test.py +24 -0
  197. gitlabcis/tests/unit/benchmarks/source_code_1/code_changes_1_1_test.py +565 -0
  198. gitlabcis/tests/unit/benchmarks/source_code_1/code_risks_1_5_test.py +419 -0
  199. gitlabcis/tests/unit/benchmarks/source_code_1/contribution_access_1_3_test.py +265 -0
  200. gitlabcis/tests/unit/benchmarks/source_code_1/repository_management_1_2_test.py +142 -0
  201. gitlabcis/tests/unit/benchmarks/source_code_1/third_party_1_4_test.py +119 -0
  202. gitlabcis/tests/unit/conftest.py +94 -0
  203. gitlabcis/tests/unit/log/log_test.py +23 -0
  204. gitlabcis/tests/unit/utils/argfilters_test.py +9 -0
  205. gitlabcis/tests/unit/utils/ci_test.py +156 -0
  206. gitlabcis/tests/unit/utils/output_test.py +95 -0
  207. gitlabcis/tests/unit/utils/utils_general_test.py +149 -0
  208. gitlabcis/tests/unit/utils/version_test.py +11 -0
  209. gitlabcis/tests/unit/yaml/bad_file_test.py +15 -0
  210. gitlabcis/tests/unit/yaml/recommendation_test.py +123 -0
  211. gitlabcis/utils/__init__.py +146 -0
  212. gitlabcis/utils/ci.py +132 -0
  213. gitlabcis-1.3.2.dist-info/LICENSE +21 -0
  214. gitlabcis-1.3.2.dist-info/METADATA +241 -0
  215. gitlabcis-1.3.2.dist-info/RECORD +218 -0
  216. gitlabcis-1.3.2.dist-info/WHEEL +5 -0
  217. gitlabcis-1.3.2.dist-info/entry_points.txt +2 -0
  218. gitlabcis-1.3.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,149 @@
1
+ # -----------------------------------------------------------------------------
2
+
3
+ from collections import namedtuple
4
+ from unittest.mock import Mock, mock_open, patch
5
+
6
+ import pytest
7
+ import yaml
8
+
9
+ from gitlabcis.utils import (countRecommendations, mapRecommendations,
10
+ readRecommendations, readYaml)
11
+
12
+ # -----------------------------------------------------------------------------
13
+
14
+
15
+ def test_count_recommendations():
16
+ assert countRecommendations() == 123
17
+
18
+ # -----------------------------------------------------------------------------
19
+
20
+
21
+ def test_read_recommendations():
22
+ # options:
23
+ ArgFilters = namedtuple(
24
+ 'ArgFilters', ['profile', 'recommendation_ids', 'cis_controls',
25
+ 'implementation_groups', 'skip_recommendation_ids'])
26
+
27
+ # controls:
28
+ control1 = {
29
+ 'id': '1.1.1', 'profile': '1',
30
+ 'cis_controls': [{'id': '2.4', 'implementation_groups': 'IG2'}]}
31
+
32
+ control2 = {
33
+ 'id': '1.1.12', 'profile': '2',
34
+ 'cis_controls': [{'id': '16.1', 'implementation_groups': 'IG3'}]}
35
+
36
+ # run:
37
+ @patch('gitlabcis.utils.Path')
38
+ @patch('gitlabcis.utils.readYaml')
39
+ def run_test(mock_read_yaml, mock_path, arg_filters=None,
40
+ expected_result=None, expect_exit=False):
41
+ mock_path.return_value.exists.return_value = True
42
+ mock_path.return_value.rglob.return_value = ['file1.yml', 'file2.yml']
43
+ mock_read_yaml.side_effect = [control1, control2]
44
+
45
+ if expect_exit:
46
+ mock_path.return_value.exists.return_value = False
47
+ with pytest.raises(SystemExit):
48
+ readRecommendations(arg_filters)
49
+ else:
50
+ result = readRecommendations(arg_filters)
51
+ assert result == expected_result
52
+
53
+ # Test no filters
54
+ run_test(expected_result=[control1, control2])
55
+
56
+ # Test profile filter
57
+ run_test(
58
+ arg_filters=ArgFilters(profile='1', recommendation_ids=None,
59
+ cis_controls=None, implementation_groups=None,
60
+ skip_recommendation_ids=None),
61
+ expected_result=[control1]
62
+ )
63
+
64
+ # Test recommendation_ids filter
65
+ run_test(
66
+ arg_filters=ArgFilters(profile=None, recommendation_ids=['1.1.12'],
67
+ cis_controls=None, implementation_groups=None,
68
+ skip_recommendation_ids=None),
69
+ expected_result=[control2]
70
+ )
71
+
72
+ # Test skip_recommendation_ids filter
73
+ run_test(
74
+ arg_filters=ArgFilters(profile=None, recommendation_ids=None,
75
+ cis_controls=None, implementation_groups=None,
76
+ skip_recommendation_ids=['1.1.12']),
77
+ expected_result=[control1]
78
+ )
79
+
80
+ # Test cis_controls filter
81
+ run_test(
82
+ arg_filters=ArgFilters(profile=None, recommendation_ids=None,
83
+ cis_controls=['16.1'],
84
+ implementation_groups=None,
85
+ skip_recommendation_ids=None),
86
+ expected_result=[control2]
87
+ )
88
+
89
+ # Test implementation_groups filter
90
+ run_test(
91
+ arg_filters=ArgFilters(profile=None, recommendation_ids=None,
92
+ cis_controls=None,
93
+ implementation_groups=['IG2'],
94
+ skip_recommendation_ids=None),
95
+ expected_result=[control1]
96
+ )
97
+
98
+ # Test directory not found
99
+ run_test(expect_exit=True)
100
+
101
+ # -----------------------------------------------------------------------------
102
+
103
+
104
+ def test_read_yaml():
105
+ example_input = "key1: value1\nkey2: value2"
106
+ example_output = {'key1': 'value1', 'key2': 'value2'}
107
+
108
+ # Test successful read
109
+ with patch('builtins.open', new_callable=mock_open,
110
+ read_data=example_input):
111
+ result = readYaml('dummy_path.yml')
112
+ assert result == example_output
113
+
114
+ # Test file not found
115
+ with patch('builtins.open', side_effect=FileNotFoundError), \
116
+ patch('sys.exit') as mock_exit, \
117
+ pytest.raises(SystemExit):
118
+ readYaml('non_existent_file.yml')
119
+ mock_exit.assert_called_once_with(1)
120
+
121
+ # Test logging
122
+ with patch('builtins.open', new_callable=mock_open,
123
+ read_data=example_input), \
124
+ patch('logging.debug') as mock_debug:
125
+ readYaml('test_file.yml')
126
+ mock_debug.assert_called_once_with('Opening: test_file.yml')
127
+
128
+ # Test invalid YAML content
129
+ with patch('builtins.open', new_callable=mock_open,
130
+ read_data="invalid: yaml: content"), \
131
+ pytest.raises(yaml.YAMLError):
132
+ readYaml('invalid_yaml.yml')
133
+
134
+ # -----------------------------------------------------------------------------
135
+
136
+
137
+ def test_map_recommendations():
138
+ functionList = [
139
+ Mock(__name__='func1'),
140
+ Mock(__name__='func2'),
141
+ Mock(__name__='func3')
142
+ ]
143
+
144
+ recommendations = [{'name': 'func1'}, {'name': 'func2'},
145
+ {'name': 'func3'}]
146
+
147
+ assert len(
148
+ mapRecommendations(functionList, recommendations)
149
+ ) == 3
@@ -0,0 +1,11 @@
1
+ # -----------------------------------------------------------------------------
2
+
3
+ from gitlabcis import __version__
4
+
5
+ import re
6
+
7
+ # -----------------------------------------------------------------------------
8
+
9
+
10
+ def test_gitlabcis_version():
11
+ assert re.match(r'^\d+\.\d+\.\d+$', __version__) is not None
@@ -0,0 +1,15 @@
1
+ # -----------------------------------------------------------------------------
2
+
3
+ import pytest
4
+
5
+ # -----------------------------------------------------------------------------
6
+
7
+
8
+ def test_bad_file(capsys):
9
+
10
+ with pytest.raises(SystemExit) as execCtx:
11
+ from gitlabcis.utils import readYaml # noqa: F401
12
+
13
+ readYaml('non-existant.yml')
14
+
15
+ assert execCtx.value.code == 1
@@ -0,0 +1,123 @@
1
+ # -----------------------------------------------------------------------------
2
+
3
+
4
+ def test_valid_yaml_syntax(recommendations):
5
+ """
6
+ This test ensures that all the required keys are present in the yaml
7
+ recommendation
8
+ """
9
+
10
+ # the required keys of the yaml file:
11
+ requiredKeys = [
12
+ 'id', 'name', 'title', 'profile', 'category', 'sub_category',
13
+ 'description', 'rationale', 'impact', 'audit', 'remediation',
14
+ 'default_value', 'references', 'cis_controls', 'additional_info'
15
+ ]
16
+
17
+ requiredKeys.sort()
18
+
19
+ for yamlName, yamlData in recommendations.items():
20
+ print(f' - Testing: {yamlData.get("id")} - {yamlName}') # noqa: E221,E501
21
+
22
+ yamlKeys = list(yamlData.keys())
23
+ yamlKeys.sort()
24
+
25
+ assert yamlKeys == requiredKeys
26
+
27
+ # -----------------------------------------------------------------------------
28
+
29
+
30
+ def test_categories(recommendationDirs, recommendations):
31
+ """
32
+ This test ensures that the category inside the yaml recommendation
33
+ matches those of the directories under /recommendations.
34
+ """
35
+
36
+ for yamlName, yamlData in recommendations.items():
37
+ print(f' - Testing: {yamlData.get("id")} - {yamlName}') # noqa: E221,E501
38
+
39
+ _expectedPath = (
40
+ f'{yamlData.get("category")}_{yamlData.get("id").split(".")[0]}'
41
+ )
42
+ assert _expectedPath in recommendationDirs
43
+
44
+ # -----------------------------------------------------------------------------
45
+
46
+
47
+ def test_sub_categories(recommendationDirs, recommendations):
48
+ """
49
+ This test ensures that the sub_category match those of the directories
50
+ under /recommendations.
51
+ """
52
+
53
+ for yamlName, yamlData in recommendations.items():
54
+ print(f' - Testing: {yamlData.get("id")} - {yamlName}') # noqa: E221,E501
55
+
56
+ _idSplit = yamlData.get('id').split('.')
57
+ _expectedPath = (
58
+ f'{yamlData.get("sub_category")}_{_idSplit[0]}_{_idSplit[1]}'
59
+ )
60
+
61
+ assert _expectedPath in recommendationDirs
62
+
63
+ # -----------------------------------------------------------------------------
64
+
65
+
66
+ def test_duplicate_ids(recommendations):
67
+ """
68
+ This test ensures that there are no duplicate IDs in the yaml files.
69
+ """
70
+
71
+ _ids = []
72
+
73
+ for yamlName, yamlData in recommendations.items():
74
+
75
+ print(f' - Testing: {yamlData.get("id")} - {yamlName}') # noqa: E221,E501
76
+
77
+ if yamlData['id'] in _ids:
78
+ raise AssertionError(
79
+ f'Duplicate ID: {yamlData["id"]} found in file: {yamlName}'
80
+ )
81
+ _ids.append(yamlData['id'])
82
+
83
+ # -----------------------------------------------------------------------------
84
+
85
+
86
+ def test_duplicate_names(recommendations):
87
+ """
88
+ This test ensures that there are no duplicate names in the yaml files.
89
+ """
90
+
91
+ _names = []
92
+
93
+ for yamlName, _yamlData in recommendations.items():
94
+ if yamlName in _names:
95
+ raise AssertionError(
96
+ f'Duplicate Name: {yamlName} found in {yamlName}')
97
+ _names.append(yamlName)
98
+
99
+ # -----------------------------------------------------------------------------
100
+
101
+
102
+ def test_template_values(recommendations):
103
+ """
104
+ This test ensures that there are no template values still in the file.
105
+ """
106
+
107
+ for yamlName, yamlData in recommendations.items():
108
+
109
+ _checks = []
110
+
111
+ print(f' - Testing: {yamlData.get("id")} - {yamlName}') # noqa: E221,E501
112
+
113
+ for key, value in yamlData.items():
114
+
115
+ # for list items:
116
+ if key == 'references' and value:
117
+ _checks.append('example' not in [v for v in value])
118
+
119
+ # for single value items:
120
+ else:
121
+ _checks.append(value != 'example')
122
+
123
+ assert False not in _checks
@@ -0,0 +1,146 @@
1
+ # -------------------------------------------------------------------------
2
+
3
+ import logging
4
+ from pathlib import Path
5
+ from sys import exit
6
+
7
+ import yaml
8
+
9
+ from . import ci # noqa: F401
10
+
11
+ # -----------------------------------------------------------------------------
12
+ # count recommendations:
13
+ # -----------------------------------------------------------------------------
14
+
15
+
16
+ def countRecommendations():
17
+ return len(list(
18
+ Path(f'{Path(__file__).parent.parent}/recommendations').rglob('*.yml')
19
+ ))
20
+
21
+ # -----------------------------------------------------------------------------
22
+ # Load the recommendations:
23
+ # -----------------------------------------------------------------------------
24
+
25
+
26
+ def readRecommendations(argFilters=None):
27
+
28
+ _rDir = Path(f'{Path(__file__).parent.parent}/recommendations')
29
+
30
+ logging.debug(f'recommendations directory: {_rDir}')
31
+
32
+ if not _rDir.exists():
33
+ print(
34
+ f'Error: Recommendations directory {_rDir} was not found'
35
+ )
36
+ exit(1)
37
+
38
+ # -------------------------------------------------------------------------
39
+ # Gather all recommendations:
40
+ # -------------------------------------------------------------------------
41
+
42
+ recommendationFiles = list(_rDir.rglob('*.yml'))
43
+
44
+ if argFilters is None:
45
+
46
+ recommendations = [readYaml(r) for r in recommendationFiles]
47
+ logging.debug(f'Loaded: {len(recommendations)} recommendations')
48
+
49
+ return recommendations
50
+
51
+ # -------------------------------------------------------------------------
52
+ # Filter recommendations based on user input:
53
+ # -------------------------------------------------------------------------
54
+
55
+ logging.debug(f'Filtering with args: {argFilters}')
56
+
57
+ recommendations = []
58
+
59
+ for r in recommendationFiles:
60
+
61
+ yamlData = readYaml(r)
62
+
63
+ cisControls = yamlData.get('cis_controls', [])
64
+
65
+ # ---------------------------------------------------------------------
66
+
67
+ # if a profile was provided and it doesn't match in the recommendation:
68
+ if argFilters.profile:
69
+ if argFilters.profile != yamlData.get('profile'):
70
+ continue
71
+
72
+ # if a recommendation id was provided but it doesn't match:
73
+ if argFilters.recommendation_ids:
74
+ if yamlData.get('id') not in argFilters.recommendation_ids:
75
+ continue
76
+
77
+ # if a user wishes to skip certain recommendation id's:
78
+ if argFilters.skip_recommendation_ids:
79
+ if yamlData.get('id') in argFilters.skip_recommendation_ids:
80
+ continue
81
+
82
+ # ---------------------------------------------------------------------
83
+ # Iterate through the CIS Controls:
84
+ # ---------------------------------------------------------------------
85
+
86
+ # if a CIS Control ID was provided and it's not found:
87
+ if argFilters.cis_controls:
88
+ _cisIds = set([cis.get('id') for cis in cisControls])
89
+
90
+ if not set(argFilters.cis_controls).intersection(_cisIds):
91
+ continue
92
+
93
+ # if an implementation group was provided and it's not found:
94
+ if argFilters.implementation_groups:
95
+
96
+ if not set(argFilters.implementation_groups).intersection(
97
+ set([
98
+ control.get('implementation_groups')
99
+ for control in cisControls
100
+ ])):
101
+ continue
102
+
103
+ # for everything else, append it, this allows a user to pass no
104
+ # args and they return all the recommendations...
105
+ recommendations.append(yamlData)
106
+
107
+ return recommendations
108
+
109
+ # -----------------------------------------------------------------------------
110
+ # Read a recommendation file:
111
+ # -----------------------------------------------------------------------------
112
+
113
+
114
+ def readYaml(recommendation):
115
+ """
116
+ Desc: This function takes a str path to a yaml file, reads it and
117
+ returns it.
118
+ """
119
+
120
+ logging.debug(f'Opening: {recommendation}')
121
+
122
+ try:
123
+ with open(recommendation, 'r') as f:
124
+ return yaml.safe_load(f)
125
+ except FileNotFoundError:
126
+ print(f'Error: Failed to find recommendation: {recommendation}')
127
+ exit(1)
128
+
129
+ # -----------------------------------------------------------------------------
130
+ # Map functions to recommendations:
131
+ # -----------------------------------------------------------------------------
132
+
133
+
134
+ def mapRecommendations(functionList, recommendationList):
135
+ """
136
+ Desc: This function maps a function to its relevant recommendation
137
+ It accepts a list of functions, and a list of recommendation dicts.
138
+ """
139
+
140
+ mappedFuncs = {func.__name__: func for func in functionList}
141
+
142
+ return {
143
+ mappedFuncs[recommendation['name']]: recommendation
144
+ for recommendation in recommendationList
145
+ if recommendation['name'] in mappedFuncs
146
+ }
gitlabcis/utils/ci.py ADDED
@@ -0,0 +1,132 @@
1
+ # -------------------------------------------------------------------------
2
+
3
+
4
+ def getConfig(glEntity, glObject, **kwargs):
5
+ """
6
+ This function attempts to obtain the .gitlab-ci.yml file.
7
+
8
+ The filepath can be:
9
+ - Remote: e.g. https://example.com/.gitlab-ci.yml
10
+ - External: e.g. rel/path/.gitlab-ci.yml@my/project:refName
11
+ - Local: e.g. custom/local/repo/path/.gitlab-ci.yml
12
+
13
+ Ref: https://docs.gitlab.com/ee/ci/pipelines/settings.html#specify-a-custom-cicd-configuration-file # noqa: E501
14
+
15
+ Because remote links can potentially include any type of file, they are
16
+ not supported.
17
+
18
+ The function returns a dict in the similar manner as the benchmarks:
19
+
20
+ Examples:
21
+ - SKIP: {None: 'reason'}
22
+ - FAIL: {False: 'reason'}
23
+ - PASS: {ProjectFile, None}
24
+ """
25
+
26
+ import logging
27
+ import re
28
+
29
+ from gitlab.exceptions import (GitlabAuthenticationError, GitlabGetError,
30
+ GitlabHttpError)
31
+
32
+ try:
33
+ # set the default path if one is not set:
34
+ gitlabCiYamlPath = (
35
+ str(glEntity.ci_config_path)
36
+ if glEntity.ci_config_path != ''
37
+ else '.gitlab-ci.yml'
38
+ )
39
+
40
+ # SKIP remote CI files:
41
+ if '://' in gitlabCiYamlPath:
42
+ return {None: f'Remote CI file: {gitlabCiYamlPath} not supported'}
43
+
44
+ # handle different project locations:
45
+ remote = re.match(
46
+ r'(?P<filePath>.*)@(?P<namespace>.*):?(?P<refName>.*)',
47
+ gitlabCiYamlPath)
48
+
49
+ # define the location:
50
+ if remote is not None:
51
+ _tempEntity = glObject.projects.get(
52
+ remote.group('namespace')
53
+ )
54
+ _tempRef = remote.group('refName')
55
+ _tempPath = remote.group('filePath')
56
+ else:
57
+ _tempEntity = glEntity
58
+ _tempRef = glEntity.default_branch
59
+ _tempPath = gitlabCiYamlPath
60
+
61
+ # obtain the CI config file:
62
+ gitlabCiYaml = None
63
+ try:
64
+ gitlabCiYaml = _tempEntity.files.get(
65
+ file_path=_tempPath,
66
+ ref=_tempRef
67
+ )
68
+ except (GitlabHttpError, GitlabGetError) as e:
69
+ if e.response_code == 404:
70
+ logging.debug(f'No CI config file found at: {_tempPath}')
71
+ return {False: f'Pipeline config file not found: {_tempPath}'}
72
+
73
+ # return a dict with a ProjectFile class var as the key
74
+ # to differentiate results or not:
75
+ if gitlabCiYaml is not None:
76
+ return {gitlabCiYaml: None}
77
+
78
+ return {False: f'Pipeline config file not found: {_tempPath}'}
79
+
80
+ except (GitlabHttpError, GitlabGetError, GitlabAuthenticationError):
81
+ return {None: 'Insufficient permissions'}
82
+
83
+ # this is for pytest to run unauthed tests:
84
+ except AttributeError:
85
+ return {None: 'Insufficient permissions'}
86
+
87
+ # -------------------------------------------------------------------------
88
+
89
+
90
+ def searchConfig(glEntity, glObject, searchString, **kwargs):
91
+ """
92
+ This function allows to search for a string inside the .gitlab-ci.yml file
93
+
94
+ It uses the above getConfig() function to obtain it, then checks the str
95
+ based on its lowercase value.
96
+ """
97
+
98
+ import base64
99
+
100
+ from gitlab.exceptions import (GitlabAuthenticationError, GitlabGetError,
101
+ GitlabHttpError)
102
+ from gitlab.v4.objects.files import ProjectFile
103
+
104
+ try:
105
+ # obtain the ci config file:
106
+ gitlabCiYaml = getConfig(
107
+ glEntity, glObject, **kwargs)
108
+
109
+ # obtain the result:
110
+ try:
111
+ _result = next(iter(gitlabCiYaml))
112
+
113
+ # a SKIP was identified:
114
+ except TypeError:
115
+ # ensure the reason is returned:
116
+ return {None: gitlabCiYaml[None]}
117
+
118
+ # ensure we actually found the file:
119
+ if not isinstance(_result, ProjectFile):
120
+ return {_result: gitlabCiYaml[_result]}
121
+
122
+ yamlContents = base64.b64decode(
123
+ _result.content).decode('utf-8')
124
+
125
+ if searchString.lower() in yamlContents.lower():
126
+ return {True: f'{searchString} was found in CI config file'}
127
+ else:
128
+ return {False: f'{searchString} was not found in CI config file'} # noqa: E713, E501
129
+
130
+ except (GitlabHttpError, GitlabGetError, GitlabAuthenticationError) as e:
131
+ if e.response_code in [401, 403]:
132
+ return {None: 'Insufficient permissions'}
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 GitLab
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.