1pub mod link_provenance;
22mod parser;
23mod sanitize;
24pub mod sanitize_by_tier;
25mod ssrf_guard;
26pub mod types;
27mod validate;
28mod validate_config;
29
30pub use parser::ExpressionParser;
31pub use sanitize::{
32 sanitize_url, sanitize_url_strict, sanitize_url_with_schemes, DEFAULT_SCHEMES, STRICT_SCHEMES,
33};
34pub use ssrf_guard::is_private_host;
35pub use types::{Config, Link, LinkWithId, Macro, Protocol, ProtocolHandler, RegexValidation, Tier};
36pub use validate::validate_regex;
37pub use validate_config::{
38 sanitize_link_urls, validate_config, validate_config_with_options, ValidateOptions,
39};
40
41use sanitize::sanitize_link;
42use std::collections::HashMap;
43
44#[must_use]
46pub fn resolve(config: &Config, expression: &str) -> Vec<LinkWithId> {
47 let mut parser = ExpressionParser::new(config);
48 let ids = parser.query(expression, "");
49 ids.iter()
50 .filter_map(|id| {
51 config.all_links.get(id).map(|link| LinkWithId {
52 id: id.clone(),
53 link: sanitize_link(link),
54 })
55 })
56 .collect()
57}
58
59#[must_use]
61pub fn cherry_pick(config: &Config, expression: &str) -> HashMap<String, Link> {
62 let mut parser = ExpressionParser::new(config);
63 let ids = parser.query(expression, "");
64 ids.iter()
65 .filter_map(|id| {
66 config
67 .all_links
68 .get(id)
69 .map(|link| (id.clone(), sanitize_link(link)))
70 })
71 .collect()
72}
73
74#[must_use]
76pub fn merge_configs(configs: &[&Config]) -> Config {
77 const BLOCKED: &[&str] = &["__proto__", "constructor", "prototype"];
78
79 let mut merged = Config::default();
80
81 for cfg in configs {
82 for (k, v) in &cfg.settings {
83 if !BLOCKED.contains(&k.as_str()) {
84 merged.settings.insert(k.clone(), v.clone());
85 }
86 }
87 for (k, v) in &cfg.macros {
88 if !BLOCKED.contains(&k.as_str()) {
89 merged.macros.insert(k.clone(), v.clone());
90 }
91 }
92 for (k, v) in &cfg.all_links {
93 if !BLOCKED.contains(&k.as_str()) {
94 merged.all_links.insert(k.clone(), v.clone());
95 }
96 }
97 for (k, v) in &cfg.search_patterns {
98 if !BLOCKED.contains(&k.as_str()) {
99 merged.search_patterns.insert(k.clone(), v.clone());
100 }
101 }
102 }
103
104 merged
105}