Quel est le problème
Tout d'abord, comme pour de nombreux utilitaires, vous aurez un problème avec les noms de fichiers commençant par -
. Pendant:
sh -c 'inline sh script here' other args
Les autres arguments sont passés au inline sh script
; avec l' perl
équivalent,
perl -e 'inline perl script here' other args
Les autres arguments sont analysés pour plus d'options pour perl en premier, pas pour le script en ligne. Ainsi, par exemple, s'il y a un fichier appelé -eBEGIN{do something evil}
dans le répertoire courant,
perl -ne 'inline perl script here;' *
(avec ou sans -n
) fera quelque chose de mal.
Comme pour les autres utilitaires, la solution consiste à utiliser le marqueur de fin d'options ( --
):
perl -ne 'inline perl script here;' -- *
Mais même dans ce cas, c'est toujours dangereux et cela dépend de l' <>
opérateur utilisé par -n
/ -p
.
Le problème est expliqué dans la perldoc perlop
documentation.
Cet opérateur spécial est utilisé pour lire une ligne (un enregistrement, les enregistrements étant des lignes par défaut) d'entrée, où cette entrée provient de chacun des arguments transmis à son tour @ARGV
.
Dans:
perl -pe '' a b
-p
implique une while (<>)
boucle autour du code (ici vide).
<>
va d'abord s'ouvrir a
, lire les enregistrements une ligne à la fois jusqu'à ce que le fichier soit épuisé puis s'ouvrir b
...
Le problème est que, pour ouvrir le fichier, il utilise la première forme non sécurisée de open
:
open ARGV, "the file as provided"
Avec ce formulaire, si l'argument est
"> afile"
, il s'ouvre afile
en écriture,
"cmd|"
, il s'exécute cmd
et lit sa sortie.
"|cmd"
, vous avez un flux ouvert pour l'écriture à l'entrée de cmd
.
Ainsi, par exemple:
perl -pe '' 'uname|'
Ne produit pas le contenu du fichier appelé uname|
(un nom de fichier parfaitement valide btw), mais la sortie de la uname
commande.
Si vous courez:
perl -ne 'something' -- *
Et quelqu'un a créé un fichier appelé rm -rf "$HOME"|
(encore une fois un nom de fichier parfaitement valide) dans le répertoire courant (par exemple parce que ce répertoire était autrefois accessible en écriture par d'autres, ou que vous avez extrait une archive douteuse, ou que vous avez exécuté une commande douteuse, ou une autre vulnérabilité dans un autre logiciel a été exploitée), alors vous avez de gros problèmes. Les domaines où il est important d'être conscient de ce problème sont les outils qui traitent les fichiers automatiquement dans les espaces publics comme /tmp
(ou les outils qui peuvent être appelés par de tels outils).
Fichiers appelés > foo
, foo|
, |foo
sont un problème. Mais dans une moindre mesure < foo
et foo
avec des caractères d'espacement ASCII en tête ou en fin (y compris l'espace, la tabulation, la nouvelle ligne, cr ...), cela signifie que ces fichiers ne seront pas traités ou que le mauvais sera.
Sachez également que certains caractères de certains jeux de caractères multi-octets (comme ǖ
dans BIG5-HKSCS) se terminent par l'octet 0x7c, le codage de |
.
$ printf ǖ | iconv -t BIG5-HKSCS | od -tx1 -tc
0000000 88 7c
210 |
0000002
Donc, dans les paramètres régionaux utilisant ce jeu de caractères,
perl -pe '' ./nǖ
J'essaierais d'exécuter la ./n\x88
commande comme perl
je n'essaierais pas d'interpréter ce nom de fichier dans les paramètres régionaux de l'utilisateur!
Comment réparer / contourner
AFAIK, vous ne pouvez rien faire pour changer ce comportement par défaut dangereux perl
une fois pour toutes à l'échelle du système.
Tout d'abord, le problème se produit uniquement avec des caractères au début et à la fin du nom de fichier. Donc, alors que perl -ne '' *
ou perl -ne '' *.txt
sont un problème,
perl -ne 'some code' ./*.txt
est pas parce que tous les arguments commencent maintenant avec ./
et fin en .txt
(donc pas -
, <
, >
, |
, espace ...). Plus généralement, c'est une bonne idée de préfixer les globes avec ./
. Cela évite également les problèmes avec les fichiers appelés -
ou commençant par -
de nombreux autres utilitaires (et ici, cela signifie que vous n'avez plus besoin du --
marqueur de fin d'options ( )).
Utiliser -T
pour activer le taint
mode aide dans une certaine mesure. Il abandonnera la commande si un tel fichier malveillant est rencontré (uniquement pour les cas >
et |
, mais pas pour les <
espaces).
C'est utile lorsque vous utilisez de telles commandes de manière interactive car cela vous avertit qu'il se passe quelque chose de douteux. Cependant, cela peut ne pas être souhaitable lors d'un traitement automatique, car cela signifie que quelqu'un peut faire échouer ce traitement simplement en créant un fichier.
Si vous souhaitez traiter chaque fichier, quel que soit leur nom, vous pouvez utiliser le ARGV::readonly
perl
module sur CPAN (malheureusement généralement pas installé par défaut). C'est un module très court qui fait:
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
};
};
Fondamentalement, il désinfecte @ARGV en le transformant " foo|"
par exemple en "< ./ foo|\0"
.
Vous pouvez faire de même dans une BEGIN
instruction de votre perl -n/-p
commande:
perl -pe 'BEGIN{$_.="\0" for @ARGV} your code here' ./*
Ici, nous le simplifions sur l'hypothèse qui ./
est utilisée.
Un effet secondaire de cela (et ARGV::readonly
) est que $ARGV
dans your code here
montre ce caractère NUL final.
Mise à jour 2015-06-03
perl
Les versions 5.21.5 et supérieures ont un nouvel <<>>
opérateur qui se comporte comme <>
ceci, sauf qu'il ne fera pas ce traitement spécial. Les arguments ne seront considérés que comme des noms de fichiers. Donc avec ces versions, vous pouvez maintenant écrire:
perl -e 'while(<<>>){ ...;}' -- *
(n'oubliez pas le --
ou utilisez-le ./*
cependant) sans craindre d'écraser des fichiers ou d'exécuter des commandes inattendues.
-n
/ -p
Toujours utiliser la dangereuse <>
forme cependant. Et méfiez-vous des liens symboliques sont toujours suivis, ce qui ne signifie pas nécessairement qu'il est sûr d'utiliser dans des répertoires non fiables.
En plus de la réponse de @ Stéphane Chazelas , nous n'avons pas à nous soucier de ce problème si nous utilisons l'
-i
option de ligne de commande:Parce que lors de l'utilisation de l'
-i
option,perl
utilisé stat pour vérifier l'état du fichier avant de le traiter:la source
stat
contrôle et le traitement efficace de perl en cours juste après?stat
. C'est juste-i
pour éditer des fichiers sur place, donc cela n'a pas de sens d'accepter des arguments autres que les chemins de fichiers réels, donc avec-i
, ce traitement spécial n'est pas fait.