Can type of defined variable be undefined in javascript?

Viewed 116

Here is an example code from Google about how to safely initialize its recaptcha service https://developers.google.com/recaptcha/docs/loading

<script async src="https://www.google.com/recaptcha/api.js"></script>

<script>    
  if(typeof grecaptcha === 'undefined') {
    grecaptcha = {};
  }

  grecaptcha.ready = function(cb){
    //How is it possible for grecaptcha to be undefined here?
    if(typeof grecaptcha === 'undefined') {
      
      const c = '___grecaptcha_cfg';
      window[c] = window[c] || {};
      (window[c]['fns'] = window[c]['fns']||[]).push(cb);
    } else {
      cb();
    }
  }

  grecaptcha.ready(function(){
    grecaptcha.render("container", {
      sitekey: "ABC-123"
    });
  });
</script>

As you can see grecaptcha.ready is a function and it again checks if(typeof grecaptcha === 'undefined'). But how can grecaptcha be undefined at the time when grecaptcha.ready is called? As I understand grecaptcha has already been initialized as a property of global object and it's value is {} hence it's type should be "object"

2 Answers

It'd have to be code elsewhere, but sure, it's a global object, so a malicious actor could potentially overwrite it, e.g. you could have something like:

var grecaptcha = { 
     start() {
         var that = this;
         return new Promise((resolve, reject) => {
             setTimeout(() => {
                 if (that.ready) {
                     that.ready(that)
                 }
                 resolve(true);
             }, 5000);
             let remaining = 4;
             let interval = setInterval(() => {
                 console.log(remaining--);
                 if (remaining == 0) {
                     clearInterval(interval);
                 }
             }, 1000);
         });
     }
 
};

grecaptcha.start().then(() => console.log('But we still do stuff with it'));

if(typeof grecaptcha === 'undefined') {
    grecaptcha = {};
}

grecaptcha.ready = function(cb){
    console.log(typeof grecaptcha);
}

setTimeout(() => grecaptcha = undefined, 1000)

I think the code is wrong: https://jsfiddle.net/657b1gso/

Reported: https://github.com/google/recaptcha/issues/485

and works with a few small modifications: https://jsfiddle.net/p6ubwLme/

<script async src="https://www.google.com/recaptcha/api.js"></script>
<script>
  // How this code snippet works:
  // This logic overwrites the default behavior of `grecaptcha.ready()` to
  // ensure that it can be safely called at any time. When `grecaptcha.ready()`
  // is called before reCAPTCHA is loaded, the callback function that is passed
  // by `grecaptcha.ready()` is enqueued for execution after reCAPTCHA is
  // loaded.
  if(typeof grecaptcha === 'undefined') {
    grecaptcha = {};
  }
  grecaptcha.asyncready = function(cb){
    if(typeof grecaptcha.render === 'undefined') {
      // window.__grecaptcha_cfg is a global variable that stores reCAPTCHA's
      // configuration. By default, any functions listed in its 'fns' property
      // are automatically executed when reCAPTCHA loads.
      const c = '___grecaptcha_cfg';
      window[c] = window[c] || {};
      (window[c]['fns'] = window[c]['fns']||[]).push(cb);
    } else {
      cb();
    }
  }

  // Usage
  grecaptcha.asyncready(function(){
    grecaptcha.render("container", {
      sitekey: "ABC-123"
    });
  });
</script>
<div id="container">

</div>
Related