Dado o script abaixo, como posso garantir que o argumento contenha apenas um nome de arquivo válido dentro /home/charlesingalls/
e não um caminho ( ../home/carolineingalls/
) ou curinga, etc?
Eu só quero que o script possa excluir um único arquivo do diretório codificado fornecido. Este script será executado como um usuário privilegiado.
#!/bin/bash
rm -f /home/charlesingalls/"$1"
/
. Os curingas não são interpretados entre aspas.-r
comrm
.rm -r
é para exclusão recursiva de um diretório e de todos os arquivos e diretórios abaixo dele. Só é útil ao excluir diretórios. De maneira mais geral, não faça culto à carga. ou seja, não copie apenas as coisas que parecem úteis em sua linha de comando ou script sem entender o que elas fazem ou como funcionam. Os deuses do avião que trazem a carga mágica podem ficar com raiva e excluir todos os seus arquivos.Respostas:
Se você deseja excluir apenas um arquivo
/home/charlesingalls
(e não um arquivo em um subdiretório), é fácil: basta verificar se o argumento não contém a/
.Isso é executado
rm
mesmo que o argumento seja.
ou..
ou vazio, mas nesse casorm
falhará inofensivamente ao excluir um diretório.Os curingas não são relevantes aqui, pois nenhuma expansão de curinga é realizada.
Isso é seguro mesmo na presença de links simbólicos: se o arquivo for um link simbólico, o link simbólico (que está dentro
/home/charlesingalls
) será removido e o destino desse link não será afetado.Observe que isso pressupõe que
/home/charlesingalls
não pode ser movido ou alterado. Isso deve ser bom se o diretório for codificado no script, mas se for determinado a partir de variáveis, a determinação poderá não ser mais válida no momento em que orm
comando for executado.Com base nas informações adicionais de que o argumento é um nome de host virtual, você deve fazer a lista de permissões em vez da lista negra: verifique se o nome é um nome de host virtual sensato, em vez de apenas proibir barras. Verifico se o nome começa com uma letra minúscula ou dígito e se não contém caracteres que não sejam letras minúsculas, dígitos, pontos e traços.
fonte
.
Esta resposta assume que
$1
é permitido incluir subdiretórios. Se você estiver interessado no caso mais simples, onde$1
deve haver um nome de diretório simples, consulte uma das outras respostas.Os curingas não são expandidos quando estiver entre aspas duplas. Como
$1
está entre aspas duplas, os curingas não são um problema.Os
../
links simbólicos e podem obscurecer a localização real de um arquivo. Abaixo, são mostrados testes para determinar se o arquivo está realmente, e não apenas aparentemente, no caminho que queremos.Sistemas mais recentes: usando
realpath
Quanto a descobrir se o arquivo é realmente se o arquivo está realmente abaixo
/home/charlesingalls/
ou não, você pode usarrealpath
:As execuções acima
exit 1
se o arquivo especificado pelo$1
é em qualquer lugar que não seja sob o diretório/home/charlesingalls/
.realpath
canoniza todo o caminho, eliminando os links simbólicos e../
.realpath
faz parte do GNU coreutils e deve estar disponível em qualquer sistema Linux.realpath
requer GNU coreutils 8.15 (janeiro de 2012) ou melhor .Exemplos
Para demonstrar como o realpath segue
../
para determinar a localização real de um arquivo (por exemplo, a-q
opção grep é omitida para que a saída real do grep seja visível):Para demonstrar como segue os links simbólicos:
Sistemas mais antigos: usando
readlink -e
readlink
também é capaz de cononicalizar um caminho, seguindo os links simbólicos e../
:Usando os mesmos arquivos de exemplo:
Além de estar disponível em sistemas GNU mais antigos, versões do
readlink
estão disponíveis no BSD.fonte
coreutils
não terrealpath
-f
("todos, exceto o último componente deve existir") e os exemplos usam-e
("todos os componentes devem existir"), o que é um pouco confuso.rm
age sobre o argumento em si, não sobre seu alvo.grep -q
para evitar agrep
saída de linhas correspondentes. Você ainda obtém o status de saída&&
e||
ainda trabalha exatamente como está acostumado.Se você deseja proibir completamente os caminhos, a maneira mais simples é testar se a variável contém uma barra (
/
). Na festança:Isso bloqueará todos os caminhos, inclusive
foo/bar
. Você poderia testar..
, mas isso deixaria a possibilidade de links simbólicos apontarem para diretórios fora do caminho de destino.Se você deseja permitir apenas a exclusão de um único arquivo, acho que não deveria estar usando
rm -r
.Além disso, dependendo do que você está fazendo, você pode usar as permissões de arquivo do sistema para permitir apenas a exclusão de arquivos que o usuário pode excluir por conta própria. Algo assim:
Embora, como o @Gilles comentou, isso tenha um problema de citação: falhará se
$1
contiver uma única citação; portanto, a variável deve ser testada para isso primeiro (com, por exemplo,if [[ "$1" = *\'* ]] ; then fail...
ou melhor, colocando na lista branca um conjunto sensível de caracteres) ou o nome do arquivo passado uma variável de ambiente com por exemplofonte
su
comando está quebrado porque a citação está errada. Você está executando um comando com o argumento interpolado como um snippet de shell. Por exemplo, se o argumento for$(touch foo)
, seu código será executadotouch foo
.'
by'\''
) ou passá-la por outro canal, como uma variável de ambiente (que é o que eu faria aquifile_to_remove="$1" su -c 'rm "/home/charlesingalls/$file_to_remove"'
:). Acontece que o nome do arquivo deve ser um nome de host virtual, portanto, rejeitar todos os caracteres especiais também seria aceitável aqui.