diff --git a/CHANGELOG b/CHANGELOG index 99d202e5f..3155fc8e8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ +#TESTAR v2.7.21 (24-Feb-2026) +- Bump org.seleniumhq.selenium:selenium-java from 4.40.0 to 4.41.0 +- Update devtools dependencies to v145 +- Ignore dynamic numbers when deduplicating AndroidLogcatOracle messages + + #TESTAR v2.7.21 (17-Feb-2026) - Add logic to detect Android logcat suspicious messages - Refactor the GenerateMode logic to report initialState issues diff --git a/VERSION b/VERSION index 980cb2fae..af14db8ef 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.7.21 \ No newline at end of file +2.7.22 \ No newline at end of file diff --git a/build.gradle b/build.gradle index 8444e8981..66af9ddf7 100644 --- a/build.gradle +++ b/build.gradle @@ -33,7 +33,7 @@ subprojects { implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.15.0' implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.15.0' // https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java - implementation group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '4.40.0' + implementation group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '4.41.0' // https://mvnrepository.com/artifact/io.github.bonigarcia/webdrivermanager implementation group: 'io.github.bonigarcia', name: 'webdrivermanager', version: '6.3.3' // https://mvnrepository.com/artifact/io.appium/java-client diff --git a/testar/resources/settings/webdriver_security_analysis/Protocol_webdriver_security_analysis.java b/testar/resources/settings/webdriver_security_analysis/Protocol_webdriver_security_analysis.java index 142ae431c..db842800f 100644 --- a/testar/resources/settings/webdriver_security_analysis/Protocol_webdriver_security_analysis.java +++ b/testar/resources/settings/webdriver_security_analysis/Protocol_webdriver_security_analysis.java @@ -33,7 +33,7 @@ import org.openqa.selenium.WebElement; import org.openqa.selenium.devtools.DevTools; import org.openqa.selenium.devtools.HasDevTools; -import org.openqa.selenium.devtools.v144.network.Network; +import org.openqa.selenium.devtools.v145.network.Network; import org.openqa.selenium.interactions.Actions; import org.openqa.selenium.remote.RemoteWebDriver; import org.testar.SutVisualization; diff --git a/testar/src/org/testar/monkey/Main.java b/testar/src/org/testar/monkey/Main.java index aff2a03d8..b3db5f6a7 100644 --- a/testar/src/org/testar/monkey/Main.java +++ b/testar/src/org/testar/monkey/Main.java @@ -64,7 +64,7 @@ public class Main { - public static final String TESTAR_VERSION = "v2.7.21 (17-Feb-2026)"; + public static final String TESTAR_VERSION = "v2.7.22 (24-Feb-2026)"; //public static final String TESTAR_DIR_PROPERTY = "DIRNAME"; //Use the OS environment to obtain TESTAR directory public static final String SETTINGS_FILE = "test.settings"; diff --git a/testar/src/org/testar/oracles/log/AndroidLogcatOracle.java b/testar/src/org/testar/oracles/log/AndroidLogcatOracle.java index 42650b4da..c10127f1f 100644 --- a/testar/src/org/testar/oracles/log/AndroidLogcatOracle.java +++ b/testar/src/org/testar/oracles/log/AndroidLogcatOracle.java @@ -185,22 +185,52 @@ private List detectRegexMatches(List lines, String regex) { // logcat threadtime format: // 02-09 08:59:33.844 17550 17575 E Accessibility exception content... - private static final Pattern THREADTIME_PATTERN = Pattern.compile( + private final Pattern THREADTIME_PATTERN = Pattern.compile( "^\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}:\\d{2}\\.\\d{3}\\s+\\d+\\s+\\d+\\s+([VDIWEAF])\\s+([^:]+):\\s*(.*)$" ); - private static String normalizeThreadtimeLine(String line) { + private String normalizeThreadtimeLine(String line) { if (line == null) return ""; line = line.trim(); Matcher m = THREADTIME_PATTERN.matcher(line); if (!m.matches()) { - return line.replaceAll("\\s+", " "); + return normalizeNumbers(line.replaceAll("\\s+", " ")); } String tag = m.group(2).trim(); - String msg = m.group(3).trim().replaceAll("\\s+", " "); + String msg = normalizeNumbers(m.group(3).trim().replaceAll("\\s+", " ")); return tag + ": " + msg; } + private String normalizeNumbers(String text) { + if (text == null || text.isEmpty()) { + return ""; + } + Matcher matcher = Pattern.compile("\\d+").matcher(text); + StringBuffer sb = new StringBuffer(); + while (matcher.find()) { + String num = matcher.group(); + if (isHttpFailureStatus(num)) { + matcher.appendReplacement(sb, num); + } else { + matcher.appendReplacement(sb, ""); + } + } + matcher.appendTail(sb); + return sb.toString(); + } + + private boolean isHttpFailureStatus(String num) { + if (num.length() != 3) { + return false; + } + try { + int value = Integer.parseInt(num); + return value >= 300 && value <= 599; + } catch (NumberFormatException e) { + return false; + } + } + } diff --git a/testar/src/org/testar/securityanalysis/oracles/HeaderAnalysisSecurityOracle.java b/testar/src/org/testar/securityanalysis/oracles/HeaderAnalysisSecurityOracle.java index d9537b5a6..6a7073afd 100644 --- a/testar/src/org/testar/securityanalysis/oracles/HeaderAnalysisSecurityOracle.java +++ b/testar/src/org/testar/securityanalysis/oracles/HeaderAnalysisSecurityOracle.java @@ -31,8 +31,8 @@ package org.testar.securityanalysis.oracles; import org.openqa.selenium.devtools.DevTools; -import org.openqa.selenium.devtools.v144.network.Network; -import org.openqa.selenium.devtools.v144.network.model.Headers; +import org.openqa.selenium.devtools.v145.network.Network; +import org.openqa.selenium.devtools.v145.network.model.Headers; import org.testar.monkey.alayer.Verdict; import org.testar.monkey.alayer.webdriver.WdDriver; import org.testar.securityanalysis.NetworkCollector; diff --git a/testar/src/org/testar/securityanalysis/oracles/SqlInjectionSecurityOracle.java b/testar/src/org/testar/securityanalysis/oracles/SqlInjectionSecurityOracle.java index c8419d761..3067c102f 100644 --- a/testar/src/org/testar/securityanalysis/oracles/SqlInjectionSecurityOracle.java +++ b/testar/src/org/testar/securityanalysis/oracles/SqlInjectionSecurityOracle.java @@ -31,7 +31,7 @@ package org.testar.securityanalysis.oracles; import org.openqa.selenium.devtools.DevTools; -import org.openqa.selenium.devtools.v144.network.Network; +import org.openqa.selenium.devtools.v145.network.Network; import org.openqa.selenium.remote.RemoteWebDriver; import org.testar.monkey.alayer.*; import org.testar.monkey.alayer.actions.WdSecurityInjectionAction; diff --git a/testar/test/org/testar/oracles/log/TestAndroidLogcatOracle.java b/testar/test/org/testar/oracles/log/TestAndroidLogcatOracle.java index f66fe80ed..1f249d798 100644 --- a/testar/test/org/testar/oracles/log/TestAndroidLogcatOracle.java +++ b/testar/test/org/testar/oracles/log/TestAndroidLogcatOracle.java @@ -146,6 +146,63 @@ public void generateModeVerdict_DeduplicatesAndOrdersMatches() { } } + @Test + public void generateModeVerdict_DeduplicatesNumbersInMatches() { + OutputStructure.logsOutputDir = Path.of("target").toString(); + OutputStructure.startInnerLoopDateString = "YYYY-MM-DD_hh-mm-ss"; + OutputStructure.executedSUTname = "test-sut"; + + Settings settings = buildSettings(RuntimeControlsProtocol.Modes.Generate, "(?i)(.*Exception.*)"); + AndroidLogcatOracle androidLogcatOracle = new AndroidLogcatOracle(settings); + State state = Mockito.mock(State.class); + + String line1 = "02-09 08:59:33.844 17550 17575 E ViewRootImpl: Exception @1:207875, unable to find 3421 viewState"; + String line2 = "02-09 08:59:33.845 17550 17575 E ViewRootImpl: Exception @1:204868, unable to find 9008 viewState"; + + try (MockedStatic mocked = Mockito.mockStatic(AndroidAppiumFramework.class)) { + mocked.when(AndroidAppiumFramework::getAppPackageFromCapabilitiesOrCurrent).thenReturn("org.testar.app"); + mocked.when(() -> AndroidAppiumFramework.dumpLogcatThreadtimeForPackage("org.testar.app")) + .thenReturn(line1 + "\n" + line2); + + androidLogcatOracle.initialize(); + Verdict verdict = androidLogcatOracle.getVerdict(state); + + Assert.assertEquals(Verdict.Severity.SUSPICIOUS_LOG.getValue(), verdict.severity(), 0.0); + String expected = "Suspicious Android logcat line(s) detected " + + "ViewRootImpl: Exception @:, unable to find viewState"; + Assert.assertEquals(expected, verdict.info()); + } + } + + @Test + public void generateModeVerdict_KeepsHttpStatusCodes() { + OutputStructure.logsOutputDir = Path.of("target").toString(); + OutputStructure.startInnerLoopDateString = "YYYY-MM-DD_hh-mm-ss"; + OutputStructure.executedSUTname = "test-sut"; + + Settings settings = buildSettings(RuntimeControlsProtocol.Modes.Generate, "(?i)(.*Exception.*)"); + AndroidLogcatOracle androidLogcatOracle = new AndroidLogcatOracle(settings); + State state = Mockito.mock(State.class); + + String line1 = "02-09 08:59:33.844 17550 17575 E ViewRootImpl: Exception, http status 404"; + String line2 = "02-09 08:59:33.845 17550 17575 E ViewRootImpl: Exception, http status 503"; + + try (MockedStatic mocked = Mockito.mockStatic(AndroidAppiumFramework.class)) { + mocked.when(AndroidAppiumFramework::getAppPackageFromCapabilitiesOrCurrent).thenReturn("org.testar.app"); + mocked.when(() -> AndroidAppiumFramework.dumpLogcatThreadtimeForPackage("org.testar.app")) + .thenReturn(line1 + "\n" + line2); + + androidLogcatOracle.initialize(); + Verdict verdict = androidLogcatOracle.getVerdict(state); + + Assert.assertEquals(Verdict.Severity.SUSPICIOUS_LOG.getValue(), verdict.severity(), 0.0); + String expected = "Suspicious Android logcat line(s) detected " + + "ViewRootImpl: Exception, http status 404" + + " | ViewRootImpl: Exception, http status 503"; + Assert.assertEquals(expected, verdict.info()); + } + } + private Settings buildSettings(RuntimeControlsProtocol.Modes mode, String regex) { List> tags = new ArrayList<>(); tags.add(Pair.from(ConfigTags.Mode, mode));