Start Debugging

Semantic Kernel 1.81 закрывает утечку сведений о файлах за пределами AllowedFolders в FileIOPlugin

Semantic Kernel .NET 1.81.0 устраняет оракул в FileIOPlugin: WriteAsync сообщал модели, что файл вне AllowedFolders существует, доступен только для чтения и где он находится. Замеры до и после.

Semantic Kernel .NET 1.81.0 вышел 6 октября 2026 года, и в тот же день на NuGet появился Microsoft.SemanticKernel.Plugins.Core 1.81.0-preview. В заметках к выпуску PR #14525 назван “Update file handling for FileIOPlugin”, и это сильно преуменьшает суть. Вплоть до 1.80.1 метод FileIOPlugin.WriteAsync проверял, доступен ли файл только для чтения, до проверки AllowedFolders, а выбрасываемое исключение содержало полный канонический путь. Если этот плагин доступен модели, он работает как оракул существования файлов для всего диска.

Это уже второй подряд выпуск с усилением защиты файлового и сетевого доступа после того, как 1.80.0 запретил плагинам OpenAPI следовать перенаправлениям.

Что модель могла узнать в 1.80.1

Я запустил одну и ту же файловую проверку на обеих версиях с SDK 10.0.302. Она создает доступный только для чтения secrets.txt вне разрешенной папки, рядом с ним несуществующий nope.txt, а также доступный только для чтения locked.txt внутри разрешенной папки, после чего вызывает WriteAsync для каждого из них:

#:package Microsoft.SemanticKernel.Plugins.Core@1.80.1-preview
#:property PublishAot=false
#:property NoWarn=SKEXP0050
using Microsoft.SemanticKernel.Plugins.Core;

// allowed, readOnlyOutside, missingOutside, readOnlyInside: temp paths set up earlier
var plugin = new FileIOPlugin { AllowedFolders = [allowed], DisableFileOverwrite = false };

foreach (var f in new[] { readOnlyOutside, missingOutside, readOnlyInside })
{
    try { await plugin.WriteAsync(f, "y"); }
    catch (Exception e) { Console.WriteLine($"{Path.GetFileName(f)}: {e.GetType().Name}: {e.Message}"); }
}

Вывод на 1.80.1-preview (временный путь сокращен):

secrets.txt: UnauthorizedAccessException: File is read-only: /private/var/folders/.../skprobe/outside/secrets.txt
nope.txt: InvalidOperationException: Writing to the provided location is not allowed.
locked.txt: UnauthorizedAccessException: File is read-only: /private/var/folders/.../skprobe/allowed/locked.txt

Проблема в первых двух строках. Для файла вне песочницы выбрасывается другое исключение, чем для несуществующего, поэтому факт существования файла можно наблюдать. То же самое происходит и с new FileIOPlugin() по умолчанию, у которого AllowedFolders пуст, то есть с конфигурацией, задокументированной как “no folders allowed”.

И это сообщение действительно доходит до модели. При автоматическом вызове функций FunctionCallsProcessor перехватывает исключение и возвращает в качестве результата инструмента Error: Exception while invoking function. {e.Message}. Агент, подвергшийся prompt injection, может прощупывать пути вроде ~/.ssh/id_rsa или /etc/shadow и считывать ответ.

Что меняется в 1.81.0

TryGetAllowedFilePath теперь сразу возвращает false, если папки не настроены, оборачивает канонизацию пути в перехват IOException, UnauthorizedAccessException, InvalidOperationException и SecurityException (так что циклы символических ссылок и ошибки прав доступа превращаются в обычный отказ) и выполняет проверку на режим только для чтения лишь после того, как путь совпал с разрешенной папкой. Из исключения о режиме только для чтения также убран путь. Та же проверка на 1.81.0-preview:

secrets.txt: InvalidOperationException: Writing to the provided location is not allowed.
nope.txt: InvalidOperationException: Writing to the provided location is not allowed.
locked.txt: UnauthorizedAccessException: File is read-only.

За пределами песочницы все случаи теперь неотличимы. Внутри нее вы по-прежнему получаете полезную ошибку, но без пути. PR #14476, также вошедший в 1.81.0, применяет такое же выравнивание проверок к DocumentPlugin и CloudDrivePlugin.

Что делать

Обновите Microsoft.SemanticKernel.Plugins.Core до 1.81.0-preview, если хоть один агент может вызывать FileIOPlugin. Публичный API не изменился, так что достаточно поднять версию. Если у вас есть собственные обертки над файловыми инструментами, позаимствуйте этот подход: сначала проверяйте песочницу, возвращайте одно и то же сообщение при любом отказе и никогда не помещайте разрешенный путь в исключение, которое результат инструмента может вернуть модели.

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< Назад