summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorrubenmoya <rmoyarodriguez@gmail.com>2019-01-05 09:40:05 +0100
committerrubenmoya <rmoyarodriguez@gmail.com>2019-01-05 09:40:05 +0100
commitcf5a9d2993c2998e6394560f5c4fe2fef3f35b1c (patch)
treedbd6f1c6a9c7878122f485300795d5b4b5b621e8 /spec
parent2269061e7151718d750bef4bbf1348dae8ac8a4a (diff)
parentd432d674148601555c4ba693bb7c282ac9fe3d4a (diff)
downloadgitlab-ce-cf5a9d2993c2998e6394560f5c4fe2fef3f35b1c.tar.gz
Merge branch 'master' into 54311-fix-board-add-label
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/dashboard/milestones_controller_spec.rb2
-rw-r--r--spec/controllers/groups/milestones_controller_spec.rb2
-rw-r--r--spec/controllers/groups/settings/ci_cd_controller_spec.rb55
-rw-r--r--spec/controllers/projects/avatars_controller_spec.rb36
-rw-r--r--spec/controllers/projects/ci/lints_controller_spec.rb1
-rw-r--r--spec/controllers/projects/jobs_controller_spec.rb35
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb64
-rw-r--r--spec/controllers/projects/raw_controller_spec.rb75
-rw-r--r--spec/controllers/projects/snippets_controller_spec.rb40
-rw-r--r--spec/controllers/projects/wikis_controller_spec.rb85
-rw-r--r--spec/controllers/projects_controller_spec.rb24
-rw-r--r--spec/controllers/snippets_controller_spec.rb41
-rw-r--r--spec/db/importers/common_metrics_importer_spec.rb8
-rw-r--r--spec/factories/appearances.rb4
-rw-r--r--spec/factories/releases.rb7
-rw-r--r--spec/factories/releases/link.rb9
-rw-r--r--spec/features/dashboard/merge_requests_spec.rb1
-rw-r--r--spec/features/dashboard/root_explore_spec.rb33
-rw-r--r--spec/features/explore/user_explores_projects_spec.rb18
-rw-r--r--spec/features/group_variables_spec.rb2
-rw-r--r--spec/features/groups/milestone_spec.rb5
-rw-r--r--spec/features/groups/milestones_sorting_spec.rb1
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb44
-rw-r--r--spec/features/merge_request/user_creates_image_diff_notes_spec.rb33
-rw-r--r--spec/features/merge_request/user_sees_diff_spec.rb70
-rw-r--r--spec/features/merge_request/user_sees_versions_spec.rb11
-rw-r--r--spec/features/merge_request/user_tries_to_access_private_repository_through_new_mr_spec.rb37
-rw-r--r--spec/features/profiles/user_edit_profile_spec.rb14
-rw-r--r--spec/features/projects/clusters/gcp_spec.rb56
-rw-r--r--spec/features/projects/clusters/user_spec.rb36
-rw-r--r--spec/features/projects/commits/user_browses_commits_spec.rb2
-rw-r--r--spec/features/runners_spec.rb3
-rw-r--r--spec/features/users/overview_spec.rb3
-rw-r--r--spec/finders/releases_finder_spec.rb42
-rw-r--r--spec/fixtures/api/schemas/entities/diff_viewer.json16
-rw-r--r--spec/fixtures/api/schemas/release.json37
-rw-r--r--spec/fixtures/api/schemas/releases.json4
-rw-r--r--spec/fixtures/emails/merge_request_multiple_patches.eml2
-rw-r--r--spec/fixtures/emails/merge_request_with_conflicting_patch.eml2
-rw-r--r--spec/fixtures/emails/merge_request_with_patch_and_target_branch.eml2
-rw-r--r--spec/fixtures/emails/valid_merge_request_with_patch.eml2
-rw-r--r--spec/fixtures/emails/valid_new_issue.eml4
-rw-r--r--spec/fixtures/emails/valid_new_issue_empty.eml4
-rw-r--r--spec/fixtures/emails/valid_new_issue_legacy.eml23
-rw-r--r--spec/fixtures/emails/valid_new_issue_with_quote.eml4
-rw-r--r--spec/fixtures/emails/valid_new_merge_request.eml6
-rw-r--r--spec/fixtures/emails/valid_new_merge_request_legacy.eml20
-rw-r--r--spec/fixtures/emails/valid_new_merge_request_no_description.eml4
-rw-r--r--spec/fixtures/emails/valid_new_merge_request_no_subject.eml4
-rw-r--r--spec/fixtures/emails/wrong_issue_incoming_email_token.eml (renamed from spec/fixtures/emails/wrong_incoming_email_token.eml)4
-rw-r--r--spec/fixtures/emails/wrong_merge_request_incoming_email_token.eml18
-rw-r--r--spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml1
-rw-r--r--spec/fixtures/security-reports/deprecated/gl-dependency-scanning-report.json178
-rw-r--r--spec/fixtures/security-reports/deprecated/gl-sast-report.json944
-rw-r--r--spec/fixtures/security-reports/feature-branch.zipbin7163 -> 7140 bytes
-rw-r--r--spec/fixtures/security-reports/feature-branch/gl-dependency-scanning-report.json337
-rw-r--r--spec/fixtures/security-reports/feature-branch/gl-license-management-report.json222
-rw-r--r--spec/fixtures/security-reports/feature-branch/gl-sast-report.json1825
-rw-r--r--spec/fixtures/security-reports/master.zipbin6710 -> 9413 bytes
-rw-r--r--spec/fixtures/security-reports/master/gl-dependency-scanning-report.json337
-rw-r--r--spec/fixtures/security-reports/master/gl-license-management-report.json705
-rw-r--r--spec/fixtures/security-reports/master/gl-sast-report.json1825
-rw-r--r--spec/helpers/application_settings_helper_spec.rb39
-rw-r--r--spec/helpers/diff_helper_spec.rb37
-rw-r--r--spec/helpers/runners_helper_spec.rb36
-rw-r--r--spec/initializers/active_record_locking_spec.rb44
-rw-r--r--spec/javascripts/blob_edit/blob_bundle_spec.js13
-rw-r--r--spec/javascripts/lib/utils/text_markdown_spec.js387
-rw-r--r--spec/javascripts/notes/components/discussion_filter_spec.js36
-rw-r--r--spec/javascripts/user_popovers_spec.js25
-rw-r--r--spec/javascripts/vue_mr_widget/mr_widget_options_spec.js49
-rw-r--r--spec/javascripts/vue_shared/components/callout_spec.js65
-rw-r--r--spec/javascripts/vue_shared/components/gl_modal_vuex_spec.js151
-rw-r--r--spec/javascripts/vue_shared/components/user_popover/user_popover_spec.js9
-rw-r--r--spec/javascripts/vuex_shared/modules/modal/actions_spec.js31
-rw-r--r--spec/javascripts/vuex_shared/modules/modal/mutations_spec.js49
-rw-r--r--spec/lib/api/api_spec.rb21
-rw-r--r--spec/lib/api/helpers/version_spec.rb26
-rw-r--r--spec/lib/api/helpers_spec.rb32
-rw-r--r--spec/lib/banzai/filter/external_link_filter_spec.rb8
-rw-r--r--spec/lib/banzai/filter/label_reference_filter_spec.rb18
-rw-r--r--spec/lib/banzai/filter/milestone_reference_filter_spec.rb30
-rw-r--r--spec/lib/gitlab/blob_helper_spec.rb4
-rw-r--r--spec/lib/gitlab/checks/diff_check_spec.rb38
-rw-r--r--spec/lib/gitlab/checks/push_check_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/external/file/base_spec.rb36
-rw-r--r--spec/lib/gitlab/ci/config/external/file/local_spec.rb34
-rw-r--r--spec/lib/gitlab/ci/config/external/file/remote_spec.rb31
-rw-r--r--spec/lib/gitlab/ci/config/external/file/template_spec.rb93
-rw-r--r--spec/lib/gitlab/ci/config/external/mapper_spec.rb116
-rw-r--r--spec/lib/gitlab/ci/config/external/processor_spec.rb7
-rw-r--r--spec/lib/gitlab/ci/config_spec.rb18
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/command_spec.rb20
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb21
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/build_spec.rb3
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb3
-rw-r--r--spec/lib/gitlab/cleanup/remote_uploads_spec.rb2
-rw-r--r--spec/lib/gitlab/current_settings_spec.rb80
-rw-r--r--spec/lib/gitlab/diff/file_spec.rb8
-rw-r--r--spec/lib/gitlab/diff/lines_unfolder_spec.rb2
-rw-r--r--spec/lib/gitlab/discussions_diff/file_collection_spec.rb61
-rw-r--r--spec/lib/gitlab/discussions_diff/highlight_cache_spec.rb102
-rw-r--r--spec/lib/gitlab/email/handler/create_issue_handler_spec.rb65
-rw-r--r--spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb75
-rw-r--r--spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb38
-rw-r--r--spec/lib/gitlab/email/handler_spec.rb3
-rw-r--r--spec/lib/gitlab/git/blob_spec.rb8
-rw-r--r--spec/lib/gitlab/git_access_spec.rb36
-rw-r--r--spec/lib/gitlab/git_access_wiki_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/blobs_stitcher_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml3
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml7
-rw-r--r--spec/lib/gitlab/incoming_email_spec.rb2
-rw-r--r--spec/lib/gitlab/legacy_github_import/importer_spec.rb2
-rw-r--r--spec/lib/gitlab/middleware/go_spec.rb43
-rw-r--r--spec/lib/gitlab/prometheus/metric_group_spec.rb7
-rw-r--r--spec/lib/json_web_token/rsa_token_spec.rb4
-rw-r--r--spec/models/appearance_spec.rb30
-rw-r--r--spec/models/blob_spec.rb22
-rw-r--r--spec/models/ci/build_spec.rb13
-rw-r--r--spec/models/ci/pipeline_spec.rb4
-rw-r--r--spec/models/ci/runner_spec.rb9
-rw-r--r--spec/models/clusters/applications/cert_manager_spec.rb14
-rw-r--r--spec/models/clusters/applications/helm_spec.rb10
-rw-r--r--spec/models/clusters/applications/ingress_spec.rb8
-rw-r--r--spec/models/clusters/applications/jupyter_spec.rb8
-rw-r--r--spec/models/clusters/applications/knative_spec.rb17
-rw-r--r--spec/models/clusters/applications/prometheus_spec.rb23
-rw-r--r--spec/models/clusters/applications/runner_spec.rb14
-rw-r--r--spec/models/clusters/cluster_spec.rb1
-rw-r--r--spec/models/clusters/platforms/kubernetes_spec.rb15
-rw-r--r--spec/models/clusters/providers/gcp_spec.rb12
-rw-r--r--spec/models/concerns/cacheable_attributes_spec.rb8
-rw-r--r--spec/models/concerns/has_ref_spec.rb59
-rw-r--r--spec/models/diff_viewer/base_spec.rb23
-rw-r--r--spec/models/diff_viewer/server_side_spec.rb20
-rw-r--r--spec/models/event_spec.rb18
-rw-r--r--spec/models/global_milestone_spec.rb119
-rw-r--r--spec/models/group_milestone_spec.rb27
-rw-r--r--spec/models/merge_request_spec.rb51
-rw-r--r--spec/models/milestone_spec.rb9
-rw-r--r--spec/models/project_spec.rb233
-rw-r--r--spec/models/prometheus_metric_spec.rb54
-rw-r--r--spec/models/release_spec.rb29
-rw-r--r--spec/models/releases/link_spec.rb70
-rw-r--r--spec/models/releases/source_spec.rb41
-rw-r--r--spec/models/remote_mirror_spec.rb14
-rw-r--r--spec/models/repository_spec.rb61
-rw-r--r--spec/models/snippet_spec.rb37
-rw-r--r--spec/policies/group_policy_spec.rb13
-rw-r--r--spec/policies/issuable_policy_spec.rb27
-rw-r--r--spec/policies/project_policy_spec.rb21
-rw-r--r--spec/presenters/clusterable_presenter_spec.rb64
-rw-r--r--spec/requests/api/branches_spec.rb12
-rw-r--r--spec/requests/api/files_spec.rb2
-rw-r--r--spec/requests/api/internal_spec.rb10
-rw-r--r--spec/requests/api/jobs_spec.rb32
-rw-r--r--spec/requests/api/releases_spec.rb664
-rw-r--r--spec/requests/api/repositories_spec.rb2
-rw-r--r--spec/requests/api/runner_spec.rb2
-rw-r--r--spec/requests/api/tags_spec.rb33
-rw-r--r--spec/requests/lfs_http_spec.rb2
-rw-r--r--spec/serializers/pipeline_serializer_spec.rb2
-rw-r--r--spec/services/auth/container_registry_authentication_service_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb34
-rw-r--r--spec/services/ci/register_job_service_spec.rb4
-rw-r--r--spec/services/ci/retry_pipeline_service_spec.rb2
-rw-r--r--spec/services/clusters/gcp/kubernetes/create_or_update_namespace_service_spec.rb2
-rw-r--r--spec/services/create_release_service_spec.rb39
-rw-r--r--spec/services/groups/update_service_spec.rb2
-rw-r--r--spec/services/issuable/bulk_update_service_spec.rb27
-rw-r--r--spec/services/issues/update_service_spec.rb2
-rw-r--r--spec/services/members/destroy_service_spec.rb2
-rw-r--r--spec/services/members/update_service_spec.rb17
-rw-r--r--spec/services/merge_requests/build_service_spec.rb55
-rw-r--r--spec/services/projects/lfs_pointers/lfs_download_service_spec.rb59
-rw-r--r--spec/services/projects/update_service_spec.rb4
-rw-r--r--spec/services/releases/create_service_spec.rb72
-rw-r--r--spec/services/releases/destroy_service_spec.rb61
-rw-r--r--spec/services/releases/update_service_spec.rb50
-rw-r--r--spec/services/todo_service_spec.rb1
-rw-r--r--spec/services/update_release_service_spec.rb34
-rw-r--r--spec/spec_helper.rb4
-rw-r--r--spec/support/gitlab_stubs/gitlab_ci.yml3
-rw-r--r--spec/support/helpers/fake_blob_helpers.rb2
-rw-r--r--spec/support/helpers/kubernetes_helpers.rb20
-rw-r--r--spec/support/helpers/migrations_helpers.rb16
-rw-r--r--spec/support/helpers/test_env.rb3
-rw-r--r--spec/support/shared_contexts/email_shared_context.rb (renamed from spec/support/shared_contexts/email_shared_blocks.rb)0
-rw-r--r--spec/support/shared_examples/policies/clusterable_shared_examples.rb37
-rw-r--r--spec/support/shared_examples/project_list_shared_examples.rb19
-rw-r--r--spec/uploaders/file_uploader_spec.rb2
-rw-r--r--spec/uploaders/object_storage_spec.rb2
-rw-r--r--spec/workers/mail_scheduler/notification_service_worker_spec.rb17
195 files changed, 8618 insertions, 3452 deletions
diff --git a/spec/controllers/dashboard/milestones_controller_spec.rb b/spec/controllers/dashboard/milestones_controller_spec.rb
index 8a8cc14fd4c..c9ccd5f7c55 100644
--- a/spec/controllers/dashboard/milestones_controller_spec.rb
+++ b/spec/controllers/dashboard/milestones_controller_spec.rb
@@ -52,7 +52,7 @@ describe Dashboard::MilestonesController do
expect(response).to have_gitlab_http_status(200)
expect(json_response.size).to eq(2)
- expect(json_response.map { |i| i["first_milestone"]["id"] }).to match_array([group_milestone.id, project_milestone.id])
+ expect(json_response.map { |i| i["name"] }).to match_array([group_milestone.name, project_milestone.name])
expect(json_response.map { |i| i["group_name"] }.compact).to match_array(group.name)
end
diff --git a/spec/controllers/groups/milestones_controller_spec.rb b/spec/controllers/groups/milestones_controller_spec.rb
index b8e1e08cff7..40d991a669c 100644
--- a/spec/controllers/groups/milestones_controller_spec.rb
+++ b/spec/controllers/groups/milestones_controller_spec.rb
@@ -64,7 +64,7 @@ describe Groups::MilestonesController do
context 'when there is a title parameter' do
it 'searches for a legacy group milestone' do
- expect(GlobalMilestone).to receive(:build)
+ expect(GroupMilestone).to receive(:build)
expect(Milestone).not_to receive(:find_by_iid)
get :show, params: { group_id: group.to_param, id: title, title: milestone1.safe_title }
diff --git a/spec/controllers/groups/settings/ci_cd_controller_spec.rb b/spec/controllers/groups/settings/ci_cd_controller_spec.rb
index b7f04f732b9..40673d10b91 100644
--- a/spec/controllers/groups/settings/ci_cd_controller_spec.rb
+++ b/spec/controllers/groups/settings/ci_cd_controller_spec.rb
@@ -5,30 +5,65 @@ describe Groups::Settings::CiCdController do
let(:user) { create(:user) }
before do
- group.add_maintainer(user)
sign_in(user)
end
describe 'GET #show' do
- it 'renders show with 200 status code' do
- get :show, params: { group_id: group }
+ context 'when user is owner' do
+ before do
+ group.add_owner(user)
+ end
- expect(response).to have_gitlab_http_status(200)
- expect(response).to render_template(:show)
+ it 'renders show with 200 status code' do
+ get :show, params: { group_id: group }
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to render_template(:show)
+ end
+ end
+
+ context 'when user is not owner' do
+ before do
+ group.add_maintainer(user)
+ end
+
+ it 'renders a 404' do
+ get :show, params: { group_id: group }
+
+ expect(response).to have_gitlab_http_status(404)
+ end
end
end
describe 'PUT #reset_registration_token' do
subject { put :reset_registration_token, params: { group_id: group } }
- it 'resets runner registration token' do
- expect { subject }.to change { group.reload.runners_token }
+ context 'when user is owner' do
+ before do
+ group.add_owner(user)
+ end
+
+ it 'resets runner registration token' do
+ expect { subject }.to change { group.reload.runners_token }
+ end
+
+ it 'redirects the user to admin runners page' do
+ subject
+
+ expect(response).to redirect_to(group_settings_ci_cd_path)
+ end
end
- it 'redirects the user to admin runners page' do
- subject
+ context 'when user is not owner' do
+ before do
+ group.add_maintainer(user)
+ end
+
+ it 'renders a 404' do
+ subject
- expect(response).to redirect_to(group_settings_ci_cd_path)
+ expect(response).to have_gitlab_http_status(404)
+ end
end
end
end
diff --git a/spec/controllers/projects/avatars_controller_spec.rb b/spec/controllers/projects/avatars_controller_spec.rb
index 40ab81395ea..95b7ae5885a 100644
--- a/spec/controllers/projects/avatars_controller_spec.rb
+++ b/spec/controllers/projects/avatars_controller_spec.rb
@@ -26,37 +26,13 @@ describe Projects::AvatarsController do
context 'when the avatar is stored in the repository' do
let(:filepath) { 'files/images/logo-white.png' }
- context 'when feature flag workhorse_set_content_type is' do
- before do
- stub_feature_flags(workhorse_set_content_type: flag_value)
- end
+ it 'sends the avatar' do
+ subject
- context 'enabled' do
- let(:flag_value) { true }
-
- it 'sends the avatar' do
- subject
-
- expect(response).to have_gitlab_http_status(200)
- expect(response.header['Content-Disposition']).to eq('inline')
- expect(response.header['Content-Type']).to eq 'image/png'
- expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
- expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
- end
- end
-
- context 'disabled' do
- let(:flag_value) { false }
-
- it 'sends the avatar' do
- subject
-
- expect(response).to have_gitlab_http_status(200)
- expect(response.header['Content-Type']).to eq('image/png')
- expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
- expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq nil
- end
- end
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.header['Content-Disposition']).to eq('inline')
+ expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
+ expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
end
end
diff --git a/spec/controllers/projects/ci/lints_controller_spec.rb b/spec/controllers/projects/ci/lints_controller_spec.rb
index 82c1374aa4f..cfa010c2d1c 100644
--- a/spec/controllers/projects/ci/lints_controller_spec.rb
+++ b/spec/controllers/projects/ci/lints_controller_spec.rb
@@ -51,7 +51,6 @@ describe Projects::Ci::LintsController do
- apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs
- ruby -v
- which ruby
- - gem install bundler --no-ri --no-rdoc
- bundle install --jobs $(nproc) "${FLAGS[@]}"
HEREDOC
end
diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb
index e1133fdd562..7f65fe551e9 100644
--- a/spec/controllers/projects/jobs_controller_spec.rb
+++ b/spec/controllers/projects/jobs_controller_spec.rb
@@ -892,36 +892,13 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
context "when job has a trace artifact" do
let(:job) { create(:ci_build, :trace_artifact, pipeline: pipeline) }
- context 'when feature flag workhorse_set_content_type is' do
- before do
- stub_feature_flags(workhorse_set_content_type: flag_value)
- end
-
- context 'enabled' do
- let(:flag_value) { true }
-
- it "sets #{Gitlab::Workhorse::DETECT_HEADER} header" do
- response = subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.headers["Content-Type"]).to eq("text/plain; charset=utf-8")
- expect(response.body).to eq(job.job_artifacts_trace.open.read)
- expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
- end
- end
-
- context 'disabled' do
- let(:flag_value) { false }
-
- it 'returns a trace' do
- response = subject
+ it "sets #{Gitlab::Workhorse::DETECT_HEADER} header" do
+ response = subject
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.headers["Content-Type"]).to eq("text/plain; charset=utf-8")
- expect(response.body).to eq(job.job_artifacts_trace.open.read)
- expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to be nil
- end
- end
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.headers["Content-Type"]).to eq("text/plain; charset=utf-8")
+ expect(response.body).to eq(job.job_artifacts_trace.open.read)
+ expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
end
end
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 759a4b8bdce..4f4d3ca226f 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -942,6 +942,70 @@ describe Projects::MergeRequestsController do
end
end
+ describe 'GET discussions' do
+ context 'when authenticated' do
+ before do
+ project.add_developer(user)
+ sign_in(user)
+ end
+
+ it 'returns 200' do
+ get :discussions, params: { namespace_id: project.namespace, project_id: project, id: merge_request.iid }
+
+ expect(response.status).to eq(200)
+ end
+
+ context 'highlight preloading' do
+ context 'with commit diff notes' do
+ let!(:commit_diff_note) do
+ create(:diff_note_on_commit, project: merge_request.project)
+ end
+
+ it 'preloads notes diffs highlights' do
+ expect_next_instance_of(Gitlab::DiscussionsDiff::FileCollection) do |collection|
+ note_diff_file = commit_diff_note.note_diff_file
+
+ expect(collection).to receive(:load_highlight).with([note_diff_file.id]).and_call_original
+ expect(collection).to receive(:find_by_id).with(note_diff_file.id).and_call_original
+ end
+
+ get :discussions, params: { namespace_id: project.namespace, project_id: project, id: merge_request.iid }
+ end
+ end
+
+ context 'with diff notes' do
+ let!(:diff_note) do
+ create(:diff_note_on_merge_request, noteable: merge_request, project: merge_request.project)
+ end
+
+ it 'preloads notes diffs highlights' do
+ expect_next_instance_of(Gitlab::DiscussionsDiff::FileCollection) do |collection|
+ note_diff_file = diff_note.note_diff_file
+
+ expect(collection).to receive(:load_highlight).with([note_diff_file.id]).and_call_original
+ expect(collection).to receive(:find_by_id).with(note_diff_file.id).and_call_original
+ end
+
+ get :discussions, params: { namespace_id: project.namespace, project_id: project, id: merge_request.iid }
+ end
+
+ it 'does not preload highlights when diff note is resolved' do
+ Notes::ResolveService.new(diff_note.project, user).execute(diff_note)
+
+ expect_next_instance_of(Gitlab::DiscussionsDiff::FileCollection) do |collection|
+ note_diff_file = diff_note.note_diff_file
+
+ expect(collection).to receive(:load_highlight).with([]).and_call_original
+ expect(collection).to receive(:find_by_id).with(note_diff_file.id).and_call_original
+ end
+
+ get :discussions, params: { namespace_id: project.namespace, project_id: project, id: merge_request.iid }
+ end
+ end
+ end
+ end
+ end
+
describe 'GET edit' do
it 'responds successfully' do
get :edit, params: { namespace_id: project.namespace, project_id: project, id: merge_request }
diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb
index 24e2441cf9d..cffdf30da6b 100644
--- a/spec/controllers/projects/raw_controller_spec.rb
+++ b/spec/controllers/projects/raw_controller_spec.rb
@@ -16,74 +16,27 @@ describe Projects::RawController do
context 'regular filename' do
let(:filepath) { 'master/README.md' }
- context 'when feature flag workhorse_set_content_type is' do
- before do
- stub_feature_flags(workhorse_set_content_type: flag_value)
-
- subject
- end
-
- context 'enabled' do
- let(:flag_value) { true }
-
- it 'delivers ASCII file' do
- expect(response).to have_gitlab_http_status(200)
- expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
- expect(response.header['Content-Disposition']).to eq('inline')
- expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
- expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
- end
- end
-
- context 'disabled' do
- let(:flag_value) { false }
-
- it 'delivers ASCII file' do
- expect(response).to have_gitlab_http_status(200)
- expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
- expect(response.header['Content-Disposition']).to eq('inline')
- expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq nil
- expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
- end
- end
+ it 'delivers ASCII file' do
+ subject
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
+ expect(response.header['Content-Disposition']).to eq('inline')
+ expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
+ expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
end
end
context 'image header' do
let(:filepath) { 'master/files/images/6049019_460s.jpg' }
- context 'when feature flag workhorse_set_content_type is' do
- before do
- stub_feature_flags(workhorse_set_content_type: flag_value)
- end
-
- context 'enabled' do
- let(:flag_value) { true }
-
- it 'leaves image content disposition' do
- subject
-
- expect(response).to have_gitlab_http_status(200)
- expect(response.header['Content-Type']).to eq('image/jpeg')
- expect(response.header['Content-Disposition']).to eq('inline')
- expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
- expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
- end
- end
-
- context 'disabled' do
- let(:flag_value) { false }
-
- it 'sets image content type header' do
- subject
+ it 'leaves image content disposition' do
+ subject
- expect(response).to have_gitlab_http_status(200)
- expect(response.header['Content-Type']).to eq('image/jpeg')
- expect(response.header['Content-Disposition']).to eq('inline')
- expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq nil
- expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
- end
- end
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.header['Content-Disposition']).to eq('inline')
+ expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
+ expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
end
end
diff --git a/spec/controllers/projects/snippets_controller_spec.rb b/spec/controllers/projects/snippets_controller_spec.rb
index 1a3fb4da15f..75c9839dd9b 100644
--- a/spec/controllers/projects/snippets_controller_spec.rb
+++ b/spec/controllers/projects/snippets_controller_spec.rb
@@ -379,6 +379,46 @@ describe Projects::SnippetsController do
end
end
+ describe "GET #show for embeddable content" do
+ let(:project_snippet) { create(:project_snippet, snippet_permission, project: project, author: user) }
+
+ before do
+ sign_in(user)
+
+ get :show, params: { namespace_id: project.namespace, project_id: project, id: project_snippet.to_param }, format: :js
+ end
+
+ context 'when snippet is private' do
+ let(:snippet_permission) { :private }
+
+ it 'responds with status 404' do
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
+ context 'when snippet is public' do
+ let(:snippet_permission) { :public }
+
+ it 'responds with status 200' do
+ expect(assigns(:snippet)).to eq(project_snippet)
+ expect(response).to have_gitlab_http_status(200)
+ end
+ end
+
+ context 'when the project is private' do
+ let(:project) { create(:project_empty_repo, :private) }
+
+ context 'when snippet is public' do
+ let(:project_snippet) { create(:project_snippet, :public, project: project, author: user) }
+
+ it 'responds with status 404' do
+ expect(assigns(:snippet)).to eq(project_snippet)
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+ end
+ end
+
describe 'GET #raw' do
let(:project_snippet) do
create(
diff --git a/spec/controllers/projects/wikis_controller_spec.rb b/spec/controllers/projects/wikis_controller_spec.rb
index 341bf244397..b2f40231796 100644
--- a/spec/controllers/projects/wikis_controller_spec.rb
+++ b/spec/controllers/projects/wikis_controller_spec.rb
@@ -52,56 +52,24 @@ describe Projects::WikisController do
let(:path) { upload_file_to_wiki(project, user, file_name) }
- subject { get :show, params: { namespace_id: project.namespace, project_id: project, id: path } }
+ before do
+ get :show, params: { namespace_id: project.namespace, project_id: project, id: path }
+ end
context 'when file is an image' do
let(:file_name) { 'dk.png' }
- context 'when feature flag workhorse_set_content_type is' do
- before do
- stub_feature_flags(workhorse_set_content_type: flag_value)
-
- subject
- end
-
- context 'enabled' do
- let(:flag_value) { true }
-
- it 'delivers the image' do
- expect(response.headers['Content-Type']).to eq('image/png')
- expect(response.headers['Content-Disposition']).to match(/^inline/)
- expect(response.headers[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
- end
-
- context 'when file is a svg' do
- let(:file_name) { 'unsanitized.svg' }
-
- it 'delivers the image' do
- expect(response.headers['Content-Type']).to eq('image/svg+xml')
- expect(response.headers['Content-Disposition']).to match(/^attachment/)
- expect(response.headers[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
- end
- end
- end
-
- context 'disabled' do
- let(:flag_value) { false }
-
- it 'renders the content inline' do
- expect(response.headers['Content-Type']).to eq('image/png')
- expect(response.headers['Content-Disposition']).to match(/^inline/)
- expect(response.headers[Gitlab::Workhorse::DETECT_HEADER]).to eq nil
- end
+ it 'delivers the image' do
+ expect(response.headers['Content-Disposition']).to match(/^inline/)
+ expect(response.headers[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
+ end
- context 'when file is a svg' do
- let(:file_name) { 'unsanitized.svg' }
+ context 'when file is a svg' do
+ let(:file_name) { 'unsanitized.svg' }
- it 'renders the content as an attachment' do
- expect(response.headers['Content-Type']).to eq('image/svg+xml')
- expect(response.headers['Content-Disposition']).to match(/^attachment/)
- expect(response.headers[Gitlab::Workhorse::DETECT_HEADER]).to eq nil
- end
- end
+ it 'delivers the image' do
+ expect(response.headers['Content-Disposition']).to match(/^inline/)
+ expect(response.headers[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
end
end
end
@@ -109,32 +77,9 @@ describe Projects::WikisController do
context 'when file is a pdf' do
let(:file_name) { 'git-cheat-sheet.pdf' }
- context 'when feature flag workhorse_set_content_type is' do
- before do
- stub_feature_flags(workhorse_set_content_type: flag_value)
-
- subject
- end
-
- context 'enabled' do
- let(:flag_value) { true }
-
- it 'sets the content type to sets the content response headers' do
- expect(response.headers['Content-Type']).to eq 'application/octet-stream'
- expect(response.headers['Content-Disposition']).to match(/^inline/)
- expect(response.headers[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
- end
- end
-
- context 'disabled' do
- let(:flag_value) { false }
-
- it 'sets the content response headers' do
- expect(response.headers['Content-Type']).to eq 'application/octet-stream'
- expect(response.headers['Content-Disposition']).to match(/^inline/)
- expect(response.headers[Gitlab::Workhorse::DETECT_HEADER]).to eq nil
- end
- end
+ it 'sets the content type to sets the content response headers' do
+ expect(response.headers['Content-Disposition']).to match(/^inline/)
+ expect(response.headers[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
end
end
end
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index ea067a01295..f84f069f4db 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -621,10 +621,10 @@ describe ProjectsController do
end
describe "GET refs" do
- let(:public_project) { create(:project, :public, :repository) }
+ let(:project) { create(:project, :public, :repository) }
it 'gets a list of branches and tags' do
- get :refs, params: { namespace_id: public_project.namespace, id: public_project, sort: 'updated_desc' }
+ get :refs, params: { namespace_id: project.namespace, id: project, sort: 'updated_desc' }
parsed_body = JSON.parse(response.body)
expect(parsed_body['Branches']).to include('master')
@@ -634,7 +634,7 @@ describe ProjectsController do
end
it "gets a list of branches, tags and commits" do
- get :refs, params: { namespace_id: public_project.namespace, id: public_project, ref: "123456" }
+ get :refs, params: { namespace_id: project.namespace, id: project, ref: "123456" }
parsed_body = JSON.parse(response.body)
expect(parsed_body["Branches"]).to include("master")
@@ -649,7 +649,7 @@ describe ProjectsController do
end
it "gets a list of branches, tags and commits" do
- get :refs, params: { namespace_id: public_project.namespace, id: public_project, ref: "123456" }
+ get :refs, params: { namespace_id: project.namespace, id: project, ref: "123456" }
parsed_body = JSON.parse(response.body)
expect(parsed_body["Branches"]).to include("master")
@@ -657,6 +657,22 @@ describe ProjectsController do
expect(parsed_body["Commits"]).to include("123456")
end
end
+
+ context 'when private project' do
+ let(:project) { create(:project, :repository) }
+
+ context 'as a guest' do
+ it 'renders forbidden' do
+ user = create(:user)
+ project.add_guest(user)
+
+ sign_in(user)
+ get :refs, params: { namespace_id: project.namespace, id: project }
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+ end
end
describe 'POST #preview_markdown' do
diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb
index 01a5161f775..5c6858dc7b2 100644
--- a/spec/controllers/snippets_controller_spec.rb
+++ b/spec/controllers/snippets_controller_spec.rb
@@ -80,6 +80,12 @@ describe SnippetsController do
expect(assigns(:snippet)).to eq(personal_snippet)
expect(response).to have_gitlab_http_status(200)
end
+
+ it 'responds with status 404 when embeddable content is requested' do
+ get :show, params: { id: personal_snippet.to_param }, format: :js
+
+ expect(response).to have_gitlab_http_status(404)
+ end
end
end
@@ -106,6 +112,12 @@ describe SnippetsController do
expect(assigns(:snippet)).to eq(personal_snippet)
expect(response).to have_gitlab_http_status(200)
end
+
+ it 'responds with status 404 when embeddable content is requested' do
+ get :show, params: { id: personal_snippet.to_param }, format: :js
+
+ expect(response).to have_gitlab_http_status(404)
+ end
end
context 'when not signed in' do
@@ -131,6 +143,13 @@ describe SnippetsController do
expect(assigns(:snippet)).to eq(personal_snippet)
expect(response).to have_gitlab_http_status(200)
end
+
+ it 'responds with status 200 when embeddable content is requested' do
+ get :show, params: { id: personal_snippet.to_param }, format: :js
+
+ expect(assigns(:snippet)).to eq(personal_snippet)
+ expect(response).to have_gitlab_http_status(200)
+ end
end
context 'when not signed in' do
@@ -437,10 +456,7 @@ describe SnippetsController do
end
context 'when signed in user is the author' do
- let(:flag_value) { false }
-
before do
- stub_feature_flags(workhorse_set_content_type: flag_value)
get :raw, params: { id: personal_snippet.to_param }
end
@@ -455,22 +471,9 @@ describe SnippetsController do
expect(response.header['Content-Disposition']).to match(/inline/)
end
- context 'when feature flag workhorse_set_content_type is' do
- context 'enabled' do
- let(:flag_value) { true }
-
- it "sets #{Gitlab::Workhorse::DETECT_HEADER} header" do
- expect(response).to have_gitlab_http_status(200)
- expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
- end
- end
-
- context 'disabled' do
- it "does not set #{Gitlab::Workhorse::DETECT_HEADER} header" do
- expect(response).to have_gitlab_http_status(200)
- expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to be nil
- end
- end
+ it "sets #{Gitlab::Workhorse::DETECT_HEADER} header" do
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
end
end
end
diff --git a/spec/db/importers/common_metrics_importer_spec.rb b/spec/db/importers/common_metrics_importer_spec.rb
index 68260820958..6133b17ac61 100644
--- a/spec/db/importers/common_metrics_importer_spec.rb
+++ b/spec/db/importers/common_metrics_importer_spec.rb
@@ -4,12 +4,18 @@ require 'rails_helper'
require Rails.root.join("db", "importers", "common_metrics_importer.rb")
describe Importers::PrometheusMetric do
+ let(:existing_group_titles) do
+ ::PrometheusMetric::GROUP_DETAILS.each_with_object({}) do |(key, value), memo|
+ memo[key] = value[:group_title]
+ end
+ end
+
it 'group enum equals ::PrometheusMetric' do
expect(described_class.groups).to eq(::PrometheusMetric.groups)
end
it 'GROUP_TITLES equals ::PrometheusMetric' do
- expect(described_class::GROUP_TITLES).to eq(::PrometheusMetric::GROUP_TITLES)
+ expect(described_class::GROUP_TITLES).to eq(existing_group_titles)
end
end
diff --git a/spec/factories/appearances.rb b/spec/factories/appearances.rb
index 18c7453bd1b..dd5129229d4 100644
--- a/spec/factories/appearances.rb
+++ b/spec/factories/appearances.rb
@@ -15,6 +15,10 @@ FactoryBot.define do
header_logo { fixture_file_upload('spec/fixtures/dk.png') }
end
+ trait :with_favicon do
+ favicon { fixture_file_upload('spec/fixtures/dk.png') }
+ end
+
trait :with_logos do
with_logo
with_header_logo
diff --git a/spec/factories/releases.rb b/spec/factories/releases.rb
index 18047c74a5d..cab6b4a811f 100644
--- a/spec/factories/releases.rb
+++ b/spec/factories/releases.rb
@@ -1,8 +1,15 @@
FactoryBot.define do
factory :release do
tag "v1.1.0"
+ sha 'b83d6e391c22777fca1ed3012fce84f633d7fed0'
name { tag }
description "Awesome release"
project
+ author
+
+ trait :legacy do
+ sha nil
+ author nil
+ end
end
end
diff --git a/spec/factories/releases/link.rb b/spec/factories/releases/link.rb
new file mode 100644
index 00000000000..d23db6d4bad
--- /dev/null
+++ b/spec/factories/releases/link.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :release_link, class: ::Releases::Link do
+ release
+ sequence(:name) { |n| "release-18.#{n}.dmg" }
+ sequence(:url) { |n| "https://example.com/scrambled-url/app-#{n}.zip" }
+ end
+end
diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb
index 282bf542e77..9ffa75aee47 100644
--- a/spec/features/dashboard/merge_requests_spec.rb
+++ b/spec/features/dashboard/merge_requests_spec.rb
@@ -6,6 +6,7 @@ describe 'Dashboard Merge Requests' do
include ProjectForksHelper
let(:current_user) { create :user }
+ let(:user) { current_user }
let(:project) { create(:project) }
let(:public_project) { create(:project, :public, :repository) }
diff --git a/spec/features/dashboard/root_explore_spec.rb b/spec/features/dashboard/root_explore_spec.rb
new file mode 100644
index 00000000000..5b686d8b6f1
--- /dev/null
+++ b/spec/features/dashboard/root_explore_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Root explore' do
+ set(:public_project) { create(:project, :public) }
+ set(:archived_project) { create(:project, :archived) }
+ set(:internal_project) { create(:project, :internal) }
+ set(:private_project) { create(:project, :private) }
+
+ before do
+ allow(Gitlab).to receive(:com?).and_return(true)
+ end
+
+ context 'when logged in' do
+ set(:user) { create(:user) }
+
+ before do
+ sign_in(user)
+ visit explore_projects_path
+ end
+
+ include_examples 'shows public and internal projects'
+ end
+
+ context 'when not logged in' do
+ before do
+ visit explore_projects_path
+ end
+
+ include_examples 'shows public projects'
+ end
+end
diff --git a/spec/features/explore/user_explores_projects_spec.rb b/spec/features/explore/user_explores_projects_spec.rb
index 6ac9497b024..c724c3d17f8 100644
--- a/spec/features/explore/user_explores_projects_spec.rb
+++ b/spec/features/explore/user_explores_projects_spec.rb
@@ -6,24 +6,6 @@ describe 'User explores projects' do
set(:private_project) { create(:project, :private) }
set(:public_project) { create(:project, :public) }
- shared_examples_for 'shows public projects' do
- it 'shows projects' do
- expect(page).to have_content(public_project.title)
- expect(page).not_to have_content(internal_project.title)
- expect(page).not_to have_content(private_project.title)
- expect(page).not_to have_content(archived_project.title)
- end
- end
-
- shared_examples_for 'shows public and internal projects' do
- it 'shows projects' do
- expect(page).to have_content(public_project.title)
- expect(page).to have_content(internal_project.title)
- expect(page).not_to have_content(private_project.title)
- expect(page).not_to have_content(archived_project.title)
- end
- end
-
context 'when not signed in' do
context 'when viewing public projects' do
before do
diff --git a/spec/features/group_variables_spec.rb b/spec/features/group_variables_spec.rb
index 89e0cdd8ed7..57e3ddfb39c 100644
--- a/spec/features/group_variables_spec.rb
+++ b/spec/features/group_variables_spec.rb
@@ -7,7 +7,7 @@ describe 'Group variables', :js do
let(:page_path) { group_settings_ci_cd_path(group) }
before do
- group.add_maintainer(user)
+ group.add_owner(user)
gitlab_sign_in(user)
visit page_path
diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb
index 174840794ed..d57eb87ca77 100644
--- a/spec/features/groups/milestone_spec.rb
+++ b/spec/features/groups/milestone_spec.rb
@@ -81,7 +81,7 @@ describe 'Group milestones' do
description: 'Lorem Ipsum is simply dummy text'
)
end
- let!(:active_project_milestone2) { create(:milestone, project: other_project, state: 'active', title: 'v1.0') }
+ let!(:active_project_milestone2) { create(:milestone, project: other_project, state: 'active', title: 'v1.1') }
let!(:closed_project_milestone1) { create(:milestone, project: project, state: 'closed', title: 'v2.0') }
let!(:closed_project_milestone2) { create(:milestone, project: other_project, state: 'closed', title: 'v2.0') }
let!(:active_group_milestone) { create(:milestone, group: group, state: 'active', title: 'GL-113') }
@@ -104,7 +104,7 @@ describe 'Group milestones' do
legacy_milestone = GroupMilestone.build_collection(group, group.projects, { state: 'active' }).first
expect(page).to have_selector("#milestone_#{active_group_milestone.id}", count: 1)
- expect(page).to have_selector("#milestone_#{legacy_milestone.milestones.first.id}", count: 1)
+ expect(page).to have_selector("#milestone_#{legacy_milestone.milestone.id}", count: 1)
end
it 'shows milestone detail and supports its edit' do
@@ -121,6 +121,7 @@ describe 'Group milestones' do
it 'renders milestones' do
expect(page).to have_content('v1.0')
+ expect(page).to have_content('v1.1')
expect(page).to have_content('GL-113')
expect(page).to have_link(
'1 Issue',
diff --git a/spec/features/groups/milestones_sorting_spec.rb b/spec/features/groups/milestones_sorting_spec.rb
index bc226ff41c1..7bc015ea28f 100644
--- a/spec/features/groups/milestones_sorting_spec.rb
+++ b/spec/features/groups/milestones_sorting_spec.rb
@@ -42,6 +42,7 @@ describe 'Milestones sorting', :js do
expect(page).to have_button('Due later')
+ # assert descending sorting
within '.milestones' do
expect(page.all('ul.content-list > li').first.text).to include('v1.0')
expect(page.all('ul.content-list > li')[1].text).to include('v3.0')
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index d7531d5fcd9..3b7a17ef355 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -3,6 +3,8 @@ require 'rails_helper'
describe 'GFM autocomplete', :js do
let(:issue_xss_title) { 'This will execute alert<img src=x onerror=alert(2)&lt;img src=x onerror=alert(1)&gt;' }
let(:user_xss_title) { 'eve <img src=x onerror=alert(2)&lt;img src=x onerror=alert(1)&gt;' }
+ let(:label_xss_title) { 'alert label &lt;img src=x onerror="alert(\'Hello xss\');" a'}
+ let(:milestone_xss_title) { 'alert milestone &lt;img src=x onerror="alert(\'Hello xss\');" a' }
let(:user_xss) { create(:user, name: user_xss_title, username: 'xss.user') }
let(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') }
@@ -25,10 +27,14 @@ describe 'GFM autocomplete', :js do
simulate_input('#issue-description', "@#{user.name[0...3]}")
+ wait_for_requests
+
find('.atwho-view .cur').click
click_button 'Save changes'
+ wait_for_requests
+
expect(find('.description')).to have_content(user.to_reference)
end
@@ -47,6 +53,8 @@ describe 'GFM autocomplete', :js do
find('#note-body').native.send_keys('#')
end
+ wait_for_requests
+
expect(page).to have_selector('.atwho-container')
page.within '.atwho-container #at-view-issues' do
@@ -59,6 +67,8 @@ describe 'GFM autocomplete', :js do
find('#note-body').native.send_keys('@ev')
end
+ wait_for_requests
+
expect(page).to have_selector('.atwho-container')
page.within '.atwho-container #at-view-users' do
@@ -66,6 +76,22 @@ describe 'GFM autocomplete', :js do
end
end
+ it 'opens autocomplete menu for Milestone when field starts with text with item escaping HTML characters' do
+ create(:milestone, project: project, title: milestone_xss_title)
+
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys('%')
+ end
+
+ wait_for_requests
+
+ expect(page).to have_selector('.atwho-container')
+
+ page.within '.atwho-container #at-view-milestones' do
+ expect(find('li').text).to have_content('alert milestone')
+ end
+ end
+
it 'doesnt open autocomplete menu character is prefixed with text' do
page.within '.timeline-content-form' do
find('#note-body').native.send_keys('testing')
@@ -258,12 +284,28 @@ describe 'GFM autocomplete', :js do
let!(:bug) { create(:label, project: project, title: 'bug') }
let!(:feature_proposal) { create(:label, project: project, title: 'feature proposal') }
+ it 'opens autocomplete menu for Labels when field starts with text with item escaping HTML characters' do
+ create(:label, project: project, title: label_xss_title)
+
+ note = find('#note-body')
+
+ # It should show all the labels on "~".
+ type(note, '~')
+
+ wait_for_requests
+
+ page.within '.atwho-container #at-view-labels' do
+ expect(find('.atwho-view-ul').text).to have_content('alert label')
+ end
+ end
+
context 'when no labels are assigned' do
it 'shows labels' do
note = find('#note-body')
# It should show all the labels on "~".
type(note, '~')
+ wait_for_requests
expect_labels(shown: [backend, bug, feature_proposal])
# It should show all the labels on "/label ~".
@@ -290,6 +332,7 @@ describe 'GFM autocomplete', :js do
# It should show all the labels on "~".
type(note, '~')
+ wait_for_requests
expect_labels(shown: [backend, bug, feature_proposal])
# It should show only unset labels on "/label ~".
@@ -316,6 +359,7 @@ describe 'GFM autocomplete', :js do
# It should show all the labels on "~".
type(note, '~')
+ wait_for_requests
expect_labels(shown: [backend, bug, feature_proposal])
# It should show no labels on "/label ~".
diff --git a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
index d790bdc82ce..d19408ee87f 100644
--- a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
@@ -90,9 +90,6 @@ describe 'Merge request > User creates image diff notes', :js do
%w(inline parallel).each do |view|
context "#{view} view" do
- let(:merge_request) { create(:merge_request_with_diffs, :with_image_diffs, source_project: project, author: user) }
- let(:path) { "files/images/ee_repo_logo.png" }
-
let(:position) do
Gitlab::Diff::Position.new(
old_path: path,
@@ -108,9 +105,11 @@ describe 'Merge request > User creates image diff notes', :js do
let!(:note) { create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: position) }
- describe 'creating a new diff note' do
+ shared_examples 'creates image diff note' do
before do
visit diffs_project_merge_request_path(project, merge_request, view: view)
+ wait_for_requests
+
create_image_diff_note
end
@@ -132,6 +131,32 @@ describe 'Merge request > User creates image diff notes', :js do
expect(page).to have_content('image diff test comment')
end
end
+
+ context 'when images are not stored in LFS' do
+ let(:merge_request) { create(:merge_request_with_diffs, :with_image_diffs, source_project: project, author: user) }
+ let(:path) { 'files/images/ee_repo_logo.png' }
+
+ it_behaves_like 'creates image diff note'
+ end
+
+ context 'when images are stored in LFS' do
+ let(:merge_request) { create(:merge_request, source_project: project, target_project: project, source_branch: 'png-lfs', target_branch: 'master', author: user) }
+ let(:path) { 'files/images/logo-black.png' }
+
+ before do
+ allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
+ project.update_attribute(:lfs_enabled, true)
+ end
+
+ it 'shows lfs badges' do
+ visit diffs_project_merge_request_path(project, merge_request, view: view)
+ wait_for_requests
+
+ expect(page.all('.diff-file span.label-lfs', visible: :all)).not_to be_empty
+ end
+
+ it_behaves_like 'creates image diff note'
+ end
end
end
diff --git a/spec/features/merge_request/user_sees_diff_spec.rb b/spec/features/merge_request/user_sees_diff_spec.rb
index 0df9e4bbc1a..04b07525919 100644
--- a/spec/features/merge_request/user_sees_diff_spec.rb
+++ b/spec/features/merge_request/user_sees_diff_spec.rb
@@ -87,20 +87,6 @@ describe 'Merge request > User sees diff', :js do
let(:current_user) { project.owner }
let(:branch_name) {"test_branch"}
- def create_file(branch_name, file_name, content)
- Files::CreateService.new(
- project,
- current_user,
- start_branch: branch_name,
- branch_name: branch_name,
- commit_message: "Create file",
- file_path: file_name,
- file_content: content
- ).execute
-
- project.commit(branch_name)
- end
-
it 'escapes any HTML special characters in the diff chunk header' do
file_content =
<<~CONTENT
@@ -136,5 +122,61 @@ describe 'Merge request > User sees diff', :js do
expect(page).to have_css(".line[lang='rust'] .k")
end
end
+
+ context 'when file is stored in LFS' do
+ let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:current_user) { project.owner }
+
+ context 'when LFS is enabled on the project' do
+ before do
+ allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
+ project.update_attribute(:lfs_enabled, true)
+
+ create_file('master', file_name, project.repository.blob_at('master', 'files/lfs/lfs_object.iso').data)
+
+ visit diffs_project_merge_request_path(project, merge_request)
+ end
+
+ context 'when file is an image', :js do
+ let(:file_name) { 'files/lfs/image.png' }
+
+ it 'shows an error message' do
+ expect(page).not_to have_content('could not be displayed because it is stored in LFS')
+ end
+ end
+
+ context 'when file is not an image' do
+ let(:file_name) { 'files/lfs/ruby.rb' }
+
+ it 'shows an error message' do
+ expect(page).to have_content('This source diff could not be displayed because it is stored in LFS')
+ end
+ end
+ end
+
+ context 'when LFS is not enabled' do
+ before do
+ visit diffs_project_merge_request_path(project, merge_request)
+ end
+
+ it 'displays the diff' do
+ expect(page).to have_content('size 1575078')
+ end
+ end
+ end
+
+ def create_file(branch_name, file_name, content)
+ Files::CreateService.new(
+ project,
+ current_user,
+ start_branch: branch_name,
+ branch_name: branch_name,
+ commit_message: "Create file",
+ file_path: file_name,
+ file_content: content
+ ).execute
+
+ project.commit(branch_name)
+ end
end
end
diff --git a/spec/features/merge_request/user_sees_versions_spec.rb b/spec/features/merge_request/user_sees_versions_spec.rb
index f7512294bef..63d8decc2d2 100644
--- a/spec/features/merge_request/user_sees_versions_spec.rb
+++ b/spec/features/merge_request/user_sees_versions_spec.rb
@@ -64,6 +64,17 @@ describe 'Merge request > User sees versions', :js do
end
end
+ it 'shows the commit SHAs for every version in the dropdown' do
+ page.within '.mr-version-dropdown' do
+ find('.btn-default').click
+
+ page.within('.dropdown-content') do
+ shas = merge_request.merge_request_diffs.map { |diff| Commit.truncate_sha(diff.head_commit_sha) }
+ shas.each { |sha| expect(page).to have_content(sha) }
+ end
+ end
+ end
+
it 'shows comments that were last relevant at that version' do
expect(page).to have_content '5 changed files'
diff --git a/spec/features/merge_request/user_tries_to_access_private_repository_through_new_mr_spec.rb b/spec/features/merge_request/user_tries_to_access_private_repository_through_new_mr_spec.rb
new file mode 100644
index 00000000000..9318b5f1ebb
--- /dev/null
+++ b/spec/features/merge_request/user_tries_to_access_private_repository_through_new_mr_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+
+describe 'Merge Request > Tries to access private repo of public project' do
+ let(:current_user) { create(:user) }
+ let(:private_project) do
+ create(:project, :public, :repository,
+ path: 'nothing-to-see-here',
+ name: 'nothing to see here',
+ repository_access_level: ProjectFeature::PRIVATE)
+ end
+ let(:owned_project) do
+ create(:project, :public, :repository,
+ namespace: current_user.namespace,
+ creator: current_user)
+ end
+
+ context 'when the user enters the querystring info for the other project' do
+ let(:mr_path) do
+ project_new_merge_request_diffs_path(
+ owned_project,
+ merge_request: {
+ source_project_id: private_project.id,
+ source_branch: 'feature'
+ }
+ )
+ end
+
+ before do
+ sign_in current_user
+ visit mr_path
+ end
+
+ it "does not mention the project the user can't see the repo of" do
+ expect(page).not_to have_content('nothing-to-see-here')
+ end
+ end
+end
diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb
index 5e0434c1c2c..f45bcabd196 100644
--- a/spec/features/profiles/user_edit_profile_spec.rb
+++ b/spec/features/profiles/user_edit_profile_spec.rb
@@ -147,6 +147,9 @@ describe 'User edit profile' do
end
context 'user menu' do
+ let(:issue) { create(:issue, project: project)}
+ let(:project) { create(:project) }
+
def open_user_status_modal
find('.header-user-dropdown-toggle').click
@@ -205,6 +208,17 @@ describe 'User edit profile' do
end
end
+ it 'does not update the awards panel emoji' do
+ project.add_maintainer(user)
+ visit(project_issue_path(project, issue))
+
+ emoji = 'biohazard'
+ open_user_status_modal
+ select_emoji(emoji, true)
+
+ expect(page.all('.award-control .js-counter')).to all(have_content('0'))
+ end
+
it 'adds message to user status' do
message = 'I have something to say'
open_user_status_modal
diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb
index 06e30571336..9322e29d744 100644
--- a/spec/features/projects/clusters/gcp_spec.rb
+++ b/spec/features/projects/clusters/gcp_spec.rb
@@ -33,32 +33,6 @@ describe 'Gcp Cluster', :js do
context 'when user filled form with valid parameters' do
subject { click_button 'Create Kubernetes cluster' }
- shared_examples 'valid cluster gcp form' do
- it 'users sees a form with the GCP token' do
- expect(page).to have_selector(:css, 'form[data-token="token"]')
- end
-
- it 'user sees a cluster details page and creation status' do
- subject
-
- expect(page).to have_content('Kubernetes cluster is being created on Google Kubernetes Engine...')
-
- Clusters::Cluster.last.provider.make_created!
-
- expect(page).to have_content('Kubernetes cluster was successfully created on Google Kubernetes Engine')
- end
-
- it 'user sees a error if something wrong during creation' do
- subject
-
- expect(page).to have_content('Kubernetes cluster is being created on Google Kubernetes Engine...')
-
- Clusters::Cluster.last.provider.make_errored!('Something wrong!')
-
- expect(page).to have_content('Something wrong!')
- end
- end
-
before do
allow_any_instance_of(GoogleApi::CloudPlatform::Client)
.to receive(:projects_zones_clusters_create) do
@@ -82,14 +56,32 @@ describe 'Gcp Cluster', :js do
fill_in 'cluster[provider_gcp_attributes][machine_type]', with: 'n1-standard-2'
end
- it_behaves_like 'valid cluster gcp form'
+ it 'users sees a form with the GCP token' do
+ expect(page).to have_selector(:css, 'form[data-token="token"]')
+ end
- context 'RBAC is enabled for the cluster' do
- before do
- check 'cluster_provider_gcp_attributes_legacy_abac'
- end
+ it 'user sees a cluster details page and creation status' do
+ subject
+
+ expect(page).to have_content('Kubernetes cluster is being created on Google Kubernetes Engine...')
+
+ Clusters::Cluster.last.provider.make_created!
+
+ expect(page).to have_content('Kubernetes cluster was successfully created on Google Kubernetes Engine')
+ end
+
+ it 'user sees a error if something wrong during creation' do
+ subject
+
+ expect(page).to have_content('Kubernetes cluster is being created on Google Kubernetes Engine...')
+
+ Clusters::Cluster.last.provider.make_errored!('Something wrong!')
+
+ expect(page).to have_content('Something wrong!')
+ end
- it_behaves_like 'valid cluster gcp form'
+ it 'user sees RBAC is enabled by default' do
+ expect(page).to have_checked_field('RBAC-enabled cluster')
end
end
diff --git a/spec/features/projects/clusters/user_spec.rb b/spec/features/projects/clusters/user_spec.rb
index 250c964cc32..1f2f7592d8b 100644
--- a/spec/features/projects/clusters/user_spec.rb
+++ b/spec/features/projects/clusters/user_spec.rb
@@ -23,19 +23,6 @@ describe 'User Cluster', :js do
end
context 'when user filled form with valid parameters' do
- shared_examples 'valid cluster user form' do
- it 'user sees a cluster details page' do
- subject
-
- expect(page).to have_content('Kubernetes cluster integration')
- expect(page.find_field('cluster[name]').value).to eq('dev-cluster')
- expect(page.find_field('cluster[platform_kubernetes_attributes][api_url]').value)
- .to have_content('http://example.com')
- expect(page.find_field('cluster[platform_kubernetes_attributes][token]').value)
- .to have_content('my-token')
- end
- end
-
before do
fill_in 'cluster_name', with: 'dev-cluster'
fill_in 'cluster_platform_kubernetes_attributes_api_url', with: 'http://example.com'
@@ -44,20 +31,19 @@ describe 'User Cluster', :js do
subject { click_button 'Add Kubernetes cluster' }
- it_behaves_like 'valid cluster user form'
-
- context 'RBAC is enabled for the cluster' do
- before do
- check 'cluster_platform_kubernetes_attributes_authorization_type'
- end
-
- it_behaves_like 'valid cluster user form'
+ it 'user sees a cluster details page' do
+ subject
- it 'user sees a cluster details page with RBAC enabled' do
- subject
+ expect(page).to have_content('Kubernetes cluster integration')
+ expect(page.find_field('cluster[name]').value).to eq('dev-cluster')
+ expect(page.find_field('cluster[platform_kubernetes_attributes][api_url]').value)
+ .to have_content('http://example.com')
+ expect(page.find_field('cluster[platform_kubernetes_attributes][token]').value)
+ .to have_content('my-token')
+ end
- expect(page.find_field('cluster[platform_kubernetes_attributes][authorization_type]', disabled: true)).to be_checked
- end
+ it 'user sees RBAC is enabled by default' do
+ expect(page).to have_checked_field('RBAC-enabled cluster')
end
end
diff --git a/spec/features/projects/commits/user_browses_commits_spec.rb b/spec/features/projects/commits/user_browses_commits_spec.rb
index 2159adf49fc..574a8aefd63 100644
--- a/spec/features/projects/commits/user_browses_commits_spec.rb
+++ b/spec/features/projects/commits/user_browses_commits_spec.rb
@@ -93,7 +93,7 @@ describe 'User browses commits' do
it 'shows a blank label' do
allow_any_instance_of(Gitlab::Diff::File).to receive(:blob).and_return(nil)
- allow_any_instance_of(Gitlab::Diff::File).to receive(:raw_binary?).and_return(true)
+ allow_any_instance_of(Gitlab::Diff::File).to receive(:binary?).and_return(true)
visit(project_commit_path(project, commit))
diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb
index cb7a912946c..09de983f669 100644
--- a/spec/features/runners_spec.rb
+++ b/spec/features/runners_spec.rb
@@ -259,8 +259,9 @@ describe 'Runners' do
context 'group runners in group settings' do
let(:group) { create(:group) }
+
before do
- group.add_maintainer(user)
+ group.add_owner(user)
end
context 'group with no runners' do
diff --git a/spec/features/users/overview_spec.rb b/spec/features/users/overview_spec.rb
index 873de85708a..8748230fa0c 100644
--- a/spec/features/users/overview_spec.rb
+++ b/spec/features/users/overview_spec.rb
@@ -33,6 +33,8 @@ describe 'Overview tab on a user profile', :js do
it 'does not show any entries in the list of activities' do
page.within('.activities-block') do
+ expect(page).to have_selector('.loading', visible: false)
+ expect(page).to have_content('No activities found')
expect(page).not_to have_selector('.event-item')
end
end
@@ -93,6 +95,7 @@ describe 'Overview tab on a user profile', :js do
it 'it shows an empty project list with an info message' do
page.within('.projects-block') do
+ expect(page).to have_selector('.loading', visible: false)
expect(page).to have_content('No projects found')
expect(page).not_to have_selector('.project-row')
end
diff --git a/spec/finders/releases_finder_spec.rb b/spec/finders/releases_finder_spec.rb
new file mode 100644
index 00000000000..32ee15134a2
--- /dev/null
+++ b/spec/finders/releases_finder_spec.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ReleasesFinder do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository) }
+ let(:repository) { project.repository }
+ let(:v1_0_0) { create(:release, project: project, tag: 'v1.0.0') }
+ let(:v1_1_0) { create(:release, project: project, tag: 'v1.1.0') }
+
+ subject { described_class.new(project, user)}
+
+ before do
+ v1_0_0.update_attribute(:created_at, 2.days.ago)
+ v1_1_0.update_attribute(:created_at, 1.day.ago)
+ end
+
+ describe '#execute' do
+ context 'when the user is not part of the project' do
+ it 'returns no releases' do
+ releases = subject.execute
+
+ expect(releases).to be_empty
+ end
+ end
+
+ context 'when the user is a project developer' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'sorts by creation date' do
+ releases = subject.execute
+
+ expect(releases).to be_present
+ expect(releases.size).to eq(2)
+ expect(releases).to eq([v1_1_0, v1_0_0])
+ end
+ end
+ end
+end
diff --git a/spec/fixtures/api/schemas/entities/diff_viewer.json b/spec/fixtures/api/schemas/entities/diff_viewer.json
index 19780f49a88..81325cd86c6 100644
--- a/spec/fixtures/api/schemas/entities/diff_viewer.json
+++ b/spec/fixtures/api/schemas/entities/diff_viewer.json
@@ -1,8 +1,20 @@
{
"type": "object",
- "required": ["name"],
+ "required": [
+ "name"
+ ],
"properties": {
- "name": { "type": ["string"] }
+ "name": {
+ "type": [
+ "string"
+ ]
+ },
+ "error": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
},
"additionalProperties": false
}
diff --git a/spec/fixtures/api/schemas/release.json b/spec/fixtures/api/schemas/release.json
new file mode 100644
index 00000000000..45fa8b074d4
--- /dev/null
+++ b/spec/fixtures/api/schemas/release.json
@@ -0,0 +1,37 @@
+{
+ "type": "object",
+ "required": ["name", "tag_name"],
+ "properties": {
+ "name": { "type": "string" },
+ "tag_name": { "type": "string" },
+ "description": { "type": "string" },
+ "description_html": { "type": "string" },
+ "created_at": { "type": "date" },
+ "commit": {
+ "oneOf": [{ "type": "null" }, { "$ref": "public_api/v4/commit/basic.json" }]
+ },
+ "author": {
+ "oneOf": [{ "type": "null" }, { "$ref": "public_api/v4/user/basic.json" }]
+ },
+ "assets": {
+ "count": { "type": "integer" },
+ "links": {
+ "type": "array",
+ "items": {
+ "id": "integer",
+ "name": "string",
+ "url": "string",
+ "external": "boolean"
+ }
+ },
+ "sources": {
+ "type": "array",
+ "items": {
+ "format": "zip",
+ "url": "string"
+ }
+ }
+ }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/releases.json b/spec/fixtures/api/schemas/releases.json
new file mode 100644
index 00000000000..e26215707fe
--- /dev/null
+++ b/spec/fixtures/api/schemas/releases.json
@@ -0,0 +1,4 @@
+{
+ "type": "array",
+ "items": { "$ref": "release.json" }
+}
diff --git a/spec/fixtures/emails/merge_request_multiple_patches.eml b/spec/fixtures/emails/merge_request_multiple_patches.eml
index 311b99a525d..7d2e0cd4e50 100644
--- a/spec/fixtures/emails/merge_request_multiple_patches.eml
+++ b/spec/fixtures/emails/merge_request_multiple_patches.eml
@@ -1,5 +1,5 @@
From: "Jake the Dog" <jake@adventuretime.ooo>
-To: incoming+gitlabhq/gitlabhq+merge-request+auth_token@appmail.adventuretime.ooo
+To: incoming+gitlabhq-gitlabhq-project_id-auth_token-merge-request@appmail.adventuretime.ooo
Subject: new-branch-with-a-patch
Date: Wed, 31 Oct 2018 17:27:52 +0100
X-Mailer: MailMate (1.12r5523)
diff --git a/spec/fixtures/emails/merge_request_with_conflicting_patch.eml b/spec/fixtures/emails/merge_request_with_conflicting_patch.eml
index ddfdfe9e24a..5c9eda640bc 100644
--- a/spec/fixtures/emails/merge_request_with_conflicting_patch.eml
+++ b/spec/fixtures/emails/merge_request_with_conflicting_patch.eml
@@ -1,5 +1,5 @@
From: "Jake the Dog" <jake@adventuretime.ooo>
-To: incoming+gitlabhq/gitlabhq+merge-request+auth_token@appmail.adventuretime.ooo
+To: incoming+gitlabhq-gitlabhq-project_id-auth_token-merge-request@appmail.adventuretime.ooo
Subject: feature
Date: Wed, 31 Oct 2018 17:27:52 +0100
X-Mailer: MailMate (1.12r5523)
diff --git a/spec/fixtures/emails/merge_request_with_patch_and_target_branch.eml b/spec/fixtures/emails/merge_request_with_patch_and_target_branch.eml
index 965658721cd..9fabfc23e3b 100644
--- a/spec/fixtures/emails/merge_request_with_patch_and_target_branch.eml
+++ b/spec/fixtures/emails/merge_request_with_patch_and_target_branch.eml
@@ -1,5 +1,5 @@
From: "Jake the Dog" <jake@adventuretime.ooo>
-To: incoming+gitlabhq/gitlabhq+merge-request+auth_token@appmail.adventuretime.ooo
+To: incoming+gitlabhq-gitlabhq-project_id-auth_token-merge-request@appmail.adventuretime.ooo
Subject: new-branch-with-a-patch
Date: Wed, 24 Oct 2018 16:39:49 +0200
X-Mailer: MailMate (1.12r5523)
diff --git a/spec/fixtures/emails/valid_merge_request_with_patch.eml b/spec/fixtures/emails/valid_merge_request_with_patch.eml
index 143fa77d1fa..e0f406639a3 100644
--- a/spec/fixtures/emails/valid_merge_request_with_patch.eml
+++ b/spec/fixtures/emails/valid_merge_request_with_patch.eml
@@ -1,5 +1,5 @@
From: "Jake the Dog" <jake@adventuretime.ooo>
-To: incoming+gitlabhq/gitlabhq+merge-request+auth_token@appmail.adventuretime.ooo
+To: incoming+gitlabhq-gitlabhq-project_id-auth_token-merge-request@appmail.adventuretime.ooo
Subject: new-branch-with-a-patch
Date: Wed, 24 Oct 2018 16:39:49 +0200
X-Mailer: MailMate (1.12r5523)
diff --git a/spec/fixtures/emails/valid_new_issue.eml b/spec/fixtures/emails/valid_new_issue.eml
index 3cf53a656a5..7d63016ed04 100644
--- a/spec/fixtures/emails/valid_new_issue.eml
+++ b/spec/fixtures/emails/valid_new_issue.eml
@@ -1,11 +1,11 @@
Return-Path: <jake@adventuretime.ooo>
Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400
Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <incoming+gitlabhq/gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 17:03:50 -0400
-Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <incoming+gitlabhq/gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700
+Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <incoming+gitlabhq-gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700
Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700
Date: Thu, 13 Jun 2013 17:03:48 -0400
From: Jake the Dog <jake@adventuretime.ooo>
-To: incoming+gitlabhq/gitlabhq+auth_token@appmail.adventuretime.ooo
+To: incoming+gitlabhq-gitlabhq-project_id-auth_token-issue@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
Subject: New Issue by email
Mime-Version: 1.0
diff --git a/spec/fixtures/emails/valid_new_issue_empty.eml b/spec/fixtures/emails/valid_new_issue_empty.eml
index fc1d52a3f42..58a6ef29d69 100644
--- a/spec/fixtures/emails/valid_new_issue_empty.eml
+++ b/spec/fixtures/emails/valid_new_issue_empty.eml
@@ -1,11 +1,11 @@
Return-Path: <jake@adventuretime.ooo>
Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400
Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <incoming+gitlabhq/gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 17:03:50 -0400
-Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <incoming+gitlabhq/gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700
+Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <incoming+gitlabhq-gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700
Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700
Date: Thu, 13 Jun 2013 17:03:48 -0400
From: Jake the Dog <jake@adventuretime.ooo>
-To: incoming+gitlabhq/gitlabhq+auth_token@appmail.adventuretime.ooo
+To: incoming+gitlabhq-gitlabhq-project_id-auth_token-issue@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
Subject: New Issue by email
Mime-Version: 1.0
diff --git a/spec/fixtures/emails/valid_new_issue_legacy.eml b/spec/fixtures/emails/valid_new_issue_legacy.eml
new file mode 100644
index 00000000000..3cf53a656a5
--- /dev/null
+++ b/spec/fixtures/emails/valid_new_issue_legacy.eml
@@ -0,0 +1,23 @@
+Return-Path: <jake@adventuretime.ooo>
+Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400
+Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <incoming+gitlabhq/gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 17:03:50 -0400
+Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <incoming+gitlabhq/gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700
+Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700
+Date: Thu, 13 Jun 2013 17:03:48 -0400
+From: Jake the Dog <jake@adventuretime.ooo>
+To: incoming+gitlabhq/gitlabhq+auth_token@appmail.adventuretime.ooo
+Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
+Subject: New Issue by email
+Mime-Version: 1.0
+Content-Type: text/plain;
+ charset=ISO-8859-1
+Content-Transfer-Encoding: 7bit
+X-Sieve: CMU Sieve 2.2
+X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu,
+ 13 Jun 2013 14:03:48 -0700 (PDT)
+X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1
+
+The reply by email functionality should be extended to allow creating a new issue by email.
+
+* Allow an admin to specify which project the issue should be created under by checking the sender domain.
+* Possibly allow the use of regular expression matches within the subject/body to specify which project the issue should be created under.
diff --git a/spec/fixtures/emails/valid_new_issue_with_quote.eml b/spec/fixtures/emails/valid_new_issue_with_quote.eml
index 0caf8ed4e9e..3a9b9dbbba5 100644
--- a/spec/fixtures/emails/valid_new_issue_with_quote.eml
+++ b/spec/fixtures/emails/valid_new_issue_with_quote.eml
@@ -1,11 +1,11 @@
Return-Path: <jake@adventuretime.ooo>
Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400
Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <incoming+gitlabhq/gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 17:03:50 -0400
-Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <incoming+gitlabhq/gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700
+Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <incoming+gitlabhq-gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700
Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700
Date: Thu, 13 Jun 2013 17:03:48 -0400
From: Jake the Dog <jake@adventuretime.ooo>
-To: incoming+gitlabhq/gitlabhq+auth_token@appmail.adventuretime.ooo
+To: incoming+gitlabhq-gitlabhq-project_id-auth_token-issue@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
Subject: New Issue by email
Mime-Version: 1.0
diff --git a/spec/fixtures/emails/valid_new_merge_request.eml b/spec/fixtures/emails/valid_new_merge_request.eml
index 729df674604..e12843ea76b 100644
--- a/spec/fixtures/emails/valid_new_merge_request.eml
+++ b/spec/fixtures/emails/valid_new_merge_request.eml
@@ -1,11 +1,11 @@
Return-Path: <jake@adventuretime.ooo>
Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400
-Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <incoming+gitlabhq/gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 17:03:50 -0400
-Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <incoming+gitlabhq/gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700
+Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <incoming+gitlabhq-gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 17:03:50 -0400
+Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <incoming+gitlabhq-gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700
Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700
Date: Thu, 13 Jun 2013 17:03:48 -0400
From: Jake the Dog <jake@adventuretime.ooo>
-To: incoming+gitlabhq/gitlabhq+merge-request+auth_token@appmail.adventuretime.ooo
+To: incoming+gitlabhq-gitlabhq-project_id-auth_token-merge-request@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
Subject: feature
Mime-Version: 1.0
diff --git a/spec/fixtures/emails/valid_new_merge_request_legacy.eml b/spec/fixtures/emails/valid_new_merge_request_legacy.eml
new file mode 100644
index 00000000000..b6cf064af19
--- /dev/null
+++ b/spec/fixtures/emails/valid_new_merge_request_legacy.eml
@@ -0,0 +1,20 @@
+Return-Path: <jake@adventuretime.ooo>
+Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400
+Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <incoming+gitlabhq-gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 17:03:50 -0400
+Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <incoming+gitlabhq/gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700
+Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700
+Date: Thu, 13 Jun 2013 17:03:48 -0400
+From: Jake the Dog <jake@adventuretime.ooo>
+To: incoming+gitlabhq/gitlabhq+merge-request+auth_token@appmail.adventuretime.ooo
+Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
+Subject: feature
+Mime-Version: 1.0
+Content-Type: text/plain;
+ charset=ISO-8859-1
+Content-Transfer-Encoding: 7bit
+X-Sieve: CMU Sieve 2.2
+X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu,
+ 13 Jun 2013 14:03:48 -0700 (PDT)
+X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1
+
+Merge request description
diff --git a/spec/fixtures/emails/valid_new_merge_request_no_description.eml b/spec/fixtures/emails/valid_new_merge_request_no_description.eml
index 480675a6d7e..3ac0ea191a9 100644
--- a/spec/fixtures/emails/valid_new_merge_request_no_description.eml
+++ b/spec/fixtures/emails/valid_new_merge_request_no_description.eml
@@ -1,11 +1,11 @@
Return-Path: <jake@adventuretime.ooo>
Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400
Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <incoming+gitlabhq/gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 17:03:50 -0400
-Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <incoming+gitlabhq/gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700
+Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <incoming+gitlabhq-gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700
Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700
Date: Thu, 13 Jun 2013 17:03:48 -0400
From: Jake the Dog <jake@adventuretime.ooo>
-To: incoming+gitlabhq/gitlabhq+merge-request+auth_token@appmail.adventuretime.ooo
+To: incoming+gitlabhq-gitlabhq-project_id-auth_token-merge-request@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
Subject: feature
Mime-Version: 1.0
diff --git a/spec/fixtures/emails/valid_new_merge_request_no_subject.eml b/spec/fixtures/emails/valid_new_merge_request_no_subject.eml
index 27eb1b7d922..c2735ccb08a 100644
--- a/spec/fixtures/emails/valid_new_merge_request_no_subject.eml
+++ b/spec/fixtures/emails/valid_new_merge_request_no_subject.eml
@@ -1,11 +1,11 @@
Return-Path: <jake@adventuretime.ooo>
Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400
Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <incoming+gitlabhq/gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 17:03:50 -0400
-Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <incoming+gitlabhq/gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700
+Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <incoming+gitlabhq-gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700
Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700
Date: Thu, 13 Jun 2013 17:03:48 -0400
From: Jake the Dog <jake@adventuretime.ooo>
-To: incoming+gitlabhq/gitlabhq+merge-request+auth_token@appmail.adventuretime.ooo
+To: incoming+gitlabhq-gitlabhq-project_id-auth_token-merge-request@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
Subject:
Mime-Version: 1.0
diff --git a/spec/fixtures/emails/wrong_incoming_email_token.eml b/spec/fixtures/emails/wrong_issue_incoming_email_token.eml
index 0994c2f7775..d3ba6943a90 100644
--- a/spec/fixtures/emails/wrong_incoming_email_token.eml
+++ b/spec/fixtures/emails/wrong_issue_incoming_email_token.eml
@@ -1,11 +1,11 @@
Return-Path: <jake@adventuretime.ooo>
Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400
Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <incoming+gitlabhq/gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 17:03:50 -0400
-Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <incoming+gitlabhq/gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700
+Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <incoming+gitlabhq-gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700
Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700
Date: Thu, 13 Jun 2013 17:03:48 -0400
From: Jake the Dog <jake@adventuretime.ooo>
-To: incoming+gitlabhq/gitlabhq+bad_token@appmail.adventuretime.ooo
+To: incoming+gitlabhq-gitlabhq-project_id-bad_token-issue@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
Subject: New Issue by email
Mime-Version: 1.0
diff --git a/spec/fixtures/emails/wrong_merge_request_incoming_email_token.eml b/spec/fixtures/emails/wrong_merge_request_incoming_email_token.eml
new file mode 100644
index 00000000000..c7b758b8f1f
--- /dev/null
+++ b/spec/fixtures/emails/wrong_merge_request_incoming_email_token.eml
@@ -0,0 +1,18 @@
+Return-Path: <jake@adventuretime.ooo>
+Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400
+Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <incoming+gitlabhq/gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 17:03:50 -0400
+Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <incoming+gitlabhq-gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700
+Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700
+Date: Thu, 13 Jun 2013 17:03:48 -0400
+From: Jake the Dog <jake@adventuretime.ooo>
+To: incoming+gitlabhq-gitlabhq-project_id-bad_token-merge-request@appmail.adventuretime.ooo
+Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
+Subject: New Issue by email
+Mime-Version: 1.0
+Content-Type: text/plain;
+ charset=ISO-8859-1
+Content-Transfer-Encoding: 7bit
+X-Sieve: CMU Sieve 2.2
+X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu,
+ 13 Jun 2013 14:03:48 -0700 (PDT)
+X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1
diff --git a/spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml b/spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml
index 0bab94a7c2e..1e88cd120aa 100644
--- a/spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml
+++ b/spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml
@@ -2,7 +2,6 @@ before_script:
- apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs
- ruby -v
- which ruby
- - gem install bundler --no-ri --no-rdoc
- bundle install --jobs $(nproc) "${FLAGS[@]}"
rspec:
diff --git a/spec/fixtures/security-reports/deprecated/gl-dependency-scanning-report.json b/spec/fixtures/security-reports/deprecated/gl-dependency-scanning-report.json
new file mode 100644
index 00000000000..ce66f562175
--- /dev/null
+++ b/spec/fixtures/security-reports/deprecated/gl-dependency-scanning-report.json
@@ -0,0 +1,178 @@
+[
+ {
+ "category": "dependency_scanning",
+ "name": "io.netty/netty - CVE-2014-3488",
+ "message": "DoS by CPU exhaustion when using malicious SSL packets",
+ "cve": "app/pom.xml:io.netty/netty@3.9.1.Final:CVE-2014-3488",
+ "severity": "Unknown",
+ "solution": "Upgrade to the latest version",
+ "scanner": {
+ "id": "gemnasium",
+ "name": "Gemnasium"
+ },
+ "location": {
+ "file": "app/pom.xml",
+ "dependency": {
+ "package": {
+ "name": "io.netty/netty"
+ },
+ "version": "3.9.1.Final"
+ }
+ },
+ "identifiers": [
+ {
+ "type": "gemnasium",
+ "name": "Gemnasium-d1bf36d9-9f07-46cd-9cfc-8675338ada8f",
+ "value": "d1bf36d9-9f07-46cd-9cfc-8675338ada8f",
+ "url": "https://deps.sec.gitlab.com/packages/maven/io.netty/netty/versions/3.9.1.Final/advisories"
+ },
+ {
+ "type": "cve",
+ "name": "CVE-2014-3488",
+ "value": "CVE-2014-3488",
+ "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-3488"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://bugzilla.redhat.com/CVE-2014-3488"
+ },
+ {
+ "url": "http://netty.io/news/2014/06/11/3.html"
+ },
+ {
+ "url": "https://github.com/netty/netty/issues/2562"
+ }
+ ],
+ "priority": "Unknown",
+ "file": "app/pom.xml",
+ "url": "https://bugzilla.redhat.com/CVE-2014-3488",
+ "tool": "gemnasium"
+ },
+ {
+ "category": "dependency_scanning",
+ "name": "Django - CVE-2017-12794",
+ "message": "Possible XSS in traceback section of technical 500 debug page",
+ "cve": "app/requirements.txt:Django@1.11.3:CVE-2017-12794",
+ "severity": "Unknown",
+ "solution": "Upgrade to latest version or apply patch.",
+ "scanner": {
+ "id": "gemnasium",
+ "name": "Gemnasium"
+ },
+ "location": {
+ "file": "app/requirements.txt",
+ "dependency": {
+ "package": {
+ "name": "Django"
+ },
+ "version": "1.11.3"
+ }
+ },
+ "identifiers": [
+ {
+ "type": "gemnasium",
+ "name": "Gemnasium-6162a015-8635-4a15-8d7c-dc9321db366f",
+ "value": "6162a015-8635-4a15-8d7c-dc9321db366f",
+ "url": "https://deps.sec.gitlab.com/packages/pypi/Django/versions/1.11.3/advisories"
+ },
+ {
+ "type": "cve",
+ "name": "CVE-2017-12794",
+ "value": "CVE-2017-12794",
+ "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-12794"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://www.djangoproject.com/weblog/2017/sep/05/security-releases/"
+ }
+ ],
+ "priority": "Unknown",
+ "file": "app/requirements.txt",
+ "url": "https://www.djangoproject.com/weblog/2017/sep/05/security-releases/",
+ "tool": "gemnasium"
+ },
+ {
+ "category": "dependency_scanning",
+ "name": "nokogiri - USN-3424-1",
+ "message": "Vulnerabilities in libxml2",
+ "cve": "rails/Gemfile.lock:nokogiri@1.8.0:USN-3424-1",
+ "severity": "Unknown",
+ "solution": "Upgrade to latest version.",
+ "scanner": {
+ "id": "gemnasium",
+ "name": "Gemnasium"
+ },
+ "location": {
+ "file": "rails/Gemfile.lock",
+ "dependency": {
+ "package": {
+ "name": "nokogiri"
+ },
+ "version": "1.8.0"
+ }
+ },
+ "identifiers": [
+ {
+ "type": "gemnasium",
+ "name": "Gemnasium-06565b64-486d-4326-b906-890d9915804d",
+ "value": "06565b64-486d-4326-b906-890d9915804d",
+ "url": "https://deps.sec.gitlab.com/packages/gem/nokogiri/versions/1.8.0/advisories"
+ },
+ {
+ "type": "usn",
+ "name": "USN-3424-1",
+ "value": "USN-3424-1",
+ "url": "https://usn.ubuntu.com/3424-1/"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://github.com/sparklemotion/nokogiri/issues/1673"
+ }
+ ],
+ "priority": "Unknown",
+ "file": "rails/Gemfile.lock",
+ "url": "https://github.com/sparklemotion/nokogiri/issues/1673",
+ "tool": "gemnasium"
+ },
+ {
+ "category": "dependency_scanning",
+ "name": "ffi - CVE-2018-1000201",
+ "message": "ruby-ffi DDL loading issue on Windows OS",
+ "cve": "ffi:1.9.18:CVE-2018-1000201",
+ "severity": "High",
+ "solution": "upgrade to \u003e= 1.9.24",
+ "scanner": {
+ "id": "bundler_audit",
+ "name": "bundler-audit"
+ },
+ "location": {
+ "file": "sast-sample-rails/Gemfile.lock",
+ "dependency": {
+ "package": {
+ "name": "ffi"
+ },
+ "version": "1.9.18"
+ }
+ },
+ "identifiers": [
+ {
+ "type": "cve",
+ "name": "CVE-2018-1000201",
+ "value": "CVE-2018-1000201",
+ "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-1000201"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://github.com/ffi/ffi/releases/tag/1.9.24"
+ }
+ ],
+ "priority": "High",
+ "file": "sast-sample-rails/Gemfile.lock",
+ "url": "https://github.com/ffi/ffi/releases/tag/1.9.24",
+ "tool": "bundler_audit"
+ }
+]
diff --git a/spec/fixtures/security-reports/deprecated/gl-sast-report.json b/spec/fixtures/security-reports/deprecated/gl-sast-report.json
new file mode 100644
index 00000000000..a85b9be8b5f
--- /dev/null
+++ b/spec/fixtures/security-reports/deprecated/gl-sast-report.json
@@ -0,0 +1,944 @@
+[
+ {
+ "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": "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": "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": "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": "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"
+ }
+]
diff --git a/spec/fixtures/security-reports/feature-branch.zip b/spec/fixtures/security-reports/feature-branch.zip
index 730ce3dc5f8..dd49f4e9e1d 100644
--- a/spec/fixtures/security-reports/feature-branch.zip
+++ b/spec/fixtures/security-reports/feature-branch.zip
Binary files differ
diff --git a/spec/fixtures/security-reports/feature-branch/gl-dependency-scanning-report.json b/spec/fixtures/security-reports/feature-branch/gl-dependency-scanning-report.json
index ce66f562175..8555be6618c 100644
--- a/spec/fixtures/security-reports/feature-branch/gl-dependency-scanning-report.json
+++ b/spec/fixtures/security-reports/feature-branch/gl-dependency-scanning-report.json
@@ -1,178 +1,181 @@
-[
- {
- "category": "dependency_scanning",
- "name": "io.netty/netty - CVE-2014-3488",
- "message": "DoS by CPU exhaustion when using malicious SSL packets",
- "cve": "app/pom.xml:io.netty/netty@3.9.1.Final:CVE-2014-3488",
- "severity": "Unknown",
- "solution": "Upgrade to the latest version",
- "scanner": {
- "id": "gemnasium",
- "name": "Gemnasium"
- },
- "location": {
- "file": "app/pom.xml",
- "dependency": {
- "package": {
- "name": "io.netty/netty"
+{
+ "version": "1.3",
+ "vulnerabilities": [
+ {
+ "category": "dependency_scanning",
+ "name": "io.netty/netty - CVE-2014-3488",
+ "message": "DoS by CPU exhaustion when using malicious SSL packets",
+ "cve": "app/pom.xml:io.netty/netty@3.9.1.Final:CVE-2014-3488",
+ "severity": "Unknown",
+ "solution": "Upgrade to the latest version",
+ "scanner": {
+ "id": "gemnasium",
+ "name": "Gemnasium"
+ },
+ "location": {
+ "file": "app/pom.xml",
+ "dependency": {
+ "package": {
+ "name": "io.netty/netty"
+ },
+ "version": "3.9.1.Final"
+ }
+ },
+ "identifiers": [
+ {
+ "type": "gemnasium",
+ "name": "Gemnasium-d1bf36d9-9f07-46cd-9cfc-8675338ada8f",
+ "value": "d1bf36d9-9f07-46cd-9cfc-8675338ada8f",
+ "url": "https://deps.sec.gitlab.com/packages/maven/io.netty/netty/versions/3.9.1.Final/advisories"
+ },
+ {
+ "type": "cve",
+ "name": "CVE-2014-3488",
+ "value": "CVE-2014-3488",
+ "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-3488"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://bugzilla.redhat.com/CVE-2014-3488"
},
- "version": "3.9.1.Final"
- }
+ {
+ "url": "http://netty.io/news/2014/06/11/3.html"
+ },
+ {
+ "url": "https://github.com/netty/netty/issues/2562"
+ }
+ ],
+ "priority": "Unknown",
+ "file": "app/pom.xml",
+ "url": "https://bugzilla.redhat.com/CVE-2014-3488",
+ "tool": "gemnasium"
},
- "identifiers": [
- {
- "type": "gemnasium",
- "name": "Gemnasium-d1bf36d9-9f07-46cd-9cfc-8675338ada8f",
- "value": "d1bf36d9-9f07-46cd-9cfc-8675338ada8f",
- "url": "https://deps.sec.gitlab.com/packages/maven/io.netty/netty/versions/3.9.1.Final/advisories"
- },
- {
- "type": "cve",
- "name": "CVE-2014-3488",
- "value": "CVE-2014-3488",
- "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-3488"
- }
- ],
- "links": [
- {
- "url": "https://bugzilla.redhat.com/CVE-2014-3488"
+ {
+ "category": "dependency_scanning",
+ "name": "Django - CVE-2017-12794",
+ "message": "Possible XSS in traceback section of technical 500 debug page",
+ "cve": "app/requirements.txt:Django@1.11.3:CVE-2017-12794",
+ "severity": "Unknown",
+ "solution": "Upgrade to latest version or apply patch.",
+ "scanner": {
+ "id": "gemnasium",
+ "name": "Gemnasium"
},
- {
- "url": "http://netty.io/news/2014/06/11/3.html"
+ "location": {
+ "file": "app/requirements.txt",
+ "dependency": {
+ "package": {
+ "name": "Django"
+ },
+ "version": "1.11.3"
+ }
},
- {
- "url": "https://github.com/netty/netty/issues/2562"
- }
- ],
- "priority": "Unknown",
- "file": "app/pom.xml",
- "url": "https://bugzilla.redhat.com/CVE-2014-3488",
- "tool": "gemnasium"
- },
- {
- "category": "dependency_scanning",
- "name": "Django - CVE-2017-12794",
- "message": "Possible XSS in traceback section of technical 500 debug page",
- "cve": "app/requirements.txt:Django@1.11.3:CVE-2017-12794",
- "severity": "Unknown",
- "solution": "Upgrade to latest version or apply patch.",
- "scanner": {
- "id": "gemnasium",
- "name": "Gemnasium"
- },
- "location": {
- "file": "app/requirements.txt",
- "dependency": {
- "package": {
- "name": "Django"
+ "identifiers": [
+ {
+ "type": "gemnasium",
+ "name": "Gemnasium-6162a015-8635-4a15-8d7c-dc9321db366f",
+ "value": "6162a015-8635-4a15-8d7c-dc9321db366f",
+ "url": "https://deps.sec.gitlab.com/packages/pypi/Django/versions/1.11.3/advisories"
},
- "version": "1.11.3"
- }
+ {
+ "type": "cve",
+ "name": "CVE-2017-12794",
+ "value": "CVE-2017-12794",
+ "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-12794"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://www.djangoproject.com/weblog/2017/sep/05/security-releases/"
+ }
+ ],
+ "priority": "Unknown",
+ "file": "app/requirements.txt",
+ "url": "https://www.djangoproject.com/weblog/2017/sep/05/security-releases/",
+ "tool": "gemnasium"
},
- "identifiers": [
- {
- "type": "gemnasium",
- "name": "Gemnasium-6162a015-8635-4a15-8d7c-dc9321db366f",
- "value": "6162a015-8635-4a15-8d7c-dc9321db366f",
- "url": "https://deps.sec.gitlab.com/packages/pypi/Django/versions/1.11.3/advisories"
+ {
+ "category": "dependency_scanning",
+ "name": "nokogiri - USN-3424-1",
+ "message": "Vulnerabilities in libxml2",
+ "cve": "rails/Gemfile.lock:nokogiri@1.8.0:USN-3424-1",
+ "severity": "Unknown",
+ "solution": "Upgrade to latest version.",
+ "scanner": {
+ "id": "gemnasium",
+ "name": "Gemnasium"
},
- {
- "type": "cve",
- "name": "CVE-2017-12794",
- "value": "CVE-2017-12794",
- "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-12794"
- }
- ],
- "links": [
- {
- "url": "https://www.djangoproject.com/weblog/2017/sep/05/security-releases/"
- }
- ],
- "priority": "Unknown",
- "file": "app/requirements.txt",
- "url": "https://www.djangoproject.com/weblog/2017/sep/05/security-releases/",
- "tool": "gemnasium"
- },
- {
- "category": "dependency_scanning",
- "name": "nokogiri - USN-3424-1",
- "message": "Vulnerabilities in libxml2",
- "cve": "rails/Gemfile.lock:nokogiri@1.8.0:USN-3424-1",
- "severity": "Unknown",
- "solution": "Upgrade to latest version.",
- "scanner": {
- "id": "gemnasium",
- "name": "Gemnasium"
- },
- "location": {
- "file": "rails/Gemfile.lock",
- "dependency": {
- "package": {
- "name": "nokogiri"
+ "location": {
+ "file": "rails/Gemfile.lock",
+ "dependency": {
+ "package": {
+ "name": "nokogiri"
+ },
+ "version": "1.8.0"
+ }
+ },
+ "identifiers": [
+ {
+ "type": "gemnasium",
+ "name": "Gemnasium-06565b64-486d-4326-b906-890d9915804d",
+ "value": "06565b64-486d-4326-b906-890d9915804d",
+ "url": "https://deps.sec.gitlab.com/packages/gem/nokogiri/versions/1.8.0/advisories"
},
- "version": "1.8.0"
- }
+ {
+ "type": "usn",
+ "name": "USN-3424-1",
+ "value": "USN-3424-1",
+ "url": "https://usn.ubuntu.com/3424-1/"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://github.com/sparklemotion/nokogiri/issues/1673"
+ }
+ ],
+ "priority": "Unknown",
+ "file": "rails/Gemfile.lock",
+ "url": "https://github.com/sparklemotion/nokogiri/issues/1673",
+ "tool": "gemnasium"
},
- "identifiers": [
- {
- "type": "gemnasium",
- "name": "Gemnasium-06565b64-486d-4326-b906-890d9915804d",
- "value": "06565b64-486d-4326-b906-890d9915804d",
- "url": "https://deps.sec.gitlab.com/packages/gem/nokogiri/versions/1.8.0/advisories"
+ {
+ "category": "dependency_scanning",
+ "name": "ffi - CVE-2018-1000201",
+ "message": "ruby-ffi DDL loading issue on Windows OS",
+ "cve": "ffi:1.9.18:CVE-2018-1000201",
+ "severity": "High",
+ "solution": "upgrade to \u003e= 1.9.24",
+ "scanner": {
+ "id": "bundler_audit",
+ "name": "bundler-audit"
},
- {
- "type": "usn",
- "name": "USN-3424-1",
- "value": "USN-3424-1",
- "url": "https://usn.ubuntu.com/3424-1/"
- }
- ],
- "links": [
- {
- "url": "https://github.com/sparklemotion/nokogiri/issues/1673"
- }
- ],
- "priority": "Unknown",
- "file": "rails/Gemfile.lock",
- "url": "https://github.com/sparklemotion/nokogiri/issues/1673",
- "tool": "gemnasium"
- },
- {
- "category": "dependency_scanning",
- "name": "ffi - CVE-2018-1000201",
- "message": "ruby-ffi DDL loading issue on Windows OS",
- "cve": "ffi:1.9.18:CVE-2018-1000201",
- "severity": "High",
- "solution": "upgrade to \u003e= 1.9.24",
- "scanner": {
- "id": "bundler_audit",
- "name": "bundler-audit"
- },
- "location": {
+ "location": {
+ "file": "sast-sample-rails/Gemfile.lock",
+ "dependency": {
+ "package": {
+ "name": "ffi"
+ },
+ "version": "1.9.18"
+ }
+ },
+ "identifiers": [
+ {
+ "type": "cve",
+ "name": "CVE-2018-1000201",
+ "value": "CVE-2018-1000201",
+ "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-1000201"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://github.com/ffi/ffi/releases/tag/1.9.24"
+ }
+ ],
+ "priority": "High",
"file": "sast-sample-rails/Gemfile.lock",
- "dependency": {
- "package": {
- "name": "ffi"
- },
- "version": "1.9.18"
- }
- },
- "identifiers": [
- {
- "type": "cve",
- "name": "CVE-2018-1000201",
- "value": "CVE-2018-1000201",
- "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-1000201"
- }
- ],
- "links": [
- {
- "url": "https://github.com/ffi/ffi/releases/tag/1.9.24"
- }
- ],
- "priority": "High",
- "file": "sast-sample-rails/Gemfile.lock",
- "url": "https://github.com/ffi/ffi/releases/tag/1.9.24",
- "tool": "bundler_audit"
- }
-]
+ "url": "https://github.com/ffi/ffi/releases/tag/1.9.24",
+ "tool": "bundler_audit"
+ }
+ ]
+}
diff --git a/spec/fixtures/security-reports/feature-branch/gl-license-management-report.json b/spec/fixtures/security-reports/feature-branch/gl-license-management-report.json
index c1d20fa02fa..5fd81fd69bd 100644
--- a/spec/fixtures/security-reports/feature-branch/gl-license-management-report.json
+++ b/spec/fixtures/security-reports/feature-branch/gl-license-management-report.json
@@ -1,16 +1,12 @@
{
"licenses": [
{
- "count": 13,
- "name": "MIT"
- },
- {
- "count": 2,
- "name": "New BSD"
+ "count": 1,
+ "name": "WTFPL"
},
{
"count": 1,
- "name": "LGPL"
+ "name": "MIT"
}
],
"dependencies": [
@@ -20,107 +16,9 @@
"url": "http://opensource.org/licenses/mit-license"
},
"dependency": {
- "name": "bundler",
- "url": "http://bundler.io",
- "description": "The best way to manage your application's dependencies",
- "pathes": [
- "."
- ]
- }
- },
- {
- "license": {
- "name": "MIT",
- "url": "http://opensource.org/licenses/mit-license"
- },
- "dependency": {
- "name": "concurrent-ruby",
- "url": "http://www.concurrent-ruby.com",
- "description": "Modern concurrency tools for Ruby. Inspired by Erlang, Clojure, Scala, Haskell, F#, C#, Java, and classic concurrency patterns.",
- "pathes": [
- "."
- ]
- }
- },
- {
- "license": {
- "name": "MIT",
- "url": "http://opensource.org/licenses/mit-license"
- },
- "dependency": {
- "name": "connection_pool",
- "url": "https://github.com/mperham/connection_pool",
- "description": "Generic connection pool for Ruby",
- "pathes": [
- "."
- ]
- }
- },
- {
- "license": {
- "name": "MIT",
- "url": "http://opensource.org/licenses/mit-license"
- },
- "dependency": {
- "name": "mini_portile2",
- "url": "http://github.com/flavorjones/mini_portile",
- "description": "Simplistic port-like solution for developers",
- "pathes": [
- "."
- ]
- }
- },
- {
- "license": {
- "name": "MIT",
- "url": "http://opensource.org/licenses/mit-license"
- },
- "dependency": {
- "name": "mustermann",
- "url": "https://github.com/sinatra/mustermann",
- "description": "Your personal string matching expert.",
- "pathes": [
- "."
- ]
- }
- },
- {
- "license": {
- "name": "MIT",
- "url": "http://opensource.org/licenses/mit-license"
- },
- "dependency": {
- "name": "nokogiri",
- "url": "http://nokogiri.org",
- "description": "Nokogiri (鋸) is an HTML, XML, SAX, and Reader parser",
- "pathes": [
- "."
- ]
- }
- },
- {
- "license": {
- "name": "New BSD",
- "url": "http://opensource.org/licenses/BSD-3-Clause"
- },
- "dependency": {
- "name": "pg",
- "url": "https://bitbucket.org/ged/ruby-pg",
- "description": "Pg is the Ruby interface to the {PostgreSQL RDBMS}[http://www.postgresql.org/]",
- "pathes": [
- "."
- ]
- }
- },
- {
- "license": {
- "name": "New BSD",
- "url": "http://opensource.org/licenses/BSD-3-Clause"
- },
- "dependency": {
- "name": "puma",
- "url": "http://puma.io",
- "description": "Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server for Ruby/Rack applications",
+ "name": "actioncable",
+ "url": "http://rubyonrails.org",
+ "description": "WebSocket framework for Rails.",
"pathes": [
"."
]
@@ -128,111 +26,13 @@
},
{
"license": {
- "name": "MIT",
- "url": "http://opensource.org/licenses/mit-license"
- },
- "dependency": {
- "name": "rack",
- "url": "https://rack.github.io/",
- "description": "a modular Ruby webserver interface",
- "pathes": [
- "."
- ]
- }
- },
- {
- "license": {
- "name": "MIT",
- "url": "http://opensource.org/licenses/mit-license"
- },
- "dependency": {
- "name": "rack-protection",
- "url": "http://github.com/sinatra/sinatra/tree/master/rack-protection",
- "description": "Protect against typical web attacks, works with all Rack apps, including Rails.",
- "pathes": [
- "."
- ]
- }
- },
- {
- "license": {
- "name": "MIT",
- "url": "http://opensource.org/licenses/mit-license"
- },
- "dependency": {
- "name": "redis",
- "url": "https://github.com/redis/redis-rb",
- "description": "A Ruby client library for Redis",
- "pathes": [
- "."
- ]
- }
- },
- {
- "license": {
- "name": "LGPL",
- "url": "http://www.gnu.org/licenses/lgpl.txt"
- },
- "dependency": {
- "name": "sidekiq",
- "url": "http://sidekiq.org",
- "description": "Simple, efficient background processing for Ruby",
- "pathes": [
- "."
- ]
- }
- },
- {
- "license": {
- "name": "MIT",
- "url": "http://opensource.org/licenses/mit-license"
- },
- "dependency": {
- "name": "sinatra",
- "url": "http://www.sinatrarb.com/",
- "description": "Classy web-development dressed in a DSL",
- "pathes": [
- "."
- ]
- }
- },
- {
- "license": {
- "name": "MIT",
- "url": "http://opensource.org/licenses/mit-license"
- },
- "dependency": {
- "name": "slim",
- "url": "http://slim-lang.com/",
- "description": "Slim is a template language.",
- "pathes": [
- "."
- ]
- }
- },
- {
- "license": {
- "name": "MIT",
- "url": "http://opensource.org/licenses/mit-license"
- },
- "dependency": {
- "name": "temple",
- "url": "https://github.com/judofyr/temple",
- "description": "Template compilation framework in Ruby",
- "pathes": [
- "."
- ]
- }
- },
- {
- "license": {
- "name": "MIT",
- "url": "http://opensource.org/licenses/mit-license"
+ "name": "WTFPL",
+ "url": "http://www.wtfpl.net/"
},
"dependency": {
- "name": "tilt",
- "url": "http://github.com/rtomayko/tilt/",
- "description": "Generic interface to multiple Ruby template engines",
+ "name": "wtfpl_init",
+ "url": "https://rubygems.org/gems/wtfpl_init",
+ "description": "Download WTFPL license file and rename to LICENSE.md or something",
"pathes": [
"."
]
diff --git a/spec/fixtures/security-reports/feature-branch/gl-sast-report.json b/spec/fixtures/security-reports/feature-branch/gl-sast-report.json
index a85b9be8b5f..4bef3d22f70 100644
--- a/spec/fixtures/security-reports/feature-branch/gl-sast-report.json
+++ b/spec/fixtures/security-reports/feature-branch/gl-sast-report.json
@@ -1,944 +1,947 @@
-[
- {
- "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": {
+{
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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": "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",
- "start_line": 4
- },
- "identifiers": [
- {
- "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": {
+ "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": "cwe",
+ "name": "CWE-362",
+ "value": "362",
+ "url": "https://cwe.mitre.org/data/definitions/362.html"
+ }
+ ],
"file": "c/subdir/utils.c",
- "start_line": 8
- },
- "identifiers": [
- {
- "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": {
+ "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": "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",
- "start_line": 6
- },
- "identifiers": [
- {
- "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": {
+ "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": "cwe",
+ "name": "CWE-120",
+ "value": "120",
+ "url": "https://cwe.mitre.org/data/definitions/120.html"
+ }
+ ],
"file": "cplusplus/src/hello.cpp",
- "start_line": 7
- },
- "identifiers": [
- {
- "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"
- }
-]
+ "line": 7,
+ "url": "https://cwe.mitre.org/data/definitions/120.html",
+ "tool": "flawfinder"
+ }
+ ]
+}
diff --git a/spec/fixtures/security-reports/master.zip b/spec/fixtures/security-reports/master.zip
index 4684aecb738..2261b5a1674 100644
--- a/spec/fixtures/security-reports/master.zip
+++ b/spec/fixtures/security-reports/master.zip
Binary files differ
diff --git a/spec/fixtures/security-reports/master/gl-dependency-scanning-report.json b/spec/fixtures/security-reports/master/gl-dependency-scanning-report.json
index ce66f562175..8555be6618c 100644
--- a/spec/fixtures/security-reports/master/gl-dependency-scanning-report.json
+++ b/spec/fixtures/security-reports/master/gl-dependency-scanning-report.json
@@ -1,178 +1,181 @@
-[
- {
- "category": "dependency_scanning",
- "name": "io.netty/netty - CVE-2014-3488",
- "message": "DoS by CPU exhaustion when using malicious SSL packets",
- "cve": "app/pom.xml:io.netty/netty@3.9.1.Final:CVE-2014-3488",
- "severity": "Unknown",
- "solution": "Upgrade to the latest version",
- "scanner": {
- "id": "gemnasium",
- "name": "Gemnasium"
- },
- "location": {
- "file": "app/pom.xml",
- "dependency": {
- "package": {
- "name": "io.netty/netty"
+{
+ "version": "1.3",
+ "vulnerabilities": [
+ {
+ "category": "dependency_scanning",
+ "name": "io.netty/netty - CVE-2014-3488",
+ "message": "DoS by CPU exhaustion when using malicious SSL packets",
+ "cve": "app/pom.xml:io.netty/netty@3.9.1.Final:CVE-2014-3488",
+ "severity": "Unknown",
+ "solution": "Upgrade to the latest version",
+ "scanner": {
+ "id": "gemnasium",
+ "name": "Gemnasium"
+ },
+ "location": {
+ "file": "app/pom.xml",
+ "dependency": {
+ "package": {
+ "name": "io.netty/netty"
+ },
+ "version": "3.9.1.Final"
+ }
+ },
+ "identifiers": [
+ {
+ "type": "gemnasium",
+ "name": "Gemnasium-d1bf36d9-9f07-46cd-9cfc-8675338ada8f",
+ "value": "d1bf36d9-9f07-46cd-9cfc-8675338ada8f",
+ "url": "https://deps.sec.gitlab.com/packages/maven/io.netty/netty/versions/3.9.1.Final/advisories"
+ },
+ {
+ "type": "cve",
+ "name": "CVE-2014-3488",
+ "value": "CVE-2014-3488",
+ "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-3488"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://bugzilla.redhat.com/CVE-2014-3488"
},
- "version": "3.9.1.Final"
- }
+ {
+ "url": "http://netty.io/news/2014/06/11/3.html"
+ },
+ {
+ "url": "https://github.com/netty/netty/issues/2562"
+ }
+ ],
+ "priority": "Unknown",
+ "file": "app/pom.xml",
+ "url": "https://bugzilla.redhat.com/CVE-2014-3488",
+ "tool": "gemnasium"
},
- "identifiers": [
- {
- "type": "gemnasium",
- "name": "Gemnasium-d1bf36d9-9f07-46cd-9cfc-8675338ada8f",
- "value": "d1bf36d9-9f07-46cd-9cfc-8675338ada8f",
- "url": "https://deps.sec.gitlab.com/packages/maven/io.netty/netty/versions/3.9.1.Final/advisories"
- },
- {
- "type": "cve",
- "name": "CVE-2014-3488",
- "value": "CVE-2014-3488",
- "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-3488"
- }
- ],
- "links": [
- {
- "url": "https://bugzilla.redhat.com/CVE-2014-3488"
+ {
+ "category": "dependency_scanning",
+ "name": "Django - CVE-2017-12794",
+ "message": "Possible XSS in traceback section of technical 500 debug page",
+ "cve": "app/requirements.txt:Django@1.11.3:CVE-2017-12794",
+ "severity": "Unknown",
+ "solution": "Upgrade to latest version or apply patch.",
+ "scanner": {
+ "id": "gemnasium",
+ "name": "Gemnasium"
},
- {
- "url": "http://netty.io/news/2014/06/11/3.html"
+ "location": {
+ "file": "app/requirements.txt",
+ "dependency": {
+ "package": {
+ "name": "Django"
+ },
+ "version": "1.11.3"
+ }
},
- {
- "url": "https://github.com/netty/netty/issues/2562"
- }
- ],
- "priority": "Unknown",
- "file": "app/pom.xml",
- "url": "https://bugzilla.redhat.com/CVE-2014-3488",
- "tool": "gemnasium"
- },
- {
- "category": "dependency_scanning",
- "name": "Django - CVE-2017-12794",
- "message": "Possible XSS in traceback section of technical 500 debug page",
- "cve": "app/requirements.txt:Django@1.11.3:CVE-2017-12794",
- "severity": "Unknown",
- "solution": "Upgrade to latest version or apply patch.",
- "scanner": {
- "id": "gemnasium",
- "name": "Gemnasium"
- },
- "location": {
- "file": "app/requirements.txt",
- "dependency": {
- "package": {
- "name": "Django"
+ "identifiers": [
+ {
+ "type": "gemnasium",
+ "name": "Gemnasium-6162a015-8635-4a15-8d7c-dc9321db366f",
+ "value": "6162a015-8635-4a15-8d7c-dc9321db366f",
+ "url": "https://deps.sec.gitlab.com/packages/pypi/Django/versions/1.11.3/advisories"
},
- "version": "1.11.3"
- }
+ {
+ "type": "cve",
+ "name": "CVE-2017-12794",
+ "value": "CVE-2017-12794",
+ "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-12794"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://www.djangoproject.com/weblog/2017/sep/05/security-releases/"
+ }
+ ],
+ "priority": "Unknown",
+ "file": "app/requirements.txt",
+ "url": "https://www.djangoproject.com/weblog/2017/sep/05/security-releases/",
+ "tool": "gemnasium"
},
- "identifiers": [
- {
- "type": "gemnasium",
- "name": "Gemnasium-6162a015-8635-4a15-8d7c-dc9321db366f",
- "value": "6162a015-8635-4a15-8d7c-dc9321db366f",
- "url": "https://deps.sec.gitlab.com/packages/pypi/Django/versions/1.11.3/advisories"
+ {
+ "category": "dependency_scanning",
+ "name": "nokogiri - USN-3424-1",
+ "message": "Vulnerabilities in libxml2",
+ "cve": "rails/Gemfile.lock:nokogiri@1.8.0:USN-3424-1",
+ "severity": "Unknown",
+ "solution": "Upgrade to latest version.",
+ "scanner": {
+ "id": "gemnasium",
+ "name": "Gemnasium"
},
- {
- "type": "cve",
- "name": "CVE-2017-12794",
- "value": "CVE-2017-12794",
- "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-12794"
- }
- ],
- "links": [
- {
- "url": "https://www.djangoproject.com/weblog/2017/sep/05/security-releases/"
- }
- ],
- "priority": "Unknown",
- "file": "app/requirements.txt",
- "url": "https://www.djangoproject.com/weblog/2017/sep/05/security-releases/",
- "tool": "gemnasium"
- },
- {
- "category": "dependency_scanning",
- "name": "nokogiri - USN-3424-1",
- "message": "Vulnerabilities in libxml2",
- "cve": "rails/Gemfile.lock:nokogiri@1.8.0:USN-3424-1",
- "severity": "Unknown",
- "solution": "Upgrade to latest version.",
- "scanner": {
- "id": "gemnasium",
- "name": "Gemnasium"
- },
- "location": {
- "file": "rails/Gemfile.lock",
- "dependency": {
- "package": {
- "name": "nokogiri"
+ "location": {
+ "file": "rails/Gemfile.lock",
+ "dependency": {
+ "package": {
+ "name": "nokogiri"
+ },
+ "version": "1.8.0"
+ }
+ },
+ "identifiers": [
+ {
+ "type": "gemnasium",
+ "name": "Gemnasium-06565b64-486d-4326-b906-890d9915804d",
+ "value": "06565b64-486d-4326-b906-890d9915804d",
+ "url": "https://deps.sec.gitlab.com/packages/gem/nokogiri/versions/1.8.0/advisories"
},
- "version": "1.8.0"
- }
+ {
+ "type": "usn",
+ "name": "USN-3424-1",
+ "value": "USN-3424-1",
+ "url": "https://usn.ubuntu.com/3424-1/"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://github.com/sparklemotion/nokogiri/issues/1673"
+ }
+ ],
+ "priority": "Unknown",
+ "file": "rails/Gemfile.lock",
+ "url": "https://github.com/sparklemotion/nokogiri/issues/1673",
+ "tool": "gemnasium"
},
- "identifiers": [
- {
- "type": "gemnasium",
- "name": "Gemnasium-06565b64-486d-4326-b906-890d9915804d",
- "value": "06565b64-486d-4326-b906-890d9915804d",
- "url": "https://deps.sec.gitlab.com/packages/gem/nokogiri/versions/1.8.0/advisories"
+ {
+ "category": "dependency_scanning",
+ "name": "ffi - CVE-2018-1000201",
+ "message": "ruby-ffi DDL loading issue on Windows OS",
+ "cve": "ffi:1.9.18:CVE-2018-1000201",
+ "severity": "High",
+ "solution": "upgrade to \u003e= 1.9.24",
+ "scanner": {
+ "id": "bundler_audit",
+ "name": "bundler-audit"
},
- {
- "type": "usn",
- "name": "USN-3424-1",
- "value": "USN-3424-1",
- "url": "https://usn.ubuntu.com/3424-1/"
- }
- ],
- "links": [
- {
- "url": "https://github.com/sparklemotion/nokogiri/issues/1673"
- }
- ],
- "priority": "Unknown",
- "file": "rails/Gemfile.lock",
- "url": "https://github.com/sparklemotion/nokogiri/issues/1673",
- "tool": "gemnasium"
- },
- {
- "category": "dependency_scanning",
- "name": "ffi - CVE-2018-1000201",
- "message": "ruby-ffi DDL loading issue on Windows OS",
- "cve": "ffi:1.9.18:CVE-2018-1000201",
- "severity": "High",
- "solution": "upgrade to \u003e= 1.9.24",
- "scanner": {
- "id": "bundler_audit",
- "name": "bundler-audit"
- },
- "location": {
+ "location": {
+ "file": "sast-sample-rails/Gemfile.lock",
+ "dependency": {
+ "package": {
+ "name": "ffi"
+ },
+ "version": "1.9.18"
+ }
+ },
+ "identifiers": [
+ {
+ "type": "cve",
+ "name": "CVE-2018-1000201",
+ "value": "CVE-2018-1000201",
+ "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-1000201"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://github.com/ffi/ffi/releases/tag/1.9.24"
+ }
+ ],
+ "priority": "High",
"file": "sast-sample-rails/Gemfile.lock",
- "dependency": {
- "package": {
- "name": "ffi"
- },
- "version": "1.9.18"
- }
- },
- "identifiers": [
- {
- "type": "cve",
- "name": "CVE-2018-1000201",
- "value": "CVE-2018-1000201",
- "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-1000201"
- }
- ],
- "links": [
- {
- "url": "https://github.com/ffi/ffi/releases/tag/1.9.24"
- }
- ],
- "priority": "High",
- "file": "sast-sample-rails/Gemfile.lock",
- "url": "https://github.com/ffi/ffi/releases/tag/1.9.24",
- "tool": "bundler_audit"
- }
-]
+ "url": "https://github.com/ffi/ffi/releases/tag/1.9.24",
+ "tool": "bundler_audit"
+ }
+ ]
+}
diff --git a/spec/fixtures/security-reports/master/gl-license-management-report.json b/spec/fixtures/security-reports/master/gl-license-management-report.json
index fe91e4fb7ee..e0de6f58fdf 100644
--- a/spec/fixtures/security-reports/master/gl-license-management-report.json
+++ b/spec/fixtures/security-reports/master/gl-license-management-report.json
@@ -1,8 +1,20 @@
{
"licenses": [
{
- "count": 10,
+ "count": 52,
"name": "MIT"
+ },
+ {
+ "count": 3,
+ "name": "New BSD"
+ },
+ {
+ "count": 1,
+ "name": "Apache 2.0"
+ },
+ {
+ "count": 1,
+ "name": "unknown"
}
],
"dependencies": [
@@ -12,6 +24,369 @@
"url": "http://opensource.org/licenses/mit-license"
},
"dependency": {
+ "name": "actioncable",
+ "url": "http://rubyonrails.org",
+ "description": "WebSocket framework for Rails.",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "actionmailer",
+ "url": "http://rubyonrails.org",
+ "description": "Email composition, delivery, and receiving framework (part of Rails).",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "actionpack",
+ "url": "http://rubyonrails.org",
+ "description": "Web-flow and rendering framework putting the VC in MVC (part of Rails).",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "actionview",
+ "url": "http://rubyonrails.org",
+ "description": "Rendering framework putting the V in MVC (part of Rails).",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "activejob",
+ "url": "http://rubyonrails.org",
+ "description": "Job framework with pluggable queues.",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "activemodel",
+ "url": "http://rubyonrails.org",
+ "description": "A toolkit for building modeling frameworks (part of Rails).",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "activerecord",
+ "url": "http://rubyonrails.org",
+ "description": "Object-relational mapper framework (part of Rails).",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "activesupport",
+ "url": "http://rubyonrails.org",
+ "description": "A toolkit of support libraries and Ruby core extensions extracted from the Rails framework.",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "arel",
+ "url": "https://github.com/rails/arel",
+ "description": "Arel Really Exasperates Logicians Arel is a SQL AST manager for Ruby",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "builder",
+ "url": "http://onestepback.org",
+ "description": "Builders for MarkUp.",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "bundler",
+ "url": "http://bundler.io",
+ "description": "The best way to manage your application's dependencies",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "coffee-rails",
+ "url": "https://github.com/rails/coffee-rails",
+ "description": "CoffeeScript adapter for the Rails asset pipeline.",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "coffee-script",
+ "url": "http://github.com/josh/ruby-coffee-script",
+ "description": "Ruby CoffeeScript Compiler",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "coffee-script-source",
+ "url": "http://coffeescript.org",
+ "description": "The CoffeeScript Compiler",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "concurrent-ruby",
+ "url": "http://www.concurrent-ruby.com",
+ "description": "Modern concurrency tools for Ruby. Inspired by Erlang, Clojure, Scala, Haskell, F#, C#, Java, and classic concurrency patterns.",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "crass",
+ "url": "https://github.com/rgrove/crass/",
+ "description": "CSS parser based on the CSS Syntax Level 3 spec.",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "erubis",
+ "url": "http://www.kuwata-lab.com/erubis/",
+ "description": "a fast and extensible eRuby implementation which supports multi-language",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "execjs",
+ "url": "https://github.com/rails/execjs",
+ "description": "Run JavaScript code from Ruby",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "New BSD",
+ "url": "http://opensource.org/licenses/BSD-3-Clause"
+ },
+ "dependency": {
+ "name": "ffi",
+ "url": "http://wiki.github.com/ffi/ffi",
+ "description": "Ruby FFI",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "globalid",
+ "url": "http://www.rubyonrails.org",
+ "description": "Refer to any model with a URI: gid://app/class/id",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "i18n",
+ "url": "http://github.com/svenfuchs/i18n",
+ "description": "New wave Internationalization support for Ruby",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "jbuilder",
+ "url": "https://github.com/rails/jbuilder",
+ "description": "Create JSON structures via a Builder-style DSL",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "loofah",
+ "description": "",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "mail",
+ "url": "https://github.com/mikel/mail",
+ "description": "Mail provides a nice Ruby DSL for making, sending and reading emails.",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "method_source",
+ "url": "http://banisterfiend.wordpress.com",
+ "description": "retrieve the sourcecode for a method",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "mini_mime",
+ "url": "https://github.com/discourse/mini_mime",
+ "description": "A lightweight mime type lookup toy",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
"name": "mini_portile2",
"url": "http://github.com/flavorjones/mini_portile",
"description": "Simplistic port-like solution for developers",
@@ -26,9 +401,37 @@
"url": "http://opensource.org/licenses/mit-license"
},
"dependency": {
- "name": "mustermann",
- "url": "https://github.com/sinatra/mustermann",
- "description": "Your personal string matching expert.",
+ "name": "minitest",
+ "url": "https://github.com/seattlerb/minitest",
+ "description": "minitest provides a complete suite of testing facilities supporting TDD, BDD, mocking, and benchmarking",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "multi_json",
+ "url": "http://github.com/intridea/multi_json",
+ "description": "A common interface to multiple JSON libraries.",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "nio4r",
+ "url": "https://github.com/celluloid/nio4r",
+ "description": "NIO provides a high performance selector API for monitoring IO objects",
"pathes": [
"."
]
@@ -50,6 +453,20 @@
},
{
"license": {
+ "name": "New BSD",
+ "url": "http://opensource.org/licenses/BSD-3-Clause"
+ },
+ "dependency": {
+ "name": "puma",
+ "url": "http://puma.io",
+ "description": "Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server for Ruby/Rack applications",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
"name": "MIT",
"url": "http://opensource.org/licenses/mit-license"
},
@@ -68,9 +485,147 @@
"url": "http://opensource.org/licenses/mit-license"
},
"dependency": {
- "name": "rack-protection",
- "url": "http://github.com/sinatra/sinatra/tree/master/rack-protection",
- "description": "Protect against typical web attacks, works with all Rack apps, including Rails.",
+ "name": "rack-test",
+ "url": "http://github.com/brynary/rack-test",
+ "description": "Simple testing API built on Rack",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "rails",
+ "url": "http://rubyonrails.org",
+ "description": "Full-stack web application framework.",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "rails-dom-testing",
+ "url": "https://github.com/rails/rails-dom-testing",
+ "description": "Dom and Selector assertions for Rails applications",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "rails-html-sanitizer",
+ "url": "https://github.com/rails/rails-html-sanitizer",
+ "description": "This gem is responsible to sanitize HTML fragments in Rails applications.",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "railties",
+ "url": "http://rubyonrails.org",
+ "description": "Tools for creating, working with, and running Rails applications.",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "rake",
+ "url": "https://github.com/ruby/rake",
+ "description": "Rake is a Make-like program implemented in Ruby",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "rb-fsevent",
+ "url": "http://rubygems.org/gems/rb-fsevent",
+ "description": "Very simple & usable FSEvents API",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "rb-inotify",
+ "url": "https://github.com/guard/rb-inotify",
+ "description": "A Ruby wrapper for Linux inotify, using FFI",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "unknown"
+ },
+ "dependency": {
+ "name": "ruby-bundler-rails",
+ "description": "",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "sass",
+ "url": "http://sass-lang.com/",
+ "description": "A powerful but elegant CSS compiler that makes CSS fun again.",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "sass-listen",
+ "url": "https://github.com/sass/listen",
+ "description": "Fork of guard/listen",
"pathes": [
"."
]
@@ -82,9 +637,9 @@
"url": "http://opensource.org/licenses/mit-license"
},
"dependency": {
- "name": "redis",
- "url": "https://github.com/redis/redis-rb",
- "description": "A Ruby client library for Redis",
+ "name": "sass-rails",
+ "url": "https://github.com/rails/sass-rails",
+ "description": "Sass adapter for the Rails asset pipeline.",
"pathes": [
"."
]
@@ -96,9 +651,9 @@
"url": "http://opensource.org/licenses/mit-license"
},
"dependency": {
- "name": "sinatra",
- "url": "http://www.sinatrarb.com/",
- "description": "Classy web-development dressed in a DSL",
+ "name": "sprockets",
+ "url": "https://github.com/rails/sprockets",
+ "description": "Rack-based asset packaging system",
"pathes": [
"."
]
@@ -110,9 +665,23 @@
"url": "http://opensource.org/licenses/mit-license"
},
"dependency": {
- "name": "slim",
- "url": "http://slim-lang.com/",
- "description": "Slim is a template language.",
+ "name": "sprockets-rails",
+ "url": "https://github.com/rails/sprockets-rails",
+ "description": "Sprockets Rails integration",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "New BSD",
+ "url": "http://opensource.org/licenses/BSD-3-Clause"
+ },
+ "dependency": {
+ "name": "sqlite3",
+ "url": "https://github.com/sparklemotion/sqlite3-ruby",
+ "description": "This module allows Ruby programs to interface with the SQLite3 database engine (http://www.sqlite.org)",
"pathes": [
"."
]
@@ -124,9 +693,23 @@
"url": "http://opensource.org/licenses/mit-license"
},
"dependency": {
- "name": "temple",
- "url": "https://github.com/judofyr/temple",
- "description": "Template compilation framework in Ruby",
+ "name": "thor",
+ "url": "http://whatisthor.com/",
+ "description": "Thor is a toolkit for building powerful command-line interfaces.",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ },
+ "dependency": {
+ "name": "thread_safe",
+ "url": "https://github.com/ruby-concurrency/thread_safe",
+ "description": "Thread-safe collections and utilities for Ruby",
"pathes": [
"."
]
@@ -145,6 +728,90 @@
"."
]
}
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "turbolinks",
+ "url": "https://github.com/turbolinks/turbolinks",
+ "description": "Turbolinks makes navigating your web application faster",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "turbolinks-source",
+ "url": "https://github.com/turbolinks/turbolinks-source-gem",
+ "description": "Turbolinks JavaScript assets",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "tzinfo",
+ "url": "http://tzinfo.github.io",
+ "description": "Daylight savings aware timezone library",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "uglifier",
+ "url": "http://github.com/lautis/uglifier",
+ "description": "Ruby wrapper for UglifyJS JavaScript compressor",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "websocket-driver",
+ "url": "http://github.com/faye/websocket-driver-ruby",
+ "description": "WebSocket protocol handler with pluggable I/O",
+ "pathes": [
+ "."
+ ]
+ }
+ },
+ {
+ "license": {
+ "name": "MIT",
+ "url": "http://opensource.org/licenses/mit-license"
+ },
+ "dependency": {
+ "name": "websocket-extensions",
+ "url": "https://github.com/faye/websocket-extensions-ruby",
+ "description": "Generic extension manager for WebSocket connections",
+ "pathes": [
+ "."
+ ]
+ }
}
]
}
diff --git a/spec/fixtures/security-reports/master/gl-sast-report.json b/spec/fixtures/security-reports/master/gl-sast-report.json
index a85b9be8b5f..4bef3d22f70 100644
--- a/spec/fixtures/security-reports/master/gl-sast-report.json
+++ b/spec/fixtures/security-reports/master/gl-sast-report.json
@@ -1,944 +1,947 @@
-[
- {
- "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": {
+{
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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",
- "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": {
+ "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": "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",
- "start_line": 4
- },
- "identifiers": [
- {
- "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": {
+ "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": "cwe",
+ "name": "CWE-362",
+ "value": "362",
+ "url": "https://cwe.mitre.org/data/definitions/362.html"
+ }
+ ],
"file": "c/subdir/utils.c",
- "start_line": 8
- },
- "identifiers": [
- {
- "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": {
+ "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": "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",
- "start_line": 6
- },
- "identifiers": [
- {
- "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": {
+ "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": "cwe",
+ "name": "CWE-120",
+ "value": "120",
+ "url": "https://cwe.mitre.org/data/definitions/120.html"
+ }
+ ],
"file": "cplusplus/src/hello.cpp",
- "start_line": 7
- },
- "identifiers": [
- {
- "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"
- }
-]
+ "line": 7,
+ "url": "https://cwe.mitre.org/data/definitions/120.html",
+ "tool": "flawfinder"
+ }
+ ]
+}
diff --git a/spec/helpers/application_settings_helper_spec.rb b/spec/helpers/application_settings_helper_spec.rb
new file mode 100644
index 00000000000..705523f1110
--- /dev/null
+++ b/spec/helpers/application_settings_helper_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ApplicationSettingsHelper do
+ context 'when all protocols in use' do
+ before do
+ stub_application_setting(enabled_git_access_protocol: '')
+ end
+
+ it { expect(all_protocols_enabled?).to be_truthy }
+ it { expect(http_enabled?).to be_truthy }
+ it { expect(ssh_enabled?).to be_truthy }
+ end
+
+ context 'when SSH is only in use' do
+ before do
+ stub_application_setting(enabled_git_access_protocol: 'ssh')
+ end
+
+ it { expect(all_protocols_enabled?).to be_falsey }
+ it { expect(http_enabled?).to be_falsey }
+ it { expect(ssh_enabled?).to be_truthy }
+ end
+
+ shared_examples 'when HTTP protocol is in use' do |protocol|
+ before do
+ allow(Gitlab.config.gitlab).to receive(:protocol).and_return(protocol)
+ stub_application_setting(enabled_git_access_protocol: 'http')
+ end
+
+ it { expect(all_protocols_enabled?).to be_falsey }
+ it { expect(http_enabled?).to be_truthy }
+ it { expect(ssh_enabled?).to be_falsey }
+ end
+
+ it_behaves_like 'when HTTP protocol is in use', 'https'
+ it_behaves_like 'when HTTP protocol is in use', 'http'
+end
diff --git a/spec/helpers/diff_helper_spec.rb b/spec/helpers/diff_helper_spec.rb
index 53c010fa0db..5396243f44d 100644
--- a/spec/helpers/diff_helper_spec.rb
+++ b/spec/helpers/diff_helper_spec.rb
@@ -256,43 +256,6 @@ describe DiffHelper do
end
end
- context 'viewer related' do
- let(:viewer) { diff_file.simple_viewer }
-
- before do
- assign(:project, project)
- end
-
- describe '#diff_render_error_reason' do
- context 'for error :too_large' do
- before do
- expect(viewer).to receive(:render_error).and_return(:too_large)
- end
-
- it 'returns an error message' do
- expect(helper.diff_render_error_reason(viewer)).to eq('it is too large')
- end
- end
-
- context 'for error :server_side_but_stored_externally' do
- before do
- expect(viewer).to receive(:render_error).and_return(:server_side_but_stored_externally)
- expect(diff_file).to receive(:external_storage).and_return(:lfs)
- end
-
- it 'returns an error message' do
- expect(helper.diff_render_error_reason(viewer)).to eq('it is stored in LFS')
- end
- end
- end
-
- describe '#diff_render_error_options' do
- it 'includes a "view the blob" link' do
- expect(helper.diff_render_error_options(viewer)).to include(/view the blob/)
- end
- end
- end
-
context '#diff_file_path_text' do
it 'returns full path by default' do
expect(diff_file_path_text(diff_file)).to eq(diff_file.new_path)
diff --git a/spec/helpers/runners_helper_spec.rb b/spec/helpers/runners_helper_spec.rb
index a4a483e68a8..bf00841fcb6 100644
--- a/spec/helpers/runners_helper_spec.rb
+++ b/spec/helpers/runners_helper_spec.rb
@@ -15,4 +15,40 @@ describe RunnersHelper do
runner = FactoryBot.build(:ci_runner, contacted_at: 1.second.ago, active: true)
expect(runner_status_icon(runner)).to include("Runner is online")
end
+
+ describe '#runner_contacted_at' do
+ let(:contacted_at_stored) { 1.hour.ago.change(usec: 0) }
+ let(:contacted_at_cached) { 1.second.ago.change(usec: 0) }
+ let(:runner) { create(:ci_runner, contacted_at: contacted_at_stored) }
+
+ before do
+ runner.cache_attributes(contacted_at: contacted_at_cached)
+ end
+
+ context 'without sorting' do
+ it 'returns cached value' do
+ expect(runner_contacted_at(runner)).to eq(contacted_at_cached)
+ end
+ end
+
+ context 'with sorting set to created_date' do
+ before do
+ controller.params[:sort] = 'created_date'
+ end
+
+ it 'returns cached value' do
+ expect(runner_contacted_at(runner)).to eq(contacted_at_cached)
+ end
+ end
+
+ context 'with sorting set to contacted_asc' do
+ before do
+ controller.params[:sort] = 'contacted_asc'
+ end
+
+ it 'returns stored value' do
+ expect(runner_contacted_at(runner)).to eq(contacted_at_stored)
+ end
+ end
+ end
end
diff --git a/spec/initializers/active_record_locking_spec.rb b/spec/initializers/active_record_locking_spec.rb
new file mode 100644
index 00000000000..5a16aef78e6
--- /dev/null
+++ b/spec/initializers/active_record_locking_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'ActiveRecord locking' do
+ let(:issue) { create(:issue) }
+
+ shared_examples 'locked model' do
+ before do
+ issue.update_column(:lock_version, start_lock_version)
+ end
+
+ it 'can be updated' do
+ issue.update(title: "New title")
+
+ expect(issue.reload.lock_version).to eq(new_lock_version)
+ end
+
+ it 'can be deleted' do
+ expect { issue.destroy }.to change { Issue.count }.by(-1)
+ end
+ end
+
+ context 'when lock_version is NULL' do
+ let(:start_lock_version) { nil }
+ let(:new_lock_version) { 1 }
+
+ it_behaves_like 'locked model'
+ end
+
+ context 'when lock_version is 0' do
+ let(:start_lock_version) { 0 }
+ let(:new_lock_version) { 1 }
+
+ it_behaves_like 'locked model'
+ end
+
+ context 'when lock_version is 1' do
+ let(:start_lock_version) { 1 }
+ let(:new_lock_version) { 2 }
+
+ it_behaves_like 'locked model'
+ end
+end
diff --git a/spec/javascripts/blob_edit/blob_bundle_spec.js b/spec/javascripts/blob_edit/blob_bundle_spec.js
index 57f60a4a3dd..48af0148e3f 100644
--- a/spec/javascripts/blob_edit/blob_bundle_spec.js
+++ b/spec/javascripts/blob_edit/blob_bundle_spec.js
@@ -1,18 +1,11 @@
import blobBundle from '~/blob_edit/blob_bundle';
import $ from 'jquery';
-window.ace = {
- config: {
- set: () => {},
- loadModule: () => {},
- },
- edit: () => ({ focus: () => {} }),
-};
-
-describe('EditBlob', () => {
+describe('BlobBundle', () => {
beforeEach(() => {
+ spyOnDependency(blobBundle, 'EditBlob').and.stub();
setFixtures(`
- <div class="js-edit-blob-form">
+ <div class="js-edit-blob-form" data-blob-filename="blah">
<button class="js-commit-button"></button>
<a class="btn btn-cancel" href="#"></a>
</div>`);
diff --git a/spec/javascripts/lib/utils/text_markdown_spec.js b/spec/javascripts/lib/utils/text_markdown_spec.js
index f71d27eb4e4..df4029555bb 100644
--- a/spec/javascripts/lib/utils/text_markdown_spec.js
+++ b/spec/javascripts/lib/utils/text_markdown_spec.js
@@ -13,215 +13,296 @@ describe('init markdown', () => {
textArea.parentNode.removeChild(textArea);
});
- describe('without selection', () => {
- it('inserts the tag on an empty line', () => {
- const initialValue = '';
+ describe('textArea', () => {
+ describe('without selection', () => {
+ it('inserts the tag on an empty line', () => {
+ const initialValue = '';
- textArea.value = initialValue;
- textArea.selectionStart = 0;
- textArea.selectionEnd = 0;
-
- insertMarkdownText({
- textArea,
- text: textArea.value,
- tag: '*',
- blockTag: null,
- selected: '',
- wrap: false,
- });
-
- expect(textArea.value).toEqual(`${initialValue}* `);
- });
-
- it('inserts the tag on a new line if the current one is not empty', () => {
- const initialValue = 'some text';
+ textArea.value = initialValue;
+ textArea.selectionStart = 0;
+ textArea.selectionEnd = 0;
- textArea.value = initialValue;
- textArea.setSelectionRange(initialValue.length, initialValue.length);
+ insertMarkdownText({
+ textArea,
+ text: textArea.value,
+ tag: '*',
+ blockTag: null,
+ selected: '',
+ wrap: false,
+ });
- insertMarkdownText({
- textArea,
- text: textArea.value,
- tag: '*',
- blockTag: null,
- selected: '',
- wrap: false,
+ expect(textArea.value).toEqual(`${initialValue}* `);
});
- expect(textArea.value).toEqual(`${initialValue}\n* `);
- });
+ it('inserts the tag on a new line if the current one is not empty', () => {
+ const initialValue = 'some text';
- it('inserts the tag on the same line if the current line only contains spaces', () => {
- const initialValue = ' ';
+ textArea.value = initialValue;
+ textArea.setSelectionRange(initialValue.length, initialValue.length);
- textArea.value = initialValue;
- textArea.setSelectionRange(initialValue.length, initialValue.length);
+ insertMarkdownText({
+ textArea,
+ text: textArea.value,
+ tag: '*',
+ blockTag: null,
+ selected: '',
+ wrap: false,
+ });
- insertMarkdownText({
- textArea,
- text: textArea.value,
- tag: '*',
- blockTag: null,
- selected: '',
- wrap: false,
+ expect(textArea.value).toEqual(`${initialValue}\n* `);
});
- expect(textArea.value).toEqual(`${initialValue}* `);
- });
+ it('inserts the tag on the same line if the current line only contains spaces', () => {
+ const initialValue = ' ';
- it('inserts the tag on the same line if the current line only contains tabs', () => {
- const initialValue = '\t\t\t';
+ textArea.value = initialValue;
+ textArea.setSelectionRange(initialValue.length, initialValue.length);
- textArea.value = initialValue;
- textArea.setSelectionRange(initialValue.length, initialValue.length);
+ insertMarkdownText({
+ textArea,
+ text: textArea.value,
+ tag: '*',
+ blockTag: null,
+ selected: '',
+ wrap: false,
+ });
- insertMarkdownText({
- textArea,
- text: textArea.value,
- tag: '*',
- blockTag: null,
- selected: '',
- wrap: false,
+ expect(textArea.value).toEqual(`${initialValue}* `);
});
- expect(textArea.value).toEqual(`${initialValue}* `);
- });
+ it('inserts the tag on the same line if the current line only contains tabs', () => {
+ const initialValue = '\t\t\t';
- it('places the cursor inside the tags', () => {
- const start = 'lorem ';
- const end = ' ipsum';
- const tag = '*';
+ textArea.value = initialValue;
+ textArea.setSelectionRange(initialValue.length, initialValue.length);
- textArea.value = `${start}${end}`;
- textArea.setSelectionRange(start.length, start.length);
+ insertMarkdownText({
+ textArea,
+ text: textArea.value,
+ tag: '*',
+ blockTag: null,
+ selected: '',
+ wrap: false,
+ });
- insertMarkdownText({
- textArea,
- text: textArea.value,
- tag,
- blockTag: null,
- selected: '',
- wrap: true,
+ expect(textArea.value).toEqual(`${initialValue}* `);
});
- expect(textArea.value).toEqual(`${start}**${end}`);
+ it('places the cursor inside the tags', () => {
+ const start = 'lorem ';
+ const end = ' ipsum';
+ const tag = '*';
- // cursor placement should be between tags
- expect(textArea.selectionStart).toBe(start.length + tag.length);
- });
- });
+ textArea.value = `${start}${end}`;
+ textArea.setSelectionRange(start.length, start.length);
- describe('with selection', () => {
- const text = 'initial selected value';
- const selected = 'selected';
- beforeEach(() => {
- textArea.value = text;
- const selectedIndex = text.indexOf(selected);
- textArea.setSelectionRange(selectedIndex, selectedIndex + selected.length);
- });
+ insertMarkdownText({
+ textArea,
+ text: textArea.value,
+ tag,
+ blockTag: null,
+ selected: '',
+ wrap: true,
+ });
- it('applies the tag to the selected value', () => {
- const selectedIndex = text.indexOf(selected);
- const tag = '*';
+ expect(textArea.value).toEqual(`${start}**${end}`);
- insertMarkdownText({
- textArea,
- text: textArea.value,
- tag,
- blockTag: null,
- selected,
- wrap: true,
+ // cursor placement should be between tags
+ expect(textArea.selectionStart).toBe(start.length + tag.length);
});
-
- expect(textArea.value).toEqual(text.replace(selected, `*${selected}*`));
-
- // cursor placement should be after selection + 2 tag lengths
- expect(textArea.selectionStart).toBe(selectedIndex + selected.length + 2 * tag.length);
});
- it('replaces the placeholder in the tag', () => {
- insertMarkdownText({
- textArea,
- text: textArea.value,
- tag: '[{text}](url)',
- blockTag: null,
- selected,
- wrap: false,
+ describe('with selection', () => {
+ const text = 'initial selected value';
+ const selected = 'selected';
+ beforeEach(() => {
+ textArea.value = text;
+ const selectedIndex = text.indexOf(selected);
+ textArea.setSelectionRange(selectedIndex, selectedIndex + selected.length);
});
- expect(textArea.value).toEqual(text.replace(selected, `[${selected}](url)`));
- });
+ it('applies the tag to the selected value', () => {
+ const selectedIndex = text.indexOf(selected);
+ const tag = '*';
- describe('and text to be selected', () => {
- const tag = '[{text}](url)';
- const select = 'url';
-
- it('selects the text', () => {
insertMarkdownText({
textArea,
text: textArea.value,
tag,
blockTag: null,
selected,
- wrap: false,
- select,
+ wrap: true,
});
- const expectedText = text.replace(selected, `[${selected}](url)`);
+ expect(textArea.value).toEqual(text.replace(selected, `*${selected}*`));
- expect(textArea.value).toEqual(expectedText);
- expect(textArea.selectionStart).toEqual(expectedText.indexOf(select));
- expect(textArea.selectionEnd).toEqual(expectedText.indexOf(select) + select.length);
+ // cursor placement should be after selection + 2 tag lengths
+ expect(textArea.selectionStart).toBe(selectedIndex + selected.length + 2 * tag.length);
});
- it('selects the right text when multiple tags are present', () => {
- const initialValue = `${tag} ${tag} ${selected}`;
- textArea.value = initialValue;
- const selectedIndex = initialValue.indexOf(selected);
- textArea.setSelectionRange(selectedIndex, selectedIndex + selected.length);
+ it('replaces the placeholder in the tag', () => {
insertMarkdownText({
textArea,
text: textArea.value,
- tag,
+ tag: '[{text}](url)',
blockTag: null,
selected,
wrap: false,
- select,
});
- const expectedText = initialValue.replace(selected, `[${selected}](url)`);
-
- expect(textArea.value).toEqual(expectedText);
- expect(textArea.selectionStart).toEqual(expectedText.lastIndexOf(select));
- expect(textArea.selectionEnd).toEqual(expectedText.lastIndexOf(select) + select.length);
+ expect(textArea.value).toEqual(text.replace(selected, `[${selected}](url)`));
});
- it('should support selected urls', () => {
- const expectedUrl = 'http://www.gitlab.com';
- const expectedSelectionText = 'text';
- const expectedText = `text [${expectedSelectionText}](${expectedUrl}) text`;
- const initialValue = `text ${expectedUrl} text`;
+ describe('and text to be selected', () => {
+ const tag = '[{text}](url)';
+ const select = 'url';
+
+ it('selects the text', () => {
+ insertMarkdownText({
+ textArea,
+ text: textArea.value,
+ tag,
+ blockTag: null,
+ selected,
+ wrap: false,
+ select,
+ });
+
+ const expectedText = text.replace(selected, `[${selected}](url)`);
+
+ expect(textArea.value).toEqual(expectedText);
+ expect(textArea.selectionStart).toEqual(expectedText.indexOf(select));
+ expect(textArea.selectionEnd).toEqual(expectedText.indexOf(select) + select.length);
+ });
- textArea.value = initialValue;
- const selectedIndex = initialValue.indexOf(expectedUrl);
- textArea.setSelectionRange(selectedIndex, selectedIndex + expectedUrl.length);
+ it('selects the right text when multiple tags are present', () => {
+ const initialValue = `${tag} ${tag} ${selected}`;
+ textArea.value = initialValue;
+ const selectedIndex = initialValue.indexOf(selected);
+ textArea.setSelectionRange(selectedIndex, selectedIndex + selected.length);
+ insertMarkdownText({
+ textArea,
+ text: textArea.value,
+ tag,
+ blockTag: null,
+ selected,
+ wrap: false,
+ select,
+ });
+
+ const expectedText = initialValue.replace(selected, `[${selected}](url)`);
+
+ expect(textArea.value).toEqual(expectedText);
+ expect(textArea.selectionStart).toEqual(expectedText.lastIndexOf(select));
+ expect(textArea.selectionEnd).toEqual(expectedText.lastIndexOf(select) + select.length);
+ });
- insertMarkdownText({
- textArea,
- text: textArea.value,
- tag,
- blockTag: null,
- selected: expectedUrl,
- wrap: false,
- select,
+ it('should support selected urls', () => {
+ const expectedUrl = 'http://www.gitlab.com';
+ const expectedSelectionText = 'text';
+ const expectedText = `text [${expectedSelectionText}](${expectedUrl}) text`;
+ const initialValue = `text ${expectedUrl} text`;
+
+ textArea.value = initialValue;
+ const selectedIndex = initialValue.indexOf(expectedUrl);
+ textArea.setSelectionRange(selectedIndex, selectedIndex + expectedUrl.length);
+
+ insertMarkdownText({
+ textArea,
+ text: textArea.value,
+ tag,
+ blockTag: null,
+ selected: expectedUrl,
+ wrap: false,
+ select,
+ });
+
+ expect(textArea.value).toEqual(expectedText);
+ expect(textArea.selectionStart).toEqual(expectedText.indexOf(expectedSelectionText, 1));
+ expect(textArea.selectionEnd).toEqual(
+ expectedText.indexOf(expectedSelectionText, 1) + expectedSelectionText.length,
+ );
});
+ });
+ });
+ });
+
+ describe('Ace Editor', () => {
+ let editor;
+
+ beforeEach(() => {
+ editor = {
+ getSelectionRange: () => ({
+ start: 0,
+ end: 0,
+ }),
+ getValue: () => 'this is text \n in two lines',
+ insert: () => {},
+ navigateLeft: () => {},
+ };
+ });
+
+ it('uses ace editor insert text when editor is passed in', () => {
+ spyOn(editor, 'insert');
- expect(textArea.value).toEqual(expectedText);
- expect(textArea.selectionStart).toEqual(expectedText.indexOf(expectedSelectionText, 1));
- expect(textArea.selectionEnd).toEqual(
- expectedText.indexOf(expectedSelectionText, 1) + expectedSelectionText.length,
- );
+ insertMarkdownText({
+ text: editor.getValue,
+ tag: '*',
+ blockTag: null,
+ selected: '',
+ wrap: false,
+ editor,
+ });
+
+ expect(editor.insert).toHaveBeenCalled();
+ });
+
+ it('adds block tags on line above and below selection', () => {
+ spyOn(editor, 'insert');
+
+ const selected = 'this text \n is multiple \n lines';
+ const text = `before \n ${selected} \n after`;
+
+ insertMarkdownText({
+ text,
+ tag: '',
+ blockTag: '***',
+ selected,
+ wrap: true,
+ editor,
+ });
+
+ expect(editor.insert).toHaveBeenCalledWith(`***\n${selected}\n***`);
+ });
+
+ it('uses ace editor to navigate back tag length when nothing is selected', () => {
+ spyOn(editor, 'navigateLeft');
+
+ insertMarkdownText({
+ text: editor.getValue,
+ tag: '*',
+ blockTag: null,
+ selected: '',
+ wrap: true,
+ editor,
});
+
+ expect(editor.navigateLeft).toHaveBeenCalledWith(1);
+ });
+
+ it('ace editor does not navigate back when there is selected text', () => {
+ spyOn(editor, 'navigateLeft');
+
+ insertMarkdownText({
+ text: editor.getValue,
+ tag: '*',
+ blockTag: null,
+ selected: 'foobar',
+ wrap: true,
+ editor,
+ });
+
+ expect(editor.navigateLeft).not.toHaveBeenCalled();
});
});
});
diff --git a/spec/javascripts/notes/components/discussion_filter_spec.js b/spec/javascripts/notes/components/discussion_filter_spec.js
index 9070d968cfd..5efcab436e4 100644
--- a/spec/javascripts/notes/components/discussion_filter_spec.js
+++ b/spec/javascripts/notes/components/discussion_filter_spec.js
@@ -7,8 +7,9 @@ import { discussionFiltersMock, discussionMock } from '../mock_data';
describe('DiscussionFilter component', () => {
let vm;
let store;
+ let eventHub;
- beforeEach(() => {
+ const mountComponent = () => {
store = createStore();
const discussions = [
@@ -22,7 +23,7 @@ describe('DiscussionFilter component', () => {
const selectedValue = discussionFiltersMock[0].value;
store.state.discussions = discussions;
- vm = mountComponentWithStore(Component, {
+ return mountComponentWithStore(Component, {
el: null,
store,
props: {
@@ -30,6 +31,11 @@ describe('DiscussionFilter component', () => {
selectedValue,
},
});
+ };
+
+ beforeEach(() => {
+ window.mrTabs = undefined;
+ vm = mountComponent();
});
afterEach(() => {
@@ -83,4 +89,30 @@ describe('DiscussionFilter component', () => {
expect(defaultFilter.lastChild.classList).toContain('dropdown-divider');
});
+
+ describe('Merge request tabs', () => {
+ eventHub = new Vue();
+
+ beforeEach(() => {
+ window.mrTabs = {
+ eventHub,
+ currentTab: 'show',
+ };
+
+ vm = mountComponent();
+ });
+
+ afterEach(() => {
+ window.mrTabs = undefined;
+ });
+
+ it('only renders when discussion tab is active', done => {
+ eventHub.$emit('MergeRequestTabChange', 'commit');
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector).toBeUndefined();
+ done();
+ });
+ });
+ });
});
diff --git a/spec/javascripts/user_popovers_spec.js b/spec/javascripts/user_popovers_spec.js
index 6cf8dd81b36..b174a51c1a0 100644
--- a/spec/javascripts/user_popovers_spec.js
+++ b/spec/javascripts/user_popovers_spec.js
@@ -2,6 +2,9 @@ import initUserPopovers from '~/user_popovers';
import UsersCache from '~/lib/utils/users_cache';
describe('User Popovers', () => {
+ const fixtureTemplate = 'merge_requests/diff_comment.html.raw';
+ preloadFixtures(fixtureTemplate);
+
const selector = '.js-user-link';
const dummyUser = { name: 'root' };
@@ -15,11 +18,7 @@ describe('User Popovers', () => {
};
beforeEach(() => {
- setFixtures(`
- <a href="/root" data-user-id="1" class="js-user-link" data-username="root" data-original-title="" title="">
- Root
- </a>
- `);
+ loadFixtures(fixtureTemplate);
const usersCacheSpy = () => Promise.resolve(dummyUser);
spyOn(UsersCache, 'retrieveById').and.callFake(userId => usersCacheSpy(userId));
@@ -31,7 +30,9 @@ describe('User Popovers', () => {
});
it('Should Show+Hide Popover on mouseenter and mouseleave', done => {
- triggerEvent('mouseenter', document.querySelector(selector));
+ const targetLink = document.querySelector(selector);
+ const { userId } = targetLink.dataset;
+ triggerEvent('mouseenter', targetLink);
setTimeout(() => {
const shownPopover = document.querySelector('.popover');
@@ -39,9 +40,9 @@ describe('User Popovers', () => {
expect(shownPopover).not.toBeNull();
expect(shownPopover.innerHTML).toContain(dummyUser.name);
- expect(UsersCache.retrieveById).toHaveBeenCalledWith('1');
+ expect(UsersCache.retrieveById).toHaveBeenCalledWith(userId.toString());
- triggerEvent('mouseleave', document.querySelector(selector));
+ triggerEvent('mouseleave', targetLink);
setTimeout(() => {
// After Mouse leave it should be hidden now
@@ -52,13 +53,15 @@ describe('User Popovers', () => {
});
it('Should Not show a popover on short mouse over', done => {
- triggerEvent('mouseenter', document.querySelector(selector));
+ const targetLink = document.querySelector(selector);
+ const { userId } = targetLink.dataset;
+ triggerEvent('mouseenter', targetLink);
setTimeout(() => {
expect(document.querySelector('.popover')).toBeNull();
- expect(UsersCache.retrieveById).not.toHaveBeenCalledWith('1');
+ expect(UsersCache.retrieveById).not.toHaveBeenCalledWith(userId.toString());
- triggerEvent('mouseleave', document.querySelector(selector));
+ triggerEvent('mouseleave', targetLink);
done();
});
diff --git a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
index f72bf627c10..99b80df766a 100644
--- a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
+++ b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
@@ -18,6 +18,8 @@ describe('mrWidgetOptions', () => {
let vm;
let MrWidgetOptions;
+ const COLLABORATION_MESSAGE = 'Allows commits from members who can merge to the target branch';
+
beforeEach(() => {
// Prevent component mounting
delete mrWidgetOptions.el;
@@ -132,6 +134,53 @@ describe('mrWidgetOptions', () => {
expect(vm.shouldRenderSourceBranchRemovalStatus).toEqual(false);
});
});
+
+ describe('shouldRenderCollaborationStatus', () => {
+ describe('when collaboration is allowed', () => {
+ beforeEach(() => {
+ vm.mr.allowCollaboration = true;
+ });
+
+ describe('when merge request is opened', () => {
+ beforeEach(done => {
+ vm.mr.isOpen = true;
+ vm.$nextTick(done);
+ });
+
+ it('should render collaboration status', () => {
+ expect(vm.$el.textContent).toContain(COLLABORATION_MESSAGE);
+ });
+ });
+
+ describe('when merge request is not opened', () => {
+ beforeEach(done => {
+ vm.mr.isOpen = false;
+ vm.$nextTick(done);
+ });
+
+ it('should not render collaboration status', () => {
+ expect(vm.$el.textContent).not.toContain(COLLABORATION_MESSAGE);
+ });
+ });
+ });
+
+ describe('when collaboration is not allowed', () => {
+ beforeEach(() => {
+ vm.mr.allowCollaboration = false;
+ });
+
+ describe('when merge request is opened', () => {
+ beforeEach(done => {
+ vm.mr.isOpen = true;
+ vm.$nextTick(done);
+ });
+
+ it('should not render collaboration status', () => {
+ expect(vm.$el.textContent).not.toContain(COLLABORATION_MESSAGE);
+ });
+ });
+ });
+ });
});
describe('methods', () => {
diff --git a/spec/javascripts/vue_shared/components/callout_spec.js b/spec/javascripts/vue_shared/components/callout_spec.js
index e62bd86f4ca..91208dfb31a 100644
--- a/spec/javascripts/vue_shared/components/callout_spec.js
+++ b/spec/javascripts/vue_shared/components/callout_spec.js
@@ -1,45 +1,66 @@
-import Vue from 'vue';
-import callout from '~/vue_shared/components/callout.vue';
-import createComponent from 'spec/helpers/vue_mount_component_helper';
+import { createLocalVue, shallowMount } from '@vue/test-utils';
+import Callout from '~/vue_shared/components/callout.vue';
+
+const TEST_MESSAGE = 'This is a callout message!';
+const TEST_SLOT = '<button>This is a callout slot!</button>';
+
+const localVue = createLocalVue();
describe('Callout Component', () => {
- let CalloutComponent;
- let vm;
- const exampleMessage = 'This is a callout message!';
+ let wrapper;
- beforeEach(() => {
- CalloutComponent = Vue.extend(callout);
- });
+ const factory = options => {
+ wrapper = shallowMount(localVue.extend(Callout), {
+ localVue,
+ ...options,
+ });
+ };
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
});
it('should render the appropriate variant of callout', () => {
- vm = createComponent(CalloutComponent, {
- category: 'info',
- message: exampleMessage,
+ factory({
+ propsData: {
+ category: 'info',
+ message: TEST_MESSAGE,
+ },
});
- expect(vm.$el.getAttribute('class')).toEqual('bs-callout bs-callout-info');
+ expect(wrapper.classes()).toEqual(['bs-callout', 'bs-callout-info']);
- expect(vm.$el.tagName).toEqual('DIV');
+ expect(wrapper.element.tagName).toEqual('DIV');
});
it('should render accessibility attributes', () => {
- vm = createComponent(CalloutComponent, {
- message: exampleMessage,
+ factory({
+ propsData: {
+ message: TEST_MESSAGE,
+ },
});
- expect(vm.$el.getAttribute('role')).toEqual('alert');
- expect(vm.$el.getAttribute('aria-live')).toEqual('assertive');
+ expect(wrapper.attributes('role')).toEqual('alert');
+ expect(wrapper.attributes('aria-live')).toEqual('assertive');
});
it('should render the provided message', () => {
- vm = createComponent(CalloutComponent, {
- message: exampleMessage,
+ factory({
+ propsData: {
+ message: TEST_MESSAGE,
+ },
+ });
+
+ expect(wrapper.element.innerHTML.trim()).toEqual(TEST_MESSAGE);
+ });
+
+ it('should render the provided slot', () => {
+ factory({
+ slots: {
+ default: TEST_SLOT,
+ },
});
- expect(vm.$el.innerHTML.trim()).toEqual(exampleMessage);
+ expect(wrapper.element.innerHTML.trim()).toEqual(TEST_SLOT);
});
});
diff --git a/spec/javascripts/vue_shared/components/gl_modal_vuex_spec.js b/spec/javascripts/vue_shared/components/gl_modal_vuex_spec.js
new file mode 100644
index 00000000000..eb78d37db3e
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/gl_modal_vuex_spec.js
@@ -0,0 +1,151 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
+import { GlModal } from '@gitlab/ui';
+import GlModalVuex from '~/vue_shared/components/gl_modal_vuex.vue';
+import createState from '~/vuex_shared/modules/modal/state';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+const TEST_SLOT = 'Lorem ipsum modal dolar sit.';
+const TEST_MODAL_ID = 'my-modal-id';
+const TEST_MODULE = 'myModal';
+
+describe('GlModalVuex', () => {
+ let wrapper;
+ let state;
+ let actions;
+
+ const factory = (options = {}) => {
+ const store = new Vuex.Store({
+ modules: {
+ [TEST_MODULE]: {
+ namespaced: true,
+ state,
+ actions,
+ },
+ },
+ });
+
+ const propsData = {
+ modalId: TEST_MODAL_ID,
+ modalModule: TEST_MODULE,
+ ...options.propsData,
+ };
+
+ wrapper = shallowMount(localVue.extend(GlModalVuex), {
+ ...options,
+ localVue,
+ store,
+ propsData,
+ });
+ };
+
+ beforeEach(() => {
+ state = createState();
+
+ actions = {
+ show: jasmine.createSpy('show'),
+ hide: jasmine.createSpy('hide'),
+ };
+ });
+
+ it('renders gl-modal', () => {
+ factory({
+ slots: {
+ default: `<div>${TEST_SLOT}</div>`,
+ },
+ });
+ const glModal = wrapper.find(GlModal);
+
+ expect(glModal.props('modalId')).toBe(TEST_MODAL_ID);
+ expect(glModal.text()).toContain(TEST_SLOT);
+ });
+
+ it('passes props through to gl-modal', () => {
+ const title = 'Test Title';
+ const okVariant = 'success';
+
+ factory({
+ propsData: {
+ title,
+ okTitle: title,
+ okVariant,
+ },
+ });
+ const glModal = wrapper.find(GlModal);
+
+ expect(glModal.attributes('title')).toEqual(title);
+ expect(glModal.attributes('oktitle')).toEqual(title);
+ expect(glModal.attributes('okvariant')).toEqual(okVariant);
+ });
+
+ it('passes listeners through to gl-modal', () => {
+ const ok = jasmine.createSpy('ok');
+
+ factory({
+ listeners: { ok },
+ });
+
+ const glModal = wrapper.find(GlModal);
+ glModal.vm.$emit('ok');
+
+ expect(ok).toHaveBeenCalledTimes(1);
+ });
+
+ it('calls vuex action on show', () => {
+ expect(actions.show).not.toHaveBeenCalled();
+
+ factory();
+
+ const glModal = wrapper.find(GlModal);
+ glModal.vm.$emit('shown');
+
+ expect(actions.show).toHaveBeenCalledTimes(1);
+ });
+
+ it('calls vuex action on hide', () => {
+ expect(actions.hide).not.toHaveBeenCalled();
+
+ factory();
+
+ const glModal = wrapper.find(GlModal);
+ glModal.vm.$emit('hidden');
+
+ expect(actions.hide).toHaveBeenCalledTimes(1);
+ });
+
+ it('calls bootstrap show when isVisible changes', done => {
+ state.isVisible = false;
+
+ factory();
+ const rootEmit = spyOn(wrapper.vm.$root, '$emit');
+
+ state.isVisible = true;
+
+ localVue
+ .nextTick()
+ .then(() => {
+ expect(rootEmit).toHaveBeenCalledWith('bv::show::modal', TEST_MODAL_ID);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('calls bootstrap hide when isVisible changes', done => {
+ state.isVisible = true;
+
+ factory();
+ const rootEmit = spyOn(wrapper.vm.$root, '$emit');
+
+ state.isVisible = false;
+
+ localVue
+ .nextTick()
+ .then(() => {
+ expect(rootEmit).toHaveBeenCalledWith('bv::hide::modal', TEST_MODAL_ID);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/user_popover/user_popover_spec.js b/spec/javascripts/vue_shared/components/user_popover/user_popover_spec.js
index 25b6e3b6bc8..de3e0c149de 100644
--- a/spec/javascripts/vue_shared/components/user_popover/user_popover_spec.js
+++ b/spec/javascripts/vue_shared/components/user_popover/user_popover_spec.js
@@ -17,14 +17,13 @@ const DEFAULT_PROPS = {
const UserPopover = Vue.extend(userPopover);
describe('User Popover Component', () => {
+ const fixtureTemplate = 'merge_requests/diff_comment.html.raw';
+ preloadFixtures(fixtureTemplate);
+
let vm;
beforeEach(() => {
- setFixtures(`
- <a href="/root" data-user-id="1" class="js-user-link" title="testuser">
- Root
- </a>
- `);
+ loadFixtures(fixtureTemplate);
});
afterEach(() => {
diff --git a/spec/javascripts/vuex_shared/modules/modal/actions_spec.js b/spec/javascripts/vuex_shared/modules/modal/actions_spec.js
new file mode 100644
index 00000000000..04f64663ae4
--- /dev/null
+++ b/spec/javascripts/vuex_shared/modules/modal/actions_spec.js
@@ -0,0 +1,31 @@
+import * as types from '~/vuex_shared/modules/modal/mutation_types';
+import * as actions from '~/vuex_shared/modules/modal/actions';
+import testAction from 'spec/helpers/vuex_action_helper';
+
+describe('Vuex ModalModule actions', () => {
+ describe('open', () => {
+ it('works', done => {
+ const data = { id: 7 };
+
+ testAction(actions.open, data, {}, [{ type: types.OPEN, payload: data }], [], done);
+ });
+ });
+
+ describe('close', () => {
+ it('works', done => {
+ testAction(actions.close, null, {}, [{ type: types.CLOSE }], [], done);
+ });
+ });
+
+ describe('show', () => {
+ it('works', done => {
+ testAction(actions.show, null, {}, [{ type: types.SHOW }], [], done);
+ });
+ });
+
+ describe('hide', () => {
+ it('works', done => {
+ testAction(actions.hide, null, {}, [{ type: types.HIDE }], [], done);
+ });
+ });
+});
diff --git a/spec/javascripts/vuex_shared/modules/modal/mutations_spec.js b/spec/javascripts/vuex_shared/modules/modal/mutations_spec.js
new file mode 100644
index 00000000000..d07f8ba1e65
--- /dev/null
+++ b/spec/javascripts/vuex_shared/modules/modal/mutations_spec.js
@@ -0,0 +1,49 @@
+import mutations from '~/vuex_shared/modules/modal/mutations';
+import * as types from '~/vuex_shared/modules/modal/mutation_types';
+
+describe('Vuex ModalModule mutations', () => {
+ describe(types.SHOW, () => {
+ it('sets isVisible to true', () => {
+ const state = {
+ isVisible: false,
+ };
+
+ mutations[types.SHOW](state);
+
+ expect(state).toEqual({
+ isVisible: true,
+ });
+ });
+ });
+
+ describe(types.HIDE, () => {
+ it('sets isVisible to false', () => {
+ const state = {
+ isVisible: true,
+ };
+
+ mutations[types.HIDE](state);
+
+ expect(state).toEqual({
+ isVisible: false,
+ });
+ });
+ });
+
+ describe(types.OPEN, () => {
+ it('sets data and sets isVisible to true', () => {
+ const data = { id: 7 };
+ const state = {
+ isVisible: false,
+ data: null,
+ };
+
+ mutations[types.OPEN](state, data);
+
+ expect(state).toEqual({
+ isVisible: true,
+ data,
+ });
+ });
+ });
+});
diff --git a/spec/lib/api/api_spec.rb b/spec/lib/api/api_spec.rb
new file mode 100644
index 00000000000..ceef0b41e59
--- /dev/null
+++ b/spec/lib/api/api_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe API::API do
+ describe '.prefix' do
+ it 'has a prefix defined' do
+ expect(described_class.prefix).to eq :api
+ end
+ end
+
+ describe '.version' do
+ it 'uses most recent version of the API' do
+ expect(described_class.version).to eq 'v4'
+ end
+ end
+
+ describe '.versions' do
+ it 'returns all available versions' do
+ expect(described_class.versions).to eq %w[v3 v4]
+ end
+ end
+end
diff --git a/spec/lib/api/helpers/version_spec.rb b/spec/lib/api/helpers/version_spec.rb
new file mode 100644
index 00000000000..34006e0930b
--- /dev/null
+++ b/spec/lib/api/helpers/version_spec.rb
@@ -0,0 +1,26 @@
+require 'spec_helper'
+
+describe API::Helpers::Version do
+ describe '.new' do
+ it 'is possible to initialize it with existing API version' do
+ expect(described_class.new('v4').to_s).to eq 'v4'
+ end
+
+ it 'raises an error when unsupported API version is provided' do
+ expect { described_class.new('v111') }.to raise_error ArgumentError
+ end
+ end
+
+ describe '#root_path' do
+ it 'returns a root path of the API version' do
+ expect(described_class.new('v4').root_path).to eq '/api/v4'
+ end
+ end
+
+ describe '#root_url' do
+ it 'returns an URL for a root path for the API version' do
+ expect(described_class.new('v4').root_url)
+ .to eq 'http://localhost/api/v4'
+ end
+ end
+end
diff --git a/spec/lib/api/helpers_spec.rb b/spec/lib/api/helpers_spec.rb
index 58a49124ce6..1c73a936e17 100644
--- a/spec/lib/api/helpers_spec.rb
+++ b/spec/lib/api/helpers_spec.rb
@@ -148,4 +148,36 @@ describe API::Helpers do
it_behaves_like 'user namespace finder'
end
+
+ describe '#send_git_blob' do
+ context 'content disposition' do
+ let(:repository) { double }
+ let(:blob) { double(name: 'foobar') }
+
+ let(:send_git_blob) do
+ subject.send(:send_git_blob, repository, blob)
+ end
+
+ before do
+ allow(subject).to receive(:env).and_return({})
+ allow(subject).to receive(:content_type)
+ allow(subject).to receive(:header).and_return({})
+ allow(Gitlab::Workhorse).to receive(:send_git_blob)
+ end
+
+ context 'when blob name is null' do
+ let(:blob) { double(name: nil) }
+
+ it 'returns only the disposition' do
+ expect(send_git_blob['Content-Disposition']).to eq 'attachment'
+ end
+ end
+
+ context 'when blob name is not null' do
+ it 'returns disposition with the blob name' do
+ expect(send_git_blob['Content-Disposition']).to eq 'attachment; filename="foobar"'
+ end
+ end
+ end
+ end
end
diff --git a/spec/lib/banzai/filter/external_link_filter_spec.rb b/spec/lib/banzai/filter/external_link_filter_spec.rb
index 2a3c0cd78b8..e6dae8d5382 100644
--- a/spec/lib/banzai/filter/external_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/external_link_filter_spec.rb
@@ -49,16 +49,16 @@ describe Banzai::Filter::ExternalLinkFilter do
end
context 'for invalid urls' do
- it 'skips broken hrefs' do
+ it 'adds rel and target attributes to broken hrefs' do
doc = filter %q(<p><a href="don't crash on broken urls">Google</a></p>)
- expected = %q(<p><a href="don't%20crash%20on%20broken%20urls">Google</a></p>)
+ expected = %q(<p><a href="don't%20crash%20on%20broken%20urls" rel="nofollow noreferrer noopener" target="_blank">Google</a></p>)
expect(doc.to_html).to eq(expected)
end
- it 'skips improperly formatted mailtos' do
+ it 'adds rel and target to improperly formatted mailtos' do
doc = filter %q(<p><a href="mailto://jblogs@example.com">Email</a></p>)
- expected = %q(<p><a href="mailto://jblogs@example.com">Email</a></p>)
+ expected = %q(<p><a href="mailto://jblogs@example.com" rel="nofollow noreferrer noopener" target="_blank">Email</a></p>)
expect(doc.to_html).to eq(expected)
end
diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb
index 00257ed7904..9cfdb9e53a2 100644
--- a/spec/lib/banzai/filter/label_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb
@@ -236,6 +236,24 @@ describe Banzai::Filter::LabelReferenceFilter do
end
end
+ context 'References with html entities' do
+ let!(:label) { create(:label, name: '&lt;html&gt;', project: project) }
+
+ it 'links to a valid reference' do
+ doc = reference_filter('See ~"&lt;html&gt;"')
+
+ expect(doc.css('a').first.attr('href')).to eq urls
+ .project_issues_url(project, label_name: label.name)
+ expect(doc.text).to eq 'See <html>'
+ end
+
+ it 'ignores invalid label names and escapes entities' do
+ act = %(Label #{Label.reference_prefix}"&lt;non valid&gt;")
+
+ expect(reference_filter(act).to_html).to eq act
+ end
+ end
+
describe 'consecutive references' do
let(:bug) { create(:label, name: 'bug', project: project) }
let(:feature_proposal) { create(:label, name: 'feature proposal', project: project) }
diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
index 1a87cfa5b45..4c94e4fdae0 100644
--- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
@@ -59,7 +59,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do
it 'links with adjacent text' do
doc = reference_filter("Milestone (#{reference}.)")
- expect(doc.to_html).to match(%r(\(<a.+>#{milestone.name}</a>\.\)))
+ expect(doc.to_html).to match(%r(\(<a.+>#{milestone.reference_link_text}</a>\.\)))
end
it 'ignores invalid milestone IIDs' do
@@ -80,12 +80,12 @@ describe Banzai::Filter::MilestoneReferenceFilter do
doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls.milestone_url(milestone)
- expect(doc.text).to eq 'See gfm'
+ expect(doc.text).to eq "See #{milestone.reference_link_text}"
end
it 'links with adjacent text' do
doc = reference_filter("Milestone (#{reference}.)")
- expect(doc.to_html).to match(%r(\(<a.+>#{milestone.name}</a>\.\)))
+ expect(doc.to_html).to match(%r(\(<a.+>#{milestone.reference_link_text}</a>\.\)))
end
it 'ignores invalid milestone names' do
@@ -106,12 +106,12 @@ describe Banzai::Filter::MilestoneReferenceFilter do
doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls.milestone_url(milestone)
- expect(doc.text).to eq 'See gfm references'
+ expect(doc.text).to eq "See #{milestone.reference_link_text}"
end
it 'links with adjacent text' do
doc = reference_filter("Milestone (#{reference}.)")
- expect(doc.to_html).to match(%r(\(<a.+>#{milestone.name}</a>\.\)))
+ expect(doc.to_html).to match(%r(\(<a.+>#{milestone.reference_link_text}</a>\.\)))
end
it 'ignores invalid milestone names' do
@@ -201,14 +201,14 @@ describe Banzai::Filter::MilestoneReferenceFilter do
doc = reference_filter("See (#{reference}.)")
expect(doc.css('a').first.text)
- .to eq("#{milestone.name} in #{another_project.full_path}")
+ .to eq("#{milestone.reference_link_text} in #{another_project.full_path}")
end
it 'has valid text' do
doc = reference_filter("See (#{reference}.)")
expect(doc.text)
- .to eq("See (#{milestone.name} in #{another_project.full_path}.)")
+ .to eq("See (#{milestone.reference_link_text} in #{another_project.full_path}.)")
end
it 'escapes the name attribute' do
@@ -217,7 +217,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do
doc = reference_filter("See #{reference}")
expect(doc.css('a').first.text)
- .to eq "#{milestone.name} in #{another_project.full_path}"
+ .to eq "#{milestone.reference_link_text} in #{another_project.full_path}"
end
end
@@ -238,14 +238,14 @@ describe Banzai::Filter::MilestoneReferenceFilter do
doc = reference_filter("See (#{reference}.)")
expect(doc.css('a').first.text)
- .to eq("#{milestone.name} in #{another_project.path}")
+ .to eq("#{milestone.reference_link_text} in #{another_project.path}")
end
it 'has valid text' do
doc = reference_filter("See (#{reference}.)")
expect(doc.text)
- .to eq("See (#{milestone.name} in #{another_project.path}.)")
+ .to eq("See (#{milestone.reference_link_text} in #{another_project.path}.)")
end
it 'escapes the name attribute' do
@@ -254,7 +254,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do
doc = reference_filter("See #{reference}")
expect(doc.css('a').first.text)
- .to eq "#{milestone.name} in #{another_project.path}"
+ .to eq "#{milestone.reference_link_text} in #{another_project.path}"
end
end
@@ -275,14 +275,14 @@ describe Banzai::Filter::MilestoneReferenceFilter do
doc = reference_filter("See (#{reference}.)")
expect(doc.css('a').first.text)
- .to eq("#{milestone.name} in #{another_project.path}")
+ .to eq("#{milestone.reference_link_text} in #{another_project.path}")
end
it 'has valid text' do
doc = reference_filter("See (#{reference}.)")
expect(doc.text)
- .to eq("See (#{milestone.name} in #{another_project.path}.)")
+ .to eq("See (#{milestone.reference_link_text} in #{another_project.path}.)")
end
it 'escapes the name attribute' do
@@ -291,7 +291,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do
doc = reference_filter("See #{reference}")
expect(doc.css('a').first.text)
- .to eq "#{milestone.name} in #{another_project.path}"
+ .to eq "#{milestone.reference_link_text} in #{another_project.path}"
end
end
@@ -346,7 +346,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do
milestone.update!(group: parent_group)
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.text).to eq(milestone.name)
+ expect(doc.css('a').first.text).to eq(milestone.reference_link_text)
end
end
diff --git a/spec/lib/gitlab/blob_helper_spec.rb b/spec/lib/gitlab/blob_helper_spec.rb
index 0b56f8687c3..e057385b35f 100644
--- a/spec/lib/gitlab/blob_helper_spec.rb
+++ b/spec/lib/gitlab/blob_helper_spec.rb
@@ -53,11 +53,11 @@ describe Gitlab::BlobHelper do
describe '#text?' do
it 'returns true' do
- expect(blob.text?).to be_truthy
+ expect(blob.text_in_repo?).to be_truthy
end
it 'returns false' do
- expect(large_blob.text?).to be_falsey
+ expect(large_blob.text_in_repo?).to be_falsey
end
end
diff --git a/spec/lib/gitlab/checks/diff_check_spec.rb b/spec/lib/gitlab/checks/diff_check_spec.rb
index eeec1e83179..a341dfa5636 100644
--- a/spec/lib/gitlab/checks/diff_check_spec.rb
+++ b/spec/lib/gitlab/checks/diff_check_spec.rb
@@ -47,5 +47,43 @@ describe Gitlab::Checks::DiffCheck do
end
end
end
+
+ context 'commit diff validations' do
+ before do
+ allow(subject).to receive(:validations_for_diff).and_return([lambda { |diff| return }])
+
+ expect_any_instance_of(Commit).to receive(:raw_deltas).and_call_original
+
+ subject.validate!
+ end
+
+ context 'when request store is inactive' do
+ it 'are run for every commit' do
+ expect_any_instance_of(Commit).to receive(:raw_deltas).and_call_original
+
+ subject.validate!
+ end
+ end
+
+ context 'when request store is active', :request_store do
+ it 'are cached for every commit' do
+ expect_any_instance_of(Commit).not_to receive(:raw_deltas)
+
+ subject.validate!
+ end
+
+ it 'are run for not cached commits' do
+ allow(project.repository).to receive(:new_commits).and_return(
+ project.repository.commits_between('be93687618e4b132087f430a4d8fc3a609c9b77c', 'a5391128b0ef5d21df5dd23d98557f4ef12fae20')
+ )
+ change_access.instance_variable_set(:@commits, project.repository.new_commits)
+
+ expect(project.repository.new_commits.first).not_to receive(:raw_deltas).and_call_original
+ expect(project.repository.new_commits.last).to receive(:raw_deltas).and_call_original
+
+ subject.validate!
+ end
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/checks/push_check_spec.rb b/spec/lib/gitlab/checks/push_check_spec.rb
index 25f0d428cb9..e1bd52d6c0b 100644
--- a/spec/lib/gitlab/checks/push_check_spec.rb
+++ b/spec/lib/gitlab/checks/push_check_spec.rb
@@ -13,7 +13,7 @@ describe Gitlab::Checks::PushCheck do
context 'when the user is not allowed to push to the repo' do
it 'raises an error' do
expect(user_access).to receive(:can_do_action?).with(:push_code).and_return(false)
- expect(user_access).to receive(:can_push_to_branch?).with('master').and_return(false)
+ expect(project).to receive(:branch_allows_collaboration?).with(user_access.user, 'master').and_return(false)
expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to push code to this project.')
end
diff --git a/spec/lib/gitlab/ci/config/external/file/base_spec.rb b/spec/lib/gitlab/ci/config/external/file/base_spec.rb
index 2e92d5204d6..ada8775c489 100644
--- a/spec/lib/gitlab/ci/config/external/file/base_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/base_spec.rb
@@ -3,13 +3,43 @@
require 'fast_spec_helper'
describe Gitlab::Ci::Config::External::File::Base do
- subject { described_class.new(location) }
+ let(:context) { described_class::Context.new(nil, 'HEAD') }
+
+ let(:test_class) do
+ Class.new(described_class) do
+ def initialize(params, context = {})
+ @location = params
+
+ super
+ end
+ end
+ end
+
+ subject { test_class.new(location, context) }
before do
- allow_any_instance_of(described_class)
+ allow_any_instance_of(test_class)
.to receive(:content).and_return('key: value')
end
+ describe '#matching?' do
+ context 'when a location is present' do
+ let(:location) { 'some-location' }
+
+ it 'should return true' do
+ expect(subject).to be_matching
+ end
+ end
+
+ context 'with a location is missing' do
+ let(:location) { nil }
+
+ it 'should return false' do
+ expect(subject).not_to be_matching
+ end
+ end
+ end
+
describe '#valid?' do
context 'when location is not a YAML file' do
let(:location) { 'some/file.txt' }
@@ -39,7 +69,7 @@ describe Gitlab::Ci::Config::External::File::Base do
let(:location) { 'some/file/config.yml' }
before do
- allow_any_instance_of(described_class)
+ allow_any_instance_of(test_class)
.to receive(:content).and_return('invalid_syntax')
end
diff --git a/spec/lib/gitlab/ci/config/external/file/local_spec.rb b/spec/lib/gitlab/ci/config/external/file/local_spec.rb
index 541deb13b97..83be43e240b 100644
--- a/spec/lib/gitlab/ci/config/external/file/local_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/local_spec.rb
@@ -3,8 +3,37 @@
require 'spec_helper'
describe Gitlab::Ci::Config::External::File::Local do
- let(:project) { create(:project, :repository) }
- let(:local_file) { described_class.new(location, { project: project, sha: '12345' }) }
+ set(:project) { create(:project, :repository) }
+
+ let(:context) { described_class::Context.new(project, '12345') }
+ let(:params) { { local: location } }
+ let(:local_file) { described_class.new(params, context) }
+
+ describe '#matching?' do
+ context 'when a local is specified' do
+ let(:params) { { local: 'file' } }
+
+ it 'should return true' do
+ expect(local_file).to be_matching
+ end
+ end
+
+ context 'with a missing local' do
+ let(:params) { { local: nil } }
+
+ it 'should return false' do
+ expect(local_file).not_to be_matching
+ end
+ end
+
+ context 'with a missing local key' do
+ let(:params) { {} }
+
+ it 'should return false' do
+ expect(local_file).not_to be_matching
+ end
+ end
+ end
describe '#valid?' do
context 'when is a valid local path' do
@@ -44,7 +73,6 @@ describe Gitlab::Ci::Config::External::File::Local do
- apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs
- ruby -v
- which ruby
- - gem install bundler --no-ri --no-rdoc
- bundle install --jobs $(nproc) "${FLAGS[@]}"
HEREDOC
end
diff --git a/spec/lib/gitlab/ci/config/external/file/remote_spec.rb b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
index 7c1a1c38736..319e7137f9f 100644
--- a/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
@@ -3,7 +3,9 @@
require 'spec_helper'
describe Gitlab::Ci::Config::External::File::Remote do
- let(:remote_file) { described_class.new(location) }
+ let(:context) { described_class::Context.new(nil, '12345') }
+ let(:params) { { remote: location } }
+ let(:remote_file) { described_class.new(params, context) }
let(:location) { 'https://gitlab.com/gitlab-org/gitlab-ce/blob/1234/.gitlab-ci-1.yml' }
let(:remote_file_content) do
<<~HEREDOC
@@ -11,11 +13,36 @@ describe Gitlab::Ci::Config::External::File::Remote do
- apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs
- ruby -v
- which ruby
- - gem install bundler --no-ri --no-rdoc
- bundle install --jobs $(nproc) "${FLAGS[@]}"
HEREDOC
end
+ describe '#matching?' do
+ context 'when a remote is specified' do
+ let(:params) { { remote: 'http://remote' } }
+
+ it 'should return true' do
+ expect(remote_file).to be_matching
+ end
+ end
+
+ context 'with a missing remote' do
+ let(:params) { { remote: nil } }
+
+ it 'should return false' do
+ expect(remote_file).not_to be_matching
+ end
+ end
+
+ context 'with a missing remote key' do
+ let(:params) { {} }
+
+ it 'should return false' do
+ expect(remote_file).not_to be_matching
+ end
+ end
+ end
+
describe "#valid?" do
context 'when is a valid remote url' do
before do
diff --git a/spec/lib/gitlab/ci/config/external/file/template_spec.rb b/spec/lib/gitlab/ci/config/external/file/template_spec.rb
new file mode 100644
index 00000000000..1fb5655309a
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/external/file/template_spec.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Config::External::File::Template do
+ let(:context) { described_class::Context.new(nil, '12345') }
+ let(:template) { 'Auto-DevOps.gitlab-ci.yml' }
+ let(:params) { { template: template } }
+
+ subject { described_class.new(params, context) }
+
+ describe '#matching?' do
+ context 'when a template is specified' do
+ let(:params) { { template: 'some-template' } }
+
+ it 'should return true' do
+ expect(subject).to be_matching
+ end
+ end
+
+ context 'with a missing template' do
+ let(:params) { { template: nil } }
+
+ it 'should return false' do
+ expect(subject).not_to be_matching
+ end
+ end
+
+ context 'with a missing template key' do
+ let(:params) { {} }
+
+ it 'should return false' do
+ expect(subject).not_to be_matching
+ end
+ end
+ end
+
+ describe "#valid?" do
+ context 'when is a valid template name' do
+ let(:template) { 'Auto-DevOps.gitlab-ci.yml' }
+
+ it 'should return true' do
+ expect(subject).to be_valid
+ end
+ end
+
+ context 'with invalid template name' do
+ let(:template) { 'Template.yml' }
+
+ it 'should return false' do
+ expect(subject).not_to be_valid
+ expect(subject.error_message).to include('Template file `Template.yml` is not a valid location!')
+ end
+ end
+
+ context 'with a non-existing template' do
+ let(:template) { 'I-Do-Not-Have-This-Template.gitlab-ci.yml' }
+
+ it 'should return false' do
+ expect(subject).not_to be_valid
+ expect(subject.error_message).to include('Included file `I-Do-Not-Have-This-Template.gitlab-ci.yml` is empty or does not exist!')
+ end
+ end
+ end
+
+ describe '#template_name' do
+ let(:template_name) { subject.send(:template_name) }
+
+ context 'when template does end with .gitlab-ci.yml' do
+ let(:template) { 'my-template.gitlab-ci.yml' }
+
+ it 'returns template name' do
+ expect(template_name).to eq('my-template')
+ end
+ end
+
+ context 'when template is nil' do
+ let(:template) { nil }
+
+ it 'returns nil' do
+ expect(template_name).to be_nil
+ end
+ end
+
+ context 'when template does not end with .gitlab-ci.yml' do
+ let(:template) { 'my-template' }
+
+ it 'returns nil' do
+ expect(template_name).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/external/mapper_spec.rb b/spec/lib/gitlab/ci/config/external/mapper_spec.rb
index 5b236fe99f1..e27d2cd9422 100644
--- a/spec/lib/gitlab/ci/config/external/mapper_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/mapper_spec.rb
@@ -3,84 +3,130 @@
require 'spec_helper'
describe Gitlab::Ci::Config::External::Mapper do
- let(:project) { create(:project, :repository) }
+ set(:project) { create(:project, :repository) }
+
+ let(:local_file) { '/lib/gitlab/ci/templates/non-existent-file.yml' }
+ let(:remote_url) { 'https://gitlab.com/gitlab-org/gitlab-ce/blob/1234/.gitlab-ci-1.yml' }
+ let(:template_file) { 'Auto-DevOps.gitlab-ci.yml' }
+
let(:file_content) do
<<~HEREDOC
image: 'ruby:2.2'
HEREDOC
end
+ before do
+ WebMock.stub_request(:get, remote_url).to_return(body: file_content)
+ end
+
describe '#process' do
- subject { described_class.new(values, project, '123456').process }
+ subject { described_class.new(values, project: project, sha: '123456').process }
- context "when 'include' keyword is defined as string" do
+ context "when single 'include' keyword is defined" do
context 'when the string is a local file' do
let(:values) do
- {
- include: '/lib/gitlab/ci/templates/non-existent-file.yml',
- image: 'ruby:2.2'
- }
+ { include: local_file,
+ image: 'ruby:2.2' }
+ end
+
+ it 'returns File instances' do
+ expect(subject).to contain_exactly(
+ an_instance_of(Gitlab::Ci::Config::External::File::Local))
end
+ end
- it 'returns an array' do
- expect(subject).to be_an(Array)
+ context 'when the key is a local file hash' do
+ let(:values) do
+ { include: { 'local' => local_file },
+ image: 'ruby:2.2' }
end
it 'returns File instances' do
- expect(subject.first)
- .to be_an_instance_of(Gitlab::Ci::Config::External::File::Local)
+ expect(subject).to contain_exactly(
+ an_instance_of(Gitlab::Ci::Config::External::File::Local))
end
end
context 'when the string is a remote file' do
- let(:remote_url) { 'https://gitlab.com/gitlab-org/gitlab-ce/blob/1234/.gitlab-ci-1.yml' }
let(:values) do
- {
- include: remote_url,
- image: 'ruby:2.2'
- }
+ { include: remote_url, image: 'ruby:2.2' }
+ end
+
+ it 'returns File instances' do
+ expect(subject).to contain_exactly(
+ an_instance_of(Gitlab::Ci::Config::External::File::Remote))
+ end
+ end
+
+ context 'when the key is a remote file hash' do
+ let(:values) do
+ { include: { 'remote' => remote_url },
+ image: 'ruby:2.2' }
end
- before do
- WebMock.stub_request(:get, remote_url).to_return(body: file_content)
+ it 'returns File instances' do
+ expect(subject).to contain_exactly(
+ an_instance_of(Gitlab::Ci::Config::External::File::Remote))
end
+ end
- it 'returns an array' do
- expect(subject).to be_an(Array)
+ context 'when the key is a template file hash' do
+ let(:values) do
+ { include: { 'template' => template_file },
+ image: 'ruby:2.2' }
end
it 'returns File instances' do
- expect(subject.first)
- .to be_an_instance_of(Gitlab::Ci::Config::External::File::Remote)
+ expect(subject).to contain_exactly(
+ an_instance_of(Gitlab::Ci::Config::External::File::Template))
+ end
+ end
+
+ context 'when the key is a hash of file and remote' do
+ let(:values) do
+ { include: { 'local' => local_file, 'remote' => remote_url },
+ image: 'ruby:2.2' }
+ end
+
+ it 'returns ambigious specification error' do
+ expect { subject }.to raise_error(described_class::AmbigiousSpecificationError)
end
end
end
context "when 'include' is defined as an array" do
- let(:remote_url) { 'https://gitlab.com/gitlab-org/gitlab-ce/blob/1234/.gitlab-ci-1.yml' }
let(:values) do
- {
- include:
- [
- remote_url,
- '/lib/gitlab/ci/templates/template.yml'
- ],
- image: 'ruby:2.2'
- }
+ { include: [remote_url, local_file],
+ image: 'ruby:2.2' }
end
- before do
- WebMock.stub_request(:get, remote_url).to_return(body: file_content)
+ it 'returns Files instances' do
+ expect(subject).to all(respond_to(:valid?))
+ expect(subject).to all(respond_to(:content))
end
+ end
- it 'returns an array' do
- expect(subject).to be_an(Array)
+ context "when 'include' is defined as an array of hashes" do
+ let(:values) do
+ { include: [{ remote: remote_url }, { local: local_file }],
+ image: 'ruby:2.2' }
end
it 'returns Files instances' do
expect(subject).to all(respond_to(:valid?))
expect(subject).to all(respond_to(:content))
end
+
+ context 'when it has ambigious match' do
+ let(:values) do
+ { include: [{ remote: remote_url, local: local_file }],
+ image: 'ruby:2.2' }
+ end
+
+ it 'returns ambigious specification error' do
+ expect { subject }.to raise_error(described_class::AmbigiousSpecificationError)
+ end
+ end
end
context "when 'include' is not defined" do
diff --git a/spec/lib/gitlab/ci/config/external/processor_spec.rb b/spec/lib/gitlab/ci/config/external/processor_spec.rb
index 1a05f716247..d2d4fbc5115 100644
--- a/spec/lib/gitlab/ci/config/external/processor_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/processor_spec.rb
@@ -3,8 +3,9 @@
require 'spec_helper'
describe Gitlab::Ci::Config::External::Processor do
- let(:project) { create(:project, :repository) }
- let(:processor) { described_class.new(values, project, '12345') }
+ set(:project) { create(:project, :repository) }
+
+ let(:processor) { described_class.new(values, project: project, sha: '12345') }
describe "#perform" do
context 'when no external files defined' do
@@ -51,7 +52,6 @@ describe Gitlab::Ci::Config::External::Processor do
- apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs
- ruby -v
- which ruby
- - gem install bundler --no-ri --no-rdoc
- bundle install --jobs $(nproc) "${FLAGS[@]}"
rspec:
@@ -86,7 +86,6 @@ describe Gitlab::Ci::Config::External::Processor do
- apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs
- ruby -v
- which ruby
- - gem install bundler --no-ri --no-rdoc
- bundle install --jobs $(nproc) "${FLAGS[@]}"
HEREDOC
end
diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb
index 975e11e8cc1..49988468d1a 100644
--- a/spec/lib/gitlab/ci/config_spec.rb
+++ b/spec/lib/gitlab/ci/config_spec.rb
@@ -170,7 +170,6 @@ describe Gitlab::Ci::Config do
before_script_values = [
"apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs", "ruby -v",
"which ruby",
- "gem install bundler --no-ri --no-rdoc",
"bundle install --jobs $(nproc) \"${FLAGS[@]}\""
]
variables = {
@@ -206,6 +205,23 @@ describe Gitlab::Ci::Config do
end
end
+ context "when gitlab_ci.yml has ambigious 'include' defined" do
+ let(:gitlab_ci_yml) do
+ <<~HEREDOC
+ include:
+ remote: http://url
+ local: /local/file.yml
+ HEREDOC
+ end
+
+ it 'raises error YamlProcessor validationError' do
+ expect { config }.to raise_error(
+ described_class::ConfigError,
+ 'Include `{"remote":"http://url","local":"/local/file.yml"}` needs to match exactly one accessor!'
+ )
+ end
+ end
+
describe 'external file version' do
context 'when external local file SHA is defined' do
it 'is using a defined value' do
diff --git a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
index 75a177d2d1f..6aa802ce6fd 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
@@ -182,4 +182,24 @@ describe Gitlab::Ci::Pipeline::Chain::Command do
it { is_expected.to eq(false) }
end
end
+
+ describe '#ambiguous_ref' do
+ let(:project) { create(:project, :repository) }
+ let(:command) { described_class.new(project: project, origin_ref: 'ref') }
+
+ subject { command.ambiguous_ref? }
+
+ context 'when ref is not ambiguous' do
+ it { is_expected. to eq(false) }
+ end
+
+ context 'when ref is ambiguous' do
+ before do
+ project.repository.add_tag(project.creator, 'ref', 'master')
+ project.repository.add_branch(project.creator, 'ref', 'master')
+ end
+
+ it { is_expected. to eq(true) }
+ end
+ end
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb
index 284aed91e29..1b014ecfaa4 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb
@@ -14,6 +14,7 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do
Gitlab::Ci::Pipeline::Chain::Command.new(
project: project,
current_user: user,
+ origin_ref: 'master',
seeds_block: nil)
end
@@ -106,6 +107,7 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do
Gitlab::Ci::Pipeline::Chain::Command.new(
project: project,
current_user: user,
+ origin_ref: 'master',
seeds_block: seeds_block)
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb
index fb1b53fc55c..a7cad423d09 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb
@@ -42,6 +42,27 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Repository do
end
end
+ context 'when ref is ambiguous' do
+ let(:project) do
+ create(:project, :repository).tap do |proj|
+ proj.repository.add_tag(user, 'master', 'master')
+ end
+ end
+ let(:command) do
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ project: project, current_user: user, origin_ref: 'master')
+ end
+
+ it 'breaks the chain' do
+ expect(step.break?).to be true
+ end
+
+ it 'adds an error about missing ref' do
+ expect(pipeline.errors.to_a)
+ .to include 'Ref is ambiguous'
+ end
+ end
+
context 'when does not have existing SHA set' do
let(:command) do
Gitlab::Ci::Pipeline::Chain::Command.new(
diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
index fffa727c2ed..2cf812b26dc 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
@@ -1,7 +1,8 @@
require 'spec_helper'
describe Gitlab::Ci::Pipeline::Seed::Build do
- let(:pipeline) { create(:ci_empty_pipeline) }
+ let(:project) { create(:project, :repository) }
+ let(:pipeline) { create(:ci_empty_pipeline, project: project) }
let(:attributes) do
{ name: 'rspec',
diff --git a/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb
index 05ce3412fd8..82f741845db 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb
@@ -1,7 +1,8 @@
require 'spec_helper'
describe Gitlab::Ci::Pipeline::Seed::Stage do
- let(:pipeline) { create(:ci_empty_pipeline) }
+ let(:project) { create(:project, :repository) }
+ let(:pipeline) { create(:ci_empty_pipeline, project: project) }
let(:attributes) do
{ name: 'test',
diff --git a/spec/lib/gitlab/cleanup/remote_uploads_spec.rb b/spec/lib/gitlab/cleanup/remote_uploads_spec.rb
index 8d03baeb07b..35642cd6e50 100644
--- a/spec/lib/gitlab/cleanup/remote_uploads_spec.rb
+++ b/spec/lib/gitlab/cleanup/remote_uploads_spec.rb
@@ -25,7 +25,7 @@ describe Gitlab::Cleanup::RemoteUploads do
expect(::Fog::Storage).to receive(:new).and_return(connection)
- expect(connection).to receive(:directories).and_return(double(get: directory))
+ expect(connection).to receive(:directories).and_return(double(new: directory))
expect(directory).to receive(:files).and_return(remote_files)
end
diff --git a/spec/lib/gitlab/current_settings_spec.rb b/spec/lib/gitlab/current_settings_spec.rb
index 55490f37ac7..caf9fc5442c 100644
--- a/spec/lib/gitlab/current_settings_spec.rb
+++ b/spec/lib/gitlab/current_settings_spec.rb
@@ -54,7 +54,7 @@ describe Gitlab::CurrentSettings do
expect(ApplicationSetting).not_to receive(:current)
end
- it 'returns an in-memory ApplicationSetting object' do
+ it 'returns a FakeApplicationSettings object' do
expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings)
end
@@ -91,6 +91,14 @@ describe Gitlab::CurrentSettings do
allow(ActiveRecord::Base.connection).to receive(:cached_table_exists?).with('application_settings').and_return(true)
end
+ context 'with RequestStore enabled', :request_store do
+ it 'fetches the settings from DB only once' do
+ described_class.current_application_settings # warm the cache
+
+ expect(ActiveRecord::QueryRecorder.new { described_class.current_application_settings }.count).to eq(0)
+ end
+ end
+
it 'creates default ApplicationSettings if none are present' do
settings = described_class.current_application_settings
@@ -99,34 +107,45 @@ describe Gitlab::CurrentSettings do
expect(settings).to have_attributes(settings_from_defaults)
end
- context 'with migrations pending' do
+ context 'with pending migrations' do
before do
expect(ActiveRecord::Migrator).to receive(:needs_migration?).and_return(true)
end
- it 'returns an in-memory ApplicationSetting object' do
- settings = described_class.current_application_settings
+ shared_examples 'a non-persisted ApplicationSetting object' do
+ let(:current_settings) { described_class.current_application_settings }
+
+ it 'returns a non-persisted ApplicationSetting object' do
+ expect(current_settings).to be_a(ApplicationSetting)
+ expect(current_settings).not_to be_persisted
+ end
+
+ it 'uses the default value from ApplicationSetting.defaults' do
+ expect(current_settings.signup_enabled).to eq(ApplicationSetting.defaults[:signup_enabled])
+ end
+
+ it 'uses the default value from custom ApplicationSetting accessors' do
+ expect(current_settings.commit_email_hostname).to eq(ApplicationSetting.default_commit_email_hostname)
+ end
+
+ it 'responds to predicate methods' do
+ expect(current_settings.signup_enabled?).to eq(current_settings.signup_enabled)
+ end
+ end
- expect(settings).to be_a(Gitlab::FakeApplicationSettings)
- expect(settings.sign_in_enabled?).to eq(settings.sign_in_enabled)
- expect(settings.sign_up_enabled?).to eq(settings.sign_up_enabled)
+ context 'with no ApplicationSetting DB record' do
+ it_behaves_like 'a non-persisted ApplicationSetting object'
end
- it 'uses the existing database settings and falls back to defaults' do
- db_settings = create(:application_setting,
- home_page_url: 'http://mydomain.com',
- signup_enabled: false)
- settings = described_class.current_application_settings
- app_defaults = ApplicationSetting.last
-
- expect(settings).to be_a(Gitlab::FakeApplicationSettings)
- expect(settings.home_page_url).to eq(db_settings.home_page_url)
- expect(settings.signup_enabled?).to be_falsey
- expect(settings.signup_enabled).to be_falsey
-
- # Check that unspecified values use the defaults
- settings.reject! { |key, _| [:home_page_url, :signup_enabled].include? key }
- settings.each { |key, _| expect(settings[key]).to eq(app_defaults[key]) }
+ context 'with an existing ApplicationSetting DB record' do
+ let!(:db_settings) { ApplicationSetting.build_from_defaults(home_page_url: 'http://mydomain.com').save! && ApplicationSetting.last }
+ let(:current_settings) { described_class.current_application_settings }
+
+ it_behaves_like 'a non-persisted ApplicationSetting object'
+
+ it 'uses the value from the DB attribute if present and not overriden by an accessor' do
+ expect(current_settings.home_page_url).to eq(db_settings.home_page_url)
+ end
end
end
@@ -138,17 +157,12 @@ describe Gitlab::CurrentSettings do
end
end
- context 'when the application_settings table does not exists' do
- it 'returns an in-memory ApplicationSetting object' do
- expect(ApplicationSetting).to receive(:create_from_defaults).and_raise(ActiveRecord::StatementInvalid)
-
- expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings)
- end
- end
-
- context 'when the application_settings table is not fully migrated' do
- it 'returns an in-memory ApplicationSetting object' do
- expect(ApplicationSetting).to receive(:create_from_defaults).and_raise(ActiveRecord::UnknownAttributeError)
+ context 'when the application_settings table does not exist' do
+ it 'returns a FakeApplicationSettings object' do
+ expect(Gitlab::Database)
+ .to receive(:cached_table_exists?)
+ .with('application_settings')
+ .and_return(false)
expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings)
end
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index b15d22c634a..862590268ca 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -310,7 +310,7 @@ describe Gitlab::Diff::File do
context 'when the content changed' do
context 'when the file represented by the diff file is binary' do
before do
- allow(diff_file).to receive(:raw_binary?).and_return(true)
+ allow(diff_file).to receive(:binary?).and_return(true)
end
it 'returns a No Preview viewer' do
@@ -345,7 +345,7 @@ describe Gitlab::Diff::File do
context 'when the file represented by the diff file is binary' do
before do
- allow(diff_file).to receive(:raw_binary?).and_return(true)
+ allow(diff_file).to receive(:binary?).and_return(true)
end
it 'returns an Added viewer' do
@@ -380,7 +380,7 @@ describe Gitlab::Diff::File do
context 'when the file represented by the diff file is binary' do
before do
- allow(diff_file).to receive(:raw_binary?).and_return(true)
+ allow(diff_file).to receive(:binary?).and_return(true)
end
it 'returns a Deleted viewer' do
@@ -436,7 +436,7 @@ describe Gitlab::Diff::File do
allow(diff_file).to receive(:deleted_file?).and_return(false)
allow(diff_file).to receive(:renamed_file?).and_return(false)
allow(diff_file).to receive(:mode_changed?).and_return(false)
- allow(diff_file).to receive(:raw_text?).and_return(false)
+ allow(diff_file).to receive(:text?).and_return(false)
end
it 'returns a No Preview viewer' do
diff --git a/spec/lib/gitlab/diff/lines_unfolder_spec.rb b/spec/lib/gitlab/diff/lines_unfolder_spec.rb
index 8e00c8e0e30..f22c2c90334 100644
--- a/spec/lib/gitlab/diff/lines_unfolder_spec.rb
+++ b/spec/lib/gitlab/diff/lines_unfolder_spec.rb
@@ -185,7 +185,7 @@ describe Gitlab::Diff::LinesUnfolder do
let(:project) { create(:project) }
- let(:old_blob) { Gitlab::Git::Blob.new(data: raw_old_blob) }
+ let(:old_blob) { Blob.decorate(Gitlab::Git::Blob.new(data: raw_old_blob, size: 10)) }
let(:diff) do
Gitlab::Git::Diff.new(diff: raw_diff,
diff --git a/spec/lib/gitlab/discussions_diff/file_collection_spec.rb b/spec/lib/gitlab/discussions_diff/file_collection_spec.rb
new file mode 100644
index 00000000000..0489206458b
--- /dev/null
+++ b/spec/lib/gitlab/discussions_diff/file_collection_spec.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::DiscussionsDiff::FileCollection do
+ let(:merge_request) { create(:merge_request) }
+ let!(:diff_note_a) { create(:diff_note_on_merge_request, project: merge_request.project, noteable: merge_request) }
+ let!(:diff_note_b) { create(:diff_note_on_merge_request, project: merge_request.project, noteable: merge_request) }
+ let(:note_diff_file_a) { diff_note_a.note_diff_file }
+ let(:note_diff_file_b) { diff_note_b.note_diff_file }
+
+ subject { described_class.new([note_diff_file_a, note_diff_file_b]) }
+
+ describe '#load_highlight', :clean_gitlab_redis_shared_state do
+ it 'writes uncached diffs highlight' do
+ file_a_caching_content = diff_note_a.diff_file.highlighted_diff_lines.map(&:to_hash)
+ file_b_caching_content = diff_note_b.diff_file.highlighted_diff_lines.map(&:to_hash)
+
+ expect(Gitlab::DiscussionsDiff::HighlightCache)
+ .to receive(:write_multiple)
+ .with({ note_diff_file_a.id => file_a_caching_content,
+ note_diff_file_b.id => file_b_caching_content })
+ .and_call_original
+
+ subject.load_highlight([note_diff_file_a.id, note_diff_file_b.id])
+ end
+
+ it 'does not write cache for already cached file' do
+ subject.load_highlight([note_diff_file_a.id])
+
+ file_b_caching_content = diff_note_b.diff_file.highlighted_diff_lines.map(&:to_hash)
+
+ expect(Gitlab::DiscussionsDiff::HighlightCache)
+ .to receive(:write_multiple)
+ .with({ note_diff_file_b.id => file_b_caching_content })
+ .and_call_original
+
+ subject.load_highlight([note_diff_file_a.id, note_diff_file_b.id])
+ end
+
+ it 'does not err when given ID does not exist in @collection' do
+ expect { subject.load_highlight([999]) }.not_to raise_error
+ end
+
+ it 'loaded diff files have highlighted lines loaded' do
+ subject.load_highlight([note_diff_file_a.id])
+
+ diff_file = subject.find_by_id(note_diff_file_a.id)
+
+ expect(diff_file.highlight_loaded?).to be(true)
+ end
+
+ it 'not loaded diff files does not have highlighted lines loaded' do
+ subject.load_highlight([note_diff_file_a.id])
+
+ diff_file = subject.find_by_id(note_diff_file_b.id)
+
+ expect(diff_file.highlight_loaded?).to be(false)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/discussions_diff/highlight_cache_spec.rb b/spec/lib/gitlab/discussions_diff/highlight_cache_spec.rb
new file mode 100644
index 00000000000..fe26ebb8796
--- /dev/null
+++ b/spec/lib/gitlab/discussions_diff/highlight_cache_spec.rb
@@ -0,0 +1,102 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::DiscussionsDiff::HighlightCache, :clean_gitlab_redis_cache do
+ describe '#write_multiple' do
+ it 'sets multiple keys serializing content as JSON' do
+ mapping = {
+ 3 => [
+ {
+ text: 'foo',
+ type: 'new',
+ index: 2,
+ old_pos: 10,
+ new_pos: 11,
+ line_code: 'xpto',
+ rich_text: '<blips>blops</blips>'
+ },
+ {
+ text: 'foo',
+ type: 'new',
+ index: 3,
+ old_pos: 11,
+ new_pos: 12,
+ line_code: 'xpto',
+ rich_text: '<blops>blips</blops>'
+ }
+ ]
+ }
+
+ described_class.write_multiple(mapping)
+
+ mapping.each do |key, value|
+ full_key = described_class.cache_key_for(key)
+ found = Gitlab::Redis::Cache.with { |r| r.get(full_key) }
+
+ expect(found).to eq(value.to_json)
+ end
+ end
+ end
+
+ describe '#read_multiple' do
+ it 'reads multiple keys and serializes content into Gitlab::Diff::Line objects' do
+ mapping = {
+ 3 => [
+ {
+ text: 'foo',
+ type: 'new',
+ index: 2,
+ old_pos: 11,
+ new_pos: 12,
+ line_code: 'xpto',
+ rich_text: '<blips>blops</blips>'
+ },
+ {
+ text: 'foo',
+ type: 'new',
+ index: 3,
+ old_pos: 10,
+ new_pos: 11,
+ line_code: 'xpto',
+ rich_text: '<blips>blops</blips>'
+ }
+ ]
+ }
+
+ described_class.write_multiple(mapping)
+
+ found = described_class.read_multiple(mapping.keys)
+
+ expect(found.size).to eq(1)
+ expect(found.first.size).to eq(2)
+ expect(found.first).to all(be_a(Gitlab::Diff::Line))
+ end
+
+ it 'returns nil when cached key is not found' do
+ mapping = {
+ 3 => [
+ {
+ text: 'foo',
+ type: 'new',
+ index: 2,
+ old_pos: 11,
+ new_pos: 12,
+ line_code: 'xpto',
+ rich_text: '<blips>blops</blips>'
+ }
+ ]
+ }
+
+ described_class.write_multiple(mapping)
+
+ found = described_class.read_multiple([2, 3])
+
+ expect(found.size).to eq(2)
+
+ expect(found.first).to eq(nil)
+ expect(found.second.size).to eq(1)
+ expect(found.second).to all(be_a(Gitlab::Diff::Line))
+ end
+ end
+end
diff --git a/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb b/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb
index 1d75e8cb5da..48139c2f9dc 100644
--- a/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb
@@ -11,7 +11,7 @@ describe Gitlab::Email::Handler::CreateIssueHandler do
stub_config_setting(host: 'localhost')
end
- let(:email_raw) { fixture_file('emails/valid_new_issue.eml') }
+ let(:email_raw) { email_fixture('emails/valid_new_issue.eml') }
let(:namespace) { create(:namespace, path: 'gitlabhq') }
let!(:project) { create(:project, :public, namespace: namespace, path: 'gitlabhq') }
@@ -23,21 +23,58 @@ describe Gitlab::Email::Handler::CreateIssueHandler do
)
end
+ context "when email key" do
+ let(:mail) { Mail::Message.new(email_raw) }
+
+ it "matches the new format" do
+ handler = described_class.new(mail, "gitlabhq-gitlabhq-#{project.project_id}-#{user.incoming_email_token}-issue")
+
+ expect(handler.instance_variable_get(:@project_id)).to eq project.project_id
+ expect(handler.instance_variable_get(:@project_slug)).to eq project.full_path_slug
+ expect(handler.instance_variable_get(:@incoming_email_token)).to eq user.incoming_email_token
+ expect(handler.can_handle?).to be_truthy
+ end
+
+ it "matches the legacy format" do
+ handler = described_class.new(mail, "h5bp/html5-boilerplate+#{user.incoming_email_token}")
+
+ expect(handler.instance_variable_get(:@project_path)).to eq 'h5bp/html5-boilerplate'
+ expect(handler.instance_variable_get(:@incoming_email_token)).to eq user.incoming_email_token
+ expect(handler.can_handle?).to be_truthy
+ end
+
+ it "doesn't match either format" do
+ handler = described_class.new(mail, "h5bp-html5-boilerplate+something+invalid")
+
+ expect(handler.can_handle?).to be_falsey
+ end
+ end
+
context "when everything is fine" do
- it "creates a new issue" do
- setup_attachment
+ shared_examples "a new issue" do
+ it "creates a new issue" do
+ setup_attachment
- expect { receiver.execute }.to change { project.issues.count }.by(1)
- issue = project.issues.last
+ expect { receiver.execute }.to change { project.issues.count }.by(1)
+ issue = project.issues.last
+
+ expect(issue.author).to eq(user)
+ expect(issue.title).to eq('New Issue by email')
+ expect(issue.description).to include('reply by email')
+ expect(issue.description).to include(markdown)
+ end
+ end
+
+ it_behaves_like "a new issue"
- expect(issue.author).to eq(user)
- expect(issue.title).to eq('New Issue by email')
- expect(issue.description).to include('reply by email')
- expect(issue.description).to include(markdown)
+ context "creates a new issue with legacy email address" do
+ let(:email_raw) { fixture_file('emails/valid_new_issue_legacy.eml') }
+
+ it_behaves_like "a new issue"
end
context "when the reply is blank" do
- let(:email_raw) { fixture_file("emails/valid_new_issue_empty.eml") }
+ let(:email_raw) { email_fixture("emails/valid_new_issue_empty.eml") }
it "creates a new issue" do
expect { receiver.execute }.to change { project.issues.count }.by(1)
@@ -50,7 +87,7 @@ describe Gitlab::Email::Handler::CreateIssueHandler do
end
context "when there are quotes in email" do
- let(:email_raw) { fixture_file("emails/valid_new_issue_with_quote.eml") }
+ let(:email_raw) { email_fixture("emails/valid_new_issue_with_quote.eml") }
it "creates a new issue" do
expect { receiver.execute }.to change { project.issues.count }.by(1)
@@ -76,7 +113,7 @@ describe Gitlab::Email::Handler::CreateIssueHandler do
end
context "when we can't find the incoming_email_token" do
- let(:email_raw) { fixture_file("emails/wrong_incoming_email_token.eml") }
+ let(:email_raw) { email_fixture("emails/wrong_issue_incoming_email_token.eml") }
it "raises an UserNotFoundError" do
expect { receiver.execute }.to raise_error(Gitlab::Email::UserNotFoundError)
@@ -91,4 +128,8 @@ describe Gitlab::Email::Handler::CreateIssueHandler do
end
end
end
+
+ def email_fixture(path)
+ fixture_file(path).gsub('project_id', project.project_id.to_s)
+ end
end
diff --git a/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb b/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb
index f276f1a8ddf..2fa86b2b46f 100644
--- a/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb
@@ -15,7 +15,7 @@ describe Gitlab::Email::Handler::CreateMergeRequestHandler do
TestEnv.clean_test_path
end
- let(:email_raw) { fixture_file('emails/valid_new_merge_request.eml') }
+ let(:email_raw) { email_fixture('emails/valid_new_merge_request.eml') }
let(:namespace) { create(:namespace, path: 'gitlabhq') }
let!(:project) { create(:project, :public, :repository, namespace: namespace, path: 'gitlabhq') }
@@ -27,6 +27,33 @@ describe Gitlab::Email::Handler::CreateMergeRequestHandler do
)
end
+ context "when email key" do
+ let(:mail) { Mail::Message.new(email_raw) }
+
+ it "matches the new format" do
+ handler = described_class.new(mail, "gitlabhq-gitlabhq-#{project.project_id}-#{user.incoming_email_token}-merge-request")
+
+ expect(handler.instance_variable_get(:@project_id)).to eq project.project_id
+ expect(handler.instance_variable_get(:@project_slug)).to eq project.full_path_slug
+ expect(handler.instance_variable_get(:@incoming_email_token)).to eq user.incoming_email_token
+ expect(handler.can_handle?).to be_truthy
+ end
+
+ it "matches the legacy format" do
+ handler = described_class.new(mail, "h5bp/html5-boilerplate+merge-request+#{user.incoming_email_token}")
+
+ expect(handler.instance_variable_get(:@project_path)).to eq 'h5bp/html5-boilerplate'
+ expect(handler.instance_variable_get(:@incoming_email_token)).to eq user.incoming_email_token
+ expect(handler.can_handle?).to be_truthy
+ end
+
+ it "doesn't match either format" do
+ handler = described_class.new(mail, "h5bp-html5-boilerplate+merge-request")
+
+ expect(handler.can_handle?).to be_falsey
+ end
+ end
+
context "as a non-developer" do
before do
project.add_guest(user)
@@ -43,15 +70,25 @@ describe Gitlab::Email::Handler::CreateMergeRequestHandler do
end
context "when everything is fine" do
- it "creates a new merge request" do
- expect { receiver.execute }.to change { project.merge_requests.count }.by(1)
- merge_request = project.merge_requests.last
-
- expect(merge_request.author).to eq(user)
- expect(merge_request.source_branch).to eq('feature')
- expect(merge_request.title).to eq('Feature added')
- expect(merge_request.description).to eq('Merge request description')
- expect(merge_request.target_branch).to eq(project.default_branch)
+ shared_examples "a new merge request" do
+ it "creates a new merge request" do
+ expect { receiver.execute }.to change { project.merge_requests.count }.by(1)
+ merge_request = project.merge_requests.last
+
+ expect(merge_request.author).to eq(user)
+ expect(merge_request.source_branch).to eq('feature')
+ expect(merge_request.title).to eq('Feature added')
+ expect(merge_request.description).to eq('Merge request description')
+ expect(merge_request.target_branch).to eq(project.default_branch)
+ end
+ end
+
+ it_behaves_like "a new merge request"
+
+ context "creates a new merge request with legacy email address" do
+ let(:email_raw) { fixture_file('emails/valid_new_merge_request_legacy.eml') }
+
+ it_behaves_like "a new merge request"
end
end
@@ -67,7 +104,7 @@ describe Gitlab::Email::Handler::CreateMergeRequestHandler do
end
context "when we can't find the incoming_email_token" do
- let(:email_raw) { fixture_file("emails/wrong_incoming_email_token.eml") }
+ let(:email_raw) { email_fixture("emails/wrong_merge_request_incoming_email_token.eml") }
it "raises an UserNotFoundError" do
expect { receiver.execute }.to raise_error(Gitlab::Email::UserNotFoundError)
@@ -75,7 +112,7 @@ describe Gitlab::Email::Handler::CreateMergeRequestHandler do
end
context "when the subject is blank" do
- let(:email_raw) { fixture_file("emails/valid_new_merge_request_no_subject.eml") }
+ let(:email_raw) { email_fixture("emails/valid_new_merge_request_no_subject.eml") }
it "raises an InvalidMergeRequestError" do
expect { receiver.execute }.to raise_error(Gitlab::Email::InvalidMergeRequestError)
@@ -83,7 +120,7 @@ describe Gitlab::Email::Handler::CreateMergeRequestHandler do
end
context "when the message body is blank" do
- let(:email_raw) { fixture_file("emails/valid_new_merge_request_no_description.eml") }
+ let(:email_raw) { email_fixture("emails/valid_new_merge_request_no_description.eml") }
it "creates a new merge request with description set from the last commit" do
expect { receiver.execute }.to change { project.merge_requests.count }.by(1)
@@ -95,7 +132,7 @@ describe Gitlab::Email::Handler::CreateMergeRequestHandler do
end
context 'when the email contains patch attachments' do
- let(:email_raw) { fixture_file("emails/valid_merge_request_with_patch.eml") }
+ let(:email_raw) { email_fixture("emails/valid_merge_request_with_patch.eml") }
it 'creates the source branch and applies the patches' do
receiver.execute
@@ -120,7 +157,7 @@ describe Gitlab::Email::Handler::CreateMergeRequestHandler do
end
context 'when the patch could not be applied' do
- let(:email_raw) { fixture_file("emails/merge_request_with_conflicting_patch.eml") }
+ let(:email_raw) { email_fixture("emails/merge_request_with_conflicting_patch.eml") }
it 'raises an error' do
expect { receiver.execute }.to raise_error(Gitlab::Email::InvalidAttachment)
@@ -128,7 +165,7 @@ describe Gitlab::Email::Handler::CreateMergeRequestHandler do
end
context 'when specifying the target branch using quick actions' do
- let(:email_raw) { fixture_file('emails/merge_request_with_patch_and_target_branch.eml') }
+ let(:email_raw) { email_fixture('emails/merge_request_with_patch_and_target_branch.eml') }
it 'creates the merge request with the correct target branch' do
receiver.execute
@@ -150,7 +187,7 @@ describe Gitlab::Email::Handler::CreateMergeRequestHandler do
end
describe '#patch_attachments' do
- let(:email_raw) { fixture_file('emails/merge_request_multiple_patches.eml') }
+ let(:email_raw) { email_fixture('emails/merge_request_multiple_patches.eml') }
let(:mail) { Mail::Message.new(email_raw) }
subject(:handler) { described_class.new(mail, mail_key) }
@@ -163,4 +200,8 @@ describe Gitlab::Email::Handler::CreateMergeRequestHandler do
expect(attachments).to eq(expected_filenames)
end
end
+
+ def email_fixture(path)
+ fixture_file(path).gsub('project_id', project.project_id.to_s)
+ end
end
diff --git a/spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb b/spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb
index b8660b133ec..dcddd00df59 100644
--- a/spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb
@@ -10,13 +10,35 @@ describe Gitlab::Email::Handler::UnsubscribeHandler do
stub_config_setting(host: 'localhost')
end
- let(:email_raw) { fixture_file('emails/valid_reply.eml').gsub(mail_key, "#{mail_key}+unsubscribe") }
- let(:project) { create(:project, :public) }
- let(:user) { create(:user) }
- let(:noteable) { create(:issue, project: project) }
+ let(:email_raw) { fixture_file('emails/valid_reply.eml').gsub(mail_key, "#{mail_key}#{Gitlab::IncomingEmail::UNSUBSCRIBE_SUFFIX}") }
+ let(:project) { create(:project, :public) }
+ let(:user) { create(:user) }
+ let(:noteable) { create(:issue, project: project) }
let!(:sent_notification) { SentNotification.record(noteable, user.id, mail_key) }
+ context "when email key" do
+ let(:mail) { Mail::Message.new(email_raw) }
+
+ it "matches the new format" do
+ handler = described_class.new(mail, "#{mail_key}#{Gitlab::IncomingEmail::UNSUBSCRIBE_SUFFIX}")
+
+ expect(handler.can_handle?).to be_truthy
+ end
+
+ it "matches the legacy format" do
+ handler = described_class.new(mail, "#{mail_key}#{Gitlab::IncomingEmail::UNSUBSCRIBE_SUFFIX_LEGACY}")
+
+ expect(handler.can_handle?).to be_truthy
+ end
+
+ it "doesn't match either format" do
+ handler = described_class.new(mail, "+#{mail_key}#{Gitlab::IncomingEmail::UNSUBSCRIBE_SUFFIX}")
+
+ expect(handler.can_handle?).to be_falsey
+ end
+ end
+
context 'when notification concerns a commit' do
let(:commit) { create(:commit, project: project) }
let!(:sent_notification) { SentNotification.record(commit, user.id, mail_key) }
@@ -40,6 +62,14 @@ describe Gitlab::Email::Handler::UnsubscribeHandler do
it 'unsubscribes user from notable' do
expect { receiver.execute }.to change { noteable.subscribed?(user) }.from(true).to(false)
end
+
+ context 'when using old style unsubscribe link' do
+ let(:email_raw) { fixture_file('emails/valid_reply.eml').gsub(mail_key, "#{mail_key}#{Gitlab::IncomingEmail::UNSUBSCRIBE_SUFFIX_LEGACY}") }
+
+ it 'unsubscribes user from notable' do
+ expect { receiver.execute }.to change { noteable.subscribed?(user) }.from(true).to(false)
+ end
+ end
end
context 'when the noteable could not be found' do
diff --git a/spec/lib/gitlab/email/handler_spec.rb b/spec/lib/gitlab/email/handler_spec.rb
index c651765dc0f..d2920b08956 100644
--- a/spec/lib/gitlab/email/handler_spec.rb
+++ b/spec/lib/gitlab/email/handler_spec.rb
@@ -19,7 +19,8 @@ describe Gitlab::Email::Handler do
describe 'regexps are set properly' do
let(:addresses) do
- %W(sent_notification_key#{Gitlab::IncomingEmail::UNSUBSCRIBE_SUFFIX} sent_notification_key path/to/project+merge-request+user_email_token path/to/project+user_email_token)
+ %W(sent_notification_key#{Gitlab::IncomingEmail::UNSUBSCRIBE_SUFFIX} sent_notification_key path-to-project-123-user_email_token-merge-request path-to-project-123-user_email_token-issue) +
+ %W(sent_notification_key#{Gitlab::IncomingEmail::UNSUBSCRIBE_SUFFIX_LEGACY} sent_notification_key path/to/project+merge-request+user_email_token path/to/project+user_email_token)
end
it 'picks each handler at least once' do
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index 80dd3dcc58e..1bcec04d28f 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -59,7 +59,7 @@ describe Gitlab::Git::Blob, :seed_helper do
it { expect(blob.data[0..10]).to eq("*.rbc\n*.sas") }
it { expect(blob.size).to eq(241) }
it { expect(blob.mode).to eq("100644") }
- it { expect(blob).not_to be_binary }
+ it { expect(blob).not_to be_binary_in_repo }
end
context 'file in root with leading slash' do
@@ -92,7 +92,7 @@ describe Gitlab::Git::Blob, :seed_helper do
end
it 'does not mark the blob as binary' do
- expect(blob).not_to be_binary
+ expect(blob).not_to be_binary_in_repo
end
end
@@ -123,7 +123,7 @@ describe Gitlab::Git::Blob, :seed_helper do
.with(hash_including(binary: true))
.and_call_original
- expect(blob).to be_binary
+ expect(blob).to be_binary_in_repo
end
end
end
@@ -196,7 +196,7 @@ describe Gitlab::Git::Blob, :seed_helper do
it { expect(blob.id).to eq('409f37c4f05865e4fb208c771485f211a22c4c2d') }
it { expect(blob.data).to eq('') }
it 'does not mark the blob as binary' do
- expect(blob).not_to be_binary
+ expect(blob).not_to be_binary_in_repo
end
end
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index a417ef77c9e..3e34dd592f2 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -14,7 +14,7 @@ describe Gitlab::GitAccess do
let(:authentication_abilities) { %i[read_project download_code push_code] }
let(:redirected_path) { nil }
let(:auth_result_type) { nil }
- let(:changes) { '_any' }
+ let(:changes) { Gitlab::GitAccess::ANY }
let(:push_access_check) { access.check('git-receive-pack', changes) }
let(:pull_access_check) { access.check('git-upload-pack', changes) }
@@ -437,7 +437,7 @@ describe Gitlab::GitAccess do
let(:project) { nil }
context 'when changes is _any' do
- let(:changes) { '_any' }
+ let(:changes) { Gitlab::GitAccess::ANY }
context 'when authentication abilities include push code' do
let(:authentication_abilities) { [:push_code] }
@@ -483,7 +483,7 @@ describe Gitlab::GitAccess do
end
context 'when project exists' do
- let(:changes) { '_any' }
+ let(:changes) { Gitlab::GitAccess::ANY }
let!(:project) { create(:project) }
it 'does not create a new project' do
@@ -497,7 +497,7 @@ describe Gitlab::GitAccess do
let(:project_path) { "nonexistent" }
let(:project) { nil }
let(:namespace_path) { user.namespace.path }
- let(:changes) { '_any' }
+ let(:changes) { Gitlab::GitAccess::ANY }
it 'does not create a new project' do
expect { access.send(:ensure_project_on_push!, cmd, changes) }.not_to change { Project.count }
@@ -507,7 +507,7 @@ describe Gitlab::GitAccess do
context 'when pull' do
let(:cmd) { 'git-upload-pack' }
- let(:changes) { '_any' }
+ let(:changes) { Gitlab::GitAccess::ANY }
context 'when project does not exist' do
let(:project_path) { "new-project" }
@@ -709,10 +709,22 @@ describe Gitlab::GitAccess do
project.add_developer(user)
end
- it 'checks LFS integrity only for first change' do
- expect_any_instance_of(Gitlab::Checks::LfsIntegrity).to receive(:objects_missing?).exactly(1).times
+ context 'when LFS is not enabled' do
+ it 'does not run LFSIntegrity check' do
+ expect(Gitlab::Checks::LfsIntegrity).not_to receive(:new)
- push_access_check
+ push_access_check
+ end
+ end
+
+ context 'when LFS is enabled' do
+ it 'checks LFS integrity only for first change' do
+ allow(project).to receive(:lfs_enabled?).and_return(true)
+
+ expect_any_instance_of(Gitlab::Checks::LfsIntegrity).to receive(:objects_missing?).exactly(1).times
+
+ push_access_check
+ end
end
end
@@ -724,7 +736,8 @@ describe Gitlab::GitAccess do
end
let(:changes) do
- { push_new_branch: "#{Gitlab::Git::BLANK_SHA} 570e7b2ab refs/heads/wow",
+ { any: Gitlab::GitAccess::ANY,
+ push_new_branch: "#{Gitlab::Git::BLANK_SHA} 570e7b2ab refs/heads/wow",
push_master: '6f6d7e7ed 570e7b2ab refs/heads/master',
push_protected_branch: '6f6d7e7ed 570e7b2ab refs/heads/feature',
push_remove_protected_branch: "570e7b2ab #{Gitlab::Git::BLANK_SHA} "\
@@ -786,6 +799,7 @@ describe Gitlab::GitAccess do
permissions_matrix = {
admin: {
+ any: true,
push_new_branch: true,
push_master: true,
push_protected_branch: true,
@@ -797,6 +811,7 @@ describe Gitlab::GitAccess do
},
maintainer: {
+ any: true,
push_new_branch: true,
push_master: true,
push_protected_branch: true,
@@ -808,6 +823,7 @@ describe Gitlab::GitAccess do
},
developer: {
+ any: true,
push_new_branch: true,
push_master: true,
push_protected_branch: false,
@@ -819,6 +835,7 @@ describe Gitlab::GitAccess do
},
reporter: {
+ any: false,
push_new_branch: false,
push_master: false,
push_protected_branch: false,
@@ -830,6 +847,7 @@ describe Gitlab::GitAccess do
},
guest: {
+ any: false,
push_new_branch: false,
push_master: false,
push_protected_branch: false,
diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb
index 9c6c9fe13bf..6ba65b56618 100644
--- a/spec/lib/gitlab/git_access_wiki_spec.rb
+++ b/spec/lib/gitlab/git_access_wiki_spec.rb
@@ -38,7 +38,7 @@ describe Gitlab::GitAccessWiki do
end
describe '#access_check_download!' do
- subject { access.check('git-upload-pack', '_any') }
+ subject { access.check('git-upload-pack', Gitlab::GitAccess::ANY) }
before do
project.add_developer(user)
diff --git a/spec/lib/gitlab/gitaly_client/blobs_stitcher_spec.rb b/spec/lib/gitlab/gitaly_client/blobs_stitcher_spec.rb
index 9db710e759e..742b2872c40 100644
--- a/spec/lib/gitlab/gitaly_client/blobs_stitcher_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/blobs_stitcher_spec.rb
@@ -21,7 +21,7 @@ describe Gitlab::GitalyClient::BlobsStitcher do
expect(blobs[0].size).to eq(1642)
expect(blobs[0].commit_id).to eq('f00ba7')
expect(blobs[0].data).to eq("first-line\nsecond-line")
- expect(blobs[0].binary?).to be false
+ expect(blobs[0].binary_in_repo?).to be false
expect(blobs[1].id).to eq('abcdef2')
expect(blobs[1].mode).to eq('100644')
@@ -30,7 +30,7 @@ describe Gitlab::GitalyClient::BlobsStitcher do
expect(blobs[1].size).to eq(2461)
expect(blobs[1].commit_id).to eq('f00ba8')
expect(blobs[1].data).to eq("GIF87a\x90\x01".b)
- expect(blobs[1].binary?).to be true
+ expect(blobs[1].binary_in_repo?).to be true
end
end
end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index c8c74883640..d3cae137c3c 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -66,6 +66,9 @@ snippets:
releases:
- author
- project
+- links
+links:
+- release
project_members:
- created_by
- user
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 24b1f2d995b..2422868474e 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -120,6 +120,13 @@ Release:
- project_id
- created_at
- updated_at
+Releases::Link:
+- id
+- release_id
+- url
+- name
+- created_at
+- updated_at
ProjectMember:
- id
- access_level
diff --git a/spec/lib/gitlab/incoming_email_spec.rb b/spec/lib/gitlab/incoming_email_spec.rb
index 4c0c3fcbcc7..2db62ab983a 100644
--- a/spec/lib/gitlab/incoming_email_spec.rb
+++ b/spec/lib/gitlab/incoming_email_spec.rb
@@ -61,7 +61,7 @@ describe Gitlab::IncomingEmail do
end
it 'returns the address with interpolated reply key and unsubscribe suffix' do
- expect(described_class.unsubscribe_address('key')).to eq('replies+key+unsubscribe@example.com')
+ expect(described_class.unsubscribe_address('key')).to eq("replies+key#{Gitlab::IncomingEmail::UNSUBSCRIBE_SUFFIX}@example.com")
end
end
diff --git a/spec/lib/gitlab/legacy_github_import/importer_spec.rb b/spec/lib/gitlab/legacy_github_import/importer_spec.rb
index d2df21d7bb5..6bc3792eb22 100644
--- a/spec/lib/gitlab/legacy_github_import/importer_spec.rb
+++ b/spec/lib/gitlab/legacy_github_import/importer_spec.rb
@@ -138,7 +138,7 @@ describe Gitlab::LegacyGithubImport::Importer do
let(:release2) do
double(
- tag_name: 'v2.0.0',
+ tag_name: 'v1.1.0',
name: 'Second release',
body: nil,
draft: false,
diff --git a/spec/lib/gitlab/middleware/go_spec.rb b/spec/lib/gitlab/middleware/go_spec.rb
index 7a3a9ab875b..f52095bf633 100644
--- a/spec/lib/gitlab/middleware/go_spec.rb
+++ b/spec/lib/gitlab/middleware/go_spec.rb
@@ -96,43 +96,36 @@ describe Gitlab::Middleware::Go do
it_behaves_like 'unauthorized'
end
- end
-
- context 'using warden' do
- before do
- env['warden'] = double(authenticate: current_user)
- end
- context 'when active' do
- it_behaves_like 'authenticated'
- end
-
- context 'when blocked' do
+ context 'with user is blocked' do
before do
- current_user.block!
+ current_user.block
end
it_behaves_like 'unauthorized'
end
end
- context 'using a personal access token' do
- let(:personal_access_token) { create(:personal_access_token, user: current_user) }
-
- before do
- env['HTTP_PRIVATE_TOKEN'] = personal_access_token.token
- end
-
- context 'with api scope' do
- it_behaves_like 'authenticated'
- end
+ context 'using basic auth' do
+ context 'using a personal access token' do
+ let(:personal_access_token) { create(:personal_access_token, user: current_user) }
- context 'with read_user scope' do
before do
- personal_access_token.update_attribute(:scopes, [:read_user])
+ env['REMOTE_ADDR'] = "192.168.0.1"
+ env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(current_user.username, personal_access_token.token)
end
- it_behaves_like 'unauthorized'
+ context 'with api scope' do
+ it_behaves_like 'authenticated'
+ end
+
+ context 'with read_user scope' do
+ before do
+ personal_access_token.update_attribute(:scopes, [:read_user])
+ end
+
+ it_behaves_like 'unauthorized'
+ end
end
end
end
diff --git a/spec/lib/gitlab/prometheus/metric_group_spec.rb b/spec/lib/gitlab/prometheus/metric_group_spec.rb
index e7d16e73663..5cc6827488b 100644
--- a/spec/lib/gitlab/prometheus/metric_group_spec.rb
+++ b/spec/lib/gitlab/prometheus/metric_group_spec.rb
@@ -21,6 +21,13 @@ describe Gitlab::Prometheus::MetricGroup do
common_metric_group_a.id, common_metric_group_b_q1.id,
common_metric_group_b_q2.id)
end
+
+ it 'orders by priority' do
+ priorities = subject.map(&:priority)
+ names = subject.map(&:name)
+ expect(priorities).to eq([10, 5])
+ expect(names).to eq(['Response metrics (AWS ELB)', 'System metrics (Kubernetes)'])
+ end
end
describe '.for_project' do
diff --git a/spec/lib/json_web_token/rsa_token_spec.rb b/spec/lib/json_web_token/rsa_token_spec.rb
index d6edc964844..a3c54651e80 100644
--- a/spec/lib/json_web_token/rsa_token_spec.rb
+++ b/spec/lib/json_web_token/rsa_token_spec.rb
@@ -25,7 +25,7 @@ describe JSONWebToken::RSAToken do
rsa_token['key'] = 'value'
end
- subject { JWT.decode(rsa_encoded, rsa_key) }
+ subject { JWT.decode(rsa_encoded, rsa_key, true, { algorithm: 'RS256' }) }
it { expect {subject}.not_to raise_error }
it { expect(subject.first).to include('key' => 'value') }
@@ -39,7 +39,7 @@ describe JSONWebToken::RSAToken do
context 'for invalid key to raise an exception' do
let(:new_key) { OpenSSL::PKey::RSA.generate(512) }
- subject { JWT.decode(rsa_encoded, new_key) }
+ subject { JWT.decode(rsa_encoded, new_key, true, { algorithm: 'RS256' }) }
it { expect {subject}.to raise_error(JWT::DecodeError) }
end
diff --git a/spec/models/appearance_spec.rb b/spec/models/appearance_spec.rb
index 35415030154..ec2e7d672f0 100644
--- a/spec/models/appearance_spec.rb
+++ b/spec/models/appearance_spec.rb
@@ -26,4 +26,34 @@ describe Appearance do
let(:uploader_class) { AttachmentUploader }
end
end
+
+ shared_examples 'logo paths' do |logo_type|
+ let(:appearance) { create(:appearance, "with_#{logo_type}".to_sym) }
+ let(:filename) { 'dk.png' }
+ let(:expected_path) { "/uploads/-/system/appearance/#{logo_type}/#{appearance.id}/#{filename}" }
+
+ it 'returns nil when there is no upload' do
+ expect(subject.send("#{logo_type}_path")).to be_nil
+ end
+
+ it 'returns a local path using the system route' do
+ expect(appearance.send("#{logo_type}_path")).to eq(expected_path)
+ end
+
+ describe 'with asset host configured' do
+ let(:asset_host) { 'https://gitlab-assets.example.com' }
+
+ before do
+ allow(ActionController::Base).to receive(:asset_host) { asset_host }
+ end
+
+ it 'returns a full URL with the system path' do
+ expect(appearance.send("#{logo_type}_path")).to eq("#{asset_host}#{expected_path}")
+ end
+ end
+ end
+
+ %i(logo header_logo favicon).each do |logo_type|
+ it_behaves_like 'logo paths', logo_type
+ end
end
diff --git a/spec/models/blob_spec.rb b/spec/models/blob_spec.rb
index e8c03b587e2..05cf242e84d 100644
--- a/spec/models/blob_spec.rb
+++ b/spec/models/blob_spec.rb
@@ -122,14 +122,14 @@ describe Blob do
end
end
- describe '#raw_binary?' do
+ describe '#binary?' do
context 'if the blob is stored externally' do
context 'if the extension has a rich viewer' do
context 'if the viewer is binary' do
it 'returns true' do
blob = fake_blob(path: 'file.pdf', lfs: true)
- expect(blob.raw_binary?).to be_truthy
+ expect(blob.binary?).to be_truthy
end
end
@@ -137,7 +137,7 @@ describe Blob do
it 'return false' do
blob = fake_blob(path: 'file.md', lfs: true)
- expect(blob.raw_binary?).to be_falsey
+ expect(blob.binary?).to be_falsey
end
end
end
@@ -148,7 +148,7 @@ describe Blob do
it 'returns false' do
blob = fake_blob(path: 'file.txt', lfs: true)
- expect(blob.raw_binary?).to be_falsey
+ expect(blob.binary?).to be_falsey
end
end
@@ -156,7 +156,7 @@ describe Blob do
it 'returns false' do
blob = fake_blob(path: 'file.ics', lfs: true)
- expect(blob.raw_binary?).to be_falsey
+ expect(blob.binary?).to be_falsey
end
end
end
@@ -166,7 +166,7 @@ describe Blob do
it 'returns false' do
blob = fake_blob(path: 'file.rb', lfs: true)
- expect(blob.raw_binary?).to be_falsey
+ expect(blob.binary?).to be_falsey
end
end
@@ -174,7 +174,7 @@ describe Blob do
it 'returns true' do
blob = fake_blob(path: 'file.exe', lfs: true)
- expect(blob.raw_binary?).to be_truthy
+ expect(blob.binary?).to be_truthy
end
end
end
@@ -184,7 +184,7 @@ describe Blob do
it 'returns false' do
blob = fake_blob(path: 'file.ini', lfs: true)
- expect(blob.raw_binary?).to be_falsey
+ expect(blob.binary?).to be_falsey
end
end
@@ -192,7 +192,7 @@ describe Blob do
it 'returns true' do
blob = fake_blob(path: 'file.wtf', lfs: true)
- expect(blob.raw_binary?).to be_truthy
+ expect(blob.binary?).to be_truthy
end
end
end
@@ -204,7 +204,7 @@ describe Blob do
it 'returns true' do
blob = fake_blob(path: 'file.pdf', binary: true)
- expect(blob.raw_binary?).to be_truthy
+ expect(blob.binary?).to be_truthy
end
end
@@ -212,7 +212,7 @@ describe Blob do
it 'return false' do
blob = fake_blob(path: 'file.md')
- expect(blob.raw_binary?).to be_falsey
+ expect(blob.binary?).to be_falsey
end
end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index fe7f5f8e1e3..28b1a1e37e5 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -2132,6 +2132,7 @@ describe Ci::Build do
{ key: 'CI_PROJECT_NAMESPACE', value: project.namespace.full_path, public: true },
{ key: 'CI_PROJECT_URL', value: project.web_url, public: true },
{ key: 'CI_PROJECT_VISIBILITY', value: 'private', public: true },
+ { key: 'CI_API_V4_URL', value: 'http://localhost/api/v4', public: true },
{ key: 'CI_PIPELINE_IID', value: pipeline.iid.to_s, public: true },
{ key: 'CI_CONFIG_PATH', value: pipeline.ci_yaml_file_path, public: true },
{ key: 'CI_PIPELINE_SOURCE', value: pipeline.source, public: true },
@@ -2386,6 +2387,8 @@ describe Ci::Build do
end
context 'when protected variable is defined' do
+ let(:ref) { Gitlab::Git::BRANCH_REF_PREFIX + build.ref }
+
let(:protected_variable) do
{ key: 'PROTECTED_KEY', value: 'protected_value', public: false }
end
@@ -2398,7 +2401,7 @@ describe Ci::Build do
context 'when the branch is protected' do
before do
- allow(build.project).to receive(:protected_for?).with(build.ref).and_return(true)
+ allow(build.project).to receive(:protected_for?).with(ref).and_return(true)
end
it { is_expected.to include(protected_variable) }
@@ -2406,7 +2409,7 @@ describe Ci::Build do
context 'when the tag is protected' do
before do
- allow(build.project).to receive(:protected_for?).with(build.ref).and_return(true)
+ allow(build.project).to receive(:protected_for?).with(ref).and_return(true)
end
it { is_expected.to include(protected_variable) }
@@ -2431,6 +2434,8 @@ describe Ci::Build do
end
context 'when group protected variable is defined' do
+ let(:ref) { Gitlab::Git::BRANCH_REF_PREFIX + build.ref }
+
let(:protected_variable) do
{ key: 'PROTECTED_KEY', value: 'protected_value', public: false }
end
@@ -2443,7 +2448,7 @@ describe Ci::Build do
context 'when the branch is protected' do
before do
- allow(build.project).to receive(:protected_for?).with(build.ref).and_return(true)
+ allow(build.project).to receive(:protected_for?).with(ref).and_return(true)
end
it { is_expected.to include(protected_variable) }
@@ -2451,7 +2456,7 @@ describe Ci::Build do
context 'when the tag is protected' do
before do
- allow(build.project).to receive(:protected_for?).with(build.ref).and_return(true)
+ allow(build.project).to receive(:protected_for?).with(ref).and_return(true)
end
it { is_expected.to include(protected_variable) }
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index b67c6a4cffa..17f33785fda 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -397,6 +397,10 @@ describe Ci::Pipeline, :mailer do
end
describe '#protected_ref?' do
+ before do
+ pipeline.project = create(:project, :repository)
+ end
+
it 'delegates method to project' do
expect(pipeline).not_to be_protected_ref
end
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index ad79f8d4ce0..eb2daed7f32 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -817,4 +817,13 @@ describe Ci::Runner do
expect(runners).to eq([runner2, runner1])
end
end
+
+ describe '#uncached_contacted_at' do
+ let(:contacted_at_stored) { 1.hour.ago.change(usec: 0) }
+ let(:runner) { create(:ci_runner, contacted_at: contacted_at_stored) }
+
+ subject { runner.uncached_contacted_at }
+
+ it { is_expected.to eq(contacted_at_stored) }
+ end
end
diff --git a/spec/models/clusters/applications/cert_manager_spec.rb b/spec/models/clusters/applications/cert_manager_spec.rb
index 170c6001eaf..8e14abe098d 100644
--- a/spec/models/clusters/applications/cert_manager_spec.rb
+++ b/spec/models/clusters/applications/cert_manager_spec.rb
@@ -14,7 +14,7 @@ describe Clusters::Applications::CertManager do
let(:application) { create(:clusters_applications_cert_managers, :scheduled, version: 'v0.4.0') }
it 'updates the application version' do
- expect(application.reload.version).to eq('v0.5.0')
+ expect(application.reload.version).to eq('v0.5.2')
end
end
end
@@ -28,8 +28,8 @@ describe Clusters::Applications::CertManager do
it 'should be initialized with cert_manager arguments' do
expect(subject.name).to eq('certmanager')
expect(subject.chart).to eq('stable/cert-manager')
- expect(subject.version).to eq('v0.5.0')
- expect(subject).not_to be_rbac
+ expect(subject.version).to eq('v0.5.2')
+ expect(subject).to be_rbac
expect(subject.files).to eq(cert_manager.files.merge(cluster_issuer_file))
expect(subject.postinstall).to eq(['/usr/bin/kubectl create -f /data/helm/certmanager/config/cluster_issuer.yaml'])
end
@@ -45,19 +45,19 @@ describe Clusters::Applications::CertManager do
end
end
- context 'on a rbac enabled cluster' do
+ context 'on a non rbac enabled cluster' do
before do
- cert_manager.cluster.platform_kubernetes.rbac!
+ cert_manager.cluster.platform_kubernetes.abac!
end
- it { is_expected.to be_rbac }
+ it { is_expected.not_to be_rbac }
end
context 'application failed to install previously' do
let(:cert_manager) { create(:clusters_applications_cert_managers, :errored, version: '0.0.1') }
it 'should be initialized with the locked version' do
- expect(subject.version).to eq('v0.5.0')
+ expect(subject.version).to eq('v0.5.2')
end
end
end
diff --git a/spec/models/clusters/applications/helm_spec.rb b/spec/models/clusters/applications/helm_spec.rb
index 2c37cd20ecc..64f6d9c8bb4 100644
--- a/spec/models/clusters/applications/helm_spec.rb
+++ b/spec/models/clusters/applications/helm_spec.rb
@@ -49,16 +49,16 @@ describe Clusters::Applications::Helm do
end
describe 'rbac' do
- context 'non rbac cluster' do
- it { expect(subject).not_to be_rbac }
+ context 'rbac cluster' do
+ it { expect(subject).to be_rbac }
end
- context 'rbac cluster' do
+ context 'non rbac cluster' do
before do
- helm.cluster.platform_kubernetes.rbac!
+ helm.cluster.platform_kubernetes.abac!
end
- it { expect(subject).to be_rbac }
+ it { expect(subject).not_to be_rbac }
end
end
end
diff --git a/spec/models/clusters/applications/ingress_spec.rb b/spec/models/clusters/applications/ingress_spec.rb
index cd28f1fe9c6..de313a8ca36 100644
--- a/spec/models/clusters/applications/ingress_spec.rb
+++ b/spec/models/clusters/applications/ingress_spec.rb
@@ -91,16 +91,16 @@ describe Clusters::Applications::Ingress do
expect(subject.name).to eq('ingress')
expect(subject.chart).to eq('stable/nginx-ingress')
expect(subject.version).to eq('0.23.0')
- expect(subject).not_to be_rbac
+ expect(subject).to be_rbac
expect(subject.files).to eq(ingress.files)
end
- context 'on a rbac enabled cluster' do
+ context 'on a non rbac enabled cluster' do
before do
- ingress.cluster.platform_kubernetes.rbac!
+ ingress.cluster.platform_kubernetes.abac!
end
- it { is_expected.to be_rbac }
+ it { is_expected.not_to be_rbac }
end
context 'application failed to install previously' do
diff --git a/spec/models/clusters/applications/jupyter_spec.rb b/spec/models/clusters/applications/jupyter_spec.rb
index a40edbf267b..391e5425384 100644
--- a/spec/models/clusters/applications/jupyter_spec.rb
+++ b/spec/models/clusters/applications/jupyter_spec.rb
@@ -52,17 +52,17 @@ describe Clusters::Applications::Jupyter do
expect(subject.name).to eq('jupyter')
expect(subject.chart).to eq('jupyter/jupyterhub')
expect(subject.version).to eq('v0.6')
- expect(subject).not_to be_rbac
+ expect(subject).to be_rbac
expect(subject.repository).to eq('https://jupyterhub.github.io/helm-chart/')
expect(subject.files).to eq(jupyter.files)
end
- context 'on a rbac enabled cluster' do
+ context 'on a non rbac enabled cluster' do
before do
- jupyter.cluster.platform_kubernetes.rbac!
+ jupyter.cluster.platform_kubernetes.abac!
end
- it { is_expected.to be_rbac }
+ it { is_expected.not_to be_rbac }
end
context 'application failed to install previously' do
diff --git a/spec/models/clusters/applications/knative_spec.rb b/spec/models/clusters/applications/knative_spec.rb
index 809880f5969..8fc755d2a26 100644
--- a/spec/models/clusters/applications/knative_spec.rb
+++ b/spec/models/clusters/applications/knative_spec.rb
@@ -108,6 +108,23 @@ describe Clusters::Applications::Knative do
expect(subject.version).to eq('0.2.2')
expect(subject.files).to eq(knative.files)
end
+
+ it 'should not install metrics for prometheus' do
+ expect(subject.postinstall).to be_nil
+ end
+
+ context 'with prometheus installed' do
+ let(:prometheus) { create(:clusters_applications_prometheus, :installed) }
+ let(:knative) { create(:clusters_applications_knative, cluster: prometheus.cluster) }
+
+ subject { knative.install_command }
+
+ it 'should install metrics' do
+ expect(subject.postinstall).not_to be_nil
+ expect(subject.postinstall.length).to be(1)
+ expect(subject.postinstall[0]).to eql("kubectl apply -f #{Clusters::Applications::Knative::METRICS_CONFIG}")
+ end
+ end
end
describe '#files' do
diff --git a/spec/models/clusters/applications/prometheus_spec.rb b/spec/models/clusters/applications/prometheus_spec.rb
index 893ed3e3f64..de6b844023a 100644
--- a/spec/models/clusters/applications/prometheus_spec.rb
+++ b/spec/models/clusters/applications/prometheus_spec.rb
@@ -161,16 +161,16 @@ describe Clusters::Applications::Prometheus do
expect(subject.name).to eq('prometheus')
expect(subject.chart).to eq('stable/prometheus')
expect(subject.version).to eq('6.7.3')
- expect(subject).not_to be_rbac
+ expect(subject).to be_rbac
expect(subject.files).to eq(prometheus.files)
end
- context 'on a rbac enabled cluster' do
+ context 'on a non rbac enabled cluster' do
before do
- prometheus.cluster.platform_kubernetes.rbac!
+ prometheus.cluster.platform_kubernetes.abac!
end
- it { is_expected.to be_rbac }
+ it { is_expected.not_to be_rbac }
end
context 'application failed to install previously' do
@@ -180,6 +180,21 @@ describe Clusters::Applications::Prometheus do
expect(subject.version).to eq('6.7.3')
end
end
+
+ it 'should not install knative metrics' do
+ expect(subject.postinstall).to be_nil
+ end
+
+ context 'with knative installed' do
+ let(:knative) { create(:clusters_applications_knative, :installed ) }
+ let(:prometheus) { create(:clusters_applications_prometheus, cluster: knative.cluster) }
+
+ subject { prometheus.install_command }
+
+ it 'should install knative metrics' do
+ expect(subject.postinstall).to include("kubectl apply -f #{Clusters::Applications::Knative::METRICS_CONFIG}")
+ end
+ end
end
describe '#files' do
diff --git a/spec/models/clusters/applications/runner_spec.rb b/spec/models/clusters/applications/runner_spec.rb
index 47daa79873e..3d0735c6d0b 100644
--- a/spec/models/clusters/applications/runner_spec.rb
+++ b/spec/models/clusters/applications/runner_spec.rb
@@ -18,7 +18,7 @@ describe Clusters::Applications::Runner do
let(:application) { create(:clusters_applications_runner, :scheduled, version: '0.1.30') }
it 'updates the application version' do
- expect(application.reload.version).to eq('0.1.39')
+ expect(application.reload.version).to eq('0.1.43')
end
end
end
@@ -46,25 +46,25 @@ describe Clusters::Applications::Runner do
it 'should be initialized with 4 arguments' do
expect(subject.name).to eq('runner')
expect(subject.chart).to eq('runner/gitlab-runner')
- expect(subject.version).to eq('0.1.39')
- expect(subject).not_to be_rbac
+ expect(subject.version).to eq('0.1.43')
+ expect(subject).to be_rbac
expect(subject.repository).to eq('https://charts.gitlab.io')
expect(subject.files).to eq(gitlab_runner.files)
end
- context 'on a rbac enabled cluster' do
+ context 'on a non rbac enabled cluster' do
before do
- gitlab_runner.cluster.platform_kubernetes.rbac!
+ gitlab_runner.cluster.platform_kubernetes.abac!
end
- it { is_expected.to be_rbac }
+ it { is_expected.not_to be_rbac }
end
context 'application failed to install previously' do
let(:gitlab_runner) { create(:clusters_applications_runner, :errored, runner: ci_runner, version: '0.1.13') }
it 'should be initialized with the locked version' do
- expect(subject.version).to eq('0.1.39')
+ expect(subject.version).to eq('0.1.43')
end
end
end
diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb
index 840f74c9890..f447e64b029 100644
--- a/spec/models/clusters/cluster_spec.rb
+++ b/spec/models/clusters/cluster_spec.rb
@@ -29,6 +29,7 @@ describe Clusters::Cluster do
it { is_expected.to delegate_method(:available?).to(:application_helm).with_prefix }
it { is_expected.to delegate_method(:available?).to(:application_ingress).with_prefix }
it { is_expected.to delegate_method(:available?).to(:application_prometheus).with_prefix }
+ it { is_expected.to delegate_method(:available?).to(:application_knative).with_prefix }
it { is_expected.to respond_to :project }
diff --git a/spec/models/clusters/platforms/kubernetes_spec.rb b/spec/models/clusters/platforms/kubernetes_spec.rb
index f3af9d59786..6c8a223092e 100644
--- a/spec/models/clusters/platforms/kubernetes_spec.rb
+++ b/spec/models/clusters/platforms/kubernetes_spec.rb
@@ -154,19 +154,11 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
end
describe '#rbac?' do
- subject { kubernetes.rbac? }
-
let(:kubernetes) { build(:cluster_platform_kubernetes, :configured) }
- context 'when authorization type is rbac' do
- let(:kubernetes) { build(:cluster_platform_kubernetes, :rbac_enabled, :configured) }
-
- it { is_expected.to be_truthy }
- end
+ subject { kubernetes.rbac? }
- context 'when authorization type is nil' do
- it { is_expected.to be_falsey }
- end
+ it { is_expected.to be_truthy }
end
describe '#actual_namespace' do
@@ -325,12 +317,13 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
context 'with valid pods' do
let(:pod) { kube_pod(app: environment.slug) }
+ let(:pod_with_no_terminal) { kube_pod(app: environment.slug, status: "Pending") }
let(:terminals) { kube_terminals(service, pod) }
before do
stub_reactive_cache(
service,
- pods: [pod, pod, kube_pod(app: "should-be-filtered-out")]
+ pods: [pod, pod, pod_with_no_terminal, kube_pod(app: "should-be-filtered-out")]
)
end
diff --git a/spec/models/clusters/providers/gcp_spec.rb b/spec/models/clusters/providers/gcp_spec.rb
index d134608b538..5012e6f15c6 100644
--- a/spec/models/clusters/providers/gcp_spec.rb
+++ b/spec/models/clusters/providers/gcp_spec.rb
@@ -79,17 +79,7 @@ describe Clusters::Providers::Gcp do
subject { gcp }
- it 'should default to true' do
- is_expected.to be_legacy_abac
- end
-
- context 'legacy_abac is set to false' do
- let(:gcp) { build(:cluster_provider_gcp, legacy_abac: false) }
-
- it 'is false' do
- is_expected.not_to be_legacy_abac
- end
- end
+ it { is_expected.not_to be_legacy_abac }
end
describe '#state_machine' do
diff --git a/spec/models/concerns/cacheable_attributes_spec.rb b/spec/models/concerns/cacheable_attributes_spec.rb
index 827fbc9d7d5..689e7d3058f 100644
--- a/spec/models/concerns/cacheable_attributes_spec.rb
+++ b/spec/models/concerns/cacheable_attributes_spec.rb
@@ -20,6 +20,10 @@ describe CacheableAttributes do
@_last ||= new('foo' => 'a', 'bar' => 'b')
end
+ def self.column_names
+ %w[foo bar baz]
+ end
+
attr_accessor :attributes
def initialize(attrs = {}, *)
@@ -75,13 +79,13 @@ describe CacheableAttributes do
context 'without any attributes given' do
it 'intializes a new object with the defaults' do
- expect(minimal_test_class.build_from_defaults.attributes).to eq(minimal_test_class.defaults)
+ expect(minimal_test_class.build_from_defaults.attributes).to eq(minimal_test_class.defaults.stringify_keys)
end
end
context 'with attributes given' do
it 'intializes a new object with the given attributes merged into the defaults' do
- expect(minimal_test_class.build_from_defaults(foo: 'd').attributes[:foo]).to eq('d')
+ expect(minimal_test_class.build_from_defaults(foo: 'd').attributes['foo']).to eq('d')
end
end
diff --git a/spec/models/concerns/has_ref_spec.rb b/spec/models/concerns/has_ref_spec.rb
new file mode 100644
index 00000000000..8aed72d77a4
--- /dev/null
+++ b/spec/models/concerns/has_ref_spec.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe HasRef do
+ describe '#branch?' do
+ let(:build) { create(:ci_build) }
+
+ subject { build.branch? }
+
+ context 'is not a tag' do
+ before do
+ build.tag = false
+ end
+
+ it 'return true when tag is set to false' do
+ is_expected.to be_truthy
+ end
+ end
+
+ context 'is not a tag' do
+ before do
+ build.tag = true
+ end
+
+ it 'return false when tag is set to true' do
+ is_expected.to be_falsey
+ end
+ end
+ end
+
+ describe '#git_ref' do
+ subject { build.git_ref }
+
+ context 'when tag is true' do
+ let(:build) { create(:ci_build, tag: true) }
+
+ it 'returns a tag ref' do
+ is_expected.to start_with(Gitlab::Git::TAG_REF_PREFIX)
+ end
+ end
+
+ context 'when tag is false' do
+ let(:build) { create(:ci_build, tag: false) }
+
+ it 'returns a branch ref' do
+ is_expected.to start_with(Gitlab::Git::BRANCH_REF_PREFIX)
+ end
+ end
+
+ context 'when tag is nil' do
+ let(:build) { create(:ci_build, tag: nil) }
+
+ it 'returns a branch ref' do
+ is_expected.to start_with(Gitlab::Git::BRANCH_REF_PREFIX)
+ end
+ end
+ end
+end
diff --git a/spec/models/diff_viewer/base_spec.rb b/spec/models/diff_viewer/base_spec.rb
index c90b32c5d77..f4efe5a7b3a 100644
--- a/spec/models/diff_viewer/base_spec.rb
+++ b/spec/models/diff_viewer/base_spec.rb
@@ -58,7 +58,7 @@ describe DiffViewer::Base do
context 'when the binaryness does not match' do
before do
- allow_any_instance_of(Blob).to receive(:binary?).and_return(true)
+ allow_any_instance_of(Blob).to receive(:binary_in_repo?).and_return(true)
end
it 'returns false' do
@@ -141,4 +141,25 @@ describe DiffViewer::Base do
end
end
end
+
+ describe '#render_error_message' do
+ it 'returns nothing when no render_error' do
+ expect(viewer.render_error).to be_nil
+ expect(viewer.render_error_message).to be_nil
+ end
+
+ context 'when render_error error' do
+ before do
+ allow(viewer).to receive(:render_error).and_return(:too_large)
+ end
+
+ it 'returns an error message' do
+ expect(viewer.render_error_message).to include('it is too large')
+ end
+
+ it 'includes a "view the blob" link' do
+ expect(viewer.render_error_message).to include('view the blob')
+ end
+ end
+ end
end
diff --git a/spec/models/diff_viewer/server_side_spec.rb b/spec/models/diff_viewer/server_side_spec.rb
index 98a8f6d4cc9..86b14b6ebf3 100644
--- a/spec/models/diff_viewer/server_side_spec.rb
+++ b/spec/models/diff_viewer/server_side_spec.rb
@@ -32,4 +32,24 @@ describe DiffViewer::ServerSide do
end
end
end
+
+ describe '#render_error_reason' do
+ context 'when the diff file is stored externally' do
+ before do
+ allow(diff_file).to receive(:stored_externally?).and_return(true)
+ end
+
+ it 'returns error message if stored in LFS' do
+ allow(diff_file).to receive(:external_storage).and_return(:lfs)
+
+ expect(subject.render_error_message).to include('it is stored in LFS')
+ end
+
+ it 'returns error message if stored externally' do
+ allow(diff_file).to receive(:external_storage).and_return(:foo)
+
+ expect(subject.render_error_message).to include('it is stored externally')
+ end
+ end
+ end
end
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index 81748681528..a64720f1876 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -243,6 +243,20 @@ describe Event do
expect(event.visible_to_user?(admin)).to eq true
end
end
+
+ context 'private project' do
+ let(:project) { create(:project, :private) }
+ let(:target) { note_on_issue }
+
+ it do
+ expect(event.visible_to_user?(non_member)).to eq false
+ expect(event.visible_to_user?(author)).to eq false
+ expect(event.visible_to_user?(assignee)).to eq false
+ expect(event.visible_to_user?(member)).to eq true
+ expect(event.visible_to_user?(guest)).to eq true
+ expect(event.visible_to_user?(admin)).to eq true
+ end
+ end
end
context 'merge request diff note event' do
@@ -265,8 +279,8 @@ describe Event do
it do
expect(event.visible_to_user?(non_member)).to eq false
- expect(event.visible_to_user?(author)).to eq true
- expect(event.visible_to_user?(assignee)).to eq true
+ expect(event.visible_to_user?(author)).to eq false
+ expect(event.visible_to_user?(assignee)).to eq false
expect(event.visible_to_user?(member)).to eq true
expect(event.visible_to_user?(guest)).to eq false
expect(event.visible_to_user?(admin)).to eq true
diff --git a/spec/models/global_milestone_spec.rb b/spec/models/global_milestone_spec.rb
index b6355455c1d..62699df5611 100644
--- a/spec/models/global_milestone_spec.rb
+++ b/spec/models/global_milestone_spec.rb
@@ -65,56 +65,103 @@ describe GlobalMilestone do
)
end
- before do
- projects = [
+ let!(:projects) do
+ [
project1,
project2,
project3
]
-
- @global_milestones = described_class.build_collection(projects, {})
end
- it 'has all project milestones' do
- expect(@global_milestones.count).to eq(2)
+ let!(:global_milestones) { described_class.build_collection(projects, {}) }
+
+ context 'when building a collection of milestones' do
+ it 'has all project milestones' do
+ expect(global_milestones.count).to eq(6)
+ end
+
+ it 'has all project milestones titles' do
+ expect(global_milestones.map(&:title)).to match_array(['Milestone v1.2', 'Milestone v1.2', 'Milestone v1.2', 'VD-123', 'VD-123', 'VD-123'])
+ end
+
+ it 'has all project milestones' do
+ expect(global_milestones.size).to eq(6)
+ end
+
+ it 'sorts collection by due date' do
+ expect(global_milestones.map(&:due_date)).to eq [milestone1_due_date, milestone1_due_date, milestone1_due_date, nil, nil, nil]
+ end
end
- it 'has all project milestones titles' do
- expect(@global_milestones.map(&:title)).to match_array(['Milestone v1.2', 'VD-123'])
+ context 'when adding new milestones' do
+ it 'does not add more queries' do
+ control_count = ActiveRecord::QueryRecorder.new do
+ described_class.build_collection(projects, {})
+ end.count
+
+ create_list(:milestone, 3, project: project3)
+
+ expect do
+ described_class.build_collection(projects, {})
+ end.not_to exceed_all_query_limit(control_count)
+ end
end
+ end
+
+ describe '.states_count' do
+ context 'when the projects have milestones' do
+ before do
+ create(:closed_milestone, title: 'Active Group Milestone', project: project3)
+ create(:active_milestone, title: 'Active Group Milestone', project: project1)
+ create(:active_milestone, title: 'Active Group Milestone', project: project2)
+ create(:closed_milestone, title: 'Closed Group Milestone', project: project1)
+ create(:closed_milestone, title: 'Closed Group Milestone', project: project2)
+ create(:closed_milestone, title: 'Closed Group Milestone', project: project3)
+ create(:closed_milestone, title: 'Closed Group Milestone 4', group: group)
+ end
+
+ it 'returns the quantity of global milestones and group milestones in each possible state' do
+ expected_count = { opened: 2, closed: 5, all: 7 }
- it 'has all project milestones' do
- expect(@global_milestones.map { |group_milestone| group_milestone.milestones.count }.sum).to eq(6)
+ count = described_class.states_count(Project.all, group)
+
+ expect(count).to eq(expected_count)
+ end
+
+ it 'returns the quantity of global milestones in each possible state' do
+ expected_count = { opened: 2, closed: 4, all: 6 }
+
+ count = described_class.states_count(Project.all)
+
+ expect(count).to eq(expected_count)
+ end
end
- it 'sorts collection by due date' do
- expect(@global_milestones.map(&:due_date)).to eq [nil, milestone1_due_date]
+ context 'when the projects do not have milestones' do
+ before do
+ project1
+ end
+
+ it 'returns 0 as the quantity of global milestones in each state' do
+ expected_count = { opened: 0, closed: 0, all: 0 }
+
+ count = described_class.states_count(Project.all)
+
+ expect(count).to eq(expected_count)
+ end
end
end
describe '#initialize' do
let(:milestone1_project1) { create(:milestone, title: "Milestone v1.2", project: project1) }
- let(:milestone1_project2) { create(:milestone, title: "Milestone v1.2", project: project2) }
- let(:milestone1_project3) { create(:milestone, title: "Milestone v1.2", project: project3) }
-
- before do
- milestones =
- [
- milestone1_project1,
- milestone1_project2,
- milestone1_project3
- ]
- milestones_relation = Milestone.where(id: milestones.map(&:id))
-
- @global_milestone = described_class.new(milestone1_project1.title, milestones_relation)
- end
+ subject(:global_milestone) { described_class.new(milestone1_project1) }
it 'has exactly one group milestone' do
- expect(@global_milestone.title).to eq('Milestone v1.2')
+ expect(global_milestone.title).to eq('Milestone v1.2')
end
it 'has all project milestones with the same title' do
- expect(@global_milestone.milestones.count).to eq(3)
+ expect(global_milestone.milestone).to eq(milestone1_project1)
end
end
@@ -122,7 +169,7 @@ describe GlobalMilestone do
let(:milestone) { create(:milestone, title: "git / test", project: project1) }
it 'strips out slashes and spaces' do
- global_milestone = described_class.new(milestone.title, Milestone.where(id: milestone.id))
+ global_milestone = described_class.new(milestone)
expect(global_milestone.safe_title).to eq('git-test')
end
@@ -132,11 +179,8 @@ describe GlobalMilestone do
context 'when at least one milestone is active' do
it 'returns active' do
title = 'Active Group Milestone'
- milestones = [
- create(:active_milestone, title: title),
- create(:closed_milestone, title: title)
- ]
- global_milestone = described_class.new(title, milestones)
+
+ global_milestone = described_class.new(create(:active_milestone, title: title))
expect(global_milestone.state).to eq('active')
end
@@ -145,11 +189,8 @@ describe GlobalMilestone do
context 'when all milestones are closed' do
it 'returns closed' do
title = 'Closed Group Milestone'
- milestones = [
- create(:closed_milestone, title: title),
- create(:closed_milestone, title: title)
- ]
- global_milestone = described_class.new(title, milestones)
+
+ global_milestone = described_class.new(create(:closed_milestone, title: title))
expect(global_milestone.state).to eq('closed')
end
diff --git a/spec/models/group_milestone_spec.rb b/spec/models/group_milestone_spec.rb
index b60676afc91..fcc33cd95fe 100644
--- a/spec/models/group_milestone_spec.rb
+++ b/spec/models/group_milestone_spec.rb
@@ -20,13 +20,36 @@ describe GroupMilestone do
end
describe '.build_collection' do
- before do
- project_milestone
+ let(:group) { create(:group) }
+ let(:project1) { create(:project, group: group) }
+ let(:project2) { create(:project, path: 'gitlab-ci', group: group) }
+ let(:project3) { create(:project, path: 'cookbook-gitlab', group: group) }
+
+ let!(:projects) do
+ [
+ project1,
+ project2,
+ project3
+ ]
end
it 'returns array of milestones, each with group assigned' do
milestones = described_class.build_collection(group, [project], {})
expect(milestones).to all(have_attributes(group: group))
end
+
+ context 'when adding new milestones' do
+ it 'does not add more queries' do
+ control_count = ActiveRecord::QueryRecorder.new do
+ described_class.build_collection(group, projects, {})
+ end.count
+
+ create(:milestone, title: 'This title', project: project1)
+
+ expect do
+ described_class.build_collection(group, projects, {})
+ end.not_to exceed_all_query_limit(control_count)
+ end
+ end
end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 6793d4e8718..4cc3a6a3644 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -559,6 +559,57 @@ describe MergeRequest do
end
end
+ describe '#preload_discussions_diff_highlight' do
+ let(:merge_request) { create(:merge_request) }
+
+ context 'with commit diff note' do
+ let(:other_merge_request) { create(:merge_request) }
+
+ let!(:diff_note) do
+ create(:diff_note_on_commit, project: merge_request.project)
+ end
+
+ let!(:other_mr_diff_note) do
+ create(:diff_note_on_commit, project: other_merge_request.project)
+ end
+
+ it 'preloads diff highlighting' do
+ expect_next_instance_of(Gitlab::DiscussionsDiff::FileCollection) do |collection|
+ note_diff_file = diff_note.note_diff_file
+
+ expect(collection)
+ .to receive(:load_highlight)
+ .with([note_diff_file.id]).and_call_original
+ end
+
+ merge_request.preload_discussions_diff_highlight
+ end
+ end
+
+ context 'with merge request diff note' do
+ let!(:unresolved_diff_note) do
+ create(:diff_note_on_merge_request, project: merge_request.project, noteable: merge_request)
+ end
+
+ let!(:resolved_diff_note) do
+ create(:diff_note_on_merge_request, :resolved, project: merge_request.project, noteable: merge_request)
+ end
+
+ it 'preloads diff highlighting' do
+ expect_next_instance_of(Gitlab::DiscussionsDiff::FileCollection) do |collection|
+ note_diff_file = unresolved_diff_note.note_diff_file
+
+ expect(collection)
+ .to receive(:load_highlight)
+ .with([note_diff_file.id])
+ .and_call_original
+ end
+
+ merge_request.preload_discussions_diff_highlight
+ end
+ end
+ end
+
describe '#diff_size' do
let(:merge_request) do
build(:merge_request, source_branch: 'expand-collapse-files', target_branch: 'master')
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index d11eb46159e..b3d31e65c85 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -316,6 +316,15 @@ describe Milestone do
end
end
+ describe '#reference_link_text' do
+ let(:project) { build_stubbed(:project, name: 'sample-project') }
+ let(:milestone) { build_stubbed(:milestone, iid: 1, project: project, name: 'milestone') }
+
+ it 'returns the title with the reference prefix' do
+ expect(milestone.reference_link_text).to eq '%milestone'
+ end
+ end
+
describe '#participants' do
let(:project) { build(:project, name: 'sample-project') }
let(:milestone) { build(:milestone, iid: 1, project: project) }
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index a01f76a5bab..65b59c7b21b 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -299,6 +299,13 @@ describe Project do
expect(project.errors[:import_url].first).to include('Requests to localhost are not allowed')
end
+ it 'does not allow import_url pointing to the local network' do
+ project = build(:project, import_url: 'https://192.168.1.1')
+
+ expect(project).to be_invalid
+ expect(project.errors[:import_url].first).to include('Requests to the local network are not allowed')
+ end
+
it "does not allow import_url with invalid ports for new projects" do
project = build(:project, import_url: 'http://github.com:25/t.git')
@@ -603,16 +610,20 @@ describe Project do
end
it 'returns the address to create a new issue' do
- address = "p+#{project.full_path}+#{user.incoming_email_token}@gl.ab"
+ address = "p+#{project.full_path_slug}-#{project.project_id}-#{user.incoming_email_token}-issue@gl.ab"
expect(project.new_issuable_address(user, 'issue')).to eq(address)
end
it 'returns the address to create a new merge request' do
- address = "p+#{project.full_path}+merge-request+#{user.incoming_email_token}@gl.ab"
+ address = "p+#{project.full_path_slug}-#{project.project_id}-#{user.incoming_email_token}-merge-request@gl.ab"
expect(project.new_issuable_address(user, 'merge_request')).to eq(address)
end
+
+ it 'returns nil with invalid address type' do
+ expect(project.new_issuable_address(user, 'invalid_param')).to be_nil
+ end
end
context 'incoming email disabled' do
@@ -2543,6 +2554,10 @@ describe Project do
end
context 'when the ref is not protected' do
+ before do
+ allow(project).to receive(:protected_for?).with('ref').and_return(false)
+ end
+
it 'contains only the CI variables' do
is_expected.to contain_exactly(ci_variable)
end
@@ -2582,42 +2597,139 @@ describe Project do
end
describe '#protected_for?' do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
- subject { project.protected_for?('ref') }
+ subject { project.protected_for?(ref) }
- context 'when the ref is not protected' do
+ shared_examples 'ref is not protected' do
before do
stub_application_setting(
default_branch_protection: Gitlab::Access::PROTECTION_NONE)
end
it 'returns false' do
- is_expected.to be_falsey
+ is_expected.to be false
end
end
- context 'when the ref is a protected branch' do
+ shared_examples 'ref is protected branch' do
before do
- allow(project).to receive(:repository).and_call_original
- allow(project).to receive_message_chain(:repository, :branch_exists?).and_return(true)
- create(:protected_branch, name: 'ref', project: project)
+ create(:protected_branch, name: 'master', project: project)
end
it 'returns true' do
- is_expected.to be_truthy
+ is_expected.to be true
end
end
- context 'when the ref is a protected tag' do
+ shared_examples 'ref is protected tag' do
before do
- allow(project).to receive_message_chain(:repository, :branch_exists?).and_return(false)
- allow(project).to receive_message_chain(:repository, :tag_exists?).and_return(true)
- create(:protected_tag, name: 'ref', project: project)
+ create(:protected_tag, name: 'v1.0.0', project: project)
end
it 'returns true' do
- is_expected.to be_truthy
+ is_expected.to be true
+ end
+ end
+
+ context 'when ref is nil' do
+ let(:ref) { nil }
+
+ it 'returns false' do
+ is_expected.to be false
+ end
+ end
+
+ context 'when ref is ref name' do
+ context 'when ref is ambiguous' do
+ let(:ref) { 'ref' }
+
+ before do
+ project.repository.add_branch(project.creator, 'ref', 'master')
+ project.repository.add_tag(project.creator, 'ref', 'master')
+ end
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(Repository::AmbiguousRefError)
+ end
+ end
+
+ context 'when the ref is not protected' do
+ let(:ref) { 'master' }
+
+ it_behaves_like 'ref is not protected'
+ end
+
+ context 'when the ref is a protected branch' do
+ let(:ref) { 'master' }
+
+ it_behaves_like 'ref is protected branch'
+ end
+
+ context 'when the ref is a protected tag' do
+ let(:ref) { 'v1.0.0' }
+
+ it_behaves_like 'ref is protected tag'
+ end
+
+ context 'when ref does not exist' do
+ let(:ref) { 'something' }
+
+ it 'returns false' do
+ is_expected.to be false
+ end
+ end
+ end
+
+ context 'when ref is full ref' do
+ context 'when the ref is not protected' do
+ let(:ref) { 'refs/heads/master' }
+
+ it_behaves_like 'ref is not protected'
+ end
+
+ context 'when the ref is a protected branch' do
+ let(:ref) { 'refs/heads/master' }
+
+ it_behaves_like 'ref is protected branch'
+ end
+
+ context 'when the ref is a protected tag' do
+ let(:ref) { 'refs/tags/v1.0.0' }
+
+ it_behaves_like 'ref is protected tag'
+ end
+
+ context 'when branch ref name is a full tag ref' do
+ let(:ref) { 'refs/tags/something' }
+
+ before do
+ project.repository.add_branch(project.creator, ref, 'master')
+ end
+
+ context 'when ref is not protected' do
+ it 'returns false' do
+ is_expected.to be false
+ end
+ end
+
+ context 'when ref is a protected branch' do
+ before do
+ create(:protected_branch, name: 'refs/tags/something', project: project)
+ end
+
+ it 'returns true' do
+ is_expected.to be true
+ end
+ end
+ end
+
+ context 'when ref does not exist' do
+ let(:ref) { 'refs/heads/something' }
+
+ it 'returns false' do
+ is_expected.to be false
+ end
end
end
end
@@ -2837,7 +2949,7 @@ describe Project do
it 'shows full error updating an invalid MR' do
error_message = 'Failed to replace merge_requests because one or more of the new records could not be saved.'\
- ' Validate fork Source project is not a fork of the target project'
+ ' Validate fork Source project is not a fork of the target project'
expect { project.append_or_update_attribute(:merge_requests, [create(:merge_request)]) }
.to raise_error(ActiveRecord::RecordNotSaved, error_message)
@@ -2851,6 +2963,24 @@ describe Project do
end
end
+ describe '#update' do
+ let(:project) { create(:project) }
+
+ it 'validates the visibility' do
+ expect(project).to receive(:visibility_level_allowed_as_fork).and_call_original
+ expect(project).to receive(:visibility_level_allowed_by_group).and_call_original
+
+ project.update(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
+ end
+
+ it 'does not validate the visibility' do
+ expect(project).not_to receive(:visibility_level_allowed_as_fork).and_call_original
+ expect(project).not_to receive(:visibility_level_allowed_by_group).and_call_original
+
+ project.update(updated_at: Time.now)
+ end
+ end
+
describe '#last_repository_updated_at' do
it 'sets to created_at upon creation' do
project = create(:project, created_at: 2.hours.ago)
@@ -3077,6 +3207,13 @@ describe Project do
expect { project.migrate_to_hashed_storage! }.to change { project.repository_read_only }.to(true)
end
+ it 'does not validate project visibility' do
+ expect(project).not_to receive(:visibility_level_allowed_as_fork)
+ expect(project).not_to receive(:visibility_level_allowed_by_group)
+
+ project.migrate_to_hashed_storage!
+ end
+
it 'schedules ProjectMigrateHashedStorageWorker with delayed start when the project repo is in use' do
Gitlab::ReferenceCounter.new(project.gl_repository(is_wiki: false)).increase
@@ -3388,7 +3525,31 @@ describe Project do
end
end
- context '#auto_devops_variables' do
+ describe '#api_variables' do
+ set(:project) { create(:project) }
+
+ it 'exposes API v4 URL' do
+ expect(project.api_variables.first[:key]).to eq 'CI_API_V4_URL'
+ expect(project.api_variables.first[:value]).to include '/api/v4'
+ end
+
+ it 'contains a URL variable for every supported API version' do
+ # Ensure future API versions have proper variables defined. We're not doing this for v3.
+ supported_versions = API::API.versions - ['v3']
+ supported_versions = supported_versions.select do |version|
+ API::API.routes.select { |route| route.version == version }.many?
+ end
+
+ required_variables = supported_versions.map do |version|
+ "CI_API_#{version.upcase}_URL"
+ end
+
+ expect(project.api_variables.map { |variable| variable[:key] })
+ .to contain_exactly(*required_variables)
+ end
+ end
+
+ describe '#auto_devops_variables' do
set(:project) { create(:project) }
subject { project.auto_devops_variables }
@@ -3723,6 +3884,16 @@ describe Project do
let(:user) { create(:user) }
let(:target_project) { create(:project, :repository) }
let(:project) { fork_project(target_project, nil, repository: true) }
+ let!(:local_merge_request) do
+ create(
+ :merge_request,
+ target_project: project,
+ target_branch: 'target-branch',
+ source_project: project,
+ source_branch: 'awesome-feature-1',
+ allow_collaboration: true
+ )
+ end
let!(:merge_request) do
create(
:merge_request,
@@ -3767,14 +3938,23 @@ describe Project do
end
end
- describe '#branch_allows_collaboration_push?' do
- it 'allows access if the user can merge the merge request' do
- expect(project.branch_allows_collaboration?(user, 'awesome-feature-1'))
+ describe '#any_branch_allows_collaboration?' do
+ it 'allows access when there are merge requests open allowing collaboration' do
+ expect(project.any_branch_allows_collaboration?(user))
.to be_truthy
end
- it 'allows access when there are merge requests open but no branch name is given' do
- expect(project.branch_allows_collaboration?(user, nil))
+ it 'does not allow access when there are no merge requests open allowing collaboration' do
+ merge_request.close!
+
+ expect(project.any_branch_allows_collaboration?(user))
+ .to be_falsey
+ end
+ end
+
+ describe '#branch_allows_collaboration?' do
+ it 'allows access if the user can merge the merge request' do
+ expect(project.branch_allows_collaboration?(user, 'awesome-feature-1'))
.to be_truthy
end
@@ -3805,13 +3985,6 @@ describe Project do
.to be_falsy
end
- it 'caches the result' do
- control = ActiveRecord::QueryRecorder.new { project.branch_allows_collaboration?(user, 'awesome-feature-1') }
-
- expect { 3.times { project.branch_allows_collaboration?(user, 'awesome-feature-1') } }
- .not_to exceed_query_limit(control)
- end
-
context 'when the requeststore is active', :request_store do
it 'only queries per project across instances' do
control = ActiveRecord::QueryRecorder.new { project.branch_allows_collaboration?(user, 'awesome-feature-1') }
diff --git a/spec/models/prometheus_metric_spec.rb b/spec/models/prometheus_metric_spec.rb
index 3692fe9a559..2b978c1c8ff 100644
--- a/spec/models/prometheus_metric_spec.rb
+++ b/spec/models/prometheus_metric_spec.rb
@@ -59,11 +59,65 @@ describe PrometheusMetric do
end
end
+ it_behaves_like 'group_title', :nginx_ingress_vts, 'Response metrics (NGINX Ingress VTS)'
+ it_behaves_like 'group_title', :nginx_ingress, 'Response metrics (NGINX Ingress)'
+ it_behaves_like 'group_title', :ha_proxy, 'Response metrics (HA Proxy)'
+ it_behaves_like 'group_title', :aws_elb, 'Response metrics (AWS ELB)'
+ it_behaves_like 'group_title', :nginx, 'Response metrics (NGINX)'
+ it_behaves_like 'group_title', :kubernetes, 'System metrics (Kubernetes)'
it_behaves_like 'group_title', :business, 'Business metrics (Custom)'
it_behaves_like 'group_title', :response, 'Response metrics (Custom)'
it_behaves_like 'group_title', :system, 'System metrics (Custom)'
end
+ describe '#priority' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:group, :priority) do
+ :nginx_ingress_vts | 10
+ :nginx_ingress | 10
+ :ha_proxy | 10
+ :aws_elb | 10
+ :nginx | 10
+ :kubernetes | 5
+ :business | 0
+ :response | -5
+ :system | -10
+ end
+
+ with_them do
+ before do
+ subject.group = group
+ end
+
+ it { expect(subject.priority).to eq(priority) }
+ end
+ end
+
+ describe '#required_metrics' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:group, :required_metrics) do
+ :nginx_ingress_vts | %w(nginx_upstream_responses_total nginx_upstream_response_msecs_avg)
+ :nginx_ingress | %w(nginx_ingress_controller_requests nginx_ingress_controller_ingress_upstream_latency_seconds_sum)
+ :ha_proxy | %w(haproxy_frontend_http_requests_total haproxy_frontend_http_responses_total)
+ :aws_elb | %w(aws_elb_request_count_sum aws_elb_latency_average aws_elb_httpcode_backend_5_xx_sum)
+ :nginx | %w(nginx_server_requests nginx_server_requestMsec)
+ :kubernetes | %w(container_memory_usage_bytes container_cpu_usage_seconds_total)
+ :business | %w()
+ :response | %w()
+ :system | %w()
+ end
+
+ with_them do
+ before do
+ subject.group = group
+ end
+
+ it { expect(subject.required_metrics).to eq(required_metrics) }
+ end
+ end
+
describe '#to_query_metric' do
it 'converts to queryable metric object' do
expect(subject.to_query_metric).to be_instance_of(Gitlab::Prometheus::Metric)
diff --git a/spec/models/release_spec.rb b/spec/models/release_spec.rb
index 51725eeacac..157c96c1f65 100644
--- a/spec/models/release_spec.rb
+++ b/spec/models/release_spec.rb
@@ -1,17 +1,44 @@
require 'rails_helper'
RSpec.describe Release do
- let(:release) { create(:release) }
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :public, :repository) }
+ let(:release) { create(:release, project: project, author: user) }
it { expect(release).to be_valid }
describe 'associations' do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:author).class_name('User') }
+ it { is_expected.to have_many(:links).class_name('Releases::Link') }
end
describe 'validation' do
it { is_expected.to validate_presence_of(:project) }
it { is_expected.to validate_presence_of(:description) }
end
+
+ describe '#assets_count' do
+ subject { release.assets_count }
+
+ it 'returns the number of sources' do
+ is_expected.to eq(Releases::Source::FORMATS.count)
+ end
+
+ context 'when a links exists' do
+ let!(:link) { create(:release_link, release: release) }
+
+ it 'counts the link as an asset' do
+ is_expected.to eq(1 + Releases::Source::FORMATS.count)
+ end
+ end
+ end
+
+ describe '#sources' do
+ subject { release.sources }
+
+ it 'returns sources' do
+ is_expected.to all(be_a(Releases::Source))
+ end
+ end
end
diff --git a/spec/models/releases/link_spec.rb b/spec/models/releases/link_spec.rb
new file mode 100644
index 00000000000..e88c186cbb8
--- /dev/null
+++ b/spec/models/releases/link_spec.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Releases::Link do
+ let(:release) { create(:release, project: project) }
+ let(:project) { create(:project) }
+
+ describe 'associations' do
+ it { is_expected.to belong_to(:release) }
+ end
+
+ describe 'validation' do
+ it { is_expected.to validate_presence_of(:url) }
+ it { is_expected.to validate_presence_of(:name) }
+
+ context 'when url is invalid' do
+ let(:link) { build(:release_link, url: 'hoge') }
+
+ it 'will be invalid' do
+ expect(link).to be_invalid
+ end
+ end
+
+ context 'when duplicate name is added to a release' do
+ let!(:link) { create(:release_link, name: 'alpha', release: release) }
+
+ it 'raises an error' do
+ expect do
+ create(:release_link, name: 'alpha', release: release)
+ end.to raise_error(ActiveRecord::RecordInvalid)
+ end
+ end
+ end
+
+ describe '.sorted' do
+ subject { described_class.sorted }
+
+ let!(:link_1) { create(:release_link, name: 'alpha', release: release, created_at: 1.day.ago) }
+ let!(:link_2) { create(:release_link, name: 'beta', release: release, created_at: 2.days.ago) }
+
+ it 'returns a list of links by created_at order' do
+ is_expected.to eq([link_1, link_2])
+ end
+ end
+
+ describe '#internal?' do
+ subject { link.internal? }
+
+ let(:link) { build(:release_link, release: release, url: url) }
+ let(:url) { "#{project.web_url}/-/jobs/140463678/artifacts/download" }
+
+ it { is_expected.to be_truthy }
+
+ context 'when link does not include project web url' do
+ let(:url) { 'https://google.com/-/jobs/140463678/artifacts/download' }
+
+ it { is_expected.to be_falsy }
+ end
+ end
+
+ describe '#external?' do
+ subject { link.external? }
+
+ let(:link) { build(:release_link, release: release, url: url) }
+ let(:url) { 'https://google.com/-/jobs/140463678/artifacts/download' }
+
+ it { is_expected.to be_truthy }
+ end
+end
diff --git a/spec/models/releases/source_spec.rb b/spec/models/releases/source_spec.rb
new file mode 100644
index 00000000000..c5213196962
--- /dev/null
+++ b/spec/models/releases/source_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Releases::Source do
+ set(:project) { create(:project, :repository, name: 'finance-cal') }
+ let(:tag_name) { 'v1.0' }
+
+ describe '.all' do
+ subject { described_class.all(project, tag_name) }
+
+ it 'returns all formats of sources' do
+ expect(subject.map(&:format))
+ .to match_array(described_class::FORMATS)
+ end
+ end
+
+ describe '#url' do
+ subject { source.url }
+
+ let(:source) do
+ described_class.new(project: project, tag_name: tag_name, format: format)
+ end
+
+ let(:format) { 'zip' }
+
+ it 'returns zip archived source url' do
+ is_expected
+ .to eq("#{project.web_url}/-/archive/v1.0/finance-cal-v1.0.zip")
+ end
+
+ context 'when ref is directory structure' do
+ let(:tag_name) { 'beta/v1.0' }
+
+ it 'converts slash to dash' do
+ is_expected
+ .to eq("#{project.web_url}/-/archive/beta/v1.0/finance-cal-beta-v1.0.zip")
+ end
+ end
+ end
+end
diff --git a/spec/models/remote_mirror_spec.rb b/spec/models/remote_mirror_spec.rb
index 5d3c25062d5..224bc9ed935 100644
--- a/spec/models/remote_mirror_spec.rb
+++ b/spec/models/remote_mirror_spec.rb
@@ -24,6 +24,20 @@ describe RemoteMirror, :mailer do
expect(remote_mirror).to be_invalid
expect(remote_mirror.errors[:url].first).to include('Username needs to start with an alphanumeric character')
end
+
+ it 'does not allow url pointing to localhost' do
+ remote_mirror = build(:remote_mirror, url: 'http://127.0.0.2/t.git')
+
+ expect(remote_mirror).to be_invalid
+ expect(remote_mirror.errors[:url].first).to include('Requests to loopback addresses are not allowed')
+ end
+
+ it 'does not allow url pointing to the local network' do
+ remote_mirror = build(:remote_mirror, url: 'https://192.168.1.1')
+
+ expect(remote_mirror).to be_invalid
+ expect(remote_mirror.errors[:url].first).to include('Requests to the local network are not allowed')
+ end
end
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index f09b4b67061..2063b4bbe75 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -1005,6 +1005,67 @@ describe Repository do
end
end
+ describe '#ambiguous_ref?' do
+ let(:ref) { 'ref' }
+
+ subject { repository.ambiguous_ref?(ref) }
+
+ context 'when ref is ambiguous' do
+ before do
+ repository.add_tag(project.creator, ref, 'master')
+ repository.add_branch(project.creator, ref, 'master')
+ end
+
+ it 'should be true' do
+ is_expected.to eq(true)
+ end
+ end
+
+ context 'when ref is not ambiguous' do
+ before do
+ repository.add_tag(project.creator, ref, 'master')
+ end
+
+ it 'should be false' do
+ is_expected.to eq(false)
+ end
+ end
+ end
+
+ describe '#expand_ref' do
+ let(:ref) { 'ref' }
+
+ subject { repository.expand_ref(ref) }
+
+ context 'when ref is not tag or branch name' do
+ let(:ref) { 'refs/heads/master' }
+
+ it 'returns nil' do
+ is_expected.to eq(nil)
+ end
+ end
+
+ context 'when ref is tag name' do
+ before do
+ repository.add_tag(project.creator, ref, 'master')
+ end
+
+ it 'returns the tag ref' do
+ is_expected.to eq("refs/tags/#{ref}")
+ end
+ end
+
+ context 'when ref is branch name' do
+ before do
+ repository.add_branch(project.creator, ref, 'master')
+ end
+
+ it 'returns the branch ref' do
+ is_expected.to eq("refs/heads/#{ref}")
+ end
+ end
+ end
+
describe '#add_branch' do
let(:branch_name) { 'new_feature' }
let(:target) { 'master' }
diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb
index 7a7272ccb60..664dc3fa145 100644
--- a/spec/models/snippet_spec.rb
+++ b/spec/models/snippet_spec.rb
@@ -423,4 +423,41 @@ describe Snippet do
expect(blob.data).to eq(snippet.content)
end
end
+
+ describe '#embeddable?' do
+ context 'project snippet' do
+ [
+ { project: :public, snippet: :public, embeddable: true },
+ { project: :internal, snippet: :public, embeddable: false },
+ { project: :private, snippet: :public, embeddable: false },
+ { project: :public, snippet: :internal, embeddable: false },
+ { project: :internal, snippet: :internal, embeddable: false },
+ { project: :private, snippet: :internal, embeddable: false },
+ { project: :public, snippet: :private, embeddable: false },
+ { project: :internal, snippet: :private, embeddable: false },
+ { project: :private, snippet: :private, embeddable: false }
+ ].each do |combination|
+ it 'only returns true when both project and snippet are public' do
+ project = create(:project, combination[:project])
+ snippet = create(:project_snippet, combination[:snippet], project: project)
+
+ expect(snippet.embeddable?).to eq(combination[:embeddable])
+ end
+ end
+ end
+
+ context 'personal snippet' do
+ [
+ { snippet: :public, embeddable: true },
+ { snippet: :internal, embeddable: false },
+ { snippet: :private, embeddable: false }
+ ].each do |combination|
+ it 'only returns true when snippet is public' do
+ snippet = create(:personal_snippet, combination[:snippet])
+
+ expect(snippet.embeddable?).to eq(combination[:embeddable])
+ end
+ end
+ end
+ end
end
diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb
index baf21efa75c..be1804c5ce0 100644
--- a/spec/policies/group_policy_spec.rb
+++ b/spec/policies/group_policy_spec.rb
@@ -25,7 +25,8 @@ describe GroupPolicy do
:read_cluster,
:create_cluster,
:update_cluster,
- :admin_cluster
+ :admin_cluster,
+ :add_cluster
]
end
@@ -382,4 +383,14 @@ describe GroupPolicy do
it { expect_disallowed(:change_share_with_group_lock) }
end
end
+
+ it_behaves_like 'clusterable policies' do
+ let(:clusterable) { create(:group) }
+ let(:cluster) do
+ create(:cluster,
+ :provided_by_gcp,
+ :group,
+ groups: [clusterable])
+ end
+ end
end
diff --git a/spec/policies/issuable_policy_spec.rb b/spec/policies/issuable_policy_spec.rb
index d1bf98995e7..db3df760472 100644
--- a/spec/policies/issuable_policy_spec.rb
+++ b/spec/policies/issuable_policy_spec.rb
@@ -7,6 +7,33 @@ describe IssuablePolicy, models: true do
let(:policies) { described_class.new(user, issue) }
describe '#rules' do
+ context 'when user is author of issuable' do
+ let(:merge_request) { create(:merge_request, source_project: project, author: user) }
+ let(:policies) { described_class.new(user, merge_request) }
+
+ context 'when user is able to read project' do
+ it 'enables user to read and update issuables' do
+ expect(policies).to be_allowed(:read_issue, :update_issue, :reopen_issue, :read_merge_request, :update_merge_request)
+ end
+ end
+
+ context 'when project is private' do
+ let(:project) { create(:project, :private) }
+
+ context 'when user belongs to the projects team' do
+ it 'enables user to read and update issuables' do
+ project.add_maintainer(user)
+
+ expect(policies).to be_allowed(:read_issue, :update_issue, :reopen_issue, :read_merge_request, :update_merge_request)
+ end
+ end
+
+ it 'disallows user from reading and updating issuables from that project' do
+ expect(policies).to be_disallowed(:read_issue, :update_issue, :reopen_issue, :read_merge_request, :update_merge_request)
+ end
+ end
+ end
+
context 'when discussion is locked for the issuable' do
let(:issue) { create(:issue, project: project, discussion_locked: true) }
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index 69468f9ad85..9cb20854f6e 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -15,7 +15,7 @@ describe ProjectPolicy do
read_project_for_iids read_issue_iid read_merge_request_iid read_label
read_milestone read_project_snippet read_project_member read_note
create_project create_issue create_note upload_file create_merge_request_in
- award_emoji
+ award_emoji read_release
]
end
@@ -38,7 +38,7 @@ describe ProjectPolicy do
update_commit_status create_build update_build create_pipeline
update_pipeline create_merge_request_from create_wiki push_code
resolve_note create_container_image update_container_image
- create_environment create_deployment
+ create_environment create_deployment create_release update_release
]
end
@@ -48,7 +48,7 @@ describe ProjectPolicy do
update_deployment admin_project_snippet
admin_project_member admin_note admin_wiki admin_project
admin_commit_status admin_build admin_container_image
- admin_pipeline admin_environment admin_deployment
+ admin_pipeline admin_environment admin_deployment destroy_release add_cluster
]
end
@@ -56,7 +56,7 @@ describe ProjectPolicy do
%i[
download_code fork_project read_commit_status read_pipeline
read_container_image build_download_code build_read_container_image
- download_wiki_code
+ download_wiki_code read_release
]
end
@@ -183,7 +183,8 @@ describe ProjectPolicy do
:create_pipeline_schedule, :read_pipeline_schedule, :update_pipeline_schedule, :admin_pipeline_schedule, :destroy_pipeline_schedule,
:create_environment, :read_environment, :update_environment, :admin_environment, :destroy_environment,
:create_cluster, :read_cluster, :update_cluster, :admin_cluster,
- :create_deployment, :read_deployment, :update_deployment, :admin_deployment, :destroy_deployment
+ :create_deployment, :read_deployment, :update_deployment, :admin_deployment, :destroy_deployment,
+ :destroy_release
]
expect_disallowed(*repository_permissions)
@@ -465,4 +466,14 @@ describe ProjectPolicy do
expect_disallowed(*maintainer_abilities)
end
end
+
+ it_behaves_like 'clusterable policies' do
+ let(:clusterable) { create(:project, :repository) }
+ let(:cluster) do
+ create(:cluster,
+ :provided_by_gcp,
+ :project,
+ projects: [clusterable])
+ end
+ end
end
diff --git a/spec/presenters/clusterable_presenter_spec.rb b/spec/presenters/clusterable_presenter_spec.rb
index 4f4ae5e07c5..05afe5347d1 100644
--- a/spec/presenters/clusterable_presenter_spec.rb
+++ b/spec/presenters/clusterable_presenter_spec.rb
@@ -14,4 +14,68 @@ describe ClusterablePresenter do
expect(subject).to be_kind_of(ProjectClusterablePresenter)
end
end
+
+ shared_examples 'appropriate member permissions' do
+ context 'with a developer' do
+ before do
+ clusterable.add_developer(user)
+ end
+
+ it { is_expected.to be_falsy }
+ end
+
+ context 'with a maintainer' do
+ before do
+ clusterable.add_maintainer(user)
+ end
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ describe '#can_create_cluster?' do
+ let(:user) { create(:user) }
+
+ subject { described_class.new(clusterable).can_create_cluster? }
+
+ before do
+ allow(clusterable).to receive(:current_user).and_return(user)
+ end
+
+ context 'when clusterable is a group' do
+ let(:clusterable) { create(:group) }
+
+ it_behaves_like 'appropriate member permissions'
+ end
+
+ context 'when clusterable is a project' do
+ let(:clusterable) { create(:project, :repository) }
+
+ it_behaves_like 'appropriate member permissions'
+ end
+ end
+
+ describe '#can_add_cluster?' do
+ let(:user) { create(:user) }
+
+ subject { described_class.new(clusterable).can_add_cluster? }
+
+ before do
+ clusterable.add_maintainer(user)
+
+ allow(clusterable).to receive(:current_user).and_return(user)
+ end
+
+ context 'when clusterable is a group' do
+ let(:clusterable) { create(:group) }
+
+ it_behaves_like 'appropriate member permissions'
+ end
+
+ context 'when clusterable is a project' do
+ let(:clusterable) { create(:project, :repository) }
+
+ it_behaves_like 'appropriate member permissions'
+ end
+ end
end
diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb
index 93c411476bb..b38cd66986f 100644
--- a/spec/requests/api/branches_spec.rb
+++ b/spec/requests/api/branches_spec.rb
@@ -20,6 +20,12 @@ describe API::Branches do
let(:route) { "/projects/#{project_id}/repository/branches" }
shared_examples_for 'repository branches' do
+ RSpec::Matchers.define :has_merged_branch_names_count do |expected|
+ match do |actual|
+ actual[:merged_branch_names].count == expected
+ end
+ end
+
it 'returns the repository branches' do
get api(route, current_user), params: { per_page: 100 }
@@ -30,6 +36,12 @@ describe API::Branches do
expect(branch_names).to match_array(project.repository.branch_names)
end
+ it 'determines only a limited number of merged branch names' do
+ expect(API::Entities::Branch).to receive(:represent).with(anything, has_merged_branch_names_count(2))
+
+ get api(route, current_user), params: { per_page: 2 }
+ end
+
context 'when repository is disabled' do
include_context 'disabled repository'
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index 0ba1f2d7a2b..a0aee937185 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -190,7 +190,7 @@ describe API::Files do
get api(url, current_user), params: params
- expect(headers['Content-Disposition']).to match(/^attachment/)
+ expect(headers['Content-Disposition']).to eq('attachment; filename="popen.rb"')
end
context 'when mandatory params are not given' do
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index 589816b5d8f..0fe63e2e517 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -809,7 +809,8 @@ describe API::Internal do
gl_repository: gl_repository,
secret_token: secret_token,
identifier: identifier,
- changes: changes
+ changes: changes,
+ push_options: push_options
}
end
@@ -817,6 +818,11 @@ describe API::Internal do
"#{Gitlab::Git::BLANK_SHA} 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/new_branch"
end
+ let(:push_options) do
+ ['ci.skip',
+ 'another push option']
+ end
+
before do
project.add_developer(user)
allow(described_class).to receive(:identify).and_return(user)
@@ -825,7 +831,7 @@ describe API::Internal do
it 'enqueues a PostReceive worker job' do
expect(PostReceive).to receive(:perform_async)
- .with(gl_repository, identifier, changes)
+ .with(gl_repository, identifier, changes, push_options)
post api("/internal/post_receive"), params: valid_params
end
diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb
index 73131dba542..97aa71bf231 100644
--- a/spec/requests/api/jobs_spec.rb
+++ b/spec/requests/api/jobs_spec.rb
@@ -142,10 +142,20 @@ describe API::Jobs do
end
context 'unauthorized user' do
- let(:api_user) { nil }
+ context 'when user is not logged in' do
+ let(:api_user) { nil }
- it 'does not return project jobs' do
- expect(response).to have_gitlab_http_status(401)
+ it 'does not return project jobs' do
+ expect(response).to have_gitlab_http_status(401)
+ end
+ end
+
+ context 'when user is guest' do
+ let(:api_user) { guest }
+
+ it 'does not return project jobs' do
+ expect(response).to have_gitlab_http_status(403)
+ end
end
end
@@ -241,10 +251,20 @@ describe API::Jobs do
end
context 'unauthorized user' do
- let(:api_user) { nil }
+ context 'when user is not logged in' do
+ let(:api_user) { nil }
- it 'does not return jobs' do
- expect(response).to have_gitlab_http_status(401)
+ it 'does not return jobs' do
+ expect(response).to have_gitlab_http_status(401)
+ end
+ end
+
+ context 'when user is guest' do
+ let(:api_user) { guest }
+
+ it 'does not return jobs' do
+ expect(response).to have_gitlab_http_status(403)
+ end
end
end
end
diff --git a/spec/requests/api/releases_spec.rb b/spec/requests/api/releases_spec.rb
new file mode 100644
index 00000000000..978fa0142c2
--- /dev/null
+++ b/spec/requests/api/releases_spec.rb
@@ -0,0 +1,664 @@
+require 'spec_helper'
+
+describe API::Releases do
+ let(:project) { create(:project, :repository, :private) }
+ let(:maintainer) { create(:user) }
+ let(:reporter) { create(:user) }
+ let(:non_project_member) { create(:user) }
+ let(:commit) { create(:commit, project: project) }
+
+ before do
+ project.add_maintainer(maintainer)
+ project.add_reporter(reporter)
+
+ project.repository.add_tag(maintainer, 'v0.1', commit.id)
+ project.repository.add_tag(maintainer, 'v0.2', commit.id)
+ end
+
+ describe 'GET /projects/:id/releases' do
+ context 'when there are two releases' do
+ let!(:release_1) do
+ create(:release,
+ project: project,
+ tag: 'v0.1',
+ author: maintainer,
+ created_at: 2.days.ago)
+ end
+
+ let!(:release_2) do
+ create(:release,
+ project: project,
+ tag: 'v0.2',
+ author: maintainer,
+ created_at: 1.day.ago)
+ end
+
+ it 'returns 200 HTTP status' do
+ get api("/projects/#{project.id}/releases", maintainer)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ it 'returns releases ordered by created_at' do
+ get api("/projects/#{project.id}/releases", maintainer)
+
+ expect(json_response.count).to eq(2)
+ expect(json_response.first['tag_name']).to eq(release_2.tag)
+ expect(json_response.second['tag_name']).to eq(release_1.tag)
+ end
+
+ it 'matches response schema' do
+ get api("/projects/#{project.id}/releases", maintainer)
+
+ expect(response).to match_response_schema('releases')
+ end
+ end
+
+ context 'when tag does not exist in git repository' do
+ let!(:release) { create(:release, project: project, tag: 'v1.1.5') }
+
+ it 'returns the tag' do
+ get api("/projects/#{project.id}/releases", maintainer)
+
+ expect(json_response.count).to eq(1)
+ expect(json_response.first['tag_name']).to eq('v1.1.5')
+ expect(release).to be_tag_missing
+ end
+ end
+
+ context 'when user is not a project member' do
+ it 'cannot find the project' do
+ get api("/projects/#{project.id}/releases", non_project_member)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ context 'when project is public' do
+ let(:project) { create(:project, :repository, :public) }
+
+ it 'allows the request' do
+ get api("/projects/#{project.id}/releases", non_project_member)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+ end
+
+ context 'when feature flag is disabled' do
+ before do
+ stub_feature_flags(releases_page: false)
+ end
+
+ it 'cannot find the API' do
+ get api("/projects/#{project.id}/releases", maintainer)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+
+ describe 'GET /projects/:id/releases/:tag_name' do
+ context 'when there is a release' do
+ let!(:release) do
+ create(:release,
+ project: project,
+ tag: 'v0.1',
+ sha: commit.id,
+ author: maintainer,
+ description: 'This is v0.1')
+ end
+
+ it 'returns 200 HTTP status' do
+ get api("/projects/#{project.id}/releases/v0.1", maintainer)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ it 'returns a release entry' do
+ get api("/projects/#{project.id}/releases/v0.1", maintainer)
+
+ expect(json_response['tag_name']).to eq(release.tag)
+ expect(json_response['description']).to eq('This is v0.1')
+ expect(json_response['author']['name']).to eq(maintainer.name)
+ expect(json_response['commit']['id']).to eq(commit.id)
+ expect(json_response['assets']['count']).to eq(4)
+ end
+
+ it 'matches response schema' do
+ get api("/projects/#{project.id}/releases/v0.1", maintainer)
+
+ expect(response).to match_response_schema('release')
+ end
+
+ it 'contains source information as assets' do
+ get api("/projects/#{project.id}/releases/v0.1", maintainer)
+
+ expect(json_response['assets']['sources'].map { |h| h['format'] })
+ .to match_array(release.sources.map(&:format))
+ expect(json_response['assets']['sources'].map { |h| h['url'] })
+ .to match_array(release.sources.map(&:url))
+ end
+
+ context 'when release has link asset' do
+ let!(:link) do
+ create(:release_link,
+ release: release,
+ name: 'release-18.04.dmg',
+ url: url)
+ end
+
+ let(:url) { 'https://my-external-hosting.example.com/scrambled-url/app.zip' }
+
+ it 'contains link information as assets' do
+ get api("/projects/#{project.id}/releases/v0.1", maintainer)
+
+ expect(json_response['assets']['links'].count).to eq(1)
+ expect(json_response['assets']['links'].first['id']).to eq(link.id)
+ expect(json_response['assets']['links'].first['name'])
+ .to eq('release-18.04.dmg')
+ expect(json_response['assets']['links'].first['url'])
+ .to eq('https://my-external-hosting.example.com/scrambled-url/app.zip')
+ expect(json_response['assets']['links'].first['external'])
+ .to be_truthy
+ end
+
+ context 'when link is internal' do
+ let(:url) do
+ "#{project.web_url}/-/jobs/artifacts/v11.6.0-rc4/download?" \
+ "job=rspec-mysql+41%2F50"
+ end
+
+ it 'has external false' do
+ get api("/projects/#{project.id}/releases/v0.1", maintainer)
+
+ expect(json_response['assets']['links'].first['external'])
+ .to be_falsy
+ end
+ end
+ end
+ end
+
+ context 'when specified tag is not found in the project' do
+ it 'cannot find the release entry' do
+ get api("/projects/#{project.id}/releases/non_exist_tag", maintainer)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'when user is not a project member' do
+ let!(:release) { create(:release, tag: 'v0.1', project: project) }
+
+ it 'cannot find the project' do
+ get api("/projects/#{project.id}/releases/v0.1", non_project_member)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ context 'when project is public' do
+ let(:project) { create(:project, :repository, :public) }
+
+ it 'allows the request' do
+ get api("/projects/#{project.id}/releases/v0.1", non_project_member)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+ end
+
+ context 'when feature flag is disabled' do
+ before do
+ stub_feature_flags(releases_page: false)
+ end
+
+ it 'cannot find the API' do
+ get api("/projects/#{project.id}/releases/v0.1", maintainer)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+
+ describe 'POST /projects/:id/releases' do
+ let(:params) do
+ {
+ name: 'New release',
+ tag_name: 'v0.1',
+ description: 'Super nice release'
+ }
+ end
+
+ it 'accepts the request' do
+ post api("/projects/#{project.id}/releases", maintainer), params: params
+
+ expect(response).to have_gitlab_http_status(:created)
+ end
+
+ it 'creates a new release' do
+ expect do
+ post api("/projects/#{project.id}/releases", maintainer), params: params
+ end.to change { Release.count }.by(1)
+
+ expect(project.releases.last.name).to eq('New release')
+ expect(project.releases.last.tag).to eq('v0.1')
+ expect(project.releases.last.description).to eq('Super nice release')
+ end
+
+ context 'when description is empty' do
+ let(:params) do
+ {
+ name: 'New release',
+ tag_name: 'v0.1',
+ description: ''
+ }
+ end
+
+ it 'returns an error as validation failure' do
+ expect do
+ post api("/projects/#{project.id}/releases", maintainer), params: params
+ end.not_to change { Release.count }
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message'])
+ .to eq("Validation failed: Description can't be blank")
+ end
+ end
+
+ it 'matches response schema' do
+ post api("/projects/#{project.id}/releases", maintainer), params: params
+
+ expect(response).to match_response_schema('release')
+ end
+
+ it 'does not create a new tag' do
+ expect do
+ post api("/projects/#{project.id}/releases", maintainer), params: params
+ end.not_to change { Project.find_by_id(project.id).repository.tag_count }
+ end
+
+ context 'when user is a reporter' do
+ it 'forbids the request' do
+ post api("/projects/#{project.id}/releases", reporter), params: params
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'when user is not a project member' do
+ it 'forbids the request' do
+ post api("/projects/#{project.id}/releases", non_project_member),
+ params: params
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ context 'when project is public' do
+ let(:project) { create(:project, :repository, :public) }
+
+ it 'forbids the request' do
+ post api("/projects/#{project.id}/releases", non_project_member),
+ params: params
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'when create assets altogether' do
+ let(:base_params) do
+ {
+ name: 'New release',
+ tag_name: 'v0.1',
+ description: 'Super nice release'
+ }
+ end
+
+ context 'when create one asset' do
+ let(:params) do
+ base_params.merge({
+ assets: {
+ links: [{ name: 'beta', url: 'https://dosuken.example.com/inspection.exe' }]
+ }
+ })
+ end
+
+ it 'accepts the request' do
+ post api("/projects/#{project.id}/releases", maintainer), params: params
+
+ expect(response).to have_gitlab_http_status(:created)
+ end
+
+ it 'creates an asset with specified parameters' do
+ post api("/projects/#{project.id}/releases", maintainer), params: params
+
+ expect(json_response['assets']['links'].count).to eq(1)
+ expect(json_response['assets']['links'].first['name']).to eq('beta')
+ expect(json_response['assets']['links'].first['url'])
+ .to eq('https://dosuken.example.com/inspection.exe')
+ end
+
+ it 'matches response schema' do
+ post api("/projects/#{project.id}/releases", maintainer), params: params
+
+ expect(response).to match_response_schema('release')
+ end
+ end
+
+ context 'when create two assets' do
+ let(:params) do
+ base_params.merge({
+ assets: {
+ links: [
+ { name: 'alpha', url: 'https://dosuken.example.com/alpha.exe' },
+ { name: 'beta', url: 'https://dosuken.example.com/beta.exe' }
+ ]
+ }
+ })
+ end
+
+ it 'creates two assets with specified parameters' do
+ post api("/projects/#{project.id}/releases", maintainer), params: params
+
+ expect(json_response['assets']['links'].count).to eq(2)
+ expect(json_response['assets']['links'].map { |h| h['name'] })
+ .to match_array(%w[alpha beta])
+ expect(json_response['assets']['links'].map { |h| h['url'] })
+ .to match_array(%w[https://dosuken.example.com/alpha.exe
+ https://dosuken.example.com/beta.exe])
+ end
+
+ context 'when link names are duplicates' do
+ let(:params) do
+ base_params.merge({
+ assets: {
+ links: [
+ { name: 'alpha', url: 'https://dosuken.example.com/alpha.exe' },
+ { name: 'alpha', url: 'https://dosuken.example.com/beta.exe' }
+ ]
+ }
+ })
+ end
+
+ it 'recognizes as a bad request' do
+ post api("/projects/#{project.id}/releases", maintainer), params: params
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
+ end
+ end
+ end
+
+ context 'when tag does not exist in git repository' do
+ let(:params) do
+ {
+ name: 'Android ~ Ice Cream Sandwich ~',
+ tag_name: tag_name,
+ description: 'Android 4.0–4.0.4 "Ice Cream Sandwich" is the ninth' \
+ 'version of the Android mobile operating system developed' \
+ 'by Google.',
+ ref: 'master'
+ }
+ end
+
+ let(:tag_name) { 'v4.0' }
+
+ it 'creates a new tag' do
+ expect do
+ post api("/projects/#{project.id}/releases", maintainer), params: params
+ end.to change { Project.find_by_id(project.id).repository.tag_count }.by(1)
+
+ expect(project.repository.find_tag('v4.0').dereferenced_target.id)
+ .to eq(project.repository.commit('master').id)
+ end
+
+ it 'creates a new release' do
+ expect do
+ post api("/projects/#{project.id}/releases", maintainer), params: params
+ end.to change { Release.count }.by(1)
+
+ expect(project.releases.last.name).to eq('Android ~ Ice Cream Sandwich ~')
+ expect(project.releases.last.tag).to eq('v4.0')
+ expect(project.releases.last.description).to eq(
+ 'Android 4.0–4.0.4 "Ice Cream Sandwich" is the ninth' \
+ 'version of the Android mobile operating system developed' \
+ 'by Google.')
+ end
+
+ context 'when tag name is HEAD' do
+ let(:tag_name) { 'HEAD' }
+
+ it 'returns an error as failure on tag creation' do
+ post api("/projects/#{project.id}/releases", maintainer), params: params
+
+ expect(response).to have_gitlab_http_status(:internal_server_error)
+ expect(json_response['message']).to eq('Tag name invalid')
+ end
+ end
+
+ context 'when tag name is empty' do
+ let(:tag_name) { '' }
+
+ it 'returns an error as failure on tag creation' do
+ post api("/projects/#{project.id}/releases", maintainer), params: params
+
+ expect(response).to have_gitlab_http_status(:internal_server_error)
+ expect(json_response['message']).to eq('Tag name invalid')
+ end
+ end
+ end
+
+ context 'when release already exists' do
+ before do
+ create(:release, project: project, tag: 'v0.1', name: 'New release')
+ end
+
+ it 'returns an error as conflicted request' do
+ post api("/projects/#{project.id}/releases", maintainer), params: params
+
+ expect(response).to have_gitlab_http_status(:conflict)
+ end
+ end
+
+ context 'when feature flag is disabled' do
+ before do
+ stub_feature_flags(releases_page: false)
+ end
+
+ it 'cannot find the API' do
+ post api("/projects/#{project.id}/releases", maintainer), params: params
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+
+ describe 'PUT /projects/:id/releases/:tag_name' do
+ let(:params) { { description: 'Best release ever!' } }
+
+ let!(:release) do
+ create(:release,
+ project: project,
+ tag: 'v0.1',
+ name: 'New release',
+ description: 'Super nice release')
+ end
+
+ it 'accepts the request' do
+ put api("/projects/#{project.id}/releases/v0.1", maintainer), params: params
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ it 'updates the description' do
+ put api("/projects/#{project.id}/releases/v0.1", maintainer), params: params
+
+ expect(project.releases.last.description).to eq('Best release ever!')
+ end
+
+ it 'does not change other attributes' do
+ put api("/projects/#{project.id}/releases/v0.1", maintainer), params: params
+
+ expect(project.releases.last.tag).to eq('v0.1')
+ expect(project.releases.last.name).to eq('New release')
+ end
+
+ it 'matches response schema' do
+ put api("/projects/#{project.id}/releases/v0.1", maintainer), params: params
+
+ expect(response).to match_response_schema('release')
+ end
+
+ context 'when user tries to update sha' do
+ let(:params) { { sha: 'xxx' } }
+
+ it 'does not allow the request' do
+ put api("/projects/#{project.id}/releases/v0.1", maintainer), params: params
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
+
+ context 'when params is empty' do
+ let(:params) { {} }
+
+ it 'does not allow the request' do
+ put api("/projects/#{project.id}/releases/v0.1", maintainer), params: params
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
+
+ context 'when there are no corresponding releases' do
+ let!(:release) { }
+
+ it 'forbids the request' do
+ put api("/projects/#{project.id}/releases/v0.1", maintainer), params: params
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'when user is a reporter' do
+ it 'forbids the request' do
+ put api("/projects/#{project.id}/releases/v0.1", reporter), params: params
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'when user is not a project member' do
+ it 'forbids the request' do
+ put api("/projects/#{project.id}/releases/v0.1", non_project_member),
+ params: params
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ context 'when project is public' do
+ let(:project) { create(:project, :repository, :public) }
+
+ it 'forbids the request' do
+ put api("/projects/#{project.id}/releases/v0.1", non_project_member),
+ params: params
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+ end
+
+ context 'when feature flag is disabled' do
+ before do
+ stub_feature_flags(releases_page: false)
+ end
+
+ it 'cannot find the API' do
+ put api("/projects/#{project.id}/releases/v0.1", non_project_member),
+ params: params
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+
+ describe 'DELETE /projects/:id/releases/:tag_name' do
+ let!(:release) do
+ create(:release,
+ project: project,
+ tag: 'v0.1',
+ name: 'New release',
+ description: 'Super nice release')
+ end
+
+ it 'accepts the request' do
+ delete api("/projects/#{project.id}/releases/v0.1", maintainer)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ it 'destroys the release' do
+ expect do
+ delete api("/projects/#{project.id}/releases/v0.1", maintainer)
+ end.to change { Release.count }.by(-1)
+ end
+
+ it 'does not remove a tag in repository' do
+ expect do
+ delete api("/projects/#{project.id}/releases/v0.1", maintainer)
+ end.not_to change { Project.find_by_id(project.id).repository.tag_count }
+ end
+
+ it 'matches response schema' do
+ delete api("/projects/#{project.id}/releases/v0.1", maintainer)
+
+ expect(response).to match_response_schema('release')
+ end
+
+ context 'when there are no corresponding releases' do
+ let!(:release) { }
+
+ it 'forbids the request' do
+ delete api("/projects/#{project.id}/releases/v0.1", maintainer)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'when user is a reporter' do
+ it 'forbids the request' do
+ delete api("/projects/#{project.id}/releases/v0.1", reporter)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'when user is not a project member' do
+ it 'forbids the request' do
+ delete api("/projects/#{project.id}/releases/v0.1", non_project_member)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ context 'when project is public' do
+ let(:project) { create(:project, :repository, :public) }
+
+ it 'forbids the request' do
+ delete api("/projects/#{project.id}/releases/v0.1", non_project_member)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+ end
+
+ context 'when feature flag is disabled' do
+ before do
+ stub_feature_flags(releases_page: false)
+ end
+
+ it 'cannot find the API' do
+ delete api("/projects/#{project.id}/releases/v0.1", non_project_member)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index 181fe6246ae..b6b57803a6a 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -171,7 +171,7 @@ describe API::Repositories do
it 'forces attachment content disposition' do
get api(route, current_user)
- expect(headers['Content-Disposition']).to match(/^attachment/)
+ expect(headers['Content-Disposition']).to eq 'attachment'
end
context 'when sha does not exist' do
diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb
index c63621fe7d1..2f322cc7054 100644
--- a/spec/requests/api/runner_spec.rb
+++ b/spec/requests/api/runner_spec.rb
@@ -1320,7 +1320,7 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
end
before do
- fog_connection.directories.get('artifacts').files.create(
+ fog_connection.directories.new(key: 'artifacts').files.create(
key: 'tmp/uploads/12312300',
body: 'content'
)
diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb
index 12cfac96d31..d09b6fe72b1 100644
--- a/spec/requests/api/tags_spec.rb
+++ b/spec/requests/api/tags_spec.rb
@@ -107,9 +107,12 @@ describe API::Tags do
context 'with releases' do
let(:description) { 'Awesome release!' }
- before do
- release = project.releases.find_or_initialize_by(tag: tag_name)
- release.update(description: description)
+ let!(:release) do
+ create(:release,
+ :legacy,
+ project: project,
+ tag: tag_name,
+ description: description)
end
it 'returns an array of project tags with release info' do
@@ -373,7 +376,7 @@ describe API::Tags do
it_behaves_like '404 response' do
let(:request) { post api(route, current_user), params: { description: description } }
- let(:message) { 'Tag does not exist' }
+ let(:message) { '404 Tag Not Found' }
end
end
@@ -398,10 +401,7 @@ describe API::Tags do
end
context 'on tag with existing release' do
- before do
- release = project.releases.find_or_initialize_by(tag: tag_name)
- release.update(description: description)
- end
+ let!(:release) { create(:release, :legacy, project: project, tag: tag_name, description: description) }
it 'returns 409 if there is already a release' do
post api(route, user), params: { description: description }
@@ -420,9 +420,12 @@ describe API::Tags do
shared_examples_for 'repository update release' do
context 'on tag with existing release' do
- before do
- release = project.releases.find_or_initialize_by(tag: tag_name)
- release.update(description: description)
+ let!(:release) do
+ create(:release,
+ :legacy,
+ project: project,
+ tag: tag_name,
+ description: description)
end
it 'updates the release description' do
@@ -437,9 +440,9 @@ describe API::Tags do
context 'when tag does not exist' do
let(:tag_name) { 'unknown' }
- it_behaves_like '404 response' do
+ it_behaves_like '403 response' do
let(:request) { put api(route, current_user), params: { description: new_description } }
- let(:message) { 'Tag does not exist' }
+ let(:message) { '403 Forbidden' }
end
end
@@ -464,9 +467,9 @@ describe API::Tags do
end
context 'when release does not exist' do
- it_behaves_like '404 response' do
+ it_behaves_like '403 response' do
let(:request) { put api(route, current_user), params: { description: new_description } }
- let(:message) { 'Release does not exist' }
+ let(:message) { '403 Forbidden' }
end
end
end
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
index 3cc29a7076d..f1514e90eb2 100644
--- a/spec/requests/lfs_http_spec.rb
+++ b/spec/requests/lfs_http_spec.rb
@@ -1123,7 +1123,7 @@ describe 'Git LFS API and storage' do
context 'with valid remote_id' do
before do
- fog_connection.directories.get('lfs-objects').files.create(
+ fog_connection.directories.new(key: 'lfs-objects').files.create(
key: 'tmp/uploads/12312300',
body: 'content'
)
diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb
index cf57776346a..79aa32b29bb 100644
--- a/spec/serializers/pipeline_serializer_spec.rb
+++ b/spec/serializers/pipeline_serializer_spec.rb
@@ -144,7 +144,7 @@ describe PipelineSerializer do
# pipeline. With the same ref this check is cached but if refs are
# different then there is an extra query per ref
# https://gitlab.com/gitlab-org/gitlab-ce/issues/46368
- expect(recorded.count).to be_within(2).of(34)
+ expect(recorded.count).to be_within(2).of(38)
expect(recorded.cached_count).to eq(0)
end
end
diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb
index f2e9799452a..8021bd338e0 100644
--- a/spec/services/auth/container_registry_authentication_service_spec.rb
+++ b/spec/services/auth/container_registry_authentication_service_spec.rb
@@ -5,7 +5,7 @@ describe Auth::ContainerRegistryAuthenticationService do
let(:current_user) { nil }
let(:current_params) { {} }
let(:rsa_key) { OpenSSL::PKey::RSA.generate(512) }
- let(:payload) { JWT.decode(subject[:token], rsa_key).first }
+ let(:payload) { JWT.decode(subject[:token], rsa_key, true, { algorithm: 'RS256' }).first }
let(:authentication_abilities) do
[:read_container_image, :create_container_image, :admin_container_image]
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index ffa47d527f7..87b60387c52 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -19,12 +19,14 @@ describe Ci::CreatePipelineService do
ref: ref_name,
trigger_request: nil,
variables_attributes: nil,
- merge_request: nil)
+ merge_request: nil,
+ push_options: nil)
params = { ref: ref,
before: '00000000',
after: after,
commits: [{ message: message }],
- variables_attributes: variables_attributes }
+ variables_attributes: variables_attributes,
+ push_options: push_options }
described_class.new(project, user, params).execute(
source, trigger_request: trigger_request, merge_request: merge_request)
@@ -357,6 +359,22 @@ describe Ci::CreatePipelineService do
end
end
+ context 'when push options contain ci.skip' do
+ let(:push_options) do
+ ['ci.skip',
+ 'another push option']
+ end
+
+ it 'creates a pipline in the skipped state' do
+ pipeline = execute_service(push_options: push_options)
+
+ # TODO: DRY these up with "skips builds creation if the commit message"
+ expect(pipeline).to be_persisted
+ expect(pipeline.builds.any?).to be false
+ expect(pipeline.status).to eq("skipped")
+ end
+ end
+
context 'when there are no jobs for this pipeline' do
before do
config = YAML.dump({ test: { script: 'ls', only: ['feature'] } })
@@ -667,7 +685,7 @@ describe Ci::CreatePipelineService do
stub_ci_pipeline_yaml_file(YAML.dump(config))
end
- let(:ref_name) { 'feature' }
+ let(:ref_name) { 'refs/heads/feature' }
context 'when source is merge request' do
let(:source) { :merge_request }
@@ -696,7 +714,7 @@ describe Ci::CreatePipelineService do
let(:merge_request) do
create(:merge_request,
source_project: project,
- source_branch: ref_name,
+ source_branch: Gitlab::Git.ref_name(ref_name),
target_project: project,
target_branch: 'master')
end
@@ -709,7 +727,7 @@ describe Ci::CreatePipelineService do
end
context 'when ref is tag' do
- let(:ref_name) { 'v1.1.0' }
+ let(:ref_name) { 'refs/tags/v1.1.0' }
it 'does not create a merge request pipeline' do
expect(pipeline).not_to be_persisted
@@ -721,7 +739,7 @@ describe Ci::CreatePipelineService do
let(:merge_request) do
create(:merge_request,
source_project: project,
- source_branch: ref_name,
+ source_branch: Gitlab::Git.ref_name(ref_name),
target_project: target_project,
target_branch: 'master')
end
@@ -786,7 +804,7 @@ describe Ci::CreatePipelineService do
let(:merge_request) do
create(:merge_request,
source_project: project,
- source_branch: ref_name,
+ source_branch: Gitlab::Git.ref_name(ref_name),
target_project: project,
target_branch: 'master')
end
@@ -928,7 +946,7 @@ describe Ci::CreatePipelineService do
let(:merge_request) do
create(:merge_request,
source_project: project,
- source_branch: ref_name,
+ source_branch: Gitlab::Git.ref_name(ref_name),
target_project: project,
target_branch: 'master')
end
diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb
index 56e2a405bcd..9d65ac15213 100644
--- a/spec/services/ci/register_job_service_spec.rb
+++ b/spec/services/ci/register_job_service_spec.rb
@@ -244,7 +244,9 @@ module Ci
context 'when first build is stalled' do
before do
- pending_job.update(lock_version: 0)
+ allow_any_instance_of(Ci::RegisterJobService).to receive(:assign_runner!).and_call_original
+ allow_any_instance_of(Ci::RegisterJobService).to receive(:assign_runner!)
+ .with(pending_job, anything).and_raise(ActiveRecord::StaleObjectError)
end
subject { described_class.new(specific_runner).execute }
diff --git a/spec/services/ci/retry_pipeline_service_spec.rb b/spec/services/ci/retry_pipeline_service_spec.rb
index 55445e71539..75042b29bea 100644
--- a/spec/services/ci/retry_pipeline_service_spec.rb
+++ b/spec/services/ci/retry_pipeline_service_spec.rb
@@ -285,7 +285,7 @@ describe Ci::RetryPipelineService, '#execute' do
end
it 'allows to retry failed pipeline' do
- allow_any_instance_of(Project).to receive(:fetch_branch_allows_collaboration?).and_return(true)
+ allow_any_instance_of(Project).to receive(:branch_allows_collaboration?).and_return(true)
allow_any_instance_of(Project).to receive(:empty_repo?).and_return(false)
service.execute(pipeline)
diff --git a/spec/services/clusters/gcp/kubernetes/create_or_update_namespace_service_spec.rb b/spec/services/clusters/gcp/kubernetes/create_or_update_namespace_service_spec.rb
index fe785735fef..18f218fc236 100644
--- a/spec/services/clusters/gcp/kubernetes/create_or_update_namespace_service_spec.rb
+++ b/spec/services/clusters/gcp/kubernetes/create_or_update_namespace_service_spec.rb
@@ -27,6 +27,8 @@ describe Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService, '#execute' d
stub_kubeclient_get_secret_error(api_url, 'gitlab-token')
stub_kubeclient_create_secret(api_url)
+ stub_kubeclient_get_role_binding(api_url, "gitlab-#{namespace}", namespace: namespace)
+ stub_kubeclient_put_role_binding(api_url, "gitlab-#{namespace}", namespace: namespace)
stub_kubeclient_get_namespace(api_url, namespace: namespace)
stub_kubeclient_get_service_account_error(api_url, "#{namespace}-service-account", namespace: namespace)
stub_kubeclient_create_service_account(api_url, namespace: namespace)
diff --git a/spec/services/create_release_service_spec.rb b/spec/services/create_release_service_spec.rb
deleted file mode 100644
index 1a2dd0b39ee..00000000000
--- a/spec/services/create_release_service_spec.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-require 'spec_helper'
-
-describe CreateReleaseService do
- let(:project) { create(:project, :repository) }
- let(:user) { create(:user) }
- let(:tag_name) { project.repository.tag_names.first }
- let(:description) { 'Awesome release!' }
- let(:service) { described_class.new(project, user) }
- let(:tag) { project.repository.find_tag(tag_name) }
- let(:sha) { tag.dereferenced_target.sha }
-
- it 'creates a new release' do
- result = service.execute(tag_name, description)
- expect(result[:status]).to eq(:success)
- release = project.releases.find_by(tag: tag_name)
- expect(release).not_to be_nil
- expect(release.description).to eq(description)
- expect(release.name).to eq(tag_name)
- expect(release.sha).to eq(sha)
- expect(release.author).to eq(user)
- end
-
- it 'raises an error if the tag does not exist' do
- result = service.execute("foobar", description)
- expect(result[:status]).to eq(:error)
- end
-
- context 'there already exists a release on a tag' do
- before do
- service.execute(tag_name, description)
- end
-
- it 'raises an error and does not update the release' do
- result = service.execute(tag_name, 'The best release!')
- expect(result[:status]).to eq(:error)
- expect(project.releases.find_by(tag: tag_name).description).to eq(description)
- end
- end
-end
diff --git a/spec/services/groups/update_service_spec.rb b/spec/services/groups/update_service_spec.rb
index 84cfa53ea05..d87a7dd234d 100644
--- a/spec/services/groups/update_service_spec.rb
+++ b/spec/services/groups/update_service_spec.rb
@@ -56,7 +56,7 @@ describe Groups::UpdateService do
create(:project, :private, group: internal_group)
expect(TodosDestroyer::GroupPrivateWorker).to receive(:perform_in)
- .with(1.hour, internal_group.id)
+ .with(Todo::WAIT_FOR_DELETE, internal_group.id)
end
it "changes permission level to private" do
diff --git a/spec/services/issuable/bulk_update_service_spec.rb b/spec/services/issuable/bulk_update_service_spec.rb
index f0b0f7956ce..ca366cdf1df 100644
--- a/spec/services/issuable/bulk_update_service_spec.rb
+++ b/spec/services/issuable/bulk_update_service_spec.rb
@@ -28,6 +28,33 @@ describe Issuable::BulkUpdateService do
expect(project.issues.opened).to be_empty
expect(project.issues.closed).not_to be_empty
end
+
+ context 'when issue for a different project is created' do
+ let(:private_project) { create(:project, :private) }
+ let(:issue) { create(:issue, project: private_project, author: user) }
+
+ context 'when user has access to the project' do
+ it 'closes all issues passed' do
+ private_project.add_maintainer(user)
+
+ bulk_update(issues + [issue], state_event: 'close')
+
+ expect(project.issues.opened).to be_empty
+ expect(project.issues.closed).not_to be_empty
+ expect(private_project.issues.closed).not_to be_empty
+ end
+ end
+
+ context 'when user does not have access to project' do
+ it 'only closes all issues that the user has access to' do
+ bulk_update(issues + [issue], state_event: 'close')
+
+ expect(project.issues.opened).to be_empty
+ expect(project.issues.closed).not_to be_empty
+ expect(private_project.issues.closed).to be_empty
+ end
+ end
+ end
end
describe 'reopen issues' do
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index bd519e7f077..ce20bf2bef6 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -77,7 +77,7 @@ describe Issues::UpdateService, :mailer do
end
it 'enqueues ConfidentialIssueWorker when an issue is made confidential' do
- expect(TodosDestroyer::ConfidentialIssueWorker).to receive(:perform_in).with(1.hour, issue.id)
+ expect(TodosDestroyer::ConfidentialIssueWorker).to receive(:perform_in).with(Todo::WAIT_FOR_DELETE, issue.id)
update_issue(confidential: true)
end
diff --git a/spec/services/members/destroy_service_spec.rb b/spec/services/members/destroy_service_spec.rb
index 0a5220c7c61..5aa7165e135 100644
--- a/spec/services/members/destroy_service_spec.rb
+++ b/spec/services/members/destroy_service_spec.rb
@@ -22,7 +22,7 @@ describe Members::DestroyService do
shared_examples 'a service destroying a member' do
before do
type = member.is_a?(GroupMember) ? 'Group' : 'Project'
- expect(TodosDestroyer::EntityLeaveWorker).to receive(:perform_in).with(1.hour, member.user_id, member.source_id, type)
+ expect(TodosDestroyer::EntityLeaveWorker).to receive(:perform_in).with(Todo::WAIT_FOR_DELETE, member.user_id, member.source_id, type)
end
it 'destroys the member' do
diff --git a/spec/services/members/update_service_spec.rb b/spec/services/members/update_service_spec.rb
index 6d19a95ffeb..599ed39ca37 100644
--- a/spec/services/members/update_service_spec.rb
+++ b/spec/services/members/update_service_spec.rb
@@ -20,11 +20,28 @@ describe Members::UpdateService do
shared_examples 'a service updating a member' do
it 'updates the member' do
+ expect(TodosDestroyer::EntityLeaveWorker).not_to receive(:perform_in).with(Todo::WAIT_FOR_DELETE, member.user_id, member.source_id, source.class.name)
+
updated_member = described_class.new(current_user, params).execute(member, permission: permission)
expect(updated_member).to be_valid
expect(updated_member.access_level).to eq(Gitlab::Access::MAINTAINER)
end
+
+ context 'when member is downgraded to guest' do
+ let(:params) do
+ { access_level: Gitlab::Access::GUEST }
+ end
+
+ it 'schedules to delete confidential todos' do
+ expect(TodosDestroyer::EntityLeaveWorker).to receive(:perform_in).with(Todo::WAIT_FOR_DELETE, member.user_id, member.source_id, source.class.name).once
+
+ updated_member = described_class.new(current_user, params).execute(member, permission: permission)
+
+ expect(updated_member).to be_valid
+ expect(updated_member.access_level).to eq(Gitlab::Access::GUEST)
+ end
+ end
end
before do
diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb
index 1894d8c8d0e..536d0d345a4 100644
--- a/spec/services/merge_requests/build_service_spec.rb
+++ b/spec/services/merge_requests/build_service_spec.rb
@@ -3,6 +3,7 @@ require 'spec_helper'
describe MergeRequests::BuildService do
using RSpec::Parameterized::TableSyntax
include RepoHelpers
+ include ProjectForksHelper
let(:project) { create(:project, :repository) }
let(:source_project) { nil }
@@ -49,7 +50,7 @@ describe MergeRequests::BuildService do
describe '#execute' do
it 'calls the compare service with the correct arguments' do
- allow_any_instance_of(described_class).to receive(:branches_valid?).and_return(true)
+ allow_any_instance_of(described_class).to receive(:projects_and_branches_valid?).and_return(true)
expect(CompareService).to receive(:new)
.with(project, Gitlab::Git::BRANCH_REF_PREFIX + source_branch)
.and_call_original
@@ -393,11 +394,27 @@ describe MergeRequests::BuildService do
end
end
+ context 'target_project is set but repo is not accessible by current_user' do
+ let(:target_project) do
+ create(:project, :public, :repository, repository_access_level: ProjectFeature::PRIVATE)
+ end
+
+ it 'sets target project correctly' do
+ expect(merge_request.target_project).to eq(project)
+ end
+ end
+
context 'source_project is set and accessible by current_user' do
let(:source_project) { create(:project, :public, :repository)}
let(:commits) { Commit.decorate([commit_1], project) }
- it 'sets target project correctly' do
+ before do
+ # To create merge requests _from_ a project the user needs at least
+ # developer access
+ source_project.add_developer(user)
+ end
+
+ it 'sets source project correctly' do
expect(merge_request.source_project).to eq(source_project)
end
end
@@ -406,11 +423,43 @@ describe MergeRequests::BuildService do
let(:source_project) { create(:project, :private, :repository)}
let(:commits) { Commit.decorate([commit_1], project) }
- it 'sets target project correctly' do
+ it 'sets source project correctly' do
+ expect(merge_request.source_project).to eq(project)
+ end
+ end
+
+ context 'source_project is set but the user cannot create merge requests from the project' do
+ let(:source_project) do
+ create(:project, :public, :repository, merge_requests_access_level: ProjectFeature::PRIVATE)
+ end
+
+ it 'sets the source_project correctly' do
expect(merge_request.source_project).to eq(project)
end
end
+ context 'target_project is not in the fork network of source_project' do
+ let(:target_project) { create(:project, :public, :repository) }
+
+ it 'adds an error to the merge request' do
+ expect(merge_request.errors[:validate_fork]).to contain_exactly('Source project is not a fork of the target project')
+ end
+ end
+
+ context 'target_project is in the fork network of source project but no longer accessible' do
+ let!(:project) { fork_project(target_project, user, namespace: user.namespace, repository: true) }
+ let(:source_project) { project }
+ let(:target_project) { create(:project, :public, :repository) }
+
+ before do
+ target_project.update(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ it 'sets the target_project correctly' do
+ expect(merge_request.target_project).to eq(project)
+ end
+ end
+
context 'when specifying target branch in the description' do
let(:description) { "A merge request targeting another branch\n\n/target_branch with-codeowners" }
diff --git a/spec/services/projects/lfs_pointers/lfs_download_service_spec.rb b/spec/services/projects/lfs_pointers/lfs_download_service_spec.rb
index d7d7f1874eb..95c9b6e63b8 100644
--- a/spec/services/projects/lfs_pointers/lfs_download_service_spec.rb
+++ b/spec/services/projects/lfs_pointers/lfs_download_service_spec.rb
@@ -4,17 +4,15 @@ describe Projects::LfsPointers::LfsDownloadService do
let(:project) { create(:project) }
let(:oid) { '9e548e25631dd9ce6b43afd6359ab76da2819d6a5b474e66118c7819e1d8b3e8' }
let(:download_link) { "http://gitlab.com/#{oid}" }
- let(:lfs_content) do
- <<~HEREDOC
- whatever
- HEREDOC
- end
+ let(:lfs_content) { SecureRandom.random_bytes(10) }
subject { described_class.new(project) }
before do
allow(project).to receive(:lfs_enabled?).and_return(true)
WebMock.stub_request(:get, download_link).to_return(body: lfs_content)
+
+ allow(Gitlab::CurrentSettings).to receive(:allow_local_requests_from_hooks_and_services?).and_return(false)
end
describe '#execute' do
@@ -32,7 +30,7 @@ describe Projects::LfsPointers::LfsDownloadService do
it 'stores the content' do
subject.execute(oid, download_link)
- expect(File.read(LfsObject.first.file.file.file)).to eq lfs_content
+ expect(File.binread(LfsObject.first.file.file.file)).to eq lfs_content
end
end
@@ -54,18 +52,61 @@ describe Projects::LfsPointers::LfsDownloadService do
end
end
+ context 'when localhost requests are allowed' do
+ let(:download_link) { 'http://192.168.2.120' }
+
+ before do
+ allow(Gitlab::CurrentSettings).to receive(:allow_local_requests_from_hooks_and_services?).and_return(true)
+ end
+
+ it 'downloads the file' do
+ expect(subject).to receive(:download_and_save_file).and_call_original
+
+ expect { subject.execute(oid, download_link) }.to change { LfsObject.count }.by(1)
+ end
+ end
+
context 'when a bad URL is used' do
- where(download_link: ['/etc/passwd', 'ftp://example.com', 'http://127.0.0.2'])
+ where(download_link: ['/etc/passwd', 'ftp://example.com', 'http://127.0.0.2', 'http://192.168.2.120'])
with_them do
it 'does not download the file' do
- expect(subject).not_to receive(:download_and_save_file)
-
expect { subject.execute(oid, download_link) }.not_to change { LfsObject.count }
end
end
end
+ context 'when the URL points to a redirected URL' do
+ context 'that is blocked' do
+ where(redirect_link: ['ftp://example.com', 'http://127.0.0.2', 'http://192.168.2.120'])
+
+ with_them do
+ before do
+ WebMock.stub_request(:get, download_link).to_return(status: 301, headers: { 'Location' => redirect_link })
+ end
+
+ it 'does not follow the redirection' do
+ expect(Rails.logger).to receive(:error).with(/LFS file with oid #{oid} couldn't be downloaded/)
+
+ expect { subject.execute(oid, download_link) }.not_to change { LfsObject.count }
+ end
+ end
+ end
+
+ context 'that is valid' do
+ let(:redirect_link) { "http://example.com/"}
+
+ before do
+ WebMock.stub_request(:get, download_link).to_return(status: 301, headers: { 'Location' => redirect_link })
+ WebMock.stub_request(:get, redirect_link).to_return(body: lfs_content)
+ end
+
+ it 'follows the redirection' do
+ expect { subject.execute(oid, download_link) }.to change { LfsObject.count }.from(0).to(1)
+ end
+ end
+ end
+
context 'when an lfs object with the same oid already exists' do
before do
create(:lfs_object, oid: 'oid')
diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb
index d58ff2cedc0..8adfc63222e 100644
--- a/spec/services/projects/update_service_spec.rb
+++ b/spec/services/projects/update_service_spec.rb
@@ -41,7 +41,7 @@ describe Projects::UpdateService do
end
it 'updates the project to private' do
- expect(TodosDestroyer::ProjectPrivateWorker).to receive(:perform_in).with(1.hour, project.id)
+ expect(TodosDestroyer::ProjectPrivateWorker).to receive(:perform_in).with(Todo::WAIT_FOR_DELETE, project.id)
result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::PRIVATE)
@@ -191,7 +191,7 @@ describe Projects::UpdateService do
context 'when changing feature visibility to private' do
it 'updates the visibility correctly' do
expect(TodosDestroyer::PrivateFeaturesWorker)
- .to receive(:perform_in).with(1.hour, project.id)
+ .to receive(:perform_in).with(Todo::WAIT_FOR_DELETE, project.id)
result = update_project(project, user, project_feature_attributes:
{ issues_access_level: ProjectFeature::PRIVATE }
diff --git a/spec/services/releases/create_service_spec.rb b/spec/services/releases/create_service_spec.rb
new file mode 100644
index 00000000000..612e9f152e7
--- /dev/null
+++ b/spec/services/releases/create_service_spec.rb
@@ -0,0 +1,72 @@
+require 'spec_helper'
+
+describe Releases::CreateService do
+ let(:project) { create(:project, :repository) }
+ let(:user) { create(:user) }
+ let(:tag_name) { project.repository.tag_names.first }
+ let(:tag_sha) { project.repository.find_tag(tag_name).dereferenced_target.sha }
+ let(:name) { 'Bionic Beaver' }
+ let(:description) { 'Awesome release!' }
+ let(:params) { { tag: tag_name, name: name, description: description, ref: ref } }
+ let(:ref) { nil }
+ let(:service) { described_class.new(project, user, params) }
+
+ before do
+ project.add_maintainer(user)
+ end
+
+ describe '#execute' do
+ shared_examples 'a successful release creation' do
+ it 'creates a new release' do
+ result = service.execute
+ expect(result[:status]).to eq(:success)
+ expect(result[:tag]).not_to be_nil
+ expect(result[:release]).not_to be_nil
+ expect(result[:release].description).to eq(description)
+ expect(result[:release].name).to eq(name)
+ expect(result[:release].author).to eq(user)
+ expect(result[:release].sha).to eq(tag_sha)
+ end
+ end
+
+ it_behaves_like 'a successful release creation'
+
+ context 'when the tag does not exist' do
+ let(:tag_name) { 'non-exist-tag' }
+
+ it 'raises an error' do
+ result = service.execute
+
+ expect(result[:status]).to eq(:error)
+ end
+ end
+
+ context 'when ref is provided' do
+ let(:ref) { 'master' }
+ let(:tag_name) { 'foobar' }
+
+ it_behaves_like 'a successful release creation'
+
+ it 'creates a tag if the tag does not exist' do
+ expect(project.repository.ref_exists?("refs/tags/#{tag_name}")).to be_falsey
+
+ result = service.execute
+ expect(result[:status]).to eq(:success)
+ expect(result[:tag]).not_to be_nil
+ expect(result[:release]).not_to be_nil
+ end
+ end
+
+ context 'there already exists a release on a tag' do
+ let!(:release) do
+ create(:release, project: project, tag: tag_name, description: description)
+ end
+
+ it 'raises an error and does not update the release' do
+ result = service.execute
+ expect(result[:status]).to eq(:error)
+ expect(project.releases.find_by(tag: tag_name).description).to eq(description)
+ end
+ end
+ end
+end
diff --git a/spec/services/releases/destroy_service_spec.rb b/spec/services/releases/destroy_service_spec.rb
new file mode 100644
index 00000000000..dd5b8708f36
--- /dev/null
+++ b/spec/services/releases/destroy_service_spec.rb
@@ -0,0 +1,61 @@
+require 'spec_helper'
+
+describe Releases::DestroyService do
+ let(:project) { create(:project, :repository) }
+ let(:mainatiner) { create(:user) }
+ let(:repoter) { create(:user) }
+ let(:tag) { 'v1.1.0' }
+ let!(:release) { create(:release, project: project, tag: tag) }
+ let(:service) { described_class.new(project, user, params) }
+ let(:params) { { tag: tag } }
+ let(:user) { mainatiner }
+
+ before do
+ project.add_maintainer(mainatiner)
+ project.add_reporter(repoter)
+ end
+
+ describe '#execute' do
+ subject { service.execute }
+
+ context 'when there is a release' do
+ it 'removes the release' do
+ expect { subject }.to change { project.releases.count }.by(-1)
+ end
+
+ it 'returns the destroyed object' do
+ is_expected.to include(status: :success, release: release)
+ end
+ end
+
+ context 'when tag is not found' do
+ let(:tag) { 'v1.1.1' }
+
+ it 'returns an error' do
+ is_expected.to include(status: :error,
+ message: 'Tag does not exist',
+ http_status: 404)
+ end
+ end
+
+ context 'when release is not found' do
+ let!(:release) { }
+
+ it 'returns an error' do
+ is_expected.to include(status: :error,
+ message: 'Release does not exist',
+ http_status: 404)
+ end
+ end
+
+ context 'when user does not have permission' do
+ let(:user) { repoter }
+
+ it 'returns an error' do
+ is_expected.to include(status: :error,
+ message: 'Access Denied',
+ http_status: 403)
+ end
+ end
+ end
+end
diff --git a/spec/services/releases/update_service_spec.rb b/spec/services/releases/update_service_spec.rb
new file mode 100644
index 00000000000..6c68f364739
--- /dev/null
+++ b/spec/services/releases/update_service_spec.rb
@@ -0,0 +1,50 @@
+require 'spec_helper'
+
+describe Releases::UpdateService do
+ let(:project) { create(:project, :repository) }
+ let(:user) { create(:user) }
+ let(:new_name) { 'A new name' }
+ let(:new_description) { 'The best release!' }
+ let(:params) { { name: new_name, description: new_description, tag: tag_name } }
+ let(:service) { described_class.new(project, user, params) }
+ let!(:release) { create(:release, project: project, author: user, tag: tag_name) }
+ let(:tag_name) { 'v1.1.0' }
+
+ before do
+ project.add_developer(user)
+ end
+
+ describe '#execute' do
+ shared_examples 'a failed update' do
+ it 'raises an error' do
+ result = service.execute
+ expect(result[:status]).to eq(:error)
+ end
+ end
+
+ it 'successfully updates an existing release' do
+ result = service.execute
+ expect(result[:status]).to eq(:success)
+ expect(result[:release].name).to eq(new_name)
+ expect(result[:release].description).to eq(new_description)
+ end
+
+ context 'when the tag does not exists' do
+ let(:tag_name) { 'foobar' }
+
+ it_behaves_like 'a failed update'
+ end
+
+ context 'when the release does not exist' do
+ let!(:release) { }
+
+ it_behaves_like 'a failed update'
+ end
+
+ context 'with an invalid update' do
+ let(:new_description) { '' }
+
+ it_behaves_like 'a failed update'
+ end
+ end
+end
diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb
index c52515aefd8..253f2e44d10 100644
--- a/spec/services/todo_service_spec.rb
+++ b/spec/services/todo_service_spec.rb
@@ -19,6 +19,7 @@ describe TodoService do
before do
project.add_guest(guest)
project.add_developer(author)
+ project.add_developer(assignee)
project.add_developer(member)
project.add_developer(john_doe)
project.add_developer(skipped)
diff --git a/spec/services/update_release_service_spec.rb b/spec/services/update_release_service_spec.rb
deleted file mode 100644
index dc2d0e2d47a..00000000000
--- a/spec/services/update_release_service_spec.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-require 'spec_helper'
-
-describe UpdateReleaseService do
- let(:project) { create(:project, :repository) }
- let(:user) { create(:user) }
- let(:tag_name) { project.repository.tag_names.first }
- let(:description) { 'Awesome release!' }
- let(:new_description) { 'The best release!' }
- let(:service) { described_class.new(project, user) }
-
- context 'with an existing release' do
- let(:create_service) { CreateReleaseService.new(project, user) }
-
- before do
- create_service.execute(tag_name, description)
- end
-
- it 'successfully updates an existing release' do
- result = service.execute(tag_name, new_description)
- expect(result[:status]).to eq(:success)
- expect(project.releases.find_by(tag: tag_name).description).to eq(new_description)
- end
- end
-
- it 'raises an error if the tag does not exist' do
- result = service.execute("foobar", description)
- expect(result[:status]).to eq(:error)
- end
-
- it 'raises an error if the release does not exist' do
- result = service.execute(tag_name, description)
- expect(result[:status]).to eq(:error)
- end
-end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 4042120e2c2..89357056c93 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -216,11 +216,15 @@ RSpec.configure do |config|
# Each example may call `migrate!`, so we must ensure we are migrated down every time
config.before(:each, :migration) do
+ use_fake_application_settings
+
schema_migrate_down!
end
config.after(:context, :migration) do
schema_migrate_up!
+
+ Gitlab::CurrentSettings.clear_in_memory_application_settings!
end
config.around(:each, :nested_groups) do |example|
diff --git a/spec/support/gitlab_stubs/gitlab_ci.yml b/spec/support/gitlab_stubs/gitlab_ci.yml
index e55a61b2b94..f3755e52b2c 100644
--- a/spec/support/gitlab_stubs/gitlab_ci.yml
+++ b/spec/support/gitlab_stubs/gitlab_ci.yml
@@ -1,9 +1,8 @@
-image: ruby:2.1
+image: ruby:2.6
services:
- postgres
before_script:
- - gem install bundler
- bundle install
- bundle exec rake db:create
diff --git a/spec/support/helpers/fake_blob_helpers.rb b/spec/support/helpers/fake_blob_helpers.rb
index bc9686ed9cf..801ca8b7412 100644
--- a/spec/support/helpers/fake_blob_helpers.rb
+++ b/spec/support/helpers/fake_blob_helpers.rb
@@ -23,7 +23,7 @@ module FakeBlobHelpers
0
end
- def binary?
+ def binary_in_repo?
@binary
end
diff --git a/spec/support/helpers/kubernetes_helpers.rb b/spec/support/helpers/kubernetes_helpers.rb
index 39bd305d88a..e7d97561bfc 100644
--- a/spec/support/helpers/kubernetes_helpers.rb
+++ b/spec/support/helpers/kubernetes_helpers.rb
@@ -58,6 +58,11 @@ module KubernetesHelpers
.to_return(status: [status, "Internal Server Error"])
end
+ def stub_kubeclient_get_service_account(api_url, name, namespace: 'default')
+ WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{namespace}/serviceaccounts/#{name}")
+ .to_return(kube_response({}))
+ end
+
def stub_kubeclient_get_service_account_error(api_url, name, namespace: 'default', status: 404)
WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{namespace}/serviceaccounts/#{name}")
.to_return(status: [status, "Internal Server Error"])
@@ -73,6 +78,11 @@ module KubernetesHelpers
.to_return(status: [500, "Internal Server Error"])
end
+ def stub_kubeclient_put_service_account(api_url, name, namespace: 'default')
+ WebMock.stub_request(:put, api_url + "/api/v1/namespaces/#{namespace}/serviceaccounts/#{name}")
+ .to_return(kube_response({}))
+ end
+
def stub_kubeclient_create_secret(api_url, namespace: 'default')
WebMock.stub_request(:post, api_url + "/api/v1/namespaces/#{namespace}/secrets")
.to_return(kube_response({}))
@@ -93,6 +103,11 @@ module KubernetesHelpers
.to_return(kube_response({}))
end
+ def stub_kubeclient_get_role_binding(api_url, name, namespace: 'default')
+ WebMock.stub_request(:get, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings/#{name}")
+ .to_return(kube_response({}))
+ end
+
def stub_kubeclient_get_role_binding_error(api_url, name, namespace: 'default', status: 404)
WebMock.stub_request(:get, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings/#{name}")
.to_return(status: [status, "Internal Server Error"])
@@ -103,6 +118,11 @@ module KubernetesHelpers
.to_return(kube_response({}))
end
+ def stub_kubeclient_put_role_binding(api_url, name, namespace: 'default')
+ WebMock.stub_request(:put, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings/#{name}")
+ .to_return(kube_response({}))
+ end
+
def stub_kubeclient_create_namespace(api_url)
WebMock.stub_request(:post, api_url + "/api/v1/namespaces")
.to_return(kube_response({}))
diff --git a/spec/support/helpers/migrations_helpers.rb b/spec/support/helpers/migrations_helpers.rb
index 5887c3eab74..cc1a28cb264 100644
--- a/spec/support/helpers/migrations_helpers.rb
+++ b/spec/support/helpers/migrations_helpers.rb
@@ -62,6 +62,22 @@ module MigrationsHelpers
klass.reset_column_information
end
+ # In some migration tests, we're using factories to create records,
+ # however those models might be depending on a schema version which
+ # doesn't have the columns we want in application_settings.
+ # In these cases, we'll need to use the fake application settings
+ # as if we have migrations pending
+ def use_fake_application_settings
+ # We stub this way because we can't stub on
+ # `current_application_settings` due to `method_missing` is
+ # depending on current_application_settings...
+ allow(ActiveRecord::Base.connection)
+ .to receive(:active?)
+ .and_return(false)
+
+ stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
+ end
+
def previous_migration
migrations.each_cons(2) do |previous, migration|
break previous if migration.name == described_class.name
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index d52c40ff4f1..d352a7cdf1a 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -62,7 +62,8 @@ module TestEnv
'between-create-delete-modify-move' => '3f5f443',
'after-create-delete-modify-move' => 'ba3faa7',
'with-codeowners' => '219560e',
- 'submodule_inside_folder' => 'b491b92'
+ 'submodule_inside_folder' => 'b491b92',
+ 'png-lfs' => 'fe42f41'
}.freeze
# gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily
diff --git a/spec/support/shared_contexts/email_shared_blocks.rb b/spec/support/shared_contexts/email_shared_context.rb
index 9d806fc524d..9d806fc524d 100644
--- a/spec/support/shared_contexts/email_shared_blocks.rb
+++ b/spec/support/shared_contexts/email_shared_context.rb
diff --git a/spec/support/shared_examples/policies/clusterable_shared_examples.rb b/spec/support/shared_examples/policies/clusterable_shared_examples.rb
new file mode 100644
index 00000000000..d99f94c76c3
--- /dev/null
+++ b/spec/support/shared_examples/policies/clusterable_shared_examples.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+shared_examples 'clusterable policies' do
+ describe '#add_cluster?' do
+ let(:current_user) { create(:user) }
+
+ subject { described_class.new(current_user, clusterable) }
+
+ context 'with a developer' do
+ before do
+ clusterable.add_developer(current_user)
+ end
+
+ it { expect_disallowed(:add_cluster) }
+ end
+
+ context 'with a maintainer' do
+ before do
+ clusterable.add_maintainer(current_user)
+ end
+
+ context 'with no clusters' do
+ it { expect_allowed(:add_cluster) }
+ end
+
+ context 'with an existing cluster' do
+ before do
+ cluster
+ end
+
+ it { expect_disallowed(:add_cluster) }
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/project_list_shared_examples.rb b/spec/support/shared_examples/project_list_shared_examples.rb
new file mode 100644
index 00000000000..675d489fcab
--- /dev/null
+++ b/spec/support/shared_examples/project_list_shared_examples.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+shared_examples 'shows public projects' do
+ it 'shows projects' do
+ expect(page).to have_content(public_project.title)
+ expect(page).not_to have_content(internal_project.title)
+ expect(page).not_to have_content(private_project.title)
+ expect(page).not_to have_content(archived_project.title)
+ end
+end
+
+shared_examples 'shows public and internal projects' do
+ it 'shows projects' do
+ expect(page).to have_content(public_project.title)
+ expect(page).to have_content(internal_project.title)
+ expect(page).not_to have_content(private_project.title)
+ expect(page).not_to have_content(archived_project.title)
+ end
+end
diff --git a/spec/uploaders/file_uploader_spec.rb b/spec/uploaders/file_uploader_spec.rb
index c74e0bf1955..db9e5eb2ad6 100644
--- a/spec/uploaders/file_uploader_spec.rb
+++ b/spec/uploaders/file_uploader_spec.rb
@@ -201,7 +201,7 @@ describe FileUploader do
end
let!(:fog_file) do
- fog_connection.directories.get('uploads').files.create(
+ fog_connection.directories.new(key: 'uploads').files.create(
key: 'tmp/uploads/test/123123',
body: 'content'
)
diff --git a/spec/uploaders/object_storage_spec.rb b/spec/uploaders/object_storage_spec.rb
index 7e673681c31..533e9d87ea6 100644
--- a/spec/uploaders/object_storage_spec.rb
+++ b/spec/uploaders/object_storage_spec.rb
@@ -716,7 +716,7 @@ describe ObjectStorage do
end
let!(:fog_file) do
- fog_connection.directories.get('uploads').files.create(
+ fog_connection.directories.new(key: 'uploads').files.create(
key: 'tmp/uploads/test/123123',
body: 'content'
)
diff --git a/spec/workers/mail_scheduler/notification_service_worker_spec.rb b/spec/workers/mail_scheduler/notification_service_worker_spec.rb
index f725c8763a0..1033557ee88 100644
--- a/spec/workers/mail_scheduler/notification_service_worker_spec.rb
+++ b/spec/workers/mail_scheduler/notification_service_worker_spec.rb
@@ -17,10 +17,21 @@ describe MailScheduler::NotificationServiceWorker do
end
context 'when the arguments cannot be deserialized' do
- it 'does nothing' do
- expect(worker.notification_service).not_to receive(method)
+ context 'when the arguments are not deserializeable' do
+ it 'raises exception' do
+ expect(worker.notification_service).not_to receive(method)
+ expect { worker.perform(method, key.to_global_id.to_s.succ) }.to raise_exception(ArgumentError)
+ end
+ end
+
+ context 'when the arguments are deserializeable' do
+ it 'does nothing' do
+ serialized_arguments = *serialize(key)
+ key.destroy!
- worker.perform(method, key.to_global_id.to_s.succ)
+ expect(worker.notification_service).not_to receive(method)
+ expect { worker.perform(method, serialized_arguments) }.not_to raise_exception
+ end
end
end