1

In the section of secret server code in the meteor docs (https://guide.meteor.com/security.html#secret-code) they seem to use a global variable defined only on the server, thus, the code can only be seen and reached on the server. Seems simple enough.

But when I do

upload = { test: "my secret code" }

Inside the folder server/upload.js I get the error

W20170726-10:04:59.843(2)? (STDERR) 
C:\Users\myuser\AppData\Local\.meteor\packages\meteor-tool\1.5.0\mt-os.windows.x86_32\dev_bundle\server-lib\node_modules\fibers\future.js:280
W20170726-10:04:59.844(2)? (STDERR)                                             throw(ex);
W20170726-10:04:59.846(2)? (STDERR)                                             ^
W20170726-10:04:59.847(2)? (STDERR)
W20170726-10:04:59.847(2)? (STDERR) ReferenceError: upload is not defined
W20170726-10:04:59.848(2)? (STDERR)     at meteorInstall.server.upload.upload.js (server/upload/upload.js:1:1)
W20170726-10:04:59.849(2)? (STDERR)     at fileEvaluate (packages\modules-runtime.js:333:9)
W20170726-10:04:59.850(2)? (STDERR)     at require (packages\modules-runtime.js:228:16)
W20170726-10:04:59.851(2)? (STDERR)     at C:\Users\myuser\Documents\projects\myproject\.meteor\local\build\programs\server\app\app.js:10417:1
W20170726-10:04:59.852(2)? (STDERR)     at C:\Users\myuser\Documents\projects\myproject\.meteor\local\build\programs\server\boot.js:338:34
W20170726-10:04:59.853(2)? (STDERR)     at Array.forEach (native)
W20170726-10:04:59.854(2)? (STDERR)     at Function._.each._.forEach (C:\Users\myuser\AppData\Local\.meteor\packages\meteor-tool\1.5.0\mt-os.windows.x86_32\dev_bundle\server-lib\node_modules\underscore\underscore.js:79:11)
W20170726-10:04:59.855(2)? (STDERR)     at C:\Users\myuser\Documents\projects\myproject\.meteor\local\build\programs\server\boot.js:158:5
W20170726-10:04:59.856(2)? (STDERR)     at C:\Users\myuser\Documents\projects\myproject\.meteor\local\build\programs\server\boot.js:387:5
W20170726-10:04:59.858(2)? (STDERR)     at Function.run (C:\Users\myuser\Documents\projects\myproject\.meteor\local\build\programs\server\profile.js:510:12)

Are the docs wrong or am I just doing something weird? I'm using meteor version 1.5.0, happens on both windows and linux.

2 Answers2

1

The documentation states, that

Secret business logic in your app should be located in code that is only loaded on the server

It (unfortunately just) implies, that code by meteor methods or validated methods is also virtually executed on the client (see this.isSimulation) as part of the optimistic UI and thus may expose secrets, such as keys.

Using global.myvariable = { ... } is not a good solution here.

To make it more clear to you, I extend the example from the docs a little bit:

/server/mmr.js (only loaded by your server)

export const MMR = {
  updateWithSecretAlgorithm(userId) {
    // your secret code here
  }
}

/both/updatemmr.js (loaded by both server and client)

if (Meteor.isServer) {
    //eslint will nag but it does not cause any error
    import {MMR} from '../server/mmr.js';
}

// In a file loaded on client and server
const Meteor.users.methods.updateMMR = new ValidatedMethod({
  name: 'Meteor.users.methods.updateMMR',
  validate: null,
  run() {
    if (this.isSimulation) {
      // Simulation code for the client (optional)
    } else {
      MMR.updateWithSecretAlgorithm(this.userId);
    }
  }
});

The Meteor.isServer only assures, that there will be no attempt of the client to import MMR, which would cause an error on startup. As long as you load the mmr.js file only on the server there will be no MMR object exposed to the client.

I hope this makes the example a bit more clear.

Jankapunkt
  • 8,128
  • 4
  • 30
  • 59
  • Yeah I did want to try something like this but I thought the conditional import was dirty (as you mentioned eslint will nag), but perhaps you are right that global variables indeed are more messy still. I did discover however that we can use require instead (https://guide.meteor.com/structure.html#using-require) which seems to be the recommended meteor way for conditional import. – Michael Rehn Jul 26 '17 at 12:49
  • Yes, using require is even more save. I use explicit import just because of the named exports and I never got problems within any non-toplevel code, even not in production. Also note, that the mentioned issue with the dynamic require is now solved by [dynamic imports](https://blog.meteor.com/dynamic-imports-in-meteor-1-5-c6130419c3cd) – Jankapunkt Jul 26 '17 at 12:53
0

Okay, for some reason just typing myvariable = { ... } added it to the global object, so I added i explicitly with global.myvariable = { ... }. It seems to work well so far!

Edit

As Jankapunkt's correctly pointed out, global variables are indeed discouraged. But instead of using import inside of a if-statement as Jankapunkt suggested you should use the CommonJS syntax of require instead, as recommended in the meteor docs (https://guide.meteor.com/structure.html#using-require), e.g.

let MMR;
if (Meteor.isServer) {
   MMR = require('../server/mmr.js').MMR;
}