mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2025-12-17 23:55:27 +00:00
189 lines
4.6 KiB
Go
189 lines
4.6 KiB
Go
package aws
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"path"
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/aws"
|
|
"github.com/aws/aws-sdk-go-v2/config"
|
|
"github.com/aws/aws-sdk-go-v2/credentials"
|
|
"github.com/aws/aws-sdk-go-v2/feature/s3/manager"
|
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
|
)
|
|
|
|
// Catalog manages the AWS S3 template catalog
|
|
type Catalog struct {
|
|
svc client
|
|
}
|
|
|
|
// client interface abstracts S3 connections
|
|
type client interface {
|
|
getAllKeys() ([]string, error)
|
|
downloadKey(name string) (io.ReadCloser, error)
|
|
setBucket(bucket string)
|
|
}
|
|
|
|
type s3svc struct {
|
|
client *s3.Client
|
|
bucket string
|
|
}
|
|
|
|
// NewCatalog creates a new AWS Catalog object given a required S3 bucket name and optional configurations. If
|
|
// no configurations to set AWS keys are provided then environment variables will be used to obtain AWS credentials.
|
|
func NewCatalog(bucket string, configurations ...func(*Catalog) error) (Catalog, error) {
|
|
var c Catalog
|
|
|
|
for _, configuration := range configurations {
|
|
err := configuration(&c)
|
|
if err != nil {
|
|
return c, err
|
|
}
|
|
}
|
|
|
|
if c.svc == nil {
|
|
cfg, err := config.LoadDefaultConfig(context.TODO())
|
|
if err != nil {
|
|
return c, err
|
|
}
|
|
|
|
c.svc = &s3svc{
|
|
client: s3.NewFromConfig(cfg),
|
|
}
|
|
}
|
|
c.svc.setBucket(bucket)
|
|
|
|
return c, nil
|
|
}
|
|
|
|
// WithAWSKeys enables explicitly setting the AWS access key, secret key and region
|
|
func WithAWSKeys(accessKey, secretKey, region string) func(*Catalog) error {
|
|
return func(c *Catalog) error {
|
|
cfg, err := config.LoadDefaultConfig(context.TODO(),
|
|
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKey, secretKey, "")),
|
|
config.WithRegion(region))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
c.svc = &s3svc{
|
|
client: s3.NewFromConfig(cfg),
|
|
bucket: "",
|
|
}
|
|
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// OpenFile downloads a file from S3 and returns the contents as an io.ReadCloser
|
|
func (c Catalog) OpenFile(filename string) (io.ReadCloser, error) {
|
|
if filename == "" {
|
|
return nil, errors.New("empty filename")
|
|
}
|
|
|
|
return c.svc.downloadKey(filename)
|
|
}
|
|
|
|
// GetTemplatePath looks for a target string performing a simple substring check
|
|
// against all S3 keys. If the input includes a wildcard (*) it is removed.
|
|
func (c Catalog) GetTemplatePath(target string) ([]string, error) {
|
|
target = strings.ReplaceAll(target, "*", "")
|
|
|
|
keys, err := c.svc.getAllKeys()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var matches []string
|
|
for _, key := range keys {
|
|
if strings.Contains(key, target) {
|
|
matches = append(matches, key)
|
|
}
|
|
}
|
|
|
|
return matches, nil
|
|
}
|
|
|
|
// GetTemplatesPath returns all templates from S3
|
|
func (c Catalog) GetTemplatesPath(definitions []string) ([]string, map[string]error) {
|
|
keys, err := c.svc.getAllKeys()
|
|
if err != nil {
|
|
// necessary to implement the Catalog interface
|
|
return nil, map[string]error{"aws": err}
|
|
}
|
|
|
|
return keys, nil
|
|
}
|
|
|
|
// ResolvePath gets a full S3 key given the first param. If the second parameter is
|
|
// provided it tries to find paths relative to the second path.
|
|
func (c Catalog) ResolvePath(templateName, second string) (string, error) {
|
|
keys, err := c.svc.getAllKeys()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// if c second path is given, it's c folder and we join the two and check against keys
|
|
if second != "" {
|
|
// Note: Do not replace `path` with `filepath` since filepath is aware of Os path separator
|
|
// and we only see `/` in s3 paths changing it to filepath cause build fail and other errors
|
|
target := path.Join(path.Dir(second), templateName)
|
|
for _, key := range keys {
|
|
if key == target {
|
|
return key, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
// check if templateName is already an absolute path to c key
|
|
if slices.Contains(keys, templateName) {
|
|
return templateName, nil
|
|
}
|
|
|
|
return "", fmt.Errorf("no such path found: %s%s for keys: %v", second, templateName, keys)
|
|
}
|
|
|
|
func (s *s3svc) getAllKeys() ([]string, error) {
|
|
paginator := s3.NewListObjectsV2Paginator(s.client, &s3.ListObjectsV2Input{
|
|
Bucket: &s.bucket,
|
|
})
|
|
|
|
var keys []string
|
|
|
|
for paginator.HasMorePages() {
|
|
page, err := paginator.NextPage(context.TODO())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, obj := range page.Contents {
|
|
key := aws.ToString(obj.Key)
|
|
keys = append(keys, key)
|
|
}
|
|
}
|
|
|
|
return keys, nil
|
|
}
|
|
|
|
func (s *s3svc) downloadKey(name string) (io.ReadCloser, error) {
|
|
downloader := manager.NewDownloader(s.client)
|
|
buf := manager.NewWriteAtBuffer([]byte{})
|
|
_, err := downloader.Download(context.TODO(), buf, &s3.GetObjectInput{
|
|
Bucket: aws.String(s.bucket),
|
|
Key: aws.String(name),
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return io.NopCloser(bytes.NewReader(buf.Bytes())), nil
|
|
}
|
|
|
|
func (s *s3svc) setBucket(bucket string) {
|
|
s.bucket = bucket
|
|
}
|