Simon
Simon

Reputation: 17

How to sanitize user input in Bash

I am writing a script in bash which will prompt the user for two inputs. These inputs are assigned to the variables 'TO_SCHEMA' and 'FROM_SCHEMA' respectively.

I need a way to verify proper input. My requirements are as follows:

Each variable will have 3 acceptable values. They are the same values for both variables, but both variables must be in this list of three, and they cannot be the same value.

So if the values are 'myco', 'myco_int', and 'teambatch', then both variables must be one of those values, but they can't be the same.

${TO_SCHEMA} = myco && ${FROM_SCHEMA} = myco_int
   Pass

${TO_SCHEMA} = myco_int && ${FROM_SCHEMA} = myco_int
   Fail

${TO_SCHEMA} = mco && ${FROM_SCHEMA} = myco_int
   Fail

${TO_SCHEMA} = myco && ${FROM_SCHEMA} = donkey
   Fail

How can I accomplish this?

I began with an if statement full of AND and OR operators, but they got ugly fast. My experience with regex is limited, and my experience with sed and awk is non-existent, but I'm willing to learn and try any of that. Any help would be appreciated.

EDIT:

I should also mention that this script is just for a somewhat small tedious one off task I have to do a lot at work that I would love to automate. If I'm not the one using it, then someone on my team will be. So this input checking is a want and not a need. It's not the end of the world if the script breaks because of bad input. I would just like it to handle bad input more elegantly.

EDIT AGAIN: I appreciate everyone's suggestions, but I have to make some clarifications. The values won't actually be schema 1,2 and 3. I'm not allowed to provide proper names for security reasons, but I'm changing them to values more similar to the real ones.

Upvotes: 1

Views: 1471

Answers (2)

chepner
chepner

Reputation: 532268

The simplest solution requires bash 4.3. You simply store the valid inputs as the keys of an associative array, then use the -v operator to check if a given input is defined as a key.

declare -A valid
# Values don't matter, as long as the key exists.
# I put spaces in the keys just to show it's possible
valid[schema 1]=
valid[schema 2]=
valid[schema 3]=

if [[ $FROM_SCHEMA != $TO_SCHEMA && -v valid[$FROM_SCHEMA] && -v valid[$TO_SCHEMA] ]]; then
    # inputs pass
else
    # inputs fail
fi

In earlier 4.x versions, you can check for undefined values in an associative array slightly differently.

declare -A valid
# Now, we need to make sure the values are non-null, but
# otherwise it doesn't matter what they are
valid[schema 1]=1
valid[schema 2]=1
valid[schema 3]=1
if [[ $FROM_SCHEMA != $TO_SCHEMA && -n ${valid[$FROM_SCHEMA]} && -n ${valid[$TO_SCHEMA]} ]]; then
    # inputs pass
else
    # inputs fail
fi

Prior to bash 4, with no associative arrays, you can fall back to scanning the list of valid inputs stored in a regular array.

valid=("schema 1" "schema 2" "schema 3")
if [[ $TO_SCHEMA == $FROM_SCHEMA ]]; then
    # inputs fail
else
    ok_count=0
    # We've already established that TO and FROM are different,
    # so only at most one per candidate can match.
    for candidate in "${valid[@]}"; do
        if [[ $TO_SCHEMA == $candidate || $FROM_SCHEMA == $candidate ]]; then
            ok_count+=1
        fi
    done
    if (( ok_count == 2 )); then
        # inputs pass
    else
        # inputs fail
    fi
fi

Upvotes: 1

Lizardx
Lizardx

Reputation: 1195

Note, quick and dirty, lacking in elegance, but works. This is assuming your schema1 answers are accurate. And that I read your question right. you'd be replacing the [your input 1] etc with wherever you are getting the data from, it's a read of user input right? Why not just ask them to select it from a list?

values='1 2 3'
result1=''
result2=''

input1=[your input 1]
input2=[your input 2]
# force to lower case
input1=$(tr '[A-Z]' '[a-z]' <<< "$input1" )
input2=$(tr '[A-Z]' '[a-z]' <<< "$input2" )

for item in $values
do
    if [[ -n $( grep -E "^schema$item$" <<< $input1 ) ]];then
        values=$(sed "s/$item//" <<< $values )
        result1=$input1
    fi
done
for item in $values
do
    if [[ -n $( grep -E "^schema$item$" <<< $input2 ) ]];then
        result2=$input2
    fi
done

if [[ -z $result1 ]];then
    echo 'Your first entry is not right: ' $input1
elif [[ -z $result2 ]];then
    echo 'Your second entry is not right: ' $input2
else
    echo 'Valid data'
fi

Note that if you wanted to just test for the literal full string, you'd make it:

values='schema1 schema2 schema3'

then remove the 'schema' from the grep test, which would then just look for the full string thing. And sed would just remove the found item from the list of values.

If you are relying on user typed input, you must force it to lower to avoid spastic user actions or do a case insensitive pattern match with grep, but it's best to force it to lower before testing it.

input1=$(tr '[A-Z]' '[a-z]' <<< "$input1" )

Upvotes: 0

Related Questions