Skip to content

Server-side snippets

Tip

Server-side execution allows you to run NodeJS snippets directly in your modules javascript file, similiar to PHP.

Example

In the below example Lodash is used server-side to search for an object and generate a random number. The output of the commands is then directly available to the hooked browser.

Module source code

This is what you put in your modules javascript file. When the client requests the module, the server evaluates calls to the $ class using some magic ✨

let activeUser = $.FUNCTION(`
  const _ = require('lodash')
  return _.find([
    { 'user': 'barney',  'age': 36, 'active': true },
    { 'user': 'fred',    'age': 40, 'active': false },
    { 'user': 'pebbles', 'age': 1,  'active': true }
  ], 'active')
`)
console.log(activeUser)

let randomNumber = $(_.random(100))

Parsed module output

This is what the server sends back to the client (aka the parsed output)

let activeUser = { 'user': 'barney', 'age': 36, 'active': true }
console.log(activeUser)

let randomNumber = 46

Overview

Use cases

  • Minimize network traffic between the server and client
    • Large libraries don't need to be sent to the client
    • CPU intensive calculations can be performed on the server
  • Perform web requests without worrying about CORS
  • Randomize the modules actions to prevent reverse-engineering
  • Run shell commands on the server

Is it secure?

Yes. The only way for server-side code to be executed is by placing it in the modules bundle.js. Code cannot be changed at processing time or by using the Web interfacce.

Documentation

Typescript typings

If you are using Typescript, import the typings by adding the following to the top of your module.ts

import ModuleTypings from '../../../definitions/module'
declare const { Module, injectify, $ } : ModuleTypings
Typings for the $ global class
$: {
  /**
   * Performs an expression / function on the server and returns the value. Type checking is disabled
   */
  _(data: Function | String | Object | Number | Boolean | any): any

  /**
   * Executes shell commands on the server and returns output
   * @returns {string} stdout of the command
   */
  SHELL(commands: String): String

  /**
   * Runs a NodeJS code snippet on the server
   * @returns {any} The return value of the function
   */
  FUNCTION(script: String | Function): any

  /**
   * Returns a javascript object populated with server-processed keys
   * Type-checking - If the result is not an object, the module won't execute
   */
  OBJECT(data: String | Object): {[key: string]: any}

  /**
   * Returns a number evaluated from an expression
   * Type-checking - If the result is not a number, the module won't execute
   */
  NUMBER(data: Number | String): Number

  /**
   * Returns a string evaluated from an expression
   * Type-checking - If the result is not a string, the module won't execute
   */
  STRING(data: String): String

  /**
   * Returns a array evaluated from an expression.
   * Type-checking - If the result is not an array, the module won't execute
   */
  ARRAY(data: any[] | string): any[]

  /**
   * Returns a boolean evaluated from an expression.
   * Type-checking - If the result is not an array, the module won't execute
   */
  BOOLEAN(data: boolean | string): boolean

  /**
   * Writes data to the servers filesystem
   */
  WRITE(filename: string, data: string): void
}

Dynamic methods

Dynamic methods are quick and easy to use and suited for most use cases. They allow you to quickly evaluate and return data. They are evaluated in the same process as the server - have access to all the same variables & NodeJS APIs

Note

If you intend to use functions which make use of require(), or Typescript is throwing errors, you should use Fixed Methods

Module source code

Example:

let bool   = $._(2 > 1)
let object = $._({ a: 'b' })
let array  = $._([1,2,3])
let string = $._('test')
let func   = $._(() => { return 'func value' }))

Output:

let bool   = true
let object = {"a":"b"}
let array  = [1,2,3]
let string = "test"
let func   = "func value"

Fixed methods

Fixed methods are used when you want to:

  • Enhance & prevent typescript errors
  • Prevent webpack from transforming the snippets

Functions

Fixed functions can be escaped in quotes to prevent Webpack conflicts and Typescript errors (eg. require() being converted to __webpack_require__)

Module source code

Normal:

let uptime = $.FUNCTION(() => {
 const os = require('os')
 return os.uptime()
})

Escaped:

let uptime = $.FUNCTION(`() => {
 const os = require('os')
 return os.uptime()
}`)

You can also omit the arrow function:

let uptime = $.FUNCTION(`
 const os = require('os')
 return os.uptime()
`)

Output:

let uptime = 4523

Objects & Arrays

Module source code

Normal:

let object = $.OBJECT({
  uptime: process.uptime()
})
let array = $.ARRAY([+new Date(), 1+2])

Escaped:

let object = $.OBJECT(`{
  uptime: process.uptime()
}`)
let array = $.ARRAY(`[+new Date(), 1+2]`)

Output:

let object = {"uptime":4523}
let array = [1519876291276, 3]

Strings, Numbers & Booleans

Module source code

Normal:

let string = $.STRING(`The Date is ${new Date()}`)
let number = $.NUMBER(+new Date())
let boolean = $.BOOLEAN(2 > 1)

Escaped:

let string = $.STRING("`The Date is ${new Date()}`")
let number = $.NUMBER(`+new Date()`)
let boolean = $.BOOLEAN(`2 > 1`)

Output:

let string = "The Date is Thu Mar 01 2018"
let number = 1519876291276
let boolean = true

Shell commands

You can easily perform shell commands on the server and retrieve the standard output stream. Uses shelljs and the synchronous shelljs.exec() method

Module source code

Source:

let stdout = $.SHELL(`echo Hello world, the date is ${new Date()}`)

Output:

let stdout = "Hello world, the date is Sun Mar 04 2018 19:13:40 GMT+0000\r\n"

Filesystem commands

Each module has it's own designated ./data folder in which you can write and append to files.

Module source code

Example:

$.WRITE(`${injectify.info.ip.query}.txt`, Module.params)

Each time the module is called, it will create a text file with the clients IP address as the filename and append the Modules parameters to that file.

API

Runtime variables

Each time the module is called, the server inserts the injectify and Module methods into the snippet's scope.

This basically means you can seamlessly access client-side variables such as Module.params & injectify.info.ip etc.

Note

These variables are replicated server-side based on the Injectify API. This means that not all methods exist (eg. Module.resolve) and what the client actually has may differ.

Module source code

Source:

console.log($._(injectify.info))
console.log($._(Module))

Output:

console.log({
  id: '5dba5160-fb72-4ea6-a9da-d27df1864dc9',
  debug: true,
  project: 'private',
  ip: { query: '127.0.0.1' }
  window: ...,
  devtools: ...,
  socket: ...,
  ...
})
console.log({
  name: 'example',
  token: 'b9ab7ae4-538c-53a7-498a-409fb2bf1943'
})