@@ -32,8 +32,9 @@ import org.ini4j.Ini
3232import org.json.JSONArray
3333import org.json.JSONObject
3434import org.json.JSONTokener
35- import org.yaml.snakeyaml.TypeDescription
35+ import org.yaml.snakeyaml.LoaderOptions
3636import org.yaml.snakeyaml.Yaml
37+ import org.yaml.snakeyaml.constructor.SafeConstructor
3738import org.yaml.snakeyaml.error.YAMLException
3839import java.io.StringReader
3940import androidx.core.net.toUri
@@ -254,9 +255,23 @@ object RawUpdater : GroupUpdater() {
254255
255256 try {
256257
257- val yaml = Yaml ().apply {
258- addTypeDescription(TypeDescription (String ::class .java, " str" ))
259- }.loadAs(text, Map ::class .java)
258+ // Parse untrusted subscription YAML safely.
259+ // SafeConstructor only produces standard types (Map/List/String/
260+ // Number/Boolean/null) and rejects custom/global tags such as
261+ // !!javax..., preventing arbitrary class instantiation
262+ // (CVE-2022-1471 style gadget chains). A code-point limit bounds
263+ // input size to mitigate decompression/billion-laughs style DoS.
264+ // Note: load() is used instead of loadAs(..., Map::class.java)
265+ // because SafeConstructor cannot construct an explicit root type
266+ // tag; a YAML mapping is returned as a LinkedHashMap natively.
267+ val loaderOptions = LoaderOptions ().apply {
268+ codePointLimit = 10 * 1024 * 1024 // 10 MiB
269+ }
270+ // In SnakeYAML 2.x, Yaml(BaseConstructor) adopts the constructor's
271+ // LoaderOptions (getLoadingConfig()), so codePointLimit set above is
272+ // enforced during parsing (verified: input over the limit throws).
273+ val yaml = Yaml (SafeConstructor (loaderOptions)).load<Any ?>(text) as ? Map <String , Any ?>
274+ ? : error(app.getString(R .string.no_proxies_found_in_file))
260275
261276 val globalClientFingerprint = yaml[" global-client-fingerprint" ]?.toString() ? : " "
262277
0 commit comments