mindtwo / base-security-txt
Framework-agnostic core of the mindtwo security.txt packages: validates input and renders an RFC 9116 security.txt whose expiry follows the last confirmation.
Requires
- php: ^8.0
Requires (Dev)
- laravel/pint: ^1.13
- pestphp/pest: ^2.34|^3.0|^4.0
- phpstan/phpstan: ^1.11|^2.0
- phpstan/phpstan-deprecation-rules: ^1.1|^2.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-05 11:54:30 UTC
README
Framework-agnostic core of the mindtwo security.txt packages. It validates the input and renders
a security.txt (RFC 9116) whose Expires follows the
last confirmation: whoever maintains the file only has to check and save it once a year, nobody
edits a date, and a file nobody confirms expires as the RFC intends.
Used by mindtwo/wordpress-security-txt,
mindtwo/craftcms-security-txt and
mindtwo/statamic-security-txt. No runtime
dependencies.
Usage
use Mindtwo\SecurityTxt\SecurityTxt; use Mindtwo\SecurityTxt\Settings; use Mindtwo\SecurityTxt\Validator; // On save: normalise and validate the form input. $settings = Settings::fromArray([ 'contact' => "security@example.com\n+49 228 123456", 'policy' => 'https://example.com/security-policy', 'preferred_languages' => 'de, en', ]); $violations = Validator::validate($settings); // list<Violation>, empty when valid // store $settings->toArray() together with the time of the save // On request: render the file, or answer 404 when null. $body = SecurityTxt::fromInput($storedArray, $confirmedAt, ['https://example.com/.well-known/security.txt']);
Contact: mailto:security@example.com
Contact: tel:+49228123456
Expires: 2027-11-04T09:16:54Z
Preferred-Languages: de, en
Canonical: https://example.com/.well-known/security.txt
Policy: https://example.com/security-policy
| Class | Responsibility |
|---|---|
Settings |
Normalises input: e-mail addresses become mailto:, + numbers become tel:, every value is a single line. toArray() is the storage format shared by all plugins. |
Validator |
RFC 9116 checks: at least one contact, https for every web URI (dns:/openpgp4fpr: allowed for Encryption), valid language tags. Returns Violations with a code for translation and an English message(). |
Expiry |
after($confirmedAt) = 364 days later in UTC (the RFC recommends less than a year), status() = valid, expiring (30 days) or expired. |
SecurityTxt |
Renders the body in a stable field order; holds PATH, LEGACY_PATH and CONTENT_TYPE. |
Not supported on purpose: OpenPGP signatures (the signing key does not belong on a web server, and any edit would break the signature).
Development
composer install
composer check # pint --test, phpstan (level 8), pest
License
MIT, see LICENSE.md.