From cc22163711cb538e96d4b3d8ba1f12b4ae48b37a Mon Sep 17 00:00:00 2001 From: Simon Bennetts Date: Thu, 5 Sep 2024 14:47:29 +0100 Subject: [PATCH] OAST Trim database Signed-off-by: Simon Bennetts --- .../org/zaproxy/addon/database/Database.java | 15 +++++ addOns/oast/CHANGELOG.md | 1 + .../org/zaproxy/addon/oast/ExtensionOast.java | 27 ++++++++- .../java/org/zaproxy/addon/oast/OastApi.java | 17 ++++++ .../org/zaproxy/addon/oast/OastParam.java | 12 ++++ .../oast/internal/OastPermanentDatabase.java | 60 +++++++++++++++++++ .../services/boast/BoastOptionsPanelTab.java | 8 ++- .../oast/services/boast/BoastService.java | 4 ++ .../oast/ui/GeneralOastOptionsPanelTab.java | 56 ++++++++++++++++- .../oast/resources/help/contents/api.html | 15 +++-- .../oast/resources/help/contents/options.html | 8 +++ .../addon/oast/resources/Messages.properties | 7 +++ .../zaproxy/addon/oast/OastApiUnitTests.java | 43 +++++++++++-- 13 files changed, 257 insertions(+), 16 deletions(-) diff --git a/addOns/database/src/main/java/org/zaproxy/addon/database/Database.java b/addOns/database/src/main/java/org/zaproxy/addon/database/Database.java index f365f315893..860f4b6b007 100644 --- a/addOns/database/src/main/java/org/zaproxy/addon/database/Database.java +++ b/addOns/database/src/main/java/org/zaproxy/addon/database/Database.java @@ -27,6 +27,7 @@ import javax.jdo.JDOHelper; import javax.jdo.PersistenceManager; import javax.jdo.PersistenceManagerFactory; +import javax.jdo.Query; import javax.jdo.Transaction; import org.datanucleus.PropertyNames; import org.datanucleus.api.jdo.JDOPersistenceManagerFactory; @@ -65,6 +66,9 @@ protected Database(String persistenceUnitName, ClassLoader classLoader) { jdoProperties.put(PropertyNames.PROPERTY_SCHEMA_VALIDATE_COLUMNS, false); jdoProperties.put(PropertyNames.PROPERTY_SCHEMA_VALIDATE_CONSTRAINTS, false); + // Required for non "select" SQL statements. + jdoProperties.put(PropertyNames.PROPERTY_QUERY_SQL_ALLOWALL, true); + pmf = JDOHelper.getPersistenceManagerFactory(jdoProperties, classLoader); this.classLoader = classLoader; } @@ -104,6 +108,17 @@ public void persistEntity(Object entity) { } } + public Object runQuery(String sql, Class clazz, boolean unique) { + PersistenceManager pm = pmf.getPersistenceManager(); + + Query query = pm.newQuery("javax.jdo.query.SQL", sql); + if (clazz != null) { + query.setResultClass(clazz); + } + query.setUnique(unique); + return query.execute(); + } + public List getAll(Class clazz) { if (clazz == null) { throw new IllegalArgumentException("Class cannot be null."); diff --git a/addOns/oast/CHANGELOG.md b/addOns/oast/CHANGELOG.md index 1ca720a0110..dbb251f7a3e 100644 --- a/addOns/oast/CHANGELOG.md +++ b/addOns/oast/CHANGELOG.md @@ -9,6 +9,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Added - API support. - Raise alerts for OAST interactions that happened in other sessions. +- Options to trim the OAST permanent database. ### Changed - Depend on newer version of Database add-on. diff --git a/addOns/oast/src/main/java/org/zaproxy/addon/oast/ExtensionOast.java b/addOns/oast/src/main/java/org/zaproxy/addon/oast/ExtensionOast.java index 1e92c6694a3..0c831249fee 100644 --- a/addOns/oast/src/main/java/org/zaproxy/addon/oast/ExtensionOast.java +++ b/addOns/oast/src/main/java/org/zaproxy/addon/oast/ExtensionOast.java @@ -75,6 +75,7 @@ public class ExtensionOast extends ExtensionAdaptor { private final Map services = new HashMap<>(); private OastOptionsPanel oastOptionsPanel; + private BoastOptionsPanelTab boastOptionsPanelTab; private OastPanel oastPanel; private OastParam oastParam; private BoastService boastService; @@ -129,8 +130,8 @@ public void hook(ExtensionHook extensionHook) { if (hasView()) { extensionHook.getHookView().addOptionPanel(getOastOptionsPanel()); - getOastOptionsPanel().addServicePanel(new GeneralOastOptionsPanelTab()); - getOastOptionsPanel().addServicePanel(new BoastOptionsPanelTab(boastService)); + getOastOptionsPanel().addServicePanel(new GeneralOastOptionsPanelTab(this)); + getOastOptionsPanel().addServicePanel(getBoastOptionsPanelTab()); getOastOptionsPanel().addServicePanel(new CallbackOptionsPanelTab(callbackService)); getOastOptionsPanel().addServicePanel(new InteractshOptionsPanelTab(interactshService)); extensionHook.getHookMenu().addPopupMenuItem(new OastInsertPayloadMenu(this)); @@ -154,13 +155,26 @@ public void optionsLoaded() { @Override public void postInit() { if (oastParam.isUsePermanentDatabase()) { - getPermanentDatabase(); + trimDatabase(oastParam.getDaysToKeepRecords()); } + boastService.startService(); callbackService.startService(); interactshService.startService(); } + public void trimDatabase(int days) { + getPermanentDatabase().trim(days); + } + + public void clearAllRecords() { + getPermanentDatabase().clearAllRecords(); + boastService.clearRegisteredServers(); + if (hasView()) { + ThreadUtils.invokeAndWaitHandled(() -> getBoastOptionsPanelTab().resetBoastServers()); + } + } + private void optionsChanged(OptionsParam optionsParam) { getOastServices().values().forEach(OastService::fireOastStateChanged); if (!wasUsePermanentDatabase && oastParam.isUsePermanentDatabase()) { @@ -240,6 +254,13 @@ private OastOptionsPanel getOastOptionsPanel() { return oastOptionsPanel; } + private BoastOptionsPanelTab getBoastOptionsPanelTab() { + if (boastOptionsPanelTab == null) { + boastOptionsPanelTab = new BoastOptionsPanelTab(boastService); + } + return boastOptionsPanelTab; + } + public OastPanel getOastPanel() { if (oastPanel == null) { oastPanel = new OastPanel(this); diff --git a/addOns/oast/src/main/java/org/zaproxy/addon/oast/OastApi.java b/addOns/oast/src/main/java/org/zaproxy/addon/oast/OastApi.java index 48277288808..08e5b840360 100644 --- a/addOns/oast/src/main/java/org/zaproxy/addon/oast/OastApi.java +++ b/addOns/oast/src/main/java/org/zaproxy/addon/oast/OastApi.java @@ -35,11 +35,14 @@ public class OastApi extends ApiImplementor { private static final String PREFIX = "oast"; private static final String ACTION_SET_ACTIVE_SCAN_SERVICE = "setActiveScanService"; + private static final String ACTION_SET_DAYS_TO_KEEP_RECORDS = "setDaysToKeepRecords"; private static final String ACTION_SET_BOAST_OPTIONS = "setBoastOptions"; private static final String ACTION_SET_CALLBACK_OPTIONS = "setCallbackOptions"; private static final String ACTION_SET_INTERACTSH_OPTIONS = "setInteractshOptions"; + private static final String VIEW_GET_ACTIVE_SCAN_SERVICE = "getActiveScanService"; private static final String VIEW_GET_SERVICES = "getServices"; + private static final String VIEW_GET_DAYS_TO_KEEP_RECORDS = "getDaysToKeepRecords"; private static final String VIEW_GET_BOAST_OPTIONS = "getBoastOptions"; private static final String VIEW_GET_CALLBACK_OPTIONS = "getCallbackOptions"; private static final String VIEW_GET_INTERACTSH_OPTIONS = "getInteractshOptions"; @@ -51,6 +54,7 @@ public class OastApi extends ApiImplementor { private static final String PARAM_SERVER = "server"; private static final String PARAM_POLL_IN_SECS = "pollInSecs"; private static final String PARAM_PORT = "port"; + private static final String PARAM_DAYS = "days"; private ExtensionOast ext; @@ -68,6 +72,8 @@ public OastApi(ExtensionOast ext) { this.addApiView(new ApiView(VIEW_GET_BOAST_OPTIONS)); this.addApiView(new ApiView(VIEW_GET_CALLBACK_OPTIONS)); this.addApiView(new ApiView(VIEW_GET_INTERACTSH_OPTIONS)); + this.addApiView(new ApiView(VIEW_GET_DAYS_TO_KEEP_RECORDS)); + this.addApiAction(new ApiAction(ACTION_SET_ACTIVE_SCAN_SERVICE, new String[] {PARAM_NAME})); this.addApiAction( new ApiAction( @@ -80,6 +86,8 @@ public OastApi(ExtensionOast ext) { new ApiAction( ACTION_SET_INTERACTSH_OPTIONS, new String[] {PARAM_SERVER, PARAM_POLL_IN_SECS, PARAM_AUTH_TOKEN})); + this.addApiAction( + new ApiAction(ACTION_SET_DAYS_TO_KEEP_RECORDS, new String[] {PARAM_DAYS})); } @Override @@ -98,6 +106,12 @@ public ApiResponse handleApiAction(String name, JSONObject params) throws ApiExc ApiException.Type.ILLEGAL_PARAMETER, params.getString(PARAM_NAME)); } break; + case ACTION_SET_DAYS_TO_KEEP_RECORDS: + int days = ApiUtils.getIntParam(params, PARAM_DAYS); + ext.getParams().setDaysToKeepRecords(days); + ext.trimDatabase(days); + break; + case ACTION_SET_BOAST_OPTIONS: ext.getBoastService().getParam().setBoastUri(params.getString(PARAM_SERVER)); ext.getBoastService() @@ -140,6 +154,9 @@ public ApiResponse handleApiView(String name, JSONObject params) throws ApiExcep case VIEW_GET_ACTIVE_SCAN_SERVICE: OastService service = ext.getActiveScanOastService(); return new ApiResponseElement(name, service != null ? service.getName() : ""); + case VIEW_GET_DAYS_TO_KEEP_RECORDS: + return new ApiResponseElement( + name, Integer.toString(ext.getParams().getDaysToKeepRecords())); case VIEW_GET_SERVICES: ApiResponseList servList = new ApiResponseList(name); diff --git a/addOns/oast/src/main/java/org/zaproxy/addon/oast/OastParam.java b/addOns/oast/src/main/java/org/zaproxy/addon/oast/OastParam.java index 9468b6a5177..baabebe3381 100644 --- a/addOns/oast/src/main/java/org/zaproxy/addon/oast/OastParam.java +++ b/addOns/oast/src/main/java/org/zaproxy/addon/oast/OastParam.java @@ -30,6 +30,7 @@ public class OastParam extends VersionedAbstractParam { PARAM_BASE_KEY + ".activeScanService"; private static final String PARAM_USE_PERMANENT_DATABASE = PARAM_BASE_KEY + ".usePermanentDatabase"; + private static final String PARAM_DAYS_TO_KEEP_RECORDS = PARAM_BASE_KEY + ".daysToKeepRecords"; public static final String NO_ACTIVE_SCAN_SERVICE_SELECTED_OPTION = "None"; @@ -41,6 +42,7 @@ public class OastParam extends VersionedAbstractParam { private String activeScanServiceName; private boolean usePermanentDatabase; + private int daysToKeepRecords; public OastParam() {} @@ -62,11 +64,21 @@ public void setUsePermanentDatabase(boolean usePermanentDatabase) { getConfig().setProperty(PARAM_USE_PERMANENT_DATABASE, usePermanentDatabase); } + public int getDaysToKeepRecords() { + return daysToKeepRecords; + } + + public void setDaysToKeepRecords(int daysToKeepRecords) { + this.daysToKeepRecords = daysToKeepRecords; + getConfig().setProperty(PARAM_DAYS_TO_KEEP_RECORDS, daysToKeepRecords); + } + @Override protected void parseImpl() { activeScanServiceName = getString(PARAM_ACTIVE_SCAN_SERVICE_NAME, NO_ACTIVE_SCAN_SERVICE_SELECTED_OPTION); usePermanentDatabase = getBoolean(PARAM_USE_PERMANENT_DATABASE, true); + daysToKeepRecords = getInteger(PARAM_DAYS_TO_KEEP_RECORDS, 45); } @Override diff --git a/addOns/oast/src/main/java/org/zaproxy/addon/oast/internal/OastPermanentDatabase.java b/addOns/oast/src/main/java/org/zaproxy/addon/oast/internal/OastPermanentDatabase.java index 2ce28a74c52..5e761ac659b 100644 --- a/addOns/oast/src/main/java/org/zaproxy/addon/oast/internal/OastPermanentDatabase.java +++ b/addOns/oast/src/main/java/org/zaproxy/addon/oast/internal/OastPermanentDatabase.java @@ -68,4 +68,64 @@ public AlertEntity getAlertForPayload(String payload) { } return null; } + + public void trim(int days) { + if (days <= 0) { + return; + } + LOGGER.debug("Trimming records older than {} days", days); + try { + // Yes, passing parameters in this way is horrible! + // But I couldn't get the parameters working the "correct" way and we know its + // definitely a positive int.. + String dateClause = "DATE_SUB(CURRENT_TIMESTAMP, INTERVAL " + days + " DAY)"; + + Object res = + runQuery( + "DELETE FROM BOAST WHERE REGISTERED_TIMESTAMP < " + dateClause, + null, + false); + if (Integer.parseInt(res.toString()) > 0) { + LOGGER.info("Number of old BOAST records trimmed: {}", res); + } + + res = runQuery("DELETE FROM ALERT WHERE CREATETIMESTAMP < " + dateClause, null, false); + if (Integer.parseInt(res.toString()) > 0) { + LOGGER.info("Number of old ALERT records trimmed: {}", res); + } + + res = + runQuery( + "DELETE FROM MESSAGE WHERE CREATETIMESTAMP < " + dateClause, + null, + false); + if (Integer.parseInt(res.toString()) > 0) { + LOGGER.info("Number of old MESSAGE records trimmed: {}", res); + } + + } catch (Exception e) { + LOGGER.error("Failed to trim OAST permanent db", e); + } + } + + public void clearAllRecords() { + try { + Object res = runQuery("DELETE FROM BOAST", null, false); + if (Integer.parseInt(res.toString()) > 0) { + LOGGER.info("Number of old BOAST records trimmed: {}", res); + } + + res = runQuery("DELETE FROM ALERT", null, false); + if (Integer.parseInt(res.toString()) > 0) { + LOGGER.info("Number of old ALERT records trimmed: {}", res); + } + + res = runQuery("DELETE FROM MESSAGE", null, false); + if (Integer.parseInt(res.toString()) > 0) { + LOGGER.info("Number of old MESSAGE records trimmed: {}", res); + } + } catch (Exception e) { + LOGGER.error("Failed to trim db", e); + } + } } diff --git a/addOns/oast/src/main/java/org/zaproxy/addon/oast/services/boast/BoastOptionsPanelTab.java b/addOns/oast/src/main/java/org/zaproxy/addon/oast/services/boast/BoastOptionsPanelTab.java index bcf35dc43db..30162b57329 100644 --- a/addOns/oast/src/main/java/org/zaproxy/addon/oast/services/boast/BoastOptionsPanelTab.java +++ b/addOns/oast/src/main/java/org/zaproxy/addon/oast/services/boast/BoastOptionsPanelTab.java @@ -140,13 +140,17 @@ private void registerButtonAction() { } } + public void resetBoastServers() { + boastServersTableModel = null; + getBoastServersTable().setModel(getBoastServersTableModel()); + } + @Override public void initParam(OptionsParam options) { final BoastParam param = options.getParamSet(BoastParam.class); getBoastUri().setText(param.getBoastUri()); getPollingFrequencySpinner().setValue(param.getPollingFrequency()); - boastServersTableModel = null; - getBoastServersTable().setModel(getBoastServersTableModel()); + resetBoastServers(); } @Override diff --git a/addOns/oast/src/main/java/org/zaproxy/addon/oast/services/boast/BoastService.java b/addOns/oast/src/main/java/org/zaproxy/addon/oast/services/boast/BoastService.java index f2dbbb280fb..52471a08c26 100644 --- a/addOns/oast/src/main/java/org/zaproxy/addon/oast/services/boast/BoastService.java +++ b/addOns/oast/src/main/java/org/zaproxy/addon/oast/services/boast/BoastService.java @@ -111,6 +111,10 @@ public List getRegisteredServers() { return registeredServers; } + public void clearRegisteredServers() { + registeredServers.clear(); + } + public BoastServer register(String uri) throws IOException { getParam().setBoastUri(uri); return register(); diff --git a/addOns/oast/src/main/java/org/zaproxy/addon/oast/ui/GeneralOastOptionsPanelTab.java b/addOns/oast/src/main/java/org/zaproxy/addon/oast/ui/GeneralOastOptionsPanelTab.java index 8f02ecfaadf..9eb5dffd058 100644 --- a/addOns/oast/src/main/java/org/zaproxy/addon/oast/ui/GeneralOastOptionsPanelTab.java +++ b/addOns/oast/src/main/java/org/zaproxy/addon/oast/ui/GeneralOastOptionsPanelTab.java @@ -23,29 +23,40 @@ import java.util.HashSet; import java.util.Optional; import java.util.Set; +import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; +import javax.swing.JOptionPane; import org.parosproxy.paros.Constant; import org.parosproxy.paros.control.Control; import org.parosproxy.paros.model.OptionsParam; +import org.parosproxy.paros.view.View; import org.zaproxy.addon.oast.ExtensionOast; import org.zaproxy.addon.oast.OastParam; +import org.zaproxy.zap.utils.ZapNumberSpinner; import org.zaproxy.zap.view.LayoutHelper; /** Contains general options not specific to one OAST service. */ +@SuppressWarnings("serial") public class GeneralOastOptionsPanelTab extends OastOptionsPanelTab { private static final long serialVersionUID = 1L; private JComboBox activeScanServices; private JCheckBox usePermanentDatabase; + private ZapNumberSpinner daysToKeepSpinner; + private JButton clearRecordsButton; + private ExtensionOast ext; - public GeneralOastOptionsPanelTab() { + public GeneralOastOptionsPanelTab(ExtensionOast ext) { super(Constant.messages.getString("oast.options.general.title")); + this.ext = ext; int rowIndex = -1; JLabel activeScanServiceLabel = new JLabel(Constant.messages.getString("oast.options.activeScanService")); activeScanServiceLabel.setLabelFor(getActiveScanServicesComboBox()); + JLabel daysToKeepLabel = new JLabel(Constant.messages.getString("oast.options.daysToKeep")); + daysToKeepLabel.setLabelFor(getDaysToKeepSpinner()); add( activeScanServiceLabel, LayoutHelper.getGBC(0, ++rowIndex, GridBagConstraints.RELATIVE, 1.0, 0)); @@ -55,6 +66,15 @@ public GeneralOastOptionsPanelTab() { add( getUsePermanentDatabaseCheckbox(), LayoutHelper.getGBC(0, ++rowIndex, GridBagConstraints.REMAINDER, 1.0, 0)); + add( + daysToKeepLabel, + LayoutHelper.getGBC(0, ++rowIndex, GridBagConstraints.RELATIVE, 1.0, 0)); + add( + getDaysToKeepSpinner(), + LayoutHelper.getGBC(1, rowIndex, GridBagConstraints.REMAINDER, 1.0, 0)); + add( + getClearRecordsButton(), + LayoutHelper.getGBC(1, ++rowIndex, GridBagConstraints.REMAINDER, 1.0, 0)); add( new JLabel(), LayoutHelper.getGBC(0, ++rowIndex, GridBagConstraints.REMAINDER, 1.0, 1.0)); @@ -75,6 +95,12 @@ public void saveParam(OptionsParam options) { .orElse(OastParam.NO_ACTIVE_SCAN_SERVICE_SELECTED_OPTION) .toString()); param.setUsePermanentDatabase(getUsePermanentDatabaseCheckbox().isSelected()); + int prevDays = param.getDaysToKeepRecords(); + int newDays = getDaysToKeepSpinner().getValue(); + param.setDaysToKeepRecords(newDays); + if (newDays > 0 && newDays < prevDays) { + ext.trimDatabase(newDays); + } } private JComboBox getActiveScanServicesComboBox() { @@ -100,4 +126,32 @@ private JCheckBox getUsePermanentDatabaseCheckbox() { } return usePermanentDatabase; } + + private ZapNumberSpinner getDaysToKeepSpinner() { + if (daysToKeepSpinner == null) { + daysToKeepSpinner = new ZapNumberSpinner(0, 45, Integer.MAX_VALUE); + daysToKeepSpinner.setToolTipText( + Constant.messages.getString("oast.options.daysToKeep.tooltip")); + } + return daysToKeepSpinner; + } + + private JButton getClearRecordsButton() { + if (clearRecordsButton == null) { + clearRecordsButton = + new JButton(Constant.messages.getString("oast.options.clearRecords")); + clearRecordsButton.addActionListener( + l -> { + if (View.getSingleton() + .showConfirmDialog( + GeneralOastOptionsPanelTab.this, + Constant.messages.getString( + "oast.options.clearRecords.confirm")) + == JOptionPane.OK_OPTION) { + ext.clearAllRecords(); + } + }); + } + return clearRecordsButton; + } } diff --git a/addOns/oast/src/main/javahelp/org/zaproxy/addon/oast/resources/help/contents/api.html b/addOns/oast/src/main/javahelp/org/zaproxy/addon/oast/resources/help/contents/api.html index cd9ae1594ab..41c5763b4c3 100644 --- a/addOns/oast/src/main/javahelp/org/zaproxy/addon/oast/resources/help/contents/api.html +++ b/addOns/oast/src/main/javahelp/org/zaproxy/addon/oast/resources/help/contents/api.html @@ -11,10 +11,11 @@

OAST API

Views

  • getActiveScanService: Gets the service used with the active scanner, if any.
  • -
  • getBoastOptions: Gets the BOAST options.
  • getCallbackOptions Gets the Callback options.getInteractshOptions: Gets the Interactsh options.getServices: Gets all of the services.getBoastOptions: Gets the BOAST options. +
  • getCallbackOptions: Gets the Callback options.
  • +
  • getDaysToKeepRecords: Gets the number of days the OAST records will be kept for.
  • +
  • getInteractshOptions: Gets the Interactsh options.
  • +
  • getServices: Gets all of the services.
@@ -43,6 +44,12 @@

Actions

  • port: The port to listen on.
  • +
  • + setDaysToKeepRecords (days*): Sets the number of days the OAST records will be kept for. +
      +
    • days: The number of days.
    • +
    +
  • setInteractshOptions (server* pollInSecs* authToken*): Sets the Interactsh options.
      diff --git a/addOns/oast/src/main/javahelp/org/zaproxy/addon/oast/resources/help/contents/options.html b/addOns/oast/src/main/javahelp/org/zaproxy/addon/oast/resources/help/contents/options.html index ee0d0bb2f8a..8a1a26c6739 100644 --- a/addOns/oast/src/main/javahelp/org/zaproxy/addon/oast/resources/help/contents/options.html +++ b/addOns/oast/src/main/javahelp/org/zaproxy/addon/oast/resources/help/contents/options.html @@ -25,6 +25,14 @@

      Permanent Database

      NOTE: This means that Alerts may appear in a ZAP session which is not directly or specifically related to the original assessment/scan. +

      Days to Keep Records

      +The number of days records will be kept in the Permanent Database.
      +Records will only be deleted when ZAP starts up, or if you save the options with a lower "Days to Keep Records" value. + +

      Clear All Records

      +This button will delete all records from the Permanent Database. +You will not get any OOB alerts for any records that have been deleted. +

      Service-Specific Configurations

      Look at the individual options page of each service for more information about the available settings for that service. diff --git a/addOns/oast/src/main/resources/org/zaproxy/addon/oast/resources/Messages.properties b/addOns/oast/src/main/resources/org/zaproxy/addon/oast/resources/Messages.properties index fca91dfa777..948993ad829 100644 --- a/addOns/oast/src/main/resources/org/zaproxy/addon/oast/resources/Messages.properties +++ b/addOns/oast/src/main/resources/org/zaproxy/addon/oast/resources/Messages.properties @@ -11,6 +11,8 @@ oast.api.action.setCallbackOptions = Sets the Callback options. oast.api.action.setCallbackOptions.param.localAddress = The local address oast.api.action.setCallbackOptions.param.port = The port to listen on. oast.api.action.setCallbackOptions.param.remoteAddress = The remote address. +oast.api.action.setDaysToKeepRecords = Sets the number of days the OAST records will be kept for. +oast.api.action.setDaysToKeepRecords.param.days = The number of days. oast.api.action.setInteractshOptions = Sets the Interactsh options. oast.api.action.setInteractshOptions.param.authToken = The Interactsh authentication token. oast.api.action.setInteractshOptions.param.pollInSecs = The polling frequency. @@ -19,6 +21,7 @@ oast.api.desc = Out-of-band Application Security Testing Support oast.api.view.getActiveScanService = Gets the service used with the active scanner, if any. oast.api.view.getBoastOptions = Gets the BOAST options. oast.api.view.getCallbackOptions = Gets the Callback options. +oast.api.view.getDaysToKeepRecords = Gets the number of days the OAST records will be kept for. oast.api.view.getInteractshOptions = Gets the Interactsh options. oast.api.view.getServices = Gets all of the services. @@ -60,6 +63,10 @@ oast.interactsh.options.label.url = Server URL: oast.options.activeScanService = OOB Service Used In Active Scans: oast.options.activeScanService.tooltip = The selected service will be used to generate payloads for active scan rules that support OAST. +oast.options.clearRecords = Clear All Records +oast.options.clearRecords.confirm = Are you sure you want to clear all of the OAST records? +oast.options.daysToKeep = Days to Keep Records +oast.options.daysToKeep.tooltip = The number of days that OAST payload records will be kept for, if zero then the records will never be deleted. oast.options.general.title = General oast.options.title = OAST oast.options.usePermanentDatabase = Use Permanent Database diff --git a/addOns/oast/src/test/java/org/zaproxy/addon/oast/OastApiUnitTests.java b/addOns/oast/src/test/java/org/zaproxy/addon/oast/OastApiUnitTests.java index 2da32374fe4..f737b5ce9c8 100644 --- a/addOns/oast/src/test/java/org/zaproxy/addon/oast/OastApiUnitTests.java +++ b/addOns/oast/src/test/java/org/zaproxy/addon/oast/OastApiUnitTests.java @@ -29,6 +29,7 @@ import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import java.util.ArrayList; import java.util.HashMap; @@ -125,16 +126,46 @@ void shouldGetDefaultActiveService() throws Exception { @Test void shouldSetActiveService() throws Exception { // Given - JSONObject params = new JSONObject(); - params.put("name", "BOAST"); + JSONObject apiParams = new JSONObject(); + apiParams.put("name", "BOAST"); + + // When + ApiResponse res = api.handleApiAction("setActiveScanService", apiParams); + + // Then + assertThat(res instanceof ApiResponseElement, is(true)); + assertThat(((ApiResponseElement) res).getName(), is("Result")); + assertThat(((ApiResponseElement) res).getValue(), is("OK")); + } + + @Test + void shouldGetDaysToKeepRecords() throws Exception { + // Given / When + given(ext.getParams()).willReturn(params); + ApiResponse res = api.handleApiView("getDaysToKeepRecords", new JSONObject()); + + // Then + assertThat(res instanceof ApiResponseElement, is(true)); + assertThat(((ApiResponseElement) res).getName(), is("getDaysToKeepRecords")); + assertThat(((ApiResponseElement) res).getValue(), is("45")); + } + + @Test + void shouldSetDaysToKeepRecords() throws Exception { + // Given + given(ext.getParams()).willReturn(params); + JSONObject apiParams = new JSONObject(); + apiParams.put("days", "44"); // When - ApiResponse res = api.handleApiAction("setActiveScanService", params); + ApiResponse res = api.handleApiAction("setDaysToKeepRecords", apiParams); // Then assertThat(res instanceof ApiResponseElement, is(true)); assertThat(((ApiResponseElement) res).getName(), is("Result")); assertThat(((ApiResponseElement) res).getValue(), is("OK")); + assertThat(params.getDaysToKeepRecords(), is(44)); + verify(ext).trimDatabase(44); } @Test @@ -203,7 +234,7 @@ void shouldGetBoastOptions() throws Exception { void shouldSetBoastOptions() throws Exception { // Given Model model = mock(Model.class); - given(model.getOptionsParam()).willReturn(new OptionsParam()); + given(model.getOptionsParam()).willReturn(mock(OptionsParam.class)); given(ext.getModel()).willReturn(model); BoastService service = mock(BoastService.class); BoastParam bparams = new BoastParam(); @@ -254,7 +285,7 @@ void shouldGetCallbackOptions() throws Exception { void shouldSetCallbackOptions() throws Exception { // Given Model model = mock(Model.class); - given(model.getOptionsParam()).willReturn(new OptionsParam()); + given(model.getOptionsParam()).willReturn(mock(OptionsParam.class)); given(ext.getModel()).willReturn(model); CallbackService service = mock(CallbackService.class); CallbackParam bparams = new CallbackParam(); @@ -307,7 +338,7 @@ void shouldGetInteractshOptions() throws Exception { void shouldSetInteractshOptions() throws Exception { // Given Model model = mock(Model.class); - given(model.getOptionsParam()).willReturn(new OptionsParam()); + given(model.getOptionsParam()).willReturn(mock(OptionsParam.class)); given(ext.getModel()).willReturn(model); InteractshService service = mock(InteractshService.class); InteractshParam bparams = new InteractshParam();