Path: blob/dev/pkg/reporting/exporters/mongo/mongo.go
2866 views
package mongo12import (3"context"4"net/url"5"os"6"strings"7"sync"89"github.com/pkg/errors"10"github.com/projectdiscovery/gologger"11"github.com/projectdiscovery/nuclei/v3/pkg/output"12"go.mongodb.org/mongo-driver/mongo"1314mongooptions "go.mongodb.org/mongo-driver/mongo/options"15)1617type Exporter struct {18options *Options19mutex *sync.Mutex20rows []output.ResultEvent21collection *mongo.Collection22connection *mongo.Client23}2425// Options contains the configuration options for MongoDB exporter client26type Options struct {27// ConnectionString is the connection string to the MongoDB database28ConnectionString string `yaml:"connection-string"`29// CollectionName is the name of the MongoDB collection in which to store the results30CollectionName string `yaml:"collection-name"`31// OmitRaw excludes the Request and Response from the results (helps with filesize)32OmitRaw bool `yaml:"omit-raw"`33// BatchSize determines the number of results to be kept in memory before writing it to the database or 0 to34// persist all in memory and write all results at the end (default)35BatchSize int `yaml:"batch-size"`36}3738// New creates a new MongoDB exporter integration client based on options.39func New(options *Options) (*Exporter, error) {40exporter := &Exporter{41mutex: &sync.Mutex{},42options: options,43rows: []output.ResultEvent{},44}4546// If the environment variable for the connection string is set, then use that instead. This allows for easier47// management of sensitive items such as credentials48envConnectionString := os.Getenv("MONGO_CONNECTION_STRING")49if envConnectionString != "" {50options.ConnectionString = envConnectionString51gologger.Info().Msgf("Using connection string from environment variable MONGO_CONNECTION_STRING")52}5354// Create the connection to the database55clientOptions := mongooptions.Client().ApplyURI(options.ConnectionString)5657// Create a new client and connect to the MongoDB server58client, err := mongo.Connect(context.TODO(), clientOptions)59if err != nil {60gologger.Error().Msgf("Error creating MongoDB client: %s", err)61return nil, err62}6364// Ensure the connection is valid65err = client.Ping(context.Background(), nil)66if err != nil {67gologger.Error().Msgf("Error connecting to MongoDB: %s", err)68return nil, err69}7071// Get the database from the connection string to set the database and collection72parsed, err := url.Parse(options.ConnectionString)73if err != nil {74gologger.Error().Msgf("Error parsing connection string: %s", options.ConnectionString)75return nil, err76}7778databaseName := strings.TrimPrefix(parsed.Path, "/")7980if databaseName == "" {81return nil, errors.New("error getting database name from connection string")82}8384exporter.connection = client85exporter.collection = client.Database(databaseName).Collection(options.CollectionName)8687return exporter, nil88}8990// Export writes a result document to the configured MongoDB collection91// in the database configured by the connection string92func (exporter *Exporter) Export(event *output.ResultEvent) error {93exporter.mutex.Lock()94defer exporter.mutex.Unlock()9596if exporter.options.OmitRaw {97event.Request = ""98event.Response = ""99}100101// Add the row to the queue to be processed102exporter.rows = append(exporter.rows, *event)103104// If the batch size is greater than 0 and the number of rows has reached the batch, flush it to the database105if exporter.options.BatchSize > 0 && len(exporter.rows) >= exporter.options.BatchSize {106err := exporter.WriteRows()107if err != nil {108// The error is already logged, return it to bubble up to the caller109return err110}111}112113return nil114}115116// WriteRows writes all rows from the rows list to the MongoDB collection and removes them from the list117func (exporter *Exporter) WriteRows() error {118// Loop through the rows and write them, removing them as they're entered119for len(exporter.rows) > 0 {120data := exporter.rows[0]121122// Write the data to the database123_, err := exporter.collection.InsertOne(context.TODO(), data)124if err != nil {125gologger.Fatal().Msgf("Error inserting record into MongoDB collection: %s", err)126return err127}128129// Remove the item from the list130exporter.rows = exporter.rows[1:]131}132133return nil134}135136func (exporter *Exporter) Close() error {137exporter.mutex.Lock()138defer exporter.mutex.Unlock()139140// Write all pending rows141err := exporter.WriteRows()142if err != nil {143// The error is already logged, return it to bubble up to the caller144return err145}146147// Close the database connection148err = exporter.connection.Disconnect(context.TODO())149if err != nil {150gologger.Error().Msgf("Error disconnecting from MongoDB: %s", err)151return err152}153154return nil155}156157158