1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
use super::parse_error::ParseError;
use chrono::{DateTime, Utc};
use iri_string::types::IriString;
use oxilangtag::{LanguageTag, LanguageTagParseError};
use std::cmp::Ordering;
use valuable::{Valuable, Value, Visit};
macro_rules! IriStringImpl {
($structname:ident) => {
impl $structname {
pub(crate) fn new(uri: &str) -> Result<Self, ParseError> {
let uri = uri.trim().parse::<IriString>()?;
if uri.scheme_str() == "http" {
return Err(ParseError::InsecureHTTP);
}
let log_value = uri.as_str().to_string();
Ok(Self { uri, log_value })
}
}
impl Valuable for $structname {
fn as_value(&self) -> Value<'_> {
self.log_value.as_value()
}
fn visit(&self, _visit: &mut dyn Visit) {}
}
};
}
/// An [Acknowledgments field](https://www.rfc-editor.org/rfc/rfc9116#name-acknowledgments) links to a page where security researchers are recognized
#[derive(Debug, PartialEq)]
pub struct AcknowledgmentsField {
/// The URI of the link according to [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986)
pub uri: IriString,
log_value: String,
}
IriStringImpl!(AcknowledgmentsField);
/// A [Canonical field](https://www.rfc-editor.org/rfc/rfc9116#name-canonical) contains a canonical URI for the security.txt file
#[derive(Debug, PartialEq)]
pub struct CanonicalField {
/// The URI of the link according to [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986)
pub uri: IriString,
log_value: String,
}
IriStringImpl!(CanonicalField);
/// A [Contact field](https://www.rfc-editor.org/rfc/rfc9116#name-contact) contains contact information to use for reporting vulnerabilities
#[derive(Debug, PartialEq)]
pub struct ContactField {
/// The URI of the link according to [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986)
pub uri: IriString,
log_value: String,
}
IriStringImpl!(ContactField);
/// An [Encryption field](https://www.rfc-editor.org/rfc/rfc9116#name-encryption) links to a key to be used for encrypted communication
#[derive(Debug, PartialEq)]
pub struct EncryptionField {
/// The URI of the link according to [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986)
pub uri: IriString,
log_value: String,
}
IriStringImpl!(EncryptionField);
/// The [Expires field](https://www.rfc-editor.org/rfc/rfc9116#name-expires) represents the date and time after which the security.txt file is considered stale
#[derive(Debug, PartialEq)]
pub struct ExpiresField {
/// The date and time from which the security.txt file is considered stale
pub datetime: DateTime<Utc>,
log_value: String,
}
impl ExpiresField {
pub(crate) fn new(datetime: &str, now: DateTime<Utc>) -> Result<Self, ParseError> {
let datetime: DateTime<Utc> = datetime.trim().parse()?;
if datetime < now {
return Err(ParseError::ExpiresFieldExpired);
}
let log_value = datetime.to_rfc3339();
Ok(Self { datetime, log_value })
}
}
impl PartialOrd for ExpiresField {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.datetime.partial_cmp(&other.datetime)
}
}
impl Valuable for ExpiresField {
fn as_value(&self) -> Value<'_> {
self.log_value.as_value()
}
fn visit(&self, _visit: &mut dyn Visit) {}
}
/// A [Hiring field](https://www.rfc-editor.org/rfc/rfc9116#name-hiring) links to the vendor's security-related job positions
#[derive(Debug, PartialEq)]
pub struct HiringField {
/// The URI of the link according to [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986)
pub uri: IriString,
log_value: String,
}
IriStringImpl!(HiringField);
/// A [Policy field](https://www.rfc-editor.org/rfc/rfc9116#name-policy) links to the security policy page
#[derive(Debug, PartialEq)]
pub struct PolicyField {
/// The URI of the link according to [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986)
pub uri: IriString,
log_value: String,
}
IriStringImpl!(PolicyField);
/// The [Preferred-Languages field](https://www.rfc-editor.org/rfc/rfc9116#name-preferred-languages) lists the preferred languages for security reports
#[derive(Debug, PartialEq)]
pub struct PreferredLanguagesField {
/// The set of preferred languages according to [RFC 5646](https://www.rfc-editor.org/rfc/rfc5646)
pub languages: Vec<LanguageTag<String>>,
log_value: String,
}
impl PreferredLanguagesField {
pub(crate) fn new(languages: &str) -> Result<Self, ParseError> {
let languages = languages
.split(',')
.map(str::trim)
.map(LanguageTag::parse_and_normalize)
.collect::<Result<Vec<LanguageTag<String>>, LanguageTagParseError>>()?;
if languages.is_empty() {
return Err(ParseError::IllegalField);
}
let log_value = languages.join(", ");
Ok(Self { languages, log_value })
}
}
impl Valuable for PreferredLanguagesField {
fn as_value(&self) -> Value<'_> {
self.log_value.as_value()
}
fn visit(&self, _visit: &mut dyn Visit) {}
}
/// The "Extension" field acts as a catch-all for any fields not explicitly supported by this library
///
/// This feature accommodates [section 2.4 on Extensibility](https://www.rfc-editor.org/rfc/rfc9116#name-extensibility) in the specification.
#[derive(Debug, PartialEq, Valuable)]
pub struct ExtensionField {
/// Name of the extension field
pub name: String,
/// Value of the extension field
pub value: String,
}
impl ExtensionField {
pub(crate) fn new(name: String, value: String) -> Result<Self, ParseError> {
Ok(Self { name, value })
}
}