Apakah mengutip nama file keamanan cukup untuk menjalankan `xargs sudo rm -rf`?

10

Saya menulis sebuah skrip yang menghapus semua kecuali dua file terakhir dalam folder:

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

Script ini akan dieksekusi sebagai tugas cron setiap hari.

Alasannya adalah sebagai berikut:

  1. Dapatkan daftar semua file menggunakan ls -1(sehingga saya mendapatkan satu file per baris);

  2. Hapus dua yang terakhir dari daftar menggunakan head -n -2;

  3. Karena lsmencetak path relatif, gunakan xargs printfhal itu untuk menambahkan path folder dan menjadikannya path absolut;

  4. Kirim mereka untuk sudo rm -rfmenggunakan xargs.

Setiap orang memiliki akses ke folder ini, jadi siapa pun dapat membuat dan menghapus file apa pun di folder ini.

Masalahnya adalah: sudo rm -rf menakutkan. xargs sudo rm -rfsangat menakutkan.

Saya ingin memastikan bahwa tidak ada yang dapat merusak folder / sistem lain dengan membuat file pintar untuk dihapus (baik secara tidak sengaja atau sengaja). Saya tidak tahu, sesuatu yang pintar seperti:

file with / spaces.txt

yang bisa menghasilkan super menakutkan sudo rm -rf /.

EDIT: Kesalahan saya, nama file tidak bisa mengandung /, jadi masalah khusus ini tidak akan terjadi, tetapi pertanyaan tentang apakah masih ada risiko lain.

Inilah sebabnya saya menggunakan --quoting-style=shell-always, ini harus mencegah trik dengan file dengan spasi. Tapi sekarang saya bertanya-tanya apakah seseorang bisa lebih pintar dengan spasi dan kutipan dalam nama file, mungkin.

Apakah skrip saya aman?


Catatan: Saya perlu sudokarena saya mengakses folder dari jarak jauh (dari drive jaringan yang dipetakan menggunakan mount), dan saya tidak bisa membuatnya bekerja tanpa sudo.

Pedro A
sumber
3
Sudahkah Anda mempertimbangkan untuk melakukan sesuatu printf -- '%s\0' /path/to/some/folder/* | head -zn -2 | xargs -0 rm?
steeldriver
Bisakah file dengan karakter /dalam nama dibuat Saya mencoba untuk mencapai ini kembali di sini
George Udosen
3
@ George Tidak, nama file tidak boleh mengandung garis miring.
wjandrea
Jadi ketika OP mengatakan orang pintar saya bertanya-tanya ...
George Udosen
6
Hanya karena Anda mem-parsing lsoutput, ini sudah merupakan perintah yang ditulis dengan buruk, bahkan dengan mengutip. lsjuga menggunakan lokal untuk menyortir pesanan, saya pikir, jadi saya tidak melihat apa tujuan headmenghapus 2 terakhir (kecuali Anda mencoba untuk menyingkirkan .dan ..yang iirc tidak diizinkan sebagai argumen untuk rmtetap. Cukup gunakan find /path/to/folder -type f delete. Dan tidak sudojika Anda lari dari cron - cron sudah di level root
Sergiy Kolodyazhnyy

Jawaban:

10

Di Linux, karakter apa pun adalah nama file yang merupakan karakter kecuali kecuali:

  • \0 (ASCII NUL): seperti yang digunakan untuk terminasi string dalam C
  • / (garis miring): seperti yang digunakan untuk pemisahan jalur

Jadi, pendekatan Anda pasti tidak akan berfungsi dalam banyak kasus seperti yang Anda bayangkan misalnya apakah ia menangani baris baru ( \n) dalam nama file? ( Petunjuk: Tidak ).

Beberapa catatan:

  • Jangan diurai ls; gunakan alat khusus (setidaknya ada satu untuk sebagian besar kasus penggunaan)
  • Ketika berhadapan dengan nama file, cobalah untuk meningkatkan output yang dipisahkan NUL yang disediakan oleh hampir semua alat GNU yang bekerja dengan data tersebut
  • Berhati-hatilah saat pemipaan, pastikan kedua program dapat memahami pemisahan NUL
  • Setiap kali Anda memohon xargs, lihat apakah Anda bisa lolos find ... -exec; dalam banyak kasus, Anda akan baik-baik saja dengan findsendirian

Saya pikir ini akan membuat Anda pergi sekarang. steeldriver sudah memberikan gagasan NUL yang terpisah dalam komentar ( printf -- '%s\0' /path/to/some/folder/* | head -zn -2 | xargs -0 rm), gunakan ini sebagai titik awal.

heemayl
sumber
Terima kasih atas jawaban Anda :) Saya pikir Anda harus mengutip komentar steeldriver alih-alih hanya menyebutkan (karena komentar tidak permanen). Saya akan memeriksanya findjuga, terima kasih atas sarannya.
Pedro A
Saya punya satu pertanyaan: Saya tidak mengerti apa yang Anda maksud dengan "pendekatan Anda tidak akan bekerja dalam banyak kasus seperti yang Anda bayangkan" - "tidak berfungsi" seperti pada "tidak aman" atau "tidak tidak aman"? Karena "pendekatan Anda" merujuk kepada saya dan bukan kepada pengguna jahat, dan pernyataan Anda sebelumnya menguntungkan saya, jadi saya bingung.
Pedro A
@ Pedro Anda adalah Anda :) Seperti yang saya katakan, karena semua karakter valid kecuali dua yang disebutkan, Anda harus dapat membayangkan banyak kasus lspendekatan parsing Anda akan gagal mis. Apakah Anda memperhitungkan baris baru dalam nama file?
heemayl
Oh, baris baru dalam nama file ... Saya tidak memikirkan itu. Jika Anda juga tidak keberatan menambahkannya ke jawaban Anda :) Juga, minta maaf untuk bertanya, tetapi apa fungsinya head -z? Kedengarannya konyol tapi saya tidak punya manatau infodalam linux wadah CoreOS saya ... Tidak dapat menemukan di internet juga. Saya mendapatkanhead: invalid option 'z'
Pedro A
@PedroA Newline hanya satu kasus, ada banyak seperti yang Anda duga . Anda membutuhkan GNU head(dilengkapi dengan GNU coreutils). Inilah versi online: manpages.ubuntu.com/manpages/xenial/man1/head.1.html
heemayl
3

xargs memang mendukung beberapa penawaran: dengan tanda kutip tunggal, tanda kutip ganda atau backslash yang memungkinkannya untuk menerima argumen arbitrer¹, tetapi dengan sintaksis yang berbeda dari sintaks mengutip kerang-kerang Bourne-like.

Implementasi GNU lsseperti yang ditemukan di Ubuntu tidak memiliki mode kutip yang kompatibel dengan xargsformat input.

Ini ls --quoting-style=shell-alwayskompatibel dengan ksh93, bash, dan zsh yang mengutip sintaks, tetapi hanya ketika output lsdiinterpretasikan oleh shell di lokal yang sama seperti lsketika output. Juga, beberapa lokal, seperti yang menggunakan BIG5, BIG5-HKSCS, GBK atau GB18030 harus dihindari.

Jadi dengan cangkang itu, Anda benar-benar dapat melakukan:

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

Tapi itu memiliki sedikit keunggulan dibandingkan:

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

Satu-satunya kasus di mana itu menjadi berguna adalah ketika Anda ingin menggunakan -topsi lsuntuk mengurutkan file berdasarkan mtime / atime / ctime atau -S/ -V. Namun meskipun begitu, Anda mungkin sebaiknya menggunakan zsh:

files=(*(Nom))

misalnya untuk mengurutkan file berdasarkan mtime (gunakan oLuntuk -S, dan nuntuk -V).

Untuk menghapus semua kecuali dua file biasa yang dimodifikasi terakhir:

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

¹ masih ada beberapa batasan panjang (oleh execve()dan dalam beberapa xargsimplementasi non-GNU yang jauh lebih rendah dari yang sewenang-wenang), dan beberapa xargsimplementasi non-GNU akan tersedak input yang berisi urutan byte yang tidak membentuk karakter yang valid.

Stéphane Chazelas
sumber