Solução alternativa potencial para inotifywait não pode produzir saída delimitada por NUL

4

Atualmente, estou escrevendo um script bash que usa inotifywait para executar determinadas ações em um usuário lista fornecida de arquivos e diretórios.

Chegou ao meu conhecimento que, ao contrário de muitas ferramentas de shell, inotifywait não é possível para separar registros de saída com inotifywait . Isso deixa a possibilidade de ataques de injeção com nomes de arquivos específicos, mas legais (contendo novas linhas).

Eu gostaria de contornar isso para garantir que meu script não introduza vulnerabilidades desnecessárias. Minha abordagem é a seguinte:

  • Garanta que todos os arquivos / caminhos passados por inotifywait assistam à remoção de barras invertidas
  • Formate --format "%e %w%f//" output com <EVENT LIST> <FILE PATH>// para produzir a saída da seguinte forma:
    • inotifywait
  • Pipe // output para sed; qualquer while read encontrado nas extremidades das linhas com ${watchlist[]}
  • Use o bash $events loop para ler os registros $filepath -separados
  • Isso significa que, após o primeiro registro, todos os registros a seguir terão uma nova linha extra. Isso é retirado
  • Cada registro pode ser dividido no primeiro espaço - antes de o espaço ser a lista de eventos (separado por vírgulas conforme inotifywait) - e após o espaço, o caminho completo associado ao evento
#!/bin/bash

shopt -s extglob
watchlist=("${@}")

# Remove trailing slashes from any watchlist elements
watchlist=("${watchlist[@]%%+(/)}")

# Reduce multiple consecutive slashes to singles as per @meuh
watchlist=("${watchlist[@]//+(\/)/\/}")

printf -vnewline "\n"

inotifywait -qrm "${watchlist[@]}" --format "%e %w%f//" | \
    sed -u 's%//$%\x00%' | \
    while IFS= read -r -d '' line; do
        line="${line#${newline}}"
        events="${line%% *}"
        filepath="${line#* }"
        printf "events=%s\nfilepath=%q\n" "$events" "$filepath"
    done

Tanto quanto eu posso dizer, isso lida com nomes de arquivos / caminhos contendo caracteres engraçados - espaços, novas linhas, citações, etc. Mas parece um kludge bastante deselegante.

Para os fins desta questão, a matriz inotify_add_watch() é copiada dos parâmetros da linha de comando, mas essa matriz pode ser criada de outra forma e pode conter caracteres "engraçados".

  1. Existe algum caminho malicioso que possa quebrar isso? ou seja, tornar o conteúdo das variáveis %code% e %code% incorretas para qualquer evento?

  2. Se esta for à prova d'água, existe alguma maneira mais limpa de fazer isso?

Observação: sei que posso escrever facilmente um programa para ligar para %code% e amigos para contornar isso. Mas por enquanto, devido a outras dependências, estou trabalhando no bash.

Estou em conflito se devo postar isso aqui ou codereview.SE ou até mesmo o principal so.SE.

    
por Digital Trauma 06.05.2018 / 00:50

2 respostas

1

Você precisaria limpar watchlist para substituir qualquer // por / . Considere um diretório chamado \nabc (onde \n é uma nova linha):

$ mkdir t
$ mkdir t/$'\nabc'
$ touch t/$'\nabc'/x

Se passar o diretório t//$'\nabc' , você verá a saída com bogus // no final das linhas:

$ inotifywait -m -r t//$'\nabc' --format "%e %w%f//" 
Setting up watches.  Beware: since -r was given, this may take a while!
Watches established.
OPEN t//
abc/x//
ATTRIB t//
abc/x//
CLOSE_WRITE,CLOSE t//
abc/x//

Observe que você também pode usar -c em vez de --format para obter a saída em estilo csv , que duplica os nomes de arquivos com novas linhas, mas é mais difícil de analisar e, no meu caso, despejos no exemplo acima.

Exemplo de saída para -c e touch t/$'new\nfile' :

t/,CREATE,"new
file"
    
por 06.05.2018 / 10:04
1

fork que suporta saída delimitada por NUL, Você pode usá-lo assim:

#!/bin/bash

shopt -s extglob
watchlist=("${@}")

# Remove trailing slashes from any watchlist elements
watchlist=("${watchlist[@]%%+(/)}")

printf -vnewline "\n"

inotifywait -qrm "${watchlist[@]}" --format "%e %w%f%0" | \
while IFS= read -r -d '' line; do
    events="${line%% *}"
    filepath="${line#* }"
    printf "events=%s\nfilepath=%q\n" "$events" "$filepath"
done

Note que esta versão não adiciona nova linha por padrão quando --format é usado.

    
por 06.05.2018 / 13:20