diff --git a/dongtai-common/src/main/java/io/dongtai/iast/common/exception/DongTaiIastException.java b/dongtai-common/src/main/java/io/dongtai/iast/common/exception/DongTaiIastException.java new file mode 100644 index 000000000..a30bafdfd --- /dev/null +++ b/dongtai-common/src/main/java/io/dongtai/iast/common/exception/DongTaiIastException.java @@ -0,0 +1,29 @@ +package io.dongtai.iast.common.exception; + +/** + * 动态Agent整个项目内异常的基类,以后的异常尽量都继承这个类 + * + * @author CC11001100 + * @since 1.13.2 + */ +public class DongTaiIastException extends Exception { + + public DongTaiIastException() { + } + + public DongTaiIastException(String message) { + super(message); + } + + public DongTaiIastException(String message, Throwable cause) { + super(message, cause); + } + + public DongTaiIastException(Throwable cause) { + super(cause); + } + + public DongTaiIastException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/dongtai-common/src/main/java/io/dongtai/iast/common/string/ObjectFormatResult.java b/dongtai-common/src/main/java/io/dongtai/iast/common/string/ObjectFormatResult.java new file mode 100644 index 000000000..abfff3fa3 --- /dev/null +++ b/dongtai-common/src/main/java/io/dongtai/iast/common/string/ObjectFormatResult.java @@ -0,0 +1,22 @@ +package io.dongtai.iast.common.string; + +/** + * 用于表示对 对象格式化的结果 + * + * @author CC11001100 + * @since 1.13.2 + */ +public class ObjectFormatResult { + + // 对象格式化后的字符串,可能不是原始的完整的字符串是被格式化过的,仅作为展示之类的使用 + public String objectFormatString; + + // 原始的字符串长度,对象格式化可以认为有三个步骤: + // + // object --> original string --> format string + // + // 其中original string通常是调用object的toString()得到的,长度可能比较短,也可能老长老长了 + // format string这一步相当于是对original string进行截断,控制字符串的长度 + public int originalLength; + +} diff --git a/dongtai-common/src/main/java/io/dongtai/iast/common/string/ObjectFormatter.java b/dongtai-common/src/main/java/io/dongtai/iast/common/string/ObjectFormatter.java new file mode 100644 index 000000000..e48a4e235 --- /dev/null +++ b/dongtai-common/src/main/java/io/dongtai/iast/common/string/ObjectFormatter.java @@ -0,0 +1,134 @@ +package io.dongtai.iast.common.string; + +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.function.Consumer; + +/** + * 用于把对象格式化为字符串 + * + * @author CC11001100 + * @since 1.13.2 + */ +public class ObjectFormatter { + + /** + * 把对象格式化为字符串,高频调用要尽可能快 + * + * @param value 要转换为字符串的对象 + * @param charLimit 转换时的字符长度限制,超过此长度将被格式化为一个祖传下来的表示字符串省略的格式 :) + * @return 比如"aaa",如果超长可能会发生省略: "aaaaaaaaaaaaaaaaaa...aaaaaaaaaaaaaa" + * @see ObjectFormatResult + */ + public static ObjectFormatResult formatObject(Object value, int charLimit) { + + ObjectFormatResult r = new ObjectFormatResult(); + + if (null == value) { + return r; + } + + try { + if (value.getClass().isArray() && !value.getClass().getComponentType().isPrimitive()) { + // 判断是否是基本类型的数组,基本类型的数组无法类型转换为Object[],导致java.lang.ClassCastException异常 + Object[] taints = (Object[]) value; + return objArray2StringV2(taints, charLimit); + } else if (value instanceof StringWriter) { + String s = ((StringWriter) value).getBuffer().toString(); + r.originalLength = s.length(); + r.objectFormatString = StringUtils.normalize(s, charLimit); + return r; + } else { + String s = value.toString(); + r.originalLength = s.length(); + r.objectFormatString = StringUtils.normalize(s, charLimit); + return r; + } + } catch (Throwable e) { + // org.jruby.RubyBasicObject.hashCode() may cause NullPointerException when RubyBasicObject.metaClass is null + String typeName = value.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(value)); + r.originalLength = typeName.length(); + r.objectFormatString = StringUtils.normalize(typeName, charLimit); + return r; + } + } + + /** + * 对象数组转为字符串,会往下穿透到第二层 + * + * @param objArray 要转换为字符串的对象数组 + * @param charLimit 同 {{@link #formatObject(Object, int)}} + * @return + * @see #formatObject + */ + private static ObjectFormatResult objArray2StringV2(Object[] objArray, int charLimit) { + + ObjectFormatResult r = new ObjectFormatResult(); + + // 第一步,先把对象都收集一下,把要处理的对象打平 + List objList = new ArrayList<>(); + for (Object taint : objArray) { + if (taint != null) { + if (taint.getClass().isArray() && !taint.getClass().getComponentType().isPrimitive()) { + Object[] subTaints = (Object[]) taint; + for (Object subTaint : subTaints) { + if (subTaint == null) { + continue; + } + objList.add(subTaint); + } + } else { + objList.add(taint); + } + } + } + + // 从前往后开始读取 + StringBuilder header = new StringBuilder(); + int headIndex = 0; + while (headIndex < objList.size()) { + + String s = objList.get(headIndex).toString(); + headIndex++; + // 如果这个地方的字符串比较长怎么办?是不是应该截断一下?还是有优化空间的 + header.append(s); + r.originalLength += s.length(); + + // 如果进来的话,说明长度是超了,这个时候应该做的是从尾部读取一部分进来,然后等会儿做截断用 + if (header.length() > charLimit) { + + // 然后就从尾部开始向前读取 + int readCount = 0; + LinkedList tailStringList = new LinkedList<>(); + int needReadChar = charLimit / 2 + charLimit % 2; + for (int tailIndex = objList.size() - 1; tailIndex >= headIndex; tailIndex--) { + s = objList.get(tailIndex).toString(); + // 仅读取需要的字符数,超过的话则不再读取 + if (readCount < needReadChar) { + readCount += s.length(); + tailStringList.addFirst(s); + } + // 但是长度是要整个计算的 + r.originalLength += s.length(); + } + + // 然后开始拼接处理 + tailStringList.forEach(new Consumer() { + @Override + public void accept(String s) { + // 是不是应该避免一下不必要的拼接拷贝?某些特殊数据下还是可能会发生占用较长时间 + header.append(s); + } + }); + + break; + } + } + + r.objectFormatString = StringUtils.normalize(header.toString(), charLimit); + return r; + } + +} diff --git a/dongtai-core/src/main/java/io/dongtai/iast/core/utils/StringUtils.java b/dongtai-common/src/main/java/io/dongtai/iast/common/string/StringUtils.java similarity index 89% rename from dongtai-core/src/main/java/io/dongtai/iast/core/utils/StringUtils.java rename to dongtai-common/src/main/java/io/dongtai/iast/common/string/StringUtils.java index ef638a302..90e2fb8d8 100644 --- a/dongtai-core/src/main/java/io/dongtai/iast/core/utils/StringUtils.java +++ b/dongtai-common/src/main/java/io/dongtai/iast/common/string/StringUtils.java @@ -1,4 +1,4 @@ -package io.dongtai.iast.core.utils; +package io.dongtai.iast.common.string; /** * @author dongzhiyong@huoxian.cn @@ -32,6 +32,17 @@ public static boolean isEmpty(String str) { return str == null || str.length() == 0; } + /** + * 判断字符串是否为空白字符串,比如 " " 会被认为是空白字符串 + * + * @param s + * @return + * @since 1.13.2 + */ + public static boolean isBlank(String s) { + return s == null || s.trim().isEmpty(); + } + public static String normalize(String str, int maxLength) { int max = Math.max(maxLength, 5); if (str != null && str.length() != 0 && str.length() > max) { diff --git a/dongtai-common/src/test/java/io/dongtai/iast/common/string/ObjectFormatterTest.java b/dongtai-common/src/test/java/io/dongtai/iast/common/string/ObjectFormatterTest.java new file mode 100644 index 000000000..ab7aa0342 --- /dev/null +++ b/dongtai-common/src/test/java/io/dongtai/iast/common/string/ObjectFormatterTest.java @@ -0,0 +1,146 @@ +package io.dongtai.iast.common.string; + +import org.junit.Assert; +import org.junit.Test; + +/** + * @author CC11001100 + */ +public class ObjectFormatterTest { + + @Test + public void formatObject() { + + // 普通的字符串 + ObjectFormatResult r = ObjectFormatter.formatObject("CC11001100", 1024); + Assert.assertNotNull(r); + Assert.assertEquals(10, r.originalLength); + Assert.assertEquals("CC11001100", r.objectFormatString); + + // 超过长度限制的字符串 + r = ObjectFormatter.formatObject("Dongtai IAST is an open-source Interactive Application Security Testing (IAST) tool that enables real-time detection of common vulnerabilities in Java applications and third-party components through passive instrumentation. It is particularly suitable for use in the testing phase of the development pipeline.", 100); + Assert.assertNotNull(r); + Assert.assertEquals(309, r.originalLength); + Assert.assertEquals("Dongtai IAST is an open-source Interactive Applic...n the testing phase of the development pipeline.", r.objectFormatString); + + // int + r = ObjectFormatter.formatObject(10086, 100); + Assert.assertNotNull(r); + Assert.assertEquals(5, r.originalLength); + Assert.assertEquals("10086", r.objectFormatString); + + // null + r = ObjectFormatter.formatObject(null, 100); + Assert.assertNotNull(r); + Assert.assertEquals(0, r.originalLength); + Assert.assertNull(r.objectFormatString); + + // 数组,加起来长度超了 + r = ObjectFormatter.formatObject(new String[]{ + "foo", + "bar", + "blablablablablablablablablablablabla" + }, 10); + Assert.assertNotNull(r); + Assert.assertEquals(42, r.originalLength); + Assert.assertEquals("foob...bla", r.objectFormatString); + + // 数组,第一个长度超了 + r = ObjectFormatter.formatObject(new String[]{ + "foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo", + "bar", + "blabla" + }, 10); + Assert.assertNotNull(r); + Assert.assertEquals(117, r.originalLength); + Assert.assertEquals("foof...bla", r.objectFormatString); + + // 数组,第一个长度没超,加起来超了 + r = ObjectFormatter.formatObject(new String[]{ + "foofoo", + "barbarbarbarbar", + "blabla" + }, 10); + Assert.assertNotNull(r); + Assert.assertEquals(27, r.originalLength); + Assert.assertEquals("foof...bla", r.objectFormatString); + + // 数组,第一个长度没超,加起来也没超 + r = ObjectFormatter.formatObject(new String[]{ + "foo", + "bar", + "bla" + }, 10); + Assert.assertNotNull(r); + Assert.assertEquals(9, r.originalLength); + Assert.assertEquals("foobarbla", r.objectFormatString); + + // 对象数组 + r = ObjectFormatter.formatObject(new Object[]{ + new Object(), + new Object(), + new Object(), + new Object() + }, 40); + Assert.assertNotNull(r); + Assert.assertTrue(r.originalLength > 0); + Assert.assertNotNull(r.objectFormatString); + + // null数组 + r = ObjectFormatter.formatObject(new Object[]{ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + }, 40); + Assert.assertNotNull(r); + Assert.assertEquals(0, r.originalLength); + Assert.assertEquals("", r.objectFormatString); + + // Integer数组 + r = ObjectFormatter.formatObject(new Integer[]{ + 1, + 2, + 3, + 4, + 5 + }, 40); + Assert.assertNotNull(r); + Assert.assertEquals(5, r.originalLength); + Assert.assertEquals("12345", r.objectFormatString); + + // int数组 + r = ObjectFormatter.formatObject(new int[]{ + 1, + 2, + 3, + 4, + 5 + }, 40); + Assert.assertNotNull(r); + Assert.assertEquals(11, r.originalLength); + Assert.assertNotNull(r.objectFormatString); + + // 数组中偶尔有null的 + r = ObjectFormatter.formatObject(new Integer[]{ + 1, + null, + 3, + null, + 5 + }, 40); + Assert.assertNotNull(r); + Assert.assertEquals(3, r.originalLength); + Assert.assertEquals("135", r.objectFormatString); + + } + +} \ No newline at end of file diff --git a/dongtai-common/src/test/java/io/dongtai/iast/common/string/StringUtilsBenchmarkTest.java b/dongtai-common/src/test/java/io/dongtai/iast/common/string/StringUtilsBenchmarkTest.java new file mode 100644 index 000000000..748059ca6 --- /dev/null +++ b/dongtai-common/src/test/java/io/dongtai/iast/common/string/StringUtilsBenchmarkTest.java @@ -0,0 +1,40 @@ +package io.dongtai.iast.common.string; + +//import org.openjdk.jmh.annotations.*; +//import org.openjdk.jmh.results.format.ResultFormatType; +//import org.openjdk.jmh.runner.Runner; +//import org.openjdk.jmh.runner.options.Options; +//import org.openjdk.jmh.runner.options.OptionsBuilder; +// +//import java.util.concurrent.TimeUnit; +// +///** +// * @author CC11001100 +// */ +//@BenchmarkMode(Mode.All) +//@OutputTimeUnit(TimeUnit.MILLISECONDS) +//@State(Scope.Thread) +//@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) +//@Measurement(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) +//@Threads(1) +//public class StringUtilsBenchmarkTest { +// +// @Benchmark +// public void formatClassNameToSlashDelimiterTest() { +// String s = StringUtils.formatClassNameToSlashDelimiter("com.foo.bar"); +// } +// +// @Benchmark +// public void replace() { +// String s = "com.foo.bar".replace(".", "/"); +// } +// +// public static void main(String[] args) throws Exception { +// Options opts = new OptionsBuilder() +// .include(StringUtilsBenchmarkTest.class.getSimpleName()) +// .resultFormat(ResultFormatType.JSON) +// .build(); +// new Runner(opts).run(); +// } +// +//} diff --git a/dongtai-core/src/test/java/io/dongtai/iast/core/utils/StringUtilsTest.java b/dongtai-common/src/test/java/io/dongtai/iast/common/string/StringUtilsTest.java similarity index 93% rename from dongtai-core/src/test/java/io/dongtai/iast/core/utils/StringUtilsTest.java rename to dongtai-common/src/test/java/io/dongtai/iast/common/string/StringUtilsTest.java index 8cf1ad5ca..6c6aa70ab 100644 --- a/dongtai-core/src/test/java/io/dongtai/iast/core/utils/StringUtilsTest.java +++ b/dongtai-common/src/test/java/io/dongtai/iast/common/string/StringUtilsTest.java @@ -1,5 +1,6 @@ -package io.dongtai.iast.core.utils; +package io.dongtai.iast.common.string; +import io.dongtai.iast.common.string.StringUtils; import org.junit.Assert; import org.junit.Test; diff --git a/dongtai-core/src/main/java/io/dongtai/iast/core/bytecode/enhance/IastClassDiagram.java b/dongtai-core/src/main/java/io/dongtai/iast/core/bytecode/enhance/IastClassDiagram.java index 554df9883..747fd04b8 100644 --- a/dongtai-core/src/main/java/io/dongtai/iast/core/bytecode/enhance/IastClassDiagram.java +++ b/dongtai-core/src/main/java/io/dongtai/iast/core/bytecode/enhance/IastClassDiagram.java @@ -9,8 +9,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; -import static io.dongtai.iast.core.utils.StringUtils.formatClassNameToDotDelimiter; -import static io.dongtai.iast.core.utils.StringUtils.formatClassNameToSlashDelimiter; +import static io.dongtai.iast.common.string.StringUtils.formatClassNameToDotDelimiter; +import static io.dongtai.iast.common.string.StringUtils.formatClassNameToSlashDelimiter; /** * 用于存储类名称到祖先类的映射关系:类的名字 --> Set<祖先类的名称集合> diff --git a/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/SpyDispatcherImpl.java b/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/SpyDispatcherImpl.java index 40877056a..ff6df5793 100644 --- a/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/SpyDispatcherImpl.java +++ b/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/SpyDispatcherImpl.java @@ -17,7 +17,7 @@ import io.dongtai.iast.core.handler.hookpoint.service.trace.DubboService; import io.dongtai.iast.core.handler.hookpoint.service.trace.FeignService; import io.dongtai.iast.core.handler.hookpoint.service.trace.HttpService; -import io.dongtai.iast.core.utils.StringUtils; +import io.dongtai.iast.common.string.StringUtils; import io.dongtai.iast.core.utils.matcher.ConfigMatcher; import io.dongtai.log.DongTaiLog; import io.dongtai.log.ErrorCode; diff --git a/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/controller/impl/DubboImpl.java b/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/controller/impl/DubboImpl.java index 07beb687e..fe2cfe570 100644 --- a/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/controller/impl/DubboImpl.java +++ b/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/controller/impl/DubboImpl.java @@ -3,6 +3,7 @@ import com.alibaba.fastjson2.JSONArray; import io.dongtai.iast.common.config.ConfigBuilder; import io.dongtai.iast.common.config.ConfigKey; +import io.dongtai.iast.common.string.ObjectFormatResult; import io.dongtai.iast.core.EngineManager; import io.dongtai.iast.core.handler.bypass.BlackUrlBypass; import io.dongtai.iast.core.handler.context.ContextManager; @@ -96,13 +97,13 @@ public static void collectDubboRequestSource(Object handler, Object invocation, if (requestMeta == null) { return; } - if (null != headers.get(BlackUrlBypass.getHeaderKey()) && headers.get(BlackUrlBypass.getHeaderKey()).equals("true")){ + if (null != headers.get(BlackUrlBypass.getHeaderKey()) && headers.get(BlackUrlBypass.getHeaderKey()).equals("true")) { BlackUrlBypass.setIsBlackUrl(true); return; } - String url =requestMeta.get("requestURL").toString() + "/" + methodName; - String uri =requestMeta.get("requestURI").toString() + "/" + methodName; + String url = requestMeta.get("requestURL").toString() + "/" + methodName; + String uri = requestMeta.get("requestURI").toString() + "/" + methodName; StringBuilder argSign = new StringBuilder("("); if (argumentTypes != null && argumentTypes.length > 0) { @@ -172,7 +173,9 @@ public static void collectDubboRequestSource(Object handler, Object invocation, requestMeta.put("headers", sHeaders); JSONArray arr = new JSONArray(); for (Object arg : arguments) { - arr.add(event.obj2String(arg)); + // 2023-9-5 11:31:53 直接拿完整的string可能会OOM(排队上报时可能会挤压占用较多的内存),这里只传递format之后的 + ObjectFormatResult r = MethodEvent.formatObject(arg); + arr.add(r.objectFormatString); } requestMeta.put("body", arr.toString()); EngineManager.REQUEST_CONTEXT.set(requestMeta); diff --git a/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/controller/impl/HttpImpl.java b/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/controller/impl/HttpImpl.java index f12285a36..1ac3b6feb 100644 --- a/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/controller/impl/HttpImpl.java +++ b/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/controller/impl/HttpImpl.java @@ -2,6 +2,7 @@ import io.dongtai.iast.common.config.*; import io.dongtai.iast.common.constants.AgentConstant; +import io.dongtai.iast.common.string.StringUtils; import io.dongtai.iast.core.EngineManager; import io.dongtai.iast.core.handler.bypass.BlackUrlBypass; import io.dongtai.iast.core.handler.hookpoint.IastClassLoader; diff --git a/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/graphy/GraphBuilder.java b/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/graphy/GraphBuilder.java index 391d74b15..28df6e27b 100644 --- a/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/graphy/GraphBuilder.java +++ b/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/graphy/GraphBuilder.java @@ -11,7 +11,7 @@ import io.dongtai.iast.core.handler.hookpoint.models.policy.TaintPosition; import io.dongtai.iast.core.handler.hookpoint.vulscan.normal.AbstractNormalVulScan; import io.dongtai.iast.core.service.ThreadPools; -import io.dongtai.iast.core.utils.StringUtils; +import io.dongtai.iast.common.string.StringUtils; import io.dongtai.log.DongTaiLog; import io.dongtai.log.ErrorCode; diff --git a/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/models/MethodEvent.java b/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/models/MethodEvent.java index 4e59fcbce..c63d67d20 100644 --- a/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/models/MethodEvent.java +++ b/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/models/MethodEvent.java @@ -1,12 +1,17 @@ package io.dongtai.iast.core.handler.hookpoint.models; import com.alibaba.fastjson2.JSONObject; +import io.dongtai.iast.common.string.ObjectFormatResult; +import io.dongtai.iast.common.string.ObjectFormatter; import io.dongtai.iast.core.handler.hookpoint.models.policy.TaintPosition; import io.dongtai.iast.core.handler.hookpoint.models.taint.range.TaintRanges; import io.dongtai.iast.core.utils.PropertyUtils; import java.io.StringWriter; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; /** * 方法事件 @@ -239,10 +244,10 @@ public void setReturnValue(Object ret, boolean hasTaint) { this.returnValue = formatValue(ret, hasTaint); } - private String formatValue(Object val, boolean hasTaint) { - String str = obj2String(val); - return "[" + str + "]" - + (hasTaint ? "*" : "") + str.length(); + private static String formatValue(Object val, boolean hasTaint) { + ObjectFormatResult r = formatObject(val); + return "[" + r.objectFormatString + "]" + + (hasTaint ? "*" : "") + r.originalLength; } public Set getSourceHashes() { @@ -281,8 +286,14 @@ public void setCallStack(StackTraceElement callStack) { this.callStack = callStack; } - public String obj2String(Object value) { - int taintValueLength = PropertyUtils.getInstance().getTaintValueLength(); + /** + * @param value + * @return + * @deprecated 开始是发现了有性能问题,然后进行了一版修改,这版修改影响到了与服务端的数据结构交互逻辑,因此废弃,再重写一版 + */ + @Deprecated() + public static String obj2String(Object value) { + int taintValueLength = PropertyUtils.getTaintToStringCharLimit(); StringBuilder sb = new StringBuilder(); if (null == value) { return ""; @@ -318,7 +329,8 @@ public String obj2String(Object value) { return sb.toString(); } - private void appendWithMaxLength(StringBuilder sb, String content, int maxLength) { + @Deprecated + private static void appendWithMaxLength(StringBuilder sb, String content, int maxLength) { if (sb.length() + content.length() > maxLength) { int remainingSpace = maxLength - sb.length(); if (remainingSpace > 0) { @@ -335,9 +347,21 @@ public List getStacks() { public void setStacks(StackTraceElement[] stackTraceElements) { List stacks = new ArrayList<>(); - for(StackTraceElement stackTraceElement:stackTraceElements){ + for (StackTraceElement stackTraceElement : stackTraceElements) { stacks.add(stackTraceElement.toString()); } this.stacks = stacks; } + + /** + * 把对象格式化为字符串,高频调用要尽可能快 + * + * @param value 要转换为字符串的对象 + * @return + */ + public static ObjectFormatResult formatObject(Object value) { + // TODO 2023-9-5 11:49:14 晚点再看看要不要把这个方法也下沉到commons中,目前是因为有依赖无法下沉... + return ObjectFormatter.formatObject(value, PropertyUtils.getTaintToStringCharLimit()); + } + } diff --git a/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/models/policy/Inheritable.java b/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/models/policy/Inheritable.java index 90c7c0afa..a0d1dafd4 100644 --- a/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/models/policy/Inheritable.java +++ b/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/models/policy/Inheritable.java @@ -1,6 +1,6 @@ package io.dongtai.iast.core.handler.hookpoint.models.policy; -import io.dongtai.iast.core.utils.StringUtils; +import io.dongtai.iast.common.string.StringUtils; public enum Inheritable { ALL("all"), diff --git a/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/models/policy/PolicyBuilder.java b/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/models/policy/PolicyBuilder.java index dec181946..f23024888 100644 --- a/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/models/policy/PolicyBuilder.java +++ b/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/models/policy/PolicyBuilder.java @@ -6,7 +6,7 @@ import io.dongtai.iast.core.handler.hookpoint.models.taint.tag.TaintTag; import io.dongtai.iast.core.handler.hookpoint.vulscan.VulnType; import io.dongtai.iast.core.utils.HttpClientUtils; -import io.dongtai.iast.core.utils.StringUtils; +import io.dongtai.iast.common.string.StringUtils; import io.dongtai.log.DongTaiLog; import io.dongtai.log.ErrorCode; import org.apache.commons.io.FileUtils; diff --git a/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/models/policy/PolicyManager.java b/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/models/policy/PolicyManager.java index a924a5037..62d2da4b3 100644 --- a/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/models/policy/PolicyManager.java +++ b/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/models/policy/PolicyManager.java @@ -3,7 +3,7 @@ import io.dongtai.iast.core.bytecode.enhance.plugin.framework.dubbo.DispatchDubbo; import io.dongtai.iast.core.bytecode.enhance.plugin.framework.feign.DispatchFeign; import io.dongtai.iast.core.bytecode.enhance.plugin.framework.j2ee.dispatch.DispatchJ2ee; -import io.dongtai.iast.core.utils.StringUtils; +import io.dongtai.iast.common.string.StringUtils; import io.dongtai.log.DongTaiLog; import io.dongtai.log.ErrorCode; import org.json.JSONArray; diff --git a/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/models/taint/range/TaintCommand.java b/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/models/taint/range/TaintCommand.java index f131b2623..529b71387 100644 --- a/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/models/taint/range/TaintCommand.java +++ b/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/models/taint/range/TaintCommand.java @@ -1,6 +1,7 @@ package io.dongtai.iast.core.handler.hookpoint.models.taint.range; -import io.dongtai.iast.core.utils.StringUtils; + +import io.dongtai.iast.common.string.StringUtils; import java.util.HashMap; import java.util.Map; diff --git a/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/models/taint/range/TaintRanges.java b/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/models/taint/range/TaintRanges.java index 1f1fcc1e8..9cd7b1576 100644 --- a/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/models/taint/range/TaintRanges.java +++ b/dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/models/taint/range/TaintRanges.java @@ -2,7 +2,7 @@ import com.alibaba.fastjson2.JSONArray; import io.dongtai.iast.core.handler.hookpoint.models.taint.tag.TaintTag; -import io.dongtai.iast.core.utils.StringUtils; +import io.dongtai.iast.common.string.StringUtils; import java.util.*; diff --git a/dongtai-core/src/main/java/io/dongtai/iast/core/utils/PropertyUtils.java b/dongtai-core/src/main/java/io/dongtai/iast/core/utils/PropertyUtils.java index 116d2b966..e5bb31e73 100644 --- a/dongtai-core/src/main/java/io/dongtai/iast/core/utils/PropertyUtils.java +++ b/dongtai-core/src/main/java/io/dongtai/iast/core/utils/PropertyUtils.java @@ -3,6 +3,8 @@ import io.dongtai.iast.common.config.ConfigBuilder; import io.dongtai.iast.common.config.ConfigKey; import io.dongtai.iast.common.constants.PropertyConstant; +import io.dongtai.iast.common.exception.DongTaiIastException; +import io.dongtai.iast.common.string.StringUtils; import io.dongtai.log.DongTaiLog; import io.dongtai.log.ErrorCode; @@ -33,10 +35,12 @@ public class PropertyUtils { private final String propertiesFilePath; - private int taintValueLength = -1; + public static final Integer DEFAULT_TAINT_TO_STRING_CHAR_LIMIT = 1024; + // 污点转换为字符串的时候字符数长度限制 + private Integer taintToStringCharLimit = DEFAULT_TAINT_TO_STRING_CHAR_LIMIT; - public static PropertyUtils getInstance(String propertiesFilePath) { + public static PropertyUtils getInstance(String propertiesFilePath) throws DongTaiPropertyConfigException, DongTaiEnvConfigException { if (null == instance) { instance = new PropertyUtils(propertiesFilePath); } @@ -51,7 +55,7 @@ public static void clear() { instance = null; } - private PropertyUtils(String propertiesFilePath) { + private PropertyUtils(String propertiesFilePath) throws DongTaiPropertyConfigException, DongTaiEnvConfigException { this.propertiesFilePath = propertiesFilePath; init(); } @@ -59,7 +63,7 @@ private PropertyUtils(String propertiesFilePath) { /** * 根据配置文件初始化单例配置类 */ - private void init() { + private void init() throws DongTaiPropertyConfigException, DongTaiEnvConfigException { try { File propertiesFile = new File(propertiesFilePath); if (propertiesFile.exists()) { @@ -71,6 +75,10 @@ private void init() { } catch (Throwable e) { DongTaiLog.error(ErrorCode.get("ENGINE_PROPERTIES_INITIALIZE_FAILED"), e); } + + // 初始化一些参数 + this.initTaintToStringCharLimit(); + } public static String getTmpDir() { @@ -224,12 +232,96 @@ public static Boolean validatedSink() { return ConfigBuilder.getInstance().get(ConfigKey.VALIDATED_SINK); } - public int getTaintValueLength() { - if (-1 == taintValueLength) { - taintValueLength = Integer - .parseInt(System.getProperty(PropertyConstant.PROPERTY_TAINT_LENGTH, - cfg.getProperty(PropertyConstant.PROPERTY_TAINT_LENGTH, "1024"))); + /** + * 污点转为字符串时的长度限制 + * + * @return + */ + public static Integer getTaintToStringCharLimit() { + if (instance == null) { + return DEFAULT_TAINT_TO_STRING_CHAR_LIMIT; } - return taintValueLength; + return instance.taintToStringCharLimit; } + + /** + * 初始化taintToStringCharLimit参数的值 + * + * @throws DongTaiPropertyConfigException + * @throws DongTaiEnvConfigException + */ + public void initTaintToStringCharLimit() throws DongTaiPropertyConfigException, DongTaiEnvConfigException { + + // 1. 先从配置文件中读取 + String s = cfg.getProperty(PropertyConstant.PROPERTY_TAINT_LENGTH); + if (!StringUtils.isBlank(s)) { + this.taintToStringCharLimit = Integer.parseInt(s.trim()); + if (this.taintToStringCharLimit <= 0) { + throw new DongTaiPropertyConfigException("The value of this parameter " + PropertyConstant.PROPERTY_TAINT_LENGTH + + " value " + s + " in your configuration file " + this.propertiesFilePath + " is illegal, such as passing an number greater than 1"); + } + } + + // 2. 然后从环境变量中读取 + s = System.getProperty(PropertyConstant.PROPERTY_TAINT_LENGTH); + if (!StringUtils.isBlank(s)) { + this.taintToStringCharLimit = Integer.parseInt(s.trim()); + if (this.taintToStringCharLimit <= 0) { + throw new DongTaiEnvConfigException("The value of this parameter " + PropertyConstant.PROPERTY_TAINT_LENGTH + + " value " + s + " in your environment variables is illegal, such as passing an number greater than 1"); + } + } + + } + + /** + * Property文件配置错误 + */ + public static class DongTaiPropertyConfigException extends DongTaiIastException { + + public DongTaiPropertyConfigException() { + } + + public DongTaiPropertyConfigException(String message) { + super(message); + } + + public DongTaiPropertyConfigException(String message, Throwable cause) { + super(message, cause); + } + + public DongTaiPropertyConfigException(Throwable cause) { + super(cause); + } + + public DongTaiPropertyConfigException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + } + + /** + * 环境变量传参配置错误 + */ + public static class DongTaiEnvConfigException extends DongTaiIastException { + + public DongTaiEnvConfigException() { + } + + public DongTaiEnvConfigException(String message) { + super(message); + } + + public DongTaiEnvConfigException(String message, Throwable cause) { + super(message, cause); + } + + public DongTaiEnvConfigException(Throwable cause) { + super(cause); + } + + public DongTaiEnvConfigException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + } + } diff --git a/dongtai-core/src/test/java/io/dongtai/iast/core/handler/hookpoint/models/MethodEventBenchmarkTest.java b/dongtai-core/src/test/java/io/dongtai/iast/core/handler/hookpoint/models/MethodEventBenchmarkTest.java new file mode 100644 index 000000000..7e028e346 --- /dev/null +++ b/dongtai-core/src/test/java/io/dongtai/iast/core/handler/hookpoint/models/MethodEventBenchmarkTest.java @@ -0,0 +1,96 @@ +package io.dongtai.iast.core.handler.hookpoint.models; + +import org.openjdk.jmh.annotations.*; + +import java.util.Random; +import java.util.concurrent.TimeUnit; + +/** + * @author CC11001100 + */ +@BenchmarkMode(Mode.All) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@State(Scope.Thread) +@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) +@Threads(1) +public class MethodEventBenchmarkTest { + + /** + * 生成给定范围内的随机长度的字符串 + * + * @param begin + * @param end + * @return + */ + private String random(int begin, int end) { + Random r = new Random(); + int length = (r.nextInt() % (end - begin)) + begin; + char[] chars = new char[length]; + for (int i = 0; i < chars.length; i++) { + char c = (char) ((r.nextInt() % 26) + 'A'); + chars[i] = c; + } + return new String(chars); + } + + private String[] randomArray(int ArrayLengthBegin, int ArrayLengthEnd, int begin, int end) { + Random r = new Random(); +// int length = (r.nextInt() % (ArrayLengthEnd - ArrayLengthBegin)) + ArrayLengthBegin; + int length = 100; + String[] ss = new String[length]; + for (int i = 0; i < ss.length; i++) { + ss[i] = random(begin, end); + } + return ss; + } + +// @Benchmark +// public void formatObjTest() { +// MethodEvent.formatObj(random(1000, 2000), 1024); +// } +// +// @Benchmark +// public void obj2StringTest() { +// MethodEvent.obj2String(random(1000, 2000)); +// } +// +// @Benchmark +// public void formatObjLongStringTest() { +// MethodEvent.formatObj(random(100000, 200000), 1024); +// } +// +// @Benchmark +// public void obj2StringLongStringTest() { +// MethodEvent.obj2String(random(100000, 200000)); +// } + +// @Benchmark +// public void formatObjArrayTest() { +// MethodEvent.formatObject(randomArray(10, 100, 1000, 2000), 1024); +// } +// +// @Benchmark +// public void obj2StringArrayTest() { +// MethodEvent.obj2String(randomArray(10, 100, 1000, 2000)); +// } +// +// @Benchmark +// public void formatObjArrayLongStringTest() { +// MethodEvent.formatObject(randomArray(10, 100, 100000, 200000), 1024); +// } +// +// @Benchmark +// public void obj2StringArrayLongStringTest() { +// MethodEvent.obj2String(randomArray(10, 100, 100000, 200000)); +// } +// +// public static void main(String[] args) throws Exception { +// Options opts = new OptionsBuilder() +// .include(MethodEventBenchmarkTest.class.getSimpleName()) +// .resultFormat(ResultFormatType.JSON) +// .build(); +// new Runner(opts).run(); +// } + +} diff --git a/dongtai-core/src/test/java/io/dongtai/iast/core/handler/hookpoint/models/MethodEventTest.java b/dongtai-core/src/test/java/io/dongtai/iast/core/handler/hookpoint/models/MethodEventTest.java new file mode 100644 index 000000000..28cafda7a --- /dev/null +++ b/dongtai-core/src/test/java/io/dongtai/iast/core/handler/hookpoint/models/MethodEventTest.java @@ -0,0 +1,38 @@ +package io.dongtai.iast.core.handler.hookpoint.models; + +import org.junit.Test; + +import java.util.Random; + +/** + * @author CC11001100 + */ +public class MethodEventTest { + +// /** +// * 生成给定范围内的随机长度的字符串 +// * +// * @param begin +// * @param end +// * @return +// */ +// private String random(int begin, int end) { +// Random r = new Random(); +// int length = (r.nextInt() % (end - begin)) + begin; +// char[] chars = new char[length]; +// for (int i = 0; i < chars.length; i++) { +// char c = (char) ((r.nextInt() % 26) + 'A'); +// chars[i] = c; +// } +// return new String(chars); +// } +// +// @Test +// public void formatObj() { +// String s = random(10000, 100000); +// System.out.println(s); +// s = MethodEvent.formatObject(s, 10); +// System.out.println(s); +// } + +} \ No newline at end of file diff --git a/dongtai-core/src/test/java/io/dongtai/iast/core/handler/hookpoint/models/policy/PolicyBuilderTest.java b/dongtai-core/src/test/java/io/dongtai/iast/core/handler/hookpoint/models/policy/PolicyBuilderTest.java index 0cf658c0f..9b8ba47cd 100644 --- a/dongtai-core/src/test/java/io/dongtai/iast/core/handler/hookpoint/models/policy/PolicyBuilderTest.java +++ b/dongtai-core/src/test/java/io/dongtai/iast/core/handler/hookpoint/models/policy/PolicyBuilderTest.java @@ -21,7 +21,7 @@ public class PolicyBuilderTest { @Before - public void setUp() { + public void setUp() throws PropertyUtils.DongTaiPropertyConfigException, PropertyUtils.DongTaiEnvConfigException { PropertyUtils.getInstance(PROPERTY_FILE); DongTaiLog.ENABLED = true; clear(); diff --git a/dongtai-core/src/test/java/io/dongtai/iast/core/utils/StringUtilsBenchmarkTest.java b/dongtai-core/src/test/java/io/dongtai/iast/core/utils/StringUtilsBenchmarkTest.java deleted file mode 100644 index d5a560e9e..000000000 --- a/dongtai-core/src/test/java/io/dongtai/iast/core/utils/StringUtilsBenchmarkTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.dongtai.iast.core.utils; - -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.results.format.ResultFormatType; -import org.openjdk.jmh.runner.Runner; -import org.openjdk.jmh.runner.options.Options; -import org.openjdk.jmh.runner.options.OptionsBuilder; - -import java.util.concurrent.TimeUnit; - -/** - * @author CC11001100 - */ -@BenchmarkMode(Mode.All) -@OutputTimeUnit(TimeUnit.MILLISECONDS) -@State(Scope.Thread) -@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) -@Measurement(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) -@Threads(1) -public class StringUtilsBenchmarkTest { - - @Benchmark - public void formatClassNameToSlashDelimiterTest() { - String s = StringUtils.formatClassNameToSlashDelimiter("com.foo.bar"); - } - - @Benchmark - public void replace() { - String s = "com.foo.bar".replace(".", "/"); - } - - public static void main(String[] args) throws Exception { - Options opts = new OptionsBuilder() - .include(StringUtilsBenchmarkTest.class.getSimpleName()) - .resultFormat(ResultFormatType.JSON) - .build(); - new Runner(opts).run(); - } - -} diff --git a/pom.xml b/pom.xml index d3d3a15e1..17355f0a2 100644 --- a/pom.xml +++ b/pom.xml @@ -178,7 +178,6 @@ ${jmh.version} test -