ubershmekel
ubershmekel

Reputation: 12798

How do you prevent a NodeJS server from potentially exposing function code?

Let's imagine you're building a banking app backend. You want to respond to a user with a string that returns the balance but you forgot to add ().

class User {
  constructor() {console.log("ctor")}
  balance() { console.log("secret balance code")}
}

Then when referencing the user, instead of writing this:

const userA = new User();
return `Your balance is $${userA.balance()}`;

I accidentally write this:

const userA = new User();
return `Your balance is $${userA.balance}`;

Which sadly outputs:

'Your balance is balance() { console.log("secret balance code")}'

Which leaks the source code.

Upvotes: 0

Views: 99

Answers (4)

Shobhit Chittora
Shobhit Chittora

Reputation: 1279

I've a solution which is definitely slower than raw templates, but here it goes.

So basically I just send a context object which has all the string I want to resolve. And before the actual string replacement, I just check for the types of arguments.

function resolveTemplates(str, args){
  if(args && Array.isArray(args) && args.length){
    args.forEach((argument) => {
      // check here for any unwanted types
      if(typeof arg === 'function'){
        throw new Error('Cannot send function to create raw Strings')
      }
    })
  }

  const rx = /\{([^{}]*)\}/g;

  let match = {};
  let matches = [];
  while(match = rx.exec(str)){
    matches.push(match)    
  }

  matches.reverse();
  matches.forEach(function(match){
    const key = match[1];
    const index = match.index;
    str = str.slice(0, index) + args[key] + str.slice(index + 2 + key.length) 
  })

  return str;
}


resolveTemplates('Hello! My name is {firstName} {lastName}', {firstName: 'Shobhit', lastName: 'Chittora'})

PS: Instead of throwing errors for functions as arguments, you can call the functions. But binding the functions to the correct context can be a overhead to think about and generally not suggested.

Upvotes: 0

Tim Times
Tim Times

Reputation: 422

You do not need to worry about it, if you forget something, then testing will help to find it. Nobody deploy in production without testing when he has a serious project. It is better to write tests than to try to correct language behavior.

Upvotes: 2

Brad
Brad

Reputation: 163232

When responding to a request, you're undoubtedly going to be running the response through some sort of serializer. JSON, CBOR, etc. Handle it on that layer.

Fortunately for you, if you're returning JSON data, it's already handled:

JSON.stringify(someFunction);
// undefined

If you really are returning plain text strings, you can still have such a layer that ensures you're not putting out functions.

Upvotes: 0

ubershmekel
ubershmekel

Reputation: 12798

One workaround is to override all functions' toString like so:

> Function.prototype.toString = () => {return "bla"}
[Function]
> '' + new User().balance
'bla'

Upvotes: 1

Related Questions