'use strict' const rx = require('./rx') module.exports = redactor function redactor ({ secret, serialize, wcLen, strict, isCensorFct, censorFctTakesPath }, state) { /* eslint-disable-next-line */ const redact = Function('o', ` if (typeof o !== 'object' || o == null) { ${strictImpl(strict, serialize)} } const { censor, secret } = this ${redactTmpl(secret, isCensorFct, censorFctTakesPath)} this.compileRestore() ${dynamicRedactTmpl(wcLen > 0, isCensorFct, censorFctTakesPath)} ${resultTmpl(serialize)} `).bind(state) if (serialize === false) { redact.restore = (o) => state.restore(o) } return redact } function redactTmpl (secret, isCensorFct, censorFctTakesPath) { return Object.keys(secret).map((path) => { const { escPath, leadingBracket, path: arrPath } = secret[path] const skip = leadingBracket ? 1 : 0 const delim = leadingBracket ? '' : '.' const hops = [] var match while ((match = rx.exec(path)) !== null) { const [ , ix ] = match const { index, input } = match if (index > skip) hops.push(input.substring(0, index - (ix ? 0 : 1))) } var existence = hops.map((p) => `o${delim}${p}`).join(' && ') if (existence.length === 0) existence += `o${delim}${path} != null` else existence += ` && o${delim}${path} != null` const circularDetection = ` switch (true) { ${hops.reverse().map((p) => ` case o${delim}${p} === censor: secret[${escPath}].circle = ${JSON.stringify(p)} break `).join('\n')} } ` const censorArgs = censorFctTakesPath ? `val, ${JSON.stringify(arrPath)}` : `val` return ` if (${existence}) { const val = o${delim}${path} if (val === censor) { secret[${escPath}].precensored = true } else { secret[${escPath}].val = val o${delim}${path} = ${isCensorFct ? `censor(${censorArgs})` : 'censor'} ${circularDetection} } } ` }).join('\n') } function dynamicRedactTmpl (hasWildcards, isCensorFct, censorFctTakesPath) { 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, ${isCensorFct}, ${censorFctTakesPath}) } else secret[beforeStr] = groupRedact(o, before, censor, ${isCensorFct}, ${censorFctTakesPath}) } } ` : '' } function resultTmpl (serialize) { return serialize === false ? `return o` : ` var s = this.serialize(o) this.restore(o) return s ` } function strictImpl (strict, serialize) { return strict === true ? `throw Error('fast-redact: primitives cannot be redacted')` : serialize === false ? `return o` : `return this.serialize(o)` }