mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
fix: sql escape issues identified by CodeQL (#11338)
* fix: sql escape issues identified by CodeQL * fix: random generation in sample code
This commit is contained in:
parent
c3bebdcb4e
commit
863caf1471
@ -1,24 +1,26 @@
|
||||
import crypto from "node:crypto"
|
||||
|
||||
import { AfterInsert } from "../../../src/decorator/listeners/AfterInsert"
|
||||
import { AfterLoad } from "../../../src/decorator/listeners/AfterLoad"
|
||||
import { AfterRecover } from "../../../src/decorator/listeners/AfterRecover"
|
||||
import { AfterRemove } from "../../../src/decorator/listeners/AfterRemove"
|
||||
import { AfterSoftRemove } from "../../../src/decorator/listeners/AfterSoftRemove"
|
||||
import { AfterUpdate } from "../../../src/decorator/listeners/AfterUpdate"
|
||||
import { BeforeInsert } from "../../../src/decorator/listeners/BeforeInsert"
|
||||
import { BeforeRecover } from "../../../src/decorator/listeners/BeforeRecover"
|
||||
import { BeforeRemove } from "../../../src/decorator/listeners/BeforeRemove"
|
||||
import { BeforeSoftRemove } from "../../../src/decorator/listeners/BeforeSoftRemove"
|
||||
import { BeforeUpdate } from "../../../src/decorator/listeners/BeforeUpdate"
|
||||
import { JoinTable } from "../../../src/decorator/relations/JoinTable"
|
||||
import { ManyToOne } from "../../../src/decorator/relations/ManyToOne"
|
||||
import {
|
||||
Column,
|
||||
Entity,
|
||||
ManyToMany,
|
||||
PrimaryGeneratedColumn,
|
||||
} from "../../../src/index"
|
||||
import { PostCategory } from "./PostCategory"
|
||||
import { PostAuthor } from "./PostAuthor"
|
||||
import { ManyToOne } from "../../../src/decorator/relations/ManyToOne"
|
||||
import { AfterLoad } from "../../../src/decorator/listeners/AfterLoad"
|
||||
import { AfterInsert } from "../../../src/decorator/listeners/AfterInsert"
|
||||
import { BeforeInsert } from "../../../src/decorator/listeners/BeforeInsert"
|
||||
import { BeforeUpdate } from "../../../src/decorator/listeners/BeforeUpdate"
|
||||
import { AfterUpdate } from "../../../src/decorator/listeners/AfterUpdate"
|
||||
import { BeforeRemove } from "../../../src/decorator/listeners/BeforeRemove"
|
||||
import { AfterRemove } from "../../../src/decorator/listeners/AfterRemove"
|
||||
import { AfterRecover } from "../../../src/decorator/listeners/AfterRecover"
|
||||
import { BeforeRecover } from "../../../src/decorator/listeners/BeforeRecover"
|
||||
import { AfterSoftRemove } from "../../../src/decorator/listeners/AfterSoftRemove"
|
||||
import { BeforeSoftRemove } from "../../../src/decorator/listeners/BeforeSoftRemove"
|
||||
import { JoinTable } from "../../../src/decorator/relations/JoinTable"
|
||||
import { PostCategory } from "./PostCategory"
|
||||
|
||||
@Entity("sample9_post")
|
||||
export class Post {
|
||||
@ -31,12 +33,12 @@ export class Post {
|
||||
@Column()
|
||||
text: string
|
||||
|
||||
@ManyToOne((type) => PostAuthor, (post) => post.posts, {
|
||||
@ManyToOne(() => PostAuthor, (post) => post.posts, {
|
||||
cascade: true,
|
||||
})
|
||||
author: PostAuthor
|
||||
|
||||
@ManyToMany((type) => PostCategory, (category) => category.posts, {
|
||||
@ManyToMany(() => PostCategory, (category) => category.posts, {
|
||||
cascade: true,
|
||||
})
|
||||
@JoinTable()
|
||||
@ -49,7 +51,7 @@ export class Post {
|
||||
console.log(
|
||||
`event: Post "${this.title}" entity has been loaded and callback executed`,
|
||||
)
|
||||
this.uid = Math.ceil(Math.random() * 1000)
|
||||
this.uid = crypto.randomInt(0, 1000)
|
||||
}
|
||||
|
||||
@BeforeInsert()
|
||||
|
||||
@ -111,7 +111,7 @@ export class MigrationGenerateCommand implements yargs.CommandModule {
|
||||
sqlInMemory.upQueries.forEach((upQuery) => {
|
||||
upSqls.push(
|
||||
" await queryRunner.query(`" +
|
||||
upQuery.query.replace(new RegExp("`", "g"), "\\`") +
|
||||
upQuery.query.replaceAll("`", "\\`") +
|
||||
"`" +
|
||||
MigrationGenerateCommand.queryParams(
|
||||
upQuery.parameters,
|
||||
@ -122,10 +122,7 @@ export class MigrationGenerateCommand implements yargs.CommandModule {
|
||||
sqlInMemory.downQueries.forEach((downQuery) => {
|
||||
downSqls.push(
|
||||
" await queryRunner.query(`" +
|
||||
downQuery.query.replace(
|
||||
new RegExp("`", "g"),
|
||||
"\\`",
|
||||
) +
|
||||
downQuery.query.replaceAll("`", "\\`") +
|
||||
"`" +
|
||||
MigrationGenerateCommand.queryParams(
|
||||
downQuery.parameters,
|
||||
|
||||
@ -461,16 +461,14 @@ export class CockroachDriver implements Driver {
|
||||
|
||||
// manually convert enum array to array of values (pg does not support, see https://github.com/brianc/node-pg-types/issues/56)
|
||||
value = (value as string)
|
||||
.substr(1, (value as string).length - 2)
|
||||
.slice(1, -1)
|
||||
.split(",")
|
||||
.map((val) => {
|
||||
// replace double quotes from the beginning and from the end
|
||||
if (val.startsWith(`"`) && val.endsWith(`"`))
|
||||
val = val.slice(1, -1)
|
||||
// replace double escaped backslash to single escaped e.g. \\\\ -> \\
|
||||
val = val.replace(/(\\\\)/g, "\\")
|
||||
// replace escaped double quotes to non-escaped e.g. \"asd\" -> "asd"
|
||||
return val.replace(/(\\")/g, '"')
|
||||
// replace escaped backslash and double quotes
|
||||
return val.replace(/\\(\\|")/g, "$1")
|
||||
})
|
||||
|
||||
// convert to number if that exists in possible enum options
|
||||
|
||||
@ -3863,7 +3863,7 @@ export class CockroachQueryRunner
|
||||
): Query {
|
||||
if (!enumName) enumName = this.buildEnumName(table, column)
|
||||
const enumValues = column
|
||||
.enum!.map((value) => `'${value.replace("'", "''")}'`)
|
||||
.enum!.map((value) => `'${value.replaceAll("'", "''")}'`)
|
||||
.join(", ")
|
||||
return new Query(`CREATE TYPE ${enumName} AS ENUM(${enumValues})`)
|
||||
}
|
||||
|
||||
@ -774,16 +774,14 @@ export class PostgresDriver implements Driver {
|
||||
|
||||
// manually convert enum array to array of values (pg does not support, see https://github.com/brianc/node-pg-types/issues/56)
|
||||
value = (value as string)
|
||||
.substr(1, (value as string).length - 2)
|
||||
.slice(1, -1)
|
||||
.split(",")
|
||||
.map((val) => {
|
||||
// replace double quotes from the beginning and from the end
|
||||
if (val.startsWith(`"`) && val.endsWith(`"`))
|
||||
val = val.slice(1, -1)
|
||||
// replace double escaped backslash to single escaped e.g. \\\\ -> \\
|
||||
val = val.replace(/(\\\\)/g, "\\")
|
||||
// replace escaped double quotes to non-escaped e.g. \"asd\" -> "asd"
|
||||
return val.replace(/(\\")/g, '"')
|
||||
// replace escaped backslash and double quotes
|
||||
return val.replace(/\\(\\|")/g, "$1")
|
||||
})
|
||||
|
||||
// convert to number if that exists in possible enum options
|
||||
|
||||
@ -4284,7 +4284,7 @@ export class PostgresQueryRunner
|
||||
): Query {
|
||||
if (!enumName) enumName = this.buildEnumName(table, column)
|
||||
const enumValues = column
|
||||
.enum!.map((value) => `'${value.replace("'", "''")}'`)
|
||||
.enum!.map((value) => `'${value.replaceAll("'", "''")}'`)
|
||||
.join(", ")
|
||||
return new Query(`CREATE TYPE ${enumName} AS ENUM(${enumValues})`)
|
||||
}
|
||||
|
||||
@ -26,6 +26,12 @@ export enum HeterogeneousEnum {
|
||||
YES = "YES",
|
||||
}
|
||||
|
||||
export enum EscapeCharEnum {
|
||||
Backslash = "\\",
|
||||
DoubleQuote = '"',
|
||||
AllEscapeChars = `\\"`,
|
||||
}
|
||||
|
||||
export type ArrayDefinedStringEnumType = "admin" | "editor" | "ghost"
|
||||
|
||||
export type ArrayDefinedNumericEnumType = 11 | 12 | 13
|
||||
@ -51,6 +57,14 @@ export class EnumArrayEntity {
|
||||
})
|
||||
stringEnums: StringEnum[]
|
||||
|
||||
@Column({
|
||||
type: "enum",
|
||||
enum: EscapeCharEnum,
|
||||
array: true,
|
||||
default: [],
|
||||
})
|
||||
escapeCharEnums: EscapeCharEnum[]
|
||||
|
||||
@Column({
|
||||
type: "enum",
|
||||
enum: StringNumericEnum,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import "reflect-metadata"
|
||||
|
||||
import { DataSource } from "../../../../src"
|
||||
import {
|
||||
closeTestingConnections,
|
||||
@ -7,9 +8,10 @@ import {
|
||||
} from "../../../utils/test-utils"
|
||||
import {
|
||||
EnumArrayEntity,
|
||||
EscapeCharEnum,
|
||||
HeterogeneousEnum,
|
||||
NumericEnum,
|
||||
StringEnum,
|
||||
HeterogeneousEnum,
|
||||
StringNumericEnum,
|
||||
} from "./entity/EnumArrayEntity"
|
||||
|
||||
@ -72,6 +74,11 @@ describe("database schema > enum arrays", () => {
|
||||
NumericEnum.EDITOR,
|
||||
]
|
||||
enumEntity.stringEnums = [StringEnum.MODERATOR]
|
||||
enumEntity.escapeCharEnums = [
|
||||
EscapeCharEnum.Backslash,
|
||||
EscapeCharEnum.DoubleQuote,
|
||||
EscapeCharEnum.AllEscapeChars,
|
||||
]
|
||||
enumEntity.stringNumericEnums = [StringNumericEnum.FOUR]
|
||||
enumEntity.heterogeneousEnums = [HeterogeneousEnum.NO]
|
||||
enumEntity.arrayDefinedStringEnums = ["editor"]
|
||||
@ -89,6 +96,11 @@ describe("database schema > enum arrays", () => {
|
||||
loadedEnumEntity!.stringEnums.should.be.eql([
|
||||
StringEnum.MODERATOR,
|
||||
])
|
||||
loadedEnumEntity!.escapeCharEnums.should.be.eql([
|
||||
EscapeCharEnum.Backslash,
|
||||
EscapeCharEnum.DoubleQuote,
|
||||
EscapeCharEnum.AllEscapeChars,
|
||||
])
|
||||
loadedEnumEntity!.stringNumericEnums.should.be.eql([
|
||||
StringNumericEnum.FOUR,
|
||||
])
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user