View Javadoc
1   package de.juplo.facebook.aspects;
2   
3   
4   import org.aspectj.lang.ProceedingJoinPoint;
5   import org.aspectj.lang.annotation.Around;
6   import org.aspectj.lang.annotation.Aspect;
7   import org.slf4j.Logger;
8   import org.slf4j.LoggerFactory;
9   
10  
11  
12  /**
13   * @author Kai Moritz
14   */
15  @Aspect
16  public class SanitizeAspect
17  {
18    private static final Logger log =
19        LoggerFactory.getLogger(SanitizeAspect.class);
20  
21  
22    /**
23     * This method sanitizes the given string in all means:
24     * <ul>
25     * <li>It removes leading and trailing whitspace.</li>
26     * <li>It removes characters, that are not allowed in the XML-output</li>
27     * <li>It checks the allowed length of the string</li>
28     * </ul>
29     *
30     * This method ensures that the output String has only
31     * valid XML unicode characters as specified by the
32     * XML 1.0 standard. For reference, please see
33     * <a href="http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char">the
34     * standard</a>. This method will return an empty
35     * String if the input is null or empty.
36     *
37     * @param jp The join-point captured by AspectJ.
38     * @param in The String whose non-valid characters we want to remove.
39     * @param sanitize The annotation, the field was marked with.
40     * @see <a href="http://blog.mark-mclaren.info/2007/02/invalid-xml-characters-when-valid-utf8_5873.html">Invalid XML Characters: when valid UTF8 does not mean valid XML</a>
41     * @see <a href="http://up-download.de/up/docs/werkstatt/de/intrexx-werkstatt-ungueltige-zeichen-in-eingabefeldern-abfangen.pdf">Ungültige Zeichen in Eingabefeldern abfangen</a>
42     */
43    @Around("set(String *) && args(in) && @annotation(sanitize)")
44    public void sanitize(
45        ProceedingJoinPoint jp,
46        String in,
47        Sanitize sanitize
48        )
49        throws Throwable
50    {
51      if (in == null)
52      {
53        jp.proceed(new Object[] { null });
54        return;
55      }
56  
57      in = in.trim();
58      if ("".equals(in))
59      {
60        jp.proceed(new Object[] { null });
61        return;
62      }
63  
64      StringBuilder out = new StringBuilder(); // Used to hold the output.
65      char current; // Used to reference the current character.
66  
67      for (int i = 0; i < in.length(); i++)
68      {
69        current = in.charAt(i); // NOTE: No IndexOutOfBoundsException caught here; it should not happen.
70        if ((current == 0x9) ||
71            (current == 0xA) ||
72            (current == 0xD) ||
73            ((current >= 0x20) && (current <= 0xD7FF)) ||
74            ((current >= 0xE000) && (current <= 0xFFFD)) ||
75            ((current >= 0x10000) && (current <= 0x10FFFF)))
76          out.append(current);
77      }
78      if (out.length() > sanitize.length())
79      {
80        log.error(
81            "Maximum length for attribute {} exceeded: should={}, was={}",
82            jp.getSignature().getName(),
83            sanitize.length(),
84            out.length()
85            );
86        if (sanitize.fail())
87          throw new RuntimeException("String is longer than " + sanitize.length());
88        else
89          out.setLength(sanitize.length());
90      }
91      jp.proceed(new Object[] { out.toString() });
92    }
93  }