Adding `security` as a well-known URL label and `[project.security]` table to pyproject.toml

Hi all,

I’ve been contributing to CPython’s security tooling and Python docs translation, and I keep running into the same practical gap: there is no standard, machine-readable way for a Python package to declare its security contact, policy, or disclosure method.

Checking the current well-known URL labels (from PEP 753): homepage, source, download, changelog, releasenotes, documentation, issues, fundingsecurity is not on this list. Package authors who add "Security Policy" = "..." to their [project.urls] table get no consistent treatment by PyPI, pip-audit, or any tooling.

A February 2026 peer-reviewed study (“Security by documentation? Characterising GitHub SECURITY.md policy and their adoption in Python libraries”) confirmed widespread inconsistency in how Python packages declare security information across the PyPI ecosystem.

I’d like to propose two things:

1. Small, fast: Add security as a well-known URL label in the PEP 753 / well-known labels specification — with aliases like securitypolicy, vulnerability, advisories.

2. Full proposal: Add an optional [project.security] table to pyproject.toml:

[project.security]
contact = "security@myproject.org"
policy-url = "https://myproject.org/security"
disclosure = "coordinated"   # or "private" / "public"
advisory-url = "https://github.com/user/project/security/advisories"

This is fully backwards-compatible — an optional table, no existing fields touched. It would give PyPI, pip-audit, and the PSF’s upcoming automated package review tools a standard field to read, directly supporting the work from the Anthropic/PSF security roadmap.

Has this been discussed before? I searched open PEPs and packaging Discourse threads and found nothing covering self-declared security metadata at the pyproject.toml level. Happy to draft a PEP if there’s interest.

7 Likes

Seems like a reasonable idea to me (and I don’t recall it coming up before).

I’d be happy to sponsor such a PEP.

4 Likes

Thank you! I really appreciate the offer to sponsor a PEP.

I’ll take some time to develop the proposal further and separate the well-known security URL label from the broader [project.security] metadata design, including the interoperability, security, and backwards-compatibility considerations.

I’ll also keep the discussion here open for additional feedback before drafting the PEP.

For any further coordination, you can also find me on GitHub: BHUVANSH855 (Bhuvansh)

2 Likes

I help oversee vulnerability reporting for an ecosystem of about a thousand packages on PyPI, and would make an effort to support this in all of them if it gets standardized. Sounds like a great idea!

1 Like

Thank you! That’s really encouraging to hear, especially given that you oversee vulnerability reporting across such a large PyPI package ecosystem.

One of the things I’m trying to establish with the research is exactly this distinction between standardizing the discovery mechanism and prescribing how individual projects handle vulnerabilities. The idea is to make the security entry point consistently discoverable while leaving each project free to use its existing policy, advisory system, disclosure process, or security page.

I’m putting together the detailed proposal now, including the PyPI ecosystem data and how this could fit cleanly into the existing Project-URL / PEP 753 model.

Your point about supporting it across your packages is also very useful evidence for the proposal. Thanks again for weighing in!

1 Like

I think the overall idea of exposing security contact metadata is a good one. However, I’m not sure if [project.security] is the best fit for it:

  • project.security.contact might be overfit for email addresses, when the majority of projects with vulnerability policies on PyPI probably use GitHub/GitLab/etc’s PVR functionality.
  • project.security.disclosure as a state seems tricky to expose in a contextually safe way: depending on the vulnerability, etc., there isn’t a single clean answer about whether a disclosure should be coordinated or not. And the semantics of coordinated versus private aren’t super clear.

I think a potentially better solution here might be to re-use our existing standard functionality for project URLs, and add another Well-known label for security reporting. To my eye this has a few advantages:

  • PEP 753 explicitly describes these labels in living terms, meaning that they don’t require a full PEP process (they can be proposed directly to the living spec);
  • Project URLs are permissive of HTTPS URLs, which I would expect to be the more common reporting venue

As a rough sketch, I’d imagine something like this (in pyproject format):

[project.urls]
"security-contact" = "https://example.com/security"

(and we’d then define a few aliases per the PEP 753 rules, e.g. security-disclosure, vuln-disclosure, etc.)

The main downside to this is that it’s simpler, i.e. exposes less rich metadata about what kind of security process might happen. But I think this is an upside as well, because we shouldn’t proscribe the security process itself (it’s going to vary too much by project, and in the worst case we run the risk of over-fitting for legacy processes like email or vendor-specific processes like GitHub’s PVR).

13 Likes

Oops, forgot to mention two other nice things about doing this with well-known labels and project URLs:

  • They already work as-is, i.e. PyPI will render these links. Assigning a well-known label is merely a pro-forma step to get a cute icon added, whereas doing it through a PEP means a relatively slow adoption process with PyPI (since it would need to ingest a new form of metadata).
  • Project URLs are additive, so we can express any number of facets we want here. For example if we want to express both “report to us” and “look at our vulns”:
[project.urls]
"security-contact" = "https://example.com/report-vuln"
"security-advisories" = "https://example.com/advisories"

(And similarly this is relatively low-touch in terms of standards efforts.)

5 Likes

The motivation sounds great, thanks for opening this! I agree with @woodruffw that this seems like it could be handled by defining a few “well-known” labels for project URLs. I’m thinking of the following topics:

  • Threat Model
  • Report a Vulnerability
  • Security Advisories

For “Report a Vulnerability”, I’m wondering if we can support email addresses with "mailto:"? Not sure if we should do this, but knowing whether it’s possible at all would be interesting. I would love to surface this security-related information specially within the PyPI UI for projects.

6 Likes

Yeah, I was just thinking this: the metadata spec says that Project-URL should be “browsable.” I think a plain reading of that is meant to imply HTTP(S) only, but technically mailto: is also browsable (since most browsers will jump to the user’s webmail or similar). So I think it’d be fine for us to encourage mailto: there :slightly_smiling_face:

Edit: ref: Core metadata specifications - Python Packaging User Guide

3 Likes

Stop copying AI output and respond in your own words.

15 Likes

I agree that this is better handled through Project-URL rather than adding a new [project.security] table.

The main labels I suggest are:

  • Report a Vulnerability
  • Security Advisories
  • Threat Model

This keeps the metadata focused on discovery without prescribing how a project handles vulnerabilities.

I think we also clarify whether mailto: is valid for Project-URL, since it would be useful for projects that use an email contact.

So, the next step would be defining the labels and aliases in the living specification.

(Let’s keep discussing more,I use a little bit AI to convey what i am thinking properly, from my experience it happens many time with me like one or two words misplaced changes the meaning of what i was trying to convey.)

The projects I’m involved with maintain a dedicated website with links to instructions for vulnerability reporting, lists of published security advisories, and other security-relevant information so in their case having a single URL on PyPI would work out fine, but I can understand that some projects may not have documentation organized this way and would prefer multiple fields. I guess filling all of those fields with identical URLs wouldn’t be a problem though when they happen to all be addressed in one spot.

1 Like

Sorry, I’m not following – I think both approaches here enable multiple links equally well? The only material difference is whether those links should go in an existing shape ([project.urls]) or a new shape ([project.security])

In other words, it would be up to each project to decide which labels and URLs are sufficient for their security process.

1 Like

One other important point is that [project.security] is data in pyproject.toml, and as such, isn’t available from wheels. What you would really need here would be new core metadata fields, with associated pyproject.toml input data. It’s a technical point, but worth being clear about.

There’s also another relevant point here - if a project changes its security process, how will that be reflected? Because wheel metadata is immutable, so older wheels will show data that is now incorrect, which could cause problems (people reporting vulnerabilities to the wrong place, for example).

6 Likes

The immutability concern also applies to Project-URL, so I agree we should treat these as discovery metadata rather than authoritative security policy. This makes the well-known Project-URL approach preferable to adding new Core Metadata fields.

Let’s bring some data to the party! (Nothing here is secret, but you’d have to scrape metadata to get it, so I took a shortcut for us all.)

warehouse=> SELECT name,
       count(*) AS url_rows,
       count(DISTINCT r.project_id) AS projects
FROM release_urls ru
JOIN releases r ON r.id = ru.release_id
WHERE ru.name ILIKE '%security%'
GROUP BY name
ORDER BY url_rows DESC;
           name           | url_rows | projects 
--------------------------+----------+----------
 Security                 |     3354 |      510
 Security Policy          |      996 |       63
 Security Advisories      |       74 |        2
 Security policy          |       43 |       10
 NSA MCP Security Mapping |       14 |        1
 Security Research        |       10 |        1
 Report Security Issue    |       10 |        1
 Security Validation      |        6 |        1
 Report a security issue  |        4 |        1
 Security Reports         |        4 |        1
 security                 |        3 |        2
 Security Guide           |        2 |        1
 Security Patches         |        2 |        1
 skillscan-security       |        2 |        1
 Vault Cyber Security     |        1 |        1
 Nucleus Security         |        1 |        1
 Registry - Security      |        1 |        1
 Security Books           |        1 |        1
 Full Security Suite      |        1 |        1
(19 rows)

So folks are already doing something with Project-URLs today. Here’s one example: bluedot-ares · PyPI

Seems to me like the right move is to add some treatment to the icon, document it and let folks know, and thus sidestep any further need for adding to the specification, getting tools to adopt it, etc etc?

13 Likes

Oh, to be clear I’d rely on standardized link names for project.urls entries (I don’t think creating a separate project.security field is warranted). What I was weighing in favor of is having an agreed-upon way of identifying these on PyPI rather than everyone making up their own names for the links. If there is an established convention I’ll get projects I’m involved with to start relying on it.

In my case, a URL with a generic name like “Security” would be sufficient if that’s a popular choice, but some other projects may want multiple more specific links for various aspects of information security and vulnerability handling tasks.

1 Like

To me it seems that:

  • If a project documents its threat model, links to the other two should be in that document.
  • If a project publishes advisories, that page typically has the link to report a new vunlerability.
  • If it’s just “report”… I don’t think we should encourage publishing bare mailto: links (even if the spec allows it). Linking to a page with instructions, which you can update over time, seems much better for that purpose.

Looking at the data, this needs a single well-known label: security with a securitypolicy alias.
(Projects could add an additional “Security Advisories”/“Security Reports” link, but that’s not worth standardizing.)

Could you run that for %threat% and %vuln% as well?

2 Likes

I sure can!

          name           | url_rows | projects 
-------------------------+----------+----------
 Threat Database         |       92 |        1
 Threat Model            |        9 |        3
 Threat model            |        6 |        4
 Threat Registry         |        3 |        1
 Threat Intel            |        3 |        1
 Threatray API reference |        2 |        1
 Threat Model Paper      |        1 |        1
 Agent Threat Rules      |        1 |        1

and

          name           | url_rows | projects 
-------------------------+----------+----------
 Patched-Vulnerabilities |       36 |        1
 Vulners API key         |        2 |        1
6 Likes

Based on the discussion here, i have made a PR regarding adding security as a well-known project URL label directly to living spec

background research: