Add enable_secure_identifiers setting to disallow insecure identifiers

This commit is contained in:
Tuan Pham Anh 2024-09-12 08:36:55 +00:00
parent cb1b7dc43c
commit 48a9d2b660
7 changed files with 146 additions and 1 deletions

View File

@ -901,6 +901,7 @@ class IColumn;
M(Bool, restore_replace_external_engines_to_null, false, "Replace all the external table engines to Null on restore. Useful for testing purposes", 0) \
M(Bool, restore_replace_external_table_functions_to_null, false, "Replace all table functions to Null on restore. Useful for testing purposes", 0) \
M(Bool, create_if_not_exists, false, "Enable IF NOT EXISTS for CREATE statements by default", 0) \
M(Bool, enable_secure_identifiers, false, "If enabled, only secure identifiers are allowed", 0) \
\
\
/* ###################################### */ \

View File

@ -79,6 +79,7 @@ static std::initializer_list<std::pair<ClickHouseVersion, SettingsChangesHistory
{"output_format_identifier_quoting_style", "Backticks", "Backticks", "New setting."},
{"database_replicated_allow_replicated_engine_arguments", 1, 0, "Don't allow explicit arguments by default"},
{"database_replicated_allow_explicit_uuid", 0, 0, "Added a new setting to disallow explicitly specifying table UUID"},
{"enable_secure_identifiers", false, false, "New setting."},
}
},
{"24.8",

View File

@ -929,6 +929,14 @@ static std::tuple<ASTPtr, BlockIO> executeQueryImpl(
InterpreterSetQuery::applySettingsFromQuery(ast, context);
validateAnalyzerSettings(ast, context->getSettingsRef().allow_experimental_analyzer);
if (settings.enable_secure_identifiers)
{
WriteBufferFromOwnString buf;
IAST::FormatSettings enable_secure_identifiers_settings(buf, true);
enable_secure_identifiers_settings.enable_secure_identifiers = true;
ast->format(enable_secure_identifiers_settings);
}
if (auto * insert_query = ast->as<ASTInsertQuery>())
insert_query->tail = istr;

View File

@ -219,6 +219,7 @@ String IAST::getColumnNameWithoutAlias() const
void IAST::FormatSettings::writeIdentifier(const String & name) const
{
checkIdentifier(name);
switch (identifier_quoting_style)
{
case IdentifierQuotingStyle::None:
@ -260,6 +261,7 @@ void IAST::FormatSettings::writeIdentifier(const String & name) const
void IAST::FormatSettings::quoteIdentifier(const String & name) const
{
checkIdentifier(name);
switch (identifier_quoting_style)
{
case IdentifierQuotingStyle::None:
@ -285,6 +287,20 @@ void IAST::FormatSettings::quoteIdentifier(const String & name) const
}
}
void IAST::FormatSettings::checkIdentifier(const String & name) const
{
if (enable_secure_identifiers)
{
for (char c : name)
{
if (!std::isalnum(c) && c != '_')
{
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Not a secure identifier: `{}`", name);
}
}
}
}
void IAST::dumpTree(WriteBuffer & ostr, size_t indent) const
{
String indent_str(indent, '-');

View File

@ -202,6 +202,7 @@ public:
char nl_or_ws; /// Newline or whitespace.
LiteralEscapingStyle literal_escaping_style;
bool print_pretty_type_names;
bool enable_secure_identifiers;
explicit FormatSettings(
WriteBuffer & ostr_,
@ -211,7 +212,8 @@ public:
IdentifierQuotingStyle identifier_quoting_style_ = IdentifierQuotingStyle::Backticks,
bool show_secrets_ = true,
LiteralEscapingStyle literal_escaping_style_ = LiteralEscapingStyle::Regular,
bool print_pretty_type_names_ = false)
bool print_pretty_type_names_ = false,
bool enable_secure_identifiers_ = false)
: ostr(ostr_)
, one_line(one_line_)
, hilite(hilite_)
@ -221,6 +223,7 @@ public:
, nl_or_ws(one_line ? ' ' : '\n')
, literal_escaping_style(literal_escaping_style_)
, print_pretty_type_names(print_pretty_type_names_)
, enable_secure_identifiers(enable_secure_identifiers_)
{
}
@ -234,6 +237,7 @@ public:
, nl_or_ws(other.nl_or_ws)
, literal_escaping_style(other.literal_escaping_style)
, print_pretty_type_names(other.print_pretty_type_names)
, enable_secure_identifiers(other.enable_secure_identifiers)
{
}
@ -241,6 +245,7 @@ public:
// Quote identifier `name` even when `always_quote_identifiers` is false.
// If `identifier_quoting_style` is `IdentifierQuotingStyle::None`, quote it with `IdentifierQuotingStyle::Backticks`
void quoteIdentifier(const String & name) const;
void checkIdentifier(const String & name) const;
};
/// State. For example, a set of nodes can be remembered, which we already walk through.

View File

@ -0,0 +1,3 @@
CREATE TABLE default.test_foo\n(\n `secure_123` Int8,\n `date` Date,\n `town` LowCardinality(String)\n)\nENGINE = MergeTree\nPARTITION BY toYear(date)\nPRIMARY KEY (town, date)\nORDER BY (town, date)\nSETTINGS index_granularity = 8192\nCOMMENT \'test\'
CREATE TABLE default.test_foo\n(\n `123_secure` Int8,\n `date` Date,\n `town` LowCardinality(String)\n)\nENGINE = MergeTree\nPARTITION BY toYear(date)\nPRIMARY KEY (town, date)\nORDER BY (town, date)\nSETTINGS index_granularity = 8192\nCOMMENT \'test\'
CREATE TABLE default.test_foo\n(\n `insecure_$` Int8,\n `date` Date,\n `town` LowCardinality(String)\n)\nENGINE = MergeTree\nPARTITION BY toYear(date)\nPRIMARY KEY (town, date)\nORDER BY (town, date)\nSETTINGS index_granularity = 8192

View File

@ -0,0 +1,111 @@
DROP TABLE IF EXISTS `test_foo_#`;
CREATE TABLE `test_foo_#` (
`date` Date,
`town` LowCardinality(String),
)
ENGINE = MergeTree
PRIMARY KEY (town, date)
PARTITION BY toYear(date)
COMMENT 'test' -- to end ENGINE definition, so SETTINGS will be in the query level
SETTINGS
enable_secure_identifiers=true; -- { serverError BAD_ARGUMENTS }
DROP TABLE IF EXISTS `test_foo_#`;
DROP TABLE IF EXISTS test_foo;
CREATE TABLE test_foo (
`insecure_#` Int8,
`date` Date,
`town` LowCardinality(String),
)
ENGINE = MergeTree
PRIMARY KEY (town, date)
PARTITION BY toYear(date)
COMMENT 'test' -- to end ENGINE definition, so SETTINGS will be in the query level
SETTINGS
enable_secure_identifiers=true; -- { serverError BAD_ARGUMENTS }
DROP TABLE IF EXISTS test_foo;
CREATE TABLE test_foo (
`insecure_'` Int8,
`date` Date,
`town` LowCardinality(String),
)
ENGINE = MergeTree
PRIMARY KEY (town, date)
PARTITION BY toYear(date)
COMMENT 'test' -- to end ENGINE definition, so SETTINGS will be in the query level
SETTINGS
enable_secure_identifiers=true; -- { serverError BAD_ARGUMENTS }
DROP TABLE IF EXISTS test_foo;
CREATE TABLE test_foo (
`insecure_"` Int8,
`date` Date,
`town` LowCardinality(String),
)
ENGINE = MergeTree
PRIMARY KEY (town, date)
PARTITION BY toYear(date)
COMMENT 'test' -- to end ENGINE definition, so SETTINGS will be in the query level
SETTINGS
enable_secure_identifiers=true; -- { serverError BAD_ARGUMENTS }
DROP TABLE IF EXISTS test_foo;
CREATE TABLE test_foo (
`secure_123` Int8,
`date` Date,
`town` LowCardinality(String),
)
ENGINE = MergeTree
PRIMARY KEY (town, date)
PARTITION BY toYear(date)
COMMENT 'test' -- to end ENGINE definition, so SETTINGS will be in the query level
SETTINGS
enable_secure_identifiers=true;
SHOW CREATE TABLE test_foo
SETTINGS
enable_secure_identifiers=true;
DROP TABLE IF EXISTS test_foo;
CREATE TABLE test_foo (
`123_secure` Int8,
`date` Date,
`town` LowCardinality(String),
)
ENGINE = MergeTree
PRIMARY KEY (town, date)
PARTITION BY toYear(date)
COMMENT 'test' -- to end ENGINE definition, so SETTINGS will be in the query level
SETTINGS
enable_secure_identifiers=true;
SHOW CREATE TABLE test_foo
SETTINGS
enable_secure_identifiers=true;
-- CREATE TABLE without `enable_secure_identifiers`
DROP TABLE IF EXISTS test_foo;
CREATE TABLE `test_foo` (
`insecure_$` Int8,
`date` Date,
`town` LowCardinality(String),
)
ENGINE = MergeTree
PRIMARY KEY (town, date)
PARTITION BY toYear(date);
-- Then SHOW CREATE .. with `enable_secure_identifiers`
-- While the result contains insecure identifiers (`insecure_$`), the `SHOW CREATE TABLE ...` query does not have any. So the query is expected to succeed.
SHOW CREATE TABLE test_foo
SETTINGS
enable_secure_identifiers=true;
DROP TABLE IF EXISTS test_foo;
-- SHOW CREATE .. query contains an insecure identifier (`test_foo$`) with `enable_secure_identifiers`
SHOW CREATE TABLE `test_foo$`
SETTINGS
enable_secure_identifiers=true; -- { serverError BAD_ARGUMENTS }
DROP TABLE IF EXISTS test_foo;