mirror of
https://forge.fsky.io/oneflux/omegafox.git
synced 2026-02-10 20:02:05 -08:00
Load default addons through launcher
- Default addons are now downloaded through the launcher to avoid redownloading when new profiles are created - Updated README
This commit is contained in:
parent
ad87cec317
commit
8aadd3e1f2
9 changed files with 247 additions and 37 deletions
43
README.md
43
README.md
|
|
@ -17,8 +17,8 @@ Camoufox aims to be a minimalistic browser for robust fingerprint injection & an
|
|||
## Features
|
||||
|
||||
- Fingerprint injection (override properties of `navigator`, `window`, `screen`, etc) ✅
|
||||
- Patches to avoid Playwright detection ✅
|
||||
- Custom Playwright Juggler implementation with minimal leaks ✅
|
||||
- Patches to avoid bot detection ✅
|
||||
- Custom Playwright Juggler implementation for the latest Firefox ✅
|
||||
- Font spoofing & anti-fingerprinting ✅
|
||||
- Patches from LibreWolf & Ghostery to remove Mozilla services ✅
|
||||
- Optimized for memory and speed ✅
|
||||
|
|
@ -34,6 +34,7 @@ Camoufox is built on top of Firefox/Juggler instead of Chromium because:
|
|||
|
||||
### What's planned?
|
||||
|
||||
- Continue research on potential leaks
|
||||
- Built in TLS fingerprinting protection using [Hazetunnel](https://github.com/daijro/hazetunnel)
|
||||
- Create integration tests
|
||||
- Chromium port (long-term)
|
||||
|
|
@ -198,6 +199,25 @@ Spoofing document.body has been implemented, but it is more advicable to set `wi
|
|||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
HTTP Headers
|
||||
</summary>
|
||||
|
||||
Camoufox can override the following network headers:
|
||||
|
||||
| Property | Status |
|
||||
| ----------------------- | ------ |
|
||||
| headers.User-Agent | ✅ |
|
||||
| headers.Accept-Language | ✅ |
|
||||
| headers.Accept-Encoding | ✅ |
|
||||
|
||||
**Notes:**
|
||||
|
||||
- If `headers.User-Agent` is not set, it will fall back to `navigator.userAgent`.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
||||
<summary>
|
||||
|
|
@ -214,6 +234,14 @@ Example:
|
|||
|
||||
Camoufox will automatically download and use the latest uBlock Origin with custom privacy/adblock filters, and B.P.C. by default to help with scraping.
|
||||
|
||||
You can also exclude default addons with the `--exclude-addons` flag:
|
||||
|
||||
```bash
|
||||
./launcher --exclude-addons '["uBO", "BPC"]'
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
|
@ -285,6 +313,7 @@ Miscellaneous (WebGl spoofing, battery status, etc)
|
|||
- Added B.P.C.
|
||||
- Addons are not allowed to open tabs
|
||||
- Addons are automatically enabled in Private Browsing mode
|
||||
- Addons are automatically pinned to the toolbar
|
||||
|
||||
## Stealth Performance
|
||||
|
||||
|
|
@ -319,7 +348,7 @@ Camoufox performs well against every major WAF I've tested. (Test sites from [Bo
|
|||
| [**BrowserScan**](https://browserscan.net/) | ✔️ |
|
||||
| [**Bet365**](https://www.bet365.com/#/AC/B1/C1/D1002/E79147586/G40/) | ✔️ |
|
||||
|
||||
Camoufox does **not** fully support injecting Chromium fingerprints. Some websites (such as Cloudflare [Interstitial](https://nopecha.com/demo/cloudflare)) look for the Gecko webdriver underneath.
|
||||
Camoufox does **not** fully support injecting Chromium fingerprints. Some WAFs (such as [Interstitial](https://nopecha.com/demo/cloudflare)) look for the Gecko webdriver underneath.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -334,11 +363,11 @@ graph TD
|
|||
FFSRC[Firefox Source] -->|make fetch| REPO
|
||||
|
||||
subgraph REPO[Camoufox Repository]
|
||||
MASKING[Camoufox masking module]
|
||||
PATCHES[Debloat/optimizations]
|
||||
PATCHES[Fingerprint masking patches]
|
||||
ADDONS[uBlock & B.P.C.]
|
||||
SYSTEM_FONTS[Win, Mac, Linux System fonts]
|
||||
JUGGLER[Playwright Juggler]
|
||||
DEBLOAT[Debloat/optimizations]
|
||||
SYSTEM_FONTS[Win, Mac, Linux fonts]
|
||||
JUGGLER[Patched Juggler]
|
||||
end
|
||||
|
||||
subgraph Local
|
||||
|
|
|
|||
|
|
@ -5,6 +5,12 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// Default addons to extract to /addons
|
||||
var DefaultAddons = map[string]string{
|
||||
"uBO": "https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi",
|
||||
"BPC": "https://gitflic.ru/project/magnolia1234/bpc_uploads/blob/raw?file=bypass_paywalls_clean-latest.xpi",
|
||||
}
|
||||
|
||||
// Exclude lines from output
|
||||
var ExclusionRules = []string{
|
||||
// Ignore search related warnings
|
||||
|
|
@ -17,6 +23,7 @@ var ExclusionRules = []string{
|
|||
"^console\\.error:\\ \\(\\{\\}\\)$",
|
||||
"^console\\.error:\\ \"Could\\ not\\ record\\ event:\\ \"\\ \\(\\{\\}\\)$",
|
||||
"^\\s*?$",
|
||||
"Rejected by Camoufox\\.$",
|
||||
// Ignore missing urlbar provider errors
|
||||
"^JavaScript\\ error:\\ resource:///modules/UrlbarProvider",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,14 +4,12 @@ import (
|
|||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func getExecutableName() string {
|
||||
|
|
@ -68,23 +66,6 @@ func filterOutput(r io.Reader, w io.Writer) {
|
|||
}
|
||||
}
|
||||
|
||||
func tryLoadAddons(debugPortInt int, addonsList []string) {
|
||||
// Wait for the server to be open
|
||||
for {
|
||||
conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", debugPortInt))
|
||||
if err == nil {
|
||||
conn.Close()
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Load addons
|
||||
for _, addon := range addonsList {
|
||||
loadFirefoxAddon(debugPortInt, addon)
|
||||
}
|
||||
}
|
||||
|
||||
// Run Camoufox
|
||||
func runCamoufox(execName string, args []string, addonsList []string) {
|
||||
// If addons are specified, get the debug port
|
||||
|
|
@ -8,8 +8,10 @@ import (
|
|||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Gets the debug port from the args, or creates a new one if not provided
|
||||
func getDebugPort(args *[]string) int {
|
||||
debugPort := parseArgs("-start-debugger-server", "", args, false)
|
||||
|
||||
|
|
@ -30,8 +32,8 @@ func getDebugPort(args *[]string) int {
|
|||
return debugPortInt
|
||||
}
|
||||
|
||||
// Confirm paths are valid
|
||||
func confirmPaths(paths []string) {
|
||||
// Confirm paths are valid
|
||||
for _, path := range paths {
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
fmt.Printf("Error: %s is not a valid addon path.\n", path)
|
||||
|
|
@ -40,8 +42,8 @@ func confirmPaths(paths []string) {
|
|||
}
|
||||
}
|
||||
|
||||
// Generate an open port
|
||||
func getOpenPort() int {
|
||||
// Generate an open port
|
||||
ln, err := net.Listen("tcp", ":0") // listen on a random port
|
||||
if err != nil {
|
||||
return 0
|
||||
|
|
@ -52,6 +54,24 @@ func getOpenPort() int {
|
|||
return addr.Port
|
||||
}
|
||||
|
||||
// Waits for the server to start, then loads the addons
|
||||
func tryLoadAddons(debugPortInt int, addonsList []string) {
|
||||
// Wait for the server to be open
|
||||
for {
|
||||
conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", debugPortInt))
|
||||
if err == nil {
|
||||
conn.Close()
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Load addons
|
||||
for _, addon := range addonsList {
|
||||
go loadFirefoxAddon(debugPortInt, addon)
|
||||
}
|
||||
}
|
||||
|
||||
// Firefox addon loader
|
||||
// Ported from this Nodejs implementation:
|
||||
// https://github.com/microsoft/playwright/issues/7297#issuecomment-1211763085
|
||||
|
|
@ -17,11 +17,16 @@ func main() {
|
|||
|
||||
configPath := parseArgs("--config", "{}", &args, true)
|
||||
addons := parseArgs("--addons", "[]", &args, true)
|
||||
excludeAddons := parseArgs("--exclude-addons", "[]", &args, true)
|
||||
|
||||
//*** PARSE CONFIG ***//
|
||||
|
||||
// Read and parse the config file
|
||||
var configMap map[string]interface{}
|
||||
parseJson(configPath, &configMap)
|
||||
|
||||
//*** PARSE ADDONS ***//
|
||||
|
||||
// If addons are passed, handle them
|
||||
var addonsList []string
|
||||
parseJson(addons, &addonsList)
|
||||
|
|
@ -29,10 +34,21 @@ func main() {
|
|||
// Confirm addon paths are valid
|
||||
confirmPaths(addonsList)
|
||||
|
||||
userAgentOS := determineUserAgentOS(configMap) // Determine the user agent OS
|
||||
// Add the default addons, excluding the ones specified in --exclude-addons
|
||||
var excludeAddonsList []string
|
||||
parseJson(excludeAddons, &excludeAddonsList)
|
||||
|
||||
// OS specific font config
|
||||
addDefaultAddons(excludeAddonsList, &addonsList)
|
||||
|
||||
//*** FONTS ***//
|
||||
|
||||
// Determine the target OS
|
||||
userAgentOS := determineUserAgentOS(configMap)
|
||||
// Add OS specific fonts
|
||||
updateFonts(configMap, userAgentOS)
|
||||
|
||||
//*** LAUNCH ***//
|
||||
|
||||
setEnvironmentVariables(configMap, userAgentOS)
|
||||
|
||||
// Run the Camoufox executable
|
||||
|
|
@ -44,6 +60,7 @@ func main() {
|
|||
runCamoufox(execName, args, addonsList)
|
||||
}
|
||||
|
||||
// Parses & removes an argument from the args list
|
||||
func parseArgs(param string, defaultValue string, args *[]string, removeFromArgs bool) string {
|
||||
for i := 0; i < len(*args); i++ {
|
||||
if (*args)[i] != param {
|
||||
|
|
@ -62,6 +79,7 @@ func parseArgs(param string, defaultValue string, args *[]string, removeFromArgs
|
|||
return defaultValue
|
||||
}
|
||||
|
||||
// Parses a JSON string or file into a map
|
||||
func parseJson(argv string, target interface{}) {
|
||||
// Unmarshal the config input into a map
|
||||
var data []byte
|
||||
|
|
@ -84,6 +102,7 @@ func parseJson(argv string, target interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
// Determines the target OS from the user agent string if provided
|
||||
func determineUserAgentOS(configMap map[string]interface{}) string {
|
||||
// Determine the OS from the user agent string if provided
|
||||
defaultOS := normalizeOS(runtime.GOOS)
|
||||
|
|
@ -96,8 +115,8 @@ func determineUserAgentOS(configMap map[string]interface{}) string {
|
|||
return defaultOS
|
||||
}
|
||||
|
||||
// Get the OS name as {macos, windows, linux}
|
||||
func normalizeOS(osName string) string {
|
||||
// Get the OS name as {macos, windows, linux}
|
||||
osName = strings.ToLower(osName)
|
||||
switch {
|
||||
case osName == "darwin" || strings.Contains(osName, "mac"):
|
||||
|
|
@ -109,8 +128,8 @@ func normalizeOS(osName string) string {
|
|||
}
|
||||
}
|
||||
|
||||
// Add fonts associated with the OS to the config map
|
||||
func updateFonts(configMap map[string]interface{}, userAgentOS string) {
|
||||
// Add fonts associated with the OS to the config map
|
||||
fonts, ok := configMap["fonts"].([]interface{})
|
||||
if !ok {
|
||||
fonts = []interface{}{}
|
||||
|
|
@ -129,8 +148,8 @@ func updateFonts(configMap map[string]interface{}, userAgentOS string) {
|
|||
configMap["fonts"] = fonts
|
||||
}
|
||||
|
||||
// Update the config map with the fonts and environment variables
|
||||
func setEnvironmentVariables(configMap map[string]interface{}, userAgentOS string) {
|
||||
// Update the config map with the fonts and environment variables
|
||||
updatedConfigData, err := json.Marshal(configMap)
|
||||
if err != nil {
|
||||
fmt.Printf("Error updating config: %v\n", err)
|
||||
|
|
|
|||
158
launcher/xpi-dl.go
Normal file
158
launcher/xpi-dl.go
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// Downloads and extracts the default addons
|
||||
func addDefaultAddons(excludeList []string, addonsList *[]string) {
|
||||
// Build a map from DefaultAddons, excluding keys found in excludeAddonsList
|
||||
addonsMap := make(map[string]string)
|
||||
for name, url := range DefaultAddons {
|
||||
if len(excludeList) == 0 || !contains(excludeList, name) {
|
||||
addonsMap[name] = url
|
||||
}
|
||||
}
|
||||
|
||||
// Download if not already downloaded
|
||||
maybeDownloadAddons(addonsMap, addonsList)
|
||||
}
|
||||
|
||||
// Downloads and extracts the addon
|
||||
func downloadAndExtract(url, extractPath string) error {
|
||||
// Create a temporary file to store the downloaded zip
|
||||
tempFile, err := os.CreateTemp("", "camoufox-addon-*.zip")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create temp file: %w", err)
|
||||
}
|
||||
defer os.Remove(tempFile.Name()) // Clean up the temp file when done
|
||||
|
||||
// Download the zip file
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to download addon: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Write the body to the temp file
|
||||
_, err = io.Copy(tempFile, resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write addon to temp file: %w", err)
|
||||
}
|
||||
|
||||
// Close the file before unzipping
|
||||
tempFile.Close()
|
||||
|
||||
// Extract the zip file
|
||||
err = unzip(tempFile.Name(), extractPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to extract addon: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Extracts the zip file
|
||||
func unzip(src, dest string) error {
|
||||
r, err := zip.OpenReader(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
for _, f := range r.File {
|
||||
fpath := filepath.Join(dest, f.Name)
|
||||
|
||||
if f.FileInfo().IsDir() {
|
||||
os.MkdirAll(fpath, os.ModePerm)
|
||||
continue
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rc, err := f.Open()
|
||||
if err != nil {
|
||||
outFile.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(outFile, rc)
|
||||
outFile.Close()
|
||||
rc.Close()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Checks if a slice contains a string
|
||||
func contains(slice []string, item string) bool {
|
||||
for _, s := range slice {
|
||||
if s == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Returns the absolute path to the target addon location
|
||||
func getAddonPath(addonName string) (string, error) {
|
||||
execPath, err := os.Executable()
|
||||
if err != nil {
|
||||
fmt.Printf("Error getting executable path: %v\n", err)
|
||||
return "", err
|
||||
}
|
||||
execDir := filepath.Dir(execPath)
|
||||
|
||||
addonPath := filepath.Join(execDir, "addons", addonName)
|
||||
return addonPath, nil
|
||||
}
|
||||
|
||||
// Downloads and extracts the addons
|
||||
func maybeDownloadAddons(addons map[string]string, addonsList *[]string) {
|
||||
for addonName, url := range addons {
|
||||
// Get the addon path
|
||||
addonPath, err := getAddonPath(addonName)
|
||||
if err != nil {
|
||||
fmt.Printf("Error getting addon path: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if the addon is already extracted
|
||||
if _, err := os.Stat(addonPath); !os.IsNotExist(err) {
|
||||
// Add the existing addon path to addonsList
|
||||
*addonsList = append(*addonsList, addonPath)
|
||||
continue
|
||||
}
|
||||
|
||||
// Addon doesn't exist, create directory and download
|
||||
err = os.MkdirAll(addonPath, 0755)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to create directory for %s: %v\n", addonName, err)
|
||||
continue
|
||||
}
|
||||
|
||||
err = downloadAndExtract(url, addonPath)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to download and extract %s: %v\n", addonName, err)
|
||||
} else {
|
||||
fmt.Printf("Successfully downloaded and extracted %s\n", addonName)
|
||||
// Add the new addon directory path to addonsList
|
||||
*addonsList = append(*addonsList, addonPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -35,10 +35,6 @@
|
|||
"Exceptions": ["https://localhost/*"]
|
||||
},
|
||||
"Extensions": {
|
||||
"Install": [
|
||||
"https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi",
|
||||
"https://github.com/bpc-clone/bpc_updates/releases/download/latest/bypass_paywalls_clean-latest.xpi"
|
||||
],
|
||||
"Uninstall": [
|
||||
"google@search.mozilla.org",
|
||||
"bing@search.mozilla.org",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue