| Internet-Draft | Machine-Readable CVD Policies | September 2026 |
| Behring & Berg | Expires 5 March 2027 | [Page] |
This document defines a JSON format for machine-readable Coordinated
Vulnerability Disclosure (CVD) policies. It also defines the proposed
CVD-Policy field for discovery through security.txt and requests
registration of the application/cvd-policy+json media type. The format
complements security.txt and human-readable policy documents. A policy does
not prove ownership and does not establish legal authorization to test, legal
safe harbor, or the safety of an activity.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 5 March 2027.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
Coordinated Vulnerability Disclosure depends on publishers communicating where
reports are accepted and under which conditions security research is expected.
security.txt [RFC9116] provides standardized discovery of contact and
human-readable policy information, but it does not define a machine-readable
policy model for scope, testing statements, or conditions.¶
This document defines a strict JSON policy format, discovery by a proposed
security.txt field, retrieval and authority rules, deterministic scope and
testing evaluation, and an extension model. The format records statements made
by a publisher. It does not determine ownership, legal authorization, or legal
safe harbor.¶
The proposed field and media type are not registered at the time this document is written. The registrations requested by this document are specified in Section 17.¶
The key words MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, and MAY in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals.¶
This document uses the following terms:¶
The party that publishes a CVD policy.¶
The normalized host from the URI originally used to retrieve security.txt,
before redirects.¶
The URI carried by the proposed CVD-Policy field.¶
An absolute HTTP or HTTPS URL evaluated for one testing activity.¶
Evidence that a policy was discovered through an assessed security.txt for
the exact Discovery Host. Authority is not an ownership certificate.¶
Assets and products for which the publisher states that reports are accepted.¶
An explicit publisher statement in a matching testing rule whose conditions are satisfied.¶
Human-readable policies cannot be evaluated consistently by automated clients. A client needs to distinguish reporting scope from testing permission, bind a policy to the host that actually advertised it, reject ambiguous JSON, apply exclusions and prohibitions independently of array order, and fail closed when conditions or critical extensions are not understood.¶
A JSON document fetched from an arbitrary location cannot establish authority for the hosts it names. Without discovery-host binding, a compromised or unrelated host could publish statements about third-party targets. Without strict result semantics, a UI or automation could also turn an incomplete publisher statement into an unsafe Boolean decision.¶
The design goals are:¶
deterministic parsing, validation, scope matching, and evaluation;¶
exact binding between a Target host and its Discovery Host;¶
separation of Reporting Scope from Testing Permission;¶
fail-closed processing of conditions, prohibitions, and critical extensions;¶
support for one policy document referenced independently by multiple hosts;¶
useful reporting preferences without defining report transport; and¶
machine-readable status and diagnostic values that do not claim legal effect.¶
The following are outside the scope of this document:¶
vulnerability-report transmission and intake endpoints;¶
attachment upload, report authentication, and ticket or case management;¶
website architecture, command-line interfaces, TypeScript APIs, and package versioning;¶
legal authorization to test or guaranteed safe harbor; and¶
an Activity or extension registry in this revision.¶
Version 1 does not define a state named allowed, a default testing decision, an
explicit-order mode, or order-dependent first-match processing. Software
package versions are independent of the document format version.¶
A publisher advertises a Policy URI with the proposed CVD-Policy field in the
security.txt file defined by [RFC9116]. Field names are compared
case-insensitively. CVD-Policy contains exactly one absolute HTTPS URI as
specified by [RFC3986]. The
URI MUST NOT contain userinfo or a fragment.¶
A security.txt file MUST contain no more than one CVD-Policy field.
Automatic processing MUST reject multiple appearances and MUST NOT choose one
by field order. The existing Policy field remains the mechanism for linking
to a human-readable policy.¶
A client MUST NOT guess /.well-known/cvd.json or any other policy path when
CVD-Policy is absent. This document does not request a new well-known URI
suffix [RFC8615].¶
Before creating Authority evidence, a client MUST successfully parse and assess
the retrieved security.txt. The file MUST contain at least one valid
Contact, exactly one syntactically valid Expires whose timestamp is later
than the retrieval time, and exactly one valid CVD-Policy. A malformed
Expires and a valid but expired Expires are distinct diagnostic conditions.
Neither condition establishes Authority.¶
The originally requested security.txt URI, the final URI, and every redirect
hop MUST use HTTPS. Redirect processing MUST preserve the original Discovery
Host. If the final host differs from the originally requested host, at least
one valid Canonical field in the resulting file MUST exactly equal the
originally requested security.txt URI. Otherwise, assessment MUST NOT
establish Authority. Detailed canonical-mismatch diagnostics are
implementation-specific and informative.¶
Whenever one or more Canonical fields are present, at least one value MUST
exactly equal the originally requested security.txt URI before Authority is
established. This requirement applies without a redirect and to same-host
redirects.¶
An OpenPGP cleartext signature can protect security.txt as described by
[RFC9116]. Software that cannot genuinely re-sign a signed file MUST NOT
rewrite it automatically. Core assessment software is not required to verify
such signatures; a client claiming signature verification has to perform that
verification independently.¶
External hosting of the JSON policy is permitted. The Policy URI host does not become the Discovery Host and does not gain Authority over any Target.¶
The media type defined by this document is
application/cvd-policy+json. Network clients MUST accept that media type. A
separately enabled compatibility mode MAY accept application/json while
presenting a notice; it MUST NOT broaden acceptance to other media types. A
client performing automatic evaluation MUST reject HTML, plain text, and other
unexpected representations.¶
A network client MUST retrieve the Policy URI with HTTPS GET as defined by
[RFC9110]. It MUST send an Accept field preferring
application/cvd-policy+json and MUST list application/json only when the
compatibility mode is enabled. Only 200 OK is a complete Policy
representation. 204 No Content, 206 Partial Content, and every other
status are not complete Policy representations. Every redirect hop MUST use
HTTPS. A client MUST NOT automatically send ambient credentials, cookies,
Authorization, or Proxy-Authorization, and MUST NOT forward such fields
across redirects.¶
A retrieved representation is processed as UTF-8 JSON under
Section 9. Clients should apply finite redirect,
response-size, time, and resource limits and block automatic access to
loopback, link-local, private, reserved, and metadata addresses. Redirects of
the JSON policy do not change the Discovery Host recorded during
security.txt assessment.¶
Clients should retain the retrieval time, final Policy URI, redirect chain,
media type, and validators such as ETag or Last-Modified. A cached policy
should not be treated as current after its expires instant or after the
associated security.txt evidence expires.¶
A Version 1 policy is one JSON object with these required members:
cvd_policy, last_updated, expires, organization, contact, research,
reporting_scope, and reporting. The optional members are testing,
response_targets, disclosure, critical_extensions, and extensions.
Missing optional members MUST NOT create Testing Permission. In particular,
missing testing means that no Testing Permission is established.¶
Core object members are exactly as follows. Every unlisted member is forbidden:¶
organization requires name and optionally contains uri;¶
contact requires channels and optionally contains
preferred_languages and encryption;¶
research requires posture and optionally contains statement;¶
reporting_scope contains web, products, or both;¶
a web entry requires id, state, host, schemes, path_prefix, and
include_subdomains, and optionally contains ports;¶
a product entry requires id, state, and name, and optionally contains
identifiers;¶
testing requires only rules;¶
a testing rule requires id, activity, and state, and optionally contains
target_ids and, only when permitted, conditions;¶
conditions contains one or more defined condition members;¶
reporting requires requested_fields and proof_of_exploitation and has no
optional members;¶
response_targets contains one or more of acknowledgement_days,
initial_assessment_days, and update_interval_days;¶
disclosure requires approach and optionally contains default_days and
statement; and¶
extensions has absolute-URI member names and extension-defined values.¶
Arrays declared non-empty MUST contain at least one item.
reporting.requested_fields, contact.preferred_languages,
contact.encryption, and critical_extensions MAY be empty; other optional
arrays, when present, MUST be non-empty.¶
The cvd_policy member identifies the document format. A Version 1 document
MUST contain the JSON number 1; the string "1" and every 0.x value are not
Version 1 documents. A missing or non-integer cvd_policy member MUST produce
invalid-policy. An unknown integer version MUST produce
unsupported-policy. Implementations MUST keep software package versions
separate from this document format value.¶
last_updated and expires are date-time strings conforming to [RFC3339].
expires MUST identify an instant strictly later than last_updated. An
expired policy MUST produce the normative status invalid-policy and MUST NOT
produce Testing Permission. Detailed expiry diagnostics are informative and
implementation-specific. Evaluators MUST compare expiry with an explicitly
supplied or recorded evaluation time.¶
organization contains the required non-empty string name and the optional
uri. If present, uri MUST be an absolute HTTPS URI without userinfo.
Organization metadata does not establish Authority.¶
contact.channels is an ordered, non-empty array of unique contact URIs. Every
entry MUST be an absolute URI using only the mailto, tel, or https scheme.
An HTTPS contact URI MUST NOT contain userinfo or a fragment. Array order
states channel preference.¶
contact.preferred_languages, when present, is an array of unique language tags
conforming to BCP 47 [RFC5646]. Its order has no defined meaning.
contact.encryption, when present, is an array of unique absolute URIs that
reference encryption keys or instructions. It MUST NOT embed key material.¶
research.posture is one of open, limited, report_only, or prohibited.
The optional research.statement is explanatory text and has no evaluation
effect.¶
open states a generally welcoming posture but MUST NOT imply Testing
Permission without a matching permitted rule. report_only states that
reports are accepted but active testing under this Policy is not permitted. A
policy with report_only or prohibited posture MUST NOT contain a permitted
testing rule. Such a posture produces publisher-stated-prohibited only after
the evaluation has
established Authority and matching Reporting Scope as specified in
Section 11.¶
reporting_scope contains a non-empty web array, a non-empty products
array, or both. Every scope-entry ID and testing-rule ID MUST be unique across
the entire document. Every ID MUST be 1 through 128 ASCII characters, start
with an ASCII letter or digit, and otherwise contain only ASCII letters,
digits, ., _, or -. IDs and references are compared as exact,
case-sensitive strings.¶
A web entry contains:¶
id: a document-wide identifier;¶
state: in or out;¶
host: a DNS name, IPv4 literal, or IPv6 literal;¶
schemes: a non-empty unique array containing http, https, or both;¶
optional ports: a non-empty unique array of integers from 1 through 65535;¶
path_prefix: an absolute-path prefix beginning with /; and¶
include_subdomains: a Boolean.¶
The host value MUST NOT contain a scheme, port, path, userinfo, wildcard,
query, or fragment. An IP-literal entry MUST set include_subdomains to
false. schemes MUST be non-empty and unique and MUST contain no value other
than http or https. path_prefix MUST NOT contain query or fragment
syntax.¶
If ports is absent, only the default port for the matched scheme applies: 80
for HTTP and 443 for HTTPS. A non-default port is covered only when explicitly
listed.¶
A matching out entry MUST override every matching in entry. Scope and rule
array order MUST NOT change an evaluation result.¶
A product entry contains id, state, a non-empty name, and an optional
non-empty array of unique identifiers. Every product identifier MUST be an
absolute RFC 3986 URI and is compared as an exact, case-sensitive string
without scheme-specific equivalence processing. Product entries are reporting
metadata only and MUST NOT produce automatic Testing Permission. Version-range
syntax is not defined by Version 1.¶
testing.rules, when present, is a non-empty array. Each rule contains a
unique id, an activity, a state, optional target_ids, and optional
conditions.¶
The core activity identifiers are:¶
manual_testing: manually initiated investigation not described by a more
specific core activity;¶
automated_scanning: automated detection or vulnerability checking against a
web or network service;¶
fuzzing: automated transmission of generated or mutated inputs; and¶
credential_testing: testing only with researcher-controlled or explicitly
provided test accounts; it excludes credential stuffing, password spraying,
and use of third-party credentials.¶
An extension activity identifier MUST be an absolute URI. An unknown unqualified token is structurally invalid. Activity identifiers, including extension URI identifiers, are compared as exact, case-sensitive strings.¶
A rule's state is permitted or prohibited. A permitted rule MUST contain
at least one target_id. A prohibited rule without target_ids applies to
every web Target in Reporting Scope. Every target_id MUST reference an
existing web entry whose state is in; references to products, out entries,
or missing IDs are invalid.¶
For one activity and Target, every applicable rule is collected. Any matching
prohibited rule MUST override every matching permitted rule. No matching
rule means no established Testing Permission.¶
A prohibited rule MUST NOT contain conditions; a prohibition is
unconditional once the rule applies.¶
Version 1 defines these condition members:¶
max_requests_per_second: a positive number;¶
max_concurrent_requests: a positive integer;¶
required_user_agent_token: a non-empty visible-ASCII string matched
case-sensitively as a complete substring of the planned User-Agent; and¶
test_accounts_only: the literal JSON value true.¶
A conditions object MUST contain at least one member. Unknown members in
conditions MUST make the policy structurally invalid.¶
A permitted automated_scanning or fuzzing rule MUST contain both rate and
concurrency limits. A permitted credential_testing rule MUST contain those
two limits and test_accounts_only: true.¶
A condition is satisfied only if every value required from the planned activity is present and compliant. A missing planned value MUST be treated as unsatisfied. A client MUST NOT ignore an unknown condition. Planned rate and concurrency MUST be no greater than their limits, the planned User-Agent MUST contain the required token, and controlled or explicitly provided test accounts MUST be affirmatively confirmed when required.¶
reporting.requested_fields is a unique array containing zero or more of:
affected_asset, vulnerability_type, description, reproduction_steps,
impact, environment, evidence, researcher_contact, and
disclosure_preference.¶
Requested fields are preferences. A missing requested field MUST NOT by itself cause a report to be rejected.¶
reporting.proof_of_exploitation is not_requested, requested_if_safe, or
prohibited. requested_if_safe MUST NOT be interpreted as a request for
access to third-party data, persistence, exfiltration, lateral movement, damage,
availability impairment, or further exploitation.¶
This document does not define report transmission, an intake endpoint, or an attachment protocol.¶
response_targets is optional and contains one or more positive integer values:
acknowledgement_days, initial_assessment_days, and update_interval_days.
Acknowledgement and initial-assessment periods start when the publisher
receives the initial report. Each update interval starts when the preceding
substantive update is sent. They are publisher targets, not guarantees,
deadlines imposed on a reporter, or permission to test.¶
disclosure.approach is required when disclosure is present and is one of
coordinated, case_by_case, or no_preference. The optional
default_days is a positive integer whose period starts when the publisher
receives the initial report. The optional statement is explanatory text. These members express publisher preferences and do not override a
Testing Rule, a condition, or applicable law.¶
Extension identifiers are absolute RFC 3986 URIs compared as exact,
case-sensitive strings. Extension data appears only as values of same-named
members in the extensions object. critical_extensions is an
array of unique extension identifiers. Every identifier in
critical_extensions MUST have a same-named member in extensions.¶
An unknown critical extension MUST produce unsupported-policy during
permission evaluation. An unknown non-critical extension MAY be ignored as
metadata but MUST NOT weaken any core prohibition or condition. An extension
that changes permission, prohibition, or conditions MUST be listed as critical.¶
This revision creates no Activity or extension registry. Such registries can be considered in future work if interoperable extensions are deployed.¶
Policy text MUST be UTF-8 JSON conforming to [RFC8259], with exactly one top-level value, and that value MUST be an object. Parsers MUST reject comments, trailing commas, multiple JSON texts, non-JSON numbers, and duplicate member names at every nesting level. A parser MUST NOT silently normalize duplicate names by retaining an earlier or later value.¶
Automatic discovery and evaluation MUST use a duplicate-aware text parser before ordinary object validation. Duplicate safety cannot be recovered from an object that has already passed through a parser that discarded duplicate names.¶
A validator MUST NOT insert defaults or coerce types. Every core object MUST
reject unknown properties. Extension data is permitted only under
extensions.¶
Validation proceeds through four distinct layers:¶
text parsing and duplicate detection;¶
format-version selection and structural validation;¶
semantic and cross-reference validation; and¶
expiry, supported-extension, Authority, Target, scope, rule, and condition evaluation.¶
The JSON Schema Draft 2020-12 [JSON-SCHEMA-2020-12] schema published with the implementation corpus is a supporting artifact. The normative requirements in this document govern interoperability. An implementation cannot rely on external access to that schema to understand this format.¶
Semantic validation includes timestamp ordering, valid URIs and language tags, document-wide ID uniqueness, scope-host normalization, permitted-rule target references, posture conflicts, required conditions, and critical-extension data presence.¶
A Target is an absolute HTTP or HTTPS URL without userinfo. Product identifiers and URIs with any other scheme are not evaluation Targets. Query and fragment components do not participate in scope matching. The scheme and host are normalized, an omitted port becomes 80 for HTTP or 443 for HTTPS, and the URL pathname is used for path matching.¶
DNS names MUST be converted to lowercase ASCII A-label form as described by [RFC5890] and [RFC5891], and one trailing dot MUST be removed. IPv4 and IPv6 literals MUST be compared in canonical form; IPv6 text representation SHOULD follow [RFC5952].¶
A web scope entry matches only if all of the following hold:¶
the normalized hosts are equal, or include_subdomains is true and the
Target is the named DNS host or a proper subdomain on a label boundary;¶
the normalized scheme appears in schemes;¶
the effective port is present in ports, or ports is absent and the
effective port is the scheme's default; and¶
the normalized pathname matches path_prefix.¶
Scope path_prefix and Target paths MUST use the same normalization. An empty
path becomes /; RFC 3986 dot-segments are removed; repeated slashes are
preserved; hexadecimal digits in percent-encoded triplets are converted to
uppercase; and no percent-encoded octet is decoded for matching. A percent
sign not followed by exactly two hexadecimal digits is invalid. An encoded
slash therefore remains distinct from /.¶
Path matching is case-sensitive. / matches every path. A prefix ending in / matches paths that
start with that prefix. Any other prefix matches the exact path or that prefix
followed by /. Therefore, /api matches /api, /api/, and /api/v1, but
not /apix.¶
Matching scope entries are collected without order-based priority. If any
matching entry is out, the Target is excluded. Otherwise, at least one
matching in web entry is required before rule evaluation. Scope matching for
a subdomain does not establish Authority for that subdomain.¶
An evaluator accepts policy text, a Target, one activity, the planned values
needed by conditions, the set of understood extensions, an evaluation time, and
Authority evidence. Before policy evaluation, it MUST validate and normalize
the Target input. Invalid Target input MUST produce a typed machine-readable
input-validation failure with no evaluation status; it MUST NOT produce
not-covered or any other status from this document.¶
After successful Target input validation, an evaluator MUST execute the following order and return at the first terminal outcome:¶
Parse the policy with duplicate detection. A failure returns
invalid-policy.¶
Check cvd_policy. A missing or non-integer value returns
invalid-policy; an unknown integer version returns unsupported-policy.¶
Perform structural validation. A failure returns invalid-policy.¶
Perform semantic and reference validation. A failure returns
invalid-policy.¶
Check policy expiry. Expiry returns invalid-policy.¶
Check critical extensions and the requested extension activity. Unsupported
critical behavior returns unsupported-policy.¶
Require successfully established Authority evidence and Policy retrieval
evidence bound to its advertised Policy URI. Missing, invalid, or mismatched
evidence returns authority-not-established.¶
Require exact normalized Target and Discovery Host equality. A mismatch
returns authority-not-established.¶
Collect matching Reporting Scope entries.¶
If any matching entry is out, return not-covered; otherwise, if no
matching in web entry exists, return not-covered.¶
Apply Research Posture. report_only or prohibited returns
publisher-stated-prohibited.¶
Collect all rules for the requested activity that apply globally or refer to
a matching in target ID.¶
If any collected rule is prohibited, return
publisher-stated-prohibited.¶
If no collected permitted rule exists, return not-covered. Otherwise,
evaluate all conditions fail closed. If no permitted rule is fully
satisfied, return conditions-not-satisfied.¶
Return publisher-stated-permitted when one or more permitted rules are
fully satisfied. Implementations MAY report all satisfied rule IDs
informatively. Draft 00 defines neither lexicographic rule selection nor
one aggregated constraint object; each satisfied rule retains its own
conditions.¶
A positive status requires a valid unexpired policy, understood critical
behavior, established Authority, exact Target and Discovery Host equality, at
least one matching in web entry, no matching out, no applicable prohibition,
and at least one fully satisfied permitted rule.¶
The primary public result MUST NOT be a Boolean named allowed, authorized,
or safe. Calling software MUST treat every status other than
publisher-stated-permitted as no established Testing Permission. Even
publisher-stated-permitted reports only a publisher statement obtained by the
algorithm above; it is not a determination of legal authorization or safe
harbor.¶
The status is exactly one of:¶
publisher-stated-permitted: every positive precondition is satisfied;¶
publisher-stated-prohibited: posture or an applicable rule prohibits the
activity;¶
not-covered: a syntactically valid normalized Target has no matching in
web scope, matches an out web scope, or has no matching testing rule;¶
authority-not-established: required Authority evidence is absent, invalid,
or bound to another host;¶
conditions-not-satisfied: applicable permission statements exist, but none
has all conditions satisfied;¶
invalid-policy: parsing, structural, semantic, reference, or expiry
validation failed; or¶
unsupported-policy: the format version, critical extension, or requested
extension activity is not understood.¶
The ordered algorithm in Section 11 defines precedence. Array order and implementation iteration order MUST NOT alter the status.¶
Input-validation and policy-processing failures MUST be machine-readable and
identify affected locations when available. Only failures that validate
evaluation-call input, such as an invalid Target URL, are structurally outside
status-bearing evaluation results. Policy parsing, version dispatch,
structural, semantic, reference, and expiry failures remain normal
status-bearing results; in particular, invalid-policy is a normative status. Localized prose MUST NOT be the
only interface contract. Implementations are not required to emit identical
detailed diagnostic identifiers. Such identifiers are informative and
implementation-specific.¶
Publishers should choose expiry periods that force regular review; approximately
one year is a useful operational starting point. They should update
last_updated only for substantive changes and should preserve old policy
versions for incident records when practical.¶
Clients should record the policy representation, retrieval context, evaluation time, status, diagnostic details, and satisfied rules used for a decision. A policy can change during an investigation; a later policy must not silently rewrite the record of an earlier evaluation. Clients should revalidate after cache revalidation, redirects, media-type changes, or expiry.¶
Response and disclosure targets are publisher statements. Operational tooling must not present them as service guarantees or as conditions imposed on a reporter.¶
RFC Editor: remove this entire section and the informative reference to RFC 7942 before publication.¶
This section records known implementation status at the time of posting, as recommended by [RFC7942]. Its purpose is to assist IETF review; listing an implementation does not imply IETF endorsement or independently verified interoperability.¶
The specification commit
a7e359ac2bc2efbc89febc7c4a5cd42dec03eade contains the Version 1 source,
Draft 2020-12 schema, examples, and a conformance corpus mapping all 71
normative requirement IDs to executable checks [CVD-POLICY-SPEC]. The
TypeScript reference implementation commit
acc609efc4adc33683cc6c71acd57a8a8e06169b exposes the isolated
@cvd-policy/core/v1 entry point and executes that corpus
[CVD-POLICY-CORE]. It validates supplied retrieval evidence but does not
perform network retrieval or OpenPGP signature verification. The package root,
CLI, and website retain their published 0.x behavior.¶
At the 2026-09-01 build, both referenced GitHub commit URLs returned HTTP 200 and the artifacts were publicly inspectable. This Implementation Status evidence is ready for Datatracker submission. The references require revalidation immediately before submission.¶
Policies and security.txt files are untrusted input. Implementations MUST fail
closed for every security-sensitive ambiguity in parsing, Authority, scope,
rule matching, conditions, and extensions.¶
Duplicate member names can produce inconsistent interpretations between parsers. Clients need duplicate-aware parsing before ordinary object mapping, must reject non-JSON syntax, and should bound input size, nesting depth, member count, string length, and total validation errors. Such limits mitigate memory, CPU, stack, and denial-of-service attacks involving overly complex documents.¶
Automatic retrieval creates SSRF and redirect risks. Clients should enforce HTTPS at every hop, finite redirect and response limits, timeouts, address filtering before every connection, DNS-rebinding defenses, and credential isolation. They should reject HTTPS downgrades and unexpected media types.¶
A compromised Policy URI host can alter or suppress a policy. A compromised Discovery Host can advertise a malicious policy for that exact host. Authority records what the Discovery Host published; it does not prove ownership, organizational control, or legal permission. High-risk decisions need independent corroboration.¶
Claims about third-party targets are not permission. Exact Discovery Host binding prevents parent-domain, subdomain, CNAME, shared-address, shared- certificate, and organization-name confusion from transferring Authority. Clients should display normalized A-label hostnames and, when useful, their Unicode presentation to reduce IDNA and Unicode confusion.¶
Caches can preserve withdrawn or stale statements. Clients must honor both
policy and security.txt expiry and should retain the exact policy used during
an investigation. Unknown conditions fail validation or remain unsatisfied;
unknown critical extensions produce unsupported-policy; and non-critical
extensions cannot weaken core prohibitions.¶
No status in this document determines that an activity is legally authorized.
publisher-stated-permitted is not guaranteed safe harbor and does not remove
the need to assess law, contracts, third-party rights, safety, or operational
risk.¶
Implementations MUST NOT transmit policy text, security.txt text, Target
details, or researcher plan data unless a user explicitly invokes a network
operation that requires the transmission.¶
A policy can reveal internal asset names, product identifiers, test systems, or contact addresses. This document does not require a publisher to disclose confidential infrastructure. Publishers should provide only information they intend to make public and should consider role-based rather than personal contact addresses.¶
Policy retrieval can be logged by the Discovery Host, Policy URI host, intermediaries, and DNS operators. Per-client URLs, tokens, query parameters, or similar registration mechanisms can track researchers and should be avoided. A client should disclose the destination of every network request and minimize retention of retrieval and evaluation data.¶
Nothing in this document requires a researcher to publish or transmit identity
information. researcher_contact is only a reporting preference, and omission
alone cannot justify rejecting a report. Contact information in policies can
be personal data and should be processed with appropriate minimization and
retention controls.¶
This document requests the following registrations. The field and media type remain proposed until IANA completes the applicable actions.¶
IANA is requested to add this entry to the "security.txt Fields" registry:¶
IANA is requested to add this entry to the "Media Types" registry, using the template defined by [RFC6838] and the structured-syntax suffix rules in [RFC6839]:¶
application¶
cvd-policy+json¶
N/A¶
N/A¶
binary; representations are UTF-8 JSON as required by this document and [RFC8259]¶
Policy representations are untrusted JSON and can influence decisions about
active security testing. Implementations need duplicate-aware parsing,
strict schema and semantic validation, bounded resource use, expiry checks,
exact Authority and Policy-URI binding, fail-closed rule processing, and
rejection of unknown critical behavior. Automatic retrieval introduces
SSRF, redirect, credential-disclosure, DNS-rebinding, and stale-cache risks;
clients are required to use HTTPS, avoid ambient credentials, and accept only
complete 200 OK representations; they are advised to enforce finite limits
and filter unsafe network destinations. No result establishes ownership, legal authorization,
safe harbor, or operational safety. See Section 15.¶
Interoperability requirements are specified throughout this document, including strict parsing, validation, Authority, scope matching, condition processing, and extension handling.¶
This document.¶
CVD policy publishers, validators, discovery clients, and policy evaluators.¶
The syntax and semantics of fragment identifiers for this +json media type
follow application/json as specified by [RFC6839]. At the time of this
registration, application/json defines no fragment identifier syntax; if
such syntax is defined in the future, it applies to this media type unless a
later specification states otherwise. Independently, a Policy URI carried
by CVD-Policy cannot contain a fragment.¶
Deprecated alias names: none. Magic number(s): none. File extension(s): none. Macintosh file type code(s): none.¶
Ben Luca Behring, behring@skalvar.de; Marco Berg, berg@skalvar.de.¶
COMMON¶
none¶
IETF¶
IETF¶
no¶
This document does not request registration of a well-known URI suffix. It also does not create an Activity or extension registry in Draft 00.¶
All policy examples in this section are complete documents. The Draft build checks them with the Version 1 schema and semantic validator. Evaluation outcomes are tied to named conformance-corpus vectors.¶
{
"cvd_policy": 1,
"last_updated": "2026-08-29T08:00:00Z",
"expires": "2027-02-28T08:00:00Z",
"organization": {
"name": "Example Organization"
},
"contact": {
"channels": [
"mailto:security@example.com"
]
},
"research": {
"posture": "report_only"
},
"reporting_scope": {
"web": [
{
"id": "main-web",
"state": "in",
"host": "example.com",
"schemes": [
"https"
],
"path_prefix": "/",
"include_subdomains": false
}
]
},
"reporting": {
"requested_fields": [
"affected_asset",
"description"
],
"proof_of_exploitation": "not_requested"
}
}
¶
This policy accepts reports but contains no Testing Permission.¶
{
"cvd_policy": 1,
"last_updated": "2026-08-29T08:00:00Z",
"expires": "2027-02-28T08:00:00Z",
"organization": {
"name": "Example Organization",
"uri": "https://example.com/"
},
"contact": {
"channels": [
"mailto:security@example.com",
"https://example.com/security/contact"
],
"preferred_languages": [
"en",
"de"
],
"encryption": [
"https://example.com/security/pgp-key.txt"
]
},
"research": {
"posture": "limited",
"statement": "Security research is welcome only under the rules below."
},
"reporting_scope": {
"web": [
{
"id": "main-web",
"state": "in",
"host": "example.com",
"schemes": [
"https"
],
"ports": [
443
],
"path_prefix": "/",
"include_subdomains": false
}
]
},
"testing": {
"rules": [
{
"id": "automated-scan-main",
"activity": "automated_scanning",
"state": "permitted",
"target_ids": [
"main-web"
],
"conditions": {
"max_requests_per_second": 2,
"max_concurrent_requests": 2,
"required_user_agent_token": "security-research"
}
}
]
},
"reporting": {
"requested_fields": [
"affected_asset",
"description",
"reproduction_steps",
"impact"
],
"proof_of_exploitation": "not_requested"
},
"response_targets": {
"acknowledgement_days": 2,
"initial_assessment_days": 10,
"update_interval_days": 14
},
"disclosure": {
"approach": "coordinated",
"default_days": 90
},
"critical_extensions": [],
"extensions": {}
}
¶
For the plan in corpus vector conditions-rate-exceeded, a planned rate of 3
requests per second exceeds the limit of 2. The normative result is
conditions-not-satisfied.¶
The minimal policy in Section 18.1 can be hosted by a provider. The
Discovery Host retains Authority because its own assessed security.txt
contains the reference:¶
Contact: mailto:security@example.com Policy: https://example.com/security-policy CVD-Policy: https://policies.provider.example/example.json Expires: 2027-02-28T08:00:00Z Canonical: https://example.com/.well-known/security.txt¶
The provider host does not gain Authority over example.com.¶
{
"cvd_policy": 1,
"last_updated": "2026-08-29T08:00:00Z",
"expires": "2027-02-28T08:00:00Z",
"organization": {
"name": "Example Organization"
},
"contact": {
"channels": [
"mailto:security@example.com"
]
},
"research": {
"posture": "limited"
},
"reporting_scope": {
"web": [
{
"id": "example",
"state": "in",
"host": "example.com",
"schemes": [
"https"
],
"path_prefix": "/",
"include_subdomains": true
}
]
},
"testing": {
"rules": [
{
"id": "manual",
"activity": "manual_testing",
"state": "permitted",
"target_ids": [
"example"
]
}
]
},
"reporting": {
"requested_fields": [
"affected_asset",
"description"
],
"proof_of_exploitation": "not_requested"
}
}
¶
example.com and api.example.com each publish their own security.txt and
both point to https://policies.provider.example/shared.json. Separate
Authority evidence is required for each exact Discovery Host. The
include_subdomains member affects scope matching only.¶
{
"cvd_policy": 1,
"last_updated": "2026-08-29T08:00:00Z",
"expires": "2027-02-28T08:00:00Z",
"organization": {
"name": "Example Organization",
"uri": "https://example.com/"
},
"contact": {
"channels": [
"mailto:security@example.com",
"https://example.com/security/contact"
],
"preferred_languages": [
"en",
"de"
],
"encryption": [
"https://example.com/security/pgp-key.txt"
]
},
"research": {
"posture": "limited",
"statement": "Security research is welcome only under the rules below."
},
"reporting_scope": {
"web": [
{
"id": "main-web",
"state": "in",
"host": "example.com",
"schemes": [
"https"
],
"ports": [
443
],
"path_prefix": "/",
"include_subdomains": false
},
{
"id": "excluded",
"state": "out",
"host": "example.com",
"schemes": [
"https"
],
"path_prefix": "/",
"include_subdomains": false
}
]
},
"testing": {
"rules": [
{
"id": "automated-scan-main",
"activity": "automated_scanning",
"state": "permitted",
"target_ids": [
"main-web"
],
"conditions": {
"max_requests_per_second": 2,
"max_concurrent_requests": 2,
"required_user_agent_token": "security-research"
}
}
]
},
"reporting": {
"requested_fields": [
"affected_asset",
"description",
"reproduction_steps",
"impact"
],
"proof_of_exploitation": "not_requested"
},
"response_targets": {
"acknowledgement_days": 2,
"initial_assessment_days": 10,
"update_interval_days": 14
},
"disclosure": {
"approach": "coordinated",
"default_days": 90
},
"critical_extensions": [],
"extensions": {}
}
¶
For https://example.com/, both entries match. The out entry wins regardless
of array order, producing the normative status not-covered as specified by
corpus vector scope-out-wins.¶