Upload a File in a Google Chrome Extension - google-chrome-extension

I'm writing an extension for Chrome, and I need to upload a file from the page the user is currently on to my server to be processed, I cannot figure out how to upload the file though. I considered just passing the link to the server and having the server download the file, however if the site requires authentication this will not work. Is it possible to upload a file via a Chrome extension to my server?

I've recently developed a Chrome extension which retrieves content from a page, and sends it to the server.
The following approach was used:
File downloads: Get the src property of an <img> element, for example.
Fetch the file from the Cache - use XMLHttpRequest from the background page.
Use a Web Worker in the background page to handle the upload.
Side note, to take the checksum of the image, Crypto-JS: MD5 can be used. Example (where xhr is the XMLHttpRequest object with responseType set to arraybuffer, see Worker demo):
var md5sum = Crypto.MD5( new Uint8Array(xhr.response) );
Full example
Content script
// Example: Grab the first <img> from the document if it exists.
var img = document.images[0];
if (img) {
// Send the target of the image:
chrome.runtime.sendMessage({method: 'postUrl', url: img.src});
}
Background script (with Worker)
chrome.runtime.onMessage.addListener(function(request) {
if (request.method == 'postUrl') {
var worker = new Worker('worker.js');
worker.postMessage(request.url);
}
});
Web Worker
// Define the FormData object for the Web worker:
importScripts('xhr2-FormData.js')
// Note: In a Web worker, the global object is called "self" instead of "window"
self.onmessage = function(event) {
var resourceUrl = event.data; // From the background page
var xhr = new XMLHttpRequest();
xhr.open('GET', resourceUrl, true);
// Response type arraybuffer - XMLHttpRequest 2
xhr.responseType = 'arraybuffer';
xhr.onload = function(e) {
if (xhr.status == 200) {
nextStep(xhr.response);
}
};
xhr.send();
};
function nextStep(arrayBuffer) {
var xhr = new XMLHttpRequest();
// Using FormData polyfill for Web workers!
var fd = new FormData();
fd.append('server-method', 'upload');
// The native FormData.append method ONLY takes Blobs, Files or strings
// The FormData for Web workers polyfill can also deal with array buffers
fd.append('file', arrayBuffer);
xhr.open('POST', 'http://YOUR.DOMAIN.HERE/posturl.php', true);
// Transmit the form to the server
xhr.send(fd);
};
FormData for Web workers POLYFILL
Web workers do not natively support the FormData object, used to transmit multipart/form-data forms. That's why I've written a polyfill for it. This code has to be included in the Web worker, using importScripts('xhr2-FormData.js').
The source code of the polyfill is available at https://gist.github.com/Rob--W/8b5adedd84c0d36aba64
Manifest file:
{
"name": "Rob W - Demo: Scraping images and posting data",
"version": "1.0",
"manifest_version": 2,
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["contentscript.js"]
}
],
"background": {
"scripts": ["background.js"]
},
"permissions": ["http://*/*", "https://*/*"]
}
Relevant documentation
Message passing Google Chrome Extensions
chrome.runtime.onMessage Google Chrome Extensions
XMLHttpRequest Level 2 W3c specification
FormData (XHR2) MDN
ArrayBuffer responses (XHR2) HTML5 rocks (note: arraybuffer responses are deprecated in favor of typed arrays, the polyfill has been updated to reflect this change)

The simplest solutions seems to be for your extension to send the file's URI to your server, and then your server-side code will download it from the page into the server and process it.
Create a server-side script like http://mysite.com/process.php?uri=[file's URI goes here] that will process the given file. Use AJAX to call this URL (more info at http://code.google.com/chrome/extensions/xhr.html ). The script will return the processed file, which you could then use in your extension.

You should checkout the following:
chrome.extension.sendRequest() and chrome.extension.onRequest()
You can read more about them here: http://code.google.com/chrome/extensions/messaging.html
Basically you will setup the page on the server to watch for the Chrome extension, and once they connect you will need to have a javascript that will do the upload task for you.
I haven't tested this out, but it may get you where you need to be. Also you may want to read the Long-lived connections section.
Goodluck

Related

How to make a http request from a Chrome extension to an api

I'm doing some tests with a Chrome extension and I can't make a simple get request to my API. My API returns a json and when the extension receives the response, it should console log it.
In my manifest.json I have this permission:
"permissions": [
"https://*/"
],
And in my background.js I have this
var xhr = new XMLHttpRequest();
xhr.open("GET", "https//localhost:5001/api/values", true);
xhr.onreadystatechange = function () {
console.log(xhr);
if (xhr.readyState == 4) {
console.log(xhr.responseText);
}
}
xhr.send();
When this code gets executed I get this message in the browser's Console windows
GET chrome-extension://cajbfgbekfaohncbcejeobaamefgfebd/https//localhost:5001/api/values net::ERR_FILE_NOT_FOUND
My guess is that the request includes chrome-extension://cajbfgbekfaohncbcejeobaamefgfebd and this is what's causing the issue. But even if that's the case I don't know how to fix it.
Yep nothing special here, the problem was a typo as #wOxxOm pointed out.

Modify HTTP responses from a Chrome extension

Is it possible to create a Chrome extension that modifies HTTP response bodies?
I have looked in the Chrome Extension APIs, but I haven't found anything to do this.
In general, you cannot change the response body of a HTTP request using the standard Chrome extension APIs.
This feature is being requested at 104058: WebRequest API: allow extension to edit response body. Star the issue to get notified of updates.
If you want to edit the response body for a known XMLHttpRequest, inject code via a content script to override the default XMLHttpRequest constructor with a custom (full-featured) one that rewrites the response before triggering the real event. Make sure that your XMLHttpRequest object is fully compliant with Chrome's built-in XMLHttpRequest object, or AJAX-heavy sites will break.
In other cases, you can use the chrome.webRequest or chrome.declarativeWebRequest APIs to redirect the request to a data:-URI. Unlike the XHR-approach, you won't get the original contents of the request. Actually, the request will never hit the server because redirection can only be done before the actual request is sent. And if you redirect a main_frame request, the user will see the data:-URI instead of the requested URL.
I just released a Devtools extension that does just that :)
It's called tamper, it's based on mitmproxy and it allows you to see all requests made by the current tab, modify them and serve the modified version next time you refresh.
It's a pretty early version but it should be compatible with OS X and Windows. Let me know if it doesn't work for you.
You can get it here http://dutzi.github.io/tamper/
How this works
As #Xan commented below, the extension communicates through Native Messaging with a python script that extends mitmproxy.
The extension lists all requests using chrome.devtools.network.onRequestFinished.
When you click on of the requests it downloads its response using the request object's getContent() method, and then sends that response to the python script which saves it locally.
It then opens file in an editor (using call for OSX or subprocess.Popen for windows).
The python script uses mitmproxy to listen to all communication made through that proxy, if it detects a request for a file that was saved it serves the file that was saved instead.
I used Chrome's proxy API (specifically chrome.proxy.settings.set()) to set a PAC as the proxy setting. That PAC file redirect all communication to the python script's proxy.
One of the greatest things about mitmproxy is that it can also modify HTTPs communication. So you have that also :)
Like #Rob w said, I've override XMLHttpRequest and this is a result for modification any XHR requests in any sites (working like transparent modification proxy):
var _open = XMLHttpRequest.prototype.open;
window.XMLHttpRequest.prototype.open = function (method, URL) {
var _onreadystatechange = this.onreadystatechange,
_this = this;
_this.onreadystatechange = function () {
// catch only completed 'api/search/universal' requests
if (_this.readyState === 4 && _this.status === 200 && ~URL.indexOf('api/search/universal')) {
try {
//////////////////////////////////////
// THIS IS ACTIONS FOR YOUR REQUEST //
// EXAMPLE: //
//////////////////////////////////////
var data = JSON.parse(_this.responseText); // {"fields": ["a","b"]}
if (data.fields) {
data.fields.push('c','d');
}
// rewrite responseText
Object.defineProperty(_this, 'responseText', {value: JSON.stringify(data)});
/////////////// END //////////////////
} catch (e) {}
console.log('Caught! :)', method, URL/*, _this.responseText*/);
}
// call original callback
if (_onreadystatechange) _onreadystatechange.apply(this, arguments);
};
// detect any onreadystatechange changing
Object.defineProperty(this, "onreadystatechange", {
get: function () {
return _onreadystatechange;
},
set: function (value) {
_onreadystatechange = value;
}
});
return _open.apply(_this, arguments);
};
for example this code can be used successfully by Tampermonkey for making any modifications on any sites :)
Yes. It is possible with the chrome.debugger API, which grants extension access to the Chrome DevTools Protocol, which supports HTTP interception and modification through its Network API.
This solution was suggested by a comment on Chrome Issue 487422:
For anyone wanting an alternative which is doable at the moment, you can use chrome.debugger in a background/event page to attach to the specific tab you want to listen to (or attach to all tabs if that's possible, haven't tested all tabs personally), then use the network API of the debugging protocol.
The only problem with this is that there will be the usual yellow bar at the top of the tab's viewport, unless the user turns it off in chrome://flags.
First, attach a debugger to the target:
chrome.debugger.getTargets((targets) => {
let target = /* Find the target. */;
let debuggee = { targetId: target.id };
chrome.debugger.attach(debuggee, "1.2", () => {
// TODO
});
});
Next, send the Network.setRequestInterceptionEnabled command, which will enable interception of network requests:
chrome.debugger.getTargets((targets) => {
let target = /* Find the target. */;
let debuggee = { targetId: target.id };
chrome.debugger.attach(debuggee, "1.2", () => {
chrome.debugger.sendCommand(debuggee, "Network.setRequestInterceptionEnabled", { enabled: true });
});
});
Chrome will now begin sending Network.requestIntercepted events. Add a listener for them:
chrome.debugger.getTargets((targets) => {
let target = /* Find the target. */;
let debuggee = { targetId: target.id };
chrome.debugger.attach(debuggee, "1.2", () => {
chrome.debugger.sendCommand(debuggee, "Network.setRequestInterceptionEnabled", { enabled: true });
});
chrome.debugger.onEvent.addListener((source, method, params) => {
if(source.targetId === target.id && method === "Network.requestIntercepted") {
// TODO
}
});
});
In the listener, params.request will be the corresponding Request object.
Send the response with Network.continueInterceptedRequest:
Pass a base64 encoding of your desired HTTP raw response (including HTTP status line, headers, etc!) as rawResponse.
Pass params.interceptionId as interceptionId.
Note that I have not tested any of this, at all.
While Safari has this feature built-in, the best workaround I've found for Chrome so far is to use Cypress's intercept functionality. It cleanly allows me to stub HTTP responses in Chrome. I call cy.intercept then cy.visit(<URL>) and it intercepts and provides a stubbed response for a specific request the visited page makes. Here's an example:
cy.intercept('GET', '/myapiendpoint', {
statusCode: 200,
body: {
myexamplefield: 'Example value',
},
})
cy.visit('http://localhost:8080/mytestpage')
Note: You may also need to configure Cypress to disable some Chrome-specific security settings.
The original question was about Chrome extensions, but I notice that it has branched out into different methods, going by the upvotes on answers that have non-Chrome-extension methods.
Here's a way to kind of achieve this with Puppeteer. Note the caveat mentioned on the originalContent line - the fetched response may be different to the original response in some circumstances.
With Node.js:
npm install puppeteer node-fetch#2.6.7
Create this main.js:
const puppeteer = require("puppeteer");
const fetch = require("node-fetch");
(async function() {
const browser = await puppeteer.launch({headless:false});
const page = await browser.newPage();
await page.setRequestInterception(true);
page.on('request', async (request) => {
let url = request.url().replace(/\/$/g, ""); // remove trailing slash from urls
console.log("REQUEST:", url);
let originalContent = await fetch(url).then(r => r.text()); // TODO: Pass request headers here for more accurate response (still not perfect, but more likely to be the same as the "actual" response)
if(url === "https://example.com") {
request.respond({
status: 200,
contentType: 'text/html; charset=utf-8', // For JS files: 'application/javascript; charset=utf-8'
body: originalContent.replace(/example/gi, "TESTING123"),
});
} else {
request.continue();
}
});
await page.goto("https://example.com");
})();
Run it:
node main.js
With Deno:
Install Deno:
curl -fsSL https://deno.land/install.sh | sh # linux, mac
irm https://deno.land/install.ps1 | iex # windows powershell
Download Chrome for Puppeteer:
PUPPETEER_PRODUCT=chrome deno run -A --unstable https://deno.land/x/puppeteer#16.2.0/install.ts
Create this main.js:
import puppeteer from "https://deno.land/x/puppeteer#16.2.0/mod.ts";
const browser = await puppeteer.launch({headless:false});
const page = await browser.newPage();
await page.setRequestInterception(true);
page.on('request', async (request) => {
let url = request.url().replace(/\/$/g, ""); // remove trailing slash from urls
console.log("REQUEST:", url);
let originalContent = await fetch(url).then(r => r.text()); // TODO: Pass request headers here for more accurate response (still not perfect, but more likely to be the same as the "actual" response)
if(url === "https://example.com") {
request.respond({
status: 200,
contentType: 'text/html; charset=utf-8', // For JS files: 'application/javascript; charset=utf-8'
body: originalContent.replace(/example/gi, "TESTING123"),
});
} else {
request.continue();
}
});
await page.goto("https://example.com");
Run it:
deno run -A --unstable main.js
(I'm currently running into a TimeoutError with this that will hopefully be resolved soon: https://github.com/lucacasonato/deno-puppeteer/issues/65)
Yes, you can modify HTTP response in a Chrome extension. I built ModResponse (https://modheader.com/modresponse) that does that. It can record and replay your HTTP response, modify it, add delay, and even use the HTTP response from a different server (like from your localhost)
The way it works is to use the chrome.debugger API (https://developer.chrome.com/docs/extensions/reference/debugger/), which gives you access to Chrome DevTools Protocol (https://chromedevtools.github.io/devtools-protocol/). You can then intercept the request and response using the Fetch Domain API (https://chromedevtools.github.io/devtools-protocol/tot/Fetch/), then override the response you want. (You can also use the Network Domain, though it is deprecated in favor of the Fetch Domain)
The nice thing about this approach is that it will just work out of box. No desktop app installation required. No extra proxy setup. However, it will show a debugging banner in Chrome (which you can add an argument to Chrome to hide), and it is significantly more complicated to setup than other APIs.
For examples on how to use the debugger API, take a look at the chrome-extensions-samples: https://github.com/GoogleChrome/chrome-extensions-samples/tree/main/mv2-archive/api/debugger/live-headers
I've just found this extension and it does a lot of other things but modifying api responses in the browser works really well: https://requestly.io/
Follow these steps to get it working:
Install the extension
Go to HttpRules
Add a new rule and add a url and a response
Enable the rule with the radio button
Go to Chrome and you should see the response is modified
You can have multiple rules with different responses and enable/disable as required. I've not found out how you can have a different response per request though if the url is the same unfortunately.

http requests not working chrome extension

I am developing a chrome extension that requires corss domain XHR. So I need to make get requests to a server and get some text out of it. I am currently loading the unpacked extension from my computer. The script doesn't seem to be working.
Here is my manifest.json:
{
"name": "My extension",
"version": "1.1",
"manifest_version": 2,
"description": "Testing http requests",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"http://*/"
]
}
And here is the script that performs the get request (from this tutorial):
function showresponse(){
var query = document.getElementById("query").value;
var url = "http://blah.com/search.php?term="+query;
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
console.log("hello world");
document.getElementById("container").innerHTML = xhr.responseText;
}
}
xhr.send(null);
}
The id's etc are set according to my popup.html file and that's set up correctly and it includes this js file containing the showresponse() function definition.
I also tried packaging my extension to get a myextension.crx file after reading this question and I tried opening the file in my browser, but chrome doesn't allow installing the extension from localhost or unknown servers for security reasons I suppose.
My question is how do I make a cross domain XHR in a chrome extension?
Also the response from the get request to the server is actually an html document and I need to filter some text out of the returned html tags. As I am making a query to a php script, can I receive and therefore play around with the html output if I make a get request in javascript?
How do I go about acheiving this?

Chrome extension manifest version 2 API call error [duplicate]

Small problem with my chrome extension.
I just wanted to get a JSON array from another server. But manifest 2 doesn't allow me to do it. I tried specify content_security_policy, but the JSON array is stored on a server without SSL cert.
So, what should I do without using manifest 1?
The CSP cannot cause the problem you've described. It's very likely that you're using JSONP instead of plain JSON. JSONP does not work in Chrome, because JSONP works by inserting a <script> tag in the document, whose src attribute is set to the URL of the webservice. This is disallowed by the CSP.
Provided that you've set the correct permission in the manifest file (e.g. "permissions": ["http://domain/getjson*"], you will always be able to get and parse the JSON:
var xhr = new XMLHttpRequest();
xhr.onload = function() {
var json = xhr.responseText; // Response
json = json.replace(/^[^(]*\(([\S\s]+)\);?$/, '$1'); // Turn JSONP in JSON
json = JSON.parse(json); // Parse JSON
// ... enjoy your parsed json...
};
// Example:
data = 'Example: appended to the query string..';
xhr.open('GET', 'http://domain/getjson?data=' + encodeURIComponent(data));
xhr.send();
When using jQuery for ajax, make sure that JSONP is not requested by using jsonp: false:
$.ajax({url:'...',
jsonp: false ... });
Or, when using $.getJSON:
$.getJSON('URL which does NOT contain callback=?', ...);

Chrome extensions - Other ways to read response bodies than chrome.devtools.network?

I'd like to read (not modify) the response body for all requests that match some pattern in a Chrome extension. I'm currently using chrome.devtools.network.onRequestFinished, which gives you a Request object with a getContent() method. This works just fine, but of course requires the devtools to be open for the extension to work. Ideally the extension would be a popup, but chrome.webRequest.onCompleted doesn't seem to give access to the response body. There is a feature request to allow the webRequest API to edit response bodies - but can webRequest even read them? If not, is there any other way to read response bodies outside of devtools extensions?
The feature request you linked to implies that there is no support for reading either:
Unfortunately, this request is not trivial. (...) Regarding reading the Response Body: This is challenging from a performance perspective. (...) So overall, this is just not easy to achieve...
So, no, there doesn't seem to be a way for an extension to access network response bodies, except for devtools.
Here is what I did
I used the chrome.webRequest & requestBody to get the post requests body
I used a decoder the parse the body into a string
Here is an example
chrome.webRequest.onBeforeRequest.addListener(
function(details) {
if(details.method == "POST")
// Use this to decode the body of your post
var postedString = decodeURIComponent(String.fromCharCode.apply(null,
new Uint8Array(details.requestBody.raw[0].bytes)));
console.log(postedString)
},
{urls: ["<all_urls>"]},
["blocking", "requestBody"]
);
If you have the this pattern of requests you can run something like that in your background.html file:
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://example.com/" + yourStringForPattern, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
var body = xhr.responseText;
// call some function to do something with the html body
}
}
xhr.send();

Resources