Skip to content

Instantly share code, notes, and snippets.

@rnaveiras
Created December 5, 2025 14:26
Show Gist options
  • Select an option

  • Save rnaveiras/7284849ee1cd1ec6f3feddfdd7779ee7 to your computer and use it in GitHub Desktop.

Select an option

Save rnaveiras/7284849ee1cd1ec6f3feddfdd7779ee7 to your computer and use it in GitHub Desktop.
/*
Script to publish EmergingThreatPublished events to the events SNS topic.
This script is designed to manually trigger an EmergingThreatPublished event,
which will cause downstream subscribers (e.g., notification services) to process
the event as if the threat had just been published through the normal flow.
Required:
For non-local environments, you must have AWS credentials configured
(e.g., via AWS_PROFILE or SSO login).
Usage:
./publish_event -threat-id=<uuid> -user-id=<uuid> -env=<environment> [-dry-run]
Flags:
-env string
Required. Environment to run in (local, dev, pen, demo, prod)
-threat-id string
Required. The ID of the emerging threat to publish the event for
-user-id string
Required. The ID of the user who published the threat
-dry-run
Optional. Defaults to true. Set to false to actually publish the event
-y
Optional. Skip confirmation prompt
Examples:
# Dry run in dev environment
./publish_event -threat-id=abc123 -user-id=user456 -env=dev
# Actually publish in dev environment
./publish_event -threat-id=abc123 -user-id=user456 -env=dev -dry-run=false
# Publish in prod with confirmation skip
./publish_event -threat-id=abc123 -user-id=user456 -env=prod -dry-run=false -y
*/
package main
import (
"context"
"flag"
"fmt"
"time"
"google.golang.org/protobuf/types/known/timestamppb"
"riskledger.com/rl/pkg/config/common"
"riskledger.com/rl/pkg/pubsub"
pubsubmw "riskledger.com/rl/pkg/pubsub/middleware"
"riskledger.com/rl/pkg/pubsub/snssqs"
emerging_threatsv1 "riskledger.com/rl/proto/emerging_threats/v1"
"riskledger.com/rl/scripts/util"
)
// envConfig holds the SNS topic ARN and AWS settings for each environment.
type envConfig struct {
SNSTopicARN string
AWSEndpoint string
AWSRegion string
}
// getEnvConfig returns the SNS topic ARN for the given environment.
func getEnvConfig(env string) (*envConfig, error) {
configs := map[string]envConfig{
"local": {
SNSTopicARN: "arn:aws:sns:eu-west-1:000000000000:local-events",
AWSEndpoint: "http://localhost:4566",
AWSRegion: "eu-west-1",
},
"dev": {
SNSTopicARN: "arn:aws:sns:eu-west-1:141388701384:dev-events",
AWSEndpoint: "",
AWSRegion: "eu-west-1",
},
"pen": {
SNSTopicARN: "arn:aws:sns:eu-west-1:141388701384:pen-events",
AWSEndpoint: "",
AWSRegion: "eu-west-1",
},
"demo": {
SNSTopicARN: "arn:aws:sns:eu-west-1:141388701384:demo-events",
AWSEndpoint: "",
AWSRegion: "eu-west-1",
},
"prod": {
SNSTopicARN: "arn:aws:sns:eu-west-1:141388701384:prod-events",
AWSEndpoint: "",
AWSRegion: "eu-west-1",
},
}
config, exists := configs[env]
if !exists {
return nil, fmt.Errorf("invalid environment: %s. Must be one of: local, dev, pen, demo, prod", env)
}
return &config, nil
}
func main() {
var threatID string
var userID string
var environment string
var dryRun bool
var skipConfirmation bool
var publishedAtStr string
flag.StringVar(&threatID, "threat-id", "", "Required. The ID of the emerging threat")
flag.StringVar(&userID, "user-id", "", "Required. The ID of the user who published the threat")
flag.StringVar(&environment, "env", "", "Required. Environment (local, dev, pen, demo, prod)")
flag.BoolVar(&dryRun, "dry-run", true, "Set to false to actually publish the event")
flag.BoolVar(&skipConfirmation, "y", false, "Skip confirmation prompt")
flag.StringVar(&publishedAtStr, "published-at", "", "Optional. Published timestamp in RFC3339 format (defaults to now)")
flag.Parse()
log := util.Logger()
ctx := context.Background()
// Validate required flags
if threatID == "" {
log.Error("threat-id flag is required")
return
}
if userID == "" {
log.Error("user-id flag is required")
return
}
if environment == "" {
log.Error("env flag is required")
return
}
// Get environment config
envCfg, err := getEnvConfig(environment)
if err != nil {
log.Error("invalid environment", "error", err)
return
}
// Parse published_at timestamp
var publishedAt time.Time
if publishedAtStr != "" {
publishedAt, err = time.Parse(time.RFC3339, publishedAtStr)
if err != nil {
log.Error("invalid published-at format, expected RFC3339",
"error", err,
"value", publishedAtStr,
)
return
}
} else {
publishedAt = time.Now()
}
log.Info("preparing to publish EmergingThreatPublished event",
"threat_id", threatID,
"user_id", userID,
"published_at", publishedAt.Format(time.RFC3339),
"environment", environment,
"sns_topic_arn", envCfg.SNSTopicARN,
"dry_run", dryRun,
)
if !dryRun && !skipConfirmation {
if !util.PromptForConfirmation("This will publish an EmergingThreatPublished event to SNS. Continue?") {
log.Info("Operation cancelled by user")
return
}
}
if dryRun {
log.Info("dry run: would publish EmergingThreatPublished event",
"threat_id", threatID,
"user_id", userID,
"published_at", publishedAt.Format(time.RFC3339),
"sns_topic_arn", envCfg.SNSTopicARN,
)
return
}
// Load AWS config
awsConfig := common.AWS{
Region: envCfg.AWSRegion,
AWSEndpoint: envCfg.AWSEndpoint,
}
awsV2Config, err := awsConfig.LoadConfig(ctx)
if err != nil {
log.Error("failed to load AWS config", "error", err)
return
}
// create the SNS publisher using the snssqs driver
pubmw := pubsubmw.PublishDefaults(log, "emerging threats publish event script")
eventsPub := pubsub.NewPub(snssqs.NewSender(awsV2Config, envCfg.SNSTopicARN), log, pubmw)
defer eventsPub.Stop()
// create the event
event := &emerging_threatsv1.EmergingThreatPublishedEvent{
Id: threatID,
PublishedBy: userID,
PublishedAt: timestamppb.New(publishedAt),
}
// publish the event
err = eventsPub.Publish(ctx, "EmergingThreatPublished", event)
if err != nil {
log.Error("failed to publish event", "error", err)
return
}
log.Info("EmergingThreatPublished event published successfully",
"threat_id", threatID,
"user_id", userID,
"published_at", publishedAt.Format(time.RFC3339),
"environment", environment,
"sns_topic_arn", envCfg.SNSTopicARN,
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment