diff options
32 files changed, 1573 insertions, 22 deletions
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue index 5127ab3d400..6d66a45f382 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue @@ -181,17 +181,12 @@ export default { > {{ __('Rebase') }} </gl-button> - <span - v-if="!rebasingError" - class="gl-font-weight-bold gl-ml-0!" - data-testid="rebase-message" - >{{ - __( - 'Fast-forward merge is not possible. Rebase the source branch onto the target branch.', - ) - }}</span - > - <span v-else class="gl-font-weight-bold danger gl-ml-0!" data-testid="rebase-message">{{ + <span v-if="!rebasingError" class="gl-font-weight-bold" data-testid="rebase-message">{{ + __( + 'Fast-forward merge is not possible. Rebase the source branch onto the target branch.', + ) + }}</span> + <span v-else class="gl-font-weight-bold danger" data-testid="rebase-message">{{ rebasingError }}</span> </div> diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index b694efbc1eb..ffdd9fca95b 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -23,6 +23,7 @@ class Projects::CommitController < Projects::ApplicationController end BRANCH_SEARCH_LIMIT = 1000 + COMMIT_DIFFS_PER_PAGE = 75 feature_category :source_code_management diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 973e43831f1..efbb958cbae 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -168,6 +168,14 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo } end + def sast_reports + reports_response(merge_request.compare_sast_reports(current_user), head_pipeline) + end + + def secret_detection_reports + reports_response(merge_request.compare_secret_detection_reports(current_user), head_pipeline) + end + def context_commits return render_404 unless project.context_commits_enabled? diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 2d4ace5a5bf..f5c75d62097 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -126,6 +126,14 @@ module CommitsHelper %w(btn gpg-status-box) + Array(additional_classes) end + def conditionally_paginate_diff_files(diffs, paginate:, per: Projects::CommitController::COMMIT_DIFFS_PER_PAGE) + if paginate && Feature.enabled?(:paginate_commit_view, @project, type: :development) + Kaminari.paginate_array(diffs.diff_files.to_a).page(params[:page]).per(per) + else + diffs.diff_files + end + end + protected # Private: Returns a link to a person. If the person has a matching user and diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index a9bd890aa4b..db151126caf 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -786,7 +786,9 @@ module Ci end def artifacts_file_for_type(type) - job_artifacts.find_by(file_type: Ci::JobArtifact.file_types[type])&.file + file_types = Ci::JobArtifact.associated_file_types_for(type) + file_types_ids = file_types&.map { |file_type| Ci::JobArtifact.file_types[file_type] } + job_artifacts.find_by(file_type: file_types_ids)&.file end def coverage_regex diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb index f13be3b3c86..f927111758a 100644 --- a/app/models/ci/job_artifact.rb +++ b/app/models/ci/job_artifact.rb @@ -19,6 +19,8 @@ module Ci NON_ERASABLE_FILE_TYPES = %w[trace].freeze TERRAFORM_REPORT_FILE_TYPES = %w[terraform].freeze UNSUPPORTED_FILE_TYPES = %i[license_management].freeze + SAST_REPORT_TYPES = %w[sast].freeze + SECRET_DETECTION_REPORT_TYPES = %w[secret_detection].freeze DEFAULT_FILE_NAMES = { archive: nil, metadata: nil, @@ -150,6 +152,14 @@ module Ci with_file_types(REPORT_TYPES.keys.map(&:to_s)) end + scope :sast_reports, -> do + with_file_types(SAST_REPORT_TYPES) + end + + scope :secret_detection_reports, -> do + with_file_types(SECRET_DETECTION_REPORT_TYPES) + end + scope :test_reports, -> do with_file_types(TEST_REPORT_FILE_TYPES) end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 54d7e92f782..58aaadd5d49 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -1219,6 +1219,16 @@ module Ci false end + def security_reports(report_types: []) + reports_scope = report_types.empty? ? ::Ci::JobArtifact.security_reports : ::Ci::JobArtifact.security_reports(file_types: report_types) + + ::Gitlab::Ci::Reports::Security::Reports.new(self).tap do |security_reports| + latest_report_builds(reports_scope).each do |build| + build.collect_security_reports!(security_reports) + end + end + end + private def add_message(severity, content) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index e41781dce4f..5fad876d3fb 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -1554,6 +1554,26 @@ class MergeRequest < ApplicationRecord end || { status: :parsing } end + def has_sast_reports? + !!actual_head_pipeline&.has_reports?(::Ci::JobArtifact.sast_reports) + end + + def has_secret_detection_reports? + !!actual_head_pipeline&.has_reports?(::Ci::JobArtifact.secret_detection_reports) + end + + def compare_sast_reports(current_user) + return missing_report_error("SAST") unless has_sast_reports? + + compare_reports(::Ci::CompareSecurityReportsService, current_user, 'sast') + end + + def compare_secret_detection_reports(current_user) + return missing_report_error("secret detection") unless has_secret_detection_reports? + + compare_reports(::Ci::CompareSecurityReportsService, current_user, 'secret_detection') + end + def calculate_reactive_cache(identifier, current_user_id = nil, report_type = nil, *args) service_class = identifier.constantize @@ -1799,8 +1819,19 @@ class MergeRequest < ApplicationRecord merge_request_reviewers.find_by(user_id: user.id) end + def enabled_reports + { + sast: report_type_enabled?(:sast), + secret_detection: report_type_enabled?(:secret_detection) + } + end + private + def missing_report_error(report_type) + { status: :error, status_reason: "This merge request does not have #{report_type} reports" } + end + def with_rebase_lock if Feature.enabled?(:merge_request_rebase_nowait_lock, default_enabled: true) with_retried_nowait_lock { yield } @@ -1842,6 +1873,10 @@ class MergeRequest < ApplicationRecord key = Gitlab::Routing.url_helpers.cached_widget_project_json_merge_request_path(project, self, format: :json) Gitlab::EtagCaching::Store.new.touch(key) end + + def report_type_enabled?(report_type) + !!actual_head_pipeline&.batch_lookup_report_artifact_for_file_type(report_type) + end end MergeRequest.prepend_if_ee('::EE::MergeRequest') diff --git a/app/serializers/merge_request_widget_entity.rb b/app/serializers/merge_request_widget_entity.rb index ca4e16bc5ff..560dd2ea08b 100644 --- a/app/serializers/merge_request_widget_entity.rb +++ b/app/serializers/merge_request_widget_entity.rb @@ -133,6 +133,10 @@ class MergeRequestWidgetEntity < Grape::Entity help_page_path('user/application_security/index.md', anchor: 'viewing-security-scan-information-in-merge-requests') end + expose :enabled_reports do |merge_request| + merge_request.enabled_reports + end + private delegate :current_user, to: :request diff --git a/app/views/groups/settings/packages_and_registries/index.html.haml b/app/views/groups/settings/packages_and_registries/index.html.haml index b6bd16d51a6..1a12ad4902b 100644 --- a/app/views/groups/settings/packages_and_registries/index.html.haml +++ b/app/views/groups/settings/packages_and_registries/index.html.haml @@ -2,4 +2,4 @@ - page_title _('Packages & Registries') - @content_class = 'limit-container-width' unless fluid_layout -%section#js-packages-and-registries-settings{ data: { default_expanded: expanded_by_default?.to_s, group_path: @group.path } } +%section#js-packages-and-registries-settings{ data: { default_expanded: expanded_by_default?.to_s, group_path: @group.full_path } } diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml index afe97a06400..5652b503a6d 100644 --- a/app/views/projects/commit/show.html.haml +++ b/app/views/projects/commit/show.html.haml @@ -12,7 +12,7 @@ .container-fluid{ class: [limited_container_width, container_class] } = render "commit_box" = render "ci_menu" - = render "projects/diffs/diffs", diffs: @diffs, environment: @environment, diff_page_context: "is-commit" + = render "projects/diffs/diffs", diffs: @diffs, environment: @environment, diff_page_context: "is-commit", paginate_diffs: true .limited-width-notes = render "shared/notes/notes_with_form", :autocomplete => true diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml index 8364311796f..2f533b5848d 100644 --- a/app/views/projects/diffs/_diffs.html.haml +++ b/app/views/projects/diffs/_diffs.html.haml @@ -1,9 +1,10 @@ - environment = local_assigns.fetch(:environment, nil) - show_whitespace_toggle = local_assigns.fetch(:show_whitespace_toggle, true) - can_create_note = !@diff_notes_disabled && can?(current_user, :create_note, diffs.project) -- diff_files = diffs.diff_files - diff_page_context = local_assigns.fetch(:diff_page_context, nil) - load_diff_files_async = Feature.enabled?(:async_commit_diff_files, @project) && diff_page_context == "is-commit" +- paginate_diffs = local_assigns.fetch(:paginate_diffs, false) && !load_diff_files_async && Feature.enabled?(:paginate_commit_view, @project, type: :development) +- diff_files = conditionally_paginate_diff_files(diffs, paginate: paginate_diffs) .content-block.oneline-block.files-changed.diff-files-changed.js-diff-files-changed .files-changed-inner @@ -27,7 +28,6 @@ - if render_overflow_warning?(diffs) = render 'projects/diffs/warning', diff_files: diffs - .files{ data: { can_create_note: can_create_note } } - if load_diff_files_async - url = url_for(safe_params.merge(action: 'diff_files')) @@ -36,3 +36,6 @@ %span.spinner.spinner-md - else = render partial: 'projects/diffs/file', collection: diff_files, as: :diff_file, locals: { project: diffs.project, environment: environment, diff_page_context: diff_page_context } + + - if paginate_diffs + = paginate(diff_files, theme: "gitlab") diff --git a/changelogs/unreleased/gy-artifact-download-multiple-types.yml b/changelogs/unreleased/gy-artifact-download-multiple-types.yml new file mode 100644 index 00000000000..15f9fa78372 --- /dev/null +++ b/changelogs/unreleased/gy-artifact-download-multiple-types.yml @@ -0,0 +1,5 @@ +--- +title: Adjust job report artifacts downloads to handle multiple types +merge_request: 53141 +author: +type: fixed diff --git a/changelogs/unreleased/paginate-commit.yml b/changelogs/unreleased/paginate-commit.yml new file mode 100644 index 00000000000..c75435cdf3b --- /dev/null +++ b/changelogs/unreleased/paginate-commit.yml @@ -0,0 +1,5 @@ +--- +title: Paginate single commit view +merge_request: 52819 +author: +type: performance diff --git a/changelogs/unreleased/sh-handle-fog-host-params.yml b/changelogs/unreleased/sh-handle-fog-host-params.yml new file mode 100644 index 00000000000..4cfcac84b6d --- /dev/null +++ b/changelogs/unreleased/sh-handle-fog-host-params.yml @@ -0,0 +1,5 @@ +--- +title: Support fog-aws host options for Workhorse S3 client +merge_request: 53326 +author: +type: fixed diff --git a/changelogs/unreleased/vs-fix-margin-for-rebase-based-workflows.yml b/changelogs/unreleased/vs-fix-margin-for-rebase-based-workflows.yml new file mode 100644 index 00000000000..1815933af09 --- /dev/null +++ b/changelogs/unreleased/vs-fix-margin-for-rebase-based-workflows.yml @@ -0,0 +1,5 @@ +--- +title: Fix left margin of Merge button in FF merge mode +merge_request: 53756 +author: +type: fixed diff --git a/config/feature_flags/development/paginate_commit_view.yml b/config/feature_flags/development/paginate_commit_view.yml new file mode 100644 index 00000000000..ee89788a219 --- /dev/null +++ b/config/feature_flags/development/paginate_commit_view.yml @@ -0,0 +1,8 @@ +--- +name: paginate_commit_view +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52819 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/300540 +milestone: '13.9' +type: development +group: group::source code +default_enabled: false diff --git a/lib/gitlab/import_export/repo_restorer.rb b/lib/gitlab/import_export/repo_restorer.rb index 7701916a855..8af7b68d78e 100644 --- a/lib/gitlab/import_export/repo_restorer.rb +++ b/lib/gitlab/import_export/repo_restorer.rb @@ -19,6 +19,9 @@ module Gitlab ensure_repository_does_not_exist! repository.create_from_bundle(path_to_bundle) + update_importable_repository_info + + true rescue => e shared.error(e) false @@ -32,6 +35,10 @@ module Gitlab attr_accessor :path_to_bundle, :shared + def update_importable_repository_info + # No-op. Overridden in EE + end + def ensure_repository_does_not_exist! if repository.exists? shared.logger.info( @@ -44,3 +51,5 @@ module Gitlab end end end + +Gitlab::ImportExport::RepoRestorer.prepend_if_ee('EE::Gitlab::ImportExport::RepoRestorer') diff --git a/lib/object_storage/config.rb b/lib/object_storage/config.rb index f933d4e4866..0e6408b4917 100644 --- a/lib/object_storage/config.rb +++ b/lib/object_storage/config.rb @@ -2,6 +2,8 @@ module ObjectStorage class Config + include Gitlab::Utils::StrongMemoize + AWS_PROVIDER = 'AWS' AZURE_PROVIDER = 'AzureRM' GOOGLE_PROVIDER = 'Google' @@ -66,6 +68,36 @@ module ObjectStorage def provider credentials[:provider].to_s end + + # This method converts fog-aws parameters to an endpoint for the + # Workhorse S3 client. + def s3_endpoint + strong_memoize(:s3_endpoint) do + # We could omit this line and let the following code handle this, but + # this will ensure that working configurations that use `endpoint` + # will continue to work. + next credentials[:endpoint] if credentials[:endpoint].present? + + generate_s3_endpoint_from_credentials + end + end + + def generate_s3_endpoint_from_credentials + # fog-aws has special handling of the host, region, scheme, etc: + # https://github.com/fog/fog-aws/blob/c7a11ba377a76d147861d0e921eb1e245bc11b6c/lib/fog/aws/storage.rb#L440-L449 + # Rather than reimplement this, we derive it from a sample GET URL. + url = fog_connection.get_object_url(bucket, "tmp", nil) + uri = ::Addressable::URI.parse(url) + + return unless uri&.scheme && uri&.host + + endpoint = "#{uri.scheme}://#{uri.host}" + endpoint += ":#{uri.port}" if uri.port + endpoint + rescue ::URI::InvalidComponentError, ::Addressable::URI::InvalidURIError => e + Gitlab::ErrorTracking.track_exception(e) + nil + end # End AWS-specific options # Begin Azure-specific options @@ -91,6 +123,10 @@ module ObjectStorage end end + def fog_connection + @connection ||= ::Fog::Storage.new(credentials) + end + private # This returns a Hash of HTTP encryption headers to send along to S3. diff --git a/lib/object_storage/direct_upload.rb b/lib/object_storage/direct_upload.rb index 7f1c30e574d..9fb4b571e06 100644 --- a/lib/object_storage/direct_upload.rb +++ b/lib/object_storage/direct_upload.rb @@ -80,7 +80,7 @@ module ObjectStorage S3Config: { Bucket: bucket_name, Region: credentials[:region], - Endpoint: credentials[:endpoint], + Endpoint: config.s3_endpoint, PathStyle: config.use_path_style?, UseIamProfile: config.use_iam_profile?, ServerSideEncryption: config.server_side_encryption, @@ -229,7 +229,7 @@ module ObjectStorage end def connection - @connection ||= ::Fog::Storage.new(credentials) + config.fog_connection end end end diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb index 0f4216708d2..c4f9a4ce82b 100644 --- a/spec/factories/ci/builds.rb +++ b/spec/factories/ci/builds.rb @@ -290,6 +290,24 @@ FactoryBot.define do end end + trait :codequality_report do + after(:build) do |build| + build.job_artifacts << create(:ci_job_artifact, :codequality, job: build) + end + end + + trait :sast_report do + after(:build) do |build| + build.job_artifacts << create(:ci_job_artifact, :sast, job: build) + end + end + + trait :secret_detection_report do + after(:build) do |build| + build.job_artifacts << create(:ci_job_artifact, :secret_detection, job: build) + end + end + trait :test_reports do after(:build) do |build| build.job_artifacts << create(:ci_job_artifact, :junit, job: build) diff --git a/spec/factories/ci/job_artifacts.rb b/spec/factories/ci/job_artifacts.rb index ad98e9d1f24..bfd8506566b 100644 --- a/spec/factories/ci/job_artifacts.rb +++ b/spec/factories/ci/job_artifacts.rb @@ -269,6 +269,26 @@ FactoryBot.define do end end + trait :sast do + file_type { :sast } + file_format { :raw } + + after(:build) do |artifact, _| + artifact.file = fixture_file_upload( + Rails.root.join('spec/fixtures/security_reports/master/gl-sast-report.json'), 'application/json') + end + end + + trait :secret_detection do + file_type { :secret_detection } + file_format { :raw } + + after(:build) do |artifact, _| + artifact.file = fixture_file_upload( + Rails.root.join('spec/fixtures/security_reports/master/gl-secret-detection-report.json'), 'application/json') + end + end + trait :lsif do file_type { :lsif } file_format { :zip } diff --git a/spec/factories/ci/pipelines.rb b/spec/factories/ci/pipelines.rb index 530bb0cd25b..e0d7ad3c133 100644 --- a/spec/factories/ci/pipelines.rb +++ b/spec/factories/ci/pipelines.rb @@ -93,6 +93,30 @@ FactoryBot.define do end end + trait :with_codequality_report do + status { :success } + + after(:build) do |pipeline, evaluator| + pipeline.builds << build(:ci_build, :codequality_report, pipeline: pipeline, project: pipeline.project) + end + end + + trait :with_sast_report do + status { :success } + + after(:build) do |pipeline, evaluator| + pipeline.builds << build(:ci_build, :sast_report, pipeline: pipeline, project: pipeline.project) + end + end + + trait :with_secret_detection_report do + status { :success } + + after(:build) do |pipeline, evaluator| + pipeline.builds << build(:ci_build, :secret_detection_report, pipeline: pipeline, project: pipeline.project) + end + end + trait :with_test_reports do status { :success } diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb index ecdb1c95866..40068d0da0d 100644 --- a/spec/factories/merge_requests.rb +++ b/spec/factories/merge_requests.rb @@ -221,6 +221,30 @@ FactoryBot.define do end end + trait :with_sast_reports do + after(:build) do |merge_request| + merge_request.head_pipeline = build( + :ci_pipeline, + :success, + :with_sast_report, + project: merge_request.source_project, + ref: merge_request.source_branch, + sha: merge_request.diff_head_sha) + end + end + + trait :with_secret_detection_reports do + after(:build) do |merge_request| + merge_request.head_pipeline = build( + :ci_pipeline, + :success, + :with_secret_detection_report, + project: merge_request.source_project, + ref: merge_request.source_branch, + sha: merge_request.diff_head_sha) + end + end + trait :with_exposed_artifacts do after(:build) do |merge_request| merge_request.head_pipeline = build( diff --git a/spec/features/commit_spec.rb b/spec/features/commit_spec.rb new file mode 100644 index 00000000000..02754cc803e --- /dev/null +++ b/spec/features/commit_spec.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Commit' do + let_it_be(:project) { create(:project, :repository) } + let_it_be(:user) { create(:user) } + + describe "single commit view" do + let(:commit) do + project.repository.commits(nil, limit: 100).find do |commit| + commit.diffs.size > 1 + end + end + + let(:files) { commit.diffs.diff_files.to_a } + + before do + stub_feature_flags(async_commit_diff_files: false) + project.add_maintainer(user) + sign_in(user) + end + + describe "commit details" do + before do + visit project_commit_path(project, commit) + end + + it "shows the short commit message" do + expect(page).to have_content(commit.title) + end + + it "reports the correct number of total changes" do + expect(page).to have_content("Changes #{commit.diffs.size}") + end + end + + context "pagination enabled" do + before do + stub_feature_flags(paginate_commit_view: true) + stub_const("Projects::CommitController::COMMIT_DIFFS_PER_PAGE", 1) + + visit project_commit_path(project, commit) + end + + it "shows an adjusted count for changed files on this page" do + expect(page).to have_content("Showing 1 changed file") + end + + it "shows only the first diff on the first page" do + expect(page).to have_selector(".files ##{files[0].file_hash}") + expect(page).not_to have_selector(".files ##{files[1].file_hash}") + end + + it "can navigate to the second page" do + within(".files .gl-pagination") do + click_on("2") + end + + expect(page).not_to have_selector(".files ##{files[0].file_hash}") + expect(page).to have_selector(".files ##{files[1].file_hash}") + end + end + + context "pagination disabled" do + before do + stub_feature_flags(paginate_commit_view: false) + + visit project_commit_path(project, commit) + end + + it "shows both diffs on the page" do + expect(page).to have_selector(".files ##{files[0].file_hash}") + expect(page).to have_selector(".files ##{files[1].file_hash}") + end + end + end +end diff --git a/spec/features/groups/settings/packages_and_registries_spec.rb b/spec/features/groups/settings/packages_and_registries_spec.rb index d53b7603058..e4078fae956 100644 --- a/spec/features/groups/settings/packages_and_registries_spec.rb +++ b/spec/features/groups/settings/packages_and_registries_spec.rb @@ -7,9 +7,11 @@ RSpec.describe 'Group Packages & Registries settings' do let(:user) { create(:user) } let(:group) { create(:group) } + let(:sub_group) { create(:group, parent: group) } before do group.add_owner(user) + sub_group.add_owner(user) sign_in(user) end @@ -85,6 +87,18 @@ RSpec.describe 'Group Packages & Registries settings' do expect(page).to have_content('is an invalid regexp') end + + context 'in a sub group' do + it 'works correctly', :js do + visit_sub_group_settings_page + + expect(page).to have_content('Allow duplicates') + + find('.gl-toggle').click + + expect(page).to have_content('Do not allow duplicates') + end + end end def find_settings_menu @@ -94,4 +108,8 @@ RSpec.describe 'Group Packages & Registries settings' do def visit_settings_page visit group_settings_packages_and_registries_path(group) end + + def visit_sub_group_settings_page + visit group_settings_packages_and_registries_path(sub_group) + end end diff --git a/spec/fixtures/security_reports/master/gl-sast-report.json b/spec/fixtures/security_reports/master/gl-sast-report.json new file mode 100644 index 00000000000..98bb15e349f --- /dev/null +++ b/spec/fixtures/security_reports/master/gl-sast-report.json @@ -0,0 +1,983 @@ +{ + "version": "1.2", + "vulnerabilities": [ + { + "category": "sast", + "message": "Probable insecure usage of temp file/directory.", + "cve": "python/hardcoded/hardcoded-tmp.py:52865813c884a507be1f152d654245af34aba8a391626d01f1ab6d3f52ec8779:B108", + "severity": "Medium", + "confidence": "Medium", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/hardcoded/hardcoded-tmp.py", + "start_line": 1, + "end_line": 1 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B108", + "value": "B108", + "url": "https://docs.openstack.org/bandit/latest/plugins/b108_hardcoded_tmp_directory.html" + } + ], + "priority": "Medium", + "file": "python/hardcoded/hardcoded-tmp.py", + "line": 1, + "url": "https://docs.openstack.org/bandit/latest/plugins/b108_hardcoded_tmp_directory.html", + "tool": "bandit" + }, + { + "category": "sast", + "name": "Predictable pseudorandom number generator", + "message": "Predictable pseudorandom number generator", + "cve": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy:47:PREDICTABLE_RANDOM", + "severity": "Medium", + "confidence": "Medium", + "scanner": { + "id": "find_sec_bugs", + "name": "Find Security Bugs" + }, + "location": { + "file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy", + "start_line": 47, + "end_line": 47, + "class": "com.gitlab.security_products.tests.App", + "method": "generateSecretToken2" + }, + "identifiers": [ + { + "type": "find_sec_bugs_type", + "name": "Find Security Bugs-PREDICTABLE_RANDOM", + "value": "PREDICTABLE_RANDOM", + "url": "https://find-sec-bugs.github.io/bugs.htm#PREDICTABLE_RANDOM" + } + ], + "priority": "Medium", + "file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy", + "line": 47, + "url": "https://find-sec-bugs.github.io/bugs.htm#PREDICTABLE_RANDOM", + "tool": "find_sec_bugs" + }, + { + "category": "sast", + "name": "Predictable pseudorandom number generator", + "message": "Predictable pseudorandom number generator", + "cve": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy:41:PREDICTABLE_RANDOM", + "severity": "Medium", + "confidence": "Medium", + "scanner": { + "id": "find_sec_bugs", + "name": "Find Security Bugs" + }, + "location": { + "file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy", + "start_line": 41, + "end_line": 41, + "class": "com.gitlab.security_products.tests.App", + "method": "generateSecretToken1" + }, + "identifiers": [ + { + "type": "find_sec_bugs_type", + "name": "Find Security Bugs-PREDICTABLE_RANDOM", + "value": "PREDICTABLE_RANDOM", + "url": "https://find-sec-bugs.github.io/bugs.htm#PREDICTABLE_RANDOM" + } + ], + "priority": "Medium", + "file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy", + "line": 41, + "url": "https://find-sec-bugs.github.io/bugs.htm#PREDICTABLE_RANDOM", + "tool": "find_sec_bugs" + }, + { + "category": "sast", + "message": "Use of insecure MD2, MD4, or MD5 hash function.", + "cve": "python/imports/imports-aliases.py:cb203b465dffb0cb3a8e8bd8910b84b93b0a5995a938e4b903dbb0cd6ffa1254:B303", + "severity": "Medium", + "confidence": "High", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/imports/imports-aliases.py", + "start_line": 11, + "end_line": 11 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B303", + "value": "B303" + } + ], + "priority": "Medium", + "file": "python/imports/imports-aliases.py", + "line": 11, + "tool": "bandit" + }, + { + "category": "sast", + "message": "Use of insecure MD2, MD4, or MD5 hash function.", + "cve": "python/imports/imports-aliases.py:a7173c43ae66bd07466632d819d450e0071e02dbf782763640d1092981f9631b:B303", + "severity": "Medium", + "confidence": "High", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/imports/imports-aliases.py", + "start_line": 12, + "end_line": 12 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B303", + "value": "B303" + } + ], + "priority": "Medium", + "file": "python/imports/imports-aliases.py", + "line": 12, + "tool": "bandit" + }, + { + "category": "sast", + "message": "Use of insecure MD2, MD4, or MD5 hash function.", + "cve": "python/imports/imports-aliases.py:017017b77deb0b8369b6065947833eeea752a92ec8a700db590fece3e934cf0d:B303", + "severity": "Medium", + "confidence": "High", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/imports/imports-aliases.py", + "start_line": 13, + "end_line": 13 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B303", + "value": "B303" + } + ], + "priority": "Medium", + "file": "python/imports/imports-aliases.py", + "line": 13, + "tool": "bandit" + }, + { + "category": "sast", + "message": "Use of insecure MD2, MD4, or MD5 hash function.", + "cve": "python/imports/imports-aliases.py:45fc8c53aea7b84f06bc4e590cc667678d6073c4c8a1d471177ca2146fb22db2:B303", + "severity": "Medium", + "confidence": "High", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/imports/imports-aliases.py", + "start_line": 14, + "end_line": 14 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B303", + "value": "B303" + } + ], + "priority": "Medium", + "file": "python/imports/imports-aliases.py", + "line": 14, + "tool": "bandit" + }, + { + "category": "sast", + "message": "Pickle library appears to be in use, possible security issue.", + "cve": "python/imports/imports-aliases.py:5f200d47291e7bbd8352db23019b85453ca048dd98ea0c291260fa7d009963a4:B301", + "severity": "Medium", + "confidence": "High", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/imports/imports-aliases.py", + "start_line": 15, + "end_line": 15 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B301", + "value": "B301" + } + ], + "priority": "Medium", + "file": "python/imports/imports-aliases.py", + "line": 15, + "tool": "bandit" + }, + { + "category": "sast", + "name": "ECB mode is insecure", + "message": "ECB mode is insecure", + "cve": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy:29:ECB_MODE", + "severity": "Medium", + "confidence": "High", + "scanner": { + "id": "find_sec_bugs", + "name": "Find Security Bugs" + }, + "location": { + "file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy", + "start_line": 29, + "end_line": 29, + "class": "com.gitlab.security_products.tests.App", + "method": "insecureCypher" + }, + "identifiers": [ + { + "type": "find_sec_bugs_type", + "name": "Find Security Bugs-ECB_MODE", + "value": "ECB_MODE", + "url": "https://find-sec-bugs.github.io/bugs.htm#ECB_MODE" + } + ], + "priority": "Medium", + "file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy", + "line": 29, + "url": "https://find-sec-bugs.github.io/bugs.htm#ECB_MODE", + "tool": "find_sec_bugs" + }, + { + "category": "sast", + "name": "Cipher with no integrity", + "message": "Cipher with no integrity", + "cve": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy:29:CIPHER_INTEGRITY", + "severity": "Medium", + "confidence": "High", + "scanner": { + "id": "find_sec_bugs", + "name": "Find Security Bugs" + }, + "location": { + "file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy", + "start_line": 29, + "end_line": 29, + "class": "com.gitlab.security_products.tests.App", + "method": "insecureCypher" + }, + "identifiers": [ + { + "type": "find_sec_bugs_type", + "name": "Find Security Bugs-CIPHER_INTEGRITY", + "value": "CIPHER_INTEGRITY", + "url": "https://find-sec-bugs.github.io/bugs.htm#CIPHER_INTEGRITY" + } + ], + "priority": "Medium", + "file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy", + "line": 29, + "url": "https://find-sec-bugs.github.io/bugs.htm#CIPHER_INTEGRITY", + "tool": "find_sec_bugs" + }, + { + "category": "sast", + "message": "Probable insecure usage of temp file/directory.", + "cve": "python/hardcoded/hardcoded-tmp.py:63dd4d626855555b816985d82c4614a790462a0a3ada89dc58eb97f9c50f3077:B108", + "severity": "Medium", + "confidence": "Medium", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/hardcoded/hardcoded-tmp.py", + "start_line": 14, + "end_line": 14 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B108", + "value": "B108", + "url": "https://docs.openstack.org/bandit/latest/plugins/b108_hardcoded_tmp_directory.html" + } + ], + "priority": "Medium", + "file": "python/hardcoded/hardcoded-tmp.py", + "line": 14, + "url": "https://docs.openstack.org/bandit/latest/plugins/b108_hardcoded_tmp_directory.html", + "tool": "bandit" + }, + { + "category": "sast", + "message": "Probable insecure usage of temp file/directory.", + "cve": "python/hardcoded/hardcoded-tmp.py:4ad6d4c40a8c263fc265f3384724014e0a4f8dd6200af83e51ff120420038031:B108", + "severity": "Medium", + "confidence": "Medium", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/hardcoded/hardcoded-tmp.py", + "start_line": 10, + "end_line": 10 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B108", + "value": "B108", + "url": "https://docs.openstack.org/bandit/latest/plugins/b108_hardcoded_tmp_directory.html" + } + ], + "priority": "Medium", + "file": "python/hardcoded/hardcoded-tmp.py", + "line": 10, + "url": "https://docs.openstack.org/bandit/latest/plugins/b108_hardcoded_tmp_directory.html", + "tool": "bandit" + }, + { + "category": "sast", + "message": "Consider possible security implications associated with Popen module.", + "cve": "python/imports/imports-aliases.py:2c3e1fa1e54c3c6646e8bcfaee2518153c6799b77587ff8d9a7b0631f6d34785:B404", + "severity": "Low", + "confidence": "High", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/imports/imports-aliases.py", + "start_line": 1, + "end_line": 1 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B404", + "value": "B404" + } + ], + "priority": "Low", + "file": "python/imports/imports-aliases.py", + "line": 1, + "tool": "bandit" + }, + { + "category": "sast", + "message": "Consider possible security implications associated with pickle module.", + "cve": "python/imports/imports.py:af58d07f6ad519ef5287fcae65bf1a6999448a1a3a8bc1ac2a11daa80d0b96bf:B403", + "severity": "Low", + "confidence": "High", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/imports/imports.py", + "start_line": 2, + "end_line": 2 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B403", + "value": "B403" + } + ], + "priority": "Low", + "file": "python/imports/imports.py", + "line": 2, + "tool": "bandit" + }, + { + "category": "sast", + "message": "Consider possible security implications associated with subprocess module.", + "cve": "python/imports/imports.py:8de9bc98029d212db530785a5f6780cfa663548746ff228ab8fa96c5bb82f089:B404", + "severity": "Low", + "confidence": "High", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/imports/imports.py", + "start_line": 4, + "end_line": 4 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B404", + "value": "B404" + } + ], + "priority": "Low", + "file": "python/imports/imports.py", + "line": 4, + "tool": "bandit" + }, + { + "category": "sast", + "message": "Possible hardcoded password: 'blerg'", + "cve": "python/hardcoded/hardcoded-passwords.py:97c30f1d76d2a88913e3ce9ae74087874d740f87de8af697a9c455f01119f633:B106", + "severity": "Low", + "confidence": "Medium", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/hardcoded/hardcoded-passwords.py", + "start_line": 22, + "end_line": 22 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B106", + "value": "B106", + "url": "https://docs.openstack.org/bandit/latest/plugins/b106_hardcoded_password_funcarg.html" + } + ], + "priority": "Low", + "file": "python/hardcoded/hardcoded-passwords.py", + "line": 22, + "url": "https://docs.openstack.org/bandit/latest/plugins/b106_hardcoded_password_funcarg.html", + "tool": "bandit" + }, + { + "category": "sast", + "message": "Possible hardcoded password: 'root'", + "cve": "python/hardcoded/hardcoded-passwords.py:7431c73a0bc16d94ece2a2e75ef38f302574d42c37ac0c3c38ad0b3bf8a59f10:B105", + "severity": "Low", + "confidence": "Medium", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/hardcoded/hardcoded-passwords.py", + "start_line": 5, + "end_line": 5 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B105", + "value": "B105", + "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html" + } + ], + "priority": "Low", + "file": "python/hardcoded/hardcoded-passwords.py", + "line": 5, + "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html", + "tool": "bandit" + }, + { + "category": "sast", + "message": "Possible hardcoded password: ''", + "cve": "python/hardcoded/hardcoded-passwords.py:d2d1857c27caedd49c57bfbcdc23afcc92bd66a22701fcdc632869aab4ca73ee:B105", + "severity": "Low", + "confidence": "Medium", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/hardcoded/hardcoded-passwords.py", + "start_line": 9, + "end_line": 9 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B105", + "value": "B105", + "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html" + } + ], + "priority": "Low", + "file": "python/hardcoded/hardcoded-passwords.py", + "line": 9, + "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html", + "tool": "bandit" + }, + { + "category": "sast", + "message": "Possible hardcoded password: 'ajklawejrkl42348swfgkg'", + "cve": "python/hardcoded/hardcoded-passwords.py:fb3866215a61393a5c9c32a3b60e2058171a23219c353f722cbd3567acab21d2:B105", + "severity": "Low", + "confidence": "Medium", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/hardcoded/hardcoded-passwords.py", + "start_line": 13, + "end_line": 13 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B105", + "value": "B105", + "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html" + } + ], + "priority": "Low", + "file": "python/hardcoded/hardcoded-passwords.py", + "line": 13, + "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html", + "tool": "bandit" + }, + { + "category": "sast", + "message": "Possible hardcoded password: 'blerg'", + "cve": "python/hardcoded/hardcoded-passwords.py:63c62a8b7e1e5224439bd26b28030585ac48741e28ca64561a6071080c560a5f:B105", + "severity": "Low", + "confidence": "Medium", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/hardcoded/hardcoded-passwords.py", + "start_line": 23, + "end_line": 23 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B105", + "value": "B105", + "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html" + } + ], + "priority": "Low", + "file": "python/hardcoded/hardcoded-passwords.py", + "line": 23, + "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html", + "tool": "bandit" + }, + { + "category": "sast", + "message": "Possible hardcoded password: 'blerg'", + "cve": "python/hardcoded/hardcoded-passwords.py:4311b06d08df8fa58229b341c531da8e1a31ec4520597bdff920cd5c098d86f9:B105", + "severity": "Low", + "confidence": "Medium", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/hardcoded/hardcoded-passwords.py", + "start_line": 24, + "end_line": 24 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B105", + "value": "B105", + "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html" + } + ], + "priority": "Low", + "file": "python/hardcoded/hardcoded-passwords.py", + "line": 24, + "url": "https://docs.openstack.org/bandit/latest/plugins/b105_hardcoded_password_string.html", + "tool": "bandit" + }, + { + "category": "sast", + "message": "Consider possible security implications associated with subprocess module.", + "cve": "python/imports/imports-function.py:5858400c2f39047787702de44d03361ef8d954c9d14bd54ee1c2bef9e6a7df93:B404", + "severity": "Low", + "confidence": "High", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/imports/imports-function.py", + "start_line": 4, + "end_line": 4 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B404", + "value": "B404" + } + ], + "priority": "Low", + "file": "python/imports/imports-function.py", + "line": 4, + "tool": "bandit" + }, + { + "category": "sast", + "message": "Consider possible security implications associated with pickle module.", + "cve": "python/imports/imports-function.py:dbda3cf4190279d30e0aad7dd137eca11272b0b225e8af4e8bf39682da67d956:B403", + "severity": "Low", + "confidence": "High", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/imports/imports-function.py", + "start_line": 2, + "end_line": 2 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B403", + "value": "B403" + } + ], + "priority": "Low", + "file": "python/imports/imports-function.py", + "line": 2, + "tool": "bandit" + }, + { + "category": "sast", + "message": "Consider possible security implications associated with Popen module.", + "cve": "python/imports/imports-from.py:eb8a0db9cd1a8c1ab39a77e6025021b1261cc2a0b026b2f4a11fca4e0636d8dd:B404", + "severity": "Low", + "confidence": "High", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/imports/imports-from.py", + "start_line": 7, + "end_line": 7 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B404", + "value": "B404" + } + ], + "priority": "Low", + "file": "python/imports/imports-from.py", + "line": 7, + "tool": "bandit" + }, + { + "category": "sast", + "message": "subprocess call with shell=True seems safe, but may be changed in the future, consider rewriting without shell", + "cve": "python/imports/imports-aliases.py:f99f9721e27537fbcb6699a4cf39c6740d6234d2c6f06cfc2d9ea977313c483d:B602", + "severity": "Low", + "confidence": "High", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/imports/imports-aliases.py", + "start_line": 9, + "end_line": 9 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B602", + "value": "B602", + "url": "https://docs.openstack.org/bandit/latest/plugins/b602_subprocess_popen_with_shell_equals_true.html" + } + ], + "priority": "Low", + "file": "python/imports/imports-aliases.py", + "line": 9, + "url": "https://docs.openstack.org/bandit/latest/plugins/b602_subprocess_popen_with_shell_equals_true.html", + "tool": "bandit" + }, + { + "category": "sast", + "message": "Consider possible security implications associated with subprocess module.", + "cve": "python/imports/imports-from.py:332a12ab1146698f614a905ce6a6a5401497a12281aef200e80522711c69dcf4:B404", + "severity": "Low", + "confidence": "High", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/imports/imports-from.py", + "start_line": 6, + "end_line": 6 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B404", + "value": "B404" + } + ], + "priority": "Low", + "file": "python/imports/imports-from.py", + "line": 6, + "tool": "bandit" + }, + { + "category": "sast", + "message": "Consider possible security implications associated with Popen module.", + "cve": "python/imports/imports-from.py:0a48de4a3d5348853a03666cb574697e3982998355e7a095a798bd02a5947276:B404", + "severity": "Low", + "confidence": "High", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/imports/imports-from.py", + "start_line": 1, + "end_line": 2 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B404", + "value": "B404" + } + ], + "priority": "Low", + "file": "python/imports/imports-from.py", + "line": 1, + "tool": "bandit" + }, + { + "category": "sast", + "message": "Consider possible security implications associated with pickle module.", + "cve": "python/imports/imports-aliases.py:51b71661dff994bde3529639a727a678c8f5c4c96f00d300913f6d5be1bbdf26:B403", + "severity": "Low", + "confidence": "High", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/imports/imports-aliases.py", + "start_line": 7, + "end_line": 8 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B403", + "value": "B403" + } + ], + "priority": "Low", + "file": "python/imports/imports-aliases.py", + "line": 7, + "tool": "bandit" + }, + { + "category": "sast", + "message": "Consider possible security implications associated with loads module.", + "cve": "python/imports/imports-aliases.py:6ff02aeb3149c01ab68484d794a94f58d5d3e3bb0d58557ef4153644ea68ea54:B403", + "severity": "Low", + "confidence": "High", + "scanner": { + "id": "bandit", + "name": "Bandit" + }, + "location": { + "file": "python/imports/imports-aliases.py", + "start_line": 6, + "end_line": 6 + }, + "identifiers": [ + { + "type": "bandit_test_id", + "name": "Bandit Test ID B403", + "value": "B403" + } + ], + "priority": "Low", + "file": "python/imports/imports-aliases.py", + "line": 6, + "tool": "bandit" + }, + { + "category": "sast", + "message": "Statically-sized arrays can be improperly restricted, leading to potential overflows or other issues (CWE-119!/CWE-120)", + "cve": "c/subdir/utils.c:b466873101951fe96e1332f6728eb7010acbbd5dfc3b65d7d53571d091a06d9e:CWE-119!/CWE-120", + "confidence": "Low", + "solution": "Perform bounds checking, use functions that limit length, or ensure that the size is larger than the maximum possible length", + "scanner": { + "id": "flawfinder", + "name": "Flawfinder" + }, + "location": { + "file": "c/subdir/utils.c", + "start_line": 4 + }, + "identifiers": [ + { + "type": "flawfinder_func_name", + "name": "Flawfinder - char", + "value": "char" + }, + { + "type": "cwe", + "name": "CWE-119", + "value": "119", + "url": "https://cwe.mitre.org/data/definitions/119.html" + }, + { + "type": "cwe", + "name": "CWE-120", + "value": "120", + "url": "https://cwe.mitre.org/data/definitions/120.html" + } + ], + "file": "c/subdir/utils.c", + "line": 4, + "url": "https://cwe.mitre.org/data/definitions/119.html", + "tool": "flawfinder" + }, + { + "category": "sast", + "message": "Check when opening files - can an attacker redirect it (via symlinks), force the opening of special file type (e.g., device files), move things around to create a race condition, control its ancestors, or change its contents? (CWE-362)", + "cve": "c/subdir/utils.c:bab681140fcc8fc3085b6bba74081b44ea145c1c98b5e70cf19ace2417d30770:CWE-362", + "confidence": "Low", + "scanner": { + "id": "flawfinder", + "name": "Flawfinder" + }, + "location": { + "file": "c/subdir/utils.c", + "start_line": 8 + }, + "identifiers": [ + { + "type": "flawfinder_func_name", + "name": "Flawfinder - fopen", + "value": "fopen" + }, + { + "type": "cwe", + "name": "CWE-362", + "value": "362", + "url": "https://cwe.mitre.org/data/definitions/362.html" + } + ], + "file": "c/subdir/utils.c", + "line": 8, + "url": "https://cwe.mitre.org/data/definitions/362.html", + "tool": "flawfinder" + }, + { + "category": "sast", + "message": "Statically-sized arrays can be improperly restricted, leading to potential overflows or other issues (CWE-119!/CWE-120)", + "cve": "cplusplus/src/hello.cpp:c8c6dd0afdae6814194cf0930b719f757ab7b379cf8f261e7f4f9f2f323a818a:CWE-119!/CWE-120", + "confidence": "Low", + "solution": "Perform bounds checking, use functions that limit length, or ensure that the size is larger than the maximum possible length", + "scanner": { + "id": "flawfinder", + "name": "Flawfinder" + }, + "location": { + "file": "cplusplus/src/hello.cpp", + "start_line": 6 + }, + "identifiers": [ + { + "type": "flawfinder_func_name", + "name": "Flawfinder - char", + "value": "char" + }, + { + "type": "cwe", + "name": "CWE-119", + "value": "119", + "url": "https://cwe.mitre.org/data/definitions/119.html" + }, + { + "type": "cwe", + "name": "CWE-120", + "value": "120", + "url": "https://cwe.mitre.org/data/definitions/120.html" + } + ], + "file": "cplusplus/src/hello.cpp", + "line": 6, + "url": "https://cwe.mitre.org/data/definitions/119.html", + "tool": "flawfinder" + }, + { + "category": "sast", + "message": "Does not check for buffer overflows when copying to destination [MS-banned] (CWE-120)", + "cve": "cplusplus/src/hello.cpp:331c04062c4fe0c7c486f66f59e82ad146ab33cdd76ae757ca41f392d568cbd0:CWE-120", + "confidence": "Low", + "solution": "Consider using snprintf, strcpy_s, or strlcpy (warning: strncpy easily misused)", + "scanner": { + "id": "flawfinder", + "name": "Flawfinder" + }, + "location": { + "file": "cplusplus/src/hello.cpp", + "start_line": 7 + }, + "identifiers": [ + { + "type": "flawfinder_func_name", + "name": "Flawfinder - strcpy", + "value": "strcpy" + }, + { + "type": "cwe", + "name": "CWE-120", + "value": "120", + "url": "https://cwe.mitre.org/data/definitions/120.html" + } + ], + "file": "cplusplus/src/hello.cpp", + "line": 7, + "url": "https://cwe.mitre.org/data/definitions/120.html", + "tool": "flawfinder" + } + ], + "remediations": [], + "scan": { + "scanner": { + "id": "gosec", + "name": "Gosec", + "url": "https://github.com/securego/gosec", + "vendor": { + "name": "GitLab" + }, + "version": "2.3.0" + }, + "type": "sast", + "status": "success", + "start_time": "placeholder-value", + "end_time": "placeholder-value" + } +} diff --git a/spec/fixtures/security_reports/master/gl-secret-detection-report.json b/spec/fixtures/security_reports/master/gl-secret-detection-report.json new file mode 100644 index 00000000000..f0250ec9145 --- /dev/null +++ b/spec/fixtures/security_reports/master/gl-secret-detection-report.json @@ -0,0 +1,33 @@ +{ + "version": "3.0", + "vulnerabilities": [ + { + "id": "27d2322d519c94f803ffed1cf6d14e455df97e5a0668e229eb853fdb0d277d2c", + "category": "secret_detection", + "name": "AWS API key", + "message": "AWS API key", + "description": "Historic AWS secret has been found in commit 0830d9e4c0b43c0533cde798841b499e9df0653a.", + "cve": "aws-key.py:e275768c071cf6a6ea70a70b40f27c98debfe26bfe623c1539ec21c4478c6fca:AWS", + "severity": "Critical", + "confidence": "Unknown", + "scanner": { + "id": "gitleaks", + "name": "Gitleaks" + }, + "location": { + "file": "aws-key.py", + "dependency": { + "package": {} + } + }, + "identifiers": [ + { + "type": "gitleaks_rule_id", + "name": "Gitleaks rule ID AWS", + "value": "AWS" + } + ] + } + ], + "remediations": [] +} diff --git a/spec/helpers/commits_helper_spec.rb b/spec/helpers/commits_helper_spec.rb index 45485a12574..2f5f4c4596b 100644 --- a/spec/helpers/commits_helper_spec.rb +++ b/spec/helpers/commits_helper_spec.rb @@ -176,4 +176,77 @@ RSpec.describe CommitsHelper do expect(helper.commit_path(project, commit)).to eq(project_commit_path(project, commit)) end end + + describe "#conditionally_paginate_diff_files" do + let(:diffs_collection) { instance_double(Gitlab::Diff::FileCollection::Commit, diff_files: diff_files) } + let(:diff_files) { Gitlab::Git::DiffCollection.new(files) } + let(:page) { nil } + + let(:files) do + Array.new(85).map do + { too_large: false, diff: "" } + end + end + + let(:params) do + { + page: page + } + end + + subject { helper.conditionally_paginate_diff_files(diffs_collection, paginate: paginate) } + + before do + allow(helper).to receive(:params).and_return(params) + end + + context "pagination is enabled" do + let(:paginate) { true } + + it "has been paginated" do + expect(subject).to be_an(Array) + end + + it "can change the number of items per page" do + commits = helper.conditionally_paginate_diff_files(diffs_collection, paginate: paginate, per: 10) + + expect(commits).to be_an(Array) + expect(commits.size).to eq(10) + end + + context "page 1" do + let(:page) { 1 } + + it "has 20 diffs" do + expect(subject.size).to eq(75) + end + end + + context "page 2" do + let(:page) { 2 } + + it "has the remaining 10 diffs" do + expect(subject.size).to eq(10) + end + end + end + + context "pagination is disabled" do + let(:paginate) { false } + + it "returns a standard DiffCollection" do + expect(subject).to be_a(Gitlab::Git::DiffCollection) + end + end + + context "feature flag is disabled" do + let(:paginate) { true } + + it "returns a standard DiffCollection" do + stub_feature_flags(paginate_commit_view: false) + + expect(subject).to be_a(Gitlab::Git::DiffCollection) + end + end + end end diff --git a/spec/lib/object_storage/config_spec.rb b/spec/lib/object_storage/config_spec.rb index 0ead2a1d269..1361d80fe75 100644 --- a/spec/lib/object_storage/config_spec.rb +++ b/spec/lib/object_storage/config_spec.rb @@ -1,8 +1,7 @@ # frozen_string_literal: true -require 'fast_spec_helper' +require 'spec_helper' require 'rspec-parameterized' -require 'fog/core' RSpec.describe ObjectStorage::Config do using RSpec::Parameterized::TableSyntax @@ -34,7 +33,9 @@ RSpec.describe ObjectStorage::Config do } end - subject { described_class.new(raw_config.as_json) } + subject do + described_class.new(raw_config.as_json) + end describe '#load_provider' do before do @@ -45,6 +46,10 @@ RSpec.describe ObjectStorage::Config do it 'registers AWS as a provider' do expect(Fog.providers.keys).to include(:aws) end + + describe '#fog_connection' do + it { expect(subject.fog_connection).to be_a_kind_of(Fog::AWS::Storage::Real) } + end end context 'with Google' do @@ -59,6 +64,10 @@ RSpec.describe ObjectStorage::Config do it 'registers Google as a provider' do expect(Fog.providers.keys).to include(:google) end + + describe '#fog_connection' do + it { expect(subject.fog_connection).to be_a_kind_of(Fog::Storage::GoogleXML::Real) } + end end context 'with Azure' do @@ -73,6 +82,10 @@ RSpec.describe ObjectStorage::Config do it 'registers AzureRM as a provider' do expect(Fog.providers.keys).to include(:azurerm) end + + describe '#fog_connection' do + it { expect(subject.fog_connection).to be_a_kind_of(Fog::Storage::AzureRM::Real) } + end end end @@ -170,6 +183,50 @@ RSpec.describe ObjectStorage::Config do it { expect(subject.provider).to eq('AWS') } it { expect(subject.aws?).to be true } it { expect(subject.google?).to be false } + + it 'returns the default S3 endpoint' do + subject.load_provider + + expect(subject.s3_endpoint).to eq("https://test-bucket.s3.amazonaws.com") + end + + describe 'with a custom endpoint' do + let(:endpoint) { 'https://my.example.com' } + + before do + credentials[:endpoint] = endpoint + end + + it 'returns the custom endpoint' do + subject.load_provider + + expect(subject.s3_endpoint).to eq(endpoint) + end + end + + context 'with custom S3 host and port' do + where(:host, :port, :scheme, :expected) do + 's3.example.com' | 8080 | nil | 'https://test-bucket.s3.example.com:8080' + 's3.example.com' | 443 | nil | 'https://test-bucket.s3.example.com' + 's3.example.com' | 443 | "https" | 'https://test-bucket.s3.example.com' + 's3.example.com' | nil | nil | 'https://test-bucket.s3.example.com' + 's3.example.com' | 80 | "http" | 'http://test-bucket.s3.example.com' + 's3.example.com' | "bogus" | nil | nil + end + + with_them do + before do + credentials[:host] = host + credentials[:port] = port + credentials[:scheme] = scheme + subject.load_provider + end + + it 'returns expected host' do + expect(subject.s3_endpoint).to eq(expected) + end + end + end end context 'with Google credentials' do diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 63a626d4c8e..4ad7ce70a44 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -1860,7 +1860,7 @@ RSpec.describe Ci::Build do subject { build.artifacts_file_for_type(file_type) } it 'queries artifacts for type' do - expect(build).to receive_message_chain(:job_artifacts, :find_by).with(file_type: Ci::JobArtifact.file_types[file_type]) + expect(build).to receive_message_chain(:job_artifacts, :find_by).with(file_type: [Ci::JobArtifact.file_types[file_type]]) subject end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 9ead3d531d5..ebe2cd2ac03 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -2054,6 +2054,50 @@ RSpec.describe MergeRequest, factory_default: :keep do end end + describe '#has_sast_reports?' do + subject { merge_request.has_sast_reports? } + + let(:project) { create(:project, :repository) } + + before do + stub_licensed_features(sast: true) + end + + context 'when head pipeline has sast reports' do + let(:merge_request) { create(:merge_request, :with_sast_reports, source_project: project) } + + it { is_expected.to be_truthy } + end + + context 'when head pipeline does not have sast reports' do + let(:merge_request) { create(:merge_request, source_project: project) } + + it { is_expected.to be_falsey } + end + end + + describe '#has_secret_detection_reports?' do + subject { merge_request.has_secret_detection_reports? } + + let(:project) { create(:project, :repository) } + + before do + stub_licensed_features(secret_detection: true) + end + + context 'when head pipeline has secret detection reports' do + let(:merge_request) { create(:merge_request, :with_secret_detection_reports, source_project: project) } + + it { is_expected.to be_truthy } + end + + context 'when head pipeline does not have secrets detection reports' do + let(:merge_request) { create(:merge_request, source_project: project) } + + it { is_expected.to be_falsey } + end + end + describe '#calculate_reactive_cache' do let(:merge_request) { create(:merge_request) } @@ -4587,4 +4631,34 @@ RSpec.describe MergeRequest, factory_default: :keep do .from(nil).to(ref) end end + + describe '#enabled_reports' do + let(:project) { create(:project, :repository) } + + where(:report_type, :with_reports, :feature) do + :sast | :with_sast_reports | :sast + :secret_detection | :with_secret_detection_reports | :secret_detection + end + + with_them do + subject { merge_request.enabled_reports[report_type] } + + before do + stub_feature_flags(drop_license_management_artifact: false) + stub_licensed_features({ feature => true }) + end + + context "when head pipeline has reports" do + let(:merge_request) { create(:merge_request, with_reports, source_project: project) } + + it { is_expected.to be_truthy } + end + + context "when head pipeline does not have reports" do + let(:merge_request) { create(:merge_request, source_project: project) } + + it { is_expected.to be_falsy } + end + end + end end |