@@ -83,39 +83,71 @@ static bool ContainsMacroDefinition(const std::string &content) {
83
83
// Parse Function Name
84
84
static std::string ExtractMacroName (const std::string ¯o_sql) {
85
85
try {
86
- // Convert to uppercase for case-insensitive matching
87
86
std::string upper_sql = StringUtil::Upper (macro_sql);
88
-
89
- // Find the MACRO keyword
90
87
size_t macro_pos = upper_sql.find (" MACRO" );
91
88
if (macro_pos == std::string::npos) {
92
89
return " unknown" ;
93
90
}
94
-
95
- // Find the start of the name (after MACRO and any whitespace)
91
+
96
92
size_t name_start = macro_pos + 5 ; // length of "MACRO"
97
93
while (name_start < upper_sql.length () && std::isspace (upper_sql[name_start])) {
98
94
name_start++;
99
95
}
100
-
101
- // Find the end of the name (before the opening parenthesis)
96
+
102
97
size_t name_end = upper_sql.find (' (' , name_start);
103
98
if (name_end == std::string::npos) {
104
99
return " unknown" ;
105
100
}
106
-
107
- // Trim any trailing whitespace
101
+
108
102
while (name_end > name_start && std::isspace (upper_sql[name_end - 1 ])) {
109
103
name_end--;
110
104
}
111
-
112
- // Get the original case version of the name from the input string
105
+
113
106
return macro_sql.substr (name_start, name_end - name_start);
114
107
} catch (...) {
115
108
return " unknown" ;
116
109
}
117
110
}
118
111
112
+ // Helper function to check for potentially dangerous SQL commands
113
+ static std::pair<bool , std::string> ContainsDangerousCommands (const std::string &sql) {
114
+ const std::vector<std::string> dangerous_commands = {
115
+ " DELETE" , " DROP" , " TRUNCATE" , " ALTER" , " GRANT" , " REVOKE" ,
116
+ " CREATE USER" , " ALTER USER" , " DROP USER" ,
117
+ " CREATE DATABASE" , " DROP DATABASE" ,
118
+ " EXEC" , " EXECUTE" ,
119
+ " SHUTDOWN" , " RESTART" ,
120
+ " SET GLOBAL" , " SET SYSTEM" ,
121
+ " LOAD EXTENSION" , " UNLOAD EXTENSION" ,
122
+ " ATTACH" , " DETACH" ,
123
+ " COPY" , " EXPORT" ,
124
+ " UPDATE" , " MERGE"
125
+ };
126
+
127
+ std::string upper_sql = StringUtil::Upper (sql);
128
+ std::vector<std::string> found_commands;
129
+
130
+ for (const auto & cmd : dangerous_commands) {
131
+ if (upper_sql.find (cmd) != std::string::npos) {
132
+ found_commands.push_back (cmd);
133
+ }
134
+ }
135
+
136
+ if (!found_commands.empty ()) {
137
+ std::string warning = " Warning: SQL contains potentially dangerous commands: " ;
138
+ for (size_t i = 0 ; i < found_commands.size (); i++) {
139
+ warning += found_commands[i];
140
+ if (i < found_commands.size () - 1 ) {
141
+ warning += " , " ;
142
+ }
143
+ }
144
+ warning += " . Please review the macro carefully before using it." ;
145
+ return std::make_pair (true , warning);
146
+ }
147
+
148
+ return std::make_pair (false , " " );
149
+ }
150
+
119
151
// Function to fetch and create macro from URL
120
152
static void LoadMacroFromUrlFunction (DataChunk &args, ExpressionState &state, Vector &result, DatabaseInstance *db_instance) {
121
153
auto &context = state.GetContext ();
@@ -142,6 +174,12 @@ static void LoadMacroFromUrlFunction(DataChunk &args, ExpressionState &state, Ve
142
174
// Get the SQL content
143
175
std::string macro_sql = res->body ;
144
176
177
+ // Check for dangerous commands
178
+ auto dangerous_check = ContainsDangerousCommands (macro_sql);
179
+ if (dangerous_check.first ) {
180
+ throw std::runtime_error (dangerous_check.second );
181
+ }
182
+
145
183
// Replace all \r\n with \n
146
184
macro_sql = StringUtil::Replace (macro_sql, " \r\n " , " \n " );
147
185
// Replace any remaining \r with \n
0 commit comments