作者 RuoYi

定时任务目标字符串验证包名白名单

@@ -150,8 +150,18 @@ public class Constants @@ -150,8 +150,18 @@ public class Constants
150 public static final String LOOKUP_LDAP = "ldap:"; 150 public static final String LOOKUP_LDAP = "ldap:";
151 151
152 /** 152 /**
  153 + * LDAPS 远程方法调用
  154 + */
  155 + public static final String LOOKUP_LDAPS = "ldaps:";
  156 +
  157 + /**
  158 + * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
  159 + */
  160 + public static final String[] JOB_WHITELIST_STR = { "com.ruoyi" };
  161 +
  162 + /**
153 * 定时任务违规的字符 163 * 定时任务违规的字符
154 */ 164 */
155 public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml", 165 public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
156 - "org.springframework" };  
157 -}  
  166 + "org.springframework", "org.apache" };
  167 +}
@@ -25,6 +25,7 @@ import com.ruoyi.common.utils.poi.ExcelUtil; @@ -25,6 +25,7 @@ import com.ruoyi.common.utils.poi.ExcelUtil;
25 import com.ruoyi.quartz.domain.SysJob; 25 import com.ruoyi.quartz.domain.SysJob;
26 import com.ruoyi.quartz.service.ISysJobService; 26 import com.ruoyi.quartz.service.ISysJobService;
27 import com.ruoyi.quartz.util.CronUtils; 27 import com.ruoyi.quartz.util.CronUtils;
  28 +import com.ruoyi.quartz.util.ScheduleUtils;
28 29
29 /** 30 /**
30 * 调度任务信息操作处理 31 * 调度任务信息操作处理
@@ -89,18 +90,22 @@ public class SysJobController extends BaseController @@ -89,18 +90,22 @@ public class SysJobController extends BaseController
89 { 90 {
90 return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用"); 91 return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用");
91 } 92 }
92 - else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_LDAP)) 93 + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS }))
93 { 94 {
94 return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap'调用"); 95 return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap'调用");
95 } 96 }
96 else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS })) 97 else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS }))
97 { 98 {
98 - return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)//'调用"); 99 + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用");
99 } 100 }
100 else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR)) 101 else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR))
101 { 102 {
102 return error("新增任务'" + job.getJobName() + "'失败,目标字符串存在违规"); 103 return error("新增任务'" + job.getJobName() + "'失败,目标字符串存在违规");
103 } 104 }
  105 + else if (!ScheduleUtils.whiteList(job.getInvokeTarget()))
  106 + {
  107 + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不在白名单内");
  108 + }
104 job.setCreateBy(getUsername()); 109 job.setCreateBy(getUsername());
105 return toAjax(jobService.insertJob(job)); 110 return toAjax(jobService.insertJob(job));
106 } 111 }
@@ -121,18 +126,22 @@ public class SysJobController extends BaseController @@ -121,18 +126,22 @@ public class SysJobController extends BaseController
121 { 126 {
122 return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用"); 127 return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用");
123 } 128 }
124 - else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_LDAP)) 129 + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS }))
125 { 130 {
126 return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap'调用"); 131 return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap'调用");
127 } 132 }
128 else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS })) 133 else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS }))
129 { 134 {
130 - return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)//'调用"); 135 + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用");
131 } 136 }
132 else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR)) 137 else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR))
133 { 138 {
134 return error("修改任务'" + job.getJobName() + "'失败,目标字符串存在违规"); 139 return error("修改任务'" + job.getJobName() + "'失败,目标字符串存在违规");
135 } 140 }
  141 + else if (!ScheduleUtils.whiteList(job.getInvokeTarget()))
  142 + {
  143 + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不在白名单内");
  144 + }
136 job.setUpdateBy(getUsername()); 145 job.setUpdateBy(getUsername());
137 return toAjax(jobService.updateJob(job)); 146 return toAjax(jobService.updateJob(job));
138 } 147 }
@@ -10,9 +10,11 @@ import org.quartz.Scheduler; @@ -10,9 +10,11 @@ import org.quartz.Scheduler;
10 import org.quartz.SchedulerException; 10 import org.quartz.SchedulerException;
11 import org.quartz.TriggerBuilder; 11 import org.quartz.TriggerBuilder;
12 import org.quartz.TriggerKey; 12 import org.quartz.TriggerKey;
  13 +import com.ruoyi.common.constant.Constants;
13 import com.ruoyi.common.constant.ScheduleConstants; 14 import com.ruoyi.common.constant.ScheduleConstants;
14 import com.ruoyi.common.exception.job.TaskException; 15 import com.ruoyi.common.exception.job.TaskException;
15 import com.ruoyi.common.exception.job.TaskException.Code; 16 import com.ruoyi.common.exception.job.TaskException.Code;
  17 +import com.ruoyi.common.utils.StringUtils;
16 import com.ruoyi.quartz.domain.SysJob; 18 import com.ruoyi.quartz.domain.SysJob;
17 19
18 /** 20 /**
@@ -110,4 +112,24 @@ public class ScheduleUtils @@ -110,4 +112,24 @@ public class ScheduleUtils
110 + "' cannot be used in cron schedule tasks", Code.CONFIG_ERROR); 112 + "' cannot be used in cron schedule tasks", Code.CONFIG_ERROR);
111 } 113 }
112 } 114 }
  115 +
  116 + /**
  117 + * 检查包名是否为白名单配置
  118 + *
  119 + * @param invokeTarget 目标字符串
  120 + * @return 结果
  121 + */
  122 + public static boolean whiteList(String invokeTarget)
  123 + {
  124 + String packageName = StringUtils.substringBefore(invokeTarget, ")");
  125 + int count = StringUtils.countMatches(packageName, ".");
  126 + if (count > 1)
  127 + {
  128 + if (!StringUtils.containsAnyIgnoreCase(invokeTarget, Constants.JOB_WHITELIST_STR))
  129 + {
  130 + return false;
  131 + }
  132 + }
  133 + return true;
  134 + }
113 } 135 }