Skip to content

Commit

Permalink
chore(cli): refactor checkpoint call and sanitize options (#12908)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jolg42 committed Apr 22, 2022
1 parent fed2a0f commit 2bbe4eb
Show file tree
Hide file tree
Showing 4 changed files with 334 additions and 71 deletions.
118 changes: 118 additions & 0 deletions packages/cli/src/__tests__/checkpoint.test.ts
@@ -0,0 +1,118 @@
import { jestContext } from '@prisma/sdk'

import { redactCommandArray, SENSITIVE_CLI_OPTIONS, tryToReadDataFromSchema } from '../utils/checkpoint'

const ctx = jestContext.new().assemble()

it('should redact --option [value]', () => {
for (const option of SENSITIVE_CLI_OPTIONS) {
expect(redactCommandArray(['cmd', option, 'secret'])).toEqual(['cmd', option, '[redacted]'])
}
})

it('should redact --option=[value]', () => {
for (const option of SENSITIVE_CLI_OPTIONS) {
expect(redactCommandArray(['cmd', `${option}=secret`])).toEqual(['cmd', `${option}=[redacted]`])
}
})

it('should redact a PostgreSQL connection string', () => {
expect(redactCommandArray(['init', '--url', '"postgresql://janedoe:mypassword@localhost:5432/mydb?schema=sample"']))
.toMatchInlineSnapshot(`
Array [
init,
--url,
[redacted],
]
`)
})

it('should redact a MySQL connection string', () => {
expect(
redactCommandArray([
'init',
`--url "mysql://janedoe:mypassword@localhost:3306/mydb?connection_limit=5&socket_timeout"`,
]),
).toMatchInlineSnapshot(`
Array [
init,
--url=[redacted],
]
`)
})

it('should redact a MongoDB connection string', () => {
expect(
redactCommandArray([
'init',
`--url "mongodb+srv://root:<password>@cluster0.ab1cd.mongodb.net/myDatabase?retryWrites=true&w=majority"`,
]),
).toMatchInlineSnapshot(`
Array [
init,
--url=[redacted],
]
`)
})

it('should redact a SQLite connection string', () => {
expect(redactCommandArray(['init', '--url', '"file:./dev.db"'])).toMatchInlineSnapshot(`
Array [
init,
--url,
[redacted],
]
`)
})

it('should redact a SQL Server connection string', () => {
expect(
redactCommandArray([
'init',
`--url="sqlserver://localhost:1433;initial catalog=sample;user=sa;password=mypassword;"`,
]),
).toMatchInlineSnapshot(`
Array [
init,
--url=[redacted],
]
`)
})

it('should redact a path with for example --schema', () => {
expect(redactCommandArray(['cmd', '--schema', '../../../../directory/my_schema.prisma'])).toMatchInlineSnapshot(`
Array [
cmd,
--schema,
[redacted],
]
`)
})

it('should redact a name with for example --name', () => {
expect(redactCommandArray(['cmd', '--name', '1234_my_name'])).toMatchInlineSnapshot(`
Array [
cmd,
--name,
[redacted],
]
`)
})

it('should read data from Prisma schema', async () => {
ctx.fixture('checkpoint-read-schema')

await expect(tryToReadDataFromSchema('./schema.prisma')).resolves.toMatchInlineSnapshot(`
Object {
schemaGeneratorsProviders: Array [
prisma-client-js,
something,
],
schemaPreviewFeatures: Array [
extendedIndexes,
cockroachdb,
],
schemaProvider: sqlite,
}
`)
})
@@ -0,0 +1,18 @@
datasource my_db {
provider = "sqlite"
url = env("SQLITE_URL")
}

generator client {
provider = "prisma-client-js"
previewFeatures = ["extendedIndexes", "cockroachdb"]
}

generator something {
provider = "something"
}

model Blog {
id Int @id
viewCount Int
}
92 changes: 21 additions & 71 deletions packages/cli/src/bin.ts
Expand Up @@ -17,20 +17,8 @@ import {
MigrateResolve,
MigrateStatus,
} from '@prisma/migrate'
import {
arg,
getCLIPathHash,
getConfig,
getProjectHash,
getSchema,
handlePanic,
HelpError,
isCurrentBinInstalledGlobally,
isError,
parseEnvValue,
} from '@prisma/sdk'
import { arg, handlePanic, HelpError, isCurrentBinInstalledGlobally, isError } from '@prisma/sdk'
import chalk from 'chalk'
import * as checkpoint from 'checkpoint-client'
import path from 'path'

import { CLI } from './CLI'
Expand All @@ -49,6 +37,7 @@ import { Init } from './Init'
*/
import { Studio } from './Studio'
import { Telemetry } from './Telemetry'
import { redactCommandArray, runCheckpointClientCheck } from './utils/checkpoint'
import { detectPrisma1 } from './utils/detectPrisma1'
import { printUpdateMessage } from './utils/printUpdateMessage'
import { Validate } from './Validate'
Expand Down Expand Up @@ -91,6 +80,9 @@ const args = arg(
true,
)

// Redact the command options and make it a string
const redactedCommandAsString = redactCommandArray([...commandArray]).join(' ')

// because chalk ...
if (process.env.NO_COLOR) {
chalk.level = 0
Expand Down Expand Up @@ -164,63 +156,21 @@ async function main(): Promise<number> {
}
console.log(result)

try {
// SHA256 identifier for the project based on the Prisma schema path
const projectPathHash = await getProjectHash()
// SHA256 of the cli path
const cliPathHash = getCLIPathHash()

let schemaProvider: string | undefined
let schemaPreviewFeatures: string[] | undefined
let schemaGeneratorsProviders: string[] | undefined
try {
const schema = await getSchema(args['--schema'])
const config = await getConfig({
datamodel: schema,
ignoreEnvVarErrors: true,
})

if (config.datasources.length > 0) {
schemaProvider = config.datasources[0].provider
}

// restrict the search to previewFeatures of `provider = 'prisma-client-js'`
// (this was not scoped to `prisma-client-js` before Prisma 3.0)
const generator = config.generators.find(
(generator) => parseEnvValue(generator.provider) === 'prisma-client-js' && generator.previewFeatures.length > 0,
)
if (generator) {
schemaPreviewFeatures = generator.previewFeatures
}

// Example 'prisma-client-js'
schemaGeneratorsProviders = config.generators.map((generator) => parseEnvValue(generator.provider))
} catch (e) {
debug('Error from cli/src/bin.ts')
debug(e)
}

// check prisma for updates
const checkResult = await checkpoint.check({
product: 'prisma',
cli_path_hash: cliPathHash,
project_hash: projectPathHash,
version: packageJson.version,
schema_providers: schemaProvider ? [schemaProvider] : undefined,
schema_preview_features: schemaPreviewFeatures,
schema_generators_providers: schemaGeneratorsProviders,
cli_path: process.argv[1],
cli_install_type: isPrismaInstalledGlobally ? 'global' : 'local',
command: commandArray.join(' '),
information: args['--telemetry-information'] || process.env.PRISMA_TELEMETRY_INFORMATION,
})
// if the result is cached and we're outdated, show this prompt
const shouldHide = process.env.PRISMA_HIDE_UPDATE_MESSAGE
if (checkResult.status === 'ok' && checkResult.data.outdated && !shouldHide) {
printUpdateMessage(checkResult)
}
} catch (e) {
debug(e)
/**
* Prepare data and run the Checkpoint Client
* See function for more info
*/
const checkResult = await runCheckpointClientCheck({
command: redactedCommandAsString,
isPrismaInstalledGlobally,
schemaPath: args['--schema'],
telemetryInformation: args['--telemetry-information'],
version: packageJson.version,
})
// if the result is cached and CLI outdated, show the `Update available` message
const shouldHide = process.env.PRISMA_HIDE_UPDATE_MESSAGE
if (checkResult && checkResult.status === 'ok' && checkResult.data.outdated && !shouldHide) {
printUpdateMessage(checkResult)
}

return 0
Expand Down Expand Up @@ -254,7 +204,7 @@ if (eval('require.main === module')) {

function handleIndividualError(error): void {
if (error.rustStack) {
handlePanic(error, packageJson.version, enginesVersion, commandArray.join(' '))
handlePanic(error, packageJson.version, enginesVersion, redactedCommandAsString)
.catch((e) => {
if (Debug.enabled('prisma')) {
console.error(chalk.redBright.bold('Error: ') + e.stack)
Expand Down

0 comments on commit 2bbe4eb

Please sign in to comment.