Add or remove proxy based on url in chrome extension - javascript

I want to proxy only certain domains through my chrome extension but i need to do some checks on current url to decide if i need to proxy.
chrome.webRequest.onBeforeRequest.addListener(function(d){
chrome.proxy.settings.set({value: getProxyConfig(d.url), scope: 'regular'},function() {});
},{urls:[
"http://*/*",
"https://*/*"]},
["blocking"]);
The function getProxyConfig(d.url) is a simple function that will return the appropriate ProxyConfig object(mode is direct or fixed_servers) based on the url. The function doesn't make any external calls and just fetch a list of domains from local storage and does comparison.
What is the problem ?
Chrome try to proxy some urls that should not have been proxied leading to a ERR_TUNNEL_CONNECTION_FAILED because the proxy will allow only specific domains to be proxied.If i log getProxyConfig(d.url)output to the console for urls that are falling i see the mode as direct as expected.Note: An html page may contain both links that must/must not be proxied.
chrome.proxy.settings.set is asynchronous so i am thinking that maybe chrome.webRequest.onBeforeRequest is done executing before chrome.proxy.settings.set

You cannot modify proxy automatically while a request is being made. You have to Create a custom PAC script, where the script decides whether to proxy a URL or not:
const 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() {}
)
Regarding, chrome.webRequest.onBeforeRequest , this is called AFTER the proxy been set, hence why you will get a isProxy attribute there.

Related

How to call browser function with URL/URI local to the client?

I want to call a browser function, e.g. AddSearchProvider(engineURL), which requires an URL to a XML-file. However, I want the user to generate the content of the XML-file himself, thus I want to call the function by passing (a reference to) the user generated content directly. It is key that all of this happens client-side only, such that no server is required to temporarily host files in the process.
I tried to encode the XML file into the URI:
uri = "data:application/xml;charset=utf-8," + encodeURIComponent($('#edit-search-engine').val());
window.external.AddSearchProvider(uri);
But Firefox (57.0) rejects this approach with the following message:
I guess¹ Firefox expects a "true", remote, URL. How can I realize the above functionality without a server in the loop?
¹Update: Firefox is indeed enforcing the URL to be of HTTP, HTTPS or FTP:
[...]
// Make sure the URLs are HTTP, HTTPS, or FTP.
let isWeb = ["https", "http", "ftp"];
if (isWeb.indexOf(engineURL.scheme) < 0)
throw "Unsupported search engine URL: " + engineURL;
if (iconURL && isWeb.indexOf(iconURL.scheme) < 0)
throw "Unsupported search icon URL: " + iconURL;
[...]

Can WinJS xhr make HTTPS request?

For example it needs to call a web service hosted with SSL.
If it can, how to pass the client certificate then?
Thanks a lot!!
WinJS.xhr({
type: "GET",
url: "https://localhost:442/WebService1.asmx?op=Login",
}).then(function success(res) {
var debug1 = res.responseText + res.responseURL;
}, function error(err) {
var debug2 = err.responseText + err.responseURL;
}, function completed(result) {
if (result.status === 200) {
// do something
}
});
The debugging point will jump to 'complete(result)' function, but the status code is '0'. Even if I change URL to other https site (e.g. https://www.w3.org), result is the same.
------------- Update 1 ---------------------
If it's in C# I could use following code to pass client certificate. However if I want to change origial WinJs.xhr to HttpClient, just copy & paste seems not working as .js file could not understand all syntax?
var certQuery = new CertificateQuery();
var cert = (await CertificateStores.FindAllAsync(certQuery)).FirstOrDefault(c=>c.Issuer.StartsWith("xxxx",StringComparison.CurrentCultureIgnoreCase));
var filter = new HttpBaseProtocolFilter();
if (cert != null)
{
filter.ClientCertificate = cert;
filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted | ChainValidationResult.InvalidName);
}
var hc = new Windows.Web.Http.HttpClient(filter);
var uri = new Windows.Foundation.Uri(url);
hc.getStringAsync(uri).done({.......});
E.g.
1) How to write 'Using .... ' in JS file?
2) How to use "await" or "'FindAllAsync'" in this line? etc.
var cert = (await CertificateStores.FindAllAsync(certQuery)).FirstOrDefault(c=>c.Issuer.StartsWith("xxxx",StringComparison.CurrentCultureIgnoreCase));
WinJS.xhr wraps XMLHttpRequest( https://msdn.microsoft.com/en-us/library/windows/apps/br229787.aspx ) with a Promise-like interface (a WinJS Promise, not an ES6 Promise, but the concept is similar).
XMLHttpRequest has the withCredentials property which allows you to specify whether client-side credentials, including client-side certificates, should be sent or not - but there is no API that would allow you to specify which specific client-side certificate should be used.
Fortunately WinJS exposes the Windows.Web.Http.HttpClient type which gives you more control over client authentication, including client-side certificates - but your UWP application must have "Enterprise capability" to use the user's My certificate store - otherwise non-Enterprise UWP applications only have certificates in their Application Certificate Store:
https://blogs.windows.com/buildingapps/2015/11/23/demystifying-httpclient-apis-in-the-universal-windows-platform/#Dr3C9IMHv5pTPOrB.97
You must first add it to the app’s certificate store by following these instructions. Apps with enterprise capability can also use existing client certificates in the user’s ‘My’ store.

window.location.state does not work as expected

I have one situation,where i need to pass some json from one window to new window in the same domain.I have first window lets say it window1 and second window,let say it window2.
I have following code in window1:
var params = [
'height=750',
'width=720',
'scrollbars=yes',
'left=0',
'top=0',
'fullscreen=no', // only works in IE, but here for completeness
'location=no'
].join(',');
var port = location.port;
var url = "http://" + hostName + ':' + port + "/isecalreport" + location.search;
var newWindow = window.open(url,'photocal_report',params);
while(true){
if(newWindow! == undefined) {
newWindow.location.state={payloadFromIseCalWeekly : payloadFromIseCalWeekly,instrumentIdObj : instrumentIdObj};
break;
}
}
Code in window2:
var payloadFromIseCalWeekly = location.state.payloadFromIseCalWeekly ? location.state.payloadFromIseCalWeekly : {};
I want to make use of the json set in window.location.state.
So the problem is ,It works fine in chrome ,mozilla,but fails in IE 11(when debugger is not open.)
When i open debugger in IE11 ,it works fine.
I debugged it and found out that after the instruction which is opening the new window ,the next instruction get run and it doesnot find the new window object.
Its strange as it works when developer console is open.
It would be good if i can get insights about how to resolve the issue.
My aim is to open a new window ,to which i need to pass some data and using that data i want to do an API call.
With few exceptions, you cannot tell one window, tab or frame to talk to another directly. This is to prevent malicious scripts in one of these contexts from hijacking another across pages.
To cope with this, you have two options, you can use postMessage() or simply pass your data via the URL that you open in the new window. There are technically more options if you're on the same domain, but I recommend against going down that rabbit hole.
Sending data via the URL is a one-way affair. You can send query string variables in the URL that the new window can read, but it can't send anything back to the window that created it.
postMessage(), on the other hand, can be used to communicate between multiple contexts and across domains. It is considered secure because it requires that all participants be listening for messages, rather than allowing direct code access.
Your various pages can listen for messages with a simple event listener.
// listen for incoming messages on this page
window.addEventListener('message', function(e) {
// this is the handler function
// do we trust where this was sent from?
if (event.origin !== "http://example.com") {
// if so, print the resulting event object
console.log('message received', e);
}
}, false);
You can then send a message from another page to your window.
// the * is the targetOrigin, read the docs!
newWindow.postMessage("some message data", "*");

Cross-frame cross-site scripting - creating a webpage reloader/watchdog

Setup:
There are remote measurement stations, there is centralized collection/processing/presentation server (with a webserver) and there are observation stations which are to display the collected data for the customers.
These observation stations consist of bare bones simple embedded computer equipped with web browser working in kiosk mode displaying one specific webpage from the central server each. This webpage is updated with AJAX displaying latest measurements of given measurement station. Connected to a fixed monitor, these stations should run almost maintenance-free for years.
Now we've worked out most of the kinks but there's the matter: what if the webserver fails?
The browser will load a "unreachable", "404", "Permission denied", "500", or whatever mode of failure the server took at that point, and remain there until someone manually reboots the observation station.
The general solution I came up with is to set the browser's home page not to the observed page, but to an always-available local HTML file which would perform periodic checks if the remote page was loaded and updates correctly, and reload it if it fails to perform for any reason.
Problem:
the problem lies in cross-frame-scripting. I guess the target webpage would have to load as a frame, iframe, object of type text/HTML, or some other way that will make it show up without removing/disabling the local "container" file. I wrote a cross-frame-scripting page a couple years ago, and circumventing the security counter-measures wasn't easy. Since then the security must have been tightened.
So, the page loaded from remote server contains a piece of javascript that is launched periodically (some setInterval) if everything went well, or doesn't if something was broken. Periodic arrival of this signal to the container frame makes it reset its timeout and not take any other action.
In case the signal does not arrive, as the timeout expires, the container begins periodically refreshing the loaded webpage, until the server is fixed and proper content is loaded, signalling that to the loader.
How do I get the remote page to signal "alive" (say, setting a variable) to the local (container) page loaded from a file:// URL each time a specific function is triggered?
There is a library called porthole which basically does what SF.'s answer describes but in a more formal form. I just wrote a web page to switch showing one of two iframes. In the top level web page I have
var windowProxy;
windowProxy = new Porthole.WindowProxy(baseURL + '/porthole/proxy.html', frameId);
windowProxy.addEventListener(onMessage);
...
function onMessage(messageEvent) {
if (messageEvent.origin !== baseURL) {
$log.error(logPrefix + ': onMessage: invalid origin');
console.dir(messageEvent);
return;
}
if (messageEvent.data.pong) {
pongReceived();
return;
}
$log.log(logPrefix + ': onMessage: unknown message');
console.dir(messageEvent);
}
...
var sendPing = function () {
$log.log(logPrefix + ': ping to ' + baseURL);
...
windowProxy.post({ 'ping': true });
};
plus some additional control logic. In the child web page the following is everything I had to add (plus a call to portholeService.init() from a controller):
// This service takes care of porthole (https://github.com/ternarylabs/porthole)
// communication if this is invoked from a parent frame having this web page
// as a child iframe. Usage of porthole is completely optional, and should
// have no impact on anything outside this service. The purpose of this
// service is to enable some failover service to be build on top of this
// using two iframes to switch between.
services.factory('portholeService', ['$rootScope', '$log', '$location', function ($rootScope, $log, $location) {
$log.log('Hello from portholeService');
function betterOffWithFailover() {
...
}
function onMessage(messageEvent) {
$rootScope.$apply(function () {
if (messageEvent.origin !== baseUrl) {
$log.error('onMessage: invalid origin');
console.dir(messageEvent);
return;
}
if (!messageEvent.data.ping) {
$log.error('unknown message');
console.dir(messageEvent.data);
return;
}
if (betterOffWithFailover()) {
$log.log('not sending pong');
return;
}
windowProxy.post({ 'pong': true });
});
}
var windowProxy;
var baseUrl;
function init() {
baseUrl = $location.protocol() + '://' + $location.host() + ':' + $location.port();
windowProxy = new Porthole.WindowProxy(baseUrl + '/porthole/proxy.html');
windowProxy.addEventListener(onMessage);
}
return {
init: init
};
}]);
For reference these pages are using AngularJS in case $rootScope.$apply etc was unfamiliar to you.
The method for cross-frame, cross-site communication is using postMessage.
The contained frame, on each correct execution should perform:
window.top.postMessage('tyrp', '*');
The container document should contain:
window.onmessage = function(e)
{
if (e.data == 'tyrp') {
//reset timeout here
}
};

read/write Google Spreadsheet cell feed with Javascript

I try to read and write a cell in google spreadsheet with http request by javascript. The "read" operation works, but the "write" operation fail.
Please help to point out which part I should modify in my code of "write" operation.
The write example I followed is from here https://developers.google.com/google-apps/spreadsheets/,
and it is not working.
My read operation (this is working):
http_request.onreadystatechange = function() {
process_cellrw(http_request);
};
http_request.open('GET',"https://spreadsheets.google.com/feeds/cells/0Aqed....RHdGc/od6/private/full/R1C1", true);
http_request.setRequestHeader('Authorization','Bearer ' + strAccessToken);
http_request.send(null);
My write operation (this is not working):
var testxml = ['<entry xmlns="http://www.w3.org/2005/Atom" <br>
xmlns:gs="http://schemas.google.com/spreadsheets/2006">',<br>
'<id>https://spreadsheets.google.com/feeds/cells/0Aqed....RHdGc/od6/private/full/R1C1</id>',<br>
'<link rel="edit" type="application/atom+xml"<br> href="https://spreadsheets.google.com/feeds/cells/0Aqed....RHdGc/od6/private/full/R1C2/9zlgi"/>',<br>
'<gs:cell row="1" col="1" inputValue="xxxx"/>',<br>
'</entry>'].join('');<br>
http_request.onreadystatechange = function() {
process_cellrw();
};
http_request.open('PUT',"https://spreadsheets.google.com/feeds/cells/0Aqed....RHdGc/od6/private/full/R1C2/9zlgi");
http_request.setRequestHeader('Authorization','Bearer ' + strAccessToken);
http_request.setRequestHeader('GData-Version','3.0');
http_request.setRequestHeader('Content-Type', 'application/atom+xml');
http_request.setRequestHeader('If-Match','*');
http_request.setRequestHeader('Content-Length', testxml.length.toString());
http_request.send(testxml);
The write operation always receive http_request.status = 0 at callback function process_cellrw().
My environment is Windows 7 + Chrome browser. I also tested it on Android + Webkit, still fails.
I also tested to add a row by list feed, also fails by receive http_request.status = 0.
I know this doesn't answer your question, but I would open up the Chrome "Developer Tools", go to "Network" and inspect the response from google for the API call. It may contain headers that explain what failed...
I found the root cause : cross domain XMLHttpRequest POST/PUT are not support by docs.googole.com and spreadsheets.google.com
The XMLHttpRequest POST/PUT will first send a HTTP OPTIONS request header to the resource on the other domain, in order to determine whether the actual request is safe to send. But docs.googole.com and preadsheets.google.com always reply "404 Not Found" for this request. That's why I always received http_request.status = 0 at callback function process_cellrw().
One solution is to use another CGI which allows cross domain HTTP request, such as PHP.
Another solution is to implement the write operation with the function UrlFetchApp to send HTTP PUT request in Google Apps Script, and then we can use XMLHttpRequest GET to trigger this Apps Script.

Categories