CVE-2021-29061 (Java, published 2021-06-21T20:15Z)
Description: value: A Regular Expression Denial of Service (ReDOS) vulnerability was discovered in Vfsjfilechooser2 version 0.2.9 and below which occurs when the application attempts to validate crafted URIs.
Severity: MEDIUM (CVSS v3 base score: 7.5)
Weakness: CWE-770 \u2014 Allocation of Resources Without Limits or Throttling
The product allocates a reusable resource or group of resources on behalf of an actor without imposing any restrictions on the size or number of resources that can be allocated, in violation of the intended security policy for that actor.
Repository: https://github.com/fracpete/vfsjfilechooser2
Files changed:
src/main/java/com/googlecode/vfsjfilechooser2/utils/VFSURIValidator.java
Commit message: incorporated Yeting Li's fix for Potential Regex Denial of Service (ReDoS), see https://github.com/fracpete/vfsjfilechooser2/issues/7
passwords can now also contain special characters (eg :), which have to be URL encoded (ie %3A)


Vulnerable code:
				.compile("(ftp|FTP|sftp|SFTP|http|HTTP|https|HTTPS|webdav|WEBDAV|smb|SMB)://(.+:.+@)*([^:]+?/*)([ 	]*:[0-9]+)*([ 	]*:)*(/.*)");
				.compile("(ftp|FTP|sftp|SFTP|http|HTTP|https|HTTPS|webdav|WEBDAV|smb|SMB)://(.+:.+@)*([^:]+)([ 	]*:[0-9]+)*([ 	]*:)*(/*?.*)");
		s = "ftp://user:pass:@machine/the_file"; //can ":" be part of a password?
		v.assertEquals(v.getPassword(), "pass:");
		s = "ftp://user:pass:@machine/the_dir/";
		v.assertEquals(v.getPassword(), "pass:");
		s = "FTP://user:pass:@machine/the_file"; //can ":" be part of a password?
		s = "FTP://user:pass:@machine/the_dir/";
		s = "sftp://user:pass:@machine/the_file"; //can ":" be part of a password?
		s = "sftp://user:pass:@machine/the_dir/";
		s = "sftp: //user:pass:@machine/the_file"; //failure tests
		s = "sftp:/ /user:pass:@machine/the_file";
		s = "sftp:/ /user:pass:@machine";
		s = "sftp://user:pass:@:123/a";
		s = "sftp://user:pass:@machine:a/the_file";
		s = "SFTP://user:pass:@machine/the_file"; //can ":" be part of a password?
		s = "SFTP://user:pass:@machine/the_dir/";
		s = "http://user:pass:@machine/the_file"; //can ":" be part of a password?
		s = "http://user:pass:@machine/the_dir/";
		s = "http: //user:pass:@machine/the_file"; //failure tests
		s = "http:/ /user:pass:@machine/the_file";
		s = "http:/ /user:pass:@machine";
		s = "http://user:pass:@:123/a";
		s = "http://user:pass:@machine:a/the_file";
		s = "HTTP://user:pass:@machine/the_file"; //can ":" be part of a password?
		s = "HTTP://user:pass:@machine/the_dir/";
		s = "HTTP: //user:pass:@machine/the_file"; //failure tests
		s = "HTTP:/ /user:pass:@machine/the_file";
		s = "HTTP:/ /user:pass:@machine";
		s = "HTTP://user:pass:@:123/a";
		s = "HTTP://user:pass:@machine:a/the_file";
		s = "https://user:pass:@machine/the_file"; //can ":" be part of a password?
		s = "https://user:pass:@machine/the_dir/";
		s = "https: //user:pass:@machine/the_file"; //failure tests
		s = "https:/ /user:pass:@machine/the_file";
		s = "https:/ /user:pass:@machine";
		s = "https://user:pass:@:123/a";
		s = "https://user:pass:@machine:a/the_file";
		s = "HTTPS://user:pass:@machine/the_file"; //can ":" be part of a password?
		s = "HTTPS://user:pass:@machine/the_dir/";
		s = "HTTPS: //user:pass:@machine/the_file"; //failure tests
		s = "HTTPS:/ /user:pass:@machine/the_file";
		s = "HTTPS:/ /user:pass:@machine";
		s = "HTTPS://user:pass:@:123/a";
		s = "HTTPS://user:pass:@machine:a/the_file";
		s = "webdav://user:pass:@machine/the_file"; //can ":" be part of a password?
		s = "webdav://user:pass:@machine/the_dir/";
		s = "webdav: //user:pass:@machine/the_file"; //failure tests
		s = "webdav:/ /user:pass:@machine/the_file";
		s = "webdav:/ /user:pass:@machine";
		s = "webdav://user:pass:@:123/a";
		s = "webdav://user:pass:@machine:a/the_file";
		s = "WEBDAV://user:pass:@machine/the_file"; //can ":" be part of a password?
		s = "WEBDAV://user:pass:@machine/the_dir/";
		s = "WEBDAV: //user:pass:@machine/the_file"; //failure tests
		s = "WEBDAV:/ /user:pass:@machine/the_file";
		s = "WEBDAV:/ /user:pass:@machine";
		s = "WEBDAV://user:pass:@:123/a";
		s = "WEBDAV://user:pass:@machine:a/the_file";
		s = "smb://user:pass:@machine/the_file"; //can ":" be part of a password?
		s = "smb://user:pass:@machine/the_dir/";
		s = "smb: //user:pass:@machine/the_file"; //failure tests
		s = "smb:/ /user:pass:@machine/the_file";
		s = "smb:/ /user:pass:@machine";
		s = "smb://user:pass:@:123/a";
		s = "smb://user:pass:@machine:a/the_file";
		s = "SMB://user:pass:@machine/the_file"; //can ":" be part of a password?
		s = "SMB://user:pass:@machine/the_dir/";
		s = "SMB: //user:pass:@machine/the_file"; //failure tests
		s = "SMB:/ /user:pass:@machine/the_file";
		s = "SMB:/ /user:pass:@machine";
		s = "SMB://user:pass:@:123/a";
		s = "SMB://user:pass:@machine:a/the_file";

Fixed code:
 * Copyright (C) 2020 University of Waikato, Hamilton, NZ
 * Copyright (C) 2020 Yeting Li
				.compile("(ftp|FTP|sftp|SFTP|http|HTTP|https|HTTPS|webdav|WEBDAV|smb|SMB)://([^:@]+:[^:@]+@)*([^:]+?/*)([ ]*:[0-9]+)*([ ]*:)*(/.*)");
				.compile("(ftp|FTP|sftp|SFTP|http|HTTP|https|HTTPS|webdav|WEBDAV|smb|SMB)://([^:@]+:[^:@]+@)*([^:]+)([ 	]*:[0-9]+)*([ 	]*:)*(/*?.*)");
			// decode specials chars (URL encoded %XY)
			if (local_pass.contains("%")) {
				String tmp_local_pass = local_pass;
				StringBuilder new_local_pass = new StringBuilder();
				while (tmp_local_pass.contains("%")) {
					new_local_pass.append(tmp_local_pass.substring(0, tmp_local_pass.indexOf('%')));
					tmp_local_pass = tmp_local_pass.substring(tmp_local_pass.indexOf('%'));
					if (tmp_local_pass.length() >= 3) {
						char c = (char) Integer.parseInt(tmp_local_pass.substring(1, 3), 16);
						new_local_pass.append(c);
						tmp_local_pass = tmp_local_pass.substring(3);
					}
					else {
						break;
					}
				}
				if (!tmp_local_pass.isEmpty())
					new_local_pass.append(tmp_local_pass);
				local_pass = new_local_pass.toString();
			}
		s = "ftp://user:pass%3Aa@machine/the_file"; //if ":" is part of a password, it must be encoded (: -> %3A)
		v.assertEquals(v.getPassword(), "pass:a");
		s = "ftp://user:pass%3A%3a@machine/the_dir/";
		v.assertEquals(v.getPassword(), "pass::");
		s = "FTP://user:pass%3A@machine/the_file"; //if ":" is part of a password, it must be encoded (: -> %3A)
		s = "FTP://user:pass%3A@machine/the_dir/";
		s = "sftp://user:pass%3A@machine/the_file"; //if ":" is part of a password, it must be encoded (: -> %3A)
		s = "sftp://user:pass%3A@machine/the_dir/";
		s = "sftp: //user:pass%3A@machine/the_file"; //failure tests
		s = "sftp:/ /user:pass%3A@machine/the_file";
		s = "sftp:/ /user:pass%3A@machine";
		s = "sftp://user:pass%3A@:123/a";
		s = "sftp://user:pass%3A@machine:a/the_file";
		s = "SFTP://user:pass%3A@machine/the_file"; //if ":" is part of a password, it must be encoded (: -> %3A)
		s = "SFTP://user:pass%3A@machine/the_dir/";
		s = "http://user:pass%3A@machine/the_file"; //if ":" is part of a password, it must be encoded (: -> %3A)
		s = "http://user:pass%3A@machine/the_dir/";
		s = "http: //user:pass%3A@machine/the_file"; //failure tests
		s = "http:/ /user:pass%3A@machine/the_file";
		s = "http:/ /user:pass%3A@machine";
		s = "http://user:pass%3A@:123/a";
		s = "http://user:pass%3A@machine:a/the_file";
		s = "HTTP://user:pass%3A@machine/the_file"; //if ":" is part of a password, it must be encoded (: -> %3A)
		s = "HTTP://user:pass%3A@machine/the_dir/";
		s = "HTTP: //user:pass%3A@machine/the_file"; //failure tests
		s = "HTTP:/ /user:pass%3A@machine/the_file";
		s = "HTTP:/ /user:pass%3A@machine";
		s = "HTTP://user:pass%3A@:123/a";
		s = "HTTP://user:pass%3A@machine:a/the_file";
		s = "https://user:pass%3A@machine/the_file"; //if ":" is part of a password, it must be encoded (: -> %3A)
		s = "https://user:pass%3A@machine/the_dir/";
		s = "https: //user:pass%3A@machine/the_file"; //failure tests
		s = "https:/ /user:pass%3A@machine/the_file";
		s = "https:/ /user:pass%3A@machine";
		s = "https://user:pass%3A@:123/a";
		s = "https://user:pass%3A@machine:a/the_file";
		s = "HTTPS://user:pass%3A@machine/the_file"; //if ":" is part of a password, it must be encoded (: -> %3A)
		s = "HTTPS://user:pass%3A@machine/the_dir/";
		s = "HTTPS: //user:pass%3A@machine/the_file"; //failure tests
		s = "HTTPS:/ /user:pass%3A@machine/the_file";
		s = "HTTPS:/ /user:pass%3A@machine";
		s = "HTTPS://user:pass%3A@:123/a";
		s = "HTTPS://user:pass%3A@machine:a/the_file";
		s = "webdav://user:pass%3A@machine/the_file"; //if ":" is part of a password, it must be encoded (: -> %3A)
		s = "webdav://user:pass%3A@machine/the_dir/";
		s = "webdav: //user:pass%3A@machine/the_file"; //failure tests
		s = "webdav:/ /user:pass%3A@machine/the_file";
		s = "webdav:/ /user:pass%3A@machine";
		s = "webdav://user:pass%3A@:123/a";
		s = "webdav://user:pass%3A@machine:a/the_file";
		s = "WEBDAV://user:pass%3A@machine/the_file"; //if ":" is part of a password, it must be encoded (: -> %3A)
		s = "WEBDAV://user:pass%3A@machine/the_dir/";
		s = "WEBDAV: //user:pass%3A@machine/the_file"; //failure tests
		s = "WEBDAV:/ /user:pass%3A@machine/the_file";
		s = "WEBDAV:/ /user:pass%3A@machine";
		s = "WEBDAV://user:pass%3A@:123/a";
		s = "WEBDAV://user:pass%3A@machine:a/the_file";
		s = "smb://user:pass%3A@machine/the_file"; //if ":" is part of a password, it must be encoded (: -> %3A)
		s = "smb://user:pass%3A@machine/the_dir/";
		s = "smb: //user:pass%3A@machine/the_file"; //failure tests
		s = "smb:/ /user:pass%3A@machine/the_file";
		s = "smb:/ /user:pass%3A@machine";
		s = "smb://user:pass%3A@:123/a";
		s = "smb://user:pass%3A@machine:a/the_file";
		s = "SMB://user:pass%3A@machine/the_file"; //if ":" is part of a password, it must be encoded (: -> %3A)
		s = "SMB://user:pass%3A@machine/the_dir/";
		s = "SMB: //user:pass%3A@machine/the_file"; //failure tests
		s = "SMB:/ /user:pass%3A@machine/the_file";
		s = "SMB:/ /user:pass%3A@machine";
		s = "SMB://user:pass%3A@:123/a";
		s = "SMB://user:pass%3A@machine:a/the_file";