web-dev-qa-db-fra.com

Est-ce que citer les noms de fichiers est une sécurité suffisante pour exécuter `xargs Sudo rm -rf`?

J'ai écrit un script qui supprime tous sauf les deux derniers fichiers d'un dossier:

#!/bin/bash
ls -1 --quoting-style=Shell-always /path/to/some/folder \
    | head -n -2 \
    | xargs printf -- "'/path/to/some/folder/%s'\n" \
    | xargs Sudo rm -rf

Ce script sera exécuté quotidiennement comme un travail cron.

Le raisonnement est le suivant:

  1. Obtenez une liste de tous les fichiers en utilisant ls -1 (afin que je reçoive un fichier par ligne);

  2. Supprimez les deux derniers de la liste en utilisant head -n -2;

  3. Puisque ls imprime les chemins relatifs, utilisez le xargs printf pour ajouter le chemin du dossier et en faire un chemin absolu;

  4. Envoyez-les à Sudo rm -rf en utilisant xargs.

Tout le monde a accès à ce dossier, ainsi tout le monde peut créer et supprimer tous les fichiers de ce dossier.

Le problème est:Sudo rm -rf est effrayant. xargs Sudo rm -rf est incroyablement effrayant.

Je veux être sûr que personne ne peut endommager d'autres dossiers/systèmes en créant des fichiers intelligents à supprimer (accidentellement ou volontairement). Je ne sais pas, quelque chose d'intelligent comme:

file with / spaces.txt

ce qui pourrait entraîner un très effrayant Sudo rm -rf /.

EDIT: Mon erreur, les noms de fichiers ne peuvent pas contenir /, ce problème spécifique n’aurait donc pas lieu, mais la question de savoir s’il existe ou non d’autres risques subsiste.

C'est pourquoi j'utilise --quoting-style=Shell-always, cela devrait empêcher toute astuce avec des fichiers avec des espaces. Mais maintenant je me demande si quelqu'un pourrait être très intelligent avec des espaces et des citations , peut-être dans le nom du fichier.

Est-ce que mon script est en sécurité?


Remarque: J'ai besoin de Sudo car j'accède au dossier à distance (à partir d'un lecteur réseau mappé à l'aide de mount) et je ne pouvais pas le faire fonctionner sans Sudo.

10
Pedro A

Sous Linux, tout caractère est un caractère de nom de fichier valide sauf:

  • \0 (ASCII NUL): utilisé pour la terminaison de chaîne en C
  • / (barre oblique): utilisé pour la séparation de chemin

Ainsi, votre approche ne fonctionnera certainement pas dans de nombreux cas, comme vous pouvez l'imaginer, par exemple. gère-t-il une nouvelle ligne (\n) dans le nom du fichier? (Indice: Non ).

Quelques notes:

  • Ne pas analyser ls; utiliser des outils dédiés (il y en a au moins un pour la plupart des cas d'utilisation)
  • Lorsque vous utilisez des noms de fichiers, essayez de tirer parti de la sortie séparée de NUL fournie par presque tous les GNU outils qui fonctionnent avec de telles données.
  • Faites attention lors de la tuyauterie, assurez-vous que les deux programmes peuvent comprendre les séparations NUL
  • Chaque fois que vous appelez xargs, voyez si vous pouvez vous en sortir avec find ... -exec; dans la plupart des cas, vous irez bien avec find seul

Je pense que ceux-ci vont vous aider pour le moment. steeldriver a déjà fourni l'idée séparée NUL dans le commentaire (printf -- '%s\0' /path/to/some/folder/* | head -zn -2 | xargs -0 rm), utilisez-la comme point de départ.

10
heemayl

xargssupporte certains guillemets: avec des guillemets simples, des guillemets doubles ou des barres obliques inverses qui lui permettent d'accepter des arguments arbitraires¹, mais avec une syntaxe différente de celle des guillemets de type Bourne.

L'implémentation GNU de lstelle qu'elle est trouvée sur Ubuntu n'a aucun mode de citation compatible avec le format d'entrée xargsname__.

Son ls --quoting-style=Shell-always est compatible avec la syntaxe citant les shells ksh93, bash et zsh, mais uniquement lorsque la sortie de lsest interprétée par le shell dans les mêmes paramètres régionaux que le lslors de sa sortie. De plus, certaines langues, comme celles qui utilisent BIG5, BIG5-HKSCS, GBK ou GB18030 devraient être évitées.

Donc, avec ces coquilles, vous pouvez réellement faire:

typeset -a files
eval "files=($(ls --quoting-style=Shell-always))"
xargs -r0a <(printf '%s\0' "${files[@]:0:3}") ...

Mais cela a peu d’avantage sur:

files=(*(N))                 # zsh
files=(~(N)*)                # ksh93
shopt -s nullglob; files=(*) # bash

Cela devient utile uniquement lorsque vous souhaitez utiliser l'option -t de lspour trier les fichiers par mtime/atime/ctime ou -S/-V. Mais même dans ce cas, vous pourriez aussi bien utiliser zshname __:

files=(*(Nom))

par exemple, pour trier les fichiers par mtime (utilisez oLpour -S et npour -V).

Pour supprimer tous les fichiers normaux les plus récemment modifiés, à l'exception des deux derniers:

rm -f -- *(D.om[3,-1])

¹ il y a toujours des limitations de longueur (par execve() et dans certaines implémentations non-GNU xargsbeaucoup moins élevées), et certaines implémentations non-GNU xargsvont étouffer les entrées contenant des séquences d'octets ne formant pas des caractères valides.

3
Stéphane Chazelas