@nozbe/watermelondb
Version:
Build powerful React Native and React web apps that scale from hundreds to tens of thousands of records and remain fast
71 lines (64 loc) • 2.45 kB
JavaScript
// @flow
import invariant from '../../common/invariant'
import type { TableName, ColumnName } from '../../../Schema'
// Asserts that `name` (table or column name) should be safe for inclusion in SQL queries
// and Loki queries (JS objects)
//
// IMPORTANT: This should NEVER be used as the only line of defense! These checks may be incomplete.
// Any table or column name passed anywhere near the database should be hardcoded or whitelisted.
// This is a "defense in depth" type of check - checking for common mistakes in case library user
// is not following safe coding practices or the primary defense fails.
//
// This will throw an error on:
// - JavaScript Object prototype properties
// - Magic Loki and SQLite column names
// - names starting with __
// - names that are not essentially alphanumeric
//
// Note that for SQL, you always MUST wrap table/column names with `'name'`, otherwise query may fail
// for some keywords
//
// Note that this doesn't throw for Watermelon builtins (id, _changed, _status...)
const safeNameCharacters = /^[a-zA-Z_]\w*$/
const knownSafeNames: Set<string> = new Set()
export default function checkName<T: string | TableName<any> | ColumnName>(name: T): T {
if (knownSafeNames.has((name: string))) {
return name
}
invariant(typeof name === 'string', `Unsafe name '${name}' not allowed (not a string)`)
invariant(
![
'__proto__',
'constructor',
'prototype',
'hasOwnProperty',
'isPrototypeOf',
'toString',
'toLocaleString',
'valueOf',
].includes(name),
`Unsafe name '${name}' not allowed (Object prototype property)`,
)
invariant(
name.toLowerCase() !== '$loki',
`Unsafe name '${name}' not allowed (reserved for LokiJS compatibility)`,
)
invariant(
!['rowid', 'oid', '_rowid_', 'sqlite_master'].includes(name.toLowerCase()),
`Unsafe name '${name}' not allowed (reserved for SQLite compatibility)`,
)
invariant(
!name.toLowerCase().startsWith('sqlite_stat'),
`Unsafe name '${name}' not allowed (reserved for SQLite compatibility)`,
)
invariant(
!name.startsWith('__'),
`Unsafe name '${name}' not allowed (names starting with '__' are reserved for internal purposes)`,
)
invariant(
safeNameCharacters.test(name),
`Unsafe name '${name}' not allowed (names must contain only safe characters ${safeNameCharacters.toString()})`,
)
knownSafeNames.add(name)
return name
}