Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Модель безопасности

Aura исходит из того, что конфиги собирают из чужого кода (пакеты, шаблоны коллег, скопированные сниппеты), и строит защиту по построению, а не по договорённости.

Capability-модель

По умолчанию манифест не может ничего: ни читать файлы, ни переменные окружения. Права выдаются флагами запуска и не «протекают»:

ФлагРазрешает
--allow-read=<dir>read_file() внутри каталога; пути канонизируются, .. не сбегает (E0311). Относительный путь читается от папки самого манифеста (D37)
--allow-env[=A,B]env() для перечисленных переменных (без списка — все)
--hermeticI/O нет вовсе: env() и read_file() — E0505 в любом модуле; несовместим с --allow-*
--allow-imports-ioраспространить права корня на импортированные модули

Вызов без прав — E0310 с подсказкой, какой флаг добавить.

Герметичный режим

--hermetic — та же шкала с другого конца: вместо выдачи прав он объявляет, что прав нет вовсе. env() и read_file() становятся E0505 где угодно — и в корне, и в импорте, — а сам флаг несовместим с любым --allow-*.

Отличие от простого «не выдавать права» — в том, когда узнаёшь. Запрещённый вызов — это E0310 во время выполнения, на той ветке, которая исполнилась; E0505 — ошибка анализа, поэтому aura check --hermetic доказывает свойство для всего манифеста, ничего не вычисляя. Это и делает его пригодным для гейта в CI:

$ aura check --hermetic manifests/*.aura

CUE, KCL и Starlark герметичны по построению. В Aura это режим — так один репозиторий может держать и манифесты, которым нужно читать окружение, и те, которым нельзя, а CI может требовать строгий вид по каталогам.

Ограниченное вычисление

Герметичность доказывает, что манифест ничего не может взять. Но не доказывает, что он закончится: три вложенных range(1000).map умещаются в 95 байт, проходят check --hermetic и работали бы больше минуты. Поэтому у каждого вычисления есть бюджет — у CLI, eval_file, песочницы и Script одинаково:

БюджетИсчерпанПо умолчаниюФлаг
шаги — вычисленные выраженияE032510 млн--max-steps=N
байты — каждое созданное значение и кадрE0326256 МиБ--max-memory=MB

Оба считаются, а не замеряются по времени, поэтому один и тот же манифест останавливается в одной и той же точке на любой машине. Значения по умолчанию примерно в тридцать тысяч раз больше, чем тратит самый тяжёлый пример в репозитории; --stats печатает расход, чтобы бюджет выбирался по замеру.

У глубины свой предел, потому что шаги её не ограничивают: функция, вызывающая себя несколько сотен раз, тратит мало шагов и очень много стека. Одно выражение вкладывается не глубже 256 уровней - длинная цепочка вроде a + b + c + ... засчитывает свою длину, - а вычисление во время работы вкладывается не глубже 256 уровней, причём рекурсивный вызов засчитывает выражения, через которые проходит. За любым из пределов - ошибка, E0208 или E0399, а не переполнение стека (D42). Как и бюджет, предел - это счёт, поэтому манифест останавливается в одной и той же точке везде. Release-сборке на него хватает 512 КиБ стека, так что хост может вызывать Script с любого потока, где стека не меньше, - у вторичного потока iOS ровно 512 КиБ.

У значений тот же предел (D43). Цикл может построить глубокое значение без всякой собственной вложенности - reduce, заворачивающий аккумулятор, добавляет уровень на каждом шаге, - а любой обход значения, от вывода до освобождения памяти, рекурсивен по уровням. Поэтому список или объект глубже 256 - это E0328 там, где его строят, а значение от хоста такой глубины отвергается на границе. Разобранные JSON и YAML и так останавливаются на 128.

Изоляция импортов

Права принадлежат корневому манифесту. Импортированный модуль:

  • не может вызывать env()/read_file() — даже если у корня права есть;
  • не получает права через свои экспортированные функции: тело pub def выполняется с capability модуля-происхождения, а не вызывающего;
  • статически помечается предупреждением W0512, если содержит эффектные вызовы.
$ aura eval main.aura --allow-read=.
[E0310] Error: imported module has no capability to call read_file()
   ╭─[ evil_dependency.aura:2:7 ]

Живой пример — examples/security_demo/.

Функции хоста и перезагрузка

Приложение, встраивающее Aura, может дать скрипту свои функции (D35) и перезагружать его на ходу (D36). Оба механизма подчинены той же модели:

  • Функция хоста - это разрешение, которое приложение выдаёт по имени, по одной. pure - обещание хоста, что функция детерминирована, - оставляет скрипт герметичным; effect - разрешение, как --allow-env.
  • Они доступны только корневому модулю. Импортированный пакет не может их вызвать, если приложение не открыло ему эту функцию по имени (E0310).
  • Каждое использование проверяется при загрузке, до любого вычисления: непредоставленная функция - E0007. Функция хоста не может вызвать скрипт обратно (E0010).
  • Перезагрузка - это полная загрузка с разрешениями первой и её aura.lock, замороженным: правка скрипта не расширит его права, не подтянет незакреплённый пакет и не уберёт экспорт, к которому привязалось приложение (E0011). Недописанный файл не загружается, пока его не оставят в покое на период тишины.

Supply chain

  • Версия пакета обязательна в самом импорте: import github/acme/pkg@v1.2 as p.
  • aura.lock фиксирует точную версию и хеш потока токенов модуля; подмена — E0402. Хеш по токенам, а не по байтам, означает, что переформатирование или правка комментария проверку не срабатывают, а любое изменение, способное повлиять на поведение, — срабатывает. Lock, записанный старой версией, содержит байтовый sha256-; он по-прежнему проверяется и обновляется на первом же запуске без --frozen.
  • --frozen (режим CI) запрещает резолв мимо лока (E0403).
  • Сеть существует только в aura add; вычисление всегда оффлайн.

Детерминизм

  • now() не существует (E0533) — время передаёт хост через env.
  • Порядок ключей вывода = порядок объявления; два прогона побайтно идентичны.
  • --dry-run выполняет всё вычисление, но не пишет ни JSON, ни лок, и отчитывается о каждом чтении: [dry-run] read: ./Cargo.toml.