Commit dd2554bc authored by Etienne Baqué's avatar Etienne Baqué

Merge branch '341486-corpus-fetch-query' into 'master'

Graphql query- fetch corpus

See merge request gitlab-org/gitlab!72793
parents 705a27e0 8d36828d
......@@ -5692,6 +5692,29 @@ The edge type for [`ContainerRepositoryTag`](#containerrepositorytag).
| <a id="containerrepositorytagedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="containerrepositorytagedgenode"></a>`node` | [`ContainerRepositoryTag`](#containerrepositorytag) | The item at the end of the edge. |
#### `CoverageFuzzingCorpusConnection`
The connection type for [`CoverageFuzzingCorpus`](#coveragefuzzingcorpus).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="coveragefuzzingcorpusconnectionedges"></a>`edges` | [`[CoverageFuzzingCorpusEdge]`](#coveragefuzzingcorpusedge) | A list of edges. |
| <a id="coveragefuzzingcorpusconnectionnodes"></a>`nodes` | [`[CoverageFuzzingCorpus]`](#coveragefuzzingcorpus) | A list of nodes. |
| <a id="coveragefuzzingcorpusconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
#### `CoverageFuzzingCorpusEdge`
The edge type for [`CoverageFuzzingCorpus`](#coveragefuzzingcorpus).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="coveragefuzzingcorpusedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="coveragefuzzingcorpusedgenode"></a>`node` | [`CoverageFuzzingCorpus`](#coveragefuzzingcorpus) | The item at the end of the edge. |
#### `CustomEmojiConnection`
The connection type for [`CustomEmoji`](#customemoji).
......@@ -9030,6 +9053,17 @@ A tag from a container repository.
| <a id="containerrepositorytagshortrevision"></a>`shortRevision` | [`String`](#string) | Short revision of the tag. |
| <a id="containerrepositorytagtotalsize"></a>`totalSize` | [`BigInt`](#bigint) | Size of the tag. |
### `CoverageFuzzingCorpus`
Corpus for a coverage fuzzing job.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="coveragefuzzingcorpusid"></a>`id` | [`AppSecFuzzingCoverageCorpusID!`](#appsecfuzzingcoveragecorpusid) | ID of the corpus. |
| <a id="coveragefuzzingcorpuspackage"></a>`package` | [`PackageDetailsType!`](#packagedetailstype) | Package of the corpus. |
### `CurrentLicense`
Represents the current license.
......@@ -12802,6 +12836,7 @@ Represents vulnerability finding of a security report on the pipeline.
| <a id="projectcontainerexpirationpolicy"></a>`containerExpirationPolicy` | [`ContainerExpirationPolicy`](#containerexpirationpolicy) | Container expiration policy of the project. |
| <a id="projectcontainerregistryenabled"></a>`containerRegistryEnabled` | [`Boolean`](#boolean) | Indicates if Container Registry is enabled for the current user. |
| <a id="projectcontainerrepositoriescount"></a>`containerRepositoriesCount` | [`Int!`](#int) | Number of container repositories in the project. |
| <a id="projectcorpuses"></a>`corpuses` | [`CoverageFuzzingCorpusConnection`](#coveragefuzzingcorpusconnection) | Find corpuses of the project. Available only when feature flag `corpus_management` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice. (see [Connections](#connections)) |
| <a id="projectcreatedat"></a>`createdAt` | [`Time`](#time) | Timestamp of the project creation. |
| <a id="projectdastscannerprofiles"></a>`dastScannerProfiles` | [`DastScannerProfileConnection`](#dastscannerprofileconnection) | DAST scanner profiles associated with the project. (see [Connections](#connections)) |
| <a id="projectdastsiteprofiles"></a>`dastSiteProfiles` | [`DastSiteProfileConnection`](#dastsiteprofileconnection) | DAST Site Profiles associated with the project. (see [Connections](#connections)) |
......@@ -17188,6 +17223,12 @@ A `AnalyticsDevopsAdoptionEnabledNamespaceID` is a global ID. It is encoded as a
An example `AnalyticsDevopsAdoptionEnabledNamespaceID` is: `"gid://gitlab/Analytics::DevopsAdoption::EnabledNamespace/1"`.
### `AppSecFuzzingCoverageCorpusID`
A `AppSecFuzzingCoverageCorpusID` is a global ID. It is encoded as a string.
An example `AppSecFuzzingCoverageCorpusID` is: `"gid://gitlab/AppSec::Fuzzing::Coverage::Corpus/1"`.
### `AuditEventsExternalAuditEventDestinationID`
A `AuditEventsExternalAuditEventDestinationID` is a global ID. It is encoded as a string.
# frozen_string_literal: true
module AppSec
module Fuzzing
module Coverage
class CorpusesFinder
attr_reader :project
def initialize(project:)
@project = project
end
def execute
AppSec::Fuzzing::Coverage::Corpus.by_project_id(project)
end
end
end
end
end
......@@ -144,6 +144,12 @@ module EE
null: true,
description: 'API fuzzing configuration for the project. '
field :corpuses, ::Types::AppSec::Fuzzing::Coverage::CorpusType.connection_type,
null: true,
resolver: ::Resolvers::AppSec::Fuzzing::Coverage::CorpusesResolver,
feature_flag: :corpus_management,
description: "Find corpuses of the project."
field :push_rules,
::Types::PushRulesType,
null: true,
......
# frozen_string_literal: true
module Resolvers
module AppSec
module Fuzzing
module Coverage
class CorpusesResolver < BaseResolver
type Types::AppSec::Fuzzing::Coverage::CorpusType, null: true
alias_method :project, :object
def resolve
::AppSec::Fuzzing::Coverage::CorpusesFinder.new(
project: project
).execute
end
end
end
end
end
end
# frozen_string_literal: true
module Types
module AppSec
module Fuzzing
module Coverage
class CorpusType < BaseObject
graphql_name 'CoverageFuzzingCorpus'
description 'Corpus for a coverage fuzzing job.'
authorize :read_coverage_fuzzing
field :id, ::Types::GlobalIDType[::AppSec::Fuzzing::Coverage::Corpus], null: false,
description: 'ID of the corpus.'
field :package, ::Types::Packages::PackageDetailsType, null: false,
description: 'Package of the corpus.'
end
end
end
end
end
......@@ -12,6 +12,10 @@ module AppSec
validate :project_same_as_package_project
scope :by_project_id, -> (project_id) do
joins(:package).where(package: { project_id: project_id })
end
def audit_details
user&.name
end
......
# frozen_string_literal: true
module AppSec
module Fuzzing
module Coverage
class CorpusPolicy < BasePolicy
delegate { @subject.project }
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe AppSec::Fuzzing::Coverage::CorpusesFinder do
let_it_be(:corpus1) { create(:corpus) }
let_it_be(:corpus2) { create(:corpus, project: corpus1.project, user: corpus1.user) }
let_it_be(:corpus3) { create(:corpus) }
subject do
described_class.new(project: corpus1.project).execute
end
describe '#execute' do
it 'returns corpuses records' do
aggregate_failures do
expect(subject).to contain_exactly(corpus1, corpus2)
end
end
context 'when the corpus does not exist' do
let(:subject) { described_class.new(project: 0).execute }
it 'returns an empty relation' do
expect(subject).to be_empty
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Resolvers::AppSec::Fuzzing::Coverage::CorpusesResolver do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:corpus1) { create(:corpus, project: project) }
let_it_be(:corpus2) { create(:corpus, project: project) }
context 'when resolving corpuses' do
subject { resolve_corpus }
context 'when the corpus exists' do
it 'finds all the corpuses' do
expect(subject).to match_array([corpus1, corpus2])
end
end
context 'when the corpus does not exists' do
let_it_be(:project) { create(:project) }
it { is_expected.to be_empty }
end
end
private
def resolve_corpus
resolve(described_class, obj: project)
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['CoverageFuzzingCorpus'] do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository) }
let_it_be(:object) { create(:corpus, project: project) }
let_it_be(:user) { create(:user, developer_projects: [project]) }
let_it_be(:fields) { %i[id package] }
specify { expect(described_class.graphql_name).to eq('CoverageFuzzingCorpus') }
specify { expect(described_class.description).to eq('Corpus for a coverage fuzzing job.') }
specify { expect(described_class).to require_graphql_authorizations(:read_coverage_fuzzing) }
before do
stub_licensed_features(coverage_fuzzing: true)
end
it { expect(described_class).to have_graphql_fields(fields) }
describe 'id field' do
it 'correctly resolves the field' do
expect(resolve_field(:id, object, current_user: user)).to eq(object.to_global_id)
end
end
describe 'package field' do
it 'correctly resolves the field' do
expect(resolve_field(:package, object, current_user: user)).to eq(object.package)
end
end
end
......@@ -20,7 +20,7 @@ RSpec.describe GitlabSchema.types['Project'] do
vulnerabilities vulnerability_scanners requirement_states_count
vulnerability_severities_count packages compliance_frameworks vulnerabilities_count_by_day
security_dashboard_path iterations iteration_cadences repository_size_excess actual_repository_size_limit
code_coverage_summary api_fuzzing_ci_configuration path_locks incident_management_escalation_policies
code_coverage_summary api_fuzzing_ci_configuration corpuses path_locks incident_management_escalation_policies
incident_management_escalation_policy scan_execution_policies network_policies
]
......
......@@ -24,4 +24,19 @@ RSpec.describe AppSec::Fuzzing::Coverage::Corpus, type: :model do
end
end
end
describe 'scopes' do
describe 'by_project_id' do
it 'includes the correct records' do
another_corpus_profile = create(:corpus)
result = described_class.by_project_id(subject.package.project_id)
aggregate_failures do
expect(result).to include(subject)
expect(result).not_to include(another_corpus_profile)
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe AppSec::Fuzzing::Coverage::CorpusPolicy do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
let_it_be(:user) { create(:user) }
let_it_be(:corpus) { create(:corpus, project: project) }
subject { described_class.new(user, corpus) }
before do
stub_licensed_features(coverage_fuzzing: true)
end
describe 'coverage_fuzzing policies' do
let(:policies) { [:read_coverage_fuzzing] }
context 'when a user does not have access to the project' do
it { is_expected.to be_disallowed(*policies) }
end
context 'when the user is a developer' do
before do
project.add_developer(user)
end
it { is_expected.to be_allowed(*policies) }
end
context 'when the user is a guest' do
before do
project.add_guest(user)
end
it { is_expected.to be_disallowed(*policies) }
end
context 'when the user is a reporter' do
before do
project.add_reporter(user)
end
it { is_expected.to be_disallowed(*policies) }
end
context 'when the user is a developer' do
before do
project.add_developer(user)
end
it { is_expected.to be_allowed(*policies) }
end
context 'when the user is a maintainer' do
before do
project.add_maintainer(user)
end
it { is_expected.to be_allowed(*policies) }
end
context 'when the user is an owner' do
before do
group.add_owner(user)
end
it { is_expected.to be_allowed(*policies) }
end
context 'when the user is allowed' do
before do
project.add_developer(user)
end
context 'coverage_fuzzing licensed feature is not available' do
let(:project) { create(:project, group: group) }
before do
stub_licensed_features(coverage_fuzzing: false)
end
it { is_expected.to be_disallowed(*policies) }
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Query.project(fullPath).corpuses' do
include GraphqlHelpers
include StubRequests
let_it_be(:corpus) { create(:corpus) }
let_it_be(:project) { corpus.project }
let_it_be(:user) { create(:user) }
let_it_be(:query) do
%(
query {
project(fullPath: "#{project.full_path}") {
corpuses {
nodes {
id
package {
id
}
}
}
}
}
)
end
before do
project.add_developer(user)
end
context 'when the user can read corpus for the project' do
before do
stub_licensed_features(coverage_fuzzing: true)
end
it 'returns corpus and package' do
post_graphql(query, current_user: user)
expect(response).to have_gitlab_http_status(:ok)
nodes = graphql_data.dig('project', 'corpuses', 'nodes')
expect(nodes).to contain_exactly({
'id' => corpus.to_global_id.to_s, 'package' => { 'id' => corpus.package.to_global_id.to_s }
})
end
end
context 'when the user cannot read corpus for the project' do
before do
stub_licensed_features(coverage_fuzzing: false)
end
it 'returns nil' do
post_graphql(query, current_user: user)
expect(response).to have_gitlab_http_status(:ok)
nodes = graphql_data.dig('project', 'corpuses', 'nodes')
expect(nodes).to be_empty
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment