mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-26 04:12:34 +00:00
most uri pass docomo and exotic fail
This commit is contained in:
@@ -33,21 +33,21 @@ use crate::{BarcodeFormat, RXingResult, client::result::{ParsedRXingResult, Pars
|
||||
use super::ResultParser;
|
||||
|
||||
#[test]
|
||||
fn testProduct() {
|
||||
doTest("123456789012", "123456789012", BarcodeFormat::UPC_A);
|
||||
doTest("00393157", "00393157", BarcodeFormat::EAN_8);
|
||||
doTest("5051140178499", "5051140178499", BarcodeFormat::EAN_13);
|
||||
doTest("01234565", "012345000065", BarcodeFormat::UPC_E);
|
||||
fn test_product() {
|
||||
do_test("123456789012", "123456789012", BarcodeFormat::UPC_A);
|
||||
do_test("00393157", "00393157", BarcodeFormat::EAN_8);
|
||||
do_test("5051140178499", "5051140178499", BarcodeFormat::EAN_13);
|
||||
do_test("01234565", "012345000065", BarcodeFormat::UPC_E);
|
||||
}
|
||||
|
||||
fn doTest( contents:&str, normalized:&str, format:BarcodeFormat) {
|
||||
let fakeRXingResult = RXingResult::new(contents, Vec::new(), Vec::new(), format);
|
||||
let result = ResultParser::parseRXingResult(&fakeRXingResult);
|
||||
fn do_test( contents:&str, normalized:&str, format:BarcodeFormat) {
|
||||
let fake_rxing_result = RXingResult::new(contents, Vec::new(), Vec::new(), format);
|
||||
let result = ResultParser::parseRXingResult(&fake_rxing_result);
|
||||
assert_eq!(ParsedRXingResultType::PRODUCT, result.getType());
|
||||
|
||||
if let ParsedClientResult::ProductResult(productRXingResult) = result {
|
||||
assert_eq!(contents, productRXingResult.getProductID());
|
||||
assert_eq!(normalized, productRXingResult.getNormalizedProductID());
|
||||
if let ParsedClientResult::ProductResult(product_rxing_result) = result {
|
||||
assert_eq!(contents, product_rxing_result.getProductID());
|
||||
assert_eq!(normalized, product_rxing_result.getNormalizedProductID());
|
||||
}else{
|
||||
panic!("Expected ParsedClientResult::ProductResult")
|
||||
}
|
||||
|
||||
@@ -34,8 +34,9 @@ use urlencoding::decode;
|
||||
use crate::{exceptions::Exceptions, RXingResult};
|
||||
|
||||
use super::{
|
||||
GeoResultParser, ISBNResultParser, ParsedClientResult, ParsedRXingResult, SMSMMSResultParser,
|
||||
TelResultParser, TextParsedRXingResult, WifiResultParser, ProductResultParser,
|
||||
GeoResultParser, ISBNResultParser, ParsedClientResult, ParsedRXingResult, ProductResultParser,
|
||||
SMSMMSResultParser, TelResultParser, TextParsedRXingResult, URIResultParser, URLTOResultParser,
|
||||
WifiResultParser,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -102,7 +103,7 @@ pub fn getMassagedText(result: &RXingResult) -> String {
|
||||
}
|
||||
|
||||
pub fn parseRXingResult(theRXingResult: &RXingResult) -> ParsedClientResult {
|
||||
let PARSERS: [&ParserFunction; 6] = [
|
||||
let PARSERS: [&ParserFunction; 8] = [
|
||||
// new BookmarkDoCoMoRXingResultParser(),
|
||||
// new AddressBookDoCoMoRXingResultParser(),
|
||||
// new EmailDoCoMoRXingResultParser(),
|
||||
@@ -117,8 +118,8 @@ pub fn parseRXingResult(theRXingResult: &RXingResult) -> ParsedClientResult {
|
||||
// new SMSTOMMSTORXingResultParser(),
|
||||
&GeoResultParser::parse,
|
||||
&WifiResultParser::parse,
|
||||
// new URLTORXingResultParser(),
|
||||
// new URIRXingResultParser(),
|
||||
&URLTOResultParser::parse,
|
||||
&URIResultParser::parse,
|
||||
&ISBNResultParser::parse,
|
||||
&ProductResultParser::parse,
|
||||
// new ExpandedProductRXingResultParser(),
|
||||
@@ -212,14 +213,14 @@ pub fn isStringOfDigits(value: &str, length: usize) -> bool {
|
||||
!value.is_empty() && length > 0 && length == value.len() && matcher.is_match(value)
|
||||
}
|
||||
|
||||
pub fn isSubstringOfDigits(value: &str, offset: i32, length: usize) -> bool {
|
||||
pub fn isSubstringOfDigits(value: &str, offset: usize, length: usize) -> bool {
|
||||
if value.is_empty() || length <= 0 {
|
||||
return false;
|
||||
}
|
||||
let max = offset as usize + length;
|
||||
|
||||
let matcher = Regex::new(DIGITS).unwrap();
|
||||
let sub_seq = &value[offset as usize..offset as usize + max];
|
||||
let sub_seq = &value[offset as usize..max];
|
||||
|
||||
value.len() >= max && matcher.is_match(sub_seq)
|
||||
}
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* 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 com.google.zxing.client.result;
|
||||
|
||||
/**
|
||||
* A simple result type encapsulating a URI that has no further interpretation.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class URIParsedRXingResult extends ParsedRXingResult {
|
||||
|
||||
private final String uri;
|
||||
private final String title;
|
||||
|
||||
public URIParsedRXingResult(String uri, String title) {
|
||||
super(ParsedRXingResultType.URI);
|
||||
this.uri = massageURI(uri);
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getURI() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the URI contains suspicious patterns that may suggest it intends to
|
||||
* mislead the user about its true nature
|
||||
* @deprecated see {@link URIRXingResultParser#isPossiblyMaliciousURI(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean isPossiblyMaliciousURI() {
|
||||
return URIRXingResultParser.isPossiblyMaliciousURI(uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayRXingResult() {
|
||||
StringBuilder result = new StringBuilder(30);
|
||||
maybeAppend(title, result);
|
||||
maybeAppend(uri, result);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a string that represents a URI into something more proper, by adding or canonicalizing
|
||||
* the protocol.
|
||||
*/
|
||||
private static String massageURI(String uri) {
|
||||
uri = uri.trim();
|
||||
int protocolEnd = uri.indexOf(':');
|
||||
if (protocolEnd < 0 || isColonFollowedByPortNumber(uri, protocolEnd)) {
|
||||
// No protocol, or found a colon, but it looks like it is after the host, so the protocol is still missing,
|
||||
// so assume http
|
||||
uri = "http://" + uri;
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
private static boolean isColonFollowedByPortNumber(String uri, int protocolEnd) {
|
||||
int start = protocolEnd + 1;
|
||||
int nextSlash = uri.indexOf('/', start);
|
||||
if (nextSlash < 0) {
|
||||
nextSlash = uri.length();
|
||||
}
|
||||
return RXingResultParser.isSubstringOfDigits(uri, start, nextSlash - start);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
102
src/client/result/URIParsedResult.rs
Normal file
102
src/client/result/URIParsedResult.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, V&ersion 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 com.google.zxing.client.result;
|
||||
|
||||
use super::{ParsedRXingResult, ParsedRXingResultType, ResultParser, URIResultParser};
|
||||
|
||||
/**
|
||||
* A simple result type encapsulating a URI that has no further interpretation.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
pub struct URIParsedRXingResult {
|
||||
uri: String,
|
||||
title: String,
|
||||
}
|
||||
impl ParsedRXingResult for URIParsedRXingResult {
|
||||
fn getType(&self) -> super::ParsedRXingResultType {
|
||||
ParsedRXingResultType::URI
|
||||
}
|
||||
|
||||
fn getDisplayRXingResult(&self) -> String {
|
||||
let mut result = String::with_capacity(30);
|
||||
ResultParser::maybe_append_string(&self.title, &mut result);
|
||||
ResultParser::maybe_append_string(&self.uri, &mut result);
|
||||
result
|
||||
}
|
||||
}
|
||||
impl URIParsedRXingResult {
|
||||
pub fn new(uri: String, title: String) -> Self {
|
||||
Self {
|
||||
uri: Self::massage_uri(&uri),
|
||||
title
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getURI(&self) -> &str {
|
||||
&self.uri
|
||||
}
|
||||
|
||||
pub fn getTitle(&self) -> &str {
|
||||
&self.title
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the URI contains suspicious patterns that may suggest it intends to
|
||||
* mislead the user about its true nature
|
||||
* @deprecated see {@link URIRXingResultParser#isPossiblyMaliciousURI(String)}
|
||||
*/
|
||||
#[deprecated]
|
||||
pub fn is_possibly_malicious_uri(&self) -> bool {
|
||||
return URIResultParser::is_possibly_malicious_uri(&self.uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a string that represents a URI into something more proper, by adding or canonicalizing
|
||||
* the protocol.
|
||||
*/
|
||||
fn massage_uri(uri: &str) -> String {
|
||||
let mut uri = String::from(uri.trim());
|
||||
// let protocolEnd = uri.find(':');
|
||||
if let Some(protocolEnd) = uri.find(':') {
|
||||
if Self::is_colon_followed_by_port_number(&uri, protocolEnd) {
|
||||
// No protocol, or found a colon, but it looks like it is after the host, so the protocol is still missing,
|
||||
// so assume http
|
||||
uri = format!("http://{}", &uri);
|
||||
// uri = updated_uri.as_str()
|
||||
}
|
||||
}else {
|
||||
uri = format!("http://{}", &uri);
|
||||
}
|
||||
|
||||
uri
|
||||
}
|
||||
|
||||
fn is_colon_followed_by_port_number(uri: &str, protocol_end: usize) -> bool {
|
||||
let start = protocol_end + 1;
|
||||
let nextSlash = if let Some(pos) = uri[start..].find('/') {
|
||||
pos + start
|
||||
} else {
|
||||
uri.len()
|
||||
};
|
||||
// let nextSlash = uri.indexOf('/', start);
|
||||
// if (nextSlash < 0) {
|
||||
// nextSlash = uri.length();
|
||||
// }
|
||||
return ResultParser::isSubstringOfDigits(uri, start, nextSlash - start);
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* 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 com.google.zxing.client.result;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.RXingResult;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Tests {@link URIParsedRXingResult}.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class URIParsedRXingResultTestCase extends Assert {
|
||||
|
||||
@Test
|
||||
public void testBookmarkDocomo() {
|
||||
doTest("MEBKM:URL:google.com;;", "http://google.com", null);
|
||||
doTest("MEBKM:URL:http://google.com;;", "http://google.com", null);
|
||||
doTest("MEBKM:URL:google.com;TITLE:Google;", "http://google.com", "Google");
|
||||
}
|
||||
|
||||
@SuppressWarnings("checkstyle:lineLength")
|
||||
@Test
|
||||
public void testURI() {
|
||||
doTest("google.com", "http://google.com", null);
|
||||
doTest("123.com", "http://123.com", null);
|
||||
doTest("http://google.com", "http://google.com", null);
|
||||
doTest("https://google.com", "https://google.com", null);
|
||||
doTest("google.com:443", "http://google.com:443", null);
|
||||
doTest("https://www.google.com/calendar/hosted/google.com/embed?mode=AGENDA&force_login=true&src=google.com_726f6f6d5f6265707075@resource.calendar.google.com",
|
||||
"https://www.google.com/calendar/hosted/google.com/embed?mode=AGENDA&force_login=true&src=google.com_726f6f6d5f6265707075@resource.calendar.google.com",
|
||||
null);
|
||||
doTest("otpauth://remoteaccess?devaddr=00%a1b2%c3d4&devname=foo&key=bar",
|
||||
"otpauth://remoteaccess?devaddr=00%a1b2%c3d4&devname=foo&key=bar",
|
||||
null);
|
||||
doTest("s3://amazon.com:8123", "s3://amazon.com:8123", null);
|
||||
doTest("HTTP://R.BEETAGG.COM/?12345", "HTTP://R.BEETAGG.COM/?12345", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotURI() {
|
||||
doTestNotUri("google.c");
|
||||
doTestNotUri(".com");
|
||||
doTestNotUri(":80/");
|
||||
doTestNotUri("ABC,20.3,AB,AD");
|
||||
doTestNotUri("http://google.com?q=foo bar");
|
||||
doTestNotUri("12756.501");
|
||||
doTestNotUri("google.50");
|
||||
doTestNotUri("foo.bar.bing.baz.foo.bar.bing.baz");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testURLTO() {
|
||||
doTest("urlto::bar.com", "http://bar.com", null);
|
||||
doTest("urlto::http://bar.com", "http://bar.com", null);
|
||||
doTest("urlto:foo:bar.com", "http://bar.com", "foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGarbage() {
|
||||
doTestNotUri("Da65cV1g^>%^f0bAbPn1CJB6lV7ZY8hs0Sm:DXU0cd]GyEeWBz8]bUHLB");
|
||||
doTestNotUri("DEA\u0003\u0019M\u0006\u0000\bå\u0000‡HO\u0000X$\u0001\u0000\u001Fwfc\u0007!þ“˜" +
|
||||
"\u0013\u0013¾Z{ùÎÝڗZ§¨+y_zbñk\u00117¸\u000E†Ü\u0000\u0000\u0000\u0000" +
|
||||
"\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" +
|
||||
"\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000£.ux");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsPossiblyMalicious() {
|
||||
doTestIsPossiblyMalicious("http://google.com", false);
|
||||
doTestIsPossiblyMalicious("http://google.com@evil.com", true);
|
||||
doTestIsPossiblyMalicious("http://google.com:@evil.com", true);
|
||||
doTestIsPossiblyMalicious("google.com:@evil.com", false);
|
||||
doTestIsPossiblyMalicious("https://google.com:443", false);
|
||||
doTestIsPossiblyMalicious("https://google.com:443/", false);
|
||||
doTestIsPossiblyMalicious("https://evil@google.com:443", true);
|
||||
doTestIsPossiblyMalicious("http://google.com/foo@bar", false);
|
||||
doTestIsPossiblyMalicious("http://google.com/@@", false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaliciousUnicode() {
|
||||
doTestIsPossiblyMalicious("https://google.com\u2215.evil.com/stuff", true);
|
||||
doTestIsPossiblyMalicious("\u202ehttps://dylankatz.com/moc.elgoog.www//:sptth", true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExotic() {
|
||||
doTest("bitcoin:mySD89iqpmptrK3PhHFW9fa7BXiP7ANy3Y", "bitcoin:mySD89iqpmptrK3PhHFW9fa7BXiP7ANy3Y", null);
|
||||
doTest("BTCTX:-TC4TO3$ZYZTC5NC83/SYOV+YGUGK:$BSF0P8/STNTKTKS.V84+JSA$LB+EHCG+8A725.2AZ-NAVX3VBV5K4MH7UL2.2M:" +
|
||||
"F*M9HSL*$2P7T*FX.ZT80GWDRV0QZBPQ+O37WDCNZBRM3EQ0S9SZP+3BPYZG02U/LA*89C2U.V1TS.CT1VF3DIN*HN3W-O-" +
|
||||
"0ZAKOAB32/.8:J501GJJTTWOA+5/6$MIYBERPZ41NJ6-WSG/*Z48ZH*LSAOEM*IXP81L:$F*W08Z60CR*C*P.JEEVI1F02J07L6+" +
|
||||
"W4L1G$/IC*$16GK6A+:I1-:LJ:Z-P3NW6Z6ADFB-F2AKE$2DWN23GYCYEWX9S8L+LF$VXEKH7/R48E32PU+A:9H:8O5",
|
||||
"BTCTX:-TC4TO3$ZYZTC5NC83/SYOV+YGUGK:$BSF0P8/STNTKTKS.V84+JSA$LB+EHCG+8A725.2AZ-NAVX3VBV5K4MH7UL2.2M:" +
|
||||
"F*M9HSL*$2P7T*FX.ZT80GWDRV0QZBPQ+O37WDCNZBRM3EQ0S9SZP+3BPYZG02U/LA*89C2U.V1TS.CT1VF3DIN*HN3W-O-" +
|
||||
"0ZAKOAB32/.8:J501GJJTTWOA+5/6$MIYBERPZ41NJ6-WSG/*Z48ZH*LSAOEM*IXP81L:$F*W08Z60CR*C*P.JEEVI1F02J07L6+" +
|
||||
"W4L1G$/IC*$16GK6A+:I1-:LJ:Z-P3NW6Z6ADFB-F2AKE$2DWN23GYCYEWX9S8L+LF$VXEKH7/R48E32PU+A:9H:8O5",
|
||||
null);
|
||||
doTest("opc.tcp://test.samplehost.com:4841", "opc.tcp://test.samplehost.com:4841", null);
|
||||
}
|
||||
|
||||
private static void doTest(String contents, String uri, String title) {
|
||||
RXingResult fakeRXingResult = new RXingResult(contents, null, null, BarcodeFormat.QR_CODE);
|
||||
ParsedRXingResult result = RXingResultParser.parseRXingResult(fakeRXingResult);
|
||||
assertSame(ParsedRXingResultType.URI, result.getType());
|
||||
URIParsedRXingResult uriRXingResult = (URIParsedRXingResult) result;
|
||||
assertEquals(uri, uriRXingResult.getURI());
|
||||
assertEquals(title, uriRXingResult.getTitle());
|
||||
}
|
||||
|
||||
private static void doTestNotUri(String text) {
|
||||
RXingResult fakeRXingResult = new RXingResult(text, null, null, BarcodeFormat.QR_CODE);
|
||||
ParsedRXingResult result = RXingResultParser.parseRXingResult(fakeRXingResult);
|
||||
assertSame(ParsedRXingResultType.TEXT, result.getType());
|
||||
assertEquals(text, result.getDisplayRXingResult());
|
||||
}
|
||||
|
||||
private static void doTestIsPossiblyMalicious(String uri, boolean malicious) {
|
||||
RXingResult fakeRXingResult = new RXingResult(uri, null, null, BarcodeFormat.QR_CODE);
|
||||
ParsedRXingResult result = RXingResultParser.parseRXingResult(fakeRXingResult);
|
||||
assertSame(malicious ? ParsedRXingResultType.TEXT : ParsedRXingResultType.URI, result.getType());
|
||||
}
|
||||
|
||||
}
|
||||
173
src/client/result/URIParsedResultTestCase.rs
Normal file
173
src/client/result/URIParsedResultTestCase.rs
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* 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 com.google.zxing.client.result;
|
||||
|
||||
// import com.google.zxing.BarcodeFormat;
|
||||
// import com.google.zxing.RXingResult;
|
||||
// import org.junit.Assert;
|
||||
// import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Tests {@link URIParsedRXingResult}.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
// public final class URIParsedRXingResultTestCase extends Assert {
|
||||
use crate::{
|
||||
client::result::{ParsedClientResult, ParsedRXingResult, ParsedRXingResultType, ResultParser},
|
||||
BarcodeFormat, RXingResult,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_bookmark_docomo() {
|
||||
do_test("MEBKM:URL:google.com;;", "http://google.com", "");
|
||||
do_test("MEBKM:URL:http://google.com;;", "http://google.com", "");
|
||||
do_test(
|
||||
"MEBKM:URL:google.com;TITLE:Google;",
|
||||
"http://google.com",
|
||||
"Google",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uri() {
|
||||
do_test("google.com", "http://google.com", "");
|
||||
do_test("123.com", "http://123.com", "");
|
||||
do_test("http://google.com", "http://google.com", "");
|
||||
do_test("https://google.com", "https://google.com", "");
|
||||
do_test("google.com:443", "http://google.com:443", "");
|
||||
do_test("https://www.google.com/calendar/hosted/google.com/embed?mode=AGENDA&force_login=true&src=google.com_726f6f6d5f6265707075@resource.calendar.google.com",
|
||||
"https://www.google.com/calendar/hosted/google.com/embed?mode=AGENDA&force_login=true&src=google.com_726f6f6d5f6265707075@resource.calendar.google.com",
|
||||
"");
|
||||
do_test(
|
||||
"otpauth://remoteaccess?devaddr=00%a1b2%c3d4&devname=foo&key=bar",
|
||||
"otpauth://remoteaccess?devaddr=00%a1b2%c3d4&devname=foo&key=bar",
|
||||
"",
|
||||
);
|
||||
do_test("s3://amazon.com:8123", "s3://amazon.com:8123", "");
|
||||
do_test(
|
||||
"HTTP://R.BEETAGG.COM/?12345",
|
||||
"HTTP://R.BEETAGG.COM/?12345",
|
||||
"",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_not_uri() {
|
||||
do_test_not_uri("google.c");
|
||||
do_test_not_uri(".com");
|
||||
do_test_not_uri(":80/");
|
||||
do_test_not_uri("ABC,20.3,AB,AD");
|
||||
do_test_not_uri("http://google.com?q=foo bar");
|
||||
do_test_not_uri("12756.501");
|
||||
do_test_not_uri("google.50");
|
||||
do_test_not_uri("foo.bar.bing.baz.foo.bar.bing.baz");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_urlto() {
|
||||
do_test("urlto::bar.com", "http://bar.com", "");
|
||||
do_test("urlto::http://bar.com", "http://bar.com", "");
|
||||
do_test("urlto:foo:bar.com", "http://bar.com", "foo");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_garbage() {
|
||||
do_test_not_uri("Da65cV1g^>%^f0bAbPn1CJB6lV7ZY8hs0Sm:DXU0cd]GyEeWBz8]bUHLB");
|
||||
// used \u{0008} as java \b
|
||||
do_test_not_uri("DEA\u{0003}\u{0019}M\u{0006}\u{0000}\u{0008}å\u{0000}‡HO\u{0000}X$\u{0001}\u{0000}\u{001F}wfc\u{0007}!þ“˜\
|
||||
\u{0013}\u{0013}¾Z{ùÎÝڗZ§¨+y_zbñk\u{0011}7¸\u{000E}†Ü\u{0000}\u{0000}\u{0000}\u{0000}\
|
||||
\u{0000}\u{0000}\u{0000}\u{0000}\u{0000}\u{0000}\u{0000}\u{0000}\u{0000}\u{0000}\u{0000}\u{0000}\u{0000}\u{0000}\
|
||||
\u{0000}\u{0000}\u{0000}\u{0000}\u{0000}\u{0000}\u{0000}\u{0000}£.ux");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_possibly_malicious() {
|
||||
do_test_is_possibly_malicious("http://google.com", false);
|
||||
do_test_is_possibly_malicious("http://google.com@evil.com", true);
|
||||
do_test_is_possibly_malicious("http://google.com:@evil.com", true);
|
||||
do_test_is_possibly_malicious("google.com:@evil.com", false);
|
||||
do_test_is_possibly_malicious("https://google.com:443", false);
|
||||
do_test_is_possibly_malicious("https://google.com:443/", false);
|
||||
do_test_is_possibly_malicious("https://evil@google.com:443", true);
|
||||
do_test_is_possibly_malicious("http://google.com/foo@bar", false);
|
||||
do_test_is_possibly_malicious("http://google.com/@@", false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_malicious_unicode() {
|
||||
do_test_is_possibly_malicious("https://google.com\u{2215}.evil.com/stuff", true);
|
||||
do_test_is_possibly_malicious("\u{202e}https://dylankatz.com/moc.elgoog.www//:sptth", true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exotic() {
|
||||
do_test(
|
||||
"bitcoin:mySD89iqpmptrK3PhHFW9fa7BXiP7ANy3Y",
|
||||
"bitcoin:mySD89iqpmptrK3PhHFW9fa7BXiP7ANy3Y",
|
||||
"",
|
||||
);
|
||||
do_test("BTCTX:-TC4TO3$ZYZTC5NC83/SYOV+YGUGK:$BSF0P8/STNTKTKS.V84+JSA$LB+EHCG+8A725.2AZ-NAVX3VBV5K4MH7UL2.2M:/
|
||||
F*M9HSL*$2P7T*FX.ZT80GWDRV0QZBPQ+O37WDCNZBRM3EQ0S9SZP+3BPYZG02U/LA*89C2U.V1TS.CT1VF3DIN*HN3W-O-/
|
||||
0ZAKOAB32/.8:J501GJJTTWOA+5/6$MIYBERPZ41NJ6-WSG/*Z48ZH*LSAOEM*IXP81L:$F*W08Z60CR*C*P.JEEVI1F02J07L6+/
|
||||
W4L1G$/IC*$16GK6A+:I1-:LJ:Z-P3NW6Z6ADFB-F2AKE$2DWN23GYCYEWX9S8L+LF$VXEKH7/R48E32PU+A:9H:8O5",
|
||||
"BTCTX:-TC4TO3$ZYZTC5NC83/SYOV+YGUGK:$BSF0P8/STNTKTKS.V84+JSA$LB+EHCG+8A725.2AZ-NAVX3VBV5K4MH7UL2.2M:/
|
||||
F*M9HSL*$2P7T*FX.ZT80GWDRV0QZBPQ+O37WDCNZBRM3EQ0S9SZP+3BPYZG02U/LA*89C2U.V1TS.CT1VF3DIN*HN3W-O-/
|
||||
0ZAKOAB32/.8:J501GJJTTWOA+5/6$MIYBERPZ41NJ6-WSG/*Z48ZH*LSAOEM*IXP81L:$F*W08Z60CR*C*P.JEEVI1F02J07L6+/
|
||||
W4L1G$/IC*$16GK6A+:I1-:LJ:Z-P3NW6Z6ADFB-F2AKE$2DWN23GYCYEWX9S8L+LF$VXEKH7/R48E32PU+A:9H:8O5",
|
||||
"");
|
||||
do_test(
|
||||
"opc.tcp://test.samplehost.com:4841",
|
||||
"opc.tcp://test.samplehost.com:4841",
|
||||
"",
|
||||
);
|
||||
}
|
||||
|
||||
fn do_test(contents: &str, uri: &str, title: &str) {
|
||||
let fake_rxing_result =
|
||||
RXingResult::new(contents, Vec::new(), Vec::new(), BarcodeFormat::QR_CODE);
|
||||
let result = ResultParser::parseRXingResult(&fake_rxing_result);
|
||||
assert_eq!(ParsedRXingResultType::URI, result.getType());
|
||||
if let ParsedClientResult::URIResult(uriRXingResult) = result {
|
||||
assert_eq!(uri, uriRXingResult.getURI());
|
||||
assert_eq!(title, uriRXingResult.getTitle());
|
||||
} else {
|
||||
panic!("Expected ParsedClientResult::URIResult(uriRXingResult)");
|
||||
}
|
||||
}
|
||||
|
||||
fn do_test_not_uri(text: &str) {
|
||||
let fake_rxing_result = RXingResult::new(text, Vec::new(), Vec::new(), BarcodeFormat::QR_CODE);
|
||||
let result = ResultParser::parseRXingResult(&fake_rxing_result);
|
||||
assert_eq!(ParsedRXingResultType::TEXT, result.getType());
|
||||
assert_eq!(text, result.getDisplayRXingResult());
|
||||
}
|
||||
|
||||
fn do_test_is_possibly_malicious(uri: &str, malicious: bool) {
|
||||
let fake_rxing_result = RXingResult::new(uri, Vec::new(), Vec::new(), BarcodeFormat::QR_CODE);
|
||||
let result = ResultParser::parseRXingResult(&fake_rxing_result);
|
||||
assert_eq!(
|
||||
if malicious {
|
||||
ParsedRXingResultType::TEXT
|
||||
} else {
|
||||
ParsedRXingResultType::URI
|
||||
},
|
||||
result.getType()
|
||||
);
|
||||
}
|
||||
|
||||
// }
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* 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 com.google.zxing.client.result;
|
||||
|
||||
import com.google.zxing.RXingResult;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Tries to parse results that are a URI of some kind.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class URIRXingResultParser extends RXingResultParser {
|
||||
|
||||
private static final Pattern ALLOWED_URI_CHARS_PATTERN =
|
||||
Pattern.compile("[-._~:/?#\\[\\]@!$&'()*+,;=%A-Za-z0-9]+");
|
||||
private static final Pattern USER_IN_HOST = Pattern.compile(":/*([^/@]+)@[^/]+");
|
||||
// See http://www.ietf.org/rfc/rfc2396.txt
|
||||
private static final Pattern URL_WITH_PROTOCOL_PATTERN = Pattern.compile("[a-zA-Z][a-zA-Z0-9+-.]+:");
|
||||
private static final Pattern URL_WITHOUT_PROTOCOL_PATTERN = Pattern.compile(
|
||||
"([a-zA-Z0-9\\-]+\\.){1,6}[a-zA-Z]{2,}" + // host name elements; allow up to say 6 domain elements
|
||||
"(:\\d{1,5})?" + // maybe port
|
||||
"(/|\\?|$)"); // query, path or nothing
|
||||
|
||||
@Override
|
||||
public URIParsedRXingResult parse(RXingResult result) {
|
||||
String rawText = getMassagedText(result);
|
||||
// We specifically handle the odd "URL" scheme here for simplicity and add "URI" for fun
|
||||
// Assume anything starting this way really means to be a URI
|
||||
if (rawText.startsWith("URL:") || rawText.startsWith("URI:")) {
|
||||
return new URIParsedRXingResult(rawText.substring(4).trim(), null);
|
||||
}
|
||||
rawText = rawText.trim();
|
||||
if (!isBasicallyValidURI(rawText) || isPossiblyMaliciousURI(rawText)) {
|
||||
return null;
|
||||
}
|
||||
return new URIParsedRXingResult(rawText, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the URI contains suspicious patterns that may suggest it intends to
|
||||
* mislead the user about its true nature. At the moment this looks for the presence
|
||||
* of user/password syntax in the host/authority portion of a URI which may be used
|
||||
* in attempts to make the URI's host appear to be other than it is. Example:
|
||||
* http://yourbank.com@phisher.com This URI connects to phisher.com but may appear
|
||||
* to connect to yourbank.com at first glance.
|
||||
*/
|
||||
static boolean isPossiblyMaliciousURI(String uri) {
|
||||
return !ALLOWED_URI_CHARS_PATTERN.matcher(uri).matches() || USER_IN_HOST.matcher(uri).find();
|
||||
}
|
||||
|
||||
static boolean isBasicallyValidURI(String uri) {
|
||||
if (uri.contains(" ")) {
|
||||
// Quick hack check for a common case
|
||||
return false;
|
||||
}
|
||||
Matcher m = URL_WITH_PROTOCOL_PATTERN.matcher(uri);
|
||||
if (m.find() && m.start() == 0) { // match at start only
|
||||
return true;
|
||||
}
|
||||
m = URL_WITHOUT_PROTOCOL_PATTERN.matcher(uri);
|
||||
return m.find() && m.start() == 0;
|
||||
}
|
||||
|
||||
}
|
||||
118
src/client/result/URIResultParser.rs
Normal file
118
src/client/result/URIResultParser.rs
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* 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 com.google.zxing.client.result;
|
||||
|
||||
// import com.google.zxing.RXingResult;
|
||||
|
||||
// import java.util.regex.Matcher;
|
||||
// import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Tries to parse results that are a URI of some kind.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
// public final class URIRXingResultParser extends RXingResultParser {
|
||||
use regex::Regex;
|
||||
|
||||
use crate::RXingResult;
|
||||
|
||||
use super::{ParsedClientResult, ResultParser, URIParsedRXingResult};
|
||||
|
||||
const ALLOWED_URI_CHARS_PATTERN: &'static str = "[-._~:/?#\\[\\]@!$&'()*+,;=%A-Za-z0-9]+";
|
||||
const USER_IN_HOST: &'static str = ":/*([^/@]+)@[^/]+";
|
||||
/// See http://www.ietf.org/rfc/rfc2396.txt
|
||||
const URL_WITH_PROTOCOL_PATTERN: &'static str = "[a-zA-Z][a-zA-Z0-9+-.]+:";
|
||||
/// (host name elements; allow up to say 6 domain elements), (maybe port), (query, path or nothing)
|
||||
const URL_WITHOUT_PROTOCOL_PATTERN: &'static str =
|
||||
"([a-zA-Z0-9\\-]+\\.){1,6}[a-zA-Z]{2,}(:\\d{1,5})?(/|\\?|$)";
|
||||
|
||||
pub fn parse(result: &RXingResult) -> Option<ParsedClientResult> {
|
||||
let raw_text = ResultParser::getMassagedText(result);
|
||||
// We specifically handle the odd "URL" scheme here for simplicity and add "URI" for fun
|
||||
// Assume anything starting this way really means to be a URI
|
||||
if raw_text.starts_with("URL:") || raw_text.starts_with("URI:") {
|
||||
return Some(ParsedClientResult::URIResult(URIParsedRXingResult::new(
|
||||
raw_text[4..].trim().to_owned(),
|
||||
"".to_owned(),
|
||||
)));
|
||||
// return new URIParsedRXingResult(rawText.substring(4).trim(), null);
|
||||
}
|
||||
let raw_text = raw_text.trim();
|
||||
if !is_basically_valid_uri(raw_text) || is_possibly_malicious_uri(raw_text) {
|
||||
return None;
|
||||
}
|
||||
Some(ParsedClientResult::URIResult(URIParsedRXingResult::new(
|
||||
raw_text.to_owned(),
|
||||
"".to_owned(),
|
||||
)))
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the URI contains suspicious patterns that may suggest it intends to
|
||||
* mislead the user about its true nature. At the moment this looks for the presence
|
||||
* of user/password syntax in the host/authority portion of a URI which may be used
|
||||
* in attempts to make the URI's host appear to be other than it is. Example:
|
||||
* http://yourbank.com@phisher.com This URI connects to phisher.com but may appear
|
||||
* to connect to yourbank.com at first glance.
|
||||
*/
|
||||
pub fn is_possibly_malicious_uri(uri: &str) -> bool {
|
||||
let allowed_chars_regex =
|
||||
Regex::new(ALLOWED_URI_CHARS_PATTERN).expect("Regex patterns should always copile");
|
||||
let user_in_host = Regex::new(USER_IN_HOST).expect("Regex patterns should always copile");
|
||||
|
||||
let allowed = if let Some(fnd) = allowed_chars_regex.find(uri){
|
||||
if fnd.start() == 0 && fnd.end() == uri.len() {
|
||||
true
|
||||
}else {
|
||||
false
|
||||
}
|
||||
}else{
|
||||
false
|
||||
};
|
||||
let user = user_in_host.is_match(uri);
|
||||
|
||||
!allowed || user
|
||||
}
|
||||
|
||||
pub fn is_basically_valid_uri(uri: &str) -> bool {
|
||||
if uri.contains(" ") {
|
||||
// Quick hack check for a common case
|
||||
return false;
|
||||
}
|
||||
let m = Regex::new(URL_WITH_PROTOCOL_PATTERN).expect("Regex patterns should always copile"); //.matcher(uri);
|
||||
if let Some(found) = m.find(uri) {
|
||||
if found.start() == 0 {
|
||||
// match at start only
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
let m = Regex::new(URL_WITHOUT_PROTOCOL_PATTERN).expect("Regex patterns should always copile"); //.matcher(uri);
|
||||
if let Some(found) = m.find(uri) {
|
||||
if found.start() == 0 {
|
||||
// match at start only
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// }
|
||||
@@ -14,9 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.client.result;
|
||||
// package com.google.zxing.client.result;
|
||||
|
||||
import com.google.zxing.RXingResult;
|
||||
// import com.google.zxing.RXingResult;
|
||||
|
||||
use crate::RXingResult;
|
||||
|
||||
use super::{ParsedClientResult, ResultParser, URIParsedRXingResult};
|
||||
|
||||
/**
|
||||
* Parses the "URLTO" result format, which is of the form "URLTO:[title]:[url]".
|
||||
@@ -25,21 +29,27 @@ import com.google.zxing.RXingResult;
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class URLTORXingResultParser extends RXingResultParser {
|
||||
|
||||
@Override
|
||||
public URIParsedRXingResult parse(RXingResult result) {
|
||||
String rawText = getMassagedText(result);
|
||||
if (!rawText.startsWith("urlto:") && !rawText.startsWith("URLTO:")) {
|
||||
return null;
|
||||
pub fn parse(result: &RXingResult) -> Option<ParsedClientResult> {
|
||||
let rawText = ResultParser::getMassagedText(result);
|
||||
if !rawText.starts_with("urlto:") && !rawText.starts_with("URLTO:") {
|
||||
return None;
|
||||
}
|
||||
int titleEnd = rawText.indexOf(':', 6);
|
||||
if (titleEnd < 0) {
|
||||
return null;
|
||||
}
|
||||
String title = titleEnd <= 6 ? null : rawText.substring(6, titleEnd);
|
||||
String uri = rawText.substring(titleEnd + 1);
|
||||
return new URIParsedRXingResult(uri, title);
|
||||
}
|
||||
let titleEnd = if let Some(pos) = rawText[6..].find(':') {
|
||||
pos + 6
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
let title = if titleEnd <= 6 {
|
||||
""
|
||||
} else {
|
||||
&rawText[6..titleEnd]
|
||||
};
|
||||
let uri = &rawText[titleEnd + 1..];
|
||||
|
||||
}
|
||||
Some(ParsedClientResult::URIResult(URIParsedRXingResult::new(
|
||||
uri.to_owned(),
|
||||
title.to_owned(),
|
||||
)))
|
||||
}
|
||||
|
||||
// }
|
||||
@@ -14,6 +14,9 @@ mod SMSParsedResult;
|
||||
mod SMSMMSResultParser;
|
||||
mod ProductParsedResult;
|
||||
mod ProductResultParser;
|
||||
mod URIParsedResult;
|
||||
mod URIResultParser;
|
||||
mod URLTOResultParser;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
@@ -31,7 +34,7 @@ pub use GeoParsedResult::*;
|
||||
// pub use GeoResultParser::*;
|
||||
pub use SMSParsedResult::*;
|
||||
pub use ProductParsedResult::*;
|
||||
|
||||
pub use URIParsedResult::*;
|
||||
|
||||
#[cfg(test)]
|
||||
mod TelParsedResultTestCase;
|
||||
@@ -45,6 +48,8 @@ mod GeoParsedResultTestCase;
|
||||
mod SMSMMSParsedResultTestCase;
|
||||
#[cfg(test)]
|
||||
mod ProductParsedResultTestCase;
|
||||
#[cfg(test)]
|
||||
mod URIParsedResultTestCase;
|
||||
|
||||
pub enum ParsedClientResult {
|
||||
TextResult(TextParsedRXingResult),
|
||||
@@ -54,6 +59,7 @@ pub enum ParsedClientResult {
|
||||
GeoResult(GeoParsedRXingResult),
|
||||
SMSResult(SMSParsedRXingResult),
|
||||
ProductResult(ProductParsedRXingResult),
|
||||
URIResult(URIParsedRXingResult),
|
||||
}
|
||||
|
||||
impl ParsedRXingResult for ParsedClientResult {
|
||||
@@ -66,6 +72,7 @@ impl ParsedRXingResult for ParsedClientResult {
|
||||
ParsedClientResult::GeoResult(a) => a.getType(),
|
||||
ParsedClientResult::SMSResult(a) => a.getType(),
|
||||
ParsedClientResult::ProductResult(a) => a.getType(),
|
||||
ParsedClientResult::URIResult(a) => a.getType(),
|
||||
|
||||
|
||||
}
|
||||
@@ -80,6 +87,7 @@ impl ParsedRXingResult for ParsedClientResult {
|
||||
ParsedClientResult::GeoResult(a) => a.getDisplayRXingResult(),
|
||||
ParsedClientResult::SMSResult(a) => a.getDisplayRXingResult(),
|
||||
ParsedClientResult::ProductResult(a) => a.getDisplayRXingResult(),
|
||||
ParsedClientResult::URIResult(a) => a.getDisplayRXingResult(),
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user