/* * Zed Attack Proxy (ZAP) and its related class files. * * ZAP is an HTTP/HTTPS proxy for assessing web application security. * * Copyright 2016 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.spider; import java.awt.EventQueue; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.event.TableModelEvent; import org.parosproxy.paros.Constant; import org.parosproxy.paros.control.Control; import org.parosproxy.paros.extension.history.ExtensionHistory; import org.parosproxy.paros.model.HistoryReference; import org.zaproxy.zap.ZAP; import org.zaproxy.zap.eventBus.Event; import org.zaproxy.zap.eventBus.EventConsumer; import org.zaproxy.zap.extension.alert.AlertEventPublisher; import org.zaproxy.zap.view.table.AbstractCustomColumnHistoryReferencesTableModel; import org.zaproxy.zap.view.table.AbstractHistoryReferencesTableEntry; import org.zaproxy.zap.view.table.DefaultHistoryReferencesTableEntry; class SpiderMessagesTableModel extends AbstractCustomColumnHistoryReferencesTableModel<SpiderMessagesTableModel.SpiderTableEntry> { private static final long serialVersionUID = 1093393768186896931L; private static final Column[] COLUMNS = new Column[] { Column.CUSTOM, Column.HREF_ID, Column.REQUEST_TIMESTAMP, Column.RESPONSE_TIMESTAMP, Column.METHOD, Column.URL, Column.STATUS_CODE, Column.STATUS_REASON, Column.RTT, Column.SIZE_REQUEST_HEADER, Column.SIZE_REQUEST_BODY, Column.SIZE_RESPONSE_HEADER, Column.SIZE_RESPONSE_BODY, Column.HIGHEST_ALERT, Column.TAGS }; private static final String[] CUSTOM_COLUMN_NAMES = { Constant.messages.getString("spider.table.messages.header.processed") }; private static final ProcessedCellItem SUCCESSFULLY_PROCESSED_CELL_ITEM; private static final ProcessedCellItem IO_ERROR_CELL_ITEM; private final ExtensionHistory extensionHistory; private AlertEventConsumer alertEventConsumer; private List<SpiderTableEntry> resources; private Map<Integer, Integer> idsToRows; static { SUCCESSFULLY_PROCESSED_CELL_ITEM = new ProcessedCellItem( true, Constant.messages.getString("spider.table.messages.column.processed.successfully")); IO_ERROR_CELL_ITEM = new ProcessedCellItem( false, Constant.messages.getString("spider.table.messages.column.processed.ioerror")); } public SpiderMessagesTableModel() { this(true); } public SpiderMessagesTableModel(boolean createAlertEventConsumer) { super(COLUMNS); resources = new ArrayList<>(); idsToRows = new HashMap<>(); if (createAlertEventConsumer) { alertEventConsumer = new AlertEventConsumer(); extensionHistory = Control.getSingleton().getExtensionLoader().getExtension(ExtensionHistory.class); ZAP.getEventBus().registerConsumer(alertEventConsumer, AlertEventPublisher.getPublisher().getPublisherName()); } else { alertEventConsumer = null; extensionHistory = null; } } @Override public void addEntry(SpiderTableEntry entry) { // Nothing to do, the entries are added with the following method. } public void addHistoryReference(HistoryReference historyReference, boolean ioError) { HistoryReference latestHistoryReference = historyReference; if (extensionHistory != null) { latestHistoryReference = extensionHistory.getHistoryReference(historyReference.getHistoryId()); } final SpiderTableEntry entry = new SpiderTableEntry(latestHistoryReference, ioError); EventQueue.invokeLater(new Runnable() { @Override public void run() { final int row = resources.size(); idsToRows.put(Integer.valueOf(entry.getHistoryId()), Integer.valueOf(row)); resources.add(entry); fireTableRowsInserted(row, row); } }); } @Override public void clear() { resources = new ArrayList<>(); idsToRows = new HashMap<>(); fireTableDataChanged(); if (alertEventConsumer != null) { ZAP.getEventBus().unregisterConsumer(alertEventConsumer, AlertEventPublisher.getPublisher().getPublisherName()); alertEventConsumer = null; } } @Override public void refreshEntryRow(int historyReferenceId) { final DefaultHistoryReferencesTableEntry entry = getEntryWithHistoryId(historyReferenceId); if (entry != null) { int rowIndex = getEntryRowIndex(historyReferenceId); getEntryWithHistoryId(historyReferenceId).refreshCachedValues(); fireTableRowsUpdated(rowIndex, rowIndex); } } @Override public void removeEntry(int historyReferenceId) { // Nothing to do, the entries are not removed. } @Override public SpiderTableEntry getEntry(int rowIndex) { return resources.get(rowIndex); } @Override public SpiderTableEntry getEntryWithHistoryId(int historyReferenceId) { final int row = getEntryRowIndex(historyReferenceId); if (row != -1) { return resources.get(row); } return null; } @Override public int getEntryRowIndex(int historyReferenceId) { final Integer row = idsToRows.get(Integer.valueOf(historyReferenceId)); if (row != null) { return row.intValue(); } return -1; } @Override public int getRowCount() { return resources.size(); } @Override protected Class<?> getColumnClass(Column column) { return AbstractHistoryReferencesTableEntry.getColumnClass(column); } @Override protected Object getPrototypeValue(Column column) { return AbstractHistoryReferencesTableEntry.getPrototypeValue(column); } @Override public Object getValueAt(int rowIndex, int columnIndex) { if (columnIndex == -1) { return getEntry(rowIndex); } return super.getValueAt(rowIndex, columnIndex); } @Override protected Object getCustomValueAt(SpiderTableEntry entry, int columnIndex) { if (getCustomColumnIndex(columnIndex) == 0) { return entry.isIoError() ? IO_ERROR_CELL_ITEM : SUCCESSFULLY_PROCESSED_CELL_ITEM; } return null; } @Override protected String getCustomColumnName(int columnIndex) { return CUSTOM_COLUMN_NAMES[getCustomColumnIndex(columnIndex)]; } @Override protected Class<?> getCustomColumnClass(int columnIndex) { if (getCustomColumnIndex(columnIndex) == 0) { return ProcessedCellItem.class; } return null; } @Override protected Object getCustomPrototypeValue(int columnIndex) { if (getCustomColumnIndex(columnIndex) == 0) { return "Successful"; } return null; } private void refreshEntryRows() { if (resources.isEmpty()) { return; } for (SpiderTableEntry entry : resources) { entry.refreshCachedValues(); } fireTableChanged( new TableModelEvent( this, 0, resources.size() - 1, getColumnIndex(Column.HIGHEST_ALERT), TableModelEvent.UPDATE)); } static class SpiderTableEntry extends DefaultHistoryReferencesTableEntry { private final boolean ioError; public SpiderTableEntry(HistoryReference historyReference, boolean ioError) { super(historyReference, COLUMNS); this.ioError = ioError; } public boolean isIoError() { return ioError; } } static class ProcessedCellItem implements Comparable<ProcessedCellItem> { private final boolean successful; private final String label; public ProcessedCellItem(boolean successful, String label) { this.successful = successful; this.label = label; } public boolean isSuccessful() { return successful; } public String getLabel() { return label; } @Override public String toString() { return label; } @Override public int hashCode() { return 31 * (successful ? 1231 : 1237); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } ProcessedCellItem other = (ProcessedCellItem) obj; if (successful != other.successful) { return false; } return true; } @Override public int compareTo(ProcessedCellItem other) { if (other == null) { return 1; } if (successful && !other.successful) { return 1; } else if (!successful && other.successful) { return -1; } return label.compareTo(other.label); } } private class AlertEventConsumer implements EventConsumer { @Override public void eventReceived(Event event) { switch (event.getEventType()) { case AlertEventPublisher.ALERT_ADDED_EVENT: case AlertEventPublisher.ALERT_CHANGED_EVENT: case AlertEventPublisher.ALERT_REMOVED_EVENT: refreshEntry(Integer.valueOf(event.getParameters().get(AlertEventPublisher.HISTORY_REFERENCE_ID))); break; case AlertEventPublisher.ALL_ALERTS_REMOVED_EVENT: default: refreshEntries(); break; } } private void refreshEntry(final int id) { if (EventQueue.isDispatchThread()) { refreshEntryRow(id); return; } EventQueue.invokeLater(new Runnable() { @Override public void run() { refreshEntry(id); } }); } private void refreshEntries() { if (EventQueue.isDispatchThread()) { refreshEntryRows(); return; } EventQueue.invokeLater(new Runnable() { @Override public void run() { refreshEntries(); } }); } } }