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 037 whitelistInvalidJid(XmppStringPrepper.ICU4J, "♚@example.com"); 038 whitelistInvalidJid(XmppStringPrepper.ICU4J, "henry\u2163@example.com"); 039 whitelistInvalidJid(XmppStringPrepper.ICU4J, "user@@host/resource"); 040 whitelistInvalidJid(XmppStringPrepper.ICU4J, "user@@host"); 041 whitelistInvalidJid(XmppStringPrepper.ICU4J, "user@@"); 042 whitelistInvalidJid(XmppStringPrepper.ICU4J, "username@example.org@example.org"); 043 044 // LIBIDN 045 whitelistValidJid(XmppStringPrepper.LIBIDN, "fußball@example.com"); 046 047 whitelistInvalidJid(XmppStringPrepper.LIBIDN, "♚@example.com"); 048 whitelistInvalidJid(XmppStringPrepper.LIBIDN, "henry\u2163@example.com"); 049 whitelistInvalidJid(XmppStringPrepper.LIBIDN, "user@@host/resource"); 050 whitelistInvalidJid(XmppStringPrepper.LIBIDN, "user@@host"); 051 whitelistInvalidJid(XmppStringPrepper.LIBIDN, "user@@"); 052 whitelistInvalidJid(XmppStringPrepper.LIBIDN, "xsf@muc.xmpp.org/x"); 053 whitelistInvalidJid(XmppStringPrepper.LIBIDN, "username@example.org@example.org"); 054 055 056 // ROCKS_XMPP_PRECIS 057 whitelistValidJid(XmppStringPrepper.ROCKS_XMPP_PRECIS, "ς@example.com"); 058 whitelistValidJid(XmppStringPrepper.ROCKS_XMPP_PRECIS, "user@[2001:638:a000:4134::ffff:40]"); 059 whitelistValidJid(XmppStringPrepper.ROCKS_XMPP_PRECIS, "user@[2001:638:a000:4134::ffff:40%eno1]"); 060 whitelistValidJid(XmppStringPrepper.ROCKS_XMPP_PRECIS, "user@averylongdomainpartisstillvalideventhoughitexceedsthesixtyfourbytelimitofdnslabels"); 061 062 063 // SIMPLE 064 whitelistValidJid(XmppStringPrepper.SIMPLE, "ς@example.com"); 065 066 whitelistInvalidJid(XmppStringPrepper.SIMPLE, "♚@example.com"); 067 whitelistInvalidJid(XmppStringPrepper.SIMPLE, "henry\u2163@example.com"); 068 whitelistInvalidJid(XmppStringPrepper.SIMPLE, "user@@host/resource"); 069 whitelistInvalidJid(XmppStringPrepper.SIMPLE, "user@@host"); 070 whitelistInvalidJid(XmppStringPrepper.SIMPLE, "user@@"); 071 whitelistInvalidJid(XmppStringPrepper.SIMPLE, "xsf@muc.xmpp.org/x"); 072 whitelistInvalidJid(XmppStringPrepper.SIMPLE, "username@example.org@example.org"); 073 074 } 075 076 /** 077 * Check a XMPP Strings Testframeworkresult against the list of known failures (whitelist). 078 * 079 * @param result the result of the XMPP Strings Testframework. 080 * @return the result of whitelist check. 081 */ 082 public static Result checkAgainstWhitelist(StringsTestframework.Result result) { 083 List<ValidJidTestresult.Failed> validJidFailedTestresults = new ArrayList<>(result.validJidFailedTestresults); 084 List<InvalidJidTestresult.Failed> invalidJidFailedTestresults = new ArrayList<>(result.invalidJidFailedTestresults); 085 086 Set<XmppStringprepStringCoupling> validJidsWhitelist, invalidJidsWhitelist; 087 synchronized (VALID_JIDS_WHITELIST) { 088 validJidsWhitelist = new HashSet<>(VALID_JIDS_WHITELIST); 089 } 090 synchronized (INVALID_JIDS_WHITELIST) { 091 invalidJidsWhitelist = new HashSet<>(INVALID_JIDS_WHITELIST); 092 } 093 094 { 095 Iterator<ValidJidTestresult.Failed> it = validJidFailedTestresults.iterator(); 096 while (it.hasNext()) { 097 ValidJidTestresult.Failed failed = it.next(); 098 XmppStringprepStringCoupling coupling = new XmppStringprepStringCoupling( 099 failed.xmppStringPrepper.xmppStringprepClass, failed.validJid.unnormalizedJid); 100 boolean wasWhitelisted = validJidsWhitelist.remove(coupling); 101 if (wasWhitelisted) { 102 it.remove(); 103 } 104 } 105 } 106 107 { 108 Iterator<InvalidJidTestresult.Failed> it = invalidJidFailedTestresults.iterator(); 109 while (it.hasNext()) { 110 InvalidJidTestresult.Failed failed = it.next(); 111 String invalidJid = failed.invalidJid.invalidJid; 112 XmppStringprepStringCoupling coupling = new XmppStringprepStringCoupling( 113 failed.xmppStringPrepper.xmppStringprepClass, invalidJid); 114 boolean wasWhitelisted = invalidJidsWhitelist.remove(coupling); 115 if (wasWhitelisted) { 116 it.remove(); 117 } 118 } 119 } 120 121 return new Result(validJidFailedTestresults, invalidJidFailedTestresults, validJidsWhitelist, invalidJidsWhitelist); 122 } 123 124 /** 125 * Whitelist the given valid JID and the used XMPP String prepper. 126 * 127 * @param xmppStringPrepper the used XMPP String prepper. 128 * @param validJid the valid JID. 129 */ 130 public static void whitelistValidJid(XmppStringPrepper xmppStringPrepper, String validJid) { 131 whitelistValidJid(xmppStringPrepper.xmppStringprepClass, validJid); 132 } 133 134 /** 135 * Whitelist the given valid JID and the used XMPP String prepper. 136 * 137 * @param xmppStringprepClass the class of the used XMPP String prepper. 138 * @param validJid the valid JID. 139 */ 140 public static void whitelistValidJid(Class<? extends XmppStringprep> xmppStringprepClass, String validJid) { 141 XmppStringprepStringCoupling coupling = new XmppStringprepStringCoupling(xmppStringprepClass, validJid); 142 final boolean newCoupling; 143 synchronized (VALID_JIDS_WHITELIST) { 144 newCoupling = VALID_JIDS_WHITELIST.add(coupling); 145 } 146 if (!newCoupling) { 147 throw new IllegalArgumentException(coupling + " is already whitelisted for valid JIDs"); 148 } 149 } 150 151 /** 152 * Whitelist the given invalid JID and the used XMPP String prepper. 153 * 154 * @param xmppStringPrepper the used XMPP String prepper. 155 * @param invalidJid the invalid JID. 156 */ 157 public static void whitelistInvalidJid(XmppStringPrepper xmppStringPrepper, String invalidJid) { 158 whitelistInvalidJid(xmppStringPrepper.xmppStringprepClass, invalidJid); 159 } 160 161 /** 162 * Whitelist the given invalid JID and the used XMPP String prepper. 163 * 164 * @param xmppStringprepClass the class of the used XMPP String prepper. 165 * @param invalidJid the invalid JID. 166 */ 167 public static void whitelistInvalidJid(Class<? extends XmppStringprep> xmppStringprepClass, String invalidJid) { 168 XmppStringprepStringCoupling coupling = new XmppStringprepStringCoupling(xmppStringprepClass, invalidJid); 169 final boolean newCoupling; 170 synchronized (INVALID_JIDS_WHITELIST) { 171 newCoupling = INVALID_JIDS_WHITELIST.add(coupling); 172 } 173 if (!newCoupling) { 174 throw new IllegalArgumentException(coupling + " is already whitelisted for invalid JIDs"); 175 } 176 } 177 178 public static class Result { 179 public final boolean noUnknownFailures; 180 public final List<ValidJidTestresult.Failed> remainingValidJidFailedTestresults; 181 public final List<InvalidJidTestresult.Failed> remainingInvalidJidFailedTestresults; 182 183 public final Set<XmppStringprepStringCoupling> remainingValidJidsWhitelist, remainingInvalidJidsWhitelist; 184 185 private Result(List<ValidJidTestresult.Failed> remainingValidJidFailedTestresults, 186 List<InvalidJidTestresult.Failed> remainingInvalidJidFailedTestresults, 187 Set<XmppStringprepStringCoupling> remainingValidJidsWhitelist, 188 Set<XmppStringprepStringCoupling> remainingInvalidJidsWhitelist) { 189 noUnknownFailures = remainingValidJidFailedTestresults.isEmpty() 190 && remainingInvalidJidFailedTestresults.isEmpty() && remainingValidJidsWhitelist.isEmpty() 191 && remainingInvalidJidsWhitelist.isEmpty(); 192 193 this.remainingValidJidFailedTestresults = Collections.unmodifiableList(remainingValidJidFailedTestresults); 194 this.remainingInvalidJidFailedTestresults = Collections.unmodifiableList(remainingInvalidJidFailedTestresults); 195 this.remainingValidJidsWhitelist = Collections.unmodifiableSet(remainingValidJidsWhitelist); 196 this.remainingInvalidJidsWhitelist = Collections.unmodifiableSet(remainingInvalidJidsWhitelist); 197 } 198 199 @Override 200 public String toString() { 201 StringBuilder sb = new StringBuilder(); 202 sb.append( 203 "XMPP Strings Testframework Known Failure Report\n" + 204 "===============================================\n"); 205 if (!remainingValidJidFailedTestresults.isEmpty()) { 206 sb.append( 207 "Remaining Valid JID failed results:\n" + 208 "-----------------------------------\n" 209 ); 210 for (ValidJidTestresult.Failed failed : remainingValidJidFailedTestresults) { 211 sb.append(failed).append('\n'); 212 } 213 } 214 if (!remainingValidJidsWhitelist.isEmpty()) { 215 sb.append( 216 "Remaining Valid JID failed whitelist entries:\n" + 217 "---------------------------------------------\n" 218 ); 219 for (XmppStringprepStringCoupling coupling : remainingValidJidsWhitelist) { 220 sb.append(coupling).append('\n'); 221 } 222 } 223 if (!remainingInvalidJidFailedTestresults.isEmpty()) { 224 sb.append( 225 "Remaining Invalid JID failed results:\n" + 226 "-----------------------------------\n" 227 ); 228 for (InvalidJidTestresult.Failed failed : remainingInvalidJidFailedTestresults) { 229 sb.append(failed).append('\n'); 230 } 231 } 232 if (!remainingInvalidJidsWhitelist.isEmpty()) { 233 sb.append( 234 "Remaining Invalid JID failed whitelist entries:\n" + 235 "---------------------------------------------\n" 236 ); 237 for (XmppStringprepStringCoupling coupling : remainingInvalidJidsWhitelist) { 238 sb.append(coupling).append('\n'); 239 } 240 } 241 return sb.toString(); 242 } 243 } 244 245 private static class XmppStringprepStringCoupling { 246 public final Class<? extends XmppStringprep> xmppStringprepClass; 247 public final String string; 248 249 private XmppStringprepStringCoupling(Class<? extends XmppStringprep> xmppStringprepClass, String string) { 250 this.xmppStringprepClass = xmppStringprepClass; 251 this.string = string; 252 } 253 254 @Override 255 public int hashCode() { 256 int result = 17; 257 result = 31 * result + xmppStringprepClass.hashCode(); 258 result = 31 * result + string.hashCode(); 259 return result; 260 } 261 262 @Override 263 public boolean equals(Object other) { 264 if (this == other) { 265 return true; 266 } 267 268 if (!(other instanceof XmppStringprepStringCoupling)) { 269 return false; 270 } 271 272 XmppStringprepStringCoupling lhs = (XmppStringprepStringCoupling) other; 273 return xmppStringprepClass.equals(lhs.xmppStringprepClass) && string.equals(lhs.string); 274 } 275 276 @Override 277 public String toString() { 278 return xmppStringprepClass.getName() + " '" + string + "\'"; 279 } 280 } 281 282}