From 3690ba5d1017383273b2746dd0427c347dd137ee Mon Sep 17 00:00:00 2001 From: thc202 Date: Wed, 5 Nov 2025 10:41:59 +0000 Subject: [PATCH 1/2] Normalize credential params and usage in AF plans Use lowercase parameter names instead of capitalized. Use variables in the scripts instead of hardcoded credentials. Related to zaproxy/zap-extensions#6822. Signed-off-by: thc202 --- .../configs/plans/auth-client-script-ajax-spider.yaml | 4 ++-- .../configs/plans/auth-client-script-client-spider.yaml | 4 ++-- .../configs/scripts/auth/clientScriptAuth.zst | 4 ++-- .../configs/scripts/auth/clientScriptAuthBlocking.zst | 4 ++-- .../configs/scripts/auth/clientScriptAuthBlockingScrolls.zst | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docker/integration_tests/configs/plans/auth-client-script-ajax-spider.yaml b/docker/integration_tests/configs/plans/auth-client-script-ajax-spider.yaml index 4bd90b700f5..e9da97b94a7 100644 --- a/docker/integration_tests/configs/plans/auth-client-script-ajax-spider.yaml +++ b/docker/integration_tests/configs/plans/auth-client-script-ajax-spider.yaml @@ -27,8 +27,8 @@ env: users: - name: test credentials: - Username: test@test.com - Password: password123 + username: test@test.com + password: password123 parameters: {} jobs: - type: spiderAjax diff --git a/docker/integration_tests/configs/plans/auth-client-script-client-spider.yaml b/docker/integration_tests/configs/plans/auth-client-script-client-spider.yaml index 7f7e0254563..ae7709838b2 100644 --- a/docker/integration_tests/configs/plans/auth-client-script-client-spider.yaml +++ b/docker/integration_tests/configs/plans/auth-client-script-client-spider.yaml @@ -27,8 +27,8 @@ env: users: - name: test credentials: - Username: test@test.com - Password: password123 + username: test@test.com + password: password123 parameters: {} jobs: - type: spiderClient diff --git a/docker/integration_tests/configs/scripts/auth/clientScriptAuth.zst b/docker/integration_tests/configs/scripts/auth/clientScriptAuth.zst index 817e56fea97..1ee43a6368c 100644 --- a/docker/integration_tests/configs/scripts/auth/clientScriptAuth.zst +++ b/docker/integration_tests/configs/scripts/auth/clientScriptAuth.zst @@ -32,7 +32,7 @@ "elementType": "ZestClientElementClick" }, { - "value": "test@test.com", + "value": "{{username}}", "windowHandle": "windowHandle1", "type": "id", "element": "user", @@ -41,7 +41,7 @@ "elementType": "ZestClientElementSendKeys" }, { - "value": "password123", + "value": "{{password}}", "windowHandle": "windowHandle1", "type": "id", "element": "password", diff --git a/docker/integration_tests/configs/scripts/auth/clientScriptAuthBlocking.zst b/docker/integration_tests/configs/scripts/auth/clientScriptAuthBlocking.zst index cfaa2dc7c83..3620cdcc13c 100644 --- a/docker/integration_tests/configs/scripts/auth/clientScriptAuthBlocking.zst +++ b/docker/integration_tests/configs/scripts/auth/clientScriptAuthBlocking.zst @@ -56,7 +56,7 @@ "elementType": "ZestClientElementScrollTo" }, { - "value": "test@test.com", + "value": "{{username}}", "windowHandle": "windowHandle1", "type": "id", "element": "user", @@ -75,7 +75,7 @@ "elementType": "ZestClientElementScrollTo" }, { - "value": "password123", + "value": "{{password}}", "windowHandle": "windowHandle1", "type": "id", "element": "password", diff --git a/docker/integration_tests/configs/scripts/auth/clientScriptAuthBlockingScrolls.zst b/docker/integration_tests/configs/scripts/auth/clientScriptAuthBlockingScrolls.zst index 61ba060061a..296161bf58f 100644 --- a/docker/integration_tests/configs/scripts/auth/clientScriptAuthBlockingScrolls.zst +++ b/docker/integration_tests/configs/scripts/auth/clientScriptAuthBlockingScrolls.zst @@ -56,7 +56,7 @@ "elementType": "ZestClientElementScrollTo" }, { - "value": "test@test.com", + "value": "{{username}}", "windowHandle": "windowHandle1", "type": "id", "element": "user", @@ -75,7 +75,7 @@ "elementType": "ZestClientElementScrollTo" }, { - "value": "password123", + "value": "{{password}}", "windowHandle": "windowHandle1", "type": "id", "element": "password", From 0060835eac6d280f1e88621111efb3b6f040340d Mon Sep 17 00:00:00 2001 From: thc202 Date: Wed, 5 Nov 2025 11:13:55 +0000 Subject: [PATCH 2/2] Make script-based auth method easier to extend Expose the necessary behaviour to allow other auth methods to extend it without resorting to use of reflection and code duplication. Signed-off-by: thc202 --- .../ScriptBasedAuthenticationMethodType.java | 189 +++++++++++++----- 1 file changed, 138 insertions(+), 51 deletions(-) diff --git a/zap/src/main/java/org/zaproxy/zap/authentication/ScriptBasedAuthenticationMethodType.java b/zap/src/main/java/org/zaproxy/zap/authentication/ScriptBasedAuthenticationMethodType.java index 51cef9f99dc..ac0b4ee2534 100644 --- a/zap/src/main/java/org/zaproxy/zap/authentication/ScriptBasedAuthenticationMethodType.java +++ b/zap/src/main/java/org/zaproxy/zap/authentication/ScriptBasedAuthenticationMethodType.java @@ -57,6 +57,7 @@ import org.parosproxy.paros.network.HttpSender; import org.parosproxy.paros.view.View; import org.zaproxy.zap.authentication.GenericAuthenticationCredentials.GenericAuthenticationCredentialsOptionsPanel; +import org.zaproxy.zap.authentication.ScriptBasedAuthenticationMethodType.AuthenticationScript; import org.zaproxy.zap.extension.api.ApiDynamicActionImplementor; import org.zaproxy.zap.extension.api.ApiException; import org.zaproxy.zap.extension.api.ApiResponse; @@ -97,7 +98,7 @@ public class ScriptBasedAuthenticationMethodType extends AuthenticationMethodTyp private static final String METHOD_NAME = Constant.messages.getString("authentication.method.script.name"); - private ExtensionScript extensionScript; + private static ExtensionScript extensionScript; public class ScriptBasedAuthenticationMethod extends AuthenticationMethod { @@ -116,6 +117,55 @@ protected HttpSender getHttpSender() { return httpSender; } + /** + * Gets the script wrapper. + * + * @return the script wrapper, might be {@code null}. + * @since 2.17.0 + */ + protected ScriptWrapper getScript() { + return script; + } + + /** + * Sets the script wrapper. + * + * @param script the script wrapper. + * @since 2.17.0 + */ + protected void setScript(ScriptWrapper script) { + this.script = script; + } + + /** + * Gets the credential parameter names. + * + * @return the credential parameter names, might be {@code null}. + * @since 2.17.0 + */ + protected String[] getCredentialsParamNames() { + return credentialsParamNames; + } + + /** + * Sets the parameter values. + * + * @since 2.17.0 + */ + protected void setParamValues(Map paramValues) { + this.paramValues = paramValues; + } + + /** + * Gets the parameter values. + * + * @return the parameter values, might be {@code null}. + * @since 2.17.0 + */ + protected Map getParamValues() { + return paramValues; + } + /** * Load a script and fills in the method's filled according to the values specified by the * script. @@ -128,10 +178,7 @@ protected HttpSender getHttpSender() { * @throws IllegalArgumentException if an error occurs while loading the script. */ public void loadScript(ScriptWrapper scriptW) { - AuthenticationScript script = getScriptInterfaceV2(scriptW); - if (script == null) { - script = getScriptInterface(scriptW); - } + AuthenticationScript script = getAuthenticationScript(scriptW); if (script == null) { LOGGER.warn( "The script {} does not properly implement the Authentication Script interface.", @@ -170,8 +217,7 @@ public void loadScript(ScriptWrapper scriptW) { this.paramValues.put(param, oldValues.get(param)); this.script = scriptW; - LOGGER.info( - "Successfully loaded new script for ScriptBasedAuthentication: {}", this); + LOGGER.info("Successfully loaded new script: {}", this); } catch (Exception e) { LOGGER.error("Error while loading authentication script", e); getScriptsExtension().handleScriptException(this.script, e); @@ -184,7 +230,8 @@ public void loadScript(ScriptWrapper scriptW) { @Override public String toString() { - return "ScriptBasedAuthenticationMethod [script=" + return getClass().getSimpleName() + + " [script=" + script + ", paramValues=" + paramValues @@ -200,13 +247,24 @@ public boolean isConfigured() { @Override public AuthenticationMethod duplicate() { - ScriptBasedAuthenticationMethod method = new ScriptBasedAuthenticationMethod(); + ScriptBasedAuthenticationMethod method = createInstance(); method.script = script; method.paramValues = this.paramValues != null ? new HashMap<>(this.paramValues) : null; method.credentialsParamNames = this.credentialsParamNames; return method; } + /** + * Creates a new instance to use for duplication. + * + * @return the new instance. + * @since 2.17.0 + * @see #duplicate() + */ + protected ScriptBasedAuthenticationMethod createInstance() { + return new ScriptBasedAuthenticationMethod(); + } + @Override public boolean validateCreationOfAuthenticationCredentials() { if (credentialsParamNames != null) { @@ -253,10 +311,7 @@ public WebSession authenticate( // Call the script to get an authenticated message from which we can then extract the // session - AuthenticationScript script = getScriptInterfaceV2(this.script); - if (script == null) { - script = getScriptInterface(this.script); - } + AuthenticationScript script = getAuthenticationScript(this.script); if (script == null) { return null; @@ -361,7 +416,7 @@ public class ScriptBasedAuthenticationMethodOptionsPanel private ScriptWrapper loadedScript; - private JPanel dynamicContentPanel; + private final JPanel dynamicContentPanel; private DynamicFieldsPanel dynamicFieldsPanel; @@ -369,13 +424,11 @@ public class ScriptBasedAuthenticationMethodOptionsPanel public ScriptBasedAuthenticationMethodOptionsPanel() { super(); - initialize(); - } - private void initialize() { this.setLayout(new GridBagLayout()); - this.add(new JLabel(SCRIPT_NAME_LABEL), LayoutHelper.getGBC(0, 0, 1, 0.0d, 0.0d)); + int y = 0; + this.add(new JLabel(SCRIPT_NAME_LABEL), LayoutHelper.getGBC(0, y, 1, 0.0d, 0.0d)); scriptsComboBox = new JXComboBox(); scriptsComboBox.addHighlighter( @@ -396,13 +449,13 @@ private void initialize() { } return name; })); - this.add(this.scriptsComboBox, LayoutHelper.getGBC(1, 0, 1, 1.0d, 0.0d)); + this.add(this.scriptsComboBox, LayoutHelper.getGBC(1, y, 1, 1.0d, 0.0d)); this.loadScriptButton = new JButton( Constant.messages.getString( "authentication.method.script.load.button")); - this.add(this.loadScriptButton, LayoutHelper.getGBC(2, 0, 1, 0.0d, 0.0d)); + this.add(this.loadScriptButton, LayoutHelper.getGBC(2, y, 1, 0.0d, 0.0d)); this.loadScriptButton.addActionListener( new ActionListener() { @Override @@ -421,11 +474,24 @@ public void actionPerformed(ActionEvent e) { } }); + y++; + y = addCustomFields(y); this.dynamicContentPanel = new JPanel(new BorderLayout()); - this.add(this.dynamicContentPanel, LayoutHelper.getGBC(0, 1, 3, 1.0d, 0.0d)); + this.add(this.dynamicContentPanel, LayoutHelper.getGBC(0, y, 3, 1.0d, 0.0d)); this.dynamicContentPanel.add(new ZapHtmlLabel(LABEL_NOT_LOADED)); } + /** + * Adds custom fields to the panel. + * + * @param row the new row to add the custom fields. + * @return the row after adding the custom fields. + * @since 2.17.0 + */ + protected int addCustomFields(int y) { + return y; + } + @Override public void validateFields() throws IllegalStateException { if (this.loadedScript == null) { @@ -456,7 +522,7 @@ public void bindMethod(AuthenticationMethod method) this.method = (ScriptBasedAuthenticationMethod) method; // Make sure the list of scripts is refreshed - List scripts = getScriptsExtension().getScripts(SCRIPT_TYPE_AUTH); + List scripts = getAuthenticationScripts(); DefaultComboBoxModel model = new DefaultComboBoxModel<>(scripts.toArray(new ScriptWrapper[scripts.size()])); this.scriptsComboBox.setModel(model); @@ -471,6 +537,16 @@ public void bindMethod(AuthenticationMethod method) } } + /** + * Gets the scripts to show in the combo box. + * + * @return the scripts to show in the combo box. + * @since 2.17.0 + */ + protected List getAuthenticationScripts() { + return getScriptsExtension().getScripts(SCRIPT_TYPE_AUTH); + } + @Override public void bindMethod( AuthenticationMethod method, AuthenticationIndicatorsPanel indicatorsPanel) @@ -485,10 +561,7 @@ public AuthenticationMethod getMethod() { } private void loadScript(ScriptWrapper scriptW, boolean adaptOldValues) { - AuthenticationScript script = getScriptInterfaceV2(scriptW); - if (script == null) { - script = getScriptInterface(scriptW); - } + AuthenticationScript script = getAuthenticationScript(scriptW); if (script == null) { LOGGER.warn( @@ -680,10 +753,7 @@ public void loadMethod( method.script = script; // Check script interface and make sure we load the credentials parameter names - AuthenticationScript s = getScriptInterfaceV2(script); - if (s == null) { - s = getScriptInterface(script); - } + AuthenticationScript s = getAuthenticationScript(script); if (s == null) { LOGGER.error( "Unable to load Script Based Authentication method. The script {} does not properly implement the Authentication Script interface.", @@ -720,10 +790,7 @@ public void loadMethod( public void persistMethodToSession( Session session, int contextId, AuthenticationMethod authMethod) throws UnsupportedAuthenticationMethodException, DatabaseException { - if (!(authMethod instanceof ScriptBasedAuthenticationMethod)) - throw new UnsupportedAuthenticationMethodException( - "Script based authentication type only supports: " - + ScriptBasedAuthenticationMethod.class); + validateAuthenticationMethod(authMethod); ScriptBasedAuthenticationMethod method = (ScriptBasedAuthenticationMethod) authMethod; session.setContextData( @@ -734,6 +801,21 @@ public void persistMethodToSession( EncodingUtils.mapToString(method.paramValues)); } + /** + * Validates that this authentication method type supports the given authentication method. + * + * @param method the method to validate. + * @throws UnsupportedAuthenticationMethodException if the given method is not supported. + * @since 2.17.0 + */ + protected void validateAuthenticationMethod(AuthenticationMethod method) { + if (!(method instanceof ScriptBasedAuthenticationMethod)) { + throw new UnsupportedAuthenticationMethodException( + "Script based authentication type only supports: " + + ScriptBasedAuthenticationMethod.class); + } + } + @Override public AuthenticationCredentials createAuthenticationCredentials() { // NOTE: This method will initialize a set of Credentials without any required parameters @@ -746,14 +828,28 @@ public Class getAuthenticationCredentialsType( return GenericAuthenticationCredentials.class; } - private ExtensionScript getScriptsExtension() { + private static ExtensionScript getScriptsExtension() { if (extensionScript == null) extensionScript = Control.getSingleton().getExtensionLoader().getExtension(ExtensionScript.class); return extensionScript; } - private AuthenticationScript getScriptInterface(ScriptWrapper script) { + /** + * Gets the {@code AuthenticationScript} from the given script. + * + * @return the {@code AuthenticationScript} or {@code null} if the script does not implement it. + * @since 2.17.0 + */ + protected static AuthenticationScript getAuthenticationScript(ScriptWrapper script) { + AuthenticationScript authScript = getScriptInterfaceV2(script); + if (authScript != null) { + return authScript; + } + return getScriptInterface(script); + } + + private static AuthenticationScript getScriptInterface(ScriptWrapper script) { try { return getScriptsExtension().getInterface(script, AuthenticationScript.class); } catch (Exception e) { @@ -767,7 +863,7 @@ private AuthenticationScript getScriptInterface(ScriptWrapper script) { return null; } - private AuthenticationScriptV2 getScriptInterfaceV2(ScriptWrapper script) { + private static AuthenticationScriptV2 getScriptInterfaceV2(ScriptWrapper script) { try { AuthenticationScriptV2 authScript = getScriptsExtension().getInterface(script, AuthenticationScriptV2.class); @@ -878,10 +974,7 @@ public void handleAction(JSONObject params) throws ApiException { method.script = script; // Check script interface and make sure we load the credentials parameter names - AuthenticationScript s = getScriptInterfaceV2(script); - if (s == null) { - s = getScriptInterface(script); - } + AuthenticationScript s = getAuthenticationScript(script); if (s == null) { LOGGER.error( "Unable to load Script Based Authentication method. The script {} does not properly implement the Authentication Script interface.", @@ -943,11 +1036,8 @@ public ApiDynamicActionImplementor getSetCredentialsForUserApiAction() { @Override public void exportData(Configuration config, AuthenticationMethod authMethod) { - if (!(authMethod instanceof ScriptBasedAuthenticationMethod)) { - throw new UnsupportedAuthenticationMethodException( - "Script based authentication type only supports: " - + ScriptBasedAuthenticationMethod.class.getName()); - } + validateAuthenticationMethod(authMethod); + ScriptBasedAuthenticationMethod method = (ScriptBasedAuthenticationMethod) authMethod; config.setProperty(CONTEXT_CONFIG_AUTH_SCRIPT_NAME, method.script.getName()); config.setProperty( @@ -957,11 +1047,8 @@ public void exportData(Configuration config, AuthenticationMethod authMethod) { @Override public void importData(Configuration config, AuthenticationMethod authMethod) throws ConfigurationException { - if (!(authMethod instanceof ScriptBasedAuthenticationMethod)) { - throw new UnsupportedAuthenticationMethodException( - "Script based authentication type only supports: " - + ScriptBasedAuthenticationMethod.class.getName()); - } + validateAuthenticationMethod(authMethod); + ScriptBasedAuthenticationMethod method = (ScriptBasedAuthenticationMethod) authMethod; this.loadMethod( method,