diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-09-03 03:10:23 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-09-03 03:10:23 +0300 |
commit | 3bbc597f60f05baae319cc541564bf41639fd14e (patch) | |
tree | 3454a662854d115ad6f5a5ffc1afb21bc4fde008 | |
parent | 36e64e679dfc3e5989445f06aa238962df6ca98d (diff) |
Add latest changes from gitlab-org/gitlab@master
16 files changed, 207 insertions, 90 deletions
diff --git a/app/assets/javascripts/access_tokens/components/access_token_table_app.vue b/app/assets/javascripts/access_tokens/components/access_token_table_app.vue index 59f0e0dd17d..461b2dad479 100644 --- a/app/assets/javascripts/access_tokens/components/access_token_table_app.vue +++ b/app/assets/javascripts/access_tokens/components/access_token_table_app.vue @@ -140,6 +140,7 @@ export default { <template #cell(action)="{ item: { revokePath } }"> <gl-button + v-if="revokePath" category="tertiary" :aria-label="$options.i18n.revokeButton" :data-confirm="modalMessage" diff --git a/app/finders/environments/environments_finder.rb b/app/finders/environments/environments_finder.rb index 46c49f096c6..f2dcba04349 100644 --- a/app/finders/environments/environments_finder.rb +++ b/app/finders/environments/environments_finder.rb @@ -14,6 +14,7 @@ module Environments def execute environments = project.environments + environments = by_type(environments) environments = by_name(environments) environments = by_search(environments) environments = by_ids(environments) @@ -24,6 +25,12 @@ module Environments private + def by_type(environments) + return environments unless params[:type].present? + + environments.for_type(params[:type]) + end + def by_name(environments) if params[:name].present? environments.for_name(params[:name]) diff --git a/app/models/environment.rb b/app/models/environment.rb index 1950431446b..bc1b2daf3db 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -89,6 +89,7 @@ class Environment < ApplicationRecord scope :for_project, -> (project) { where(project_id: project) } scope :for_tier, -> (tier) { where(tier: tier).where.not(tier: nil) } + scope :for_type, -> (type) { where(environment_type: type) } scope :unfoldered, -> { where(environment_type: nil) } scope :with_rank, -> do select('environments.*, rank() OVER (PARTITION BY project_id ORDER BY id DESC)') diff --git a/app/models/personal_access_token.rb b/app/models/personal_access_token.rb index 8fc93e6f954..ed996589489 100644 --- a/app/models/personal_access_token.rb +++ b/app/models/personal_access_token.rb @@ -24,6 +24,7 @@ class PersonalAccessToken < ApplicationRecord scope :expiring_and_not_notified, ->(date) { where(["revoked = false AND expire_notification_delivered = false AND expires_at >= CURRENT_DATE AND expires_at <= ?", date]) } scope :expired_today_and_not_notified, -> { where(["revoked = false AND expires_at = CURRENT_DATE AND after_expiry_notification_delivered = false"]) } scope :inactive, -> { where("revoked = true OR expires_at < CURRENT_DATE") } + scope :created_before, -> (date) { where("personal_access_tokens.created_at < :date", date: date) } scope :last_used_before, -> (date) { where("personal_access_tokens.created_at < :date AND (last_used_at < :date OR last_used_at IS NULL)", date: date) } scope :with_impersonation, -> { where(impersonation: true) } scope :without_impersonation, -> { where(impersonation: false) } diff --git a/doc/administration/operations/rails_console.md b/doc/administration/operations/rails_console.md index 235f34e8de5..627dfbeb66c 100644 --- a/doc/administration/operations/rails_console.md +++ b/doc/administration/operations/rails_console.md @@ -218,6 +218,28 @@ instance_of_object.method(:foo).source_location project.method(:private?).source_location ``` +## Limiting output + +Adding a semicolon(`;`) and a follow-up statement at the end of a statement prevents the default implicit return output. This can be used if you are already explicitly printing details and potentially have a lot of return output: + +```ruby +puts ActiveRecord::Base.descendants; :ok +Project.select(&:pages_deployed?).each {|p| puts p.pages_url }; true +``` + +## Get or store the result of last operation + +Underscore(`_`) represents the implicit return of the previous statement. You can use this to quickly assign a variable from the output of the previous command: + +```ruby +Project.last +# => #<Project id:2537 root/discard>> +project = _ +# => #<Project id:2537 root/discard>> +project.id +# => 2537 +``` + ## Active Record objects ### Looking up database-persisted objects @@ -347,6 +369,15 @@ D, [2020-03-05T17:18:30.406047 #910] DEBUG -- : User Load (2.6ms) SELECT "use For more on different ways to retrieve data from the database using Active Record, please see the [Active Record Query Interface documentation](https://guides.rubyonrails.org/active_record_querying.html). +## Query the database using an Active Record model + +```ruby +m = Model.where('attribute like ?', 'ex%') + +# for example to query the projects +projects = Project.where('path like ?', 'Oumua%') +``` + ### Modifying Active Record objects In the previous section, we learned about retrieving database records using diff --git a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md index 4f602ecf6e3..7d2b4a58955 100644 --- a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md +++ b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md @@ -62,28 +62,6 @@ Notify.test_email(e, "Test email for #{n}", 'Test email').deliver_now Notify.test_email(u.email, "Test email for #{u.name}", 'Test email').deliver_now ``` -## Limiting output - -Adding a semicolon(`;`) and a follow-up statement at the end of a statement prevents the default implicit return output. This can be used if you are already explicitly printing details and potentially have a lot of return output: - -```ruby -puts ActiveRecord::Base.descendants; :ok -Project.select(&:pages_deployed?).each {|p| puts p.pages_url }; true -``` - -## Get or store the result of last operation - -Underscore(`_`) represents the implicit return of the previous statement. You can use this to quickly assign a variable from the output of the previous command: - -```ruby -Project.last -# => #<Project id:2537 root/discard>> -project = _ -# => #<Project id:2537 root/discard>> -project.id -# => 2537 -``` - ## Open object in `irb` Sometimes it is easier to go through a method if you are in the context of the object. You can shim into the namespace of `Object` to let you open `irb` in the context of any object: @@ -99,15 +77,6 @@ irb(#<Project>)> web_url # => "https://gitlab-example/root/discard" ``` -## Query the database using an ActiveRecord Model - -```ruby -m = Model.where('attribute like ?', 'ex%') - -# for example to query the projects -projects = Project.where('path like ?', 'Oumua%') -``` - ## View all keys in cache ```ruby diff --git a/doc/user/application_security/get-started-security.md b/doc/user/application_security/get-started-security.md index 4c2b971b5fa..9d98675c2df 100644 --- a/doc/user/application_security/get-started-security.md +++ b/doc/user/application_security/get-started-security.md @@ -6,25 +6,33 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Get started with GitLab application security **(ULTIMATE)** -Complete the following steps to get the most from GitLab application security tools. +The following steps will help you get the most from GitLab application security tools. These steps are a recommended order of operations. You can choose to implement capabilities in a different order or omit features that do not apply to your specific needs. -1. Enable [Secret Detection](secret_detection/index.md) scanning for your default branch. -1. Enable [Dependency Scanning](dependency_scanning/index.md) for your default branch so you can start identifying existing +1. Enable [Secret Detection](secret_detection/index.md) and [Dependency Scanning](dependency_scanning/index.md) + to identify any leaked secrets and vulnerable packages in your codebase. + + - For all security scanners, enable them by updating your `[.gitlab-ci.yml](../../ci/yaml/gitlab_ci_yaml.md)` directly on your `default` branch. This creates a baseline scan of your `default` branch, which is necessary for + feature branch scans to be compared against. This allows [merge requests](../project/merge_requests/index.md) + to display only newly-introduced vulnerabilities. Otherwise, merge requests will display every + vulnerability in the branch, regardless of whether it was introduced by a change in the branch. + - If you are after simplicity, enable only Secret Detection first. It only has one analyzer, + no build requirements, and relatively simple findings: is this a secret or not? + - It is good practice to enable Dependency Scanning early so you can start identifying existing vulnerable packages in your codebase. -1. Add security scans to feature branch pipelines. The same scans should be enabled as are running - on your default branch. Subsequent scans will show only new vulnerabilities by comparing the feature branch to the default branch results. 1. Let your team get comfortable with [vulnerability reports](vulnerability_report/index.md) and establish a vulnerability triage workflow. 1. Consider creating [labels](../project/labels.md) and [issue boards](../project/issue_board.md) to help manage issues created from vulnerabilities. Issue boards allow all stakeholders to have a - common view of all issues. + common view of all issues and track remediation progress. +1. Use [scheduled pipelines](../../ci/pipelines/schedules.md#scheduled-pipelines) to regularly scan important branches such as `default` or those used for maintenance releases. + - Running regular dependency and [container scans](container_scanning/index.md) will surface newly-discovered vulnerabilities that already exist in your repository. + - Scheduled scans are most useful for projects or important branches with low development activity where pipeline scans are infrequent. 1. Create a [scan result policy](policies/index.md) to limit new vulnerabilities from being merged - into your default branch. + into your `default` branch. 1. Monitor the [Security Dashboard](security_dashboard/index.md) trends to gauge success in remediating existing vulnerabilities and preventing the introduction of new ones. 1. Enable other scan types such as [SAST](sast/index.md), [DAST](dast/index.md), [Fuzz testing](coverage_fuzzing/index.md), or [Container Scanning](container_scanning/index.md). - Be sure to add the same scan types to both feature pipelines and default branch pipelines. 1. Use [Compliance Pipelines](../../user/project/settings/index.md#compliance-pipeline-configuration) or [Scan Execution Policies](policies/scan-execution-policies.md) to enforce required scan types and ensure separation of duties between security and engineering. diff --git a/lib/gitlab/cleanup/unused_personal_access_tokens.rb b/lib/gitlab/cleanup/personal_access_tokens.rb index 97bf808a0ca..88665e6ada5 100644 --- a/lib/gitlab/cleanup/unused_personal_access_tokens.rb +++ b/lib/gitlab/cleanup/personal_access_tokens.rb @@ -2,31 +2,23 @@ module Gitlab module Cleanup - # Unused active Personal Access Tokens pose a risk to organizations - # in that they may have been, or may be, leaked to unauthorized - # individuals. They are likely providing little / no current value - # because they are not actively being used, and should therefore be - # proactively revoked. - class UnusedPersonalAccessTokens - # By default tokens that haven't been used for over 1 year will - # be revoked + class PersonalAccessTokens + # By default tokens that haven't been used for over 1 year will be revoked DEFAULT_TIME_PERIOD = 1.year - # To prevent inadvertently revoking actively used tokens, we - # provide a minimum time + # To prevent inadvertently revoking all tokens, we provide a minimum time MINIMUM_TIME_PERIOD = 1.day - attr_reader :logger, :last_used_before, :revocation_time, :group + attr_reader :logger, :cut_off_date, :revocation_time, :group - def initialize(last_used_before: DEFAULT_TIME_PERIOD.ago.beginning_of_day, logger: nil, group_full_path:) - # binding.pry - # Ensure last_used_before is a Time and far enough in the past - @last_used_before = last_used_before + def initialize(cut_off_date: DEFAULT_TIME_PERIOD.ago.beginning_of_day, logger: nil, group_full_path:) + @cut_off_date = cut_off_date # rubocop: disable CodeReuse/ActiveRecord @group = Group.find_by_full_path(group_full_path) # rubocop: enable CodeReuse/ActiveRecord + raise "Group with full_path #{group_full_path} not found" unless @group - raise "Invalid time: #{@last_used_before}" unless @last_used_before <= MINIMUM_TIME_PERIOD.ago + raise "Invalid time: #{@cut_off_date}" unless @cut_off_date <= MINIMUM_TIME_PERIOD.ago # Use a static revocation time to make correlation of revoked # tokens easier, should it be needed. @@ -36,27 +28,21 @@ module Gitlab raise "Invalid logger: #{@logger}" unless @logger.respond_to?(:info) && @logger.respond_to?(:warn) end - # Revokes unused personal access tokens. - # A dry run is performed by default, logging what would be - # revoked. Pass `dry_run: false` explicitly to revoke tokens. - def run!(dry_run: true) + def run!(dry_run: true, revoke_active_tokens: false) # rubocop:disable Rails/Output if dry_run puts "Dry running. No changes will be made" + elsif revoke_active_tokens + puts "Revoking used and unused access tokens created before #{cut_off_date}..." else - puts "Revoking access tokens from before #{last_used_before}..." + puts "Revoking access tokens last used and created before #{cut_off_date}..." end # rubocop:enable Rails/Output - logger.info( - dry_run: dry_run, - group_full_path: group.full_path, - message: "Looking for Personal Access Tokens " \ - "last used before #{last_used_before}..." - ) + tokens_to_revoke = revocable_tokens(revoke_active_tokens) # rubocop:disable Cop/InBatches - revocable_tokens.in_batches do |access_tokens| + tokens_to_revoke.in_batches do |access_tokens| revoke_batch(access_tokens, dry_run) end # rubocop:enable Cop/InBatches @@ -64,12 +50,20 @@ module Gitlab private - def revocable_tokens - PersonalAccessToken - .active - .owner_is_human - .last_used_before(last_used_before) - .for_users(group.users) + def revocable_tokens(revoke_active_tokens) + if revoke_active_tokens + PersonalAccessToken + .active + .owner_is_human + .created_before(cut_off_date) + .for_users(group.users) + else + PersonalAccessToken + .active + .owner_is_human + .last_used_before(cut_off_date) + .for_users(group.users) + end end def revoke_batch(access_tokens, dry_run) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index d946108264e..bcfc87fa078 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -11576,6 +11576,9 @@ msgstr "" msgid "CycleAnalytics|Average time to completion" msgstr "" +msgid "CycleAnalytics|Average time to completion (days)" +msgstr "" + msgid "CycleAnalytics|Change Failure Rate" msgstr "" diff --git a/spec/finders/environments/environments_finder_spec.rb b/spec/finders/environments/environments_finder_spec.rb index 71d10ceb5d3..04fbd4067b4 100644 --- a/spec/finders/environments/environments_finder_spec.rb +++ b/spec/finders/environments/environments_finder_spec.rb @@ -6,8 +6,8 @@ RSpec.describe Environments::EnvironmentsFinder do let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { project.creator } let_it_be(:environment) { create(:environment, :available, project: project) } - let_it_be(:environment_stopped) { create(:environment, :stopped, name: 'test2', project: project) } - let_it_be(:environment_available) { create(:environment, :available, name: 'test3', project: project) } + let_it_be(:environment_stopped) { create(:environment, :stopped, name: 'test/test2', project: project) } + let_it_be(:environment_available) { create(:environment, :available, name: 'test/test3', project: project) } before do project.add_maintainer(user) @@ -65,5 +65,11 @@ RSpec.describe Environments::EnvironmentsFinder do expect(result).to contain_exactly(environment_available) end end + + it 'filters environments by type' do + result = described_class.new(project, user, type: 'test').execute + + expect(result).to contain_exactly(environment_stopped, environment_available) + end end end diff --git a/spec/frontend/access_tokens/components/access_token_table_app_spec.js b/spec/frontend/access_tokens/components/access_token_table_app_spec.js index 6013fa3ec39..aed3db4aa4c 100644 --- a/spec/frontend/access_tokens/components/access_token_table_app_spec.js +++ b/spec/frontend/access_tokens/components/access_token_table_app_spec.js @@ -190,6 +190,21 @@ describe('~/access_tokens/components/access_token_table_app', () => { expect(button.props('category')).toBe('tertiary'); }); + describe('revoke path', () => { + beforeEach(() => { + createComponent({ showRole: true }); + }); + + it.each([{ revoke_path: null }, { revoke_path: undefined }])( + 'with %p, does not show revoke button', + async (input) => { + await triggerSuccess(defaultActiveAccessTokens.map((data) => ({ ...data, ...input }))); + + expect(findCells().at(6).findComponent(GlButton).exists()).toBe(false); + }, + ); + }); + it('sorts rows alphabetically', async () => { createComponent({ showRole: true }); await triggerSuccess(); diff --git a/spec/lib/error_tracking/sentry_client/issue_spec.rb b/spec/lib/error_tracking/sentry_client/issue_spec.rb index d7bb0ca5c9a..8c02557b57b 100644 --- a/spec/lib/error_tracking/sentry_client/issue_spec.rb +++ b/spec/lib/error_tracking/sentry_client/issue_spec.rb @@ -151,7 +151,7 @@ RSpec.describe ErrorTracking::SentryClient::Issue do context 'with older sentry versions where keys are not present' do let(:sentry_api_response) do - issues_sample_response[0...1].map do |issue| + issues_sample_response.first(1).map do |issue| issue[:project].delete(:id) issue end @@ -167,7 +167,7 @@ RSpec.describe ErrorTracking::SentryClient::Issue do context 'when essential keys are missing in API response' do let(:sentry_api_response) do - issues_sample_response[0...1].map do |issue| + issues_sample_response.first(1).map do |issue| issue.except(:id) end end diff --git a/spec/lib/error_tracking/sentry_client/projects_spec.rb b/spec/lib/error_tracking/sentry_client/projects_spec.rb index 247f9c1c085..81b44d0f6b1 100644 --- a/spec/lib/error_tracking/sentry_client/projects_spec.rb +++ b/spec/lib/error_tracking/sentry_client/projects_spec.rb @@ -38,7 +38,7 @@ RSpec.describe ErrorTracking::SentryClient::Projects do context 'essential keys missing in API response' do let(:sentry_api_response) do - projects_sample_response[0...1].map do |project| + projects_sample_response.first(1).map do |project| project.except(:slug) end end @@ -50,7 +50,7 @@ RSpec.describe ErrorTracking::SentryClient::Projects do context 'optional keys missing in sentry response' do let(:sentry_api_response) do - projects_sample_response[0...1].map do |project| + projects_sample_response.first(1).map do |project| project[:organization].delete(:id) project.delete(:id) project.except(:status) diff --git a/spec/lib/gitlab/cleanup/unused_personal_access_tokens_spec.rb b/spec/lib/gitlab/cleanup/personal_access_tokens_spec.rb index eb83ced0e21..36c5d0e9b0c 100644 --- a/spec/lib/gitlab/cleanup/unused_personal_access_tokens_spec.rb +++ b/spec/lib/gitlab/cleanup/personal_access_tokens_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Gitlab::Cleanup::UnusedPersonalAccessTokens do +RSpec.describe Gitlab::Cleanup::PersonalAccessTokens do let_it_be(:group) { create(:group) } let_it_be(:subgroup) { create(:group, parent: group) } let_it_be(:project_bot) { create(:user, :project_bot) } @@ -16,6 +16,10 @@ RSpec.describe Gitlab::Cleanup::UnusedPersonalAccessTokens do create(:personal_access_token, created_at: last_used_at - 1.minute) end + let!(:old_actively_used_token) do + create(:personal_access_token, created_at: last_used_at - 1.minute, last_used_at: 1.day.ago) + end + let!(:old_unused_token_for_non_group_member) do create(:personal_access_token, created_at: last_used_at - 1.minute) end @@ -37,6 +41,7 @@ RSpec.describe Gitlab::Cleanup::UnusedPersonalAccessTokens do before do group.add_member(old_formerly_used_token.user, Gitlab::Access::DEVELOPER) + group.add_member(old_actively_used_token.user, Gitlab::Access::DEVELOPER) group.add_member(unused_token.user, Gitlab::Access::DEVELOPER) group.add_member(old_unused_token.user, Gitlab::Access::DEVELOPER) group.add_member(project_bot, Gitlab::Access::MAINTAINER) @@ -47,7 +52,7 @@ RSpec.describe Gitlab::Cleanup::UnusedPersonalAccessTokens do subject do described_class.new( logger: logger, - last_used_before: last_used_at, + cut_off_date: last_used_at, group_full_path: group_full_path ) end @@ -76,16 +81,42 @@ RSpec.describe Gitlab::Cleanup::UnusedPersonalAccessTokens do context 'in a real run' do let(:args) { { dry_run: false } } - it 'revokes only revocable tokens' do - subject.run!(**args) + context 'when revoking unused tokens' do + it 'revokes human-owned tokens created and last used over 1 year ago' do + subject.run!(**args) + + expect(PersonalAccessToken.active).to contain_exactly( + unused_token, + old_actively_used_token, + old_unused_project_access_token, + old_unused_token_for_non_group_member, + old_unused_token_for_subgroup_member + ) + expect(PersonalAccessToken.revoked).to contain_exactly( + old_unused_token, + old_formerly_used_token + ) + end + end + + context 'when revoking used and unused tokens' do + let(:args) { { dry_run: false, revoke_active_tokens: true } } + + it 'revokes human-owned tokens created over 1 year ago' do + subject.run!(**args) - expect(PersonalAccessToken.active).to contain_exactly( - unused_token, - old_unused_project_access_token, - old_unused_token_for_non_group_member, - old_unused_token_for_subgroup_member - ) - expect(PersonalAccessToken.revoked).to contain_exactly(old_unused_token, old_formerly_used_token) + expect(PersonalAccessToken.active).to contain_exactly( + unused_token, + old_unused_project_access_token, + old_unused_token_for_non_group_member, + old_unused_token_for_subgroup_member + ) + expect(PersonalAccessToken.revoked).to contain_exactly( + old_unused_token, + old_actively_used_token, + old_formerly_used_token + ) + end end it 'updates updated_at' do diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index 3f4372dafd0..12b0691420e 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -318,6 +318,16 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do end end + describe '.for_type' do + it 'filters by type' do + create(:environment) + create(:environment, name: 'type1/prod') + env = create(:environment, name: 'type2/prod') + + expect(described_class.for_type('type2')).to contain_exactly(env) + end + end + describe '#guess_tier' do using RSpec::Parameterized::TableSyntax diff --git a/spec/models/personal_access_token_spec.rb b/spec/models/personal_access_token_spec.rb index 9a2a35c0839..68230e81c50 100644 --- a/spec/models/personal_access_token_spec.rb +++ b/spec/models/personal_access_token_spec.rb @@ -65,6 +65,46 @@ RSpec.describe PersonalAccessToken do end end + describe '.created_before' do + let(:last_used_at) { 1.month.ago.beginning_of_hour } + let!(:new_used_token) do + create(:personal_access_token, + created_at: last_used_at + 1.minute, + last_used_at: last_used_at + 1.minute + ) + end + + let!(:old_unused_token) do + create(:personal_access_token, + created_at: last_used_at - 1.minute + ) + end + + let!(:old_formerly_used_token) do + create(:personal_access_token, + created_at: last_used_at - 1.minute, + last_used_at: last_used_at - 1.minute + ) + end + + let!(:old_still_used_token) do + create(:personal_access_token, + created_at: last_used_at - 1.minute, + last_used_at: 1.minute.ago + ) + end + + subject { described_class.created_before(last_used_at) } + + it do + is_expected.to contain_exactly( + old_unused_token, + old_formerly_used_token, + old_still_used_token + ) + end + end + describe '.last_used_before' do let(:last_used_at) { 1.month.ago.beginning_of_hour } let!(:unused_token) { create(:personal_access_token) } |