Path: blob/dev/pkg/external/customtemplates/azure_blob.go
2070 views
package customtemplates12import (3"bytes"4"context"5"os"6"path/filepath"7"strings"89"github.com/Azure/azure-sdk-for-go/sdk/azidentity"10"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"11"github.com/projectdiscovery/gologger"12"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"13"github.com/projectdiscovery/nuclei/v3/pkg/types"14"github.com/projectdiscovery/utils/errkit"15)1617var _ Provider = &customTemplateAzureBlob{}1819type customTemplateAzureBlob struct {20azureBlobClient *azblob.Client21containerName string22}2324// NewAzureProviders creates a new Azure Blob Storage provider for downloading custom templates25func NewAzureProviders(options *types.Options) ([]*customTemplateAzureBlob, error) {26providers := []*customTemplateAzureBlob{}27if options.AzureContainerName != "" && !options.AzureTemplateDisableDownload {28// Establish a connection to Azure and build a client object with which to download templates from Azure Blob Storage29azClient, err := getAzureBlobClient(options.AzureTenantID, options.AzureClientID, options.AzureClientSecret, options.AzureServiceURL)30if err != nil {31errx := errkit.FromError(err)32errx.Msgf("Error establishing Azure Blob client for %s", options.AzureContainerName)33return nil, errx34}3536// Create a new Azure Blob Storage container object37azTemplateContainer := &customTemplateAzureBlob{38azureBlobClient: azClient,39containerName: options.AzureContainerName,40}4142// Add the Azure Blob Storage container object to the list of custom templates43providers = append(providers, azTemplateContainer)44}45return providers, nil46}4748func getAzureBlobClient(tenantID string, clientID string, clientSecret string, serviceURL string) (*azblob.Client, error) {49// Create an Azure credential using the provided credentials50credentials, err := azidentity.NewClientSecretCredential(tenantID, clientID, clientSecret, nil)51if err != nil {52gologger.Error().Msgf("Invalid Azure credentials: %v", err)53return nil, err54}5556// Create a client to manage Azure Blob Storage57client, err := azblob.NewClient(serviceURL, credentials, nil)58if err != nil {59gologger.Error().Msgf("Error creating Azure Blob client: %v", err)60return nil, err61}6263return client, nil64}6566func (bk *customTemplateAzureBlob) Download(ctx context.Context) {67// Set an incrementer for the number of templates downloaded68var templatesDownloaded = 06970// Define the local path to which the templates will be downloaded71downloadPath := filepath.Join(config.DefaultConfig.CustomAzureTemplatesDirectory, bk.containerName)7273// Get the list of all templates from the container74pager := bk.azureBlobClient.NewListBlobsFlatPager(bk.containerName, &azblob.ListBlobsFlatOptions{75// Don't include previous versions of the templates if versioning is enabled on the container76Include: azblob.ListBlobsInclude{Snapshots: false, Versions: false},77})7879// Loop through the list of blobs in the container and determine if they should be added to the list of templates80// to be returned, and subsequently downloaded81for pager.More() {82resp, err := pager.NextPage(context.TODO())83if err != nil {84gologger.Error().Msgf("Error listing templates in Azure Blob container: %v", err)85return86}8788for _, blob := range resp.Segment.BlobItems {89// If the blob is a .yaml download the file to the local filesystem90if strings.HasSuffix(*blob.Name, ".yaml") {91// Download the template to the local filesystem at the downloadPath92err := downloadTemplate(bk.azureBlobClient, bk.containerName, *blob.Name, filepath.Join(downloadPath, *blob.Name), ctx)93if err != nil {94gologger.Error().Msgf("Error downloading template: %v", err)95} else {96// Increment the number of templates downloaded97templatesDownloaded++98}99}100}101}102103// Log the number of templates downloaded104gologger.Info().Msgf("Downloaded %d templates from Azure Blob Storage container '%s' to: %s", templatesDownloaded, bk.containerName, downloadPath)105}106107// Update updates the templates from the Azure Blob Storage container to the local filesystem. This is effectively a108// wrapper of the Download function which downloads of all templates from the container and doesn't manage a109// differential update.110func (bk *customTemplateAzureBlob) Update(ctx context.Context) {111// Treat the update as a download of all templates from the container112bk.Download(ctx)113}114115// downloadTemplate downloads a template from the Azure Blob Storage container to the local filesystem with the provided116// blob path and outputPath.117func downloadTemplate(client *azblob.Client, containerName string, path string, outputPath string, ctx context.Context) error {118// Download the blob as a byte stream119get, err := client.DownloadStream(ctx, containerName, path, nil)120if err != nil {121gologger.Error().Msgf("Error downloading template: %v", err)122return err123}124125downloadedData := bytes.Buffer{}126retryReader := get.NewRetryReader(ctx, &azblob.RetryReaderOptions{})127_, err = downloadedData.ReadFrom(retryReader)128if err != nil {129gologger.Error().Msgf("Error reading template: %v", err)130return err131}132133err = retryReader.Close()134if err != nil {135gologger.Error().Msgf("Error closing template filestream: %v", err)136return err137}138139// Ensure the directory exists140err = os.MkdirAll(filepath.Dir(outputPath), 0755)141if err != nil {142gologger.Error().Msgf("Error creating directory: %v", err)143return err144}145146// Write the downloaded template to the local filesystem at the outputPath with the filename of the blob name147err = os.WriteFile(outputPath, downloadedData.Bytes(), 0644)148149return err150}151152153