diff --git a/.github/workflows/maven-tests.yml b/.github/workflows/maven-tests.yml new file mode 100644 index 0000000..b5fa596 --- /dev/null +++ b/.github/workflows/maven-tests.yml @@ -0,0 +1,30 @@ +name: Maven Tests + +on: + push: + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + + - name: Test with Maven + run: mvn -B -Dmaven.test.failure.ignore=true test + + - name: Surefire Report + uses: dorny/test-reporter@v1 + if: always() + with: + name: Maven Tests + path: target/surefire-reports/*.xml + reporter: java-junit + fail-on-error: true diff --git a/.gitignore b/.gitignore index a700395..1b846e1 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ buildNumber.properties .classpath # manually added +.attach_pid* out/ *.iml src/build/ diff --git a/pom.xml b/pom.xml index 05ac174..9a65d95 100644 --- a/pom.xml +++ b/pom.xml @@ -37,6 +37,24 @@ maven-resources-plugin 3.3.1 + + org.junit.jupiter + junit-jupiter + 5.10.1 + test + + + org.assertj + assertj-core + 3.24.2 + test + + + org.awaitility + awaitility + 4.2.0 + test + @@ -98,6 +116,10 @@ + + maven-surefire-plugin + 3.2.3 + \ No newline at end of file diff --git a/src/main/java/burp/BurpExtender.java b/src/main/java/burp/BurpExtender.java index 6ed6b5f..ce073b2 100644 --- a/src/main/java/burp/BurpExtender.java +++ b/src/main/java/burp/BurpExtender.java @@ -13,7 +13,7 @@ public class BurpExtender implements IBurpExtender { public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks) { try { MainUI mainUI = new MainUI(callbacks); - mainUI.initialize(); + mainUI.initializeUI(); callbacks.setExtensionName(mainUI.getNameExtension()); diff --git a/src/main/java/com/cys4/sensitivediscoverer/MainUI.java b/src/main/java/com/cys4/sensitivediscoverer/MainUI.java index 3f35606..fef5021 100644 --- a/src/main/java/com/cys4/sensitivediscoverer/MainUI.java +++ b/src/main/java/com/cys4/sensitivediscoverer/MainUI.java @@ -29,8 +29,10 @@ public class MainUI implements ITab { private final Properties configProperties; private final ScannerOptions scannerOptions; private JTabbedPane mainPanel; + private boolean interfaceInitialized; public MainUI(IBurpExtenderCallbacks callbacks) throws Exception { + this.interfaceInitialized = false; this.callbacks = callbacks; // setup stdout/stderr @@ -50,6 +52,10 @@ public MainUI(IBurpExtenderCallbacks callbacks) throws Exception { this.extensionsRegexList = RegexSeeder.getExtensionRegexes(); } + public boolean isInterfaceInitialized() { + return interfaceInitialized; + } + public ScannerOptions getScannerOptions() { return scannerOptions; } @@ -57,11 +63,11 @@ public ScannerOptions getScannerOptions() { /** * Main function that initializes the extension and creates the UI, asynchronously */ - public void initialize() { - SwingUtilities.invokeLater(this::_initialize); + public void initializeUI() { + SwingUtilities.invokeLater(this::_initializeUI); } - private void _initialize() { + private void _initializeUI() { mainPanel = new JTabbedPane(); LoggerTab loggerTab = new LoggerTab(this); mainPanel.addTab(loggerTab.getTabName(), loggerTab.getPanel()); @@ -72,6 +78,8 @@ private void _initialize() { callbacks.customizeUiComponent(mainPanel); callbacks.addSuiteTab(MainUI.this); + + this.interfaceInitialized = true; } /** diff --git a/src/main/java/com/cys4/sensitivediscoverer/RegexSeeder.java b/src/main/java/com/cys4/sensitivediscoverer/RegexSeeder.java index 703b7a8..3049467 100644 --- a/src/main/java/com/cys4/sensitivediscoverer/RegexSeeder.java +++ b/src/main/java/com/cys4/sensitivediscoverer/RegexSeeder.java @@ -34,7 +34,8 @@ private static List fill(String[] regexFiles) { element.getDescription(), element.getRegex(), element.isActive(), - ProxyItemSection.deserializeSections(element.getSections()))) + ProxyItemSection.deserializeSections(element.getSections()), + element.getTests())) .collect(Collectors.toList()); } diff --git a/src/main/java/com/cys4/sensitivediscoverer/model/JsonRegexEntity.java b/src/main/java/com/cys4/sensitivediscoverer/model/JsonRegexEntity.java index 6ade373..ac39ef4 100644 --- a/src/main/java/com/cys4/sensitivediscoverer/model/JsonRegexEntity.java +++ b/src/main/java/com/cys4/sensitivediscoverer/model/JsonRegexEntity.java @@ -14,6 +14,7 @@ public class JsonRegexEntity { private String regex; private String description; private List sections; + private List tests; public JsonRegexEntity() { } @@ -33,4 +34,8 @@ public String getDescription() { public List getSections() { return sections; } + + public List getTests() { + return tests; + } } diff --git a/src/main/java/com/cys4/sensitivediscoverer/model/RegexEntity.java b/src/main/java/com/cys4/sensitivediscoverer/model/RegexEntity.java index 7872602..6f9e5f7 100644 --- a/src/main/java/com/cys4/sensitivediscoverer/model/RegexEntity.java +++ b/src/main/java/com/cys4/sensitivediscoverer/model/RegexEntity.java @@ -5,6 +5,7 @@ package com.cys4.sensitivediscoverer.model; import java.util.EnumSet; +import java.util.List; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -20,27 +21,34 @@ public class RegexEntity { private final transient Pattern regexCompiled; private final String description; private final EnumSet sections; + private final List tests; private boolean active; - public RegexEntity(String description, String regex) throws IllegalArgumentException { - this(description, regex, true, ProxyItemSection.getDefault()); + this(description, regex, true, ProxyItemSection.getDefault(), null); } public RegexEntity(String description, String regex, boolean active) throws IllegalArgumentException { - this(description, regex, active, ProxyItemSection.getDefault()); + this(description, regex, active, ProxyItemSection.getDefault(), null); } public RegexEntity(String description, String regex, boolean active, EnumSet sections) { - if (regex == null || regex.isBlank()) + this(description, regex, active, sections, null); + } + + public RegexEntity(String description, String regex, boolean active, EnumSet sections, List tests) { + if (regex == null || regex.isBlank()) { throw new IllegalArgumentException(getLocaleString("exception-invalidRegex")); - if (sections == null) + } + if (sections == null) { throw new IllegalArgumentException(getLocaleString("exception-invalidSections")); + } this.active = active; this.description = description; this.regex = regex; this.regexCompiled = Pattern.compile(regex); this.sections = sections; + this.tests = tests; } public RegexEntity(RegexEntity entity) throws IllegalArgumentException { @@ -59,6 +67,10 @@ public static Matcher checkRegexEntityFromCSV(String input) { .matcher(input); } + public List getTests() { + return tests; + } + public boolean isActive() { return this.active; } diff --git a/src/main/resources/regexes/extension_general.json b/src/main/resources/regexes/extension_general.json index ee0f919..94d4154 100644 --- a/src/main/resources/regexes/extension_general.json +++ b/src/main/resources/regexes/extension_general.json @@ -3,384 +3,448 @@ "active": true, "description": "1Password password manager database file", "regex": "\\.agilekeychain$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.agilekeychain"] }, { "active": true, "description": "ASP configuration file", "regex": "\\.asa$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.asa"] }, { "active": true, "description": "Apple Keychain database file", "regex": "\\.keychain$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.keychain"] }, { "active": true, "description": "Azure service configuration schema file", "regex": "\\.cscfg$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.cscfg"] }, { "active": true, "description": "Backup file", "regex": "\\.bak$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.bak"] }, { "active": true, "description": "Certificate file", "regex": "\\.cer$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.cer"] }, { "active": true, "description": "Certificate file", "regex": "\\.crt$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.crt"] }, { "active": true, "description": "Certificate file", "regex": "\\.p7b$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.p7b"] }, { "active": true, "description": "Compressed archive file", "regex": "\\.zip$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.zip"] }, { "active": true, "description": "Compressed archive file", "regex": "\\.tar$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.tar"] }, { "active": true, "description": "Compressed archive file", "regex": "\\.gz$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.gz"] }, { "active": true, "description": "Compressed archive file", "regex": "\\.tgz$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.tgz"] }, { "active": true, "description": "Compressed archive file", "regex": "\\.rar$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.rar"] }, { "active": true, "description": "Configuration file", "regex": "\\.config$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.config"] }, { "active": true, "description": "Day One journal file", "regex": "\\.dayone$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.dayone"] }, { "active": true, "description": "Document file", "regex": "\\.docx$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.docx"] }, { "active": true, "description": "Document file", "regex": "\\.doc$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.doc"] }, { "active": true, "description": "Document file", "regex": "\\.rtf$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.rtf"] }, { "active": true, "description": "Excel file", "regex": "\\.xlsx$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.xlsx"] }, { "active": true, "description": "Excel file", "regex": "\\.xls$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.xls"] }, { "active": true, "description": "Excel file", "regex": "\\.csv$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.csv"] }, { "active": true, "description": "GnuCash database file", "regex": "\\.gnucash$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.gnucash"] }, { "active": true, "description": "Include file", "regex": "\\.inc$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.inc"] }, { "active": true, "description": "Java file", "regex": "\\.java$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.java"] }, { "active": true, "description": "Java keystore file", "regex": "\\.jks$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.jks"] }, { "active": true, "description": "KDE Wallet Manager database file", "regex": "\\.kwallet$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.kwallet"] }, { "active": true, "description": "Little Snitch firewall configuration file", "regex": "\\.xpl$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.xpl"] }, { "active": true, "description": "Log file", "regex": "\\.log$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.log"] }, { "active": true, "description": "Microsoft BitLocker Trusted Platform Module password file", "regex": "\\.tpm$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.tpm"] }, { "active": true, "description": "Microsoft BitLocker recovery key file", "regex": "\\.bek$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.bek"] }, { "active": true, "description": "Microsoft SQL database file", "regex": "\\.mdf$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.mdf"] }, { "active": true, "description": "Microsoft SQL server compact database file", "regex": "\\.sdf$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.sdf"] }, { "active": true, "description": "Network traffic capture file", "regex": "\\.pcap$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.pcap"] }, { "active": true, "description": "Old file", "regex": "\\.old$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.old"] }, { "active": true, "description": "OpenVPN client configuration file", "regex": "\\.ovpn$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.ovpn"] }, { "active": true, "description": "PDF file", "regex": "\\.pdf$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.pdf"] }, { "active": true, "description": "PHP file", "regex": "\\.php$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.php"] }, { "active": true, "description": "Password Safe database file", "regex": "\\.psafe3$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.psafe3"] }, { "active": true, "description": "Potential configuration file", "regex": "\\.yml$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.yml"] }, { "active": true, "description": "Potential cryptographic key bundle", "regex": "\\.pkcs12$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.pkcs12"] }, { "active": true, "description": "Potential cryptographic key bundle", "regex": "\\.p12$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.p12"] }, { "active": true, "description": "Potential cryptographic key bundle", "regex": "\\.pfx$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.pfx"] }, { "active": true, "description": "Potential PGP public keyring", "regex": "\\.pkr$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.pkr"] }, { "active": true, "description": "Potential PGP secret keyring", "regex": "\\.skr$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.skr"] }, { "active": true, "description": "Potential cryptographic key bundle", "regex": "\\.asc$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.asc"] }, { "active": true, "description": "Potential cryptographic private key", "regex": "\\.pem$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.pem"] }, { "active": true, "description": "Potential private key", - "regex": "otr.private_key$", - "sections": ["req_url"] + "regex": "\\.private_key$", + "sections": ["req_url"], + "tests": ["otr.private_key", "test_file.private_key"] }, { "active": true, "description": "Presentation file", "regex": "\\.pptx$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.pptx"] }, { "active": true, "description": "Presentation file", "regex": "\\.ppt$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.ppt"] }, { "active": true, "description": "Python file", "regex": "\\.py$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.py"] }, { "active": true, "description": "Remote Desktop connection file", "regex": "\\.rdp$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.rdp"] }, { "active": true, "description": "Ruby On Rails file", "regex": "\\.rb$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.rb"] }, { "active": true, "description": "SQLite database file", "regex": "\\.sqlite$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.sqlite"] }, { "active": true, "description": "SQLite3 database file", "regex": "\\.sqlite3$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.sqlite3"] }, { "active": true, "description": "Sequel Pro MySQL database manager bookmark file", "regex": "\\.plist$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.plist"] }, { "active": true, "description": "Shell configuration file", "regex": "\\.exports$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.exports"] }, { "active": true, "description": "Shell configuration file", "regex": "\\.functions$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.functions"] }, { "active": true, "description": "Shell configuration file", "regex": "\\.extra$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.extra"] }, { "active": true, "description": "Temporary file", "regex": "\\.tmp$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.tmp"] }, { "active": true, "description": "Terraform variable config file", - "regex": "\\terraform.tfvars$", - "sections": ["req_url"] + "regex": "terraform\\.tfvars$", + "sections": ["req_url"], + "tests": ["terraform.tfvars"] }, { "active": true, "description": "Text file", "regex": "\\.txt$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.txt"] }, { "active": true, "description": "Tunnelblick VPN configuration file", "regex": "\\.tblk$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.tblk"] }, { "active": true, "description": "Windows BitLocker full volume encrypted data file", "regex": "\\.fve$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.fve"] }, { "active": true, "description": "XML file", "regex": "\\.xml$", - "sections": ["req_url"] + "sections": ["req_url"], + "tests": ["test_file.xml"] } ] diff --git a/src/main/resources/regexes/regex_general.jsonc b/src/main/resources/regexes/regex_general.jsonc index 91e6e87..ff17af4 100644 --- a/src/main/resources/regexes/regex_general.jsonc +++ b/src/main/resources/regexes/regex_general.jsonc @@ -2,27 +2,51 @@ { "active": true, "description": "Encapsulation boundary for data such as keys and certificates", - "regex": "-----BEGIN" + "regex": "-----BEGIN", + "tests": [ + "-----BEGIN CERTIFICATE-----", + "-----BEGIN RSA PRIVATE KEY-----", + "-----BEGIN PRIVATE KEY-----" + ] }, { "active": true, "description": "Generic API Key", - "regex": "(?i)api.{0,5}key[^&|;?,]{0,32}?['\\\"][a-zA-Z0-9_\\-+=\\/\\\\]{10,}['\\\"]" + "regex": "(?i)api.{0,5}key[^&|;?,]{0,32}?['\\\"][a-zA-Z0-9_\\-+=\\/\\\\]{10,}['\\\"]", + "tests":[ + "{backend_api_key:\"ABCDEFGH/12\"}", + "{\"apikey_mail\" : 'ABCDEFGH=12'}", + "{\"mail_apikey\" : \"ABCDEFGH-12\"}" + ] }, { "active": true, "description": "Generic Secret", - "regex": "(?i)secret[^&|;?,]{0,32}?['\\\"][a-zA-Z0-9_\\-+=\\/\\\\]{10,}['\\\"]" + "regex": "(?i)secret[^&|;?,]{0,32}?['\\\"][a-zA-Z0-9_\\-+=\\/\\\\]{10,}['\\\"]", + "tests":[ + "{\"my_secret_key\":\"ABCDEFGH12\"}", + "{'my_super_secret' : 'ABCDEFGH-12'}" + ] }, { // Try to match only IPs and not section numbers of js libraries "active": true, "description": "IP Address", - "regex": "(? generalRegexList = List.of(); + private final List extensionsRegexList = List.of(); + private RegexScanner regexScanner; + private BurpExtenderCallbacksMock burpExtenderCallbacks; + private ExtensionHelpersMock extensionHelpers; + private ScannerOptions scannerOptions; + + private static void accept(Integer integer) { + } + + private byte[] B(String str) { + return str.getBytes(StandardCharsets.UTF_8); + } + + private HttpRequestResponseMock HRR(String req, String res) { + return new HttpRequestResponseMock(B(req), B(res)); + } + + @BeforeEach + void setUp() { + extensionHelpers = new ExtensionHelpersMock(); + //TODO missing way to test url & headers + extensionHelpers.setAnalyzeRequestFunction(httpRequestResponse -> { + try { + return new RequestInfoMock(List.of(), new URL("https://null")); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + }); + //TODO missing way to test headers + extensionHelpers.setAnalyzeResponseFunction(response -> new ResponseInfoMock(List.of())); + extensionHelpers.setBytesToStringFunction(bytes -> new String(bytes, StandardCharsets.UTF_8)); + burpExtenderCallbacks = new BurpExtenderCallbacksMock(); + burpExtenderCallbacks.setHelpers(extensionHelpers); + + scannerOptions = new ScannerOptions(); + scannerOptions.setConfigMaxResponseSize(10000000); + scannerOptions.setConfigNumberOfThreads(1); + scannerOptions.setFilterInScopeCheckbox(false); + scannerOptions.setFilterSkipMaxSizeCheckbox(false); + scannerOptions.setFilterSkipMediaTypeCheckbox(false); + + this.regexScanner = new RegexScanner(burpExtenderCallbacks, scannerOptions, generalRegexList, extensionsRegexList); + } + + @Test + void testAnalysisOfGeneralRegexes() { + final List logEntities = new ArrayList<>(); + final Object loggerLock = new Object(); + + // set-up callbacks + Consumer itemAnalyzedCallback = RegexScannerTest::accept; + Consumer logEntityConsumer = logEntity -> { + synchronized (loggerLock) { + logEntities.add(logEntity); + } + }; + // set-up BurpExtenderCallbacks + this.burpExtenderCallbacks.setIsInScopePredicate(url -> true); + HttpRequestResponseMock[] proxyHistory = { + HRR("testing", "testing") + }; + this.burpExtenderCallbacks.setProxyHistory(proxyHistory); + + // test 1 + logEntities.clear(); + this.regexScanner = new RegexScanner(this.burpExtenderCallbacks, this.scannerOptions, + List.of( + new RegexEntity("Match test string", "test", true, ProxyItemSection.ALL) + ), + List.of()); + regexScanner.analyzeProxyHistory(itemAnalyzedCallback, logEntityConsumer); + assertThat(logEntities).as("Check count of entries found").hasSize(2); + + // test 2 + logEntities.clear(); + this.regexScanner = new RegexScanner(this.burpExtenderCallbacks, this.scannerOptions, + List.of( + new RegexEntity("Match random string", "random", true, ProxyItemSection.ALL) + ), + List.of()); + regexScanner.analyzeProxyHistory(itemAnalyzedCallback, logEntityConsumer); + assertThat(logEntities).as("Check count of entries found").hasSize(0); + } +} \ No newline at end of file diff --git a/src/test/java/com/cys4/sensitivediscoverer/RegexSeederTest.java b/src/test/java/com/cys4/sensitivediscoverer/RegexSeederTest.java new file mode 100644 index 0000000..7a63f8d --- /dev/null +++ b/src/test/java/com/cys4/sensitivediscoverer/RegexSeederTest.java @@ -0,0 +1,48 @@ +package com.cys4.sensitivediscoverer; + +import com.cys4.sensitivediscoverer.model.RegexEntity; +import org.assertj.core.api.Condition; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class RegexSeederTest { + private final Condition positiveMatch = new Condition<>(s -> !(s.startsWith("!")), "positive match"); + + /** + * Assert that each regex in the list has at least one test string and also assert that each test string matches correctly + * @param regexList The regex list + */ + private void testRegexList(List regexList) { + assertThat(regexList) + .isNotEmpty() + .allSatisfy(regex -> { + assertThat(regex.getTests()) + .as("%s", regex.getDescription()) + .isNotEmpty() + .haveAtLeastOne(positiveMatch) + .allSatisfy(s -> { + if (s.startsWith("!")) + assertThat(s.substring(1)) + .isNotEmpty() + .doesNotContainPattern(regex.getRegexCompiled()); + else + assertThat(s) + .isNotEmpty() + .containsPattern(regex.getRegexCompiled()); + }); + }); + } + + @Test + public void generalRegexesMatching() { + testRegexList(RegexSeeder.getGeneralRegexes()); + } + + @Test + public void extensionRegexesMatching() { + testRegexList(RegexSeeder.getExtensionRegexes()); + } +} \ No newline at end of file diff --git a/src/test/java/com/cys4/sensitivediscoverer/mock/BurpExtenderCallbacksMock.java b/src/test/java/com/cys4/sensitivediscoverer/mock/BurpExtenderCallbacksMock.java new file mode 100644 index 0000000..b8a4e03 --- /dev/null +++ b/src/test/java/com/cys4/sensitivediscoverer/mock/BurpExtenderCallbacksMock.java @@ -0,0 +1,515 @@ +package com.cys4.sensitivediscoverer.mock; + +import burp.*; +import org.apache.commons.lang3.NotImplementedException; + +import java.awt.*; +import java.io.File; +import java.io.OutputStream; +import java.net.URL; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Predicate; + +public class BurpExtenderCallbacksMock implements IBurpExtenderCallbacks { + private IExtensionHelpers extensionHelpers; + private IHttpRequestResponse[] proxyHistory; + private Predicate isInScopePredicate; + + @Override + public IExtensionHelpers getHelpers() { + return extensionHelpers; + } + + public void setHelpers(IExtensionHelpers extensionHelpers) { + this.extensionHelpers = extensionHelpers; + } + + @Override + public IHttpRequestResponse[] getProxyHistory() { + return proxyHistory; + } + + public void setProxyHistory(IHttpRequestResponse[] proxyHistory) { + this.proxyHistory = proxyHistory; + } + + @Override + public boolean isInScope(URL url) { + return isInScopePredicate.test(url); + } + + public void setIsInScopePredicate(Predicate isInScopePredicate) { + this.isInScopePredicate = isInScopePredicate; + } + + @Override + public void addSuiteTab(ITab tab) { + if (Objects.isNull(tab)) throw new IllegalArgumentException("Tab is null"); + } + + @Override + public void customizeUiComponent(Component component) { + } + + @Override + public OutputStream getStdout() { + return System.out; + } + + @Override + public OutputStream getStderr() { + return System.err; + } + + @Override + public ITextEditor createTextEditor() { + return new TextEditorMock(); + } + + @Override + public void setExtensionName(String s) { + throw new NotImplementedException(); + } + + @Override + public void printOutput(String s) { + throw new NotImplementedException(); + } + + @Override + public void printError(String s) { + throw new NotImplementedException(); + } + + @Override + public void registerExtensionStateListener(IExtensionStateListener iExtensionStateListener) { + throw new NotImplementedException(); + } + + @Override + public List getExtensionStateListeners() { + throw new NotImplementedException(); + } + + @Override + public void removeExtensionStateListener(IExtensionStateListener iExtensionStateListener) { + throw new NotImplementedException(); + } + + @Override + public void registerHttpListener(IHttpListener iHttpListener) { + throw new NotImplementedException(); + } + + @Override + public List getHttpListeners() { + throw new NotImplementedException(); + } + + @Override + public void removeHttpListener(IHttpListener iHttpListener) { + throw new NotImplementedException(); + } + + @Override + public void registerProxyListener(IProxyListener iProxyListener) { + throw new NotImplementedException(); + } + + @Override + public List getProxyListeners() { + throw new NotImplementedException(); + } + + @Override + public void removeProxyListener(IProxyListener iProxyListener) { + throw new NotImplementedException(); + } + + @Override + public void registerScannerListener(IScannerListener iScannerListener) { + throw new NotImplementedException(); + } + + @Override + public List getScannerListeners() { + throw new NotImplementedException(); + } + + @Override + public void removeScannerListener(IScannerListener iScannerListener) { + throw new NotImplementedException(); + } + + @Override + public void registerScopeChangeListener(IScopeChangeListener iScopeChangeListener) { + throw new NotImplementedException(); + } + + @Override + public List getScopeChangeListeners() { + throw new NotImplementedException(); + } + + @Override + public void removeScopeChangeListener(IScopeChangeListener iScopeChangeListener) { + throw new NotImplementedException(); + } + + @Override + public void registerContextMenuFactory(IContextMenuFactory iContextMenuFactory) { + throw new NotImplementedException(); + } + + @Override + public List getContextMenuFactories() { + throw new NotImplementedException(); + } + + @Override + public void removeContextMenuFactory(IContextMenuFactory iContextMenuFactory) { + throw new NotImplementedException(); + } + + @Override + public void registerMessageEditorTabFactory(IMessageEditorTabFactory iMessageEditorTabFactory) { + throw new NotImplementedException(); + } + + @Override + public List getMessageEditorTabFactories() { + throw new NotImplementedException(); + } + + @Override + public void removeMessageEditorTabFactory(IMessageEditorTabFactory iMessageEditorTabFactory) { + throw new NotImplementedException(); + } + + @Override + public void registerScannerInsertionPointProvider(IScannerInsertionPointProvider iScannerInsertionPointProvider) { + throw new NotImplementedException(); + } + + @Override + public List getScannerInsertionPointProviders() { + throw new NotImplementedException(); + } + + @Override + public void removeScannerInsertionPointProvider(IScannerInsertionPointProvider iScannerInsertionPointProvider) { + throw new NotImplementedException(); + } + + @Override + public void registerScannerCheck(IScannerCheck iScannerCheck) { + throw new NotImplementedException(); + } + + @Override + public List getScannerChecks() { + throw new NotImplementedException(); + } + + @Override + public void removeScannerCheck(IScannerCheck iScannerCheck) { + throw new NotImplementedException(); + } + + @Override + public void registerIntruderPayloadGeneratorFactory(IIntruderPayloadGeneratorFactory iIntruderPayloadGeneratorFactory) { + throw new NotImplementedException(); + } + + @Override + public List getIntruderPayloadGeneratorFactories() { + throw new NotImplementedException(); + } + + @Override + public void removeIntruderPayloadGeneratorFactory(IIntruderPayloadGeneratorFactory iIntruderPayloadGeneratorFactory) { + throw new NotImplementedException(); + } + + @Override + public void registerIntruderPayloadProcessor(IIntruderPayloadProcessor iIntruderPayloadProcessor) { + throw new NotImplementedException(); + } + + @Override + public List getIntruderPayloadProcessors() { + throw new NotImplementedException(); + } + + @Override + public void removeIntruderPayloadProcessor(IIntruderPayloadProcessor iIntruderPayloadProcessor) { + throw new NotImplementedException(); + } + + @Override + public void registerSessionHandlingAction(ISessionHandlingAction iSessionHandlingAction) { + throw new NotImplementedException(); + } + + @Override + public List getSessionHandlingActions() { + throw new NotImplementedException(); + } + + @Override + public void removeSessionHandlingAction(ISessionHandlingAction iSessionHandlingAction) { + throw new NotImplementedException(); + } + + @Override + public void unloadExtension() { + throw new NotImplementedException(); + } + + @Override + public void removeSuiteTab(ITab iTab) { + throw new NotImplementedException(); + } + + @Override + public IMessageEditor createMessageEditor(IMessageEditorController iMessageEditorController, boolean b) { + throw new NotImplementedException(); + } + + @Override + public String[] getCommandLineArguments() { + throw new NotImplementedException(); + } + + @Override + public void saveExtensionSetting(String s, String s1) { + throw new NotImplementedException(); + } + + @Override + public String loadExtensionSetting(String s) { + throw new NotImplementedException(); + } + + @Override + public void sendToRepeater(String s, int i, boolean b, byte[] bytes, String s1) { + throw new NotImplementedException(); + } + + @Override + public void sendToIntruder(String s, int i, boolean b, byte[] bytes) { + throw new NotImplementedException(); + } + + @Override + public void sendToIntruder(String s, int i, boolean b, byte[] bytes, List list) { + throw new NotImplementedException(); + } + + @Override + public void sendToComparer(byte[] bytes) { + throw new NotImplementedException(); + } + + @Override + public void sendToSpider(URL url) { + throw new NotImplementedException(); + } + + @Override + public IScanQueueItem doActiveScan(String s, int i, boolean b, byte[] bytes) { + throw new NotImplementedException(); + } + + @Override + public IScanQueueItem doActiveScan(String s, int i, boolean b, byte[] bytes, List list) { + throw new NotImplementedException(); + } + + @Override + public void doPassiveScan(String s, int i, boolean b, byte[] bytes, byte[] bytes1) { + throw new NotImplementedException(); + } + + @Override + public IHttpRequestResponse makeHttpRequest(IHttpService iHttpService, byte[] bytes) { + throw new NotImplementedException(); + } + + @Override + public IHttpRequestResponse makeHttpRequest(IHttpService iHttpService, byte[] bytes, boolean b) { + throw new NotImplementedException(); + } + + @Override + public byte[] makeHttpRequest(String s, int i, boolean b, byte[] bytes) { + throw new NotImplementedException(); + } + + @Override + public byte[] makeHttpRequest(String s, int i, boolean b, byte[] bytes, boolean b1) { + throw new NotImplementedException(); + } + + @Override + public byte[] makeHttp2Request(IHttpService iHttpService, List list, byte[] bytes) { + throw new NotImplementedException(); + } + + @Override + public byte[] makeHttp2Request(IHttpService iHttpService, List list, byte[] bytes, boolean b) { + throw new NotImplementedException(); + } + + @Override + public byte[] makeHttp2Request(IHttpService iHttpService, List list, byte[] bytes, boolean b, String s) { + throw new NotImplementedException(); + } + + @Override + public void includeInScope(URL url) { + throw new NotImplementedException(); + } + + @Override + public void excludeFromScope(URL url) { + throw new NotImplementedException(); + } + + @Override + public void issueAlert(String s) { + throw new NotImplementedException(); + } + + @Override + public IHttpRequestResponse[] getSiteMap(String s) { + throw new NotImplementedException(); + } + + @Override + public IScanIssue[] getScanIssues(String s) { + throw new NotImplementedException(); + } + + @Override + public void generateScanReport(String s, IScanIssue[] iScanIssues, File file) { + throw new NotImplementedException(); + } + + @Override + public List getCookieJarContents() { + throw new NotImplementedException(); + } + + @Override + public void updateCookieJar(ICookie iCookie) { + throw new NotImplementedException(); + } + + @Override + public void addToSiteMap(IHttpRequestResponse iHttpRequestResponse) { + throw new NotImplementedException(); + } + + @Override + public void restoreState(File file) { + throw new NotImplementedException(); + } + + @Override + public void saveState(File file) { + throw new NotImplementedException(); + } + + @Override + public Map saveConfig() { + throw new NotImplementedException(); + } + + @Override + public void loadConfig(Map map) { + throw new NotImplementedException(); + } + + @Override + public String saveConfigAsJson(String... strings) { + throw new NotImplementedException(); + } + + @Override + public void loadConfigFromJson(String s) { + throw new NotImplementedException(); + } + + @Override + public void setProxyInterceptionEnabled(boolean b) { + throw new NotImplementedException(); + } + + @Override + public String[] getBurpVersion() { + throw new NotImplementedException(); + } + + @Override + public String getExtensionFilename() { + throw new NotImplementedException(); + } + + @Override + public boolean isExtensionBapp() { + throw new NotImplementedException(); + } + + @Override + public void exitSuite(boolean b) { + throw new NotImplementedException(); + } + + @Override + public ITempFile saveToTempFile(byte[] bytes) { + throw new NotImplementedException(); + } + + @Override + public IHttpRequestResponsePersisted saveBuffersToTempFiles(IHttpRequestResponse iHttpRequestResponse) { + throw new NotImplementedException(); + } + + @Override + public IHttpRequestResponseWithMarkers applyMarkers(IHttpRequestResponse iHttpRequestResponse, List list, List list1) { + throw new NotImplementedException(); + } + + @Override + public String getToolName(int i) { + throw new NotImplementedException(); + } + + @Override + public void addScanIssue(IScanIssue iScanIssue) { + throw new NotImplementedException(); + } + + @Override + public IBurpCollaboratorClientContext createBurpCollaboratorClientContext() { + throw new NotImplementedException(); + } + + @Override + public String[][] getParameters(byte[] bytes) { + throw new NotImplementedException(); + } + + @Override + public String[] getHeaders(byte[] bytes) { + throw new NotImplementedException(); + } + + @Override + public void registerMenuItem(String s, IMenuItemHandler iMenuItemHandler) { + throw new NotImplementedException(); + } +} diff --git a/src/test/java/com/cys4/sensitivediscoverer/mock/ExtensionHelpersMock.java b/src/test/java/com/cys4/sensitivediscoverer/mock/ExtensionHelpersMock.java new file mode 100644 index 0000000..5d140c1 --- /dev/null +++ b/src/test/java/com/cys4/sensitivediscoverer/mock/ExtensionHelpersMock.java @@ -0,0 +1,172 @@ +package com.cys4.sensitivediscoverer.mock; + +import burp.*; +import org.apache.commons.lang3.NotImplementedException; + +import java.net.URL; +import java.util.List; +import java.util.function.Function; + +public class ExtensionHelpersMock implements IExtensionHelpers { + Function analyzeRequestFunction; + Function analyzeResponseFunction; + Function bytesToStringFunction; + + @Override + public IRequestInfo analyzeRequest(IHttpRequestResponse iHttpRequestResponse) { + return analyzeRequestFunction.apply(iHttpRequestResponse); + } + + public void setAnalyzeRequestFunction(Function analyzeRequestFunction) { + this.analyzeRequestFunction = analyzeRequestFunction; + } + + @Override + public IResponseInfo analyzeResponse(byte[] bytes) { + return analyzeResponseFunction.apply(bytes); + } + + public void setAnalyzeResponseFunction(Function analyzeResponseFunction) { + this.analyzeResponseFunction = analyzeResponseFunction; + } + + @Override + public String bytesToString(byte[] bytes) { + return bytesToStringFunction.apply(bytes); + } + + public void setBytesToStringFunction(Function bytesToStringFunction) { + this.bytesToStringFunction = bytesToStringFunction; + } + + + @Override + public IRequestInfo analyzeRequest(IHttpService iHttpService, byte[] bytes) { + throw new NotImplementedException(); + } + + @Override + public IRequestInfo analyzeRequest(byte[] bytes) { + throw new NotImplementedException(); + } + + @Override + public IParameter getRequestParameter(byte[] bytes, String s) { + throw new NotImplementedException(); + } + + @Override + public String urlDecode(String s) { + throw new NotImplementedException(); + } + + @Override + public String urlEncode(String s) { + throw new NotImplementedException(); + } + + @Override + public byte[] urlDecode(byte[] bytes) { + throw new NotImplementedException(); + } + + @Override + public byte[] urlEncode(byte[] bytes) { + throw new NotImplementedException(); + } + + @Override + public byte[] base64Decode(String s) { + throw new NotImplementedException(); + } + + @Override + public byte[] base64Decode(byte[] bytes) { + throw new NotImplementedException(); + } + + @Override + public String base64Encode(String s) { + throw new NotImplementedException(); + } + + @Override + public String base64Encode(byte[] bytes) { + throw new NotImplementedException(); + } + + @Override + public byte[] stringToBytes(String s) { + throw new NotImplementedException(); + } + + @Override + public int indexOf(byte[] bytes, byte[] bytes1, boolean b, int i, int i1) { + throw new NotImplementedException(); + } + + @Override + public byte[] buildHttpMessage(List list, byte[] bytes) { + throw new NotImplementedException(); + } + + @Override + public byte[] buildHttpRequest(URL url) { + throw new NotImplementedException(); + } + + @Override + public byte[] addParameter(byte[] bytes, IParameter iParameter) { + throw new NotImplementedException(); + } + + @Override + public byte[] removeParameter(byte[] bytes, IParameter iParameter) { + throw new NotImplementedException(); + } + + @Override + public byte[] updateParameter(byte[] bytes, IParameter iParameter) { + throw new NotImplementedException(); + } + + @Override + public byte[] toggleRequestMethod(byte[] bytes) { + throw new NotImplementedException(); + } + + @Override + public IHttpService buildHttpService(String s, int i, String s1) { + throw new NotImplementedException(); + } + + @Override + public IHttpService buildHttpService(String s, int i, boolean b) { + throw new NotImplementedException(); + } + + @Override + public IParameter buildParameter(String s, String s1, byte b) { + throw new NotImplementedException(); + } + + @Override + public IHttpHeader buildHeader(String s, String s1) { + throw new NotImplementedException(); + } + + @Override + public IScannerInsertionPoint makeScannerInsertionPoint(String s, byte[] bytes, int i, int i1) { + throw new NotImplementedException(); + } + + @Override + public IResponseVariations analyzeResponseVariations(byte[]... bytes) { + throw new NotImplementedException(); + } + + @Override + public IResponseKeywords analyzeResponseKeywords(List list, byte[]... bytes) { + throw new NotImplementedException(); + } +} diff --git a/src/test/java/com/cys4/sensitivediscoverer/mock/HttpRequestResponseMock.java b/src/test/java/com/cys4/sensitivediscoverer/mock/HttpRequestResponseMock.java new file mode 100644 index 0000000..f11e169 --- /dev/null +++ b/src/test/java/com/cys4/sensitivediscoverer/mock/HttpRequestResponseMock.java @@ -0,0 +1,66 @@ +package com.cys4.sensitivediscoverer.mock; + +import burp.IHttpRequestResponse; +import burp.IHttpService; +import org.apache.commons.lang3.NotImplementedException; + +public class HttpRequestResponseMock implements IHttpRequestResponse { + byte[] request; + byte[] response; + IHttpService httpServiceMock = new HttpServiceMock(); + + public HttpRequestResponseMock(byte[] request, byte[] response) { + this.request = request; + this.response = response; + } + + @Override + public byte[] getRequest() { + return this.request; + } + + @Override + public void setRequest(byte[] bytes) { + throw new NotImplementedException(); + } + + @Override + public IHttpService getHttpService() { + return this.httpServiceMock; + } + + @Override + public void setHttpService(IHttpService iHttpService) { + this.httpServiceMock = iHttpService; + } + + @Override + public byte[] getResponse() { + return this.response; + } + + @Override + public void setResponse(byte[] bytes) { + throw new NotImplementedException(); + } + + @Override + public String getComment() { + throw new NotImplementedException(); + } + + @Override + public void setComment(String s) { + throw new NotImplementedException(); + } + + @Override + public String getHighlight() { + throw new NotImplementedException(); + } + + @Override + public void setHighlight(String s) { + throw new NotImplementedException(); + } +} diff --git a/src/test/java/com/cys4/sensitivediscoverer/mock/HttpServiceMock.java b/src/test/java/com/cys4/sensitivediscoverer/mock/HttpServiceMock.java new file mode 100644 index 0000000..307fd4a --- /dev/null +++ b/src/test/java/com/cys4/sensitivediscoverer/mock/HttpServiceMock.java @@ -0,0 +1,21 @@ +package com.cys4.sensitivediscoverer.mock; + +import burp.IHttpService; + +public class HttpServiceMock implements IHttpService { + + @Override + public String getHost() { + return "test.com"; + } + + @Override + public int getPort() { + return 443; + } + + @Override + public String getProtocol() { + return "https"; + } +} diff --git a/src/test/java/com/cys4/sensitivediscoverer/mock/RequestInfoMock.java b/src/test/java/com/cys4/sensitivediscoverer/mock/RequestInfoMock.java new file mode 100644 index 0000000..12190ba --- /dev/null +++ b/src/test/java/com/cys4/sensitivediscoverer/mock/RequestInfoMock.java @@ -0,0 +1,51 @@ +package com.cys4.sensitivediscoverer.mock; + +import burp.IParameter; +import burp.IRequestInfo; + +import java.net.URL; +import java.util.List; + +public class RequestInfoMock implements IRequestInfo { + List headers; + URL url; + + public RequestInfoMock(List headers, URL url) { + this.headers = headers; + this.url = url; + } + + @Override + public URL getUrl() { + return this.url; + } + + @Override + public List getHeaders() { + return this.headers; + } + + @Override + public int getBodyOffset() { + // request only contains the body. The headers are set manually + return 0; + } + + @Override + public byte getContentType() { + // don't care + return 0; + } + + @Override + public String getMethod() { + // don't care + return null; + } + + @Override + public List getParameters() { + // don't care + return null; + } +} diff --git a/src/test/java/com/cys4/sensitivediscoverer/mock/ResponseInfoMock.java b/src/test/java/com/cys4/sensitivediscoverer/mock/ResponseInfoMock.java new file mode 100644 index 0000000..2ce0939 --- /dev/null +++ b/src/test/java/com/cys4/sensitivediscoverer/mock/ResponseInfoMock.java @@ -0,0 +1,49 @@ +package com.cys4.sensitivediscoverer.mock; + +import burp.ICookie; +import burp.IResponseInfo; + +import java.util.List; + +public class ResponseInfoMock implements IResponseInfo { + List headers; + + public ResponseInfoMock(List headers) { + this.headers = headers; + } + + @Override + public List getHeaders() { + return this.headers; + } + + @Override + public int getBodyOffset() { + // response only contains the body. The headers are set manually + return 0; + } + + @Override + public short getStatusCode() { + // don't care + return 200; + } + + @Override + public List getCookies() { + // don't care + return null; + } + + @Override + public String getStatedMimeType() { + // don't care + return "TEST"; + } + + @Override + public String getInferredMimeType() { + // don't care + return "TEST"; + } +} diff --git a/src/test/java/com/cys4/sensitivediscoverer/mock/TextEditorMock.java b/src/test/java/com/cys4/sensitivediscoverer/mock/TextEditorMock.java new file mode 100644 index 0000000..ee5ef7a --- /dev/null +++ b/src/test/java/com/cys4/sensitivediscoverer/mock/TextEditorMock.java @@ -0,0 +1,49 @@ +package com.cys4.sensitivediscoverer.mock; + +import burp.ITextEditor; +import org.apache.commons.lang3.NotImplementedException; + +import javax.swing.*; +import java.awt.*; + +public class TextEditorMock implements ITextEditor { + @Override + public Component getComponent() { + return new JPanel(); + } + + @Override + public void setEditable(boolean b) { + throw new NotImplementedException(); + } + + @Override + public byte[] getText() { + throw new NotImplementedException(); + } + + @Override + public void setText(byte[] bytes) { + throw new NotImplementedException(); + } + + @Override + public boolean isTextModified() { + throw new NotImplementedException(); + } + + @Override + public byte[] getSelectedText() { + throw new NotImplementedException(); + } + + @Override + public int[] getSelectionBounds() { + throw new NotImplementedException(); + } + + @Override + public void setSearchExpression(String s) { + throw new NotImplementedException(); + } +} diff --git a/src/test/java/com/cys4/sensitivediscoverer/model/RegexEntityTest.java b/src/test/java/com/cys4/sensitivediscoverer/model/RegexEntityTest.java new file mode 100644 index 0000000..1bc4480 --- /dev/null +++ b/src/test/java/com/cys4/sensitivediscoverer/model/RegexEntityTest.java @@ -0,0 +1,81 @@ +package com.cys4.sensitivediscoverer.model; + +import org.junit.jupiter.api.Test; + +import java.util.EnumSet; +import java.util.regex.Matcher; + +import static org.assertj.core.api.Assertions.*; + +class RegexEntityTest { + + @Test + void testInvalidRegexConstructor() { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> new RegexEntity("desc", "")); + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> new RegexEntity("desc", null)); + assertThatNoException().isThrownBy(() -> new RegexEntity("desc", "^regex$")); + } + + @Test + void testInvalidSectionsConstructor() { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> + new RegexEntity("desc", "^regex$", true, null)); + assertThatNoException() + .isThrownBy(() -> + new RegexEntity("desc", "^regex$", true, EnumSet.of(ProxyItemSection.RES_BODY, ProxyItemSection.REQ_BODY))); + } + + @Test + void testDefaultRegexIsActive() { + RegexEntity entity = new RegexEntity("desc", "regex"); + assertThat(entity.isActive()).isTrue(); + } + + @Test + void testActiveFlag() { + RegexEntity entity; + + entity = new RegexEntity("desc", "regex", true); + assertThat(entity.isActive()).isTrue(); + + entity = new RegexEntity("desc", "regex", false); + assertThat(entity.isActive()).isFalse(); + + entity = new RegexEntity("desc", "regex", true); + entity.setActive(false); + assertThat(entity.isActive()).isFalse(); + entity.setActive(true); + assertThat(entity.isActive()).isTrue(); + } + + @Test + void checkRegexEntityFromCSV() { + Matcher csvMatcher = RegexEntity.checkRegexEntityFromCSV("\"description\",\"regex\",\"SECTION_1,SECTION_2\""); + assertThat(csvMatcher.find()).isTrue(); + assertThat(csvMatcher.groupCount()).isEqualTo(3); + assertThat(csvMatcher.group(1)).isEqualTo("description"); + assertThat(csvMatcher.group(2)).isEqualTo("regex"); + assertThat(csvMatcher.group(3)).isEqualTo("SECTION_1,SECTION_2"); + } + + @Test + void getSectionsHumanReadable() { + RegexEntity entity; + EnumSet sections; + + sections = ProxyItemSection.ALL; + entity = new RegexEntity("desc", "regex", true, sections); + assertThat(entity.getSectionsHumanReadable()).isEqualTo("REQ[URL, Headers, Body], RES[Headers, Body]"); + + sections = EnumSet.of(ProxyItemSection.RES_BODY); + entity = new RegexEntity("desc", "regex", true, sections); + assertThat(entity.getSectionsHumanReadable()).isEqualTo("RES[Body]"); + + sections = EnumSet.of(ProxyItemSection.REQ_HEADERS, ProxyItemSection.REQ_BODY); + entity = new RegexEntity("desc", "regex", true, sections); + assertThat(entity.getSectionsHumanReadable()).isEqualTo("REQ[Headers, Body]"); + } +} \ No newline at end of file