All files / fast-redact/lib redactor.js

100% Statements 25/25
100% Branches 12/12
100% Functions 8/8
100% Lines 21/21

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88    1x   1x       66x                     66x 59x     66x       66x 51x   51x   51x 139x 139x 139x   88x 51x 43x   51x   88x             51x                               66x                             66x            
'use strict'
 
const rx = require('./rx')
 
module.exports = redactor
 
function redactor ({secret, serialize, wcLen}, state) {
  /* eslint-disable-next-line */
  const redact = Function('o', `
    if (typeof o !== 'object' || o == null) {
      throw Error('fast-redact: primitives cannot be redacted')
    }
    const { censor, secret } = this
    ${redactTmpl(secret)}
    this.compileRestore()
    ${dynamicRedactTmpl(wcLen > 0)}
    ${resultTmpl(serialize)}
  `).bind(state)
 
  if (serialize === false) {
    redact.restore = (o) => state.restore(o)
  }
 
  return redact
}
 
function redactTmpl (secret) {
  return Object.keys(secret).map((path) => {
    const { escPath } = secret[path]
 
    const hops = []
    var match
    while ((match = rx.exec(path)) !== null) {
      const [ , ix ] = match
      const { index, input } = match
      if (index > 0) hops.push(input.substring(0, index - (ix ? 0 : 1)))
    }
    var existence = hops.map((p) => `o.${p}`).join(' && ')
    if (existence.length === 0) existence += `o.${path} != null`
    else existence += ` && o.${path} != null`
 
    const circularDetection = `
      switch (true) {
        ${hops.reverse().map((p) => `
          case o.${p} === censor: 
            secret[${escPath}].circle = ${JSON.stringify(p)}
            break
        `).join('\n')}
      }
    `
    return `
      if (${existence}) {
        const val = o.${path}
        if (val === censor) {
          secret[${escPath}].precensored = true
        } else {
          secret[${escPath}].val = val
          o.${path} = censor
          ${circularDetection}
        }
      }
    `
  }).join('\n')
}
 
function dynamicRedactTmpl (hasWildcards) {
  return hasWildcards === true ? `
    {
      const { wildcards, wcLen, groupRedact, nestedRedact } = this
      for (var i = 0; i < wcLen; i++) {
        const { before, beforeStr, after, nested } = wildcards[i]
        if (nested === true) {
          secret[beforeStr] = secret[beforeStr] || []
          nestedRedact(secret[beforeStr], o, before, after, censor)
        } else secret[beforeStr] = groupRedact(o, before, censor)
      }
    }
  ` : ''
}
 
function resultTmpl (serialize) {
  return serialize === false ? `return o` : `
    var s = this.serialize(o)
    this.restore(o)
    return s
  `
}