I am writing a javascript extention that modifies a website. The extention adds a button to the website, and when clicked it modifies the page content. My script has to send cross domain xmlhttprequests to websiteB, and gets permissions upon installation to do so.
My problem is that the code that runs in the content scope of the website that I am modifying does not have permission to do cross domain requests, because it is within a content scope of the webpage. Nor can I easily pass variables between my main extention code and the code that runs upon the button click on the page.
These are the relevant parts of my code:
// ==UserScript==
// #include http://www.WebsiteB.com/*
// #match http://www.WebsiteB.com/*
// ==/UserScript==
var a = 1;
button.innerHTML = 'clickMe';
menu.appendChild(button);
contentEval(function() {
myFunc = function() {
//a is undefined
//this part does not have cross domain request permission
};
}
I use contentEval taken from http://wiki.greasespot.net/Content_Script_Injection to get the relevant code to be able to interact with the page my script is running on. Without it, I would not be able to call myFunc from a button on the website.
variables can not be transferred from the main code area to myFunc() by passing them directly using myFunc(a) either. myFunc(1) works, but does not suit my needs.
It's possible that I am going about things the wrong way. If so, please enlighten me.
Is there a way to have cross domain permissions from the code that interacts with the web page?
If not, what should I use to do message passing from the main code area to the content script of the page?
Related
I am using the code below to redirect users that did not come from google. It can also prevent direct access to that link without going to google. But why does when I view the page source of my domain ( view-source:https://owndomain.com/NMkrujlS ), it does not redirect to example.com? isn't viewing page source a direct access to my domain? How can I fix this?
Thanks
My code:
var ref = document.referrer;
if (ref.match(/^https?:\/\/([^\/]+\.)?google\.com(\/|$)/i)) {
// do nothing
} else {
// redirect
window.location.replace("https://example.com" + window.location.pathname);
}
The example you linked in the comments of your original question uses a header-based redirect, which is why when you point your instance of Chrome to view-source:rshrt.com/getlink/YCy, it redirects you to the target page. This is in line with how browsers are expected to handle header-based redirects. This fact is confirmed if you open your developer tools' Network tab and inspect the requests that your browser sends to the target site, which show the rshrt.com page redirecting with a 301 Moved Permanently status code (along with the respective Location header, which points to the target page):
In your own example, you're performing a redirect using JavaScript. This JavaScript isn't executed/interpreted by the browser when using view-source. As such, the redirection never happens.
If you'd like this to function moreso like the site you've given as an example, you'll have to configure these redirection rules at a server level to correctly return a 301 or 302 status code (as well as the appropriate Location header), instead of on the client level using JavaScript. How specifically you would accomplish that is entirely dependent on the stack your server runs, and is arguably outside the current scope of the question as you've posed it.
Background information: We have a platform which runs on https://system.example.com. This platform consists of 10 separate web applications (all written in PHP and JS). Each application has historically been in a sub-directory within the same subdomain:
https://system.example.com/app1/
https://system.example.com/app2/
...
https://system.example.com/app10/
We are in the process of rebuilding one of the applications, app2, and have decided to host this on a new separate subdomain, https://app2.example.com.
Part of the app2 application uses JavaScript to open a pop-up window for app10. Most functionality inside this popup works as expected. However, when attempting to use a "Save" button inside the popup my browser console was showing:
Uncaught DOMException: Blocked a frame with origin "https://app2.example.com" from accessing a cross-origin frame.
at https://system.example.com/app10/manage.php:1:334
I have read both SecurityError: Blocked a frame with origin from accessing a cross-origin frame and https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage but still unclear as to how to fix this.
The code and process I have is as follows:
The popup is opened from https://app2.example.com by a button which has an onclick event handler:
<button onclick="postToPopUp('https://system.example.com/app10/manage.php', 'fileManage', 'width=800px,height=600px', ['f_id', '123'], 'app2', 'filesCallbackManage')">Open app10</button>
The postToPopup() function is used to pass POST data from app2 into https://system.example.com/app10/manage.php based on Javascript window.open pass values using POST - this works fine.
The problem occurs when I click a "Save" button inside the popup which renders the following markup within the popup window:
<!doctype HTML><html><head><title>upload</title>
<script type="text/javascript" language="javascript" charset="utf-8">
var fileObject = {"files":{"0":{"f_id":"1784","f_title":"test07.pdf"},"f_id":123}};
window.opener.filesCallbackManage(fileObject);
window.close();
</script><body></body></html>
What this did originally - when everything was under the same subdomain - was called a js function filesCallbackManage() which resided in the code for https://system.example.com/app2. The function itself was passed an object, fileObject, which updated various parts of the UI inside app2. The popup was closed after clicking the Save button due to window.close();
Although I've read about using postMessage I don't understand how this fits in or whether this is even the correct solution to my problem? The data is being posted from the subdomain https://app2.example.com to https://system.example.com/app10 correctly. The problem is that filesCallbackManage() won't fire because of the cross origin restriction. Inside my code for https://app2.example.com I have a simple statement to see if it's firing:
function filesCallbackManage(data)
{
console.log('filesCallbackManage has fired');
}
This never fires because of the problem I have. I get the console error mentioned previously and a blank popup window (technically this is correct since there is nothing in the <body> tag in the above markup) but the window doesn't close and the callback isn't fired.
The example given on the Mozilla website isn't extensive enough to understand how it can be adapted to this type of scenario. Please can someone elaborate? Furthermore, the linked Stack Overflow post is four years old so I want to be sure anything I put on this is secure and up-to-date.
The postToPopup() function is used to pass POST data
Submitting a form across origins is fine. So you can do this.
The problem occurs when I click a "Save" button inside the popup
You're trying to access the DOM of the window across origins. This is forbidden.
Although I've read about using postMessage I don't understand how this fits in or whether this is even the correct solution to my problem?
postMessage is as close as you can get to accessing the DOM of a window across origins.
You can't do this.
var fileObject = {"files":{"0":{"f_id":"1784","f_title":"test07.pdf"},"f_id":123}};
window.opener.filesCallbackManage(fileObject);
Instead you have to send a message:
window.opener.postMessage(fileObject, "https://system.example.com");
And have code which listens for it:
addEventListener("message", receiveMessage);
function receiveMessage(event) {
if (event.origin !== "http://app2.example.com") { return false; }
filesCallbackManage(event.data);
}
I've searched many forums and am pretty confident this will be a no, but I thought I would open it up to the community just in case ;)
I've been tasked with creating a tool on our Google Sites pages that records the visit times of our employees after visiting a page. It helps with confirming compliance with document access as well as activity logs. If an iFrame is on the same domain as the page it is hosted on, it's fairly easy to query the URL of the parent page from within the frame, but security limitations restrict this across domains or sub-domains.
I'm hoping that the fact that I am embedding a Google apps script into a Google sites page will give me more options. So far, I have tried the commands document.referrer, parent.document.location, parent.window.document.location, parent.window.location, parent.document.location.href, and the same commands from window and document perspectives. They all respond the same:
https://n-labp6vtqrpsdn12345neycmicqw7krolscvdkda-0lu-script.googleusercontent.com/userCodeAppPanel
When I want:
https://sites.google.com/mysite.com/mysite/test/test3
Do any Google veterans have additional tricks?
Edit: I've just tried to pass variables via an html link the Google image placeholder for Apps Scripts on Google Sites and got a tad bit farther. You see, I can run this url: https://script.google.com/a/macros/coordinationcentric.com/s/AKfycbxDX2OLs4LV3EWmo7F9KuSFRljMcvYz6dF0Nm0A2Q/exec?test=hello&test2=howareyou and get the variables test1 and test2 if I run the url in a separate window. If I try to embed that URL into the HTML page on Google Sites, it throws this mixed-content error:
trog_edit__en.js:1544 Mixed Content: The page at
'https://sites.google.com/a/mysite.com/mysite/test/test3' was loaded over HTTPS, but requested an insecure image 'http://www.google.com/chart?chc=sites&cht=d&chdp=sites&chl=%5B%5BGoogle+Apps+Script%27%3D20%27f%5Cv%27a%5C%3D0%2710%27%3D499%270%27dim%27%5Cbox1%27b%5CF6F6F6%27fC%5CF6F6F6%27eC%5C0%27sk%27%5C%5B%22Apps+Script+Gadget%22%27%5D%27a%5CV%5C%3D12%27f%5C%5DV%5Cta%5C%3D10%27%3D0%27%3D500%27%3D197%27dim%27%5C%3D10%27%3D10%27%3D500%27%3D197%27vdim%27%5Cbox1%27b%5Cva%5CF6F6F6%27fC%5CC8C8C8%27eC%5C%27a%5C%5Do%5CLauto%27f%5C&sig=TbGPi2pnqyuhJ_BfSq_CO5U6FOI'. This content should also be served over HTTPS.
Has someone tried that approach, perhaps?
In short - I understand it's not possible to investigate a parent URL from an iFrame in Google Sites.
The content of iframes/embedded content is hosted all over the place, separate from the site itself. The Same-Origin rules prevent checking as you've found.
Your first URL "https://n-labp...googleusercontent.com..." is where the script itself is hosted. Any output from the script, like HTML, will appear to come from here.
You can embed HTML and javascript directly in Sites using the Embed function. If you investigate that, you'll find that it's hosted at something like "https://1457130292-atari-embeds.googleusercontent.com..."
Calling parent will always give this *-atari-based URL, rather then the actual page it's hosted on.
A fairly lightweight solution is to use a combination of the two.
Use simple doGet pings and handle the work in your Apps Script.
On your Site, use Embed feature to insert:
<!DOCTYPE html>
<html>
<body onbeforeunload="return depart()">
<script>
var page = "testpage"; // manually set a name for each page you paste this code in to
var script = "https://script.google.com/macros/s/... your script, ending with exec ...";
fetch(script+"?page="+page+"&direction=arrive");
function depart(){
fetch(script+"?page="+page+"&direction=depart");
}
</script>
</body>
</html>
Then in your Apps Script:
function doGet(e){
var httpParams = e.parameter ? e.parameter : "";
// params is an object like {"page": "testpage1", "n": "1"}
var getPage = httpParams.page ? httpParams.page : "";
var getDirection = httpParams.direction ? httpParams.direction : "";
/* Handle it as you please, perhaps like: */
var user = Session.getActiveUser().getEmail();
/* maybe use a temporary active key if open to non-Google users */
/* first-time Google users will have to authenticate, so embed one frame somewhere full-size maybe, or just tell users to go to the script's link */
/* hand off to a helper script */
var time = new Date();
var timeUTC = time.toUTCString(); // I like UTC
doSomethingWithThis(user, direction, timeUTC);
/* etc... */
/* Return some blank HTML so it doesn't look too funny */
return HtmlService.createHtmlOutput("<html><body></body></html>");
}
Then publish as a web app. If you'll use temporary active keys instead of Google accounts, you'll have the script run as you and be available to anyone, even anonymous.
You've probably already solved this, but I hope it can be of use to someone else who stumbles across it!
I'm new at JavaScript, so can someone help me? How can I execute a command on another tab? For example, I have opened new tab from my main tab and opened translate.com (just for textbox) on it, and the problem is I don't know how to put text in search textbox?
I open the page with this command:
var page = window.open('https://www.translate.com/');
On the page, I can enter text with this code:
$string1 = $("#source_text");
$string1.val("text");
I have tried this, but this code doesn't work the way I want it to.
var page = window.open('https://www.translate.com/');
setTimeout(function(){page.f1()},20000);
setTimeout(function(){page.close()},30000);
function f1() {
$string1 = $("#source_text");
$string1.val("ka tu");
}
I find the function that you are trying to run very abnormal. So, let's start with small steps.
The page.close() function is perfect you can do that and it works. The other part won't work first of all because the page object created by window.open has no function called f1 on it.
Furthermore it is very important from where you are trying to run the script on the other window you always must take into consideration the cross-origin limitations. Easily explained if you try to run a function from a google.com tab on a separate window yahoo.com it won't work this is a security issue.
In order to run function f1 in that window it is important that f1 function is declared globally and ussualy you try and do this the following way page.window.f1() - and there you have it. So for example your code would be
page.window.$("#source_text").val('something');
refactoring your code would be like this:
var page = window.open('https://www.translate.com/');
setTimeout(function(){ page.window.$("#source_text").val('something');},20000);
setTimeout(function(){page.close()},30000);
open translate.com in a tab open dev tools in chrome and paste the above code in the console tab and see the results, it will work.
I recommend that before running code in another window you should check that that window is loaded first of all (in your case works) because of the long timeout.
A better solution would be using HTML5's postMessage API: check this link here for a good tutorial: https://robertnyman.com/2010/03/18/postmessage-in-html5-to-send-messages-between-windows-and-iframes/ - also this meens of course that the window you are opening is listening for some sort of postMessages.
In general you do this things with hosts that you manage and not other hosts because it might not work always. Also it will always work if you are on a host and open the same host in another window otherwise you end up with this security error: VM88:2 Uncaught SecurityError: Blocked a frame with origin "https://some_other_host" from accessing a frame with origin "https://www.translate.com". Protocols, domains, and ports must match.
Hope this helps, and you understand the way it works now.
Cheers
I am writing a simple lightbox-like plugin for my app, and I need to embed an iframe that is linked to an arbitrary page. The problem is, many web sites (for example, facebook, nytimes, and even stackoverflow) will check to see if is being embedded within a frame and if so, will refresh the page with itself as the parent page. This is a known issue, and I don't think there's anything that can be done about this. However, I would like the ability to know before hand if a site supports embed or not. If it doesn't, I'd like to open the page in a new tab/window instead of using an iframe.
Is there a trick that allows me to check this in javascript?
Maybe there is a server-side script that can check links to see if they permit an iframe embed?
I am developing a browser extension, so there is an opportunity to do something very creative. My extension is loaded on every page, so I'm thinking there's a way to pass a parameter in the iframe url that can be picked up by the extension if it destroys the iframe. Then I can add the domain to a list of sites that don't support iframe embed. This may work since extensions aren't loaded within iframes. I will work on this, but in the meantime....
Clarification:
I am willing to accept that there's no way to "bust" the "frame buster," i.e. I know that I can't display a page in an iframe that doesn't want to be in one. But I'd like for my app to fail gracefully, which means opening the link in a new window if iframe embed is not supported. Ideally, I'd like to check iframe embed support at runtime (javascript), but I can see a potential server-side solution using a proxy like suggested in the comments above. Hopefully, I can build a database of sites that don't allow iframe embed.
Check x-frame-options header by using following code
$url = "http://stackoverflow.com";
$header = get_headers($url, 1);
echo $header["X-Frame-Options"];
If return value DENY, SAMEORIGIN or ALLOW-FROM then you can't use iframe with that url.
Probably pretty late but what you need to do is make a request, likely from your server and look for the x-frame-options header. If it's there at all you can just open a new tab because if it is there is is one of the following: DENY, SAMEORIGIN, ALLOW-FROM. In any of these cases it's likely that you don't have access to open it in an iframe.
This subject has been discussed forever on the web with a particularly interesting (failed) attempt here:
Frame Buster Buster ... buster code needed
The bottom line is that even if you are able to construct a proxy that parses the contents of the page that you want in your iframe and removes the offending code before it is served to the iframe you may still come under "cease and desist" from the site if they get to hear about you doing it.
If you don't want your development to be widely available, you could probably get away with it. If you want your development to become popular, forget about it, and build a less underhand way of dealing with it.
Or develop it for mobile only... ;)
UPDATE: OK following on from your comment here's a bit of taster:
in javascript capture the click on the link
$("a").click(function(e){
preventDefault(e); // make sure the click doesn't happen
// call a server side script using ajax and pass the URL this.href
// return either a true or false; true = iframe breakout
// set the target attribute of the link to "_blank" for new window (if true)
// set the target attribute of the link to "yourframename" for iframe (if false)
// only now load the page in the new window or iframe
});
server side in PHP
$d = file_get_contents($url); // $url is the url your sent from the browser
// now parse $d to find .top .parent etc... in the <head></head> block
// return true or false