Path: blob/dev/pkg/reporting/exporters/mongo/mongo.go
2070 views
package mongo12import (3"context"4"github.com/pkg/errors"5"github.com/projectdiscovery/gologger"6"github.com/projectdiscovery/nuclei/v3/pkg/output"7"go.mongodb.org/mongo-driver/mongo"8"net/url"9"os"10"strings"11"sync"1213mongooptions "go.mongodb.org/mongo-driver/mongo/options"14)1516type Exporter struct {17options *Options18mutex *sync.Mutex19rows []output.ResultEvent20collection *mongo.Collection21connection *mongo.Client22}2324// Options contains the configuration options for MongoDB exporter client25type Options struct {26// ConnectionString is the connection string to the MongoDB database27ConnectionString string `yaml:"connection-string"`28// CollectionName is the name of the MongoDB collection in which to store the results29CollectionName string `yaml:"collection-name"`30// OmitRaw excludes the Request and Response from the results (helps with filesize)31OmitRaw bool `yaml:"omit-raw"`32// BatchSize determines the number of results to be kept in memory before writing it to the database or 0 to33// persist all in memory and write all results at the end (default)34BatchSize int `yaml:"batch-size"`35}3637// New creates a new MongoDB exporter integration client based on options.38func New(options *Options) (*Exporter, error) {39exporter := &Exporter{40mutex: &sync.Mutex{},41options: options,42rows: []output.ResultEvent{},43}4445// If the environment variable for the connection string is set, then use that instead. This allows for easier46// management of sensitive items such as credentials47envConnectionString := os.Getenv("MONGO_CONNECTION_STRING")48if envConnectionString != "" {49options.ConnectionString = envConnectionString50gologger.Info().Msgf("Using connection string from environment variable MONGO_CONNECTION_STRING")51}5253// Create the connection to the database54clientOptions := mongooptions.Client().ApplyURI(options.ConnectionString)5556// Create a new client and connect to the MongoDB server57client, err := mongo.Connect(context.TODO(), clientOptions)58if err != nil {59gologger.Error().Msgf("Error creating MongoDB client: %s", err)60return nil, err61}6263// Ensure the connection is valid64err = client.Ping(context.Background(), nil)65if err != nil {66gologger.Error().Msgf("Error connecting to MongoDB: %s", err)67return nil, err68}6970// Get the database from the connection string to set the database and collection71parsed, err := url.Parse(options.ConnectionString)72if err != nil {73gologger.Error().Msgf("Error parsing connection string: %s", options.ConnectionString)74return nil, err75}7677databaseName := strings.TrimPrefix(parsed.Path, "/")7879if databaseName == "" {80return nil, errors.New("error getting database name from connection string")81}8283exporter.connection = client84exporter.collection = client.Database(databaseName).Collection(options.CollectionName)8586return exporter, nil87}8889// Export writes a result document to the configured MongoDB collection90// in the database configured by the connection string91func (exporter *Exporter) Export(event *output.ResultEvent) error {92exporter.mutex.Lock()93defer exporter.mutex.Unlock()9495if exporter.options.OmitRaw {96event.Request = ""97event.Response = ""98}99100// Add the row to the queue to be processed101exporter.rows = append(exporter.rows, *event)102103// If the batch size is greater than 0 and the number of rows has reached the batch, flush it to the database104if exporter.options.BatchSize > 0 && len(exporter.rows) >= exporter.options.BatchSize {105err := exporter.WriteRows()106if err != nil {107// The error is already logged, return it to bubble up to the caller108return err109}110}111112return nil113}114115// WriteRows writes all rows from the rows list to the MongoDB collection and removes them from the list116func (exporter *Exporter) WriteRows() error {117// Loop through the rows and write them, removing them as they're entered118for len(exporter.rows) > 0 {119data := exporter.rows[0]120121// Write the data to the database122_, err := exporter.collection.InsertOne(context.TODO(), data)123if err != nil {124gologger.Fatal().Msgf("Error inserting record into MongoDB collection: %s", err)125return err126}127128// Remove the item from the list129exporter.rows = exporter.rows[1:]130}131132return nil133}134135func (exporter *Exporter) Close() error {136exporter.mutex.Lock()137defer exporter.mutex.Unlock()138139// Write all pending rows140err := exporter.WriteRows()141if err != nil {142// The error is already logged, return it to bubble up to the caller143return err144}145146// Close the database connection147err = exporter.connection.Disconnect(context.TODO())148if err != nil {149gologger.Error().Msgf("Error disconnecting from MongoDB: %s", err)150return err151}152153return nil154}155156157