La citation des noms de fichiers est-elle une sécurité suffisante pour exécuter `xargs sudo rm -rf`?

10

J'ai écrit un script qui supprime tout 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 en tant que tâche cron.

Le raisonnement est le suivant:

  1. Obtenir une liste de tous les fichiers en utilisant ls -1(pour que j'obtienne un fichier par ligne);

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

  3. Depuis lsimprime des chemins relatifs, utilisez la xargs printfchose pour ajouter le chemin du dossier et en faire un chemin absolu;

  4. Envoyez-les à l' sudo rm -rfaide xargs.

Tout le monde a accès à ce dossier, donc 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 -rfest 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 super effrayant sudo rm -rf /.

EDIT: Mon erreur, les noms de fichiers ne peuvent pas contenir /, donc ce problème spécifique ne se produirait pas, mais la question de savoir s'il existe ou non d'autres risques persiste.

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 guillemets dans le nom de fichier, peut-être.

Mon script est-il sûr?


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

Pedro A
la source
3
Avez-vous pensé à faire quelque chose comme ça printf -- '%s\0' /path/to/some/folder/* | head -zn -2 | xargs -0 rm?
Steeldriver
Peut- /on créer un fichier avec le personnage dans le nom J'essaie de réaliser ceci ici
George Udosen
3
@George Non, un nom de fichier ne peut pas contenir de barre oblique.
wjandrea
Alors quand OP dit une personne intelligente, je me demandais ...
George Udosen
6
Tout simplement parce que vous analysez la lssortie, c'est déjà une commande mal écrite, même avec des guillemets. lsutilise également les paramètres régionaux pour l'ordre de tri, je pense, donc je ne vois pas à quoi sert la headsuppression des 2 derniers (sauf si vous essayez de vous débarrasser .et ..quels iirc ne sont pas autorisés comme arguments de rmtoute façon. Utilisez simplement find /path/to/folder -type f delete. Et non sudosi vous exécutez depuis cron - cron est déjà au niveau racine
Sergiy Kolodyazhnyy

Réponses:

10

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

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

Ainsi, votre approche ne fonctionnera certainement pas dans de nombreux cas, comme vous pouvez l'imaginer, par exemple, gère-t-elle une nouvelle ligne ( \n) dans le nom de 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 traitez des noms de fichiers, essayez de tirer parti de la sortie séparée par NUL fournie par presque tous les outils GNU qui fonctionnent avec ces données
  • Faites attention lors de la tuyauterie, assurez-vous que les deux programmes peuvent comprendre les séparations NUL
  • Chaque fois que vous invoquez xargs, voyez si vous pouvez vous en sortir find ... -exec; dans la plupart des cas, vous serez bien avec juste findseul

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

heemayl
la source
Merci pour votre réponse :) Je pense que vous devriez citer le commentaire de Steeldriver au lieu de simplement le mentionner (car les commentaires ne sont pas permanents). Je vais également jeter un coup d'œil find, merci pour la suggestion.
Pedro A
J'ai une question cependant: je ne comprends pas ce que vous entendez par "votre approche ne fonctionnera certainement pas dans de nombreux cas comme vous pouvez l'imaginer" - "ne fonctionne pas" comme dans "pas sûr" ou "pas dangereux"? Parce que "votre approche" se réfère à moi et non à l'utilisateur malveillant, et vos déclarations antérieures sont en ma faveur, donc je suis confus.
Pedro A
@PedroA You are you :) Comme je l'ai dit, comme tous les caractères sont valables à l'exception des deux mentionnés, vous devriez être en mesure d'imaginer les nombreux cas où votre lsapproche d'analyse échouerait. Par exemple, avez-vous pris en compte une nouvelle ligne dans le nom de fichier?
heemayl
Oh, une nouvelle ligne dans le nom de fichier ... Je n'y avais pas pensé. Si cela ne vous dérange pas d'ajouter cela à votre réponse aussi :) Désolée de demander, mais que fait head -z-elle? Cela semble ridicule, mais je n'en ai pas manni infodans mon conteneur CoreOS linux ... Impossible de trouver sur Internet non plus. Je reçoishead: invalid option 'z'
Pedro A
@PedroA Newline n'est qu'un cas, il y en a beaucoup comme vous l'avez peut-être deviné . Vous avez besoin de GNU head(livré avec GNU coreutils). Voici la version en ligne: manpages.ubuntu.com/manpages/xenial/man1/head.1.html
heemayl
3

xargs prend en charge certains guillemets: avec des guillemets simples, des guillemets doubles ou une barre oblique inverse qui lui permet d'accepter des arguments arbitraires¹, mais avec une syntaxe différente de la syntaxe des guillemets de type Bourne.

L'implémentation GNU de lsas trouvée sur Ubuntu n'a pas de mode de citation compatible avec le xargsformat d'entrée.

Il ls --quoting-style=shell-alwaysest compatible avec les shells ksh93, bash et zsh citant la syntaxe, mais uniquement lorsque la sortie de lsest interprétée par le shell dans les mêmes paramètres régionaux que lslors de sa sortie. De plus, certains paramètres régionaux, comme ceux utilisant BIG5, BIG5-HKSCS, GBK ou GB18030, doivent être évités.

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'avantages sur:

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

Le seul cas où cela devient utile est lorsque vous souhaitez utiliser l' -toption de lstrier les fichiers par mtime / atime / ctime ou -S/ -V. Mais même alors, vous pourriez aussi bien utiliser zsh:

files=(*(Nom))

par exemple pour trier les fichiers par mtime (utiliser oLpour -Set npour -V).

Pour supprimer tous les fichiers réguliers, sauf les deux plus récemment modifiés:

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

¹ il y a encore quelques limitations de longueur (par execve()et dans certaines xargsimplémentations non GNU beaucoup plus arbitraires), et certaines xargsimplémentations non GNU obstrueront l'entrée qui contient des séquences d'octets ne formant pas des caractères valides.

Stéphane Chazelas
la source