most uri pass docomo and exotic fail

This commit is contained in:
Henry Schimke
2022-09-08 12:34:12 -05:00
parent 9c7beb2073
commit c7db80cfb8
10 changed files with 449 additions and 344 deletions

View File

@@ -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")
}

View File

@@ -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)
}

View File

@@ -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);
}
}

View 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);
}
}

View File

@@ -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());
}
}

View 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()
);
}
// }

View File

@@ -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;
}
}

View 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
}
}
// }

View File

@@ -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(),
)))
}
// }

View File

@@ -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(),