Harden security: SSRF guard on all outbound fetches, auth fixes

- Route image-validation, Matrix image-download, and wayback/archive.today
  fetches through safehttp so feed-controlled URLs can't reach loopback/
  RFC1918/cloud-metadata IPs (incl. via redirects).
- Cap feed body size with safehttp.LimitedBody (16 MiB) to prevent OOM.
- Fail closed if matrix.pickle_key is unset/<16 chars; drop the hardcoded
  "pete_pickle_key" default that silently weakened E2EE-at-rest.
- Gate !post behind a matrix.admins allowlist; empty = disabled (channel
  rooms are public, so empty must not mean anyone).
- Reject /\host open-redirect bypass in post-login safeNext.
- Allowlist http(s) schemes for the Matrix link href; escape JSON embedded
  in inline <script> (prefs/sources blobs).
This commit is contained in:
prosolis
2026-06-21 16:21:03 -07:00
parent cbbedd9894
commit d5d0656dba
11 changed files with 127 additions and 35 deletions

View File

@@ -50,6 +50,10 @@ type MatrixConfig struct {
DataDir string `toml:"data_dir"`
AdminRoom string `toml:"admin_room"`
Channels map[string]string `toml:"channels"`
// Admins is the allowlist of Matrix user IDs permitted to run in-room
// commands like !post. Empty means commands are disabled — the channel
// rooms are public, so an empty list must NOT mean "anyone".
Admins []string `toml:"admins"`
}
type PostingConfig struct {
@@ -130,6 +134,12 @@ func (c *Config) validate() error {
if c.Matrix.Password == "" {
return fmt.Errorf("matrix.password is required")
}
// pickle_key encrypts the E2EE crypto store (olm/megolm + cross-signing
// keys) at rest. Never fall back to a hardcoded default: a key baked into
// the source is no protection if crypto.db ever leaks.
if len(c.Matrix.PickleKey) < 16 {
return fmt.Errorf("matrix.pickle_key must be set to a strong secret (>=16 chars); it encrypts the E2EE crypto store at rest")
}
if len(c.Matrix.Channels) == 0 {
return fmt.Errorf("matrix.channels must have at least one entry")
}
@@ -184,9 +194,6 @@ func (c *Config) applyDefaults() {
if c.Matrix.DisplayName == "" {
c.Matrix.DisplayName = "Pete"
}
if c.Matrix.PickleKey == "" {
c.Matrix.PickleKey = "pete_pickle_key"
}
if c.Posting.MinIntervalSeconds == 0 {
c.Posting.MinIntervalSeconds = 300
}