ghactions_core/repository/
reference.rs1#![allow(unused_assignments)]
4use std::{
5 fmt::Display,
6 path::{Component, PathBuf},
7};
8
9use crate::ActionsError;
10
11#[derive(Debug, Default, Clone, PartialEq, Eq)]
13pub struct RepositoryReference {
14 pub owner: String,
16 pub name: String,
18 pub path: Option<String>,
20 pub reference: Option<String>,
22}
23
24impl RepositoryReference {
25 pub fn parse(reporef: &str) -> Result<RepositoryReference, ActionsError> {
43 let mut repo_ref = RepositoryReference::default();
44
45 let mut repository = String::new();
46 let mut path = PathBuf::new();
47
48 match reporef.split_once('@') {
49 Some((repo, refe)) => {
50 repository = repo.to_string();
51 repo_ref.reference = Some(refe.to_string());
52 }
53 None => {
54 repository = reporef.to_string();
55 }
56 }
57
58 for (index, path_ref) in repository.split('/').enumerate() {
61 if index == 0 {
62 repo_ref.owner = path_ref.to_string();
63 } else if index == 1 {
64 repo_ref.name = path_ref.to_string();
65 } else {
66 path.push(path_ref);
67 }
68 }
69
70 if !path.as_os_str().is_empty() {
72 if path.components().any(|x| x == Component::ParentDir) {
74 return Err(ActionsError::RepositoryReferenceError(
75 "Path traversal detected".to_string(),
76 ));
77 }
78 if path.is_absolute() {
79 return Err(ActionsError::RepositoryReferenceError(
80 "Absolute paths are not allowed".to_string(),
81 ));
82 }
83 repo_ref.path = Some(path.display().to_string());
84 }
85
86 Ok(repo_ref)
87 }
88
89 pub fn display(&self) -> String {
91 format!("{}", self)
92 }
93}
94
95impl Display for RepositoryReference {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 let mut retvalue = format!("{}/{}", self.owner, self.name);
98 if let Some(path) = &self.path {
99 retvalue.push('/');
100 retvalue.push_str(path.as_str());
101 }
102 if let Some(refer) = &self.reference {
103 retvalue.push('@');
104 retvalue.push_str(refer.as_str());
105 }
106
107 write!(f, "{}", retvalue)
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::RepositoryReference;
114
115 #[test]
116 fn test_owner_repo() {
117 let repo_ref = RepositoryReference::parse("geekmasher/ghactions").unwrap();
118
119 assert_eq!(repo_ref.owner, String::from("geekmasher"));
120 assert_eq!(repo_ref.name, String::from("ghactions"));
121 assert_eq!(repo_ref.reference, None);
122 assert_eq!(repo_ref.path, None);
123
124 assert_eq!(repo_ref.display(), String::from("geekmasher/ghactions"));
125 }
126 #[test]
127 fn test_owner_repo_branch() {
128 let repo_ref = RepositoryReference::parse("geekmasher/ghactions@main").unwrap();
129
130 assert_eq!(repo_ref.owner, String::from("geekmasher"));
131 assert_eq!(repo_ref.name, String::from("ghactions"));
132 assert_eq!(repo_ref.reference, Some(String::from("main")));
133 assert_eq!(repo_ref.path, None);
134
135 assert_eq!(
136 repo_ref.display(),
137 String::from("geekmasher/ghactions@main")
138 );
139 }
140 #[test]
141 fn test_owner_repo_branch_path() {
142 let repo_ref = RepositoryReference::parse("geekmasher/ghactions@feature/xyz").unwrap();
143
144 assert_eq!(repo_ref.owner, String::from("geekmasher"));
145 assert_eq!(repo_ref.name, String::from("ghactions"));
146 assert_eq!(repo_ref.reference, Some(String::from("feature/xyz")));
147 assert_eq!(repo_ref.path, None);
148
149 assert_eq!(
150 repo_ref.display(),
151 String::from("geekmasher/ghactions@feature/xyz")
152 );
153 }
154 #[test]
155 fn test_owner_repo_path() {
156 let repo_ref =
157 RepositoryReference::parse("geekmasher/ghactions/path/to/action@main").unwrap();
158
159 assert_eq!(repo_ref.owner, String::from("geekmasher"));
160 assert_eq!(repo_ref.name, String::from("ghactions"));
161 assert_eq!(repo_ref.reference, Some(String::from("main")));
162 assert_eq!(repo_ref.path, Some(String::from("path/to/action")));
163
164 assert_eq!(
165 repo_ref.display(),
166 String::from("geekmasher/ghactions/path/to/action@main")
167 );
168 }
169 #[test]
170 fn test_owner_repo_path_traversal() {
171 let repo_ref = RepositoryReference::parse("geekmasher/ghactions/../test@main");
172 assert!(repo_ref.is_err());
173
174 }
178}