NaturalCycles/nodejs-lib

View on GitHub
src/secret/secrets-decrypt.util.ts

Summary

Maintainability
A
0 mins
Test Coverage
C
79%
import fs from 'node:fs'
import path from 'node:path'
import { _assert } from '@naturalcycles/js-lib'
import { dimGrey, yellow } from '../colors/colors'
import { fastGlob, fs2 } from '../index'
import { decryptObject, decryptRandomIVBuffer } from '../security/crypto.util'

export interface DecryptCLIOptions {
  dir: string[]
  file?: string
  encKeyBuffer: Buffer
  del?: boolean
  jsonMode?: boolean
}

// Debug it like this:
// yarn tsn ./src/bin/secrets-encrypt.ts --file ./src/test/secrets2.plain.json --jsonMode --encKey MPd/30v0Zcce4I5mfwF4NSXrpTYD9OO4/fIqw6rjNiWp2b1GN9Xm8nQZqr7c9kKSsATqtwe0HkJFDUGzDSow44GDgDICgB1u1rGa5eNqtxnOVGRR+lIinCvN/1OnpjzeoJy2bStXPj1DKx8anMqgA8SoOZdlWRNSkEeZlolru8Ey0ujZo22dfwMyRIEniLcqvBm/iMiAkV82fn/TxYw05GarAoJcrfPeDBvuOXsARnMCyX18qTFL0iojxeTU8JHxr8TX3eXDq9cJJmridEKlwRIAzADwtetI4ttlP8lwJj1pmgsBIN3iqYssZYCkZ3HMV6BoEc7LTI5z/45rKrAT1A==
// yarn tsn ./src/bin/secrets-decrypt.ts --file ./src/test/secrets2.json --jsonMode --encKey MPd/30v0Zcce4I5mfwF4NSXrpTYD9OO4/fIqw6rjNiWp2b1GN9Xm8nQZqr7c9kKSsATqtwe0HkJFDUGzDSow44GDgDICgB1u1rGa5eNqtxnOVGRR+lIinCvN/1OnpjzeoJy2bStXPj1DKx8anMqgA8SoOZdlWRNSkEeZlolru8Ey0ujZo22dfwMyRIEniLcqvBm/iMiAkV82fn/TxYw05GarAoJcrfPeDBvuOXsARnMCyX18qTFL0iojxeTU8JHxr8TX3eXDq9cJJmridEKlwRIAzADwtetI4ttlP8lwJj1pmgsBIN3iqYssZYCkZ3HMV6BoEc7LTI5z/45rKrAT1A==

// yarn tsn ./src/bin/secrets-encrypt.ts --file ./src/test/secrets.json -encKey MPd/30v0Zcce4I5mfwF4NSXrpTYD9OO4/fIqw6rjNiWp2b1GN9Xm8nQZqr7c9kKSsATqtwe0HkJFDUGzDSow44GDgDICgB1u1rGa5eNqtxnOVGRR+lIinCvN/1OnpjzeoJy2bStXPj1DKx8anMqgA8SoOZdlWRNSkEeZlolru8Ey0ujZo22dfwMyRIEniLcqvBm/iMiAkV82fn/TxYw05GarAoJcrfPeDBvuOXsARnMCyX18qTFL0iojxeTU8JHxr8TX3eXDq9cJJmridEKlwRIAzADwtetI4ttlP8lwJj1pmgsBIN3iqYssZYCkZ3HMV6BoEc7LTI5z/45rKrAT1A==
// yarn tsn ./src/bin/secrets-decrypt.ts --file ./src/test/secrets.json.enc -encKey MPd/30v0Zcce4I5mfwF4NSXrpTYD9OO4/fIqw6rjNiWp2b1GN9Xm8nQZqr7c9kKSsATqtwe0HkJFDUGzDSow44GDgDICgB1u1rGa5eNqtxnOVGRR+lIinCvN/1OnpjzeoJy2bStXPj1DKx8anMqgA8SoOZdlWRNSkEeZlolru8Ey0ujZo22dfwMyRIEniLcqvBm/iMiAkV82fn/TxYw05GarAoJcrfPeDBvuOXsARnMCyX18qTFL0iojxeTU8JHxr8TX3eXDq9cJJmridEKlwRIAzADwtetI4ttlP8lwJj1pmgsBIN3iqYssZYCkZ3HMV6BoEc7LTI5z/45rKrAT1A==

/**
 * Decrypts all files in given directory (*.enc), saves decrypted versions without ending `.enc`.
 * Using provided encKey.
 */
export function secretsDecrypt(
  dir: string[],
  file: string | undefined,
  encKeyBuffer: Buffer,
  del = false,
  jsonMode = false,
): void {
  // If `file` is provided - only this one file is used
  const patterns = file ? [file] : dir.map(d => `${d}/**/*.enc`)

  const filenames = fastGlob.sync(patterns)

  filenames.forEach(filename => {
    let plainFilename

    if (jsonMode) {
      _assert(filename.endsWith('.json'), `${path.basename(filename)} MUST end with '.json'`)
      _assert(
        !filename.endsWith('.plain.json'),
        `${path.basename(filename)} MUST NOT end with '.plain.json'`,
      )
      plainFilename = filename.replace('.json', '.plain.json')

      const json = decryptObject(fs2.readJson(filename), encKeyBuffer)

      fs2.writeJson(plainFilename, json, { spaces: 2 })
    } else {
      const enc = fs.readFileSync(filename)
      const plain = decryptRandomIVBuffer(enc, encKeyBuffer)
      plainFilename = filename.slice(0, filename.length - '.enc'.length)
      fs.writeFileSync(plainFilename, plain)
    }

    if (del) {
      fs.unlinkSync(filename)
    }

    console.log(`  ${path.basename(filename)} > ${path.basename(plainFilename)}`)
  })

  console.log(`decrypted ${yellow(filenames.length)} files in ${dimGrey(dir.join(' '))}`)
}