範例:建立合成方法
這個範例示範如何建立合成方法。在這個合成方法中,我們先使類別欄位受影響,然後在使用者的程式碼中新增一個方法呼叫,最後將此方法呼叫的結果傳遞至接收槽。
- 應用程式實務
- 在合成方法中建立何種模型
- 步驟 1:建立空的合成方法
- 步驟 2:建立類別欄位受汙染的模型
- 步驟 3:建立 createUser() 方法呼叫的模型
- 步驟 4:建立將資料傳回到用戶端的模型
應用程式實務
在這個範例中,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
使用四個引數:
- 第一個引數是我們想要使其欄位受影響的類別參照。在此範例中,區域變數
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
,它儲存在新的 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
方法的呼叫,來建立資料傳回到用戶端的模型。