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