Replace builtin Date without being able to recover original constructor - javascript

I want to replace Date inside a JavaScript VM (V8 but this is not specific to V8), and make it impossible to access the original Date constructor. This is meant to be one part of a defence against timing attacks like Spectre with multi-tenant JavaScript running in the same process (but different JavaScript VM.) The idea is to deny access to high resolution timers. Cloudflare does this in their Workers, for the same reason. Time is advanced only when IO happens, not during computation. I know there are other ways to construct a timer, but I'm just focused on Date for this question.
I think this can be done in pure JavaScript, and this is my attempt. Is there a way to get the original Date constructor back after running this? Is there a way this differs functionally from the builtin Date - something that could break backwards compatibility?
// Current UNIX timestamp in milliseconds, but that we set manually on each call to runTasks.
// not advancing time during computation is one of our defenses against Spectre attacks.
let NOW = Date.now();
export function setDate(unixTimestampMillis) {
NOW = unixTimestampMillis;
}
Date = function (BuiltinDate) {
function Date(...args) {
if (new.target === undefined) {
// This is the deprecated naked Date() call which returns a string
return (new BuiltinDate(NOW)).toString();
}
// Otherwise it was the constructor called with new
if (args.length === 0) {
// Return the "current" time.
return new BuiltinDate(NOW);
}
// Build a Date with the specified datetime
return new BuiltinDate(...args);
}
// Make a copy of the BuiltinDate "class" and replace the constructor,
// It needs to be impossible for the user to grab an reference to BuiltinDate.
Date.prototype = BuiltinDate.prototype;
BuiltinDate.prototype.constructor = Date;
// Add the static methods now(), UTC(), and parse() - all of which return a numeric timestamp
function now() {
return NOW;
}
Date.now = now;
Date.parse = BuiltinDate.parse; // returns a number
Date.UTC = BuiltinDate.UTC; // returns a number
return Date;
}(Date);

Related

is it possible to tweak global Date function?

I'm trying to tweak the global Date function by preserving all functionality of Date but add some parameter checking inside the constructor
because I want to throw an error when calling Date function like this new Date('2021-01-01'), which will return Invalid Date in safari.
(function () {
let OldDate = window.Date;
window.Date = class Date extends (
OldDate
) {
constructor(...args) {
if (/\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d/.test(args[0])) {
throw Error('wrong date format');
}
super(...args);
}
};
})();
but this approach has a pitfall, I can't call Date without new keyword
// before override
new Date('2021/02/01'); // OK
Date('2021/02/01'); // OK
// after override
new Date('2021/02/01'); // OK
Date('2021/02/01'); // Uncaught TypeError: Class constructor Date cannot be invoked without 'new'
How do I fix it?
the motivation of tweaking the global Date function
most newbies will tend to call Date function with a format like YYYY-MM-DD HH:mm:ss, this works inside his development environment (latest version of Chrome), and he doesn't konw this won't work on safari until he tests his project on safari, so I have to tell him to change the Date format string he uses every time he falls into this problem.
Writing a code style guide won't always work, a newbie is a newbie because he always forgets things, so I have to tell him to read the style guide document again.
Instead, I want to throw an error every time the newbie use Date with the wrong date string format
new Date('2021-01-02') // Error: 'YYYY-MM-DD is wrong format', try use 'YYYY/MM/DD'
No matter why you want to override Date. My solution is
var originalDate = Date; // backup
function _date(str) {
// your implementation here
if (/\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d/.test(str)) {
throw Error('wrong date format');
}
return new originalDate(str);
}
window.Date = _date; // override
Object.assign(Date.prototype, originalDate.prototype); // I suppose you still want to have properties of the original Date.

Date Range is starting with Plus

I have created a small function which give all dates between two dates.
but this is starting from Plus 1 date.
Here's my code (also on Here is jsfiddle):
function DateRangeArr(to, from) {
console.log(to);
console.log(from);
var Dateresult = [];
var end = new Date(from);
var loop = new Date(to);
while (loop <= end) {
Dateresult.push({ 'total': 0, 'log_date': loop });
var newDate = loop.setDate(loop.getUTCDate() + 1);
loop = new Date(newDate);
}
return Dateresult;
}
console.log(DateRangeArr('2020-12-05','2020-12-12'));
It's giving me results starting from 2020-12-06 to 2020-12-13. Also will it gives result depending upon user time zone ?
There are a couple of things going on that could account for the problem.
(This is probably the thing you're seeing) You're changing the state of the Date instance that you added to the array after adding it. That changes the instance that's in the array.
Since "2020-12-05" is a date-only string, it's parsed as UTC in specification-compliant browsers (see the rules at Date.parse), so the Date objects will represent midnight on that date UTC rather than midnight on that date in your local timezone. If you want them to reliably be midnight local time, add T00:00 to the string. If you want them to be parsed reliably as UTC, you should be able to use them as-is, but the spec moved around on this (ES2015 said they should be parsed as local time, not UTC, before being updated by ES2016) so to get UTC reliably, add T00:00Z to it. (My understanding is that the spec allows just adding a Z, but others read the spec differently, and both Safari's JavaScriptCore and Firefox's SpiderMonkey reject new Date("2020-12-05Z"), so add T00:00Z.) Or use new Date(Date.UTC(year, month - 1, day)) instead.
You're mixing up accessors here: loop.setDate(loop.getUTCDate() + 1). That's using the UTC accessor to get the date, but then the local time accessor to set it. You need to be consistent in order to add a day to the date.
You haven't said how you're using the resulting array of Date objects, but just note that the date they show may seem off if (for instance) you parse them as UTC but then display the result in local time or vice-versa, because of the time zone offset.
You have to and from reversed in your code. You're using them consistently, but it's quite hard to read. :-) When building an array in date order, it goes from the earlier date to the later date.
Here's a version updated to work entirely in UTC; the resulting Date objects represent midnight UTC on each of the dates:
function dateRangeArr(from, to) {
console.log(from);
console.log(to);
var dates = [];
var end = new Date(to + "T00:00Z");
var loop = new Date(from + "T00:00Z");
while (loop <= end) {
dates.push({ "total": 0, "log_date": new Date(+loop) });
loop.setUTCDate(loop.getUTCDate() + 1);
}
return dates;
}
console.log(dateRangeArr("2020-12-05","2020-12-12"));
(I've also updated the function and variable names to be more consistent with standard practice in JavaScript, since the function isn't a constructor function.)
Here's a version that works in local time instead:
function dateRangeArr(from, to) {
console.log(from);
console.log(to);
var dates = [];
var end = new Date(to + "T00:00");
var loop = new Date(from + "T00:00");
while (loop <= end) {
dates.push({ "total": 0, "log_date": new Date(+loop) });
loop.setDate(loop.getDate() + 1);
}
return dates;
}
console.log(dateRangeArr("2020-12-05","2020-12-12"));
Which you use depends on whether you want the Date instances to represent midnight UTC (use the UTC version) or midnight local time (use the local time version).

Checking how much time passed from creating objectid in mongodb

I'm building an app that allows the user to reset his password.
The process is really simple .The user enters his email address and I sending him a link with the number of the new objectid that was created.
For exemple -> /reset-password?x=55555444475d41a000001.
After clicking the link he reaches other page and then I want to check if 24 hours have passed from the time he got the link?and yes i know there is function called "getTimestamp" but how to use it..?
get: function (request, response) {
???????
},
You can set a "creation_date" property in your object when you create it, like this:
obj = {
id: "xxxxxx...",
creation_date: new Date()
...
}
Then you store the object somewhere in your server, and then when the user opens the link with the id of the object you will do something like this to check if the object has been created more than 24hours ago:
var now = new Date();
if (now - obj.creation_date > 86400000) {
// more than 24h (86400000ms = 24h)
// do something
}
Actually reading the JavaScript API docs for ObjectId might help.
Checking wether 24 hours have passed since the creation of the object should be as easy as
var now = new Date();
/* Subtract the milliseconds a day lasts from the current time.
* If the timestamp of the ID converted to msecs after epoch is smaller
* the ObjectId was created before that.
*/
if ( myId.getTimestamp() < ( now.getTime() - 86400000 ) {
console.log("24 h have passed since creation");
}
else {
console.log("24 h haven't passed since creation");
var passed = new Date( now - myId.getTimestamp() );
console.log(passed.getUTCHours()+":"+passed.getUTCMinutes()+" have passed.");
}
Note: I downvoted your question as it would have been easily solvable by googling "MongoDB ObjectId getTimestamp JavaScript api", you didn't show any sign of working on the problem yourself and didn't bother to ask a specific question. May I politely suggest reading ESR's How To Ask Questions The Smart Way, especially the chapter about StackOverflow?

Expiring a javascript variable?

I saved some data returned from ajax in javascript variable. I would like to invalidate this variable after some time period, say one hour.
I can potentially write some setTimeout functions to handle this, but I just feel this is not optimal.
Another way is to save in cookie and set expiration, but the overhead may be unnecessary.
Is there other way that can handle this situation more elegantly?
Assuming the actual variable actually exists that long (the page stays open), just do what every cache does: save a timestamp and check for it.
var cache = { timestamp : null, data : null };
function setData(data) {
cache.data = data;
cache.timestamp = new Date;
}
function getData(maxAge) {
if (/* compare cache.timestamp to now - maxAge */) {
return null;
}
return cache.data;
}
Keep a variable that indicates when the value expires.
var foo = "bar";
var foo_expires_on = new Date(new Date().getTime() + 60 * 60 * 5); // +5 hours
deceze's answer is a more formal (monad) implementation.

Javascript Date decorator for accurate client side time

I'm currently working on some Javascript to synchronize the client side clock with our servers. I'm doing this by looking at the 'Date' header of any Ajax responses (it only has to be accurate to a few seconds).
To make the rest of the code work seamlessly with this Date function I want to override the native Date function in a decorator style so I can just enhance it with the calculated clock skew.
So far I have this (which seems to work):
var SystemDate = Date;
var ServerDate = function() {
var a = arguments;
switch(a.length){
case 0:
var system_date = new SystemDate();
return new SystemDate(system_date - ServerDate.skew);
case 1:
return new SystemDate(a[0]);
case 7:
return new SystemDate(a[0],a[1],a[2],a[3],a[4],a[5],a[6]);
}
};
ServerDate.parse = Date.parse;
ServerDate.UTC = Date.UTC;
ServerDate.skew = 0;
var Date = ServerDate;
Now whenever I get an Ajax respose I can adjust the skew property and all new instances of Date will have the adjusted value.
I'm really inviting criticism. There are a few things I'm not sure on:
Is this a good idea? - I'm not a Javascript programmer so I've no idea what sins I have just commited
How can I handle the different argument lengths more neatly - it seems very hacky
Copying the class methods UTC and parse seems very brittle
The new Date function now returns a date object on the console rather than the string representation.

Categories

Resources