Skip to content

Commit

Permalink
removed debug function and package and updated use of debug logs thro…
Browse files Browse the repository at this point in the history
…ughout cli to use new logger; added masking to the payload logged to obfuscate secret information
  • Loading branch information
rh0delta committed Jul 8, 2024
1 parent b03f6f1 commit c0f1889
Show file tree
Hide file tree
Showing 22 changed files with 121 additions and 71 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@
"ansi-colors": "^4.1.3",
"cli-progress": "^3.12.0",
"commander": "^12.0.0",
"debug": "^4.3.4",
"env-paths": "^3.0.0",
"inquirer": "8.0.0",
"lodash.clonedeep": "^4.5.0",
"mkdirp": "^3.0.1",
"typescript": "^4.8.4",
"viem": "^2.7.8",
Expand Down
2 changes: 0 additions & 2 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { cliGetBalance } from './flows/balance/cli'
import { cliListAccounts } from './flows/manage-accounts/cli'
import { cliEntropyTransfer } from './flows/entropyTransfer/cli'
import { cliSign } from './flows/sign/cli'
// import { debug } from './common/utils'

const program = new Command()

Expand Down Expand Up @@ -68,7 +67,6 @@ program.command('list')
.description('List all accounts. Output is JSON of form [{ name, address, data }]')
.action(async () => {
// TODO: test if it's an encrypted account, if no password provided, throw because later on there's no protection from a prompt coming up

const accounts = await cliListAccounts()
writeOut(accounts)
process.exit(0)
Expand Down
6 changes: 4 additions & 2 deletions src/common/initializeEntropy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import Entropy, { wasmGlobalsReady } from "@entropyxyz/sdk"
import Keyring from "@entropyxyz/sdk/keys"
import inquirer from "inquirer"
import { decrypt, encrypt } from "../flows/password"
import { debug } from "../common/utils"
import * as config from "../config"
import { EntropyAccountData } from "../config/types"
import { EntropyLogger } from "./logger"

// TODO: unused
// let defaultAccount // have a main account to use
Expand Down Expand Up @@ -36,6 +36,7 @@ type MaybeKeyMaterial = EntropyAccountData | string
// WARNING: in programatic cli mode this function should NEVER prompt users, but it will if no password was provided
// This is currently caught earlier in the code
export const initializeEntropy = async ({ keyMaterial, password, endpoint, configPath }: InitializeEntropyOpts): Promise<Entropy> => {
const logger = new EntropyLogger('initializeEntropy', endpoint)
try {
await wasmGlobalsReady()

Expand Down Expand Up @@ -90,7 +91,7 @@ export const initializeEntropy = async ({ keyMaterial, password, endpoint, confi

})
keyrings.default = keyring
debug(keyring)
logger.debug(keyring)

// TO-DO: fix in sdk: admin should be on kering.accounts by default
// /*WANT*/ keyrings[keyring.admin.address] = keyring
Expand All @@ -111,6 +112,7 @@ export const initializeEntropy = async ({ keyMaterial, password, endpoint, confi

return entropy
} catch (error) {
logger.error('Error while initializing entropy', error)
console.error(error.message)
if (error.message.includes('TimeError')) {
process.exit(1)
Expand Down
5 changes: 2 additions & 3 deletions src/common/logger.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import envPaths from 'env-paths'
import { join } from 'path'
import * as winston from 'winston'
import { maskPayload } from './masking'

/**
* Winston Base Log Levels for NPM
Expand Down Expand Up @@ -56,14 +57,12 @@ export class EntropyLogger {
const DEBUG_PATH = join(paths.log, 'entropy-cli.debug.log')
const ERROR_PATH = join(paths.log, 'entropy-cli.error.log')
const INFO_PATH = join(paths.log, 'entropy-cli.info.log')
const VERBOSE_PATH = join(paths.log, 'entropy-cli.verbose.log')

this.winstonLogger = winston.createLogger({
level: process.env.LOG_LEVEL,
format,
defaultMeta: { service: 'Entropy CLI' },
transports: [
new winston.transports.File({ filename: VERBOSE_PATH }),
new winston.transports.File({
level: 'error',
filename: ERROR_PATH
Expand Down Expand Up @@ -115,7 +114,7 @@ export class EntropyLogger {
protected writeLogMsg (level: string, message: any, context?: string, description?: string, stack?: string) {
this.winstonLogger.log({
level,
message,
message: maskPayload(message),
context: context || this.context,
endpoint: this.endpoint,
description,
Expand Down
37 changes: 37 additions & 0 deletions src/common/masking.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import cloneDeep from 'lodash.clonedeep'

const DEFAULT_MASKED_FIELDS = [
'seed',
'secretKey',
'addressRaw',
];

export function maskPayload (payload: any): any {
const clonedPayload = cloneDeep(payload);
const maskedPayload = {}

if (!clonedPayload) {
return clonedPayload;
}

// maskJSONFields doesn't handle nested objects very well so we'll
// need to recursively walk to object and mask them one by one
for (const [property, value] of Object.entries(clonedPayload)) {
console.log(property, typeof value);

if (value && typeof value === 'object') {
if (Object.keys(clonedPayload[property]).filter(key => isNaN(parseInt(key))).length === 0) {
const reconstructedUintArr: number[] = Object.values(clonedPayload[property])
maskedPayload[property] = "base64:" + Buffer.from(reconstructedUintArr).toString("base64");
} else {
maskedPayload[property] = maskPayload(value);
}
} else if (value && typeof value === 'string' && DEFAULT_MASKED_FIELDS.includes(property)) {
maskedPayload[property] = "*".repeat(clonedPayload[property].length)
} else {
maskedPayload[property] = value
}
}

return maskedPayload;
}
12 changes: 1 addition & 11 deletions src/common/utils.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,14 @@
import { decodeAddress, encodeAddress } from "@polkadot/keyring"
import { hexToU8a, isHex } from "@polkadot/util"
import { Buffer } from 'buffer'
import Debug from 'debug'
import { EntropyAccountConfig } from "../config/types"

const _debug = Debug('@entropyxyz/cli')

export function stripHexPrefix (str: string): string {
if (str.startsWith('0x')) return str.slice(2)
return str
}

export function debug (...args: any[]) {
_debug(...args.map(arg => {
return typeof arg === 'object'
? JSON.stringify(arg, replacer, 2)
: arg
}))
}
function replacer (key, value) {
export function replacer (key, value) {
if(value instanceof Uint8Array ){
return Buffer.from(value).toString('base64')
}
Expand Down
3 changes: 2 additions & 1 deletion src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import envPaths from 'env-paths'


import allMigrations from './migrations'
import { replacer } from 'src/common/utils'

const paths = envPaths('entropy-cryptography', { suffix: '' })
const CONFIG_PATH = join(paths.config, 'entropy-cli.json')
Expand Down Expand Up @@ -67,5 +68,5 @@ export function getSync (configPath = CONFIG_PATH) {

export async function set (config = {}, configPath = CONFIG_PATH) {
await mkdirp(dirname(configPath))
await writeFile(configPath, JSON.stringify(config))
await writeFile(configPath, JSON.stringify(config, replacer))
}
6 changes: 3 additions & 3 deletions src/flows/DeployPrograms/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import * as util from "@polkadot/util"
import inquirer from "inquirer"
import { readFileSync } from "fs"
import { initializeEntropy } from "../../common/initializeEntropy"
import { debug, print, getSelectedAccount } from "../../common/utils"
import { print, getSelectedAccount } from "../../common/utils"
import { EntropyTuiOptions } from "src/types"

export async function devPrograms ({ accounts, selectedAccount: selectedAccountAddress }, options) {
export async function devPrograms ({ accounts, selectedAccount: selectedAccountAddress }, options: EntropyTuiOptions) {
const { endpoint } = options
const selectedAccount = getSelectedAccount(accounts, selectedAccountAddress)

Expand Down Expand Up @@ -84,7 +85,6 @@ async function deployProgram (entropy: Entropy, account: any) {

async function getOwnedPrograms (entropy: Entropy, account: any) {
const userAddress = account.address
debug('Account address:',userAddress)
if (!userAddress) return

try {
Expand Down
8 changes: 5 additions & 3 deletions src/flows/UserPrograms/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import inquirer from "inquirer"
import * as util from "@polkadot/util"
import { initializeEntropy } from "../../common/initializeEntropy"
import { debug, getSelectedAccount, print } from "../../common/utils"
import { getSelectedAccount, print } from "../../common/utils"
import { EntropyLogger } from "src/common/logger";

let verifyingKey: string;

export async function userPrograms ({ accounts, selectedAccount: selectedAccountAddress }, options) {
export async function userPrograms ({ accounts, selectedAccount: selectedAccountAddress }, options, logger: EntropyLogger) {
const FLOW_CONTEXT = 'USER_PROGRAMS'
const { endpoint } = options
const selectedAccount = getSelectedAccount(accounts, selectedAccountAddress)

Expand Down Expand Up @@ -76,7 +78,7 @@ export async function userPrograms ({ accounts, selectedAccount: selectedAccount
message: "Enter the program pointer you wish to check:",
validate: (input) => (input ? true : "Program pointer is required!"),
}])
debug('program pointer', programPointer);
logger.debug(`program pointer: ${programPointer}`, `${FLOW_CONTEXT}::PROGRAM_PRESENCE_CHECK`);
const program = await entropy.programs.dev.get(programPointer);
print(program);
} catch (error) {
Expand Down
5 changes: 3 additions & 2 deletions src/flows/balance/cli.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { initializeEntropy } from '../../common/initializeEntropy'
import * as config from '../../config'
import { debug } from '../../common/utils'
import { getBalance } from './balance'
import { EntropyLogger } from 'src/common/logger'

export async function cliGetBalance ({ address, password, endpoint }) {
const logger = new EntropyLogger('CLI::CHECK_BALANCE', endpoint)
const storedConfig = await config.get()
const account = storedConfig.accounts.find(account => account.address === address)
if (!account) throw Error(`No account with address ${address}`)
// QUESTION: is throwing the right response?
debug('account', account)
logger.debug('account', account)

// check if data is encrypted + we have a password
if (typeof account.data === 'string' && !password) {
Expand Down
8 changes: 4 additions & 4 deletions src/flows/balance/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import { getBalance } from "./balance";

// TO-DO setup flow method to provide options to allow users to select account,
// use external address, or get balances for all accounts in config
export async function checkBalance ({ accounts, selectedAccount: selectedAccountAddress }, options) {
export async function checkBalance ({ accounts, selectedAccount: selectedAccountAddress }, options, logger: EntropyLogger) {
const FLOW_CONTEXT = 'CHECK_BALANCE'
const { endpoint } = options
const logger = new EntropyLogger('TUI:: checkBalance', endpoint)
logger.debug(`endpoint: ${endpoint}`)
logger.debug(`endpoint: ${endpoint}`, FLOW_CONTEXT)

const selectedAccount = getSelectedAccount(accounts, selectedAccountAddress)
logger.log(`selected account: ${JSON.stringify(selectedAccount)}`)
logger.log(selectedAccount, FLOW_CONTEXT)
const entropy = await initializeEntropy({ keyMaterial: selectedAccount.data, endpoint });
const accountAddress = selectedAccountAddress
const freeBalance = await getBalance(entropy, accountAddress)
Expand Down
3 changes: 1 addition & 2 deletions src/flows/entropyFaucet/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import inquirer from "inquirer"
import { print, debug, accountChoices } from "../../common/utils"
import { print, accountChoices } from "../../common/utils"
import { initializeEntropy } from "../../common/initializeEntropy"

export async function entropyFaucet ({ accounts }, options) {
Expand All @@ -14,7 +14,6 @@ export async function entropyFaucet ({ accounts }, options) {

const answers = await inquirer.prompt([accountQuestion])
const selectedAccount = answers.selectedAccount
debug('selectedAccount', selectedAccount)

const recipientAddress = selectedAccount.address
const aliceAccount = {
Expand Down
9 changes: 6 additions & 3 deletions src/flows/entropyTransfer/cli.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { initializeEntropy } from '../../common/initializeEntropy'
import * as config from '../../config'
import { debug, formatAmountAsHex } from '../../common/utils'
import { formatAmountAsHex } from '../../common/utils'
import { EntropyLogger } from 'src/common/logger'

export async function cliEntropyTransfer ({ source, password, destination, amount, endpoint }) {
const logger = new EntropyLogger('CLI::TRANSFER', endpoint)
// NOTE: password is optional, is only for source account (if that is encrypted)

const storedConfig = await config.get()
const account = storedConfig.accounts.find(account => account.address === source)
if (!account) throw Error(`No account with address ${source}`)
// QUESTION: is throwing the right response?
debug('account', account)
logger.debug('account', account)

const entropy = await initializeEntropy({ keyMaterial: account.data, password, endpoint })

Expand All @@ -24,6 +26,7 @@ export async function cliEntropyTransfer ({ source, password, destination, amoun
)

await tx.signAndSend (entropy.registrationManager.signer.pair, ({ status }) => {
debug('signAndSend status', status)
logger.debug('signAndSend status:')
logger.debug(status)
})
}
3 changes: 2 additions & 1 deletion src/flows/manage-accounts/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export async function cliListAccounts () {
return storedConfig.accounts
.map(account => ({
name: account.name,
address: account.address
address: account.address,
verifyingKeys: account?.data?.admin?.verifyingKeys
}))
}
2 changes: 0 additions & 2 deletions src/flows/manage-accounts/helpers/import-key.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { debug } from '../../../common/utils'
// import { mnemonicValidate, mnemonicToMiniSecret } from '@polkadot/util-crypto'

export const importQuestions = [
Expand All @@ -16,7 +15,6 @@ export const importQuestions = [
message: 'Enter seed:',
validate: (secret) => {
// validate: (secret, { secretType }) => {
debug('\nsecret:', secret, typeof secret)
// if (secretType === 'mnemonic') return mnemonicValidate(secret) ? true : 'not a valid mnemonic'
if (secret.includes('#debug')) return true
if (secret.length === 66 && secret.startsWith('0x')) return true
Expand Down
12 changes: 8 additions & 4 deletions src/flows/manage-accounts/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import inquirer from 'inquirer'
import { debug, print } from '../../common/utils'
import { print } from '../../common/utils'
import { newKey } from './new-key'
import { selectAccount } from './select-account'
import { listAccounts } from './list'
import { EntropyTuiOptions } from 'src/types'
import { EntropyLogger } from 'src/common/logger'

const actions = {
'Create/Import Account': newKey,
Expand All @@ -27,9 +29,11 @@ const questions = [{
choices,
}]

export async function manageAccounts (config) {
export async function manageAccounts (config, _options: EntropyTuiOptions, logger: EntropyLogger) {
const FLOW_CONTEXT = 'MANAGE_ACCOUNTS'
const { choice } = await inquirer.prompt(questions)
const responses = await actions[choice](config) || {}
debug('returned config update:', { accounts: responses.accounts ? responses.accounts : config.accounts, selectedAccount: responses.selectedAccount || config.selectedAccount })
const responses = await actions[choice](config, logger) || {}
logger.debug('returned config update', FLOW_CONTEXT)
logger.debug({ accounts: responses.accounts ? responses.accounts : config.accounts, selectedAccount: responses.selectedAccount || config.selectedAccount }, FLOW_CONTEXT)
return { accounts: responses.accounts ? responses.accounts : config.accounts, selectedAccount: responses.selectedAccount || config.selectedAccount }
}
9 changes: 6 additions & 3 deletions src/flows/manage-accounts/new-key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import { randomAsHex } from '@polkadot/util-crypto'
import Keyring from '@entropyxyz/sdk/keys'
import { importQuestions } from './helpers/import-key'
// import * as passwordFlow from '../password'
import { debug, print } from '../../common/utils'
import { print } from '../../common/utils'
import { EntropyLogger } from 'src/common/logger'

export async function newKey ({ accounts }) {
export async function newKey ({ accounts }, logger: EntropyLogger) {
const FLOW_CONTEXT = 'MANAGE_ACCOUNTS::NEW_KEY'
accounts = Array.isArray(accounts) ? accounts : []

const questions = [
Expand Down Expand Up @@ -66,7 +68,8 @@ export async function newKey ({ accounts }) {
const fullAccount = keyring.getAccount()
// TO-DO: sdk should create account on constructor
const { admin } = keyring.getAccount()
debug('fullAccount:', fullAccount)
logger.debug('fullAccount:', FLOW_CONTEXT)
logger.debug(fullAccount, FLOW_CONTEXT)

const data = fullAccount
delete admin.pair
Expand Down
Loading

0 comments on commit c0f1889

Please sign in to comment.