I need to make a customized proxy settings for my chrome extension so that traffic to a specific domain goes through a proxy server while all other (user) traffic goes normally with the default system settings. According to the chrome API documentation, the only way is to use pac_script (correct me if I am wrong). Thus, the code will be something like this:
var config = {
mode: "pac_script",
pacScript: {
data: "function FindProxyForURL(url, host) {\n" +
" if (dnsDomainIs(host, 'mydomain.com') )\n" +
" return 'SOCKS5 10.0.0.1:1234';\n" +
" return 'DIRECT';\n" +
"}"
}};
chrome.proxy.settings.set({value: config, scope: 'regular'});
However, pac_script does not have the option to route traffic using system proxy settings (only 'DIRECT' which means it will skip the system settings). I thought to obtain the system proxy settings using chrome.proxy.settings.get, but this function returns an object where mode = system with no useful information.
Does anyone know how to obtain the system proxy settings from extension? Or has suggestions to handle the original problem?
You can get it with
chrome.proxy.settings.get(
{'incognito': false},
function(config) {
console.log(JSON.stringify(config));
});
It should show you something like this:
{
"levelOfControl":"controlled_by_this_extension",
"value":
{
"mode":"pac_script",
"pacScript": {
"data": "function FindProxyForURL(url, host) {\n
return \"PROXY 10.0.0.1:1234;\";\n}",
"mandatory": false
}
}
}
Read the doc for more detailed
https://developer.chrome.com/extensions/proxy
Related
I am trying to integrate the credential manager API into my extension so I can save the API credentials using it but am having issues.
Just for testing purposes, I just tried saving the credentials from the popup (Where the user login happens) and when I tried that I got an error saying "NotSupportedError: The user agent does not support public key credentials." I did some googling but didn't find what I was looking for. I thought it might have something to do with the popup so I tried doing the same through the background script but it can't access the window object so that didn't work.
I haven't yet tried doing this though a content script which I would imagine would work but ideally I'd prefer to just store it when the user logs in and then just call it as I need it.
Here is the basic code I'm using for testing that I grabbed from the MDN site;
if ("PasswordCredential" in window) {
let cmAPICredential = new PasswordCredential({
id: "extension",
name: uname,
password: pwd
});
navigator.credentials.store(cmAPICredential).then(() => {
if (isDebugMode) {
chrome.runtime.sendMessage({ message: "debug_log", str: `**LOGIN DEBUGGING** | Credentials stored in the user agent's credential manager.`, cpage: "main.js" }, function(response) {});
}
}, (err) => {
if (isDebugMode) {
chrome.runtime.sendMessage({ message: "debug_log", str: `**LOGIN DEBUGGING** | Error while storing the credential: ${err}`, cpage: "main.js" }, function(response) {});
}
});
} else {
if (isDebugMode) {
chrome.runtime.sendMessage({ message: "debug_log", str: `**LOGIN DEBUGGING** | Password credential isn't supported in this browser`, cpage: "main.js" }, function(response) {});
}
}
Is there any way that I can do what I am attempting? The only thing I really want to secure is the password and this seems to be the only good way to do it through a chrome extension.
Based on #wOxxOm response and some other research, it looks like I can't use the credential management API with chrome extensions.
So the answer to my question is you can't.
My application uses an end-to-end testing framework (Cypress) that outputs results in the
terminal. When testing changes to frontend code, I use the Vue dev server's
proxy option to route API requests to a remote backend server.
Due to the high load of requests pummeling this test server, our application
frontend expects certain requests to fall through, and it is able to
intelligently handle retrying a given failed API call. However, the proxy
handler doesn't know this, and as a result the console log becomes cluttered
with ECONNRESET proxy errors, as can be seen below.
Proxy error: Could not proxy request /api/XXXXXX from local.myapp.io:3000 to https://myapp-dev.appspot.com.
See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (ECONNRESET).
Proxy error: Could not proxy request /api/XXXXXX from local.myapp.io:3000 to https://myapp-dev.appspot.com.
See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (ECONNRESET).
Proxy error: Could not proxy request /api/XXXXXX from local.myapp.io:3000 to https://myapp-dev.appspot.com.
See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (ECONNRESET).
Proxy error: Could not proxy request /api/XXXXXX from local.myapp.io:3000 to https://myapp-dev.appspot.com.
See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (ECONNRESET).
✓ C22756 should be disabled when no events exist (8018ms)
✓ C22757 should be clickable when events exist (2250ms)
Widget Settings
Proxy error: Could not proxy request /api/XXXXXX from local.myapp.io:3000 to https://myapp-dev.appspot.com.
See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (ECONNRESET).
✓ C22758 should have name, date range, and account fields (2435ms)
These errors can get quite verbose and tend to hide/obscure the more laconic
test output, making it difficult to scan and nearly impossible to copy
effectively.
Is there any way to suppress these error messages from the dev server/proxy
handler? Ideally, there would be some form of configuration in an NPM script
or the Vue/Webpack config JS file that would allow me to ignore or hide these
errors just when running our test suite.
For reference, here is the config object being passed to the vue.config.js
file's devServer property:
const config = {
allowedHosts: ['localhost', 'local.myapp.io'],
contentBase: path.join(__dirname, 'root/of/frontend/code'),
disableHostCheck: true,
historyApiFallback: true,
https: {
cert: LocalDevCert.getCert(),
key: LocalDevCert.getKey()
},
port: 3000,
proxy: {
'^(?!/(js|modules|img))': {
target: 'https://myapp-dev.appspot.com',
cookieDomainRewrite: 'local.myapp.io',
changeOrigin: true,
secure: true,
bypass (req, res) {
const xsrfToken = uuid();
if (req.headers.accept && req.headers.accept.includes('html')) {
res.cookie('MyXSRFToken', xsrfToken);
res.cookie('MyXSRFToken_C80', xsrfToken);
return '/index.html';
}
if (req.method === 'GET' && req.url.includes('login') && !req.url.includes('google')) {
res.cookie('MyXSRFToken', xsrfToken);
res.cookie('MyXSRFToken_C80', xsrfToken);
return '/';
}
}
}
},
public: 'local.myapp.io:3000'
};
I have tried:
Using the devServer.client.logging configuration option, but this only changes what is shown in the browser console (not the command-line terminal).
Adding the logLevel configuration option to the corresponding proxy configuration object (i.e. object with key ^(?!/(js|modules|img)) above), but this only seems to increase the amount of logging (info as well as errors).
I suspect something needs to be suppressed at a lower level, and perhaps some bash-script magic is the only plausible solution.
You could add a filter at the top of the test, or globally in /cypress/support/index.js.
Cypress.on('window:before:load', window => {
const originalConsoleLog = window.console.log;
window.console.log = (msg) => {
const isProxyError = msg.includes('ECONNRESET') || msg.startsWith('Proxy error:');
if (!isProxyError) {
originalConsoleLog(msg)
}
}
})
The workaround that I eventually settled on was intercepting process.stdout.write in the Javascript file that configured and ran the dev server/tests. Since our test command is implemented as a Vue CLI plugin, I could just put the override function in that plugin's module.exports as follows:
module.exports = (api, options) => {
api.registerCommand(
'customTestCommand',
{
description: 'A test command for posting on Stack Overflow',
usage: 'vue-cli-service customTestCommand [options]',
options: {
'--headless': 'run in headless mode without GUI'
...
}
},
async (args, rawArgs) => {
// Do other setup work here
process.stdout.write = (function (write) {
let clearBlankLine = false;
return function (...args) {
const string = args[0];
const isProxyError = !!string.match(/Proxy error:|ECONNRESET/);
const hasContent = !!string.trim();
if (isProxyError) {
clearBlankLine = true;
return;
}
if (!clearBlankLine || hasContent) {
write.apply(process.stdout, args);
}
clearBlankLine = false;
};
}(process.stdout.write));
// Wait to run your actual dev server process and test commands
// Until here.
}
);
};
This maintains all coloring, spacing, history clearing, etc. on the command line output, but any line that has the terms Proxy Error or ECONNRESET are removed. You can customize the regex to your specific needs.
For a more detailed example of intercepting both stdout and stderr, as well as redirecting those outputs to different locations (e.g. files), see the following Github gist by Ben Buckman that inspired my solution: https://gist.github.com/benbuckman/2758563
I'm trying to handle proxy authentication though chrome extensions.
On the one hand I have chrome extension (with all permissions established) that sends CONNECT request with onAuthRequired handler (background.js)
chrome.webRequest.onAuthRequired.addListener(
(details, callback) => {
console.log('onAuthRequired', details) // <- this has never been displayed
callback({
authCredentials: {
username: 'someid',
password: 'somepwd'
}
})
},{
urls: ['<all_urls>']
},
['asyncBlocking']
)
const config = {
mode: "pac_script",
pacScript: {
data: "function FindProxyForURL(url, host) {\n if (shExpMatch(host, \"*.pandora.com\"))\n return 'PROXY 127.0.0.1:8124';\n return 'DIRECT';\n }"
}
}
chrome.proxy.settings.set({
value: config,
scope: 'regular',
}, function(){})
And on the other hand I have NodeJS proxy server that always sends the 407 status code as described in the specifications
const http = require('http');
const proxy = http.createServer()
proxy.on('connect', (req, clientSocket, head) => {
clientSocket.write('HTTP/1.1 407 Proxy Authentication Required')
clientSocket.write('Proxy-Authenticate: Basic realm="Access to site"\r\n\n')
});
proxy.listen(8124)
Finally, the browser returns ERR_PROXY_AUTH_UNSUPPORTED which means that the status code is sent correcly...
The fact is onAuthRequired is never triggered, can anyone tell me why ?
Thank you in advance
Chrome aggressively caches the authentication calls to proxy servers. So you will only see your console.log call once per browser session (you need to restart Chrome completely, if you've got more than one profile open you'll need to close ALL instances of Chrome before the authentication cache is cleared).
I'm currently trying to figure out how to clear or empty said cache.
See also (How to delete Proxy-Authorization Cache on Chrome extension?)
So Bypasslist in chrome.proxy will bypass the list from using proxy, but what i need is proxy only some of website i want like only 4 website and except that bypass by the proxy, if i need to make a list inside bypass list it will be hell.
Can i do that with chrome.proxy ? So Only Some Website Get Proxied.
The documentation provides an example where you specify a PAC script:
var config = {
mode: "pac_script",
pacScript: {
data: "function FindProxyForURL(url, host) {\n" +
" if (host == 'foobar.com')\n" +
" return 'PROXY blackhole:80';\n" +
" return 'DIRECT';\n" +
"}"
}
};
chrome.proxy.settings.set(
{value: config, scope: 'regular'},
function() {});
The example does exactly what you require for 1 domain, and it should be possible to extend to several domains.
I want to connect my node.js client (node-webkit) to a socket.io server that I do not have direct access to. The server requires a connection to match a certain origin in the request header, but I cannot find a way to change it.
This is what I have:
var io = require('socket.io-client');
socket = io.connect( url );
To reproduce, the socket.io package needs to be installed using npm
It connects to the server, sends some 'pings' and 'pongs' after which the server terminates the connection (as expected because of the wrong origin).
Note that I cannot change the server, so simply adding an origin to the white-list is no possibility.
Things I've tried so far
Using extraHeaders like this:
socket = io.connect( url, {extraHeaders: {origin: url}} )
..but that doesn't change the origin. I've tried to edit other/custom headers but the extraHeaders option doesn't seem to do anything.
I've also looked into the node cors module, but the documentation only provides examples on how to set an origin for express app requests, not socket.io.
And lastly I've simply tried to set the global origin variable to the right url, but that isn't picked up by socket.io.
Note: To check whether any of these methods work I looked at the 'Network' tab in the DevTools to see the request headers.
For future reference, anyone using socket.io-client and having the same problem, you can resolve this by creating a tiny web-extension which is then added to your browser. You of course must add this as an installation condition of your app.
In the content-script and background script, add an intercepting request event handler. The handler can update the origin header value to the current tab source origin or whatever.
content-script.js
(async () => {
await browser.runtime.sendMessage({action: "setActiveTabOrigin"})
})();
background.js
browser.runtime.onMessage.addListener(async (params) => {
if (params.action == "setActiveTabOrigin") {
window.origin = await browser.tabs.query({currentWindow: true})
.then((tabs) => {
for (var tab of tabs) {
if (tab.active) {
const url = new URL(tab.url)
return url.origin
}
}
})
}
});
function rewriteWSHeaders(e) {
for (var header of e.requestHeaders) {
if (header.name.toLowerCase() == "origin") {
if (header.value == "null") {
console.log("!!! Setting request origin to the active tab url origin : ", window.origin)
header.value = window.origin
}
}
}
return {requestHeaders: e.requestHeaders};
}
// For the API details, refer to [mozilla's docs][1]
browser.webRequest.onBeforeSendHeaders.addListener(
rewriteWSHeaders,
{urls:["ws://<socket.io-server.host>/*"], types:["websocket"]},
["blocking", "requestHeaders"]
);
Partial web-extension manifest.json :
"background": {
"scripts": ["background.js"]
}
"content_scripts": [
{
"matches": ["<remoteHost>/*"],
"js": ["content-script.js"],
"runAt": "document_start"
}
]
"permissions": [
"<all_urls>",
"webRequest",
"webRequestBlocking",
"tabs"
],
Other prerequisites not explained but easily looked up
This option is only useful when you target a known remote host because the remote host must be added to the 'cors allowed origins' list, passed-in as a python-socket.io constuctor parameter.
The web-extension origin must also be passed to this list
websock = socketio.AsyncServer(
async_mode='asgi',
logger=logger,
cors_allowed_origins=[extOrigin, <remoteHost>])
You don't need to change any thing from client side, it is a server side configuration.
If you have access to there server just add your origin to the IO Server origins list or set it to *.
here is a how to link from socket.io docs.