adachsoft / file-tool-security-policy
file-tool-security-policy
Package info
gitlab.com/a.adach/file-tool-security-policy
pkg:composer/adachsoft/file-tool-security-policy
Requires
- php: ^8.3
- adachsoft/collection: ^3.0
Requires (Dev)
- adachsoft/php-code-style: ^0.5.0
- friendsofphp/php-cs-fixer: ^3.95
- phpstan/phpstan: ^2.2
- phpunit/phpunit: ^13.3
- rector/rector: ^2.6
- symplify/phpstan-rules: ^14.12.3
This package is not auto-updated.
Last update: 2026-08-29 11:22:05 UTC
README
A configurable security policy for file-tool style path operations.
Scope and input requirements
This library performs no filesystem I/O. Before calling the policy, the consumer must provide a safe, normalized relative path. The path must not be absolute, contain parent-directory traversal (..), be empty, or contain a NUL byte. The library validates these subject requirements but does not resolve paths, access the filesystem, or verify that a file exists.
Create policies through SecurityPolicyFactory:
use AdachSoft\FileToolSecurityPolicy\Dto\SecurityPolicyConfigDto;
use AdachSoft\FileToolSecurityPolicy\Factory\SecurityPolicyFactory;
$policy = new SecurityPolicyFactory()->create(new SecurityPolicyConfigDto(
allowedPathPatterns: null,
forbiddenPathPatterns: ['vendor/*'],
allowedExtensions: null,
forbiddenExtensions: ['phar'],
));
Configuration semantics
The four list options distinguish null from an empty list:
allowedPathPatterns: nulldisables path allow-listing and does not restrict paths.allowedPathPatterns: []enables an empty path allow-list and rejects every path.forbiddenPathPatterns: nulldisables path denials.forbiddenPathPatterns: []enables an empty path deny-list and rejects no path.allowedExtensions: nulldisables extension allow-listing and does not restrict file extensions.allowedExtensions: []enables an empty extension allow-list and rejects every file extension.forbiddenExtensions: nulldisables extension denials.forbiddenExtensions: []enables an empty extension deny-list and rejects no file extension.
Forbidden rules always have precedence over allowed rules. This applies both to paths and extensions: a path matching a forbidden path pattern is rejected even when it also matches an allowed pattern, and a file with a forbidden extension is rejected even when that extension is listed as allowed. Extension rules do not apply to directory subjects.
Path patterns are case-insensitive, use / as the normalized separator, and support glob syntax. For example:
// Allow exactly one file.
$policy = new SecurityPolicyFactory()->create(new SecurityPolicyConfigDto(
allowedPathPatterns: ['composer.lock'],
));
// Deny the vendor directory and every file below it, including nested files.
$policy = new SecurityPolicyFactory()->create(new SecurityPolicyConfigDto(
forbiddenPathPatterns: ['vendor/*'],
));
An allowed extension list applies to files without an extension as well. To explicitly allow such files, include the empty string '':
$policy = new SecurityPolicyFactory()->create(new SecurityPolicyConfigDto(
allowedExtensions: ['', 'php'],
));
Additional rules
Consumers can append their own rules by implementing SecurityPolicyRuleInterface and passing a SecurityPolicyRuleCollection to the factory. Additional rules run after the standard path and extension rules.
use AdachSoft\FileToolSecurityPolicy\Collection\SecurityPolicyRuleCollection;
use AdachSoft\FileToolSecurityPolicy\Contract\SecurityPolicyRuleInterface;
use AdachSoft\FileToolSecurityPolicy\Dto\SecurityPolicyConfigDto;
use AdachSoft\FileToolSecurityPolicy\Enum\SecurityPolicySubjectTypeEnum;
use AdachSoft\FileToolSecurityPolicy\Enum\SecurityPolicyViolationReasonEnum;
use AdachSoft\FileToolSecurityPolicy\Exception\SecurityPolicyViolationException;
use AdachSoft\FileToolSecurityPolicy\Factory\SecurityPolicyFactory;
$customRule = new class implements SecurityPolicyRuleInterface {
public function assertAllowed(
string $normalizedRelativePath,
SecurityPolicySubjectTypeEnum $subjectType,
): void {
if ($normalizedRelativePath === 'secret.txt') {
throw new SecurityPolicyViolationException(
$normalizedRelativePath,
$subjectType,
SecurityPolicyViolationReasonEnum::CUSTOM_RULE_REJECTED,
);
}
}
};
$policy = new SecurityPolicyFactory()->create(
new SecurityPolicyConfigDto(),
new SecurityPolicyRuleCollection([$customRule]),
);
Internal API
All classes in Internal/, PathPatternCollection, and FileExtensionCollection are marked @internal and are not part of the public API. Use SecurityPolicyFactory and FileToolSecurityPolicyInterface for application code.