Безопасный bash файл конфигурации - PullRequest
0 голосов
/ 03 марта 2020

Я хотел бы настроить сценарий bash из файла конфигурации.

Я хотел бы иметь своего рода безопасное и надежное решение.

  • [надежный]: не пытайтесь быть слишком умным (и глючным) с регулярным выражением.

Так что наиболее «распространенное» решение, которое использует исходный файл конфигурации, не подходит для мне, потому что файл конфигурации ниже будет делать забавные вещи:

touch your_computer_is_hacked
foo=bar

Какова лучшая практика для файлов конфигурации? Так я могу иметь все значения в ассоциативном массиве? Меня не волнует фактический формат (bash script, yaml, ini, xml, javascript), но он должен быть полностью указан, поэтому, если я хочу добавить пробел / символ новой строки или 101 символ переменной foo, следует указать, как это сделать.

1 Ответ

0 голосов
/ 27 марта 2020

Я думаю, что вам лучше всего написать source <(...), где ... - это команда, которая читает из стандартного ввода и записывает желаемые сопоставления. Таким образом, ... может быть Perl сценарием или Python сценарием или еще чем-то, что проще, чем делать это в чистом Bash.

В нижней части этого ответа приведен простой пример того, что удовлетворяет вашим требованиям. Он создает функцию с именем source-assoc-array, которая заполняет ассоциативный массив, указанный вызывающей стороной (которую необходимо предварительно объявить вызывающей стороне), используя информацию, считанную из стандартного ввода (которая, в типичных случаях, будет перенаправлена ​​из файла). Формат для передачи на стандартный ввод выглядит следующим образом:

  • Одно назначение на строку. Нет пустых строк, нет строк комментариев, нет назначений, охватывающих несколько строк, нет команд, кроме назначений.
  • Каждое назначение принимает форму <i>key</i>=<i>value</i>. Пробелы не допускаются, за исключением того, что <i>value</i> может содержать пробелы.
  • Ключ - это одна или несколько букв ASCII, цифр и подчеркиваний, не начинающихся с di git. (Это правила для Bash имен переменных.)
  • Значение равно нулю или более ненулевых байтов. (Bash слова не могут содержать нулевые байты , поэтому это неизбежное ограничение.) Символы новой строки представляются как \n; обратные слеши представлены как \\; все остальные байты представляются как они сами.
  • Так, например, строка foo='bar\n\\\nbaz' переводится в arr[foo]=$'\'bar\n\\\nbaz\'', если arr является именем ассоциативного массива.
  • Функция прекращает работу с сообщением об ошибке, если встречает любую строку, которая не соответствует вышеуказанным правилам.

Вы можете легко сделать формат более удобным для пользователя, поддерживая различные функции, такие как пустые строки, комментарии , начальные и конечные пробелы (включая конечные возвраты каретки, для поддержки Windows окончаний строк), пробелы вокруг = и другие escape-последовательности, кроме \n и \\.

Вы также можете захотеть продумать вашу модель угрозы немного больше. Например, это проблема, если пользователь помещает в файл странные символы (возврат каретки, символы справа налево, управляющие символы и т. Д. c.), Из-за которых системному администратору трудно точно понять, что такое сопоставления будет? (Приведенная ниже функция выдает сообщения об ошибках, которые не включают строку проблемати c - только номер строки - по этой причине.) Пользователь может обмануть ваш скрипт, чтобы прочитать массивный файл, который заставляет Bash не хватает памяти или чтение файла устройства, например /dev/random? И, конечно, любая подобная конфигурация автоматически означает, что ввод пользователя должен повлиять на последующее поведение вашего скрипта; вам нужно убедиться, что ни один из возможных эффектов не является таким, который вы считаете вредным.


Без дальнейших действий, вот функция:

source-assoc-array() {
  if [[ "$1" = -- ]] ; then
    shift
  fi
  if [[ "$#" != 1 ]] ; then
    echo 'Usage: source-assoc-array ARRAY_NAME < FILE' >&2
    return 1
  fi
  if ! [[ "$(declare -p -- "$1" 2>&1)" == 'declare -A '* ]] ; then
    printf '[%s] is not an associative array.\n' "$1" >&2
    return 1
  fi
  source <(
    perl -w -e '
      my $array_name = $ARGV[0];
      while (<STDIN>) {
        unless (m/^([^=]*)=(.*)$/) {
          die "[source-assoc-array] Error on line $.: No equals sign.\n";
        }
        my ($key, $val) = ($1, $2);
        unless ($key =~ m/^[A-Za-z_][A-Za-z0-9_]*$/) {
          die "[source-assoc-array] Error on line $.: Invalid key.\n";
        }
        if ($val =~ m/\0/) {
          die "[source-assoc-array] Error on line $.: Null byte.\n";
        }
        unless ($val =~ m/^(?:[^\\]|\\[\\n])*$/) {
          die "[source-assoc-array] Error on line $.: Unescaped backslash.\n";
        }
        $val =~ s/\x27/\\\x27/g; # escape single-quotes
        print "$array_name\[$key]=\$\x27$val\x27\n";
      }
    ' -- "$1" \
    || echo 'return 1'
  )
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...