diff options
author | rubenmoya <rmoyarodriguez@gmail.com> | 2019-01-05 09:40:05 +0100 |
---|---|---|
committer | rubenmoya <rmoyarodriguez@gmail.com> | 2019-01-05 09:40:05 +0100 |
commit | cf5a9d2993c2998e6394560f5c4fe2fef3f35b1c (patch) | |
tree | dbd6f1c6a9c7878122f485300795d5b4b5b621e8 /spec | |
parent | 2269061e7151718d750bef4bbf1348dae8ac8a4a (diff) | |
parent | d432d674148601555c4ba693bb7c282ac9fe3d4a (diff) | |
download | gitlab-ce-cf5a9d2993c2998e6394560f5c4fe2fef3f35b1c.tar.gz |
Merge branch 'master' into 54311-fix-board-add-label
Diffstat (limited to 'spec')
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)<img src=x onerror=alert(1)>' } let(:user_xss_title) { 'eve <img src=x onerror=alert(2)<img src=x onerror=alert(1)>' } + let(:label_xss_title) { 'alert label <img src=x onerror="alert(\'Hello xss\');" a'} + let(:milestone_xss_title) { 'alert milestone <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 Binary files differindex 730ce3dc5f8..dd49f4e9e1d 100644 --- a/spec/fixtures/security-reports/feature-branch.zip +++ b/spec/fixtures/security-reports/feature-branch.zip 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 Binary files differindex 4684aecb738..2261b5a1674 100644 --- a/spec/fixtures/security-reports/master.zip +++ b/spec/fixtures/security-reports/master.zip 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: '<html>', project: project) } + + it 'links to a valid reference' do + doc = reference_filter('See ~"<html>"') + + 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}"<non valid>") + + 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 |