Script injection on mail.google.com using chrome extension - google-chrome-extension

I am working on a chrome extension in which I am replacing the chrome's web API function navigator.credentials.create with a custom handler. navigator.credentials.create is called when a user initiates a 2FA registration for a security key.
The custom handler is working on facebook.com but, it doesn't work on mail.google.com.
I have attached the minimal code for the extension I'm working on. This script is injected on every page and can be confirmed through console "injected script". In my sample app, I printed a console statement "In extension's create" in the custom handler of navigator.credentials.create. This should be printed whenever a user initiates the 2FA registration process. It is working for facebook but doesn't work on https://myaccount.google.com/. I have verified that the script is inserted in all iframes available on the website. I also confirmed that mail.google.com is not using service workers to call navigator.credentials.create API.
content_script.js
const webauthnInject = document.createElement('script');
webauthnInject.type = 'text/javascript';
webauthnInject.src = 'chrome-extension://' + chrome.runtime.id + '/inject_webauthn.js';
document.documentElement.appendChild(webauthnInject);
inject_webauthn.js
(() => {
console.log("injected script");
const real_create = navigator.credentials.create;
navigator.credentials.create = async function() {
console.log("In extension's create");
let res = await real_create.bind(navigator.credentials)(arguments[0]);
return res;
};
})();
manifest.json
{
"manifest_version": 2,
"name": "sample app",
"description": "A Sample app",
"version": "1.0.2",
"minimum_chrome_version": "36.0.1985.18",
"content_scripts": [
{
"all_frames": true,
"match_about_blank": true,
"matches": [
"https://*/*",
"http://*/*"
],
"exclude_matches": [
"https://*/*.xml"
],
"run_at": "document_start",
"js": [
"content_script.js"
]
}
],
"background": {
"persistent": false,
"scripts": [
"background.js"
]
},
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"permissions": [
"tabs",
"storage",
"https://*/*",
"http://*/*"
],
"web_accessible_resources": [
"inject_webauthn.js"
]
}

Related

Access open shadow-root from browser extension content script (Chrome / Firefox)

Within the elements tab of the browser development tools, the shadow root appears as #shadow-root (open) and it is accessible from the development console via document.getElementById('parent').shadowRoot, where the element with id of parent contains the shadow root. However, from the content script of a Chrome browser extension the same code results in null (while document.getElementById('parent') of course works as expected). Is it possible to access shadow-root from a browser extension?
TL;DR. Tried to access the shadow-root element from a browser extension in the same way that it is accessible from the developer's console. Expected it to work in a browser extension in the same way that it works in the developer's console.
manifest.json
{
"content_scripts": [ {
"exclude_globs": [ ],
"exclude_matches": [ ],
"include_globs": [
"https://*.test.com/*"
],
"js": [
"script.js",
],
"matches": ["http://*/*","https://*/*"],
"run_at": "document_idle",
//"run_at": "document_end",
"all_frames": true
} ],
"permissions": [
"cookies",
"storage",
"http://*/",
"https://*/",
"tabs",
"contextMenus",
"management",
"clipboardWrite",
],
"converted_from_user_script": false,
"description": "test",
"key": "--",
"name": "test",
"version": "1.0",
"manifest_version": 2
}
script.js
if (document.readyState == "complete" || document.readyState == "interactive") {
console.log(document.getElementById('parent').shadowRoot);
} else {
document.onreadystatechange = function () {
if (document.readyState == "complete" || document.readyState == "interactive")
{
console.log(document.getElementById('parent').shadowRoot);
}
}
}

Why is the background script not loading on Firefox add-on (works on Chrome)?

I'm developing a cross-browser extension which works in Chrome but not in Firefox - the background script is not loading.
I tried console.log in background.js and sending a message to the content script and logging message there.
background.js
browser.action.onClicked.addListener(async function (tab) {
console.log("clicked on extension icon");
browser.tabs.sendMessage(tab.id, { text: "toggle_overlay" })
});
js/content.js
...
browser.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
console.log("message received", msg)
});
Content script works as expected on all code that's not depended on background.js
Folder structure
manifest.json (had to downgrade to v2 because Firefox doesn't support v3 yet)
{
"name": "Dev Mode",
"description": "Dev Mode",
"version": "0.0.1",
"manifest_version": 2,
"icons": {
"16": "./imgs/icon-16-dark.png",
"48": "./imgs/icon-48.png",
"128": "./imgs/icon-128.png"
},
"permissions": [
"activeTab",
"contextMenus",
"bookmarks",
"scripting",
"storage",
"<all_urls>"
],
"background": {
"scripts": ["background.js"],
"persistent": false // <-- also tried without, same result - background script doesn't lod
},
"browser_action": {
"default_icon": "./imgs/icon-16-dark.png",
"default_title": "Default Title"
},
"commands": {
"save-page": {
"suggested_key": {
"default": "Ctrl+Shift+S",
"mac": "Command+Shift+S"
},
"description": "some description"
}
},
"content_security_policy": "script-src 'self'; object-src 'self'; sandbox allow-scripts; script-src 'self' https://apis.google.com https://www.gstatic.com https://www.googleapis.com https://securetoken.googleapis.com; object-src 'self'",
"web_accessible_resources": [ "imgs/*.png", "overlay.html"],
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": [
"/js/content.js"
],
"run_at": "document_end",
"all_frames": false
}
]
}
I'm testing the Firefox extension with web-ext run to test the extension locally.
The correct API for this in Manifest v2 is browserAction instead of action that is only available in MV3.
So to fix it, in your background.js, switch to
browser.browserAction.onClicked.addListener
browser.action in Firefox is available in MV3. Your extension uses MV2 i.e. "manifest_version": 2,
Note: This API is available in Manifest V3 or higher.
Note: MV3 support is very limited in Firefox at the moment.

Google extension - message listener not working

this is a bit of my code that I have currently
Manifest
{
"name": "hidden",
"manifest_version": 2,
"content_security_policy": "script-src 'self'; object-src 'self'",
"permissions": [
"activeTab",
"storage"
],
"version": "hidden",
"icons": {hidden},
"description": "hidden",
"browser_action": {hidden},
"content_scripts": [
{
"matches": [hidden],
"run_at": "document_start",
"js": [ "injected.js", "content.js"]
}
],
"web_accessible_resources": ["injected.js"],
"background": {
"scripts": ["background.js"],
"persistent": false
}
}
Injected.js
if (new RegExp(allowedUrls.join("|")).test(this._url))
{
console.log('test') <- I can see this message in console
chrome.runtime.sendMessage({interception: true});
}
Background.js
console.log("Atleast reached background.js") <- I can see that
chrome.runtime.onMessage.addListener(function (message, sender) {
console.log('inside listener') <- I cannot see that which means it doesn't get fired on message sent
if (message.interception) {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {interception: true});
});
}
});
Can someone help me and tell me why it isn't working? My goal is to intercept XHR request in one file (injected.js), then receive that message on background.js and send that to another content.js file and do some stuff base on the response.

Is it possible to inject a custom authentication library into a chrome extension background script?

I'm in the process of building a chrome extension that needs to leverage my companies internal SSO system. I'm using a background script because I need to make a CORS call to a service that can return an auth token. I've developed a library that can handle this for me but based on what I have read there is no way to inject it into a background script. I could relax the Content Policy to allow for it but the documentation states that they strongly recommend not doing this because of XSS vulnerabilities. My question is: Is there a way to inject my library into the background script without altering the content policy?
//manifest.json
{
"name": "Learning",
"version": "1.0",
"description": "Chrome extension to enable xAPI",
"background": {
"scripts": ["tx.js", "tx-auth.js", "main.js"],
"persistent": true
},
"permissions": [
"activeTab",
"tabs"
],
"content_scripts": [
{
"run_at": "document_end",
"matches": ["http://*/*", "https://*/*"],
"js": ["interactionProfile.js", "tx.js", "all.js"]
},
{
"run_at": "document_end",
"matches": ["*://*.youtube.com/*"],
"js": ["interactionProfile.js", "tx.js", "youtube.js"]
}
],
"browser_action": {
"default_popup": "main.html"
},
"manifest_version": 2
}
// all.js
console.log('all')
const iframe = document.createElement('iframe');
iframe.setAttribute('src', "http:****/identification");
iframe.style.display = "none";
document.body.appendChild(iframe);
chrome.runtime.sendMessage({contentScriptQuery: '', create: tx.default.create}, (tx) => {
console.log(tx)
})
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
request.create({
endpoint: '*****',
username: '*****',
password: '*****',
allowfail: false,
env: "sandbox",
xapiEndpoint: '******'
}).then((tx) => sendResponse(tx));
return true;
});

Running jquery script triggered by context menu in chrome extension

I am new to chrome extension development. The sample code I have is not running properly.
Requirement: Executing any jquery script(say $("body").hide();) on click of context menu button.
From the code, only jquery part is not working.
I have following files:
manifest.json
{
"manifest_version": 2,
"name": "jQuery DOM",
"version": "1",
"permissions": [
"contextMenus","tabs","activeTab"
],
"background": {
"scripts": ["jquery.min.js","sample.js"]
},
"description": "Manipulate the DOM when the page is done loading",
"browser_action": {
"name": "Manipulate DOM",
"icons": ["icon.png"],
"default_icon": "icon.png"
},
"content_scripts": [ {
"js": [ "jquery.min.js", "background.js" ],
"matches": [ "http://*/*", "https://*/*"],
"run_at": "document_end"
}]
}
background.js
$("body").append('Test');
I have icon.png in folder, and it gets loaded well.
jquery.min.js in same folder
sample.js
alert("Extension loaded");
function genericOnClick(info, tab) {
alert("Tab "+tab.id);
chrome.tabs.executeScript(tab.id, {
file: "jquery.min.js",
allFrames: true
},function(){
alert("callback");
$("body").hide();
});
alert("Completed");
$("body").hide();
}
var contexts = ["page"];
for (var i = 0; i < contexts.length; i++) {
var context = contexts[i];
var title = "Test Page menu item";
var id = chrome.contextMenus.create({"title": title, "contexts":[context],
"onclick": genericOnClick});
console.log("'" + context + "' item:" + id);
}
background.js works!
All the alerts work file, but .hide function from genericOnClick doesn't work.
Even if I move the code from sample.js to backgroud.js, it won't work.
Can you please tell me where did i go wrong ?
As I mentioned, a background script isn't allowed to interact with the DOM, (while a content script isn't allowed to use chrome.contextMenus). You need to combine both, and use message passing to tell the content script when to execute. Some other adjustments I made:
I renamed background.js and content.js so that their names now
reflect what they do, and made background.js into an event page.
I removed the browser action (the
extension would need browserAction.html or background.js would
need chrome.browserAction.onClicked.addListener to do anything
other than show the icon).
Programmatically injecting jquery means that always loading it as a
content script is unnecessary (although skipping programmatic injection allows you to omit the last three permissions).
background.js doesn't need jquery
anymore, so it isn't loaded there either.
The default executeScript
tab is the active tab, so we don't need it's id.
Here's the finished product:
manifest.json
{
"manifest_version": 2,
"name": "jQuery DOM",
"version": "1",
"permissions": [
"contextMenus", "activeTab", "tabs", "http://*/", "https://*/"
],
"background": {
"scripts": [ "background.js" ],
"persistent": false
},
"content_scripts": [ {
"js": [ "content.js" ],
"matches": [ "<all_urls>" ]
}],
"description": "Manipulate the DOM when the page is done loading"
}
background.js
function genericOnClick(info, tab) {
chrome.tabs.executeScript(null,
{"file": "jquery.min.js"},
function() {
chrome.tabs.sendMessage(tab.id,{"message":"hide"});
});
}
chrome.contextMenus.create({"title": "Test Page menu item",
"contexts":["page"],
"id":"contextId"});
chrome.contextMenus.onClicked.addListener(genericOnClick);
content.js
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
if (msg.message == 'hide') {
$("body").hide();
}
sendResponse();
});
Your content.js is probably much larger than it is here (+1 for the SSCCE). But if it is small, another option would be to omit the content script entirely, and replace the sendMessage with chrome.tabs.executeScript(null,{code:"$('body').hide();"});.

Resources