557 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			557 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Go
		
	
	
	
| // Copyright 2023 The go-fuzz-headers Authors.
 | |
| //
 | |
| // Licensed under the Apache License, Version 2.0 (the "License");
 | |
| // you may not use this file except in compliance with the License.
 | |
| // You may obtain a copy of the License at
 | |
| //
 | |
| //     http://www.apache.org/licenses/LICENSE-2.0
 | |
| //
 | |
| // Unless required by applicable law or agreed to in writing, software
 | |
| // distributed under the License is distributed on an "AS IS" BASIS,
 | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| // See the License for the specific language governing permissions and
 | |
| // limitations under the License.
 | |
| 
 | |
| package gofuzzheaders
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| // returns a keyword by index
 | |
| func getKeyword(f *ConsumeFuzzer) (string, error) {
 | |
| 	index, err := f.GetInt()
 | |
| 	if err != nil {
 | |
| 		return keywords[0], err
 | |
| 	}
 | |
| 	for i, k := range keywords {
 | |
| 		if i == index {
 | |
| 			return k, nil
 | |
| 		}
 | |
| 	}
 | |
| 	return keywords[0], fmt.Errorf("could not get a kw")
 | |
| }
 | |
| 
 | |
| // Simple utility function to check if a string
 | |
| // slice contains a string.
 | |
| func containsString(s []string, e string) bool {
 | |
| 	for _, a := range s {
 | |
| 		if a == e {
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| // These keywords are used specifically for fuzzing Vitess
 | |
| var keywords = []string{
 | |
| 	"accessible", "action", "add", "after", "against", "algorithm",
 | |
| 	"all", "alter", "always", "analyze", "and", "as", "asc", "asensitive",
 | |
| 	"auto_increment", "avg_row_length", "before", "begin", "between",
 | |
| 	"bigint", "binary", "_binary", "_utf8mb4", "_utf8", "_latin1", "bit",
 | |
| 	"blob", "bool", "boolean", "both", "by", "call", "cancel", "cascade",
 | |
| 	"cascaded", "case", "cast", "channel", "change", "char", "character",
 | |
| 	"charset", "check", "checksum", "coalesce", "code", "collate", "collation",
 | |
| 	"column", "columns", "comment", "committed", "commit", "compact", "complete",
 | |
| 	"compressed", "compression", "condition", "connection", "constraint", "continue",
 | |
| 	"convert", "copy", "cume_dist", "substr", "substring", "create", "cross",
 | |
| 	"csv", "current_date", "current_time", "current_timestamp", "current_user",
 | |
| 	"cursor", "data", "database", "databases", "day", "day_hour", "day_microsecond",
 | |
| 	"day_minute", "day_second", "date", "datetime", "dec", "decimal", "declare",
 | |
| 	"default", "definer", "delay_key_write", "delayed", "delete", "dense_rank",
 | |
| 	"desc", "describe", "deterministic", "directory", "disable", "discard",
 | |
| 	"disk", "distinct", "distinctrow", "div", "double", "do", "drop", "dumpfile",
 | |
| 	"duplicate", "dynamic", "each", "else", "elseif", "empty", "enable",
 | |
| 	"enclosed", "encryption", "end", "enforced", "engine", "engines", "enum",
 | |
| 	"error", "escape", "escaped", "event", "exchange", "exclusive", "exists",
 | |
| 	"exit", "explain", "expansion", "export", "extended", "extract", "false",
 | |
| 	"fetch", "fields", "first", "first_value", "fixed", "float", "float4",
 | |
| 	"float8", "flush", "for", "force", "foreign", "format", "from", "full",
 | |
| 	"fulltext", "function", "general", "generated", "geometry", "geometrycollection",
 | |
| 	"get", "global", "gtid_executed", "grant", "group", "grouping", "groups",
 | |
| 	"group_concat", "having", "header", "high_priority", "hosts", "hour", "hour_microsecond",
 | |
| 	"hour_minute", "hour_second", "if", "ignore", "import", "in", "index", "indexes",
 | |
| 	"infile", "inout", "inner", "inplace", "insensitive", "insert", "insert_method",
 | |
| 	"int", "int1", "int2", "int3", "int4", "int8", "integer", "interval",
 | |
| 	"into", "io_after_gtids", "is", "isolation", "iterate", "invoker", "join",
 | |
| 	"json", "json_table", "key", "keys", "keyspaces", "key_block_size", "kill", "lag",
 | |
| 	"language", "last", "last_value", "last_insert_id", "lateral", "lead", "leading",
 | |
| 	"leave", "left", "less", "level", "like", "limit", "linear", "lines",
 | |
| 	"linestring", "load", "local", "localtime", "localtimestamp", "lock", "logs",
 | |
| 	"long", "longblob", "longtext", "loop", "low_priority", "manifest",
 | |
| 	"master_bind", "match", "max_rows", "maxvalue", "mediumblob", "mediumint",
 | |
| 	"mediumtext", "memory", "merge", "microsecond", "middleint", "min_rows", "minute",
 | |
| 	"minute_microsecond", "minute_second", "mod", "mode", "modify", "modifies",
 | |
| 	"multilinestring", "multipoint", "multipolygon", "month", "name",
 | |
| 	"names", "natural", "nchar", "next", "no", "none", "not", "no_write_to_binlog",
 | |
| 	"nth_value", "ntile", "null", "numeric", "of", "off", "offset", "on",
 | |
| 	"only", "open", "optimize", "optimizer_costs", "option", "optionally",
 | |
| 	"or", "order", "out", "outer", "outfile", "over", "overwrite", "pack_keys",
 | |
| 	"parser", "partition", "partitioning", "password", "percent_rank", "plugins",
 | |
| 	"point", "polygon", "precision", "primary", "privileges", "processlist",
 | |
| 	"procedure", "query", "quarter", "range", "rank", "read", "reads", "read_write",
 | |
| 	"real", "rebuild", "recursive", "redundant", "references", "regexp", "relay",
 | |
| 	"release", "remove", "rename", "reorganize", "repair", "repeat", "repeatable",
 | |
| 	"replace", "require", "resignal", "restrict", "return", "retry", "revert",
 | |
| 	"revoke", "right", "rlike", "rollback", "row", "row_format", "row_number",
 | |
| 	"rows", "s3", "savepoint", "schema", "schemas", "second", "second_microsecond",
 | |
| 	"security", "select", "sensitive", "separator", "sequence", "serializable",
 | |
| 	"session", "set", "share", "shared", "show", "signal", "signed", "slow",
 | |
| 	"smallint", "spatial", "specific", "sql", "sqlexception", "sqlstate",
 | |
| 	"sqlwarning", "sql_big_result", "sql_cache", "sql_calc_found_rows",
 | |
| 	"sql_no_cache", "sql_small_result", "ssl", "start", "starting",
 | |
| 	"stats_auto_recalc", "stats_persistent", "stats_sample_pages", "status",
 | |
| 	"storage", "stored", "straight_join", "stream", "system", "vstream",
 | |
| 	"table", "tables", "tablespace", "temporary", "temptable", "terminated",
 | |
| 	"text", "than", "then", "time", "timestamp", "timestampadd", "timestampdiff",
 | |
| 	"tinyblob", "tinyint", "tinytext", "to", "trailing", "transaction", "tree",
 | |
| 	"traditional", "trigger", "triggers", "true", "truncate", "uncommitted",
 | |
| 	"undefined", "undo", "union", "unique", "unlock", "unsigned", "update",
 | |
| 	"upgrade", "usage", "use", "user", "user_resources", "using", "utc_date",
 | |
| 	"utc_time", "utc_timestamp", "validation", "values", "variables", "varbinary",
 | |
| 	"varchar", "varcharacter", "varying", "vgtid_executed", "virtual", "vindex",
 | |
| 	"vindexes", "view", "vitess", "vitess_keyspaces", "vitess_metadata",
 | |
| 	"vitess_migration", "vitess_migrations", "vitess_replication_status",
 | |
| 	"vitess_shards", "vitess_tablets", "vschema", "warnings", "when",
 | |
| 	"where", "while", "window", "with", "without", "work", "write", "xor",
 | |
| 	"year", "year_month", "zerofill",
 | |
| }
 | |
| 
 | |
| // Keywords that could get an additional keyword
 | |
| var needCustomString = []string{
 | |
| 	"DISTINCTROW", "FROM", // Select keywords:
 | |
| 	"GROUP BY", "HAVING", "WINDOW",
 | |
| 	"FOR",
 | |
| 	"ORDER BY", "LIMIT",
 | |
| 	"INTO", "PARTITION", "AS", // Insert Keywords:
 | |
| 	"ON DUPLICATE KEY UPDATE",
 | |
| 	"WHERE", "LIMIT", // Delete keywords
 | |
| 	"INFILE", "INTO TABLE", "CHARACTER SET", // Load keywords
 | |
| 	"TERMINATED BY", "ENCLOSED BY",
 | |
| 	"ESCAPED BY", "STARTING BY",
 | |
| 	"TERMINATED BY", "STARTING BY",
 | |
| 	"IGNORE",
 | |
| 	"VALUE", "VALUES", // Replace tokens
 | |
| 	"SET",                                   // Update tokens
 | |
| 	"ENGINE =",                              // Drop tokens
 | |
| 	"DEFINER =", "ON SCHEDULE", "RENAME TO", // Alter tokens
 | |
| 	"COMMENT", "DO", "INITIAL_SIZE = ", "OPTIONS",
 | |
| }
 | |
| 
 | |
| var alterTableTokens = [][]string{
 | |
| 	{"CUSTOM_FUZZ_STRING"},
 | |
| 	{"CUSTOM_ALTTER_TABLE_OPTIONS"},
 | |
| 	{"PARTITION_OPTIONS_FOR_ALTER_TABLE"},
 | |
| }
 | |
| 
 | |
| var alterTokens = [][]string{
 | |
| 	{
 | |
| 		"DATABASE", "SCHEMA", "DEFINER = ", "EVENT", "FUNCTION", "INSTANCE",
 | |
| 		"LOGFILE GROUP", "PROCEDURE", "SERVER",
 | |
| 	},
 | |
| 	{"CUSTOM_FUZZ_STRING"},
 | |
| 	{
 | |
| 		"ON SCHEDULE", "ON COMPLETION PRESERVE", "ON COMPLETION NOT PRESERVE",
 | |
| 		"ADD UNDOFILE", "OPTIONS",
 | |
| 	},
 | |
| 	{"RENAME TO", "INITIAL_SIZE = "},
 | |
| 	{"ENABLE", "DISABLE", "DISABLE ON SLAVE", "ENGINE"},
 | |
| 	{"COMMENT"},
 | |
| 	{"DO"},
 | |
| }
 | |
| 
 | |
| var setTokens = [][]string{
 | |
| 	{"CHARACTER SET", "CHARSET", "CUSTOM_FUZZ_STRING", "NAMES"},
 | |
| 	{"CUSTOM_FUZZ_STRING", "DEFAULT", "="},
 | |
| 	{"CUSTOM_FUZZ_STRING"},
 | |
| }
 | |
| 
 | |
| var dropTokens = [][]string{
 | |
| 	{"TEMPORARY", "UNDO"},
 | |
| 	{
 | |
| 		"DATABASE", "SCHEMA", "EVENT", "INDEX", "LOGFILE GROUP",
 | |
| 		"PROCEDURE", "FUNCTION", "SERVER", "SPATIAL REFERENCE SYSTEM",
 | |
| 		"TABLE", "TABLESPACE", "TRIGGER", "VIEW",
 | |
| 	},
 | |
| 	{"IF EXISTS"},
 | |
| 	{"CUSTOM_FUZZ_STRING"},
 | |
| 	{"ON", "ENGINE = ", "RESTRICT", "CASCADE"},
 | |
| }
 | |
| 
 | |
| var renameTokens = [][]string{
 | |
| 	{"TABLE"},
 | |
| 	{"CUSTOM_FUZZ_STRING"},
 | |
| 	{"TO"},
 | |
| 	{"CUSTOM_FUZZ_STRING"},
 | |
| }
 | |
| 
 | |
| var truncateTokens = [][]string{
 | |
| 	{"TABLE"},
 | |
| 	{"CUSTOM_FUZZ_STRING"},
 | |
| }
 | |
| 
 | |
| var createTokens = [][]string{
 | |
| 	{"OR REPLACE", "TEMPORARY", "UNDO"}, // For create spatial reference system
 | |
| 	{
 | |
| 		"UNIQUE", "FULLTEXT", "SPATIAL", "ALGORITHM = UNDEFINED", "ALGORITHM = MERGE",
 | |
| 		"ALGORITHM = TEMPTABLE",
 | |
| 	},
 | |
| 	{
 | |
| 		"DATABASE", "SCHEMA", "EVENT", "FUNCTION", "INDEX", "LOGFILE GROUP",
 | |
| 		"PROCEDURE", "SERVER", "SPATIAL REFERENCE SYSTEM", "TABLE", "TABLESPACE",
 | |
| 		"TRIGGER", "VIEW",
 | |
| 	},
 | |
| 	{"IF NOT EXISTS"},
 | |
| 	{"CUSTOM_FUZZ_STRING"},
 | |
| }
 | |
| 
 | |
| /*
 | |
| // For future use.
 | |
| var updateTokens = [][]string{
 | |
| 	{"LOW_PRIORITY"},
 | |
| 	{"IGNORE"},
 | |
| 	{"SET"},
 | |
| 	{"WHERE"},
 | |
| 	{"ORDER BY"},
 | |
| 	{"LIMIT"},
 | |
| }
 | |
| */
 | |
| 
 | |
| var replaceTokens = [][]string{
 | |
| 	{"LOW_PRIORITY", "DELAYED"},
 | |
| 	{"INTO"},
 | |
| 	{"PARTITION"},
 | |
| 	{"CUSTOM_FUZZ_STRING"},
 | |
| 	{"VALUES", "VALUE"},
 | |
| }
 | |
| 
 | |
| var loadTokens = [][]string{
 | |
| 	{"DATA"},
 | |
| 	{"LOW_PRIORITY", "CONCURRENT", "LOCAL"},
 | |
| 	{"INFILE"},
 | |
| 	{"REPLACE", "IGNORE"},
 | |
| 	{"INTO TABLE"},
 | |
| 	{"PARTITION"},
 | |
| 	{"CHARACTER SET"},
 | |
| 	{"FIELDS", "COLUMNS"},
 | |
| 	{"TERMINATED BY"},
 | |
| 	{"OPTIONALLY"},
 | |
| 	{"ENCLOSED BY"},
 | |
| 	{"ESCAPED BY"},
 | |
| 	{"LINES"},
 | |
| 	{"STARTING BY"},
 | |
| 	{"TERMINATED BY"},
 | |
| 	{"IGNORE"},
 | |
| 	{"LINES", "ROWS"},
 | |
| 	{"CUSTOM_FUZZ_STRING"},
 | |
| }
 | |
| 
 | |
| // These Are everything that comes after "INSERT"
 | |
| var insertTokens = [][]string{
 | |
| 	{"LOW_PRIORITY", "DELAYED", "HIGH_PRIORITY", "IGNORE"},
 | |
| 	{"INTO"},
 | |
| 	{"PARTITION"},
 | |
| 	{"CUSTOM_FUZZ_STRING"},
 | |
| 	{"AS"},
 | |
| 	{"ON DUPLICATE KEY UPDATE"},
 | |
| }
 | |
| 
 | |
| // These are everything that comes after "SELECT"
 | |
| var selectTokens = [][]string{
 | |
| 	{"*", "CUSTOM_FUZZ_STRING", "DISTINCTROW"},
 | |
| 	{"HIGH_PRIORITY"},
 | |
| 	{"STRAIGHT_JOIN"},
 | |
| 	{"SQL_SMALL_RESULT", "SQL_BIG_RESULT", "SQL_BUFFER_RESULT"},
 | |
| 	{"SQL_NO_CACHE", "SQL_CALC_FOUND_ROWS"},
 | |
| 	{"CUSTOM_FUZZ_STRING"},
 | |
| 	{"FROM"},
 | |
| 	{"WHERE"},
 | |
| 	{"GROUP BY"},
 | |
| 	{"HAVING"},
 | |
| 	{"WINDOW"},
 | |
| 	{"ORDER BY"},
 | |
| 	{"LIMIT"},
 | |
| 	{"CUSTOM_FUZZ_STRING"},
 | |
| 	{"FOR"},
 | |
| }
 | |
| 
 | |
| // These are everything that comes after "DELETE"
 | |
| var deleteTokens = [][]string{
 | |
| 	{"LOW_PRIORITY", "QUICK", "IGNORE", "FROM", "AS"},
 | |
| 	{"PARTITION"},
 | |
| 	{"WHERE"},
 | |
| 	{"ORDER BY"},
 | |
| 	{"LIMIT"},
 | |
| }
 | |
| 
 | |
| var alter_table_options = []string{
 | |
| 	"ADD", "COLUMN", "FIRST", "AFTER", "INDEX", "KEY", "FULLTEXT", "SPATIAL",
 | |
| 	"CONSTRAINT", "UNIQUE", "FOREIGN KEY", "CHECK", "ENFORCED", "DROP", "ALTER",
 | |
| 	"NOT", "INPLACE", "COPY", "SET", "VISIBLE", "INVISIBLE", "DEFAULT", "CHANGE",
 | |
| 	"CHARACTER SET", "COLLATE", "DISABLE", "ENABLE", "KEYS", "TABLESPACE", "LOCK",
 | |
| 	"FORCE", "MODIFY", "SHARED", "EXCLUSIVE", "NONE", "ORDER BY", "RENAME COLUMN",
 | |
| 	"AS", "=", "ASC", "DESC", "WITH", "WITHOUT", "VALIDATION", "ADD PARTITION",
 | |
| 	"DROP PARTITION", "DISCARD PARTITION", "IMPORT PARTITION", "TRUNCATE PARTITION",
 | |
| 	"COALESCE PARTITION", "REORGANIZE PARTITION", "EXCHANGE PARTITION",
 | |
| 	"ANALYZE PARTITION", "CHECK PARTITION", "OPTIMIZE PARTITION", "REBUILD PARTITION",
 | |
| 	"REPAIR PARTITION", "REMOVE PARTITIONING", "USING", "BTREE", "HASH", "COMMENT",
 | |
| 	"KEY_BLOCK_SIZE", "WITH PARSER", "AUTOEXTEND_SIZE", "AUTO_INCREMENT", "AVG_ROW_LENGTH",
 | |
| 	"CHECKSUM", "INSERT_METHOD", "ROW_FORMAT", "DYNAMIC", "FIXED", "COMPRESSED", "REDUNDANT",
 | |
| 	"COMPACT", "SECONDARY_ENGINE_ATTRIBUTE", "STATS_AUTO_RECALC", "STATS_PERSISTENT",
 | |
| 	"STATS_SAMPLE_PAGES", "ZLIB", "LZ4", "ENGINE_ATTRIBUTE", "KEY_BLOCK_SIZE", "MAX_ROWS",
 | |
| 	"MIN_ROWS", "PACK_KEYS", "PASSWORD", "COMPRESSION", "CONNECTION", "DIRECTORY",
 | |
| 	"DELAY_KEY_WRITE", "ENCRYPTION", "STORAGE", "DISK", "MEMORY", "UNION",
 | |
| }
 | |
| 
 | |
| // Creates an 'alter table' statement. 'alter table' is an exception
 | |
| // in that it has its own function. The majority of statements
 | |
| // are created by 'createStmt()'.
 | |
| func createAlterTableStmt(f *ConsumeFuzzer) (string, error) {
 | |
| 	maxArgs, err := f.GetInt()
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	maxArgs = maxArgs % 30
 | |
| 	if maxArgs == 0 {
 | |
| 		return "", fmt.Errorf("could not create alter table stmt")
 | |
| 	}
 | |
| 
 | |
| 	var stmt strings.Builder
 | |
| 	stmt.WriteString("ALTER TABLE ")
 | |
| 	for i := 0; i < maxArgs; i++ {
 | |
| 		// Calculate if we get existing token or custom string
 | |
| 		tokenType, err := f.GetInt()
 | |
| 		if err != nil {
 | |
| 			return "", err
 | |
| 		}
 | |
| 		if tokenType%4 == 1 {
 | |
| 			customString, err := f.GetString()
 | |
| 			if err != nil {
 | |
| 				return "", err
 | |
| 			}
 | |
| 			stmt.WriteString(" " + customString)
 | |
| 		} else {
 | |
| 			tokenIndex, err := f.GetInt()
 | |
| 			if err != nil {
 | |
| 				return "", err
 | |
| 			}
 | |
| 			stmt.WriteString(" " + alter_table_options[tokenIndex%len(alter_table_options)])
 | |
| 		}
 | |
| 	}
 | |
| 	return stmt.String(), nil
 | |
| }
 | |
| 
 | |
| func chooseToken(tokens []string, f *ConsumeFuzzer) (string, error) {
 | |
| 	index, err := f.GetInt()
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	var token strings.Builder
 | |
| 	token.WriteString(tokens[index%len(tokens)])
 | |
| 	if token.String() == "CUSTOM_FUZZ_STRING" {
 | |
| 		customFuzzString, err := f.GetString()
 | |
| 		if err != nil {
 | |
| 			return "", err
 | |
| 		}
 | |
| 		return customFuzzString, nil
 | |
| 	}
 | |
| 
 | |
| 	// Check if token requires an argument
 | |
| 	if containsString(needCustomString, token.String()) {
 | |
| 		customFuzzString, err := f.GetString()
 | |
| 		if err != nil {
 | |
| 			return "", err
 | |
| 		}
 | |
| 		token.WriteString(" " + customFuzzString)
 | |
| 	}
 | |
| 	return token.String(), nil
 | |
| }
 | |
| 
 | |
| var stmtTypes = map[string][][]string{
 | |
| 	"DELETE":      deleteTokens,
 | |
| 	"INSERT":      insertTokens,
 | |
| 	"SELECT":      selectTokens,
 | |
| 	"LOAD":        loadTokens,
 | |
| 	"REPLACE":     replaceTokens,
 | |
| 	"CREATE":      createTokens,
 | |
| 	"DROP":        dropTokens,
 | |
| 	"RENAME":      renameTokens,
 | |
| 	"TRUNCATE":    truncateTokens,
 | |
| 	"SET":         setTokens,
 | |
| 	"ALTER":       alterTokens,
 | |
| 	"ALTER TABLE": alterTableTokens, // ALTER TABLE has its own set of tokens
 | |
| }
 | |
| 
 | |
| var stmtTypeEnum = map[int]string{
 | |
| 	0:  "DELETE",
 | |
| 	1:  "INSERT",
 | |
| 	2:  "SELECT",
 | |
| 	3:  "LOAD",
 | |
| 	4:  "REPLACE",
 | |
| 	5:  "CREATE",
 | |
| 	6:  "DROP",
 | |
| 	7:  "RENAME",
 | |
| 	8:  "TRUNCATE",
 | |
| 	9:  "SET",
 | |
| 	10: "ALTER",
 | |
| 	11: "ALTER TABLE",
 | |
| }
 | |
| 
 | |
| func createStmt(f *ConsumeFuzzer) (string, error) {
 | |
| 	stmtIndex, err := f.GetInt()
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	stmtIndex = stmtIndex % len(stmtTypes)
 | |
| 
 | |
| 	queryType := stmtTypeEnum[stmtIndex]
 | |
| 	tokens := stmtTypes[queryType]
 | |
| 
 | |
| 	// We have custom creator for ALTER TABLE
 | |
| 	if queryType == "ALTER TABLE" {
 | |
| 		query, err := createAlterTableStmt(f)
 | |
| 		if err != nil {
 | |
| 			return "", err
 | |
| 		}
 | |
| 		return query, nil
 | |
| 	}
 | |
| 
 | |
| 	// Here we are creating a query that is not
 | |
| 	// an 'alter table' query. For available
 | |
| 	// queries, see "stmtTypes"
 | |
| 
 | |
| 	// First specify the first query keyword:
 | |
| 	var query strings.Builder
 | |
| 	query.WriteString(queryType)
 | |
| 
 | |
| 	// Next create the args for the
 | |
| 	queryArgs, err := createStmtArgs(tokens, f)
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	query.WriteString(" " + queryArgs)
 | |
| 	return query.String(), nil
 | |
| }
 | |
| 
 | |
| // Creates the arguments of a statements. In a select statement
 | |
| // that would be everything after "select".
 | |
| func createStmtArgs(tokenslice [][]string, f *ConsumeFuzzer) (string, error) {
 | |
| 	var query, token strings.Builder
 | |
| 
 | |
| 	// We go through the tokens in the tokenslice,
 | |
| 	// create the respective token and add it to
 | |
| 	// "query"
 | |
| 	for _, tokens := range tokenslice {
 | |
| 		// For extra randomization, the fuzzer can
 | |
| 		// choose to not include this token.
 | |
| 		includeThisToken, err := f.GetBool()
 | |
| 		if err != nil {
 | |
| 			return "", err
 | |
| 		}
 | |
| 		if !includeThisToken {
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		// There may be several tokens to choose from:
 | |
| 		if len(tokens) > 1 {
 | |
| 			chosenToken, err := chooseToken(tokens, f)
 | |
| 			if err != nil {
 | |
| 				return "", err
 | |
| 			}
 | |
| 			query.WriteString(" " + chosenToken)
 | |
| 		} else {
 | |
| 			token.WriteString(tokens[0])
 | |
| 
 | |
| 			// In case the token is "CUSTOM_FUZZ_STRING"
 | |
| 			// we will then create a non-structured string
 | |
| 			if token.String() == "CUSTOM_FUZZ_STRING" {
 | |
| 				customFuzzString, err := f.GetString()
 | |
| 				if err != nil {
 | |
| 					return "", err
 | |
| 				}
 | |
| 				query.WriteString(" " + customFuzzString)
 | |
| 				continue
 | |
| 			}
 | |
| 
 | |
| 			// Check if token requires an argument.
 | |
| 			// Tokens that take an argument can be found
 | |
| 			// in 'needCustomString'. If so, we add a
 | |
| 			// non-structured string to the token.
 | |
| 			if containsString(needCustomString, token.String()) {
 | |
| 				customFuzzString, err := f.GetString()
 | |
| 				if err != nil {
 | |
| 					return "", err
 | |
| 				}
 | |
| 				token.WriteString(fmt.Sprintf(" %s", customFuzzString))
 | |
| 			}
 | |
| 			query.WriteString(fmt.Sprintf(" %s", token.String()))
 | |
| 		}
 | |
| 	}
 | |
| 	return query.String(), nil
 | |
| }
 | |
| 
 | |
| // Creates a semi-structured query. It creates a string
 | |
| // that is a combination of the keywords and random strings.
 | |
| func createQuery(f *ConsumeFuzzer) (string, error) {
 | |
| 	queryLen, err := f.GetInt()
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	maxLen := queryLen % 60
 | |
| 	if maxLen == 0 {
 | |
| 		return "", fmt.Errorf("could not create a query")
 | |
| 	}
 | |
| 	var query strings.Builder
 | |
| 	for i := 0; i < maxLen; i++ {
 | |
| 		// Get a new token:
 | |
| 		useKeyword, err := f.GetBool()
 | |
| 		if err != nil {
 | |
| 			return "", err
 | |
| 		}
 | |
| 		if useKeyword {
 | |
| 			keyword, err := getKeyword(f)
 | |
| 			if err != nil {
 | |
| 				return "", err
 | |
| 			}
 | |
| 			query.WriteString(" " + keyword)
 | |
| 		} else {
 | |
| 			customString, err := f.GetString()
 | |
| 			if err != nil {
 | |
| 				return "", err
 | |
| 			}
 | |
| 			query.WriteString(" " + customString)
 | |
| 		}
 | |
| 	}
 | |
| 	if query.String() == "" {
 | |
| 		return "", fmt.Errorf("could not create a query")
 | |
| 	}
 | |
| 	return query.String(), nil
 | |
| }
 | |
| 
 | |
| // GetSQLString is the API that users interact with.
 | |
| //
 | |
| // Usage:
 | |
| //
 | |
| //	f := NewConsumer(data)
 | |
| //	sqlString, err := f.GetSQLString()
 | |
| func (f *ConsumeFuzzer) GetSQLString() (string, error) {
 | |
| 	var query string
 | |
| 	veryStructured, err := f.GetBool()
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	if veryStructured {
 | |
| 		query, err = createStmt(f)
 | |
| 		if err != nil {
 | |
| 			return "", err
 | |
| 		}
 | |
| 	} else {
 | |
| 		query, err = createQuery(f)
 | |
| 		if err != nil {
 | |
| 			return "", err
 | |
| 		}
 | |
| 	}
 | |
| 	return query, nil
 | |
| }
 |