Skip to content

Commit

Permalink
feat(cli): Quit on decryption failure (keyshade-xyz#381)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dark-Kernel authored and rajdip-b committed Sep 5, 2024
1 parent 4cf3838 commit 1349d15
Show file tree
Hide file tree
Showing 9 changed files with 1,323 additions and 1,272 deletions.
2 changes: 1 addition & 1 deletion .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

pnpm lint && pnpm format && pnpm test:api && pnpm test:api-client
pnpm lint && pnpm format && pnpm test:api && pnpm test:api-client
2 changes: 1 addition & 1 deletion apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"db:format": "pnpm dlx prisma format --schema=src/prisma/schema.prisma",
"db:reset": "pnpx dotenv-cli -e ../../.env -- pnpm dlx prisma migrate reset --force --schema=src/prisma/schema.prisma",
"sourcemaps": "sentry-cli sourcemaps inject ./dist && sentry-cli sourcemaps upload ./dist || echo 'Failed to upload source maps to Sentry'",
"e2e:prepare": "cd ../../ && docker compose down && docker compose -f docker-compose-test.yml up -d && cd apps/api && pnpm db:generate-types && cross-env NODE_ENV='e2e' DATABASE_URL='postgresql://prisma:prisma@localhost:5432/tests' pnpm run db:deploy-migrations",
"e2e:prepare": "cd ../../ && docker compose down && docker compose -f docker-compose-test.yml up -d && sleep 3 && cd apps/api && pnpm db:generate-types && cross-env NODE_ENV='e2e' DATABASE_URL='postgresql://prisma:prisma@localhost:5432/tests' pnpm run db:deploy-migrations",
"e2e": "pnpm run e2e:prepare && cross-env NODE_ENV='e2e' DATABASE_URL='postgresql://prisma:prisma@localhost:5432/tests' jest --runInBand --config=jest.e2e-config.ts --coverage --coverageDirectory=../../coverage-e2e/api --coverageReporters=json && pnpm run e2e:teardown",
"e2e:teardown": "cd ../../ && docker compose -f docker-compose-test.yml down",
"unit": "pnpm db:generate-types && jest --config=jest.config.ts"
Expand Down
1 change: 1 addition & 0 deletions apps/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"cli-table": "^0.3.11",
"colors": "^1.4.0",
"commander": "^12.1.0",
"eccrypto": "^1.1.6",
"figlet": "^1.7.0",
"fs": "0.0.1-security",
"nodemon": "^3.1.4",
Expand Down
11 changes: 9 additions & 2 deletions apps/cli/src/commands/init.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ export default class InitCommand extends BaseCommand {
long: '--overwrite',
description: 'Overwrite existing configuration',
defaultValue: false
},
{
short: '-q',
long: '--quit-on-decryption-failure',
description: 'Quit on decryption failure',
defaultValue: false
}
]
}
Expand All @@ -52,7 +58,7 @@ export default class InitCommand extends BaseCommand {

async action({ options }: CommandActionData): Promise<void> {
let { workspace, project, environment, privateKey } = options
const { overwrite } = options
const { overwrite, quitOnDecryptionFailure } = options

intro('Configure the project for live-updates')

Expand Down Expand Up @@ -85,7 +91,8 @@ export default class InitCommand extends BaseCommand {
await writeProjectRootConfig({
workspace,
project,
environment
environment,
quitOnDecryptionFailure
})

await writePrivateKeyConfig({
Expand Down
37 changes: 29 additions & 8 deletions apps/cli/src/commands/run.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ import {
import { Logger } from '@/util/logger'
import type {
ClientRegisteredResponse,
Configuration
Configuration,
RunData
} from '@/types/command/run.types'

import { decrypt } from '@/util/decrypt'

export default class RunCommand extends BaseCommand {
private processEnvironmentalVariables = {}

Expand All @@ -29,7 +32,7 @@ export default class RunCommand extends BaseCommand {
getName(): string {
return 'run'
}

getDescription(): string {
return 'Run a command'
}
Expand All @@ -52,9 +55,9 @@ export default class RunCommand extends BaseCommand {
}

private async fetchConfigurations(): Promise<
ProjectRootConfig & { privateKey: string }
RunData
> {
const { environment, project, workspace } = await fetchProjectRootConfig()
const { environment, project, workspace, quitOnDecryptionFailure } = await fetchProjectRootConfig()
const privateKeyConfig = await fetchPrivateKeyConfig()
const privateKey =
privateKeyConfig[`${workspace}_${project}_${environment}`]
Expand All @@ -67,7 +70,8 @@ export default class RunCommand extends BaseCommand {
environment,
project,
workspace,
privateKey
privateKey,
quitOnDecryptionFailure
}
}

Expand All @@ -78,11 +82,13 @@ export default class RunCommand extends BaseCommand {
return 'ws'
}

private async connectToSocket(data: ProjectRootConfig) {
private async connectToSocket(data: RunData) {
Logger.info('Connecting to socket...')
const host = this.baseUrl.substring(this.baseUrl.lastIndexOf('/') + 1)
const websocketUrl = `${this.getWebsocketType(this.baseUrl)}://${host}/change-notifier`

const privateKey = data.privateKey
const quitOnDecryptionFailure = data.quitOnDecryptionFailure

const ioClient = io(websocketUrl, {
autoConnect: false,
extraHeaders: {
Expand All @@ -102,8 +108,23 @@ export default class RunCommand extends BaseCommand {

ioClient.on('configuration-updated', async (data: Configuration) => {
Logger.info(
`Configuration change received from API (name: ${data.name}, value: ${data.value})`
`Configuration change received from API (name: ${data.name})`
)

if (!data.isPlaintext) {
try {
data.value = await decrypt(privateKey, data.value)
} catch (error) {
if (quitOnDecryptionFailure) {
Logger.error(`Decryption failed for ${data.name}. Stopping the process.`);
process.exit(1)
} else {
Logger.warn(`Decryption failed for ${data.name}. Skipping this configuration.`);
return;
}
}
}

this.processEnvironmentalVariables[data.name] = data.value
this.shouldRestart = true
})
Expand Down
6 changes: 6 additions & 0 deletions apps/cli/src/types/command/run.types.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { ProjectRootConfig } from "../index.types"

export interface Configuration {
name: string
value: string
Expand All @@ -9,3 +11,7 @@ export interface ClientRegisteredResponse {
projectId: string
environmentId: string
}

export interface RunData extends ProjectRootConfig{
privateKey: string
}
1 change: 1 addition & 0 deletions apps/cli/src/types/index.types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export interface ProjectRootConfig {
workspace: string
project: string
environment: string
quitOnDecryptionFailure: boolean
}

export interface ProfileConfig {
Expand Down
22 changes: 22 additions & 0 deletions apps/cli/src/util/decrypt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as eccrypto from 'eccrypto'

export const decrypt = async (
privateKey: string,
data: string
): Promise<string> => {
const parsed = JSON.parse(data)

const eicesData = {
iv: Buffer.from(parsed.iv),
ephemPublicKey: Buffer.from(parsed.ephemPublicKey),
ciphertext: Buffer.from(parsed.ciphertext),
mac: Buffer.from(parsed.mac)
}

const decrypted = await eccrypto.decrypt(
Buffer.from(privateKey, 'hex'),
eicesData
)

return decrypted.toString()
}
Loading

0 comments on commit 1349d15

Please sign in to comment.