範例:建立合成方法

這個範例示範如何建立合成方法。在這個合成方法中,我們先使類別欄位受影響,然後在使用者的程式碼中新增一個方法呼叫,最後將此方法呼叫的結果傳遞至接收槽。

應用程式實務

在這個範例中,createUser 是在 JAX-RS 架構中實作的 REST API。使用者可使用 URL 呼叫這個 API,例如 http://host:port/users/createUser。由於路徑相符,架構執行時期會委派此呼叫給 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    }

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,它儲存在新的 Local 變數中,此變數稱為 returnedObj(如第 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 Local 變數中的回覆值。
  • 在第 69 行,我們新增 PrintWriter.print 方法的呼叫,來建立資料傳回到用戶端的模型。