Skip to content

feat: add proxy extension#1

Open
kryptocodes wants to merge 3 commits intomainfrom
feat/proxy-configuration
Open

feat: add proxy extension#1
kryptocodes wants to merge 3 commits intomainfrom
feat/proxy-configuration

Conversation

@kryptocodes
Copy link

@kryptocodes kryptocodes commented Jan 27, 2026

Description

  • Add proxy configuration support for the headful browser image
  • Support HTTP/SOCKS proxies with authentication

Summary by CodeRabbit

  • New Features

    • REST API to manage proxy configuration (GET/PUT/DELETE) with host, port, credentials, scheme, and bypass list
    • Bundled browser extension for automatic proxy application and authentication at runtime
    • Container/runtime support to enable and propagate proxy settings into the headful Chromium image
  • Documentation

    • Added Proxy Configuration guide with quick start, API usage examples, geo-targeting notes, and port/scheme guidance

✏️ Tip: You can customize this high-level summary in your review settings.

Signed-off-by: kryptocodes <srivatsantb@gmail.com>
@coderabbitai
Copy link

coderabbitai bot commented Jan 27, 2026

📝 Walkthrough

Walkthrough

Adds a proxy configuration feature: new REST endpoints (GET/PUT/DELETE) with file-backed persistence and in-memory caching, a Chrome MV3 extension that polls the API and applies browser proxy/auth settings, Docker/runtime flags to load the extension, OpenAPI + generated client/server updates, and README documentation additions.

Changes

Cohort / File(s) Summary
Documentation
README.md
Added "Proxy Configuration" section (Quick Start, API examples, port/scheme notes). Section appears twice in the diff.
Chrome Extension
brightdata-proxy-extension/background.js, brightdata-proxy-extension/manifest.json
New MV3 extension: periodic fetch of proxy config from local API, apply chrome.proxy.settings (fixed_servers), credential caching, onAuthRequired handling, and alarm-based polling. Manifest grants proxy, webRequest, alarms, storage, etc.
Docker / Runtime
images/chromium-headful/run-docker.sh, images/chromium-headful/Dockerfile, images/chromium-headful/supervisor/services/chromium.conf
Bundles extension into image (COPY added), exposes env-driven proxy options; runtime flags load-only the proxy extension and set Chromium flags for proxied WebRTC behavior. COPY step duplicated in Dockerfile.
Backend API Handler
server/cmd/api/api/proxy.go
New handlers: GetProxyConfig, SetProxyConfig, DeleteProxyConfig. File-backed persistence at /chromium/proxy-config.json, RWMutex-protected in-memory cache, validation, defaults, and explicit error responses.
API Spec & Generated Code
server/openapi.yaml, server/lib/oapi/oapi.go
Added /proxy/config path (GET/PUT/DELETE) and ProxyConfig schema; large autogenerated client/server types, request/response wrappers, and updated embedded OpenAPI spec to support these endpoints.
Module deps
server/go.mod
Promoted github.com/klauspost/compress v1.18.3 from indirect to direct require.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant API as "API Server"
    participant FS as "File System"
    participant Ext as "Browser Extension"
    participant Browser as "Chromium"
    participant Proxy as "Proxy Server"

    Client->>API: PUT /proxy/config (host,port,creds)
    API->>FS: write /chromium/proxy-config.json
    API->>API: update in-memory cache
    API-->>Client: 200 ProxyConfig

    Ext->>API: GET /proxy/config (init / poll)
    API-->>Ext: ProxyConfig
    Ext->>Ext: apply proxy settings & cache creds
    Ext->>Browser: set chrome.proxy.settings (fixed_servers)

    Browser->>Proxy: proxied request (+ auth if provided)
    Proxy-->>Browser: response

    Client->>API: DELETE /proxy/config
    API->>FS: remove config file
    API->>API: clear cache
    API-->>Client: 204 No Content
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 I nibble configs, neat and spry,

I whisper routes where packets fly,
An extension hums, the browser bows,
Through proxied fields I trace my browse,
A hopping patchwork — cheers, meow.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: add proxy extension' accurately summarizes the main change—introducing a new proxy extension and proxy configuration support to the headful browser image.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@server/cmd/api/api/proxy.go`:
- Around line 65-71: The port validation currently only checks for nil or zero
and allows negative or >65535 values; update the cfg.Port validation (the same
block that returns oapi.SetProxyConfig400JSONResponse) to also reject values
outside the valid TCP port range (1–65535) by checking *cfg.Port >= 1 &&
*cfg.Port <= 65535 and returning a BadRequest response
(oapi.SetProxyConfig400JSONResponse with an appropriate message like "port must
be between 1 and 65535") when the check fails.
- Around line 95-103: The proxy config is being written with world-readable
permissions; update the file and directory creation calls so credentials are
locked down: change the os.WriteFile call that writes proxyConfigPath from mode
0o644 to 0o600 (or 0o640 if group read is required) and tighten the os.MkdirAll
directory mode (e.g., from 0o755 to 0o700) to reduce access; locate these
changes around the os.MkdirAll("/chromium", ...) and
os.WriteFile(proxyConfigPath, data, ...) lines in proxy.go and adjust the octal
permission arguments accordingly.

In `@server/lib/oapi/oapi.go`:
- Around line 497-520: ProxyConfig currently exposes credentials (Password and
Username) in API responses; remove or mark them write-only so GET responses
don't return secrets. Update the OpenAPI schema (server/openapi.yaml) to mark
password (and username if not needed in responses) as writeOnly or create a
separate response schema that omits ProxyConfig.Password and
ProxyConfig.Username, then regenerate/update the generated type (ProxyConfig) or
add a ResponseProxyConfig type used for GETs to ensure credentials are never
serialized in responses or logs.
🧹 Nitpick comments (4)
images/chromium-headful/run-docker.sh (1)

22-27: Consider validating required proxy variables.

When PROXY_ENABLED=true, the script logs PROXY_HOST:PROXY_PORT but doesn't validate that these variables are actually set. If they're empty, Chrome will load the extension but the proxy configuration will be invalid.

♻️ Proposed validation
 # Add proxy extension flags if proxy is enabled
 if [[ "${PROXY_ENABLED:-}" == "true" ]]; then
+  if [[ -z "${PROXY_HOST:-}" || -z "${PROXY_PORT:-}" ]]; then
+    echo "Error: PROXY_HOST and PROXY_PORT are required when PROXY_ENABLED=true"
+    exit 1
+  fi
   echo "Proxy enabled: $PROXY_HOST:$PROXY_PORT"
   CHROMIUM_FLAGS="$CHROMIUM_FLAGS --load-extension=/home/kernel/extensions/brightdata-proxy --disable-extensions-except=/home/kernel/extensions/brightdata-proxy"
 fi
server/openapi.yaml (1)

1772-1801: Consider making host and port required fields.

The ProxyConfig schema has no required fields, which means a PUT request with an empty object {} would be valid. The background script checks for config.host and config.port before applying, but having schema-level validation would provide clearer API contracts and better error messages.

♻️ Proposed schema change
     ProxyConfig:
       type: object
       description: Proxy configuration for the browser
+      required:
+        - host
+        - port
       properties:
         host:
           type: string
brightdata-proxy-extension/background.js (1)

31-47: Add error handling for chrome.proxy.settings.set.

The callback for chrome.proxy.settings.set only logs success but doesn't check chrome.runtime.lastError for failures. If setting the proxy fails (e.g., invalid configuration), there's no indication.

♻️ Proposed fix
   chrome.proxy.settings.set({
     value: {
       mode: "fixed_servers",
       rules: {
         singleProxy: {
           scheme: config.scheme || "http",
           host: config.host,
           port: config.port
         },
         bypassList: config.bypassList || ["localhost", "127.0.0.1"]
       }
     },
     scope: "regular"
   }, () => {
+    if (chrome.runtime.lastError) {
+      console.error("Failed to set proxy:", chrome.runtime.lastError.message);
+      return;
+    }
     console.log("Proxy configured:", config.host + ":" + config.port);
   });
server/cmd/api/api/proxy.go (1)

20-53: Avoid holding the RLock during file I/O and refresh the cache after load.

Line 24 keeps the read lock while hitting disk and parsing JSON, which can block writers longer than needed. Also, a successful file read doesn’t refresh the in-memory cache, so repeated GETs always re-read the file.

♻️ Proposed refactor
-	proxyConfigMu.RLock()
-	defer proxyConfigMu.RUnlock()
-
-	// If we have a cached config, return it
-	if proxyConfig != nil {
-		log.Info("returning cached proxy config", "host", stringVal(proxyConfig.Host))
-		return oapi.GetProxyConfig200JSONResponse(*proxyConfig), nil
-	}
+	proxyConfigMu.RLock()
+	if proxyConfig != nil {
+		log.Info("returning cached proxy config", "host", stringVal(proxyConfig.Host))
+		proxyConfigMu.RUnlock()
+		return oapi.GetProxyConfig200JSONResponse(*proxyConfig), nil
+	}
+	proxyConfigMu.RUnlock()
 
 	// Try to load from file
 	data, err := os.ReadFile(proxyConfigPath)
@@
-	log.Info("returning proxy config from file", "host", stringVal(cfg.Host))
+	// Update cache (only if still empty)
+	proxyConfigMu.Lock()
+	if proxyConfig == nil {
+		cfgCopy := cfg
+		proxyConfig = &cfgCopy
+	}
+	proxyConfigMu.Unlock()
+
+	log.Info("returning proxy config from file", "host", stringVal(cfg.Host))
 	return oapi.GetProxyConfig200JSONResponse(cfg), nil

Comment on lines +65 to +71
// Validate required fields
if cfg.Host == nil || *cfg.Host == "" {
return oapi.SetProxyConfig400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "host is required"}}, nil
}
if cfg.Port == nil || *cfg.Port == 0 {
return oapi.SetProxyConfig400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "port is required"}}, nil
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Validate proxy port range (1–65535).

Line 69 accepts negative or >65535 ports, which will serialize but fail downstream. Add a range check.

✅ Proposed fix
-	if cfg.Port == nil || *cfg.Port == 0 {
+	if cfg.Port == nil || *cfg.Port < 1 || *cfg.Port > 65535 {
 		return oapi.SetProxyConfig400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "port is required"}}, nil
 	}
🤖 Prompt for AI Agents
In `@server/cmd/api/api/proxy.go` around lines 65 - 71, The port validation
currently only checks for nil or zero and allows negative or >65535 values;
update the cfg.Port validation (the same block that returns
oapi.SetProxyConfig400JSONResponse) to also reject values outside the valid TCP
port range (1–65535) by checking *cfg.Port >= 1 && *cfg.Port <= 65535 and
returning a BadRequest response (oapi.SetProxyConfig400JSONResponse with an
appropriate message like "port must be between 1 and 65535") when the check
fails.

Comment on lines +95 to +103
if err := os.MkdirAll("/chromium", 0o755); err != nil {
log.Error("failed to create chromium dir", "error", err)
return oapi.SetProxyConfig500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "failed to create chromium dir"}}, nil
}

if err := os.WriteFile(proxyConfigPath, data, 0o644); err != nil {
log.Error("failed to write proxy config", "error", err)
return oapi.SetProxyConfig500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "failed to write proxy config"}}, nil
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Restrict proxy config file permissions to protect credentials.

Line 100 writes with 0644, which makes proxy credentials world-readable on the container filesystem. Use 0600 (or at least 0640) to limit exposure.

🔐 Proposed fix
-	if err := os.WriteFile(proxyConfigPath, data, 0o644); err != nil {
+	if err := os.WriteFile(proxyConfigPath, data, 0o600); err != nil {
 		log.Error("failed to write proxy config", "error", err)
 		return oapi.SetProxyConfig500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "failed to write proxy config"}}, nil
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if err := os.MkdirAll("/chromium", 0o755); err != nil {
log.Error("failed to create chromium dir", "error", err)
return oapi.SetProxyConfig500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "failed to create chromium dir"}}, nil
}
if err := os.WriteFile(proxyConfigPath, data, 0o644); err != nil {
log.Error("failed to write proxy config", "error", err)
return oapi.SetProxyConfig500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "failed to write proxy config"}}, nil
}
if err := os.MkdirAll("/chromium", 0o755); err != nil {
log.Error("failed to create chromium dir", "error", err)
return oapi.SetProxyConfig500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "failed to create chromium dir"}}, nil
}
if err := os.WriteFile(proxyConfigPath, data, 0o600); err != nil {
log.Error("failed to write proxy config", "error", err)
return oapi.SetProxyConfig500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "failed to write proxy config"}}, nil
}
🤖 Prompt for AI Agents
In `@server/cmd/api/api/proxy.go` around lines 95 - 103, The proxy config is being
written with world-readable permissions; update the file and directory creation
calls so credentials are locked down: change the os.WriteFile call that writes
proxyConfigPath from mode 0o644 to 0o600 (or 0o640 if group read is required)
and tighten the os.MkdirAll directory mode (e.g., from 0o755 to 0o700) to reduce
access; locate these changes around the os.MkdirAll("/chromium", ...) and
os.WriteFile(proxyConfigPath, data, ...) lines in proxy.go and adjust the octal
permission arguments accordingly.

Comment on lines +497 to +520
// ProxyConfig Proxy configuration for the browser
type ProxyConfig struct {
// BypassList List of hosts to bypass the proxy for
BypassList *[]string `json:"bypassList,omitempty"`

// Host Proxy server hostname or IP address
Host *string `json:"host,omitempty"`

// Password Proxy authentication password
Password *string `json:"password,omitempty"`

// Port Proxy server port
Port *int `json:"port,omitempty"`

// Scheme Proxy protocol scheme
Scheme *ProxyConfigScheme `json:"scheme,omitempty"`

// Username Proxy authentication username
Username *string `json:"username,omitempty"`
}

// ProxyConfigScheme Proxy protocol scheme
type ProxyConfigScheme string

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid returning proxy credentials in API responses. ProxyConfig includes Password (and Username) and is used by GET/PUT responses. If not strictly required, this exposes secrets to any caller and to logs/telemetry. Consider marking password as writeOnly in server/openapi.yaml or using a response schema that omits credentials.

🤖 Prompt for AI Agents
In `@server/lib/oapi/oapi.go` around lines 497 - 520, ProxyConfig currently
exposes credentials (Password and Username) in API responses; remove or mark
them write-only so GET responses don't return secrets. Update the OpenAPI schema
(server/openapi.yaml) to mark password (and username if not needed in responses)
as writeOnly or create a separate response schema that omits
ProxyConfig.Password and ProxyConfig.Username, then regenerate/update the
generated type (ProxyConfig) or add a ResponseProxyConfig type used for GETs to
ensure credentials are never serialized in responses or logs.

Signed-off-by: kryptocodes <srivatsantb@gmail.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@brightdata-proxy-extension/background.js`:
- Around line 49-67: The onAuthRequired handler
(chrome.webRequest.onAuthRequired) currently returns proxyConfig credentials for
any auth challenge; restrict it so you only supply credentials when
details.isProxy === true and proxyConfig exists, e.g., check details.isProxy
before calling callbackFn with authCredentials, otherwise call
callbackFn({cancel:false}); optionally also validate the challenger host in
details.challenger or details.host against your configured proxy host to avoid
sending proxy credentials to origin/server challenges.
🧹 Nitpick comments (3)
brightdata-proxy-extension/background.js (2)

6-19: Add a timeout to avoid a hung fetch when the local API is down.
A short AbortController timeout keeps the MV3 service worker from waiting indefinitely.

♻️ Suggested update
 async function fetchProxyConfig() {
+  const controller = new AbortController();
+  const timeoutId = setTimeout(() => controller.abort(), 3000);
   try {
-    const response = await fetch(CONFIG_URL);
+    const response = await fetch(CONFIG_URL, { signal: controller.signal });
     if (!response.ok) {
       console.error("Failed to fetch proxy config:", response.status);
       return null;
     }
     return await response.json();
   } catch (error) {
     console.error("Error fetching proxy config:", error);
     return null;
+  } finally {
+    clearTimeout(timeoutId);
   }
 }

21-47: Clear cached credentials when disabling the proxy and surface Chrome API errors.
Without clearing proxyConfig, stale credentials can linger; also surface chrome.runtime.lastError to avoid silent failures.

♻️ Suggested update
 function applyProxySettings(config) {
   if (!config || !config.host || !config.port) {
     console.log("No valid proxy config, disabling proxy");
-    chrome.proxy.settings.clear({ scope: "regular" });
+    proxyConfig = null;
+    chrome.proxy.settings.clear({ scope: "regular" }, () => {
+      if (chrome.runtime.lastError) {
+        console.error("Failed to clear proxy settings:", chrome.runtime.lastError.message);
+      }
+    });
     return;
   }

   proxyConfig = config;

   chrome.proxy.settings.set({
     value: {
       mode: "fixed_servers",
       rules: {
         singleProxy: {
           scheme: config.scheme || "http",
           host: config.host,
           port: config.port
         },
         bypassList: config.bypassList || ["localhost", "127.0.0.1"]
       }
     },
     scope: "regular"
   }, () => {
+    if (chrome.runtime.lastError) {
+      console.error("Failed to set proxy settings:", chrome.runtime.lastError.message);
+      return;
+    }
     console.log("Proxy configured:", config.host + ":" + config.port);
   });
 }
server/openapi.yaml (1)

1772-1801: Consider requiring host and port to prevent invalid configs.
This aligns the schema with the extension’s expectations and the existence of DELETE for “disable.”

♻️ Suggested update
     ProxyConfig:
       type: object
       description: Proxy configuration for the browser
+      required: [host, port]
       properties:
         host:
           type: string
           description: Proxy server hostname or IP address
+          minLength: 1
         port:
           type: integer
           description: Proxy server port
           minimum: 1
           maximum: 65535

Comment on lines 49 to 67
// Handle proxy authentication
chrome.webRequest.onAuthRequired.addListener(
(details, callbackFn) => {
if (proxyConfig && proxyConfig.username && proxyConfig.password) {
console.log("Auth required, providing credentials");
callbackFn({
authCredentials: {
username: proxyConfig.username,
password: proxyConfig.password
}
});
} else {
console.log("Auth required but no credentials available");
callbackFn({ cancel: false });
}
},
{ urls: ["<all_urls>"] },
["asyncBlocking"]
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Chrome extension webRequest onAuthRequired details.isProxy challenger property API documentation

💡 Result:

In chrome.webRequest.onAuthRequired, the event details object includes:

  • details.challenger: an object describing the server (or proxy) that is requesting authentication:

  • details.isProxy (boolean): indicates whether the auth challenge is coming from a proxy:

    • true = proxy auth (Proxy-Authenticate, typically HTTP 407)
    • false = origin/server auth (WWW-Authenticate, typically HTTP 401) (developer.mozilla.org)

Notes:

  • onAuthRequired is only fired for HTTP and HTTPS/TLS proxies that require authentication (not SOCKS proxies). (developer.mozilla.org)
  • In Manifest V3, Chrome requires the webRequestAuthProvider permission to use onAuthRequired. (developer.chrome.com)

Citations:


🏁 Script executed:

# Check if there are any existing security checks in the codebase
rg -i "isProxy|challenger" brightdata-proxy-extension/

Repository: reclaimprotocol/popcorn-images

Length of output: 56


🏁 Script executed:

# Look for any comments or documentation about auth handling
rg -i "auth|credential" brightdata-proxy-extension/background.js -A 2 -B 2

Repository: reclaimprotocol/popcorn-images

Length of output: 575


🏁 Script executed:

rg "proxyConfig\s*=" brightdata-proxy-extension/background.js -B 3 -A 3

Repository: reclaimprotocol/popcorn-images

Length of output: 367


🏁 Script executed:

rg "proxyConfig" brightdata-proxy-extension/background.js -B 2 -A 2 | head -40

Repository: reclaimprotocol/popcorn-images

Length of output: 615


🏁 Script executed:

rg "proxyConfig\." brightdata-proxy-extension/background.js | sort | uniq

Repository: reclaimprotocol/popcorn-images

Length of output: 227


🏁 Script executed:

# Look for the config object structure or where it's used
rg "config\." brightdata-proxy-extension/background.js -B 2 -A 2 | head -50

Repository: reclaimprotocol/popcorn-images

Length of output: 646


Restrict auth handling to proxy challenges to prevent credential leakage.

onAuthRequired fires for both HTTP proxy auth challenges (HTTP 407, isProxy=true) and origin/server auth challenges (HTTP 401, isProxy=false). Without a guard on details.isProxy, proxy credentials are leaked to any website that requests authentication. Add a check for details.isProxy === true before responding with credentials, and optionally validate the challenger host matches the proxy host.

🔒 Suggested hardening
 chrome.webRequest.onAuthRequired.addListener(
   (details, callbackFn) => {
+    if (!details.isProxy) {
+      callbackFn({ cancel: false });
+      return;
+    }
+    if (proxyConfig?.host && details.challenger?.host && details.challenger.host !== proxyConfig.host) {
+      callbackFn({ cancel: false });
+      return;
+    }
     if (proxyConfig && proxyConfig.username && proxyConfig.password) {
       console.log("Auth required, providing credentials");
       callbackFn({
         authCredentials: {
           username: proxyConfig.username,
           password: proxyConfig.password
         }
       });
     } else {
       console.log("Auth required but no credentials available");
       callbackFn({ cancel: false });
     }
   },
   { urls: ["<all_urls>"] },
   ["asyncBlocking"]
 );
🤖 Prompt for AI Agents
In `@brightdata-proxy-extension/background.js` around lines 49 - 67, The
onAuthRequired handler (chrome.webRequest.onAuthRequired) currently returns
proxyConfig credentials for any auth challenge; restrict it so you only supply
credentials when details.isProxy === true and proxyConfig exists, e.g., check
details.isProxy before calling callbackFn with authCredentials, otherwise call
callbackFn({cancel:false}); optionally also validate the challenger host in
details.challenger or details.host against your configured proxy host to avoid
sending proxy credentials to origin/server challenges.

- auto refresh/load once the config is detected

Signed-off-by: kryptocodes <srivatsantb@gmail.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@brightdata-proxy-extension/background.js`:
- Around line 20-33: fetchProxyConfig currently returns null for both "no
config" and fetch errors, causing applyProxySettings(null) to clear proxy on
transient failures; change fetchProxyConfig to distinguish these cases: when
response.ok is false, if response.status indicates "no config" (e.g. 404) return
null, but for other non-ok statuses and for caught network errors return a
distinct failure value (e.g. undefined) and log the error; then update the
caller(s) that call applyProxySettings to only call it when the returned value
is not undefined (i.e. only apply when config !== undefined), so transient fetch
failures skip applying/clearing settings.
- Around line 48-74: The applyProxySettings function uses parseInt(config.port,
10) without validating the result, so you must validate the parsed port is a
finite integer in the valid TCP range (1–65535) before calling
chrome.proxy.settings.set or saving config/cachedCredentials; if the port is
invalid (NaN, non-integer, or out of range) clear the proxy
(chrome.proxy.settings.clear), set storage proxyConfig/lastConfigHash to null,
reset cachedCredentials, and optionally log the bad config, otherwise continue
to save proxyConfig/lastConfigHash, set cachedCredentials and call
chrome.proxy.settings.set with the validated integer port.

In `@README.md`:
- Around line 180-184: The Markdown table header and divider need consistent
spacing so markdownlint MD060 is satisfied: edit the table that starts with the
header "Country | Username Suffix" and the rows containing `-country-in`,
`-country-us`, and `-country-gb` to use uniform single spaces around each pipe
and ensure the separator row uses matching column widths (e.g.,
`|---------|-----------------|`) so the pipes in the header, separator, and data
rows align consistently.

Comment on lines +20 to +33
// Fetch proxy configuration from API
async function fetchProxyConfig() {
try {
const response = await fetch(CONFIG_URL);
if (!response.ok) {
console.error("Failed to fetch proxy config:", response.status);
return null;
}
return await response.json();
} catch (error) {
console.error("Error fetching proxy config:", error);
return null;
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid clearing proxy settings on transient API failures.

Any fetch error currently returns null, which triggers applyProxySettings(null) and clears the proxy. This can drop proxying during temporary API outages. Distinguish “no config” from “fetch failed,” and skip apply on errors.

🛠️ Suggested fix
 async function fetchProxyConfig() {
   try {
     const response = await fetch(CONFIG_URL);
-    if (!response.ok) {
-      console.error("Failed to fetch proxy config:", response.status);
-      return null;
-    }
+    if (response.status === 404 || response.status === 204) {
+      return null; // explicitly disabled
+    }
+    if (!response.ok) {
+      console.error("Failed to fetch proxy config:", response.status);
+      return undefined;
+    }
     return await response.json();
   } catch (error) {
     console.error("Error fetching proxy config:", error);
-    return null;
+    return undefined;
   }
 }
 
 async function checkAndApplyConfig() {
   const config = await fetchProxyConfig();
+  if (config === undefined) return;
   const newHash = hashConfig(config);
   const stored = await chrome.storage.local.get(["lastConfigHash"]);

Also applies to: 76-85

🤖 Prompt for AI Agents
In `@brightdata-proxy-extension/background.js` around lines 20 - 33,
fetchProxyConfig currently returns null for both "no config" and fetch errors,
causing applyProxySettings(null) to clear proxy on transient failures; change
fetchProxyConfig to distinguish these cases: when response.ok is false, if
response.status indicates "no config" (e.g. 404) return null, but for other
non-ok statuses and for caught network errors return a distinct failure value
(e.g. undefined) and log the error; then update the caller(s) that call
applyProxySettings to only call it when the returned value is not undefined
(i.e. only apply when config !== undefined), so transient fetch failures skip
applying/clearing settings.

Comment on lines +48 to +74
// Apply proxy settings
async function applyProxySettings(config) {
if (!config || !config.host || !config.port) {
chrome.proxy.settings.clear({ scope: "regular" });
await chrome.storage.local.set({ proxyConfig: null, lastConfigHash: null });
cachedCredentials = null;
return;
}

await chrome.storage.local.set({ proxyConfig: config, lastConfigHash: hashConfig(config) });
cachedCredentials = config;

chrome.proxy.settings.set({
value: {
mode: "fixed_servers",
rules: {
singleProxy: {
scheme: config.scheme || "http",
host: config.host,
port: parseInt(config.port, 10)
},
bypassList: config.bypassList || ["localhost", "127.0.0.1"]
}
},
scope: "regular"
});
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Validate proxy port before applying settings.

parseInt can yield NaN or out-of-range values, which may cause a silent failure. Validate the port before calling chrome.proxy.settings.set.

✅ Proposed validation
   await chrome.storage.local.set({ proxyConfig: config, lastConfigHash: hashConfig(config) });
   cachedCredentials = config;
 
+  const port = Number.parseInt(config.port, 10);
+  if (!Number.isInteger(port) || port < 1 || port > 65535) {
+    console.error("Invalid proxy port:", config.port);
+    return;
+  }
+
   chrome.proxy.settings.set({
     value: {
       mode: "fixed_servers",
       rules: {
         singleProxy: {
           scheme: config.scheme || "http",
           host: config.host,
-          port: parseInt(config.port, 10)
+          port
         },
         bypassList: config.bypassList || ["localhost", "127.0.0.1"]
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Apply proxy settings
async function applyProxySettings(config) {
if (!config || !config.host || !config.port) {
chrome.proxy.settings.clear({ scope: "regular" });
await chrome.storage.local.set({ proxyConfig: null, lastConfigHash: null });
cachedCredentials = null;
return;
}
await chrome.storage.local.set({ proxyConfig: config, lastConfigHash: hashConfig(config) });
cachedCredentials = config;
chrome.proxy.settings.set({
value: {
mode: "fixed_servers",
rules: {
singleProxy: {
scheme: config.scheme || "http",
host: config.host,
port: parseInt(config.port, 10)
},
bypassList: config.bypassList || ["localhost", "127.0.0.1"]
}
},
scope: "regular"
});
}
// Apply proxy settings
async function applyProxySettings(config) {
if (!config || !config.host || !config.port) {
chrome.proxy.settings.clear({ scope: "regular" });
await chrome.storage.local.set({ proxyConfig: null, lastConfigHash: null });
cachedCredentials = null;
return;
}
await chrome.storage.local.set({ proxyConfig: config, lastConfigHash: hashConfig(config) });
cachedCredentials = config;
const port = Number.parseInt(config.port, 10);
if (!Number.isInteger(port) || port < 1 || port > 65535) {
console.error("Invalid proxy port:", config.port);
return;
}
chrome.proxy.settings.set({
value: {
mode: "fixed_servers",
rules: {
singleProxy: {
scheme: config.scheme || "http",
host: config.host,
port
},
bypassList: config.bypassList || ["localhost", "127.0.0.1"]
}
},
scope: "regular"
});
}
🤖 Prompt for AI Agents
In `@brightdata-proxy-extension/background.js` around lines 48 - 74, The
applyProxySettings function uses parseInt(config.port, 10) without validating
the result, so you must validate the parsed port is a finite integer in the
valid TCP range (1–65535) before calling chrome.proxy.settings.set or saving
config/cachedCredentials; if the port is invalid (NaN, non-integer, or out of
range) clear the proxy (chrome.proxy.settings.clear), set storage
proxyConfig/lastConfigHash to null, reset cachedCredentials, and optionally log
the bad config, otherwise continue to save proxyConfig/lastConfigHash, set
cachedCredentials and call chrome.proxy.settings.set with the validated integer
port.

Comment on lines +180 to +184
| Country | Username Suffix |
|---------|-----------------|
| India | `-country-in` |
| United States | `-country-us` |
| United Kingdom | `-country-gb` |
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix table divider spacing to satisfy markdownlint MD060.

✅ Proposed formatting fix
-|---------|-----------------|
+| ------- | ---------------- |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| Country | Username Suffix |
|---------|-----------------|
| India | `-country-in` |
| United States | `-country-us` |
| United Kingdom | `-country-gb` |
| Country | Username Suffix |
| ------- | ---------------- |
| India | `-country-in` |
| United States | `-country-us` |
| United Kingdom | `-country-gb` |
🧰 Tools
🪛 markdownlint-cli2 (0.20.0)

181-181: Table column style
Table pipe is missing space to the right for style "compact"

(MD060, table-column-style)


181-181: Table column style
Table pipe is missing space to the left for style "compact"

(MD060, table-column-style)


181-181: Table column style
Table pipe is missing space to the right for style "compact"

(MD060, table-column-style)


181-181: Table column style
Table pipe is missing space to the left for style "compact"

(MD060, table-column-style)

🤖 Prompt for AI Agents
In `@README.md` around lines 180 - 184, The Markdown table header and divider need
consistent spacing so markdownlint MD060 is satisfied: edit the table that
starts with the header "Country | Username Suffix" and the rows containing
`-country-in`, `-country-us`, and `-country-gb` to use uniform single spaces
around each pipe and ensure the separator row uses matching column widths (e.g.,
`|---------|-----------------|`) so the pipes in the header, separator, and data
rows align consistently.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant