4

I am attempting to make an ajax call when the browser is on my webpage it works perfectly but as soon as I leave my domain, it fails. This is for a closed system that user knows they are being tracked so nothing shady going on. I am receiving an error 406 on everything outside of my domain. For example if I on my url of www.mywebpage.com the script executes perfectly, but as soon as I visit www.yourwebpage.com it returns the error.

I have tried setting the permissions in the manifest.json to my URL, all urls, specific urls but it behaves the same way. Here is my background.js

chrome.runtime.onMessage.addListener
(
    function(message, sender, sendResponse) 
    {
        if(message.applicationcode=="VALIDAPPLICATIONKEY")
        {
            var salt=message.salt;
            var learnerid=message.learnerid;
            var behaviorkey=message.behaviorkey;
            var behaviorname=message.behaviorname;
            var behaviorkeyname=message.behaviorkeyname;
            chrome.tabs.query
            (
                {active: true}, 
                function(arrayOfTabs) 
                {
                    var data = new FormData();
                    data.append('Salt', salt);
                    data.append('LearnerID', learnerid);
                    data.append('BehaviorKey', behaviorkey);
                    data.append('BehaviorName', behaviorname);
                    data.append('BehaviorKeyName', behaviorkeyname);
                    data.append('BehaviorValue', arrayOfTabs[0].url);
                    var xhr = new XMLHttpRequest();
                    xhr.open('POST', 'https://www.mywebpage.com/myservice.php', true);
                    xhr.onreadystatechange = function() 
                    {
                        if (xhr.readyState == 4) 
                        {
                            // JSON.parse does not evaluate the attacker's scripts.
                            var resp = JSON.parse(xhr.responseText);
                            console.log(resp);
                        }
                    }
                    xhr.send(data);     
                }
            );//end query
            return true;
        }
    }
);//end listener

Here is my current manifest file.

{
    "manifest_version": 2,
    "name": "Application",
    "description": "Plugin",
    "version": "1.0",
    "background": 
    {
        "scripts": ["jquery.js","background.js"],
         "persistent": true
    },
    "permissions": [
        "tabs","http://www.mywebpage.com/*","https://www.mywebpage.com/*"

    ],
    "browser_action": 
    {
        "default_icon": "icon.png",
        "default_popup": "popup.html"
    },
    "content_scripts": 
    [
        {
            "matches": ["<all_urls>"],
            "js": ["jquery.js","popup.js"]
        }
    ]
}

Any thoughts or help on this would be greatly appreciated. According to the documentation here what I am trying to do is allowed by extensions and does work in a limited fashion. Or should this type of action being taking place in the extension page as suggested here? I am new to writing Chrome extensions and I am sure I am missing something stupid.

Thanks in advance.

Community
  • 1
  • 1
ProgrammerWannabe
  • 161
  • 1
  • 2
  • 12
  • 1
    Your manifest file is crucial here. – Xan Jun 03 '14 at 13:36
  • Out of interest, why do you `return true` in the `onMessage` listener? It tells Chrome to expect `sendResponse` to be called asynchronously, but it doesn't happen in your snippet. – Xan Jun 03 '14 at 13:39
  • It was a recommendation from this [link](http://stackoverflow.com/questions/20077487/chrome-extension-message-passing-between-extensionbackground-and-content-scrip) . I have been scouring Stack Overflow for a solution :). If it is not needed, please help me understand why as opposed to the accepted response from the other article. Thank you for the crazy fast response! – ProgrammerWannabe Jun 03 '14 at 13:47
  • In the answer you linked `resp === sendResponse`, so `sendResponse` is actually called. In your code you don't use it. – Xan Jun 03 '14 at 13:49
  • That makes perfect sense. I was originally using sendResponse but changed it due to this issue. – ProgrammerWannabe Jun 03 '14 at 13:51
  • Edited to include manifest file. – ProgrammerWannabe Jun 03 '14 at 13:55
  • 1
    Make the ajax from background not content – Zig Mandel Jun 03 '14 at 17:26
  • 1
    The ajax request is coming from the background. – ProgrammerWannabe Jun 03 '14 at 19:57

2 Answers2

10

Here was the solution I had:

manifest.json:

{
    "manifest_version": 2,
    "name": "My Name",
    "description": "My Description.",
    "version": "0.1",
    "background": 
    {
        "scripts": ["jquery.js","background.js"],
        "persistent": true
    },
    "permissions": 
    [
        "tabs",
        "storage"
    ],
    "browser_action": 
    {
        "default_icon": "icon.png",
        "default_popup": "popup.html"
    },
    "content_scripts": 
    [
        {
            "matches": ["https://www.myurl.com/*"],
            "js": ["jquery.js","popup.js"],
            "run_at": "document_end"
        }
    ]
  }

background.js:

var learnerid=0;
// Called when the user clicks on the browser action.
chrome.tabs.onUpdated.addListener
( 
    function (tabId, changeInfo, tab) 
    {
        if (changeInfo.status == 'complete') 
        {
            chrome.tabs.query
            (
                { 
                    active: true 
                }, 
                function (tabs) 
                {
                    if(learnerid!=0)
                    {
                        TrackURL(tabs);
                    }
                    else
                    {
                        console.log("User not logged in yet!");
                    }//end if
                }
            );//end query
        }
    }
);

chrome.runtime.onMessage.addListener
(
    function(message, sender, sendResponse) 
    {
        if(message.applicationcode=="appname")
        {
            learnerid=message.learnerid;
        }//end if
    }
);//end function


function TrackURL(tabs)
{
    $.ajax
    (
        {
            type: "POST",
            url: "http://www.myurl.com/action.php",
            dataType:"json",
            data: 
            {               
                Action: 'TrackURL',
                URL:tabs[0].url,
                Title:tabs[0].title,
                LearnerID:learnerid
            },
            success: function(msg)
            {
                console.log("URL Tracked");
            }//end function
        }
    );//End ajax 

}//end function

popup.js:

document.addEventListener
(
    "starttrack", 
    function(e) 
    {
        startPoll(e.detail);
    }
); 


function startPoll(e)
{
    chrome.runtime.sendMessage
    (
        {
            applicationcode: "myapp",
            learnerid: e,
        }
    ); 
}

From my webpage:

function SendLearnerID(value)
                  {
                    try
                    {

                        var event = new CustomEvent("starttrack",{'detail': value});
                        document.dispatchEvent(event);
                    }
                    catch(err) 
                    {
                        console.log(err);
                    }   

                  }//end function

My problem was where my original event call happened, from within the web page....hence the 406 error. Hope this helps someone else.

ProgrammerWannabe
  • 161
  • 1
  • 2
  • 12
1

Cause:

Luckily you're right... you are only missing something simple:

You need permissions for ANY and EVERY page/domain/URL you want this to work on, and you have only requested permissions for www.mywebpage.com in your current manifest.json:

    "permissions": [
      "tabs","http://www.mywebpage.com/*","https://www.mywebpage.com/*"
    ],

Solution:

If you want to do this within the context of the background page/script, you need to add all URLs in the permissions entry in your manifest.json. If you want to do this from a content script, then you need to add it in the content_scripts entry. If you plan on doing it in both places, then add in both entries/sections:

    "permissions": [                //needed for background script
      "tabs","http://*/*","https://*/*"
    ],
    "content_scripts":[             //needed for content script
      ...
      "http://*/*","https://*/*"
      ...
    ]

if you would also like the user to be able to use your extension when opening local files too, then add that a permission for the file schema/protocol, like so:

    "permissions": [                //needed for background script
      "tabs","http://*/*","https://*/*","file://*/*"
    ],
    "content_scripts":[             //needed for content script
      ...
      "http://*/*","https://*/*"
      ...
    ]
Flak DiNenno
  • 2,193
  • 4
  • 30
  • 57
  • 1
    I frankly don't like this answer. If XHRs are being sent from _background page's context_ as described, this should not be the problem. – Xan Jun 04 '14 at 13:25
  • 1
    @Xan: Incorrect. If you are using the background page, you need permissions. If you are using the content script, then you need to set them there as well. – Flak DiNenno Jun 04 '14 at 13:26
  • The original permissions matched the resource he was XHR'ing to. So I don't see how this applies. – Xan Jun 04 '14 at 13:28
  • 2
    I applies b/c the script is not executing in the context of www.mywebpage.com, it's executing in the context of the page that is loaded in the browser, i.e. the value of document.location.href. So, whatever code is in the content or background script will execute or fail depending on that, NOT on address of the XHR request. – Flak DiNenno Jun 04 '14 at 13:36
  • 2
    **This is simply wrong.** For XHR executing from the context of the background script `document.location.href` has nothing to do with any open page (it's `chrome-extension://[extension id here]/_generated_background_page.html` to be precise). And such XHR do not have to worry about cross-domain, as long as there are host permissions for the request address. The OP actually linked to the [relevant documentation](https://developer.chrome.com/extensions/xhr#requesting-permission). – Xan Jun 04 '14 at 13:40
  • Actually, I missed that he was running it from the background script. If that's the case, then you're right. – Flak DiNenno Jun 04 '14 at 13:42
  • Both of you are right :). My issue was where I was triggering the original call to the content script. I was hitting a cross-domain security issue. I will post my solution with description later today.Thank you both for your help. – ProgrammerWannabe Jun 04 '14 at 13:44
  • 2
    Actually, [Content Scripts docs](https://developer.chrome.com/extensions/content_scripts) say that they are permitted cross-origin requests with the same policy as background pages. – Xan Jun 04 '14 at 13:45