fix(Spotify - Sanitize sharing links): Bring back original logic
Close https://github.com/anddea/revanced-patches/issues/1244
This commit is contained in:
parent
fd42fc54b1
commit
40a14f46aa
4 changed files with 72 additions and 139 deletions
|
|
@ -1,38 +1,36 @@
|
|||
package app.revanced.extension.spotify.misc.privacy;
|
||||
|
||||
import android.net.Uri;
|
||||
import app.revanced.extension.shared.utils.Logger;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class SanitizeSharingLinksPatch {
|
||||
|
||||
private static final List<String> PARAMETERS_TO_REMOVE = Arrays.asList("context", "pi", "si", "utm_source");
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
* Removes specified tracking parameters and anything after them from a URL string.
|
||||
*
|
||||
* @param url The original URL string.
|
||||
* @return The URL string with the tracking parameters removed, or the original URL if none found or if input is null.
|
||||
*/
|
||||
public static String sanitizeUrl(String url, String parameters) {
|
||||
try {
|
||||
Uri uri = Uri.parse(url);
|
||||
Uri.Builder builder = uri.buildUpon().clearQuery();
|
||||
|
||||
Set<String> paramsToRemove = new HashSet<>(Arrays.asList(parameters.split(",\\s*")));
|
||||
|
||||
for (String paramName : uri.getQueryParameterNames()) {
|
||||
if (!paramsToRemove.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 sanitizeUrl(String url) {
|
||||
if (url == null) {
|
||||
return null;
|
||||
}
|
||||
for (String param : PARAMETERS_TO_REMOVE) {
|
||||
// Consider everything after "?" as a tracking parameter,
|
||||
// since nothing is actually needed after the artist/album/song id
|
||||
// to work properly.
|
||||
// It helps us avoid unknown tracking parameters left in the URL.
|
||||
String paramPattern = "?" + param + "=";
|
||||
int paramIndex = url.indexOf(paramPattern);
|
||||
if (paramIndex >= 0) {
|
||||
// Found the parameter, return the substring before it
|
||||
return url.substring(0, paramIndex);
|
||||
}
|
||||
}
|
||||
// No parameters found, return the original URL
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -756,6 +756,10 @@ public final class app/revanced/patches/spotify/extended/LyricsSearchPatchKt {
|
|||
public static final fun getMediaSessionMetadataConstructorFingerprint ()Lapp/revanced/patcher/Fingerprint;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/spotify/extended/SanitizeSharingLinksPatchKt {
|
||||
public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/spotify/extended/SetActivityContextPatchKt {
|
||||
public static final fun getMainActivityOnCreateFingerprint ()Lapp/revanced/patcher/Fingerprint;
|
||||
public static final fun getSetActivityContextPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
|
|
@ -789,8 +793,8 @@ public final class app/revanced/patches/spotify/misc/lyrics/ChangeLyricsProvider
|
|||
public static final fun getChangeLyricsProviderPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/spotify/misc/privacy/SanitizeSharingLinksPatchKt {
|
||||
public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
public final class app/revanced/patches/spotify/misc/privacy/FingerprintsKt {
|
||||
public static final fun getShareLinkFingerprint ()Lapp/revanced/patcher/Fingerprint;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/spotify/misc/widgets/FixThirdPartyLaunchersWidgetsKt {
|
||||
|
|
|
|||
|
|
@ -1,48 +1,33 @@
|
|||
package app.revanced.patches.spotify.misc.privacy
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.instructionsOrNull
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.util.literal
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
||||
|
||||
internal val shareCopyUrlFingerprint = fingerprint {
|
||||
returns("Ljava/lang/Object;")
|
||||
parameters("Ljava/lang/Object;")
|
||||
strings("clipboard", "Spotify Link")
|
||||
custom { method, _ ->
|
||||
method.name == "invokeSuspend"
|
||||
}
|
||||
}
|
||||
|
||||
internal val oldShareCopyUrlFingerprint = fingerprint {
|
||||
returns("Ljava/lang/Object;")
|
||||
parameters("Ljava/lang/Object;")
|
||||
strings("clipboard", "createNewSession failed")
|
||||
custom { method, _ ->
|
||||
method.name == "apply"
|
||||
}
|
||||
}
|
||||
|
||||
internal val formatAndroidShareSheetUrlFingerprint = fingerprint {
|
||||
returns("Ljava/lang/String;")
|
||||
parameters("L", "Ljava/lang/String;")
|
||||
opcodes(
|
||||
Opcode.GOTO,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.INVOKE_STATIC,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.RETURN_OBJECT
|
||||
val shareLinkFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
parameters(
|
||||
"Ljava/lang/String;",
|
||||
"Ljava/lang/String;",
|
||||
"Ljava/lang/String;",
|
||||
"Ljava/lang/String;"
|
||||
)
|
||||
literal {
|
||||
'\n'.code.toLong()
|
||||
}
|
||||
}
|
||||
returns("V")
|
||||
|
||||
internal val oldFormatAndroidShareSheetUrlFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC)
|
||||
returns("Ljava/lang/String;")
|
||||
parameters("Lcom/spotify/share/social/sharedata/ShareData;", "Ljava/lang/String;")
|
||||
literal {
|
||||
'\n'.code.toLong()
|
||||
custom { _, classDef ->
|
||||
val toStringMethod = classDef.methods.firstOrNull {
|
||||
it.name == "toString" && it.parameters.isEmpty() && it.returnType == "Ljava/lang/String;"
|
||||
} ?: return@custom false
|
||||
|
||||
val toStringInstructions = toStringMethod.instructionsOrNull ?: return@custom false
|
||||
toStringInstructions.any { instruction ->
|
||||
instruction.opcode == Opcode.CONST_STRING &&
|
||||
(instruction as? ReferenceInstruction)?.reference?.let { ref ->
|
||||
(ref as? StringReference)?.string?.startsWith("ShareUrl(url=") == true
|
||||
} == true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,21 @@
|
|||
package app.revanced.patches.spotify.misc.privacy
|
||||
package app.revanced.patches.spotify.extended
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.instructionsOrNull
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.patch.stringOption
|
||||
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.spotify.misc.privacy.shareLinkFingerprint
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/spotify/misc/privacy/SanitizeSharingLinksPatch;"
|
||||
|
||||
private val SHARE_PARAMETERS = setOf("context", "pi", "si", "utm_source")
|
||||
|
||||
@Suppress("unused")
|
||||
val sanitizeSharingLinksPatch = bytecodePatch(
|
||||
name = "Sanitize sharing links",
|
||||
|
|
@ -24,73 +23,20 @@ val sanitizeSharingLinksPatch = bytecodePatch(
|
|||
) {
|
||||
compatibleWith("com.spotify.music")
|
||||
|
||||
dependsOn(sharedExtensionPatch)
|
||||
|
||||
val shareParameters by stringOption(
|
||||
key = "shareParameters",
|
||||
default = SHARE_PARAMETERS.joinToString(", "),
|
||||
title = "Parameters to remove",
|
||||
description = "A list of parameters to be removed from sharing links, separated by commas.",
|
||||
required = true,
|
||||
)
|
||||
|
||||
execute {
|
||||
val extensionMethodDescriptor = "$EXTENSION_CLASS_DESCRIPTOR->" +
|
||||
"sanitizeUrl(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"
|
||||
|
||||
val copyFingerprint = if (shareCopyUrlFingerprint.originalMethodOrNull != null) {
|
||||
shareCopyUrlFingerprint
|
||||
} else {
|
||||
oldShareCopyUrlFingerprint
|
||||
val originalMethod = shareLinkFingerprint.method
|
||||
val invokeDirectIndex = originalMethod.indexOfFirstInstructionOrThrow {
|
||||
opcode == Opcode.INVOKE_DIRECT &&
|
||||
(this as ReferenceInstruction).reference is MethodReference &&
|
||||
(getReference<MethodReference>()?.name == "<init>") &&
|
||||
(getReference<MethodReference>()?.definingClass == "Ljava/lang/Object;")
|
||||
}
|
||||
|
||||
copyFingerprint.method.apply {
|
||||
val newPlainTextInvokeIndex = indexOfFirstInstructionOrThrow {
|
||||
getReference<MethodReference>()?.name == "newPlainText"
|
||||
}
|
||||
val register = getInstruction<FiveRegisterInstruction>(newPlainTextInvokeIndex).registerD
|
||||
val freeRegister = getInstruction<OneRegisterInstruction>(newPlainTextInvokeIndex - 1).registerA
|
||||
val smaliCodeToInsert = """
|
||||
invoke-static {p1}, $EXTENSION_CLASS_DESCRIPTOR->sanitizeUrl(Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object p1
|
||||
""".trimIndent()
|
||||
|
||||
addInstructions(
|
||||
newPlainTextInvokeIndex,
|
||||
"""
|
||||
const-string v$freeRegister, "$shareParameters"
|
||||
invoke-static { v$register, v$freeRegister }, $extensionMethodDescriptor
|
||||
move-result-object v$register
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
// Android native share sheet is used for all other quick share types (X, WhatsApp, etc).
|
||||
val shareUrlParameter: String
|
||||
val shareSheetFingerprint = if (formatAndroidShareSheetUrlFingerprint.originalMethodOrNull != null) {
|
||||
val methodAccessFlags = formatAndroidShareSheetUrlFingerprint.originalMethod
|
||||
shareUrlParameter = if (AccessFlags.STATIC.isSet(methodAccessFlags.accessFlags)) {
|
||||
// In newer implementations the method is static, so p0 is not `this`.
|
||||
"p1"
|
||||
} else {
|
||||
// In older implementations the method is not static, making it so p0 is `this`.
|
||||
// For that reason, add one to the parameter register.
|
||||
"p2"
|
||||
}
|
||||
|
||||
formatAndroidShareSheetUrlFingerprint
|
||||
} else {
|
||||
shareUrlParameter = "p2"
|
||||
oldFormatAndroidShareSheetUrlFingerprint
|
||||
}
|
||||
|
||||
shareSheetFingerprint.method.apply {
|
||||
val freeRegister = getInstruction<OneRegisterInstruction>(0).registerA
|
||||
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
const-string v$freeRegister, "$shareParameters"
|
||||
invoke-static { $shareUrlParameter, v$freeRegister }, $extensionMethodDescriptor
|
||||
move-result-object $shareUrlParameter
|
||||
"""
|
||||
)
|
||||
}
|
||||
originalMethod.addInstructions(invokeDirectIndex + 1, smaliCodeToInsert)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue