Path: blob/dev/pkg/external/customtemplates/gitlab.go
2070 views
package customtemplates12import (3"context"4"encoding/base64"5"os"6"path/filepath"78"github.com/projectdiscovery/gologger"9"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"10"github.com/projectdiscovery/nuclei/v3/pkg/types"11"github.com/projectdiscovery/utils/errkit"12gitlab "gitlab.com/gitlab-org/api/client-go"13)1415var _ Provider = &customTemplateGitLabRepo{}1617type customTemplateGitLabRepo struct {18gitLabClient *gitlab.Client19serverURL string20projectIDs []int21}2223// NewGitLabProviders returns a new list of GitLab providers for downloading custom templates24func NewGitLabProviders(options *types.Options) ([]*customTemplateGitLabRepo, error) {25providers := []*customTemplateGitLabRepo{}26if options.GitLabToken != "" && !options.GitLabTemplateDisableDownload {27// Establish a connection to GitLab and build a client object with which to download templates from GitLab28gitLabClient, err := getGitLabClient(options.GitLabServerURL, options.GitLabToken)29if err != nil {30errx := errkit.FromError(err)31errx.Msgf("Error establishing GitLab client for %s %s", options.GitLabServerURL, err)32return nil, errx33}3435// Create a new GitLab service client36gitLabContainer := &customTemplateGitLabRepo{37gitLabClient: gitLabClient,38serverURL: options.GitLabServerURL,39projectIDs: options.GitLabTemplateRepositoryIDs,40}4142// Add the GitLab service client to the list of custom templates43providers = append(providers, gitLabContainer)44}45return providers, nil46}4748// Download downloads all .yaml files from a GitLab repository49func (bk *customTemplateGitLabRepo) Download(_ context.Context) {5051// Define the project and template count52var projectCount = 053var templateCount = 05455// Append the GitLab directory to the location56location := config.DefaultConfig.CustomGitLabTemplatesDirectory5758// Ensure the CustomGitLabTemplateDirectory directory exists or create it if it doesn't yet exist59err := os.MkdirAll(filepath.Dir(location), 0755)60if err != nil {61gologger.Error().Msgf("Error creating directory: %v", err)62return63}6465// Get the projects from the GitLab serverURL66for _, projectID := range bk.projectIDs {6768// Get the project information from the GitLab serverURL to get the default branch and the project name69project, _, err := bk.gitLabClient.Projects.GetProject(projectID, nil)70if err != nil {71gologger.Error().Msgf("error retrieving GitLab project: %s %s", project, err)72return73}7475// Add a subdirectory with the project ID as the subdirectory within the location76projectOutputPath := filepath.Join(location, project.Path)7778// Ensure the subdirectory exists or create it if it doesn't yet exist79err = os.MkdirAll(projectOutputPath, 0755)80if err != nil {81gologger.Error().Msgf("Error creating subdirectory: %v", err)82return83}8485// Get the directory listing for the files in the project86tree, _, err := bk.gitLabClient.Repositories.ListTree(projectID, &gitlab.ListTreeOptions{87Ref: gitlab.Ptr(project.DefaultBranch),88Recursive: gitlab.Ptr(true),89})90if err != nil {91gologger.Error().Msgf("error retrieving files from GitLab project: %s (%d) %s", project.Name, projectID, err)92}9394// Loop through the tree and download the files95for _, file := range tree {96// If the object is not a file or file extension is not .yaml, skip it97if file.Type == "blob" && filepath.Ext(file.Path) == ".yaml" {98gf := &gitlab.GetFileOptions{99Ref: gitlab.Ptr(project.DefaultBranch),100}101f, _, err := bk.gitLabClient.RepositoryFiles.GetFile(projectID, file.Path, gf)102if err != nil {103gologger.Error().Msgf("error retrieving GitLab project file: %d %s", projectID, err)104return105}106107// Decode the file content from base64 into bytes so that it can be written to the local filesystem108contents, err := base64.StdEncoding.DecodeString(f.Content)109if err != nil {110gologger.Error().Msgf("error decoding GitLab project (%s) file: %s %s", project.Name, f.FileName, err)111return112}113114// Write the downloaded template to the local filesystem at the location with the filename of the blob name115err = os.WriteFile(filepath.Join(projectOutputPath, f.FileName), contents, 0644)116if err != nil {117gologger.Error().Msgf("error writing GitLab project (%s) file: %s %s", project.Name, f.FileName, err)118return119}120121// Increment the number of templates downloaded122templateCount++123}124}125126// Increment the number of projects downloaded127projectCount++128gologger.Info().Msgf("GitLab project '%s' (%d) cloned successfully", project.Name, projectID)129}130131// Print the number of projects and templates downloaded132gologger.Info().Msgf("%d templates downloaded from %d GitLab project(s) to: %s", templateCount, projectCount, location)133}134135// Update is a wrapper around Download since it doesn't maintain a diff of the templates downloaded versus in the136// repository for simplicity.137func (bk *customTemplateGitLabRepo) Update(ctx context.Context) {138if len(bk.projectIDs) == 0 {139// No projects to download or update140return141}142bk.Download(ctx)143}144145// getGitLabClient returns a GitLab client for the given serverURL and token146func getGitLabClient(server string, token string) (*gitlab.Client, error) {147client, err := gitlab.NewClient(token, gitlab.WithBaseURL(server))148return client, err149}150151152