eure/query/
asset_locator.rs1use query_flow::{Db, LocateResult, QueryError, asset_locator};
8use url::Url;
9
10use crate::query::config::WorkspaceConfig;
11
12use super::assets::{TextFile, TextFileContent, WorkspaceId};
13use super::error::EureQueryError;
14
15const DEFAULT_ALLOWED_HOST: &str = "eure.dev";
17
18#[asset_locator]
32pub fn text_file_locator(
33 db: &impl Db,
34 key: &TextFile,
35) -> Result<LocateResult<TextFileContent>, QueryError> {
36 match key {
37 TextFile::Local(_) => {
38 Ok(LocateResult::Pending)
40 }
41 TextFile::Remote(url) => {
42 validate_url_host(db, url)?;
44 Ok(LocateResult::Pending)
46 }
47 }
48}
49
50fn validate_url_host(db: &impl Db, url: &Url) -> Result<(), QueryError> {
54 let host = url.host_str().unwrap_or("");
55
56 if host == DEFAULT_ALLOWED_HOST {
58 return Ok(());
59 }
60
61 let allowed_hosts = get_allowed_hosts_from_workspace(db)?;
63
64 if allowed_hosts
66 .iter()
67 .any(|allowed| host_matches(host, allowed))
68 {
69 return Ok(());
70 }
71
72 Err(EureQueryError::HostNotAllowed {
74 url: url.clone(),
75 host: host.to_string(),
76 }
77 .into())
78}
79
80fn host_matches(host: &str, pattern: &str) -> bool {
86 if let Some(suffix) = pattern.strip_prefix("*.") {
87 host == suffix || host.ends_with(&format!(".{}", suffix))
89 } else {
90 host == pattern
92 }
93}
94
95fn get_allowed_hosts_from_workspace(db: &impl Db) -> Result<Vec<String>, QueryError> {
99 let mut allowed_hosts = Vec::new();
100
101 for workspace_id in db.list_asset_keys::<WorkspaceId>() {
102 let config = db.query(WorkspaceConfig::new(workspace_id))?;
103 allowed_hosts.extend(config.config.allowed_hosts().iter().cloned());
104 }
105
106 Ok(allowed_hosts)
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 mod host_matches_tests {
114 use super::*;
115
116 #[test]
117 fn exact_match() {
118 assert!(host_matches("example.com", "example.com"));
119 assert!(!host_matches("other.com", "example.com"));
120 assert!(!host_matches("sub.example.com", "example.com"));
121 }
122
123 #[test]
124 fn wildcard_match_subdomain() {
125 assert!(host_matches("sub.example.com", "*.example.com"));
126 assert!(host_matches("a.b.example.com", "*.example.com"));
127 }
128
129 #[test]
130 fn wildcard_match_base() {
131 assert!(host_matches("example.com", "*.example.com"));
133 }
134
135 #[test]
136 fn wildcard_no_match() {
137 assert!(!host_matches("other.com", "*.example.com"));
138 assert!(!host_matches("exampleXcom", "*.example.com"));
139 }
140
141 #[test]
142 fn empty_pattern() {
143 assert!(!host_matches("example.com", ""));
144 assert!(host_matches("", ""));
145 }
146 }
147}