abstract_std/objects/validation/
verifiers.rs1use super::ValidationError;
2
3pub(crate) const MIN_DESC_LENGTH: usize = 1;
4pub(crate) const MAX_DESC_LENGTH: usize = 1024;
5pub(crate) const MIN_LINK_LENGTH: usize = 11;
7pub(crate) const MAX_LINK_LENGTH: usize = 128;
8pub(crate) const MIN_TITLE_LENGTH: usize = 1;
9pub(crate) const MAX_TITLE_LENGTH: usize = 64;
10
11pub(crate) const DANGEROUS_CHARS: &[char] = &['"', '\'', '=', '>', '<'];
12
13fn contains_dangerous_characters(input: &str) -> bool {
14 input.chars().any(|c| DANGEROUS_CHARS.contains(&c))
15}
16
17fn is_valid_url(link: &str) -> bool {
18 link.starts_with("http://") || link.starts_with("https://") || link.starts_with("ipfs://")
19}
20
21pub fn validate_link(link: Option<&str>) -> Result<(), ValidationError> {
22 if let Some(link) = link {
23 if link.len() < MIN_LINK_LENGTH {
24 Err(ValidationError::LinkInvalidShort(MIN_LINK_LENGTH))
25 } else if link.len() > MAX_LINK_LENGTH {
26 Err(ValidationError::LinkInvalidLong(MAX_LINK_LENGTH))
27 } else if !is_valid_url(link) {
28 Err(ValidationError::LinkInvalidFormat {})
29 } else if contains_dangerous_characters(link) {
30 Err(ValidationError::LinkContainsDangerousCharacters {})
31 } else {
32 Ok(())
33 }
34 } else {
35 Ok(())
36 }
37}
38
39pub fn validate_name(title: &str) -> Result<(), ValidationError> {
40 if title.len() < MIN_TITLE_LENGTH {
41 Err(ValidationError::TitleInvalidShort(MIN_TITLE_LENGTH))
42 } else if title.len() > MAX_TITLE_LENGTH {
43 Err(ValidationError::TitleInvalidLong(MAX_TITLE_LENGTH))
44 } else if contains_dangerous_characters(title) {
45 Err(ValidationError::TitleContainsDangerousCharacters {})
46 } else {
47 Ok(())
48 }
49}
50
51pub fn validate_description(maybe_description: Option<&str>) -> Result<(), ValidationError> {
52 if let Some(description) = maybe_description {
53 if description.len() < MIN_DESC_LENGTH {
54 return Err(ValidationError::DescriptionInvalidShort(MIN_DESC_LENGTH));
55 } else if description.len() > MAX_DESC_LENGTH {
56 return Err(ValidationError::DescriptionInvalidLong(MAX_DESC_LENGTH));
57 } else if contains_dangerous_characters(description) {
58 return Err(ValidationError::DescriptionContainsDangerousCharacters {});
59 }
60 }
61 Ok(())
62}
63
64#[cfg(test)]
65mod tests {
66 use rstest::rstest;
67
68 use super::*;
69
70 mod link {
71 use super::*;
72
73 #[rstest(
74 input,
75 case("https://www.google.com"),
76 case("http://example.com"),
77 case("https://example.net:8080")
78 )]
79 fn valid(input: &str) {
80 assert!(validate_link(Some(input)).is_ok());
81 }
82
83 #[rstest(
84 input,
85 case("http://a.b"),
86 case("://example.com"),
87 case("example.com"),
88 case("https://example.org/path?query=value"),
89 case("https:/example.com")
90 )]
91 fn invalid(input: &str) {
92 assert!(validate_link(Some(input)).is_err());
93 }
94 }
95
96 mod name {
97 use super::*;
98
99 #[rstest(input,
100 case("name"),
101 case("name123"),
102 case("name 123"),
103 case("a"),
104 case(& "a".repeat(MAX_TITLE_LENGTH)),
105 case("name!$%&*+,-.;@^_`|~"),
106 case("名前"),
107 )]
108 fn valid_names(input: &str) {
109 assert!(validate_name(input).is_ok());
110 }
111
112 #[rstest(input,
113 case(""),
114 case(& "a".repeat(MAX_TITLE_LENGTH + 1)),
115 case("name<>'\""),
116 )]
117 fn invalid_names(input: &str) {
118 assert!(validate_name(input).is_err());
119 }
120 }
121
122 mod description {
123 use super::*;
124
125 #[rstest(input,
126 case("d"),
127 case("description123"),
128 case("description 123"),
129 case(& "a".repeat(MAX_DESC_LENGTH)),
130 case("description!$%&*+,-.;@^_`|~"),
131 case("説明"),
132 )]
133 fn valid_descriptions(input: &str) {
134 assert!(validate_description(Some(input)).is_ok());
135 }
136
137 #[rstest(input,
138 case(""),
139 case(& "a".repeat(MAX_DESC_LENGTH + 1)),
140 case("description<>'\""),
141 )]
142 fn invalid_descriptions(input: &str) {
143 assert!(validate_description(Some(input)).is_err());
144 }
145 }
146}