Eval para nomes de arquivos, mal?

4

Eu tenho o seguinte script para criar projetos:

clear
version="0.0.2"
echo "Project creator"
echo "---------------"
# ask user to specify a directory (could start with spelled-out home directory)
directoryName=""
# Input must meet the following criteria: 
# 1.) No spaces at all in the directory path
# 2.) some characters must be entered
while [[ ${#directoryName} -eq 0 || $directoryName == *" "* ]]
        do
                echo -e "\nEnter the full directory of the project (no spaces):"
                read directoryName
        done
echo "You entered: $directoryName"
# uate directoryName so that the rest of the code can understand it
# create directory if it does not exist
eval mkdir "$directoryName"
# if successful
if [ $? -eq 0 ]
then
        # call executable in /home/miwarren/CPPFileCreator to create a main.cpp file to put in the folder
        $HOME/CPPFileCreator/a.out $directoryName
        # copy a run.sh file into the folder, too...
        eval cp $HOME/CPPFileCreator/run.sh $directoryName/run.sh
        # you might want to put a fileCreator.sh in the folder as well
        eval cp $HOME/fileCreator.sh $directoryName/fileCreator.sh
fi

Certifique-se de remover todos os espaços (presumindo que todas as cadeias de caracteres não seguras tenham pelo menos um espaço, por causa do ataque ao estilo de injeção). Eu quero que os usuários não tenham que soletrar as partes do caminho quando existirem variáveis para ele (por exemplo, $HOME para o diretório inicial).

Ainda sou bom usar eval aqui e, se não, o que fazer em vez disso?

    
por Mike Warren 03.09.2016 / 07:23

2 respostas

6

"I want users to not have to spell out path parts when there exist variables for it (for example, $HOME for the home directory)."

Isso pode ser feito sem eval :

$ s='$HOME/.config'
$ s="${s//\$HOME/$HOME}"
$ echo "$s"
/home/john1024/.config

Isso tem algumas limitações. Por um lado, se HOMES e HOME forem nomes de variáveis que você deseja substituir, então, para evitar correspondências falsas, HOMES deve ser substituído antes HOME.

Aplicando substituições para todas as variáveis exportadas

Usando o bash:

while IFS== read -r name val
do
   s="${s//\$$name/$val}"
done < <(printenv)

Por exemplo:

$ export A=alpha; export B=beta
$ s='$HOME/$A/$B'
$ while IFS== read -r name val; do s="${s//\$$name/$val}"; done < <(printenv)
$ echo "$s"
/home/john1024/alpha/beta

Como essa abordagem não classifica os nomes das variáveis por tamanho, ela tem o problema de sobreposição da variável mencionado acima.

Podemos corrigir isso classificando os nomes das variáveis de acordo com o comprimento:

while IFS== read -r n name val
do
   s="${s//\$$name/$val}"
done < <(printenv | awk '/^[^ \t]/{key=$0; sub(/=.*/,"",key); printf "%s=%s\n",length(key),$0}' | sort -rnt=)

Se o usuário inserir um nome de variável que não exista, mas os caracteres iniciais corresponderem a algum nome mais curto, o nome mais curto será substituído. Se isso for importante, podemos evitá-lo exigindo que o usuário use a notação de chaves para essas variáveis com este código:

while IFS== read -r n name val
do
   s="${s//\$\{$name\}/$val}"
done < <(printenv | awk '/^[^ \t]/{key=$0; sub(/=.*/,"",key); printf "%s=%s\n",length(key),$0}' | sort -rnt=)

Como exemplo:

$ s='${HOME}/${A}/${B}'
$ while IFS== read -r n name val; do s="${s//\$\{$name\}/$val}"; done < <(printenv | awk '/^[^ \t]/{key=$0; sub(/=.*/,"",key); printf "%s=%s\n",length(key),$0}' | sort -rnt=)
$ echo "$s"
/home/john1024/alpha/beta
    
por 03.09.2016 / 08:03
3

Usar eval aqui não faz sentido algum. Antes de decidir como consertar seu script, pense cuidadosamente em seus requisitos e anote-os . Ao ler sua pergunta, fica claro que você não tem um entendimento claro do que deseja fazer e não resolverá esse problema até descobrir o que espera de uma solução.

assumed that every unsafe string has at least a space in it

Não, nem perto disso. Seu script permite que o usuário execute código arbitrário, pois permite que o usuário digite qualquer string que não contenha espaços e novas linhas. O usuário pode usar tabulações e ponto e vírgula. Mesmo sem espaço em branco, o usuário poderia digitar substituições de comandos:

'touch /tmp/this_is_executed'somefile
$(touch /tmp/this_is_executed)somefile

Isso é um problema? Depende do que você está tentando fazer. A entrada é proveniente do usuário que executa seu script de uma conta normal? Em caso afirmativo, não há nenhum problema de segurança, mas não faz sentido que o script leia entrada da entrada padrão: ele deve ter um argumento de linha de comando e, em seguida, o shell da linha de comando faria as expansões de variáveis desejadas. Se o seu script é executado com privilégios elevados ou recebe dados pela rede, então eval é definitivamente mal, e você não deve usá-lo até entender as implicações. eval usa uma string como argumento (se tiver vários argumentos, eles são unidos com espaços entre eles) e o executa como código shell. Se você não quisesse executar código de shell, eval é a ferramenta errada.

Se o script estiver recebendo uma entrada sem privilégios, é o local errado para executar a expansão de variáveis. Faça isso na interface que alimenta a entrada do seu script, executando com os privilégios do usuário (por exemplo, use o Javascript se a entrada vier de um navegador da Web). Em seu script, você precisará verificar se os nomes de arquivos especificados estão em um diretório ao qual o usuário deve ter acesso. Tenha cuidado com .. e links simbólicos que podem permitir a fuga de uma árvore de diretórios, e sobre o líder - que pode ser analisado como opções em vez de nomes de arquivos .

    
por 04.09.2016 / 02:13