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 }