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.
- gitlabcis/__init__.py +12 -0
- gitlabcis/__main__.py +7 -0
- gitlabcis/benchmarks/__init__.py +8 -0
- gitlabcis/benchmarks/artifacts_4/__init__.py +4 -0
- gitlabcis/benchmarks/artifacts_4/access_to_artifacts_4_2.py +139 -0
- gitlabcis/benchmarks/artifacts_4/origin_traceability_4_4.py +11 -0
- gitlabcis/benchmarks/artifacts_4/package_registries_4_3.py +105 -0
- gitlabcis/benchmarks/artifacts_4/verification_4_1.py +83 -0
- gitlabcis/benchmarks/build_pipelines_2/__init__.py +4 -0
- gitlabcis/benchmarks/build_pipelines_2/build_environment_2_1.py +268 -0
- gitlabcis/benchmarks/build_pipelines_2/build_worker_2_2.py +129 -0
- gitlabcis/benchmarks/build_pipelines_2/pipeline_instructions_2_3.py +444 -0
- gitlabcis/benchmarks/build_pipelines_2/pipeline_integrity_2_4.py +146 -0
- gitlabcis/benchmarks/dependencies_3/__init__.py +2 -0
- gitlabcis/benchmarks/dependencies_3/third_party_packages_3_1.py +171 -0
- gitlabcis/benchmarks/dependencies_3/validate_packages_3_2.py +182 -0
- gitlabcis/benchmarks/deployment_5/__init__.py +2 -0
- gitlabcis/benchmarks/deployment_5/deployment_configuration_5_1.py +165 -0
- gitlabcis/benchmarks/deployment_5/deployment_environment_5_2.py +66 -0
- gitlabcis/benchmarks/source_code_1/__init__.py +6 -0
- gitlabcis/benchmarks/source_code_1/code_changes_1_1.py +665 -0
- gitlabcis/benchmarks/source_code_1/code_risks_1_5.py +506 -0
- gitlabcis/benchmarks/source_code_1/contribution_access_1_3.py +334 -0
- gitlabcis/benchmarks/source_code_1/repository_management_1_2.py +168 -0
- gitlabcis/benchmarks/source_code_1/third_party_1_4.py +139 -0
- gitlabcis/cli/__init__.py +0 -0
- gitlabcis/cli/log.py +30 -0
- gitlabcis/cli/main.py +541 -0
- gitlabcis/cli/output.py +151 -0
- gitlabcis/recommendations/artifacts_4/access_to_artifacts_4_2/external_auth_server.yml +51 -0
- gitlabcis/recommendations/artifacts_4/access_to_artifacts_4_2/limit_artifact_uploaders.yml +57 -0
- gitlabcis/recommendations/artifacts_4/access_to_artifacts_4_2/limit_certifying_artifacts.yml +53 -0
- gitlabcis/recommendations/artifacts_4/access_to_artifacts_4_2/minimum_package_registry_admins.yml +54 -0
- gitlabcis/recommendations/artifacts_4/access_to_artifacts_4_2/readme.md +14 -0
- gitlabcis/recommendations/artifacts_4/access_to_artifacts_4_2/require_mfa_to_package_registry.yml +52 -0
- gitlabcis/recommendations/artifacts_4/access_to_artifacts_4_2/restrict_anonymous_access.yml +67 -0
- gitlabcis/recommendations/artifacts_4/origin_traceability_4_4/artifact_origin_info.yml +56 -0
- gitlabcis/recommendations/artifacts_4/origin_traceability_4_4/readme.md +7 -0
- gitlabcis/recommendations/artifacts_4/package_registries_4_3/all_artifact_versions_signed.yml +70 -0
- gitlabcis/recommendations/artifacts_4/package_registries_4_3/audit_package_registry_config.yml +46 -0
- gitlabcis/recommendations/artifacts_4/package_registries_4_3/readme.md +12 -0
- gitlabcis/recommendations/artifacts_4/package_registries_4_3/secure_repo_webhooks.yml +50 -0
- gitlabcis/recommendations/artifacts_4/package_registries_4_3/validate_signed_artifacts_on_upload.yml +72 -0
- gitlabcis/recommendations/artifacts_4/readme.md +12 -0
- gitlabcis/recommendations/artifacts_4/verification_4_1/encrypt_artifacts_before_distribution.yml +47 -0
- gitlabcis/recommendations/artifacts_4/verification_4_1/only_authorized_platforms_can_decrypt_artifacts.yml +59 -0
- gitlabcis/recommendations/artifacts_4/verification_4_1/readme.md +11 -0
- gitlabcis/recommendations/artifacts_4/verification_4_1/sign_artifacts_in_build_pipeline.yml +40 -0
- gitlabcis/recommendations/build_pipelines_2/build_environment_2_1/authenticate_build_access.yml +55 -0
- gitlabcis/recommendations/build_pipelines_2/build_environment_2_1/build_automation.yml +54 -0
- gitlabcis/recommendations/build_pipelines_2/build_environment_2_1/build_env_admins.yml +55 -0
- gitlabcis/recommendations/build_pipelines_2/build_environment_2_1/build_logging.yml +49 -0
- gitlabcis/recommendations/build_pipelines_2/build_environment_2_1/disable_build_tools_default_passwords.yml +54 -0
- gitlabcis/recommendations/build_pipelines_2/build_environment_2_1/immutable_pipeline_infrastructure.yml +60 -0
- gitlabcis/recommendations/build_pipelines_2/build_environment_2_1/limit_build_access.yml +64 -0
- gitlabcis/recommendations/build_pipelines_2/build_environment_2_1/limit_build_secrets_scope.yml +56 -0
- gitlabcis/recommendations/build_pipelines_2/build_environment_2_1/readme.md +19 -0
- gitlabcis/recommendations/build_pipelines_2/build_environment_2_1/secure_build_env_webhooks.yml +43 -0
- gitlabcis/recommendations/build_pipelines_2/build_environment_2_1/single_responsibility_pipeline.yml +58 -0
- gitlabcis/recommendations/build_pipelines_2/build_environment_2_1/vuln_scanning.yml +64 -0
- gitlabcis/recommendations/build_pipelines_2/build_worker_2_2/build_worker_vuln_scanning.yml +58 -0
- gitlabcis/recommendations/build_pipelines_2/build_worker_2_2/monitor_worker_resource_consumption.yml +59 -0
- gitlabcis/recommendations/build_pipelines_2/build_worker_2_2/pass_worker_envs_and_commands.yml +48 -0
- gitlabcis/recommendations/build_pipelines_2/build_worker_2_2/readme.md +16 -0
- gitlabcis/recommendations/build_pipelines_2/build_worker_2_2/restrict_worker_connectivity.yml +61 -0
- gitlabcis/recommendations/build_pipelines_2/build_worker_2_2/segregate_worker_duties.yml +78 -0
- gitlabcis/recommendations/build_pipelines_2/build_worker_2_2/single_use_workers.yml +47 -0
- gitlabcis/recommendations/build_pipelines_2/build_worker_2_2/store_worker_config.yml +62 -0
- gitlabcis/recommendations/build_pipelines_2/build_worker_2_2/worker_runtime_security.yml +37 -0
- gitlabcis/recommendations/build_pipelines_2/pipeline_instructions_2_3/build_stage_io.yml +49 -0
- gitlabcis/recommendations/build_pipelines_2/pipeline_instructions_2_3/build_steps_as_code.yml +42 -0
- gitlabcis/recommendations/build_pipelines_2/pipeline_instructions_2_3/limit_pipeline_triggers.yml +76 -0
- gitlabcis/recommendations/build_pipelines_2/pipeline_instructions_2_3/pipeline_misconfiguration_scanning.yml +48 -0
- gitlabcis/recommendations/build_pipelines_2/pipeline_instructions_2_3/pipeline_secret_scanning.yml +56 -0
- gitlabcis/recommendations/build_pipelines_2/pipeline_instructions_2_3/pipeline_vuln_scanning.yml +44 -0
- gitlabcis/recommendations/build_pipelines_2/pipeline_instructions_2_3/readme.md +16 -0
- gitlabcis/recommendations/build_pipelines_2/pipeline_instructions_2_3/secure_pipeline_output.yml +52 -0
- gitlabcis/recommendations/build_pipelines_2/pipeline_instructions_2_3/track_pipeline_files.yml +48 -0
- gitlabcis/recommendations/build_pipelines_2/pipeline_integrity_2_4/create_reproducible_artifacts.yml +52 -0
- gitlabcis/recommendations/build_pipelines_2/pipeline_integrity_2_4/lock_dependencies.yml +59 -0
- gitlabcis/recommendations/build_pipelines_2/pipeline_integrity_2_4/pipeline_produces_sbom.yml +81 -0
- gitlabcis/recommendations/build_pipelines_2/pipeline_integrity_2_4/pipeline_signs_sbom.yml +38 -0
- gitlabcis/recommendations/build_pipelines_2/pipeline_integrity_2_4/readme.md +14 -0
- gitlabcis/recommendations/build_pipelines_2/pipeline_integrity_2_4/sign_artifacts.yml +35 -0
- gitlabcis/recommendations/build_pipelines_2/pipeline_integrity_2_4/validate_dependencies.yml +63 -0
- gitlabcis/recommendations/build_pipelines_2/readme.md +12 -0
- gitlabcis/recommendations/dependencies_3/readme.md +10 -0
- gitlabcis/recommendations/dependencies_3/third_party_packages_3_1/define_package_managers.yml +84 -0
- gitlabcis/recommendations/dependencies_3/third_party_packages_3_1/dependency_sbom.yml +84 -0
- gitlabcis/recommendations/dependencies_3/third_party_packages_3_1/monitor_dependencies.yml +61 -0
- gitlabcis/recommendations/dependencies_3/third_party_packages_3_1/packages_over_60_days_old.yml +95 -0
- gitlabcis/recommendations/dependencies_3/third_party_packages_3_1/pin_dependency_version.yml +48 -0
- gitlabcis/recommendations/dependencies_3/third_party_packages_3_1/readme.md +14 -0
- gitlabcis/recommendations/dependencies_3/third_party_packages_3_1/third_party_sbom_required.yml +70 -0
- gitlabcis/recommendations/dependencies_3/third_party_packages_3_1/verify_artifacts.yml +45 -0
- gitlabcis/recommendations/dependencies_3/third_party_packages_3_1/verify_signed_metadata.yml +41 -0
- gitlabcis/recommendations/dependencies_3/validate_packages_3_2/org_wide_dependency_policy.yml +47 -0
- gitlabcis/recommendations/dependencies_3/validate_packages_3_2/package_license_scanning.yml +47 -0
- gitlabcis/recommendations/dependencies_3/validate_packages_3_2/package_ownership_change.yml +42 -0
- gitlabcis/recommendations/dependencies_3/validate_packages_3_2/package_vuln_scanning.yml +62 -0
- gitlabcis/recommendations/dependencies_3/validate_packages_3_2/readme.md +10 -0
- gitlabcis/recommendations/deployment_5/deployment_configuration_5_1/audit_deployment_config.yml +46 -0
- gitlabcis/recommendations/deployment_5/deployment_configuration_5_1/limit_deployment_config_access.yml +51 -0
- gitlabcis/recommendations/deployment_5/deployment_configuration_5_1/pin_deployment_config_manifests.yml +59 -0
- gitlabcis/recommendations/deployment_5/deployment_configuration_5_1/readme.md +13 -0
- gitlabcis/recommendations/deployment_5/deployment_configuration_5_1/scan_iac.yml +72 -0
- gitlabcis/recommendations/deployment_5/deployment_configuration_5_1/secret_scan_deployment_config.yml +45 -0
- gitlabcis/recommendations/deployment_5/deployment_configuration_5_1/separate_deployment_config.yml +50 -0
- gitlabcis/recommendations/deployment_5/deployment_configuration_5_1/verify_deployment_config.yml +49 -0
- gitlabcis/recommendations/deployment_5/deployment_environment_5_2/automate_deployment.yml +47 -0
- gitlabcis/recommendations/deployment_5/deployment_environment_5_2/disable_default_passwords.yml +63 -0
- gitlabcis/recommendations/deployment_5/deployment_environment_5_2/limit_prod_access.yml +45 -0
- gitlabcis/recommendations/deployment_5/deployment_environment_5_2/readme.md +12 -0
- gitlabcis/recommendations/deployment_5/deployment_environment_5_2/reproducible_deployment.yml +50 -0
- gitlabcis/recommendations/deployment_5/readme.md +10 -0
- gitlabcis/recommendations/readme.md +24 -0
- gitlabcis/recommendations/source_code_1/code_changes_1_1/audit_branch_protections.yml +56 -0
- gitlabcis/recommendations/source_code_1/code_changes_1_1/auto_risk_scan_merges.yml +62 -0
- gitlabcis/recommendations/source_code_1/code_changes_1_1/branch_protections_for_admins.yml +60 -0
- gitlabcis/recommendations/source_code_1/code_changes_1_1/branches_updated_before_merging.yml +56 -0
- gitlabcis/recommendations/source_code_1/code_changes_1_1/checks_pass_before_merging.yml +57 -0
- gitlabcis/recommendations/source_code_1/code_changes_1_1/code_approval_dismissals.yml +62 -0
- gitlabcis/recommendations/source_code_1/code_changes_1_1/code_approvals.yml +65 -0
- gitlabcis/recommendations/source_code_1/code_changes_1_1/code_changes_require_code_owners.yml +68 -0
- gitlabcis/recommendations/source_code_1/code_changes_1_1/code_dismissal_restrictions.yml +69 -0
- gitlabcis/recommendations/source_code_1/code_changes_1_1/code_owners.yml +61 -0
- gitlabcis/recommendations/source_code_1/code_changes_1_1/code_tracing.yml +52 -0
- gitlabcis/recommendations/source_code_1/code_changes_1_1/comments_resolved_before_merging.yml +59 -0
- gitlabcis/recommendations/source_code_1/code_changes_1_1/commits_must_be_signed_before_merging.yml +63 -0
- gitlabcis/recommendations/source_code_1/code_changes_1_1/default_branch_protected.yml +85 -0
- gitlabcis/recommendations/source_code_1/code_changes_1_1/deny_branch_deletions.yml +76 -0
- gitlabcis/recommendations/source_code_1/code_changes_1_1/ensure_force_push_is_denied.yml +59 -0
- gitlabcis/recommendations/source_code_1/code_changes_1_1/linear_history_required.yml +56 -0
- gitlabcis/recommendations/source_code_1/code_changes_1_1/merging_restrictions.yml +65 -0
- gitlabcis/recommendations/source_code_1/code_changes_1_1/readme.md +26 -0
- gitlabcis/recommendations/source_code_1/code_changes_1_1/stale_branch_reviews.yml +72 -0
- gitlabcis/recommendations/source_code_1/code_changes_1_1/version_control.yml +45 -0
- gitlabcis/recommendations/source_code_1/code_risks_1_5/dast_api_scanning.yml +50 -0
- gitlabcis/recommendations/source_code_1/code_risks_1_5/dast_web_scanning.yml +51 -0
- gitlabcis/recommendations/source_code_1/code_risks_1_5/dependency_scanning.yml +84 -0
- gitlabcis/recommendations/source_code_1/code_risks_1_5/enable_secret_detection.yml +45 -0
- gitlabcis/recommendations/source_code_1/code_risks_1_5/license_scanning.yml +47 -0
- gitlabcis/recommendations/source_code_1/code_risks_1_5/readme.md +14 -0
- gitlabcis/recommendations/source_code_1/code_risks_1_5/secure_iac_instructions.yml +81 -0
- gitlabcis/recommendations/source_code_1/code_risks_1_5/secure_pipeline_instructions.yml +62 -0
- gitlabcis/recommendations/source_code_1/code_risks_1_5/vulnerability_scanning.yml +48 -0
- gitlabcis/recommendations/source_code_1/contribution_access_1_3/domain_verification.yml +65 -0
- gitlabcis/recommendations/source_code_1/contribution_access_1_3/ensure_2_admins_per_repo.yml +56 -0
- gitlabcis/recommendations/source_code_1/contribution_access_1_3/limit_top_level_group_creation.yml +61 -0
- gitlabcis/recommendations/source_code_1/contribution_access_1_3/limit_user_registration_domain.yml +58 -0
- gitlabcis/recommendations/source_code_1/contribution_access_1_3/minimum_number_of_admins.yml +56 -0
- gitlabcis/recommendations/source_code_1/contribution_access_1_3/org_provided_ssh_certs.yml +70 -0
- gitlabcis/recommendations/source_code_1/contribution_access_1_3/readme.md +21 -0
- gitlabcis/recommendations/source_code_1/contribution_access_1_3/require_mfa_at_org_level.yml +89 -0
- gitlabcis/recommendations/source_code_1/contribution_access_1_3/require_mfa_for_contributors.yml +76 -0
- gitlabcis/recommendations/source_code_1/contribution_access_1_3/restrict_ip_addresses.yml +84 -0
- gitlabcis/recommendations/source_code_1/contribution_access_1_3/review_and_remove_inactive_users.yml +62 -0
- gitlabcis/recommendations/source_code_1/contribution_access_1_3/scm_notification_restriction.yml +46 -0
- gitlabcis/recommendations/source_code_1/contribution_access_1_3/strict_permissions_for_repo.yml +62 -0
- gitlabcis/recommendations/source_code_1/contribution_access_1_3/track_code_anomalies.yml +43 -0
- gitlabcis/recommendations/source_code_1/readme.md +13 -0
- gitlabcis/recommendations/source_code_1/repository_management_1_2/limit_issue_deletions.yml +57 -0
- gitlabcis/recommendations/source_code_1/repository_management_1_2/limit_repo_creations.yml +64 -0
- gitlabcis/recommendations/source_code_1/repository_management_1_2/limit_repo_deletions.yml +57 -0
- gitlabcis/recommendations/source_code_1/repository_management_1_2/public_repos_have_security_file.yml +59 -0
- gitlabcis/recommendations/source_code_1/repository_management_1_2/readme.md +15 -0
- gitlabcis/recommendations/source_code_1/repository_management_1_2/review_and_archive_stale_repos.yml +65 -0
- gitlabcis/recommendations/source_code_1/repository_management_1_2/track_forks.yml +74 -0
- gitlabcis/recommendations/source_code_1/repository_management_1_2/track_project_visibility_status.yml +74 -0
- gitlabcis/recommendations/source_code_1/third_party_1_4/README.md +12 -0
- gitlabcis/recommendations/source_code_1/third_party_1_4/admin_approval_for_app_installs.yml +83 -0
- gitlabcis/recommendations/source_code_1/third_party_1_4/least_privilge_app_permissions.yml +103 -0
- gitlabcis/recommendations/source_code_1/third_party_1_4/secure_webhooks.yml +73 -0
- gitlabcis/recommendations/source_code_1/third_party_1_4/stale_app_reviews.yml +66 -0
- gitlabcis/recommendations/template.yml +30 -0
- gitlabcis/tests/__init__.py +0 -0
- gitlabcis/tests/input/__init__.py +0 -0
- gitlabcis/tests/input/conftest.py +29 -0
- gitlabcis/tests/input/no_input_test.py +82 -0
- gitlabcis/tests/input/switch_test.py +19 -0
- gitlabcis/tests/input/version_test.py +7 -0
- gitlabcis/tests/unit/__init__.py +0 -0
- gitlabcis/tests/unit/benchmarks/artifacts_4/access_to_artifacts_4_2_test.py +131 -0
- gitlabcis/tests/unit/benchmarks/artifacts_4/origin_traceability_4_4_test.py +15 -0
- gitlabcis/tests/unit/benchmarks/artifacts_4/package_registries_4_3_test.py +102 -0
- gitlabcis/tests/unit/benchmarks/artifacts_4/verification_4_1_test.py +78 -0
- gitlabcis/tests/unit/benchmarks/build_pipelines_2/build_environment_2_1_test.py +239 -0
- gitlabcis/tests/unit/benchmarks/build_pipelines_2/build_worker_2_2_test.py +105 -0
- gitlabcis/tests/unit/benchmarks/build_pipelines_2/pipeline_instructions_2_3_test.py +340 -0
- gitlabcis/tests/unit/benchmarks/build_pipelines_2/pipeline_integrity_2_4_test.py +115 -0
- gitlabcis/tests/unit/benchmarks/conftest.py +47 -0
- gitlabcis/tests/unit/benchmarks/dependencies_3/third_party_packages_3_1_test.py +135 -0
- gitlabcis/tests/unit/benchmarks/dependencies_3/validate_packages_3_2_test.py +171 -0
- gitlabcis/tests/unit/benchmarks/deployment_5/deployment_configuration_5_1_test.py +140 -0
- gitlabcis/tests/unit/benchmarks/deployment_5/deployment_environment_5_2_test.py +60 -0
- gitlabcis/tests/unit/benchmarks/function_test.py +24 -0
- gitlabcis/tests/unit/benchmarks/source_code_1/code_changes_1_1_test.py +565 -0
- gitlabcis/tests/unit/benchmarks/source_code_1/code_risks_1_5_test.py +419 -0
- gitlabcis/tests/unit/benchmarks/source_code_1/contribution_access_1_3_test.py +265 -0
- gitlabcis/tests/unit/benchmarks/source_code_1/repository_management_1_2_test.py +142 -0
- gitlabcis/tests/unit/benchmarks/source_code_1/third_party_1_4_test.py +119 -0
- gitlabcis/tests/unit/conftest.py +94 -0
- gitlabcis/tests/unit/log/log_test.py +23 -0
- gitlabcis/tests/unit/utils/argfilters_test.py +9 -0
- gitlabcis/tests/unit/utils/ci_test.py +156 -0
- gitlabcis/tests/unit/utils/output_test.py +95 -0
- gitlabcis/tests/unit/utils/utils_general_test.py +149 -0
- gitlabcis/tests/unit/utils/version_test.py +11 -0
- gitlabcis/tests/unit/yaml/bad_file_test.py +15 -0
- gitlabcis/tests/unit/yaml/recommendation_test.py +123 -0
- gitlabcis/utils/__init__.py +146 -0
- gitlabcis/utils/ci.py +132 -0
- gitlabcis-1.3.2.dist-info/LICENSE +21 -0
- gitlabcis-1.3.2.dist-info/METADATA +241 -0
- gitlabcis-1.3.2.dist-info/RECORD +218 -0
- gitlabcis-1.3.2.dist-info/WHEEL +5 -0
- gitlabcis-1.3.2.dist-info/entry_points.txt +2 -0
- gitlabcis-1.3.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
# -------------------------------------------------------------------------
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def version_control(glEntity, glObject, **kwargs):
|
|
5
|
+
"""
|
|
6
|
+
id: 1.1.1
|
|
7
|
+
title: Ensure any changes to code are tracked in a version control
|
|
8
|
+
platform
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
# previously project has been validated to be a valid gitlab project
|
|
12
|
+
if glEntity is not None:
|
|
13
|
+
return {True: 'Using GitLab as version control'}
|
|
14
|
+
|
|
15
|
+
return {False: 'Not using GitLab as version control'}
|
|
16
|
+
|
|
17
|
+
# -------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def code_tracing(glEntity, glObject, **kwargs):
|
|
21
|
+
"""
|
|
22
|
+
id: 1.1.2
|
|
23
|
+
title: Ensure any change to code can be traced back to its associated
|
|
24
|
+
task
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from gitlab.exceptions import (GitlabAuthenticationError, GitlabGetError,
|
|
28
|
+
GitlabHttpError, GitlabListError)
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
if len(glEntity.issues.list(get_all=False)) > 0:
|
|
32
|
+
return {True: 'Issues are used in the project'}
|
|
33
|
+
|
|
34
|
+
return {False: 'Issues are not used in the project'}
|
|
35
|
+
|
|
36
|
+
except (GitlabHttpError, GitlabGetError, GitlabAuthenticationError,
|
|
37
|
+
GitlabListError) as e:
|
|
38
|
+
if e.response_code in [401, 403]:
|
|
39
|
+
return {None: 'Insufficient permissions'}
|
|
40
|
+
|
|
41
|
+
# -------------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def code_approvals(glEntity, glObject, **kwargs):
|
|
45
|
+
"""
|
|
46
|
+
id: 1.1.3
|
|
47
|
+
title: Ensure any change to code receives approval of two strongly
|
|
48
|
+
authenticated users
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
from gitlab.exceptions import (GitlabAuthenticationError, GitlabGetError,
|
|
52
|
+
GitlabHttpError, GitlabListError)
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
for approval in glEntity.approvalrules.list(get_all=True):
|
|
56
|
+
if approval.approvals_required >= 2:
|
|
57
|
+
return {True: '2 approvals are required for code changes'}
|
|
58
|
+
|
|
59
|
+
return {False: '2 approvals are required for code changes'}
|
|
60
|
+
|
|
61
|
+
except (GitlabHttpError, GitlabGetError, GitlabAuthenticationError,
|
|
62
|
+
GitlabListError) as e:
|
|
63
|
+
if e.response_code in [401, 403]:
|
|
64
|
+
return {None: 'Insufficient permissions'}
|
|
65
|
+
|
|
66
|
+
# -------------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def code_approval_dismissals(glEntity, glObject, **kwargs):
|
|
70
|
+
"""
|
|
71
|
+
id: 1.1.4
|
|
72
|
+
title: Ensure previous approvals are dismissed when updates are
|
|
73
|
+
introduced to a code change proposal
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
from gitlab.exceptions import (GitlabAuthenticationError, GitlabGetError,
|
|
77
|
+
GitlabHttpError)
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
mrApprovalSettings = glEntity.approvals.get()
|
|
81
|
+
|
|
82
|
+
if mrApprovalSettings.reset_approvals_on_push is True:
|
|
83
|
+
return {True: 'Approvals reset on push'}
|
|
84
|
+
|
|
85
|
+
return {False: 'Approvals not reset on push'}
|
|
86
|
+
|
|
87
|
+
except (GitlabHttpError, GitlabGetError, GitlabAuthenticationError) as e:
|
|
88
|
+
if e.response_code in [401, 403]:
|
|
89
|
+
return {None: 'Insufficient permissions'}
|
|
90
|
+
|
|
91
|
+
# -------------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def code_dismissal_restrictions(glEntity, glObject, **kwargs):
|
|
95
|
+
"""
|
|
96
|
+
id: 1.1.5
|
|
97
|
+
title: Ensure there are restrictions on who can dismiss code change
|
|
98
|
+
reviews
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
from gitlab.exceptions import (GitlabAuthenticationError, GitlabGetError,
|
|
102
|
+
GitlabHttpError, GitlabListError)
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
protectedBranches = glEntity.protectedbranches.list(get_all=False)
|
|
106
|
+
|
|
107
|
+
# by default a project sets only "Maintainers" for the users
|
|
108
|
+
# for both merge / push approvals
|
|
109
|
+
|
|
110
|
+
_userRoles = list(set(
|
|
111
|
+
level.get('access_level_description')
|
|
112
|
+
for branch in protectedBranches
|
|
113
|
+
for key in ['merge_access_levels', 'push_access_levels']
|
|
114
|
+
for levels in [getattr(branch, key, [])]
|
|
115
|
+
for level in levels
|
|
116
|
+
))
|
|
117
|
+
|
|
118
|
+
# if the defaults are still set, fail the check
|
|
119
|
+
if _userRoles:
|
|
120
|
+
if len(_userRoles) == 1 and _userRoles[0] == 'Maintainers':
|
|
121
|
+
return {
|
|
122
|
+
False: 'No restrictions on who can dismiss code changes'}
|
|
123
|
+
else:
|
|
124
|
+
return {False: 'No restrictions on who can dismiss code changes'}
|
|
125
|
+
|
|
126
|
+
# this will return true if someone has altered the defaults set:
|
|
127
|
+
return {True: 'Restrictions set on who can dismiss code changes'}
|
|
128
|
+
|
|
129
|
+
except (GitlabHttpError, GitlabGetError, GitlabAuthenticationError,
|
|
130
|
+
GitlabListError) as e:
|
|
131
|
+
if e.response_code in [401, 403]:
|
|
132
|
+
return {None: 'Insufficient permissions'}
|
|
133
|
+
|
|
134
|
+
# -------------------------------------------------------------------------
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def code_owners(glEntity, glObject, **kwargs):
|
|
138
|
+
"""
|
|
139
|
+
id: 1.1.6
|
|
140
|
+
title: Ensure code owners are set for extra sensitive code or
|
|
141
|
+
configuration
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
from gitlab.exceptions import (GitlabAuthenticationError, GitlabGetError,
|
|
145
|
+
GitlabHttpError)
|
|
146
|
+
|
|
147
|
+
# as per the docs, the CODEOWNERS file can live in multiple places:
|
|
148
|
+
# https://docs.gitlab.com/ee/user/project/codeowners/
|
|
149
|
+
|
|
150
|
+
_dirPaths = ['', '.gitlab', 'docs']
|
|
151
|
+
|
|
152
|
+
# if the CODEOWNERS file is present, pass the check:
|
|
153
|
+
for dirPath in _dirPaths:
|
|
154
|
+
|
|
155
|
+
# some repos may not have these paths, so exclude them:
|
|
156
|
+
try:
|
|
157
|
+
_files = glEntity.repository_tree(path=dirPath)
|
|
158
|
+
|
|
159
|
+
except (GitlabHttpError, GitlabGetError,
|
|
160
|
+
GitlabAuthenticationError) as e:
|
|
161
|
+
|
|
162
|
+
if e.response_code == 404:
|
|
163
|
+
continue
|
|
164
|
+
|
|
165
|
+
if e.response_code in [401, 403]:
|
|
166
|
+
return {None: 'Insufficient permissions'}
|
|
167
|
+
|
|
168
|
+
return {None: 'Unknown error'}
|
|
169
|
+
|
|
170
|
+
for _file in _files:
|
|
171
|
+
|
|
172
|
+
if _file.get('name') == 'CODEOWNERS':
|
|
173
|
+
return {True: 'CODEOWNERS file present'}
|
|
174
|
+
|
|
175
|
+
return {False: 'CODEOWNERS file not present'}
|
|
176
|
+
|
|
177
|
+
# -------------------------------------------------------------------------
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def code_changes_require_code_owners(glEntity, glObject, **kwargs):
|
|
181
|
+
"""
|
|
182
|
+
id: 1.1.7
|
|
183
|
+
title: Ensure code owner's review is required when a change affects
|
|
184
|
+
owned code
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
from gitlab.exceptions import (GitlabAuthenticationError, GitlabGetError,
|
|
188
|
+
GitlabHttpError)
|
|
189
|
+
|
|
190
|
+
try:
|
|
191
|
+
defaultBranch = glEntity.protectedbranches.get(glEntity.default_branch)
|
|
192
|
+
|
|
193
|
+
if defaultBranch is None:
|
|
194
|
+
return {False: 'Default branch is not protected'}
|
|
195
|
+
|
|
196
|
+
if defaultBranch.code_owner_approval_required is True:
|
|
197
|
+
return {True: 'CODEOWNERS approval is required'}
|
|
198
|
+
|
|
199
|
+
return {False: 'CODEOWNERS approval is not configured'}
|
|
200
|
+
|
|
201
|
+
except (GitlabHttpError, GitlabGetError, GitlabAuthenticationError) as e:
|
|
202
|
+
if e.response_code == 403 and e.error_message == '403 Forbidden':
|
|
203
|
+
return {None: 'Insufficient permissions'}
|
|
204
|
+
|
|
205
|
+
if e.response_code == 404:
|
|
206
|
+
return {False: 'Default branch is not protected'}
|
|
207
|
+
|
|
208
|
+
# -------------------------------------------------------------------------
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def stale_branch_reviews(glEntity, glObject, **kwargs):
|
|
212
|
+
"""
|
|
213
|
+
id: 1.1.8
|
|
214
|
+
title: Ensure inactive branches are periodically reviewed and removed
|
|
215
|
+
"""
|
|
216
|
+
|
|
217
|
+
from datetime import datetime
|
|
218
|
+
|
|
219
|
+
from dateutil.relativedelta import relativedelta
|
|
220
|
+
from gitlab.exceptions import (GitlabAuthenticationError, GitlabGetError,
|
|
221
|
+
GitlabHttpError, GitlabListError)
|
|
222
|
+
|
|
223
|
+
try:
|
|
224
|
+
_staleBranches = []
|
|
225
|
+
|
|
226
|
+
for branch in glEntity.branches.list(get_all=True):
|
|
227
|
+
_commitDate = datetime.strptime(
|
|
228
|
+
branch.commit.get('committed_date'),
|
|
229
|
+
'%Y-%m-%dT%H:%M:%S.%f%z'
|
|
230
|
+
).replace(tzinfo=None)
|
|
231
|
+
|
|
232
|
+
if relativedelta(datetime.now(), _commitDate).months > 3:
|
|
233
|
+
_staleBranches.append(branch.name)
|
|
234
|
+
|
|
235
|
+
if len(_staleBranches) == 0:
|
|
236
|
+
return {True: 'No stale branches found'}
|
|
237
|
+
|
|
238
|
+
return {False: 'Found stale branches'}
|
|
239
|
+
|
|
240
|
+
except (GitlabHttpError, GitlabGetError, GitlabAuthenticationError,
|
|
241
|
+
GitlabListError) as e:
|
|
242
|
+
if e.response_code in [401, 403]:
|
|
243
|
+
return {None: 'Insufficient permissions'}
|
|
244
|
+
|
|
245
|
+
# -------------------------------------------------------------------------
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def checks_pass_before_merging(glEntity, glObject, **kwargs):
|
|
249
|
+
"""
|
|
250
|
+
id: 1.1.9
|
|
251
|
+
title: Ensure all checks have passed before merging new code
|
|
252
|
+
"""
|
|
253
|
+
|
|
254
|
+
from gitlab.exceptions import (GitlabAuthenticationError, GitlabGetError,
|
|
255
|
+
GitlabHttpError)
|
|
256
|
+
|
|
257
|
+
# bool value if box is checked in Settings -> Merge Requests:
|
|
258
|
+
try:
|
|
259
|
+
_allowMerging = glEntity.only_allow_merge_if_all_status_checks_passed
|
|
260
|
+
|
|
261
|
+
if _allowMerging is True:
|
|
262
|
+
return {True: 'All checks must pass before merging'}
|
|
263
|
+
else:
|
|
264
|
+
return {False: 'All checks do not need to pass before merging'}
|
|
265
|
+
|
|
266
|
+
except (GitlabHttpError, GitlabGetError, GitlabAuthenticationError) as e:
|
|
267
|
+
if e.response_code in [401, 403]:
|
|
268
|
+
return {None: 'Insufficient permissions'}
|
|
269
|
+
|
|
270
|
+
# if a user is on a free tier plan, this attribute will not exist:
|
|
271
|
+
except AttributeError:
|
|
272
|
+
return {None: 'The project is not on an eligible plan'}
|
|
273
|
+
|
|
274
|
+
# -------------------------------------------------------------------------
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def branches_updated_before_merging(glEntity, glObject, **kwargs):
|
|
278
|
+
"""
|
|
279
|
+
id: 1.1.10
|
|
280
|
+
title: Ensure open Git branches are up to date before they can be
|
|
281
|
+
merged into code base
|
|
282
|
+
"""
|
|
283
|
+
|
|
284
|
+
from gitlab.exceptions import (GitlabAuthenticationError, GitlabGetError,
|
|
285
|
+
GitlabHttpError)
|
|
286
|
+
|
|
287
|
+
try:
|
|
288
|
+
if glEntity.merge_method in ['rebase_merge', 'ff']:
|
|
289
|
+
return {True: 'Merge methods set'}
|
|
290
|
+
|
|
291
|
+
return {False: 'Merge methods not set'}
|
|
292
|
+
|
|
293
|
+
except (GitlabHttpError, GitlabGetError, GitlabAuthenticationError) as e:
|
|
294
|
+
if e.response_code in [401, 403]:
|
|
295
|
+
return {None: 'Insufficient permissions'}
|
|
296
|
+
|
|
297
|
+
# this throws an attr error if accessed anonymously (pytest no auth)
|
|
298
|
+
except AttributeError:
|
|
299
|
+
return {None: 'Insufficient permissions'}
|
|
300
|
+
|
|
301
|
+
# -------------------------------------------------------------------------
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def comments_resolved_before_merging(glEntity, glObject, **kwargs):
|
|
305
|
+
"""
|
|
306
|
+
id: 1.1.11
|
|
307
|
+
title: Ensure all open comments are resolved before allowing code
|
|
308
|
+
change merging
|
|
309
|
+
"""
|
|
310
|
+
|
|
311
|
+
from gitlab.exceptions import (GitlabAuthenticationError, GitlabGetError,
|
|
312
|
+
GitlabHttpError)
|
|
313
|
+
|
|
314
|
+
try:
|
|
315
|
+
# bool value if box is checked in Settings -> Merge Requests:
|
|
316
|
+
try:
|
|
317
|
+
_dR = glEntity.only_allow_merge_if_all_discussions_are_resolved
|
|
318
|
+
# this throws an attr error if accessed anonymously (pytest no auth)
|
|
319
|
+
except AttributeError:
|
|
320
|
+
return {None: 'Insufficient permissions'}
|
|
321
|
+
|
|
322
|
+
if _dR is True:
|
|
323
|
+
return {True: 'All comments must be resolved before merging'}
|
|
324
|
+
|
|
325
|
+
elif _dR is False:
|
|
326
|
+
return {
|
|
327
|
+
False: 'All comments do not need to be resolved before merging'
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
except (GitlabHttpError, GitlabGetError, GitlabAuthenticationError) as e:
|
|
331
|
+
if e.response_code in [401, 403]:
|
|
332
|
+
return {None: 'Insufficient permissions'}
|
|
333
|
+
|
|
334
|
+
# -------------------------------------------------------------------------
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def commits_must_be_signed_before_merging(glEntity, glObject, **kwargs):
|
|
338
|
+
"""
|
|
339
|
+
id: 1.1.12
|
|
340
|
+
title: Ensure verification of signed commits for new changes before
|
|
341
|
+
merging
|
|
342
|
+
"""
|
|
343
|
+
|
|
344
|
+
from gitlab.exceptions import (GitlabAuthenticationError, GitlabGetError,
|
|
345
|
+
GitlabHttpError)
|
|
346
|
+
|
|
347
|
+
try:
|
|
348
|
+
pushRules = glEntity.pushrules.get()
|
|
349
|
+
|
|
350
|
+
if pushRules.reject_unsigned_commits is True:
|
|
351
|
+
return {True: 'Rejecting unsigned commits'}
|
|
352
|
+
|
|
353
|
+
return {False: 'Unsigned commits are not rejected'}
|
|
354
|
+
|
|
355
|
+
except (GitlabHttpError, GitlabGetError, GitlabAuthenticationError) as e:
|
|
356
|
+
if e.response_code in [401, 403]:
|
|
357
|
+
return {None: 'Insufficient permissions'}
|
|
358
|
+
|
|
359
|
+
if e.response_code == 404:
|
|
360
|
+
return {False: 'No push rules found'}
|
|
361
|
+
|
|
362
|
+
# -------------------------------------------------------------------------
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def linear_history_required(glEntity, glObject, **kwargs):
|
|
366
|
+
"""
|
|
367
|
+
id: 1.1.13
|
|
368
|
+
title: Ensure linear history is required
|
|
369
|
+
"""
|
|
370
|
+
|
|
371
|
+
from gitlab.exceptions import (GitlabAuthenticationError, GitlabGetError,
|
|
372
|
+
GitlabHttpError)
|
|
373
|
+
|
|
374
|
+
try:
|
|
375
|
+
if glEntity.merge_method not in ['rebase_merge', 'merge']:
|
|
376
|
+
return {True: 'Merge method set'}
|
|
377
|
+
|
|
378
|
+
return {False: 'Merge method not set'}
|
|
379
|
+
|
|
380
|
+
except (GitlabHttpError, GitlabGetError, GitlabAuthenticationError) as e:
|
|
381
|
+
if e.response_code in [401, 403]:
|
|
382
|
+
return {None: 'Insufficient permissions'}
|
|
383
|
+
|
|
384
|
+
# this throws an attr error if accessed anonymously (pytest no auth)
|
|
385
|
+
except AttributeError:
|
|
386
|
+
return {None: 'Insufficient permissions'}
|
|
387
|
+
|
|
388
|
+
# -------------------------------------------------------------------------
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
def branch_protections_for_admins(glEntity, glObject, **kwargs):
|
|
392
|
+
"""
|
|
393
|
+
id: 1.1.14
|
|
394
|
+
title: Ensure branch protection rules are enforced for administrators
|
|
395
|
+
"""
|
|
396
|
+
|
|
397
|
+
from gitlab.exceptions import (GitlabAuthenticationError, GitlabGetError,
|
|
398
|
+
GitlabHttpError)
|
|
399
|
+
|
|
400
|
+
try:
|
|
401
|
+
settings = glObject.settings.get()
|
|
402
|
+
|
|
403
|
+
if settings.group_owners_can_manage_default_branch_protection is False:
|
|
404
|
+
return {
|
|
405
|
+
True: 'Group owners cannot manage default branch protection'}
|
|
406
|
+
|
|
407
|
+
return {False: 'Group owners can manage default branch protection'}
|
|
408
|
+
|
|
409
|
+
except (GitlabHttpError, GitlabGetError, GitlabAuthenticationError) as e:
|
|
410
|
+
if e.response_code in [401, 403]:
|
|
411
|
+
return {None: 'Insufficient permissions'}
|
|
412
|
+
|
|
413
|
+
# -------------------------------------------------------------------------
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def merging_restrictions(glEntity, glObject, **kwargs):
|
|
417
|
+
"""
|
|
418
|
+
id: 1.1.15
|
|
419
|
+
title: Ensure pushing or merging of new code is restricted to specific
|
|
420
|
+
individuals or teams
|
|
421
|
+
"""
|
|
422
|
+
|
|
423
|
+
from gitlab.exceptions import (GitlabAuthenticationError, GitlabGetError,
|
|
424
|
+
GitlabHttpError, GitlabListError)
|
|
425
|
+
|
|
426
|
+
try:
|
|
427
|
+
protectedBranches = glEntity.protectedbranches.list(get_all=True)
|
|
428
|
+
|
|
429
|
+
if len(protectedBranches) == 0:
|
|
430
|
+
return {False: 'No protected branches found'}
|
|
431
|
+
|
|
432
|
+
for branch in protectedBranches:
|
|
433
|
+
|
|
434
|
+
if branch.allow_force_push is True:
|
|
435
|
+
return {False: 'Protected branches allow force push'}
|
|
436
|
+
|
|
437
|
+
return {True: 'Protected branches do not allow force push'}
|
|
438
|
+
|
|
439
|
+
except (GitlabHttpError, GitlabGetError, GitlabAuthenticationError,
|
|
440
|
+
GitlabListError) as e:
|
|
441
|
+
if e.response_code in [401, 403]:
|
|
442
|
+
return {None: 'Insufficient permissions'}
|
|
443
|
+
|
|
444
|
+
# -------------------------------------------------------------------------
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def ensure_force_push_is_denied(glEntity, glObject, **kwargs):
|
|
448
|
+
"""
|
|
449
|
+
id: 1.1.16
|
|
450
|
+
title: Ensure force push code to branches is denied
|
|
451
|
+
"""
|
|
452
|
+
|
|
453
|
+
from gitlab.exceptions import (GitlabAuthenticationError, GitlabGetError,
|
|
454
|
+
GitlabHttpError)
|
|
455
|
+
|
|
456
|
+
try:
|
|
457
|
+
defaultBranch = glEntity.protectedbranches.get(glEntity.default_branch)
|
|
458
|
+
|
|
459
|
+
if defaultBranch is not None \
|
|
460
|
+
and defaultBranch.allow_force_push is False:
|
|
461
|
+
return {
|
|
462
|
+
True: 'Default branch is protected and does not allow '
|
|
463
|
+
'force push'
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return {False: 'Default branch is not protected or allows '
|
|
467
|
+
'force push'}
|
|
468
|
+
|
|
469
|
+
except (GitlabHttpError, GitlabGetError, GitlabAuthenticationError) as e:
|
|
470
|
+
# if we don't have access, or the default branch is not protected:
|
|
471
|
+
if e.response_code in [401, 403, 404]:
|
|
472
|
+
return {None: 'Insufficient permissions'}
|
|
473
|
+
|
|
474
|
+
# -------------------------------------------------------------------------
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def deny_branch_deletions(glEntity, glObject, **kwargs):
|
|
478
|
+
"""
|
|
479
|
+
id: 1.1.17
|
|
480
|
+
title: Ensure branch deletions are denied
|
|
481
|
+
"""
|
|
482
|
+
|
|
483
|
+
from gitlab.exceptions import (GitlabAuthenticationError, GitlabGetError,
|
|
484
|
+
GitlabHttpError, GitlabListError)
|
|
485
|
+
|
|
486
|
+
try:
|
|
487
|
+
protectedBranches = glEntity.protectedbranches.list(get_all=False)
|
|
488
|
+
|
|
489
|
+
if len(protectedBranches) == 0:
|
|
490
|
+
return {False: 'No protected branches found'}
|
|
491
|
+
|
|
492
|
+
return {True: 'Protected branches found'}
|
|
493
|
+
|
|
494
|
+
except (GitlabHttpError, GitlabGetError, GitlabAuthenticationError,
|
|
495
|
+
GitlabListError) as e:
|
|
496
|
+
if e.response_code in [401, 403]:
|
|
497
|
+
return {None: 'Insufficient permissions'}
|
|
498
|
+
|
|
499
|
+
# -------------------------------------------------------------------------
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
def auto_risk_scan_merges(glEntity, glObject, **kwargs):
|
|
503
|
+
"""
|
|
504
|
+
id: 1.1.18
|
|
505
|
+
title: Ensure any merging of code is automatically scanned for risks
|
|
506
|
+
"""
|
|
507
|
+
|
|
508
|
+
import yaml
|
|
509
|
+
from gitlab.exceptions import (GitlabAuthenticationError, GitlabGetError,
|
|
510
|
+
GitlabHttpError)
|
|
511
|
+
from gql import Client, gql
|
|
512
|
+
from gql.transport.exceptions import (TransportAlreadyConnected,
|
|
513
|
+
TransportServerError)
|
|
514
|
+
from gql.transport.requests import RequestsHTTPTransport
|
|
515
|
+
from graphql import GraphQLError
|
|
516
|
+
|
|
517
|
+
try:
|
|
518
|
+
|
|
519
|
+
variables = {
|
|
520
|
+
'fullPath': glEntity.path_with_namespace
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
except (GitlabHttpError, GitlabGetError, GitlabAuthenticationError) as e:
|
|
524
|
+
if e.response_code in [401, 403]:
|
|
525
|
+
return {None: 'Insufficient permissions'}
|
|
526
|
+
|
|
527
|
+
client = Client(
|
|
528
|
+
transport=RequestsHTTPTransport(
|
|
529
|
+
url=kwargs.get('graphQLEndpoint'),
|
|
530
|
+
headers=kwargs.get('graphQLHeaders'),
|
|
531
|
+
use_json=True
|
|
532
|
+
),
|
|
533
|
+
fetch_schema_from_transport=True
|
|
534
|
+
)
|
|
535
|
+
|
|
536
|
+
query = gql('''
|
|
537
|
+
query GetSecurityScanners($fullPath: ID!) {
|
|
538
|
+
project(fullPath: $fullPath) {
|
|
539
|
+
scanExecutionPolicies {
|
|
540
|
+
nodes {
|
|
541
|
+
name
|
|
542
|
+
enabled
|
|
543
|
+
yaml
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
''')
|
|
549
|
+
|
|
550
|
+
try:
|
|
551
|
+
|
|
552
|
+
results = client.execute(query, variable_values=variables)
|
|
553
|
+
|
|
554
|
+
except (GraphQLError, TransportServerError, TransportAlreadyConnected):
|
|
555
|
+
return {None: 'Error: Issue with GraphQL Query'}
|
|
556
|
+
|
|
557
|
+
# pytest no auth:
|
|
558
|
+
except AttributeError:
|
|
559
|
+
return {None: 'Insufficient permissions'}
|
|
560
|
+
|
|
561
|
+
try:
|
|
562
|
+
required_scans = [
|
|
563
|
+
'dast', 'secret_detection', 'cluster_image_scanning',
|
|
564
|
+
'container_scanning', 'sast', 'sast_iac', 'dependency_scanning'
|
|
565
|
+
]
|
|
566
|
+
|
|
567
|
+
scans_found = set()
|
|
568
|
+
secret_detection_policy_found = False
|
|
569
|
+
for policy in results['project']['scanExecutionPolicies']['nodes']:
|
|
570
|
+
if policy.get('enabled') is True:
|
|
571
|
+
policy_yaml = yaml.safe_load(policy.get('yaml', ''))
|
|
572
|
+
actions = policy_yaml.get('actions', [])
|
|
573
|
+
rules = policy_yaml.get('rules', [])
|
|
574
|
+
for action in actions:
|
|
575
|
+
scans_found.add(action.get('scan'))
|
|
576
|
+
if action.get('scan') == 'secret_detection':
|
|
577
|
+
for rule in rules:
|
|
578
|
+
if (
|
|
579
|
+
rule.get('type') == 'pipeline'
|
|
580
|
+
and '*' in rule.get('branches', [])
|
|
581
|
+
):
|
|
582
|
+
secret_detection_policy_found = True
|
|
583
|
+
missing_scans = [
|
|
584
|
+
scan for scan in required_scans
|
|
585
|
+
if scan not in scans_found
|
|
586
|
+
]
|
|
587
|
+
if secret_detection_policy_found:
|
|
588
|
+
if missing_scans:
|
|
589
|
+
return {
|
|
590
|
+
True: (
|
|
591
|
+
'Scan Execution Policy for secret_detection is '
|
|
592
|
+
'enabled and triggers for all pipelines and '
|
|
593
|
+
'branches. Other missing scans for manual review:'
|
|
594
|
+
f'{", ".join(missing_scans)}'
|
|
595
|
+
)
|
|
596
|
+
}
|
|
597
|
+
else:
|
|
598
|
+
return {
|
|
599
|
+
True: (
|
|
600
|
+
'Scan Execution Policy for secret_detection is '
|
|
601
|
+
'enabled and triggers for all pipelines and '
|
|
602
|
+
'branches. All required scans are covered.'
|
|
603
|
+
)
|
|
604
|
+
}
|
|
605
|
+
else:
|
|
606
|
+
return {
|
|
607
|
+
False: (
|
|
608
|
+
'Scan Execution Policy for secret_detection '
|
|
609
|
+
'is not enabled to trigger for all pipelines '
|
|
610
|
+
'and branches. Other missing scans for manual '
|
|
611
|
+
f'review: {", ".join(missing_scans)}'
|
|
612
|
+
)
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
except KeyError:
|
|
616
|
+
return {False: 'Scan Execution Policy was not found'}
|
|
617
|
+
|
|
618
|
+
# -------------------------------------------------------------------------
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
def audit_branch_protections(glEntity, glObject, **kwargs):
|
|
622
|
+
"""
|
|
623
|
+
id: 1.1.19
|
|
624
|
+
title: Ensure any changes to branch protection rules are audited
|
|
625
|
+
"""
|
|
626
|
+
|
|
627
|
+
from gitlab.exceptions import (GitlabAuthenticationError, GitlabGetError,
|
|
628
|
+
GitlabHttpError, GitlabLicenseError)
|
|
629
|
+
|
|
630
|
+
try:
|
|
631
|
+
if glObject.get_license().get('plan') in ['premium', 'ultimate']:
|
|
632
|
+
return {True: 'License allows audit on branch protections'}
|
|
633
|
+
|
|
634
|
+
return {False: 'License does not allow audit on branch protections'}
|
|
635
|
+
|
|
636
|
+
except (GitlabHttpError, GitlabGetError, GitlabLicenseError) as e:
|
|
637
|
+
if e.response_code == 403 and e.error_message == '403 Forbidden':
|
|
638
|
+
return {None: 'Insufficient permissions'}
|
|
639
|
+
|
|
640
|
+
except (GitlabAuthenticationError):
|
|
641
|
+
return {None: 'Insufficient permissions'}
|
|
642
|
+
|
|
643
|
+
# -------------------------------------------------------------------------
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
def default_branch_protected(glEntity, glObject, **kwargs):
|
|
647
|
+
"""
|
|
648
|
+
id: 1.1.20
|
|
649
|
+
title: Ensure branch protection is enforced on the default branch
|
|
650
|
+
"""
|
|
651
|
+
|
|
652
|
+
from gitlab.exceptions import (GitlabAuthenticationError, GitlabGetError,
|
|
653
|
+
GitlabHttpError)
|
|
654
|
+
|
|
655
|
+
try:
|
|
656
|
+
defaultBranch = glEntity.branches.get(glEntity.default_branch)
|
|
657
|
+
|
|
658
|
+
if defaultBranch.protected is True:
|
|
659
|
+
return {True: 'Default branch is protected'}
|
|
660
|
+
|
|
661
|
+
return {False: 'Default branch is not protected'}
|
|
662
|
+
|
|
663
|
+
except (GitlabHttpError, GitlabGetError, GitlabAuthenticationError) as e:
|
|
664
|
+
if e.response_code in [401, 403]:
|
|
665
|
+
return {None: 'Insufficient permissions'}
|