diff --git a/user/src/com/google/gwt/i18n/server/GwtLocaleFactoryImpl.java b/user/src/com/google/gwt/i18n/server/GwtLocaleFactoryImpl.java index 3af2f5a6d25..1fa64d4d5f3 100644 --- a/user/src/com/google/gwt/i18n/server/GwtLocaleFactoryImpl.java +++ b/user/src/com/google/gwt/i18n/server/GwtLocaleFactoryImpl.java @@ -36,6 +36,19 @@ private static boolean isDigit(String str, int min, int max) { return matches(str, min, max, false); } + private static boolean isAlphaNumeric(String str, int min, int max) { + int len = str.length(); + if (len < min || len > max) { + return false; + } + for (int i = 0; i < len; ++i) { + if (!Character.isLetterOrDigit(str.charAt(i))) { + return false; + } + } + return true; + } + /** * Check if the supplied string matches length and composition requirements. * @@ -143,6 +156,14 @@ public GwtLocale fromString(String localeName) { ArrayList localeParts = new ArrayList(); String[] parts = localeName.split("[-_]"); for (int i = 0; i < parts.length; ++i) { + // The split only breaks on '-'/'_', so any other character stays inside + // a part. Require each subtag to be alphanumeric and 1-8 chars, matching + // BCP47, so an untrusted server-side locale can't smuggle '.', '$' or + // '/' into the class name LocalizableInstantiator resolves reflectively. + if (!isAlphaNumeric(parts[i], 1, 8)) { + throw new IllegalArgumentException("Unrecognized locale format: " + + localeName); + } if (parts[i].length() == 1 && i + 1 < parts.length) { localeParts.add(parts[i] + '-' + parts[++i]); } else { diff --git a/user/test/com/google/gwt/i18n/server/GwtLocaleTest.java b/user/test/com/google/gwt/i18n/server/GwtLocaleTest.java index 6b3e101ff8d..8e5cd48401d 100644 --- a/user/test/com/google/gwt/i18n/server/GwtLocaleTest.java +++ b/user/test/com/google/gwt/i18n/server/GwtLocaleTest.java @@ -174,6 +174,28 @@ public void testFromString() { } } + public void testFromStringRejectsInvalidLanguage() { + // The language subtag is concatenated into class names resolved reflectively + // on the server, so it must not carry characters like '.', '$' or '/'. + String[] invalid = { + "com.google.gwt.dev.Compiler", "java.lang.Runtime", "x$Evil", "a/b/c", + "en.US", + }; + for (String locale : invalid) { + try { + factory.fromString(locale); + fail("Should have thrown IllegalArgumentException on " + locale); + } catch (IllegalArgumentException expected) { + } + } + // Well-formed tags, including extended-language and private-use forms, are + // still accepted with the language preserved verbatim. + assertEquals("en", factory.fromString("en_US").getLanguage()); + assertEquals("zh-cmn", factory.fromString("zh-cmn").getLanguage()); + assertEquals("i-klingon", factory.fromString("i-klingon").getLanguage()); + assertEquals("x-foo123", factory.fromString("x-foo123").getLanguage()); + } + public void testInheritance() { GwtLocale en = factory.fromString("en_Latn_US_VARIANT"); List chain = en.getInheritanceChain();