9

I have a task that depends upon function.caller to sanity check that a caller is authorized.

According to this url, caller is supported in all major browsers... and all of my unit tests pass:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller

However, nodejs rejects all attempts to access function.caller (reports it as null).

I'm open to suggestions to make this work on nodejs... I'd hate to have this framework only work on browsers.

Thanks!

Michael
  • 121
  • 1
  • 1
  • 6
  • No. Don't use `function.caller`, it's deprecated for well-known reasons. And don't try to base any sanity checks or "authorisations" on that property. What are you trying to prevent [actually](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem)? – Bergi Sep 11 '14 at 05:37
  • What I'm trying to prevent? Trying to use the Object.defineProperty setters/getters such that they block access to properties we define as private. They should only be accessable if accessed from a method of the object. So we check function.caller and compare it to our list of registered object methods, and allow access to the property if the caller is a registered object method. Basically, a framework for enforcing java-like public/private properties. (And it works -- except for failing in nodejs, phantomjs, jenkins, etc...) – Michael Sep 11 '14 at 15:30
  • Don't try. JavaScript is not Java, accept that. You're not gaining any security through this, and I'd bet it doesn't make your code more usable or faster. – Bergi Sep 11 '14 at 15:41
  • 1
    Bergi, Both your points are valid. But, I wouldn't be an engineer if I didn't want to see if it could be done. :-) And for some use cases, it may be a desirable tradeoff. Obviously, this is not a solution for normal use cases. This is less about outright security, and more about keeping developers from casually mucking with stuff they shouldn't casually muck with. – Michael Sep 11 '14 at 16:31
  • Why can't you just use [standard local variables with privileged methods](http://stackoverflow.com/a/13418980/1048572)? Or underscore property names? For academic purposes, you might want to have a look at [these ideas](http://stackoverflow.com/a/23822558/1048572). Using a whole "framework" will probably make your classes rather slow, I would avoid such in production. – Bergi Sep 11 '14 at 17:29
  • `if (this !== that) throw new Error("wrong way");` – metadings Sep 12 '14 at 17:37
  • or `if (!(this instanceof ClassFunction)) throw new Error("wrong way");` – metadings Sep 12 '14 at 17:39
  • This solution here seems to work fine: https://stackoverflow.com/questions/16697791/nodejs-get-filename-of-caller-function/23890280 – AhHatem Mar 19 '19 at 05:28

4 Answers4

13

The Caller-id npm package makes it as easy as pie : https://www.npmjs.com/package/caller-id from the docs :

var callerId = require('caller-id');

// 1. Function calling another function
function foo() {
    bar();
}
function bar() {
    var caller = callerId.getData();
    /*
    caller = {
        typeName: 'Object',
        functionName: 'foo',
        filePath: '/path/of/this/file.js',
        lineNumber: 5,
        topLevelFlag: true,
        nativeFlag: false,
        evalFlag: false
    }
    */
}
eff_it
  • 396
  • 2
  • 9
  • Just be aware if you use this solution, that you shouldn't use it in a hot code path because the performance will not be what you might expect (this module generates (the expensive part) and parses stack traces). Also, this module is not a complete replacement for `.caller` because it does not give you a reference to the *actual* caller function. – mscdex Jan 28 '17 at 21:03
  • Didn't work for me (on nodeJS). Showed the filename of the package itself. This solution here worked fine: https://stackoverflow.com/questions/16697791/nodejs-get-filename-of-caller-function/23890280 – AhHatem Mar 19 '19 at 05:27
  • 1
    I've taken a quick glance at the source code of this 3d-party module, it's using function.caller behind the scenes for you, this solves nothing, since function.caller is deprecated. see: https://github.com/pixelsandbytes/caller-id/blob/master/lib/caller-id.js – Mathew Jan 18 '21 at 20:15
0

Because function.caller DOES work in nodejs, but fails when combined with Object.defineProperty getters/setters, I'm going to consider this a bug in nodejs, rather than a choice by nodejs not to support function.caller.

Michael
  • 121
  • 1
  • 1
  • 6
0

I'm rather late to this party but I was also trying to use arguments.caller and after a lot of trial and error, it's possible if you use prototype.

This does not work:

class MyClass {
    watchProperties() {
        Object.defineProperty(this, 'myproperty', {
            get: function f(value) { 

            },
            set: function f(value) { 
                console.log(arguments.caller.name);
            }
        });
    }
}


var foo = new MyClass;
foo.watchProperties();

foo.myproperty = 'bar';

But by using prototype instead it does:

class MyClass {

}

MyClass.prototype.watchProperties = function() {
    Object.defineProperty(this, 'myproperty', {
        get: function f(value) { 

        },
        set: function f(value) { 
            console.log(arguments.caller.name);
        }
    });
};

var foo = new MyClass;
foo.watchProperties();

foo.myproperty = 'bar';

This is functionally identical. I have no idea why one is prevented by node and the other isn't but this is a working workaround in node 6.0.

Tom B
  • 2,735
  • 2
  • 24
  • 30
-3

You can use arguments.callee.caller to get a reference to the caller. However I believe it's (still) deprecated (although its eventual removal could cause a problem for some cases, see here for that discussion) and you miss out on some compiler/interpreter optimizations too.

Example:

function foo() {
  bar();
}

function bar() {
  console.log(arguments.callee.caller.name);
}

foo();

// outputs: foo
mscdex
  • 104,356
  • 15
  • 192
  • 153
  • Yeah; in trying to solve this, I saw and tried arguments.callee.caller; also not supported in nodejs. – Michael Sep 11 '14 at 15:31
  • -1. `bar.caller` === `arguments.callee.caller` is what the OP is already using, and he says it doesn't work. Using `arguments.callee` only makes it worse, actually. – Bergi Sep 11 '14 at 15:43
  • @user2094063 The example I gave works for me in node v0.10.x and v0.11.x. – mscdex Sep 11 '14 at 16:01
  • 1
    Hi, some additional info; I misspoke in my original post: caller IS available in nodejs, but is NOT available to access the caller when using an Object.defineProperty setter/getter! – Michael Sep 11 '14 at 16:25
  • @Michael see my answer below. I've provided a workaround for NodeJS – Tom B May 03 '16 at 22:39