From ab1c921083dff48412540b68a2bbb6100a835619 Mon Sep 17 00:00:00 2001 From: thc202 Date: Fri, 22 Nov 2024 15:55:35 +0000 Subject: [PATCH] sequence: allow to import HAR from the GUI Add an import menu item to allow to import HAR as sequences through the GUI like it's possible through the Automation Framework. Signed-off-by: thc202 --- addOns/sequence/CHANGELOG.md | 2 + addOns/sequence/sequence.gradle.kts | 6 +- .../extension/sequence/ExtensionSequence.java | 63 ++- .../sequence/internal/ImportHarMenuItem.java | 61 +++ .../internal/SequenceImportDialog.java | 423 ++++++++++++++++++ .../resources/help/contents/sequence.html | 9 +- .../sequence/resources/Messages.properties | 17 + 7 files changed, 570 insertions(+), 11 deletions(-) create mode 100644 addOns/sequence/src/main/java/org/zaproxy/zap/extension/sequence/internal/ImportHarMenuItem.java create mode 100644 addOns/sequence/src/main/java/org/zaproxy/zap/extension/sequence/internal/SequenceImportDialog.java diff --git a/addOns/sequence/CHANGELOG.md b/addOns/sequence/CHANGELOG.md index 64150db98fa..b1ca5674ce2 100644 --- a/addOns/sequence/CHANGELOG.md +++ b/addOns/sequence/CHANGELOG.md @@ -11,8 +11,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Data for reporting. - Stats for import automation and active scan. - Sequence active scan policy which will be used if neither a policy nor policyDefinition are set. +- Add Import top level menu item to import HAR as sequence. ### Changed +- Depend on Import/Export add-on to allow to import HARs as sequences. - Update minimum ZAP version to 2.15.0. - Maintenance changes. - To use new sequence scan from the desktop. diff --git a/addOns/sequence/sequence.gradle.kts b/addOns/sequence/sequence.gradle.kts index 666f90bebcd..0965a11d30b 100644 --- a/addOns/sequence/sequence.gradle.kts +++ b/addOns/sequence/sequence.gradle.kts @@ -10,6 +10,9 @@ zapAddOn { url.set("https://www.zaproxy.org/docs/desktop/addons/sequence-scanner/") dependencies { addOns { + register("exim") { + version.set(">= 0.13") + } register("network") register("zest") { version.set("48.*") @@ -26,9 +29,6 @@ zapAddOn { register("automation") { version.set(">= 0.44") } - register("exim") { - version.set(">= 0.13") - } } } } diff --git a/addOns/sequence/src/main/java/org/zaproxy/zap/extension/sequence/ExtensionSequence.java b/addOns/sequence/src/main/java/org/zaproxy/zap/extension/sequence/ExtensionSequence.java index adca1aceb43..e0cbb42aee2 100644 --- a/addOns/sequence/src/main/java/org/zaproxy/zap/extension/sequence/ExtensionSequence.java +++ b/addOns/sequence/src/main/java/org/zaproxy/zap/extension/sequence/ExtensionSequence.java @@ -26,33 +26,44 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.parosproxy.paros.control.Control; +import org.parosproxy.paros.control.Control.Mode; import org.parosproxy.paros.core.scanner.AbstractPlugin; import org.parosproxy.paros.core.scanner.Scanner; import org.parosproxy.paros.core.scanner.ScannerHook; import org.parosproxy.paros.extension.Extension; import org.parosproxy.paros.extension.ExtensionAdaptor; import org.parosproxy.paros.extension.ExtensionHook; +import org.parosproxy.paros.extension.SessionChangedListener; import org.parosproxy.paros.extension.ViewDelegate; +import org.parosproxy.paros.model.Session; import org.parosproxy.paros.network.HttpMessage; +import org.zaproxy.addon.exim.ExtensionExim; import org.zaproxy.addon.network.ExtensionNetwork; import org.zaproxy.zap.extension.ascan.ExtensionActiveScan; import org.zaproxy.zap.extension.ascan.ScanPolicy; import org.zaproxy.zap.extension.script.ExtensionScript; import org.zaproxy.zap.extension.script.ScriptType; import org.zaproxy.zap.extension.script.ScriptWrapper; +import org.zaproxy.zap.extension.sequence.internal.ImportHarMenuItem; import org.zaproxy.zap.extension.zest.ExtensionZest; import org.zaproxy.zap.extension.zest.ZestScriptWrapper; public class ExtensionSequence extends ExtensionAdaptor implements ScannerHook { private static final List> DEPENDENCIES = - List.of(ExtensionNetwork.class, ExtensionScript.class, ExtensionZest.class); + List.of( + ExtensionExim.class, + ExtensionNetwork.class, + ExtensionScript.class, + ExtensionZest.class); private ExtensionScript extScript; private ExtensionActiveScan extActiveScan; private static final Logger LOGGER = LogManager.getLogger(ExtensionSequence.class); public static final String TYPE_SEQUENCE = "sequence"; + private ImportHarMenuItem importHarMenuItem; + private List directScripts = null; private SequenceAscanPanel sequencePanel; @@ -116,6 +127,10 @@ public void unload() { getExtActiveScan().removeCustomScanPanel(sequencePanel); } getExtScript().removeScriptType(scriptType); + + if (importHarMenuItem != null) { + importHarMenuItem.unload(); + } } public ScanPolicy getDefaultScanPolicy() throws ConfigurationException { @@ -164,9 +179,16 @@ public void hook(ExtensionHook extensionhook) { getExtScript().registerScriptType(scriptType); if (hasView()) { + importHarMenuItem = + new ImportHarMenuItem( + scriptType, + getExtension(ExtensionExim.class), + getExtension(ExtensionZest.class)); + extensionhook.getHookMenu().addImportMenuItem(importHarMenuItem); extensionhook .getHookMenu() .addPopupMenuItem(new SequencePopupMenuItem(this, getExtScript())); + extensionhook.addSessionListener(new SessionChangedListenerImpl()); } // Add class as a scannerhook (implements the scannerhook interface) @@ -195,19 +217,46 @@ public void setDirectScanScript(ScriptWrapper script) { private ExtensionScript getExtScript() { if (extScript == null) { - extScript = - Control.getSingleton().getExtensionLoader().getExtension(ExtensionScript.class); + extScript = getExtension(ExtensionScript.class); } return extScript; } + private T getExtension(Class clazz) { + return Control.getSingleton().getExtensionLoader().getExtension(clazz); + } + protected ExtensionActiveScan getExtActiveScan() { if (extActiveScan == null) { - extActiveScan = - Control.getSingleton() - .getExtensionLoader() - .getExtension(ExtensionActiveScan.class); + extActiveScan = getExtension(ExtensionActiveScan.class); } return extActiveScan; } + + private class SessionChangedListenerImpl implements SessionChangedListener { + + @Override + public void sessionChanged(Session session) { + // Nothing to do. + } + + @Override + public void sessionAboutToChange(Session session) { + if (importHarMenuItem != null) { + importHarMenuItem.clear(); + } + } + + @Override + public void sessionScopeChanged(Session session) { + // Nothing to do. + + } + + @Override + public void sessionModeChanged(Mode mode) { + // Nothing to do. + + } + } } diff --git a/addOns/sequence/src/main/java/org/zaproxy/zap/extension/sequence/internal/ImportHarMenuItem.java b/addOns/sequence/src/main/java/org/zaproxy/zap/extension/sequence/internal/ImportHarMenuItem.java new file mode 100644 index 00000000000..9fc59bf7940 --- /dev/null +++ b/addOns/sequence/src/main/java/org/zaproxy/zap/extension/sequence/internal/ImportHarMenuItem.java @@ -0,0 +1,61 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.zaproxy.zap.extension.sequence.internal; + +import org.parosproxy.paros.Constant; +import org.zaproxy.addon.exim.ExtensionExim; +import org.zaproxy.zap.extension.script.ScriptType; +import org.zaproxy.zap.extension.zest.ExtensionZest; +import org.zaproxy.zap.view.ZapMenuItem; + +public class ImportHarMenuItem extends ZapMenuItem { + + private static final long serialVersionUID = 1L; + + private SequenceImportDialog importDialog; + + public ImportHarMenuItem(ScriptType scriptType, ExtensionExim exim, ExtensionZest zest) { + super("sequence.topmenu.importSequence"); + + setToolTipText(Constant.messages.getString("sequence.topmenu.importSequence.tooltip")); + + addActionListener( + e -> { + if (importDialog == null) { + importDialog = + new SequenceImportDialog( + exim.getView().getMainFrame(), scriptType, exim, zest); + } + importDialog.setVisible(true); + }); + } + + public void clear() { + if (importDialog != null) { + importDialog.clearFields(); + } + } + + public void unload() { + if (importDialog != null) { + importDialog.dispose(); + } + } +} diff --git a/addOns/sequence/src/main/java/org/zaproxy/zap/extension/sequence/internal/SequenceImportDialog.java b/addOns/sequence/src/main/java/org/zaproxy/zap/extension/sequence/internal/SequenceImportDialog.java new file mode 100644 index 00000000000..be170afc159 --- /dev/null +++ b/addOns/sequence/src/main/java/org/zaproxy/zap/extension/sequence/internal/SequenceImportDialog.java @@ -0,0 +1,423 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.zaproxy.zap.extension.sequence.internal; + +import java.awt.Font; +import java.awt.Frame; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.text.AbstractDocument; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.DocumentFilter; +import org.apache.commons.lang3.StringUtils; +import org.parosproxy.paros.Constant; +import org.parosproxy.paros.extension.AbstractDialog; +import org.parosproxy.paros.model.Model; +import org.parosproxy.paros.network.HttpMessage; +import org.parosproxy.paros.view.View; +import org.zaproxy.addon.exim.ExtensionExim; +import org.zaproxy.addon.exim.ImporterOptions; +import org.zaproxy.addon.exim.ImporterResult; +import org.zaproxy.zap.extension.script.ScriptType; +import org.zaproxy.zap.extension.zest.CreateScriptOptions; +import org.zaproxy.zap.extension.zest.ExtensionZest; +import org.zaproxy.zap.utils.FontUtils; +import org.zaproxy.zap.utils.Stats; +import org.zaproxy.zap.utils.ThreadUtils; +import org.zaproxy.zap.utils.ZapHtmlLabel; +import org.zaproxy.zap.utils.ZapLabel; +import org.zaproxy.zap.utils.ZapTextField; +import org.zaproxy.zap.view.LayoutHelper; + +@SuppressWarnings("serial") +public class SequenceImportDialog extends AbstractDialog { + + private static final long serialVersionUID = 1L; + + private static final String STATS_PREFIX = "stats.sequence.gui."; + + private static final String MESSAGE_PREFIX = "sequence.importhar."; + + private final ScriptType scriptType; + private final ExtensionExim exim; + private final ExtensionZest zest; + + private ZapTextField fieldName; + private ZapTextField fieldFile; + private JCheckBox fieldAssertCode; + private ZapTextField fieldAssertLength; + private JButton buttonChooseFile; + + private JButton buttonCancel; + private JButton buttonImport; + private JProgressBar progressBar; + + public SequenceImportDialog( + Frame parent, ScriptType scriptType, ExtensionExim exim, ExtensionZest zest) { + super(parent, true); + + this.scriptType = scriptType; + this.exim = exim; + this.zest = zest; + + setTitle(Constant.messages.getString(MESSAGE_PREFIX + "title")); + + centreDialog(); + setLayout(new GridBagLayout()); + + var fieldsPanel = new JPanel(new GridBagLayout()); + int fieldsRow = 0; + + fieldsPanel.add( + new ZapLabel(Constant.messages.getString(MESSAGE_PREFIX + "labelName")), + LayoutHelper.getGBC(0, fieldsRow, 1, 0.5, new Insets(0, 0, 4, 4))); + fieldsPanel.add( + getFieldName(), LayoutHelper.getGBC(1, fieldsRow, 2, 0.5, new Insets(4, 4, 4, 0))); + + fieldsRow++; + var definitionLabel = + new ZapHtmlLabel( + "" + + Constant.messages.getString(MESSAGE_PREFIX + "labelFile") + + "*"); + fieldsPanel.add( + definitionLabel, LayoutHelper.getGBC(0, fieldsRow, 1, 0.5, new Insets(0, 0, 4, 4))); + fieldsPanel.add( + getFieldFile(), LayoutHelper.getGBC(1, fieldsRow, 1, 0.5, new Insets(0, 4, 4, 4))); + fieldsPanel.add( + getChooseFileButton(), + LayoutHelper.getGBC(2, fieldsRow, 1, 0.5, new Insets(0, 4, 4, 0))); + fieldsRow++; + fieldsPanel.add( + new JLabel(Constant.messages.getString(MESSAGE_PREFIX + "labelAssertCode")), + LayoutHelper.getGBC(0, fieldsRow, 1, 0.5, new Insets(4, 0, 4, 4))); + fieldsPanel.add( + getFieldAssertCode(), + LayoutHelper.getGBC(1, fieldsRow, 2, 0.5, new Insets(4, 4, 4, 0))); + fieldsRow++; + fieldsPanel.add( + new JLabel(Constant.messages.getString(MESSAGE_PREFIX + "labelAssertLength")), + LayoutHelper.getGBC(0, fieldsRow, 1, 0.5, new Insets(4, 0, 4, 4))); + fieldsPanel.add( + getFieldAssertLength(), + LayoutHelper.getGBC(1, fieldsRow, 2, 0.5, new Insets(4, 4, 4, 0))); + + int row = 0; + add(fieldsPanel, LayoutHelper.getGBC(0, row, 2, 1.0, new Insets(8, 8, 4, 8))); + row++; + var requiredFieldsLabel = + new ZapHtmlLabel( + "* " + + Constant.messages.getString(MESSAGE_PREFIX + "requiredFields") + + ""); + Font font = requiredFieldsLabel.getFont(); + requiredFieldsLabel.setFont(FontUtils.getFont(font, FontUtils.Size.much_smaller)); + requiredFieldsLabel.setHorizontalAlignment(JLabel.RIGHT); + add(requiredFieldsLabel, LayoutHelper.getGBC(0, row, 2, 1.0, new Insets(4, 8, 4, 8))); + row++; + add(getCancelButton(), LayoutHelper.getGBC(0, row, 1, 0.5, new Insets(4, 8, 8, 4))); + add(getImportButton(), LayoutHelper.getGBC(1, row, 1, 0.5, new Insets(4, 4, 8, 8))); + row++; + add(getProgressBar(), LayoutHelper.getGBC(0, row, 2, 1.0, new Insets(0, 8, 8, 8))); + pack(); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + } + + private ZapTextField getFieldName() { + if (fieldName == null) { + fieldName = new ZapTextField(25); + } + return fieldName; + } + + private ZapTextField getFieldFile() { + if (fieldFile == null) { + fieldFile = new ZapTextField(25); + } + return fieldFile; + } + + private JCheckBox getFieldAssertCode() { + if (fieldAssertCode == null) { + fieldAssertCode = new JCheckBox(); + } + return fieldAssertCode; + } + + private ZapTextField getFieldAssertLength() { + if (fieldAssertLength == null) { + fieldAssertLength = new ZapTextField(25); + ((AbstractDocument) fieldAssertLength.getDocument()).setDocumentFilter(new IntFilter()); + } + return fieldAssertLength; + } + + private void showWarningDialog(String message) { + showProgressBar(false); + View.getSingleton().showWarningDialog(this, message); + } + + private void showWarningFileNotFound(String fileLocation) { + showWarningDialog( + Constant.messages.getString("openapi.import.error.fileNotFound", fileLocation)); + } + + void clearFields() { + getFieldName().setText(""); + getFieldName().discardAllEdits(); + getFieldFile().setText(""); + getFieldFile().discardAllEdits(); + getFieldAssertCode().setSelected(false); + getFieldAssertLength().setText(""); + getFieldAssertLength().discardAllEdits(); + } + + private JButton getChooseFileButton() { + if (buttonChooseFile == null) { + buttonChooseFile = + new JButton(Constant.messages.getString(MESSAGE_PREFIX + "chooseFileButton")); + buttonChooseFile.addActionListener( + e -> { + JFileChooser filechooser = + new JFileChooser( + Model.getSingleton().getOptionsParam().getUserDirectory()); + int state = filechooser.showOpenDialog(this); + if (state == JFileChooser.APPROVE_OPTION) { + String filename = filechooser.getSelectedFile().getAbsolutePath(); + try { + getFieldFile().setText(filename); + Model.getSingleton() + .getOptionsParam() + .setUserDirectory(filechooser.getCurrentDirectory()); + } catch (Exception e1) { + showWarningFileNotFound(filename); + } + } + }); + } + return buttonChooseFile; + } + + private JButton getCancelButton() { + if (buttonCancel == null) { + buttonCancel = new JButton(Constant.messages.getString("all.button.cancel")); + buttonCancel.addActionListener( + e -> { + dispose(); + showProgressBar(false); + }); + } + return buttonCancel; + } + + private JButton getImportButton() { + if (buttonImport == null) { + buttonImport = + new JButton(Constant.messages.getString(MESSAGE_PREFIX + "importButton")); + buttonImport.addActionListener( + e -> { + showProgressBar(true); + new Thread( + () -> { + if (importSequence()) { + ThreadUtils.invokeAndWaitHandled( + () -> { + dispose(); + showProgressBar(false); + }); + } + }, + "ZAP-Sequence-UI-Import") + .start(); + }); + } + return buttonImport; + } + + private JProgressBar getProgressBar() { + if (progressBar == null) { + progressBar = new JProgressBar(); + progressBar.setIndeterminate(true); + progressBar.setVisible(false); + } + return progressBar; + } + + private void showProgressBar(boolean show) { + if (getProgressBar().isVisible() == show) { + return; + } + + setSize(getWidth(), getHeight() + (show ? 10 : -10)); + getProgressBar().setVisible(show); + + getImportButton().setEnabled(!show); + getFieldName().setEnabled(!show); + getFieldFile().setEnabled(!show); + getChooseFileButton().setEnabled(!show); + getFieldAssertCode().setEnabled(!show); + getFieldAssertLength().setEnabled(!show); + } + + private boolean importSequence() { + String filePath = getFieldFile().getText(); + if (filePath == null || filePath.isEmpty()) { + ThreadUtils.invokeAndWaitHandled( + () -> { + showWarningDialog( + Constant.messages.getString(MESSAGE_PREFIX + "error.missingFile")); + getFieldFile().requestFocusInWindow(); + }); + return false; + } + + Path file = Paths.get(filePath); + String sequenceName = getFieldName().getText(); + if (StringUtils.isBlank(sequenceName)) { + sequenceName = file.getFileName().toString().replaceFirst("(?i)\\.har$", ""); + } + + List messages = new ArrayList<>(); + List errors = new ArrayList<>(); + ImporterResult result = + exim.getImporter() + .apply( + ImporterOptions.builder() + .setInputFile(file) + .setMessageHandler(messages::add) + .build()); + result.getErrors().forEach(errors::add); + + if (!errors.isEmpty()) { + String message = + Constant.messages.getString( + "sequence.importhar.import.error", wrapEntriesInLiTags(errors)); + Stats.incCounter(STATS_PREFIX + "import.error"); + ThreadUtils.invokeAndWaitHandled( + () -> { + showProgressBar(false); + View.getSingleton().showMessageDialog(this, new ZapHtmlLabel(message)); + getFieldFile().requestFocusInWindow(); + }); + return false; + } + + if (result.getCount() == 0) { + Stats.incCounter(STATS_PREFIX + "import.nomessages"); + ThreadUtils.invokeAndWaitHandled( + () -> { + showWarningDialog( + Constant.messages.getString( + "sequence.importhar.import.nomessages")); + getFieldFile().requestFocusInWindow(); + }); + return false; + } + + try { + zest.createScript(sequenceName, scriptType, messages, createScriptOptions()); + Stats.incCounter(STATS_PREFIX + "import"); + Stats.incCounter(STATS_PREFIX + "import.messages", result.getCount()); + return true; + } catch (Exception e) { + Stats.incCounter(STATS_PREFIX + "import.script.error"); + ThreadUtils.invokeAndWaitHandled( + () -> { + showWarningDialog( + Constant.messages.getString( + "sequence.importhar.script.error", e.getMessage())); + getFieldFile().requestFocusInWindow(); + }); + return false; + } + } + + private CreateScriptOptions createScriptOptions() { + CreateScriptOptions.Builder builder = + CreateScriptOptions.builder() + .setIncludeResponses(CreateScriptOptions.IncludeResponses.ALWAYS) + .setAddStatusAssertion(getFieldAssertCode().isSelected()); + Integer assertLengthValue = box(getFieldAssertLength().getText()); + if (assertLengthValue != null) { + builder.setAddLengthAssertion(true).setLengthApprox(assertLengthValue); + } + return builder.build(); + } + + private static Integer box(String value) { + if (value.isEmpty()) { + return null; + } + return Integer.valueOf(value); + } + + private static class IntFilter extends DocumentFilter { + + @Override + public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) + throws BadLocationException { + String filteredString = stripNonIntChars(string); + if (filteredString.isEmpty()) { + return; + } + super.insertString(fb, offset, filteredString, attr); + } + + private static String stripNonIntChars(String str) { + return str.replaceAll("[^\\d]", ""); + } + + @Override + public void replace( + FilterBypass fb, int offset, int length, String text, AttributeSet attrs) + throws BadLocationException { + String filteredText = stripNonIntChars(text); + if (filteredText.isEmpty()) { + return; + } + super.replace(fb, offset, length, filteredText, attrs); + } + } + + private static String wrapEntriesInLiTags(List entries) { + if (entries.isEmpty()) { + return ""; + } + + StringBuilder strBuilder = new StringBuilder(entries.size() * 15); + for (String entry : entries) { + strBuilder.append("
  • "); + strBuilder.append(entry); + strBuilder.append("
  • "); + } + return strBuilder.toString(); + } +} diff --git a/addOns/sequence/src/main/javahelp/org/zaproxy/zap/extension/sequence/resources/help/contents/sequence.html b/addOns/sequence/src/main/javahelp/org/zaproxy/zap/extension/sequence/resources/help/contents/sequence.html index fbf002c755e..f8e6d3a95ed 100644 --- a/addOns/sequence/src/main/javahelp/org/zaproxy/zap/extension/sequence/resources/help/contents/sequence.html +++ b/addOns/sequence/src/main/javahelp/org/zaproxy/zap/extension/sequence/resources/help/contents/sequence.html @@ -19,14 +19,21 @@

    Creating Sequences

    One sequence script should be created for each multi-step operation in the application/site being tested.

    -There are three options for creating sequences: +There are several options for creating sequences:

    1. Use the Automation Framework sequence-import job. +
    2. Use the Import menu item Import HAR as Sequence.
    3. In either the Sites tree or History tab select the requests you wish to have included, right click, and use "Add To Zest Script" (either choosing to create a new script or adding to an existing Sequence script).
    4. From the main tool bar, use the "Record a New Zest Script..." button, selecting "Sequence" as the type.
    +

    Assertions

    +When importing the sequences it is possible to choose to create assertions for each HTTP message of the sequence: +
      +
    • Assert Status Code - asserts that the replayed HTTP message has the same status code. +
    • Assert Length - asserts that the replayed HTTP message has the same response body length, within the margin (percentage) specified. +

    Scanning

    diff --git a/addOns/sequence/src/main/resources/org/zaproxy/zap/extension/sequence/resources/Messages.properties b/addOns/sequence/src/main/resources/org/zaproxy/zap/extension/sequence/resources/Messages.properties index 965f10f2ec2..66e3ee9e2d0 100644 --- a/addOns/sequence/src/main/resources/org/zaproxy/zap/extension/sequence/resources/Messages.properties +++ b/addOns/sequence/src/main/resources/org/zaproxy/zap/extension/sequence/resources/Messages.properties @@ -32,5 +32,22 @@ sequence.custom.tab.inc.header = Include in Scan sequence.custom.tab.name.header = Script Name sequence.custom.tab.selectall.label = Select All Sequence Scripts sequence.custom.tab.title = Sequence + +sequence.importhar.chooseFileButton = Choose File +sequence.importhar.error.missingFile = A file must be specified. +sequence.importhar.import.error = There were errors importing the HAR file:
      {0}
    +sequence.importhar.import.nomessages = No messages were imported, creation of sequence will be skipped.\nTry choosing an HAR file with valid messages. +sequence.importhar.importButton = Import +sequence.importhar.labelAssertCode = Assert Status Code +sequence.importhar.labelAssertLength = Assert Length +sequence.importhar.labelFile = File +sequence.importhar.labelName = Name +sequence.importhar.requiredFields = indicates a required field +sequence.importhar.script.error = Error creating sequence.\n{0} +sequence.importhar.title = Sequence Import + sequence.popupmenuitem.activeScanSequence = Active Scan Sequence sequence.popupmenuitem.script.error.interface = The selected Sequence script ({0}) does not implement the required interface.\nPlease take a look at the provided templates for examples. + +sequence.topmenu.importSequence = Import HAR as Sequence +sequence.topmenu.importSequence.tooltip = Imports an HAR file as a Zest Sequence script.