mirror of https://github.com/daffainfo/nuclei.git
Gitlab Custom Templates (#3570)
* Configuration options for GitLab template pulls * GitLab client creation * GitLab hooks and property renames * Fix filesystem writing and update environment variables * Fix type error in formatted error message * Migrate directory config to new nucleiconfig file * refactor + add custom templates to tm * typo fix + only show installed ct with -tv * add default gitlab url if not given * fix template valid failure --------- Co-authored-by: Tarun Koyalwar <tarun@projectdiscovery.io>dev
parent
b211d6fa44
commit
dcb003211c
|
@ -417,12 +417,19 @@ func printVersion() {
|
||||||
func printTemplateVersion() {
|
func printTemplateVersion() {
|
||||||
cfg := config.DefaultConfig
|
cfg := config.DefaultConfig
|
||||||
gologger.Info().Msgf("Public nuclei-templates version: %s (%s)\n", cfg.TemplateVersion, cfg.TemplatesDirectory)
|
gologger.Info().Msgf("Public nuclei-templates version: %s (%s)\n", cfg.TemplateVersion, cfg.TemplatesDirectory)
|
||||||
if cfg.CustomS3TemplatesDirectory != "" {
|
|
||||||
|
if fileutil.FolderExists(cfg.CustomS3TemplatesDirectory) {
|
||||||
gologger.Info().Msgf("Custom S3 templates location: %s\n", cfg.CustomS3TemplatesDirectory)
|
gologger.Info().Msgf("Custom S3 templates location: %s\n", cfg.CustomS3TemplatesDirectory)
|
||||||
}
|
}
|
||||||
if cfg.CustomGithubTemplatesDirectory != "" {
|
if fileutil.FolderExists(cfg.CustomGithubTemplatesDirectory) {
|
||||||
gologger.Info().Msgf("Custom Github templates location: %s ", cfg.CustomGithubTemplatesDirectory)
|
gologger.Info().Msgf("Custom Github templates location: %s ", cfg.CustomGithubTemplatesDirectory)
|
||||||
}
|
}
|
||||||
|
if fileutil.FolderExists(cfg.CustomGitLabTemplatesDirectory) {
|
||||||
|
gologger.Info().Msgf("Custom Gitlab templates location: %s ", cfg.CustomGitLabTemplatesDirectory)
|
||||||
|
}
|
||||||
|
if fileutil.FolderExists(cfg.CustomAzureTemplatesDirectory) {
|
||||||
|
gologger.Info().Msgf("Custom Azure templates location: %s ", cfg.CustomAzureTemplatesDirectory)
|
||||||
|
}
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package installer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -14,6 +15,7 @@ import (
|
||||||
"github.com/olekukonko/tablewriter"
|
"github.com/olekukonko/tablewriter"
|
||||||
"github.com/projectdiscovery/gologger"
|
"github.com/projectdiscovery/gologger"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/config"
|
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/config"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/external/customtemplates"
|
||||||
errorutil "github.com/projectdiscovery/utils/errors"
|
errorutil "github.com/projectdiscovery/utils/errors"
|
||||||
fileutil "github.com/projectdiscovery/utils/file"
|
fileutil "github.com/projectdiscovery/utils/file"
|
||||||
stringsutil "github.com/projectdiscovery/utils/strings"
|
stringsutil "github.com/projectdiscovery/utils/strings"
|
||||||
|
@ -54,7 +56,9 @@ func (t *templateUpdateResults) String() string {
|
||||||
|
|
||||||
// TemplateManager is a manager for templates.
|
// TemplateManager is a manager for templates.
|
||||||
// It downloads / updates / installs templates.
|
// It downloads / updates / installs templates.
|
||||||
type TemplateManager struct{}
|
type TemplateManager struct {
|
||||||
|
CustomTemplates *customtemplates.CustomTemplatesManager // optional if given tries to download custom templates
|
||||||
|
}
|
||||||
|
|
||||||
// FreshInstallIfNotExists installs templates if they are not already installed
|
// FreshInstallIfNotExists installs templates if they are not already installed
|
||||||
// if templates directory already exists, it does nothing
|
// if templates directory already exists, it does nothing
|
||||||
|
@ -63,7 +67,13 @@ func (t *TemplateManager) FreshInstallIfNotExists() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
gologger.Info().Msgf("nuclei-templates are not installed, installing...")
|
gologger.Info().Msgf("nuclei-templates are not installed, installing...")
|
||||||
return t.installTemplatesAt(config.DefaultConfig.TemplatesDirectory)
|
if err := t.installTemplatesAt(config.DefaultConfig.TemplatesDirectory); err != nil {
|
||||||
|
return errorutil.NewWithErr(err).Msgf("failed to install templates at %s", config.DefaultConfig.TemplatesDirectory)
|
||||||
|
}
|
||||||
|
if t.CustomTemplates != nil {
|
||||||
|
t.CustomTemplates.Download(context.TODO())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateIfOutdated updates templates if they are outdated
|
// UpdateIfOutdated updates templates if they are outdated
|
||||||
|
@ -310,7 +320,7 @@ func (t *TemplateManager) calculateChecksumMap(dir string) (map[string]string, e
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// skip checksums of custom templates i.e github and s3
|
// skip checksums of custom templates i.e github and s3
|
||||||
if stringsutil.HasPrefixAny(path, config.DefaultConfig.CustomGithubTemplatesDirectory, config.DefaultConfig.CustomS3TemplatesDirectory) {
|
if stringsutil.HasPrefixAny(path, config.DefaultConfig.GetAllCustomTemplateDirs()...) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -129,7 +130,7 @@ func validateOptions(options *types.Options) error {
|
||||||
}
|
}
|
||||||
validateCertificatePaths([]string{options.ClientCertFile, options.ClientKeyFile, options.ClientCAFile})
|
validateCertificatePaths([]string{options.ClientCertFile, options.ClientKeyFile, options.ClientCAFile})
|
||||||
}
|
}
|
||||||
// Verify aws secrets are passed if s3 template bucket passed
|
// Verify AWS secrets are passed if a S3 template bucket is passed
|
||||||
if options.AwsBucketName != "" && options.UpdateTemplates {
|
if options.AwsBucketName != "" && options.UpdateTemplates {
|
||||||
missing := validateMissingS3Options(options)
|
missing := validateMissingS3Options(options)
|
||||||
if missing != nil {
|
if missing != nil {
|
||||||
|
@ -145,6 +146,14 @@ func validateOptions(options *types.Options) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify that all GitLab options are provided if the GitLab server or token is provided
|
||||||
|
if options.GitLabToken != "" && options.UpdateTemplates {
|
||||||
|
missing := validateMissingGitLabOptions(options)
|
||||||
|
if missing != nil {
|
||||||
|
return fmt.Errorf("gitlab server details are missing. Please provide %s", strings.Join(missing, ","))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// verify that a valid ip version type was selected (4, 6)
|
// verify that a valid ip version type was selected (4, 6)
|
||||||
if len(options.IPVersion) == 0 {
|
if len(options.IPVersion) == 0 {
|
||||||
// add ipv4 as default
|
// add ipv4 as default
|
||||||
|
@ -186,6 +195,8 @@ func validateCloudOptions(options *types.Options) error {
|
||||||
missing = validateMissingS3Options(options)
|
missing = validateMissingS3Options(options)
|
||||||
case "github":
|
case "github":
|
||||||
missing = validateMissingGithubOptions(options)
|
missing = validateMissingGithubOptions(options)
|
||||||
|
case "gitlab":
|
||||||
|
missing = validateMissingGitLabOptions(options)
|
||||||
case "azure":
|
case "azure":
|
||||||
missing = validateMissingAzureOptions(options)
|
missing = validateMissingAzureOptions(options)
|
||||||
}
|
}
|
||||||
|
@ -244,6 +255,18 @@ func validateMissingGithubOptions(options *types.Options) []string {
|
||||||
return missing
|
return missing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateMissingGitLabOptions(options *types.Options) []string {
|
||||||
|
var missing []string
|
||||||
|
if options.GitLabToken == "" {
|
||||||
|
missing = append(missing, "GITLAB_TOKEN")
|
||||||
|
}
|
||||||
|
if len(options.GitLabTemplateRepositoryIDs) == 0 {
|
||||||
|
missing = append(missing, "GITLAB_REPOSITORY_IDS")
|
||||||
|
}
|
||||||
|
|
||||||
|
return missing
|
||||||
|
}
|
||||||
|
|
||||||
// configureOutput configures the output logging levels to be displayed on the screen
|
// configureOutput configures the output logging levels to be displayed on the screen
|
||||||
func configureOutput(options *types.Options) {
|
func configureOutput(options *types.Options) {
|
||||||
// If the user desires verbose output, show verbose output
|
// If the user desires verbose output, show verbose output
|
||||||
|
@ -333,6 +356,29 @@ func readEnvInputVars(options *types.Options) {
|
||||||
if repolist != "" {
|
if repolist != "" {
|
||||||
options.GithubTemplateRepo = append(options.GithubTemplateRepo, stringsutil.SplitAny(repolist, ",")...)
|
options.GithubTemplateRepo = append(options.GithubTemplateRepo, stringsutil.SplitAny(repolist, ",")...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GitLab options for downloading templates from a repository
|
||||||
|
options.GitLabServerURL = os.Getenv("GITLAB_SERVER_URL")
|
||||||
|
if options.GitLabServerURL == "" {
|
||||||
|
options.GitLabServerURL = "https://gitlab.com"
|
||||||
|
}
|
||||||
|
options.GitLabToken = os.Getenv("GITLAB_TOKEN")
|
||||||
|
repolist = os.Getenv("GITLAB_REPOSITORY_IDS")
|
||||||
|
// Convert the comma separated list of repository IDs to a list of integers
|
||||||
|
if repolist != "" {
|
||||||
|
for _, repoID := range stringsutil.SplitAny(repolist, ",") {
|
||||||
|
// Attempt to convert the repo ID to an integer
|
||||||
|
repoIDInt, err := strconv.Atoi(repoID)
|
||||||
|
if err != nil {
|
||||||
|
gologger.Warning().Msgf("Invalid GitLab template repository ID: %s", repoID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the int repository ID to the list
|
||||||
|
options.GitLabTemplateRepositoryIDs = append(options.GitLabTemplateRepositoryIDs, repoIDInt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// AWS options for downloading templates from an S3 bucket
|
// AWS options for downloading templates from an S3 bucket
|
||||||
options.AwsAccessKey = os.Getenv("AWS_ACCESS_KEY")
|
options.AwsAccessKey = os.Getenv("AWS_ACCESS_KEY")
|
||||||
options.AwsSecretKey = os.Getenv("AWS_SECRET_KEY")
|
options.AwsSecretKey = os.Getenv("AWS_SECRET_KEY")
|
||||||
|
|
|
@ -73,7 +73,6 @@ type Runner struct {
|
||||||
hostErrors hosterrorscache.CacheInterface
|
hostErrors hosterrorscache.CacheInterface
|
||||||
resumeCfg *types.ResumeCfg
|
resumeCfg *types.ResumeCfg
|
||||||
pprofServer *http.Server
|
pprofServer *http.Server
|
||||||
customTemplates []customtemplates.Provider
|
|
||||||
cloudClient *nucleicloud.Client
|
cloudClient *nucleicloud.Client
|
||||||
cloudTargets []string
|
cloudTargets []string
|
||||||
}
|
}
|
||||||
|
@ -102,8 +101,16 @@ func New(options *types.Options) (*Runner, error) {
|
||||||
gologger.Error().Msgf("nuclei version check failed got: %s\n", err)
|
gologger.Error().Msgf("nuclei version check failed got: %s\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check for custom template updates and update if available
|
||||||
|
ctm, err := customtemplates.NewCustomTemplatesManager(options)
|
||||||
|
if err != nil {
|
||||||
|
gologger.Error().Label("custom-templates").Msgf("Failed to create custom templates manager: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Check for template updates and update if available
|
// Check for template updates and update if available
|
||||||
tm := &installer.TemplateManager{}
|
// if custom templates manager is not nil, we will install custom templates if there is fresh installation
|
||||||
|
tm := &installer.TemplateManager{CustomTemplates: ctm}
|
||||||
if err := tm.FreshInstallIfNotExists(); err != nil {
|
if err := tm.FreshInstallIfNotExists(); err != nil {
|
||||||
gologger.Warning().Msgf("failed to install nuclei templates: %s\n", err)
|
gologger.Warning().Msgf("failed to install nuclei templates: %s\n", err)
|
||||||
}
|
}
|
||||||
|
@ -116,6 +123,18 @@ func New(options *types.Options) (*Runner, error) {
|
||||||
gologger.Warning().Msgf("failed to update nuclei ignore file: %s\n", err)
|
gologger.Warning().Msgf("failed to update nuclei ignore file: %s\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if options.UpdateTemplates {
|
||||||
|
// we automatically check for updates unless explicitly disabled
|
||||||
|
// this print statement is only to inform the user that there are no updates
|
||||||
|
if !config.DefaultConfig.NeedsTemplateUpdate() {
|
||||||
|
gologger.Info().Msgf("No new updates found for nuclei templates")
|
||||||
|
}
|
||||||
|
// manually trigger update of custom templates
|
||||||
|
if ctm != nil {
|
||||||
|
ctm.Update(context.TODO())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.Validate {
|
if options.Validate {
|
||||||
|
@ -125,8 +144,6 @@ func New(options *types.Options) (*Runner, error) {
|
||||||
// TODO: refactor to pass options reference globally without cycles
|
// TODO: refactor to pass options reference globally without cycles
|
||||||
parsers.NoStrictSyntax = options.NoStrictSyntax
|
parsers.NoStrictSyntax = options.NoStrictSyntax
|
||||||
yaml.StrictSyntax = !options.NoStrictSyntax
|
yaml.StrictSyntax = !options.NoStrictSyntax
|
||||||
// parse the runner.options.GithubTemplateRepo and store the valid repos in runner.customTemplateRepos
|
|
||||||
runner.customTemplates = customtemplates.ParseCustomTemplates(runner.options)
|
|
||||||
|
|
||||||
if options.Headless {
|
if options.Headless {
|
||||||
if engine.MustDisableSandbox() {
|
if engine.MustDisableSandbox() {
|
||||||
|
|
|
@ -9,8 +9,6 @@ import (
|
||||||
const (
|
const (
|
||||||
TemplateConfigFileName = ".templates-config.json"
|
TemplateConfigFileName = ".templates-config.json"
|
||||||
NucleiTemplatesDirName = "nuclei-templates"
|
NucleiTemplatesDirName = "nuclei-templates"
|
||||||
CustomS3TemplatesDirName = "s3"
|
|
||||||
CustomGithubTemplatesDirName = "github"
|
|
||||||
OfficialNucleiTeamplatesRepoName = "nuclei-templates"
|
OfficialNucleiTeamplatesRepoName = "nuclei-templates"
|
||||||
NucleiIgnoreFileName = ".nuclei-ignore"
|
NucleiIgnoreFileName = ".nuclei-ignore"
|
||||||
NucleiTemplatesCheckSumFileName = ".checksum"
|
NucleiTemplatesCheckSumFileName = ".checksum"
|
||||||
|
@ -19,6 +17,12 @@ const (
|
||||||
ReportingConfigFilename = "reporting-config.yaml"
|
ReportingConfigFilename = "reporting-config.yaml"
|
||||||
// Version is the current version of nuclei
|
// Version is the current version of nuclei
|
||||||
Version = `v2.9.2-dev`
|
Version = `v2.9.2-dev`
|
||||||
|
|
||||||
|
// Directory Names of custom templates
|
||||||
|
CustomS3TemplatesDirName = "s3"
|
||||||
|
CustomGithubTemplatesDirName = "github"
|
||||||
|
CustomAzureTemplatesDirName = "azure"
|
||||||
|
CustomGitLabTemplatesDirName = "gitlab"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsOutdatedVersion compares two versions and returns true
|
// IsOutdatedVersion compares two versions and returns true
|
||||||
|
|
|
@ -21,8 +21,12 @@ var DefaultConfig *Config
|
||||||
type Config struct {
|
type Config struct {
|
||||||
TemplatesDirectory string `json:"nuclei-templates-directory,omitempty"`
|
TemplatesDirectory string `json:"nuclei-templates-directory,omitempty"`
|
||||||
|
|
||||||
|
// customtemplates exists in templates directory with the name of custom-templates provider
|
||||||
|
// below custom paths are absolute paths to respecitive custom-templates directories
|
||||||
CustomS3TemplatesDirectory string `json:"custom-s3-templates-directory"`
|
CustomS3TemplatesDirectory string `json:"custom-s3-templates-directory"`
|
||||||
CustomGithubTemplatesDirectory string `json:"custom-github-templates-directory"`
|
CustomGithubTemplatesDirectory string `json:"custom-github-templates-directory"`
|
||||||
|
CustomGitLabTemplatesDirectory string `json:"custom-gitlab-templates-directory"`
|
||||||
|
CustomAzureTemplatesDirectory string `json:"custom-azure-templates-directory"`
|
||||||
|
|
||||||
TemplateVersion string `json:"nuclei-templates-version,omitempty"`
|
TemplateVersion string `json:"nuclei-templates-version,omitempty"`
|
||||||
NucleiIgnoreHash string `json:"nuclei-ignore-hash,omitempty"`
|
NucleiIgnoreHash string `json:"nuclei-ignore-hash,omitempty"`
|
||||||
|
@ -104,6 +108,11 @@ func (c *Config) GetConfigDir() string {
|
||||||
return c.configDir
|
return c.configDir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAllCustomTemplateDirs returns all custom template directories
|
||||||
|
func (c *Config) GetAllCustomTemplateDirs() []string {
|
||||||
|
return []string{c.CustomS3TemplatesDirectory, c.CustomGithubTemplatesDirectory, c.CustomGitLabTemplatesDirectory, c.CustomAzureTemplatesDirectory}
|
||||||
|
}
|
||||||
|
|
||||||
// GetReportingConfigFilePath returns the nuclei reporting config file path
|
// GetReportingConfigFilePath returns the nuclei reporting config file path
|
||||||
func (c *Config) GetReportingConfigFilePath() string {
|
func (c *Config) GetReportingConfigFilePath() string {
|
||||||
return filepath.Join(c.configDir, ReportingConfigFilename)
|
return filepath.Join(c.configDir, ReportingConfigFilename)
|
||||||
|
@ -175,7 +184,9 @@ func (c *Config) SetTemplatesDir(dirPath string) {
|
||||||
c.TemplatesDirectory = dirPath
|
c.TemplatesDirectory = dirPath
|
||||||
// Update the custom templates directory
|
// Update the custom templates directory
|
||||||
c.CustomGithubTemplatesDirectory = filepath.Join(dirPath, CustomGithubTemplatesDirName)
|
c.CustomGithubTemplatesDirectory = filepath.Join(dirPath, CustomGithubTemplatesDirName)
|
||||||
c.CustomS3TemplatesDirectory = filepath.Join(dirPath, CustomGithubTemplatesDirName)
|
c.CustomS3TemplatesDirectory = filepath.Join(dirPath, CustomS3TemplatesDirName)
|
||||||
|
c.CustomGitLabTemplatesDirectory = filepath.Join(dirPath, CustomGitLabTemplatesDirName)
|
||||||
|
c.CustomAzureTemplatesDirectory = filepath.Join(dirPath, CustomAzureTemplatesDirName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTemplatesVersion sets the new nuclei templates version
|
// SetTemplatesVersion sets the new nuclei templates version
|
||||||
|
@ -202,8 +213,6 @@ func (c *Config) ReadTemplatesConfig() error {
|
||||||
return errorutil.NewWithErr(err).Msgf("could not unmarshal nuclei config file at %s", c.getTemplatesConfigFilePath())
|
return errorutil.NewWithErr(err).Msgf("could not unmarshal nuclei config file at %s", c.getTemplatesConfigFilePath())
|
||||||
}
|
}
|
||||||
// apply config
|
// apply config
|
||||||
c.CustomGithubTemplatesDirectory = cfg.CustomGithubTemplatesDirectory
|
|
||||||
c.CustomS3TemplatesDirectory = cfg.CustomS3TemplatesDirectory
|
|
||||||
c.TemplatesDirectory = cfg.TemplatesDirectory
|
c.TemplatesDirectory = cfg.TemplatesDirectory
|
||||||
c.TemplateVersion = cfg.TemplateVersion
|
c.TemplateVersion = cfg.TemplateVersion
|
||||||
c.NucleiIgnoreHash = cfg.NucleiIgnoreHash
|
c.NucleiIgnoreHash = cfg.NucleiIgnoreHash
|
||||||
|
@ -279,6 +288,11 @@ func init() {
|
||||||
gologger.Error().Msgf("failed to write config file at %s got: %s", DefaultConfig.getTemplatesConfigFilePath(), err)
|
gologger.Error().Msgf("failed to write config file at %s got: %s", DefaultConfig.getTemplatesConfigFilePath(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Loads/updates paths of custom templates
|
||||||
|
// Note: custom templates paths should not be updated in config file
|
||||||
|
// and even if it is changed we don't follow it since it is not expected behavior
|
||||||
|
// If custom templates are in default locations only then they are loaded while running nuclei
|
||||||
|
DefaultConfig.SetTemplatesDir(DefaultConfig.TemplatesDirectory)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDefaultConfigDir() string {
|
func getDefaultConfigDir() string {
|
||||||
|
@ -297,6 +311,6 @@ func getDefaultConfigDir() string {
|
||||||
// Add Default Config adds default when .templates-config.json file is not present
|
// Add Default Config adds default when .templates-config.json file is not present
|
||||||
func applyDefaultConfig() {
|
func applyDefaultConfig() {
|
||||||
DefaultConfig.TemplatesDirectory = filepath.Join(DefaultConfig.homeDir, NucleiTemplatesDirName)
|
DefaultConfig.TemplatesDirectory = filepath.Join(DefaultConfig.homeDir, NucleiTemplatesDirName)
|
||||||
DefaultConfig.CustomGithubTemplatesDirectory = filepath.Join(DefaultConfig.TemplatesDirectory, CustomGithubTemplatesDirName)
|
// updates all necessary paths
|
||||||
DefaultConfig.CustomS3TemplatesDirectory = filepath.Join(DefaultConfig.TemplatesDirectory, CustomS3TemplatesDirName)
|
DefaultConfig.SetTemplatesDir(DefaultConfig.TemplatesDirectory)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,19 +3,47 @@ package customtemplates
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
|
|
||||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
|
|
||||||
"github.com/projectdiscovery/gologger"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
|
||||||
|
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
|
||||||
|
"github.com/projectdiscovery/gologger"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/config"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||||
|
errorutil "github.com/projectdiscovery/utils/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ Provider = &customTemplateAzureBlob{}
|
||||||
|
|
||||||
type customTemplateAzureBlob struct {
|
type customTemplateAzureBlob struct {
|
||||||
azureBlobClient *azblob.Client
|
azureBlobClient *azblob.Client
|
||||||
containerName string
|
containerName string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewAzureProviders creates a new Azure Blob Storage provider for downloading custom templates
|
||||||
|
func NewAzureProviders(options *types.Options) ([]*customTemplateAzureBlob, error) {
|
||||||
|
providers := []*customTemplateAzureBlob{}
|
||||||
|
if options.AzureContainerName != "" {
|
||||||
|
// Establish a connection to Azure and build a client object with which to download templates from Azure Blob Storage
|
||||||
|
azClient, err := getAzureBlobClient(options.AzureTenantID, options.AzureClientID, options.AzureClientSecret, options.AzureServiceURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errorutil.NewWithErr(err).Msgf("Error establishing Azure Blob client for %s", options.AzureContainerName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new Azure Blob Storage container object
|
||||||
|
azTemplateContainer := &customTemplateAzureBlob{
|
||||||
|
azureBlobClient: azClient,
|
||||||
|
containerName: options.AzureContainerName,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the Azure Blob Storage container object to the list of custom templates
|
||||||
|
providers = append(providers, azTemplateContainer)
|
||||||
|
}
|
||||||
|
return providers, nil
|
||||||
|
}
|
||||||
|
|
||||||
func getAzureBlobClient(tenantID string, clientID string, clientSecret string, serviceURL string) (*azblob.Client, error) {
|
func getAzureBlobClient(tenantID string, clientID string, clientSecret string, serviceURL string) (*azblob.Client, error) {
|
||||||
// Create an Azure credential using the provided credentials
|
// Create an Azure credential using the provided credentials
|
||||||
credentials, err := azidentity.NewClientSecretCredential(tenantID, clientID, clientSecret, nil)
|
credentials, err := azidentity.NewClientSecretCredential(tenantID, clientID, clientSecret, nil)
|
||||||
|
@ -34,12 +62,12 @@ func getAzureBlobClient(tenantID string, clientID string, clientSecret string, s
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bk *customTemplateAzureBlob) Download(location string, ctx context.Context) {
|
func (bk *customTemplateAzureBlob) Download(ctx context.Context) {
|
||||||
// Set an incrementer for the number of templates downloaded
|
// Set an incrementer for the number of templates downloaded
|
||||||
var templatesDownloaded = 0
|
var templatesDownloaded = 0
|
||||||
|
|
||||||
// Define the local path to which the templates will be downloaded
|
// Define the local path to which the templates will be downloaded
|
||||||
downloadPath := filepath.Join(location, CustomAzureTemplateDirectory, bk.containerName)
|
downloadPath := filepath.Join(config.DefaultConfig.CustomAzureTemplatesDirectory, bk.containerName)
|
||||||
|
|
||||||
// Get the list of all templates from the container
|
// Get the list of all templates from the container
|
||||||
pager := bk.azureBlobClient.NewListBlobsFlatPager(bk.containerName, &azblob.ListBlobsFlatOptions{
|
pager := bk.azureBlobClient.NewListBlobsFlatPager(bk.containerName, &azblob.ListBlobsFlatOptions{
|
||||||
|
@ -78,9 +106,9 @@ func (bk *customTemplateAzureBlob) Download(location string, ctx context.Context
|
||||||
// Update updates the templates from the Azure Blob Storage container to the local filesystem. This is effectively a
|
// Update updates the templates from the Azure Blob Storage container to the local filesystem. This is effectively a
|
||||||
// wrapper of the Download function which downloads of all templates from the container and doesn't manage a
|
// wrapper of the Download function which downloads of all templates from the container and doesn't manage a
|
||||||
// differential update.
|
// differential update.
|
||||||
func (bk *customTemplateAzureBlob) Update(location string, ctx context.Context) {
|
func (bk *customTemplateAzureBlob) Update(ctx context.Context) {
|
||||||
// Treat the update as a download of all templates from the container
|
// Treat the update as a download of all templates from the container
|
||||||
bk.Download(location, ctx)
|
bk.Download(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// downloadTemplate downloads a template from the Azure Blob Storage container to the local filesystem with the provided
|
// downloadTemplate downloads a template from the Azure Blob Storage container to the local filesystem with the provided
|
||||||
|
|
|
@ -10,11 +10,15 @@ import (
|
||||||
"github.com/google/go-github/github"
|
"github.com/google/go-github/github"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/projectdiscovery/gologger"
|
"github.com/projectdiscovery/gologger"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/config"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||||
fileutil "github.com/projectdiscovery/utils/file"
|
fileutil "github.com/projectdiscovery/utils/file"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
"gopkg.in/src-d/go-git.v4/plumbing/transport/http"
|
"gopkg.in/src-d/go-git.v4/plumbing/transport/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ Provider = &customTemplateGithubRepo{}
|
||||||
|
|
||||||
type customTemplateGithubRepo struct {
|
type customTemplateGithubRepo struct {
|
||||||
owner string
|
owner string
|
||||||
reponame string
|
reponame string
|
||||||
|
@ -23,9 +27,8 @@ type customTemplateGithubRepo struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function download the custom github template repository
|
// This function download the custom github template repository
|
||||||
func (customTemplate *customTemplateGithubRepo) Download(location string, ctx context.Context) {
|
func (customTemplate *customTemplateGithubRepo) Download(ctx context.Context) {
|
||||||
downloadPath := filepath.Join(location, CustomGithubTemplateDirectory)
|
clonePath := customTemplate.getLocalRepoClonePath(config.DefaultConfig.CustomGithubTemplatesDirectory)
|
||||||
clonePath := customTemplate.getLocalRepoClonePath(downloadPath)
|
|
||||||
|
|
||||||
if !fileutil.FolderExists(clonePath) {
|
if !fileutil.FolderExists(clonePath) {
|
||||||
err := customTemplate.cloneRepo(clonePath, customTemplate.githubToken)
|
err := customTemplate.cloneRepo(clonePath, customTemplate.githubToken)
|
||||||
|
@ -38,13 +41,13 @@ func (customTemplate *customTemplateGithubRepo) Download(location string, ctx co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (customTemplate *customTemplateGithubRepo) Update(location string, ctx context.Context) {
|
func (customTemplate *customTemplateGithubRepo) Update(ctx context.Context) {
|
||||||
downloadPath := filepath.Join(location, CustomGithubTemplateDirectory)
|
downloadPath := config.DefaultConfig.CustomGithubTemplatesDirectory
|
||||||
clonePath := customTemplate.getLocalRepoClonePath(downloadPath)
|
clonePath := customTemplate.getLocalRepoClonePath(downloadPath)
|
||||||
|
|
||||||
// If folder does not exits then clone/download the repo
|
// If folder does not exits then clone/download the repo
|
||||||
if !fileutil.FolderExists(clonePath) {
|
if !fileutil.FolderExists(clonePath) {
|
||||||
customTemplate.Download(location, ctx)
|
customTemplate.Download(ctx)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err := customTemplate.pullChanges(clonePath, customTemplate.githubToken)
|
err := customTemplate.pullChanges(clonePath, customTemplate.githubToken)
|
||||||
|
@ -55,6 +58,33 @@ func (customTemplate *customTemplateGithubRepo) Update(location string, ctx cont
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewGithubProviders returns new instance of github providers for downloading custom templates
|
||||||
|
func NewGithubProviders(options *types.Options) ([]*customTemplateGithubRepo, error) {
|
||||||
|
providers := []*customTemplateGithubRepo{}
|
||||||
|
gitHubClient := getGHClientIncognito()
|
||||||
|
|
||||||
|
for _, repoName := range options.GithubTemplateRepo {
|
||||||
|
owner, repo, err := getOwnerAndRepo(repoName)
|
||||||
|
if err != nil {
|
||||||
|
gologger.Error().Msgf("%s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
githubRepo, err := getGithubRepo(gitHubClient, owner, repo, options.GithubToken)
|
||||||
|
if err != nil {
|
||||||
|
gologger.Error().Msgf("%s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
customTemplateRepo := &customTemplateGithubRepo{
|
||||||
|
owner: owner,
|
||||||
|
reponame: repo,
|
||||||
|
gitCloneURL: githubRepo.GetCloneURL(),
|
||||||
|
githubToken: options.GithubToken,
|
||||||
|
}
|
||||||
|
providers = append(providers, customTemplateRepo)
|
||||||
|
}
|
||||||
|
return providers, nil
|
||||||
|
}
|
||||||
|
|
||||||
// getOwnerAndRepo returns the owner, repo, err from the given string
|
// getOwnerAndRepo returns the owner, repo, err from the given string
|
||||||
// eg. it takes input projectdiscovery/nuclei-templates and
|
// eg. it takes input projectdiscovery/nuclei-templates and
|
||||||
// returns owner=> projectdiscovery , repo => nuclei-templates
|
// returns owner=> projectdiscovery , repo => nuclei-templates
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/projectdiscovery/gologger"
|
"github.com/projectdiscovery/gologger"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/config"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/testutils"
|
"github.com/projectdiscovery/nuclei/v2/pkg/testutils"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -18,14 +19,16 @@ func TestDownloadCustomTemplatesFromGitHub(t *testing.T) {
|
||||||
require.Nil(t, err, "could not create temp directory")
|
require.Nil(t, err, "could not create temp directory")
|
||||||
defer os.RemoveAll(templatesDirectory)
|
defer os.RemoveAll(templatesDirectory)
|
||||||
|
|
||||||
|
config.DefaultConfig.SetTemplatesDir(templatesDirectory)
|
||||||
|
|
||||||
options := testutils.DefaultOptions
|
options := testutils.DefaultOptions
|
||||||
options.GithubTemplateRepo = []string{"projectdiscovery/nuclei-templates", "ehsandeep/nuclei-templates"}
|
options.GithubTemplateRepo = []string{"projectdiscovery/nuclei-templates", "ehsandeep/nuclei-templates"}
|
||||||
options.GithubToken = os.Getenv("GITHUB_TOKEN")
|
options.GithubToken = os.Getenv("GITHUB_TOKEN")
|
||||||
customTemplates := ParseCustomTemplates(options)
|
|
||||||
|
|
||||||
for _, ct := range customTemplates {
|
ctm, err := NewCustomTemplatesManager(options)
|
||||||
ct.Download(templatesDirectory, context.Background())
|
require.Nil(t, err, "could not create custom templates manager")
|
||||||
}
|
|
||||||
|
ctm.Download(context.Background())
|
||||||
|
|
||||||
require.DirExists(t, filepath.Join(templatesDirectory, "github", "nuclei-templates"), "cloned directory does not exists")
|
require.DirExists(t, filepath.Join(templatesDirectory, "github", "nuclei-templates"), "cloned directory does not exists")
|
||||||
require.DirExists(t, filepath.Join(templatesDirectory, "github", "nuclei-templates-ehsandeep"), "cloned directory does not exists")
|
require.DirExists(t, filepath.Join(templatesDirectory, "github", "nuclei-templates-ehsandeep"), "cloned directory does not exists")
|
||||||
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
package customtemplates
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/projectdiscovery/gologger"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/config"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||||
|
errorutil "github.com/projectdiscovery/utils/errors"
|
||||||
|
"github.com/xanzy/go-gitlab"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ Provider = &customTemplateGitLabRepo{}
|
||||||
|
|
||||||
|
type customTemplateGitLabRepo struct {
|
||||||
|
gitLabClient *gitlab.Client
|
||||||
|
serverURL string
|
||||||
|
projectIDs []int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGitlabProviders returns a new list of GitLab providers for downloading custom templates
|
||||||
|
func NewGitlabProviders(options *types.Options) ([]*customTemplateGitLabRepo, error) {
|
||||||
|
providers := []*customTemplateGitLabRepo{}
|
||||||
|
if options.GitLabToken != "" {
|
||||||
|
// Establish a connection to GitLab and build a client object with which to download templates from GitLab
|
||||||
|
gitLabClient, err := getGitLabClient(options.GitLabServerURL, options.GitLabToken)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errorutil.NewWithErr(err).Msgf("Error establishing GitLab client for %s %s", options.GitLabServerURL, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new GitLab service client
|
||||||
|
gitLabContainer := &customTemplateGitLabRepo{
|
||||||
|
gitLabClient: gitLabClient,
|
||||||
|
serverURL: options.GitLabServerURL,
|
||||||
|
projectIDs: options.GitLabTemplateRepositoryIDs,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the GitLab service client to the list of custom templates
|
||||||
|
providers = append(providers, gitLabContainer)
|
||||||
|
}
|
||||||
|
return providers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download downloads all .yaml files from a GitLab repository
|
||||||
|
func (bk *customTemplateGitLabRepo) Download(_ context.Context) {
|
||||||
|
|
||||||
|
// Define the project and template count
|
||||||
|
var projectCount = 0
|
||||||
|
var templateCount = 0
|
||||||
|
|
||||||
|
// Append the GitLab directory to the location
|
||||||
|
location := config.DefaultConfig.CustomGitLabTemplatesDirectory
|
||||||
|
|
||||||
|
// Ensure the CustomGitLabTemplateDirectory directory exists or create it if it doesn't yet exist
|
||||||
|
err := os.MkdirAll(filepath.Dir(location), 0755)
|
||||||
|
if err != nil {
|
||||||
|
gologger.Error().Msgf("Error creating directory: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the projects from the GitLab serverURL
|
||||||
|
for _, projectID := range bk.projectIDs {
|
||||||
|
|
||||||
|
// Get the project information from the GitLab serverURL to get the default branch and the project name
|
||||||
|
project, _, err := bk.gitLabClient.Projects.GetProject(projectID, nil)
|
||||||
|
if err != nil {
|
||||||
|
gologger.Error().Msgf("error retrieving GitLab project: %s %s", project, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a subdirectory with the project ID as the subdirectory within the location
|
||||||
|
projectOutputPath := filepath.Join(location, project.Path)
|
||||||
|
|
||||||
|
// Ensure the subdirectory exists or create it if it doesn't yet exist
|
||||||
|
err = os.MkdirAll(projectOutputPath, 0755)
|
||||||
|
if err != nil {
|
||||||
|
gologger.Error().Msgf("Error creating subdirectory: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the directory listing for the files in the project
|
||||||
|
tree, _, err := bk.gitLabClient.Repositories.ListTree(projectID, &gitlab.ListTreeOptions{
|
||||||
|
Ref: gitlab.String(project.DefaultBranch),
|
||||||
|
Recursive: gitlab.Bool(true),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
gologger.Error().Msgf("error retrieving files from GitLab project: %s (%d) %s", project.Name, projectID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through the tree and download the files
|
||||||
|
for _, file := range tree {
|
||||||
|
// If the object is not a file or file extension is not .yaml, skip it
|
||||||
|
if file.Type == "blob" && filepath.Ext(file.Path) == ".yaml" {
|
||||||
|
gf := &gitlab.GetFileOptions{
|
||||||
|
Ref: gitlab.String(project.DefaultBranch),
|
||||||
|
}
|
||||||
|
f, _, err := bk.gitLabClient.RepositoryFiles.GetFile(projectID, file.Path, gf)
|
||||||
|
if err != nil {
|
||||||
|
gologger.Error().Msgf("error retrieving GitLab project file: %d %s", projectID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the file content from base64 into bytes so that it can be written to the local filesystem
|
||||||
|
contents, err := base64.StdEncoding.DecodeString(f.Content)
|
||||||
|
if err != nil {
|
||||||
|
gologger.Error().Msgf("error decoding GitLab project (%s) file: %s %s", project.Name, f.FileName, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the downloaded template to the local filesystem at the location with the filename of the blob name
|
||||||
|
err = os.WriteFile(filepath.Join(projectOutputPath, f.FileName), contents, 0644)
|
||||||
|
if err != nil {
|
||||||
|
gologger.Error().Msgf("error writing GitLab project (%s) file: %s %s", project.Name, f.FileName, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment the number of templates downloaded
|
||||||
|
templateCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment the number of projects downloaded
|
||||||
|
projectCount++
|
||||||
|
gologger.Info().Msgf("GitLab project '%s' (%d) cloned successfully", project.Name, projectID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the number of projects and templates downloaded
|
||||||
|
gologger.Info().Msgf("%d templates downloaded from %d GitLab project(s) to: %s", templateCount, projectCount, location)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update is a wrapper around Download since it doesn't maintain a diff of the templates downloaded versus in the
|
||||||
|
// repository for simplicity.
|
||||||
|
func (bk *customTemplateGitLabRepo) Update(ctx context.Context) {
|
||||||
|
bk.Download(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getGitLabClient returns a GitLab client for the given serverURL and token
|
||||||
|
func getGitLabClient(server string, token string) (*gitlab.Client, error) {
|
||||||
|
client, err := gitlab.NewClient(token, gitlab.WithBaseURL(server))
|
||||||
|
return client, err
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go-v2/aws"
|
"github.com/aws/aws-sdk-go-v2/aws"
|
||||||
"github.com/aws/aws-sdk-go-v2/config"
|
"github.com/aws/aws-sdk-go-v2/config"
|
||||||
|
@ -11,9 +12,14 @@ import (
|
||||||
"github.com/aws/aws-sdk-go-v2/feature/s3/manager"
|
"github.com/aws/aws-sdk-go-v2/feature/s3/manager"
|
||||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||||
"github.com/projectdiscovery/gologger"
|
"github.com/projectdiscovery/gologger"
|
||||||
|
nucleiConfig "github.com/projectdiscovery/nuclei/v2/pkg/catalog/config"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||||
|
errorutil "github.com/projectdiscovery/utils/errors"
|
||||||
stringsutil "github.com/projectdiscovery/utils/strings"
|
stringsutil "github.com/projectdiscovery/utils/strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ Provider = &customTemplateS3Bucket{}
|
||||||
|
|
||||||
type customTemplateS3Bucket struct {
|
type customTemplateS3Bucket struct {
|
||||||
s3Client *s3.Client
|
s3Client *s3.Client
|
||||||
bucketName string
|
bucketName string
|
||||||
|
@ -22,8 +28,8 @@ type customTemplateS3Bucket struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Download retrieves all custom templates from s3 bucket
|
// Download retrieves all custom templates from s3 bucket
|
||||||
func (bk *customTemplateS3Bucket) Download(location string, ctx context.Context) {
|
func (bk *customTemplateS3Bucket) Download(ctx context.Context) {
|
||||||
downloadPath := filepath.Join(location, CustomS3TemplateDirectory, bk.bucketName)
|
downloadPath := filepath.Join(nucleiConfig.DefaultConfig.CustomS3TemplatesDirectory, bk.bucketName)
|
||||||
|
|
||||||
s3Manager := manager.NewDownloader(bk.s3Client)
|
s3Manager := manager.NewDownloader(bk.s3Client)
|
||||||
paginator := s3.NewListObjectsV2Paginator(bk.s3Client, &s3.ListObjectsV2Input{
|
paginator := s3.NewListObjectsV2Paginator(bk.s3Client, &s3.ListObjectsV2Input{
|
||||||
|
@ -47,9 +53,31 @@ func (bk *customTemplateS3Bucket) Download(location string, ctx context.Context)
|
||||||
gologger.Info().Msgf("AWS bucket %s was cloned successfully at %s", bk.bucketName, downloadPath)
|
gologger.Info().Msgf("AWS bucket %s was cloned successfully at %s", bk.bucketName, downloadPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update download custom templates from s3 bucket
|
// Update downloads custom templates from s3 bucket
|
||||||
func (bk *customTemplateS3Bucket) Update(location string, ctx context.Context) {
|
func (bk *customTemplateS3Bucket) Update(ctx context.Context) {
|
||||||
bk.Download(location, ctx)
|
bk.Download(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewS3Providers returns a new instances of a s3 providers for downloading custom templates
|
||||||
|
func NewS3Providers(options *types.Options) ([]*customTemplateS3Bucket, error) {
|
||||||
|
providers := []*customTemplateS3Bucket{}
|
||||||
|
if options.AwsBucketName != "" {
|
||||||
|
s3c, err := getS3Client(context.TODO(), options.AwsAccessKey, options.AwsSecretKey, options.AwsRegion)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errorutil.NewWithErr(err).Msgf("error downloading s3 bucket %s", options.AwsBucketName)
|
||||||
|
}
|
||||||
|
ctBucket := &customTemplateS3Bucket{
|
||||||
|
bucketName: options.AwsBucketName,
|
||||||
|
s3Client: s3c,
|
||||||
|
}
|
||||||
|
if strings.Contains(options.AwsBucketName, "/") {
|
||||||
|
bPath := strings.SplitN(options.AwsBucketName, "/", 2)
|
||||||
|
ctBucket.bucketName = bPath[0]
|
||||||
|
ctBucket.prefix = bPath[1]
|
||||||
|
}
|
||||||
|
providers = append(providers, ctBucket)
|
||||||
|
}
|
||||||
|
return providers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func downloadToFile(downloader *manager.Downloader, targetDirectory, bucket, key string) error {
|
func downloadToFile(downloader *manager.Downloader, targetDirectory, bucket, key string) error {
|
||||||
|
|
|
@ -2,84 +2,79 @@ package customtemplates
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/projectdiscovery/gologger"
|
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||||
)
|
errorutil "github.com/projectdiscovery/utils/errors"
|
||||||
|
|
||||||
const (
|
|
||||||
CustomGithubTemplateDirectory = "github"
|
|
||||||
CustomS3TemplateDirectory = "s3"
|
|
||||||
CustomAzureTemplateDirectory = "azure"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Provider interface {
|
type Provider interface {
|
||||||
Download(location string, ctx context.Context)
|
Download(ctx context.Context)
|
||||||
Update(location string, ctx context.Context)
|
Update(ctx context.Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseCustomTemplates function reads the options.GithubTemplateRepo list,
|
// CustomTemplatesManager is a manager for custom templates
|
||||||
// Checks the given repos are valid or not and stores them into runner.CustomTemplates
|
type CustomTemplatesManager struct {
|
||||||
func ParseCustomTemplates(options *types.Options) []Provider {
|
providers []Provider
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download downloads the custom templates
|
||||||
|
func (c *CustomTemplatesManager) Download(ctx context.Context) {
|
||||||
|
for _, provider := range c.providers {
|
||||||
|
provider.Download(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update updates the custom templates
|
||||||
|
func (c *CustomTemplatesManager) Update(ctx context.Context) {
|
||||||
|
for _, provider := range c.providers {
|
||||||
|
provider.Update(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCustomTemplatesManager returns a new instance of a custom templates manager
|
||||||
|
func NewCustomTemplatesManager(options *types.Options) (*CustomTemplatesManager, error) {
|
||||||
|
ctm := &CustomTemplatesManager{providers: []Provider{}}
|
||||||
|
|
||||||
if options.Cloud {
|
if options.Cloud {
|
||||||
return nil
|
// if cloud is enabled, custom templates are Nop
|
||||||
}
|
return ctm, nil
|
||||||
var customTemplates []Provider
|
|
||||||
gitHubClient := getGHClientIncognito()
|
|
||||||
|
|
||||||
for _, repoName := range options.GithubTemplateRepo {
|
|
||||||
owner, repo, err := getOwnerAndRepo(repoName)
|
|
||||||
if err != nil {
|
|
||||||
gologger.Error().Msgf("%s", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
githubRepo, err := getGithubRepo(gitHubClient, owner, repo, options.GithubToken)
|
|
||||||
if err != nil {
|
|
||||||
gologger.Error().Msgf("%s", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
customTemplateRepo := &customTemplateGithubRepo{
|
|
||||||
owner: owner,
|
|
||||||
reponame: repo,
|
|
||||||
gitCloneURL: githubRepo.GetCloneURL(),
|
|
||||||
githubToken: options.GithubToken,
|
|
||||||
}
|
|
||||||
customTemplates = append(customTemplates, customTemplateRepo)
|
|
||||||
}
|
|
||||||
if options.AwsBucketName != "" {
|
|
||||||
s3c, err := getS3Client(context.TODO(), options.AwsAccessKey, options.AwsSecretKey, options.AwsRegion)
|
|
||||||
if err != nil {
|
|
||||||
gologger.Error().Msgf("error downloading s3 bucket %s %s", options.AwsBucketName, err)
|
|
||||||
return customTemplates
|
|
||||||
}
|
|
||||||
ctBucket := &customTemplateS3Bucket{
|
|
||||||
bucketName: options.AwsBucketName,
|
|
||||||
s3Client: s3c,
|
|
||||||
}
|
|
||||||
if strings.Contains(options.AwsBucketName, "/") {
|
|
||||||
bPath := strings.SplitN(options.AwsBucketName, "/", 2)
|
|
||||||
ctBucket.bucketName = bPath[0]
|
|
||||||
ctBucket.prefix = bPath[1]
|
|
||||||
}
|
|
||||||
customTemplates = append(customTemplates, ctBucket)
|
|
||||||
}
|
|
||||||
if options.AzureContainerName != "" {
|
|
||||||
// Establish a connection to Azure and build a client object with which to download templates from Azure Blob Storage
|
|
||||||
azClient, err := getAzureBlobClient(options.AzureTenantID, options.AzureClientID, options.AzureClientSecret, options.AzureServiceURL)
|
|
||||||
if err != nil {
|
|
||||||
gologger.Error().Msgf("Error establishing Azure Blob client for %s %s", options.AzureContainerName, err)
|
|
||||||
return customTemplates
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new Azure Blob Storage container object
|
// Add github providers
|
||||||
azTemplateContainer := &customTemplateAzureBlob{
|
githubProviders, err := NewGithubProviders(options)
|
||||||
azureBlobClient: azClient,
|
if err != nil {
|
||||||
containerName: options.AzureContainerName,
|
return nil, errorutil.NewWithErr(err).Msgf("could not create github providers for custom templates")
|
||||||
|
}
|
||||||
|
for _, v := range githubProviders {
|
||||||
|
ctm.providers = append(ctm.providers, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the Azure Blob Storage container object to the list of custom templates
|
// Add Aws S3 providers
|
||||||
customTemplates = append(customTemplates, azTemplateContainer)
|
s3Providers, err := NewS3Providers(options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errorutil.NewWithErr(err).Msgf("could not create s3 providers for custom templates")
|
||||||
}
|
}
|
||||||
return customTemplates
|
for _, v := range s3Providers {
|
||||||
|
ctm.providers = append(ctm.providers, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Azure providers
|
||||||
|
azureProviders, err := NewAzureProviders(options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errorutil.NewWithErr(err).Msgf("could not create azure providers for custom templates")
|
||||||
|
}
|
||||||
|
for _, v := range azureProviders {
|
||||||
|
ctm.providers = append(ctm.providers, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add GitLab providers
|
||||||
|
gitlabProviders, err := NewGitlabProviders(options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errorutil.NewWithErr(err).Msgf("could not create gitlab providers for custom templates")
|
||||||
|
}
|
||||||
|
for _, v := range gitlabProviders {
|
||||||
|
ctm.providers = append(ctm.providers, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctm, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -326,17 +326,23 @@ type Options struct {
|
||||||
ScanAllIPs bool
|
ScanAllIPs bool
|
||||||
// IPVersion to scan (4,6)
|
// IPVersion to scan (4,6)
|
||||||
IPVersion goflags.StringSlice
|
IPVersion goflags.StringSlice
|
||||||
// Github token used to clone/pull from private repos for custom templates
|
// GitHub token used to clone/pull from private repos for custom templates
|
||||||
GithubToken string
|
GithubToken string
|
||||||
// GithubTemplateRepo is the list of custom public/private templates github repos
|
// GithubTemplateRepo is the list of custom public/private templates GitHub repos
|
||||||
GithubTemplateRepo []string
|
GithubTemplateRepo []string
|
||||||
// AWS access key for downloading templates from s3 bucket
|
// GitLabServerURL is the gitlab server to use for custom templates
|
||||||
|
GitLabServerURL string
|
||||||
|
// GitLabToken used to clone/pull from private repos for custom templates
|
||||||
|
GitLabToken string
|
||||||
|
// GitLabTemplateRepositoryIDs is the comma-separated list of custom gitlab repositories IDs
|
||||||
|
GitLabTemplateRepositoryIDs []int
|
||||||
|
// AWS access key for downloading templates from S3 bucket
|
||||||
AwsAccessKey string
|
AwsAccessKey string
|
||||||
// AWS secret key for downloading templates from s3 bucket
|
// AWS secret key for downloading templates from S3 bucket
|
||||||
AwsSecretKey string
|
AwsSecretKey string
|
||||||
// AWS bucket name for downloading templates from s3 bucket
|
// AWS bucket name for downloading templates from S3 bucket
|
||||||
AwsBucketName string
|
AwsBucketName string
|
||||||
// AWS Region name where aws s3 bucket is located
|
// AWS Region name where AWS S3 bucket is located
|
||||||
AwsRegion string
|
AwsRegion string
|
||||||
// AzureContainerName for downloading templates from Azure Blob Storage. Example: templates
|
// AzureContainerName for downloading templates from Azure Blob Storage. Example: templates
|
||||||
AzureContainerName string
|
AzureContainerName string
|
||||||
|
|
Loading…
Reference in New Issue