Ответ 1
Примечание. Следующее относится ко всем основным POSIX-совместимым оболочкам, за исключением случаев, когда указано иначе: bash
, dash
, ksh
и zsh
. (dash
, оболочка Almquist Debian, является оболочкой по умолчанию (sh
) на дистрибутивах на базе Debian, таких как Ubuntu).
-
unset
имеет свое первоначальное значение - встроенный, который может деинфицировать функции оболочки с помощью параметра-f
- это ключ для обеспечения того, что любое другое ключевое слово, команда или встроенный ключ оболочки имеет свое первоначальное значение.- Начиная с немодифицированного
unset
, вы можете обеспечить немодифицированныйshopt
и/илиcommand
, и вместе они могут использоваться для обхода или деинфиниции любых псевдонимов или функций оболочки, которые могут теневые ключевые слова оболочки, встроенные функции и внешние утилиты. - В качестве альтернативы неопределенным функциям
command
можно использовать для обхода их, включая те, которые могут быть определены вне вашего кода, через среду.
экспортирующие функции, поскольку поддерживается толькоbash
, является только одним из этих механизмов; разные оболочки имеют разные и могут поддерживать несколько - см. ниже.
- Начиная с немодифицированного
-
Только
dash
,ksh
иbash
, когда в режиме совместимости с POSIX гарантируется, чтоunset
не было переопределено:-
dash
иksh
являются безопасными, поскольку они не позволяют определить функцию с именемunset
, как вы обнаружили, и любую форму псевдонима можно обойти, вызвав как\unset
. -
bash
, когда в режиме совместимости с POSIX вы можете определить функцию с именемunset
, но игнорируете ее при вызовеunset
и всегда выполняете встроенную функцию, как вы позже ее обнаружили.- Учитывая, что режим POSIX совместимости ограничивает набор Bash и изменяет его поведение, обычно не желательно запускать в нем свой Bash код. В в нижней части этого сообщения представлена реализация обходного пути, которую вы предлагаете, которая временно активирует режим совместимости POSIX, чтобы гарантировать, что функция
unset
не определена. li >
- Учитывая, что режим POSIX совместимости ограничивает набор Bash и изменяет его поведение, обычно не желательно запускать в нем свой Bash код. В в нижней части этого сообщения представлена реализация обходного пути, которую вы предлагаете, которая временно активирует режим совместимости POSIX, чтобы гарантировать, что функция
-
-
К сожалению, насколько я знаю, в
zsh
- а также в режимеbash
по умолчанию - существует , чтобы гарантировать, чтоunset
сам hasn 't переопределены, и могут быть другие POSIX-подобные оболочки, которые ведут себя аналогично.- Вызов
\unset
(со ссылкой на любую часть имени) обходит переопределение псевдонимов, но не переопределяет функцию - и отменить то, что вам потребуется оригинальныйunset
: catch 22.
- Вызов
-
Таким образом, без контроля над средой исполнения, вы не можете писать сценарии оболочки, которые полностью защищены от несанкционированного доступа, , если вы не знаете, что ваш код будет выполнен
dash
,ksh
илиbash
(с обходным решением на месте).-
Если вы согласны с тем, что
unset
не был изменен, наиболее надежный подход:-
Используйте
\unset -f
, чтобы гарантировать, чтоunalias
иcommand
немодифицированы (не затенены функцией оболочки:\unset -f unalias command
)- Функции, в отличие от псевдонимов, должны быть явно undefined по имени, но не все оболочки предоставляют механизм для перечисления всех определенных функций, к сожалению (
typeset -f
работает вbash
,ksh
иzsh
, ноdash
не имеет никакого механизма вообще), так что невозможно определить все функции не всегда.
- Функции, в отличие от псевдонимов, должны быть явно undefined по имени, но не все оболочки предоставляют механизм для перечисления всех определенных функций, к сожалению (
-
Используйте
\unalias -a
, чтобы удалить все псевдонимы. -
Затем вызовите все с помощью
command [-p]
, за исключением функций, которые вы определили. При вызове внешних утилит используйте явные пути, когда это возможно, и/или, в случае стандартных утилит, используйтеcommand -p
, который использует минимальное определение$PATH
, ограниченное стандартными местоположениями (запуститеcommand -p getconf PATH
, чтобы увидеть это определение).
-
-
Дополнительная информация:
-
В POSIX, цитируя любую часть имени команды (например,
\unset
), обходит любую форму псевдонима или форму ключевого слова (зарезервированное слово в POSIX иzsh
parlance) этим именем - но не функциями оболочки. -
В POSIX,
unalias -a
undefine all aliases. Нет эквивалента, совместимой с POSIX командой для определения всех функций.- Предостережение: более старые версии
zsh
не поддерживают-a
; по крайней мере,v5.0.8
, однако, они делают.
- Предостережение: более старые версии
-
Встроенный
command
может использоваться для обхода ключевых слов, псевдонимов, функций вbash
,dash
иksh
- другими словами:command
выполняет только встроенные и внешние утилиты. Напротив,zsh
по умолчанию также обходит встроенные функции; чтобы сделатьzsh
выполнение встроенных команд, используйтеoptions[POSIX_BUILTINS]=on
. -
Для выполнения внешних утилит с именем
<name>
только для всех оболочек можно использовать следующее:"$(command which <name>)" ...
Обратите внимание: хотяwhich
не является утилитой POSIX, он широко доступен на современных Unix-подобных платформах. -
Приоритет командных форм:
-
bash
,zsh
: alias > shell keyword > shell function > встроенная > внешняя утилита -
ksh
,dash
: shell keyword > alias > shell function > встроенная > внешняя утилита - I.e.: В
bash
иzsh
псевдоним может переопределить ключевое слово оболочки, а вksh
иdash
он не может.
-
-
bash
,ksh
иzsh
- но неdash
- все позволяют использовать нестандартную сигнатуру функцииfunction <name> { ...
в качестве альтернативы POSIX-совместимой форме<name>() { ...
.- Синтаксис
function
является обязательным условием для:- гарантируя, что
<name>
сам не подвержен расширению псевдонимов до того, как будет определена функция. - возможность выбрать
<name>
, который также является ключевым словом для оболочки;
обратите внимание, что такую функцию можно вызвать только в кавычковой форме; например,\while
. - (В случае
ksh
, используя синтаксисfunction
, дополнительно подразумевает, что операторыtypeset
создают локальные переменные.)
- гарантируя, что
-
dash
,ksh
иbash
, когда в режиме POSIX дополнительно запрещаются функции именования для специальных встроенных функций (например,unset
,break
,set
,shift
); список специальных встроенных функций, определенных POSIX, можно найти здесь; какdash
иksh
добавить еще несколько, которые нельзя переопределить (например,local
вdash
;typeset
иunalias
вksh
), но обе оболочки имеют дополнительные, неспецифические встроенные которые могут быть переопределены (например,type
).
Обратите внимание, что в случаеksh
указанные правила применяются независимо от того, используется ли синтаксисfunction
или нет.
- Синтаксис
-
Потенциальные источники функций оболочки среды в области вашего кода:
-
Примечание. Самый простой способ защитить это - использовать (немодифицированный)
command
встроенный (вzsh
сoptions[POSIX_BUILTINS]=on
, чтобы предотвратить обход встроенных функций), когда вы хотите позвонить встроенная или внешняя утилита. -
POSIX требует, чтобы script, указанный его абсолютным путем в переменной окружения
ENV
, был получен для интерактивных оболочек (с некоторыми ограничениями - см. спецификация);ksh
иdash
всегда чтят это, тогда какbash
делает это только при вызовеsh
или, в v4.2 +, с--posix
; напротив,zsh
никогда не выполняет эту переменную.- Примечание. Ваш код, запускаемый как script, обычно запускается в неинтерактивной оболочке, но это не гарантируется; например, ваш код может быть получен из интерактивного script, или кто-то может вызвать ваш script с помощью, например,
sh -i
, чтобы заставить интерактивный экземпляр.
- Примечание. Ваш код, запускаемый как script, обычно запускается в неинтерактивной оболочке, но это не гарантируется; например, ваш код может быть получен из интерактивного script, или кто-то может вызвать ваш script с помощью, например,
-
bash
имеет 2 механизма:- Экспорт отдельных функций с помощью
export -f
илиdeclare -fx
(другие оболочки поддерживают только экспортирующие переменные) - Указание полного пути script к источнику при запуске неинтерактивной оболочки в необязательной переменной среды
BASH_ENV
.
- Экспорт отдельных функций с помощью
-
ksh
поддерживает автоматическую загрузку функций через необязательную переменную средыFPATH
: файлы, содержащие определения функций, расположенные в любом каталоге, указанном вFPATH
, неявно и автоматически загружаются.- (
zsh
тоже поддерживаетFPATH
, но для функций автоматической загрузки требуется явный операторautoload <name>
, поэтому, если вы специально не запрашиваете функцию с заданным именем для автоматической загрузки, никакие функции не будут добавлены в ваш оболочки.)
- (
-
zsh
поддерживает сценарии поиска для любого экземпляраzsh
(будь то интерактивного или нет) через его файлы/etc/zshenv
и~/.zhsenv
. -
(
dash
, похоже, не поддерживает какой-либо механизм определения функций через среду.)
-
Обходной путь для bash
: убедитесь, что unset
имеет свое первоначальное значение:
Это обходное решение безопасно, если вы знаете, что bash
будет выполнять ваш script, который, к сожалению, не может быть гарантирован.
Кроме того, поскольку он изменяет среду оболочки (удаление псевдонимов и функций), она не подходит для сценариев, которые предназначены для источника.
Как указано, обычно не желательно запускать ваш код в режиме совместимости с Bash POSIX, но вы можете временно активировать его, чтобы гарантировать, что unset
не затеняется функцией:
#!/bin/bash
# *Temporarily* force Bash into POSIX compatibility mode, where `unset` cannot
# be shadowed, which allows us to undefine any `unset` *function* as well
# as other functions that may shadow crucial commands.
# Note: Fortunately, POSIXLY_CORRECT= works even without `export`, because
# use of `export` is not safe at this point.
# By contrast, a simple assignment cannot be tampered with.
POSIXLY_CORRECT=
# If defined, unset unset() and other functions that may shadow crucial commands.
# Note the \ prefix to ensure that aliases are bypassed.
\unset -f unset unalias read declare
# Remove all aliases.
# (Note that while alias expansion is off by default in scripts, it may
# have been turned on explicitly in a tampered-with environment.)
\unalias -a # Note: After this, \ to bypass aliases is no longer needed.
# Now it is safe to turn POSIX mode back off, so as to reenable all Bash
# features.
unset POSIXLY_CORRECT
# Now UNDEFINE ALL REMAINING FUNCTIONS:
# Note that we do this AFTER switching back from POSIX mode, because
# Bash in its default mode allows defining functions with nonstandard names
# such as `[` or `z?`, and such functions can also only be *unset* while
# in default mode.
# Also note that we needn't worry about keywords `while`, `do` and `done`
# being shadowed by functions, because the only way to invoke such functions
# (which you can only define with the nonstandard `function` keyword) would
# be with `\` (e.g., `\while`).
while read _ _ n; do unset -f "$n"; done < <(declare -F)
# IN THE REST OF THE SCRIPT:
# - It is now safe to call *builtins* as-is.
# - *External utilities* should be invoked:
# - by full path, if feasible
# - and/or, in the case of *standard utilities*, with
# command -p, which uses a minimal $PATH definition that only
# comprises the locations of standard utilities.
# - alternatively, as @jarno suggests, you can redefine your $PATH
# to contain standard locations only, after which you can invoke
# standard utilities by name only, as usual:
# PATH=$(command -p getconf PATH)
# Example command:
# Verify that `unset` now refers to the *builtin*:
type unset
Команда тестирования:
Предположим, что приведенный выше код был сохранен в файле script
в текущем каталоге.
Следующая команда имитирует среду с несанкционированным доступом, где unset
затеняется как псевдонимом, так и функцией, а файл script
является источником, что приводит к тому, что он видит эту функцию и, при интерактивном доступе, расширяет псевдоним тоже:
$ (unset() { echo hi; }; alias unset='echo here'; . ./script)
unset is a shell builtin
type unset
вывод unset is a shell builtin
является доказательством того, что и функция, и алиас, затеняющий встроенный unset
, были деактивированы.