|  |  |  | @ -1,51 +1,105 @@ | 
			
		
	
		
			
				
					|  |  |  |  | package extension | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | import ( | 
			
		
	
		
			
				
					|  |  |  |  | 	"errors" | 
			
		
	
		
			
				
					|  |  |  |  | 	"os" | 
			
		
	
		
			
				
					|  |  |  |  | 	"path/filepath" | 
			
		
	
		
			
				
					|  |  |  |  | 	"strings" | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 	"github.com/tidwall/gjson" | 
			
		
	
		
			
				
					|  |  |  |  | 	"golang.org/x/text/language" | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 	"github.com/moond4rk/hackbrowserdata/item" | 
			
		
	
		
			
				
					|  |  |  |  | 	"github.com/moond4rk/hackbrowserdata/log" | 
			
		
	
		
			
				
					|  |  |  |  | 	"github.com/moond4rk/hackbrowserdata/utils/fileutil" | 
			
		
	
		
			
				
					|  |  |  |  | ) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | type ChromiumExtension []*extension | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | type extension struct { | 
			
		
	
		
			
				
					|  |  |  |  | 	ID          string | 
			
		
	
		
			
				
					|  |  |  |  | 	URL         string | 
			
		
	
		
			
				
					|  |  |  |  | 	Enabled     bool | 
			
		
	
		
			
				
					|  |  |  |  | 	Name        string | 
			
		
	
		
			
				
					|  |  |  |  | 	Description string | 
			
		
	
		
			
				
					|  |  |  |  | 	Version     string | 
			
		
	
		
			
				
					|  |  |  |  | 	HomepageURL string | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | const ( | 
			
		
	
		
			
				
					|  |  |  |  | 	manifest = "manifest.json" | 
			
		
	
		
			
				
					|  |  |  |  | ) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | func (c *ChromiumExtension) Parse(_ []byte) error { | 
			
		
	
		
			
				
					|  |  |  |  | 	files, err := fileutil.FilesInFolder(item.TempChromiumExtension, manifest) | 
			
		
	
		
			
				
					|  |  |  |  | 	profilePath, err := fileutil.ReadFile(item.TempChromiumExtension) | 
			
		
	
		
			
				
					|  |  |  |  | 	if err != nil { | 
			
		
	
		
			
				
					|  |  |  |  | 		return err | 
			
		
	
		
			
				
					|  |  |  |  | 	} | 
			
		
	
		
			
				
					|  |  |  |  | 	defer os.RemoveAll(item.TempChromiumExtension) | 
			
		
	
		
			
				
					|  |  |  |  | 	for _, f := range files { | 
			
		
	
		
			
				
					|  |  |  |  | 		content, err := fileutil.ReadFile(f) | 
			
		
	
		
			
				
					|  |  |  |  | 		if err != nil { | 
			
		
	
		
			
				
					|  |  |  |  | 			log.Error("Failed to read content: %s", err) | 
			
		
	
		
			
				
					|  |  |  |  | 			continue | 
			
		
	
		
			
				
					|  |  |  |  | 	_ = os.Remove(item.TempChromiumExtension) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 	var extensions *gjson.Result | 
			
		
	
		
			
				
					|  |  |  |  | 	if result, err := readChromiumExts(filepath.Join(profilePath, "Secure Preferences")); err == nil { | 
			
		
	
		
			
				
					|  |  |  |  | 		extensions = result | 
			
		
	
		
			
				
					|  |  |  |  | 	} else if result, err := readChromiumExts(filepath.Join(profilePath, "Preferences")); err == nil { | 
			
		
	
		
			
				
					|  |  |  |  | 		extensions = result | 
			
		
	
		
			
				
					|  |  |  |  | 	} else { | 
			
		
	
		
			
				
					|  |  |  |  | 		return err | 
			
		
	
		
			
				
					|  |  |  |  | 	} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 	extensions.ForEach(func(id, ext gjson.Result) bool { | 
			
		
	
		
			
				
					|  |  |  |  | 		location := ext.Get("location") | 
			
		
	
		
			
				
					|  |  |  |  | 		if !location.Exists() { | 
			
		
	
		
			
				
					|  |  |  |  | 			return true | 
			
		
	
		
			
				
					|  |  |  |  | 		} | 
			
		
	
		
			
				
					|  |  |  |  | 		b := gjson.Parse(content) | 
			
		
	
		
			
				
					|  |  |  |  | 		switch location.Int() { | 
			
		
	
		
			
				
					|  |  |  |  | 		case 5, 10: // https://source.chromium.org/chromium/chromium/src/+/main:extensions/common/mojom/manifest.mojom
 | 
			
		
	
		
			
				
					|  |  |  |  | 			return true | 
			
		
	
		
			
				
					|  |  |  |  | 		} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 		// https://source.chromium.org/chromium/chromium/src/+/main:extensions/browser/disable_reason.h
 | 
			
		
	
		
			
				
					|  |  |  |  | 		enabled := !ext.Get("disable_reasons").Exists() | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 		b := ext.Get("manifest") | 
			
		
	
		
			
				
					|  |  |  |  | 		if !b.Exists() { | 
			
		
	
		
			
				
					|  |  |  |  | 			*c = append(*c, &extension{ | 
			
		
	
		
			
				
					|  |  |  |  | 				ID:      id.String(), | 
			
		
	
		
			
				
					|  |  |  |  | 				Enabled: enabled, | 
			
		
	
		
			
				
					|  |  |  |  | 				Name:    ext.Get("path").String(), | 
			
		
	
		
			
				
					|  |  |  |  | 			}) | 
			
		
	
		
			
				
					|  |  |  |  | 			return true | 
			
		
	
		
			
				
					|  |  |  |  | 		} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 		*c = append(*c, &extension{ | 
			
		
	
		
			
				
					|  |  |  |  | 			ID:          id.String(), | 
			
		
	
		
			
				
					|  |  |  |  | 			URL:         getChromiumExtURL(id.String(), b.Get("update_url").String()), | 
			
		
	
		
			
				
					|  |  |  |  | 			Enabled:     enabled, | 
			
		
	
		
			
				
					|  |  |  |  | 			Name:        b.Get("name").String(), | 
			
		
	
		
			
				
					|  |  |  |  | 			Description: b.Get("description").String(), | 
			
		
	
		
			
				
					|  |  |  |  | 			Version:     b.Get("version").String(), | 
			
		
	
		
			
				
					|  |  |  |  | 			HomepageURL: b.Get("homepage_url").String(), | 
			
		
	
		
			
				
					|  |  |  |  | 		}) | 
			
		
	
		
			
				
					|  |  |  |  | 	} | 
			
		
	
		
			
				
					|  |  |  |  | 		return true | 
			
		
	
		
			
				
					|  |  |  |  | 	}) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 	return nil | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | func readChromiumExts(filename string) (*gjson.Result, error) { | 
			
		
	
		
			
				
					|  |  |  |  | 	prefs, err := fileutil.ReadFile(filename) | 
			
		
	
		
			
				
					|  |  |  |  | 	if err != nil { | 
			
		
	
		
			
				
					|  |  |  |  | 		return nil, err | 
			
		
	
		
			
				
					|  |  |  |  | 	} | 
			
		
	
		
			
				
					|  |  |  |  | 	extensions := gjson.Parse(prefs).Get("extensions.settings") | 
			
		
	
		
			
				
					|  |  |  |  | 	if extensions.Exists() { | 
			
		
	
		
			
				
					|  |  |  |  | 		return &extensions, nil | 
			
		
	
		
			
				
					|  |  |  |  | 	} | 
			
		
	
		
			
				
					|  |  |  |  | 	return nil, errors.New("extensions not found") | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | func getChromiumExtURL(id, updateURL string) string { | 
			
		
	
		
			
				
					|  |  |  |  | 	if strings.HasSuffix(updateURL, "clients2.google.com/service/update2/crx") { | 
			
		
	
		
			
				
					|  |  |  |  | 		return "https://chrome.google.com/webstore/detail/" + id | 
			
		
	
		
			
				
					|  |  |  |  | 	} else if strings.HasSuffix(updateURL, "edge.microsoft.com/extensionwebstorebase/v1/crx") { | 
			
		
	
		
			
				
					|  |  |  |  | 		return "https://microsoftedge.microsoft.com/addons/detail/" + id | 
			
		
	
		
			
				
					|  |  |  |  | 	} | 
			
		
	
		
			
				
					|  |  |  |  | 	return "" | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | func (c *ChromiumExtension) Name() string { | 
			
		
	
		
			
				
					|  |  |  |  | 	return "extension" | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
	
		
			
				
					|  |  |  | @ -56,15 +110,37 @@ func (c *ChromiumExtension) Len() int { | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | type FirefoxExtension []*extension | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | var lang = language.Und | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | func (f *FirefoxExtension) Parse(_ []byte) error { | 
			
		
	
		
			
				
					|  |  |  |  | 	s, err := fileutil.ReadFile(item.TempFirefoxExtension) | 
			
		
	
		
			
				
					|  |  |  |  | 	if err != nil { | 
			
		
	
		
			
				
					|  |  |  |  | 		return err | 
			
		
	
		
			
				
					|  |  |  |  | 	} | 
			
		
	
		
			
				
					|  |  |  |  | 	defer os.Remove(item.TempFirefoxExtension) | 
			
		
	
		
			
				
					|  |  |  |  | 	_ = os.Remove(item.TempFirefoxExtension) | 
			
		
	
		
			
				
					|  |  |  |  | 	j := gjson.Parse(s) | 
			
		
	
		
			
				
					|  |  |  |  | 	for _, v := range j.Get("addons").Array() { | 
			
		
	
		
			
				
					|  |  |  |  | 		// https://searchfox.org/mozilla-central/source/toolkit/mozapps/extensions/internal/XPIDatabase.jsm#157
 | 
			
		
	
		
			
				
					|  |  |  |  | 		if v.Get("location").String() != "app-profile" { | 
			
		
	
		
			
				
					|  |  |  |  | 			continue | 
			
		
	
		
			
				
					|  |  |  |  | 		} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 		if lang != language.Und { | 
			
		
	
		
			
				
					|  |  |  |  | 			locale := findFirefoxLocale(v.Get("locales").Array(), lang) | 
			
		
	
		
			
				
					|  |  |  |  | 			*f = append(*f, &extension{ | 
			
		
	
		
			
				
					|  |  |  |  | 				ID:          v.Get("id").String(), | 
			
		
	
		
			
				
					|  |  |  |  | 				Enabled:     v.Get("active").Bool(), | 
			
		
	
		
			
				
					|  |  |  |  | 				Name:        locale.Get("name").String(), | 
			
		
	
		
			
				
					|  |  |  |  | 				Description: locale.Get("description").String(), | 
			
		
	
		
			
				
					|  |  |  |  | 				Version:     v.Get("version").String(), | 
			
		
	
		
			
				
					|  |  |  |  | 				HomepageURL: locale.Get("homepageURL").String(), | 
			
		
	
		
			
				
					|  |  |  |  | 			}) | 
			
		
	
		
			
				
					|  |  |  |  | 			continue | 
			
		
	
		
			
				
					|  |  |  |  | 		} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 		*f = append(*f, &extension{ | 
			
		
	
		
			
				
					|  |  |  |  | 			ID:          v.Get("id").String(), | 
			
		
	
		
			
				
					|  |  |  |  | 			Enabled:     v.Get("active").Bool(), | 
			
		
	
		
			
				
					|  |  |  |  | 			Name:        v.Get("defaultLocale.name").String(), | 
			
		
	
		
			
				
					|  |  |  |  | 			Description: v.Get("defaultLocale.description").String(), | 
			
		
	
		
			
				
					|  |  |  |  | 			Version:     v.Get("version").String(), | 
			
		
	
	
		
			
				
					|  |  |  | @ -74,6 +150,23 @@ func (f *FirefoxExtension) Parse(_ []byte) error { | 
			
		
	
		
			
				
					|  |  |  |  | 	return nil | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | func findFirefoxLocale(locales []gjson.Result, targetLang language.Tag) gjson.Result { | 
			
		
	
		
			
				
					|  |  |  |  | 	tags := make([]language.Tag, 0, len(locales)) | 
			
		
	
		
			
				
					|  |  |  |  | 	indices := make([]int, 0, len(locales)) | 
			
		
	
		
			
				
					|  |  |  |  | 	for i, locale := range locales { | 
			
		
	
		
			
				
					|  |  |  |  | 		for _, tagStr := range locale.Get("locales").Array() { | 
			
		
	
		
			
				
					|  |  |  |  | 			tag, _ := language.Parse(tagStr.String()) | 
			
		
	
		
			
				
					|  |  |  |  | 			if tag == language.Und { | 
			
		
	
		
			
				
					|  |  |  |  | 				continue | 
			
		
	
		
			
				
					|  |  |  |  | 			} | 
			
		
	
		
			
				
					|  |  |  |  | 			tags = append(tags, tag) | 
			
		
	
		
			
				
					|  |  |  |  | 			indices = append(indices, i) | 
			
		
	
		
			
				
					|  |  |  |  | 		} | 
			
		
	
		
			
				
					|  |  |  |  | 	} | 
			
		
	
		
			
				
					|  |  |  |  | 	_, tagIndex, _ := language.NewMatcher(tags).Match(targetLang) | 
			
		
	
		
			
				
					|  |  |  |  | 	return locales[indices[tagIndex]] | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | func (f *FirefoxExtension) Name() string { | 
			
		
	
		
			
				
					|  |  |  |  | 	return "extension" | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
	
		
			
				
					|  |  |  | 
 |