• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

galaxy-sec / orion-variate / 16833159723

08 Aug 2025 02:38PM UTC coverage: 70.216% (+0.03%) from 70.183%
16833159723

push

github

sec-wukong
clippy

9 of 20 new or added lines in 4 files covered. (45.0%)

2 existing lines in 2 files now uncovered.

3027 of 4311 relevant lines covered (70.22%)

4.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.88
/src/addr/validation.rs
1
//! 地址配置验证模块
2
//!
3
//! 提供地址配置验证功能,确保地址格式正确且可访问
4

5
use crate::addr::constants;
6
use std::path::Path;
7
use url::Url;
8

9
use super::{Address, GitRepository, HttpResource, LocalPath};
10

11
/// 地址验证结果
12
pub type ValidationResult = Result<(), Vec<ValidationError>>;
13

14
/// 验证错误类型
15
#[derive(Debug, Clone, PartialEq)]
16
pub struct ValidationError {
17
    /// 错误字段
18
    pub field: String,
19
    /// 错误描述
20
    pub message: String,
21
    /// 错误代码
22
    pub code: String,
23
}
24

25
impl ValidationError {
26
    pub fn new(field: &str, message: &str, code: &str) -> Self {
8✔
27
        Self {
8✔
28
            field: field.to_string(),
8✔
29
            message: message.to_string(),
8✔
30
            code: code.to_string(),
8✔
31
        }
8✔
32
    }
8✔
33
}
34

35
/// 地址验证trait
36
pub trait Validate {
37
    /// 验证地址配置
38
    fn validate(&self) -> ValidationResult;
39

40
    /// 验证地址是否可访问
41
    fn is_accessible(&self) -> bool;
42
}
43

44
impl Validate for Address {
45
    fn validate(&self) -> ValidationResult {
7✔
46
        match self {
7✔
47
            Address::Git(repo) => repo.validate(),
3✔
48
            Address::Http(resource) => resource.validate(),
3✔
49
            Address::Local(path) => path.validate(),
1✔
50
        }
51
    }
7✔
52

53
    fn is_accessible(&self) -> bool {
×
54
        match self {
×
55
            Address::Git(repo) => repo.is_accessible(),
×
56
            Address::Http(resource) => resource.is_accessible(),
×
57
            Address::Local(path) => path.is_accessible(),
×
58
        }
59
    }
×
60
}
61

62
impl Validate for GitRepository {
63
    fn validate(&self) -> ValidationResult {
6✔
64
        let mut errors = Vec::new();
6✔
65

66
        // 验证仓库地址
67
        if self.repo().is_empty() {
6✔
68
            errors.push(ValidationError::new(
2✔
69
                "repo",
2✔
70
                "仓库地址不能为空",
2✔
71
                "EMPTY_REPO",
2✔
72
            ));
2✔
73
        } else if !is_valid_git_url(self.repo()) {
4✔
74
            errors.push(ValidationError::new(
×
75
                "repo",
×
76
                "无效的Git仓库地址格式",
×
77
                "INVALID_GIT_URL",
×
78
            ));
×
79
        }
4✔
80

81
        // 验证SSH密钥路径
82
        if let Some(ssh_key) = &self.ssh_key()
6✔
NEW
83
            && !Path::new(ssh_key).exists()
×
NEW
84
        {
×
NEW
85
            errors.push(ValidationError::new(
×
NEW
86
                "ssh_key",
×
NEW
87
                &format!("SSH密钥文件不存在: {ssh_key}",),
×
NEW
88
                "SSH_KEY_NOT_FOUND",
×
NEW
89
            ));
×
90
        }
6✔
91

92
        // 验证认证配置
93
        if self.token().is_some() && self.username().is_none() {
6✔
94
            errors.push(ValidationError::new(
×
95
                "username",
×
96
                "使用Token认证时必须提供用户名",
×
97
                "MISSING_USERNAME",
×
98
            ));
×
99
        }
6✔
100

101
        // 验证版本标识符
102
        let version_count = [
6✔
103
            self.tag().as_ref(),
6✔
104
            self.branch().as_ref(),
6✔
105
            self.rev().as_ref(),
6✔
106
        ]
6✔
107
        .iter()
6✔
108
        .filter(|x| x.is_some())
18✔
109
        .count();
6✔
110

111
        if version_count > 1 {
6✔
112
            errors.push(ValidationError::new(
×
113
                "version",
×
114
                "不能同时指定tag、branch和rev中的多个",
×
115
                "CONFLICTING_VERSIONS",
×
116
            ));
×
117
        }
6✔
118

119
        if errors.is_empty() {
6✔
120
            Ok(())
4✔
121
        } else {
122
            Err(errors)
2✔
123
        }
124
    }
6✔
125

126
    fn is_accessible(&self) -> bool {
×
127
        // 简化的可访问性检查
128
        // 实际实现可能需要网络连接测试
129
        is_valid_git_url(self.repo())
×
130
    }
×
131
}
132

133
impl Validate for HttpResource {
134
    fn validate(&self) -> ValidationResult {
6✔
135
        let mut errors = Vec::new();
6✔
136

137
        // 验证URL格式
138
        if self.url().is_empty() {
6✔
139
            errors.push(ValidationError::new("url", "URL不能为空", "EMPTY_URL"));
1✔
140
        } else if let Err(e) = Url::parse(self.url()) {
5✔
141
            errors.push(ValidationError::new(
2✔
142
                "url",
2✔
143
                &format!("无效的URL格式: {e}"),
2✔
144
                "INVALID_URL",
2✔
145
            ));
2✔
146
        }
3✔
147

148
        // 验证认证信息
149
        if self.username().is_some() && self.password().is_none() {
6✔
150
            errors.push(ValidationError::new(
×
151
                "password",
×
152
                "提供用户名时必须提供密码",
×
153
                "MISSING_PASSWORD",
×
154
            ));
×
155
        }
6✔
156

157
        if errors.is_empty() {
6✔
158
            Ok(())
3✔
159
        } else {
160
            Err(errors)
3✔
161
        }
162
    }
6✔
163

164
    fn is_accessible(&self) -> bool {
×
165
        // 简化的可访问性检查
166
        Url::parse(self.url()).is_ok()
×
167
    }
×
168
}
169

170
impl Validate for LocalPath {
171
    fn validate(&self) -> ValidationResult {
4✔
172
        let mut errors = Vec::new();
4✔
173

174
        // 验证路径格式
175
        let path_str = self.path();
4✔
176
        if path_str.is_empty() {
4✔
177
            errors.push(ValidationError::new(
1✔
178
                "path",
1✔
179
                "本地路径不能为空",
1✔
180
                "EMPTY_PATH",
1✔
181
            ));
1✔
182
        } else {
1✔
183
            let path = Path::new(path_str);
3✔
184

185
            // 检查路径是否包含非法字符
186
            if path_str.contains("\\") && cfg!(not(target_os = "windows")) {
3✔
187
                errors.push(ValidationError::new(
×
188
                    "path",
×
189
                    "在非Windows系统上使用了反斜杠路径分隔符",
×
190
                    "INVALID_PATH_SEPARATOR",
×
191
                ));
×
192
            }
3✔
193

194
            // 检查相对路径
195
            if path.is_relative() && !path_str.starts_with("./") && !path_str.starts_with("../") {
3✔
196
                errors.push(ValidationError::new(
×
197
                    "path",
×
198
                    "相对路径应以./或../开头",
×
199
                    "INVALID_RELATIVE_PATH",
×
200
                ));
×
201
            }
3✔
202
        }
203

204
        if errors.is_empty() {
4✔
205
            Ok(())
3✔
206
        } else {
207
            Err(errors)
1✔
208
        }
209
    }
4✔
210

211
    fn is_accessible(&self) -> bool {
×
212
        Path::new(self.path()).exists()
×
213
    }
×
214
}
215

216
/// 验证Git URL格式
217
fn is_valid_git_url(url: &str) -> bool {
10✔
218
    // HTTPS格式
219
    if url.starts_with(constants::git::HTTPS_PREFIX) && url.ends_with(".git") {
10✔
220
        return Url::parse(url).is_ok();
5✔
221
    }
5✔
222

223
    // SSH格式 (git@host:repo.git)
224
    if url.starts_with(constants::git::SSH_PREFIX) && url.contains(':') && url.ends_with(".git") {
5✔
225
        return true;
2✔
226
    }
3✔
227

228
    // Git协议格式
229
    if url.starts_with(constants::git::GIT_PROTOCOL) && url.ends_with(".git") {
3✔
230
        return Url::parse(url).is_ok();
1✔
231
    }
2✔
232

233
    // 简化的GitHub/GitLab等格式
234
    url.contains("github.com") || url.contains("gitlab.com") || url.contains("gitea.com")
2✔
235
}
10✔
236

237
/// 批量验证多个地址
238
pub fn validate_addresses(addresses: &[Address]) -> ValidationResult {
2✔
239
    let mut all_errors = Vec::new();
2✔
240

241
    for (index, addr) in addresses.iter().enumerate() {
4✔
242
        if let Err(errors) = addr.validate() {
4✔
243
            for error in errors {
4✔
244
                all_errors.push(ValidationError::new(
2✔
245
                    &format!("address[{}].{}", index, error.field),
2✔
246
                    &error.message,
2✔
247
                    &error.code,
2✔
248
                ));
2✔
249
            }
2✔
250
        }
2✔
251
    }
252

253
    if all_errors.is_empty() {
2✔
254
        Ok(())
1✔
255
    } else {
256
        Err(all_errors)
1✔
257
    }
258
}
2✔
259

260
#[cfg(test)]
261
mod tests {
262
    use super::*;
263

264
    #[test]
265
    fn test_git_repository_validation() {
1✔
266
        let repo = GitRepository::from("https://github.com/user/repo.git");
1✔
267
        assert!(repo.validate().is_ok());
1✔
268

269
        let invalid_repo = GitRepository::from("");
1✔
270
        assert!(invalid_repo.validate().is_err());
1✔
271

272
        let ssh_repo = GitRepository::from("git@github.com:user/repo.git");
1✔
273
        assert!(ssh_repo.validate().is_ok());
1✔
274
    }
1✔
275

276
    #[test]
277
    fn test_http_resource_validation() {
1✔
278
        let resource = HttpResource::from("https://example.com/file.zip");
1✔
279
        assert!(resource.validate().is_ok());
1✔
280

281
        let invalid_resource = HttpResource::from("invalid-url");
1✔
282
        assert!(invalid_resource.validate().is_err());
1✔
283

284
        let empty_resource = HttpResource::from("");
1✔
285
        assert!(empty_resource.validate().is_err());
1✔
286
    }
1✔
287

288
    #[test]
289
    fn test_local_path_validation() {
1✔
290
        let path = LocalPath::from("./relative/path");
1✔
291
        assert!(path.validate().is_ok());
1✔
292

293
        let absolute_path = LocalPath::from("/absolute/path");
1✔
294
        assert!(absolute_path.validate().is_ok());
1✔
295

296
        let invalid_path = LocalPath::from("");
1✔
297
        assert!(invalid_path.validate().is_err());
1✔
298
    }
1✔
299

300
    #[test]
301
    fn test_address_validation() {
1✔
302
        let git_addr = Address::Git(GitRepository::from("https://github.com/user/repo.git"));
1✔
303
        assert!(git_addr.validate().is_ok());
1✔
304

305
        let http_addr = Address::Http(HttpResource::from("https://example.com/file.zip"));
1✔
306
        assert!(http_addr.validate().is_ok());
1✔
307

308
        let local_addr = Address::Local(LocalPath::from("./path"));
1✔
309
        assert!(local_addr.validate().is_ok());
1✔
310
    }
1✔
311

312
    #[test]
313
    fn test_batch_validation() {
1✔
314
        let addresses = vec![
1✔
315
            Address::Git(GitRepository::from("https://github.com/user/repo.git")),
1✔
316
            Address::Http(HttpResource::from("https://example.com/file.zip")),
1✔
317
        ];
318

319
        assert!(validate_addresses(&addresses).is_ok());
1✔
320

321
        let invalid_addresses = vec![
1✔
322
            Address::Git(GitRepository::from("")),
1✔
323
            Address::Http(HttpResource::from("invalid-url")),
1✔
324
        ];
325

326
        assert!(validate_addresses(&invalid_addresses).is_err());
1✔
327
    }
1✔
328

329
    #[test]
330
    fn test_is_valid_git_url() {
1✔
331
        assert!(is_valid_git_url("https://github.com/user/repo.git"));
1✔
332
        assert!(is_valid_git_url("git@github.com:user/repo.git"));
1✔
333
        assert!(is_valid_git_url("git://github.com/user/repo.git"));
1✔
334
        assert!(is_valid_git_url("https://gitlab.com/user/repo.git"));
1✔
335
        assert!(!is_valid_git_url("invalid-url"));
1✔
336
        assert!(!is_valid_git_url(""));
1✔
337
    }
1✔
338
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc