Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-09-03 00:10:23 +00:00
parent 36e64e679d
commit 3bbc597f60
16 changed files with 207 additions and 90 deletions

View file

@ -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"

View file

@ -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])

View file

@ -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)')

View file

@ -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) }

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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)

View file

@ -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 ""

View file

@ -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

View file

@ -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();

View file

@ -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

View file

@ -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)

View file

@ -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_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_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_actively_used_token,
old_formerly_used_token
)
end
end
it 'updates updated_at' do

View file

@ -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

View file

@ -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) }