85 lines
3.0 KiB
Go
85 lines
3.0 KiB
Go
package notification_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.zaynet.fr/DoGaMa/DoGaMa-serv/internal/notification"
|
|
"git.zaynet.fr/DoGaMa/DoGaMa-serv/internal/persistence/sqlite"
|
|
)
|
|
|
|
func TestChannelSecretsAreEncryptedAndWriteOnly(t *testing.T) {
|
|
ctx := context.Background()
|
|
db, err := sqlite.Open(ctx, filepath.Join(t.TempDir(), "dogama.db"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer db.Close()
|
|
service, err := notification.New(db, bytes.Repeat([]byte{7}, 32))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
channel, err := service.Upsert(ctx, "", notification.Input{Name: "ops", Type: "webhook", Enabled: true, Events: []string{"backup.failed"}, Config: map[string]string{"url": "https://example.com/hook", "signing_secret": "highly-sensitive"}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var encrypted []byte
|
|
if err := db.QueryRowContext(ctx, `SELECT encrypted_config FROM notification_channels WHERE id=?`, channel.ID).Scan(&encrypted); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(string(encrypted), "highly-sensitive") {
|
|
t.Fatal("secret stored in plaintext")
|
|
}
|
|
channels, err := service.List(ctx)
|
|
if err != nil || len(channels) != 1 || !channels[0].Configured {
|
|
t.Fatalf("channels=%#v err=%v", channels, err)
|
|
}
|
|
}
|
|
|
|
func TestDeliveryBlocksPrivateWebhookAndRetriesWithRedactedError(t *testing.T) {
|
|
ctx := context.Background()
|
|
db, err := sqlite.Open(ctx, filepath.Join(t.TempDir(), "dogama.db"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer db.Close()
|
|
service, _ := notification.New(db, bytes.Repeat([]byte{8}, 32))
|
|
channel, err := service.Upsert(ctx, "", notification.Input{Name: "unsafe", Type: "webhook", Enabled: true, Events: []string{"backup.failed"}, Config: map[string]string{"url": "https://127.0.0.1/hook", "signing_secret": "never-leak"}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := service.Queue(ctx, notification.Event{Type: "backup.failed", Title: "Backup failed", Message: "Operation failed", OperationID: "op-1"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := service.RunDue(ctx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var status, code string
|
|
var attempt int
|
|
if err := db.QueryRowContext(ctx, `SELECT status,attempt,last_error_code FROM notification_deliveries WHERE channel_id=?`, channel.ID).Scan(&status, &attempt, &code); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if status != "retrying" || attempt != 1 || code != "unsafe_destination" || strings.Contains(code, "never-leak") {
|
|
t.Fatalf("status=%s attempt=%d code=%q", status, attempt, code)
|
|
}
|
|
}
|
|
|
|
func TestRejectsMissingKeyAndInsecureURL(t *testing.T) {
|
|
if _, err := notification.New(nil, []byte("short")); err == nil {
|
|
t.Fatal("short key accepted")
|
|
}
|
|
ctx := context.Background()
|
|
db, err := sqlite.Open(ctx, filepath.Join(t.TempDir(), "dogama.db"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer db.Close()
|
|
service, _ := notification.New(db, bytes.Repeat([]byte{9}, 32))
|
|
if _, err := service.Upsert(ctx, "", notification.Input{Name: "bad", Type: "discord", Enabled: true, Config: map[string]string{"url": "http://example.com"}}); err == nil {
|
|
t.Fatal("insecure URL accepted")
|
|
}
|
|
}
|