Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot read property 'disguiseToken' of undefined

This question is about the property disguiseToken itself, not about the error directly.

disguiseToken does not show up in the codebase and a google search does not bring up anything.

  • What is disguiseToken?
  • What is it a property of?
  • What is trying to access this property?

The error happens when calling getOwnPropertyDescriptor.


Example stack trace :

TypeError: Cannot read property 'disguiseToken' of undefined
at getOwnPropertyDescriptor (eval at  (:1:38695), :560:24)
at Function.Object.getOwnPropertyDescriptor (https://www.mywebsite.com/polyfills.d8680adf69e7ebd1de57.js:1:39700)
at a (https://www.mywebsite.com/main.25a9fda6ea42f4308b79.js:1:1075141)
at https://www.mywebsite.com/5.6c58d8732681a35a1f8b.js:1:1533
at Object.26NW (https://www.mywebsite.com/5.6c58d8732681a35a1f8b.js:1:1805)
at i (https://www.mywebsite.com/runtime.8928e149b3f1200cf1ca.js:1:507)
at Module.L6id (https://www.mywebsite.com/5.6c58d8732681a35a1f8b.js:1:62181)
at i (https://www.mywebsite.com/runtime.8928e149b3f1200cf1ca.js:1:507)
at https://www.mywebsite.com/main.25a9fda6ea42f4308b79.js:1:914848
at t.invoke (https://www.mywebsite.com/polyfills.d8680adf69e7ebd1de57.js:1:8160)
at M (https://www.mywebsite.com/polyfills.d8680adf69e7ebd1de57.js:1:14076)
at M (https://www.mywebsite.com/polyfills.d8680adf69e7ebd1de57.js:1:13634)
at https://www.mywebsite.com/polyfills.d8680adf69e7ebd1de57.js:1:14864
at t.invokeTask (https://www.mywebsite.com/polyfills.d8680adf69e7ebd1de57.js:1:8844)
at Object.onInvokeTask (https://www.mywebsite.com/main.25a9fda6ea42f4308b79.js:1:467756)
at t.invokeTask (https://www.mywebsite.com/polyfills.d8680adf69e7ebd1de57.js:1:8765)
at e.runTask (https://www.mywebsite.com/polyfills.d8680adf69e7ebd1de57.js:1:4026)
at g (https://www.mywebsite.com/polyfills.d8680adf69e7ebd1de57.js:1:11111)

This error happens in multiple browsers with different engines. Over the past 3 months (today is 2020-06-13) we have seen it in :

  • Chrome
  • Mobile Safari
  • Chrome Mobile
  • Firefox
  • Samsung Internet

Update (1) :

I totally understand what the error means. My question is what is a 'disguise Token'? I've never heard of that, and it's not something in my code. I searched my entire code base for the word 'disguise', and no hits, so it must be something inside of Angular. What the heck is it? I am getting this error a few dozen times a day, but I have no idea what it is. I am running Angular 7.

Looking through the source code of angular and angular.js does not give any results for disguiseToken


Update (2) :

This code results in the exact error message as seen in the error reports. This snippet was not taken from actual source code. It is included here so others can reproduce the error.

<script type="application/javascript">
    try {
        class Dummy extends HTMLElement {
            get dummyFuncA() {
                return 'dummy';
            }
        }

        var old = Object.getOwnPropertyDescriptor;
        Object.getOwnPropertyDescriptor = function(obj, property) {
            var descriptor = old.call(this, obj, property);

            var _disguiseToken = descriptor.disguiseToken;

            return descriptor;
        };

        var descriptorA = Object.getOwnPropertyDescriptor(Dummy.prototype, 'dummyFuncA');
        var descriptorB = Object.getOwnPropertyDescriptor(Dummy.prototype, 'dummyFuncB');
    } catch (err) {
        console.warn(err);
    }
</script>

This snippet on it's own can be used to test if Object.getOwnPropertyDescriptor has been overridden :

<script type="application/javascript">
    try {
        class Dummy extends HTMLElement {
            get dummyFuncA() {
                return 'dummy';
            }
        }

        var descriptorA = Object.getOwnPropertyDescriptor(Dummy.prototype, 'dummyFuncA');
        var descriptorB = Object.getOwnPropertyDescriptor(Dummy.prototype, 'dummyFuncB');
    } catch (err) {
        if (
            err &&
            err.toString &&
            err.toString().indexOf('Cannot read property \'disguiseToken\' of undefined') > -1
        ) {
            console.log('Something did override \'Object.getOwnPropertyDescriptor\'');
            console.warn(err);
            console.log(Object.getOwnPropertyDescriptor);
        }
    }
</script>

Update (3) :

We added the above but didn't learn much :/

Log for console.log(Object.getOwnPropertyDescriptor); :

function getOwnPropertyDescriptor() { [native code] }

So that seems to be fine.

We also added logs to send all script tag source url's or innerHTML to our bug reporting service when this error happens. This did not yield any unexpected results.


Update (4) :

We added code and logs to trap use of Proxy. This is not something we use in our code bases and this can theoretically also be part of this issue.

A few hours later we had some hits and it does seem that something is injecting code that creates Proxy's. This is the first direct evidence of code that isn't ours running on these pages.

Stacktrace captured when something tried to construct a Proxy :

[redacted-url]:72:24 
[redacted-url]:356:25 ObjectWithDefaultValues
[redacted-url]:404:59 parseMetaTags
[redacted-url]:453:18 
[redacted-url]:465:3 global code

The first row with line 72 is where we called new Error(). Lines 356-456 are not our code but something that was injected.

The code we used to detect Proxy use :

<script type="text/javascript">
    var proxyUses = [];

    function reportProxyUse() {
        if (true) { // [redacted] check if error reporting is ready
            for (var i = 0; i < proxyUses.length; i++) {
                console.log('target', proxyUses[i].target);
                console.log('handler', proxyUses[i].handler);
                
                // [redacted]
                // error reported here
            }

            proxyUses = [];
        } else {
            setTimeout(function() {
                reportProxyUse();
            }, 2000);
        }
    }

    window.Proxy = function( target, handler ) {
        proxyUses.push({
            'target': target,
            'handler': handler,
            'error': new Error() // this captures a stack trace
        });

        reportProxyUse();
        return target;
    };
</script>

We will add a MutationObserver next to try and trap all scripts. Even those that delete themselves.


Update (5) :

Haven't analysed this bit fully yet but it seems that "Add to Reader list" in Safari does an initial render of a page and injects the Javascript that includes the Proxy stuff.

As this is obviously only Safari the use of Proxy and the stacktrace above does not explain disguiseToken.

like image 739
Brian Kitt Avatar asked Mar 16 '19 06:03

Brian Kitt


People also ask

How do you fix undefined property Cannot be read?

To solve the "Cannot read properties of undefined" error, make sure that the DOM element you are accessing exists. The error is often thrown when trying to access a property at a non-existent index after using the getElementsByClassName() method.

What is Cannot read property of undefined?

What Causes TypeError: Cannot Read Property of Undefined. Undefined means that a variable has been declared but has not been assigned a value. In JavaScript, properties and functions can only belong to objects.

Can not read the property of undefined reading then?

JavaScript TypeError is thrown when an operand or argument passed to a function is incompatible with the type expected by that operator or function. This error occurs in Chrome Browser when you read a property or call a method on an undefined object .

What does Cannot read property toString of undefined?

The "Cannot read property 'toString' of undefined" error occurs when the toString() method is called on an undefined value. To solve the error, make sure to only call the toString method on data types that support it.


1 Answers

The jury is still out there. Just to sum up the current state and the comments:

  1. disguise + eval + no errors noticeable by users on the pages + inability to find the string. All of that implies the possibility of malicious npm package. With probably obfuscated code, that's why search for disguiseToken yields nothing. Running npm install is all you need to get one. Sometimes even respectable packages get hijacked.

  2. But there're some other possibilities

    1. injection by browser extension
    2. injection by user's software (firewalls, antiviruses or viruses, and God knows what else)
    3. injection by some lousy ISP. Even with https it is still possible, at least in some countries (Kazakhstan). Or maybe in come corporate networks.
    4. manual injection by a user
  3. It seams to be already figured out that the issue arises outside of the own code base. But that was known from the starts.

  4. Looks like the issue arises from within a Proxy. But that's not nessasery so from what we can see in the question now:

    • The fact that some code creates a Proxy doesn't mean that the error is thrown from that Proxy.
    • And because you prevent creation of the proxy by just returning the target, you shouldn't see the error.
    • And if you do - then it's not the Proxy that causes the troubles
    • And if you don't - it doesn't mean much because the error rarely occurs.
  5. What can be done? Some open questions:

    1. If you use try-catch for your logs it will capture only error from inside of your code and it's dependencies (implies malicious npm package).
    2. window.onerror - it can catch some errors from some injected scripts (those that was injected an thrown after window.onerror handler was set up)
    3. MutationObserver can catch scripts that also injected only after it was set up, and only those that was injected from the scripts that already present on a page. That again means - inside of your code's dependencies. It won't catch MiM scripts because in this case a user loads a page with whose scripts already there. So you probably should log <head> as well. Right after page load. And on the error. (But script can be as well injected at the end of HTML or elsewhere).
    4. Although there is a possibility that a page got modified only after browser loads it. That's probable with extension. Less probable with antiviruses. Improbable with MiM.
    5. Get user's IP addresses, maybe you'll find some patterns there. Even if you don't use server it looks like it's still possible: How to get client's IP address using JavaScript?
    6. As @user120242 have noted, someone had the same issue: https://gitlab.com/minds/front/-/issues/1864 I've posted a comment there asking for help, but no reply yet. The reply unfortunately was:

      I don't think we made any changes to fix this, it was closed because it stopped getting picked up in Sentry. I guess a vendor update resolved the underlying issue.

like image 75
x00 Avatar answered Sep 19 '22 07:09

x00