Достаточно ли защищено цитирование имен файлов для запуска `xargs sudo rm -rf`?

10

Я написал скрипт, который удаляет все, кроме двух последних файлов в папке:

#!/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

Этот скрипт будет выполняться как задание cron каждый день.

Аргументация следующая:

  1. Получить список всех используемых файлов ls -1(так что я получаю один файл на строку);

  2. Удалите последние два из списка, используя head -n -2;

  3. Поскольку lsпечатает относительные пути, используйте эту xargs printfвещь, чтобы добавить путь к папке и сделать ее абсолютным путем;

  4. Отправь их в sudo rm -rfпользование xargs.

Каждый имеет доступ к этой папке, поэтому любой может создавать и удалять любые файлы в этой папке.

Проблема в том, что sudo rm -rf это страшно. xargs sudo rm -rfневероятно страшно

Я хочу быть уверен, что никто не сможет повредить другие папки / системы, создав умные файлы для удаления (случайно или намеренно). Я не знаю, что-то умное, как:

file with / spaces.txt

что может привести к супер страшно sudo rm -rf /.

РЕДАКТИРОВАТЬ: Моя ошибка, имена файлов не могут содержать /, так что эта конкретная проблема не возникнет, но вопрос о том, есть ли другие риски все еще остается.

Вот почему я использую --quoting-style=shell-always, это должно предотвратить любые уловки с файлами с пробелами. Но теперь я задаюсь вопросом, может ли кто-нибудь быть более умным с пробелами и кавычками в имени файла, возможно.

Мой сценарий безопасен?


Примечание: мне нужно, sudoпотому что я обращаюсь к папке удаленно (с подключенного сетевого диска с помощью mount), и я не могу заставить его работать без sudo.

Педро А
источник
3
Рассматривали ли вы сделать что-то вроде printf -- '%s\0' /path/to/some/folder/* | head -zn -2 | xargs -0 rm?
Steeldriver
Можно ли создать файл с символом /в имени? Я пытаюсь добиться этого здесь
Джордж Удосен,
3
@ Джордж Нет, имя файла не может содержать косую черту.
wjandrea
Поэтому, когда ОП говорит умный человек, мне было интересно ...
Джордж Удосен
6
Просто потому, что вы анализируете lsвывод, это уже плохо написанная команда, даже с кавычками. lsя также думаю, что он также использует локаль для порядка сортировки, поэтому я не понимаю, какова цель headудаления последних 2 (если только вы не пытаетесь избавиться .и от того, ..что iirc не разрешено использовать в качестве аргументов в rmлюбом случае. Просто используйте find /path/to/folder -type f delete. И нет, sudoесли вы запускаете из cron - cron уже на корневом уровне
Сергей Колодяжный,

Ответы:

10

В Linux любой символ является допустимым символом имени файла, кроме:

  • \0 (ASCII NUL): используется для завершения строки в C
  • / (косая черта): используется для разделения пути

Итак, ваш подход определенно не будет работать во многих случаях, как вы можете себе представить, например, обрабатывает ли он символ новой строки ( \n) в имени файла? ( Подсказка: нет ).

Несколько заметок:

  • Не разбирайся ls; использовать специальные инструменты (для большинства случаев есть хотя бы один)
  • При работе с именами файлов старайтесь использовать вывод, разделенный NUL, предоставляемый почти всеми инструментами GNU, работающими с такими данными.
  • Будьте осторожны при прокладке труб, убедитесь, что обе программы могут понимать разделение NUL
  • Всякий раз, когда вы вызываете xargs, посмотрите, сможете ли вы сойти с рук find ... -exec; в большинстве случаев вам будет хорошо только с findодним

Я думаю, что это поможет вам сейчас. Steeldriver уже предоставил идею NUL в комментариях ( printf -- '%s\0' /path/to/some/folder/* | head -zn -2 | xargs -0 rm), используйте это как отправную точку.

heemayl
источник
Спасибо за ваш ответ :) Я думаю, что вы должны цитировать комментарий Steeldriver, а не просто упоминать (так как комментарии не являются постоянными). Я тоже посмотрю find, спасибо за предложение.
Педро А
У меня есть один вопрос: я не понимаю, что вы подразумеваете под «ваш подход не будет определенно работать во многих случаях, как вы можете себе представить» - «не работает», как в «небезопасно» или «не небезопасно»? Потому что «ваш подход» относится ко мне, а не к злоумышленнику, и ваши предыдущие заявления в мою пользу, поэтому я в замешательстве.
Педро А
@PedroA Вы - вы :) Как я уже сказал, поскольку все символы действительны, кроме упомянутых двух, вы сможете представить многочисленные случаи, когда ваш lsподход к синтаксическому анализу потерпит неудачу, например, учли ли вы новую строку в имени файла?
Heemayl
О, новая строка в имени файла ... Я не думал об этом. Если вы не против добавить это и в свой ответ :) Также, извините, спрашиваю, но что же head -zделать? Звучит смешно, но у меня нет manни infoв моем контейнере CoreOS Linux ... Не могу найти в Интернете. Я получаюhead: invalid option 'z'
Педро
@PedroA Newline - это всего лишь один случай, есть много таких, о которых вы могли догадаться . Вам нужен GNU head(поставляется с GNU coreutils). Вот онлайн-версия: manpages.ubuntu.com/manpages/xenial/man1/head.1.html
heemayl
3

xargs поддерживает некоторые кавычки: с одинарными кавычками, двойными кавычками или обратной косой чертой, что позволяет ему принимать произвольные аргументы¹, но с синтаксисом, который отличается от синтаксиса кавычек, подобного оболочкам типа Борна.

Реализация GNU, lsкак в Ubuntu, не имеет режима цитирования, совместимого с xargsформатом ввода.

Его ls --quoting-style=shell-alwaysсовместим с ksh93, Баш и ЗШ оболочек со ссылкой синтаксис, но только тогда , когда выходной сигнал lsинтерпретируется оболочки в той же местности , как lsбыло , когда это выход его. Кроме того, следует избегать использования некоторых локалей, таких как BIG5, BIG5-HKSCS, GBK или GB18030.

Таким образом, с этими оболочками вы можете сделать:

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

Но это имеет небольшое преимущество перед:

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

Единственный случай, когда это становится полезным, это когда вы хотите использовать -tопцию lsсортировки файлов по mtime / atime / ctime или -S/ -V. Но даже тогда вы можете использовать zshs:

files=(*(Nom))

например, для сортировки файлов по mtime (используйте oLдля -Sи nдля -V).

Чтобы удалить все, кроме двух самых последних измененных обычных файлов:

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

Still все еще существуют некоторые ограничения по длине ( execve()и в некоторых реализациях не-GNU xargsнамного меньше произвольных), и некоторые реализации не-GNU xargsбудут задыхаться от ввода, который содержит последовательности байтов, не образующих допустимых символов.

Стефан Шазелас
источник