Qual é o problema
Primeiro, como em muitos utilitários, você terá um problema com os nomes de arquivo começando com -
. Enquanto estiver em:
sh -c 'inline sh script here' other args
Os outros argumentos são passados para o inline sh script
; com o perl
equivalente
perl -e 'inline perl script here' other args
Os outros argumentos são verificados para obter mais opções para perl primeiro, não para o script embutido. Então, por exemplo, se houver um arquivo chamado -eBEGIN{do something evil}
no diretório atual,
perl -ne 'inline perl script here;' *
(com ou sem -n
) fará algo mau.
Como em outros utilitários, a solução alternativa é usar o marcador de fim de opções ( --
):
perl -ne 'inline perl script here;' -- *
Mas, mesmo assim, ainda é perigoso e isso depende do <>
operador usado por -n
/ -p
.
O problema é explicado na perldoc perlop
documentação.
Esse operador especial é usado para ler uma linha (um registro, registros sendo linhas por padrão) da entrada, onde essa entrada é proveniente de cada um dos argumentos passados por sua vez @ARGV
.
Em:
perl -pe '' a b
-p
implica um while (<>)
loop em torno do código (aqui vazio).
<>
abrirá primeiro a
, lerá os registros uma linha de cada vez até que o arquivo esteja esgotado e, em seguida, abrirá b
...
O problema é que, para abrir o arquivo, ele usa a primeira forma insegura de open
:
open ARGV, "the file as provided"
Com esse formulário, se o argumento for
"> afile"
, abre afile
no modo de escrita,
"cmd|"
, ele roda cmd
e lê sua saída.
"|cmd"
, você tem um fluxo aberto para gravação na entrada de cmd
.
Então, por exemplo:
perl -pe '' 'uname|'
Não gera o conteúdo do arquivo chamado uname|
(um nome de arquivo perfeitamente válido, btw), mas a saída do uname
comando.
Se você estiver executando:
perl -ne 'something' -- *
E alguém criou um arquivo chamado rm -rf "$HOME"|
(novamente um nome de arquivo perfeitamente válido) no diretório atual (por exemplo, porque esse diretório já foi gravável por outras pessoas, ou você extraiu um arquivo desonesto ou executou algum comando desonesto, ou outra vulnerabilidade em outro software foi explorada), então você está com um grande problema. As áreas em que é importante estar ciente desse problema são as ferramentas que processam arquivos automaticamente em áreas públicas como /tmp
(ou ferramentas que podem ser chamadas por essas ferramentas).
Arquivos chamados > foo
, foo|
, |foo
são um problema. Porém, em menor grau < foo
e foo
com caracteres de espaçamento ASCII iniciais ou finais (incluindo espaço, tab, nova linha, cr ...), isso significa que esses arquivos não serão processados ou o errado.
ǖ
Lembre-se também de que alguns caracteres em alguns conjuntos de caracteres de vários bytes (como no BIG5-HKSCS) terminam no byte 0x7c, a codificação de |
.
$ printf ǖ | iconv -t BIG5-HKSCS | od -tx1 -tc
0000000 88 7c
210 |
0000002
Portanto, nas localidades que usam esse conjunto de caracteres,
perl -pe '' ./nǖ
Iria tentar executar o ./n\x88
comando como perl
se não tentar interpretar o nome do arquivo na localidade do usuário!
Como corrigir / contornar
AFAIK, não há nada que você possa fazer para alterar esse comportamento padrão inseguro de perl
uma vez por todas em todo o sistema.
Primeiro, o problema ocorre apenas com caracteres no início e no final do nome do arquivo. Então, enquanto perl -ne '' *
ouperl -ne '' *.txt
é um problema,
perl -ne 'some code' ./*.txt
não é porque todos os argumentos agora começar com ./
e terminam em .txt
(por isso não -
, <
, >
, |
, espaço ...). De modo mais geral, é uma boa idéia para prefixo globs com ./
. Isso também evita problemas com arquivos chamados -
ou iniciados com-
muitos outros utilitários (e aqui, isso significa que você não precisa --
mais do marcador de fim de opções ( )).
Usar -T
para ativar o taint
modo ajuda até certo ponto. Ele irá abortar o comando se tal arquivo malicioso for encontrado (apenas para o >
e|
casos , não <
ou espaço em branco).
Isso é útil ao usar esses comandos de maneira interativa, alertando que há algo desonesto. No entanto, isso pode não ser desejável ao fazer algum processamento automático, pois isso significa que alguém pode fazer isso. processamento falhe apenas criando um arquivo.
Se você deseja processar todos os arquivos, independentemente do nome, pode usar o ARGV::readonly
perl
módulo no CPAN (infelizmente, normalmente não é instalado por padrão). Esse é um módulo muito curto que faz:
sub import{
# Tom Christiansen in Message-ID: <24692.1217339882@chthon>
# reccomends essentially the following:
for (@ARGV){
s/^(\s+)/.\/$1/; # leading whitespace preserved
s/^/< /; # force open for input
$_.=qq/\0/; # trailing whitespace preserved & pipes forbidden
};
};
Basicamente, ele limpa o @ARGV, transformando, " foo|"
por exemplo, em "< ./ foo|\0"
.
Você pode fazer o mesmo em uma BEGIN
declaração em seu perl -n/-p
comando:
perl -pe 'BEGIN{$_.="\0" for @ARGV} your code here' ./*
Aqui, simplificamos a suposição que ./
está sendo usada.
Um efeito colateral disso (e ARGV::readonly
) é que$ARGV
em your code here
mostra que arrasta caractere NUL.
Atualização 2015-06-03
perl
A v5.21.5 e superior têm um novo <<>>
operador que se comporta como <>
exceto que ele não fará esse processamento especial. Os argumentos serão considerados apenas como nomes de arquivo. Portanto, com essas versões, agora você pode escrever:
perl -e 'while(<<>>){ ...;}' -- *
(não esqueça --
ou use./*
) sem medo de sobrescrever arquivos ou executar comandos inesperados.
-n
/ -p
Ainda usam o perigoso <>
forma embora. E tenha em atenção que os links simbólicos ainda estão sendo seguidos, de modo que não significa necessariamente que seja seguro usá-lo em diretórios não confiáveis.