From 4e33b5b478a3ac038d92bb31ebacbf4fe1d34e1e Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 30 Jul 2025 14:49:12 +0800 Subject: [PATCH] perf: some risk example file path --- apps/acls/models/base.py | 2 +- apps/audits/tasks.py | 31 +++++++++---------- apps/common/utils/safe.py | 9 ++++++ apps/common/utils/time_period.py | 24 -------------- apps/common/utils/timezone.py | 23 ++++++++++++++ apps/i18n/lina/en.json | 4 +-- apps/i18n/lina/es.json | 2 +- apps/i18n/lina/ja.json | 2 +- apps/i18n/lina/ko.json | 2 +- apps/i18n/lina/pt_br.json | 2 +- apps/i18n/lina/ru.json | 2 +- apps/i18n/lina/zh.json | 6 +++- apps/i18n/lina/zh_hant.json | 2 +- .../rewriting/storage/permissions.py | 9 +++--- apps/jumpserver/settings/base.py | 14 +++++++-- apps/rbac/backends.py | 5 +-- apps/rbac/permissions.py | 6 ++-- 17 files changed, 83 insertions(+), 62 deletions(-) create mode 100644 apps/common/utils/safe.py delete mode 100644 apps/common/utils/time_period.py diff --git a/apps/acls/models/base.py b/apps/acls/models/base.py index b8757cea5..a78b77b5b 100644 --- a/apps/acls/models/base.py +++ b/apps/acls/models/base.py @@ -5,7 +5,7 @@ from django.utils.translation import gettext_lazy as _ from common.db.fields import JSONManyToManyField from common.db.models import JMSBaseModel from common.utils import contains_ip -from common.utils.time_period import contains_time_period +from common.utils.timezone import contains_time_period from orgs.mixins.models import OrgModelMixin, OrgManager from ..const import ActionChoices diff --git a/apps/audits/tasks.py b/apps/audits/tasks.py index b9d2c617e..e61404372 100644 --- a/apps/audits/tasks.py +++ b/apps/audits/tasks.py @@ -10,10 +10,12 @@ from django.core.files.storage import default_storage from django.db import transaction from django.utils import timezone from django.utils.translation import gettext_lazy as _ +from django.utils._os import safe_join from common.const.crontab import CRONTAB_AT_AM_TWO from common.storage.ftp_file import FTPFileStorageHandler from common.utils import get_log_keep_day, get_logger +from common.utils.safe import safe_run_cmd from ops.celery.decorator import register_as_period_task from ops.models import CeleryTaskExecution from orgs.utils import tmp_to_root_org @@ -57,14 +59,12 @@ def clean_ftp_log_period(): now = timezone.now() days = get_log_keep_day('FTP_LOG_KEEP_DAYS') expired_day = now - datetime.timedelta(days=days) - file_store_dir = os.path.join(default_storage.base_location, FTPLog.upload_to) + file_store_dir = safe_join(default_storage.base_location, FTPLog.upload_to) FTPLog.objects.filter(date_start__lt=expired_day).delete() - command = "find %s -mtime +%s -type f -exec rm -f {} \\;" % ( - file_store_dir, days - ) - subprocess.call(command, shell=True) - command = "find %s -type d -empty -delete;" % file_store_dir - subprocess.call(command, shell=True) + command = "find %s -mtime +%s -type f -exec rm -f {} \\;" + safe_run_cmd(command, (file_store_dir, days)) + command = "find %s -type d -empty -delete;" + safe_run_cmd(command, (file_store_dir,)) logger.info("Clean FTP file done") @@ -76,12 +76,11 @@ def clean_celery_tasks_period(): tasks.delete() tasks = CeleryTaskExecution.objects.filter(date_start__isnull=True) tasks.delete() - command = "find %s -mtime +%s -name '*.log' -type f -exec rm -f {} \\;" % ( - settings.CELERY_LOG_DIR, expire_days - ) - subprocess.call(command, shell=True) - command = "echo > {}".format(os.path.join(settings.LOG_DIR, 'celery.log')) - subprocess.call(command, shell=True) + command = "find %s -mtime +%s -name '*.log' -type f -exec rm -f {} \\;" + safe_run_cmd(command, (settings.CELERY_LOG_DIR, expire_days)) + celery_log_path = safe_join(settings.LOG_DIR, 'celery.log') + command = "echo > {}".format(celery_log_path) + safe_run_cmd(command, (celery_log_path,)) def batch_delete(queryset, batch_size=3000): @@ -119,15 +118,15 @@ def clean_expired_session_period(): expired_sessions = Session.objects.filter(date_start__lt=expire_date) timestamp = expire_date.timestamp() expired_commands = Command.objects.filter(timestamp__lt=timestamp) - replay_dir = os.path.join(default_storage.base_location, 'replay') + replay_dir = safe_join(default_storage.base_location, 'replay') batch_delete(expired_sessions) logger.info("Clean session item done") batch_delete(expired_commands) logger.info("Clean session command done") remove_files_by_days(replay_dir, days) - command = "find %s -type d -empty -delete;" % replay_dir - subprocess.call(command, shell=True) + command = "find %s -type d -empty -delete;" + safe_run_cmd(command, (replay_dir,)) logger.info("Clean session replay done") diff --git a/apps/common/utils/safe.py b/apps/common/utils/safe.py new file mode 100644 index 000000000..26ae79e9b --- /dev/null +++ b/apps/common/utils/safe.py @@ -0,0 +1,9 @@ +import re +import subprocess +import shlex + + +def safe_run_cmd(cmd_str, cmd_args=(), shell=True): + cmd_args = [shlex.quote(arg) for arg in cmd_args] + cmd = cmd_str % tuple(cmd_args) + return subprocess.run(cmd, shell=shell) \ No newline at end of file diff --git a/apps/common/utils/time_period.py b/apps/common/utils/time_period.py deleted file mode 100644 index 0b25b4e34..000000000 --- a/apps/common/utils/time_period.py +++ /dev/null @@ -1,24 +0,0 @@ -from common.utils.timezone import local_now - - -def contains_time_period(time_periods, ctime=None): - """ - time_periods: [{"id": 1, "value": "00:00~07:30、10:00~13:00"}, {"id": 2, "value": "00:00~00:00"}] - """ - if not time_periods: - return None - - if ctime is None: - ctime = local_now() - current_time = ctime.strftime('%H:%M') - today_time_period = next(filter(lambda x: str(x['id']) == local_now().strftime("%w"), time_periods)) - today_time_period = today_time_period['value'] - if not today_time_period: - return False - - for time in today_time_period.split('、'): - start, end = time.split('~') - end = "24:00" if end == "00:00" else end - if start <= current_time <= end: - return True - return False diff --git a/apps/common/utils/timezone.py b/apps/common/utils/timezone.py index 9db9d34d2..6c2b6e5aa 100644 --- a/apps/common/utils/timezone.py +++ b/apps/common/utils/timezone.py @@ -57,6 +57,29 @@ def is_date_more_than(d1, d2, threshold='1d'): return d1 - d2 > delta +def contains_time_period(time_periods, ctime=None): + """ + time_periods: [{"id": 1, "value": "00:00~07:30、10:00~13:00"}, {"id": 2, "value": "00:00~00:00"}] + """ + if not time_periods: + return None + + if ctime is None: + ctime = local_now() + current_time = ctime.strftime('%H:%M') + today_time_period = next(filter(lambda x: str(x['id']) == local_now().strftime("%w"), time_periods)) + today_time_period = today_time_period['value'] + if not today_time_period: + return False + + for time in today_time_period.split('、'): + start, end = time.split('~') + end = "24:00" if end == "00:00" else end + if start <= current_time <= end: + return True + return False + + _rest_dt_field = DateTimeField() dt_parser = _rest_dt_field.to_internal_value dt_formatter = _rest_dt_field.to_representation diff --git a/apps/i18n/lina/en.json b/apps/i18n/lina/en.json index a5e6d8de7..eb8e64182 100644 --- a/apps/i18n/lina/en.json +++ b/apps/i18n/lina/en.json @@ -616,7 +616,7 @@ "Gateway": "Gateway", "GatewayCreate": "Create gateway", "GatewayList": "Gateways", - "GatewayPlatformHelpText": "Only platforms with names starting with ‘Gateway’ can be used as gateways.", + "GatewayPlatformHelpText": "Only platforms with names starting with 'Gateway' can be used as gateways.", "GatewayUpdate": "Update the gateway", "General": "General", "GeneralAccounts": "General accounts", @@ -1560,4 +1560,4 @@ "userId": "User ID", "userName": "User name", "EmailHelpText": "Please click the 'Submit' button to save the current configuration before clicking 'Test Connection' to ensure the settings take effect." -} \ No newline at end of file +} diff --git a/apps/i18n/lina/es.json b/apps/i18n/lina/es.json index 93e3123d9..3f97e8a61 100644 --- a/apps/i18n/lina/es.json +++ b/apps/i18n/lina/es.json @@ -1564,4 +1564,4 @@ "setVariable": "configurar parámetros", "userId": "ID de usuario", "userName": "Nombre de usuario" -} \ No newline at end of file +} diff --git a/apps/i18n/lina/ja.json b/apps/i18n/lina/ja.json index beb455e36..4b05ece8c 100644 --- a/apps/i18n/lina/ja.json +++ b/apps/i18n/lina/ja.json @@ -1569,4 +1569,4 @@ "setVariable": "パラメータ設定", "userId": "ユーザーID", "userName": "ユーザー名" -} \ No newline at end of file +} diff --git a/apps/i18n/lina/ko.json b/apps/i18n/lina/ko.json index 448c8d739..095bbd899 100644 --- a/apps/i18n/lina/ko.json +++ b/apps/i18n/lina/ko.json @@ -1564,4 +1564,4 @@ "setVariable": "설정 매개변수", "userId": "사용자 ID", "userName": "사용자명" -} \ No newline at end of file +} diff --git a/apps/i18n/lina/pt_br.json b/apps/i18n/lina/pt_br.json index 7a2f5ac84..8ae5ebd26 100644 --- a/apps/i18n/lina/pt_br.json +++ b/apps/i18n/lina/pt_br.json @@ -1565,4 +1565,4 @@ "setVariable": "Parâmetros de configuração", "userId": "ID do usuário", "userName": "Usuário" -} \ No newline at end of file +} diff --git a/apps/i18n/lina/ru.json b/apps/i18n/lina/ru.json index e44387a8e..35df9341b 100644 --- a/apps/i18n/lina/ru.json +++ b/apps/i18n/lina/ru.json @@ -1566,4 +1566,4 @@ "setVariable": "Задать переменную", "userId": "ID пользователя", "userName": "Имя пользовател" -} \ No newline at end of file +} diff --git a/apps/i18n/lina/zh.json b/apps/i18n/lina/zh.json index 749f6f7a1..062ecf78f 100644 --- a/apps/i18n/lina/zh.json +++ b/apps/i18n/lina/zh.json @@ -580,6 +580,10 @@ "FaviconTip": "提示:网站图标(建议图片大小为: 16px*16px)", "Features": "功能设置", "FeiShu": "飞书", + "PasskeySummary": "无密码生物识别认证", + "Common": "通用", + "SSO": "单点登录", + "IdP": "身份提供者", "FeiShuOAuth": "飞书认证", "FeiShuTest": "测试", "FieldRequiredError": "此字段是必填项", @@ -1566,4 +1570,4 @@ "userName": "用户名", "EmailHelpText": "请点击'提交'按钮保存当前配置后,再点击'测试连接'以确保信息生效", "None": "无" -} \ No newline at end of file +} diff --git a/apps/i18n/lina/zh_hant.json b/apps/i18n/lina/zh_hant.json index b0df5165a..7a549b410 100644 --- a/apps/i18n/lina/zh_hant.json +++ b/apps/i18n/lina/zh_hant.json @@ -1569,4 +1569,4 @@ "setVariable": "設置參數", "userId": "用戶ID", "userName": "用戶名" -} \ No newline at end of file +} diff --git a/apps/jumpserver/rewriting/storage/permissions.py b/apps/jumpserver/rewriting/storage/permissions.py index c33f78268..7eb0bc771 100644 --- a/apps/jumpserver/rewriting/storage/permissions.py +++ b/apps/jumpserver/rewriting/storage/permissions.py @@ -1,9 +1,9 @@ # ~*~ coding: utf-8 ~*~ path_perms_map = { - 'xpack': '*', - 'settings': '*', - 'img': '*', + 'xpack': 'none', + 'settings': 'none', + 'img': 'none', 'replay': 'terminal.view_sessionreplay', 'applets': 'terminal.view_applet', 'virtual_apps': 'terminal.view_virtualapp', @@ -23,7 +23,8 @@ def allow_access(private_file): return False if not path_perm: return False - if path_perm == '*' or request.user.has_perms([path_perm]): + if path_perm == 'none' or request.user.has_perms([path_perm]): + # 不需要权限检查,任何人都可以访问 return True if path_perm == 'default': return request.user.is_authenticated and request.user.is_staff diff --git a/apps/jumpserver/settings/base.py b/apps/jumpserver/settings/base.py index cbef10008..56b745f85 100644 --- a/apps/jumpserver/settings/base.py +++ b/apps/jumpserver/settings/base.py @@ -87,15 +87,23 @@ ALLOWED_DOMAINS.extend(DEBUG_HOST_PORTS) # for host in ALLOWED_DOMAINS: # print(' - ' + host.lstrip('.')) -ALLOWED_HOSTS = ['*'] +ALLOWED_HOSTS = [] # https://docs.djangoproject.com/en/4.1/ref/settings/#std-setting-CSRF_TRUSTED_ORIGINS CSRF_TRUSTED_ORIGINS = [] for host_port in ALLOWED_DOMAINS: origin = host_port.strip('.') - if origin.startswith('http'): - CSRF_TRUSTED_ORIGINS.append(origin) + + if not origin: continue + + if origin.startswith('http'): + origin = origin.replace('http://', '').replace('https://', '') + + host = origin.split(':')[0] + if host not in ALLOWED_HOSTS: + ALLOWED_HOSTS.append(host) + is_local_origin = origin.split(':')[0] in DEBUG_HOSTS for schema in ['https', 'http']: if is_local_origin and schema == 'https': diff --git a/apps/rbac/backends.py b/apps/rbac/backends.py index 4e91e818d..9b539f537 100644 --- a/apps/rbac/backends.py +++ b/apps/rbac/backends.py @@ -16,10 +16,11 @@ class RBACBackend(JMSBaseAuthBackend): return False def has_perm(self, user_obj, perm, obj=None): + # 扫描软件对 * 毕竟敏感,所以改成 none, 虽说这个 * 是我们自定义的标识 + if perm == 'none': + return True if not user_obj.is_active or not perm: raise PermissionDenied() - if perm == '*': - return True if isinstance(perm, str): perm_set = set(i.strip() for i in perm.split('|')) elif isinstance(perm, (list, tuple, set)): diff --git a/apps/rbac/permissions.py b/apps/rbac/permissions.py index 746e5a038..d8c158a87 100644 --- a/apps/rbac/permissions.py +++ b/apps/rbac/permissions.py @@ -16,10 +16,10 @@ class RBACPermission(permissions.DjangoModelPermissions): ('bulk_update', '%(app_label)s.change_%(model_name)s'), ('partial_bulk_update', '%(app_label)s.change_%(model_name)s'), ('bulk_destroy', '%(app_label)s.delete_%(model_name)s'), - ('render_to_json', '*'), - ('metadata', '*'), + ('render_to_json', 'none'), + ('metadata', 'none'), ('GET', '%(app_label)s.view_%(model_name)s'), - ('OPTIONS', '*'), + ('OPTIONS', 'none'), ('HEAD', '%(app_label)s.view_%(model_name)s'), ('POST', '%(app_label)s.add_%(model_name)s'), ('PUT', '%(app_label)s.change_%(model_name)s'),