Exemple : création d'une méthode synthétique

Cet exemple illustre la création d'une méthode synthétique. Dans cette méthode synthétique, nous rendons d'abord une zone de classe vulnérable aux taches, puis nous ajoutons un appel à une méthode dans le code de l'utilisateur et nous transmettons enfin le résultat de cet appel de méthode à un collecteur.

Scénario d'application

Dans cet exemple, createUser est une API REST implémentée dans la structure JAX-RS. Un utilisateur peut appeler cette API via une adresse URL telle que http://host:port/users/createUser. L'exécution de la structure délègue cet appel à UserRestService.createUser(), étant donné que le chemin correspond. En outre, il initialise la variable de classe urlInfo des données client avant d'appeler createUser(). Finalement, les données retournées par createUser() sont renvoyées au client par l'environnement d'exécution.

Composants modélisés dans la méthode synthétique

Pour la capture du Scénario d'application dans une méthode synthétique, les éléments suivants sont modélisés :

  • la vulnérabilité aux taches de la variable de classe urlInfo de UserRestService.
  • L'appel de UserRestService.createUser().
  • Le renvoi de données au client.

Voici le code de la méthode 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;
  }
}

Etape 1 : créer une méthode synthétique vide

22    public class JAXRSHandler extends F4FHandler{
23    @Override
        public void handleApp(F4FApp app, F4FActions actions) {
24    HighLevelSyntheticMethod synthMethod = HighLevelSyntheticMethod.make();           
  • L'objet de méthode synthétique est initialisé à la ligne 24.

Etape 2 : modéliser la vulnérabilité aux taches d'une zone de classe

Le fragment de code ci-dessous illustre la manière dont des zones de classe spécifiques peuvent être rendues vulnérables aux taches. Dans cet exemple, l'utilisateur souhaite rendre vulnérables aux taches les zones de classes dotées de l'annotation @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    }

L'API Framework for Frameworks addInstanceVariableWrite prend quatre arguments :

  1. Le premier argument est la référence de classe dont nous souhaitons rendre la zone vulnérable aux taches. Dans l'exemple, la variable locale userRestServiceClazzInstance fait référence à cet argument.
  2. Le second argument est la zone de classe que nous souhaitons rendre vulnérable aux taches.
  3. Le troisième argument est la nouvelle valeur qui va être affectée au champ de variable de classe. Dans l'exemple, cette variable doit être rendue vulnérable aux taches pour transmettre Taint.taint().
  4. Le dernier argument est FilePositionInfo qui a la valeur null dans l'exemple.

Etape 3 : modélisation de l'appel de la méthode createUser()

Au cours de cette étape, nous simulons l'appel dans notre méthode synthétique.

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 */);

L'API de haut niveau addCall présente trois arguments :

  • Le premier argument est la signature de la méthode que nous souhaitons appeler. Dans ce cas, User.createUser() doit être appelée, sa signature est donc utilisée.
  • Le deuxième argument est FilePositionInfo. Il est transmis avec la valeur null car il n'est pas obligatoire pour cet exemple.
  • Le dernier argument représente la liste des paramètres requis pour appeler la méthode createUser().

Etant donné que createUser() n'admet aucun argument, le seul argument transmis à cet appel est l'objet this, une variable Local (userRestServiceClazzInstance) du type User. La méthode createUser() renvoie un nouvel élément User créé et stocké dans une nouvelle variable Local nommée returnedObj (voir ligne 51).

Etape 4 : modéliser le renvoi de données au client

Lors de cette dernière étape, nous créons un collecteur afin de modéliser le renvoi de données au client.

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  }
  • A la ligne 59, un objet Local est créé pour la classe PrintWriter dans la méthode synthétique.
  • De la ligne 64 à la ligne 66, la liste des paramètres requis pour appeler la méthode print sur l'instance de classe PrintWriter est préparée (printWriterObj) :
    • Le premier paramètre est la référence d'objet (printWriterObj).
    • Le deuxième paramètre est l'argument de la méthode print. Il faut transmettre la valeur de renvoi stockée dans la variable Local returnObj.
  • A la ligne 69, un appel de méthode PrintWriter.print est ajouté pour modéliser le renvoi des données au client.