Skip to content

Sink Finder界面左边不显示上下左右的滚动条,只能通过拉大界面才能看到超出界面的内容 #23

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
gamer2456 opened this issue May 7, 2025 · 2 comments
Labels
bug Something isn't working

Comments

@gamer2456
Copy link

Image
@gamer2456
Copy link
Author

模仿git实现了一个独立界面。


import com.sec.security.action.HideBranchesAction;
import com.sec.security.action.ScanAction;
import com.sec.security.utils.ProjectIssue;
import com.sec.security.utils.SinkUtil;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.OnePixelSplitter;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.hover.TreeHoverListener;
import com.intellij.ui.table.JBTable;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.ui.components.BorderLayoutPanel;
import org.jetbrains.annotations.NotNull;
import com.intellij.util.ui.UIUtil;

import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableRowSorter;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

public class IssueProblemsTab extends JPanel {
    private final Project project;
    private BorderLayoutPanel mainPanel;
    private DefaultTableModel tableModel;
    private JBTable issueTable;
    private TableRowSorter<DefaultTableModel> tableSorter;
    private DefaultMutableTreeNode treeRootNode;
    private DefaultTreeModel treeModel;
    private Tree sinkTree;
    private final List<Object[]> allIssues;
    private final TreeMap<String, TreeMap<String, List<Object[]>>> issueTypeMap;

    public IssueProblemsTab(@NotNull Project project) {
        this.project = project;
        this.allIssues = new ArrayList<>();
        this.issueTypeMap = new TreeMap<>();
        initializeComponents();
        setupLayout();
        setupEventListeners();
    }

    private void initializeComponents() {
        this.tableModel = new DefaultTableModel(new String[]{
                "文件名", "行数", "类名", "方法", "类型", "子类", "调用方式"
        }, 0) {
            @Override
            public boolean isCellEditable(int row, int column) {
                return false;
            }
        };
        this.issueTable = new JBTable(tableModel);
        this.tableSorter = new TableRowSorter<>(tableModel);

        DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer();
        centerRenderer.setHorizontalAlignment(JLabel.CENTER);
        for (int i = 0; i < issueTable.getColumnCount(); i++) {
            issueTable.getColumnModel().getColumn(i).setCellRenderer(centerRenderer);
        }

        JTableHeader tableHeader = issueTable.getTableHeader();
        DefaultTableCellRenderer headerRenderer = new DefaultTableCellRenderer();
        headerRenderer.setHorizontalAlignment(JLabel.CENTER);
        tableHeader.setDefaultRenderer(headerRenderer);

        issueTable.setAutoCreateRowSorter(true);

        this.treeRootNode = new DefaultMutableTreeNode("漏洞类型");
        this.treeModel = new DefaultTreeModel(treeRootNode);
        this.sinkTree = new Tree(treeModel);
        configureTree();

        issueTable.getColumnModel().getColumn(0).setCellRenderer(new FirstColumnRenderer());
        issueTable.getColumnModel().getColumn(4).setCellRenderer(new HighlightRenderer());
    }

    private void configureTree() {
        sinkTree.setCellRenderer(new SinkTreeCellRenderer());
        sinkTree.setRootVisible(true);
        sinkTree.setShowsRootHandles(true);
        TreeHoverListener.DEFAULT.addTo(sinkTree);
    }

    private void setupLayout() {
        // 创建工具栏
        ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar("Security.Panel.SinkTree", createToolbarGroup(), false);

        // 创建左侧面板(包含工具栏 + 分割线)
        JComponent leftPanel = createLeftPanel(toolbar);

        // 创建树滚动面板
        JScrollPane treeScrollPane = ScrollPaneFactory.createScrollPane(sinkTree, true);

        // 创建表格滚动面板
        JScrollPane tableScrollPane = ScrollPaneFactory.createScrollPane(issueTable, true);

        // 创建右侧面板(包含 splitter + 树和表格)
        JComponent rightPanel = createRightPanel(treeScrollPane, tableScrollPane);

        // 构建主面板
        this.mainPanel = new BorderLayoutPanel();
        mainPanel.add(leftPanel, BorderLayout.WEST);
        mainPanel.add(rightPanel, BorderLayout.CENTER);

        // 设置当前组件的布局
        setLayout(new BorderLayout());
        add(mainPanel, BorderLayout.CENTER);
    }

    private Component createVerticalSeparator() {
        JSeparator separator = new JSeparator(JSeparator.VERTICAL);
        separator.setForeground(UIUtil.getBoundsColor());
        return separator;
    }

    private JComponent createRightPanel(JScrollPane treeScrollPane, JScrollPane tableScrollPane) {
        OnePixelSplitter splitPane = new OnePixelSplitter(false);
        splitPane.setFirstComponent(treeScrollPane);
        splitPane.setSecondComponent(tableScrollPane);
        splitPane.setProportion(0.2f);

        return splitPane;
    }

    private JComponent createLeftPanel(ActionToolbar toolbar) {
        JPanel leftPanel = new JPanel();
        leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.X_AXIS));
        leftPanel.add(createToolbarPanel(toolbar));
        leftPanel.add(createVerticalSeparator());
        return leftPanel;
    }

    private DefaultActionGroup createToolbarGroup() {
        AnAction scanAction = new ScanAction();
        AnAction hideBranchesAction = new HideBranchesAction();
        DefaultActionGroup group = new DefaultActionGroup();
        group.add(scanAction);
        group.add(new Separator());
        group.add(hideBranchesAction);
        return group;
    }

    private JComponent createToolbarPanel(ActionToolbar toolbar) {
        JPanel panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
        panel.add(toolbar.getComponent());
        panel.setMaximumSize(new Dimension(20, Short.MAX_VALUE));
        panel.setMinimumSize(new Dimension(20, Short.MAX_VALUE));
        return panel;
    }


    private void setupEventListeners() {

        issueTable.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(@NotNull MouseEvent event) {
                if (event.getClickCount() == 2) {
                    navigateToSelectedFile();
                }
            }
        });

        sinkTree.addTreeSelectionListener(this::handleTreeSelectionChange);
    }

    private void navigateToSelectedFile() {
        int selectedRow = issueTable.getSelectedRow();
        if (selectedRow != -1) {
            int modelRow = issueTable.getRowSorter().convertRowIndexToModel(selectedRow);
            VirtualFile file = (VirtualFile) tableModel.getValueAt(modelRow, 0);
            int line = Integer.parseInt(tableModel.getValueAt(modelRow, 1).toString()) - 1;
            new OpenFileDescriptor(project, file, line, 0).navigate(true);
        }
    }

    private void handleTreeSelectionChange(@NotNull TreeSelectionEvent event) {
        filterTableByTreeSelection(event.getPath());
    }

    public void startScanAction() {
        ProgressManager progressManager = ProgressManager.getInstance();
        tableModel.setRowCount(0);
        String taskName = "发起源码安全扫描";
        progressManager.run(new Task.Backgroundable(project, taskName) {
            @Override
            public void run(@NotNull ProgressIndicator indicator) {
                SinkUtil.collectProjectIssues(project, 100, issues -> {
                    ApplicationManager.getApplication().invokeLater(() -> {
                        updateUIWithIssues(issues);
                    });
                });
            }
        });
    }

    private void updateUIWithIssues(List<ProjectIssue> issues) {
        tableModel.setRowCount(0);
        allIssues.clear();
        issueTypeMap.clear();
        treeRootNode.removeAllChildren();
        for (ProjectIssue issue : issues) {
            Object[] row = createTableRow(issue);
            addIssueToCollections(row, issue.getType(), issue.getSubType());
        }
        rebuildTreeStructure();
        refreshTableWithAllIssues();

        treeModel.reload();
        tableSorter.sort();
    }

    private Object[] createTableRow(ProjectIssue issue) {
        return new Object[]{
                issue.getFile(),
                String.valueOf(issue.getLineNumber()),
                issue.getSinkClass(),
                issue.getSinkMethod(),
                issue.getType(),
                issue.getSubType(),
                issue.getCallMode()
        };
    }

    private void addIssueToCollections(Object[] row, String type, String subType) {
        allIssues.add(row);
        issueTypeMap.computeIfAbsent(type, key -> new TreeMap<>())
                .computeIfAbsent(subType, key -> new ArrayList<>())
                .add(row);
    }

    private void rebuildTreeStructure() {
        treeRootNode.removeAllChildren();
        for (Map.Entry<String, TreeMap<String, List<Object[]>>> typeEntry : issueTypeMap.entrySet()) {
            DefaultMutableTreeNode typeNode = createTypeNode(typeEntry);
            treeRootNode.add(typeNode);
        }
    }

    private DefaultMutableTreeNode createTypeNode(Map.Entry<String, TreeMap<String, List<Object[]>>> typeEntry) {
        String type = typeEntry.getKey();
        TreeMap<String, List<Object[]>> subTypeMap = typeEntry.getValue();
        DefaultMutableTreeNode typeNode = new DefaultMutableTreeNode(type);
        int totalCount = 0;
        for (Map.Entry<String, List<Object[]>> subTypeEntry : subTypeMap.entrySet()) {
            String subType = subTypeEntry.getKey();
            int count = subTypeEntry.getValue().size();
            totalCount += count;
            typeNode.add(new DefaultMutableTreeNode(subType + " (" + count + ")"));
        }
        typeNode.setUserObject(type + " (" + totalCount + ")");
        return typeNode;
    }

    public void refreshTableWithAllIssues() {
        updateTableData(allIssues);
    }

    private void filterTableByTreeSelection(TreePath treePath) {
        int pathLevel = treePath.getPathCount();
        if (pathLevel == 2) {
            String type = getNodeTypeFromPath(treePath.getLastPathComponent().toString());
            filterByType(type);
        } else if (pathLevel == 3) {
            String type = getNodeTypeFromPath(treePath.getPathComponent(1).toString());
            String subType = getNodeTypeFromPath(treePath.getLastPathComponent().toString());
            filterBySubType(type, subType);
        } else {
            refreshTableWithAllIssues();
        }
    }

    private String getNodeTypeFromPath(String nodeText) {
        return nodeText.split(" \\(")[0];
    }

    private void filterByType(String type) {
        Map<String, List<Object[]>> subTypeMap = issueTypeMap.get(type);
        if (subTypeMap != null) {
            List<Object[]> filteredIssues = new ArrayList<>();
            for (List<Object[]> issues : subTypeMap.values()) {
                filteredIssues.addAll(issues);
            }
            updateTableData(filteredIssues);
        }
    }

    private void filterBySubType(String type, String subType) {
        Map<String, List<Object[]>> subTypeMap = issueTypeMap.get(type);
        if (subTypeMap != null) {
            List<Object[]> issues = subTypeMap.get(subType);
            if (issues != null) {
                updateTableData(issues);
            }
        }
    }

    private void updateTableData(List<Object[]> issues) {
        tableModel.setRowCount(0);
        for (Object[] row : issues) {
            tableModel.addRow(row);
        }
    }
}```

@springkill
Copy link
Member

感谢提交代码,最近太忙没时间维护,后续会更新合并@gamer2456

@springkill springkill added the bug Something isn't working label Jun 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants