Compare commits
1 Commits
f067c13d67
...
6f6ea33eaa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f6ea33eaa |
@@ -1624,10 +1624,6 @@ authenticity.
|
|||||||
termination.
|
termination.
|
||||||
- **CORS**: The server allows all origins by default (`Access-Control-Allow-Origin: *`).
|
- **CORS**: The server allows all origins by default (`Access-Control-Allow-Origin: *`).
|
||||||
Restrict this in production via reverse proxy configuration if needed.
|
Restrict this in production via reverse proxy configuration if needed.
|
||||||
- **Content-Security-Policy**: The server sets a strict CSP header on all
|
|
||||||
responses, restricting resource loading to same-origin and disabling
|
|
||||||
dangerous features (object embeds, framing, base tag injection). The
|
|
||||||
embedded SPA works without `'unsafe-inline'` for scripts or styles.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1118,6 +1118,28 @@ func (database *Database) PruneOldQueueEntries(
|
|||||||
return deleted, nil
|
return deleted, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PruneOrphanedMessages deletes messages that are no
|
||||||
|
// longer referenced by any client_queues row and returns
|
||||||
|
// the number of rows removed.
|
||||||
|
func (database *Database) PruneOrphanedMessages(
|
||||||
|
ctx context.Context,
|
||||||
|
) (int64, error) {
|
||||||
|
res, err := database.conn.ExecContext(ctx,
|
||||||
|
`DELETE FROM messages WHERE id NOT IN
|
||||||
|
(SELECT DISTINCT message_id
|
||||||
|
FROM client_queues)`,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf(
|
||||||
|
"prune orphaned messages: %w", err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
deleted, _ := res.RowsAffected()
|
||||||
|
|
||||||
|
return deleted, nil
|
||||||
|
}
|
||||||
|
|
||||||
// RotateChannelMessages enforces MAX_HISTORY per channel
|
// RotateChannelMessages enforces MAX_HISTORY per channel
|
||||||
// by deleting the oldest messages beyond the limit for
|
// by deleting the oldest messages beyond the limit for
|
||||||
// each msg_to target. Returns the total number of rows
|
// each msg_to target. Returns the total number of rows
|
||||||
|
|||||||
@@ -245,4 +245,18 @@ func (hdlr *Handlers) pruneQueuesAndMessages(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
orphaned, err := hdlr.params.Database.
|
||||||
|
PruneOrphanedMessages(ctx)
|
||||||
|
if err != nil {
|
||||||
|
hdlr.log.Error(
|
||||||
|
"orphan message cleanup failed",
|
||||||
|
"error", err,
|
||||||
|
)
|
||||||
|
} else if orphaned > 0 {
|
||||||
|
hdlr.log.Info(
|
||||||
|
"pruned orphaned messages",
|
||||||
|
"deleted", orphaned,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -180,36 +180,3 @@ func (mware *Middleware) MetricsAuth() func(http.Handler) http.Handler {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// cspPolicy is the Content-Security-Policy header value applied to all
|
|
||||||
// responses. The embedded SPA loads scripts and styles from same-origin
|
|
||||||
// files only (no inline scripts or inline style attributes), so a strict
|
|
||||||
// policy works without 'unsafe-inline'.
|
|
||||||
const cspPolicy = "default-src 'self'; " +
|
|
||||||
"script-src 'self'; " +
|
|
||||||
"style-src 'self'; " +
|
|
||||||
"connect-src 'self'; " +
|
|
||||||
"img-src 'self'; " +
|
|
||||||
"font-src 'self'; " +
|
|
||||||
"object-src 'none'; " +
|
|
||||||
"frame-ancestors 'none'; " +
|
|
||||||
"base-uri 'self'; " +
|
|
||||||
"form-action 'self'"
|
|
||||||
|
|
||||||
// CSP returns middleware that sets the Content-Security-Policy header on
|
|
||||||
// every response for defense-in-depth against XSS.
|
|
||||||
func (mware *Middleware) CSP() func(http.Handler) http.Handler {
|
|
||||||
return func(next http.Handler) http.Handler {
|
|
||||||
return http.HandlerFunc(
|
|
||||||
func(
|
|
||||||
writer http.ResponseWriter,
|
|
||||||
request *http.Request,
|
|
||||||
) {
|
|
||||||
writer.Header().Set(
|
|
||||||
"Content-Security-Policy",
|
|
||||||
cspPolicy,
|
|
||||||
)
|
|
||||||
next.ServeHTTP(writer, request)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ func (srv *Server) SetupRoutes() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
srv.router.Use(srv.mw.CORS())
|
srv.router.Use(srv.mw.CORS())
|
||||||
srv.router.Use(srv.mw.CSP())
|
|
||||||
srv.router.Use(middleware.Timeout(routeTimeout))
|
srv.router.Use(middleware.Timeout(routeTimeout))
|
||||||
|
|
||||||
if srv.sentryEnabled {
|
if srv.sentryEnabled {
|
||||||
|
|||||||
Reference in New Issue
Block a user