示例:合成方法创建
本示例展示了合成方法的创建。在此合成方法中,我们首先感染一个类字段,然后向用户的的代码中添加一个方法,并最终将此方法的结果传递到接收器。
应用场景
在本示例中,createUser
是在 JAX-RS 框架中实施的 REST API。用可使用诸如 http://host:port/users/createUser
的 URL 调用该 API。框架运行时将该调用指派给 UserRestService.createUser()
,因为路径匹配。此外,框架运行时在调用 createUser()
之前从客户机数据初始化 urlInfo
类变量。最后,运行时会将 createUser()
返回的数据发送回客户机。
合成方法中的建模对象
为使用合成方法来捕获 应用场景,我们具备以下元素:
- 感染
UserRestService
的urlInfo
类变量。 - 针对
UserRestService.createUser()
的调用。 - 客户机的数据返回。
以下是 createUser()
方法的代码:
import java.io.BufferedWriter;
@Path("users")
public class UserRestService {
@Context
UriInfo urlInfo;
@Path("/createUser")
public User createUser(){
MultivaluedMap<String, String> queryParams =
urlInfo.getQueryParameters();
String name = queryParams.getFirst("name");
User user = new User(name);
return user;
}
}
步骤 1:创建空的合成方法
22 public class JAXRSHandler extends F4FHandler{
23 @Override
public void handleApp(F4FApp app, F4FActions actions) {
24 HighLevelSyntheticMethod synthMethod = HighLevelSyntheticMethod.make();
- 合成方法对象在第 24 行初始化。
步骤 2:对类字段的感染进行建模
以下代码段演示了如何能够感染特定类字段。在此示例中,我们希望感染使用 @Context
注释进行了注释的类字段。
27 // create a local variable of the appropriate type
28 Local userRestServiceClazzInstance =
synthMethod.newLocal("com.ibm.appscan.UserRestService");
29
30 // get all the class fields
31 for(IField field: app.getIClass
("com.ibm.appscan.UserRestService").getDeclaredInstanceFields()){
32
33 //get all the annotations associated with the field
34 Collection<Annotation> fieldAnnotations = app.getFieldAnnotations(field);
35
36 //for each annotation of the field check if it is an @Context annotation
37 for (Annotation annotation : fieldAnnotations) {
38
39 if (annotation.getType().getName().toString().equals
("Ljavax/ws/rs/core/Context")) {
40
41 // call the F4F API to assign taint to the field
43 synthMethod.addInstanceVariableWrite(
userRestServiceClazzInstance /*Variable representing
class instance*/,
44 field /*field to taint */,
Taint.taint() /* taint */,
null);
45 }
46 }
47 }
The Framework for Frameworks API addInstanceVariableWrite
采用四个参数:
- 第一个参数为我们想要感染其字段的类引用。在例子中,本地变量
userRestServiceClazzInstance
引用了该参数。 - 第二个参数是我们想要感染的类字段。
- 第三个参数为将分配给类变量字段的新值。在例子中,我们想要感染该变量,这样就会传递
Taint.taint()
。 - 最后一个参数是
FilePositionInfo
,在本例中为null
。
步骤 3:对 createUser()
方法的调用进行建模
在该步骤中,我们在我们自己的合成方法中模拟调用。
50 // call createUser() and store the returned value to a local variable
51 Local returnObj = synthMethod.addCall(
52 "com.ibm.appscan.UserRestService.createUser():com.ibm.appscan.User"
/* signature of the method to be called */,
null, /* FilePositionInfo */
53 userRestServiceClazzInstance /* Object on which the method
will be called */);
addCall
高级别 API 采用三个参数:
- 第一个参数是我们要调用的方法的签名。在本例中,我们想要调用
User.createUser()
,因此将使用其签名。 - 第二个参数为
FilePositionInfo
。该参数将作为null
传递,因为本例中不需要该参数。 - 最后一个参数代表调用
createUser()
方法所需的参数的列表。
由于 createUser()
不采用任何参数,因此传递给此调用的唯一参数是 this
对象,这是一个 User
类型的Local
变量 (userRestServiceClazzInstance
)。方法 createUser()
将返回新近创建的 User
,其存储在名为 returnedObj
的新Local
变量中(如第 51 行所示)。
步骤 4:对客户机的数据返回建模
在最后这一个步骤中,我们将创建一个接收器以对客户机的数据返回进行建模。
58 // create a PrintWriter object
59 Local printWriterObj = synthMethod.newLocal("java.io.PrintWriter");
60
61 // we want to call the print API on the PrintWriter object.
// The print API takes a single argument of Object type returns void
62
63 // we create a param list of size 2. The first item in this list is always the
// 'this' object and rest are actual method arguments.
64 Param[] printWriterObjParam = new Param[2];
65 printWriterObjParam[0] = printWriterObj; // The "this" object
// the value returned by the call to the createUser method
66 printWriterObjParam[1] = returnObj;
67
68 // Now add a call to the print method
69 synthMethod.addCall("java.io.PrintWriter.print(java.lang.Object):void",
null, printWriterObjParam);
70 }
- 在第 59 行,在合成方法中创建
PrintWriter
类的Local
对象。 - 从第 64 行到 66 行,准备在
PrintWriter
类实例 (printWriterObj
) 上调用print
方法所需的参数列表。- 第一个参数为对象引用 (
printWriterObj
) 本身。 - 第二个参数为
print
方法的参数。我们想要打印返回值,该值存储在局部变量returnObj
中。
- 第一个参数为对象引用 (
- 在第 69 行,我们添加了对
PrintWriter.print
方法的调用,以对客户机的数据返回进行建模。