525 lines
17 KiB
Go
525 lines
17 KiB
Go
|
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"},
|
||
|
}
|
||
|
|
||
|
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) {
|
||
|
var stmt strings.Builder
|
||
|
stmt.WriteString("ALTER TABLE ")
|
||
|
maxArgs, err := f.GetInt()
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
maxArgs = maxArgs % 30
|
||
|
if maxArgs == 0 {
|
||
|
return "", fmt.Errorf("Could not create alter table stmt")
|
||
|
}
|
||
|
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(fmt.Sprintf(" %s", customString))
|
||
|
} else {
|
||
|
tokenIndex, err := f.GetInt()
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
stmt.WriteString(fmt.Sprintf(" %s", 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(fmt.Sprintf(" %s", 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(fmt.Sprintf(" %s", 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(fmt.Sprintf(" %s", 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 strings.Builder
|
||
|
var 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(fmt.Sprintf(" %s", 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(fmt.Sprintf(" %s", 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(fmt.Sprintf(" %s", keyword))
|
||
|
} else {
|
||
|
customString, err := f.GetString()
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
query.WriteString(fmt.Sprintf(" %s", customString))
|
||
|
}
|
||
|
}
|
||
|
if query.String() == "" {
|
||
|
return "", fmt.Errorf("Could not create a query")
|
||
|
}
|
||
|
return query.String(), nil
|
||
|
}
|
||
|
|
||
|
// This is the API that users will 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
|
||
|
}
|