diff --git a/extensions/instagram/src/main/java/app/revanced/extension/instagram/misc/privacy/SanitizeSharingLinksPatch.java b/extensions/instagram/src/main/java/app/revanced/extension/instagram/misc/privacy/SanitizeSharingLinksPatch.java new file mode 100644 index 000000000..b7a017e77 --- /dev/null +++ b/extensions/instagram/src/main/java/app/revanced/extension/instagram/misc/privacy/SanitizeSharingLinksPatch.java @@ -0,0 +1,15 @@ +package app.revanced.extension.instagram.misc.privacy; + +import app.revanced.extension.shared.privacy.LinkSanitizer; + +@SuppressWarnings("unused") +public final class SanitizeSharingLinksPatch { + private static final LinkSanitizer sanitizer = new LinkSanitizer("igsh"); + + /** + * Injection point. + */ + public static String sanitizeSharingLink(String url) { + return sanitizer.sanitizeUrlString(url); + } +} diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/SanitizeSharingLinksPatch.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/SanitizeSharingLinksPatch.java index 6952bdcd1..d02899727 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/SanitizeSharingLinksPatch.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/SanitizeSharingLinksPatch.java @@ -1,5 +1,6 @@ package app.revanced.extension.shared.patches; +import app.revanced.extension.shared.privacy.LinkSanitizer; import app.revanced.extension.shared.settings.BaseSettings; /** @@ -7,17 +8,18 @@ import app.revanced.extension.shared.settings.BaseSettings; */ @SuppressWarnings("unused") public final class SanitizeSharingLinksPatch { - private static final String NEW_TRACKING_PARAMETER_REGEX = ".si=.+"; - private static final String OLD_TRACKING_PARAMETER_REGEX = ".feature=.+"; + + private static final LinkSanitizer sanitizer = new LinkSanitizer( + "si", + "feature" // Old tracking parameter name, and may be obsolete. + ); /** * Injection point. */ public static String sanitize(String url) { if (BaseSettings.SANITIZE_SHARED_LINKS.get()) { - url = url - .replaceAll(NEW_TRACKING_PARAMETER_REGEX, "") - .replaceAll(OLD_TRACKING_PARAMETER_REGEX, ""); + url = sanitizer.sanitizeUrlString(url); } if (BaseSettings.REPLACE_MUSIC_LINKS_WITH_YOUTUBE.get()) { diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/privacy/LinkSanitizer.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/privacy/LinkSanitizer.java new file mode 100644 index 000000000..9cfa05c1b --- /dev/null +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/privacy/LinkSanitizer.java @@ -0,0 +1,60 @@ +package app.revanced.extension.shared.privacy; + +import android.net.Uri; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import app.revanced.extension.shared.Logger; + +/** + * Strips away specific parameters from URLs. + */ +public class LinkSanitizer { + + private final Collection parametersToRemove; + + public LinkSanitizer(String ... parametersToRemove) { + final int parameterCount = parametersToRemove.length; + if (parameterCount == 0) { + throw new IllegalArgumentException("No parameters specified"); + } + + // List is faster if only checking a few parameters. + this.parametersToRemove = parameterCount > 4 + ? Set.of(parametersToRemove) + : List.of(parametersToRemove); + } + + public String sanitizeUrlString(String url) { + try { + return sanitizeUri(Uri.parse(url)).toString(); + } catch (Exception ex) { + Logger.printException(() -> "sanitizeUrlString failure: " + url, ex); + return url; + } + } + + public Uri sanitizeUri(Uri uri) { + try { + Uri.Builder builder = uri.buildUpon().clearQuery(); + + for (String paramName : uri.getQueryParameterNames()) { + if (!parametersToRemove.contains(paramName)) { + for (String value : uri.getQueryParameters(paramName)) { + builder.appendQueryParameter(paramName, value); + } + } + } + + Uri sanitizedUrl = builder.build(); + Logger.printInfo(() -> "Sanitized url: " + uri + " to: " + sanitizedUrl); + + return sanitizedUrl; + } catch (Exception ex) { + Logger.printException(() -> "sanitizeUri failure: " + uri, ex); + return uri; + } + } +} diff --git a/extensions/spotify/src/main/java/app/revanced/extension/spotify/misc/privacy/SanitizeSharingLinksPatch.java b/extensions/spotify/src/main/java/app/revanced/extension/spotify/misc/privacy/SanitizeSharingLinksPatch.java index 55b78933d..61cb977a2 100644 --- a/extensions/spotify/src/main/java/app/revanced/extension/spotify/misc/privacy/SanitizeSharingLinksPatch.java +++ b/extensions/spotify/src/main/java/app/revanced/extension/spotify/misc/privacy/SanitizeSharingLinksPatch.java @@ -1,18 +1,11 @@ package app.revanced.extension.spotify.misc.privacy; -import android.net.Uri; - -import java.util.List; - -import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.privacy.LinkSanitizer; @SuppressWarnings("unused") public final class SanitizeSharingLinksPatch { - /** - * Parameters that are considered undesirable and should be stripped away. - */ - private static final List SHARE_PARAMETERS_TO_REMOVE = List.of( + private static final LinkSanitizer sanitizer = new LinkSanitizer( "si", // Share tracking parameter. "utm_source" // Share source, such as "copy-link". ); @@ -20,25 +13,7 @@ public final class SanitizeSharingLinksPatch { /** * Injection point. */ - public static String sanitizeUrl(String url) { - try { - Uri uri = Uri.parse(url); - Uri.Builder builder = uri.buildUpon().clearQuery(); - - for (String paramName : uri.getQueryParameterNames()) { - if (!SHARE_PARAMETERS_TO_REMOVE.contains(paramName)) { - for (String value : uri.getQueryParameters(paramName)) { - builder.appendQueryParameter(paramName, value); - } - } - } - - String sanitizedUrl = builder.build().toString(); - Logger.printInfo(() -> "Sanitized url " + url + " to " + sanitizedUrl); - return sanitizedUrl; - } catch (Exception ex) { - Logger.printException(() -> "sanitizeUrl failure with " + url, ex); - return url; - } + public static String sanitizeSharingLink(String url) { + return sanitizer.sanitizeUrlString(url); } } diff --git a/patches/api/patches.api b/patches/api/patches.api index 01e889e44..806d7a46e 100644 --- a/patches/api/patches.api +++ b/patches/api/patches.api @@ -288,6 +288,10 @@ public final class app/revanced/patches/instagram/misc/links/OpenLinksExternally public static final fun getOpenLinksExternallyPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } +public final class app/revanced/patches/instagram/misc/privacy/SanitizeSharingLinksPatchKt { + public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + public final class app/revanced/patches/instagram/misc/signature/SignatureCheckPatchKt { public static final fun getSignatureCheckPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/privacy/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/privacy/Fingerprints.kt new file mode 100644 index 000000000..78ac35f85 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/privacy/Fingerprints.kt @@ -0,0 +1,23 @@ +package app.revanced.patches.instagram.misc.privacy + +import app.revanced.patcher.fingerprint + +internal val permalinkResponseJsonParserFingerprint = fingerprint { + strings("permalink", "PermalinkResponse") + custom { method, _ -> method.name == "parseFromJson" } +} + +internal val storyUrlResponseJsonParserFingerprint = fingerprint { + strings("story_item_to_share_url", "StoryItemUrlResponse") + custom { method, _ -> method.name == "parseFromJson" } +} + +internal val profileUrlResponseJsonParserFingerprint = fingerprint { + strings("profile_to_share_url", "ProfileUrlResponse") + custom { method, _ -> method.name == "parseFromJson" } +} + +internal val liveUrlResponseJsonParserFingerprint = fingerprint { + strings("live_to_share_url", "LiveItemLinkUrlResponse") + custom { method, _ -> method.name == "parseFromJson" } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/privacy/SanitizeSharingLinksPatch.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/privacy/SanitizeSharingLinksPatch.kt new file mode 100644 index 000000000..c7b4d0f40 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/privacy/SanitizeSharingLinksPatch.kt @@ -0,0 +1,48 @@ +package app.revanced.patches.instagram.misc.privacy + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.instagram.misc.extension.sharedExtensionPatch +import app.revanced.util.indexOfFirstInstructionOrThrow +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/instagram/misc/privacy/SanitizeSharingLinksPatch;" + +@Suppress("unused") +val sanitizeSharingLinksPatch = bytecodePatch( + name = "Sanitize sharing links", + description = "Removes the tracking query parameters from shared links.", +) { + compatibleWith("com.instagram.android") + + dependsOn(sharedExtensionPatch) + + execute { + arrayOf( + permalinkResponseJsonParserFingerprint, + storyUrlResponseJsonParserFingerprint, + profileUrlResponseJsonParserFingerprint, + liveUrlResponseJsonParserFingerprint + ).forEach { fingerprint -> + fingerprint.method.apply { + val putSharingUrlIndex = indexOfFirstInstructionOrThrow( + fingerprint.stringMatches!!.first().index, + Opcode.IPUT_OBJECT + ) + + val sharingUrlRegister = getInstruction(putSharingUrlIndex).registerA + + addInstructions( + putSharingUrlIndex, + """ + invoke-static { v$sharingUrlRegister }, $EXTENSION_CLASS_DESCRIPTOR->sanitizeSharingLink(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$sharingUrlRegister + """ + ) + } + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/misc/tracking/url/SanitizeUrlQueryPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/misc/tracking/url/SanitizeUrlQueryPatch.kt index 26ed42660..cfdb320e1 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/misc/tracking/url/SanitizeUrlQueryPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/misc/tracking/url/SanitizeUrlQueryPatch.kt @@ -6,7 +6,7 @@ import app.revanced.patcher.patch.bytecodePatch @Suppress("unused") val sanitizeUrlQueryPatch = bytecodePatch( name = "Sanitize sharing links", - description = "Removes (tracking) query parameters from the URLs when sharing links.", + description = "Removes the tracking query parameters from shared links.", ) { compatibleWith("com.reddit.frontpage") diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/privacy/SanitizeSharingLinksPatch.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/privacy/SanitizeSharingLinksPatch.kt index ff74565c6..0954d4ed5 100644 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/privacy/SanitizeSharingLinksPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/privacy/SanitizeSharingLinksPatch.kt @@ -16,7 +16,7 @@ private const val EXTENSION_CLASS_DESCRIPTOR = @Suppress("unused") val sanitizeSharingLinksPatch = bytecodePatch( name = "Sanitize sharing links", - description = "Removes the tracking query parameters from links before they are shared.", + description = "Removes the tracking query parameters from shared links.", ) { compatibleWith("com.spotify.music") @@ -24,7 +24,7 @@ val sanitizeSharingLinksPatch = bytecodePatch( execute { val extensionMethodDescriptor = "$EXTENSION_CLASS_DESCRIPTOR->" + - "sanitizeUrl(Ljava/lang/String;)Ljava/lang/String;" + "sanitizeSharingLink(Ljava/lang/String;)Ljava/lang/String;" val copyFingerprint = if (shareCopyUrlFingerprint.originalMethodOrNull != null) { shareCopyUrlFingerprint