From cadc8bd2ad19487b00ee36f539d8353788a98ae9 Mon Sep 17 00:00:00 2001 From: Christian Oudard Date: Tue, 23 Dec 2025 07:46:12 -0700 Subject: [PATCH] Add Brave browser support Add support for reading cookies from Brave browser, following the same pattern used for Chrome, Chromium, and Edge browsers. Changes: - Add browser/brave package with brave.go and find.go - Add braveRoots() functions for macOS, Linux, Windows, and stub platforms - Add FindBraveCookieStoreFiles() to internal/chrome/find - Register brave in browser/all/import.go Brave browser stores cookies in Chromium-compatible format at: - macOS: ~/Library/Application Support/BraveSoftware/Brave-Browser - Linux: ~/.config/BraveSoftware/Brave-Browser - Windows: %LocalAppData%\BraveSoftware\Brave-Browser\User Data Ported from browserutils/kooky#96, A PR written phantompunk. --- browser/all/import.go | 1 + browser/brave/brave.go | 47 ++++++++++++++++++++++++++++ browser/brave/find.go | 42 +++++++++++++++++++++++++ internal/chrome/find/find.go | 3 ++ internal/chrome/find/find_android.go | 4 +++ internal/chrome/find/find_darwin.go | 9 ++++++ internal/chrome/find/find_others.go | 4 +++ internal/chrome/find/find_unix.go | 19 +++++++++++ internal/chrome/find/find_windows.go | 8 +++++ 9 files changed, 137 insertions(+) create mode 100644 browser/brave/brave.go create mode 100644 browser/brave/find.go diff --git a/browser/all/import.go b/browser/all/import.go index c568b5f..2acfdc8 100644 --- a/browser/all/import.go +++ b/browser/all/import.go @@ -1,6 +1,7 @@ package all import ( + _ "github.com/j178/kooky/browser/brave" _ "github.com/j178/kooky/browser/browsh" _ "github.com/j178/kooky/browser/chrome" _ "github.com/j178/kooky/browser/chromium" diff --git a/browser/brave/brave.go b/browser/brave/brave.go new file mode 100644 index 0000000..b070d53 --- /dev/null +++ b/browser/brave/brave.go @@ -0,0 +1,47 @@ +package brave + +import ( + "net/http" + + "github.com/j178/kooky" + "github.com/j178/kooky/internal/chrome" + "github.com/j178/kooky/internal/cookies" +) + +func ReadCookies(filename string, filters ...kooky.Filter) ([]*kooky.Cookie, error) { + s, err := cookieStore(filename, filters...) + if err != nil { + return nil, err + } + defer s.Close() + + return s.ReadCookies(filters...) +} + +// CookieJar returns an initiated http.CookieJar based on the cookies stored by +// the Brave browser. Set cookies are memory stored and do not modify any +// browser files. +func CookieJar(filename string, filters ...kooky.Filter) (http.CookieJar, error) { + j, err := cookieStore(filename, filters...) + if err != nil { + return nil, err + } + defer j.Close() + if err := j.InitJar(); err != nil { + return nil, err + } + return j, nil +} + +// CookieStore has to be closed with CookieStore.Close() after use. +func CookieStore(filename string, filters ...kooky.Filter) (kooky.CookieStore, error) { + return cookieStore(filename, filters...) +} + +func cookieStore(filename string, filters ...kooky.Filter) (*cookies.CookieJar, error) { + s := &chrome.CookieStore{} + s.FileNameStr = filename + s.BrowserStr = `brave` + + return &cookies.CookieJar{CookieStore: s}, nil +} diff --git a/browser/brave/find.go b/browser/brave/find.go new file mode 100644 index 0000000..0848a06 --- /dev/null +++ b/browser/brave/find.go @@ -0,0 +1,42 @@ +package brave + +import ( + "github.com/j178/kooky" + "github.com/j178/kooky/internal/chrome" + "github.com/j178/kooky/internal/chrome/find" + "github.com/j178/kooky/internal/cookies" +) + +type braveFinder struct{} + +var _ kooky.CookieStoreFinder = (*braveFinder)(nil) + +func init() { + kooky.RegisterFinder(`brave`, &braveFinder{}) +} + +func (f *braveFinder) FindCookieStores() ([]kooky.CookieStore, error) { + files, err := find.FindBraveCookieStoreFiles() + if err != nil { + return nil, err + } + + var ret []kooky.CookieStore + for _, file := range files { + ret = append( + ret, + &cookies.CookieJar{ + CookieStore: &chrome.CookieStore{ + DefaultCookieStore: cookies.DefaultCookieStore{ + BrowserStr: file.Browser, + ProfileStr: file.Profile, + OSStr: file.OS, + IsDefaultProfileBool: file.IsDefaultProfile, + FileNameStr: file.Path, + }, + }, + }, + ) + } + return ret, nil +} diff --git a/internal/chrome/find/find.go b/internal/chrome/find/find.go index a623bca..27b8991 100644 --- a/internal/chrome/find/find.go +++ b/internal/chrome/find/find.go @@ -27,6 +27,9 @@ func FindChromiumCookieStoreFiles() ([]*chromeCookieStoreFile, error) { func FindEdgeChookieStoreFiles() ([]*chromeCookieStoreFile, error) { return FindCookieStoreFiles(edgeRoots, `edge`) } +func FindBraveCookieStoreFiles() ([]*chromeCookieStoreFile, error) { + return FindCookieStoreFiles(braveRoots, `brave`) +} func FindCookieStoreFiles(rootsFunc func() ([]string, error), browserName string) ([]*chromeCookieStoreFile, error) { if rootsFunc == nil { diff --git a/internal/chrome/find/find_android.go b/internal/chrome/find/find_android.go index 0aa898a..f192ca6 100644 --- a/internal/chrome/find/find_android.go +++ b/internal/chrome/find/find_android.go @@ -19,3 +19,7 @@ func chromiumRoots() ([]string, error) { func edgeRoots() ([]string, error) { return nil, errors.New(`not implemented`) } + +func braveRoots() ([]string, error) { + return nil, errors.New(`not implemented`) +} diff --git a/internal/chrome/find/find_darwin.go b/internal/chrome/find/find_darwin.go index 04cd851..fe0e159 100644 --- a/internal/chrome/find/find_darwin.go +++ b/internal/chrome/find/find_darwin.go @@ -42,3 +42,12 @@ func edgeRoots() ([]string, error) { } return []string{filepath.Join(cfgDir, `Microsoft Edge`)}, nil } + +func braveRoots() ([]string, error) { + // "$HOME/Library/Application Support" + cfgDir, err := os.UserConfigDir() + if err != nil { + return nil, err + } + return []string{filepath.Join(cfgDir, `BraveSoftware`, `Brave-Browser`)}, nil +} diff --git a/internal/chrome/find/find_others.go b/internal/chrome/find/find_others.go index ba9febd..7c0a798 100644 --- a/internal/chrome/find/find_others.go +++ b/internal/chrome/find/find_others.go @@ -17,3 +17,7 @@ func chromiumRoots() ([]string, error) { func edgeRoots() ([]string, error) { return nil, errNotImplemented } + +func braveRoots() ([]string, error) { + return nil, errNotImplemented +} diff --git a/internal/chrome/find/find_unix.go b/internal/chrome/find/find_unix.go index a05c839..eda60df 100644 --- a/internal/chrome/find/find_unix.go +++ b/internal/chrome/find/find_unix.go @@ -61,3 +61,22 @@ func chromiumRoots() ([]string, error) { func edgeRoots() ([]string, error) { return nil, errors.New(`not implemented`) } + +func braveRoots() ([]string, error) { + // "${XDG_CONFIG_HOME:-$HOME/.config}" + var dotConfigs, ret []string + // fallback + if home, err := os.UserHomeDir(); err == nil { + dotConfigs = append(dotConfigs, filepath.Join(home, `.config`)) + } + if dir, ok := os.LookupEnv(`XDG_CONFIG_HOME`); ok { + dotConfigs = append(dotConfigs, dir) + } + for _, dotConfig := range dotConfigs { + ret = append( + ret, + filepath.Join(dotConfig, `BraveSoftware`, `Brave-Browser`), + ) + } + return ret, nil +} diff --git a/internal/chrome/find/find_windows.go b/internal/chrome/find/find_windows.go index e403118..7b3c3b5 100644 --- a/internal/chrome/find/find_windows.go +++ b/internal/chrome/find/find_windows.go @@ -44,3 +44,11 @@ func edgeRoots() ([]string, error) { return ret, nil } + +func braveRoots() ([]string, error) { + cfgDir := os.Getenv(`LocalAppData`) + if len(cfgDir) == 0 { + return nil, errors.New(`%LocalAppData% is empty`) + } + return []string{filepath.Join(cfgDir, `BraveSoftware`, `Brave-Browser`, `User Data`)}, nil +}