Skip to content

Commit

Permalink
Guard blitz generate input against unwanted characters (#4024)
Browse files Browse the repository at this point in the history
Co-authored-by: Dillon Raphael <[email protected]>
Co-authored-by: Siddharth Suresh <[email protected]>
Closes #4021
  • Loading branch information
tordans authored Jan 26, 2023
1 parent a78bd33 commit cb63a0e
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/big-boats-lay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@blitzjs/generator": patch
---

Guard `blitz g` input via an allow-list of characters; throw if unwanted characters are found. Prevents to break the blitz command by accident (https://github.com/blitz-js/blitz/issues/4021).
13 changes: 9 additions & 4 deletions packages/generator/src/generators/model-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import * as ast from "@mrleebo/prisma-ast"
import {spawn} from "cross-spawn"
import which from "npm-which"
import path from "path"
import {log} from "../utils/log"
import {Generator, GeneratorOptions, SourceRootType} from "../generator"
import {Field} from "../prisma/field"
import {Model} from "../prisma/model"
import {checkInputsOrRaise} from "../utils/checkInputOrRaise"
import {getTemplateRoot} from "../utils/get-template-root"
import {log} from "../utils/log"

export interface ModelGeneratorOptions extends GeneratorOptions {
modelName: string
Expand Down Expand Up @@ -59,9 +60,13 @@ export class ModelGenerator extends Generator<ModelGeneratorOptions> {
const {modelName, extraArgs, dryRun} = this.options
let updatedOrCreated = "created"

let fields = (
extraArgs.length === 1 && extraArgs[0]?.includes(" ") ? extraArgs[0]?.split(" ") : extraArgs
).flatMap((input) => Field.parse(input, schema))
const splitInputCommans = (input: typeof extraArgs) => {
const inputs = input.length === 1 && input[0]?.includes(" ") ? input[0]?.split(" ") : input
checkInputsOrRaise(inputs)
return inputs
}

let fields = splitInputCommans(extraArgs).flatMap((input) => Field.parse(input, schema))

const modelDefinition = new Model(modelName, fields)

Expand Down
3 changes: 3 additions & 0 deletions packages/generator/src/prisma/field.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as ast from "@mrleebo/prisma-ast"
import {checkInputsOrRaise} from "../utils/checkInputOrRaise"
import {capitalize, singlePascal, uncapitalize} from "../utils/inflector"

export enum FieldType {
Expand Down Expand Up @@ -55,6 +56,8 @@ export class Field {

// 'name:type?[]:attribute' => Field
static parse(input: string, schema?: ast.Schema): Field[] {
checkInputsOrRaise(input)

const [_fieldName, _fieldType = "String", _attribute] = input.split(":")
let attribute = _attribute as string
let fieldName = uncapitalize(_fieldName as string)
Expand Down
28 changes: 28 additions & 0 deletions packages/generator/src/utils/checkInputOrRaise.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import chalk from "chalk"
import {log} from "./log"

export const checkInputsOrRaise = (inputs: string[] | string) => {
if (typeof inputs === "string") {
checkInputOrRaise(inputs)
} else {
inputs.forEach((input) => checkInputOrRaise(input))
}
}

const checkInputOrRaise = (input: string) => {
const regex = /^[a-zA-Z0-9-_:=\?[\]\s]+$/
if (!regex.test(input)) {
const firstInvalidCharacter = input.match(/[^a-zA-Z0-9-_:=\?[\]\s]/)
if (firstInvalidCharacter) {
log.branded(
"Blitz Generator Parser Error: " +
chalk.red(
`Input contains invalid character: "${firstInvalidCharacter[0]}" in ${firstInvalidCharacter.input} at position ${firstInvalidCharacter.index}`,
),
)
}
throw new Error(
"Input should only contain alphanumeric characters, spaces, and the following characters: - _ : = ? [ ]",
)
}
}
26 changes: 26 additions & 0 deletions packages/generator/test/generators/utils/checkInputOrRaise.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {checkInputsOrRaise} from "../../../../generator/src/utils/checkInputOrRaise"
import {describe, expect, it} from "vitest"

describe("checkInputsOrRaise()", () => {
describe("with input string", () => {
it("does not raise when input string OK", () => {
expect(() => checkInputsOrRaise("Foo:foo=bar[]?:-_ ")).not.toThrow()
})
it("raisees when input has unwanted characters", () => {
expect(() => checkInputsOrRaise("()!")).toThrowError(
"Input should only contain alphanumeric characters, spaces, and the following characters: - _ : = ? [ ]",
)
})
})

describe("with input string[]", () => {
it("does not raise when input strings OK", () => {
expect(() => checkInputsOrRaise(["ok:ok=ok", "also ok:ok"])).not.toThrow()
})
it("raisees when one input has unwanted characters", () => {
expect(() => checkInputsOrRaise(["ok:ok=ok", "not ok!"])).toThrowError(
"Input should only contain alphanumeric characters, spaces, and the following characters: - _ : = ? [ ]",
)
})
})
})
8 changes: 8 additions & 0 deletions packages/generator/test/prisma/field.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ describe("Field", () => {
expect(field?.default).toMatchObject({name: "uuid"})
})

it("disallow brackes `()` as default attribute", () => {
expect(() => Field.parse("id:string:default=now()")).toThrow()
})

it("disallow not allowed characters like `!`", () => {
expect(() => Field.parse("id:string!")).toThrow()
})

it("handles uuid convenience syntax", () => {
const [field] = Field.parse("someSpecialToken:uuid")
expect(field?.type).toBe("String")
Expand Down

0 comments on commit cb63a0e

Please sign in to comment.