Metadata-Version: 2.4
Name: Flask-SecurityTxt
Version: 1.4.0
Summary: A Flask extension for creating and serving security.txt files
Author-email: "M.P. van de Weerd" <michael@parcifal.dev>
License-Expression: AGPL-3.0-only
Project-URL: Homepage, https://scm.parcifal.dev/parcifal/Flask-SecurityTxt
Project-URL: Source, https://scm.parcifal.dev/parcifal/Flask-SecurityTxt
Project-URL: Bug Tracker, https://scm.parcifal.dev/parcifal/Flask-SecurityTxt/issues
Keywords: web,security,security.txt,rfc9116,well-known
Classifier: Environment :: Web Environment
Classifier: Framework :: Flask
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Security
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: flask
Requires-Dist: python-dateutil
Provides-Extra: babel
Requires-Dist: flask-babel; extra == "babel"
Provides-Extra: sign
Requires-Dist: pgpy; extra == "sign"
Requires-Dist: cryptography<43,>=41; extra == "sign"
Requires-Dist: standard-imghdr; python_version >= "3.13" and extra == "sign"
Provides-Extra: dev
Requires-Dist: Flask-SecurityTxt[babel,sign]; extra == "dev"
Requires-Dist: pylint; extra == "dev"
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Requires-Dist: coverage; extra == "dev"
Requires-Dist: build; extra == "dev"
Dynamic: license-file

# Flask-SecurityTxt

[![release](https://img.shields.io/gitea/v/release/parcifal/flask-security-txt?gitea_url=https%3A%2F%2Fscm.parcifal.dev&label=latest+release)][release]
[![pypi](https://img.shields.io/pypi/v/Flask-SecurityTxt?label=pypi+release)][pypi]
[![develop](https://scm.parcifal.dev/parcifal/flask-security-txt/badges/workflows/push.yml/badge.svg?label=develop&branch=develop)][develop]
[![master](https://scm.parcifal.dev/parcifal/flask-security-txt/badges/workflows/push.yml/badge.svg?label=master&branch=master)][master]
[![gitlab](https://img.shields.io/gitlab/last-commit/parcifal%2Fflask-security-txt?label=gitlab+mirror)][gitlab]
[![github](https://img.shields.io/github/last-commit/parcifal%2Fflask-security-txt?label=github+mirror)][github]

![Flask-SecurityTxt Logo][logo]

Flask-SecurityTxt is a simple extension for Flask that makes it easy to add a 
security.txt file to your website. This file, as specified by [RFC 9116][rfc] 
and described at [securitytxt.org](https://securitytxt.org/), is used to 
provide information to security researchers about how to report vulnerabilities 
in your website.

 > The Flask-SecurityTxt logo makes use of the [`cloud-lock-outline`][lock] 
 > icon created by [Michael Richins][richins] as part of the [Material Design 
 > Icons (MDI) library][mdi] and published through 
 > [Pictogrammers][pictogrammers] under the Apache License 2.0.

## Installation

You can install Flask-SecurityTxt using pip:

```bash
pip install Flask-SecurityTxt
```

Signing the security.txt with a PGP key requires `pgpy`, which is not installed
by default:

```bash
pip install Flask-SecurityTxt[sign]
```

If `Flask-Babel` is used to detect the value of the `Preferred-Languages` 
field, it can be installed through the `babel` extra in the same way:

```bash
pip install Flask-SecurityTxt[babel]
```

## Usage

```python
from flask import Flask
from flask_security_txt import SecurityTxt

app = Flask(__name__)
security_txt = SecurityTxt(app)
```

Under the application factory pattern, the application is passed to 
`init_app()` instead. A single `SecurityTxt` instance can initialize several 
applications, each with its own configuration:

```python
security_txt = SecurityTxt()

def create_app():
    app = Flask(__name__)
    security_txt.init_app(app)
    return app
```

You can also customize the contents of the security.txt file by providing the
following settings in the configuration file:

| Property                           | Type                       | Default          | Description                                                                                                                                                                                                                                                                                                                                                                                                        |
|------------------------------------|----------------------------|------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `SECURITY_TXT_ENDPOINT`            | `str`                      | `"security_txt"` | The name by which the end-point will be known to the Flask-app.                                                                                                                                                                                                                                                                                                                                                    |
| `WELL_KNOWN_DIR`                   | `str`                      | `".well-known"`  | The name of the directory that will contain the security.txt file. The key is deliberately not prefixed, so that every extension serving from this directory can share one location.                                                                                                                                                                                                                               |
| `SECURITY_TXT_FILE_NAME`           | `str`                      | `"security.txt"` | The name of the security.txt file.                                                                                                                                                                                                                                                                                                                                                                                 |
| `SECURITY_TXT_SIGN_KEY`            | `str`                      | `None`           | The path to a file containing a PGP key used for signing the security.txt file. Requires the `sign` extra. The key must be a private key, and is read and verified when the application is initialized.                                                                                                                                                                                                            |
| `SECURITY_TXT_SIGN_KEY_PASSPHRASE` | `str`                      | `None`           | The passphrase of the key in `SECURITY_TXT_SIGN_KEY`. Required if, and only if, that key is passphrase-protected.                                                                                                                                                                                                                                                                                                  |
| `SECURITY_TXT_CONTACT`             | `str` `Iterable`           | `None`           | The value of the `Contact` field. An `Iterable` type value will result in multiple `Contact` fields. If `None`, the value is automatically generated from `SECURITY_TXT_CONTACT_MAILBOX`.                                                                                                                                                                                                                          |
| `SECURITY_TXT_CONTACT_MAILBOX`     | `str`                      | `"security"`     | The local part of the automatically generated `Contact` email address. Only used if `SECURITY_TXT_CONTACT` is `None`.                                                                                                                                                                                                                                                                                              |
| `SECURITY_TXT_EXPIRES`             | `str` `datetime`           | `None`           | The value of the `Expires` field. A `str` type value is parsed into a `datetime` using `dateutil`; an unparseable string raises a `ValueError`. A `datetime` type value is formatted as an RFC 3339 timestamp with microseconds stripped; a value without a timezone is assumed to be in UTC, as RFC 9116 requires an offset. If `None`, the value is automatically generated using `SECURITY_TXT_EXPIRES_OFFSET`. |
| `SECURITY_TXT_EXPIRES_OFFSET`      | `timedelta` `dict` `tuple` | `{"weeks": 1}`   | The offset applied to `datetime.now()` to automatically generate the `Expires` field value. A `dict` is unpacked and passed to the `timedelta` constructor as keyword arguments; a `tuple` is passed as positional arguments, which are interpreted as days, seconds, microseconds, milliseconds, minutes, hours, and weeks.                                                                                       |
| `SECURITY_TXT_ENCRYPTION`          | `str` `Iterable`           | `None`           | The value of the `Encryption` field. An `Iterable` type value will result in multiple `Encryption` fields. A value of `None` will omit the field entirely.                                                                                                                                                                                                                                                         |
| `SECURITY_TXT_ACKNOWLEDGMENTS`     | `str` `Iterable`           | `None`           | The value of the `Acknowledgments` field. An `Iterable` type value will result in multiple `Acknowledgments` fields. A value of `None` will omit the field entirely.                                                                                                                                                                                                                                               |
| `SECURITY_TXT_PREFERRED_LANGUAGES` | `str` `list` `tuple`       | `None`           | The value of the `Preferred-Languages` field, which RFC 9116 permits only once; a `list` or `tuple` type value will therefore result in a single comma-separated field. If `None`, the value falls back to the translations listed by the `Flask-Babel` extension if it is loaded, or `"en"` otherwise.                                                                                                            |
| `SECURITY_TXT_CANONICAL`           | `str` `Iterable`           | `None`           | The value of the `Canonical` field. An `Iterable` type value will result in multiple `Canonical` fields. If `None`, the value is resolved from the endpoint name in `SECURITY_TXT_ENDPOINT` using `url_for`, which is the location the security.txt is served from.                                                                                                                                                |
| `SECURITY_TXT_POLICY`              | `str` `Iterable`           | `None`           | The value of the `Policy` field. An `Iterable` type value will result in multiple `Policy` fields. A value of `None` will omit the field entirely.                                                                                                                                                                                                                                                                 |
| `SECURITY_TXT_HIRING`              | `str` `Iterable`           | `None`           | The value of the `Hiring` field. An `Iterable` type value will result in multiple `Hiring` fields. A value of `None` will omit the field entirely.                                                                                                                                                                                                                                                                 |
| `SECURITY_TXT_FIELD_CASE`          | `str`                      | `"standard"`     | Controls the casing of field names in the output. Accepted values are `"standard"` (title case, e.g. `Contact:`), `"lower"` (e.g. `contact:`), and `"upper"` (e.g. `CONTACT:`).                                                                                                                                                                                                                                    |
| `SECURITY_TXT_HEADER`              | `str`                      | <default header> | A comment block prepended to the security.txt. The default header includes the Flask-SecurityTxt version and project links. The placeholder `{version}` is replaced with the installed version. Set to `None` to omit the header entirely.                                                                                                                                                                         |
| `SECURITY_TXT_FOOTER`              | `str`                      | `None`           | A comment block appended to the security.txt. The placeholder `{version}` is replaced with the installed version. A value of `None` will omit the footer entirely.                                                                                                                                                                                                                                                 |
| `SECURITY_TXT_COMMENT_LINE_PREFIX` | `str`                      | `"# "`           | The prefix added to every line of the header, the footer, and each field comment. Trailing whitespace is stripped, so that blank lines within a comment do not carry any.                                                                                                                                                                                                                                          |

Every field that holds a URL accepts, besides a full URL using one of the
schemes allowed for that field, the name of a file in the application's static
folder or the name of an end-point that can be resolved by the application.
Both are rendered as external `https:` URLs. A value that is none of the three
raises a `ValueError`.

These settings can also be passed to the constructor, as a `dict` whose keys
are the property names without the `SECURITY_TXT_` prefix and in lower case.
They then serve as defaults, which the application configuration still
overrides. An unknown key raises a `ValueError`, so that a typo is not silently
ignored:

```python
security_txt = SecurityTxt(app, config={
    "contact_mailbox": "abuse",
    "expires_offset": {"days": 90},
})
```

### Configuring Comments

For each field, a comment can be added on the line immediately preceding it by
setting a config key of the form `SECURITY_TXT_<FIELD>_COMMENT`, where
`<FIELD>` is the upper-case field name with any hyphen replaced by an
underscore (e.g. `SECURITY_TXT_CONTACT_COMMENT`,
`SECURITY_TXT_PREFERRED_LANGUAGES_COMMENT`). Each line of the comment is
prefixed with `SECURITY_TXT_COMMENT_LINE_PREFIX` automatically, and the comment
is preceded by an empty line. The comment of which a field is not rendered, is
not rendered either.

### Configuring Contact Details

The `Contact` field of the security.txt file can be configured with one of 
two different ways. First of all, the whole value string can be defined
using the `SECURITY_TXT_CONTACT` property. This takes precedence over the
alternative method, which uses the `SECURITY_TXT_CONTACT_MAILBOX` property.
The value of this property is combined with the domain name in `SERVER_NAME`,
or with that of the current request if `SERVER_NAME` is not configured. The
latter method is less reliable, as the client determines the host of a 
request; as such, the prior method is preferred if possible. By default, the
contact is set to be "security@&lt;domain&gt;", with the domain name being
provided by Flask.

## Example

A security.txt file will be available in your website's `.well-known` 
directory, with the following contents:

```text
#
# GENERATED BY FLASK-SECURITYTXT 1.4.0
#
# Flask-SecurityTxt has been developed by M.P. van de Weerd for
# Parcifal Programmatics under the AGPLv3 licence.
#
# https://pypi.org/project/Flask-SecurityTxt/
# https://scm.parcifal.dev/parcifal/flask-security-txt
#
# https://www.van-de-weerd.net/
# https://www.parcifal.dev/
#
Contact: mailto:security@example.com
Expires: 2026-10-12T13:52:49+00:00
Preferred-Languages: en
Canonical: https://example.com/.well-known/security.txt
```

## Contributing

Found a bug? Have a suggestion? Open an issue or submit a merge request at
[the Forgejo repository](https://scm.parcifal.dev/parcifal/flask-security-txt).
All contributions are welcome.

[logo]: https://scm.parcifal.dev/parcifal/flask-security-txt/raw/branch/master/assets/logo.png

[pictogrammers]: https://pictogrammers.com/
[richins]: https://pictogrammers.com/contributor/MrGrigri/
[lock]: https://pictogrammers.com/library/mdi/icon/cloud-lock-outline/
[mdi]: https://pictogrammers.com/library/mdi/

[license]: https://scm.parcifal.dev/parcifal/flask-security-txt/src/branch/master/LICENSE

[release]: https://scm.parcifal.dev/parcifal/flask-security-txt/releases/latest
[gitlab]: https://gitlab.com/parcifal/flask-security-txt
[github]: https://github.com/parcifal/flask-security-txt
[develop]: https://scm.parcifal.dev/parcifal/flask-security-txt/src/branch/develop
[master]: https://scm.parcifal.dev/parcifal/flask-security-txt/src/branch/master

[pypi]: https://pypi.org/project/Flask-SecurityTxt/

[rfc]: https://www.rfc-editor.org/rfc/rfc9116.html
