Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
82f8afa2
Commit
82f8afa2
authored
Aug 12, 2021
by
Philip Cunningham
Committed by
Russell Dickenson
Aug 12, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Use GraphQL to prefetch data for DastProfile#edit
parent
5e4a0fab
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
210 additions
and
46 deletions
+210
-46
doc/api/graphql/reference/index.md
doc/api/graphql/reference/index.md
+12
-0
ee/app/assets/javascripts/on_demand_scans/components/on_demand_scans_form.vue
...ripts/on_demand_scans/components/on_demand_scans_form.vue
+2
-2
ee/app/controllers/projects/on_demand_scans_controller.rb
ee/app/controllers/projects/on_demand_scans_controller.rb
+25
-10
ee/app/graphql/ee/types/project_type.rb
ee/app/graphql/ee/types/project_type.rb
+6
-0
ee/app/graphql/resolvers/app_sec/dast/profile_resolver.rb
ee/app/graphql/resolvers/app_sec/dast/profile_resolver.rb
+15
-1
ee/spec/frontend/on_demand_scans/components/on_demand_scans_form_spec.js
...d/on_demand_scans/components/on_demand_scans_form_spec.js
+52
-28
ee/spec/graphql/resolvers/app_sec/dast/profile_resolver_spec.rb
...c/graphql/resolvers/app_sec/dast/profile_resolver_spec.rb
+20
-0
ee/spec/requests/api/graphql/project/dast_profile_spec.rb
ee/spec/requests/api/graphql/project/dast_profile_spec.rb
+73
-0
ee/spec/requests/projects/on_demand_scans_controller_spec.rb
ee/spec/requests/projects/on_demand_scans_controller_spec.rb
+5
-5
No files found.
doc/api/graphql/reference/index.md
View file @
82f8afa2
...
...
@@ -12025,6 +12025,18 @@ four standard [pagination arguments](#connection-pagination-arguments):
|
<a
id=
"projectcontainerrepositoriesname"
></a>
`name`
|
[
`String`
](
#string
)
| Filter the container repositories by their name. |
|
<a
id=
"projectcontainerrepositoriessort"
></a>
`sort`
|
[
`ContainerRepositorySort`
](
#containerrepositorysort
)
| Sort container repositories by this criteria. |
##### `Project.dastProfile`
DAST Profile associated with the project.
Returns
[
`DastProfile`
](
#dastprofile
)
.
###### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
|
<a
id=
"projectdastprofileid"
></a>
`id`
|
[
`DastProfileID!`
](
#dastprofileid
)
| ID of the DAST Profile. |
##### `Project.dastSiteProfile`
DAST Site Profile associated with the project.
...
...
ee/app/assets/javascripts/on_demand_scans/components/on_demand_scans_form.vue
View file @
82f8afa2
...
...
@@ -143,8 +143,8 @@ export default {
scannerProfiles
:
[],
siteProfiles
:
[],
selectedBranch
:
this
.
dastScan
?.
branch
?.
name
??
this
.
defaultBranch
,
selectedScannerProfileId
:
this
.
dastScan
?.
scannerProfileI
d
||
null
,
selectedSiteProfileId
:
this
.
dastScan
?.
siteProfileI
d
||
null
,
selectedScannerProfileId
:
this
.
dastScan
?.
dastScannerProfile
.
i
d
||
null
,
selectedSiteProfileId
:
this
.
dastScan
?.
dastSiteProfile
.
i
d
||
null
,
loading
:
false
,
errorType
:
null
,
errors
:
[],
...
...
ee/app/controllers/projects/on_demand_scans_controller.rb
View file @
82f8afa2
...
...
@@ -3,6 +3,7 @@
module
Projects
class
OnDemandScansController
<
Projects
::
ApplicationController
include
SecurityAndCompliancePermissions
include
API
::
Helpers
::
GraphqlHelpers
before_action
:authorize_read_on_demand_scans!
,
only: :index
before_action
:authorize_create_on_demand_dast_scan!
,
only:
[
:new
,
:edit
]
...
...
@@ -16,16 +17,30 @@ module Projects
end
def
edit
dast_profile
=
Dast
::
ProfilesFinder
.
new
(
project_id:
@project
.
id
,
id:
params
[
:id
]).
execute
.
first!
# rubocop: disable CodeReuse/ActiveRecord
@dast_profile
=
{
id:
dast_profile
.
to_global_id
.
to_s
,
name:
dast_profile
.
name
,
description:
dast_profile
.
description
,
branch:
{
name:
dast_profile
.
branch_name
},
site_profile_id:
DastSiteProfile
.
new
(
id:
dast_profile
.
dast_site_profile_id
).
to_global_id
.
to_s
,
scanner_profile_id:
DastScannerProfile
.
new
(
id:
dast_profile
.
dast_scanner_profile_id
).
to_global_id
.
to_s
}
global_id
=
Gitlab
::
GlobalId
.
as_global_id
(
params
[
:id
],
model_name:
'Dast::Profile'
)
query
=
%(
{
project(fullPath: "#{project.full_path}") {
dastProfile(id: "#{global_id}") {
id
name
description
branch { name }
dastSiteProfile { id }
dastScannerProfile { id }
}
}
}
)
@dast_profile
=
run_graphql!
(
query:
query
,
context:
{
current_user:
current_user
},
transform:
->
(
result
)
{
result
.
dig
(
'data'
,
'project'
,
'dastProfile'
)
}
)
return
render_404
unless
@dast_profile
end
end
end
ee/app/graphql/ee/types/project_type.rb
View file @
82f8afa2
...
...
@@ -60,6 +60,12 @@ module EE
description:
'Find iteration cadences.'
,
resolver:
::
Resolvers
::
Iterations
::
CadencesResolver
field
:dast_profile
,
::
Types
::
Dast
::
ProfileType
,
null:
true
,
resolver:
::
Resolvers
::
AppSec
::
Dast
::
ProfileResolver
.
single
,
description:
'DAST Profile associated with the project.'
field
:dast_profiles
,
::
Types
::
Dast
::
ProfileType
.
connection_type
,
null:
true
,
...
...
ee/app/graphql/resolvers/app_sec/dast/profile_resolver.rb
View file @
82f8afa2
...
...
@@ -10,6 +10,12 @@ module Resolvers
type
::
Types
::
Dast
::
ProfileType
.
connection_type
,
null:
true
when_single
do
argument
:id
,
::
Types
::
GlobalIDType
[
::
Dast
::
Profile
],
required:
true
,
description:
'ID of the DAST Profile.'
end
def
resolve_with_lookahead
(
**
args
)
apply_lookahead
(
find_dast_profiles
(
args
))
end
...
...
@@ -24,7 +30,15 @@ module Resolvers
end
def
find_dast_profiles
(
args
)
::
Dast
::
ProfilesFinder
.
new
(
project_id:
project
.
id
).
execute
params
=
{
project_id:
project
.
id
}
if
args
[
:id
]
# TODO: remove this coercion when the compatibility layer is removed
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
params
[
:id
]
=
::
Types
::
GlobalIDType
[
::
Dast
::
Profile
].
coerce_isolated_input
(
args
[
:id
]).
model_id
end
::
Dast
::
ProfilesFinder
.
new
(
params
).
execute
end
end
end
...
...
ee/spec/frontend/on_demand_scans/components/on_demand_scans_form_spec.js
View file @
82f8afa2
...
...
@@ -23,6 +23,7 @@ import { scannerProfiles, siteProfiles } from '../mocks/mock_data';
const
helpPagePath
=
'
/application_security/dast/index#on-demand-scans
'
;
const
projectPath
=
'
group/project
'
;
const
defaultBranch
=
'
main
'
;
const
selectedBranch
=
'
some-other-branch
'
;
const
profilesLibraryPath
=
'
/security/configuration/dast_scans
'
;
const
scannerProfilesLibraryPath
=
'
/security/configuration/dast_scans#scanner-profiles
'
;
const
siteProfilesLibraryPath
=
'
/security/configuration/dast_scans#site-profiles
'
;
...
...
@@ -44,8 +45,8 @@ const dastScan = {
branch
:
{
name
:
'
dev
'
},
name
:
'
My daily scan
'
,
description
:
'
Tests for SQL injections
'
,
scannerProfileId
:
passiveScannerProfile
.
id
,
siteProfileId
:
validatedSiteProfile
.
id
,
dastScannerProfile
:
{
id
:
passiveScannerProfile
.
id
}
,
dastSiteProfile
:
{
id
:
validatedSiteProfile
.
id
}
,
};
useLocalStorageSpy
();
...
...
@@ -83,9 +84,14 @@ describe('OnDemandScansForm', () => {
const
findCancelButton
=
()
=>
findByTestId
(
'
on-demand-scan-cancel-button
'
);
const
findProfileSummary
=
()
=>
findByTestId
(
'
selected-profile-summary
'
);
const
hasSiteProfileAttributes
=
()
=>
{
expect
(
findScannerProfilesSelector
().
attributes
(
'
value
'
)).
toBe
(
dastScan
.
dastScannerProfile
.
id
);
expect
(
findSiteProfilesSelector
().
attributes
(
'
value
'
)).
toBe
(
dastScan
.
dastSiteProfile
.
id
);
};
const
setValidFormData
=
()
=>
{
findNameInput
().
vm
.
$emit
(
'
input
'
,
'
My daily scan
'
);
findBranchInput
().
vm
.
$emit
(
'
input
'
,
'
some-other-branch
'
);
findBranchInput
().
vm
.
$emit
(
'
input
'
,
selectedBranch
);
findScannerProfilesSelector
().
vm
.
$emit
(
'
input
'
,
passiveScannerProfile
.
id
);
findSiteProfilesSelector
().
vm
.
$emit
(
'
input
'
,
nonValidatedSiteProfile
.
id
);
return
wrapper
.
vm
.
$nextTick
();
...
...
@@ -230,24 +236,44 @@ describe('OnDemandScansForm', () => {
});
describe
(
'
when editing an existing scan
'
,
()
=>
{
beforeEach
(()
=>
{
createShallowComponent
({
propsData
:
{
dastScan
,
},
describe
(
'
when the branch is not present
'
,
()
=>
{
/**
* It is possible for pre-fetched data not to have a branch, so we must
* handle this path.
*/
beforeEach
(()
=>
{
createShallowComponent
({
propsData
:
{
...
dastScan
,
branch
:
null
,
},
});
});
});
it
(
'
sets the title properly
'
,
()
=>
{
expect
(
wrapper
.
text
()).
toContain
(
'
Edit on-demand DAST scan
'
);
it
(
'
sets the branch to the default
'
,
()
=>
{
expect
(
findBranchInput
().
props
(
'
value
'
)).
toBe
(
defaultBranch
);
});
});
it
(
'
populates the fields with passed values
'
,
()
=>
{
expect
(
findNameInput
().
attributes
(
'
value
'
)).
toBe
(
dastScan
.
name
);
expect
(
findBranchInput
().
props
(
'
value
'
)).
toBe
(
dastScan
.
branch
.
name
);
expect
(
findDescriptionInput
().
attributes
(
'
value
'
)).
toBe
(
dastScan
.
description
);
expect
(
findScannerProfilesSelector
().
attributes
(
'
value
'
)).
toBe
(
dastScan
.
scannerProfileId
);
expect
(
findSiteProfilesSelector
().
attributes
(
'
value
'
)).
toBe
(
dastScan
.
siteProfileId
);
describe
(
'
when the branch is present
'
,
()
=>
{
beforeEach
(()
=>
{
createShallowComponent
({
propsData
:
{
dastScan
,
},
});
});
it
(
'
sets the title properly
'
,
()
=>
{
expect
(
wrapper
.
text
()).
toContain
(
'
Edit on-demand DAST scan
'
);
});
it
(
'
populates the fields with passed values
'
,
()
=>
{
expect
(
findNameInput
().
attributes
(
'
value
'
)).
toBe
(
dastScan
.
name
);
expect
(
findBranchInput
().
props
(
'
value
'
)).
toBe
(
dastScan
.
branch
.
name
);
expect
(
findDescriptionInput
().
attributes
(
'
value
'
)).
toBe
(
dastScan
.
description
);
hasSiteProfileAttributes
();
});
});
});
...
...
@@ -264,7 +290,7 @@ describe('OnDemandScansForm', () => {
name
:
'
My daily scan
'
,
selectedScannerProfileId
:
'
gid://gitlab/DastScannerProfile/1
'
,
selectedSiteProfileId
:
'
gid://gitlab/DastSiteProfile/1
'
,
selectedBranch
:
'
some-other-branch
'
,
selectedBranch
,
}),
],
]);
...
...
@@ -276,8 +302,8 @@ describe('OnDemandScansForm', () => {
JSON
.
stringify
({
name
:
dastScan
.
name
,
description
:
dastScan
.
description
,
selectedScannerProfileId
:
dastScan
.
scannerProfileI
d
,
selectedSiteProfileId
:
dastScan
.
siteProfileI
d
,
selectedScannerProfileId
:
dastScan
.
dastScannerProfile
.
i
d
,
selectedSiteProfileId
:
dastScan
.
dastSiteProfile
.
i
d
,
}),
);
...
...
@@ -286,8 +312,7 @@ describe('OnDemandScansForm', () => {
expect
(
findNameInput
().
attributes
(
'
value
'
)).
toBe
(
dastScan
.
name
);
expect
(
findDescriptionInput
().
attributes
(
'
value
'
)).
toBe
(
dastScan
.
description
);
expect
(
findScannerProfilesSelector
().
attributes
(
'
value
'
)).
toBe
(
dastScan
.
scannerProfileId
);
expect
(
findSiteProfilesSelector
().
attributes
(
'
value
'
)).
toBe
(
dastScan
.
siteProfileId
);
hasSiteProfileAttributes
();
});
});
...
...
@@ -345,7 +370,7 @@ describe('OnDemandScansForm', () => {
variables
:
{
input
:
{
name
:
'
My daily scan
'
,
branchName
:
'
some-other-branch
'
,
branchName
:
selectedBranch
,
dastScannerProfileId
:
passiveScannerProfile
.
id
,
dastSiteProfileId
:
nonValidatedSiteProfile
.
id
,
fullPath
:
projectPath
,
...
...
@@ -384,7 +409,7 @@ describe('OnDemandScansForm', () => {
input
:
{
id
:
1
,
name
:
'
My daily scan
'
,
branchName
:
'
some-other-branch
'
,
branchName
:
selectedBranch
,
description
:
'
Tests for SQL injections
'
,
dastScannerProfileId
:
passiveScannerProfile
.
id
,
dastSiteProfileId
:
nonValidatedSiteProfile
.
id
,
...
...
@@ -602,16 +627,15 @@ describe('OnDemandScansForm', () => {
localStorage
.
setItem
(
LOCAL_STORAGE_KEY
,
JSON
.
stringify
({
selectedScannerProfileId
:
dastScan
.
scannerProfileI
d
,
selectedSiteProfileId
:
dastScan
.
siteProfileI
d
,
selectedScannerProfileId
:
dastScan
.
dastScannerProfile
.
i
d
,
selectedSiteProfileId
:
dastScan
.
dastSiteProfile
.
i
d
,
}),
);
createShallowComponent
();
await
wrapper
.
vm
.
$nextTick
();
expect
(
findScannerProfilesSelector
().
attributes
(
'
value
'
)).
toBe
(
dastScan
.
scannerProfileId
);
expect
(
findSiteProfilesSelector
().
attributes
(
'
value
'
)).
toBe
(
dastScan
.
siteProfileId
);
hasSiteProfileAttributes
();
});
});
...
...
ee/spec/graphql/resolvers/app_sec/dast/profile_resolver_spec.rb
View file @
82f8afa2
...
...
@@ -20,6 +20,22 @@ RSpec.describe Resolvers::AppSec::Dast::ProfileResolver do
expect
(
described_class
).
to
have_nullable_graphql_type
(
Types
::
Dast
::
ProfileType
.
connection_type
)
end
context
'when resolving a single DAST profile'
do
subject
{
sync
(
dast_profile
(
id:
gid
))
}
context
'when the DAST profile exists'
do
let
(
:gid
)
{
dast_profile1
.
to_global_id
}
it
{
is_expected
.
to
eq
dast_profile1
}
end
context
'when the DAST profile does not exist'
do
let
(
:gid
)
{
Gitlab
::
GlobalId
.
as_global_id
(
non_existing_record_id
,
model_name:
'Dast::Profile'
)
}
it
{
is_expected
.
to
be_nil
}
end
end
context
'when resolving multiple DAST profiles'
do
subject
{
sync
(
dast_profiles
)
}
...
...
@@ -45,4 +61,8 @@ RSpec.describe Resolvers::AppSec::Dast::ProfileResolver do
def
dast_profiles
resolve
(
described_class
,
obj:
project
,
ctx:
{
current_user:
current_user
})
end
def
dast_profile
(
id
:)
resolve
(
described_class
.
single
,
obj:
project
,
args:
{
id:
id
},
ctx:
{
current_user:
current_user
})
end
end
ee/spec/requests/api/graphql/project/dast_profile_spec.rb
0 → 100644
View file @
82f8afa2
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
'Query.project(fullPath).dastProfile'
do
include
GraphqlHelpers
let_it_be
(
:project
)
{
create
(
:project
,
:repository
)
}
let_it_be
(
:current_user
)
{
create
(
:user
)
}
let_it_be
(
:dast_profile
)
{
create
(
:dast_profile
,
project:
project
)
}
let
(
:query
)
do
fields
=
all_graphql_fields_for
(
'DastProfile'
)
graphql_query_for
(
:project
,
{
full_path:
project
.
full_path
},
query_graphql_field
(
:dast_profile
,
{
id:
global_id_of
(
dast_profile
)
},
fields
)
)
end
subject
do
post_graphql
(
query
,
current_user:
current_user
)
end
before
do
stub_licensed_features
(
security_on_demand_scans:
true
)
end
context
'when a user does not have access to the project'
do
it
'returns a null project'
do
subject
expect
(
graphql_data_at
(
:project
)).
to
be_nil
end
end
context
'when a user does not have access to the dast_profile'
do
before
do
project
.
add_guest
(
current_user
)
end
it
'returns a null dast_profile'
do
subject
expect
(
graphql_data_at
(
:project
,
:dast_profile
)).
to
be_nil
end
end
context
'when a user has access to the dast_profile'
do
before
do
project
.
add_developer
(
current_user
)
end
it
'returns a dast_profile'
do
subject
expect
(
graphql_data_at
(
:project
,
:dast_profile
,
:id
)).
to
eq
(
dast_profile
.
to_global_id
.
to_s
)
end
context
'when on demand scan licensed feature is not available'
do
before
do
stub_licensed_features
(
security_on_demand_scans:
false
)
end
it
'returns a null dast_profile'
do
subject
expect
(
graphql_data_at
(
:project
,
:dast_profile
)).
to
be_nil
end
end
end
end
ee/spec/requests/projects/on_demand_scans_controller_spec.rb
View file @
82f8afa2
...
...
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec
.
describe
Projects
::
OnDemandScansController
,
type: :request
do
include
GraphqlHelpers
let_it_be
(
:project
)
{
create
(
:project
)
}
let_it_be
(
:project
)
{
create
(
:project
,
:repository
)
}
let
(
:user
)
{
create
(
:user
)
}
...
...
@@ -82,7 +82,7 @@ RSpec.describe Projects::OnDemandScansController, type: :request do
end
describe
'GET #edit'
do
let_it_be
(
:dast_profile
)
{
create
(
:dast_profile
,
project:
project
)
}
let_it_be
(
:dast_profile
)
{
create
(
:dast_profile
,
project:
project
,
branch_name:
project
.
default_branch_or_main
)
}
let
(
:dast_profile_id
)
{
dast_profile
.
id
}
let
(
:edit_path
)
{
edit_project_on_demand_scan_path
(
project
,
id:
dast_profile_id
)
}
...
...
@@ -108,9 +108,9 @@ RSpec.describe Projects::OnDemandScansController, type: :request do
id:
global_id_of
(
dast_profile
),
name:
dast_profile
.
name
,
description:
dast_profile
.
description
,
branch:
{
name:
dast_profile
.
branch_name
},
site_profile_id:
global_id_of
(
DastSiteProfile
.
new
(
id:
dast_profile
.
dast_site_profile_id
))
,
scanner_profile_id:
global_id_of
(
DastScannerProfile
.
new
(
id:
dast_profile
.
dast_scanner_profile_id
))
branch:
{
name:
project
.
default_branch_or_main
},
dastSiteProfile:
{
id:
global_id_of
(
DastSiteProfile
.
new
(
id:
dast_profile
.
dast_site_profile_id
))
}
,
dastScannerProfile:
{
id:
global_id_of
(
DastScannerProfile
.
new
(
id:
dast_profile
.
dast_scanner_profile_id
))
}
}.
to_json
on_demand_div
=
Nokogiri
::
HTML
.
parse
(
response
.
body
).
at_css
(
'div#js-on-demand-scans-app'
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment