88from kernelCI_app .constants .localization import ClientStrings
99from kernelCI_app .views .proxyView import TIMEOUT_TIME_IN_SECONDS , ProxyView
1010
11+ MOCK_ALLOWED_DOMAINS = ["*.example.com" , "exact.org" ]
12+ MOCK_ALLOWED_S3_PATHS = ["/allowed-bucket/" ]
13+
1114
1215class TestProxyView (SimpleTestCase ):
1316 def setUp (self ):
1417 self .factory = APIRequestFactory ()
1518 self .view = ProxyView ()
1619 self .url = "/proxy/"
1720
21+ @patch ("kernelCI_app.views.proxyView.ALLOWED_DOMAINS" , MOCK_ALLOWED_DOMAINS )
1822 @patch ("kernelCI_app.views.proxyView.requests.get" )
1923 def test_get_proxy_success (self , mock_requests_get ):
2024 mock_requests_get .return_value .status_code = HTTPStatus .OK
@@ -25,7 +29,9 @@ def test_get_proxy_success(self, mock_requests_get):
2529 "Content-Disposition" : "attachment; filename=file.txt" ,
2630 }
2731
28- request = self .factory .get (self .url , {"url" : "https://example.com/file.txt" })
32+ request = self .factory .get (
33+ self .url , {"url" : "https://files.example.com/file.txt" }
34+ )
2935 response = self .view .get (request )
3036
3137 self .assertEqual (response .status_code , 200 )
@@ -36,16 +42,21 @@ def test_get_proxy_success(self, mock_requests_get):
3642 response ["Content-Disposition" ], "attachment; filename=file.txt"
3743 )
3844 mock_requests_get .assert_called_once_with (
39- "https://example.com/file.txt" , stream = True , timeout = TIMEOUT_TIME_IN_SECONDS
45+ "https://files.example.com/file.txt" ,
46+ stream = True ,
47+ timeout = TIMEOUT_TIME_IN_SECONDS ,
4048 )
4149
50+ @patch ("kernelCI_app.views.proxyView.ALLOWED_DOMAINS" , MOCK_ALLOWED_DOMAINS )
4251 @patch ("kernelCI_app.views.proxyView.requests.get" )
4352 def test_get_proxy_default_content_type (self , mock_requests_get ):
4453 mock_requests_get .return_value .status_code = HTTPStatus .OK
4554 mock_requests_get .return_value .content = b"content"
4655 mock_requests_get .return_value .headers = {}
4756
48- request = self .factory .get (self .url , {"url" : "https://example.com/file.txt" })
57+ request = self .factory .get (
58+ self .url , {"url" : "https://files.example.com/file.txt" }
59+ )
4960 response = self .view .get (request )
5061
5162 self .assertEqual (response .status_code , 200 )
@@ -65,23 +76,98 @@ def test_get_proxy_missing_url(self):
6576 self .assertEqual (response .status_code , HTTPStatus .BAD_REQUEST )
6677 self .assertEqual (response .data , {"error" : ClientStrings .PROXY_INVALID_URL })
6778
79+ def test_get_proxy_invalid_scheme (self ):
80+ request = self .factory .get (self .url , {"url" : "file:///etc/passwd" })
81+ response = self .view .get (request )
82+
83+ self .assertEqual (response .status_code , HTTPStatus .BAD_REQUEST )
84+ self .assertEqual (response .data , {"error" : ClientStrings .PROXY_INVALID_URL })
85+
86+ @patch ("kernelCI_app.views.proxyView.ALLOWED_DOMAINS" , MOCK_ALLOWED_DOMAINS )
6887 @patch ("kernelCI_app.views.proxyView.requests.get" )
6988 def test_get_proxy_external_error (self , mock_requests_get ):
7089 mock_requests_get .return_value .status_code = HTTPStatus .NOT_FOUND
7190 mock_requests_get .return_value .reason = "Not Found"
7291
73- request = self .factory .get (self .url , {"url" : "https://example.com/file.txt" })
92+ request = self .factory .get (
93+ self .url , {"url" : "https://files.example.com/file.txt" }
94+ )
7495 response = self .view .get (request )
7596
7697 self .assertEqual (response .status_code , HTTPStatus .NOT_FOUND )
7798 self .assertIn (ClientStrings .PROXY_FETCH_FAILED , response .data ["error" ])
7899
100+ @patch ("kernelCI_app.views.proxyView.ALLOWED_DOMAINS" , MOCK_ALLOWED_DOMAINS )
79101 @patch ("kernelCI_app.views.proxyView.requests.get" )
80102 def test_get_proxy_request_exception (self , mock_requests_get ):
81103 mock_requests_get .side_effect = requests .RequestException ("Connection error" )
82104
83- request = self .factory .get (self .url , {"url" : "https://example.com/file.txt" })
105+ request = self .factory .get (
106+ self .url , {"url" : "https://files.example.com/file.txt" }
107+ )
84108 response = self .view .get (request )
85109
86110 self .assertEqual (response .status_code , HTTPStatus .INTERNAL_SERVER_ERROR )
87111 self .assertEqual (response .data , {"error" : ClientStrings .PROXY_ERROR_FETCH })
112+
113+ @patch ("kernelCI_app.views.proxyView.ALLOWED_DOMAINS" , MOCK_ALLOWED_DOMAINS )
114+ def test_get_proxy_forbidden_domain (self ):
115+ request = self .factory .get (self .url , {"url" : "https://evil.com/file.txt" })
116+ response = self .view .get (request )
117+
118+ self .assertEqual (response .status_code , HTTPStatus .FORBIDDEN )
119+ self .assertEqual (response .data , {"error" : ClientStrings .PROXY_FORBIDDEN_DOMAIN })
120+
121+ @patch ("kernelCI_app.views.proxyView.ALLOWED_DOMAINS" , MOCK_ALLOWED_DOMAINS )
122+ @patch ("kernelCI_app.views.proxyView.requests.get" )
123+ def test_get_proxy_wildcard_domain_match (self , mock_requests_get ):
124+ mock_requests_get .return_value .status_code = HTTPStatus .OK
125+ mock_requests_get .return_value .content = b"content"
126+ mock_requests_get .return_value .headers = {}
127+
128+ request = self .factory .get (
129+ self .url , {"url" : "https://any-sub.example.com/file.txt" }
130+ )
131+ response = self .view .get (request )
132+
133+ self .assertEqual (response .status_code , 200 )
134+
135+ @patch ("kernelCI_app.views.proxyView.ALLOWED_DOMAINS" , MOCK_ALLOWED_DOMAINS )
136+ @patch ("kernelCI_app.views.proxyView.requests.get" )
137+ def test_get_proxy_exact_domain_match (self , mock_requests_get ):
138+ mock_requests_get .return_value .status_code = HTTPStatus .OK
139+ mock_requests_get .return_value .content = b"content"
140+ mock_requests_get .return_value .headers = {}
141+
142+ request = self .factory .get (self .url , {"url" : "https://exact.org/file.txt" })
143+ response = self .view .get (request )
144+
145+ self .assertEqual (response .status_code , 200 )
146+
147+ @patch ("kernelCI_app.views.proxyView.ALLOWED_S3_PATHS" , MOCK_ALLOWED_S3_PATHS )
148+ @patch ("kernelCI_app.views.proxyView.ALLOWED_DOMAINS" , ["s3.amazonaws.com" ])
149+ @patch ("kernelCI_app.views.proxyView.requests.get" )
150+ def test_get_proxy_s3_allowed_bucket (self , mock_requests_get ):
151+ mock_requests_get .return_value .status_code = HTTPStatus .OK
152+ mock_requests_get .return_value .content = b"log content"
153+ mock_requests_get .return_value .headers = {}
154+
155+ request = self .factory .get (
156+ self .url ,
157+ {"url" : "https://s3.amazonaws.com/allowed-bucket/file.log" },
158+ )
159+ response = self .view .get (request )
160+
161+ self .assertEqual (response .status_code , 200 )
162+
163+ @patch ("kernelCI_app.views.proxyView.ALLOWED_S3_PATHS" , MOCK_ALLOWED_S3_PATHS )
164+ @patch ("kernelCI_app.views.proxyView.ALLOWED_DOMAINS" , ["s3.amazonaws.com" ])
165+ def test_get_proxy_s3_forbidden_bucket (self ):
166+ request = self .factory .get (
167+ self .url ,
168+ {"url" : "https://s3.amazonaws.com/other-bucket/file.log" },
169+ )
170+ response = self .view .get (request )
171+
172+ self .assertEqual (response .status_code , HTTPStatus .FORBIDDEN )
173+ self .assertEqual (response .data , {"error" : ClientStrings .PROXY_FORBIDDEN_DOMAIN })
0 commit comments