Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
3b69a04945
commit
7e87cfd402
|
@ -386,7 +386,7 @@ export default {
|
|||
data-testid="aws-guidance-tip"
|
||||
@dismiss="dismissTip"
|
||||
>
|
||||
<div class="gl-display-flex gl-flex-direction-row">
|
||||
<div class="gl-display-flex gl-flex-direction-row gl-flex-wrap-wrap gl-md-flex-wrap-nowrap">
|
||||
<div>
|
||||
<p>
|
||||
<gl-sprintf :message="$options.awsTipMessage">
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<script>
|
||||
import { GlTabs, GlTab } from '@gitlab/ui';
|
||||
import { isString } from 'lodash';
|
||||
import { __ } from '~/locale';
|
||||
import GroupsStore from '../store/groups_store';
|
||||
import GroupsService from '../service/groups_service';
|
||||
|
@ -17,7 +18,7 @@ export default {
|
|||
return {
|
||||
tabs: [
|
||||
{
|
||||
title: this.$options.i18n.subgroupsAndProjects,
|
||||
title: this.$options.i18n[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS],
|
||||
key: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS,
|
||||
renderEmptyState: true,
|
||||
lazy: false,
|
||||
|
@ -25,7 +26,7 @@ export default {
|
|||
store: new GroupsStore({ showSchemaMarkup: true }),
|
||||
},
|
||||
{
|
||||
title: this.$options.i18n.sharedProjects,
|
||||
title: this.$options.i18n[ACTIVE_TAB_SHARED],
|
||||
key: ACTIVE_TAB_SHARED,
|
||||
renderEmptyState: false,
|
||||
lazy: true,
|
||||
|
@ -33,7 +34,7 @@ export default {
|
|||
store: new GroupsStore(),
|
||||
},
|
||||
{
|
||||
title: this.$options.i18n.archivedProjects,
|
||||
title: this.$options.i18n[ACTIVE_TAB_ARCHIVED],
|
||||
key: ACTIVE_TAB_ARCHIVED,
|
||||
renderEmptyState: false,
|
||||
lazy: true,
|
||||
|
@ -44,18 +45,40 @@ export default {
|
|||
activeTabIndex: 0,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
const activeTabIndex = this.tabs.findIndex((tab) => tab.key === this.$route.name);
|
||||
|
||||
if (activeTabIndex === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.activeTabIndex = activeTabIndex;
|
||||
},
|
||||
methods: {
|
||||
handleTabInput(tabIndex) {
|
||||
if (tabIndex === this.activeTabIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.activeTabIndex = tabIndex;
|
||||
|
||||
const tab = this.tabs[tabIndex];
|
||||
tab.lazy = false;
|
||||
|
||||
// Vue router will convert `/` to `%2F` if you pass a string as a param
|
||||
// If you pass an array as a param it will concatenate them with a `/`
|
||||
// This makes sure we are always passing an array for the group param
|
||||
const groupParam = isString(this.$route.params.group)
|
||||
? this.$route.params.group.split('/')
|
||||
: this.$route.params.group;
|
||||
|
||||
this.$router.push({ name: tab.key, params: { group: groupParam } });
|
||||
},
|
||||
},
|
||||
i18n: {
|
||||
subgroupsAndProjects: __('Subgroups and projects'),
|
||||
sharedProjects: __('Shared projects'),
|
||||
archivedProjects: __('Archived projects'),
|
||||
[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS]: __('Subgroups and projects'),
|
||||
[ACTIVE_TAB_SHARED]: __('Shared projects'),
|
||||
[ACTIVE_TAB_ARCHIVED]: __('Archived projects'),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import Vue from 'vue';
|
||||
import VueRouter from 'vue-router';
|
||||
import { GlToast } from '@gitlab/ui';
|
||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import GroupFolder from './components/group_folder.vue';
|
||||
|
@ -10,6 +11,22 @@ import {
|
|||
} from './constants';
|
||||
import OverviewTabs from './components/overview_tabs.vue';
|
||||
|
||||
export const createRouter = () => {
|
||||
const routes = [
|
||||
{ name: ACTIVE_TAB_SHARED, path: '/groups/:group*/-/shared' },
|
||||
{ name: ACTIVE_TAB_ARCHIVED, path: '/groups/:group*/-/archived' },
|
||||
{ name: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, path: '/:group*' },
|
||||
];
|
||||
|
||||
const router = new VueRouter({
|
||||
routes,
|
||||
mode: 'history',
|
||||
base: '/',
|
||||
});
|
||||
|
||||
return router;
|
||||
};
|
||||
|
||||
export const initGroupOverviewTabs = () => {
|
||||
const el = document.getElementById('js-group-overview-tabs');
|
||||
|
||||
|
@ -18,6 +35,9 @@ export const initGroupOverviewTabs = () => {
|
|||
Vue.component('GroupFolder', GroupFolder);
|
||||
Vue.component('GroupItem', GroupItem);
|
||||
Vue.use(GlToast);
|
||||
Vue.use(VueRouter);
|
||||
|
||||
const router = createRouter();
|
||||
|
||||
const {
|
||||
newSubgroupPath,
|
||||
|
@ -35,6 +55,7 @@ export const initGroupOverviewTabs = () => {
|
|||
|
||||
return new Vue({
|
||||
el,
|
||||
router,
|
||||
provide: {
|
||||
newSubgroupPath,
|
||||
newProjectPath,
|
||||
|
|
|
@ -349,3 +349,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gl-md-flex-wrap-nowrap.gl-md-flex-wrap-nowrap {
|
||||
@include gl-media-breakpoint-up(md) {
|
||||
@include gl-flex-wrap-nowrap;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -978,6 +978,9 @@ svg {
|
|||
.gl-flex-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.gl-justify-content-center {
|
||||
justify-content: center;
|
||||
}
|
||||
.gl-float-right {
|
||||
float: right;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Resolvers
|
||||
module Environments
|
||||
class LastDeploymentResolver < BaseResolver
|
||||
argument :status,
|
||||
Types::DeploymentStatusEnum,
|
||||
required: true,
|
||||
description: 'Status of the Deployment.'
|
||||
|
||||
type Types::DeploymentType, null: true
|
||||
|
||||
def resolve(status:)
|
||||
return unless object.present? && object.is_a?(::Environment)
|
||||
|
||||
validate!(status)
|
||||
|
||||
find_last_deployment(status)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_last_deployment(status)
|
||||
BatchLoader::GraphQL.for(object).batch(key: status) do |environments, loader, args|
|
||||
association_name = "last_#{args[:key]}_deployment".to_sym
|
||||
|
||||
Preloaders::Environments::DeploymentPreloader.new(environments)
|
||||
.execute_with_union(association_name, {})
|
||||
|
||||
environments.each do |environment|
|
||||
loader.call(environment, environment.public_send(association_name)) # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def validate!(status)
|
||||
unless Deployment::FINISHED_STATUSES.include?(status.to_sym) ||
|
||||
Deployment::UPCOMING_STATUSES.include?(status.to_sym)
|
||||
raise Gitlab::Graphql::Errors::ArgumentError, "\"#{status}\" status is not supported."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -21,8 +21,8 @@ module Resolvers
|
|||
def resolve(**args)
|
||||
return unless project.present?
|
||||
|
||||
Environments::EnvironmentsFinder.new(project, context[:current_user], args).execute
|
||||
rescue Environments::EnvironmentsFinder::InvalidStatesError => e
|
||||
::Environments::EnvironmentsFinder.new(project, context[:current_user], args).execute
|
||||
rescue ::Environments::EnvironmentsFinder::InvalidStatesError => e
|
||||
raise Gitlab::Graphql::Errors::ArgumentError, e.message
|
||||
end
|
||||
end
|
||||
|
|
|
@ -63,6 +63,11 @@ module Types
|
|||
resolver: Resolvers::DeploymentsResolver,
|
||||
complexity: 150
|
||||
|
||||
field :last_deployment,
|
||||
Types::DeploymentType,
|
||||
description: 'Last deployment of the environment.',
|
||||
resolver: Resolvers::Environments::LastDeploymentResolver
|
||||
|
||||
def tier
|
||||
object.tier.to_sym
|
||||
end
|
||||
|
|
|
@ -59,6 +59,7 @@ class Deployment < ApplicationRecord
|
|||
scope :finished_before, ->(date) { where('finished_at < ?', date) }
|
||||
|
||||
scope :ordered, -> { order(finished_at: :desc) }
|
||||
scope :ordered_as_upcoming, -> { order(id: :desc) }
|
||||
|
||||
VISIBLE_STATUSES = %i[running success failed canceled blocked].freeze
|
||||
FINISHED_STATUSES = %i[success failed canceled].freeze
|
||||
|
|
|
@ -30,6 +30,16 @@ class Environment < ApplicationRecord
|
|||
has_one :last_deployment, -> { success.ordered }, class_name: 'Deployment', inverse_of: :environment
|
||||
has_one :last_visible_deployment, -> { visible.order(id: :desc) }, inverse_of: :environment, class_name: 'Deployment'
|
||||
|
||||
Deployment::FINISHED_STATUSES.each do |status|
|
||||
has_one :"last_#{status}_deployment", -> { where(status: status).ordered },
|
||||
class_name: 'Deployment', inverse_of: :environment
|
||||
end
|
||||
|
||||
Deployment::UPCOMING_STATUSES.each do |status|
|
||||
has_one :"last_#{status}_deployment", -> { where(status: status).ordered_as_upcoming },
|
||||
class_name: 'Deployment', inverse_of: :environment
|
||||
end
|
||||
|
||||
has_one :upcoming_deployment, -> { upcoming.order(id: :desc) }, class_name: 'Deployment', inverse_of: :environment
|
||||
has_one :latest_opened_most_severe_alert, -> { order_severity_with_open_prometheus_alert }, class_name: 'AlertManagement::Alert', inverse_of: :environment
|
||||
|
||||
|
|
|
@ -1198,7 +1198,7 @@ class MergeRequest < ApplicationRecord
|
|||
skip_ci_check: skip_ci_check,
|
||||
skip_discussions_check: skip_discussions_check
|
||||
})
|
||||
additional_checks.execute.success?
|
||||
additional_checks.success?
|
||||
end
|
||||
|
||||
def ff_merge_possible?
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
- hide_remember_me = local_assigns.fetch(:hide_remember_me, false)
|
||||
%div{ class: Feature.enabled?(:restyle_login_page, @project) ? 'omniauth-container gl-mt-5 gl-p-5 gl-text-center gl-w-90p gl-ml-auto gl-mr-auto' : 'omniauth-container gl-mt-5 gl-p-5' }
|
||||
%label{ class: Feature.enabled?(:restyle_login_page, @project) ? 'gl-font-weight-normal' : 'gl-font-weight-bold' }
|
||||
- restyle_login_page_enabled = Feature.enabled?(:restyle_login_page, @project)
|
||||
%div{ class: restyle_login_page_enabled ? 'omniauth-container gl-mt-5 gl-p-5 gl-text-center gl-w-90p gl-ml-auto gl-mr-auto' : 'omniauth-container gl-mt-5 gl-p-5' }
|
||||
%label{ class: restyle_login_page_enabled ? 'gl-font-weight-normal' : 'gl-font-weight-bold' }
|
||||
= _('Sign in with')
|
||||
- providers = enabled_button_based_providers
|
||||
.gl-display-flex.gl-justify-content-between.gl-flex-wrap
|
||||
.gl-display-flex.gl-flex-wrap{ class: restyle_login_page_enabled ? 'gl-justify-content-center' : 'gl-justify-content-between' }
|
||||
- providers.each do |provider|
|
||||
- has_icon = provider_has_icon?(provider)
|
||||
= button_to omniauth_authorize_path(:user, provider), id: "oauth-login-#{provider}", class: "btn gl-button btn-default gl-ml-2 gl-mr-2 gl-mb-2 js-oauth-login #{qa_class_for_provider(provider)} #{'gl-w-full' if Feature.disabled?(:restyle_login_page, @project)}", form: { class: 'gl-w-full gl-mb-3' } do
|
||||
= button_to omniauth_authorize_path(:user, provider), id: "oauth-login-#{provider}", class: "btn gl-button btn-default gl-ml-2 gl-mr-2 gl-mb-2 js-oauth-login #{qa_class_for_provider(provider)} #{'gl-w-full' unless restyle_login_page_enabled}", form: { class: restyle_login_page_enabled ? 'gl-mb-3' : 'gl-w-full gl-mb-3' } do
|
||||
- if has_icon
|
||||
= provider_image_tag(provider)
|
||||
%span.gl-button-text
|
||||
= label_for_provider(provider)
|
||||
- unless hide_remember_me
|
||||
%fieldset
|
||||
%label{ class: Feature.enabled?(:restyle_login_page, @project) ? 'gl-font-weight-normal' : '' }
|
||||
%label{ class: restyle_login_page_enabled ? 'gl-font-weight-normal' : '' }
|
||||
= check_box_tag :remember_me, nil, false
|
||||
%span
|
||||
= _('Remember me')
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
- text_style = 'font-size:16px; text-align:center; line-height:30px;'
|
||||
|
||||
%p{ style: text_style }
|
||||
Your CSV import for project
|
||||
%a{ href: project_url(@project), style: "color:#3777b0; text-decoration:none;" }
|
||||
= @project.full_name
|
||||
has been completed.
|
||||
- project_link = link_to(@project.full_name, project_url(@project), style: "color:#3777b0; text-decoration:none;")
|
||||
= s_('Notify|Your CSV import for project %{project_link} has been completed.').html_safe % { project_link: project_link }
|
||||
|
||||
%p{ style: text_style }
|
||||
#{pluralize(@results[:success], 'issue')} imported.
|
||||
- issues = n_('%d issue', '%d issues', @results[:success]) % @results[:success]
|
||||
= s_('Notify|%{issues} imported.') % { issues: issues }
|
||||
|
||||
- if @results[:error_lines].present?
|
||||
%p{ style: text_style }
|
||||
Errors found on line #{'number'.pluralize(@results[:error_lines].size)}: #{@results[:error_lines].join(', ')}. Please check if these lines have an issue title.
|
||||
= s_('Notify|Errors found on %{singular_or_plural_line}: %{error_lines}. Please check if these lines have an issue title.') % { singular_or_plural_line: n_('line', 'lines', @results[:error_lines].size),
|
||||
error_lines: @results[:error_lines].join(', ') }
|
||||
|
||||
- if @results[:parse_error]
|
||||
%p{ style: text_style }
|
||||
Error parsing CSV file. Please make sure it has the correct format: a delimited text file that uses a comma to separate values.
|
||||
= s_('Notify|Error parsing CSV file. Please make sure it has the correct format: a delimited text file that uses a comma to separate values.')
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
- name: "Starboard directive in the config for the GitLab Agent for Kubernetes"
|
||||
announcement_milestone: "15.4"
|
||||
announcement_date: "2022-09-22"
|
||||
removal_milestone: "16.0"
|
||||
removal_date: "2023-05-22"
|
||||
breaking_change: true
|
||||
reporter: sam.white
|
||||
stage: secure
|
||||
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/368828
|
||||
body: |
|
||||
GitLab's operational container scanning capabilities no longer require starboard to be installed. Consequently, use of the `starboard:` directive in the configuration file for the GitLab Agent for Kubernetes is now deprecated and is scheduled for removal in GitLab 16.0. Update your configuration file to use the `container_scanning:` directive.
|
|
@ -11692,6 +11692,18 @@ four standard [pagination arguments](#connection-pagination-arguments):
|
|||
| <a id="environmentdeploymentsorderby"></a>`orderBy` | [`DeploymentsOrderByInput`](#deploymentsorderbyinput) | Order by a specified field. |
|
||||
| <a id="environmentdeploymentsstatuses"></a>`statuses` | [`[DeploymentStatus!]`](#deploymentstatus) | Statuses of the deployments. |
|
||||
|
||||
##### `Environment.lastDeployment`
|
||||
|
||||
Last deployment of the environment.
|
||||
|
||||
Returns [`Deployment`](#deployment).
|
||||
|
||||
###### Arguments
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="environmentlastdeploymentstatus"></a>`status` | [`DeploymentStatus!`](#deploymentstatus) | Status of the Deployment. |
|
||||
|
||||
##### `Environment.metricsDashboard`
|
||||
|
||||
Metrics dashboard schema for the environment.
|
||||
|
|
|
@ -86,6 +86,20 @@ default is applied:
|
|||
- On GitLab.com during the 16.0 milestone.
|
||||
- On GitLab self-managed instances when they are upgraded to 16.0.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="deprecation removal-160 breaking-change">
|
||||
|
||||
### Starboard directive in the config for the GitLab Agent for Kubernetes
|
||||
|
||||
Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
|
||||
|
||||
WARNING:
|
||||
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
|
||||
Review the details carefully before upgrading.
|
||||
|
||||
GitLab's operational container scanning capabilities no longer require starboard to be installed. Consequently, use of the `starboard:` directive in the configuration file for the GitLab Agent for Kubernetes is now deprecated and is scheduled for removal in GitLab 16.0. Update your configuration file to use the `container_scanning:` directive.
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -344,34 +344,264 @@ For details of API fuzzing configuration options, see [Available CI/CD variables
|
|||
|
||||
#### Postman variables
|
||||
|
||||
> - Support for Postman Environment file format was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356312) in GitLab 15.1.
|
||||
> - Support for multiple variable files was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356312) in GitLab 15.1.
|
||||
> - Support for Postman variable scopes: Global and Environment was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356312) in GitLab 15.1.
|
||||
|
||||
##### Variables in Postman Client
|
||||
|
||||
Postman allows the developer to define placeholders that can be used in different parts of the
|
||||
requests. These placeholders are called variables, as explained in the Postman documentation,
|
||||
[Using variables](https://learning.postman.com/docs/sending-requests/variables/).
|
||||
requests. These placeholders are called variables, as explained in [using variables](https://learning.postman.com/docs/sending-requests/variables/).
|
||||
You can use variables to store and reuse values in your requests and scripts. For example, you can
|
||||
edit the collection to add variables to the document:
|
||||
|
||||
![Edit collection variable tab View](img/api_fuzzing_postman_collection_edit_variable.png)
|
||||
|
||||
Or alternatively, you can add variables in an environment:
|
||||
|
||||
![Edit environment variables View](img/api_fuzzing_postman_environment_edit_variable.png)
|
||||
|
||||
You can then use the variables in sections such as URL, headers, and others:
|
||||
|
||||
![Edit request using variables View](img/api_fuzzing_postman_request_edit.png)
|
||||
|
||||
Variables can be defined at different [scopes](https://learning.postman.com/docs/sending-requests/variables/#variable-scopes)
|
||||
(for example, Global, Collection, Environment, Local, and Data). In this example, they're defined at
|
||||
the Environment scope:
|
||||
Postman has grown from a basic client tool with a nice UX experience to a more complex ecosystem that allows testing APIs with scripts, creating complex collections that trigger secondary requests, and setting variables along the way. Not every feature in the Postman ecosystem is supported. For example, scripts are not supported. The main focus of the Postman support is to ingest Postman Collection definitions that are used by the Postman Client and their related variables defined in the workspace, environments, and the collections themselves.
|
||||
|
||||
![Edit environment variables View](img/api_fuzzing_postman_environment_edit_variable.png)
|
||||
Postman allows creating variables in different scopes. Each scope has a different level of visibility in the Postman tools. For example, you can create a variable in a _global environment_ scope that is seen by every operation definition and workspace. You can also create a variable in a specific _environment_ scope that is only visible and used when that specific environment is selected for use. Some scopes are not always available, for example in the Postman ecosystem you can create requests in the Postman Client, these requests do not have a _local_ scope, but test scripts do.
|
||||
|
||||
When you export a Postman collection, only Postman collection variables are exported into the
|
||||
Postman file. For example, Postman does not export environment-scoped variables into the Postman
|
||||
file.
|
||||
Variable scopes in Postman can be a daunting topic and not everyone is familiar with it. We strongly recommend that you read [Variable Scopes](https://learning.postman.com/docs/sending-requests/variables/#variable-scopes) from Postman documentation before moving forward.
|
||||
|
||||
By default, the API fuzzer uses the Postman file to resolve Postman variable values. If a JSON file
|
||||
is set in a GitLab CI/CD variable `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`, then the JSON
|
||||
file takes precedence to get Postman variable values.
|
||||
As mentioned above, there are different variable scopes, and each of them has a purpose and can be used to provide more flexibility to your Postman document. There is an important note on how values for variables are computed, as per Postman documentation:
|
||||
|
||||
WARNING:
|
||||
Although Postman can export environment variables into a JSON file, the format is not compatible with the JSON expected by `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`.
|
||||
> If a variable with the same name is declared in two different scopes, the value stored in the variable with narrowest scope is used. For example, if there is a global variable named `username` and a local variable named `username`, the local value is used when the request runs.
|
||||
|
||||
The following is a summary of the variable scopes supported by the Postman Client and API Fuzzing:
|
||||
|
||||
- **Global Environment (Global) scope** is a special pre-defined environment that is available throughout a workspace. We can also refer to the _global environment_ scope as the _global_ scope. The Postman Client allows exporting the global environment into a JSON file, which can be used with API Fuzzing.
|
||||
- **Environment scope** is a named group of variables created by a user in the Postman Client.
|
||||
The Postman Client supports a single active environment along with the global environment. The variables defined in an active user-created environment take precedence over variables defined in the global environment. The Postman Client allows exporting your environment into a JSON file, which can be used with API Fuzzing.
|
||||
- **Collection scope** is a group of variables declared in a given collection. The collection variables are available to the collection where they have been declared and the nested requests or collections. Variables defined in the collection scope take precedence over the _global environment_ scope and also the _environment_ scope.
|
||||
The Postman Client can export one or more collections into a JSON file, this JSON file contains selected collections, requests, and collection variables.
|
||||
- **API Fuzzing Scope** is a new scope added by API Fuzzing to allow users to provide extra variables, or override variables defined in other supported scopes. This scope is not supported by Postman. The _API Fuzzing Scope_ variables are provided using a [custom JSON file format](#api-fuzzing-scope-custom-json-file-format).
|
||||
- Override values defined in the environment or collection
|
||||
- Defining variables from scripts
|
||||
- Define a single row of data from the unsupported _data scope_
|
||||
- **Data scope** is a group of variables in which their name and values come from JSON or CSV files. A Postman collection runner like [Newman](https://learning.postman.com/docs/running-collections/using-newman-cli/command-line-integration-with-newman/) or [Postman Collection Runner](https://learning.postman.com/docs/running-collections/intro-to-collection-runs/) executes the requests in a collection as many times as entries have the JSON or CSV file. A good use case for these variables is to automate tests using scripts in Postman.
|
||||
API Fuzzing does **not** support reading data from a CSV or JSON file.
|
||||
- **Local scope** are variables that are defined in Postman scripts. API Fuzzing does **not** support Postman scripts and by extension, variables defined in scripts. You can still provide values for the script-defined variables by defining them in one of the supported scopes, or our custom JSON format.
|
||||
|
||||
Not all scopes are supported by API Fuzzing and variables defined in scripts are not supported. The following table is sorted by broadest scope to narrowest scope.
|
||||
|
||||
| Scope |Postman | API Fuzzing | Comment |
|
||||
| ------------------ |:---------:|:------------:| :--------|
|
||||
| Global Environment | Yes | Yes | Special pre-defined environment |
|
||||
| Environment | Yes | Yes | Named environments |
|
||||
| Collection | Yes | Yes | Defined in your postman collection |
|
||||
| API Fuzzing Scope | No | Yes | Custom scope added by API Fuzzing |
|
||||
| Data | Yes | No | External files in CSV or JSON format |
|
||||
| Local | Yes | No | Variables defined in scripts |
|
||||
|
||||
For more details on how to define variables and export variables in different scopes, see:
|
||||
|
||||
- [Defining collection variables](https://learning.postman.com/docs/sending-requests/variables/#defining-collection-variables)
|
||||
- [Defining environment variables](https://learning.postman.com/docs/sending-requests/variables/#defining-environment-variables)
|
||||
- [Defining global variables](https://learning.postman.com/docs/sending-requests/variables/#defining-global-variables)
|
||||
|
||||
##### Exporting from Postman Client
|
||||
|
||||
The Postman Client lets you export different file formats, for instance, you can export a Postman collection or a Postman environment.
|
||||
The exported environment can be the global environment (which is always available) or can be any custom environment you previously have created. When you export a Postman Collection, it may contain only declarations for _collection_ and _local_ scoped variables; _environment_ scoped variables are not included.
|
||||
|
||||
To get the declaration for _environment_ scoped variables, you have to export a given environment at the time. Each exported file only includes variables from the selected environment.
|
||||
|
||||
For more details on exporting variables in different supported scopes, see:
|
||||
|
||||
- [Exporting collections](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-collections)
|
||||
- [Exporting environments](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments)
|
||||
- [Downloading global environments](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments)
|
||||
|
||||
##### API Fuzzing Scope, custom JSON file format
|
||||
|
||||
Our custom JSON file format is a JSON object where each object property represents a variable name and the property value represents the variable value. This file can be created using your favorite text editor, or it can be produced by an earlier job in your pipeline.
|
||||
|
||||
This example defines two variables `base_url` and `token` in the API Fuzzing scope:
|
||||
|
||||
```json
|
||||
{
|
||||
"base_url": "http://127.0.0.1/",
|
||||
"token": "Token 84816165151"
|
||||
}
|
||||
```
|
||||
|
||||
##### Using scopes with API Fuzzing
|
||||
|
||||
The scopes: _global_, _environment_, _collection_, and _GitLab API Fuzzing_ are supported in [GitLab 15.1 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/356312). GitLab 15.0 and earlier, supports only the _collection_, and _GitLab API Fuzzing_ scopes.
|
||||
|
||||
The following table provides a quick reference for mapping scope files/URLs to API Fuzzing configuration variables:
|
||||
|
||||
| Scope | How to Provide |
|
||||
| ------------------ | --------------- |
|
||||
| Global Environment | FUZZAPI_POSTMAN_COLLECTION_VARIABLES |
|
||||
| Environment | FUZZAPI_POSTMAN_COLLECTION_VARIABLES |
|
||||
| Collection | FUZZAPI_POSTMAN_COLLECTION |
|
||||
| API Fuzzing Scope | FUZZAPI_POSTMAN_COLLECTION_VARIABLES |
|
||||
| Data | Not supported |
|
||||
| Local | Not supported |
|
||||
|
||||
The Postman Collection document automatically includes any _collection_ scoped variables. The Postman Collection is provided with the configuration variable `FUZZAPI_POSTMAN_COLLECTION`. This variable can be set to a single [exported Postman collection](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-collections).
|
||||
|
||||
Variables from other scopes are provided through the `FUZZAPI_POSTMAN_COLLECTION_VARIABLES` configuration variable. The configuration variable supports a comma (`,`) delimited file list in [GitLab 15.1 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/356312). GitLab 15.0 and earlier, supports only one single file. The order of the files provided is not important as the files provide the needed scope information.
|
||||
|
||||
The configuration variable `FUZZAPI_POSTMAN_COLLECTION_VARIABLES` can be set to:
|
||||
|
||||
- [Exported Global environment](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments)
|
||||
- [Exported environments](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments)
|
||||
- [API Fuzzing Custom JSON format](#api-fuzzing-scope-custom-json-file-format)
|
||||
|
||||
##### Undefined Postman variables
|
||||
|
||||
There is a chance that API Fuzzing engine does not find all variables references that your Postman collection file is using. Some cases can be:
|
||||
|
||||
- You are using _data_ or _local_ scoped variables, and as stated previously these scopes are not supported by API Fuzzing. Thus, assuming the values for these variables have not been provided through [the API Fuzzing scope](#api-fuzzing-scope-custom-json-file-format), then the values of the _data_ and _local_ scoped variables are undefined.
|
||||
- A variable name was typed incorrectly, and the name does not match the defined variable.
|
||||
- Postman Client supports a new dynamic variable that is not supported by API Fuzzing.
|
||||
|
||||
When possible, API Fuzzing follows the same behavior as the Postman Client does when dealing with undefined variables. The text of the variable reference remains the same, and there is no text substitution. The same behavior also applies to any unsupported dynamic variables.
|
||||
|
||||
For example, if a request definition in the Postman Collection references the variable `{{full_url}}` and the variable is not found it is left unchanged with the value `{{full_url}}`.
|
||||
|
||||
##### Dynamic Postman variables
|
||||
|
||||
In addition to variables that a user can define at various scope levels, Postman has a set of pre-defined variables called _dynamic_ variables. The [_dynamic_ variables](https://learning.postman.com/docs/writing-scripts/script-references/variables-list/) are already defined and their name is prefixed with a dollar sign (`$`), for instance, `$guid`. _Dynamic_ variables can be used like any other variable, and in the Postman Client, they produce random values during the request/collection run.
|
||||
|
||||
An important difference between API Fuzzing and Postman is that API Fuzzing returns the same value for each usage of the same dynamic variables. This differs from the Postman Client behavior which returns a random value on each use of the same dynamic variable. In other words, API Fuzzing uses static values for dynamic variables while Postman uses random values.
|
||||
|
||||
The supported dynamic variables during the scanning process are:
|
||||
|
||||
| Variable | Value |
|
||||
| ----------- | ----------- |
|
||||
| `$guid` | `611c2e81-2ccb-42d8-9ddc-2d0bfa65c1b4` |
|
||||
| `$isoTimestamp` | `2020-06-09T21:10:36.177Z` |
|
||||
| `$randomAbbreviation` | `PCI` |
|
||||
| `$randomAbstractImage` | `http://no-a-valid-host/640/480/abstract` |
|
||||
| `$randomAdjective` | `auxiliary` |
|
||||
| `$randomAlphaNumeric` | `a` |
|
||||
| `$randomAnimalsImage` | `http://no-a-valid-host/640/480/animals` |
|
||||
| `$randomAvatarImage` | `https://no-a-valid-host/path/to/some/image.jpg` |
|
||||
| `$randomBankAccount` | `09454073` |
|
||||
| `$randomBankAccountBic` | `EZIAUGJ1` |
|
||||
| `$randomBankAccountIban` | `MU20ZPUN3039684000618086155TKZ` |
|
||||
| `$randomBankAccountName` | `Home Loan Account` |
|
||||
| `$randomBitcoin` | `3VB8JGT7Y4Z63U68KGGKDXMLLH5` |
|
||||
| `$randomBoolean` | `true` |
|
||||
| `$randomBs` | `killer leverage schemas` |
|
||||
| `$randomBsAdjective` | `viral` |
|
||||
| `$randomBsBuzz` | `repurpose` |
|
||||
| `$randomBsNoun` | `markets` |
|
||||
| `$randomBusinessImage` | `http://no-a-valid-host/640/480/business` |
|
||||
| `$randomCatchPhrase` | `Future-proofed heuristic open architecture` |
|
||||
| `$randomCatchPhraseAdjective` | `Business-focused` |
|
||||
| `$randomCatchPhraseDescriptor` | `bandwidth-monitored` |
|
||||
| `$randomCatchPhraseNoun` | `superstructure` |
|
||||
| `$randomCatsImage` | `http://no-a-valid-host/640/480/cats` |
|
||||
| `$randomCity` | `Spinkahaven` |
|
||||
| `$randomCityImage` | `http://no-a-valid-host/640/480/city` |
|
||||
| `$randomColor` | `fuchsia` |
|
||||
| `$randomCommonFileExt` | `wav` |
|
||||
| `$randomCommonFileName` | `well_modulated.mpg4` |
|
||||
| `$randomCommonFileType` | `audio` |
|
||||
| `$randomCompanyName` | `Grady LLC` |
|
||||
| `$randomCompanySuffix` | `Inc` |
|
||||
| `$randomCountry` | `Kazakhstan` |
|
||||
| `$randomCountryCode` | `MD` |
|
||||
| `$randomCreditCardMask` | `3622` |
|
||||
| `$randomCurrencyCode` | `ZMK` |
|
||||
| `$randomCurrencyName` | `Pound Sterling` |
|
||||
| `$randomCurrencySymbol` | `£` |
|
||||
| `$randomDatabaseCollation` | `utf8_general_ci` |
|
||||
| `$randomDatabaseColumn` | `updatedAt` |
|
||||
| `$randomDatabaseEngine` | `Memory` |
|
||||
| `$randomDatabaseType` | `text` |
|
||||
| `$randomDateFuture` | `Tue Mar 17 2020 13:11:50 GMT+0530 (India Standard Time)` |
|
||||
| `$randomDatePast` | `Sat Mar 02 2019 09:09:26 GMT+0530 (India Standard Time)` |
|
||||
| `$randomDateRecent` | `Tue Jul 09 2019 23:12:37 GMT+0530 (India Standard Time)` |
|
||||
| `$randomDepartment` | `Electronics` |
|
||||
| `$randomDirectoryPath` | `/usr/local/bin` |
|
||||
| `$randomDomainName` | `trevor.info` |
|
||||
| `$randomDomainSuffix` | `org` |
|
||||
| `$randomDomainWord` | `jaden` |
|
||||
| `$randomEmail` | `Iva.Kovacek61@no-a-valid-host.com` |
|
||||
| `$randomExampleEmail` | `non-a-valid-user@example.net` |
|
||||
| `$randomFashionImage` | `http://no-a-valid-host/640/480/fashion` |
|
||||
| `$randomFileExt` | `war` |
|
||||
| `$randomFileName` | `neural_sri_lanka_rupee_gloves.gdoc` |
|
||||
| `$randomFilePath` | `/home/programming_chicken.cpio` |
|
||||
| `$randomFileType` | `application` |
|
||||
| `$randomFirstName` | `Chandler` |
|
||||
| `$randomFoodImage` | `http://no-a-valid-host/640/480/food` |
|
||||
| `$randomFullName` | `Connie Runolfsdottir` |
|
||||
| `$randomHexColor` | `#47594a` |
|
||||
| `$randomImageDataUri` | `data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20baseProfile%3D%22full%22%20width%3D%22undefined%22%20height%3D%22undefined%22%3E%20%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22grey%22%2F%3E%20%20%3Ctext%20x%3D%220%22%20y%3D%2220%22%20font-size%3D%2220%22%20text-anchor%3D%22start%22%20fill%3D%22white%22%3Eundefinedxundefined%3C%2Ftext%3E%20%3C%2Fsvg%3E` |
|
||||
| `$randomImageUrl` | `http://no-a-valid-host/640/480` |
|
||||
| `$randomIngverb` | `navigating` |
|
||||
| `$randomInt` | `494` |
|
||||
| `$randomIP` | `241.102.234.100` |
|
||||
| `$randomIPV6` | `dbe2:7ae6:119b:c161:1560:6dda:3a9b:90a9` |
|
||||
| `$randomJobArea` | `Mobility` |
|
||||
| `$randomJobDescriptor` | `Senior` |
|
||||
| `$randomJobTitle` | `International Creative Liaison` |
|
||||
| `$randomJobType` | `Supervisor` |
|
||||
| `$randomLastName` | `Schneider` |
|
||||
| `$randomLatitude` | `55.2099` |
|
||||
| `$randomLocale` | `ny` |
|
||||
| `$randomLongitude` | `40.6609` |
|
||||
| `$randomLoremLines` | `Ducimus in ut mollitia.\nA itaque non.\nHarum temporibus nihil voluptas.\nIste in sed et nesciunt in quaerat sed.` |
|
||||
| `$randomLoremParagraph` | `Ab aliquid odio iste quo voluptas voluptatem dignissimos velit. Recusandae facilis qui commodi ea magnam enim nostrum quia quis. Nihil est suscipit assumenda ut voluptatem sed. Esse ab voluptas odit qui molestiae. Rem est nesciunt est quis ipsam expedita consequuntur.` |
|
||||
| `$randomLoremParagraphs` | `Voluptatem rem magnam aliquam ab id aut quaerat. Placeat provident possimus voluptatibus dicta velit non aut quasi. Mollitia et aliquam expedita sunt dolores nam consequuntur. Nam dolorum delectus ipsam repudiandae et ipsam ut voluptatum totam. Nobis labore labore recusandae ipsam quo.` |
|
||||
| `$randomLoremSentence` | `Molestias consequuntur nisi non quod.` |
|
||||
| `$randomLoremSentences` | `Et sint voluptas similique iure amet perspiciatis vero sequi atque. Ut porro sit et hic. Neque aspernatur vitae fugiat ut dolore et veritatis. Ab iusto ex delectus animi. Voluptates nisi iusto. Impedit quod quae voluptate qui.` |
|
||||
| `$randomLoremSlug` | `eos-aperiam-accusamus, beatae-id-molestiae, qui-est-repellat` |
|
||||
| `$randomLoremText` | `Quisquam asperiores exercitationem ut ipsum. Aut eius nesciunt. Et reiciendis aut alias eaque. Nihil amet laboriosam pariatur eligendi. Sunt ullam ut sint natus ducimus. Voluptas harum aspernatur soluta rem nam.` |
|
||||
| `$randomLoremWord` | `est` |
|
||||
| `$randomLoremWords` | `vel repellat nobis` |
|
||||
| `$randomMACAddress` | `33:d4:68:5f:b4:c7` |
|
||||
| `$randomMimeType` | `audio/vnd.vmx.cvsd` |
|
||||
| `$randomMonth` | `February` |
|
||||
| `$randomNamePrefix` | `Dr.` |
|
||||
| `$randomNameSuffix` | `MD` |
|
||||
| `$randomNatureImage` | `http://no-a-valid-host/640/480/nature` |
|
||||
| `$randomNightlifeImage` | `http://no-a-valid-host/640/480/nightlife` |
|
||||
| `$randomNoun` | `bus` |
|
||||
| `$randomPassword` | `t9iXe7COoDKv8k3` |
|
||||
| `$randomPeopleImage` | `http://no-a-valid-host/640/480/people` |
|
||||
| `$randomPhoneNumber` | `700-008-5275` |
|
||||
| `$randomPhoneNumberExt` | `27-199-983-3864` |
|
||||
| `$randomPhrase` | `You can't program the monitor without navigating the mobile XML program!` |
|
||||
| `$randomPrice` | `531.55` |
|
||||
| `$randomProduct` | `Pizza` |
|
||||
| `$randomProductAdjective` | `Unbranded` |
|
||||
| `$randomProductMaterial` | `Steel` |
|
||||
| `$randomProductName` | `Handmade Concrete Tuna` |
|
||||
| `$randomProtocol` | `https` |
|
||||
| `$randomSemver` | `7.0.5` |
|
||||
| `$randomSportsImage` | `http://no-a-valid-host/640/480/sports` |
|
||||
| `$randomStreetAddress` | `5742 Harvey Streets` |
|
||||
| `$randomStreetName` | `Kuhic Island` |
|
||||
| `$randomTransactionType` | `payment` |
|
||||
| `$randomTransportImage` | `http://no-a-valid-host/640/480/transport` |
|
||||
| `$randomUrl` | `https://no-a-valid-host.net` |
|
||||
| `$randomUserAgent` | `Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.9.8; rv:15.6) Gecko/20100101 Firefox/15.6.6` |
|
||||
| `$randomUserName` | `Jarrell.Gutkowski` |
|
||||
| `$randomUUID` | `6929bb52-3ab2-448a-9796-d6480ecad36b` |
|
||||
| `$randomVerb` | `navigate` |
|
||||
| `$randomWeekday` | `Thursday` |
|
||||
| `$randomWord` | `withdrawal` |
|
||||
| `$randomWords` | `Samoa Synergistic sticky copying Grocery` |
|
||||
| `$timestamp` | `1562757107` |
|
||||
|
||||
##### Example: Global Scope
|
||||
|
||||
In this example, [the _global_ scope is exported](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments) from the Postman Client as `global-scope.json` and provided to API Fuzzing through the `FUZZAPI_POSTMAN_COLLECTION_VARIABLES` configuration variable.
|
||||
|
||||
Here is an example of using `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`:
|
||||
|
||||
|
@ -384,21 +614,171 @@ include:
|
|||
|
||||
variables:
|
||||
FUZZAPI_PROFILE: Quick-10
|
||||
FUZZAPI_POSTMAN_COLLECTION: postman-collection_serviceA.json
|
||||
FUZZAPI_POSTMAN_COLLECTION: postman-collection.json
|
||||
FUZZAPI_POSTMAN_COLLECTION_VARIABLES: global-scope.json
|
||||
FUZZAPI_TARGET_URL: http://test-deployment/
|
||||
```
|
||||
|
||||
##### Example: Environment Scope
|
||||
|
||||
In this example, [the _environment_ scope is exported](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments) from the Postman Client as `environment-scope.json` and provided to API Fuzzing through the `FUZZAPI_POSTMAN_COLLECTION_VARIABLES` configuration variable.
|
||||
|
||||
Here is an example of using `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- fuzz
|
||||
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
variables:
|
||||
FUZZAPI_PROFILE: Quick
|
||||
FUZZAPI_POSTMAN_COLLECTION: postman-collection.json
|
||||
FUZZAPI_POSTMAN_COLLECTION_VARIABLES: environment-scope.json
|
||||
FUZZAPI_TARGET_URL: http://test-deployment/
|
||||
```
|
||||
|
||||
##### Example: Collection Scope
|
||||
|
||||
The _collection_ scope variables are included in the exported Postman Collection file and provided through the `FUZZAPI_POSTMAN_COLLECTION` configuration variable.
|
||||
|
||||
Here is an example of using `FUZZAPI_POSTMAN_COLLECTION`:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- fuzz
|
||||
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
variables:
|
||||
FUZZAPI_PROFILE: Quick
|
||||
FUZZAPI_POSTMAN_COLLECTION: postman-collection.json
|
||||
FUZZAPI_TARGET_URL: http://test-deployment/
|
||||
FUZZAPI_POSTMAN_COLLECTION_VARIABLES: variable-collection-dictionary.json
|
||||
```
|
||||
|
||||
The file `variable-collection-dictionary.json` is a JSON document. This JSON is an object with
|
||||
key-value pairs for properties. The keys are the variables' names, and the values are the variables'
|
||||
##### Example: API Fuzzing Scope
|
||||
|
||||
The API Fuzzing Scope is used for two main purposes, defining _data_ and _local_ scope variables that are not supported by API Fuzzing, and changing the value of an existing variable defined in another scope. The API Fuzzing Scope is provided through the `FUZZAPI_POSTMAN_COLLECTION_VARIABLES` configuration variable.
|
||||
|
||||
Here is an example of using `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- fuzz
|
||||
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
variables:
|
||||
FUZZAPI_PROFILE: Quick
|
||||
FUZZAPI_POSTMAN_COLLECTION: postman-collection.json
|
||||
FUZZAPI_POSTMAN_COLLECTION_VARIABLES: api-fuzzing-scope.json
|
||||
FUZZAPI_TARGET_URL: http://test-deployment/
|
||||
```
|
||||
|
||||
The file `api-fuzzing-scope.json` uses our [custom JSON file format](#api-fuzzing-scope-custom-json-file-format). This JSON is an object with key-value pairs for properties. The keys are the variables' names, and the values are the variables'
|
||||
values. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"base_url": "http://127.0.0.1/",
|
||||
"token": "Token 84816165151"
|
||||
}
|
||||
```
|
||||
```json
|
||||
{
|
||||
"base_url": "http://127.0.0.1/",
|
||||
"token": "Token 84816165151"
|
||||
}
|
||||
```
|
||||
|
||||
##### Example: Multiple Scopes
|
||||
|
||||
In this example, a _global_ scope, _environment_ scope, and _collection_ scope are configured. The first step is to export our various scopes.
|
||||
|
||||
- [Export the _global_ scope](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments) as `global-scope.json`
|
||||
- [Export the _environment_ scope](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments) as `environment-scope.json`
|
||||
- Export the Postman Collection which includes the _collection_ scope as `postman-collection.json`
|
||||
|
||||
The Postman Collection is provided using the `FUZZAPI_POSTMAN_COLLECTION` variable, while the other scopes are provided using the `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`. API Fuzzing can identify which scope the provided files match using data provided in each file.
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- fuzz
|
||||
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
variables:
|
||||
FUZZAPI_PROFILE: Quick
|
||||
FUZZAPI_POSTMAN_COLLECTION: postman-collection.json
|
||||
FUZZAPI_POSTMAN_COLLECTION_VARIABLES: global-scope.json,environment-scope.json
|
||||
FUZZAPI_TARGET_URL: http://test-deployment/
|
||||
```
|
||||
|
||||
##### Example: Changing a Variables Value
|
||||
|
||||
When using exported scopes, it's often the case that the value of a variable must be changed for use with API Fuzzing. For example, a _collection_ scoped variable might contain a variable named `api_version` with a value of `v2`, while your test needs a value of `v1`. Instead of modifying the exported collection to change the value, the API Fuzzing scope can be used to change its value. This works because the _API Fuzzing_ scope takes precedence over all other scopes.
|
||||
|
||||
The _collection_ scope variables are included in the exported Postman Collection file and provided through the `FUZZAPI_POSTMAN_COLLECTION` configuration variable.
|
||||
|
||||
The API Fuzzing Scope is provided through the `FUZZAPI_POSTMAN_COLLECTION_VARIABLES` configuration variable, but first, we must create the file.
|
||||
The file `api-fuzzing-scope.json` uses our [custom JSON file format](#api-fuzzing-scope-custom-json-file-format). This JSON is an object with key-value pairs for properties. The keys are the variables' names, and the values are the variables'
|
||||
values. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"api_version": "v1"
|
||||
}
|
||||
```
|
||||
|
||||
Our CI definition:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- fuzz
|
||||
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
variables:
|
||||
FUZZAPI_PROFILE: Quick
|
||||
FUZZAPI_POSTMAN_COLLECTION: postman-collection.json
|
||||
FUZZAPI_POSTMAN_COLLECTION_VARIABLES: api-fuzzing-scope.json
|
||||
FUZZAPI_TARGET_URL: http://test-deployment/
|
||||
```
|
||||
|
||||
##### Example: Changing a Variables Value with Multiple Scopes
|
||||
|
||||
When using exported scopes, it's often the case that the value of a variable must be changed for use with API Fuzzing. For example, an _environment_ scope might contain a variable named `api_version` with a value of `v2`, while your test needs a value of `v1`. Instead of modifying the exported file to change the value, the API Fuzzing scope can be used. This works because the _API Fuzzing_ scope takes precedence over all other scopes.
|
||||
|
||||
In this example, a _global_ scope, _environment_ scope, _collection_ scope, and _API Fuzzing_ scope are configured. The first step is to export and create our various scopes.
|
||||
|
||||
- [Export the _global_ scope](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments) as `global-scope.json`
|
||||
- [Export the _environment_ scope](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments) as `environment-scope.json`
|
||||
- Export the Postman Collection which includes the _collection_ scope as `postman-collection.json`
|
||||
|
||||
The API Fuzzing scope is used by creating a file `api-fuzzing-scope.json` using our [custom JSON file format](#api-fuzzing-scope-custom-json-file-format). This JSON is an object with key-value pairs for properties. The keys are the variables' names, and the values are the variables'
|
||||
values. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"api_version": "v1"
|
||||
}
|
||||
```
|
||||
|
||||
The Postman Collection is provided using the `FUZZAPI_POSTMAN_COLLECTION` variable, while the other scopes are provided using the `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`. API Fuzzing can identify which scope the provided files match using data provided in each file.
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- fuzz
|
||||
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
variables:
|
||||
FUZZAPI_PROFILE: Quick
|
||||
FUZZAPI_POSTMAN_COLLECTION: postman-collection.json
|
||||
FUZZAPI_POSTMAN_COLLECTION_VARIABLES: global-scope.json,environment-scope.json,api-fuzzing-scope.json
|
||||
FUZZAPI_TARGET_URL: http://test-deployment/
|
||||
```
|
||||
|
||||
## API fuzzing configuration
|
||||
|
||||
|
@ -420,7 +800,7 @@ provide a script that performs an authentication flow or calculates the token.
|
|||
#### HTTP Basic Authentication
|
||||
|
||||
[HTTP basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)
|
||||
is an authentication method built in to the HTTP protocol and used in conjunction with
|
||||
is an authentication method built into the HTTP protocol and used in conjunction with
|
||||
[transport layer security (TLS)](https://en.wikipedia.org/wiki/Transport_Layer_Security).
|
||||
|
||||
We recommended that you [create a CI/CD variable](../../../ci/variables/index.md#custom-cicd-variables)
|
||||
|
@ -499,7 +879,7 @@ Follow these steps to provide the bearer token with `FUZZAPI_OVERRIDES_ENV`:
|
|||
##### Token generated at test runtime
|
||||
|
||||
If the bearer token must be generated and doesn't expire during testing, you can provide to API
|
||||
fuzzing a file containing the token. A prior stage and job, or part of the API fuzzing job, can
|
||||
fuzzing with a file containing the token. A prior stage and job, or part of the API fuzzing job, can
|
||||
generate this file.
|
||||
|
||||
API fuzzing expects to receive a JSON file with the following structure:
|
||||
|
@ -612,6 +992,7 @@ profile increases as the number of tests increases.
|
|||
|[`FUZZAPI_OPENAPI_MEDIA_TYPES`](#openapi-specification) | Colon (`:`) separated media types accepted for testing. Default is disabled. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/333304) in GitLab 14.10. |
|
||||
|[`FUZZAPI_HAR`](#http-archive-har) | HTTP Archive (HAR) file. |
|
||||
|[`FUZZAPI_POSTMAN_COLLECTION`](#postman-collection) | Postman Collection file. |
|
||||
|[`FUZZAPI_POSTMAN_COLLECTION_VARIABLES`](#postman-variables) | Path to a JSON file to extract Postman variable values. The support for comma-separated (`,`) files was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356312) in GitLab 15.1. |
|
||||
|[`FUZZAPI_POSTMAN_COLLECTION_VARIABLES`](#postman-variables) | Path to a JSON file to extract Postman variable values. |
|
||||
|[`FUZZAPI_OVERRIDES_FILE`](#overrides) | Path to a JSON file containing overrides. |
|
||||
|[`FUZZAPI_OVERRIDES_ENV`](#overrides) | JSON string containing headers to override. |
|
||||
|
@ -1718,7 +2099,7 @@ For more information, see [Offline environments](../offline_deployments/index.md
|
|||
|
||||
### `Error waiting for API Security 'http://127.0.0.1:5000' to become available`
|
||||
|
||||
A bug exists in versions of the API Fuzzing analyzer prior to v1.6.196 that can cause a background process to fail under certain conditions. The solution is to update to a newer version of the DAST API analyzer.
|
||||
A bug exists in versions of the API Fuzzing analyzer prior to v1.6.196 that can cause a background process to fail under certain conditions. The solution is to update to a newer version of the API Fuzzing analyzer.
|
||||
|
||||
The version information can be found in the job details for the `apifuzzer_fuzz` job.
|
||||
|
||||
|
@ -1835,7 +2216,7 @@ TODO
|
|||
|
||||
### Use OpenAPI with an invalid schema
|
||||
|
||||
There are cases where the document is autogenerated with an invalid schema or cannot be edited manually in a timely manner. In those scenarios, the API Security is able to perform a relaxed validation by setting the variable `FUZZAPI_OPENAPI_RELAXED_VALIDATION`. We recommend providing a fully compliant OpenAPI document to prevent unexpected behaviors.
|
||||
There are cases where the document is autogenerated with an invalid schema or cannot be edited manually in a timely manner. In those scenarios, the API Fuzzing is able to perform a relaxed validation by setting the variable `FUZZAPI_OPENAPI_RELAXED_VALIDATION`. We recommend providing a fully compliant OpenAPI document to prevent unexpected behaviors.
|
||||
|
||||
#### Edit a non-compliant OpenAPI file
|
||||
|
||||
|
@ -1852,7 +2233,7 @@ If your OpenAPI document is generated manually, load your document in the editor
|
|||
|
||||
Relaxed validation is meant for cases when the OpenAPI document cannot meet OpenAPI specifications, but it still has enough content to be consumed by different tools. A validation is performed but less strictly in regards to document schema.
|
||||
|
||||
API Security can still try to consume an OpenAPI document that does not fully comply with OpenAPI specifications. To instruct API Security to perform a relaxed validation, set the variable `FUZZAPI_OPENAPI_RELAXED_VALIDATION` to any value, for example:
|
||||
API Fuzzing can still try to consume an OpenAPI document that does not fully comply with OpenAPI specifications. To instruct API Fuzzing analyzer to perform a relaxed validation, set the variable `FUZZAPI_OPENAPI_RELAXED_VALIDATION` to any value, for example:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
|
@ -1870,7 +2251,7 @@ variables:
|
|||
|
||||
### `No operation in the OpenAPI document is consuming any supported media type`
|
||||
|
||||
API Security uses the specified media types in the OpenAPI document to generate requests. If no request can be created due to the lack of supported media types, then an error will be thrown.
|
||||
API Fuzzing uses the specified media types in the OpenAPI document to generate requests. If no request can be created due to the lack of supported media types, then an error will be thrown.
|
||||
|
||||
**Error message**
|
||||
|
||||
|
|
|
@ -267,35 +267,266 @@ This is a minimal configuration for DAST API. From here you can:
|
|||
- [Add authentication](#authentication).
|
||||
- Learn how to [handle false positives](#handling-false-positives).
|
||||
|
||||
##### Postman variables
|
||||
#### Postman variables
|
||||
|
||||
> - Support for Postman Environment file format was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356312) in GitLab 15.1.
|
||||
> - Support for multiple variable files was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356312) in GitLab 15.1.
|
||||
> - Support for Postman variable scopes: Global and Environment was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356312) in GitLab 15.1.
|
||||
|
||||
##### Variables in Postman Client
|
||||
|
||||
Postman allows the developer to define placeholders that can be used in different parts of the
|
||||
requests. These placeholders are called variables, as explained in [Using variables](https://learning.postman.com/docs/sending-requests/variables/).
|
||||
requests. These placeholders are called variables, as explained in [using variables](https://learning.postman.com/docs/sending-requests/variables/).
|
||||
You can use variables to store and reuse values in your requests and scripts. For example, you can
|
||||
edit the collection to add variables to the document:
|
||||
|
||||
![Edit collection variable tab View](img/dast_api_postman_collection_edit_variable.png)
|
||||
|
||||
Or alternatively, you can add variables in an environment:
|
||||
|
||||
![Edit environment variables View](img/dast_api_postman_environment_edit_variable.png)
|
||||
|
||||
You can then use the variables in sections such as URL, headers, and others:
|
||||
|
||||
![Edit request using variables View](img/dast_api_postman_request_edit.png)
|
||||
|
||||
Variables can be defined at different [scopes](https://learning.postman.com/docs/sending-requests/variables/#variable-scopes)
|
||||
(for example, Global, Collection, Environment, Local, and Data). In this example, they're defined at
|
||||
the Environment scope:
|
||||
Postman has grown from a basic client tool with a nice UX experience to a more complex ecosystem that allows testing APIs with scripts, creating complex collections that trigger secondary requests, and setting variables along the way. Not every feature in the Postman ecosystem is supported. For example, scripts are not supported. The main focus of the Postman support is to ingest Postman Collection definitions that are used by the Postman Client and their related variables defined in the workspace, environments, and the collections themselves.
|
||||
|
||||
![Edit environment variables View](img/dast_api_postman_environment_edit_variable.png)
|
||||
Postman allows creating variables in different scopes. Each scope has a different level of visibility in the Postman tools. For example, you can create a variable in a _global environment_ scope that is seen by every operation definition and workspace. You can also create a variable in a specific _environment_ scope that is only visible and used when that specific environment is selected for use. Some scopes are not always available, for example in the Postman ecosystem you can create requests in the Postman Client, these requests do not have a _local_ scope, but test scripts do.
|
||||
|
||||
When you export a Postman collection, only Postman collection variables are exported into the
|
||||
Postman file. For example, Postman does not export environment-scoped variables into the Postman
|
||||
file.
|
||||
Variable scopes in Postman can be a daunting topic and not everyone is familiar with it. We strongly recommend that you read [Variable Scopes](https://learning.postman.com/docs/sending-requests/variables/#variable-scopes) from Postman documentation before moving forward.
|
||||
|
||||
By default, the DAST API scanner uses the Postman file to resolve Postman variable values. If a JSON file
|
||||
is set in a GitLab CI/CD environment variable `DAST_API_POSTMAN_COLLECTION_VARIABLES`, then the JSON
|
||||
file takes precedence to get Postman variable values.
|
||||
As mentioned above, there are different variable scopes, and each of them has a purpose and can be used to provide more flexibility to your Postman document. There is an important note on how values for variables are computed, as per Postman documentation:
|
||||
|
||||
WARNING:
|
||||
Although Postman can export environment variables into a JSON file, the format is not compatible with the JSON expected by `DAST_API_POSTMAN_COLLECTION_VARIABLES`.
|
||||
> If a variable with the same name is declared in two different scopes, the value stored in the variable with narrowest scope is used. For example, if there is a global variable named `username` and a local variable named `username`, the local value is used when the request runs.
|
||||
|
||||
The following is a summary of the variable scopes supported by the Postman Client and DAST API:
|
||||
|
||||
- **Global Environment (Global) scope** is a special pre-defined environment that is available throughout a workspace. We can also refer to the _global environment_ scope as the _global_ scope. The Postman Client allows exporting the global environment into a JSON file, which can be used with DAST API.
|
||||
- **Environment scope** is a named group of variables created by a user in the Postman Client.
|
||||
The Postman Client supports a single active environment along with the global environment. The variables defined in an active user-created environment take precedence over variables defined in the global environment. The Postman Client allows exporting your environment into a JSON file, which can be used with DAST API.
|
||||
- **Collection scope** is a group of variables declared in a given collection. The collection variables are available to the collection where they have been declared and the nested requests or collections. Variables defined in the collection scope take precedence over the _global environment_ scope and also the _environment_ scope.
|
||||
The Postman Client can export one or more collections into a JSON file, this JSON file contains selected collections, requests, and collection variables.
|
||||
- **DAST API Scope** is a new scope added by DAST API to allow users to provide extra variables, or override variables defined in other supported scopes. This scope is not supported by Postman. The _DAST API Scope_ variables are provided using a [custom JSON file format](#dast-api-scope-custom-json-file-format).
|
||||
- Override values defined in the environment or collection
|
||||
- Defining variables from scripts
|
||||
- Define a single row of data from the unsupported _data scope_
|
||||
- **Data scope** is a group of variables in which their name and values come from JSON or CSV files. A Postman collection runner like [Newman](https://learning.postman.com/docs/running-collections/using-newman-cli/command-line-integration-with-newman/) or [Postman Collection Runner](https://learning.postman.com/docs/running-collections/intro-to-collection-runs/) executes the requests in a collection as many times as entries have the JSON or CSV file. A good use case for these variables is to automate tests using scripts in Postman.
|
||||
DAST API does **not** support reading data from a CSV or JSON file.
|
||||
- **Local scope** are variables that are defined in Postman scripts. DAST API does **not** support Postman scripts and by extension, variables defined in scripts. You can still provide values for the script-defined variables by defining them in one of the supported scopes, or our custom JSON format.
|
||||
|
||||
Not all scopes are supported by DAST API and variables defined in scripts are not supported. The following table is sorted by broadest scope to narrowest scope.
|
||||
|
||||
| Scope |Postman | DAST API | Comment |
|
||||
| ------------------ |:---------:|:------------:| :--------|
|
||||
| Global Environment | Yes | Yes | Special pre-defined environment |
|
||||
| Environment | Yes | Yes | Named environments |
|
||||
| Collection | Yes | Yes | Defined in your postman collection |
|
||||
| DAST API Scope | No | Yes | Custom scope added by DAST API |
|
||||
| Data | Yes | No | External files in CSV or JSON format |
|
||||
| Local | Yes | No | Variables defined in scripts |
|
||||
|
||||
For more details on how to define variables and export variables in different scopes, see:
|
||||
|
||||
- [Defining collection variables](https://learning.postman.com/docs/sending-requests/variables/#defining-collection-variables)
|
||||
- [Defining environment variables](https://learning.postman.com/docs/sending-requests/variables/#defining-environment-variables)
|
||||
- [Defining global variables](https://learning.postman.com/docs/sending-requests/variables/#defining-global-variables)
|
||||
|
||||
##### Exporting from Postman Client
|
||||
|
||||
The Postman Client lets you export different file formats, for instance, you can export a Postman collection or a Postman environment.
|
||||
The exported environment can be the global environment (which is always available) or can be any custom environment you previously have created. When you export a Postman Collection, it may contain only declarations for _collection_ and _local_ scoped variables; _environment_ scoped variables are not included.
|
||||
|
||||
To get the declaration for _environment_ scoped variables, you have to export a given environment at the time. Each exported file only includes variables from the selected environment.
|
||||
|
||||
For more details on exporting variables in different supported scopes, see:
|
||||
|
||||
- [Exporting collections](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-collections)
|
||||
- [Exporting environments](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments)
|
||||
- [Downloading global environments](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments)
|
||||
|
||||
##### DAST API Scope, custom JSON file format
|
||||
|
||||
Our custom JSON file format is a JSON object where each object property represents a variable name and the property value represents the variable value. This file can be created using your favorite text editor, or it can be produced by an earlier job in your pipeline.
|
||||
|
||||
This example defines two variables `base_url` and `token` in the DAST API scope:
|
||||
|
||||
```json
|
||||
{
|
||||
"base_url": "http://127.0.0.1/",
|
||||
"token": "Token 84816165151"
|
||||
}
|
||||
```
|
||||
|
||||
##### Using scopes with DAST API
|
||||
|
||||
The scopes: _global_, _environment_, _collection_, and _GitLab DAST API_ are supported in [GitLab 15.1 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/356312). GitLab 15.0 and earlier, supports only the _collection_, and _GitLab DAST API_ scopes.
|
||||
|
||||
The following table provides a quick reference for mapping scope files/URLs to DAST API configuration variables:
|
||||
|
||||
| Scope | How to Provide |
|
||||
| ------------------ | --------------- |
|
||||
| Global Environment | DAST_API_POSTMAN_COLLECTION_VARIABLES |
|
||||
| Environment | DAST_API_POSTMAN_COLLECTION_VARIABLES |
|
||||
| Collection | DAST_API_POSTMAN_COLLECTION |
|
||||
| DAST API Scope | DAST_API_POSTMAN_COLLECTION_VARIABLES |
|
||||
| Data | Not supported |
|
||||
| Local | Not supported |
|
||||
|
||||
The Postman Collection document automatically includes any _collection_ scoped variables. The Postman Collection is provided with the configuration variable `DAST_API_POSTMAN_COLLECTION`. This variable can be set to a single [exported Postman collection](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-collections).
|
||||
|
||||
Variables from other scopes are provided through the `DAST_API_POSTMAN_COLLECTION_VARIABLES` configuration variable. The configuration variable supports a comma (`,`) delimited file list in [GitLab 15.1 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/356312). GitLab 15.0 and earlier, supports only one single file. The order of the files provided is not important as the files provide the needed scope information.
|
||||
|
||||
The configuration variable `DAST_API_POSTMAN_COLLECTION_VARIABLES` can be set to:
|
||||
|
||||
- [Exported Global environment](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments)
|
||||
- [Exported environments](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments)
|
||||
- [DAST API Custom JSON format](#dast-api-scope-custom-json-file-format)
|
||||
|
||||
##### Undefined Postman variables
|
||||
|
||||
There is a chance that DAST API engine does not find all variables references that your Postman collection file is using. Some cases can be:
|
||||
|
||||
- You are using _data_ or _local_ scoped variables, and as stated previously these scopes are not supported by DAST API. Thus, assuming the values for these variables have not been provided through [the DAST API scope](#dast-api-scope-custom-json-file-format), then the values of the _data_ and _local_ scoped variables are undefined.
|
||||
- A variable name was typed incorrectly, and the name does not match the defined variable.
|
||||
- Postman Client supports a new dynamic variable that is not supported by DAST API.
|
||||
|
||||
When possible, DAST API follows the same behavior as the Postman Client does when dealing with undefined variables. The text of the variable reference remains the same, and there is no text substitution. The same behavior also applies to any unsupported dynamic variables.
|
||||
|
||||
For example, if a request definition in the Postman Collection references the variable `{{full_url}}` and the variable is not found it is left unchanged with the value `{{full_url}}`.
|
||||
|
||||
##### Dynamic Postman variables
|
||||
|
||||
In addition to variables that a user can define at various scope levels, Postman has a set of pre-defined variables called _dynamic_ variables. The [_dynamic_ variables](https://learning.postman.com/docs/writing-scripts/script-references/variables-list/) are already defined and their name is prefixed with a dollar sign (`$`), for instance, `$guid`. _Dynamic_ variables can be used like any other variable, and in the Postman Client, they produce random values during the request/collection run.
|
||||
|
||||
An important difference between DAST API and Postman is that DAST API returns the same value for each usage of the same dynamic variables. This differs from the Postman Client behavior which returns a random value on each use of the same dynamic variable. In other words, DAST API uses static values for dynamic variables while Postman uses random values.
|
||||
|
||||
The supported dynamic variables during the scanning process are:
|
||||
|
||||
| Variable | Value |
|
||||
| ----------- | ----------- |
|
||||
| `$guid` | `611c2e81-2ccb-42d8-9ddc-2d0bfa65c1b4` |
|
||||
| `$isoTimestamp` | `2020-06-09T21:10:36.177Z` |
|
||||
| `$randomAbbreviation` | `PCI` |
|
||||
| `$randomAbstractImage` | `http://no-a-valid-host/640/480/abstract` |
|
||||
| `$randomAdjective` | `auxiliary` |
|
||||
| `$randomAlphaNumeric` | `a` |
|
||||
| `$randomAnimalsImage` | `http://no-a-valid-host/640/480/animals` |
|
||||
| `$randomAvatarImage` | `https://no-a-valid-host/path/to/some/image.jpg` |
|
||||
| `$randomBankAccount` | `09454073` |
|
||||
| `$randomBankAccountBic` | `EZIAUGJ1` |
|
||||
| `$randomBankAccountIban` | `MU20ZPUN3039684000618086155TKZ` |
|
||||
| `$randomBankAccountName` | `Home Loan Account` |
|
||||
| `$randomBitcoin` | `3VB8JGT7Y4Z63U68KGGKDXMLLH5` |
|
||||
| `$randomBoolean` | `true` |
|
||||
| `$randomBs` | `killer leverage schemas` |
|
||||
| `$randomBsAdjective` | `viral` |
|
||||
| `$randomBsBuzz` | `repurpose` |
|
||||
| `$randomBsNoun` | `markets` |
|
||||
| `$randomBusinessImage` | `http://no-a-valid-host/640/480/business` |
|
||||
| `$randomCatchPhrase` | `Future-proofed heuristic open architecture` |
|
||||
| `$randomCatchPhraseAdjective` | `Business-focused` |
|
||||
| `$randomCatchPhraseDescriptor` | `bandwidth-monitored` |
|
||||
| `$randomCatchPhraseNoun` | `superstructure` |
|
||||
| `$randomCatsImage` | `http://no-a-valid-host/640/480/cats` |
|
||||
| `$randomCity` | `Spinkahaven` |
|
||||
| `$randomCityImage` | `http://no-a-valid-host/640/480/city` |
|
||||
| `$randomColor` | `fuchsia` |
|
||||
| `$randomCommonFileExt` | `wav` |
|
||||
| `$randomCommonFileName` | `well_modulated.mpg4` |
|
||||
| `$randomCommonFileType` | `audio` |
|
||||
| `$randomCompanyName` | `Grady LLC` |
|
||||
| `$randomCompanySuffix` | `Inc` |
|
||||
| `$randomCountry` | `Kazakhstan` |
|
||||
| `$randomCountryCode` | `MD` |
|
||||
| `$randomCreditCardMask` | `3622` |
|
||||
| `$randomCurrencyCode` | `ZMK` |
|
||||
| `$randomCurrencyName` | `Pound Sterling` |
|
||||
| `$randomCurrencySymbol` | `£` |
|
||||
| `$randomDatabaseCollation` | `utf8_general_ci` |
|
||||
| `$randomDatabaseColumn` | `updatedAt` |
|
||||
| `$randomDatabaseEngine` | `Memory` |
|
||||
| `$randomDatabaseType` | `text` |
|
||||
| `$randomDateFuture` | `Tue Mar 17 2020 13:11:50 GMT+0530 (India Standard Time)` |
|
||||
| `$randomDatePast` | `Sat Mar 02 2019 09:09:26 GMT+0530 (India Standard Time)` |
|
||||
| `$randomDateRecent` | `Tue Jul 09 2019 23:12:37 GMT+0530 (India Standard Time)` |
|
||||
| `$randomDepartment` | `Electronics` |
|
||||
| `$randomDirectoryPath` | `/usr/local/bin` |
|
||||
| `$randomDomainName` | `trevor.info` |
|
||||
| `$randomDomainSuffix` | `org` |
|
||||
| `$randomDomainWord` | `jaden` |
|
||||
| `$randomEmail` | `Iva.Kovacek61@no-a-valid-host.com` |
|
||||
| `$randomExampleEmail` | `non-a-valid-user@example.net` |
|
||||
| `$randomFashionImage` | `http://no-a-valid-host/640/480/fashion` |
|
||||
| `$randomFileExt` | `war` |
|
||||
| `$randomFileName` | `neural_sri_lanka_rupee_gloves.gdoc` |
|
||||
| `$randomFilePath` | `/home/programming_chicken.cpio` |
|
||||
| `$randomFileType` | `application` |
|
||||
| `$randomFirstName` | `Chandler` |
|
||||
| `$randomFoodImage` | `http://no-a-valid-host/640/480/food` |
|
||||
| `$randomFullName` | `Connie Runolfsdottir` |
|
||||
| `$randomHexColor` | `#47594a` |
|
||||
| `$randomImageDataUri` | `data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20baseProfile%3D%22full%22%20width%3D%22undefined%22%20height%3D%22undefined%22%3E%20%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22grey%22%2F%3E%20%20%3Ctext%20x%3D%220%22%20y%3D%2220%22%20font-size%3D%2220%22%20text-anchor%3D%22start%22%20fill%3D%22white%22%3Eundefinedxundefined%3C%2Ftext%3E%20%3C%2Fsvg%3E` |
|
||||
| `$randomImageUrl` | `http://no-a-valid-host/640/480` |
|
||||
| `$randomIngverb` | `navigating` |
|
||||
| `$randomInt` | `494` |
|
||||
| `$randomIP` | `241.102.234.100` |
|
||||
| `$randomIPV6` | `dbe2:7ae6:119b:c161:1560:6dda:3a9b:90a9` |
|
||||
| `$randomJobArea` | `Mobility` |
|
||||
| `$randomJobDescriptor` | `Senior` |
|
||||
| `$randomJobTitle` | `International Creative Liaison` |
|
||||
| `$randomJobType` | `Supervisor` |
|
||||
| `$randomLastName` | `Schneider` |
|
||||
| `$randomLatitude` | `55.2099` |
|
||||
| `$randomLocale` | `ny` |
|
||||
| `$randomLongitude` | `40.6609` |
|
||||
| `$randomLoremLines` | `Ducimus in ut mollitia.\nA itaque non.\nHarum temporibus nihil voluptas.\nIste in sed et nesciunt in quaerat sed.` |
|
||||
| `$randomLoremParagraph` | `Ab aliquid odio iste quo voluptas voluptatem dignissimos velit. Recusandae facilis qui commodi ea magnam enim nostrum quia quis. Nihil est suscipit assumenda ut voluptatem sed. Esse ab voluptas odit qui molestiae. Rem est nesciunt est quis ipsam expedita consequuntur.` |
|
||||
| `$randomLoremParagraphs` | `Voluptatem rem magnam aliquam ab id aut quaerat. Placeat provident possimus voluptatibus dicta velit non aut quasi. Mollitia et aliquam expedita sunt dolores nam consequuntur. Nam dolorum delectus ipsam repudiandae et ipsam ut voluptatum totam. Nobis labore labore recusandae ipsam quo.` |
|
||||
| `$randomLoremSentence` | `Molestias consequuntur nisi non quod.` |
|
||||
| `$randomLoremSentences` | `Et sint voluptas similique iure amet perspiciatis vero sequi atque. Ut porro sit et hic. Neque aspernatur vitae fugiat ut dolore et veritatis. Ab iusto ex delectus animi. Voluptates nisi iusto. Impedit quod quae voluptate qui.` |
|
||||
| `$randomLoremSlug` | `eos-aperiam-accusamus, beatae-id-molestiae, qui-est-repellat` |
|
||||
| `$randomLoremText` | `Quisquam asperiores exercitationem ut ipsum. Aut eius nesciunt. Et reiciendis aut alias eaque. Nihil amet laboriosam pariatur eligendi. Sunt ullam ut sint natus ducimus. Voluptas harum aspernatur soluta rem nam.` |
|
||||
| `$randomLoremWord` | `est` |
|
||||
| `$randomLoremWords` | `vel repellat nobis` |
|
||||
| `$randomMACAddress` | `33:d4:68:5f:b4:c7` |
|
||||
| `$randomMimeType` | `audio/vnd.vmx.cvsd` |
|
||||
| `$randomMonth` | `February` |
|
||||
| `$randomNamePrefix` | `Dr.` |
|
||||
| `$randomNameSuffix` | `MD` |
|
||||
| `$randomNatureImage` | `http://no-a-valid-host/640/480/nature` |
|
||||
| `$randomNightlifeImage` | `http://no-a-valid-host/640/480/nightlife` |
|
||||
| `$randomNoun` | `bus` |
|
||||
| `$randomPassword` | `t9iXe7COoDKv8k3` |
|
||||
| `$randomPeopleImage` | `http://no-a-valid-host/640/480/people` |
|
||||
| `$randomPhoneNumber` | `700-008-5275` |
|
||||
| `$randomPhoneNumberExt` | `27-199-983-3864` |
|
||||
| `$randomPhrase` | `You can't program the monitor without navigating the mobile XML program!` |
|
||||
| `$randomPrice` | `531.55` |
|
||||
| `$randomProduct` | `Pizza` |
|
||||
| `$randomProductAdjective` | `Unbranded` |
|
||||
| `$randomProductMaterial` | `Steel` |
|
||||
| `$randomProductName` | `Handmade Concrete Tuna` |
|
||||
| `$randomProtocol` | `https` |
|
||||
| `$randomSemver` | `7.0.5` |
|
||||
| `$randomSportsImage` | `http://no-a-valid-host/640/480/sports` |
|
||||
| `$randomStreetAddress` | `5742 Harvey Streets` |
|
||||
| `$randomStreetName` | `Kuhic Island` |
|
||||
| `$randomTransactionType` | `payment` |
|
||||
| `$randomTransportImage` | `http://no-a-valid-host/640/480/transport` |
|
||||
| `$randomUrl` | `https://no-a-valid-host.net` |
|
||||
| `$randomUserAgent` | `Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.9.8; rv:15.6) Gecko/20100101 Firefox/15.6.6` |
|
||||
| `$randomUserName` | `Jarrell.Gutkowski` |
|
||||
| `$randomUUID` | `6929bb52-3ab2-448a-9796-d6480ecad36b` |
|
||||
| `$randomVerb` | `navigate` |
|
||||
| `$randomWeekday` | `Thursday` |
|
||||
| `$randomWord` | `withdrawal` |
|
||||
| `$randomWords` | `Samoa Synergistic sticky copying Grocery` |
|
||||
| `$timestamp` | `1562757107` |
|
||||
|
||||
##### Example: Global Scope
|
||||
|
||||
In this example, [the _global_ scope is exported](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments) from the Postman Client as `global-scope.json` and provided to DAST API through the `DAST_API_POSTMAN_COLLECTION_VARIABLES` configuration variable.
|
||||
|
||||
Here is an example of using `DAST_API_POSTMAN_COLLECTION_VARIABLES`:
|
||||
|
||||
|
@ -308,21 +539,170 @@ include:
|
|||
|
||||
variables:
|
||||
DAST_API_PROFILE: Quick
|
||||
DAST_API_POSTMAN_COLLECTION: postman-collection_serviceA.json
|
||||
DAST_API_POSTMAN_COLLECTION_VARIABLES: variable-collection-dictionary.json
|
||||
DAST_API_POSTMAN_COLLECTION: postman-collection.json
|
||||
DAST_API_POSTMAN_COLLECTION_VARIABLES: global-scope.json
|
||||
DAST_API_TARGET_URL: http://test-deployment/
|
||||
```
|
||||
|
||||
The file `variable-collection-dictionary.json` is a JSON document. This JSON is an object with
|
||||
key-value pairs for properties. The keys are the variables' names, and the values are the variables'
|
||||
##### Example: Environment Scope
|
||||
|
||||
In this example, [the _environment_ scope is exported](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments) from the Postman Client as `environment-scope.json` and provided to DAST API through the `DAST_API_POSTMAN_COLLECTION_VARIABLES` configuration variable.
|
||||
|
||||
Here is an example of using `DAST_API_POSTMAN_COLLECTION_VARIABLES`:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- dast
|
||||
|
||||
include:
|
||||
- template: DAST-API.gitlab-ci.yml
|
||||
|
||||
variables:
|
||||
DAST_API_PROFILE: Quick
|
||||
DAST_API_POSTMAN_COLLECTION: postman-collection.json
|
||||
DAST_API_POSTMAN_COLLECTION_VARIABLES: environment-scope.json
|
||||
DAST_API_TARGET_URL: http://test-deployment/
|
||||
```
|
||||
|
||||
##### Example: Collection Scope
|
||||
|
||||
The _collection_ scope variables are included in the exported Postman Collection file and provided through the `DAST_API_POSTMAN_COLLECTION` configuration variable.
|
||||
|
||||
Here is an example of using `DAST_API_POSTMAN_COLLECTION`:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- dast
|
||||
|
||||
include:
|
||||
- template: DAST-API.gitlab-ci.yml
|
||||
|
||||
variables:
|
||||
DAST_API_PROFILE: Quick
|
||||
DAST_API_POSTMAN_COLLECTION: postman-collection.json
|
||||
DAST_API_TARGET_URL: http://test-deployment/
|
||||
```
|
||||
|
||||
##### Example: DAST API Scope
|
||||
|
||||
The DAST API Scope is used for two main purposes, defining _data_ and _local_ scope variables that are not supported by DAST API, and changing the value of an existing variable defined in another scope. The DAST API Scope is provided through the `DAST_API_POSTMAN_COLLECTION_VARIABLES` configuration variable.
|
||||
|
||||
Here is an example of using `DAST_API_POSTMAN_COLLECTION_VARIABLES`:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- dast
|
||||
|
||||
include:
|
||||
- template: DAST-API.gitlab-ci.yml
|
||||
|
||||
variables:
|
||||
DAST_API_PROFILE: Quick
|
||||
DAST_API_POSTMAN_COLLECTION: postman-collection.json
|
||||
DAST_API_POSTMAN_COLLECTION_VARIABLES: dast-api-scope.json
|
||||
DAST_API_TARGET_URL: http://test-deployment/
|
||||
```
|
||||
|
||||
The file `dast-api-scope.json` uses our [custom JSON file format](#dast-api-scope-custom-json-file-format). This JSON is an object with key-value pairs for properties. The keys are the variables' names, and the values are the variables'
|
||||
values. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"base_url": "http://127.0.0.1/",
|
||||
"token": "Token 84816165151"
|
||||
}
|
||||
```
|
||||
```json
|
||||
{
|
||||
"base_url": "http://127.0.0.1/",
|
||||
"token": "Token 84816165151"
|
||||
}
|
||||
```
|
||||
|
||||
##### Example: Multiple Scopes
|
||||
|
||||
In this example, a _global_ scope, _environment_ scope, and _collection_ scope are configured. The first step is to export our various scopes.
|
||||
|
||||
- [Export the _global_ scope](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments) as `global-scope.json`
|
||||
- [Export the _environment_ scope](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments) as `environment-scope.json`
|
||||
- Export the Postman Collection which includes the _collection_ scope as `postman-collection.json`
|
||||
|
||||
The Postman Collection is provided using the `DAST_API_POSTMAN_COLLECTION` variable, while the other scopes are provided using the `DAST_API_POSTMAN_COLLECTION_VARIABLES`. DAST API can identify which scope the provided files match using data provided in each file.
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- dast
|
||||
|
||||
include:
|
||||
- template: DAST-API.gitlab-ci.yml
|
||||
|
||||
variables:
|
||||
DAST_API_PROFILE: Quick
|
||||
DAST_API_POSTMAN_COLLECTION: postman-collection.json
|
||||
DAST_API_POSTMAN_COLLECTION_VARIABLES: global-scope.json,environment-scope.json
|
||||
DAST_API_TARGET_URL: http://test-deployment/
|
||||
```
|
||||
|
||||
##### Example: Changing a Variables Value
|
||||
|
||||
When using exported scopes, it's often the case that the value of a variable must be changed for use with DAST API. For example, a _collection_ scoped variable might contain a variable named `api_version` with a value of `v2`, while your test needs a value of `v1`. Instead of modifying the exported collection to change the value, the DAST API scope can be used to change its value. This works because the _DAST API_ scope takes precedence over all other scopes.
|
||||
|
||||
The _collection_ scope variables are included in the exported Postman Collection file and provided through the `DAST_API_POSTMAN_COLLECTION` configuration variable.
|
||||
|
||||
The DAST API Scope is provided through the `DAST_API_POSTMAN_COLLECTION_VARIABLES` configuration variable, but first, we must create the file.
|
||||
The file `dast-api-scope.json` uses our [custom JSON file format](#dast-api-scope-custom-json-file-format). This JSON is an object with key-value pairs for properties. The keys are the variables' names, and the values are the variables'
|
||||
values. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"api_version": "v1"
|
||||
}
|
||||
```
|
||||
|
||||
Our CI definition:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- dast
|
||||
|
||||
include:
|
||||
- template: DAST-API.gitlab-ci.yml
|
||||
|
||||
variables:
|
||||
DAST_API_PROFILE: Quick
|
||||
DAST_API_POSTMAN_COLLECTION: postman-collection.json
|
||||
DAST_API_POSTMAN_COLLECTION_VARIABLES: dast-api-scope.json
|
||||
DAST_API_TARGET_URL: http://test-deployment/
|
||||
```
|
||||
|
||||
##### Example: Changing a Variables Value with Multiple Scopes
|
||||
|
||||
When using exported scopes, it's often the case that the value of a variable must be changed for use with DAST API. For example, an _environment_ scope might contain a variable named `api_version` with a value of `v2`, while your test needs a value of `v1`. Instead of modifying the exported file to change the value, the DAST API scope can be used. This works because the _DAST API_ scope takes precedence over all other scopes.
|
||||
|
||||
In this example, a _global_ scope, _environment_ scope, _collection_ scope, and _DAST API_ scope are configured. The first step is to export and create our various scopes.
|
||||
|
||||
- [Export the _global_ scope](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments) as `global-scope.json`
|
||||
- [Export the _environment_ scope](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments) as `environment-scope.json`
|
||||
- Export the Postman Collection which includes the _collection_ scope as `postman-collection.json`
|
||||
|
||||
The DAST API scope is used by creating a file `dast-api-scope.json` using our [custom JSON file format](#dast-api-scope-custom-json-file-format). This JSON is an object with key-value pairs for properties. The keys are the variables' names, and the values are the variables'
|
||||
values. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"api_version": "v1"
|
||||
}
|
||||
```
|
||||
|
||||
The Postman Collection is provided using the `DAST_API_POSTMAN_COLLECTION` variable, while the other scopes are provided using the `DAST_API_POSTMAN_COLLECTION_VARIABLES`. DAST API can identify which scope the provided files match using data provided in each file.
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- dast
|
||||
|
||||
include:
|
||||
- template: DAST-API.gitlab-ci.yml
|
||||
|
||||
variables:
|
||||
DAST_API_PROFILE: Quick
|
||||
DAST_API_POSTMAN_COLLECTION: postman-collection.json
|
||||
DAST_API_POSTMAN_COLLECTION_VARIABLES: global-scope.json,environment-scope.json,dast-api-scope.json
|
||||
DAST_API_TARGET_URL: http://test-deployment/
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
|
@ -332,7 +712,7 @@ provide a script that performs an authentication flow or calculates the token.
|
|||
### HTTP Basic Authentication
|
||||
|
||||
[HTTP basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)
|
||||
is an authentication method built in to the HTTP protocol and used in conjunction with
|
||||
is an authentication method built into the HTTP protocol and used in conjunction with
|
||||
[transport layer security (TLS)](https://en.wikipedia.org/wiki/Transport_Layer_Security).
|
||||
|
||||
We recommended that you [create a CI/CD variable](../../../ci/variables/index.md#custom-cicd-variables)
|
||||
|
@ -389,7 +769,7 @@ Follow these steps to provide the Bearer token with `DAST_API_OVERRIDES_ENV`:
|
|||
can create CI/CD variables from the GitLab projects page at **Settings > CI/CD**, in the
|
||||
**Variables** section.
|
||||
Due to the format of `TEST_API_BEARERAUTH` it's not possible to mask the variable.
|
||||
To mask the token's value, you can create a second variable with the token value's, and define
|
||||
To mask the token's value, you can create a second variable with the token values, and define
|
||||
`TEST_API_BEARERAUTH` with the value `{"headers":{"Authorization":"Bearer $MASKED_VARIABLE"}}`.
|
||||
|
||||
1. In your `.gitlab-ci.yml` file, set `DAST_API_OVERRIDES_ENV` to the variable you just created:
|
||||
|
@ -408,12 +788,12 @@ Follow these steps to provide the Bearer token with `DAST_API_OVERRIDES_ENV`:
|
|||
DAST_API_OVERRIDES_ENV: $TEST_API_BEARERAUTH
|
||||
```
|
||||
|
||||
1. To validate that authentication is working, run an DAST API test and review the job logs
|
||||
1. To validate that authentication is working, run a DAST API test and review the job logs
|
||||
and the test API's application logs.
|
||||
|
||||
#### Token generated at test runtime
|
||||
|
||||
If the Bearer token must be generated and doesn't expire during testing, you can provide DAST API a file that has the token. A prior stage and job, or part of the DAST API job, can
|
||||
If the Bearer token must be generated and doesn't expire during testing, you can provide DAST API with a file that has the token. A prior stage and job, or part of the DAST API job, can
|
||||
generate this file.
|
||||
|
||||
DAST API expects to receive a JSON file with the following structure:
|
||||
|
@ -445,7 +825,7 @@ variables:
|
|||
DAST_API_OVERRIDES_FILE: dast-api-overrides.json
|
||||
```
|
||||
|
||||
To validate that authentication is working, run an DAST API test and review the job logs and
|
||||
To validate that authentication is working, run a DAST API test and review the job logs and
|
||||
the test API's application logs.
|
||||
|
||||
#### Token has short expiration
|
||||
|
@ -559,7 +939,7 @@ can be added, removed, and modified by creating a custom configuration.
|
|||
|[`DAST_API_OPENAPI_MEDIA_TYPES`](#openapi-specification) | Colon (`:`) separated media types accepted for testing. Default is disabled. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/333304) in GitLab 14.10. |
|
||||
|[`DAST_API_HAR`](#http-archive-har) | HTTP Archive (HAR) file. |
|
||||
|[`DAST_API_POSTMAN_COLLECTION`](#postman-collection) | Postman Collection file. |
|
||||
|[`DAST_API_POSTMAN_COLLECTION_VARIABLES`](#postman-variables) | Path to a JSON file to extract postman variable values. |
|
||||
|[`DAST_API_POSTMAN_COLLECTION_VARIABLES`](#postman-variables) | Path to a JSON file to extract Postman variable values. The support for comma-separated (`,`) files was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356312) in GitLab 15.1. |
|
||||
|[`DAST_API_OVERRIDES_FILE`](#overrides) | Path to a JSON file containing overrides. |
|
||||
|[`DAST_API_OVERRIDES_ENV`](#overrides) | JSON string containing headers to override. |
|
||||
|[`DAST_API_OVERRIDES_CMD`](#overrides) | Overrides command. |
|
||||
|
@ -1583,7 +1963,7 @@ The DAST API engine outputs an error message when it cannot establish a connecti
|
|||
**Error message**
|
||||
|
||||
- In [GitLab 13.11 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/323939), `Failed to start scanner session (version header not found).`
|
||||
- In GitLab 13.10 and earlier, `API Security version header not found. Are you sure that you are connecting to the API Security server?`.
|
||||
- In GitLab 13.10 and earlier, `API Security version header not found. Are you sure that you are connecting to the DAST API server?`.
|
||||
|
||||
**Solution**
|
||||
|
||||
|
@ -1645,7 +2025,7 @@ deploy-test-target:
|
|||
|
||||
### Use OpenAPI with an invalid schema
|
||||
|
||||
There are cases where the document is autogenerated with an invalid schema or cannot be edited manually in a timely manner. In those scenarios, the API Security is able to perform a relaxed validation by setting the variable `DAST_API_OPENAPI_RELAXED_VALIDATION`. We recommend providing a fully compliant OpenAPI document to prevent unexpected behaviors.
|
||||
There are cases where the document is autogenerated with an invalid schema or cannot be edited manually in a timely manner. In those scenarios, the DAST API is able to perform a relaxed validation by setting the variable `DAST_API_OPENAPI_RELAXED_VALIDATION`. We recommend providing a fully compliant OpenAPI document to prevent unexpected behaviors.
|
||||
|
||||
#### Edit a non-compliant OpenAPI file
|
||||
|
||||
|
@ -1662,7 +2042,7 @@ If your OpenAPI document is generated manually, load your document in the editor
|
|||
|
||||
Relaxed validation is meant for cases when the OpenAPI document cannot meet OpenAPI specifications, but it still has enough content to be consumed by different tools. A validation is performed but less strictly in regards to document schema.
|
||||
|
||||
API Security can still try to consume an OpenAPI document that does not fully comply with OpenAPI specifications. To instruct API Security to perform a relaxed validation, set the variable `DAST_API_OPENAPI_RELAXED_VALIDATION` to any value, for example:
|
||||
DAST API can still try to consume an OpenAPI document that does not fully comply with OpenAPI specifications. To instruct DAST API to perform a relaxed validation, set the variable `DAST_API_OPENAPI_RELAXED_VALIDATION` to any value, for example:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
|
@ -1680,7 +2060,7 @@ variables:
|
|||
|
||||
### `No operation in the OpenAPI document is consuming any supported media type`
|
||||
|
||||
API Security uses the specified media types in the OpenAPI document to generate requests. If no request can be created due to the lack of supported media types, then an error will be thrown.
|
||||
DAST API uses the specified media types in the OpenAPI document to generate requests. If no request can be created due to the lack of supported media types, then an error will be thrown.
|
||||
|
||||
**Error message**
|
||||
|
||||
|
|
|
@ -85,10 +85,10 @@ You can also [view our language roadmap](https://about.gitlab.com/direction/secu
|
|||
| Elixir (Phoenix) | [Sobelow](https://github.com/nccgroup/sobelow) | 11.1 |
|
||||
| Go | [Gosec](https://github.com/securego/gosec) | 10.7 |
|
||||
| Go | [Semgrep](https://semgrep.dev) | 14.4 |
|
||||
| Groovy<sup>2</sup> | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 11.3 (Gradle) & 11.9 (Ant, Maven, SBT) |
|
||||
| Groovy<sup>2</sup> | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 11.3 (Gradle) & 11.9 (Maven, SBT) |
|
||||
| Helm Charts | [Kubesec](https://github.com/controlplaneio/kubesec) | 13.1 |
|
||||
| Java (any build system) | [Semgrep](https://semgrep.dev) | 14.10 |
|
||||
| Java<sup>2</sup> | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 10.6 (Maven), 10.8 (Gradle) & 11.9 (Ant, SBT) |
|
||||
| Java<sup>2</sup> | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 10.6 (Maven), 10.8 (Gradle) & 11.9 (SBT) |
|
||||
| Java (Android) | [MobSF (beta)](https://github.com/MobSF/Mobile-Security-Framework-MobSF) | 13.5 |
|
||||
| JavaScript | [ESLint security plugin](https://github.com/nodesecurity/eslint-plugin-security) | 11.8 |
|
||||
| JavaScript | [Semgrep](https://semgrep.dev) | 13.10 |
|
||||
|
@ -104,16 +104,16 @@ You can also [view our language roadmap](https://about.gitlab.com/direction/secu
|
|||
| React | [Semgrep](https://semgrep.dev) | 13.10 |
|
||||
| Ruby | [brakeman](https://brakemanscanner.org) | 13.9 |
|
||||
| Ruby on Rails | [brakeman](https://brakemanscanner.org) | 10.3 |
|
||||
| Scala<sup>2</sup> | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 11.0 (SBT) & 11.9 (Ant, Gradle, Maven) |
|
||||
| Scala<sup>2</sup> | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 11.0 (SBT) & 11.9 (Gradle, Maven) |
|
||||
| Swift (iOS) | [MobSF (beta)](https://github.com/MobSF/Mobile-Security-Framework-MobSF) | 13.5 |
|
||||
| TypeScript | [ESLint security plugin](https://github.com/nodesecurity/eslint-plugin-security) | 11.9, [merged](https://gitlab.com/gitlab-org/gitlab/-/issues/36059) with ESLint in 13.2 |
|
||||
| TypeScript | [Semgrep](https://semgrep.dev) | 13.10 |
|
||||
|
||||
1. .NET 4 support is limited. The analyzer runs in a Linux container and does not have access to Windows-specific libraries or features. Use the Semgrep-based scanner if you need .NET 4 support.
|
||||
1. The SpotBugs-based analyzer supports [Ant](https://ant.apache.org/), [Gradle](https://gradle.org/), [Maven](https://maven.apache.org/), and [SBT](https://www.scala-sbt.org/). It can also be used with variants like the
|
||||
1. The SpotBugs-based analyzer supports [Gradle](https://gradle.org/), [Maven](https://maven.apache.org/), and [SBT](https://www.scala-sbt.org/). It can also be used with variants like the
|
||||
[Gradle wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html),
|
||||
[Grails](https://grails.org/),
|
||||
and the [Maven wrapper](https://github.com/takari/maven-wrapper).
|
||||
and the [Maven wrapper](https://github.com/takari/maven-wrapper). However, SpotBugs has [limitations](https://gitlab.com/gitlab-org/gitlab/-/issues/350801) when used against [Ant](https://ant.apache.org/)-based projects. We recommend using the Semgrep-based analyzer for Ant-based Java projects.
|
||||
|
||||
### Multi-project support
|
||||
|
||||
|
@ -551,8 +551,8 @@ Some analyzers can be customized with CI/CD variables.
|
|||
| `KUBESEC_HELM_CHARTS_PATH` | Kubesec | Optional path to Helm charts that `helm` uses to generate a Kubernetes manifest that `kubesec` scans. If dependencies are defined, `helm dependency build` should be ran in a `before_script` to fetch the necessary dependencies. |
|
||||
| `KUBESEC_HELM_OPTIONS` | Kubesec | Additional arguments for the `helm` executable. |
|
||||
| `COMPILE` | Gosec, SpotBugs | Set to `false` to disable project compilation and dependency fetching. [Introduced for `SpotBugs`](https://gitlab.com/gitlab-org/gitlab/-/issues/195252) analyzer in GitLab 13.1 and [`Gosec`](https://gitlab.com/gitlab-org/gitlab/-/issues/330678) analyzer in GitLab 14.0. |
|
||||
| `ANT_HOME` | SpotBugs | The `ANT_HOME` variable. |
|
||||
| `ANT_PATH` | SpotBugs | Path to the `ant` executable. |
|
||||
| `ANT_HOME` | SpotBugs | The `ANT_HOME` variable. |
|
||||
| `ANT_PATH` | SpotBugs | Path to the `ant` executable. |
|
||||
| `GRADLE_PATH` | SpotBugs | Path to the `gradle` executable. |
|
||||
| `JAVA_OPTS` | SpotBugs | Additional arguments for the `java` executable. |
|
||||
| `JAVA_PATH` | SpotBugs | Path to the `java` executable. |
|
||||
|
|
|
@ -6,7 +6,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# Operational Container Scanning **(ULTIMATE)**
|
||||
|
||||
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/6346) in GitLab 14.8.
|
||||
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/6346) in GitLab 14.8.
|
||||
> - [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/368828) the starboard directive in GitLab 15.4. The starboard directive will be removed in GitLab 16.0.
|
||||
|
||||
To view cluster vulnerabilities, you can view the [vulnerability report](../../application_security/vulnerabilities/index.md).
|
||||
You can also configure your agent so the vulnerabilities are displayed with other agent information in GitLab.
|
||||
|
@ -19,12 +20,12 @@ to scan container images in your cluster for security vulnerabilities.
|
|||
NOTE:
|
||||
In GitLab 15.0 and later, you do not need to install Starboard operator in the Kubernetes cluster.
|
||||
|
||||
To begin scanning all resources in your cluster, add a `starboard`
|
||||
To begin scanning all resources in your cluster, add a `container_scanning`
|
||||
configuration block to your agent configuration with a `cadence` field
|
||||
containing a CRON expression for when the scans will be run.
|
||||
|
||||
```yaml
|
||||
starboard:
|
||||
container_scanning:
|
||||
cadence: '0 0 * * *' # Daily at 00:00 (Kubernetes cluster time)
|
||||
```
|
||||
|
||||
|
@ -42,7 +43,7 @@ if you would like to scan only the `development`, `staging`, and `production`
|
|||
namespaces, you can use this configuration:
|
||||
|
||||
```yaml
|
||||
starboard:
|
||||
container_scanning:
|
||||
cadence: '0 0 * * *'
|
||||
vulnerability_report:
|
||||
namespaces:
|
||||
|
|
|
@ -208,9 +208,9 @@ from those IPs and allow them.
|
|||
|
||||
GitLab.com is fronted by Cloudflare. For incoming connections to GitLab.com, you might need to allow CIDR blocks of Cloudflare ([IPv4](https://www.cloudflare.com/ips-v4/) and [IPv6](https://www.cloudflare.com/ips-v6/)).
|
||||
|
||||
For outgoing connections from CI/CD runners, we are not providing static IP
|
||||
addresses. All GitLab.com shared runners are deployed into Google Cloud Platform (GCP). Any
|
||||
IP-based firewall can be configured by looking up all
|
||||
For outgoing connections from CI/CD runners, we are not providing static IP addresses.
|
||||
All GitLab.com shared runners are deployed into Google Cloud Platform (GCP) in `us-east1`.
|
||||
Any IP-based firewall can be configured by looking up
|
||||
[IP address ranges or CIDR blocks for GCP](https://cloud.google.com/compute/docs/faq#find_ip_range).
|
||||
|
||||
## Hostname list
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
stage: none
|
||||
group: unassigned
|
||||
stage: Manage
|
||||
group: Optimize
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
|
|
|
@ -70,6 +70,9 @@ The following items are exported:
|
|||
- Merge request diffs
|
||||
- Merge request comments
|
||||
- Merge request resource state events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4)
|
||||
- Merge request multiple assignees ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339520) in GitLab 15.3)
|
||||
- Merge request reviewers ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339520) in GitLab 15.3)
|
||||
- Merge request approvers ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339520) in GitLab 15.3)
|
||||
- Labels
|
||||
- Milestones
|
||||
- Snippets
|
||||
|
@ -92,7 +95,7 @@ Items that are **not** exported include:
|
|||
- Pipeline triggers
|
||||
- Webhooks
|
||||
- Any encrypted tokens
|
||||
- Merge Request Approvers and [the number of required approvals](https://gitlab.com/gitlab-org/gitlab/-/issues/221088)
|
||||
- [Number of required approvals](https://gitlab.com/gitlab-org/gitlab/-/issues/221088)
|
||||
- Repository size limits
|
||||
- Deploy keys allowed to push to protected branches
|
||||
- Secure Files
|
||||
|
|
|
@ -26976,6 +26976,9 @@ msgstr ""
|
|||
msgid "Notify|%{invited_user} has %{highlight_start}declined%{highlight_end} your invitation to join the %{target_link} %{target_name}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Notify|%{issues} imported."
|
||||
msgstr ""
|
||||
|
||||
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
|
||||
msgstr ""
|
||||
|
||||
|
@ -27015,6 +27018,12 @@ msgstr ""
|
|||
msgid "Notify|Don't want to receive updates from GitLab administrators?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Notify|Error parsing CSV file. Please make sure it has the correct format: a delimited text file that uses a comma to separate values."
|
||||
msgstr ""
|
||||
|
||||
msgid "Notify|Errors found on %{singular_or_plural_line}: %{error_lines}. Please check if these lines have an issue title."
|
||||
msgstr ""
|
||||
|
||||
msgid "Notify|Fingerprint: %{fingerprint}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -27090,6 +27099,9 @@ msgstr ""
|
|||
msgid "Notify|You have been mentioned in merge request %{mr_link}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Notify|Your CSV import for project %{project_link} has been completed."
|
||||
msgstr ""
|
||||
|
||||
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
|
||||
msgstr ""
|
||||
|
||||
|
@ -47044,6 +47056,11 @@ msgstr ""
|
|||
msgid "limit of %{project_limit} reached"
|
||||
msgstr ""
|
||||
|
||||
msgid "line"
|
||||
msgid_plural "lines"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "load it anyway"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -3,53 +3,54 @@
|
|||
module RuboCop
|
||||
module Cop
|
||||
module Gitlab
|
||||
class KeysFirstAndValuesFirst < RuboCop::Cop::Cop
|
||||
FIRST_PATTERN = /\Afirst\z/.freeze
|
||||
# Detects the use of `.keys.first` or `.values.first` and suggests a
|
||||
# change to `.each_key.first` or `.each_value.first`. This reduces
|
||||
# memory usage and execution time.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
#
|
||||
# hash.keys.first
|
||||
# hash.values.first
|
||||
#
|
||||
# # good
|
||||
#
|
||||
# hash.each_key.first
|
||||
# hash.each_value.first
|
||||
#
|
||||
class KeysFirstAndValuesFirst < RuboCop::Cop::Base
|
||||
extend RuboCop::Cop::AutoCorrector
|
||||
|
||||
def message(used_method)
|
||||
<<~MSG
|
||||
Don't use `.keys.first` and `.values.first`.
|
||||
Instead use `.each_key.first` and `.each_value.first` (or `.first.first` and `first.second`)
|
||||
MSG = 'Prefer `.%{autocorrect_method}.first` over `.%{current_method}.first`. ' \
|
||||
'This reduces memory usage and execution time.'
|
||||
|
||||
This will reduce memory usage and execution time.
|
||||
MSG
|
||||
end
|
||||
AUTOCORRECT_METHOD = {
|
||||
keys: 'each_key',
|
||||
values: 'each_value'
|
||||
}.freeze
|
||||
|
||||
def_node_matcher :keys_or_values_first, <<~PATTERN
|
||||
(send (send ({send const hash lvar} ...) ${:keys :values}) :first)
|
||||
PATTERN
|
||||
|
||||
def on_send(node)
|
||||
if find_on_keys_or_values?(node)
|
||||
add_offense(node, location: :selector, message: message(node.method_name))
|
||||
current_method = keys_or_values_first(node)
|
||||
return unless current_method
|
||||
|
||||
autocorrect_method = AUTOCORRECT_METHOD.fetch(current_method)
|
||||
msg = format(MSG, autocorrect_method: autocorrect_method, current_method: current_method)
|
||||
|
||||
add_offense(node.loc.selector, message: msg) do |corrector|
|
||||
replacement = "#{autocorrect_expression(node)}.#{autocorrect_method}.first"
|
||||
corrector.replace(node, replacement)
|
||||
end
|
||||
end
|
||||
|
||||
def autocorrect(node)
|
||||
lambda do |corrector|
|
||||
replace_with = if node.descendants.first.method_name == :values
|
||||
'.each_value'
|
||||
elsif node.descendants.first.method_name == :keys
|
||||
'.each_key'
|
||||
else
|
||||
throw("Expect '.values.first' or '.keys.first', but get #{node.descendants.first.method_name}.first") # rubocop:disable Cop/BanCatchThrow
|
||||
end
|
||||
private
|
||||
|
||||
upto_including_keys_or_values = node.descendants.first.source_range
|
||||
before_keys_or_values = node.descendants[1].source_range
|
||||
range_to_replace = node.source_range
|
||||
.with(begin_pos: before_keys_or_values.end_pos,
|
||||
end_pos: upto_including_keys_or_values.end_pos)
|
||||
corrector.replace(range_to_replace, replace_with)
|
||||
end
|
||||
end
|
||||
|
||||
def find_on_keys_or_values?(node)
|
||||
chained_on_node = node.descendants.first
|
||||
node.method_name.to_s =~ FIRST_PATTERN &&
|
||||
chained_on_node.is_a?(RuboCop::AST::SendNode) &&
|
||||
[:keys, :values].include?(chained_on_node.method_name) &&
|
||||
node.descendants[1]
|
||||
end
|
||||
|
||||
def method_name_for_node(node)
|
||||
children[1].to_s
|
||||
def autocorrect_expression(node)
|
||||
node.receiver.receiver.source
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -331,7 +331,6 @@ RSpec.describe 'Group show page' do
|
|||
end
|
||||
|
||||
it 'does not include structured markup in shared projects tab', :aggregate_failures, :js do
|
||||
stub_feature_flags(group_overview_tabs_vue: false)
|
||||
other_project = create(:project, :public)
|
||||
other_project.project_group_links.create!(group: group)
|
||||
|
||||
|
@ -343,7 +342,6 @@ RSpec.describe 'Group show page' do
|
|||
end
|
||||
|
||||
it 'does not include structured markup in archived projects tab', :aggregate_failures, :js do
|
||||
stub_feature_flags(group_overview_tabs_vue: false)
|
||||
project.update!(archived: true)
|
||||
|
||||
visit group_archived_path(group)
|
||||
|
|
|
@ -6,6 +6,7 @@ import OverviewTabs from '~/groups/components/overview_tabs.vue';
|
|||
import GroupsApp from '~/groups/components/app.vue';
|
||||
import GroupsStore from '~/groups/store/groups_store';
|
||||
import GroupsService from '~/groups/service/groups_service';
|
||||
import { createRouter } from '~/groups/init_overview_tabs';
|
||||
import {
|
||||
ACTIVE_TAB_SUBGROUPS_AND_PROJECTS,
|
||||
ACTIVE_TAB_SHARED,
|
||||
|
@ -13,6 +14,8 @@ import {
|
|||
} from '~/groups/constants';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
||||
const router = createRouter();
|
||||
|
||||
describe('OverviewTabs', () => {
|
||||
let wrapper;
|
||||
|
||||
|
@ -22,11 +25,19 @@ describe('OverviewTabs', () => {
|
|||
archived: '/groups/foobar/-/children.json?archived=only',
|
||||
};
|
||||
|
||||
const createComponent = async () => {
|
||||
const routerMock = {
|
||||
push: jest.fn(),
|
||||
};
|
||||
|
||||
const createComponent = async ({
|
||||
route = { name: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, params: { group: 'foo/bar/baz' } },
|
||||
} = {}) => {
|
||||
wrapper = mountExtended(OverviewTabs, {
|
||||
router,
|
||||
provide: {
|
||||
endpoints,
|
||||
},
|
||||
mocks: { $route: route, $router: routerMock },
|
||||
});
|
||||
|
||||
await nextTick();
|
||||
|
@ -34,6 +45,7 @@ describe('OverviewTabs', () => {
|
|||
|
||||
const findTabPanels = () => wrapper.findAllComponents(GlTab);
|
||||
const findTab = (name) => wrapper.findByRole('tab', { name });
|
||||
const findSelectedTab = () => wrapper.findByRole('tab', { selected: true });
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
|
@ -42,15 +54,15 @@ describe('OverviewTabs', () => {
|
|||
beforeEach(async () => {
|
||||
// eslint-disable-next-line no-new
|
||||
new AxiosMockAdapter(axios);
|
||||
|
||||
await createComponent();
|
||||
});
|
||||
|
||||
it('renders `Subgroups and projects` tab with `GroupsApp` component', async () => {
|
||||
await createComponent();
|
||||
|
||||
const tabPanel = findTabPanels().at(0);
|
||||
|
||||
expect(tabPanel.vm.$attrs).toMatchObject({
|
||||
title: OverviewTabs.i18n.subgroupsAndProjects,
|
||||
title: OverviewTabs.i18n[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS],
|
||||
lazy: false,
|
||||
});
|
||||
expect(tabPanel.findComponent(GroupsApp).props()).toMatchObject({
|
||||
|
@ -63,14 +75,16 @@ describe('OverviewTabs', () => {
|
|||
});
|
||||
|
||||
it('renders `Shared projects` tab and renders `GroupsApp` component after clicking tab', async () => {
|
||||
await createComponent();
|
||||
|
||||
const tabPanel = findTabPanels().at(1);
|
||||
|
||||
expect(tabPanel.vm.$attrs).toMatchObject({
|
||||
title: OverviewTabs.i18n.sharedProjects,
|
||||
title: OverviewTabs.i18n[ACTIVE_TAB_SHARED],
|
||||
lazy: true,
|
||||
});
|
||||
|
||||
await findTab(OverviewTabs.i18n.sharedProjects).trigger('click');
|
||||
await findTab(OverviewTabs.i18n[ACTIVE_TAB_SHARED]).trigger('click');
|
||||
|
||||
expect(tabPanel.findComponent(GroupsApp).props()).toMatchObject({
|
||||
action: ACTIVE_TAB_SHARED,
|
||||
|
@ -84,14 +98,16 @@ describe('OverviewTabs', () => {
|
|||
});
|
||||
|
||||
it('renders `Archived projects` tab and renders `GroupsApp` component after clicking tab', async () => {
|
||||
await createComponent();
|
||||
|
||||
const tabPanel = findTabPanels().at(2);
|
||||
|
||||
expect(tabPanel.vm.$attrs).toMatchObject({
|
||||
title: OverviewTabs.i18n.archivedProjects,
|
||||
title: OverviewTabs.i18n[ACTIVE_TAB_ARCHIVED],
|
||||
lazy: true,
|
||||
});
|
||||
|
||||
await findTab(OverviewTabs.i18n.archivedProjects).trigger('click');
|
||||
await findTab(OverviewTabs.i18n[ACTIVE_TAB_ARCHIVED]).trigger('click');
|
||||
|
||||
expect(tabPanel.findComponent(GroupsApp).props()).toMatchObject({
|
||||
action: ACTIVE_TAB_ARCHIVED,
|
||||
|
@ -103,4 +119,69 @@ describe('OverviewTabs', () => {
|
|||
|
||||
expect(tabPanel.vm.$attrs.lazy).toBe(false);
|
||||
});
|
||||
|
||||
describe.each([
|
||||
[
|
||||
{ name: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, params: { group: 'foo/bar/baz' } },
|
||||
OverviewTabs.i18n[ACTIVE_TAB_SHARED],
|
||||
{
|
||||
name: ACTIVE_TAB_SHARED,
|
||||
params: { group: ['foo', 'bar', 'baz'] },
|
||||
},
|
||||
],
|
||||
[
|
||||
{ name: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, params: { group: ['foo', 'bar', 'baz'] } },
|
||||
OverviewTabs.i18n[ACTIVE_TAB_SHARED],
|
||||
{
|
||||
name: ACTIVE_TAB_SHARED,
|
||||
params: { group: ['foo', 'bar', 'baz'] },
|
||||
},
|
||||
],
|
||||
[
|
||||
{ name: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, params: { group: 'foo' } },
|
||||
OverviewTabs.i18n[ACTIVE_TAB_SHARED],
|
||||
{
|
||||
name: ACTIVE_TAB_SHARED,
|
||||
params: { group: ['foo'] },
|
||||
},
|
||||
],
|
||||
[
|
||||
{ name: ACTIVE_TAB_SHARED, params: { group: 'foo/bar' } },
|
||||
OverviewTabs.i18n[ACTIVE_TAB_ARCHIVED],
|
||||
{
|
||||
name: ACTIVE_TAB_ARCHIVED,
|
||||
params: { group: ['foo', 'bar'] },
|
||||
},
|
||||
],
|
||||
[
|
||||
{ name: ACTIVE_TAB_SHARED, params: { group: 'foo/bar' } },
|
||||
OverviewTabs.i18n[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS],
|
||||
{
|
||||
name: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS,
|
||||
params: { group: ['foo', 'bar'] },
|
||||
},
|
||||
],
|
||||
[
|
||||
{ name: ACTIVE_TAB_ARCHIVED, params: { group: ['foo'] } },
|
||||
OverviewTabs.i18n[ACTIVE_TAB_SHARED],
|
||||
{
|
||||
name: ACTIVE_TAB_SHARED,
|
||||
params: { group: ['foo'] },
|
||||
},
|
||||
],
|
||||
])('when current route is %j', (currentRoute, tabToClick, expectedRoute) => {
|
||||
beforeEach(async () => {
|
||||
await createComponent({ route: currentRoute });
|
||||
});
|
||||
|
||||
it(`sets ${OverviewTabs.i18n[currentRoute.name]} as active tab`, () => {
|
||||
expect(findSelectedTab().text()).toBe(OverviewTabs.i18n[currentRoute.name]);
|
||||
});
|
||||
|
||||
it(`pushes expected route when ${tabToClick} tab is clicked`, async () => {
|
||||
await findTab(tabToClick).trigger('click');
|
||||
|
||||
expect(routerMock.push).toHaveBeenCalledWith(expectedRoute);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Resolvers::Environments::LastDeploymentResolver do
|
||||
include GraphqlHelpers
|
||||
include Gitlab::Graphql::Laziness
|
||||
|
||||
let_it_be(:project) { create(:project, :repository, :private) }
|
||||
let_it_be(:environment) { create(:environment, project: project) }
|
||||
let_it_be(:deployment) { create(:deployment, :created, environment: environment, project: project) }
|
||||
let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } }
|
||||
|
||||
let(:current_user) { developer }
|
||||
|
||||
describe '#resolve' do
|
||||
it 'finds the deployment when status matches' do
|
||||
expect(resolve_last_deployment(status: :created)).to eq(deployment)
|
||||
end
|
||||
|
||||
it 'does not find the deployment when status does not match' do
|
||||
expect(resolve_last_deployment(status: :success)).to be_nil
|
||||
end
|
||||
|
||||
it 'raises an error when status is not specified' do
|
||||
expect { resolve_last_deployment }.to raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it 'raises an error when status is not supported' do
|
||||
expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError,
|
||||
'"skipped" status is not supported.') do
|
||||
resolve_last_deployment(status: :skipped)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def resolve_last_deployment(args = {}, context = { current_user: current_user })
|
||||
force(resolve(described_class, obj: environment, ctx: context, args: args))
|
||||
end
|
||||
end
|
|
@ -8,7 +8,7 @@ RSpec.describe GitlabSchema.types['Environment'] do
|
|||
it 'has the expected fields' do
|
||||
expected_fields = %w[
|
||||
name id state metrics_dashboard latest_opened_most_severe_alert path external_url deployments
|
||||
slug createdAt updatedAt autoStopAt autoDeleteAt tier environmentType
|
||||
slug createdAt updatedAt autoStopAt autoDeleteAt tier environmentType lastDeployment
|
||||
]
|
||||
|
||||
expect(described_class).to have_graphql_fields(*expected_fields)
|
||||
|
|
|
@ -2,11 +2,9 @@
|
|||
|
||||
require "spec_helper"
|
||||
|
||||
RSpec.describe Gitlab::Git::Blob, :seed_helper do
|
||||
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '', 'group/project') }
|
||||
let(:rugged) do
|
||||
Rugged::Repository.new(File.join(TestEnv.repos_path, TEST_REPO_PATH))
|
||||
end
|
||||
RSpec.describe Gitlab::Git::Blob do
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:repository) { project.repository.raw }
|
||||
|
||||
describe 'initialize' do
|
||||
let(:blob) { Gitlab::Git::Blob.new(name: 'test') }
|
||||
|
@ -44,7 +42,7 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
|
|||
|
||||
shared_examples '.find' do
|
||||
context 'nil path' do
|
||||
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, nil) }
|
||||
let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], nil) }
|
||||
|
||||
it { expect(blob).to eq(nil) }
|
||||
end
|
||||
|
@ -56,30 +54,30 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
|
|||
end
|
||||
|
||||
context 'blank path' do
|
||||
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, '') }
|
||||
let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], '') }
|
||||
|
||||
it { expect(blob).to eq(nil) }
|
||||
end
|
||||
|
||||
context 'file in subdir' do
|
||||
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "files/ruby/popen.rb") }
|
||||
let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], "files/ruby/popen.rb") }
|
||||
|
||||
it { expect(blob.id).to eq(SeedRepo::RubyBlob::ID) }
|
||||
it { expect(blob.name).to eq(SeedRepo::RubyBlob::NAME) }
|
||||
it { expect(blob.path).to eq("files/ruby/popen.rb") }
|
||||
it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
|
||||
it { expect(blob.commit_id).to eq(TestEnv::BRANCH_SHA['master']) }
|
||||
it { expect(blob.data[0..10]).to eq(SeedRepo::RubyBlob::CONTENT[0..10]) }
|
||||
it { expect(blob.size).to eq(669) }
|
||||
it { expect(blob.mode).to eq("100644") }
|
||||
end
|
||||
|
||||
context 'file in root' do
|
||||
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, ".gitignore") }
|
||||
let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], ".gitignore") }
|
||||
|
||||
it { expect(blob.id).to eq("dfaa3f97ca337e20154a98ac9d0be76ddd1fcc82") }
|
||||
it { expect(blob.name).to eq(".gitignore") }
|
||||
it { expect(blob.path).to eq(".gitignore") }
|
||||
it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
|
||||
it { expect(blob.commit_id).to eq(TestEnv::BRANCH_SHA['master']) }
|
||||
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") }
|
||||
|
@ -87,25 +85,25 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
|
|||
end
|
||||
|
||||
context 'file in root with leading slash' do
|
||||
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "/.gitignore") }
|
||||
let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], "/.gitignore") }
|
||||
|
||||
it { expect(blob.id).to eq("dfaa3f97ca337e20154a98ac9d0be76ddd1fcc82") }
|
||||
it { expect(blob.name).to eq(".gitignore") }
|
||||
it { expect(blob.path).to eq(".gitignore") }
|
||||
it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
|
||||
it { expect(blob.commit_id).to eq(TestEnv::BRANCH_SHA['master']) }
|
||||
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") }
|
||||
end
|
||||
|
||||
context 'non-exist file' do
|
||||
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "missing.rb") }
|
||||
let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], "missing.rb") }
|
||||
|
||||
it { expect(blob).to be_nil }
|
||||
end
|
||||
|
||||
context 'six submodule' do
|
||||
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, 'six') }
|
||||
let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], 'six') }
|
||||
|
||||
it { expect(blob.id).to eq('409f37c4f05865e4fb208c771485f211a22c4c2d') }
|
||||
it { expect(blob.data).to eq('') }
|
||||
|
@ -121,7 +119,7 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
|
|||
end
|
||||
|
||||
context 'large file' do
|
||||
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, 'files/images/6049019_460s.jpg') }
|
||||
let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], 'files/images/6049019_460s.jpg') }
|
||||
let(:blob_size) { 111803 }
|
||||
let(:stub_limit) { 1000 }
|
||||
|
||||
|
@ -159,10 +157,10 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
|
|||
describe '.find with Rugged enabled', :enable_rugged do
|
||||
it 'calls out to the Rugged implementation' do
|
||||
allow_next_instance_of(Rugged) do |instance|
|
||||
allow(instance).to receive(:rev_parse).with(SeedRepo::Commit::ID).and_call_original
|
||||
allow(instance).to receive(:rev_parse).with(TestEnv::BRANCH_SHA['master']).and_call_original
|
||||
end
|
||||
|
||||
described_class.find(repository, SeedRepo::Commit::ID, 'files/images/6049019_460s.jpg')
|
||||
described_class.find(repository, TestEnv::BRANCH_SHA['master'], 'files/images/6049019_460s.jpg')
|
||||
end
|
||||
|
||||
it_behaves_like '.find'
|
||||
|
@ -177,40 +175,13 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
|
|||
it { expect(raw_blob.size).to eq(669) }
|
||||
it { expect(raw_blob.truncated?).to be_falsey }
|
||||
it { expect(bad_blob).to be_nil }
|
||||
|
||||
context 'large file' do
|
||||
it 'limits the size of a large file' do
|
||||
blob_size = Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE + 1
|
||||
buffer = Array.new(blob_size, 0)
|
||||
rugged_blob = Rugged::Blob.from_buffer(rugged, buffer.join(''))
|
||||
blob = Gitlab::Git::Blob.raw(repository, rugged_blob)
|
||||
|
||||
expect(blob.size).to eq(blob_size)
|
||||
expect(blob.loaded_size).to eq(Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE)
|
||||
expect(blob.data.length).to eq(Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE)
|
||||
expect(blob.truncated?).to be_truthy
|
||||
|
||||
blob.load_all_data!(repository)
|
||||
expect(blob.loaded_size).to eq(blob_size)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when sha references a tree' do
|
||||
it 'returns nil' do
|
||||
tree = rugged.rev_parse('master^{tree}')
|
||||
|
||||
blob = Gitlab::Git::Blob.raw(repository, tree.oid)
|
||||
|
||||
expect(blob).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.batch' do
|
||||
let(:blob_references) do
|
||||
[
|
||||
[SeedRepo::Commit::ID, "files/ruby/popen.rb"],
|
||||
[SeedRepo::Commit::ID, 'six']
|
||||
[TestEnv::BRANCH_SHA['master'], "files/ruby/popen.rb"],
|
||||
[TestEnv::BRANCH_SHA['master'], 'six']
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -224,7 +195,7 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
|
|||
it { expect(blob.id).to eq(SeedRepo::RubyBlob::ID) }
|
||||
it { expect(blob.name).to eq(SeedRepo::RubyBlob::NAME) }
|
||||
it { expect(blob.path).to eq("files/ruby/popen.rb") }
|
||||
it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
|
||||
it { expect(blob.commit_id).to eq(TestEnv::BRANCH_SHA['master']) }
|
||||
it { expect(blob.data[0..10]).to eq(SeedRepo::RubyBlob::CONTENT[0..10]) }
|
||||
it { expect(blob.size).to eq(669) }
|
||||
it { expect(blob.mode).to eq("100644") }
|
||||
|
@ -273,21 +244,21 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
|
|||
context 'when large number of blobs requested' do
|
||||
let(:first_batch) do
|
||||
[
|
||||
[SeedRepo::Commit::ID, 'files/ruby/popen.rb'],
|
||||
[SeedRepo::Commit::ID, 'six']
|
||||
[TestEnv::BRANCH_SHA['master'], 'files/ruby/popen.rb'],
|
||||
[TestEnv::BRANCH_SHA['master'], 'six']
|
||||
]
|
||||
end
|
||||
|
||||
let(:second_batch) do
|
||||
[
|
||||
[SeedRepo::Commit::ID, 'some'],
|
||||
[SeedRepo::Commit::ID, 'other']
|
||||
[TestEnv::BRANCH_SHA['master'], 'some'],
|
||||
[TestEnv::BRANCH_SHA['master'], 'other']
|
||||
]
|
||||
end
|
||||
|
||||
let(:third_batch) do
|
||||
[
|
||||
[SeedRepo::Commit::ID, 'files']
|
||||
[TestEnv::BRANCH_SHA['master'], 'files']
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -315,8 +286,8 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
|
|||
describe '.batch_metadata' do
|
||||
let(:blob_references) do
|
||||
[
|
||||
[SeedRepo::Commit::ID, "files/ruby/popen.rb"],
|
||||
[SeedRepo::Commit::ID, 'six']
|
||||
[TestEnv::BRANCH_SHA['master'], "files/ruby/popen.rb"],
|
||||
[TestEnv::BRANCH_SHA['master'], 'six']
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -333,8 +304,6 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
|
|||
end
|
||||
|
||||
describe '.batch_lfs_pointers' do
|
||||
let(:tree_object) { rugged.rev_parse('master^{tree}') }
|
||||
|
||||
let(:non_lfs_blob) do
|
||||
Gitlab::Git::Blob.find(
|
||||
repository,
|
||||
|
@ -346,8 +315,8 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
|
|||
let(:lfs_blob) do
|
||||
Gitlab::Git::Blob.find(
|
||||
repository,
|
||||
'33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
|
||||
'files/lfs/image.jpg'
|
||||
TestEnv::BRANCH_SHA['master'],
|
||||
'files/lfs/lfs_object.iso'
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -374,12 +343,6 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
|
|||
expect(blobs_2).to eq([])
|
||||
end
|
||||
|
||||
it 'silently ignores tree objects' do
|
||||
blobs = described_class.batch_lfs_pointers(repository, [tree_object.oid])
|
||||
|
||||
expect(blobs).to eq([])
|
||||
end
|
||||
|
||||
it 'silently ignores non lfs objects' do
|
||||
blobs = described_class.batch_lfs_pointers(repository, [non_lfs_blob.id])
|
||||
|
||||
|
@ -398,7 +361,7 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
|
|||
|
||||
describe 'encoding', :aggregate_failures do
|
||||
context 'file with russian text' do
|
||||
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "encoding/russian.rb") }
|
||||
let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], "encoding/russian.rb") }
|
||||
|
||||
it 'has the correct blob attributes' do
|
||||
expect(blob.name).to eq("russian.rb")
|
||||
|
@ -412,7 +375,7 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
|
|||
end
|
||||
|
||||
context 'file with Japanese text' do
|
||||
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "encoding/テスト.txt") }
|
||||
let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], "encoding/テスト.txt") }
|
||||
|
||||
it 'has the correct blob attributes' do
|
||||
expect(blob.name).to eq("テスト.txt")
|
||||
|
@ -424,12 +387,12 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
|
|||
end
|
||||
|
||||
context 'file with ISO-8859 text' do
|
||||
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::LastCommit::ID, "encoding/iso8859.txt") }
|
||||
let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], "encoding/iso8859.txt") }
|
||||
|
||||
it 'has the correct blob attributes' do
|
||||
expect(blob.name).to eq("iso8859.txt")
|
||||
expect(blob.loaded_size).to eq(4)
|
||||
expect(blob.size).to eq(4)
|
||||
expect(blob.loaded_size).to eq(3)
|
||||
expect(blob.size).to eq(3)
|
||||
expect(blob.mode).to eq("100644")
|
||||
expect(blob.truncated?).to be_falsey
|
||||
end
|
||||
|
@ -441,7 +404,7 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
|
|||
let(:blob) do
|
||||
Gitlab::Git::Blob.find(
|
||||
repository,
|
||||
'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6',
|
||||
TestEnv::BRANCH_SHA['master'],
|
||||
'files/ruby/regex.rb'
|
||||
)
|
||||
end
|
||||
|
@ -456,14 +419,14 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
|
|||
let(:blob) do
|
||||
Gitlab::Git::Blob.find(
|
||||
repository,
|
||||
'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6',
|
||||
TestEnv::BRANCH_SHA['with-executables'],
|
||||
'files/executables/ls'
|
||||
)
|
||||
end
|
||||
|
||||
it { expect(blob.name).to eq('ls') }
|
||||
it { expect(blob.path).to eq('files/executables/ls') }
|
||||
it { expect(blob.size).to eq(110080) }
|
||||
it { expect(blob.size).to eq(23) }
|
||||
it { expect(blob.mode).to eq("100755") }
|
||||
end
|
||||
|
||||
|
@ -471,29 +434,14 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
|
|||
let(:blob) do
|
||||
Gitlab::Git::Blob.find(
|
||||
repository,
|
||||
'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6',
|
||||
'files/links/ruby-style-guide.md'
|
||||
'88ce9520c07b7067f589b7f83a30b6250883115c',
|
||||
'symlink'
|
||||
)
|
||||
end
|
||||
|
||||
it { expect(blob.name).to eq('ruby-style-guide.md') }
|
||||
it { expect(blob.path).to eq('files/links/ruby-style-guide.md') }
|
||||
it { expect(blob.size).to eq(31) }
|
||||
it { expect(blob.mode).to eq("120000") }
|
||||
end
|
||||
|
||||
context 'file symlink to binary' do
|
||||
let(:blob) do
|
||||
Gitlab::Git::Blob.find(
|
||||
repository,
|
||||
'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6',
|
||||
'files/links/touch'
|
||||
)
|
||||
end
|
||||
|
||||
it { expect(blob.name).to eq('touch') }
|
||||
it { expect(blob.path).to eq('files/links/touch') }
|
||||
it { expect(blob.size).to eq(20) }
|
||||
it { expect(blob.name).to eq('symlink') }
|
||||
it { expect(blob.path).to eq('symlink') }
|
||||
it { expect(blob.size).to eq(6) }
|
||||
it { expect(blob.mode).to eq("120000") }
|
||||
end
|
||||
end
|
||||
|
@ -503,79 +451,20 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
|
|||
let(:blob) do
|
||||
Gitlab::Git::Blob.find(
|
||||
repository,
|
||||
'33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
|
||||
'files/lfs/image.jpg'
|
||||
TestEnv::BRANCH_SHA['png-lfs'],
|
||||
'files/images/emoji.png'
|
||||
)
|
||||
end
|
||||
|
||||
it { expect(blob.lfs_pointer?).to eq(true) }
|
||||
it { expect(blob.lfs_oid).to eq("4206f951d2691c78aac4c0ce9f2b23580b2c92cdcc4336e1028742c0274938e0") }
|
||||
it { expect(blob.lfs_size).to eq(19548) }
|
||||
it { expect(blob.id).to eq("f4d76af13003d1106be7ac8c5a2a3d37ddf32c2a") }
|
||||
it { expect(blob.name).to eq("image.jpg") }
|
||||
it { expect(blob.path).to eq("files/lfs/image.jpg") }
|
||||
it { expect(blob.size).to eq(130) }
|
||||
it { expect(blob.lfs_oid).to eq("96f74c6fe7a2979eefb9ec74a5dfc6888fb25543cf99b77586b79afea1da6f97") }
|
||||
it { expect(blob.lfs_size).to eq(1219696) }
|
||||
it { expect(blob.id).to eq("ff0ab3afd1616ff78d0331865d922df103b64cf0") }
|
||||
it { expect(blob.name).to eq("emoji.png") }
|
||||
it { expect(blob.path).to eq("files/images/emoji.png") }
|
||||
it { expect(blob.size).to eq(132) }
|
||||
it { expect(blob.mode).to eq("100644") }
|
||||
end
|
||||
|
||||
describe 'file an invalid lfs pointer' do
|
||||
context 'with correct version header but incorrect size and oid' do
|
||||
let(:blob) do
|
||||
Gitlab::Git::Blob.find(
|
||||
repository,
|
||||
'33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
|
||||
'files/lfs/archive-invalid.tar'
|
||||
)
|
||||
end
|
||||
|
||||
it { expect(blob.lfs_pointer?).to eq(false) }
|
||||
it { expect(blob.lfs_oid).to eq(nil) }
|
||||
it { expect(blob.lfs_size).to eq(nil) }
|
||||
it { expect(blob.id).to eq("f8a898db217a5a85ed8b3d25b34c1df1d1094c46") }
|
||||
it { expect(blob.name).to eq("archive-invalid.tar") }
|
||||
it { expect(blob.path).to eq("files/lfs/archive-invalid.tar") }
|
||||
it { expect(blob.size).to eq(43) }
|
||||
it { expect(blob.mode).to eq("100644") }
|
||||
end
|
||||
|
||||
context 'with correct version header and size but incorrect size and oid' do
|
||||
let(:blob) do
|
||||
Gitlab::Git::Blob.find(
|
||||
repository,
|
||||
'33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
|
||||
'files/lfs/picture-invalid.png'
|
||||
)
|
||||
end
|
||||
|
||||
it { expect(blob.lfs_pointer?).to eq(false) }
|
||||
it { expect(blob.lfs_oid).to eq(nil) }
|
||||
it { expect(blob.lfs_size).to eq(1575078) }
|
||||
it { expect(blob.id).to eq("5ae35296e1f95c1ef9feda1241477ed29a448572") }
|
||||
it { expect(blob.name).to eq("picture-invalid.png") }
|
||||
it { expect(blob.path).to eq("files/lfs/picture-invalid.png") }
|
||||
it { expect(blob.size).to eq(57) }
|
||||
it { expect(blob.mode).to eq("100644") }
|
||||
end
|
||||
|
||||
context 'with correct version header and size but invalid size and oid' do
|
||||
let(:blob) do
|
||||
Gitlab::Git::Blob.find(
|
||||
repository,
|
||||
'33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
|
||||
'files/lfs/file-invalid.zip'
|
||||
)
|
||||
end
|
||||
|
||||
it { expect(blob.lfs_pointer?).to eq(false) }
|
||||
it { expect(blob.lfs_oid).to eq(nil) }
|
||||
it { expect(blob.lfs_size).to eq(nil) }
|
||||
it { expect(blob.id).to eq("d831981bd876732b85a1bcc6cc01210c9f36248f") }
|
||||
it { expect(blob.name).to eq("file-invalid.zip") }
|
||||
it { expect(blob.path).to eq("files/lfs/file-invalid.zip") }
|
||||
it { expect(blob.size).to eq(60) }
|
||||
it { expect(blob.mode).to eq("100644") }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#load_all_data!' do
|
||||
|
|
|
@ -563,6 +563,16 @@ RSpec.describe Deployment do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.ordered_as_upcoming' do
|
||||
let!(:deployment1) { create(:deployment, status: :running) }
|
||||
let!(:deployment2) { create(:deployment, status: :blocked) }
|
||||
let!(:deployment3) { create(:deployment, status: :created) }
|
||||
|
||||
it 'sorts by ID DESC' do
|
||||
expect(described_class.ordered_as_upcoming).to eq([deployment3, deployment2, deployment1])
|
||||
end
|
||||
end
|
||||
|
||||
describe 'visible' do
|
||||
subject { described_class.visible }
|
||||
|
||||
|
|
|
@ -948,6 +948,26 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'Last deployment relations' do
|
||||
Deployment::FINISHED_STATUSES.each do |status|
|
||||
it "returns the last #{status} deployment" do
|
||||
create(:deployment, status.to_sym, environment: environment, finished_at: 1.day.ago)
|
||||
expected = create(:deployment, status.to_sym, environment: environment, finished_at: Time.current)
|
||||
|
||||
expect(environment.public_send(:"last_#{status}_deployment")).to eq(expected)
|
||||
end
|
||||
end
|
||||
|
||||
Deployment::UPCOMING_STATUSES.each do |status|
|
||||
it "returns the last #{status} deployment" do
|
||||
create(:deployment, status.to_sym, environment: environment)
|
||||
expected = create(:deployment, status.to_sym, environment: environment)
|
||||
|
||||
expect(environment.public_send(:"last_#{status}_deployment")).to eq(expected)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#last_deployable' do
|
||||
subject { environment.last_deployable }
|
||||
|
||||
|
|
|
@ -6,7 +6,8 @@ RSpec.describe 'Project Environments query' do
|
|||
include GraphqlHelpers
|
||||
|
||||
let_it_be(:project) { create(:project, :private, :repository) }
|
||||
let_it_be(:environment) { create(:environment, project: project) }
|
||||
let_it_be_with_refind(:production) { create(:environment, :production, project: project) }
|
||||
let_it_be_with_refind(:staging) { create(:environment, :staging, project: project) }
|
||||
let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } }
|
||||
|
||||
subject { post_graphql(query, current_user: user) }
|
||||
|
@ -17,7 +18,7 @@ RSpec.describe 'Project Environments query' do
|
|||
%(
|
||||
query {
|
||||
project(fullPath: "#{project.full_path}") {
|
||||
environment(name: "#{environment.name}") {
|
||||
environment(name: "#{production.name}") {
|
||||
slug
|
||||
createdAt
|
||||
updatedAt
|
||||
|
@ -32,17 +33,101 @@ RSpec.describe 'Project Environments query' do
|
|||
end
|
||||
|
||||
it 'returns the specified fields of the environment', :aggregate_failures do
|
||||
environment.update!(auto_stop_at: 1.day.ago, auto_delete_at: 2.days.ago, environment_type: 'review')
|
||||
production.update!(auto_stop_at: 1.day.ago, auto_delete_at: 2.days.ago, environment_type: 'review')
|
||||
|
||||
subject
|
||||
|
||||
environment_data = graphql_data.dig('project', 'environment')
|
||||
expect(environment_data['slug']).to eq(environment.slug)
|
||||
expect(environment_data['createdAt']).to eq(environment.created_at.iso8601)
|
||||
expect(environment_data['updatedAt']).to eq(environment.updated_at.iso8601)
|
||||
expect(environment_data['autoStopAt']).to eq(environment.auto_stop_at.iso8601)
|
||||
expect(environment_data['autoDeleteAt']).to eq(environment.auto_delete_at.iso8601)
|
||||
expect(environment_data['tier']).to eq(environment.tier.upcase)
|
||||
expect(environment_data['environmentType']).to eq(environment.environment_type)
|
||||
expect(environment_data['slug']).to eq(production.slug)
|
||||
expect(environment_data['createdAt']).to eq(production.created_at.iso8601)
|
||||
expect(environment_data['updatedAt']).to eq(production.updated_at.iso8601)
|
||||
expect(environment_data['autoStopAt']).to eq(production.auto_stop_at.iso8601)
|
||||
expect(environment_data['autoDeleteAt']).to eq(production.auto_delete_at.iso8601)
|
||||
expect(environment_data['tier']).to eq(production.tier.upcase)
|
||||
expect(environment_data['environmentType']).to eq(production.environment_type)
|
||||
end
|
||||
|
||||
describe 'last deployments of environments' do
|
||||
::Deployment.statuses.each do |status, _|
|
||||
let_it_be(:"production_#{status}_deployment") do
|
||||
create(:deployment, status.to_sym, environment: production, project: project)
|
||||
end
|
||||
|
||||
let_it_be(:"staging_#{status}_deployment") do
|
||||
create(:deployment, status.to_sym, environment: staging, project: project)
|
||||
end
|
||||
end
|
||||
|
||||
let(:query) do
|
||||
%(
|
||||
query {
|
||||
project(fullPath: "#{project.full_path}") {
|
||||
environments {
|
||||
nodes {
|
||||
name
|
||||
lastSuccessDeployment: lastDeployment(status: SUCCESS) {
|
||||
iid
|
||||
}
|
||||
lastRunningDeployment: lastDeployment(status: RUNNING) {
|
||||
iid
|
||||
}
|
||||
lastBlockedDeployment: lastDeployment(status: BLOCKED) {
|
||||
iid
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
it 'returns all last deployments of the environment' do
|
||||
subject
|
||||
|
||||
environments_data = graphql_data_at(:project, :environments, :nodes)
|
||||
|
||||
environments_data.each do |environment_data|
|
||||
name = environment_data['name']
|
||||
success_deployment = public_send(:"#{name}_success_deployment")
|
||||
running_deployment = public_send(:"#{name}_running_deployment")
|
||||
blocked_deployment = public_send(:"#{name}_blocked_deployment")
|
||||
|
||||
expect(environment_data['lastSuccessDeployment']['iid']).to eq(success_deployment.iid.to_s)
|
||||
expect(environment_data['lastRunningDeployment']['iid']).to eq(running_deployment.iid.to_s)
|
||||
expect(environment_data['lastBlockedDeployment']['iid']).to eq(blocked_deployment.iid.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
it 'executes the same number of queries in single environment and multiple environments' do
|
||||
single_environment_query =
|
||||
%(
|
||||
query {
|
||||
project(fullPath: "#{project.full_path}") {
|
||||
environment(name: "#{production.name}") {
|
||||
name
|
||||
lastSuccessDeployment: lastDeployment(status: SUCCESS) {
|
||||
iid
|
||||
}
|
||||
lastRunningDeployment: lastDeployment(status: RUNNING) {
|
||||
iid
|
||||
}
|
||||
lastBlockedDeployment: lastDeployment(status: BLOCKED) {
|
||||
iid
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
baseline = ActiveRecord::QueryRecorder.new do
|
||||
run_with_clean_state(single_environment_query, context: { current_user: user })
|
||||
end
|
||||
|
||||
multi = ActiveRecord::QueryRecorder.new do
|
||||
run_with_clean_state(query, context: { current_user: user })
|
||||
end
|
||||
|
||||
expect(multi).not_to exceed_query_limit(baseline)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop_spec_helper'
|
||||
|
||||
require_relative '../../../../rubocop/cop/gitlab/keys_first_and_values_first'
|
||||
|
||||
RSpec.describe RuboCop::Cop::Gitlab::KeysFirstAndValuesFirst do
|
||||
let(:msg) { described_class::MSG }
|
||||
|
||||
subject(:cop) { described_class.new }
|
||||
|
||||
shared_examples 'inspect use of keys or values first' do |method, autocorrect|
|
||||
describe ".#{method}.first" do
|
||||
it 'flags and autocorrects' do
|
||||
expect_offense(<<~RUBY, method: method, autocorrect: autocorrect)
|
||||
hash.%{method}.first
|
||||
_{method} ^^^^^ Prefer `.%{autocorrect}.first` over `.%{method}.first`. [...]
|
||||
var = {a: 1}; var.%{method}.first
|
||||
_{method} ^^^^^ Prefer `.%{autocorrect}.first` over `.%{method}.first`. [...]
|
||||
{a: 1}.%{method}.first
|
||||
_{method} ^^^^^ Prefer `.%{autocorrect}.first` over `.%{method}.first`. [...]
|
||||
CONST.%{method}.first
|
||||
_{method} ^^^^^ Prefer `.%{autocorrect}.first` over `.%{method}.first`. [...]
|
||||
::CONST.%{method}.first
|
||||
_{method} ^^^^^ Prefer `.%{autocorrect}.first` over `.%{method}.first`. [...]
|
||||
RUBY
|
||||
|
||||
expect_correction(<<~RUBY)
|
||||
hash.#{autocorrect}.first
|
||||
var = {a: 1}; var.#{autocorrect}.first
|
||||
{a: 1}.#{autocorrect}.first
|
||||
CONST.#{autocorrect}.first
|
||||
::CONST.#{autocorrect}.first
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'does not flag unrelated code' do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
array.first
|
||||
hash.#{method}.last
|
||||
hash.#{method}
|
||||
#{method}.first
|
||||
1.#{method}.first
|
||||
'string'.#{method}.first
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'inspect use of keys or values first', :keys, :each_key
|
||||
it_behaves_like 'inspect use of keys or values first', :values, :each_value
|
||||
end
|
|
@ -88,7 +88,8 @@ module TestEnv
|
|||
'two-commits' => '304d257',
|
||||
'utf-16' => 'f05a987',
|
||||
'gitaly-rename-test' => '94bb47c',
|
||||
'smime-signed-commits' => 'ed775cc'
|
||||
'smime-signed-commits' => 'ed775cc',
|
||||
'Ääh-test-utf-8' => '7975be0'
|
||||
}.freeze
|
||||
|
||||
# gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'notify/import_issues_csv_email.html.haml' do
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project) }
|
||||
let(:correct_results) { { success: 3, valid_file: true } }
|
||||
let(:errored_results) { { success: 3, error_lines: [5, 6, 7], valid_file: true } }
|
||||
let(:parse_error_results) { { success: 0, parse_error: true } }
|
||||
|
||||
before do
|
||||
assign(:user, user)
|
||||
assign(:project, project)
|
||||
end
|
||||
|
||||
context 'when no errors found while importing' do
|
||||
before do
|
||||
assign(:results, correct_results)
|
||||
end
|
||||
|
||||
it 'renders correctly' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_link(project.full_name, href: project_url(project))
|
||||
expect(rendered).to have_content("3 issues imported")
|
||||
expect(rendered).not_to have_content("Errors found on line")
|
||||
expect(rendered).not_to have_content(
|
||||
"Error parsing CSV file. Please make sure it has the correct format: \
|
||||
a delimited text file that uses a comma to separate values.")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when import errors reported' do
|
||||
before do
|
||||
assign(:results, errored_results)
|
||||
end
|
||||
|
||||
it 'renders correctly' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_content("Errors found on lines: #{errored_results[:error_lines].join(", ")}. \
|
||||
Please check if these lines have an issue title.")
|
||||
expect(rendered).not_to have_content("Error parsing CSV file. Please make sure it has the correct format: \
|
||||
a delimited text file that uses a comma to separate values.")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when parse error reported while importing' do
|
||||
before do
|
||||
assign(:results, parse_error_results)
|
||||
end
|
||||
|
||||
it 'renders with parse error' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_content("Error parsing CSV file. \
|
||||
Please make sure it has the correct format: a delimited text file that uses a comma to separate values.")
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue