Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/external/customtemplates/gitlab.go
2070 views
1
package customtemplates
2
3
import (
4
"context"
5
"encoding/base64"
6
"os"
7
"path/filepath"
8
9
"github.com/projectdiscovery/gologger"
10
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
11
"github.com/projectdiscovery/nuclei/v3/pkg/types"
12
"github.com/projectdiscovery/utils/errkit"
13
gitlab "gitlab.com/gitlab-org/api/client-go"
14
)
15
16
var _ Provider = &customTemplateGitLabRepo{}
17
18
type customTemplateGitLabRepo struct {
19
gitLabClient *gitlab.Client
20
serverURL string
21
projectIDs []int
22
}
23
24
// NewGitLabProviders returns a new list of GitLab providers for downloading custom templates
25
func NewGitLabProviders(options *types.Options) ([]*customTemplateGitLabRepo, error) {
26
providers := []*customTemplateGitLabRepo{}
27
if options.GitLabToken != "" && !options.GitLabTemplateDisableDownload {
28
// Establish a connection to GitLab and build a client object with which to download templates from GitLab
29
gitLabClient, err := getGitLabClient(options.GitLabServerURL, options.GitLabToken)
30
if err != nil {
31
errx := errkit.FromError(err)
32
errx.Msgf("Error establishing GitLab client for %s %s", options.GitLabServerURL, err)
33
return nil, errx
34
}
35
36
// Create a new GitLab service client
37
gitLabContainer := &customTemplateGitLabRepo{
38
gitLabClient: gitLabClient,
39
serverURL: options.GitLabServerURL,
40
projectIDs: options.GitLabTemplateRepositoryIDs,
41
}
42
43
// Add the GitLab service client to the list of custom templates
44
providers = append(providers, gitLabContainer)
45
}
46
return providers, nil
47
}
48
49
// Download downloads all .yaml files from a GitLab repository
50
func (bk *customTemplateGitLabRepo) Download(_ context.Context) {
51
52
// Define the project and template count
53
var projectCount = 0
54
var templateCount = 0
55
56
// Append the GitLab directory to the location
57
location := config.DefaultConfig.CustomGitLabTemplatesDirectory
58
59
// Ensure the CustomGitLabTemplateDirectory directory exists or create it if it doesn't yet exist
60
err := os.MkdirAll(filepath.Dir(location), 0755)
61
if err != nil {
62
gologger.Error().Msgf("Error creating directory: %v", err)
63
return
64
}
65
66
// Get the projects from the GitLab serverURL
67
for _, projectID := range bk.projectIDs {
68
69
// Get the project information from the GitLab serverURL to get the default branch and the project name
70
project, _, err := bk.gitLabClient.Projects.GetProject(projectID, nil)
71
if err != nil {
72
gologger.Error().Msgf("error retrieving GitLab project: %s %s", project, err)
73
return
74
}
75
76
// Add a subdirectory with the project ID as the subdirectory within the location
77
projectOutputPath := filepath.Join(location, project.Path)
78
79
// Ensure the subdirectory exists or create it if it doesn't yet exist
80
err = os.MkdirAll(projectOutputPath, 0755)
81
if err != nil {
82
gologger.Error().Msgf("Error creating subdirectory: %v", err)
83
return
84
}
85
86
// Get the directory listing for the files in the project
87
tree, _, err := bk.gitLabClient.Repositories.ListTree(projectID, &gitlab.ListTreeOptions{
88
Ref: gitlab.Ptr(project.DefaultBranch),
89
Recursive: gitlab.Ptr(true),
90
})
91
if err != nil {
92
gologger.Error().Msgf("error retrieving files from GitLab project: %s (%d) %s", project.Name, projectID, err)
93
}
94
95
// Loop through the tree and download the files
96
for _, file := range tree {
97
// If the object is not a file or file extension is not .yaml, skip it
98
if file.Type == "blob" && filepath.Ext(file.Path) == ".yaml" {
99
gf := &gitlab.GetFileOptions{
100
Ref: gitlab.Ptr(project.DefaultBranch),
101
}
102
f, _, err := bk.gitLabClient.RepositoryFiles.GetFile(projectID, file.Path, gf)
103
if err != nil {
104
gologger.Error().Msgf("error retrieving GitLab project file: %d %s", projectID, err)
105
return
106
}
107
108
// Decode the file content from base64 into bytes so that it can be written to the local filesystem
109
contents, err := base64.StdEncoding.DecodeString(f.Content)
110
if err != nil {
111
gologger.Error().Msgf("error decoding GitLab project (%s) file: %s %s", project.Name, f.FileName, err)
112
return
113
}
114
115
// Write the downloaded template to the local filesystem at the location with the filename of the blob name
116
err = os.WriteFile(filepath.Join(projectOutputPath, f.FileName), contents, 0644)
117
if err != nil {
118
gologger.Error().Msgf("error writing GitLab project (%s) file: %s %s", project.Name, f.FileName, err)
119
return
120
}
121
122
// Increment the number of templates downloaded
123
templateCount++
124
}
125
}
126
127
// Increment the number of projects downloaded
128
projectCount++
129
gologger.Info().Msgf("GitLab project '%s' (%d) cloned successfully", project.Name, projectID)
130
}
131
132
// Print the number of projects and templates downloaded
133
gologger.Info().Msgf("%d templates downloaded from %d GitLab project(s) to: %s", templateCount, projectCount, location)
134
}
135
136
// Update is a wrapper around Download since it doesn't maintain a diff of the templates downloaded versus in the
137
// repository for simplicity.
138
func (bk *customTemplateGitLabRepo) Update(ctx context.Context) {
139
if len(bk.projectIDs) == 0 {
140
// No projects to download or update
141
return
142
}
143
bk.Download(ctx)
144
}
145
146
// getGitLabClient returns a GitLab client for the given serverURL and token
147
func getGitLabClient(server string, token string) (*gitlab.Client, error) {
148
client, err := gitlab.NewClient(token, gitlab.WithBaseURL(server))
149
return client, err
150
}
151
152