kooky

package module
v0.0.0-...-dc41c55 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 20, 2021 License: MIT Imports: 6 Imported by: 0

README

kooky

PkgGoDev Go Report Card Lines of code No Maintenance Intended PRs Welcome MIT license

Reaching into browser-specific, vaguely documented, possibly concurrently modified cookie stores to pilfer cookies is a bad idea. Since you've arrived here, you're almost certainly going to do it anyway. Me too. And if we're going to do the Wrong Thing, at least let's try to Do it Right.

Package kooky contains routines to reach into cookie stores for Chrome, Firefox, Safari, ... and retrieve the cookies.

It aspires to be pure Go (I spent quite a while making go-sqlite/sqlite3 work for it).

It also aspires to work for all major browsers, on all three major platforms.

Status

Basic functionality works on Windows, MacOS and Linux. Some functions might not yet be implemented on some platforms. The API is currently not expected to be at all stable.

PRs more than welcome.

TODOs

  • Set up CI
  • Make it work on Windows. (Look at this and this to learn how to decrypt.)
  • Handle rows in Chrome's cookie DB with other than 14 columns (?)

Example usage

package main

import (
	"fmt"

	"gitee.com/wengo/kooky"
	_ "gitee.com/wengo/kooky/allbrowsers" // register cookie store finders!
)

func main() {
	// uses registered finders to find cookie store files in default locations
	// applies the passed filters "Valid", "DomainHasSuffix()" and "Name()" in order to the cookies
	cookies := kooky.ReadCookies(kooky.Valid, kooky.DomainHasSuffix(`google.com`), kooky.Name(`NID`))

	for _, cookie := range cookies {
		fmt.Println(cookie.Domain, cookie.Name, cookie.Value)
	}
 }
Chrome on macOS
package main

import (
	"fmt"
	"log"
	"os"

	"gitee.com/wengo/kooky/chrome"
)

func main() {
	dir, _ := os.UserConfigDir() // "/<USER>/Library/Application Support/"
	cookiesFile := dir + "/Google/Chrome/Default/Cookies"
	cookies, err := chrome.ReadCookies(cookiesFile)
	if err != nil {
		log.Fatal(err)
	}
	for _, cookie := range cookies {
		fmt.Println(cookie)
	}
}
Safari
package main

import (
	"fmt"
	"log"
	"os"

	"gitee.com/wengo/kooky/safari"
)

func main() {
	dir, _ := os.UserHomeDir()
	cookiesFile := dir + "/Library/Cookies/Cookies.binarycookies"
	cookies, err := safari.ReadCookies(cookiesFile)
	if err != nil {
		log.Fatal(err)
	}
	for _, cookie := range cookies {
		fmt.Println(cookie)
	}
}

Thanks/references

Documentation

Overview

Package kooky contains routines to reach into cookie stores for various browsers and retrieve the cookies.

Example (ChromeSimpleMacOS)
package main

import (
	"fmt"
	"os"

	"gitee.com/wengo/kooky/chrome"
)

// on macOS:
var cookieStorePath = "/Google/Chrome/Default/Cookies"

func main() {
	// construct file path for the sqlite database containing the cookies
	dir, _ := os.UserConfigDir() // on macOS: "/<USER>/Library/Application Support/"
	cookieStoreFile := dir + cookieStorePath

	// read the cookies from the file
	// decryption is handled automatically
	cookies, err := chrome.ReadCookies(cookieStoreFile)
	if err != nil {
		// TODO: handle the error
		return
	}

	for _, cookie := range cookies {
		fmt.Println(cookie)
	}
}
Output:

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func ExportCookies

func ExportCookies(w io.Writer, cookies []*Cookie)

ExportCookies() export "cookies" in the Netscape format.

curl, wget, ... use this format.

Example
package main

import (
	"os"

	"gitee.com/wengo/kooky"
)

var cookieFile = `cookies.txt`

func main() {
	var cookies = []*kooky.Cookie{{Domain: `.test.com`, Name: `test`, Value: `dGVzdA==`}}

	file, err := os.OpenFile(cookieFile, os.O_RDWR|os.O_CREATE, 0644)
	if err != nil {
		// TODO: handle error
		return
	}
	defer file.Close()

	kooky.ExportCookies(file, cookies)
}
Output:

func FilterCookie

func FilterCookie(cookie *Cookie, filters ...Filter) bool

FilterCookie() tells if a "cookie" passes all "filters".

func RegisterFinder

func RegisterFinder(browser string, finder CookieStoreFinder)

RegisterFinder() registers CookieStoreFinder enabling automatic finding of cookie stores with FindAllCookieStores() and ReadCookies().

RegisterFinder() is called by init() in the browser subdirectories.

Types

type Cookie struct {
	Domain   string
	Name     string
	Path     string
	Expires  time.Time
	Secure   bool
	HttpOnly bool
	Creation time.Time
	Value    string
}

Cookie is the struct returned by functions in this package. Similar to http.Cookie.

func FilterCookies

func FilterCookies(cookies []*Cookie, filters ...Filter) []*Cookie

FilterCookies() applies "filters" in order to the "cookies".

Example
package main

import (
	"gitee.com/wengo/kooky"
	_ "gitee.com/wengo/kooky/allbrowsers" // register cookiestore finders
)

var cookieName = `NID`

func main() {
	cookies := kooky.ReadCookies() // automatic read

	cookies = kooky.FilterCookies(
		cookies,
		kooky.Valid,                    // remove expired cookies
		kooky.DomainContains(`google`), // cookie domain has to contain "google"
		kooky.Name(cookieName),         // cookie name is "NID"
		kooky.Debug,                    // print cookies after applying previous filter
	)
}
Output:

func ReadCookies

func ReadCookies(filters ...Filter) []*Cookie

ReadCookies() uses registered cookiestore finders to read cookies. Erronous reads are skipped.

Register cookie store finders for all browsers like this:

import _ "gitee.com/wengo/kooky/allbrowsers"

Or only a specific browser:

import _ "gitee.com/wengo/kooky/chrome"
Example (All)
package main

import (
	"fmt"

	"gitee.com/wengo/kooky"
	_ "gitee.com/wengo/kooky/allbrowsers" // This registers all cookiestore finders!
	// _ "gitee.com/wengo/kooky/chrome" // load only the chrome cookiestore finder
)

func main() {
	// try to find cookie stores in default locations and
	// read the cookies from them.
	// decryption is handled automatically.
	cookies := kooky.ReadCookies()

	for _, cookie := range cookies {
		fmt.Println(cookie)
	}
}

var _ struct{} // ignore this - for correct working of the documentation tool
Output:

func (Cookie) HTTPCookie

func (c Cookie) HTTPCookie() http.Cookie

HTTPCookie() returns an http.Cookie equivalent to this Cookie.

type CookieStore

type CookieStore interface {
	ReadCookies(...Filter) ([]*Cookie, error)
	Browser() string
	Profile() string
	IsDefaultProfile() bool
	FilePath() string
	Close() error
}

CookieStore represents a file, directory, etc containing cookies.

Call CookieStore.Close() after using any of its methods.

func FindAllCookieStores

func FindAllCookieStores() []CookieStore

FindAllCookieStores() tries to find cookie stores at default locations.

FindAllCookieStores() requires registered CookieStoreFinders.

Register cookie store finders for all browsers like this:

import _ "gitee.com/wengo/kooky/allbrowsers"

Or only a specific browser:

import _ "gitee.com/wengo/kooky/chrome"
Example
package main

import (
	"fmt"

	"gitee.com/wengo/kooky"
	_ "gitee.com/wengo/kooky/allbrowsers" // register cookiestore finders
)

var filters = []kooky.Filter{
	kooky.Valid, // remove expired cookies
}

func main() {
	cookieStores := kooky.FindAllCookieStores()

	for _, store := range cookieStores {
		// CookieStore keeps files/databases open for repeated reads
		// close those when no longer needed
		defer store.Close()

		var filters = []kooky.Filter{
			kooky.Valid, // remove expired cookies
		}

		cookies, _ := store.ReadCookies(filters...)
		for _, cookie := range cookies {
			fmt.Printf(
				"%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
				store.Browser(),
				store.Profile(),
				store.FilePath(),
				cookie.Domain,
				cookie.Name,
				cookie.Value,
				cookie.Expires.Format(`2006.01.02 15:04:05`),
			)
		}
	}
}
Output:

type CookieStoreFinder

type CookieStoreFinder interface {
	FindCookieStores() ([]CookieStore, error)
}

CookieStoreFinder tries to find cookie stores at default locations.

type Filter

type Filter func(*Cookie) bool

Filter is used for filtering cokies in ReadCookies() functions.

A cookie passes the Filter if Filter returns true.

Example (Regex)
package main

import (
	"fmt"
	"regexp"

	"gitee.com/wengo/kooky"
)

// example regex matching base64 strings
var reBase64 = regexp.MustCompile(`^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$`)

func main() {
	var cookies = []*kooky.Cookie{{Name: `test`, Value: `dGVzdA==`}}

	cookies = kooky.FilterCookies(
		cookies,
		ValueRegexMatch(reBase64), // filter cookies with the regex filter
		// kooky.Debug,            // print cookies after applying the regex filter
	)

	for _, cookie := range cookies {
		fmt.Println(cookie.Value)
		break // only first element
	}

}

func ValueRegexMatch(re *regexp.Regexp) kooky.Filter {
	return func(cookie *kooky.Cookie) bool {
		return cookie != nil && re != nil && re.Match([]byte(cookie.Value))
	}
}
Output:

dGVzdA==
var Debug Filter = func(cookie *Cookie) bool {
	fmt.Printf("%+#v\n", cookie)
	return true
}

Debug prints the cookie.

Position Debug after the filter you want to test.

var Expired Filter = func(cookie *Cookie) bool {
	return cookie != nil && cookie.Expires.Before(time.Now())

}
var HTTPOnly Filter = func(cookie *Cookie) bool {
	return cookie != nil && cookie.HttpOnly
}
var Secure Filter = func(cookie *Cookie) bool {
	return cookie != nil && cookie.Secure
}
var Valid Filter = func(cookie *Cookie) bool {
	return cookie != nil && cookie.Expires.After(time.Now())

}

func CreationAfter

func CreationAfter(u time.Time) Filter

func CreationBefore

func CreationBefore(u time.Time) Filter

func Domain

func Domain(domain string) Filter

func DomainContains

func DomainContains(substr string) Filter

func DomainHasPrefix

func DomainHasPrefix(prefix string) Filter

func DomainHasSuffix

func DomainHasSuffix(suffix string) Filter

func ExpiresAfter

func ExpiresAfter(u time.Time) Filter

func ExpiresBefore

func ExpiresBefore(u time.Time) Filter

func Name

func Name(name string) Filter

func NameContains

func NameContains(substr string) Filter

func NameHasPrefix

func NameHasPrefix(prefix string) Filter

func NameHasSuffix

func NameHasSuffix(suffix string) Filter

func Path

func Path(path string) Filter

func PathContains

func PathContains(substr string) Filter

func PathDepth

func PathDepth(depth int) Filter

func PathHasPrefix

func PathHasPrefix(prefix string) Filter

func PathHasSuffix

func PathHasSuffix(suffix string) Filter

func Value

func Value(value string) Filter

func ValueContains

func ValueContains(substr string) Filter

func ValueHasPrefix

func ValueHasPrefix(prefix string) Filter

func ValueHasSuffix

func ValueHasSuffix(suffix string) Filter

func ValueLen

func ValueLen(length int) Filter

Directories

Path Synopsis
Browsh Browser
Browsh Browser
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL