001/** 002 * 003 * Copyright 2019-2020 Florian Schmaus 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jxmpp.strings.testframework; 018 019import java.util.ArrayList; 020import java.util.Collections; 021import java.util.HashSet; 022import java.util.Iterator; 023import java.util.List; 024import java.util.Set; 025 026import org.jxmpp.stringprep.XmppStringprep; 027 028public class KnownFailures { 029 030 private static final Set<XmppStringprepStringCoupling> VALID_JIDS_WHITELIST = new HashSet<>(); 031 private static final Set<XmppStringprepStringCoupling> INVALID_JIDS_WHITELIST = new HashSet<>(); 032 033 static { 034 // ICU4J 035 whitelistValidJid(XmppStringPrepper.ICU4J, "fußball@example.com"); 036 whitelistValidJid(XmppStringPrepper.ICU4J, "user@example.org/🍺"); 037 038 whitelistInvalidJid(XmppStringPrepper.ICU4J, "♚@example.com"); 039 whitelistInvalidJid(XmppStringPrepper.ICU4J, "henry\u2163@example.com"); 040 whitelistInvalidJid(XmppStringPrepper.ICU4J, "user@@host/resource"); 041 whitelistInvalidJid(XmppStringPrepper.ICU4J, "user@@host"); 042 whitelistInvalidJid(XmppStringPrepper.ICU4J, "user@@"); 043 whitelistInvalidJid(XmppStringPrepper.ICU4J, "username@example.org@example.org"); 044 045 // LIBIDN 046 whitelistValidJid(XmppStringPrepper.LIBIDN, "fußball@example.com"); 047 whitelistValidJid(XmppStringPrepper.LIBIDN, "user@example.org/🍺"); 048 049 whitelistInvalidJid(XmppStringPrepper.LIBIDN, "♚@example.com"); 050 whitelistInvalidJid(XmppStringPrepper.LIBIDN, "henry\u2163@example.com"); 051 whitelistInvalidJid(XmppStringPrepper.LIBIDN, "user@@host/resource"); 052 whitelistInvalidJid(XmppStringPrepper.LIBIDN, "user@@host"); 053 whitelistInvalidJid(XmppStringPrepper.LIBIDN, "user@@"); 054 whitelistInvalidJid(XmppStringPrepper.LIBIDN, "xsf@muc.xmpp.org/x"); 055 whitelistInvalidJid(XmppStringPrepper.LIBIDN, "username@example.org@example.org"); 056 057 058 // ROCKS_XMPP_PRECIS 059 whitelistValidJid(XmppStringPrepper.ROCKS_XMPP_PRECIS, "ς@example.com"); 060 whitelistValidJid(XmppStringPrepper.ROCKS_XMPP_PRECIS, "user@[2001:638:a000:4134::ffff:40]"); 061 whitelistValidJid(XmppStringPrepper.ROCKS_XMPP_PRECIS, "user@[2001:638:a000:4134::ffff:40%eno1]"); 062 whitelistValidJid(XmppStringPrepper.ROCKS_XMPP_PRECIS, "user@averylongdomainpartisstillvalideventhoughitexceedsthesixtyfourbytelimitofdnslabels"); 063 064 065 // SIMPLE 066 whitelistValidJid(XmppStringPrepper.SIMPLE, "ς@example.com"); 067 068 whitelistInvalidJid(XmppStringPrepper.SIMPLE, "♚@example.com"); 069 whitelistInvalidJid(XmppStringPrepper.SIMPLE, "henry\u2163@example.com"); 070 whitelistInvalidJid(XmppStringPrepper.SIMPLE, "user@@host/resource"); 071 whitelistInvalidJid(XmppStringPrepper.SIMPLE, "user@@host"); 072 whitelistInvalidJid(XmppStringPrepper.SIMPLE, "user@@"); 073 whitelistInvalidJid(XmppStringPrepper.SIMPLE, "xsf@muc.xmpp.org/x"); 074 whitelistInvalidJid(XmppStringPrepper.SIMPLE, "username@example.org@example.org"); 075 076 } 077 078 /** 079 * Check a XMPP Strings Testframeworkresult against the list of known failures (whitelist). 080 * 081 * @param result the result of the XMPP Strings Testframework. 082 * @return the result of whitelist check. 083 */ 084 public static Result checkAgainstWhitelist(StringsTestframework.Result result) { 085 List<ValidJidTestresult.Failed> validJidFailedTestresults = new ArrayList<>(result.validJidFailedTestresults); 086 List<InvalidJidTestresult.Failed> invalidJidFailedTestresults = new ArrayList<>(result.invalidJidFailedTestresults); 087 088 Set<XmppStringprepStringCoupling> validJidsWhitelist, invalidJidsWhitelist; 089 synchronized (VALID_JIDS_WHITELIST) { 090 validJidsWhitelist = new HashSet<>(VALID_JIDS_WHITELIST); 091 } 092 synchronized (INVALID_JIDS_WHITELIST) { 093 invalidJidsWhitelist = new HashSet<>(INVALID_JIDS_WHITELIST); 094 } 095 096 { 097 Iterator<ValidJidTestresult.Failed> it = validJidFailedTestresults.iterator(); 098 while (it.hasNext()) { 099 ValidJidTestresult.Failed failed = it.next(); 100 XmppStringprepStringCoupling coupling = new XmppStringprepStringCoupling( 101 failed.xmppStringPrepper.xmppStringprepClass, failed.validJid.unnormalizedJid); 102 boolean wasWhitelisted = validJidsWhitelist.remove(coupling); 103 if (wasWhitelisted) { 104 it.remove(); 105 } 106 } 107 } 108 109 { 110 Iterator<InvalidJidTestresult.Failed> it = invalidJidFailedTestresults.iterator(); 111 while (it.hasNext()) { 112 InvalidJidTestresult.Failed failed = it.next(); 113 String invalidJid = failed.invalidJid.invalidJid; 114 XmppStringprepStringCoupling coupling = new XmppStringprepStringCoupling( 115 failed.xmppStringPrepper.xmppStringprepClass, invalidJid); 116 boolean wasWhitelisted = invalidJidsWhitelist.remove(coupling); 117 if (wasWhitelisted) { 118 it.remove(); 119 } 120 } 121 } 122 123 return new Result(validJidFailedTestresults, invalidJidFailedTestresults, validJidsWhitelist, invalidJidsWhitelist); 124 } 125 126 /** 127 * Whitelist the given valid JID and the used XMPP String prepper. 128 * 129 * @param xmppStringPrepper the used XMPP String prepper. 130 * @param validJid the valid JID. 131 */ 132 public static void whitelistValidJid(XmppStringPrepper xmppStringPrepper, String validJid) { 133 whitelistValidJid(xmppStringPrepper.xmppStringprepClass, validJid); 134 } 135 136 /** 137 * Whitelist the given valid JID and the used XMPP String prepper. 138 * 139 * @param xmppStringprepClass the class of the used XMPP String prepper. 140 * @param validJid the valid JID. 141 */ 142 public static void whitelistValidJid(Class<? extends XmppStringprep> xmppStringprepClass, String validJid) { 143 XmppStringprepStringCoupling coupling = new XmppStringprepStringCoupling(xmppStringprepClass, validJid); 144 final boolean newCoupling; 145 synchronized (VALID_JIDS_WHITELIST) { 146 newCoupling = VALID_JIDS_WHITELIST.add(coupling); 147 } 148 if (!newCoupling) { 149 throw new IllegalArgumentException(coupling + " is already whitelisted for valid JIDs"); 150 } 151 } 152 153 /** 154 * Whitelist the given invalid JID and the used XMPP String prepper. 155 * 156 * @param xmppStringPrepper the used XMPP String prepper. 157 * @param invalidJid the invalid JID. 158 */ 159 public static void whitelistInvalidJid(XmppStringPrepper xmppStringPrepper, String invalidJid) { 160 whitelistInvalidJid(xmppStringPrepper.xmppStringprepClass, invalidJid); 161 } 162 163 /** 164 * Whitelist the given invalid JID and the used XMPP String prepper. 165 * 166 * @param xmppStringprepClass the class of the used XMPP String prepper. 167 * @param invalidJid the invalid JID. 168 */ 169 public static void whitelistInvalidJid(Class<? extends XmppStringprep> xmppStringprepClass, String invalidJid) { 170 XmppStringprepStringCoupling coupling = new XmppStringprepStringCoupling(xmppStringprepClass, invalidJid); 171 final boolean newCoupling; 172 synchronized (INVALID_JIDS_WHITELIST) { 173 newCoupling = INVALID_JIDS_WHITELIST.add(coupling); 174 } 175 if (!newCoupling) { 176 throw new IllegalArgumentException(coupling + " is already whitelisted for invalid JIDs"); 177 } 178 } 179 180 public static class Result { 181 public final boolean noUnknownFailures; 182 public final List<ValidJidTestresult.Failed> remainingValidJidFailedTestresults; 183 public final List<InvalidJidTestresult.Failed> remainingInvalidJidFailedTestresults; 184 185 public final Set<XmppStringprepStringCoupling> remainingValidJidsWhitelist, remainingInvalidJidsWhitelist; 186 187 private Result(List<ValidJidTestresult.Failed> remainingValidJidFailedTestresults, 188 List<InvalidJidTestresult.Failed> remainingInvalidJidFailedTestresults, 189 Set<XmppStringprepStringCoupling> remainingValidJidsWhitelist, 190 Set<XmppStringprepStringCoupling> remainingInvalidJidsWhitelist) { 191 noUnknownFailures = remainingValidJidFailedTestresults.isEmpty() 192 && remainingInvalidJidFailedTestresults.isEmpty() && remainingValidJidsWhitelist.isEmpty() 193 && remainingInvalidJidsWhitelist.isEmpty(); 194 195 this.remainingValidJidFailedTestresults = Collections.unmodifiableList(remainingValidJidFailedTestresults); 196 this.remainingInvalidJidFailedTestresults = Collections.unmodifiableList(remainingInvalidJidFailedTestresults); 197 this.remainingValidJidsWhitelist = Collections.unmodifiableSet(remainingValidJidsWhitelist); 198 this.remainingInvalidJidsWhitelist = Collections.unmodifiableSet(remainingInvalidJidsWhitelist); 199 } 200 201 @Override 202 public String toString() { 203 StringBuilder sb = new StringBuilder(); 204 sb.append( 205 "XMPP Strings Testframework Known Failure Report\n" + 206 "===============================================\n"); 207 if (!remainingValidJidFailedTestresults.isEmpty()) { 208 sb.append( 209 "Remaining Valid JID failed results:\n" + 210 "-----------------------------------\n" 211 ); 212 for (ValidJidTestresult.Failed failed : remainingValidJidFailedTestresults) { 213 sb.append(failed).append('\n'); 214 } 215 } 216 if (!remainingValidJidsWhitelist.isEmpty()) { 217 sb.append( 218 "Remaining Valid JID failed whitelist entries:\n" + 219 "---------------------------------------------\n" 220 ); 221 for (XmppStringprepStringCoupling coupling : remainingValidJidsWhitelist) { 222 sb.append(coupling).append('\n'); 223 } 224 } 225 if (!remainingInvalidJidFailedTestresults.isEmpty()) { 226 sb.append( 227 "Remaining Invalid JID failed results:\n" + 228 "-----------------------------------\n" 229 ); 230 for (InvalidJidTestresult.Failed failed : remainingInvalidJidFailedTestresults) { 231 sb.append(failed).append('\n'); 232 } 233 } 234 if (!remainingInvalidJidsWhitelist.isEmpty()) { 235 sb.append( 236 "Remaining Invalid JID failed whitelist entries:\n" + 237 "---------------------------------------------\n" 238 ); 239 for (XmppStringprepStringCoupling coupling : remainingInvalidJidsWhitelist) { 240 sb.append(coupling).append('\n'); 241 } 242 } 243 return sb.toString(); 244 } 245 } 246 247 private static class XmppStringprepStringCoupling { 248 public final Class<? extends XmppStringprep> xmppStringprepClass; 249 public final String string; 250 251 private XmppStringprepStringCoupling(Class<? extends XmppStringprep> xmppStringprepClass, String string) { 252 this.xmppStringprepClass = xmppStringprepClass; 253 this.string = string; 254 } 255 256 @Override 257 public int hashCode() { 258 int result = 17; 259 result = 31 * result + xmppStringprepClass.hashCode(); 260 result = 31 * result + string.hashCode(); 261 return result; 262 } 263 264 @Override 265 public boolean equals(Object other) { 266 if (this == other) { 267 return true; 268 } 269 270 if (!(other instanceof XmppStringprepStringCoupling)) { 271 return false; 272 } 273 274 XmppStringprepStringCoupling lhs = (XmppStringprepStringCoupling) other; 275 return xmppStringprepClass.equals(lhs.xmppStringprepClass) && string.equals(lhs.string); 276 } 277 278 @Override 279 public String toString() { 280 return xmppStringprepClass.getName() + " '" + string + "\'"; 281 } 282 } 283 284}