adachsoft/file-tool-security-policy

file-tool-security-policy

Maintainers

Package info

gitlab.com/a.adach/file-tool-security-policy

Issues

pkg:composer/adachsoft/file-tool-security-policy

Transparency log

Statistics

Installs: 6

Dependents: 1

Suggesters: 0

Stars: 0

v1.0.0 2026-08-28 14:58 UTC

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: null disables path allow-listing and does not restrict paths.
  • allowedPathPatterns: [] enables an empty path allow-list and rejects every path.
  • forbiddenPathPatterns: null disables path denials.
  • forbiddenPathPatterns: [] enables an empty path deny-list and rejects no path.
  • allowedExtensions: null disables extension allow-listing and does not restrict file extensions.
  • allowedExtensions: [] enables an empty extension allow-list and rejects every file extension.
  • forbiddenExtensions: null disables 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.