diff --git a/gitlab.go b/gitlab.go index d9d4f2609..d1d59bb9d 100644 --- a/gitlab.go +++ b/gitlab.go @@ -101,105 +101,106 @@ type Client struct { UserAgent string // Services used for talking to different parts of the GitLab API. - AccessRequests *AccessRequestsService - Applications *ApplicationsService - AuditEvents *AuditEventsService - Avatar *AvatarRequestsService - AwardEmoji *AwardEmojiService - Boards *IssueBoardsService - Branches *BranchesService - BroadcastMessage *BroadcastMessagesService - CIYMLTemplate *CIYMLTemplatesService - Commits *CommitsService - ContainerRegistry *ContainerRegistryService - CustomAttribute *CustomAttributesService - DeployKeys *DeployKeysService - DeployTokens *DeployTokensService - Deployments *DeploymentsService - Discussions *DiscussionsService - Environments *EnvironmentsService - EpicIssues *EpicIssuesService - Epics *EpicsService - Events *EventsService - ExternalStatusChecks *ExternalStatusChecksService - Features *FeaturesService - FreezePeriods *FreezePeriodsService - GenericPackages *GenericPackagesService - GeoNodes *GeoNodesService - GitIgnoreTemplates *GitIgnoreTemplatesService - GroupAccessTokens *GroupAccessTokensService - GroupBadges *GroupBadgesService - GroupCluster *GroupClustersService - GroupImportExport *GroupImportExportService - GroupIssueBoards *GroupIssueBoardsService - GroupIterations *GroupIterationsService - GroupLabels *GroupLabelsService - GroupMembers *GroupMembersService - GroupMilestones *GroupMilestonesService - GroupVariables *GroupVariablesService - GroupWikis *GroupWikisService - Groups *GroupsService - InstanceCluster *InstanceClustersService - InstanceVariables *InstanceVariablesService - Invites *InvitesService - IssueLinks *IssueLinksService - Issues *IssuesService - IssuesStatistics *IssuesStatisticsService - Jobs *JobsService - Keys *KeysService - Labels *LabelsService - License *LicenseService - LicenseTemplates *LicenseTemplatesService - ManagedLicenses *ManagedLicensesService - Markdown *MarkdownService - MergeRequestApprovals *MergeRequestApprovalsService - MergeRequests *MergeRequestsService - Milestones *MilestonesService - Namespaces *NamespacesService - Notes *NotesService - NotificationSettings *NotificationSettingsService - Packages *PackagesService - Pages *PagesService - PagesDomains *PagesDomainsService - PersonalAccessTokens *PersonalAccessTokensService - PipelineSchedules *PipelineSchedulesService - PipelineTriggers *PipelineTriggersService - Pipelines *PipelinesService - PlanLimits *PlanLimitsService - ProjectBadges *ProjectBadgesService - ProjectAccessTokens *ProjectAccessTokensService - ProjectCluster *ProjectClustersService - ProjectImportExport *ProjectImportExportService - ProjectIterations *ProjectIterationsService - ProjectMembers *ProjectMembersService - ProjectMirrors *ProjectMirrorService - ProjectSnippets *ProjectSnippetsService - ProjectVariables *ProjectVariablesService - Projects *ProjectsService - ProtectedBranches *ProtectedBranchesService - ProtectedEnvironments *ProtectedEnvironmentsService - ProtectedTags *ProtectedTagsService - ReleaseLinks *ReleaseLinksService - Releases *ReleasesService - Repositories *RepositoriesService - RepositoryFiles *RepositoryFilesService - RepositorySubmodules *RepositorySubmodulesService - ResourceLabelEvents *ResourceLabelEventsService - ResourceStateEvents *ResourceStateEventsService - Runners *RunnersService - Search *SearchService - Services *ServicesService - Settings *SettingsService - Sidekiq *SidekiqService - Snippets *SnippetsService - SystemHooks *SystemHooksService - Tags *TagsService - Todos *TodosService - Topics *TopicsService - Users *UsersService - Validate *ValidateService - Version *VersionService - Wikis *WikisService + AccessRequests *AccessRequestsService + Applications *ApplicationsService + AuditEvents *AuditEventsService + Avatar *AvatarRequestsService + AwardEmoji *AwardEmojiService + Boards *IssueBoardsService + Branches *BranchesService + BroadcastMessage *BroadcastMessagesService + CIYMLTemplate *CIYMLTemplatesService + Commits *CommitsService + ContainerRegistry *ContainerRegistryService + CustomAttribute *CustomAttributesService + DeployKeys *DeployKeysService + DeployTokens *DeployTokensService + Deployments *DeploymentsService + Discussions *DiscussionsService + Environments *EnvironmentsService + EpicIssues *EpicIssuesService + Epics *EpicsService + Events *EventsService + ExternalStatusChecks *ExternalStatusChecksService + Features *FeaturesService + FreezePeriods *FreezePeriodsService + GenericPackages *GenericPackagesService + GeoNodes *GeoNodesService + GitIgnoreTemplates *GitIgnoreTemplatesService + GroupAccessTokens *GroupAccessTokensService + GroupBadges *GroupBadgesService + GroupCluster *GroupClustersService + GroupImportExport *GroupImportExportService + GroupIssueBoards *GroupIssueBoardsService + GroupIterations *GroupIterationsService + GroupLabels *GroupLabelsService + GroupMembers *GroupMembersService + GroupMilestones *GroupMilestonesService + GroupVariables *GroupVariablesService + GroupWikis *GroupWikisService + Groups *GroupsService + InstanceCluster *InstanceClustersService + InstanceVariables *InstanceVariablesService + Invites *InvitesService + IssueLinks *IssueLinksService + Issues *IssuesService + IssuesStatistics *IssuesStatisticsService + Jobs *JobsService + Keys *KeysService + Labels *LabelsService + License *LicenseService + LicenseTemplates *LicenseTemplatesService + ManagedLicenses *ManagedLicensesService + Markdown *MarkdownService + MergeRequestApprovals *MergeRequestApprovalsService + MergeRequests *MergeRequestsService + Milestones *MilestonesService + Namespaces *NamespacesService + Notes *NotesService + NotificationSettings *NotificationSettingsService + Packages *PackagesService + Pages *PagesService + PagesDomains *PagesDomainsService + PersonalAccessTokens *PersonalAccessTokensService + PipelineSchedules *PipelineSchedulesService + PipelineTriggers *PipelineTriggersService + Pipelines *PipelinesService + PlanLimits *PlanLimitsService + ProjectBadges *ProjectBadgesService + ProjectAccessTokens *ProjectAccessTokensService + ProjectCluster *ProjectClustersService + ProjectImportExport *ProjectImportExportService + ProjectIterations *ProjectIterationsService + ProjectMembers *ProjectMembersService + ProjectMirrors *ProjectMirrorService + ProjectSnippets *ProjectSnippetsService + ProjectVariables *ProjectVariablesService + ProjectVulnerabilities *ProjectVulnerabilitiesService + Projects *ProjectsService + ProtectedBranches *ProtectedBranchesService + ProtectedEnvironments *ProtectedEnvironmentsService + ProtectedTags *ProtectedTagsService + ReleaseLinks *ReleaseLinksService + Releases *ReleasesService + Repositories *RepositoriesService + RepositoryFiles *RepositoryFilesService + RepositorySubmodules *RepositorySubmodulesService + ResourceLabelEvents *ResourceLabelEventsService + ResourceStateEvents *ResourceStateEventsService + Runners *RunnersService + Search *SearchService + Services *ServicesService + Settings *SettingsService + Sidekiq *SidekiqService + Snippets *SnippetsService + SystemHooks *SystemHooksService + Tags *TagsService + Todos *TodosService + Topics *TopicsService + Users *UsersService + Validate *ValidateService + Version *VersionService + Wikis *WikisService } // ListOptions specifies the optional parameters to various List methods that @@ -373,6 +374,7 @@ func newClient(options ...ClientOptionFunc) (*Client, error) { c.ProjectMirrors = &ProjectMirrorService{client: c} c.ProjectSnippets = &ProjectSnippetsService{client: c} c.ProjectVariables = &ProjectVariablesService{client: c} + c.ProjectVulnerabilities = &ProjectVulnerabilitiesService{client: c} c.Projects = &ProjectsService{client: c} c.ProtectedBranches = &ProtectedBranchesService{client: c} c.ProtectedEnvironments = &ProtectedEnvironmentsService{client: c} diff --git a/project_vulnerabilities.go b/project_vulnerabilities.go new file mode 100644 index 000000000..168ee1adb --- /dev/null +++ b/project_vulnerabilities.go @@ -0,0 +1,150 @@ +// +// Copyright 2021, Sander van Harmelen +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package gitlab + +import ( + "fmt" + "net/http" + "time" +) + +// ProjectVulnerabilitiesService handles communication with the projects +// vulnerabilities related methods of the GitLab API. +// +// GitLab API docs: https://docs.gitlab.com/ee/api/project_vulnerabilities.html +type ProjectVulnerabilitiesService struct { + client *Client +} + +// Project represents a GitLab project vulnerability. +// +// GitLab API docs: https://docs.gitlab.com/ee/api/project_vulnerabilities.html +type ProjectVulnerability struct { + AuthorID int `json:"author_id"` + Confidence string `json:"confidence"` + CreatedAt *time.Time `json:"created_at"` + Description string `json:"description"` + DismissedAt *time.Time `json:"dismissed_at"` + DismissedByID int `json:"dismissed_by_id"` + DueDate *time.Time `json:"due_date"` + Finding *Finding `json:"finding"` + ID int `json:"id"` + LastEditedAt *time.Time `json:"last_edited_at"` + LastEditedByID int `json:"last_edited_by_id"` + Project *Project `json:"project"` + ProjectDefaultBranch string `json:"project_default_branch"` + ReportType string `json:"report_type"` + ResolvedAt *time.Time `json:"resolved_at"` + ResolvedByID int `json:"resolved_by_id"` + ResolvedOnDefaultBranch bool `json:"resolved_on_default_branch"` + Severity string `json:"severity"` + StartDate *time.Time `json:"start_date"` + State string `json:"state"` + Title string `json:"title"` + UpdatedAt *time.Time `json:"updated_at"` + UpdatedByID int `json:"updated_by_id"` +} + +// Project represents a GitLab project vulnerability finding. +// +// GitLab API docs: https://docs.gitlab.com/ee/api/project_vulnerabilities.html +type Finding struct { + Confidence string `json:"confidence"` + CreatedAt *time.Time `json:"created_at"` + ID int `json:"id"` + LocationFingerprint string `json:"location_fingerprint"` + MetadataVersion string `json:"metadata_version"` + Name string `json:"name"` + PrimaryIdentifierID int `json:"primary_identifier_id"` + ProjectFingerprint string `json:"project_fingerprint"` + ProjectID int `json:"project_id"` + RawMetadata string `json:"raw_metadata"` + ReportType string `json:"report_type"` + ScannerID int `json:"scanner_id"` + Severity string `json:"severity"` + UpdatedAt *time.Time `json:"updated_at"` + UUID string `json:"uuid"` + VulnerabilityID int `json:"vulnerability_id"` +} + +// ListProjectVulnerabilitiesOptions represents the available +// ListProjectVulnerabilities() options. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/project_vulnerabilities.html#list-project-vulnerabilities +type ListProjectVulnerabilitiesOptions struct { + ListOptions +} + +// ListProjectVulnerabilities gets a list of all project vulnerabilities. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/project_vulnerabilities.html#list-project-vulnerabilities +func (s *ProjectVulnerabilitiesService) ListProjectVulnerabilities(pid interface{}, opt *ListProjectVulnerabilitiesOptions, options ...RequestOptionFunc) ([]*ProjectVulnerability, *Response, error) { + project, err := parseID(pid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("projects/%s/vulnerabilities", PathEscape(project)) + + req, err := s.client.NewRequest(http.MethodGet, u, opt, options) + if err != nil { + return nil, nil, err + } + + var p []*ProjectVulnerability + resp, err := s.client.Do(req, &p) + if err != nil { + return nil, resp, err + } + + return p, resp, err +} + +// CreateVulnerabilityOptions represents the available CreateVulnerability() +// options. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/project_vulnerabilities.html#new-vulnerability +type CreateVulnerabilityOptions struct { + FindingID *int `url:"finding_id,omitempty" json:"finding_id,omitempty"` +} + +// CreateVulnerability creates a new vulnerability on the selected project. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/project_vulnerabilities.html#new-vulnerability +func (s *ProjectVulnerabilitiesService) CreateVulnerability(pid interface{}, opt *CreateVulnerabilityOptions, options ...RequestOptionFunc) (*ProjectVulnerability, *Response, error) { + project, err := parseID(pid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("projects/%s/vulnerabilities", PathEscape(project)) + + req, err := s.client.NewRequest(http.MethodPost, u, opt, options) + if err != nil { + return nil, nil, err + } + + p := new(ProjectVulnerability) + resp, err := s.client.Do(req, p) + if err != nil { + return nil, resp, err + } + + return p, resp, err +} diff --git a/project_vulnerabilities_test.go b/project_vulnerabilities_test.go new file mode 100644 index 000000000..19f14becb --- /dev/null +++ b/project_vulnerabilities_test.go @@ -0,0 +1,72 @@ +// +// Copyright 2021, Sander van Harmelen +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package gitlab + +import ( + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestListProjectVulnerabilities(t *testing.T) { + mux, server, client := setup(t) + defer teardown(server) + + mux.HandleFunc("/api/v4/projects/1/vulnerabilities", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + fmt.Fprint(w, `[{"id":1},{"id":2}]`) + }) + + opt := &ListProjectVulnerabilitiesOptions{ + ListOptions: ListOptions{2, 3}, + } + + projectVulnerabilities, _, err := client.ProjectVulnerabilities.ListProjectVulnerabilities(1, opt) + if err != nil { + t.Errorf("ProjectVulnerabilities.ListProjectVulnerabilities returned error: %v", err) + } + + want := []*ProjectVulnerability{{ID: 1}, {ID: 2}} + if !reflect.DeepEqual(want, projectVulnerabilities) { + t.Errorf("ProjectVulnerabilities.ListProjectVulnerabilities returned %+v, want %+v", projectVulnerabilities, want) + } +} + +func TestCreateVulnerability(t *testing.T) { + mux, server, client := setup(t) + defer teardown(server) + + mux.HandleFunc("/api/v4/projects/1/vulnerabilities", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodPost) + fmt.Fprint(w, `{"id":1}`) + }) + + opt := &CreateVulnerabilityOptions{ + FindingID: Int(1), + } + + projectVulnerability, _, err := client.ProjectVulnerabilities.CreateVulnerability(1, opt) + if err != nil { + t.Errorf("ProjectVulnerabilities.CreateVulnerability returned error: %v", err) + } + + want := &ProjectVulnerability{ID: 1} + if !reflect.DeepEqual(want, projectVulnerability) { + t.Errorf("ProjectVulnerabilities.CreateVulnerability returned %+v, want %+v", projectVulnerability, want) + } +}