Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/external/customtemplates/azure_blob.go
2070 views
1
package customtemplates
2
3
import (
4
"bytes"
5
"context"
6
"os"
7
"path/filepath"
8
"strings"
9
10
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
11
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
12
"github.com/projectdiscovery/gologger"
13
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
14
"github.com/projectdiscovery/nuclei/v3/pkg/types"
15
"github.com/projectdiscovery/utils/errkit"
16
)
17
18
var _ Provider = &customTemplateAzureBlob{}
19
20
type customTemplateAzureBlob struct {
21
azureBlobClient *azblob.Client
22
containerName string
23
}
24
25
// NewAzureProviders creates a new Azure Blob Storage provider for downloading custom templates
26
func NewAzureProviders(options *types.Options) ([]*customTemplateAzureBlob, error) {
27
providers := []*customTemplateAzureBlob{}
28
if options.AzureContainerName != "" && !options.AzureTemplateDisableDownload {
29
// Establish a connection to Azure and build a client object with which to download templates from Azure Blob Storage
30
azClient, err := getAzureBlobClient(options.AzureTenantID, options.AzureClientID, options.AzureClientSecret, options.AzureServiceURL)
31
if err != nil {
32
errx := errkit.FromError(err)
33
errx.Msgf("Error establishing Azure Blob client for %s", options.AzureContainerName)
34
return nil, errx
35
}
36
37
// Create a new Azure Blob Storage container object
38
azTemplateContainer := &customTemplateAzureBlob{
39
azureBlobClient: azClient,
40
containerName: options.AzureContainerName,
41
}
42
43
// Add the Azure Blob Storage container object to the list of custom templates
44
providers = append(providers, azTemplateContainer)
45
}
46
return providers, nil
47
}
48
49
func getAzureBlobClient(tenantID string, clientID string, clientSecret string, serviceURL string) (*azblob.Client, error) {
50
// Create an Azure credential using the provided credentials
51
credentials, err := azidentity.NewClientSecretCredential(tenantID, clientID, clientSecret, nil)
52
if err != nil {
53
gologger.Error().Msgf("Invalid Azure credentials: %v", err)
54
return nil, err
55
}
56
57
// Create a client to manage Azure Blob Storage
58
client, err := azblob.NewClient(serviceURL, credentials, nil)
59
if err != nil {
60
gologger.Error().Msgf("Error creating Azure Blob client: %v", err)
61
return nil, err
62
}
63
64
return client, nil
65
}
66
67
func (bk *customTemplateAzureBlob) Download(ctx context.Context) {
68
// Set an incrementer for the number of templates downloaded
69
var templatesDownloaded = 0
70
71
// Define the local path to which the templates will be downloaded
72
downloadPath := filepath.Join(config.DefaultConfig.CustomAzureTemplatesDirectory, bk.containerName)
73
74
// Get the list of all templates from the container
75
pager := bk.azureBlobClient.NewListBlobsFlatPager(bk.containerName, &azblob.ListBlobsFlatOptions{
76
// Don't include previous versions of the templates if versioning is enabled on the container
77
Include: azblob.ListBlobsInclude{Snapshots: false, Versions: false},
78
})
79
80
// Loop through the list of blobs in the container and determine if they should be added to the list of templates
81
// to be returned, and subsequently downloaded
82
for pager.More() {
83
resp, err := pager.NextPage(context.TODO())
84
if err != nil {
85
gologger.Error().Msgf("Error listing templates in Azure Blob container: %v", err)
86
return
87
}
88
89
for _, blob := range resp.Segment.BlobItems {
90
// If the blob is a .yaml download the file to the local filesystem
91
if strings.HasSuffix(*blob.Name, ".yaml") {
92
// Download the template to the local filesystem at the downloadPath
93
err := downloadTemplate(bk.azureBlobClient, bk.containerName, *blob.Name, filepath.Join(downloadPath, *blob.Name), ctx)
94
if err != nil {
95
gologger.Error().Msgf("Error downloading template: %v", err)
96
} else {
97
// Increment the number of templates downloaded
98
templatesDownloaded++
99
}
100
}
101
}
102
}
103
104
// Log the number of templates downloaded
105
gologger.Info().Msgf("Downloaded %d templates from Azure Blob Storage container '%s' to: %s", templatesDownloaded, bk.containerName, downloadPath)
106
}
107
108
// Update updates the templates from the Azure Blob Storage container to the local filesystem. This is effectively a
109
// wrapper of the Download function which downloads of all templates from the container and doesn't manage a
110
// differential update.
111
func (bk *customTemplateAzureBlob) Update(ctx context.Context) {
112
// Treat the update as a download of all templates from the container
113
bk.Download(ctx)
114
}
115
116
// downloadTemplate downloads a template from the Azure Blob Storage container to the local filesystem with the provided
117
// blob path and outputPath.
118
func downloadTemplate(client *azblob.Client, containerName string, path string, outputPath string, ctx context.Context) error {
119
// Download the blob as a byte stream
120
get, err := client.DownloadStream(ctx, containerName, path, nil)
121
if err != nil {
122
gologger.Error().Msgf("Error downloading template: %v", err)
123
return err
124
}
125
126
downloadedData := bytes.Buffer{}
127
retryReader := get.NewRetryReader(ctx, &azblob.RetryReaderOptions{})
128
_, err = downloadedData.ReadFrom(retryReader)
129
if err != nil {
130
gologger.Error().Msgf("Error reading template: %v", err)
131
return err
132
}
133
134
err = retryReader.Close()
135
if err != nil {
136
gologger.Error().Msgf("Error closing template filestream: %v", err)
137
return err
138
}
139
140
// Ensure the directory exists
141
err = os.MkdirAll(filepath.Dir(outputPath), 0755)
142
if err != nil {
143
gologger.Error().Msgf("Error creating directory: %v", err)
144
return err
145
}
146
147
// Write the downloaded template to the local filesystem at the outputPath with the filename of the blob name
148
err = os.WriteFile(outputPath, downloadedData.Bytes(), 0644)
149
150
return err
151
}
152
153