diff --git a/src/main/kotlin/org/jitsi/xmpp/util/RedactColibriIp.kt b/src/main/kotlin/org/jitsi/xmpp/util/RedactColibriIp.kt
new file mode 100644
index 0000000..b6ee6de
--- /dev/null
+++ b/src/main/kotlin/org/jitsi/xmpp/util/RedactColibriIp.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright @ 2023 - present 8x8, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jitsi.xmpp.util
+
+import java.io.StringReader
+import java.io.StringWriter
+import javax.xml.XMLConstants
+import javax.xml.transform.Templates
+import javax.xml.transform.TransformerFactory
+import javax.xml.transform.stream.StreamResult
+import javax.xml.transform.stream.StreamSource
+
+class RedactColibriIp {
+ companion object {
+ private val redactXslt =
+ """
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ """
+
+ private val factory: TransformerFactory by lazy {
+ TransformerFactory.newInstance().also {
+ it.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true)
+ }
+ }
+ private val templates: Templates by lazy {
+ factory.newTemplates(
+ StreamSource(StringReader(redactXslt))
+ )
+ }
+
+ fun redact(input: String): String {
+ val source = StreamSource(StringReader(input))
+ val writer = StringWriter()
+ val result = StreamResult(writer)
+ val transformer = templates.newTransformer()
+ transformer.transform(source, result)
+ return writer.toString()
+ }
+ }
+}
diff --git a/src/test/kotlin/org/jitsi/xmpp/util/RedactColibriIpTest.kt b/src/test/kotlin/org/jitsi/xmpp/util/RedactColibriIpTest.kt
new file mode 100644
index 0000000..69f66e1
--- /dev/null
+++ b/src/test/kotlin/org/jitsi/xmpp/util/RedactColibriIpTest.kt
@@ -0,0 +1,252 @@
+/*
+ * Copyright @ 2021 - present 8x8, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jitsi.xmpp.util
+
+import io.kotest.assertions.asClue
+import io.kotest.core.spec.style.ShouldSpec
+import io.kotest.matchers.shouldBe
+import org.jitsi.xmpp.util.RedactColibriIp.Companion.redact
+import org.xmlunit.builder.DiffBuilder
+
+class RedactColibriIpTest : ShouldSpec() {
+ init {
+ context("Redacting an IPv4 address from a Colibri message") {
+ val sourceXml =
+ """
+
+
+
+
+
+ 31:D8:D0:A2:E8:2F:A6:43:5D:0C:69:BE:23:7A:0C:B1:4D:22:56:18:0B:5D:79:78:F6:E2:71:62:2E:46:45:E9
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ """
+
+ val expectedXml =
+ """
+
+
+
+
+
+ 31:D8:D0:A2:E8:2F:A6:43:5D:0C:69:BE:23:7A:0C:B1:4D:22:56:18:0B:5D:79:78:F6:E2:71:62:2E:46:45:E9
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ """
+
+ val redacted = redact(sourceXml)
+
+ should("Convert to expected xml") {
+ val diff = DiffBuilder.compare(expectedXml).withTest(redacted)
+ .ignoreWhitespace()
+ .checkForIdentical().build()
+
+ diff.asClue {
+ diff.hasDifferences() shouldBe false
+ }
+ }
+ }
+
+ context("Redacting IPv6 addresses from a Colibri message") {
+ val sourceXml =
+ """
+
+
+
+
+
+
+
+ """
+ val expectedXml =
+ """
+
+
+
+
+
+
+
+ """
+ val redacted = redact(sourceXml)
+
+ should("Convert to expected xml") {
+ val diff = DiffBuilder.compare(expectedXml).withTest(redacted)
+ .ignoreWhitespace()
+ .checkForIdentical().build()
+
+ diff.asClue {
+ diff.hasDifferences() shouldBe false
+ }
+ }
+ }
+
+ context("Turn candidates") {
+ val sourceXml = """
+
+
+
+
+
+
+
+ """
+
+ val redacted = redact(sourceXml)
+
+ should("Be unchanged") {
+ val diff = DiffBuilder.compare(sourceXml).withTest(redacted)
+ .ignoreWhitespace()
+ .checkForIdentical().build()
+
+ diff.asClue {
+ diff.hasDifferences() shouldBe false
+ }
+ }
+ }
+ context("Redacting related addresses") {
+ val sourceXml = """
+
+
+
+
+
+
+ """
+ val expectedXml = """
+
+
+
+
+
+
+ """
+ val redacted = redact(sourceXml)
+
+ should("Convert to expected xml") {
+ val diff = DiffBuilder.compare(expectedXml).withTest(redacted)
+ .ignoreWhitespace()
+ .checkForIdentical().build()
+
+ diff.asClue {
+ diff.hasDifferences() shouldBe false
+ }
+ }
+ }
+ context("Candidates of relays") {
+ val sourceXml = """
+
+
+
+
+
+
+ E3:47:32:6B:70:67:56:EB:05:B3:B8:E2:95:E6:AD:A4:B7:5A:8D:CA:11:DF:5B:11:69:7C:C9:6E:4D:0B:E2:F4
+
+
+
+
+
+
+
+ """
+ val redacted = redact(sourceXml)
+
+ should("Be unchanged") {
+ val diff = DiffBuilder.compare(sourceXml).withTest(redacted)
+ .ignoreWhitespace()
+ .checkForIdentical().build()
+
+ diff.asClue {
+ diff.hasDifferences() shouldBe false
+ }
+ }
+ }
+ }
+}