dtlpy 1.117.6__py3-none-any.whl → 1.118.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,6 @@ from copy import deepcopy
2
2
  import traceback
3
3
  import logging
4
4
  import json
5
- import jwt
6
5
  import os
7
6
  from PIL import Image
8
7
  from io import BytesIO
@@ -336,9 +335,7 @@ class Annotations:
336
335
 
337
336
  def _delete_single_annotation(self, w_annotation_id):
338
337
  try:
339
- creator = jwt.decode(self._client_api.token, algorithms=['HS256'],
340
- verify=False, options={'verify_signature': False})['email']
341
- payload = {'username': creator}
338
+ payload = {'username': self._client_api.info()['user_email']}
342
339
  success, response = self._client_api.gen_request(req_type='delete',
343
340
  path='/annotations/{}'.format(w_annotation_id),
344
341
  json_req=payload)
@@ -272,15 +272,16 @@ class Apps:
272
272
 
273
273
  return app
274
274
 
275
- def uninstall(self, app_id: str = None, app_name: str = None, wait: bool = True) -> bool:
275
+ def uninstall(self, app_id: str = None, app_name: str = None, wait: bool = True, app: entities.App = None) -> bool:
276
276
  """
277
277
  Delete an app entity.
278
278
 
279
279
  Note: You are required to add either app_id or app_name.
280
280
 
281
281
  :param str app_id: optional - the id of the app.
282
- :param str app_name: optional - the name of the app.
282
+ :param str app_name: [DEPRECATED] - the name of the app.
283
283
  :param bool wait: optional - wait for the operation to finish.
284
+ :param entities.App app: optional - the app entity.
284
285
  :return whether we succeed uninstalling the specified app.
285
286
  :rtype bool
286
287
 
@@ -288,12 +289,12 @@ class Apps:
288
289
  .. code-block:: python
289
290
  # succeed = dl.apps.delete(app_id='app_id')
290
291
  """
291
- if app_id is None and app_name is None:
292
+ if app is None and app_id is None:
292
293
  raise exceptions.PlatformException(
293
294
  error='400',
294
295
  message='You must provide an identifier in inputs')
295
- if app_name is not None:
296
- app = self.__get_by_name(app_name)
296
+
297
+ if app is not None:
297
298
  app_id = app.id
298
299
 
299
300
  success, response = self._client_api.gen_request(req_type='delete', path='/apps/{}'.format(app_id))
@@ -302,18 +303,16 @@ class Apps:
302
303
 
303
304
  try:
304
305
  app = self.get(app_id=app_id)
305
- except Exception as e:
306
- if e.status_code == '404':
307
- return success
308
- else:
309
- raise e
310
- if app.metadata:
306
+ except exceptions.NotFound:
307
+ return success
308
+
309
+ if wait and app.status == entities.CompositionStatus.TERMINATING and app.metadata is not None:
311
310
  command_id = app.metadata.get('system', {}).get('commands', {}).get('uninstall', None)
312
- if wait and app.status == entities.CompositionStatus.TERMINATING and command_id is not None:
311
+ if command_id is not None:
313
312
  command = self.commands.get(command_id=command_id, url='api/v1/commands/faas/{}'.format(command_id))
314
313
  command.wait()
315
314
 
316
- logger.debug(f"App deleted successfully (id: {app_id}, name: {app_name}")
315
+ logger.debug(f"App deleted successfully (id: {app.id}, name: {app.name}")
317
316
  return success
318
317
 
319
318
  def resume(self, app: entities.App = None, app_id: str = None) -> bool:
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ import copy
2
3
  from .. import exceptions, entities, miscellaneous, _api_reference, repositories
3
4
  from ..services.api_client import ApiClient
4
5
 
@@ -9,18 +10,25 @@ class FeatureSets:
9
10
  """
10
11
  Feature Sets repository
11
12
  """
13
+
12
14
  URL = '/features/sets'
13
15
 
14
- def __init__(self,
15
- client_api: ApiClient,
16
- project_id: str = None,
17
- project: entities.Project = None,
18
- model_id: str = None,
19
- model: entities.Model = None):
16
+ def __init__(
17
+ self,
18
+ client_api: ApiClient,
19
+ project_id: str = None,
20
+ project: entities.Project = None,
21
+ model_id: str = None,
22
+ model: entities.Model = None,
23
+ dataset_id: str = None,
24
+ dataset: entities.Dataset = None,
25
+ ):
20
26
  self._project = project
21
27
  self._project_id = project_id
22
28
  self._model = model
23
29
  self._model_id = model_id
30
+ self._dataset = dataset
31
+ self._dataset_id = dataset_id
24
32
  self._client_api = client_api
25
33
 
26
34
  ############
@@ -28,6 +36,9 @@ class FeatureSets:
28
36
  ############
29
37
  @property
30
38
  def project(self) -> entities.Project:
39
+ if self._project is None and self._project_id is None and self.dataset is not None:
40
+ self._project = self.dataset.project
41
+ self._project_id = self._project.id
31
42
  if self._project is None and self._project_id is not None:
32
43
  # get from id
33
44
  self._project = repositories.Projects(client_api=self._client_api).get(project_id=self._project_id)
@@ -38,9 +49,8 @@ class FeatureSets:
38
49
  self._project = entities.Project.from_json(_json=project, client_api=self._client_api)
39
50
  if self._project is None:
40
51
  raise exceptions.PlatformException(
41
- error='2001',
42
- message='Cannot perform action WITHOUT Project entity in FeatureSets repository.'
43
- ' Please checkout or set a project')
52
+ error='2001', message='Cannot perform action WITHOUT Project entity in FeatureSets repository.' ' Please checkout or set a project'
53
+ )
44
54
  assert isinstance(self._project, entities.Project)
45
55
  return self._project
46
56
 
@@ -50,21 +60,27 @@ class FeatureSets:
50
60
  # get from id
51
61
  self._model = repositories.Models(client_api=self._client_api).get(model_id=self._model_id)
52
62
  if self._model is None:
53
- raise exceptions.PlatformException(
54
- error='2001',
55
- message='Cannot perform action WITHOUT Model entity in FeatureSets repository.')
63
+ raise exceptions.PlatformException(error='2001', message='Cannot perform action WITHOUT Model entity in FeatureSets repository.')
56
64
  assert isinstance(self._model, entities.Model)
57
65
  return self._model
58
66
 
67
+ @property
68
+ def dataset(self) -> entities.Dataset:
69
+ if self._dataset is None and self._dataset_id is not None:
70
+ # get from id
71
+ self._dataset = repositories.Datasets(client_api=self._client_api).get(dataset_id=self._dataset_id)
72
+ if self._dataset is None:
73
+ return None
74
+ assert isinstance(self._dataset, entities.Dataset)
75
+ return self._dataset
76
+
59
77
  ###########
60
78
  # methods #
61
79
  ###########
62
80
 
63
81
  def _list(self, filters: entities.Filters):
64
82
  # request
65
- success, response = self._client_api.gen_request(req_type='POST',
66
- path='/features/sets/query',
67
- json_req=filters.prepare())
83
+ success, response = self._client_api.gen_request(req_type='POST', path='/features/sets/query', json_req=filters.prepare())
68
84
  if not success:
69
85
  raise exceptions.PlatformException(response)
70
86
  return response.json()
@@ -78,29 +94,101 @@ class FeatureSets:
78
94
  :return: Paged entity
79
95
  :rtype: dtlpy.entities.paged_entities.PagedEntities
80
96
  """
81
- # default filters
97
+ # Step 1: Initialize filter (create empty if None)
82
98
  if filters is None:
83
99
  filters = entities.Filters(resource=entities.FiltersResource.FEATURE_SET)
84
- if self._project is not None:
85
- filters.context = {'projects': [self._project.id]}
86
-
87
- # assert type filters
88
- if not isinstance(filters, entities.Filters):
89
- raise exceptions.PlatformException(error='400',
90
- message='Unknown filters type: {!r}'.format(type(filters)))
91
100
 
92
101
  if filters.resource != entities.FiltersResource.FEATURE_SET:
93
102
  raise exceptions.PlatformException(
94
103
  error='400',
95
- message='Filters resource must to be FiltersResource.FEATURE_SET. Got: {!r}'.format(filters.resource))
104
+ message='Filters resource must to be FiltersResource.FEATURE_SET. Got: {!r}'.format(filters.resource),
105
+ )
106
+
107
+ # Step 2: Extract IDs inline (no helper functions, no property access)
108
+ # Extract project_id inline
109
+ if self._project_id is not None:
110
+ project_id = self._project_id
111
+ elif self._project is not None:
112
+ project_id = self._project.id
113
+ else:
114
+ raise exceptions.PlatformException(
115
+ error='2001', message='Cannot perform action WITHOUT Project entity in FeatureSets repository.' ' Please checkout or set a project'
116
+ )
117
+
118
+ # Extract dataset_id inline (only when needed for filtering)
119
+ if self._dataset_id is not None:
120
+ dataset_id = self._dataset_id
121
+ elif self._dataset is not None:
122
+ dataset_id = self._dataset.id
123
+ else:
124
+ dataset_id = None # No dataset filtering needed
125
+
126
+ # Set context with project_id
127
+ filters.context = {'projects': [project_id]}
128
+
129
+ # Preserve original page and page size values before any operations that might modify them
130
+ received_filter = copy.deepcopy(filters)
131
+
132
+ # Step 3: Execute received filter - Run filter with appropriate pagination
133
+ # When dataset_id is None: use original pagination (respect user's page request)
134
+ # When dataset_id is not None: start from page 0 to collect all IDs (pagination applied later)
135
+ if dataset_id is not None:
136
+ page_offset = 0
137
+ received_filter_paged = entities.PagedEntities(
138
+ items_repository=self,
139
+ filters=filters,
140
+ page_offset=page_offset,
141
+ page_size=filters.page_size,
142
+ client_api=self._client_api,
143
+ )
144
+ # Step 5: Dataset_id exists and items exist - extract IDs from all pages
145
+ # Extract feature set IDs from all pages (received_filter_paged.all() will fetch all pages starting from page 0)
146
+ filter_fs_ids = [feature_set.id for feature_set in received_filter_paged.all()]
147
+
148
+ # Step 6: Run aggregation API (when dataset_id exists)
149
+ payload = {
150
+ "projectId": project_id,
151
+ "datasetIds": [dataset_id],
152
+ }
153
+ success, response = self._client_api.gen_request(req_type="POST", path="/features/vectors/project-count-aggregation", json_req=payload)
154
+ if not success:
155
+ raise exceptions.PlatformException(response)
156
+ result = response.json()
157
+ # Extract dataset feature set IDs from response, filtering out entries where count == 0
158
+ dataset_fs_ids = [item['featureSetId'] for item in result if item.get('count', 0) > 0]
159
+
160
+ # Step 7: Intersect IDs
161
+ final_fs_ids = list(set(filter_fs_ids).intersection(set(dataset_fs_ids)))
162
+
163
+ # Step 8: Final return path - Create filter with intersected IDs (no join needed)
164
+ intersected_ids_filter = entities.Filters(resource=entities.FiltersResource.FEATURE_SET)
165
+ intersected_ids_filter.add(field='id', operator=entities.FiltersOperations.IN, values=final_fs_ids)
166
+ intersected_ids_filter.page = received_filter.page # Preserve original pagination
167
+ intersected_ids_filter.page_size = received_filter.page_size
168
+ intersected_ids_filter.context = {'projects': [project_id]}
169
+
170
+ filter_paged = entities.PagedEntities(
171
+ items_repository=self,
172
+ filters=intersected_ids_filter,
173
+ page_offset=intersected_ids_filter.page,
174
+ page_size=intersected_ids_filter.page_size,
175
+ client_api=self._client_api,
176
+ )
177
+ filter_paged.get_page()
178
+
179
+ else:
180
+ page_offset = filters.page if dataset_id is None else 0
96
181
 
97
- paged = entities.PagedEntities(items_repository=self,
98
- filters=filters,
99
- page_offset=filters.page,
100
- page_size=filters.page_size,
101
- client_api=self._client_api)
102
- paged.get_page()
103
- return paged
182
+ filter_paged = entities.PagedEntities(
183
+ items_repository=self,
184
+ filters=filters,
185
+ page_offset=page_offset,
186
+ page_size=filters.page_size,
187
+ client_api=self._client_api,
188
+ )
189
+ filter_paged.get_page()
190
+
191
+ return filter_paged
104
192
 
105
193
  @_api_reference.add(path='/features/sets/{id}', method='get')
106
194
  def get(self, feature_set_name: str = None, feature_set_id: str = None) -> entities.Feature:
@@ -112,45 +200,31 @@ class FeatureSets:
112
200
  :return: Feature object
113
201
  """
114
202
  if feature_set_id is not None:
115
- success, response = self._client_api.gen_request(req_type="GET",
116
- path="{}/{}".format(self.URL, feature_set_id))
203
+ success, response = self._client_api.gen_request(req_type="GET", path="{}/{}".format(self.URL, feature_set_id))
117
204
  if not success:
118
205
  raise exceptions.PlatformException(response)
119
- feature_set = entities.FeatureSet.from_json(client_api=self._client_api,
120
- _json=response.json())
206
+ feature_set = entities.FeatureSet.from_json(client_api=self._client_api, _json=response.json())
121
207
  elif feature_set_name is not None:
122
208
  if not isinstance(feature_set_name, str):
123
- raise exceptions.PlatformException(
124
- error='400',
125
- message='feature_set_name must be string')
209
+ raise exceptions.PlatformException(error='400', message='feature_set_name must be string')
126
210
  filters = entities.Filters(resource=entities.FiltersResource.FEATURE_SET)
127
211
  filters.add(field='name', values=feature_set_name)
128
212
  feature_sets = self.list(filters=filters)
129
213
  if feature_sets.items_count == 0:
130
- raise exceptions.PlatformException(
131
- error='404',
132
- message='Feature set not found. name: {!r}'.format(feature_set_name))
214
+ raise exceptions.PlatformException(error='404', message='Feature set not found. name: {!r}'.format(feature_set_name))
133
215
  elif feature_sets.items_count > 1:
134
216
  # more than one matching project
135
- raise exceptions.PlatformException(
136
- error='404',
137
- message='More than one feature_set with same name. Please "get" by id')
217
+ raise exceptions.PlatformException(error='404', message='More than one feature_set with same name. Please "get" by id')
138
218
  else:
139
219
  feature_set = feature_sets.items[0]
140
220
  else:
141
- raise exceptions.PlatformException(
142
- error='400',
143
- message='Must provide an identifier in inputs, feature_set_name or feature_set_id')
221
+ raise exceptions.PlatformException(error='400', message='Must provide an identifier in inputs, feature_set_name or feature_set_id')
144
222
  return feature_set
145
223
 
146
224
  @_api_reference.add(path='/features/sets', method='post')
147
- def create(self, name: str,
148
- size: int,
149
- set_type: str,
150
- entity_type: entities.FeatureEntityType,
151
- project_id: str = None,
152
- model_id: set = None,
153
- org_id: str = None):
225
+ def create(
226
+ self, name: str, size: int, set_type: str, entity_type: entities.FeatureEntityType, project_id: str = None, model_id: set = None, org_id: str = None
227
+ ):
154
228
  """
155
229
  Create a new Feature Set
156
230
 
@@ -169,25 +243,17 @@ class FeatureSets:
169
243
  else:
170
244
  project_id = self._project.id
171
245
 
172
- payload = {'name': name,
173
- 'size': size,
174
- 'type': set_type,
175
- 'project': project_id,
176
- 'modelId': model_id,
177
- 'entityType': entity_type}
246
+ payload = {'name': name, 'size': size, 'type': set_type, 'project': project_id, 'modelId': model_id, 'entityType': entity_type}
178
247
  if org_id is not None:
179
248
  payload['org'] = org_id
180
- success, response = self._client_api.gen_request(req_type="post",
181
- json_req=payload,
182
- path=self.URL)
249
+ success, response = self._client_api.gen_request(req_type="post", json_req=payload, path=self.URL)
183
250
 
184
251
  # exception handling
185
252
  if not success:
186
253
  raise exceptions.PlatformException(response)
187
254
 
188
255
  # return entity
189
- return entities.FeatureSet.from_json(client_api=self._client_api,
190
- _json=response.json()[0])
256
+ return entities.FeatureSet.from_json(client_api=self._client_api, _json=response.json()[0])
191
257
 
192
258
  @_api_reference.add(path='/features/sets/{id}', method='delete')
193
259
  def delete(self, feature_set_id: str):
@@ -199,8 +265,7 @@ class FeatureSets:
199
265
  :rtype: bool
200
266
  """
201
267
 
202
- success, response = self._client_api.gen_request(req_type="delete",
203
- path=f"{self.URL}/{feature_set_id}")
268
+ success, response = self._client_api.gen_request(req_type="delete", path=f"{self.URL}/{feature_set_id}")
204
269
 
205
270
  # check response
206
271
  if success:
@@ -212,31 +277,27 @@ class FeatureSets:
212
277
  @_api_reference.add(path='/features/set/{id}', method='patch')
213
278
  def update(self, feature_set: entities.FeatureSet) -> entities.FeatureSet:
214
279
  """
215
- Update a Feature Set
280
+ Update a Feature Set
216
281
 
217
- **Prerequisites**: You must be in the role of an *owner* or *developer*.
282
+ **Prerequisites**: You must be in the role of an *owner* or *developer*.
218
283
 
219
- :param dtlpy.entities.FeatureSet feature_set: FeatureSet object
220
- :return: FeatureSet
221
- :rtype: dtlpy.entities.FeatureSet
284
+ :param dtlpy.entities.FeatureSet feature_set: FeatureSet object
285
+ :return: FeatureSet
286
+ :rtype: dtlpy.entities.FeatureSet
222
287
 
223
- **Example**:
288
+ **Example**:
224
289
 
225
- .. code-block:: python
290
+ .. code-block:: python
226
291
 
227
- dl.feature_sets.update(feature_set='feature_set')
228
- """
229
- success, response = self._client_api.gen_request(req_type="patch",
230
- path=f"{self.URL}/{feature_set.id}",
231
- json_req=feature_set.to_json())
292
+ dl.feature_sets.update(feature_set='feature_set')
293
+ """
294
+ success, response = self._client_api.gen_request(req_type="patch", path=f"{self.URL}/{feature_set.id}", json_req=feature_set.to_json())
232
295
  if not success:
233
296
  raise exceptions.PlatformException(response)
234
297
 
235
298
  logger.debug("feature_set updated successfully")
236
299
  # update dataset labels
237
- feature_set = entities.FeatureSet.from_json(_json=response.json(),
238
- client_api=self._client_api,
239
- is_fetched=True)
300
+ feature_set = entities.FeatureSet.from_json(_json=response.json(), client_api=self._client_api, is_fetched=True)
240
301
  return feature_set
241
302
 
242
303
  def _build_entities_from_response(self, response_items) -> miscellaneous.List[entities.Item]:
@@ -244,9 +305,7 @@ class FeatureSets:
244
305
  jobs = [None for _ in range(len(response_items))]
245
306
  # return triggers list
246
307
  for i_item, item in enumerate(response_items):
247
- jobs[i_item] = pool.submit(entities.FeatureSet._protected_from_json,
248
- **{'client_api': self._client_api,
249
- '_json': item})
308
+ jobs[i_item] = pool.submit(entities.FeatureSet._protected_from_json, **{'client_api': self._client_api, '_json': item})
250
309
  # get all results
251
310
  results = [j.result() for j in jobs]
252
311
  # log errors
@@ -513,6 +513,13 @@ class Packages:
513
513
  if codebase is None:
514
514
  src_path = os.getcwd()
515
515
  logger.warning('No src_path is given, getting package information from cwd: {}'.format(src_path))
516
+ else:
517
+ # Validate src_path to prevent path traversal
518
+ miscellaneous.PathUtils.validate_directory_path(
519
+ src_path,
520
+ base_path=os.getcwd(),
521
+ must_exist=True
522
+ )
516
523
 
517
524
  # get package json
518
525
  package_from_json = dict()
@@ -941,6 +948,8 @@ class Packages:
941
948
 
942
949
  project.packages.deploy_from_file(project='project_entity', json_filepath='json_filepath')
943
950
  """
951
+ # Validate file path to prevent path traversal
952
+ miscellaneous.PathUtils.validate_file_path(json_filepath)
944
953
  with open(json_filepath, 'r') as f:
945
954
  data = json.load(f)
946
955
 
@@ -1,6 +1,5 @@
1
1
  import logging
2
2
  from urllib.parse import quote
3
- import jwt
4
3
 
5
4
  from .. import entities, miscellaneous, exceptions, _api_reference
6
5
  from ..services.api_client import ApiClient
@@ -159,8 +158,7 @@ class Projects:
159
158
  assert isinstance(title, str)
160
159
  assert isinstance(content, str)
161
160
  if self._client_api.token is not None:
162
- sender = jwt.decode(self._client_api.token, algorithms=['HS256'],
163
- verify=False, options={'verify_signature': False})['email']
161
+ sender = self._client_api.info()['user_email']
164
162
  else:
165
163
  raise exceptions.PlatformException('600', 'Token expired please log in')
166
164
 
@@ -856,8 +856,16 @@ class ApiClient:
856
856
  """
857
857
  user_email = 'null'
858
858
  if self.token is not None:
859
- payload = jwt.decode(self.token, algorithms=['HS256'],
860
- verify=False, options={'verify_signature': False})
859
+ # oxsec-disable jwt-signature-disabled - Client-side SDK: signature verification disabled intentionally to extract user email; server validates on API calls
860
+ payload = jwt.decode(
861
+ self.token,
862
+ options={
863
+ "verify_signature": False,
864
+ "verify_exp": False,
865
+ "verify_aud": False,
866
+ "verify_iss": False,
867
+ }
868
+ )
861
869
  user_email = payload['email']
862
870
  information = {'environment': self.environment,
863
871
  'user_email': user_email}
@@ -1353,8 +1361,16 @@ class ApiClient:
1353
1361
  if self.token is None or self.token == '':
1354
1362
  expired = True
1355
1363
  else:
1356
- payload = jwt.decode(self.token, algorithms=['HS256'],
1357
- options={'verify_signature': False}, verify=False)
1364
+ # oxsec-disable jwt-signature-disabled - Client-side SDK: signature verification disabled intentionally to check token expiration; server validates on API calls
1365
+ payload = jwt.decode(
1366
+ self.token,
1367
+ options={
1368
+ "verify_signature": False,
1369
+ "verify_exp": False,
1370
+ "verify_aud": False,
1371
+ "verify_iss": False,
1372
+ }
1373
+ )
1358
1374
  d = datetime.datetime.now(datetime.timezone.utc)
1359
1375
  epoch = datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)
1360
1376
  now = (d - epoch).total_seconds()
@@ -2,7 +2,6 @@ import time
2
2
  import logging
3
3
  import threading
4
4
  import traceback
5
- import jwt
6
5
 
7
6
  logger = logging.getLogger(name='dtlpy')
8
7
 
@@ -21,9 +20,7 @@ def check_in_thread(version, client_api):
21
20
 
22
21
  # try read token for email
23
22
  try:
24
- payload = jwt.decode(client_api.token, algorithms=['HS256'],
25
- verify=False, options={'verify_signature': False})
26
- user_email = payload['email']
23
+ user_email = client_api.info()['user_email']
27
24
  except Exception:
28
25
  user_email = 'na'
29
26
  logger.debug('SDK info: user: {}, version: {}'.format(user_email, version))
dtlpy/services/logins.py CHANGED
@@ -32,15 +32,12 @@ def login_secret(api_client, email, password, client_id, client_secret=None, for
32
32
  # TODO add deprecation warning to client_id
33
33
  # check if already logged in with SAME email
34
34
  if api_client.token is not None or api_client.token == '':
35
- try:
36
- payload = jwt.decode(api_client.token, algorithms=['HS256'],
37
- options={'verify_signature': False}, verify=False)
38
- if 'email' in payload and \
39
- payload['email'] == email and \
40
- not api_client.token_expired() and \
41
- not force:
42
- return True
43
- except jwt.exceptions.DecodeError:
35
+ logged_email = api_client.info()['user_email']
36
+ if logged_email == email and \
37
+ not api_client.token_expired() and \
38
+ not force:
39
+ return True
40
+ else:
44
41
  logger.debug('{}'.format('Cant decode token. Force login is used'))
45
42
 
46
43
  logger.info('[Start] Login Secret')
@@ -71,13 +68,12 @@ def login_secret(api_client, email, password, client_id, client_secret=None, for
71
68
  api_client.refresh_token = response_dict['refresh_token']
72
69
 
73
70
  # set new client id for refresh
74
- payload = jwt.decode(api_client.token, algorithms=['HS256'],
75
- options={'verify_signature': False}, verify=False)
76
- if 'email' in payload:
77
- logger.info('[Done] Login Secret. User: {}'.format(payload['email']))
71
+ logged_email = api_client.info()['user_email']
72
+
73
+ if not logged_email == 'null':
74
+ logger.info(f'[Done] Login Secret. User: {logged_email}')
78
75
  else:
79
- logger.info('[Done] Login Secret. User: {}'.format(email))
80
- logger.info(payload)
76
+ logger.info(f'[Done] Login Secret. User: {email}')
81
77
  return True
82
78
 
83
79
 
@@ -206,8 +202,16 @@ def login(api_client, auth0_url=None, audience=None, client_id=None, login_domai
206
202
  success, tokens = server.process_request()
207
203
 
208
204
  if success:
209
- decoded_jwt = jwt.decode(tokens['id'], verify=False,
210
- options={'verify_signature': False})
205
+ # oxsec-disable jwt-signature-disabled - Client-side SDK: signature verification disabled intentionally to extract claims for display; server validates on API calls
206
+ decoded_jwt = jwt.decode(
207
+ tokens['id'],
208
+ options={
209
+ "verify_signature": False,
210
+ "verify_exp": False,
211
+ "verify_aud": False,
212
+ "verify_iss": False,
213
+ }
214
+ )
211
215
 
212
216
  if 'email' in decoded_jwt:
213
217
  logger.info('Logged in: {}'.format(decoded_jwt['email']))
@@ -7,6 +7,7 @@ import os
7
7
  import logging
8
8
  import dtlpy as dl
9
9
  import shutil
10
+ from dtlpy import miscellaneous
10
11
 
11
12
  logger = logging.getLogger(name='dtlpy')
12
13
 
@@ -344,6 +345,9 @@ class Videos:
344
345
  raise
345
346
  # https://www.ffmpeg.org/ffmpeg-formats.html#Examples-9
346
347
 
348
+ # Validate filepath to prevent path traversal
349
+ miscellaneous.PathUtils.validate_file_path(filepath)
350
+
347
351
  if not os.path.isfile(filepath):
348
352
  raise IOError('File doesnt exists: {}'.format(filepath))
349
353
  logger.info('Extracting video information...')