From 6ca0aef5affd817b8b3401325e18b63da15b6177 Mon Sep 17 00:00:00 2001 From: Valera V Harseko Date: Sat, 19 Sep 2026 12:29:22 +0300 Subject: [PATCH 1/4] Close out the remaining small note-tier CodeQL categories - GuardedString now overrides toString() so it never inherits Object's default (fixes call-to-object-tostring at its two call sites in one place instead of patching each site). - AttributeTypeUtil.createInstantiatedObject wraps its numeric parsing in the same try/catch -> ConnectorException pattern used elsewhere (uncaught-number-format-exception), and switches from deprecated boxed constructors to the static parse methods. - Remove the dead ContractTestFactory inner class and its now-unused imports. - Mechanical fixes: StringUtil/XSDAnnotationParser empty-string checks, PrettyStringBuilder's Map iteration via entrySet(), a shadowed local in LdapInternalSearch, a javadoc @param typo/gap in MultiOpTests, and a missing space in a log message in ActiveDirectoryChangeLogSyncStrategy. --- .../contract/test/ContractITCase.java | 15 +---- .../contract/test/MultiOpTests.java | 4 +- .../common/PrettyStringBuilder.java | 9 +-- .../identityconnectors/common/StringUtil.java | 2 +- .../common/security/GuardedString.java | 9 +++ .../common/security/GuardedStringTests.java | 9 +++ .../ldap/search/LdapInternalSearch.java | 5 +- .../ActiveDirectoryChangeLogSyncStrategy.java | 4 +- .../xml/util/AttributeTypeUtil.java | 57 +++++++++++++++---- .../xml/xsdparser/XSDAnnotationParser.java | 2 +- .../xml/util/AttributeTypeUtilTests.java | 52 +++++++++++++++++ 11 files changed, 133 insertions(+), 35 deletions(-) create mode 100644 OpenICF-xml-connector/src/test/java/org/forgerock/openicf/connectors/xml/util/AttributeTypeUtilTests.java diff --git a/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/ContractITCase.java b/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/ContractITCase.java index 398cef046..a69f064c4 100644 --- a/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/ContractITCase.java +++ b/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/ContractITCase.java @@ -20,6 +20,8 @@ * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" + * + * Portions Copyrighted 2026 3A Systems LLC. */ package org.identityconnectors.contract.test; @@ -32,12 +34,9 @@ import org.identityconnectors.common.StringUtil; import org.identityconnectors.contract.data.DataProvider; -import org.identityconnectors.framework.api.ConnectorFacade; -import org.identityconnectors.framework.common.objects.Schema; import org.testng.IObjectFactory; import org.testng.ITestContext; import org.testng.annotations.Factory; -import org.testng.internal.ObjectFactoryImpl; import com.google.inject.Guice; import com.google.inject.Injector; @@ -111,14 +110,4 @@ public Injector getInjector(ITestContext context) { public DataProvider getDataProvider(ITestContext context) { return ConnectorHelper.createDataProvider(); } - - private static class ContractTestFactory { - - private ConnectorFacade connectorFacade = null; - - private Schema schema = null; - - private IObjectFactory objectFactory = new ObjectFactoryImpl(); - - } } diff --git a/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/MultiOpTests.java b/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/MultiOpTests.java index dabb83b72..d948209aa 100644 --- a/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/MultiOpTests.java +++ b/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/MultiOpTests.java @@ -21,6 +21,7 @@ * ==================== * * Portions Copyrighted 2012 ForgeRock AS + * Portions Copyrighted 2026 3A Systems LLC. * */ package org.identityconnectors.contract.test; @@ -576,11 +577,12 @@ public void testPasswordChangeIntervalPredAttribute(ObjectClass objectClass) { /** * Method to check the attrName's attribute contract * + * @param objectClass object class under test * @param attrName attribute to be checked * @param createValue value used for create * @param updateValue value used for update * @param type expected type of the value - * @param addPassword, add password to attributes in the update + * @param addPassword add password to attributes in the update */ private void checkOpAttribute(ObjectClass objectClass, String attrName, Object createValue, Object updateValue, Class type, boolean addPassword) { final int iteration = 0; diff --git a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/PrettyStringBuilder.java b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/PrettyStringBuilder.java index 4477f8c50..252a8e1c3 100644 --- a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/PrettyStringBuilder.java +++ b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/PrettyStringBuilder.java @@ -20,6 +20,7 @@ * "Portions Copyrighted [year] [name of copyright owner]" * ==================== * Portions Copyrighted 2015 ForgeRock AS. + * Portions Copyrighted 2026 3A Systems LLC. */ package org.identityconnectors.common; @@ -96,13 +97,13 @@ protected String toPrettyString(final Object obj) { s.append(')'); } else if (obj instanceof Map) { final Map map = (Map) obj; - final Iterator it = map.keySet().iterator(); + final Iterator it = map.entrySet().iterator(); int i = 0; s.append('{'); while ((it.hasNext() && (i++ < maxArrayLen))) { - final Object key = it.next(); - s.append(key).append(':'); - s.append(toPrettyString(map.get(key))); + final Map.Entry entry = (Map.Entry) it.next(); + s.append(entry.getKey()).append(':'); + s.append(toPrettyString(entry.getValue())); if (it.hasNext()) { s.append(","); } diff --git a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/StringUtil.java b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/StringUtil.java index d17023730..ea73ee0ed 100644 --- a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/StringUtil.java +++ b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/StringUtil.java @@ -285,7 +285,7 @@ public static int indexOf(final String src, final char[] ch, final int idx) { * @return true if the string is empty else false. */ public static boolean isEmpty(final String val) { - return (val == null) ? true : "".equals(val) ? true : false; + return (val == null) ? true : val.isEmpty(); } /** diff --git a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/security/GuardedString.java b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/security/GuardedString.java index 8589d7e03..118d0c487 100644 --- a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/security/GuardedString.java +++ b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/security/GuardedString.java @@ -291,4 +291,13 @@ public boolean equals(Object o) { public int hashCode() { return base64SHA1Hash.hashCode(); } + + /** + * Never prints the clear text; the default {@link Object#toString()} would not either, + * but its output is just a class name and hash code, not useful for logging. + */ + @Override + public String toString() { + return "GuardedString(...)"; + } } diff --git a/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/common/security/GuardedStringTests.java b/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/common/security/GuardedStringTests.java index af7061a3e..17e0d526f 100644 --- a/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/common/security/GuardedStringTests.java +++ b/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/common/security/GuardedStringTests.java @@ -19,6 +19,8 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * + * Portions Copyrighted 2026 3A Systems LLC. */ package org.identityconnectors.common.security; @@ -121,6 +123,13 @@ public void testDispose() { } } + @Test + public void testToStringNeverExposesTheClearText() { + GuardedString str = new GuardedString("secret".toCharArray()); + assertFalse(str.toString().contains("secret"), + "toString() must never leak the clear text"); + } + @Test public void testUnicode() { diff --git a/OpenICF-ldap-connector/src/main/java/org/identityconnectors/ldap/search/LdapInternalSearch.java b/OpenICF-ldap-connector/src/main/java/org/identityconnectors/ldap/search/LdapInternalSearch.java index c2a26f1be..6757669d4 100644 --- a/OpenICF-ldap-connector/src/main/java/org/identityconnectors/ldap/search/LdapInternalSearch.java +++ b/OpenICF-ldap-connector/src/main/java/org/identityconnectors/ldap/search/LdapInternalSearch.java @@ -20,6 +20,7 @@ * "Portions Copyrighted [year] [name of copyright owner]" * ==================== * "Portions Copyrighted 2014 ForgeRock AS" + * Portions Copyrighted 2026 3A Systems LLC. */ package org.identityconnectors.ldap.search; @@ -59,9 +60,9 @@ public LdapInternalSearch(LdapConnection conn, String filter, List baseD } public void execute(LdapSearchResultsHandler handler) { - String filter = blankAsAllObjects(this.filter); + String effectiveFilter = blankAsAllObjects(this.filter); try { - strategy.doSearch(conn.getInitialContext(), baseDNs, filter, controls, handler); + strategy.doSearch(conn.getInitialContext(), baseDNs, effectiveFilter, controls, handler); } catch (IOException e) { throw new ConnectorException(e); } catch (PartialResultException e) { diff --git a/OpenICF-ldap-connector/src/main/java/org/identityconnectors/ldap/sync/activedirectory/ActiveDirectoryChangeLogSyncStrategy.java b/OpenICF-ldap-connector/src/main/java/org/identityconnectors/ldap/sync/activedirectory/ActiveDirectoryChangeLogSyncStrategy.java index b9b6ccdd4..6c9bd7f21 100644 --- a/OpenICF-ldap-connector/src/main/java/org/identityconnectors/ldap/sync/activedirectory/ActiveDirectoryChangeLogSyncStrategy.java +++ b/OpenICF-ldap-connector/src/main/java/org/identityconnectors/ldap/sync/activedirectory/ActiveDirectoryChangeLogSyncStrategy.java @@ -20,6 +20,8 @@ * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" + * + * Portions Copyrighted 2026 3A Systems LLC. */ package org.identityconnectors.ldap.sync.activedirectory; @@ -321,7 +323,7 @@ private String gethighestCommittedUSN() { Attributes attrs = conn.getInitialContext().getAttributes("", new String[]{HCU_CHANGED_ATTR}); hcUSN = getStringAttrValue(attrs, HCU_CHANGED_ATTR); if (hcUSN == null) { - String error = "Unable to read the highestCommittedUSN attribute" + String error = "Unable to read the highestCommittedUSN attribute " + "from the rootDSE of Active Directory "; throw new ConnectorException(error); } diff --git a/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/util/AttributeTypeUtil.java b/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/util/AttributeTypeUtil.java index b6bc1296b..8ec95a699 100644 --- a/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/util/AttributeTypeUtil.java +++ b/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/util/AttributeTypeUtil.java @@ -22,6 +22,8 @@ * "Portions Copyrighted 2010 [name of copyright owner]" * * $Id$ + * + * Portions Copyrighted 2026 3A Systems LLC. */ package org.forgerock.openicf.connectors.xml.util; @@ -44,47 +46,46 @@ public class AttributeTypeUtil { public static Object createInstantiatedObject(String attrValue, String javaclass) { if (javaclass.equals(XmlHandlerUtil.STRING)) { - String s = new String(attrValue); - return s; + return attrValue; } else if (javaclass.equals(XmlHandlerUtil.INT_PRIMITIVE)) { - int i = new Integer(attrValue); + int i = parseInt(attrValue); return i; } else if (javaclass.equals(XmlHandlerUtil.INTEGER)) { - Integer i = new Integer(attrValue); + Integer i = parseInt(attrValue); return i; } else if (javaclass.equals(XmlHandlerUtil.LONG)) { - Long l = new Long(attrValue); + Long l = parseLong(attrValue); return l; } else if (javaclass.equals(XmlHandlerUtil.LONG_PRIMITIVE)) { - long l = new Long(attrValue); + long l = parseLong(attrValue); return l; } else if (javaclass.equals(XmlHandlerUtil.BOOLEAN)) { - Boolean b = new Boolean(attrValue); + Boolean b = Boolean.valueOf(attrValue); return b; } else if (javaclass.equals(XmlHandlerUtil.BOOLEAN_PRIMITIVE)) { - boolean b = new Boolean(attrValue); + boolean b = Boolean.parseBoolean(attrValue); return b; } else if (javaclass.equals(XmlHandlerUtil.DOUBLE)) { - Double d = new Double(attrValue); + Double d = parseDouble(attrValue); return d; } else if (javaclass.equals(XmlHandlerUtil.DOUBLE_PRIMITIVE)) { - double d = new Double(attrValue); + double d = parseDouble(attrValue); return d; } else if (javaclass.equals(XmlHandlerUtil.FLOAT)) { - Float f = new Float(attrValue); + Float f = parseFloat(attrValue); return f; } else if (javaclass.equals(XmlHandlerUtil.FLOAT_PRIMITIVE)) { - float f = new Float(attrValue); + float f = parseFloat(attrValue); return f; } else if (javaclass.equals(XmlHandlerUtil.CHARACTER)) { @@ -120,6 +121,38 @@ else if (javaclass.equals(XmlHandlerUtil.BYTE_ARRAY)) { } } + private static int parseInt(String attrValue) { + try { + return Integer.parseInt(attrValue); + } catch (NumberFormatException e) { + throw new ConnectorException("Malformed int value in the XML: '" + attrValue + "'", e); + } + } + + private static long parseLong(String attrValue) { + try { + return Long.parseLong(attrValue); + } catch (NumberFormatException e) { + throw new ConnectorException("Malformed long value in the XML: '" + attrValue + "'", e); + } + } + + private static double parseDouble(String attrValue) { + try { + return Double.parseDouble(attrValue); + } catch (NumberFormatException e) { + throw new ConnectorException("Malformed double value in the XML: '" + attrValue + "'", e); + } + } + + private static float parseFloat(String attrValue) { + try { + return Float.parseFloat(attrValue); + } catch (NumberFormatException e) { + throw new ConnectorException("Malformed float value in the XML: '" + attrValue + "'", e); + } + } + public static List findAttributeValue(Attribute attr, AttributeInfo attrInfo) { Class javaClass = attrInfo.getType(); diff --git a/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/xsdparser/XSDAnnotationParser.java b/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/xsdparser/XSDAnnotationParser.java index e7e0661fd..70c29342b 100644 --- a/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/xsdparser/XSDAnnotationParser.java +++ b/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/xsdparser/XSDAnnotationParser.java @@ -71,7 +71,7 @@ public void characters(char[] chars, int start, int length) throws SAXException if(parse){ StringBuilder sb = new StringBuilder(); sb.append(chars, start, length); - if(!sb.toString().replace(" ", "").trim().equals("")){ + if(!sb.toString().replace(" ", "").trim().isEmpty()){ String stringToAppend = addValue + " " + sb.toString().trim(); stringBuilder.append(stringToAppend); stringBuilder.append("\n"); diff --git a/OpenICF-xml-connector/src/test/java/org/forgerock/openicf/connectors/xml/util/AttributeTypeUtilTests.java b/OpenICF-xml-connector/src/test/java/org/forgerock/openicf/connectors/xml/util/AttributeTypeUtilTests.java new file mode 100644 index 000000000..7b1fc891d --- /dev/null +++ b/OpenICF-xml-connector/src/test/java/org/forgerock/openicf/connectors/xml/util/AttributeTypeUtilTests.java @@ -0,0 +1,52 @@ +/* + * The contents of this file are subject to the terms of the Common Development and + * Distribution License (the License). You may not use this file except in compliance with the + * License. + * + * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + * specific language governing permission and limitations under the License. + * + * When distributing Covered Software, include this CDDL Header Notice in each file and include + * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + * Header, with the fields enclosed by brackets [] replaced by your own identifying + * information: "Portions copyright [year] [name of copyright owner]". + * + * Copyright 2026 3A Systems, LLC. + */ +package org.forgerock.openicf.connectors.xml.util; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +import org.identityconnectors.framework.common.exceptions.ConnectorException; +import org.testng.annotations.Test; + +public class AttributeTypeUtilTests { + + @Test + public void testValidNumericValuesAreParsed() { + assertEquals(AttributeTypeUtil.createInstantiatedObject("42", XmlHandlerUtil.INT_PRIMITIVE), 42); + assertEquals(AttributeTypeUtil.createInstantiatedObject("42", XmlHandlerUtil.INTEGER), Integer.valueOf(42)); + assertEquals(AttributeTypeUtil.createInstantiatedObject("42", XmlHandlerUtil.LONG), Long.valueOf(42L)); + assertEquals(AttributeTypeUtil.createInstantiatedObject("42", XmlHandlerUtil.LONG_PRIMITIVE), 42L); + assertEquals(AttributeTypeUtil.createInstantiatedObject("4.2", XmlHandlerUtil.DOUBLE), Double.valueOf(4.2)); + assertEquals(AttributeTypeUtil.createInstantiatedObject("4.2", XmlHandlerUtil.DOUBLE_PRIMITIVE), 4.2); + assertEquals(AttributeTypeUtil.createInstantiatedObject("4.2", XmlHandlerUtil.FLOAT), Float.valueOf(4.2f)); + assertEquals(AttributeTypeUtil.createInstantiatedObject("4.2", XmlHandlerUtil.FLOAT_PRIMITIVE), 4.2f); + } + + @Test + public void testMalformedNumericValueThrowsConnectorExceptionNotNumberFormatException() { + String[] javaClasses = { XmlHandlerUtil.INT_PRIMITIVE, XmlHandlerUtil.INTEGER, XmlHandlerUtil.LONG, + XmlHandlerUtil.LONG_PRIMITIVE, XmlHandlerUtil.DOUBLE, XmlHandlerUtil.DOUBLE_PRIMITIVE, + XmlHandlerUtil.FLOAT, XmlHandlerUtil.FLOAT_PRIMITIVE }; + for (String javaClass : javaClasses) { + try { + AttributeTypeUtil.createInstantiatedObject("not-a-number", javaClass); + fail("expected ConnectorException for javaClass " + javaClass); + } catch (ConnectorException e) { + // expected: the raw NumberFormatException must be wrapped, not propagated + } + } + } +} From 58f13f4e12f6915c58f293aaf2ffb3c0b74aeb86 Mon Sep 17 00:00:00 2001 From: Valera V Harseko Date: Mon, 21 Sep 2026 15:30:21 +0300 Subject: [PATCH 2/4] Drop fixes duplicated by #134 AttributeTypeUtil.java, MultiOpTests.java, PrettyStringBuilder.java, StringUtil.java, ActiveDirectoryChangeLogSyncStrategy.java and XSDAnnotationParser.java were independently fixed here and in #134 with byte-identical diffs; reverting them here to avoid merging the same change twice and to let #134 own them. Drops AttributeTypeUtilTests.java too since it exercises the NumberFormatException-wrapping behavior that lived in the now-reverted AttributeTypeUtil.java (still present in #134, just not in this PR anymore). --- .../contract/test/MultiOpTests.java | 4 +- .../common/PrettyStringBuilder.java | 9 ++- .../identityconnectors/common/StringUtil.java | 2 +- .../ActiveDirectoryChangeLogSyncStrategy.java | 4 +- .../xml/util/AttributeTypeUtil.java | 57 ++++--------------- .../xml/xsdparser/XSDAnnotationParser.java | 2 +- .../xml/util/AttributeTypeUtilTests.java | 52 ----------------- 7 files changed, 20 insertions(+), 110 deletions(-) delete mode 100644 OpenICF-xml-connector/src/test/java/org/forgerock/openicf/connectors/xml/util/AttributeTypeUtilTests.java diff --git a/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/MultiOpTests.java b/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/MultiOpTests.java index d948209aa..dabb83b72 100644 --- a/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/MultiOpTests.java +++ b/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/MultiOpTests.java @@ -21,7 +21,6 @@ * ==================== * * Portions Copyrighted 2012 ForgeRock AS - * Portions Copyrighted 2026 3A Systems LLC. * */ package org.identityconnectors.contract.test; @@ -577,12 +576,11 @@ public void testPasswordChangeIntervalPredAttribute(ObjectClass objectClass) { /** * Method to check the attrName's attribute contract * - * @param objectClass object class under test * @param attrName attribute to be checked * @param createValue value used for create * @param updateValue value used for update * @param type expected type of the value - * @param addPassword add password to attributes in the update + * @param addPassword, add password to attributes in the update */ private void checkOpAttribute(ObjectClass objectClass, String attrName, Object createValue, Object updateValue, Class type, boolean addPassword) { final int iteration = 0; diff --git a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/PrettyStringBuilder.java b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/PrettyStringBuilder.java index 252a8e1c3..4477f8c50 100644 --- a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/PrettyStringBuilder.java +++ b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/PrettyStringBuilder.java @@ -20,7 +20,6 @@ * "Portions Copyrighted [year] [name of copyright owner]" * ==================== * Portions Copyrighted 2015 ForgeRock AS. - * Portions Copyrighted 2026 3A Systems LLC. */ package org.identityconnectors.common; @@ -97,13 +96,13 @@ protected String toPrettyString(final Object obj) { s.append(')'); } else if (obj instanceof Map) { final Map map = (Map) obj; - final Iterator it = map.entrySet().iterator(); + final Iterator it = map.keySet().iterator(); int i = 0; s.append('{'); while ((it.hasNext() && (i++ < maxArrayLen))) { - final Map.Entry entry = (Map.Entry) it.next(); - s.append(entry.getKey()).append(':'); - s.append(toPrettyString(entry.getValue())); + final Object key = it.next(); + s.append(key).append(':'); + s.append(toPrettyString(map.get(key))); if (it.hasNext()) { s.append(","); } diff --git a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/StringUtil.java b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/StringUtil.java index ea73ee0ed..d17023730 100644 --- a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/StringUtil.java +++ b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/StringUtil.java @@ -285,7 +285,7 @@ public static int indexOf(final String src, final char[] ch, final int idx) { * @return true if the string is empty else false. */ public static boolean isEmpty(final String val) { - return (val == null) ? true : val.isEmpty(); + return (val == null) ? true : "".equals(val) ? true : false; } /** diff --git a/OpenICF-ldap-connector/src/main/java/org/identityconnectors/ldap/sync/activedirectory/ActiveDirectoryChangeLogSyncStrategy.java b/OpenICF-ldap-connector/src/main/java/org/identityconnectors/ldap/sync/activedirectory/ActiveDirectoryChangeLogSyncStrategy.java index 6c9bd7f21..b9b6ccdd4 100644 --- a/OpenICF-ldap-connector/src/main/java/org/identityconnectors/ldap/sync/activedirectory/ActiveDirectoryChangeLogSyncStrategy.java +++ b/OpenICF-ldap-connector/src/main/java/org/identityconnectors/ldap/sync/activedirectory/ActiveDirectoryChangeLogSyncStrategy.java @@ -20,8 +20,6 @@ * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * - * Portions Copyrighted 2026 3A Systems LLC. */ package org.identityconnectors.ldap.sync.activedirectory; @@ -323,7 +321,7 @@ private String gethighestCommittedUSN() { Attributes attrs = conn.getInitialContext().getAttributes("", new String[]{HCU_CHANGED_ATTR}); hcUSN = getStringAttrValue(attrs, HCU_CHANGED_ATTR); if (hcUSN == null) { - String error = "Unable to read the highestCommittedUSN attribute " + String error = "Unable to read the highestCommittedUSN attribute" + "from the rootDSE of Active Directory "; throw new ConnectorException(error); } diff --git a/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/util/AttributeTypeUtil.java b/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/util/AttributeTypeUtil.java index 8ec95a699..b6bc1296b 100644 --- a/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/util/AttributeTypeUtil.java +++ b/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/util/AttributeTypeUtil.java @@ -22,8 +22,6 @@ * "Portions Copyrighted 2010 [name of copyright owner]" * * $Id$ - * - * Portions Copyrighted 2026 3A Systems LLC. */ package org.forgerock.openicf.connectors.xml.util; @@ -46,46 +44,47 @@ public class AttributeTypeUtil { public static Object createInstantiatedObject(String attrValue, String javaclass) { if (javaclass.equals(XmlHandlerUtil.STRING)) { - return attrValue; + String s = new String(attrValue); + return s; } else if (javaclass.equals(XmlHandlerUtil.INT_PRIMITIVE)) { - int i = parseInt(attrValue); + int i = new Integer(attrValue); return i; } else if (javaclass.equals(XmlHandlerUtil.INTEGER)) { - Integer i = parseInt(attrValue); + Integer i = new Integer(attrValue); return i; } else if (javaclass.equals(XmlHandlerUtil.LONG)) { - Long l = parseLong(attrValue); + Long l = new Long(attrValue); return l; } else if (javaclass.equals(XmlHandlerUtil.LONG_PRIMITIVE)) { - long l = parseLong(attrValue); + long l = new Long(attrValue); return l; } else if (javaclass.equals(XmlHandlerUtil.BOOLEAN)) { - Boolean b = Boolean.valueOf(attrValue); + Boolean b = new Boolean(attrValue); return b; } else if (javaclass.equals(XmlHandlerUtil.BOOLEAN_PRIMITIVE)) { - boolean b = Boolean.parseBoolean(attrValue); + boolean b = new Boolean(attrValue); return b; } else if (javaclass.equals(XmlHandlerUtil.DOUBLE)) { - Double d = parseDouble(attrValue); + Double d = new Double(attrValue); return d; } else if (javaclass.equals(XmlHandlerUtil.DOUBLE_PRIMITIVE)) { - double d = parseDouble(attrValue); + double d = new Double(attrValue); return d; } else if (javaclass.equals(XmlHandlerUtil.FLOAT)) { - Float f = parseFloat(attrValue); + Float f = new Float(attrValue); return f; } else if (javaclass.equals(XmlHandlerUtil.FLOAT_PRIMITIVE)) { - float f = parseFloat(attrValue); + float f = new Float(attrValue); return f; } else if (javaclass.equals(XmlHandlerUtil.CHARACTER)) { @@ -121,38 +120,6 @@ else if (javaclass.equals(XmlHandlerUtil.BYTE_ARRAY)) { } } - private static int parseInt(String attrValue) { - try { - return Integer.parseInt(attrValue); - } catch (NumberFormatException e) { - throw new ConnectorException("Malformed int value in the XML: '" + attrValue + "'", e); - } - } - - private static long parseLong(String attrValue) { - try { - return Long.parseLong(attrValue); - } catch (NumberFormatException e) { - throw new ConnectorException("Malformed long value in the XML: '" + attrValue + "'", e); - } - } - - private static double parseDouble(String attrValue) { - try { - return Double.parseDouble(attrValue); - } catch (NumberFormatException e) { - throw new ConnectorException("Malformed double value in the XML: '" + attrValue + "'", e); - } - } - - private static float parseFloat(String attrValue) { - try { - return Float.parseFloat(attrValue); - } catch (NumberFormatException e) { - throw new ConnectorException("Malformed float value in the XML: '" + attrValue + "'", e); - } - } - public static List findAttributeValue(Attribute attr, AttributeInfo attrInfo) { Class javaClass = attrInfo.getType(); diff --git a/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/xsdparser/XSDAnnotationParser.java b/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/xsdparser/XSDAnnotationParser.java index 70c29342b..e7e0661fd 100644 --- a/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/xsdparser/XSDAnnotationParser.java +++ b/OpenICF-xml-connector/src/main/java/org/forgerock/openicf/connectors/xml/xsdparser/XSDAnnotationParser.java @@ -71,7 +71,7 @@ public void characters(char[] chars, int start, int length) throws SAXException if(parse){ StringBuilder sb = new StringBuilder(); sb.append(chars, start, length); - if(!sb.toString().replace(" ", "").trim().isEmpty()){ + if(!sb.toString().replace(" ", "").trim().equals("")){ String stringToAppend = addValue + " " + sb.toString().trim(); stringBuilder.append(stringToAppend); stringBuilder.append("\n"); diff --git a/OpenICF-xml-connector/src/test/java/org/forgerock/openicf/connectors/xml/util/AttributeTypeUtilTests.java b/OpenICF-xml-connector/src/test/java/org/forgerock/openicf/connectors/xml/util/AttributeTypeUtilTests.java deleted file mode 100644 index 7b1fc891d..000000000 --- a/OpenICF-xml-connector/src/test/java/org/forgerock/openicf/connectors/xml/util/AttributeTypeUtilTests.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * The contents of this file are subject to the terms of the Common Development and - * Distribution License (the License). You may not use this file except in compliance with the - * License. - * - * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the - * specific language governing permission and limitations under the License. - * - * When distributing Covered Software, include this CDDL Header Notice in each file and include - * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL - * Header, with the fields enclosed by brackets [] replaced by your own identifying - * information: "Portions copyright [year] [name of copyright owner]". - * - * Copyright 2026 3A Systems, LLC. - */ -package org.forgerock.openicf.connectors.xml.util; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; - -import org.identityconnectors.framework.common.exceptions.ConnectorException; -import org.testng.annotations.Test; - -public class AttributeTypeUtilTests { - - @Test - public void testValidNumericValuesAreParsed() { - assertEquals(AttributeTypeUtil.createInstantiatedObject("42", XmlHandlerUtil.INT_PRIMITIVE), 42); - assertEquals(AttributeTypeUtil.createInstantiatedObject("42", XmlHandlerUtil.INTEGER), Integer.valueOf(42)); - assertEquals(AttributeTypeUtil.createInstantiatedObject("42", XmlHandlerUtil.LONG), Long.valueOf(42L)); - assertEquals(AttributeTypeUtil.createInstantiatedObject("42", XmlHandlerUtil.LONG_PRIMITIVE), 42L); - assertEquals(AttributeTypeUtil.createInstantiatedObject("4.2", XmlHandlerUtil.DOUBLE), Double.valueOf(4.2)); - assertEquals(AttributeTypeUtil.createInstantiatedObject("4.2", XmlHandlerUtil.DOUBLE_PRIMITIVE), 4.2); - assertEquals(AttributeTypeUtil.createInstantiatedObject("4.2", XmlHandlerUtil.FLOAT), Float.valueOf(4.2f)); - assertEquals(AttributeTypeUtil.createInstantiatedObject("4.2", XmlHandlerUtil.FLOAT_PRIMITIVE), 4.2f); - } - - @Test - public void testMalformedNumericValueThrowsConnectorExceptionNotNumberFormatException() { - String[] javaClasses = { XmlHandlerUtil.INT_PRIMITIVE, XmlHandlerUtil.INTEGER, XmlHandlerUtil.LONG, - XmlHandlerUtil.LONG_PRIMITIVE, XmlHandlerUtil.DOUBLE, XmlHandlerUtil.DOUBLE_PRIMITIVE, - XmlHandlerUtil.FLOAT, XmlHandlerUtil.FLOAT_PRIMITIVE }; - for (String javaClass : javaClasses) { - try { - AttributeTypeUtil.createInstantiatedObject("not-a-number", javaClass); - fail("expected ConnectorException for javaClass " + javaClass); - } catch (ConnectorException e) { - // expected: the raw NumberFormatException must be wrapped, not propagated - } - } - } -} From 75b60b2d8753223bc6b007005d30f22cae62755f Mon Sep 17 00:00:00 2001 From: Valera V Harseko Date: Fri, 2 Oct 2026 14:47:21 +0300 Subject: [PATCH 3/4] Pin toString() against the secret and extend it to GuardedByteArray - Replace the GuardedString toString() test, which passed with the override removed, by one asserting that two different secrets print the same string. - GuardedByteArray now overrides toString() too: the inherited Object.toString() printed hashCode(), derived from the SHA-1 hash of the clear bytes. - Reword the Javadoc to say why the override matters: the inherited output carried a fingerprint of the secret. --- .../common/security/GuardedByteArray.java | 10 ++++++++++ .../common/security/GuardedString.java | 5 +++-- .../common/security/GuardedByteArrayTests.java | 10 ++++++++++ .../common/security/GuardedStringTests.java | 9 +++++---- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/security/GuardedByteArray.java b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/security/GuardedByteArray.java index 7dea6ec91..2a824fd1a 100644 --- a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/security/GuardedByteArray.java +++ b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/security/GuardedByteArray.java @@ -270,4 +270,14 @@ public boolean equals(Object o) { public int hashCode() { return base64SHA1Hash.hashCode(); } + + /** + * Never prints the clear bytes, nor anything derived from them: the inherited + * {@link Object#toString()} would print {@link #hashCode()}, which is computed + * from the SHA-1 hash of the clear bytes. + */ + @Override + public String toString() { + return "GuardedByteArray(...)"; + } } diff --git a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/security/GuardedString.java b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/security/GuardedString.java index 118d0c487..58ebef822 100644 --- a/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/security/GuardedString.java +++ b/OpenICF-java-framework/connector-framework/src/main/java/org/identityconnectors/common/security/GuardedString.java @@ -293,8 +293,9 @@ public int hashCode() { } /** - * Never prints the clear text; the default {@link Object#toString()} would not either, - * but its output is just a class name and hash code, not useful for logging. + * Never prints the clear text, nor anything derived from it: the inherited + * {@link Object#toString()} would print {@link #hashCode()}, which is computed + * from the SHA-1 hash of the clear text. */ @Override public String toString() { diff --git a/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/common/security/GuardedByteArrayTests.java b/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/common/security/GuardedByteArrayTests.java index 328e9f6a1..5a71049a9 100644 --- a/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/common/security/GuardedByteArrayTests.java +++ b/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/common/security/GuardedByteArrayTests.java @@ -19,6 +19,8 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * + * Portions Copyright 2026 3A Systems, LLC. */ package org.identityconnectors.common.security; @@ -122,6 +124,14 @@ public void testDispose() { } } + @Test + public void testToStringDoesNotDependOnTheSecret() { + GuardedByteArray first = new GuardedByteArray(new byte[] { 0x00, 0x01, 0x02 }); + GuardedByteArray second = new GuardedByteArray(new byte[] { 0x03, 0x04 }); + assertEquals(first.toString(), second.toString(), + "toString() must not carry anything derived from the clear bytes"); + } + @Test public void testRange() { for (int i = -128; i < 128; i++) { diff --git a/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/common/security/GuardedStringTests.java b/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/common/security/GuardedStringTests.java index 17e0d526f..a489f43e6 100644 --- a/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/common/security/GuardedStringTests.java +++ b/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/common/security/GuardedStringTests.java @@ -124,10 +124,11 @@ public void testDispose() { } @Test - public void testToStringNeverExposesTheClearText() { - GuardedString str = new GuardedString("secret".toCharArray()); - assertFalse(str.toString().contains("secret"), - "toString() must never leak the clear text"); + public void testToStringDoesNotDependOnTheSecret() { + GuardedString first = new GuardedString("secret".toCharArray()); + GuardedString second = new GuardedString("other".toCharArray()); + assertEquals(first.toString(), second.toString(), + "toString() must not carry anything derived from the clear text"); } @Test From b9e0f90fa75a031c85c3d97269719a14f94bddc6 Mon Sep 17 00:00:00 2001 From: Valera V Harseko Date: Sun, 4 Oct 2026 11:53:59 +0300 Subject: [PATCH 4/4] Pin the exact toString() output, leave ContractITCase to #132, align the 3A copyright lines --- .../contract/test/ContractITCase.java | 15 +++++++++++++-- .../common/security/GuardedByteArrayTests.java | 3 ++- .../common/security/GuardedStringTests.java | 3 ++- .../ldap/search/LdapInternalSearch.java | 2 +- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/ContractITCase.java b/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/ContractITCase.java index a69f064c4..398cef046 100644 --- a/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/ContractITCase.java +++ b/OpenICF-java-framework/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/ContractITCase.java @@ -20,8 +20,6 @@ * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" - * - * Portions Copyrighted 2026 3A Systems LLC. */ package org.identityconnectors.contract.test; @@ -34,9 +32,12 @@ import org.identityconnectors.common.StringUtil; import org.identityconnectors.contract.data.DataProvider; +import org.identityconnectors.framework.api.ConnectorFacade; +import org.identityconnectors.framework.common.objects.Schema; import org.testng.IObjectFactory; import org.testng.ITestContext; import org.testng.annotations.Factory; +import org.testng.internal.ObjectFactoryImpl; import com.google.inject.Guice; import com.google.inject.Injector; @@ -110,4 +111,14 @@ public Injector getInjector(ITestContext context) { public DataProvider getDataProvider(ITestContext context) { return ConnectorHelper.createDataProvider(); } + + private static class ContractTestFactory { + + private ConnectorFacade connectorFacade = null; + + private Schema schema = null; + + private IObjectFactory objectFactory = new ObjectFactoryImpl(); + + } } diff --git a/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/common/security/GuardedByteArrayTests.java b/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/common/security/GuardedByteArrayTests.java index 5a71049a9..668fce8e2 100644 --- a/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/common/security/GuardedByteArrayTests.java +++ b/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/common/security/GuardedByteArrayTests.java @@ -20,7 +20,7 @@ * "Portions Copyrighted [year] [name of copyright owner]" * ==================== * - * Portions Copyright 2026 3A Systems, LLC. + * Portions Copyrighted 2026 3A Systems, LLC */ package org.identityconnectors.common.security; @@ -130,6 +130,7 @@ public void testToStringDoesNotDependOnTheSecret() { GuardedByteArray second = new GuardedByteArray(new byte[] { 0x03, 0x04 }); assertEquals(first.toString(), second.toString(), "toString() must not carry anything derived from the clear bytes"); + assertEquals(first.toString(), "GuardedByteArray(...)"); } @Test diff --git a/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/common/security/GuardedStringTests.java b/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/common/security/GuardedStringTests.java index a489f43e6..a47596bcd 100644 --- a/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/common/security/GuardedStringTests.java +++ b/OpenICF-java-framework/connector-framework/src/test/java/org/identityconnectors/common/security/GuardedStringTests.java @@ -20,7 +20,7 @@ * "Portions Copyrighted [year] [name of copyright owner]" * ==================== * - * Portions Copyrighted 2026 3A Systems LLC. + * Portions Copyrighted 2026 3A Systems, LLC */ package org.identityconnectors.common.security; @@ -129,6 +129,7 @@ public void testToStringDoesNotDependOnTheSecret() { GuardedString second = new GuardedString("other".toCharArray()); assertEquals(first.toString(), second.toString(), "toString() must not carry anything derived from the clear text"); + assertEquals(first.toString(), "GuardedString(...)"); } @Test diff --git a/OpenICF-ldap-connector/src/main/java/org/identityconnectors/ldap/search/LdapInternalSearch.java b/OpenICF-ldap-connector/src/main/java/org/identityconnectors/ldap/search/LdapInternalSearch.java index 6757669d4..84aa625ad 100644 --- a/OpenICF-ldap-connector/src/main/java/org/identityconnectors/ldap/search/LdapInternalSearch.java +++ b/OpenICF-ldap-connector/src/main/java/org/identityconnectors/ldap/search/LdapInternalSearch.java @@ -20,7 +20,7 @@ * "Portions Copyrighted [year] [name of copyright owner]" * ==================== * "Portions Copyrighted 2014 ForgeRock AS" - * Portions Copyrighted 2026 3A Systems LLC. + * Portions Copyrighted 2026 3A Systems, LLC */ package org.identityconnectors.ldap.search;