<?xml version="1.0" encoding="utf-8"?>
<!-- name="GENERATOR" content="github.com/mmarkdown/mmark Mmark Markdown Processor - mmark.miek.nl" -->
<rfc version="3" ipr="trust200902" docName="draft-hopkins-evp-spec-06" submissionType="independent" category="info" xml:lang="en" xmlns:xi="http://www.w3.org/2001/XInclude" indexInclude="true">

<front>
<title abbrev="evp-spec">Evidence Package Format Specification: Storing Evidence from Software Testing</title><seriesInfo value="draft-hopkins-evp-spec-06" stream="independent" status="informational" name="Internet-Draft"></seriesInfo>
<author initials="L." surname="Hopkins" fullname="Lily Hopkins"><organization></organization><address><postal><street></street>
</postal><email>lily@hpkns.uk</email>
<uri>https://github.com/lilopkins</uri>
</address></author><author surname="Turner" fullname="Eden Turner"><organization></organization><address><postal><street></street>
</postal><email>somebirb7190@gmail.com</email>
<uri>https://github.com/Some-Birb7190</uri>
</address></author><date/>
<area></area>
<workgroup></workgroup>
<keyword>evp</keyword>
<keyword>evidence</keyword>
<keyword>format</keyword>
<keyword>specification</keyword>

<abstract>
<t>Taking evidence is a key part of any robust software testing process.
This specification defines a format which collects evidence together
and stores metadata and annotations in an organised fashion from both
manual and automated testing sources.</t>
<t>This work is not a standard and does not enjoy community consensus.</t>
</abstract>

</front>

<middle>

<section anchor="introduction"><name>Introduction</name>

<section anchor="purpose"><name>Purpose</name>
<t>The purpose of this specification is to define a format for storage of
evidence produced as the result of software testing that:</t>

<ul spacing="compact">
<li>allows for basic collation of evidence;</li>
<li>can store any kind of file type that might be produced;</li>
<li>stores data compressed;</li>
<li>stores related evidence together, but allows for dividing up by test
case;</li>
<li>allows test evidence to be attested with a tracable list of attestors,
and;</li>
<li>is built upon widely available standards.</li>
</ul>
<t>The format does not attempt to:</t>

<ul spacing="compact">
<li>act as an captioned archiving solution for other purposes outside of
software testing, even if it may be suitable for them.</li>
</ul>
</section>

<section anchor="intended-audience"><name>Intended Audience</name>
<t>This specification is intended for those who might wish to write their
own implementation of the evidence package format. There are a number of
situations where writing an implementation may be desirable:</t>

<ul spacing="compact">
<li>in an automation tool that runs a number of operations to
automatically test something, to produce an evidence package
containing the results of the automated testing;</li>
<li>in a manual evidence collection tool, where a user might want to
collect evidence in a single, easy to manage place for later
processing or sharing;</li>
<li>in an analysis tool, to view, annotate, share and understand the
evidence from previous testing;</li>
<li>in a viewer, to view evidence that has been shared, for example from a
testing team to a customer, or;</li>
<li>any other situation where it may be desirable to collect test evidence
and bundle it together for later.</li>
</ul>
</section>

<section anchor="changes-from-previous-versions"><name>Changes from Previous Versions</name>
<t>This document forms the original accepted specification.</t>
</section>
</section>

<section anchor="terminology"><name>Terminology</name>
<t>The key words &quot;<bcp14>MUST</bcp14>&quot;, &quot;<bcp14>MUST NOT</bcp14>&quot;, &quot;<bcp14>REQUIRED</bcp14>&quot;, &quot;<bcp14>SHALL</bcp14>&quot;,
&quot;<bcp14>SHALL NOT</bcp14>&quot;, &quot;<bcp14>SHOULD</bcp14>&quot;, &quot;<bcp14>SHOULD NOT</bcp14>&quot;, &quot;<bcp14>RECOMMENDED</bcp14>&quot;,
&quot;<bcp14>NOT RECOMMENDED</bcp14>&quot;, &quot;<bcp14>MAY</bcp14>&quot;, and &quot;<bcp14>OPTIONAL</bcp14>&quot; in this document
are to be interpreted as described in BCP 14 <xref target="RFC2119"></xref> <xref target="RFC8174"></xref>
when, and only when, they appear in all capitals, as shown here.</t>
</section>

<section anchor="specification"><name>Specification</name>
<t>An evidence package is a structured ZIP archive <xref target="zip"></xref> using deflate
compression. It <bcp14>MUST</bcp14> contain the file &quot;manifest.json&quot;, and the
directories &quot;media&quot; and &quot;test_cases&quot; internally within the ZIP archive.
This structure does not need to be represented outside of the ZIP
archive and as such the internal structure does not need to be
understood by an end-user of any tool that works with evidence packages.</t>
<t>See <xref target="example-archive"></xref> for an example of the file's internal structure.</t>

<section anchor="manifest-json-file"><name>&quot;manifest.json&quot; File</name>
<t>The manifest.json file defines metadata relating to the entire package
of evidence. It <bcp14>MUST</bcp14> be a UTF-8 encoded, LF line ended, JSON
<xref target="RFC8259"></xref> file with the following elements:</t>
<table>
<thead>
<tr>
<th>Element</th>
<th>Condition</th>
<th>Type</th>
<th>Section</th>
<th>Description</th>
</tr>
</thead>

<tbody>
<tr>
<td>$schema</td>
<td>Optional</td>
<td>String</td>
<td><xref target="manifest-schema"></xref></td>
<td>The $schema element <bcp14>MAY</bcp14> point to a copy of the schema for the manifest.</td>
</tr>

<tr>
<td>metadata</td>
<td>Mandatory</td>
<td>Object</td>
<td><xref target="manifest-metadata"></xref></td>
<td>The metadata element stores package metadata.</td>
</tr>

<tr>
<td>custom_metadata</td>
<td>Mandatory</td>
<td>Object</td>
<td><xref target="manifest-custom-metadata"></xref></td>
<td>Custom metadata fields for test cases in this package.</td>
</tr>

<tr>
<td>media</td>
<td>Mandatory</td>
<td>Array</td>
<td><xref target="manifest-media"></xref></td>
<td>The media element stores a list of media files that are stored in this evidence package.</td>
</tr>

<tr>
<td>test_cases</td>
<td>Mandatory</td>
<td>Array</td>
<td><xref target="manifest-test-cases"></xref></td>
<td>The test_cases element stores a list of test cases.</td>
</tr>
</tbody>
</table><t>See an example manifest.json file in <xref target="example-manifest"></xref>.</t>

<section anchor="manifest-schema"><name>&quot;$schema&quot; Element</name>
<t>This element <bcp14>MAY</bcp14> optionally be provided to point to a JSON schema
describing the structure of the file. This is typically most useful for
validation, however it <bcp14>MUST</bcp14> be acceptable for it to be missing, and
this specification should be seen as the primary definition of structure
over anything defined in the linked schema.</t>
<t>The JSON schema provided at by this element may give details about any
additional fields used that are not defined in this specficiation.</t>
</section>

<section anchor="manifest-metadata"><name>&quot;metadata&quot; Element</name>
<table>
<thead>
<tr>
<th>Element</th>
<th>Condition</th>
<th>Type</th>
<th>Section</th>
<th>Description</th>
</tr>
</thead>

<tbody>
<tr>
<td>title</td>
<td>Mandatory</td>
<td>String</td>
<td></td>
<td>The name of the evidence package.</td>
</tr>

<tr>
<td>authors</td>
<td>Mandatory</td>
<td>Array</td>
<td><xref target="manifest-metadata-authors"></xref></td>
<td>The authors attributed to this evidence package.</td>
</tr>
</tbody>
</table>
<section anchor="manifest-metadata-authors"><name>&quot;authors&quot; Array Element</name>
<table>
<thead>
<tr>
<th>Element</th>
<th>Condition</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>

<tbody>
<tr>
<td>name</td>
<td>Mandatory</td>
<td>String</td>
<td>The author's name.</td>
</tr>

<tr>
<td>email</td>
<td>Optional</td>
<td>String/Null</td>
<td>The author's email address, although format is not verified.</td>
</tr>
</tbody>
</table></section>
</section>

<section anchor="manifest-custom-metadata"><name>&quot;custom_metadata&quot; Element</name>
<t>Elements within this object will become custom metadata properties for
test cases in this package. Each object <bcp14>MUST</bcp14> have the following
fields:</t>
<table>
<thead>
<tr>
<th>Element</th>
<th>Condition</th>
<th>Type</th>
<th>Section</th>
<th>Description</th>
</tr>
</thead>

<tbody>
<tr>
<td>name</td>
<td>Mandatory</td>
<td>String</td>
<td></td>
<td>The name of this custom metadata field.</td>
</tr>

<tr>
<td>description</td>
<td>Mandatory</td>
<td>String</td>
<td><xref target="manifest-metadata-authors"></xref></td>
<td>The description of this custom metadata field.</td>
</tr>

<tr>
<td>primary</td>
<td>Mandatory</td>
<td>Boolean</td>
<td><xref target="manifest-custom-metadata-primary"></xref></td>
<td>Is this custom field primary?</td>
</tr>
</tbody>
</table>
<section anchor="manifest-custom-metadata-primary"><name>&quot;primary&quot; Boolean</name>
<t>The &quot;primary&quot; value of custom metadata fields <bcp14>MAY</bcp14> be false for all
fields, or <bcp14>MAY</bcp14> be true for exactly one field. It <bcp14>MUST NOT</bcp14> be
true for more than one field.</t>
<t>The purpose of primary is not enforced as part of this specification,
however it should be seen as suggesting that one custom metadata field
is more useful than others, and as such may be used to influence the
information displayed to users, for example an implementor might choose
to show the primary custom metadata value for each test case alongside
it.</t>
</section>
</section>

<section anchor="manifest-media"><name>&quot;media&quot; Array Element</name>
<table>
<thead>
<tr>
<th>Element</th>
<th>Condition</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>

<tbody>
<tr>
<td>sha256_checksum</td>
<td>Mandatory</td>
<td>String</td>
<td>The SHA256 checksum of the associated media file.</td>
</tr>

<tr>
<td>mime_type</td>
<td>Mandatory</td>
<td>String</td>
<td>The Internet Media Type <xref target="RFC2046"></xref> of the associated media file.</td>
</tr>
</tbody>
</table></section>

<section anchor="manifest-test-cases"><name>&quot;test_cases&quot; Array Element</name>
<table>
<thead>
<tr>
<th>Element</th>
<th>Condition</th>
<th>Type</th>
<th>Section</th>
<th>Description</th>
</tr>
</thead>

<tbody>
<tr>
<td>id</td>
<td>Mandatory</td>
<td>String</td>
<td></td>
<td>The UUID of the test case. If present here, there <bcp14>MUST</bcp14> be an associated test case file in the &quot;test_cases&quot; directory of the package with the name &quot;&lt;UUID&gt;.json&quot;.</td>
</tr>

<tr>
<td>attestations</td>
<td>Mandatory</td>
<td>Array of Strings</td>
<td><xref target="manifest-test-case-attestations"></xref></td>
<td>An array of attestations over this test case.</td>
</tr>
</tbody>
</table>
<section anchor="manifest-test-case-attestations"><name>&quot;attestations&quot; Array</name>
<t>The elements within the &quot;attestations&quot; array <bcp14>MUST</bcp14> be JWS payload
un-encoded (detatched) <xref target="RFC7515"></xref> signatures. The signature payload
should be a copy of the test case manifest (i.e. the file &quot;uuid.json&quot;),
having been processed into JSON canonical format as defined in
<xref target="RFC8785"></xref>.</t>
<t>As a worked example, a manifest test_cases entry may start off like
this:</t>

<sourcecode type="json"><![CDATA[{
  "id": "7928de11-8de8-4bfe-b5b7-cbf07c7066d9",
  "attestations": [],
  "some_other_value": "Added from somewhere other than this specification!"
}
]]></sourcecode>
<t>This should then have the &quot;attestations&quot; array removed and should be
canonicalised:</t>

<sourcecode type="json"><![CDATA[{"id":"7928de11-8de8-4bfe-b5b7-cbf07c7066d9","sha256_checksum":"a2394af8d2b4e0c9ba66e797fd6060f4e6932e126f781157cdd2dda1c08b4b6f","some_other_value":"Added from somewhere other than this specification!"}
]]></sourcecode>
<t>This can now be signed and the original manifest can be modified:</t>

<sourcecode type="json"><![CDATA[{
  "id": "7928de11-8de8-4bfe-b5b7-cbf07c7066d9",
  "attestations": [
    "owGbwMvMwMH4dr363nNHa04wnj4glMSQ8Va7t1opM0XJSsnc0sgiJdXQUBdIWuiaJKWl6iaZJpnrJielGZgnmxuYmaVYKukoFWckGpmaxSdnpCZnF5fmAjUmGhlbmiSmWaQYJZmkGiRbJiWamaWaW5qnpZgZmBmkmaSaWRobpRoamaWZWxgamponp6QYpaQkGiYbWCSZJJmlgQzNz02Nzy/JSC2KL0vMKU0FmuqYkpKaopBWlJ+rAJItB8qlKoCVKJRkJOYBicxiheKC1OTMtMzkxJLM/DxFpVquTiZ/FgZGDgZLMUWWicUSryIWnPnu39fyEuZrViaQj6VFGhiAgIWBLzcxr9RIx0jPVNtQz9BQB8hk4OIUgKm+ms7/P9xXe5oSl6nYW1fJ9S9/bhZN3br8oWev2A8R+Vs7ODd88xUxncHVvIltQXxwed3mm9xiu/+LV/oYqB1KuvI4+cittFtcj3KUpqd3RikEzfM7mcXDv+5x9lKuG4e+f2Hk8zmt9Tybf62wZL/+RzXzoMtqa4LeFUcWNBQ9X81+R6z+0J7PfOJvuC+8SetJl7B+pyj+4W/S+S0+2/LTn5fUC+jPuLTdWfD+r0kThHjbVCYk9td/6JVKmPP88A2bN+fyRDc9qH/Sv4Nr3bRDi1xbDJmKFxmenJfYOGf3cpEJG0XbrN1u7rwmvqzipljKJteU/MxUe1aHh0IzwgWXHV3XwxnAcnu/4IfWq368knX5D2XkK4ovnGdZeKft+clmubgcFUGGgKqVcYvKzr/nKZpl0adm7rvFty1XhTP7xN/UnM8Z+4ymFURfc5vvbayupqC0pDnSa93EW5MnBFSoOImE+e5TNxdnXli6uflNZsXkj8wxBgvfTxVTuX/AmCvkjHkc178tHyu2uQWtFze6+PmCXJ5B9BnjJ7ZH3ZnmLZ8rpPv03v44j6PZd0R8Wf74l7WpHc56EBrF+ZNr00J3eTOTxQIvu/7vU/1bqdCWd0HugGdVwuSoqmPa0f/v7Fl8L2bPLasPrrOyZ4Xtse3t2pDp9qpgCUfjfr26Cl57/rs8njkf3ygUJIiHFZ/5ujGrPftRce6Gpft3SbS8+gIA"
  ],
  "some_other_value": "Added from somewhere other than this specification!"
}
]]></sourcecode>
</section>
</section>
</section>

<section anchor="test-cases-directory"><name>&quot;test_cases&quot; Directory</name>
<t>The test cases directory stores the manifests for each test case within
this evidence package.</t>
<t>Each test case is stored as a JSON file, with a UUIDv4 name <xref target="RFC9562"></xref>.
A test case present here <bcp14>MUST</bcp14> have a valid entry in the manifest
&quot;test_cases&quot; array defined in <xref target="manifest-test-cases"></xref>.</t>

<section anchor="uuid-json-file"><name>&quot;&lt;uuid&gt;.json&quot; File</name>
<table>
<thead>
<tr>
<th>Element</th>
<th>Condition</th>
<th>Type</th>
<th>Section</th>
<th>Description</th>
</tr>
</thead>

<tbody>
<tr>
<td>$schema</td>
<td>Optional</td>
<td>String</td>
<td><xref target="test-case-schema"></xref></td>
<td>The $schema element <bcp14>MAY</bcp14> point to a copy of the schema for the manifest.</td>
</tr>

<tr>
<td>metadata</td>
<td>Mandatory</td>
<td>Object</td>
<td><xref target="test-case-metadata"></xref></td>
<td>The metadata relating to this test case.</td>
</tr>

<tr>
<td>evidence</td>
<td>Mandatory</td>
<td>Array</td>
<td><xref target="test-case-evidence"></xref></td>
<td>The evidence within this test case.</td>
</tr>
</tbody>
</table><t>See an example &lt;uuid&gt;.json file in <xref target="example-test-case"></xref>.</t>

<section anchor="test-case-schema"><name>&quot;$schema&quot; Element</name>
<t>This element <bcp14>MAY</bcp14> optionally be provided to point to a JSON schema
describing the structure of the file. This is typically most useful for
validation, however it <bcp14>MUST</bcp14> be acceptable for it to be missing, and
this specification should be seen as the primary definition of structure
over anything defined in the linked schema.</t>
<t>The JSON schema provided at by this element may give details about any
additional fields used that are not defined in this specficiation.</t>
</section>

<section anchor="test-case-metadata"><name>&quot;metadata&quot; Element</name>
<table>
<thead>
<tr>
<th>Element</th>
<th>Condition</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>

<tbody>
<tr>
<td>title</td>
<td>Mandatory</td>
<td>String</td>
<td>The title of the test case.</td>
</tr>

<tr>
<td>execution_datetime</td>
<td>Mandatory</td>
<td>String</td>
<td>The ISO8601 date and time of the execution of this test case starting.</td>
</tr>

<tr>
<td>passed</td>
<td>Optional</td>
<td>Enumerated</td>
<td>The state of the test case, if present <bcp14>MUST</bcp14> be either the string &quot;pass&quot; or &quot;fail&quot;, or null. If absent, it <bcp14>MUST</bcp14> be interpreted as null.</td>
</tr>

<tr>
<td>custom</td>
<td>Mandatory</td>
<td>Object</td>
<td>Custom metadata values.</td>
</tr>
</tbody>
</table><t>The &quot;custom&quot; field is used to add custom metadata that has been
specified in the package manifest's &quot;custom_metadata&quot; field.
If a value is specified in &quot;custom&quot;, it <bcp14>MUST</bcp14> be present in the
package manifest, but all values in the package manifest do not need to
be present here. All values <bcp14>MUST</bcp14> be strings and are stored as a
simple key-value map, with the custom field ID defined in the manifest
as the key.</t>
</section>

<section anchor="test-case-evidence"><name>&quot;evidence&quot; Array Element</name>
<table>
<thead>
<tr>
<th>Element</th>
<th>Condition</th>
<th>Type</th>
<th>Section</th>
<th>Description</th>
</tr>
</thead>

<tbody>
<tr>
<td>kind</td>
<td>Mandatory</td>
<td>String</td>
<td><xref target="evidence-kind"></xref></td>
<td>The Internet Media Type <xref target="RFC2046"></xref> of data stored.</td>
</tr>

<tr>
<td>value</td>
<td>Mandatory</td>
<td>String</td>
<td><xref target="evidence-value"></xref></td>
<td>The data stored within this piece of evidence.</td>
</tr>

<tr>
<td>caption</td>
<td>Optional</td>
<td>String/Null</td>
<td></td>
<td>An optional caption for this piece of evidence.</td>
</tr>

<tr>
<td>original_filename</td>
<td>Optional</td>
<td>String/Null</td>
<td></td>
<td>The original filename.</td>
</tr>
</tbody>
</table>
<section anchor="evidence-kind"><name>&quot;kind&quot;</name>
<t>The &quot;kind&quot; of evidence <bcp14>MUST</bcp14> be an Internet Media Type <xref target="RFC2046"></xref>.</t>
<t>For more information about each type, see <xref target="kinds-of-evidence"></xref>.</t>
</section>

<section anchor="evidence-value"><name>&quot;value&quot;</name>
<t>The &quot;value&quot; <bcp14>MUST</bcp14> be one of the following acceptable patterns:</t>

<ul spacing="compact">
<li>&quot;plain:&quot; followed by plain text;</li>
<li>&quot;media:&quot; followed by a media file SHA256 hash, or;</li>
<li>&quot;base64:&quot; followed by a base64 string of data without padding.</li>
</ul>
</section>
</section>
</section>
</section>

<section anchor="media-directory"><name>&quot;media&quot; Directory</name>
<t>The &quot;media&quot; directory stores data in files within the ZIP archive that
would be otherwise impractical to store directly in the test cases.</t>
<t>Files stored in this directory are of abitrary type. They <bcp14>MUST</bcp14> be
named by their SHA256 checksum <xref target="RFC6234"></xref> with no extension. Their
SHA256 checksum and media type <bcp14>MUST</bcp14> be stored in the package
manifest &quot;media&quot; element.</t>
<t>In the unlikely event that there is a checksum clash, there is currently
no preferred method for resolving this. The probability of such a
situation is decided to be acceptably low given the expected size and
number of files stored in an evidence package, however implementors
<bcp14>MAY</bcp14> choose to store the clashing file as base64 data instead of as
an additional media file.</t>
</section>
</section>

<section anchor="handling-an-evidence-package"><name>Handling an Evidence Package</name>

<section anchor="locking"><name>Locking</name>
<t>When loading an evidence package, implemetors <bcp14>MUST</bcp14> use a lock file
with the file name &quot;.~lock.&quot; followed by the full name of the package it
protects, followed by &quot;#&quot;, for example for a package called
&quot;example.evp&quot;, the lock file <bcp14>MUST</bcp14> be called &quot;.~lock.example.evp#&quot;.
It <bcp14>MUST</bcp14> be located adjacent (in the same directory as) the evidence
package. The file <bcp14>MUST</bcp14> contain the process ID of the process holding
the lock.</t>
<t>The lock file should be considered as locking the package if it is
present, regardless of contents.</t>
<t>If either of these is not the case, it should be assumed that the there
is no current lock over the package.</t>
</section>

<section anchor="media-loading"><name>Media Loading</name>
<t>Software implementing the evidence package format <bcp14>MUST NOT</bcp14> load
files from the &quot;media&quot; directory into memory until it is needed for
display or for extraction. Implementors <bcp14>MUST</bcp14> use streams to load
media files to avoid trying to load the entire file into memory as it
may not fit.</t>
</section>
</section>

<section anchor="kinds-of-evidence"><name>Kinds of Evidence</name>
<t>Evidence packages can support any valid Internet Media Type <xref target="RFC2046"></xref>
as evidence. Implementors of this specification <bcp14>MUST</bcp14> be able to
display the following types:</t>
<table>
<thead>
<tr>
<th>Media Type</th>
<th>Description</th>
</tr>
</thead>

<tbody>
<tr>
<td>text/plain</td>
<td>Plain text with no formatting.</td>
</tr>

<tr>
<td>text/markdown</td>
<td>Text with markdown support.</td>
</tr>

<tr>
<td>text/vnd.angel.http-data</td>
<td>An HTTP request/response pair.</td>
</tr>

<tr>
<td>image/*</td>
<td>An image that should be rendered where possible.</td>
</tr>
</tbody>
</table><t>Common image formats <bcp14>SHOULD</bcp14> be rendered where possible, but it is
not required to support every possible type of image.</t>
<t>Markdown <bcp14>SHOULD</bcp14> be rendered where possible, but it may be adapted
for security reasons. If it is changed before display, a notice <bcp14>MUST</bcp14>
be displayed to the user disclosing that it has been adjusted for
security. For example, it is acceptable to strip raw HTML tags before
rendering.</t>
<t>Other media types <bcp14>MUST</bcp14> be supported insofar as being able to extract
the data from the evidence package so that they can be opened in other
software.</t>

<section anchor="http-requests"><name>HTTP Requests</name>
<t>Where text/vnd.angel.http-data is used, an HTTP request and
response <bcp14>MUST</bcp14> be present in plain text, and a Record Separator
character (0x1e) <bcp14>MUST</bcp14> be used to split the request and response
portion. In other words, the format <bcp14>MUST</bcp14> comply with the following
regular expression:</t>

<sourcecode type="regex"><![CDATA[^(?<request>[.\r\n]*)\x1e(?<response>[.\r\n]*)$
]]></sourcecode>
<t>For example the separator is present at <em>1</em>:</t>

<sourcecode type="http">GET / HTTP/1.1
Host: example.com
User-Agent: HTTPie

\x1eHTTP/1.1 200 OK &lt;1&gt;
Cache-Control: max-age=1366
Connection: close
...
</sourcecode>
</section>
</section>

<section anchor="extending-behaviours-of-an-evidence-package"><name>Extending Behaviours of an Evidence Package</name>
<t>Every JSON file within an evidence package <bcp14>MAY</bcp14> have new fields
added, and as such extended behaviours <bcp14>MAY</bcp14> be implemented, however
implementors <bcp14>MUST</bcp14> be able to load an evidence package without these
additional fields.</t>
<t>When an implementor loads a file with fields it cannot understand, it
<bcp14>MUST</bcp14> retain the fields on saving the file.</t>
</section>

<section anchor="iana-considerations"><name>IANA Considerations</name>
<t>This document acts as the specification for the media type
application/vnd.angel.evidence-package. Additionally, the media type
text/vnd.angel.http-data is defined in <xref target="http-requests"></xref>.</t>
</section>

<section anchor="security-considerations"><name>Security Considerations</name>
<t>The evidence package format can store arbitrary files that may or may
not be executable. Implementors <bcp14>MUST NOT</bcp14> execute any file contained
within and <bcp14>SHALL</bcp14> only extract the contained files if needed.</t>
<t>Otherwise, there are no concerns for security from the file type itself.</t>
</section>

</middle>

<back>
<references><name>Normative References</name>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.2046.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.2119.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.6234.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7515.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8174.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8259.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8785.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9562.xml"/>
<reference anchor="zip" target="https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT">
  <front>
    <title>.ZIP File Format Specification</title>
    <author>
      <organization>PKWARE, Inc.</organization>
    </author>
    <date year="2022" month="November" day="01"></date>
  </front>
</reference>
</references>

<section anchor="example-archive"><name>Example Archive Layout</name>

<artwork><![CDATA[example.evp
 |- manifest.json
 |- media
 |   \- 203073da0b36a5921f2914e2093abcae7eb987846f405b438c25792bab1617fa
 \- test_cases
     \- eabb5d31-a958-4609-ac98-83365e14d18b.json
]]></artwork>
</section>

<section anchor="example-manifest"><name>Example Package Manifest JSON</name>

<sourcecode type="json"><![CDATA[{
  "metadata": {
    "title": "Example Evidence Package",
    "authors": [
      {
        "name": "Anonymous Author"
      },
      {
        "name": "Lily Hopkins",
        "email": "lily@hpkns.uk"
      }
    ]
  },
  "custom_metadata": {
    "example": {
      "name": "Example Metadata Field",
      "description": "A field showing that custom fields can be added",
      "primary": true
    }
  },
  "media": [
    {
      "sha256_checksum": "203073da0b36a5921f2914e2093abcae7eb987846f405b438c25792bab1617fa",
      "mime_type": "text/plain"
    }
  ],
  "test_cases": [
    {
      "id": "eabb5d31-a958-4609-ac98-83365e14d18b",
      "attestations": []
    }
  ]
}
]]></sourcecode>
</section>

<section anchor="example-test-case"><name>Example Test Case Manifest JSON</name>

<sourcecode type="json"><![CDATA[{
  "metadata": {
    "title": "Example Test Case",
    "execution_datetime": "2025-05-01T11:13:29+01:00",
    "passed": null,
    "custom": {
      "example": "Example custom metadata field value"
    }
  },
  "evidence": [
    {
      "kind":"text/plain",
      "value":"plain:This is some text based evidence"
    },
    {
      "kind":"text/plain",
      "value":"base64:VGhpcyBpcyBzb21lIHRleHQgYmFzZWQgYmFzZTY0IGVuY29kZWQgZXZpZGVuY2U"
    },
    {
      "kind":"text/plain",
      "value":"media:203073da0b36a5921f2914e2093abcae7eb987846f405b438c25792bab1617fa",
      "caption": "An example file",
      "original_filename": "example.txt"
    },
    {
      "kind":"image/png",
      "value":"media:c561967275f002e65b222b4577378f5a20a5881edd00fbe648beef6b4f4971a9",
      "caption": "An example image",
      "original_filename": "image.png"
    }
  ]
}
]]></sourcecode>
</section>

<section anchor="json-schema-for-package-manifest"><name>JSON Schema for Package Manifest</name>

<sourcecode type="json"><![CDATA[{
  "$id": "https://evidenceangel-schemas.hpkns.uk/manifest.2.schema.json",
  "$schema": "http://json-schema.org/draft-07/schema",
  "type": "object",
  "description": "The metadata file `metadata.json` as part of an evidence package.",
  "properties": {
    "metadata": {
      "type": "object",
      "properties": {
        "title": {
          "type": "string",
          "description": "The name of the evidence package.",
          "minLength": 1,
          "maxLength": 30
        },
        "authors": {
          "type": "array",
          "description": "The authors attributed to this evidence package.",
          "items": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string",
                "description": "The author's name."
              },
              "email": {
                "type": "string",
                "description": "The author's email address, although format is not verified."
              }
            },
            "required": ["name"]
          }
        },
        "description": {
          "type": "string",
          "description": "An optional description of the package."
        }
      },
      "required": ["title", "authors"]
    },
    "custom_metadata": {
      "type": "object",
      "description": "Custom metadata fields for test cases",
      "patternProperties": {
        ".+": {
          "type": "object",
          "description": "A custom metadata field",
          "properties": {
            "name": {
              "type": "string",
              "description": "A user-friendly name for this custom property."
            },
            "description": {
              "type": "string",
              "description": "A description for this custom property."
            },
            "primary": {
              "type": "boolean",
              "description": "Is this custom property the main one in this package? This may influence how it is displayed in editors."
            }
          },
          "required": ["name", "description", "primary"]
        }
      }
    },
    "media": {
      "type": "array",
      "items": {
        "type": "object",
        "description": "A media entry. When an entry is present in this manifest, it MUST also be present in the `media` directory of the package.",
        "properties": {
          "sha256_checksum": {
            "type": "string",
            "description": "The SHA256 checksum of the media file. This MUST also match identically the name of the file with no extension in the `media` directory.",
            "pattern": "^[0-9a-f]{64}$"
          },
          "mime_type": {
            "type": "string",
            "description": "The MIME type of the media file."
          }
        },
        "required": ["sha256_checksum", "mime_type"]
      }
    },
    "test_cases": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The UUID of the test case. If present here, there MUST be an associated test case file in the `testcases` directory of the package with the name `<UUID>.json`."
          },
          "attestations": {
            "type": "array",
            "description": "An array of attestations over this test case.",
            "items": {
              "type": "string",
              "description": "The elements within the \"attestations\" array **MUST** be JWS payload un-encoded (detatched) [RFC7515] signatures. The signature payload should be a copy of the test case manifest (i.e. the file \"uuid.json\"), having been processed into JSON canonical format as defined in [RFC8785].",
              "pattern": "^[A-z0-9_-]+\\.\\.[A-z0-9_-]+$"
            }
          }
        },
        "required": ["id", "attestations"]
      }
    }
  },
  "required": ["metadata", "media", "test_cases"]
}
]]></sourcecode>
</section>

<section anchor="json-schema-for-test-case-manifest"><name>JSON Schema for Test Case Manifest</name>

<sourcecode type="json"><![CDATA[{
  "$id": "https://evidenceangel-schemas.hpkns.uk/testcase.2.schema.json",
  "$schema": "http://json-schema.org/draft-07/schema",
  "type": "object",
  "description": "A test case file `testcases/<UUID>.json` as part of an evidence package.",
  "properties": {
    "metadata": {
      "type": "object",
      "properties": {
        "title": {
          "type": "string",
          "description": "The title of the test case",
          "minLength": 1,
          "maxLength": 30
        },
        "execution_datetime": {
          "type": "string",
          "format": "date-time",
          "description": "The date and time of the execution of this test case starting."
        },
        "passed": {
          "type": ["string", "null"],
          "description": "The state of the test case",
          "enum": [
            "pass",
            "fail",
            null
          ]
        },
        "custom": {
          "type": "object",
          "description": "Custom metadata values",
          "patternProperties": {
            ".+": {
              "type": "string"
            }
          }
        }
      },
      "required": ["title", "execution_datetime"]
    },
    "evidence": {
      "type": "array",
      "items": {
        "type": "object",
        "description": "A piece of evidence as part of this test case.",
        "properties": {
          "kind": {
            "type": "string",
            "description": "The Internet Media Type of the data stored.",
            "pattern": "^(\\w*)\\/([\\w\\.-]*)(\\+([\\w\\.-]*))?(;((.+)=(.*);)*(.+)=(.*))?$"
          },
          "value": {
            "type": "string",
            "description": "Either `plain:` followed by plain text, `media:` followed by a media SHA256 hash, or `base64:` followed by a base64 string of data without padding.",
            "pattern": "^(plain:.*)|(media:[0-9a-f]{64})|(base64:[A-z0-9+/]*)$"
          },
          "caption": {
            "type": "string",
            "description": "An optional caption for this piece of evidence."
          },
          "original_filename": {
            "type": "string",
            "description": "The original filename for File evidence"
          }
        },
        "required": ["kind", "value"]
      }
    }
  },
  "required": ["metadata", "evidence"]
}
]]></sourcecode>
</section>

</back>

</rfc>
