forked from HXSecurity/DongTai-agent-java
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request HXSecurity#585 from java-sec/java-sec-dev-obj2Stri…
…ng-squeeze-dry fix: fix obj2string bug
- Loading branch information
Showing
24 changed files
with
673 additions
and
76 deletions.
There are no files selected for viewing
29 changes: 29 additions & 0 deletions
29
dongtai-common/src/main/java/io/dongtai/iast/common/exception/DongTaiIastException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
dongtai-common/src/main/java/io/dongtai/iast/common/string/ObjectFormatResult.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
|
||
} |
134 changes: 134 additions & 0 deletions
134
dongtai-common/src/main/java/io/dongtai/iast/common/string/ObjectFormatter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<Object> 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<String> 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<String>() { | ||
@Override | ||
public void accept(String s) { | ||
// 是不是应该避免一下不必要的拼接拷贝?某些特殊数据下还是可能会发生占用较长时间 | ||
header.append(s); | ||
} | ||
}); | ||
|
||
break; | ||
} | ||
} | ||
|
||
r.objectFormatString = StringUtils.normalize(header.toString(), charLimit); | ||
return r; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
146 changes: 146 additions & 0 deletions
146
dongtai-common/src/test/java/io/dongtai/iast/common/string/ObjectFormatterTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
|
||
} | ||
|
||
} |
Oops, something went wrong.