Proxying of document.cookie - javascript

I need to log setting of document.cookie. I can not redefine cookie property just with document.cookie = {...} So I need to get setter for document.cookie. But Object.getOwnPropertyDescriptor(document, "cookie") returns undefined.
UPD. While I was writing the question I found a working solution, but it uses deprecated __lookupGetter__ and __lookupSetter__ methods. Is there any solution which doesn't use obsolete API?

The standardized way of accessing getters and setters is with Object.getOwnPropertyDescriptor, but as the name suggests, it only looks on the objects own properties (it does not look up the prototype chain). document is an instance of HTMLDocument, which inherits from Document. In modern browsers the cookie property is defined on Document.prototype, whereas in older versions of Firefox it is defined on HTMLDocument.prototype.
var cookieDesc = Object.getOwnPropertyDescriptor(Document.prototype, 'cookie') ||
Object.getOwnPropertyDescriptor(HTMLDocument.prototype, 'cookie');
if (cookieDesc && cookieDesc.configurable) {
Object.defineProperty(document, 'cookie', {
get: function () {
return cookieDesc.get.call(document);
},
set: function (val) {
console.log(val);
cookieDesc.set.call(document, val);
}
});
}
Ironically, in the most privacy-concerned browser Safari, the descriptor has set configurable to false and does not contain the getter nor setter, and neither does __lookupGetter__ or __lookupSetter__. So I haven't found a way to override document.cookie in Safari yet (8.0.8 on OS X and iOS 9.0.2). WebKit nightly acts the same way as Safari, so it doesn't seem to get fixed anytime soon.
Update October 2019: Tested the above code in Safari 12.1.2 on MacOS Mojave, and cookieDesk is now configurable! This means my proof of concept document.cookie protection from 2015 might actually work now :)

While I was writing the question I found next code solves my problem:
var cookie_setter_orig = document.__lookupSetter__("cookie").bind(document);
var cookie_getter_orig = document.__lookupGetter__("cookie").bind(document);
Object.defineProperty(document, "cookie", {
get: function () {
return cookie_getter_orig();
},
set: function (val) {
console.log(val);
cookie_setter_orig(val);
}
});
But I don't like using deprecated methods, so I hope there is a better solution.

Related

Object.getOwnPropertyDescriptors(Node.prototype) fails

I am logging my errors on a web page that's running javascript, and I see that the following code:
JSON.stringify(
Object.getOwnPropertyDescriptors(Node.prototype)
);
fails with:
undefined is not a function
What could be the explanation for that? I am under the assumption this happens when the page was loaded not on a normal browser but by using WebView.
I opened an Android Emulator and verified this page on a normal browser and using a WebView Test app - it worked fine.
What else can I do to debug it? This error is raised quite often.
Edit
I added further logging and I checked the following:
Object.getOwnPropertyDescriptor(Node.prototype, "childNodes");
Object.getOwnPropertyDescriptor(Node.prototype, "parentNode");
Object.getOwnPropertyDescriptor(Node.prototype, "hasChildNodes");
And only for hasChildNodes a value is returned.
It would appear that on some of the browser/webview brands or versions your code runs on, either JSON.stringify or (more likely) Object.getOwnPropertyDescriptors is not defined (the former was added in ES5, the latter much more recently in ES2017).
What else can I do to debug it?
Add tests and log the user agent string:
if (!JSON.stringify) {
/* ...log that `JSON.stringify` isn't supported, w/`navigator.userAgent`...*/
} else if (!Object.getOwnPropertyDescriptors) {
/* ...log that `Object.getOwnPropertyDescriptors` isn't supported, w/`navigator.userAgent`...*/
} else {
/* ...log `JSON.stringify(Object.getOwnPropertyDescriptors(Node.prototype))` ... */
}
In a comment you've asked:
So do you know of a way in which I could get the getter function childNodes of the Node.prototype in whichever browser? In all my tests (mobile emulators and browsers) it is always an "own" property of the Node.prototype.
If you're seeing undefined for Object.getOwnPropertyDescriptor(Node.prototype, "childNodes") in some unusual implementation, you may have to loop using Object.getPrototypeOf to find where it is in the inheritance chain:
function getDescriptor(obj, name) {
let descr;
while (obj && !(descr = Object.getOwnPropertyDescriptor(obj, name))) {
obj = Object.getPrototypeOf(obj);
}
return descr;
}
Then you'd use const childNodesDescriptor = getDescriptor(Node.prototype, "childNodes"); or similar.
But beware that host-provided objects like Node.prototype are also allowed to break some (but not all) rules. You may have to allow for the possibility there are implementations where you just can't get that getter.

Javascript property thunk [duplicate]

I need to log setting of document.cookie. I can not redefine cookie property just with document.cookie = {...} So I need to get setter for document.cookie. But Object.getOwnPropertyDescriptor(document, "cookie") returns undefined.
UPD. While I was writing the question I found a working solution, but it uses deprecated __lookupGetter__ and __lookupSetter__ methods. Is there any solution which doesn't use obsolete API?
The standardized way of accessing getters and setters is with Object.getOwnPropertyDescriptor, but as the name suggests, it only looks on the objects own properties (it does not look up the prototype chain). document is an instance of HTMLDocument, which inherits from Document. In modern browsers the cookie property is defined on Document.prototype, whereas in older versions of Firefox it is defined on HTMLDocument.prototype.
var cookieDesc = Object.getOwnPropertyDescriptor(Document.prototype, 'cookie') ||
Object.getOwnPropertyDescriptor(HTMLDocument.prototype, 'cookie');
if (cookieDesc && cookieDesc.configurable) {
Object.defineProperty(document, 'cookie', {
get: function () {
return cookieDesc.get.call(document);
},
set: function (val) {
console.log(val);
cookieDesc.set.call(document, val);
}
});
}
Ironically, in the most privacy-concerned browser Safari, the descriptor has set configurable to false and does not contain the getter nor setter, and neither does __lookupGetter__ or __lookupSetter__. So I haven't found a way to override document.cookie in Safari yet (8.0.8 on OS X and iOS 9.0.2). WebKit nightly acts the same way as Safari, so it doesn't seem to get fixed anytime soon.
Update October 2019: Tested the above code in Safari 12.1.2 on MacOS Mojave, and cookieDesk is now configurable! This means my proof of concept document.cookie protection from 2015 might actually work now :)
While I was writing the question I found next code solves my problem:
var cookie_setter_orig = document.__lookupSetter__("cookie").bind(document);
var cookie_getter_orig = document.__lookupGetter__("cookie").bind(document);
Object.defineProperty(document, "cookie", {
get: function () {
return cookie_getter_orig();
},
set: function (val) {
console.log(val);
cookie_setter_orig(val);
}
});
But I don't like using deprecated methods, so I hope there is a better solution.

Retrieving document.cookie getter and setter

I'm trying to override the document.cookie since i need to control cookie
creation, but seems that getOwnPropertyDescriptor casted on document.cookie doesn't retrieve its getter and setter ( tried on chrome and firefox ). Could someone explain me this behaviour?
https://jsfiddle.net/1363ktwp/7/
var obj={};
// creating a property using document.cookie descriptors
Object.defineProperty(
obj,
"oldCookie",
Object.getOwnPropertyDescriptor(document, "cookie")
);
// setting cookies succesfully
document.cookie="test1=ok;path=/;expires=365;";
document.cookie="test2=ok;path=/;expires=365;";
alert(document.cookie);
Object.defineProperty(document, "cookie", {
get: function () {
return obj.oldCookie;
},
set: function (cookie) {
/*
...preliminar operations
*/
obj.oldCookie = cookie;
}
});
// obj.oldCookie is just a string without getter/setter
// so assignments below doesn't works correctly
document.cookie="test3=ok;path=/;expires=365;";
document.cookie="test4=ok;path=/;expires=365;";
alert(document.cookie);
The reason for
Object.getOwnPropertyDescriptor(document, 'cookie');
returning undefined is the way that getOwnPropertyDescriptor works: it does not traverse the prototype chain.
A global variable document contains object actually inheriting from Document.prototype:
Document.prototype.isPrototypeOf(document) // true
and does not own a property called "cookie", the Document.prototype does:
document.hasOwnProperty('cookie'); // false
Document.prototype.hasOwnProperty('cookie'); // true
A way to retrieve the descriptor of document.cookie is to retrieve the descriptor of Document.prototype.cookie itself:
Object.getOwnPropertyDescriptor(Document.prototype, 'cookie');
Deprecated functions called __lookupGetter__ and __lookupSetter__ do actually traverse the prototype chain and therefore, you can retrieve these methods calling it on document, and not the Document.prototype:
const cookieDescriptor = Object.getOwnPropertyDescriptor(Document.prototype, 'cookie');
cookieDescriptor.get === document.__lookupGetter__('cookie') // true
You can use __lookupSetter__ and __lookupGetter__ methods, but be warn that they are deprecated and not supported everywhere. They work properly in Chrome, Firefox, IE11. Don't work in IE<10. Opera provides such methods, but they seam to always return undefined. Didn't check anything else.
Here is an example:
var cookieSetterOrig = document.__lookupSetter__("cookie");
var cookieGetterOrig = document.__lookupGetter__("cookie");
Object.defineProperty(document, "cookie", {
get: function () {
return cookieGetterOrig.apply(document);
},
set: function () {
return cookieSetterOrig.apply(document, arguments);
},
configurable: true
});
Could someone explain me this behaviour?
document.cookie is a property of a host object. Host objects are frequently not true JavaScript objects (called native objects) and are neither required nor guaranteed to have the features of JavaScript objects.
I'd be truly shocked, in fact, if many or even more than one or two browsers implemented document.cookie using ES5 property getters/setters. Perhaps for some newer APIs (or perhaps not), but for one that old, there's going to be a lot of cruft about. (I'd also have to think a long time about the security ramifications...)
If they did implement it via ES5 getters/setters, it wouldn't surprise me if they made it a non-configurable property (e.g., such that you couldn't change it).

Object.create on IE6+

I have a little problem, because I wrote my plugin using Object.create and it's working only on IE9+.
My plugin definition:
$.fn.MYPL = function (options) {
return this.each(function () {
myplg = Object.create(MYPL);
myplg.init(options, this);
});
};
But before every JS code I have the following:
if (typeof Object.create !== "function") {
Object.create = (function () {
function F() {} // created only once
return function (o) {
F.prototype = o; // reused on each invocation
return new F();
};
})();
}
It works fine on IE9+ but IE6 and IE7 (even IE8) seems to be not supporting Object.create or what? Am I missing sth?
Check Wikipedia's JavaScript version history. If you find 1.8.5 version - and this is the language version where you find this Object factory method available - 9th version of Internet Explorer is the one supporting that.
The ECMAScript 5 Compatibility Table also has this information.
You can also try for yourself using one of Microsoft's IE virtual machines (available from here or, for very old versions of IE, Multiple IE.
Sourced from From which version, IE can support Object.create(null)?
Older IE versions will not support Object.create. Read here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
Try to create object using constructor which is described here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain

javascript window.location fake callback

I need to make a fake window.location = "testCall" call in order to generate an event to bypass parameters on a mobile device. Works as native, however, I need then to dissmiss a NotFound exception or mainly dissmiss a fake window.location call. Possible? Thank you
Object.getOwnPropertyDescriptor(window, 'location').configurable === false
in Chrome and Safari (and I presume in other browsers). So seems like you can't change the native behavior.
If it behaved as a normal EcmaScript 5 property and configurable was set to true than you could have done something like that:
var descriptor = Object.getOwnPropertyDescriptor(window, 'location');
var setter = descriptor.set; // Doesn't exist although it should in spirit of ES5
descriptor.set = function (newLocation) {
try {
setter(newLocation);
} catch (e) {
console.log('Location error: ', newLocation, e);
}
};
// The line below will throw exception in real browser :(
// TypeError: Cannot redefine property: location
Object.defineProperty(window, 'location', descriptor);
I hope browser vendors migrate all their magical properties and objects to standard EcmaScript mechanics but at the moment we are out of luck.

Categories