adachsoft / sandbox-tool-contract-tests
PHPUnit security contract tests for tools operating within filesystem sandboxes.
Package info
gitlab.com/a.adach/sandbox-tool-contract-tests
pkg:composer/adachsoft/sandbox-tool-contract-tests
Requires
- php: ^8.3
- adachsoft/ai-tool-call: ^1.0 || ^2.0
- adachsoft/collection: ^3.0
- adachsoft/normalized-safe-path: ^0.1
- adachsoft/sandbox-contracts: ^0.1
- phpunit/phpunit: ^11.0 || ^12.0 || ^13.0
Requires (Dev)
- adachsoft/php-code-style: ^0.7.1
- friendsofphp/php-cs-fixer: ^3.95
- phpstan/phpstan: ^2.3
- phpstan/phpstan-phpunit: ^2.0
- rector/rector: ^2.7
- symplify/phpstan-rules: ^14.9
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is not auto-updated.
Last update: 2026-10-26 16:00:35 UTC
README
adachsoft/sandbox-tool-contract-tests is a PHPUnit library for verifying that SPI tools respect filesystem sandbox boundaries.
Requirements
- PHP 8.3 or newer
- Composer
- Tools implementing
AdachSoft\AiToolCall\SPI\ToolInterfaceand factories implementingAdachSoft\AiToolCall\SPI\Factory\ToolFactoryInterface
Installation
composer require --dev adachsoft/sandbox-tool-contract-tests
The library provides a PHPUnit base test case, so PHPUnit is declared as a package dependency.
Usage
Extend AbstractSandboxToolTestCase and call assertSandboxToolPasses() from a PHPUnit test method:
<?php
declare(strict_types=1);
use AdachSoft\SandboxToolContractTests\AbstractSandboxToolTestCase;
use AdachSoft\SandboxToolContractTests\PublicApi\Dto\SandboxToolTestTargetDto;
use App\Tools\ReadFileToolFactory;
final class ReadFileSandboxContractTest extends AbstractSandboxToolTestCase
{
public function testReadFileToolPassesSandboxContract(): void
{
$this->assertSandboxToolPasses(
new SandboxToolTestTargetDto(new ReadFileToolFactory()),
);
}
}
Replace ReadFileToolFactory with a factory for your tool. The default verifier checks the configured base path, path traversal, leading-slash handling, path disclosure on success and I/O errors, and symlink escapes.
Path parameters are inferred from the tool definition when its schema describes a scalar string, a list of strings, or a list of objects. For object lists, the resolver uses a recognized item property (path, file_path, relative_path, and similar names); if the schema does not identify one, it defaults to path.
If the parameter shape or item path property cannot be inferred, pass a PathParameterVoCollection to SandboxToolTestTargetDto. Use PathParameterKindEnum::SCALAR_STRING for a single path, STRING_LIST for a list of strings, and OBJECT_LIST for a list of objects. The third PathParameterVo argument names the property that carries each object item's path (default: path). Replace ReadFilesToolFactory below with a factory for your tool:
<?php
declare(strict_types=1);
use AdachSoft\SandboxToolContractTests\PublicApi\Collection\PathParameterVoCollection;
use AdachSoft\SandboxToolContractTests\PublicApi\Dto\SandboxToolTestTargetDto;
use AdachSoft\SandboxToolContractTests\PublicApi\Enum\PathParameterKindEnum;
use AdachSoft\SandboxToolContractTests\PublicApi\Vo\PathParameterVo;
use App\Tools\ReadFilesToolFactory;
$target = new SandboxToolTestTargetDto(
factory: new ReadFilesToolFactory(),
pathParameters: new PathParameterVoCollection([
new PathParameterVo('files', PathParameterKindEnum::OBJECT_LIST, 'input_location'),
]),
);
Configure result fields containing paths with the DTO's outputPathKeys argument.
Direct verification
For custom integrations, create a verifier and inspect its report:
<?php
declare(strict_types=1);
use AdachSoft\SandboxToolContractTests\PublicApi\Dto\SandboxToolTestTargetDto;
use AdachSoft\SandboxToolContractTests\SandboxToolContractVerifier;
use App\Tools\ReadFileToolFactory;
$target = new SandboxToolTestTargetDto(new ReadFileToolFactory());
$report = (new SandboxToolContractVerifier())->verifyTarget($target);
if (!$report->isPassed()) {
throw new RuntimeException($report->formatFailureMessage());
}
SandboxVerificationReportDto contains the detected SandboxRuleViolationDto values. Use isPassed() to check the result and formatFailureMessage() to produce a diagnostic message.