security: reject '?' and '#' in StorePath for whatsmeow session store (#177)

PR #141 (closes #59) added a guard against SQLite URI injection for the
message store at internal/store/db.go, but the whatsmeow session store at
internal/wa/client.go:49 uses the same fmt.Sprintf("file:%s?...", path)
pattern without that guard. A --store / WACLI_STORE_DIR value containing
'?' injects arbitrary SQLite URI parameters (mode, cache, _journal_mode,
...) into the session DSN and can silently neutralize foreign keys or
change isolation.

Apply the same ContainsAny(path, "?#") guard in wa.New so both backing
stores reject the injection path identically.

Closes #177.
This commit is contained in:
JunghwanNA 2026-04-17 17:10:54 +09:00 committed by Peter Steinberger
parent 4eaad5f85f
commit ea4ca18438
2 changed files with 31 additions and 0 deletions

View File

@ -34,6 +34,10 @@ func New(opts Options) (*Client, error) {
if strings.TrimSpace(opts.StorePath) == "" {
return nil, fmt.Errorf("StorePath is required")
}
// Reject paths that could inject SQLite URI parameters (#177, mirror of #59).
if strings.ContainsAny(opts.StorePath, "?#") {
return nil, fmt.Errorf("StorePath must not contain '?' or '#'")
}
c := &Client{opts: opts}
if err := c.init(); err != nil {
return nil, err

View File

@ -0,0 +1,27 @@
package wa
import (
"strings"
"testing"
)
// TestNewRejectsURISpecialCharsInStorePath verifies that a StorePath
// containing '?' or '#' is rejected before it can reach the SQLite URI
// parser inside whatsmeow's sqlstore (#177, mirror of #59).
func TestNewRejectsURISpecialCharsInStorePath(t *testing.T) {
cases := []struct{ name, path string }{
{"question mark", "/tmp/session.db?mode=memory"},
{"hash", "/tmp/session.db#frag"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
_, err := New(Options{StorePath: tc.path})
if err == nil {
t.Fatalf("expected error for path %q, got nil", tc.path)
}
if !strings.Contains(err.Error(), "'?'") && !strings.Contains(err.Error(), "'#'") {
t.Fatalf("expected guard error mentioning '?' or '#', got: %v", err)
}
})
}
}