示例:合成方法创建

本示例展示了合成方法的创建。在此合成方法中,我们首先感染一个类字段,然后向用户的的代码中添加一个方法,并最终将此方法的结果传递到接收器。

应用场景

在本示例中,createUser 是在 JAX-RS 框架中实施的 REST API。用可使用诸如 http://host:port/users/createUser 的 URL 调用该 API。框架运行时将该调用指派给 UserRestService.createUser(),因为路径匹配。此外,框架运行时在调用 createUser() 之前从客户机数据初始化 urlInfo 类变量。最后,运行时会将 createUser() 返回的数据发送回客户机。

合成方法中的建模对象

为使用合成方法来捕获 应用场景,我们具备以下元素:

  • 感染 UserRestServiceurlInfo 类变量。
  • 针对 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 采用四个参数:

  1. 第一个参数为我们想要感染其字段的类引用。在例子中,本地变量 userRestServiceClazzInstance 引用了该参数。
  2. 第二个参数是我们想要感染的类字段。
  3. 第三个参数为将分配给类变量字段的新值。在例子中,我们想要感染该变量,这样就会传递 Taint.taint()
  4. 最后一个参数是 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 方法的调用,以对客户机的数据返回进行建模。