Disallowing prohibited columns consistently, no matter the case, and backend validation for this as well.

This commit is contained in:
mike12345567 2024-06-20 18:48:22 +01:00
parent 96be6e85e3
commit def3b0260e
6 changed files with 65 additions and 20 deletions

View File

@ -1,14 +1,5 @@
export const CONSTANT_INTERNAL_ROW_COLS = [ export {
"_id", CONSTANT_INTERNAL_ROW_COLS,
"_rev", CONSTANT_EXTERNAL_ROW_COLS,
"type", isInternalColumnName,
"createdAt", } from "@budibase/shared-core"
"updatedAt",
"tableId",
] as const
export const CONSTANT_EXTERNAL_ROW_COLS = ["_id", "_rev", "tableId"] as const
export function isInternalColumnName(name: string): boolean {
return (CONSTANT_INTERNAL_ROW_COLS as readonly string[]).includes(name)
}

View File

@ -17,6 +17,8 @@
SWITCHABLE_TYPES, SWITCHABLE_TYPES,
ValidColumnNameRegex, ValidColumnNameRegex,
helpers, helpers,
CONSTANT_INTERNAL_ROW_COLS,
CONSTANT_EXTERNAL_ROW_COLS,
} from "@budibase/shared-core" } from "@budibase/shared-core"
import { createEventDispatcher, getContext, onMount } from "svelte" import { createEventDispatcher, getContext, onMount } from "svelte"
import { cloneDeep } from "lodash/fp" import { cloneDeep } from "lodash/fp"
@ -487,20 +489,27 @@
}) })
} }
const newError = {} const newError = {}
const prohibited = externalTable
? CONSTANT_EXTERNAL_ROW_COLS
: CONSTANT_INTERNAL_ROW_COLS
if (!externalTable && fieldInfo.name?.startsWith("_")) { if (!externalTable && fieldInfo.name?.startsWith("_")) {
newError.name = `Column name cannot start with an underscore.` newError.name = `Column name cannot start with an underscore.`
} else if (fieldInfo.name && !fieldInfo.name.match(ValidColumnNameRegex)) { } else if (fieldInfo.name && !fieldInfo.name.match(ValidColumnNameRegex)) {
newError.name = `Illegal character; must be alpha-numeric.` newError.name = `Illegal character; must be alpha-numeric.`
} else if (PROHIBITED_COLUMN_NAMES.some(name => fieldInfo.name === name)) { } else if (
newError.name = `${PROHIBITED_COLUMN_NAMES.join( prohibited.some(
name => fieldInfo?.name?.toLowerCase() === name.toLowerCase()
)
) {
newError.name = `${prohibited.join(
", " ", "
)} are not allowed as column names` )} are not allowed as column names - case insensitive.`
} else if (inUse($tables.selected, fieldInfo.name, originalName)) { } else if (inUse($tables.selected, fieldInfo.name, originalName)) {
newError.name = `Column name already in use.` newError.name = `Column name already in use.`
} }
if (fieldInfo.type === FieldType.AUTO && !fieldInfo.subtype) { if (fieldInfo.type === FieldType.AUTO && !fieldInfo.subtype) {
newError.subtype = `Auto Column requires a type` newError.subtype = `Auto Column requires a type.`
} }
if (fieldInfo.fieldName && fieldInfo.tableId) { if (fieldInfo.fieldName && fieldInfo.tableId) {

View File

@ -16,7 +16,8 @@ import { EventType, updateLinks } from "../../../../db/linkedRows"
import { cloneDeep } from "lodash/fp" import { cloneDeep } from "lodash/fp"
import isEqual from "lodash/isEqual" import isEqual from "lodash/isEqual"
import { runStaticFormulaChecks } from "../../../../api/controllers/table/bulkFormula" import { runStaticFormulaChecks } from "../../../../api/controllers/table/bulkFormula"
import { context } from "@budibase/backend-core" import { context, db as dbCore } from "@budibase/backend-core"
import { findDuplicateInternalColumns } from "@budibase/shared-core"
import { getTable } from "../getters" import { getTable } from "../getters"
import { checkAutoColumns } from "./utils" import { checkAutoColumns } from "./utils"
import * as viewsSdk from "../../views" import * as viewsSdk from "../../views"
@ -44,6 +45,15 @@ export async function save(
if (hasTypeChanged(table, oldTable)) { if (hasTypeChanged(table, oldTable)) {
throw new Error("A column type has changed.") throw new Error("A column type has changed.")
} }
// check for case sensitivity - we don't want to allow duplicated columns
const duplicateColumn = findDuplicateInternalColumns(table)
if (duplicateColumn) {
throw new Error(
`Column "${duplicateColumn}" is duplicated - make sure there are no duplicate columns names, this is case insensitive.`
)
}
// check that subtypes have been maintained // check that subtypes have been maintained
table = checkAutoColumns(table, oldTable) table = checkAutoColumns(table, oldTable)

View File

@ -1,5 +1,6 @@
export * from "./api" export * from "./api"
export * from "./fields" export * from "./fields"
export * from "./rows"
export const OperatorOptions = { export const OperatorOptions = {
Equals: { Equals: {

View File

@ -0,0 +1,14 @@
export const CONSTANT_INTERNAL_ROW_COLS = [
"_id",
"_rev",
"type",
"createdAt",
"updatedAt",
"tableId",
] as const
export const CONSTANT_EXTERNAL_ROW_COLS = ["_id", "_rev", "tableId"] as const
export function isInternalColumnName(name: string): boolean {
return (CONSTANT_INTERNAL_ROW_COLS as readonly string[]).includes(name)
}

View File

@ -1,4 +1,5 @@
import { FieldType } from "@budibase/types" import { FieldType, Table } from "@budibase/types"
import { CONSTANT_INTERNAL_ROW_COLS } from "./constants"
const allowDisplayColumnByType: Record<FieldType, boolean> = { const allowDisplayColumnByType: Record<FieldType, boolean> = {
[FieldType.STRING]: true, [FieldType.STRING]: true,
@ -51,3 +52,22 @@ export function canBeDisplayColumn(type: FieldType): boolean {
export function canBeSortColumn(type: FieldType): boolean { export function canBeSortColumn(type: FieldType): boolean {
return !!allowSortColumnByType[type] return !!allowSortColumnByType[type]
} }
export function findDuplicateInternalColumns(table: Table): string | undefined {
// get the column names
const columnNames = Object.keys(table.schema)
.concat(CONSTANT_INTERNAL_ROW_COLS)
.map(colName => colName.toLowerCase())
// there are duplicates
const set = new Set(columnNames)
let foundDuplicate: string | undefined
if (set.size !== columnNames.length) {
for (let key of set.keys()) {
const count = columnNames.filter(name => name === key).length
if (count > 1) {
foundDuplicate = key
}
}
}
return foundDuplicate
}