├── .gitignore
├── LICENSE
├── README.md
├── build.gradle
├── docs
└── media
│ ├── xcheck_function.jpg
│ ├── xcheck_marketplace.jpg
│ └── xcheck_process.jpg
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
└── main
├── java
├── cn
│ └── xmirror
│ │ └── sca
│ │ ├── common
│ │ ├── CheckListener.java
│ │ ├── OpenSCASettingState.java
│ │ ├── ProjectIdentity.java
│ │ ├── ResultParser.java
│ │ ├── SCAThreadPool.java
│ │ ├── constant
│ │ │ ├── EngineOsEnum.java
│ │ │ ├── ExploitLevelEnum.java
│ │ │ └── SecurityLevelEnum.java
│ │ ├── dto
│ │ │ ├── AuthResult.java
│ │ │ ├── Component.java
│ │ │ ├── FilePath.java
│ │ │ ├── GeneralSelectDTO.java
│ │ │ ├── IdeSyncResultDTO.java
│ │ │ ├── Origin.java
│ │ │ ├── Overview.java
│ │ │ ├── ProjectSelectDTO.java
│ │ │ └── Vulnerability.java
│ │ ├── exception
│ │ │ ├── ErrorEnum.java
│ │ │ └── SCAException.java
│ │ ├── pojo
│ │ │ ├── DsnConfig.java
│ │ │ └── OpenSCASetting.java
│ │ └── util
│ │ │ ├── HttpUtils.java
│ │ │ └── VerifyUtils.java
│ │ ├── engine
│ │ ├── EngineAssistant.java
│ │ └── EngineDownloader.java
│ │ ├── service
│ │ ├── CheckService.java
│ │ └── HttpService.java
│ │ └── ui
│ │ ├── NotificationUtils.java
│ │ ├── ToolWindowAccess.java
│ │ ├── ToolWindowManager.java
│ │ ├── action
│ │ ├── CleanAction.java
│ │ ├── CollapseAll.java
│ │ ├── Configurable.java
│ │ ├── DisplayCritical.java
│ │ ├── DisplayHigh.java
│ │ ├── DisplayLow.java
│ │ ├── DisplayMedium.java
│ │ ├── ExpandAll.java
│ │ ├── ExportAction.java
│ │ ├── RunAction.java
│ │ ├── SettingAction.java
│ │ └── StopAction.java
│ │ ├── dialog
│ │ ├── AuthDialog.java
│ │ ├── DataSourceDialog.java
│ │ └── SyncResultDialog.java
│ │ └── window
│ │ ├── ConfigurablePanel.java
│ │ ├── DescriptionComponentPanel.java
│ │ ├── DescriptionOverviewPanel.java
│ │ ├── DescriptionPanel.java
│ │ ├── DescriptionVulnerabilityPanel.java
│ │ ├── DsnListPanel.java
│ │ ├── DsnTableModel.java
│ │ ├── GuidePanel.java
│ │ ├── MyGridConstraints.java
│ │ ├── OverviewPanel.java
│ │ ├── PaintUtils.java
│ │ ├── ToolWindowContentPanel.java
│ │ ├── ToolWindowFactory.java
│ │ ├── ToolWindowMainPanel.java
│ │ ├── TreeCellRenderer.java
│ │ └── tree
│ │ ├── ComponentTreeNode.java
│ │ ├── FilePathTreeNode.java
│ │ ├── RootTreeNode.java
│ │ └── VulnerabilityTreeNode.java
└── icons
│ └── Icons.java
└── resources
├── META-INF
├── plugin.xml
└── pluginIcon.svg
└── icons
├── component_statistics_20.svg
├── component_statistics_20_dark.svg
├── failed.svg
├── failed_dark.svg
├── fix_suggestion_20.svg
├── fix_suggestion_20_dark.svg
├── license_20.svg
├── license_20_dark.svg
├── license_statistics_20.svg
├── license_statistics_20_dark.svg
├── open_sca.svg
├── refresh.svg
├── sec.svg
├── severity_blacklist.svg
├── severity_blacklist_24.svg
├── severity_critical.svg
├── severity_critical_24.svg
├── severity_high.svg
├── severity_high_24.svg
├── severity_low.svg
├── severity_low_24.svg
├── severity_medium.svg
├── severity_medium_24.svg
├── severity_no_rating.svg
├── severity_no_rating_24.svg
├── succeeded.svg
├── succeeded_dark.svg
├── use_suggestion_20.svg
├── use_suggestion_20_dark.svg
├── vul_description_20.svg
├── vul_description_20_dark.svg
├── vul_statistics_20.svg
├── vul_statistics_20_dark.svg
├── warning.svg
├── xmirror_logo.png
└── xmirror_logo_24.svg
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle/
2 | .idea/
3 | build/
4 | *.iml
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://plugins.jetbrains.com/plugin/18246-opensca-xcheck)
2 | [](https://plugins.jetbrains.com/plugin/18246-opensca-xcheck)
3 | [](https://plugins.jetbrains.com/plugin/18246-opensca-xcheck)
4 | [](https://github.com/XmirrorSecurity/OpenSCA-intellij-plugin/blob/master/LICENSE)
5 |
6 |
7 |
8 |
9 | OpenSCA Xcheck
10 | IntelliJ平台的OpenSCA Xcheck插件,让代码更安全
11 |
12 | ---
13 |
14 | ## 项目介绍
15 |
16 | [Xcheck](https://plugins.jetbrains.com/plugin/18246-opensca-xcheck)是基于IntelliJ平台的OpenSCA插件。Xcheck能对当前项目进行代码质量评估,并在可视化界面中展示评估结果。评估结果包括漏洞和有漏洞的组件的统计数、具体组件信息和相关漏洞信息。
17 |
18 | ## 安装插件
19 |
20 | **安装方法一**:从 [Jetbrains 插件市场](https://plugins.jetbrains.com/plugin/18246-opensca-xcheck) 中安装(推荐)
21 |
22 | 以IntelliJ IDEA为例:在IDE中依次点击“File|Settings|Plugins|Marketplace”,在搜索框中输入“OpenSCA Xcheck”,点击“Install”
23 |
24 |
25 |
26 | **安装方法二**:在[OpenSCA平台](https://opensca.xmirror.cn/pages/plug-in )下载插件安装
27 |
28 | 以IntelliJ IDEA为例:将下载下来的插件安装包拖入适配的IDE中即可
29 |
30 | **安装方法三**:[下载源码](https://github.com/XmirrorSecurity/OpenSCA-intellij-plugin )自行编译安装
31 |
32 | 使用IntelliJ IDEA打开下载到本地的源码,需要配置运行环境:`jDK11`,待Gradle导入依赖和插件,在Gradle中执行`intellij`插件的`buildPlugin`任务,构建的安装包存放于当前项目下*build/distributions*目录下,将此目录下的安装包拖入当前IDE中即可
33 |
34 | ## 使用插件
35 |
36 | ### 插件功能
37 |
38 | - 配置:点击File|Settings|Other Settings|OpenSCA Setting或点击OpenSCA窗口中的`Setting`按钮,在配置界面中配置连接服务器Url和Token
39 | - 测试连接:在OpenSCA配置界面中,配置服务器Url和Token之后点击`测试连接`按钮可验证Url和Token是否有效
40 | - 运行:点击OpenSCA窗口中的`Run`按钮,可对当前项目进行代码评估
41 | - 停止:如果正在对当前项目代码评估,那么`Stop`按钮是可用的,点击Stop按钮可结束当前评估任务
42 | - 清除:如果OpenSCA窗口中的Xcheck子窗口已有评估结果,点击`Clean`按钮可清除Xcheck子窗口中所有结果
43 |
44 |
45 |
46 | ### 插件执行流程
47 |
48 |
49 |
50 | ### 使用插件
51 |
52 | 点击View|Tool Windows|OpenSCA可打开OpenSCA窗口。首先在OpenSCA配置界面中配置服务器参数(参考:插件功能-配置),然后在OpenSCA窗口中点击“运行”(参考:插件功能-运行)
53 |
54 | ## 友情链接
55 |
56 | [悬镜官网](https://www.xmirror.cn/), [OpenSCA官网](https://opensca.xmirror.cn)
57 |
58 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'org.jetbrains.intellij' version '1.3.0'
3 | id 'java'
4 | }
5 |
6 | group 'cn.xmirror.sca'
7 | version '1.0.7'
8 |
9 | repositories {
10 | mavenCentral()
11 | }
12 |
13 | dependencies {
14 | compileOnly group: 'org.projectlombok', name: 'lombok', version: '1.18.12'
15 | annotationProcessor group: 'org.projectlombok', name: 'lombok', version: '1.18.12'
16 | implementation group: 'com.alibaba', name: 'fastjson', version: '1.2.83'
17 | implementation group: 'org.jsoup', name: 'jsoup', version: '1.16.1'
18 |
19 | }
20 |
21 | // See https://github.com/JetBrains/gradle-intellij-plugin/
22 | intellij {
23 | version = '2021.3'
24 | }
25 | patchPluginXml {
26 | version = project.version
27 | sinceBuild = '212.*'
28 | untilBuild = '243.*'
29 | changeNotes = """
30 |
31 | 检测记录同步至SaaS
32 | 增加多数据源、自定义cli配置
33 | 优化漏洞结果展示
34 | 修复Unix系统下命令行工具执行失败
35 |
36 | """
37 | }
38 | test {
39 | useJUnitPlatform()
40 | }
41 | tasks.withType(JavaCompile) {
42 | options.encoding = "UTF-8"
43 | }
44 |
--------------------------------------------------------------------------------
/docs/media/xcheck_function.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XmirrorSecurity/OpenSCA-intellij-plugin/5dfeca6fb0bcbefb3976ad38f6b01d92ca85798b/docs/media/xcheck_function.jpg
--------------------------------------------------------------------------------
/docs/media/xcheck_marketplace.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XmirrorSecurity/OpenSCA-intellij-plugin/5dfeca6fb0bcbefb3976ad38f6b01d92ca85798b/docs/media/xcheck_marketplace.jpg
--------------------------------------------------------------------------------
/docs/media/xcheck_process.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XmirrorSecurity/OpenSCA-intellij-plugin/5dfeca6fb0bcbefb3976ad38f6b01d92ca85798b/docs/media/xcheck_process.jpg
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XmirrorSecurity/OpenSCA-intellij-plugin/5dfeca6fb0bcbefb3976ad38f6b01d92ca85798b/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MSYS* | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'opensca-intellij-plugin'
2 |
3 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/common/CheckListener.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.common;
2 |
3 | import javax.swing.tree.MutableTreeNode;
4 |
5 | /**
6 | * 检查监听器
7 | *
8 | * @author Yuan Shengjun
9 | */
10 | public interface CheckListener {
11 |
12 | void progress(boolean running);
13 |
14 | void onSuccess(MutableTreeNode resultTree);
15 |
16 | void onError(Exception e);
17 |
18 | void clean();
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/common/OpenSCASettingState.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.common;
2 |
3 | import cn.xmirror.sca.common.pojo.OpenSCASetting;
4 | import com.intellij.openapi.application.ApplicationManager;
5 | import com.intellij.openapi.components.PersistentStateComponent;
6 | import com.intellij.openapi.components.State;
7 | import com.intellij.openapi.components.Storage;
8 | import com.intellij.util.xmlb.XmlSerializerUtil;
9 | import lombok.Getter;
10 | import lombok.Setter;
11 | import org.jetbrains.annotations.NotNull;
12 | import org.jetbrains.annotations.Nullable;
13 |
14 | /**
15 | * 持久化配置数据
16 | * @author xingluheng
17 | * @date 2023/07/19 14:27
18 | **/
19 | @State(name = "cn.xmirror.sca.common.OpenSCASettingState",storages = @Storage("OpenSCAConfig.xml"))
20 | public class OpenSCASettingState implements PersistentStateComponent {
21 |
22 | @Setter
23 | @Getter
24 | private OpenSCASetting openSCASetting;
25 |
26 | public static OpenSCASettingState getInstance(){
27 | return ApplicationManager.getApplication().getService(OpenSCASettingState.class);
28 | }
29 |
30 | @Override
31 | public @Nullable OpenSCASettingState getState() {
32 | return this;
33 | }
34 |
35 | @Override
36 | public void loadState(@NotNull OpenSCASettingState state) {
37 | XmlSerializerUtil.copyBean(state, this);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/common/ProjectIdentity.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.common;
2 |
3 | import com.intellij.openapi.project.Project;
4 | import com.intellij.openapi.util.io.FileUtil;
5 |
6 | import java.io.File;
7 | import java.io.FileNotFoundException;
8 | import java.io.IOException;
9 | import java.util.UUID;
10 |
11 | /**
12 | * 项目标识,在.idea文件下生成uuid标识当前项目
13 | *
14 | * @author Yuan Shengjun
15 | */
16 | public class ProjectIdentity {
17 | private static final String filename = "OpenSCAIdentity";
18 |
19 | /**
20 | * 返回当前项目标识,若没有标识则生成一个返回
21 | *
22 | * @param project 当前项目
23 | * @return 标识
24 | */
25 | public static String checkOrSignature(Project project) {
26 | // 保存在当前项目下的.idea目录中
27 | String path = getIdentityFilename(project);
28 | File file = new File(path);
29 | String projectSignature = null;
30 | try {
31 | projectSignature = FileUtil.loadFile(file);
32 | } catch (FileNotFoundException e) {
33 | projectSignature = sign(file, project);
34 | } catch (Exception e) {
35 | e.printStackTrace();
36 | }
37 | return projectSignature;
38 | }
39 |
40 | /**
41 | * 签名
42 | *
43 | * @param file 签名保存位置
44 | * @param project 当前项目
45 | * @return
46 | */
47 | public static String sign(File file, Project project) {
48 | // 默认UUID,可以改变规则
49 | String signature = UUID.randomUUID().toString();
50 | try {
51 | FileUtil.writeToFile(file, signature);
52 | file.setReadOnly();
53 | } catch (IOException e) {
54 | e.printStackTrace();
55 | }
56 | return signature;
57 | }
58 |
59 | /**
60 | * 获取签名保存位置,默认放在当前项目下.idea目录中
61 | *
62 | * @param project 当前项目
63 | * @return
64 | */
65 | public static String getIdentityFilename(Project project) {
66 | return project.getBasePath() + File.separator + ".idea" + File.separator + filename;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/common/ResultParser.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.common;
2 |
3 | import cn.xmirror.sca.common.constant.ExploitLevelEnum;
4 | import cn.xmirror.sca.common.constant.SecurityLevelEnum;
5 | import cn.xmirror.sca.common.dto.Component;
6 | import cn.xmirror.sca.common.dto.FilePath;
7 | import cn.xmirror.sca.common.dto.Overview;
8 | import cn.xmirror.sca.common.dto.Vulnerability;
9 | import cn.xmirror.sca.common.exception.ErrorEnum;
10 | import cn.xmirror.sca.common.exception.SCAException;
11 | import cn.xmirror.sca.service.CheckService;
12 | import cn.xmirror.sca.ui.window.ConfigurablePanel;
13 | import cn.xmirror.sca.ui.window.tree.ComponentTreeNode;
14 | import cn.xmirror.sca.ui.window.tree.FilePathTreeNode;
15 | import cn.xmirror.sca.ui.window.tree.RootTreeNode;
16 | import cn.xmirror.sca.ui.window.tree.VulnerabilityTreeNode;
17 | import com.alibaba.fastjson.JSON;
18 | import com.intellij.openapi.diagnostic.Logger;
19 | import com.intellij.openapi.util.text.StringUtil;
20 | import lombok.Data;
21 |
22 | import javax.swing.tree.MutableTreeNode;
23 | import java.io.FileInputStream;
24 | import java.io.IOException;
25 | import java.io.InputStream;
26 | import java.nio.charset.StandardCharsets;
27 | import java.util.*;
28 | import java.util.stream.Collectors;
29 |
30 | /**
31 | * 结果解析器。从json文件中解析结果——转换成树
32 | *
33 | * @author Yuan Shengjun
34 | */
35 | public class ResultParser {
36 | private static final Logger LOG = Logger.getInstance(ConfigurablePanel.class);
37 |
38 |
39 | /**
40 | * 解析结果
41 | *
42 | * @param path 结果位置
43 | * @param overview 需要展示的概览数据
44 | */
45 | public static MutableTreeNode parseResult(String path, Overview overview) {
46 | try (InputStream is = new FileInputStream(path)) {
47 | // 获取结果
48 | Result result = JSON.parseObject(is, StandardCharsets.UTF_8, Result.class);
49 | if (StringUtil.isNotEmpty(result.getError())) {
50 | throw new SCAException(ErrorEnum.ENGINE_UNREACHABLE_ERROR);
51 | }
52 |
53 | // 平铺获取有漏洞的组件
54 | List componentList = new ArrayList<>();
55 | result.getChildren().forEach(item -> {
56 | List allComponent = getAllComponent(item, new ArrayList());
57 | componentList.addAll(allComponent);
58 | });
59 |
60 | return generateTree(overview, componentList.isEmpty() ? result.getChildren() : componentList);
61 | } catch (IOException e) {
62 | LOG.error(e);
63 | throw new SCAException(ErrorEnum.CHECK_PARSE_RESULT_ERROR);
64 | }
65 | }
66 |
67 | /**
68 | * 递归获取有漏洞的组件树
69 | *
70 | * @param component
71 | * @param componentList
72 | * @return
73 | */
74 | private static List getAllComponent(Component component, ArrayList componentList) {
75 | if (component.getVulnerabilities() != null && !component.getVulnerabilities().isEmpty()) {
76 | componentList.add(component);
77 | }
78 | if (component.getChildren() == null || component.getChildren().isEmpty()) {
79 | return componentList;
80 | }
81 | for (Component child : component.getChildren()) {
82 | getAllComponent(child, componentList);
83 | }
84 | return componentList;
85 | }
86 |
87 | /**
88 | * 生成树
89 | *
90 | * @param overview
91 | * @param components
92 | * @return
93 | */
94 | public static MutableTreeNode generateTree(Overview overview, List components) {
95 | RootTreeNode rootTreeNode = new RootTreeNode(overview);
96 | Set distinctVulnerability = new HashSet<>();
97 | Arrays.fill(overview.getCss(), 0);
98 | Arrays.fill(overview.getVss(), 0);
99 | if (components != null) {
100 | Collection distinctAndSortComponentCollection = distinctAndSortComponent(components);
101 | List pathList = distinctAndSortComponentCollection.stream().map(Component::getPath)
102 | .distinct().collect(Collectors.toList());
103 | pathList.forEach(path -> {
104 | FilePathTreeNode filePathTreeNode = new FilePathTreeNode(new FilePath(path));
105 | distinctAndSortComponentCollection.forEach(component -> {
106 | if (component.getPath().equals(path)) {
107 | ComponentTreeNode componentTreeNode = new ComponentTreeNode(component);
108 | for (Vulnerability vulnerability : component.getVulnerabilities()) {
109 | checkVulnerability(vulnerability);
110 | if (distinctVulnerability.add(vulnerability.getId())) {
111 | SecurityLevelEnum.statistics(vulnerability.getSecurityLevelId(), overview.getVss());
112 | }
113 | VulnerabilityTreeNode vulnerabilityTreeNode = new VulnerabilityTreeNode(vulnerability);
114 | componentTreeNode.add(vulnerabilityTreeNode);
115 | }
116 | SecurityLevelEnum.statistics(component.getSecurityLevelId(), overview.getCss());
117 | filePathTreeNode.add(componentTreeNode);
118 | }
119 | });
120 | rootTreeNode.add(filePathTreeNode);
121 | });
122 | }
123 | return rootTreeNode;
124 | }
125 |
126 | public static void checkVulnerability(Vulnerability v) {
127 | if (StringUtil.isEmpty(v.getName())) {
128 | v.setName("未知安全漏洞");
129 | }
130 | if (v.getExploitLevelId() == null) {
131 | v.setExploitLevelId(ExploitLevelEnum.DIFFICULT.getLevel());
132 | }
133 | if (v.getSecurityLevelId() == null) {
134 | v.setSecurityLevelId(SecurityLevelEnum.LOW.getLevel());
135 | }
136 | }
137 |
138 | /**
139 | * 去重并且排序组件
140 | *
141 | * @param components
142 | * @return
143 | */
144 | private static Collection distinctAndSortComponent(List components) {
145 | Map mergeComponents = new HashMap<>();
146 | for (Component component : components) {
147 | String coordinate = component.getVendor() + component.getName() + component.getVersion();
148 | Component cpt = mergeComponents.get(coordinate);
149 | if (cpt == null) {
150 | component.setPaths(component.getPaths());
151 | // 相同组件去重
152 | if (!component.getPaths().isEmpty()) {
153 | String path = component.getPaths().get(0).replace(CheckService.PROJECT_BASE_PATH, "");
154 | component.setPath(path);
155 | if (path.contains("[")) {
156 | component.setPath(path.substring(0, path.indexOf("[") - 1));
157 | }
158 | }
159 | List vulnerabilities = component.getVulnerabilities();
160 | if (vulnerabilities != null && !vulnerabilities.isEmpty()) {
161 | List sortVulnListBySecurityLevelId = vulnerabilities.stream().sorted(Comparator.comparingInt(Vulnerability::getSecurityLevelId)).collect(Collectors.toList());
162 | component.setVulnerabilities(sortVulnListBySecurityLevelId);
163 | component.setSecurityLevelId(sortVulnListBySecurityLevelId.get(0).getSecurityLevelId());
164 | } else {
165 | component.setVulnerabilities(new ArrayList<>());
166 | component.setSecurityLevelId(5);
167 | }
168 |
169 | mergeComponents.put(coordinate, component);
170 | }
171 | }
172 | return mergeComponents.values().stream().sorted(Comparator.comparingInt(Component::getSecurityLevelId)).collect(Collectors.toList());
173 | }
174 |
175 | @Data
176 | public static class Result {
177 | private List children;
178 | private String error;
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/common/SCAThreadPool.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.common;
2 |
3 | import java.util.concurrent.*;
4 |
5 | /**
6 | * SCA线程池
7 | *
8 | * @author Yuan Shengjun
9 | */
10 | public class SCAThreadPool {
11 | private final ExecutorService executor;
12 |
13 | private SCAThreadPool() {
14 | executor = new ThreadPoolExecutor(0, 10, 60L, TimeUnit.SECONDS, new SynchronousQueue<>());
15 | }
16 |
17 | public static SCAThreadPool getInstance() {
18 | return InstanceHolder.instance;
19 | }
20 |
21 | public Future> submit(Runnable task) {
22 | return executor.submit(task);
23 | }
24 |
25 | public Future submit(Callable task) {
26 | return executor.submit(task);
27 | }
28 |
29 | private static class InstanceHolder {
30 | private static final SCAThreadPool instance = new SCAThreadPool();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/common/constant/EngineOsEnum.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.common.constant;
2 |
3 | /**
4 | * 引擎和操作系统枚举类。定义操作系统和引擎命令行工具名称关系
5 | *
6 | * @author Yuan Shengjun
7 | */
8 | public enum EngineOsEnum {
9 | MAC("mac","opensca-cli"),
10 | LINUX("linux","opensca-cli"),
11 | WINDOWS("windows","opensca-cli.exe"),
12 | ;
13 |
14 | private final String osName;
15 | private final String cliName;
16 |
17 | EngineOsEnum(String osName, String cliName) {
18 | this.osName = osName;
19 | this.cliName = cliName;
20 | }
21 |
22 | public String getOsName() {
23 | return osName;
24 | }
25 |
26 | public String getCliName() {
27 | return cliName;
28 | }
29 |
30 | public static EngineOsEnum getEngineOsEnum() {
31 | String systemName = System.getProperty("os.name").toLowerCase();
32 | for (EngineOsEnum value : values()) {
33 | if (systemName.contains(value.osName)) {
34 | return value;
35 | }
36 | }
37 | return null;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/common/constant/ExploitLevelEnum.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.common.constant;
2 |
3 | /**
4 | * 漏洞利用难度枚举类
5 | *
6 | * @author Yuan Shengjun
7 | */
8 | public enum ExploitLevelEnum {
9 | EASY(1, "容易"),
10 | DIFFICULT(0, "困难"),
11 | ;
12 |
13 | private final int level;
14 | private final String tag;
15 |
16 | ExploitLevelEnum(int level, String tag) {
17 | this.level = level;
18 | this.tag = tag;
19 | }
20 |
21 | public int getLevel() {
22 | return level;
23 | }
24 |
25 | public static String getTag(int level) {
26 | for (ExploitLevelEnum value : values()) {
27 | if (value.level == level) {
28 | return value.tag;
29 | }
30 | }
31 | return null;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/common/constant/SecurityLevelEnum.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.common.constant;
2 |
3 | /**
4 | * 风险等级枚举类
5 | *
6 | * @author Yuan Shengjun
7 | */
8 | public enum SecurityLevelEnum {
9 | CRITICAL(1, "严重"),
10 | HIGH(2, "高危"),
11 | MEDIUM(3, "中危"),
12 | LOW(4, "低危"),
13 | SEC(5,"安全")
14 | ;
15 |
16 | private final int level;
17 | private final String tag;
18 |
19 | SecurityLevelEnum(int level, String tag) {
20 | this.level = level;
21 | this.tag = tag;
22 | }
23 |
24 | public int getLevel() {
25 | return level;
26 | }
27 |
28 | public String getTag() {
29 | return tag;
30 | }
31 |
32 | public static void statistics(int level, int[] statistics) {
33 | if (level == CRITICAL.level) {
34 | statistics[0] += 1;
35 | } else if (level == HIGH.level) {
36 | statistics[1] += 1;
37 | } else if (level == MEDIUM.level) {
38 | statistics[2] += 1;
39 | } else if (level == LOW.level) {
40 | statistics[3] += 1;
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/common/dto/AuthResult.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.common.dto;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * IDE认证结果
7 | * @author xingluheng
8 | * @date 2023/08/31 11:56
9 | **/
10 | @Data
11 | public class AuthResult {
12 | /**
13 | * 认证结果 200:成功 401:用户没有Token 404:稍后再试(没有点击授权|服务器响应较慢)
14 | */
15 | private Integer code;
16 | /**
17 | * token 结果
18 | */
19 | private String token;
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/common/dto/Component.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.common.dto;
2 |
3 | import lombok.Data;
4 |
5 | import java.io.Serializable;
6 | import java.util.List;
7 |
8 | /**
9 | * 组件实体类
10 | *
11 | * @author Yuan Shengjun
12 | */
13 | @Data
14 | public class Component implements Serializable {
15 | /**
16 | * 厂商
17 | */
18 | private String vendor;
19 |
20 | /**
21 | * 组件名
22 | */
23 | private String name;
24 |
25 | /**
26 | * 组件版本
27 | */
28 | private String version;
29 |
30 | /**
31 | * 许可证列表
32 | */
33 | private List licenses;
34 |
35 | /**
36 | * 所属语言
37 | */
38 | private String language;
39 |
40 | /**
41 | * 组件文件路径
42 | */
43 | private String path;
44 |
45 | /**
46 | * 组件名风险等级
47 | */
48 | private Integer securityLevelId;
49 |
50 | /**
51 | * 组件漏洞
52 | */
53 | private List vulnerabilities;
54 |
55 | /**
56 | * 组件路径集合(辅助)
57 | */
58 | private List paths;
59 |
60 | /**
61 | * 依赖类型 true直接 false间接
62 | */
63 | private boolean direct;
64 |
65 | /**
66 | * 子children
67 | */
68 | private List children;
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/common/dto/FilePath.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.common.dto;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 |
7 | import java.io.Serializable;
8 | import java.util.List;
9 |
10 | /**
11 | * @author xingluheng
12 | * @date 2023/07/18 16:19
13 | **/
14 | @Data
15 | @NoArgsConstructor
16 | @AllArgsConstructor
17 | public class FilePath implements Serializable {
18 | private String path;
19 |
20 | @Override
21 | public String toString() {
22 | return path;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/common/dto/GeneralSelectDTO.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.common.dto;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * @author xingluheng
7 | */
8 | @Data
9 | public class GeneralSelectDTO {
10 | /**
11 | * 项目id编号
12 | */
13 | private String projectUid;
14 |
15 | /**
16 | * 主键id
17 | */
18 | private String id;
19 |
20 | /**
21 | * 名称
22 | */
23 | private String name;
24 |
25 | public GeneralSelectDTO() {
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/common/dto/IdeSyncResultDTO.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.common.dto;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * 用于上传的 SaaS 数据传输对象
7 | * @author xingluheng
8 | * @date 2023/11/06 11:07
9 | **/
10 | @Data
11 | public class IdeSyncResultDTO {
12 | /**
13 | * 用户Token
14 | */
15 | private String token;
16 | /**
17 | * 项目id
18 | */
19 | private String projectId;
20 | /**
21 | * 版本
22 | */
23 | private String version;
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/common/dto/Origin.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.common.dto;
2 |
3 | import cn.xmirror.sca.common.pojo.DsnConfig;
4 | import cn.xmirror.sca.common.pojo.OpenSCASetting;
5 | import com.alibaba.fastjson.JSONObject;
6 | import lombok.Data;
7 | import org.apache.commons.lang.StringUtils;
8 |
9 | import java.io.File;
10 | import java.io.IOException;
11 | import java.nio.charset.StandardCharsets;
12 | import java.nio.file.Files;
13 | import java.nio.file.Paths;
14 | import java.util.*;
15 | import java.util.stream.Collectors;
16 |
17 | /**
18 | * 配置文件 config.json
19 | * @author xingluheng
20 | * @date 2023/08/14 19:00
21 | **/
22 | @Data
23 | public class Origin {
24 |
25 | public static List originTypeList = new ArrayList<>() {{
26 | add("--请选择数据源类型--");
27 | add("MySQL");
28 | add("SQL Server");
29 | add("SQLite");
30 | add("PostgreSQL");
31 | add("JSON");
32 | }};
33 |
34 | /**
35 | * 替换数据源配置文件
36 | * @param openSCASetting
37 | * @return
38 | */
39 | public static String buildDsnJson(File configJsonFile, OpenSCASetting openSCASetting) throws IOException {
40 | List dsnConfigList = openSCASetting.getDsnConfigList();
41 | // 收集已勾选的配置文件 写入JSON
42 | dsnConfigList = dsnConfigList.stream().filter(item -> item.getSelect().equals(Boolean.TRUE)).collect(Collectors.toList());
43 |
44 | byte[] bytes = Files.readAllBytes(Paths.get(configJsonFile.getAbsolutePath()));
45 | String content = new String(bytes, StandardCharsets.UTF_8);
46 | JSONObject configObj= JSONObject.parseObject(content);
47 |
48 | // 设置进度展示为false
49 | JSONObject optional = configObj.getJSONObject("optional");
50 | optional.put("progress",false);
51 |
52 |
53 | JSONObject origin = configObj.getJSONObject("origin");
54 | origin.clear();
55 | for (DsnConfig dsnConfig : dsnConfigList) {
56 | JSONObject object = new JSONObject();
57 | object.put("dsn",dsnConfig.getDsn());
58 | if (StringUtils.isNotEmpty(dsnConfig.getTableName())){
59 | object.put("table",dsnConfig.getTableName());
60 | }
61 | origin.put(dsnConfig.getType().toLowerCase(Locale.ROOT),object);
62 | }
63 | if (openSCASetting.getRemoteDataSourceSelected()){
64 | origin.put("url",openSCASetting.getServerAddress());
65 | origin.put("token",openSCASetting.getToken());
66 | }
67 | return configObj.toString();
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/common/dto/Overview.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.common.dto;
2 |
3 | import lombok.Data;
4 |
5 | import java.util.Date;
6 |
7 | /**
8 | * 概述实体类
9 | *
10 | * @author Yuan Shengjun
11 | */
12 | @Data
13 | public class Overview {
14 | /**
15 | * 状态码
16 | */
17 | private Integer status;
18 |
19 | /**
20 | * 开始时间
21 | */
22 | private Date startTime;
23 |
24 | /**
25 | * 结束之间
26 | */
27 | private Date endTime;
28 |
29 | /**
30 | * 组件风险统计(Component Security Statistics)
31 | *
32 | * css[0]:严重
33 | * css[1]:高危
34 | * css[2]:中危
35 | * css[3]:低危
36 | *
37 | */
38 | private int[] css = new int[4];
39 |
40 | /**
41 | * 漏洞风险统计(Vulnerability Security Statistics)
42 | *
43 | * vss[0]:严重
44 | * vss[1]:高危
45 | * vss[2]:中危
46 | * vss[3]:低危
47 | *
48 | */
49 | private int[] vss = new int[4];
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/common/dto/ProjectSelectDTO.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.common.dto;
2 |
3 | import lombok.Data;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * @author xingluheng
9 | * @date 2023/11/06 15:38
10 | **/
11 | @Data
12 | public class ProjectSelectDTO {
13 | /**
14 | * 团队名称
15 | */
16 | private String teamName;
17 | private List projectList;
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/common/dto/Vulnerability.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.common.dto;
2 |
3 | import lombok.Data;
4 |
5 | import java.io.Serializable;
6 | import java.util.Date;
7 |
8 | /**
9 | * 漏洞实体类
10 | *
11 | * @author Yuan Shengjun
12 | */
13 | @Data
14 | public class Vulnerability implements Serializable {
15 | /**
16 | * 漏洞名称
17 | */
18 | private String name;
19 |
20 | /**
21 | * xmirror编号
22 | */
23 | private String id;
24 |
25 | /**
26 | * cve编号
27 | */
28 | private String cveId;
29 |
30 | /**
31 | * cnvd编号
32 | */
33 | private String cnvdId;
34 |
35 | /**
36 | * cnnvd编号
37 | */
38 | private String cnnvdId;
39 |
40 | /**
41 | * cwe编号
42 | */
43 | private String cweId;
44 |
45 | /**
46 | * 安全等级
47 | */
48 | private Integer securityLevelId;
49 |
50 | /**
51 | * 中文漏洞描述
52 | */
53 | private String description;
54 |
55 | /**
56 | * 英文漏洞描述
57 | */
58 | private String descriptionEn;
59 |
60 | /**
61 | * 修复建议
62 | */
63 | private String suggestion;
64 |
65 | /**
66 | * 发布时间
67 | */
68 | private Date releaseDate;
69 |
70 | /**
71 | * 利用难度
72 | */
73 | private Integer exploitLevelId;
74 | }
75 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/common/exception/ErrorEnum.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.common.exception;
2 |
3 | /**
4 | * 异常枚举类
5 | *
6 | * @author Yuan Shengjun
7 | */
8 | public enum ErrorEnum {
9 |
10 | // 通用
11 | CREATE_DIR_ERROR(40001, "创建目录失败"),
12 | COMMAND_INJECTION_ERROR(40002,"存在命令注入风险"),
13 | CREATE_FILE_ERROR(40003,"文件创建失败"),
14 |
15 | // 配置相关
16 | SETTING_URL_EMPTY_ERROR(40021, "url不能为空"),
17 | SETTING_TOKEN_EMPTY_ERROR(40022, "token不能为空"),
18 | SETTING_URL_PATTERN_ERROR(40023, "url格式错误"),
19 | SETTING_TOKEN_EXPIRE(40024, "OSS令牌已过期"),
20 |
21 | // 引擎相关
22 | ENGINE_UNSUPPORTED_SYSTEM_ERROR(40041, "不支持此操作系统"),
23 | ENGINE_DOWNLOAD_ERROR(40042, "下载引擎失败"),
24 | ENGINE_UNREACHABLE_ERROR(40043, "引擎连接失败"),
25 | ENGINE_SET_EXECUTABLE_ERROR(40044, "引擎设置可执行权限失败,请手动设置后重新检测"),
26 |
27 | // 检测相关
28 | CHECK_PARSE_RESULT_ERROR(40081,"结果解析错误"),
29 |
30 | // 服务器相关
31 | SERVER_UNREACHABLE_ERROR(40101,"连接服务器失败"),
32 | SERVER_REQUEST_FAILURE_ERROR(40102,"请求服务器失败"),
33 | ;
34 |
35 | private int code;
36 | private String message;
37 |
38 | ErrorEnum(int code, String message) {
39 | this.code = code;
40 | this.message = message;
41 | }
42 |
43 | public int getCode() {
44 | return code;
45 | }
46 |
47 | public String getMessage() {
48 | return message;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/common/exception/SCAException.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.common.exception;
2 |
3 | /**
4 | * 异常类
5 | *
6 | * @author Yuan Shengjun
7 | */
8 | public class SCAException extends RuntimeException {
9 | static final long serialVersionUID = 1L;
10 | private final ErrorEnum error;
11 |
12 | public SCAException(ErrorEnum error) {
13 | super("[" + error.getCode() + "]" + error.getMessage());
14 | this.error = error;
15 | }
16 |
17 | public SCAException(ErrorEnum error, String appendMessage) {
18 | super("[" + error.getCode() + "]" + error.getMessage() + ":" + appendMessage);
19 | this.error = error;
20 | }
21 |
22 | public ErrorEnum getError() {
23 | return error;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/common/pojo/DsnConfig.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.common.pojo;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 |
7 | import java.util.Objects;
8 |
9 | /**
10 | * @author xingluheng
11 | * @date 2023/08/14 10:11
12 | **/
13 | @Data
14 | @NoArgsConstructor
15 | @AllArgsConstructor
16 | public class DsnConfig {
17 | /**
18 | * 是否使用
19 | */
20 | private Boolean select;
21 | /**
22 | * 数据源名称
23 | */
24 | private String type;
25 | /**
26 | * 数据源地址
27 | */
28 | private String dsn;
29 | /**
30 | * 表名称
31 | */
32 | private String tableName;
33 |
34 |
35 | @Override
36 | public boolean equals(Object o) {
37 | if (this == o) {
38 | return true;
39 | }
40 | if (!(o instanceof DsnConfig)) {
41 | return false;
42 | }
43 | DsnConfig other = (DsnConfig) o;
44 | return Objects.equals(select, other.select) &&
45 | Objects.equals(type, other.type) &&
46 | Objects.equals(tableName,tableName) &&
47 | Objects.equals(dsn, other.dsn);
48 | }
49 |
50 | @Override
51 | public int hashCode() {
52 | return Objects.hash(select, type, dsn);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/common/pojo/OpenSCASetting.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.common.pojo;
2 |
3 | import lombok.*;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * 配置持久化配置类
9 | * @author xingluheng
10 | * @date 2023/07/19 15:07
11 | **/
12 | @Data
13 | @AllArgsConstructor
14 | @NoArgsConstructor
15 | public class OpenSCASetting {
16 | /**
17 | * 服务器地址
18 | */
19 | private String serverAddress;
20 | /**
21 | * Token
22 | */
23 | private String token;
24 | /**
25 | * 自定义Cli地址
26 | */
27 | private Boolean useCustomerCli;
28 | /**
29 | * 自定义Cli地址
30 | */
31 | private String customerPath;
32 | /**
33 | * 远程数据源
34 | */
35 | private Boolean remoteDataSourceSelected;
36 | /**
37 | * 本地数据源
38 | */
39 | private Boolean localDataSourceSelected;
40 | /**
41 | * 数据源配置
42 | */
43 | private List dsnConfigList;
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/common/util/VerifyUtils.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.common.util;
2 |
3 | import cn.xmirror.sca.common.exception.ErrorEnum;
4 | import cn.xmirror.sca.common.exception.SCAException;
5 | import cn.xmirror.sca.service.HttpService;
6 | import org.apache.commons.lang.StringUtils;
7 | import org.jsoup.internal.StringUtil;
8 |
9 | import java.util.regex.Pattern;
10 |
11 | import static cn.xmirror.sca.service.HttpService.testConnectionUri;
12 |
13 | /**
14 | * 验证工具类
15 | *
16 | * @author Yuan Shengjun
17 | */
18 | public class VerifyUtils {
19 |
20 | private static final String urlPattern = "(http|https)://([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:/~+#-]*[\\w@?^=%&/~+#-])?";
21 |
22 | public static void verifyUrl(String url) {
23 | if (StringUtil.isBlank(url)) {
24 | throw new SCAException(ErrorEnum.SETTING_URL_EMPTY_ERROR);
25 | }
26 | Pattern pattern = Pattern.compile(urlPattern);
27 | if (!pattern.matcher(url).matches()) {
28 | throw new SCAException(ErrorEnum.SETTING_URL_PATTERN_ERROR);
29 | }
30 | }
31 |
32 | public static void verifyToken(String url,String token) {
33 | if (StringUtils.isEmpty(token)) return;
34 | try {
35 | HttpService.getRequest(testConnectionUri, url, token, null, 5000);
36 | } catch (Exception e) {
37 | throw new SCAException(ErrorEnum.SETTING_TOKEN_EXPIRE);
38 | }
39 |
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/engine/EngineAssistant.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.engine;
2 |
3 | import cn.xmirror.sca.common.ProjectIdentity;
4 | import cn.xmirror.sca.common.constant.EngineOsEnum;
5 | import cn.xmirror.sca.common.exception.ErrorEnum;
6 | import cn.xmirror.sca.common.exception.SCAException;
7 | import com.intellij.openapi.project.Project;
8 | import com.intellij.openapi.util.io.FileUtil;
9 |
10 | import java.io.File;
11 |
12 | /**
13 | * 引擎辅助类
14 | *
15 | * @author Yuan Shengjun
16 | */
17 | public class EngineAssistant {
18 | private static final String defaultSCADirectory = System.getProperty("user.home") + File.separator + "OpenSCA";
19 | private static final String defaultEngineDirectory = defaultSCADirectory + File.separator + "engine";
20 | private static final String defaultEngineCliDirectory = defaultEngineDirectory + File.separator + "cli";
21 | private static final String defaultResultDataDirectory = defaultEngineDirectory + File.separator + "data";
22 |
23 | /**
24 | * 获取引擎脚本所在路径。默认路径是~/OpenSCA/engine/cli/xxx
25 | *
26 | * @return 引擎脚本所在路径。如果不持支当前操作系统,返回null
27 | */
28 | public static String getEngineCliPath() {
29 | String engineCliName = getEngineCliName();
30 | return defaultEngineCliDirectory + File.separator + engineCliName;
31 | }
32 |
33 | /**
34 | * 获取支持当前操作系统的引擎名称
35 | *
36 | * @return 引擎名称。如果不持支当前操作系统,抛出异常
37 | */
38 | public static String getEngineCliName() {
39 | EngineOsEnum currentEngineOs = EngineOsEnum.getEngineOsEnum();
40 | if (currentEngineOs == null) {
41 | throw new SCAException(ErrorEnum.ENGINE_UNSUPPORTED_SYSTEM_ERROR);
42 | }
43 | return currentEngineOs.getCliName();
44 | }
45 |
46 | /**
47 | * 获取当前操作系统简洁名称
48 | *
49 | * @return
50 | */
51 | public static String getCurrentOsName() {
52 | EngineOsEnum currentEngineOs = EngineOsEnum.getEngineOsEnum();
53 | if (currentEngineOs == null) {
54 | throw new SCAException(ErrorEnum.ENGINE_UNSUPPORTED_SYSTEM_ERROR);
55 | }
56 | return currentEngineOs.getOsName();
57 | }
58 |
59 | /**
60 | * 获取当前操作系统架构
61 | *
62 | * @return
63 | */
64 | public static String getCurrentSystemArch() {
65 | return System.getProperty("os.arch").toLowerCase();
66 | }
67 |
68 | /**
69 | * 获取检测Json文件结果保存路径
70 | *
71 | * @return
72 | */
73 | public static String getCheckResultJsonPath(Project project) {
74 | String dirPath = EngineAssistant.getDefaultResultDataDirectory();
75 | if (!FileUtil.createDirectory(new File(dirPath))) {
76 | throw new SCAException(ErrorEnum.CREATE_DIR_ERROR, dirPath);
77 | }
78 | return dirPath + File.separator + ProjectIdentity.checkOrSignature(project) + ".json";
79 | }
80 |
81 | /**
82 | * 获取检测Dsdx文件结果保存路径
83 | *
84 | * @param project
85 | * @return
86 | */
87 | public static String getCheckResultDsdxPath(Project project){
88 | String dirPath = EngineAssistant.getDefaultResultDataDirectory();
89 | if (!FileUtil.createDirectory(new File(dirPath))) {
90 | throw new SCAException(ErrorEnum.CREATE_DIR_ERROR, dirPath);
91 | }
92 | return dirPath + File.separator + ProjectIdentity.checkOrSignature(project) + ".dsdx";
93 | }
94 |
95 | /**
96 | * 获取引擎版本路径
97 | *
98 | * @return
99 | */
100 | public static String getEngineVersionPath() {
101 | String versionPath = defaultEngineCliDirectory + File.separator + "version";
102 | FileUtil.createParentDirs(new File(versionPath));
103 | return versionPath;
104 | }
105 |
106 | /**
107 | * 获取存放引擎的默认目录路径
108 | *
109 | * @return 存放引擎的默认目录路径
110 | */
111 | public static String getDefaultEngineCliDirectory() {
112 | return defaultEngineCliDirectory;
113 | }
114 |
115 | /**
116 | * 获取存放用户数据的默认目录路径
117 | *
118 | * @return 存放用户数据的默认目录路径
119 | */
120 | public static String getDefaultResultDataDirectory() {
121 | return defaultResultDataDirectory;
122 | }
123 |
124 | /**
125 | * 获取检测结果保存路径
126 | * @param engineCliPath 自定义cli路径
127 | * @return
128 | */
129 | public static String getCustomerCheckResultPath(String engineCliPath) {
130 | String basePath = new File(engineCliPath).getParent();
131 | return basePath + File.separator + "output.json";
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/engine/EngineDownloader.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.engine;
2 |
3 | import cn.xmirror.sca.common.exception.ErrorEnum;
4 | import cn.xmirror.sca.common.exception.SCAException;
5 | import cn.xmirror.sca.service.HttpService;
6 | import com.intellij.openapi.progress.ProgressManager;
7 | import com.intellij.openapi.project.Project;
8 |
9 | import java.io.*;
10 |
11 | /**
12 | * 下载引擎
13 | */
14 | public class EngineDownloader {
15 |
16 |
17 | /**
18 | * 检查更新最新版本的cli
19 | * @param engineCliPath
20 | * @param project
21 | * @throws IOException
22 | * @throws InterruptedException
23 | */
24 | public static void checkAndUpdateToLatestVersion(String engineCliPath,Project project) throws IOException, InterruptedException {
25 | String remoteServerCliVersion = HttpService.getRemoteServerCliVersion().trim();
26 |
27 | String localCliVersion = getLocalCliVersion(engineCliPath).trim();
28 | if (remoteServerCliVersion.equals(localCliVersion)) return;
29 | // 版本号不一致 更新至最新版
30 | ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> {
31 | HttpService.downloadEngine(engineCliPath);
32 | File engineCli = new File(engineCliPath);
33 | if (!engineCli.canExecute() && !engineCli.setExecutable(true)) {
34 | throw new SCAException(ErrorEnum.ENGINE_SET_EXECUTABLE_ERROR, engineCliPath);
35 | }
36 | try {
37 | createCliConfig(engineCliPath);
38 | } catch (IOException | InterruptedException e) {
39 | throw new RuntimeException(e);
40 | }
41 | }, "更新/下载OpenSCA命令行工具", false, project);
42 | }
43 |
44 | /**
45 | * 创建当前版本cli的 配置文件
46 | * @param engineCliPath
47 | * @throws IOException
48 | * @throws InterruptedException
49 | */
50 | public static String createCliConfig(String engineCliPath) throws IOException, InterruptedException {
51 | File cliFile = new File(engineCliPath);
52 |
53 | String configJsonFile = cliFile.getParent() +File.separator + "config.json";
54 | File file = new File(configJsonFile);
55 | if (file.exists()) file.delete();
56 |
57 | String[] cmd = {engineCliPath, "-config",configJsonFile};
58 | Process process = Runtime.getRuntime().exec(cmd);
59 | process.waitFor();
60 | return configJsonFile;
61 | }
62 |
63 |
64 | /**
65 | * 获取当前版本cli的版本号
66 | * @param engineCliPath
67 | * @return
68 | * @throws IOException
69 | * @throws InterruptedException
70 | */
71 | public static String getLocalCliVersion(String engineCliPath) throws IOException, InterruptedException {
72 | String[] cmd = {engineCliPath, "-version"};
73 | Process process = Runtime.getRuntime().exec(cmd);
74 | process.waitFor();
75 |
76 | InputStream inputStream = process.getInputStream();
77 | BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
78 | String line;
79 | StringBuilder result = new StringBuilder();
80 | while ((line = reader.readLine()) != null) {
81 | result.append(line).append("\n");
82 | }
83 | return result.toString();
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/service/CheckService.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.service;
2 |
3 | import cn.xmirror.sca.common.CheckListener;
4 | import cn.xmirror.sca.common.OpenSCASettingState;
5 | import cn.xmirror.sca.common.ResultParser;
6 | import cn.xmirror.sca.common.SCAThreadPool;
7 | import cn.xmirror.sca.common.dto.Origin;
8 | import cn.xmirror.sca.common.dto.Overview;
9 | import cn.xmirror.sca.common.exception.ErrorEnum;
10 | import cn.xmirror.sca.common.exception.SCAException;
11 | import cn.xmirror.sca.common.util.VerifyUtils;
12 | import cn.xmirror.sca.engine.EngineAssistant;
13 | import cn.xmirror.sca.engine.EngineDownloader;
14 | import cn.xmirror.sca.ui.NotificationUtils;
15 | import com.intellij.notification.NotificationType;
16 | import com.intellij.openapi.diagnostic.Logger;
17 | import com.intellij.openapi.project.Project;
18 | import com.intellij.openapi.util.io.FileUtil;
19 |
20 | import javax.swing.tree.MutableTreeNode;
21 | import java.io.*;
22 | import java.util.Arrays;
23 | import java.util.Date;
24 | import java.util.Map;
25 | import java.util.concurrent.ConcurrentHashMap;
26 | import java.util.concurrent.Future;
27 |
28 | /**
29 | * 检测服务类
30 | *
31 | * @author Yuan Shengjun
32 | */
33 | public class CheckService {
34 | private static final Logger LOG = Logger.getInstance(CheckService.class);
35 |
36 | /**
37 | * 任务状态
38 | *
39 | * 0:已停止
40 | * 1:停止中
41 | * 2:运行中
42 | *
43 | */
44 | private static final Map status = new ConcurrentHashMap<>();
45 | private static final Integer stopped = 0;
46 | private static final Integer stopping = 1;
47 | private static final Integer running = 2;
48 |
49 | private static final Map> futures = new ConcurrentHashMap<>();
50 | private static final Map processes = new ConcurrentHashMap<>();
51 |
52 | public static String PROJECT_BASE_PATH;
53 | private static final OpenSCASettingState openSCASettingState = OpenSCASettingState.getInstance();
54 |
55 | /**
56 | * 开始检测
57 | *
58 | * @param project 当前项目
59 | * @param listener 检测监听器
60 | */
61 | public static void run(Project project, CheckListener listener) {
62 | PROJECT_BASE_PATH = project.getBasePath() + File.separator;
63 | status.put(project, running);
64 | Overview overview = new Overview();
65 | overview.setStartTime(new Date());
66 |
67 | Future> future = SCAThreadPool.getInstance().submit(() -> {
68 | try {
69 | listener.progress(true);
70 | clean(project, listener);
71 | if (openSCASettingState.getOpenSCASetting() == null) {
72 | NotificationUtils.balloonNotify("请配置OpenSCA后,再进行检测", NotificationType.ERROR);
73 | return;
74 | }
75 | // URL和Token 参数校验
76 | String url = openSCASettingState.getOpenSCASetting().getServerAddress();
77 | String token = openSCASettingState.getOpenSCASetting().getToken();
78 | if (!openSCASettingState.getOpenSCASetting().getRemoteDataSourceSelected()) token = "";
79 | VerifyUtils.verifyUrl(url);
80 | VerifyUtils.verifyToken(url, token);
81 |
82 | // CLI地址校验
83 | String engineCliPath = EngineAssistant.getEngineCliPath();
84 | if (openSCASettingState.getOpenSCASetting().getUseCustomerCli()) {
85 | engineCliPath = openSCASettingState.getOpenSCASetting().getCustomerPath();
86 | }
87 | LOG.info("OpenSCA CLI 目录:------>" + engineCliPath);
88 | File cli = new File(engineCliPath);
89 | if (!cli.exists() && !cli.isFile()) {
90 | NotificationUtils.balloonNotify("请配置OpenSCA CLI后进行检测", NotificationType.ERROR);
91 | return;
92 | }
93 |
94 | // 检测是否为最新版 如果不是自动更新
95 | EngineDownloader.checkAndUpdateToLatestVersion(engineCliPath, project);
96 |
97 | // 创建数据源配置文件
98 | makeConfigJsonFile(engineCliPath);
99 |
100 | String inputPath = project.getBasePath();
101 | String outputJsonPath = EngineAssistant.getCheckResultJsonPath(project);
102 | String outputDsdxPath = EngineAssistant.getCheckResultDsdxPath(project);
103 | String logFilePath = cli.getParent() + File.separator + "opensca.log";
104 | String[] cmd = {engineCliPath, "-token", token, "-path", inputPath, "-out", outputJsonPath + "," + outputDsdxPath, "-log", logFilePath};
105 | Process process = Runtime.getRuntime().exec(cmd);
106 | processes.put(project, process);
107 | LOG.info("OpenSCA开始检测:------>" + Arrays.toString(cmd));
108 |
109 | process.waitFor();
110 | // 记录检测日志
111 | InputStream inputStream = process.getInputStream();
112 | BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
113 | String line;
114 | StringBuilder result = new StringBuilder();
115 | while ((line = reader.readLine()) != null) {
116 | result.append(line).append("\n");
117 | }
118 | String executionResult = result.toString();
119 | LOG.info("OpenSCA检测结束:------>" + executionResult);
120 |
121 | // 解析结果
122 | overview.setEndTime(new Date());
123 | MutableTreeNode resultTree = ResultParser.parseResult(outputJsonPath, overview);
124 | listener.onSuccess(resultTree);
125 | } catch (Exception e) {
126 | if (e instanceof InterruptedException) {
127 | NotificationUtils.balloonNotify("检测停止成功", NotificationType.INFORMATION);
128 | } else {
129 | LOG.error(e);
130 | }
131 | listener.onError(e);
132 | } finally {
133 | processes.remove(project);
134 | listener.progress(false);
135 | status.put(project, stopped);
136 | }
137 | });
138 | if (isRunning(project)) {
139 | futures.put(project, future);
140 | }
141 | }
142 |
143 | /**
144 | * 停止检测
145 | *
146 | * @param project 当前项目
147 | */
148 | public static void stop(Project project) {
149 | futures.computeIfPresent(project, (p, f) -> {
150 | status.put(project, stopping);
151 | processes.get(project).destroyForcibly();
152 | f.cancel(true);
153 | return null;
154 | });
155 | }
156 |
157 | /**
158 | * 清除结果
159 | *
160 | * @param project
161 | * @param listener
162 | */
163 | public static void clean(Project project, CheckListener listener) {
164 | FileUtil.delete(new File(EngineAssistant.getCheckResultJsonPath(project)));
165 | listener.clean();
166 | }
167 |
168 | /**
169 | * 当前项目是否有任务在运行
170 | *
171 | * @param project 当前项目
172 | * @return true:正在运行
173 | */
174 | public static boolean isRunning(Project project) {
175 | return running.equals(status.get(project));
176 | }
177 |
178 | public static boolean isStopped(Project project) {
179 | return stopped.equals(status.getOrDefault(project, stopped));
180 | }
181 |
182 | /**
183 | * 将数据源写入配置文件
184 | * 无配置文件下载配置文件 有配置文件更新配置文件
185 | * FileWriter很坑 创建对象默认清空文件(顺序不要发生改变)
186 | *
187 | * @param engineCliPath
188 | */
189 | private static void makeConfigJsonFile(String engineCliPath) throws IOException {
190 | try {
191 | String cliParentFilePath = new File(engineCliPath).getParent();
192 | File configJsonFile = new File(cliParentFilePath + File.separator + "config.json");
193 | if (!configJsonFile.exists()) {
194 | EngineDownloader.createCliConfig(engineCliPath);
195 | }
196 | String configJson = Origin.buildDsnJson(configJsonFile, openSCASettingState.getOpenSCASetting());
197 | FileWriter fileWriter = new FileWriter(configJsonFile);
198 | fileWriter.write(configJson);
199 | fileWriter.close();
200 | } catch (Exception e) {
201 | LOG.error("创建配置文件失败:" + e);
202 | throw new SCAException(ErrorEnum.CREATE_FILE_ERROR);
203 | }
204 | }
205 |
206 | }
207 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/service/HttpService.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.service;
2 |
3 | import cn.xmirror.sca.common.OpenSCASettingState;
4 | import cn.xmirror.sca.common.SCAThreadPool;
5 | import cn.xmirror.sca.common.dto.AuthResult;
6 | import cn.xmirror.sca.common.dto.ProjectSelectDTO;
7 | import cn.xmirror.sca.common.exception.ErrorEnum;
8 | import cn.xmirror.sca.common.exception.SCAException;
9 | import cn.xmirror.sca.common.pojo.OpenSCASetting;
10 | import cn.xmirror.sca.common.util.HttpUtils;
11 | import cn.xmirror.sca.engine.EngineAssistant;
12 | import com.alibaba.fastjson.JSON;
13 | import com.alibaba.fastjson.JSONArray;
14 | import com.alibaba.fastjson.JSONObject;
15 | import com.intellij.openapi.diagnostic.Logger;
16 | import com.intellij.openapi.util.io.FileUtil;
17 | import org.apache.commons.lang.StringUtils;
18 | import org.apache.http.HttpStatus;
19 | import org.jsoup.Connection;
20 |
21 | import java.io.File;
22 | import java.io.IOException;
23 | import java.util.HashMap;
24 | import java.util.List;
25 | import java.util.Map;
26 | import java.util.function.Consumer;
27 |
28 | /**
29 | * Http服务类
30 | *
31 | * @author Yuan Shengjun
32 | */
33 | public class HttpService {
34 |
35 | private static final Logger LOG = Logger.getInstance(HttpService.class);
36 |
37 |
38 | private static final String BaseUrlRequest = "https://opensca.xmirror.cn";
39 | public static final String testConnectionUri = "/oss-saas/api-v1/oss-token/test";
40 | private static final String authUri = "/oss-saas/api-v1/oss-token/get/auth";
41 | private static final String downloadEngineUri = "/oss-saas/api-v1/ide-plugin/open-sca-cli/download";
42 | private static final String getEngineVersionUri = "/oss-saas/api-v1/ide-plugin/open-sca-cli/version";
43 | private static final String getTeamProjectSelectList = "/oss-saas/api-v1/ide-plugin/project/select";
44 | private static final String syncDetectResult = "/oss-saas/api-v1/ide-plugin/sync/result";
45 | private static final Integer sleepTime = 1500;
46 |
47 | /**
48 | * 测试连接
49 | *
50 | * @param url 服务器地址
51 | * @param token 令牌
52 | * @param consumer 测试连接之后需要做的事
53 | */
54 | public static void testConnection(String url, String token, Consumer consumer) {
55 | SCAThreadPool.getInstance().submit(() -> {
56 | String message = null;
57 | try {
58 | getRequest(testConnectionUri, url, token, null, 5000);
59 | } catch (Exception e) {
60 | message = e.getMessage();
61 | }
62 | consumer.accept(message != null ? message : "连接成功");
63 | });
64 | }
65 |
66 | /**
67 | * 获取认证结果
68 | *
69 | * @param url
70 | * @param param
71 | * @return ""->一直未点击认证按钮 error->用户之前没有生成token
72 | * @throws IOException
73 | */
74 | public static String getAuthToken(String url, String param) throws IOException {
75 | Connection.Response response = HttpUtils.get(url + authUri + "/" + param);
76 | JSONObject result = JSON.parseObject(response.body());
77 | if (!result.get("code").equals(0)) {
78 | throw new SCAException(ErrorEnum.SERVER_REQUEST_FAILURE_ERROR);
79 | }
80 | AuthResult authResult = result.getObject("data", AuthResult.class);
81 | if (authResult.getCode() == HttpStatus.SC_NOT_FOUND) {
82 | return "";
83 | } else if (authResult.getCode() == HttpStatus.SC_UNAUTHORIZED) {
84 | return "error";
85 | } else if (authResult.getCode() == HttpStatus.SC_OK) {
86 | return authResult.getToken();
87 | } else {
88 | return "";
89 | }
90 | }
91 |
92 | /**
93 | * 下载引擎
94 | *
95 | * @param output 输出位置
96 | * @return
97 | */
98 | public static void downloadEngine(String output) {
99 | Map params = new HashMap<>();
100 | params.put("osName", EngineAssistant.getCurrentOsName());
101 | params.put("arch", EngineAssistant.getCurrentSystemArch());
102 | Connection.Response response = getRequest(downloadEngineUri, params, 300 * 1000);
103 | try {
104 | FileUtil.writeToFile(new File(output), response.bodyAsBytes());
105 | } catch (IOException e) {
106 | LOG.error(e);
107 | throw new SCAException(ErrorEnum.ENGINE_DOWNLOAD_ERROR, e.getMessage());
108 | }
109 | }
110 |
111 |
112 | /**
113 | * 获取项目下拉列表
114 | *
115 | * @param token
116 | * @return
117 | */
118 | public static List getTeamProjectSelectList(String token) {
119 | Connection.Response response = getRequest(getTeamProjectSelectList, token, null, 10 * 1000);
120 | JSONObject result = JSON.parseObject(response.body());
121 | if (!result.get("code").equals(0)) {
122 | LOG.error("获取项目下拉列表失败", result.toString());
123 | throw new SCAException(ErrorEnum.SERVER_REQUEST_FAILURE_ERROR);
124 | }
125 | JSONArray data = result.getJSONArray("data");
126 | return JSON.parseArray(data.toJSONString(), ProjectSelectDTO.class);
127 | }
128 |
129 |
130 | /**
131 | * 同步检测结果->saas
132 | * @param params
133 | * @param jsonFile
134 | * @param dsdxFile
135 | * @param timeout
136 | * @throws IOException
137 | */
138 | public static String syncDetectResult(Map params, File jsonFile,File dsdxFile, Integer timeout) throws IOException {
139 | Map fileMap = new HashMap<>(2);
140 | fileMap.put("dsdxFile", dsdxFile);
141 | fileMap.put("jsonFile",jsonFile);
142 | Connection.Response response = HttpUtils.post(BaseUrlRequest+syncDetectResult, null, params, fileMap);
143 | JSONObject result = JSON.parseObject(response.body());
144 | if (!result.get("code").equals(0)) {
145 | LOG.error("同步上传结果失败:"+ result.get("message"));
146 | throw new SCAException(ErrorEnum.SERVER_REQUEST_FAILURE_ERROR);
147 | }
148 | return result.get("data").toString();
149 | }
150 |
151 | /**
152 | * 获取最新版本的cli
153 | * @return
154 | */
155 | public static String getRemoteServerCliVersion() {
156 | Connection.Response response = getRequest(getEngineVersionUri, null, 10000);
157 | JSONObject result = JSON.parseObject(response.body());
158 | return (String) result.get("data");
159 | }
160 |
161 | /**
162 | * Get请求(去除Token校验)
163 | *
164 | * @param uri 请求地址
165 | * @param params 请求参数
166 | * @return 响应体
167 | */
168 | private static Connection.Response getRequest(String uri, Map params, Integer timeout) {
169 | OpenSCASetting openSCASetting = OpenSCASettingState.getInstance().getOpenSCASetting();
170 | String url = BaseUrlRequest;
171 | if (openSCASetting != null) {
172 | if (StringUtils.isEmpty(openSCASetting.getServerAddress())) {
173 | url = openSCASetting.getServerAddress();
174 | }
175 | }
176 | return getRequest(uri, url, null, params, timeout);
177 | }
178 |
179 | /**
180 | * Get请求(包含Token)
181 | *
182 | * @param uri 请求地址
183 | * @param params 请求参数
184 | * @return 响应体
185 | */
186 | private static Connection.Response getRequest(String uri, String token, Map params, Integer timeout) {
187 | OpenSCASetting openSCASetting = OpenSCASettingState.getInstance().getOpenSCASetting();
188 | String url = BaseUrlRequest;
189 | if (StringUtils.isEmpty(openSCASetting.getServerAddress())) {
190 | url = openSCASetting.getServerAddress();
191 | }
192 | return getRequest(uri, url, token, params, timeout);
193 | }
194 |
195 |
196 | /**
197 | * Get请求
198 | *
199 | * @param uri 请求地址
200 | * @param url 服务器地址
201 | * @param token 令牌
202 | * @param params 其他参数
203 | * @return 响应体
204 | */
205 | public static Connection.Response getRequest(String uri, String url, String token, Map params, Integer timeout) {
206 | try {
207 | Connection.Response response = HttpUtils.get(url + uri + mergeParams(token, params), null, timeout);
208 | if (response.statusCode() != HttpStatus.SC_OK) {
209 | throw new SCAException(ErrorEnum.SERVER_REQUEST_FAILURE_ERROR, Integer.toString(response.statusCode()));
210 | }
211 | if (response.contentType() != null && response.contentType().contains("application/json")) {
212 | JSONObject result = JSON.parseObject(response.body());
213 | if ((int) result.get("code") != 0) {
214 | throw new SCAException(ErrorEnum.SERVER_REQUEST_FAILURE_ERROR, result.get("message").toString());
215 | }
216 | }
217 | return response;
218 | } catch (IOException e) {
219 | throw new SCAException(ErrorEnum.SERVER_UNREACHABLE_ERROR, e.getMessage());
220 | }
221 | }
222 |
223 | /**
224 | * 合并参数。将参数拼接成get请求的参数
225 | *
226 | * @param token 令牌
227 | * @param params 参数
228 | * @return
229 | */
230 | public static String mergeParams(String token, Map params) {
231 | StringBuilder sb = new StringBuilder();
232 |
233 | if (StringUtils.isNotEmpty(token)) {
234 | sb.append("?ossToken=").append(token);
235 | }
236 |
237 | if (params != null && !params.isEmpty()) {
238 | for (Map.Entry entry : params.entrySet()) {
239 | String key = entry.getKey();
240 | String value = entry.getValue();
241 | sb.append("&").append(key).append("=").append(value);
242 | }
243 | }
244 |
245 | if (StringUtils.isEmpty(token) && sb.length() > 0) {
246 | sb.replace(0, 1, "?");
247 | }
248 |
249 | return sb.toString();
250 | }
251 | }
252 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/NotificationUtils.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui;
2 |
3 | import com.intellij.notification.NotificationGroupManager;
4 | import com.intellij.notification.NotificationType;
5 | import com.intellij.openapi.actionSystem.AnAction;
6 | import com.intellij.openapi.project.Project;
7 | import com.intellij.openapi.project.ProjectManager;
8 |
9 | /**
10 | * @author xingluheng
11 | */
12 | public class NotificationUtils {
13 |
14 | /**
15 | * 右下角气泡方式提醒
16 | *
17 | * @param message
18 | * @param type
19 | */
20 | public static void balloonNotify(String message, NotificationType type) {
21 | if (message == null) {
22 | return;
23 | }
24 | NotificationGroupManager.getInstance()
25 | .getNotificationGroup("OpenSCA Notification Group")
26 | .createNotification("OpenSCA",message, type)
27 | .notify(ProjectManager.getInstance().getDefaultProject());
28 | }
29 |
30 | public static void balloonNotify(String message, NotificationType type, Project project) {
31 | if (message == null) {
32 | return;
33 | }
34 | NotificationGroupManager.getInstance()
35 | .getNotificationGroup("OpenSCA Notification Group")
36 | .createNotification("OpenSCA",message, type)
37 | .notify(project);
38 | }
39 |
40 | public static void balloonNotifyWithAction(String message, NotificationType type, AnAction anAction, Project project) {
41 | if (message == null) {
42 | return;
43 | }
44 | NotificationGroupManager.getInstance()
45 | .getNotificationGroup("OpenSCA Notification Group")
46 | .createNotification("OpenSCA",message, type)
47 | .addAction(anAction)
48 | .notify(project);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/ToolWindowAccess.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui;
2 |
3 | import cn.xmirror.sca.ui.window.OverviewPanel;
4 | import cn.xmirror.sca.ui.window.ToolWindowContentPanel;
5 | import cn.xmirror.sca.ui.window.ToolWindowMainPanel;
6 | import com.intellij.openapi.project.Project;
7 | import com.intellij.openapi.wm.ToolWindow;
8 | import com.intellij.openapi.wm.ToolWindowManager;
9 | import com.intellij.ui.content.Content;
10 |
11 | import java.util.function.Consumer;
12 | import java.util.function.Function;
13 |
14 | /**
15 | * @author xingluheng
16 | * @date 2023/07/17 17:48
17 | **/
18 | public class ToolWindowAccess {
19 |
20 | private ToolWindowAccess(){
21 | }
22 |
23 | public static R getOverViewPanel(final Project project, final Function action) {
24 | ToolWindowMainPanel mainWindow = cn.xmirror.sca.ui.ToolWindowManager.getMainWindow(project);
25 | if (mainWindow == null) return null;
26 | ToolWindowContentPanel contentPanel = mainWindow.getContentPanel();
27 | OverviewPanel overviewPanel = contentPanel.getOverviewPanel();
28 | return action.apply(overviewPanel);
29 | }
30 |
31 | public static void getOverViewPanel(final Project project, final Consumer action) {
32 | ToolWindowMainPanel mainWindow = cn.xmirror.sca.ui.ToolWindowManager.getMainWindow(project);
33 | ToolWindowContentPanel contentPanel = mainWindow.getContentPanel();
34 | OverviewPanel overviewPanel = contentPanel.getOverviewPanel();
35 | action.accept(overviewPanel);
36 | }
37 |
38 | public static ToolWindow toolWindow(final Project project) {
39 | return ToolWindowManager
40 | .getInstance(project)
41 | .getToolWindow("OpenSCA");
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/ToolWindowManager.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui;
2 |
3 | import cn.xmirror.sca.ui.window.ToolWindowMainPanel;
4 | import com.intellij.openapi.project.Project;
5 |
6 | import java.util.Map;
7 | import java.util.concurrent.ConcurrentHashMap;
8 |
9 | public class ToolWindowManager {
10 | private static final Map mainPanelMap = new ConcurrentHashMap<>();
11 |
12 | public static void addMainWindow(Project project, ToolWindowMainPanel mainPanel) {
13 | mainPanelMap.put(project, mainPanel);
14 | }
15 |
16 | public static ToolWindowMainPanel getMainWindow(Project project) {
17 | return mainPanelMap.get(project);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/action/CleanAction.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.action;
2 |
3 | import cn.xmirror.sca.service.CheckService;
4 | import cn.xmirror.sca.ui.ToolWindowManager;
5 | import com.intellij.icons.AllIcons;
6 | import com.intellij.openapi.actionSystem.AnAction;
7 | import com.intellij.openapi.actionSystem.AnActionEvent;
8 | import com.intellij.openapi.project.DumbAware;
9 | import org.jetbrains.annotations.NotNull;
10 |
11 | public class CleanAction extends AnAction implements DumbAware {
12 |
13 | public CleanAction() {
14 | super(AllIcons.Actions.GC);
15 | }
16 |
17 | @Override
18 | public void actionPerformed(@NotNull AnActionEvent e) {
19 | CheckService.clean(e.getProject(),ToolWindowManager.getMainWindow(e.getProject()));
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/action/CollapseAll.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.action;
2 |
3 | import cn.xmirror.sca.ui.ToolWindowAccess;
4 | import cn.xmirror.sca.ui.ToolWindowManager;
5 | import cn.xmirror.sca.ui.window.OverviewPanel;
6 | import cn.xmirror.sca.ui.window.ToolWindowContentPanel;
7 | import cn.xmirror.sca.ui.window.ToolWindowMainPanel;
8 | import com.intellij.openapi.actionSystem.AnAction;
9 | import com.intellij.openapi.actionSystem.AnActionEvent;
10 | import com.intellij.openapi.project.DumbAware;
11 | import org.jetbrains.annotations.NotNull;
12 |
13 | /**
14 | * @author xingluheng
15 | * @date 2023/07/17 16:35
16 | **/
17 | public class CollapseAll extends AnAction implements DumbAware {
18 | @Override
19 | public void actionPerformed(@NotNull AnActionEvent e) {
20 | ToolWindowAccess.getOverViewPanel(e.getProject(),OverviewPanel::collapseAll);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/action/Configurable.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.action;
2 |
3 | import cn.xmirror.sca.common.OpenSCASettingState;
4 | import cn.xmirror.sca.common.pojo.DsnConfig;
5 | import cn.xmirror.sca.common.pojo.OpenSCASetting;
6 | import cn.xmirror.sca.ui.window.ConfigurablePanel;
7 | import com.intellij.openapi.ui.Messages;
8 | import com.intellij.openapi.util.NlsContexts;
9 | import org.apache.commons.collections.CollectionUtils;
10 | import org.apache.commons.lang.StringUtils;
11 | import org.jetbrains.annotations.Nullable;
12 |
13 | import javax.swing.*;
14 | import java.util.List;
15 | import java.util.stream.Collectors;
16 |
17 | public class Configurable implements com.intellij.openapi.options.Configurable {
18 |
19 | private ConfigurablePanel configurablePanel;
20 | private final OpenSCASettingState openSCASettingState = OpenSCASettingState.getInstance();
21 |
22 | @Override
23 | public @NlsContexts.ConfigurableName String getDisplayName() {
24 | return "OpenSCA Setting";
25 | }
26 |
27 | @Override
28 | public @Nullable JComponent createComponent() {
29 | OpenSCASetting scaSetting = openSCASettingState.getOpenSCASetting();
30 | if (scaSetting!=null){
31 | List dsnConfigList = scaSetting.getDsnConfigList();
32 | configurablePanel = new ConfigurablePanel(scaSetting.getServerAddress(), scaSetting.getToken(),
33 | scaSetting.getUseCustomerCli(), scaSetting.getCustomerPath(),
34 | scaSetting.getRemoteDataSourceSelected(),scaSetting.getLocalDataSourceSelected(),dsnConfigList);
35 | }else {
36 | configurablePanel = new ConfigurablePanel();
37 | }
38 | return configurablePanel;
39 | }
40 |
41 | @Override
42 | public boolean isModified() {
43 | //指示是否修改了Swing表单。这个方法经常被调用,所以不会花很长时间。如果设置被修改,返回:true,否则返回false
44 | OpenSCASetting scaSetting = openSCASettingState.getOpenSCASetting();
45 | if (scaSetting == null) {
46 | return true;
47 | }
48 | return !configurablePanel.getUrl().equals(scaSetting.getServerAddress()) ||
49 | !configurablePanel.getToken().equals(scaSetting.getToken()) ||
50 | configurablePanel.getUseCustomerCli() != scaSetting.getUseCustomerCli() ||
51 | !configurablePanel.getCustomerCliPath().equals(scaSetting.getCustomerPath()) ||
52 | configurablePanel.getRemoteDataSourceCheckBox().isSelected() != scaSetting.getRemoteDataSourceSelected() ||
53 | configurablePanel.getDsnListPanel().getLocalDataSourceCheckBox().isSelected() != scaSetting.getLocalDataSourceSelected();
54 | }
55 |
56 | @Override
57 | public void apply() {
58 | String url = configurablePanel.getUrl();
59 | String token = configurablePanel.getToken();
60 | boolean useCustomerCli = configurablePanel.getUseCustomerCli();
61 | String customerCliPath = configurablePanel.getCustomerCliPath();
62 | boolean remoteDataSourceSelected = configurablePanel.getRemoteDataSourceCheckBox().isSelected();
63 | boolean localDataSourceSelected = configurablePanel.getDsnListPanel().getLocalDataSourceCheckBox().isSelected();
64 | if (paramCheckNotPass(configurablePanel)) return;
65 | List dsnConfigList = configurablePanel.getDsnListPanel().getDsnTableModel().getDsnConfigList();
66 | OpenSCASetting scaSetting = new OpenSCASetting(url, token, useCustomerCli, customerCliPath,remoteDataSourceSelected,localDataSourceSelected,dsnConfigList);
67 | openSCASettingState.setOpenSCASetting(scaSetting);
68 | }
69 |
70 | /**
71 | * 参数检查是否不通过
72 | * @return true不通过 false通过
73 | */
74 | private boolean paramCheckNotPass(ConfigurablePanel configurablePanel) {
75 | boolean useCustomerCli = configurablePanel.getUseCustomerCli();
76 | String customerCliPath = configurablePanel.getCustomerCliPath();
77 | boolean remoteDataSourceSelected = configurablePanel.getRemoteDataSourceCheckBox().isSelected();
78 | boolean localDataSourceSelected = configurablePanel.getDsnListPanel().getLocalDataSourceCheckBox().isSelected();
79 | List dsnConfigList = configurablePanel.getDsnListPanel().getDsnTableModel().getDsnConfigList();
80 |
81 | if (useCustomerCli && StringUtils.isEmpty(customerCliPath)){
82 | Messages.showErrorDialog("OpenSCA Cli地址不能为空","OpenSCA");
83 | return true;
84 | }
85 | if (!remoteDataSourceSelected && !localDataSourceSelected){
86 | Messages.showErrorDialog("请配置漏洞库数据源","OpenSCA");
87 | return true;
88 | }
89 | if (remoteDataSourceSelected && configurablePanel.getToken().isEmpty()) {
90 | Messages.showErrorDialog("Token不能为空", "OpenSCA");
91 | return true;
92 | }
93 | List collect = dsnConfigList.stream().filter(item -> item.getSelect().equals(Boolean.TRUE)).collect(Collectors.toList());
94 |
95 | if (localDataSourceSelected && CollectionUtils.isEmpty(collect)) {
96 | Messages.showErrorDialog("请配置本地数据源", "OpenSCA");
97 | return true;
98 | }
99 | return false;
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/action/DisplayCritical.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.action;
2 |
3 | import cn.xmirror.sca.ui.ToolWindowAccess;
4 | import cn.xmirror.sca.ui.window.OverviewPanel;
5 | import com.intellij.icons.AllIcons;
6 | import com.intellij.openapi.actionSystem.AnActionEvent;
7 | import com.intellij.openapi.project.DumbAwareToggleAction;
8 | import com.intellij.openapi.project.Project;
9 | import org.jetbrains.annotations.NotNull;
10 |
11 | import java.util.Objects;
12 |
13 | /**
14 | * @author xingluheng
15 | * @date 2023/07/17 17:42
16 | **/
17 | public class DisplayCritical extends DumbAwareToggleAction {
18 |
19 | @Override
20 | public boolean isSelected(@NotNull AnActionEvent event) {
21 | final Project project = getEventProject(event);
22 | if (project == null) {
23 | return false;
24 | }
25 | Boolean displayingErrors = ToolWindowAccess.getOverViewPanel(project, OverviewPanel::isDisplayingCritical);
26 | return Objects.requireNonNullElse(displayingErrors, false);
27 | }
28 |
29 | @Override
30 | public void setSelected(@NotNull AnActionEvent event, boolean selected) {
31 | final Project project = getEventProject(event);
32 | if (project == null) {
33 | return;
34 | }
35 | ToolWindowAccess.getOverViewPanel(project,panel ->{
36 | panel.setDisplayingCritical(selected);
37 | panel.filterDisplayedResults();
38 | });
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/action/DisplayHigh.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.action;
2 |
3 | import cn.xmirror.sca.ui.ToolWindowAccess;
4 | import cn.xmirror.sca.ui.window.OverviewPanel;
5 | import com.intellij.openapi.actionSystem.AnActionEvent;
6 | import com.intellij.openapi.project.DumbAwareToggleAction;
7 | import com.intellij.openapi.project.Project;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import java.util.Objects;
11 |
12 | /**
13 | * @author xingluheng
14 | * @date 2023/07/18 11:36
15 | **/
16 | public class DisplayHigh extends DumbAwareToggleAction {
17 |
18 | @Override
19 | public boolean isSelected(@NotNull AnActionEvent event) {
20 | final Project project = getEventProject(event);
21 | if (project == null) {
22 | return false;
23 | }
24 | Boolean displayingErrors = ToolWindowAccess.getOverViewPanel(project, OverviewPanel::isDisplayingHigh);
25 | return Objects.requireNonNullElse(displayingErrors, false);
26 | }
27 |
28 | @Override
29 | public void setSelected(@NotNull AnActionEvent event, boolean selected) {
30 | final Project project = getEventProject(event);
31 | if (project == null) {
32 | return;
33 | }
34 | ToolWindowAccess.getOverViewPanel(project,panel ->{
35 | panel.setDisplayingHigh(selected);
36 | panel.filterDisplayedResults();
37 | });
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/action/DisplayLow.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.action;
2 |
3 | import cn.xmirror.sca.ui.ToolWindowAccess;
4 | import cn.xmirror.sca.ui.window.OverviewPanel;
5 | import com.intellij.openapi.actionSystem.AnActionEvent;
6 | import com.intellij.openapi.project.DumbAwareToggleAction;
7 | import com.intellij.openapi.project.Project;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import java.util.Objects;
11 |
12 | /**
13 | * @author xingluheng
14 | * @date 2023/07/18 11:36
15 | **/
16 | public class DisplayLow extends DumbAwareToggleAction {
17 |
18 | @Override
19 | public boolean isSelected(@NotNull AnActionEvent event) {
20 | final Project project = getEventProject(event);
21 | if (project == null) {
22 | return false;
23 | }
24 | Boolean displayingErrors = ToolWindowAccess.getOverViewPanel(project, OverviewPanel::isDisplayingLow);
25 | return Objects.requireNonNullElse(displayingErrors, false);
26 | }
27 |
28 | @Override
29 | public void setSelected(@NotNull AnActionEvent event, boolean selected) {
30 | final Project project = getEventProject(event);
31 | if (project == null) {
32 | return;
33 | }
34 | ToolWindowAccess.getOverViewPanel(project,panel ->{
35 | panel.setDisplayingLow(selected);
36 | panel.filterDisplayedResults();
37 | });
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/action/DisplayMedium.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.action;
2 |
3 | import cn.xmirror.sca.ui.ToolWindowAccess;
4 | import cn.xmirror.sca.ui.window.OverviewPanel;
5 | import com.intellij.openapi.actionSystem.AnActionEvent;
6 | import com.intellij.openapi.project.DumbAwareToggleAction;
7 | import com.intellij.openapi.project.Project;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import java.util.Objects;
11 |
12 | /**
13 | * @author xingluheng
14 | * @date 2023/07/18 11:36
15 | **/
16 | public class DisplayMedium extends DumbAwareToggleAction {
17 |
18 | @Override
19 | public boolean isSelected(@NotNull AnActionEvent event) {
20 | final Project project = getEventProject(event);
21 | if (project == null) {
22 | return false;
23 | }
24 | Boolean displayingErrors = ToolWindowAccess.getOverViewPanel(project, OverviewPanel::isDisplayingMedium);
25 | return Objects.requireNonNullElse(displayingErrors, false);
26 | }
27 |
28 | @Override
29 | public void setSelected(@NotNull AnActionEvent event, boolean selected) {
30 | final Project project = getEventProject(event);
31 | if (project == null) {
32 | return;
33 | }
34 | ToolWindowAccess.getOverViewPanel(project,panel ->{
35 | panel.setDisplayingMedium(selected);
36 | panel.filterDisplayedResults();
37 | });
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/action/ExpandAll.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.action;
2 |
3 | import cn.xmirror.sca.ui.ToolWindowAccess;
4 | import cn.xmirror.sca.ui.ToolWindowManager;
5 | import cn.xmirror.sca.ui.window.OverviewPanel;
6 | import cn.xmirror.sca.ui.window.ToolWindowContentPanel;
7 | import cn.xmirror.sca.ui.window.ToolWindowMainPanel;
8 | import com.intellij.openapi.actionSystem.AnAction;
9 | import com.intellij.openapi.actionSystem.AnActionEvent;
10 | import com.intellij.openapi.project.DumbAware;
11 | import org.jetbrains.annotations.NotNull;
12 |
13 | /**
14 | * @author xingluheng
15 | * @date 2023/07/17 16:34
16 | **/
17 | public class ExpandAll extends AnAction implements DumbAware {
18 |
19 | @Override
20 | public void actionPerformed(@NotNull AnActionEvent e) {
21 | ToolWindowAccess.getOverViewPanel(e.getProject(),panel->{
22 | panel.expandAll(4);
23 | });
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/action/ExportAction.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.action;
2 |
3 | import cn.xmirror.sca.engine.EngineAssistant;
4 | import cn.xmirror.sca.ui.NotificationUtils;
5 | import cn.xmirror.sca.ui.window.OverviewPanel;
6 | import cn.xmirror.sca.ui.window.ToolWindowContentPanel;
7 | import cn.xmirror.sca.ui.window.ToolWindowMainPanel;
8 | import com.intellij.icons.AllIcons;
9 | import com.intellij.notification.NotificationType;
10 | import com.intellij.openapi.actionSystem.AnAction;
11 | import com.intellij.openapi.actionSystem.AnActionEvent;
12 | import com.intellij.openapi.project.DumbAware;
13 | import org.jetbrains.annotations.NotNull;
14 |
15 | import javax.swing.tree.DefaultMutableTreeNode;
16 | import java.awt.*;
17 | import java.io.File;
18 | import java.io.IOException;
19 |
20 | /**
21 | * 导出按钮
22 | *
23 | * @author xingluheng
24 | * @date 2023/07/18 20:38
25 | **/
26 | public class ExportAction extends AnAction implements DumbAware {
27 |
28 | public ExportAction() {
29 | super(AllIcons.ToolbarDecorator.Export);
30 | }
31 |
32 | @Override
33 | public void actionPerformed(@NotNull AnActionEvent e) {
34 | String checkResultPath = EngineAssistant.getCheckResultJsonPath(e.getProject());
35 | File file = new File(checkResultPath);
36 | if (file.exists()){
37 | try {
38 | Desktop.getDesktop().open(file);
39 | } catch (IOException ex) {
40 | NotificationUtils.balloonNotify(ex.getMessage(), NotificationType.ERROR);
41 | }
42 | }else {
43 | NotificationUtils.balloonNotify("检测结果文件未找到", NotificationType.ERROR);
44 | }
45 | }
46 |
47 | @Override
48 | public void update(@NotNull AnActionEvent e) {
49 | boolean enable = false;
50 | ToolWindowMainPanel mainWindow = cn.xmirror.sca.ui.ToolWindowManager.getMainWindow(e.getProject());
51 | if (mainWindow != null) {
52 | ToolWindowContentPanel contentPanel = mainWindow.getContentPanel();
53 | OverviewPanel overviewPanel = contentPanel.getOverviewPanel();
54 | DefaultMutableTreeNode rootNode = overviewPanel.getRootNode();
55 | if (rootNode.getChildCount() > 0) {
56 | enable = true;
57 | }
58 | }
59 | e.getPresentation().setEnabled(enable);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/action/RunAction.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.action;
2 |
3 | import cn.xmirror.sca.service.CheckService;
4 | import cn.xmirror.sca.ui.ToolWindowManager;
5 | import com.intellij.icons.AllIcons;
6 | import com.intellij.openapi.actionSystem.AnAction;
7 | import com.intellij.openapi.actionSystem.AnActionEvent;
8 | import com.intellij.openapi.project.DumbAware;
9 | import org.jetbrains.annotations.NotNull;
10 |
11 | public class RunAction extends AnAction implements DumbAware {
12 |
13 | public RunAction() {
14 | super(AllIcons.Actions.Execute);
15 | }
16 |
17 | @Override
18 | public void actionPerformed(@NotNull AnActionEvent e) {
19 | CheckService.run(e.getProject(), ToolWindowManager.getMainWindow(e.getProject()));
20 | }
21 |
22 | @Override
23 | public void update(@NotNull AnActionEvent e) {
24 | e.getPresentation().setEnabled(CheckService.isStopped(e.getProject()));
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/action/SettingAction.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.action;
2 |
3 | import com.intellij.icons.AllIcons;
4 | import com.intellij.openapi.actionSystem.AnAction;
5 | import com.intellij.openapi.actionSystem.AnActionEvent;
6 | import com.intellij.openapi.options.ShowSettingsUtil;
7 | import com.intellij.openapi.project.DumbAware;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | public class SettingAction extends AnAction implements DumbAware {
11 |
12 | public SettingAction() {
13 | super(AllIcons.General.Settings);
14 | }
15 |
16 | @Override
17 | public void actionPerformed(@NotNull AnActionEvent e) {
18 | ShowSettingsUtil.getInstance().showSettingsDialog(e.getProject(), Configurable.class);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/action/StopAction.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.action;
2 |
3 | import cn.xmirror.sca.service.CheckService;
4 | import com.intellij.icons.AllIcons;
5 | import com.intellij.openapi.actionSystem.AnAction;
6 | import com.intellij.openapi.actionSystem.AnActionEvent;
7 | import com.intellij.openapi.project.DumbAware;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | public class StopAction extends AnAction implements DumbAware {
11 | public StopAction() {
12 | super(AllIcons.Actions.Suspend);
13 | }
14 |
15 | @Override
16 | public void actionPerformed(@NotNull AnActionEvent e) {
17 | CheckService.stop(e.getProject());
18 | }
19 |
20 | @Override
21 | public void update(@NotNull AnActionEvent e) {
22 | e.getPresentation().setEnabled(CheckService.isRunning(e.getProject()));
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/dialog/AuthDialog.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.dialog;
2 |
3 | import cn.xmirror.sca.common.exception.ErrorEnum;
4 | import cn.xmirror.sca.common.exception.SCAException;
5 | import cn.xmirror.sca.service.HttpService;
6 | import com.intellij.icons.AllIcons;
7 | import com.intellij.openapi.ui.DialogWrapper;
8 | import com.intellij.openapi.ui.MessageType;
9 | import com.intellij.openapi.ui.popup.Balloon;
10 | import com.intellij.openapi.ui.popup.JBPopupFactory;
11 | import com.intellij.openapi.util.IconLoader;
12 | import com.intellij.ui.awt.RelativePoint;
13 | import com.intellij.ui.components.JBScrollPane;
14 | import com.intellij.ui.scale.JBUIScale;
15 | import org.apache.commons.lang.StringUtils;
16 | import org.jetbrains.annotations.NotNull;
17 | import org.jetbrains.annotations.Nullable;
18 |
19 | import javax.swing.*;
20 | import javax.swing.event.HyperlinkEvent;
21 | import java.awt.*;
22 | import java.awt.datatransfer.StringSelection;
23 | import java.awt.event.ActionEvent;
24 | import java.io.IOException;
25 |
26 | /**
27 | * 认证对话框
28 | * @author xingluheng
29 | * @date 2023/08/15 10:25
30 | **/
31 | public class AuthDialog extends DialogWrapper {
32 |
33 | private boolean authCancel = false;
34 | public CopyUrlAction copyUrlAction = new CopyUrlAction();
35 | public JEditorPane viewer = new JEditorPane();
36 |
37 | public AuthDialog() {
38 | super(true);
39 | init();
40 | copyUrlAction.setEnabled(false);
41 | setTitle("Authenticating OpenSCA Plugin");
42 | }
43 |
44 | public void updateHtmlText(String htmlText) {
45 | viewer.setText(htmlText);
46 | }
47 |
48 | @Override
49 | protected Action @NotNull [] createActions() {
50 | return new Action[] {getCancelAction(),getOKAction()};
51 | }
52 |
53 | @Override
54 | protected Action @NotNull [] createLeftSideActions() {
55 | return new Action[] {copyUrlAction};
56 | }
57 |
58 | @Override
59 | public void doCancelAction() {
60 | super.doCancelAction();
61 | authCancel = true;
62 | }
63 |
64 | @Override
65 | protected @Nullable JComponent createCenterPanel() {
66 | JPanel centerPanel = new JPanel(new BorderLayout(JBUIScale.scale(5), JBUIScale.scale(5)));
67 | viewer.setContentType("text/html");
68 | viewer.setEditable(false);
69 | String htmlContent = "Initializing authentication...";
70 | viewer.setText(htmlContent);
71 | viewer.addHyperlinkListener(e -> {
72 | if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
73 | if (Desktop.isDesktopSupported()) {
74 | try {
75 | Desktop.getDesktop().browse(e.getURL().toURI());
76 | } catch (Exception ex) {
77 | ex.printStackTrace();
78 | }
79 | }
80 | }
81 | });
82 | JBScrollPane scrollPane = new JBScrollPane(viewer, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
83 | centerPanel.add(scrollPane, BorderLayout.CENTER);
84 |
85 | JProgressBar progressBar = new JProgressBar();
86 | progressBar.setIndeterminate(true);
87 | centerPanel.add(progressBar, BorderLayout.SOUTH);
88 |
89 | centerPanel.setPreferredSize(new Dimension(500, 150));
90 | return centerPanel;
91 | }
92 |
93 | /**
94 | * 进行服务器授权认证
95 | * @param htmlLink 服务器根地址
96 | * @return 正确token|error
97 | */
98 | public String getAuth(String htmlLink,String param) {
99 | String authToken = "";
100 | while (!authCancel){
101 | try {
102 | Thread.sleep(3000);
103 | authToken = HttpService.getAuthToken(htmlLink, param);
104 | if (StringUtils.isNotEmpty(authToken)){
105 | authCancel = true;
106 | }
107 | } catch (InterruptedException e) {
108 | e.printStackTrace();
109 | } catch (IOException | SCAException e) {
110 | throw new SCAException(ErrorEnum.SERVER_REQUEST_FAILURE_ERROR);
111 | }
112 | }
113 | return authToken;
114 | }
115 |
116 |
117 | public class CopyUrlAction extends AbstractAction {
118 | private String url = "";
119 |
120 | public void setUrl(String url) {
121 | this.url = url;
122 | }
123 |
124 | public CopyUrlAction() {
125 | super("&Copy URL", IconLoader.getIcon("/actions/copy.png",CopyUrlAction.class));
126 | }
127 |
128 | @Override
129 | public void actionPerformed(ActionEvent e) {
130 | Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(url), null);
131 | Balloon balloon = JBPopupFactory.getInstance().createHtmlTextBalloonBuilder("URL copied",
132 | AllIcons.General.BalloonInformation,
133 | MessageType.INFO.getPopupBackground()
134 | , null).setHideOnClickOutside(true).setFadeoutTime(3000).createBalloon();
135 | showBalloonForComponent(balloon, getButton(this), getButton(this) != null,null);
136 |
137 | }
138 |
139 | public void showBalloonForComponent(
140 | Balloon balloon,
141 | Component component,
142 | boolean showAbove,
143 | Point point) {
144 | Point targetPoint = point != null ? point :
145 | new Point(component.getWidth() / 2, (showAbove) ? 0 : component.getHeight());
146 | RelativePoint relativePoint = new RelativePoint(component, targetPoint);
147 | Balloon.Position position = (showAbove) ? Balloon.Position.above : Balloon.Position.below;
148 | balloon.show(relativePoint, position);
149 | }
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/dialog/DataSourceDialog.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.dialog;
2 |
3 | import cn.xmirror.sca.common.dto.Origin;
4 | import cn.xmirror.sca.ui.window.MyGridConstraints;
5 | import com.intellij.ide.HelpTooltip;
6 | import com.intellij.openapi.project.Project;
7 | import com.intellij.openapi.ui.ComboBox;
8 | import com.intellij.openapi.ui.DialogWrapper;
9 | import com.intellij.ui.components.JBTextField;
10 | import com.intellij.uiDesigner.core.GridConstraints;
11 | import com.intellij.uiDesigner.core.GridLayoutManager;
12 | import org.jetbrains.annotations.Nullable;
13 |
14 | import javax.swing.*;
15 | import java.awt.*;
16 | import java.util.List;
17 |
18 | /**
19 | * 添加数据源
20 | *
21 | * @author xingluheng
22 | * @date 2023/08/14 11:49
23 | **/
24 | public class DataSourceDialog extends DialogWrapper {
25 | private ComboBox dataSourceComboBox;
26 | private JBTextField tableNameTextField;
27 | private JTextArea dsnTextField;
28 |
29 | public DataSourceDialog(@Nullable Project project, String title) {
30 | super(project, false);
31 | init();
32 | setTitle(title);
33 | }
34 |
35 | @Override
36 | protected @Nullable JComponent createCenterPanel() {
37 | JPanel panel = new JPanel(new GridLayoutManager(4, 2));
38 | panel.setPreferredSize(new Dimension(500, 120));
39 | List originTypeList = Origin.originTypeList;
40 | dataSourceComboBox = new ComboBox(originTypeList.toArray(new String[originTypeList.size()]));
41 | tableNameTextField = new JBTextField();
42 | dsnTextField = new JTextArea();
43 | dsnTextField.setBorder(tableNameTextField.getBorder());
44 | dsnTextField.setSize(tableNameTextField.getSize());
45 | panel.add(new JLabel("Type:"), new MyGridConstraints(0).setAnchor(GridConstraints.ANCHOR_WEST).build());
46 | panel.add(dataSourceComboBox, new MyGridConstraints(0).setColumn(1).setFill(GridConstraints.FILL_HORIZONTAL)
47 | .setHSizePolicy(GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK).build());
48 | panel.add(new JLabel("Data Source:"), new MyGridConstraints(1).setColumn(0).setAnchor(GridConstraints.ANCHOR_WEST).build());
49 | panel.add(dsnTextField, new MyGridConstraints(1).setColumn(1).setFill(GridConstraints.FILL_HORIZONTAL)
50 | .setHSizePolicy(GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK).build());
51 | JLabel tableLabel = new JLabel("Table Name:");
52 | panel.add(tableLabel, new MyGridConstraints(3).setAnchor(GridConstraints.ANCHOR_WEST).build());
53 | panel.add(tableNameTextField, new MyGridConstraints(3).setColumn(1).setFill(GridBagConstraints.BOTH).build());
54 |
55 | dataSourceComboBox.addItemListener(e -> {
56 | String selectedValue = (String) dataSourceComboBox.getSelectedItem();
57 | if ("JSON".equals(selectedValue)) {
58 | HelpTooltip dataSourceHelpTooltip = new HelpTooltip();
59 | dataSourceHelpTooltip.setDescription("数据源配置,如:/Users/openSCA/Desktop/origin.sql");
60 | dataSourceHelpTooltip.installOn(dsnTextField);
61 | tableLabel.setVisible(false);
62 | tableNameTextField.setVisible(false);
63 | } else {
64 | HelpTooltip dataSourceHelpTooltip = new HelpTooltip();
65 | dataSourceHelpTooltip.setDescription("数据源配置,如:root:123456@tcp(127.0.0.1:3306)/db");
66 | dataSourceHelpTooltip.installOn(dsnTextField);
67 | tableLabel.setVisible(true);
68 | tableNameTextField.setVisible(true);
69 | }
70 | });
71 | return panel;
72 | }
73 |
74 | @Override
75 | protected void doOKAction() {
76 | super.doOKAction();
77 | }
78 |
79 | public void setDataSourceType(String item) {
80 | dataSourceComboBox.setSelectedItem(item);
81 | }
82 |
83 | public ComboBox getDataSourceComboBox() {
84 | return dataSourceComboBox;
85 | }
86 |
87 | public JTextArea getDsnTextField() {
88 | return dsnTextField;
89 | }
90 |
91 | public JBTextField getTableNameTextField() {
92 | return tableNameTextField;
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/dialog/SyncResultDialog.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.dialog;
2 |
3 | import cn.xmirror.sca.common.OpenSCASettingState;
4 | import cn.xmirror.sca.common.dto.GeneralSelectDTO;
5 | import cn.xmirror.sca.common.dto.ProjectSelectDTO;
6 | import cn.xmirror.sca.common.pojo.OpenSCASetting;
7 | import cn.xmirror.sca.engine.EngineAssistant;
8 | import cn.xmirror.sca.service.HttpService;
9 | import cn.xmirror.sca.ui.NotificationUtils;
10 | import com.intellij.notification.Notification;
11 | import com.intellij.notification.NotificationAction;
12 | import com.intellij.notification.NotificationType;
13 | import com.intellij.openapi.actionSystem.AnActionEvent;
14 | import com.intellij.openapi.project.Project;
15 | import com.intellij.openapi.ui.DialogWrapper;
16 | import com.intellij.ui.components.JBScrollPane;
17 | import lombok.SneakyThrows;
18 | import org.jetbrains.annotations.NotNull;
19 | import org.jetbrains.annotations.Nullable;
20 |
21 | import javax.swing.*;
22 | import java.awt.*;
23 | import java.io.File;
24 | import java.net.URI;
25 | import java.util.HashMap;
26 | import java.util.List;
27 | import java.util.Map;
28 |
29 | /**
30 | * @author xingluheng
31 | * @date 2023/11/06 11:17
32 | **/
33 | public class SyncResultDialog extends DialogWrapper {
34 | private JLabel selectProjectLabel;
35 | private String selectProjectUid;
36 | private Map projectMap;
37 | private Project project;
38 | private String token;
39 |
40 | public SyncResultDialog(Project project) {
41 | super(project);
42 | this.project = project;
43 |
44 | OpenSCASetting scaSetting = OpenSCASettingState.getInstance().getOpenSCASetting();
45 | token = scaSetting.getToken();
46 | List teamProjectSelectList = HttpService.getTeamProjectSelectList(token);
47 | projectMap = getProjectSelectMap(teamProjectSelectList);
48 |
49 | init();
50 | setTitle("Upload Result to OpenSCA");
51 | }
52 |
53 | @Override
54 | protected @Nullable JComponent createCenterPanel() {
55 | // 创建对话框面板,将树组件添加到面板中
56 | JPanel panel = new JPanel(new BorderLayout());
57 | selectProjectLabel = new JLabel("同步检测结果至:");
58 | panel.add(selectProjectLabel, BorderLayout.NORTH);
59 | panel.add(createTreePanel(), BorderLayout.CENTER);
60 | return panel;
61 | }
62 |
63 |
64 | /**
65 | * 创建树形Panel
66 | *
67 | * @return
68 | */
69 | @NotNull
70 | private JBScrollPane createTreePanel() {
71 |
72 | ButtonGroup buttonGroup = new ButtonGroup();
73 | JPanel projectSelectPanel = new JPanel();
74 | projectSelectPanel.setLayout(new BoxLayout(projectSelectPanel, BoxLayout.Y_AXIS));
75 | JRadioButton quickDetectButton = new JRadioButton("快速检测");
76 | buttonGroup.add(quickDetectButton);
77 | projectSelectPanel.add(quickDetectButton);
78 |
79 |
80 | for (Map.Entry entry : projectMap.entrySet()) {
81 | JRadioButton radioButton = new JRadioButton(entry.getKey());
82 | radioButton.addActionListener(e -> {
83 | selectProjectUid = projectMap.get(radioButton.getText());
84 | setOKActionEnabled(true);
85 | });
86 | buttonGroup.add(radioButton);
87 | projectSelectPanel.add(radioButton);
88 | }
89 |
90 | JBScrollPane treeScrollPanel = new JBScrollPane(projectSelectPanel);
91 | treeScrollPanel.setPreferredSize(new Dimension(300, 300));
92 |
93 | return treeScrollPanel;
94 | }
95 |
96 | /**
97 | * 从团队项目选择列表中提取项目名称和对应的唯一标识符(UID),并将其映射到一个 Map 中。
98 | *
99 | * @param teamProjectSelectList 团队项目选择列表
100 | * @return 包含项目名称作为键和对应的 UID 作为值的映射
101 | */
102 | private Map getProjectSelectMap(List teamProjectSelectList) {
103 | Map projectMap = new HashMap<>();
104 | for (ProjectSelectDTO projectSelectDTO : teamProjectSelectList) {
105 | for (GeneralSelectDTO generalSelectDTO : projectSelectDTO.getProjectList()) {
106 | projectMap.put(projectSelectDTO.getTeamName() + "/" + generalSelectDTO.getName(), generalSelectDTO.getProjectUid());
107 | }
108 | }
109 | return projectMap;
110 | }
111 |
112 |
113 | @Override
114 | protected void doOKAction() {
115 | super.doOKAction();
116 | Map requestParamMap = new HashMap<>();
117 | requestParamMap.put("token", token);
118 | requestParamMap.put("projectUid", selectProjectUid == null ? "" : selectProjectUid);
119 | requestParamMap.put("version", "-");
120 | requestParamMap.put("detectOrigin", "4");
121 | File jsonFile = new File(EngineAssistant.getCheckResultJsonPath(project));
122 | File dsdxFile = new File(EngineAssistant.getCheckResultDsdxPath(project));
123 | try {
124 | String recordUrl = HttpService.syncDetectResult(requestParamMap, jsonFile, dsdxFile, 10 * 1000);
125 | String url = "https://opensca.xmirror.cn/" + recordUrl;
126 | String notificationText = "检测结果上传SaaS成功!";
127 | NotificationUtils.balloonNotifyWithAction(notificationText,NotificationType.INFORMATION, new NotificationAction("See Details In OpenSCA SaaS") {
128 | @SneakyThrows
129 | @Override
130 | public void actionPerformed(@NotNull AnActionEvent anActionEvent, @NotNull Notification notification) {
131 | Desktop.getDesktop().browse(new URI(url));
132 | }
133 | },project);
134 | } catch (Exception e) {
135 | NotificationUtils.balloonNotify("检测记录已同步到SaaS端,请勿重新操作", NotificationType.ERROR,project);
136 | } finally {
137 | selectProjectUid = null;
138 | projectMap.clear();
139 | }
140 |
141 |
142 | }
143 |
144 | @Override
145 | public void setOKActionEnabled(boolean isEnabled) {
146 | super.setOKActionEnabled(isEnabled);
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/window/DescriptionComponentPanel.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.window;
2 |
3 | import cn.xmirror.sca.common.dto.Component;
4 | import cn.xmirror.sca.service.CheckService;
5 | import com.alibaba.fastjson.JSONObject;
6 | import com.intellij.openapi.fileEditor.FileEditorManager;
7 | import com.intellij.openapi.project.Project;
8 | import com.intellij.openapi.util.text.StringUtil;
9 | import com.intellij.openapi.vfs.LocalFileSystem;
10 | import com.intellij.openapi.vfs.VirtualFile;
11 | import com.intellij.ui.components.ActionLink;
12 | import com.intellij.ui.components.JBLabel;
13 | import com.intellij.util.ui.UIUtil;
14 | import icons.Icons;
15 |
16 | import javax.swing.*;
17 | import java.awt.*;
18 | import java.awt.event.ActionListener;
19 | import java.io.File;
20 | import java.util.List;
21 |
22 | public class DescriptionComponentPanel extends JPanel {
23 |
24 | private final Component component;
25 | private final Project project;
26 |
27 | public DescriptionComponentPanel(Component component, Project project) {
28 | super(new BorderLayout());
29 | JPanel panel = new JPanel(PaintUtils.defaultMarginGridLayoutManager(10, 1));
30 | this.component = component;
31 | this.project = project;
32 | panel.add(getTitlePanel(), PaintUtils.panelGridConstraints(0));
33 | panel.add(getOriginTextPanel(), PaintUtils.panelGridConstraints(1));
34 | JPanel licensePanel = getLicenseInfo();
35 | if (licensePanel != null) {
36 | panel.add(licensePanel, PaintUtils.panelGridConstraints(2));
37 | }
38 |
39 | JPanel compressPanel = new JPanel();
40 | compressPanel.add(panel);
41 | add(compressPanel, BorderLayout.WEST);
42 | }
43 |
44 | private JPanel getTitlePanel() {
45 | JPanel panel = PaintUtils.defaultGridPanel(4, 1);
46 | JLabel titleLabel = new JLabel(component.getName());
47 | titleLabel.setFont(PaintUtils.primaryTitleFont());
48 | titleLabel.setIcon(Icons.getIconFromResources(component.getSecurityLevelId(), Icons.IconSize.SIZE24));
49 | panel.add(titleLabel, new MyGridConstraints(0).build());
50 | panel.add(vendorAndVersion(), MyGridConstraints.gridBuilder(1).build());
51 | String language = StringUtil.isNotEmpty(component.getLanguage()) ? component.getLanguage() : "--";
52 | JLabel languageLabel = new JLabel("所属语言: "+language);
53 | panel.add(languageLabel, MyGridConstraints.gridBuilder(2).build());
54 | String dependType = component.isDirect() ? "直接依赖" : "间接依赖";
55 | JLabel dependTypeLabel = new JLabel("依赖类型: "+dependType);
56 | panel.add(dependTypeLabel, MyGridConstraints.gridBuilder(3).build());
57 | return panel;
58 | }
59 |
60 | private JPanel vendorAndVersion() {
61 | JPanel panel = PaintUtils.defaultGridPanel(1, 5);
62 | String vendorTag = "发布厂商:";
63 | String versionTag = "版本号:";
64 | String vendor = StringUtil.isNotEmpty(component.getVendor()) ? component.getVendor() : "--";
65 | String version = StringUtil.isNotEmpty(component.getVersion()) ? component.getVersion() : "--";
66 | int columnCount = PaintUtils.addRowOfItemsToPanel(panel, -1, vendorTag, false, null);
67 | columnCount = PaintUtils.addRowOfItemsToPanel(panel, columnCount, vendor, false, null);
68 | columnCount = PaintUtils.addRowOfItemsToPanel(panel, columnCount, versionTag, null);
69 | PaintUtils.addRowOfItemsToPanel(panel, columnCount, version, false, null);
70 | return panel;
71 | }
72 |
73 | private JPanel getOriginTextPanel() {
74 | JPanel panel = PaintUtils.defaultGridPanel(1, 1);
75 | JPanel originTextPanel = originTextPanel();
76 | if (originTextPanel != null) {
77 | panel.add(originTextPanel, MyGridConstraints.gridBuilder(0).build());
78 | }
79 | return panel;
80 | }
81 |
82 | private JPanel originTextPanel() {
83 | List paths = component.getPaths();
84 | if (paths.isEmpty()) {
85 | return null;
86 | }
87 | JPanel panel = PaintUtils.defaultGridPanel(paths.size() * 2, 2);
88 | JLabel filePosition = new JLabel("文件位置:");
89 | JLabel componentPosition = new JLabel("组件位置:");
90 |
91 | FileEditorManager editorManager = FileEditorManager.getInstance(project);
92 | LocalFileSystem fileSystem = LocalFileSystem.getInstance();
93 |
94 | for (int i = 0; i < paths.size(); i++) {
95 | panel.add(filePosition, MyGridConstraints.gridBuilder(i * 2).setIndent(0).build());
96 | panel.add(componentPosition, MyGridConstraints.gridBuilder(i * 2 + 1).setIndent(0).build());
97 |
98 | String path = paths.get(i);
99 | String p = path;
100 | if (path.contains("[")) {
101 | p = path.substring(0, path.indexOf("[")-1);
102 | }
103 | ActionLink actionLink = new ActionLink(p);
104 | String projectBasePath = CheckService.PROJECT_BASE_PATH;
105 | if (p.contains(File.separator)){
106 | String filePath = projectBasePath + p.substring(p.indexOf(File.separator));
107 | VirtualFile file = fileSystem.findFileByPath(filePath);
108 | actionLink.addActionListener(getActionLinkListener(editorManager, file));
109 | }
110 | panel.add(actionLink, MyGridConstraints.gridBuilder(i * 2).setIndent(0).setColumn(1).build());
111 | panel.add(new JBLabel(getDependencies(path), UIUtil.ComponentStyle.LARGE), MyGridConstraints.gridBuilder(i * 2 + 1).setIndent(0).setColumn(1).build());
112 | }
113 | return panel;
114 | }
115 |
116 | private ActionListener getActionLinkListener(FileEditorManager manager, VirtualFile file) {
117 | if (file == null) return null;
118 | return e -> manager.openFile(file, true, true);
119 | }
120 |
121 | private String getDependencies(String path) {
122 | if (path == null) {
123 | return "";
124 | }
125 | String tmpDependencies = path.substring(path.indexOf("/[") + 1);
126 | String[] dependencies = tmpDependencies.split("/");
127 | return String.join(" -> ", dependencies);
128 | }
129 |
130 | private JPanel getLicenseInfo() {
131 | List licenses = component.getLicenses();
132 | if (licenses == null) return null;
133 | JPanel panel = PaintUtils.defaultGridPanel(1, 1);
134 | JLabel titleLabel = new JLabel();
135 | titleLabel.setFont(PaintUtils.secondaryTitleFont());
136 | titleLabel.setIcon(Icons.LICENSE);
137 | titleLabel.setText("许可证:"+licenseInfo());
138 | panel.add(titleLabel, new MyGridConstraints(0).setColumn(0).build());
139 | return panel;
140 | }
141 |
142 | private String licenseInfo() {
143 | StringBuilder licenseNames = new StringBuilder();
144 | for (String license : component.getLicenses()) {
145 | JSONObject jsonObject = JSONObject.parseObject(license);
146 | licenseNames.append(jsonObject.getString("name")+" ");
147 | }
148 | return licenseNames.toString();
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/window/DescriptionOverviewPanel.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.window;
2 |
3 | import cn.xmirror.sca.common.constant.SecurityLevelEnum;
4 | import cn.xmirror.sca.common.dto.Overview;
5 | import com.intellij.openapi.project.Project;
6 | import com.intellij.ui.treeStructure.Tree;
7 | import com.intellij.uiDesigner.core.GridConstraints;
8 | import icons.Icons;
9 | import lombok.Data;
10 |
11 | import javax.swing.*;
12 | import java.awt.*;
13 | import java.text.SimpleDateFormat;
14 | import java.util.ArrayList;
15 | import java.util.Comparator;
16 | import java.util.List;
17 | import java.util.Locale;
18 |
19 | public class DescriptionOverviewPanel extends JPanel {
20 | private final Tree tree;
21 | private final DescriptionPanel descriptionPanel;
22 | private final Overview rootUserObject;
23 |
24 | public DescriptionOverviewPanel(Tree tree, DescriptionPanel descriptionPanel, OverviewPanel overviewPanel, Overview overview, Project project) {
25 | this.tree = tree;
26 | this.descriptionPanel = descriptionPanel;
27 | this.rootUserObject = overview;
28 | setLayout(PaintUtils.defaultMarginGridLayoutManager(10, 1));
29 | add(getTitlePanel(project, overviewPanel), PaintUtils.panelGridConstraints(0));
30 | add(getStatisticsPanel(), PaintUtils.panelGridConstraints(1));
31 | add(PaintUtils.moreInfoPanel(), PaintUtils.panelGridConstraints(9));
32 | }
33 |
34 |
35 | private JPanel getTitlePanel(Project project, OverviewPanel overviewPanel) {
36 | JPanel panel = PaintUtils.defaultGridPanel(1, 2);
37 | JButton toolButton = new JButton("工具引导");
38 | toolButton.addActionListener(e -> {
39 | tree.getSelectionModel().clearSelection();
40 | descriptionPanel.setDetailDescriptionPanel(new GuidePanel(project, overviewPanel));
41 | });
42 | panel.add(checkInfoPanel(), MyGridConstraints.gridBuilder(0).build());
43 | panel.add(toolButton, MyGridConstraints.gridBuilder(0).setColumn(1).setAnchor(GridConstraints.ANCHOR_EAST).build());
44 | return panel;
45 | }
46 |
47 | private JPanel checkInfoPanel() {
48 | JPanel panel = PaintUtils.defaultGridPanel(1, 8);
49 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.SIMPLIFIED_CHINESE);
50 | String start = rootUserObject.getStartTime() != null ? format.format(rootUserObject.getStartTime()) : "--";
51 | String end = rootUserObject.getEndTime() != null ? format.format(rootUserObject.getEndTime()) : "--";
52 | int columnCount = PaintUtils.addRowOfItemsToPanel(panel, -1, "开始时间:", false, null);
53 | columnCount = PaintUtils.addRowOfItemsToPanel(panel, columnCount, start, false, null);
54 | columnCount = PaintUtils.addRowOfItemsToPanel(panel, columnCount, "结束时间:", null);
55 | columnCount = PaintUtils.addRowOfItemsToPanel(panel, columnCount, end, false, null);
56 | return panel;
57 | }
58 |
59 | public JPanel getStatisticsPanel() {
60 | JPanel panel = PaintUtils.defaultGridPanel(6, 6);
61 | addTitle("组件统计", Icons.COMPONENT_STATISTICS, panel, 0);
62 | addTitle("漏洞统计", Icons.VUL_STATISTICS, panel, 2);
63 | addStatistics(getComponentStatisticsList(), panel, 1);
64 | addStatistics(getVulnerabilityStatisticsList(), panel, 3);
65 | return panel;
66 | }
67 |
68 | private void addTitle(String title, Icon icon, JPanel panel, int row) {
69 | panel.add(getTitle(title, icon), MyGridConstraints.gridBuilder(row).setIndent(0).setColSpan(6).setAnchor(GridConstraints.ANCHOR_WEST).build());
70 | }
71 |
72 | private JPanel getTitle(String title, Icon icon) {
73 | JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
74 | JLabel label = new JLabel(title);
75 | label.setFont(PaintUtils.secondaryTitleFont());
76 | label.setIcon(icon);
77 | panel.add(label);
78 | return panel;
79 | }
80 |
81 | private void addStatistics(List statisticsList, JPanel panel, int row) {
82 | if (statisticsList != null) {
83 | String separator = ": ";
84 | int currentColumn = -1;
85 | statisticsList.sort(Comparator.comparingInt(Statistics::getSort));
86 | for (Statistics statistics : statisticsList) {
87 | JLabel label = new JLabel(statistics.getSecurityAlias() + separator + statistics.getSecurityNum());
88 | label.setIcon(Icons.getIconFromResources(statistics.getSecurity()));
89 | panel.add(label, MyGridConstraints.gridBuilder(row).setColumn(++currentColumn).setAnchor(GridConstraints.ANCHOR_WEST).build());
90 | }
91 | }
92 | }
93 |
94 | private List getComponentStatisticsList() {
95 | List statisticsList = new ArrayList<>();
96 | int sort = -1;
97 | statisticsList.add(generateStatistics(++sort, SecurityLevelEnum.CRITICAL, rootUserObject.getCss()[0]));
98 | statisticsList.add(generateStatistics(++sort, SecurityLevelEnum.HIGH, rootUserObject.getCss()[1]));
99 | statisticsList.add(generateStatistics(++sort, SecurityLevelEnum.MEDIUM, rootUserObject.getCss()[2]));
100 | statisticsList.add(generateStatistics(++sort, SecurityLevelEnum.LOW, rootUserObject.getCss()[3]));
101 | return statisticsList;
102 | }
103 |
104 | private List getVulnerabilityStatisticsList() {
105 | List statisticsList = new ArrayList<>();
106 | int sort = -1;
107 | statisticsList.add(generateStatistics(++sort, SecurityLevelEnum.CRITICAL, rootUserObject.getVss()[0]));
108 | statisticsList.add(generateStatistics(++sort, SecurityLevelEnum.HIGH, rootUserObject.getVss()[1]));
109 | statisticsList.add(generateStatistics(++sort, SecurityLevelEnum.MEDIUM, rootUserObject.getVss()[2]));
110 | statisticsList.add(generateStatistics(++sort, SecurityLevelEnum.LOW, rootUserObject.getVss()[3]));
111 | return statisticsList;
112 | }
113 |
114 | private Statistics generateStatistics(int sort, SecurityLevelEnum security, int securityNum) {
115 | Statistics statistics = new Statistics();
116 | statistics.setSort(sort);
117 | statistics.setSecurity(security.getLevel());
118 | statistics.setSecurityAlias(security.getTag());
119 | statistics.setSecurityNum(securityNum);
120 | return statistics;
121 | }
122 |
123 | @Data
124 | static class Statistics {
125 | private int sort;
126 | private int security;
127 | private String securityAlias;
128 | private int securityNum;
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/window/DescriptionPanel.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.window;
2 |
3 | import com.intellij.openapi.project.Project;
4 | import com.intellij.openapi.ui.SimpleToolWindowPanel;
5 |
6 | import javax.swing.*;
7 |
8 | public class DescriptionPanel extends SimpleToolWindowPanel {
9 |
10 | private Project project;
11 | private ToolWindowContentPanel contentPanel;
12 | private GuidePanel guidePanel;
13 |
14 | public DescriptionPanel(Project project, ToolWindowContentPanel contentPanel) {
15 | super(true,true);
16 | this.project = project;
17 | this.contentPanel = contentPanel;
18 | guidePanel = new GuidePanel(project, contentPanel.getOverviewPanel());
19 | setDetailDescriptionPanel(guidePanel);
20 | }
21 |
22 | public GuidePanel getGuidePanel() {
23 | return guidePanel;
24 | }
25 |
26 | public void setDetailDescriptionPanel(JPanel panel) {
27 | removeAll();
28 | JScrollPane scrollPane = PaintUtils.wrapWithScrollPanel(panel);
29 | setContent(scrollPane);
30 | revalidate();
31 | repaint();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/window/DescriptionVulnerabilityPanel.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.window;
2 |
3 | import cn.xmirror.sca.common.OpenSCASettingState;
4 | import cn.xmirror.sca.common.constant.ExploitLevelEnum;
5 | import cn.xmirror.sca.common.dto.Vulnerability;
6 | import com.intellij.openapi.util.text.StringUtil;
7 | import com.intellij.ui.components.JBTextArea;
8 | import com.intellij.uiDesigner.core.GridConstraints;
9 | import icons.Icons;
10 |
11 | import javax.swing.*;
12 | import java.text.SimpleDateFormat;
13 | import java.util.*;
14 |
15 | public class DescriptionVulnerabilityPanel extends JPanel {
16 |
17 | private Vulnerability vulnerability;
18 |
19 | public DescriptionVulnerabilityPanel(Vulnerability vulnerability) {
20 | super(PaintUtils.defaultMarginGridLayoutManager(10, 1));
21 | this.vulnerability = vulnerability;
22 | add(getTitlePanel(), PaintUtils.panelGridConstraints(0));
23 | add(getDescriptionPanel(), PaintUtils.panelGridConstraints(1));
24 | add(getSuggestionPanel(), PaintUtils.panelGridConstraints(2));
25 | add(PaintUtils.moreInfoPanel(), PaintUtils.panelGridConstraints(9));
26 | }
27 |
28 | private JPanel getTitlePanel() {
29 | JPanel panel = PaintUtils.defaultGridPanel(3, 1);
30 | JLabel titleLabel = new JLabel();
31 | titleLabel.setFont(PaintUtils.primaryTitleFont());
32 | titleLabel.setText(vulnerability.getName());
33 | titleLabel.setIcon(Icons.getIconFromResources(vulnerability.getSecurityLevelId(), Icons.IconSize.SIZE24));
34 |
35 | panel.add(titleLabel, MyGridConstraints.gridBuilder(0).build());
36 | panel.add(vulnerabilityNumberPanel(), MyGridConstraints.gridBuilder(1).build());
37 | panel.add(simpleDataPanel(), MyGridConstraints.gridBuilder(2).build());
38 | return panel;
39 | }
40 |
41 | private JPanel vulnerabilityNumberPanel() {
42 | String cweId = vulnerability.getCweId();
43 | String cveId = vulnerability.getCveId();
44 | String cnnvdId = vulnerability.getCnnvdId();
45 | String cnvdId = vulnerability.getCnvdId();
46 | String xmirrorId = vulnerability.getId();
47 | List cwes = processCwe(cweId);
48 | int columnCount = (cwes.size() > 0 ? cwes.size() * 2 - 1 : 0) + // cweId with `|`
49 | (cveId != null ? 2 : 0) + // cveId with `|`
50 | (cnnvdId != null ? 2 : 0) + // cnnvdId with `|`
51 | (cnvdId != null ? 2 : 0) +
52 | (xmirrorId != null ? 2 : 0);
53 | JPanel panel = PaintUtils.defaultGridPanel(1, columnCount);
54 |
55 | int lastColumn = PaintUtils.addRowOfItemsToPanel(panel, -1, cwes, false,
56 | item -> "https://cwe.mitre.org/data/definitions/" + item.substring(4) + ".html");
57 |
58 | lastColumn = PaintUtils.addRowOfItemsToPanel(panel, lastColumn, emptyOrSingleList(cveId),
59 | item -> "https://cve.mitre.org/cgi-bin/cvename.cgi?name=" + item);
60 |
61 | lastColumn = PaintUtils.addRowOfItemsToPanel(panel, lastColumn, emptyOrSingleList(cnnvdId),
62 | item -> "http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=" + item);
63 |
64 | lastColumn = PaintUtils.addRowOfItemsToPanel(panel, lastColumn, emptyOrSingleList(cnvdId),
65 | item -> "https://www.cnvd.org.cn/flaw/show/" + item);
66 |
67 | // xmirrorId定位到sca主页
68 | PaintUtils.addRowOfItemsToPanel(panel, lastColumn, emptyOrSingleList(xmirrorId),
69 | item -> OpenSCASettingState.getInstance().getOpenSCASetting().getServerAddress());
70 | return panel;
71 | }
72 |
73 | private List emptyOrSingleList(String item) {
74 | return StringUtil.isNotEmpty(item) ? Collections.singletonList(item) : Collections.emptyList();
75 | }
76 |
77 | private List processCwe(String cweStr) {
78 | if (StringUtil.isNotEmpty(cweStr)) {
79 | String[] cwes = cweStr.split(",");
80 | return Arrays.asList(cwes);
81 | }
82 | return new ArrayList<>();
83 | }
84 |
85 |
86 | private JPanel simpleDataPanel() {
87 | JPanel panel = PaintUtils.defaultGridPanel(1, 5);
88 | String releaseTag = "发布日期:";
89 | Date releaseDate = vulnerability.getReleaseDate();
90 | String exploitTag = "利用难度:";
91 | int exploitId = vulnerability.getExploitLevelId();
92 | int columnCount = PaintUtils.addRowOfItemsToPanel(panel, -1, releaseTag, false, null);
93 | columnCount = PaintUtils.addRowOfItemsToPanel(panel, columnCount, releaseDate != null ? new SimpleDateFormat("yyyy-MM-dd").format(releaseDate) : "-", false, null);
94 | columnCount = PaintUtils.addRowOfItemsToPanel(panel, columnCount, exploitTag, null);
95 | PaintUtils.addRowOfItemsToPanel(panel, columnCount, ExploitLevelEnum.getTag(exploitId), false, null);
96 | return panel;
97 | }
98 |
99 | private JPanel getDescriptionPanel() {
100 | JPanel panel = PaintUtils.defaultGridPanel(2, 1);
101 |
102 | JLabel descriptionHeader = new JLabel("漏洞描述");
103 | descriptionHeader.setFont(PaintUtils.secondaryTitleFont());
104 | descriptionHeader.setIcon(Icons.VUL_DESCRIPTION);
105 | String descriptionText = vulnerability.getDescription();
106 | if (!StringUtil.isNotEmpty(descriptionText)) {
107 | descriptionText = vulnerability.getDescriptionEn();
108 | }
109 | if (!StringUtil.isNotEmpty(descriptionText)) {
110 | descriptionText = "暂无漏洞描述";
111 | }
112 | descriptionText = descriptionText.trim();
113 | JBTextArea description = new JBTextArea(descriptionText);
114 | description.setEditable(false);
115 | description.setLineWrap(true);
116 | description.setOpaque(false);
117 | JScrollPane scrollPane = PaintUtils.wrapWithScrollPanel(description);
118 | scrollPane.setOpaque(false);
119 | scrollPane.getViewport().setOpaque(false);
120 |
121 | panel.add(descriptionHeader, MyGridConstraints.gridBuilder(0).build());
122 | panel.add(scrollPane, MyGridConstraints.gridBuilder(1).setFill(GridConstraints.FILL_BOTH).build());
123 | return panel;
124 | }
125 |
126 | private JPanel getSuggestionPanel() {
127 | JPanel panel = PaintUtils.defaultGridPanel(2, 1);
128 | JLabel suggestionHeader = new JLabel("修复建议");
129 | suggestionHeader.setFont(PaintUtils.secondaryTitleFont());
130 | suggestionHeader.setIcon(Icons.FIX_SUGGESTION);
131 |
132 | String suggestionText = vulnerability.getSuggestion();
133 | if (StringUtil.isNotEmpty(suggestionText)) {
134 | suggestionText = suggestionText.trim();
135 | } else {
136 | suggestionText = "暂无修复建议";
137 | }
138 |
139 | JTextArea suggestion = new JTextArea(suggestionText);
140 | suggestion.setLineWrap(true);
141 | suggestion.setEditable(false);
142 | suggestion.setOpaque(false);
143 | suggestion.setBorder(null);
144 | JScrollPane scrollPane = PaintUtils.wrapWithScrollPanel(suggestion);
145 | scrollPane.setOpaque(false);
146 | scrollPane.getViewport().setOpaque(false);
147 |
148 | panel.add(suggestionHeader, MyGridConstraints.gridBuilder(0).build());
149 | panel.add(scrollPane, MyGridConstraints.gridBuilder(1).setFill(GridConstraints.FILL_BOTH).build());
150 | return panel;
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/window/DsnTableModel.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.window;
2 |
3 | import cn.xmirror.sca.common.pojo.DsnConfig;
4 | import lombok.Getter;
5 | import org.apache.commons.collections.CollectionUtils;
6 |
7 | import javax.swing.*;
8 | import javax.swing.table.AbstractTableModel;
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | /**
13 | * @author xingluheng
14 | * @date 2023/08/14 10:14
15 | **/
16 | public class DsnTableModel extends AbstractTableModel {
17 |
18 | @Getter
19 | private List dsnConfigList = new ArrayList<>();
20 | private final String[] columnNames = {" ", "Type", "Table","Data Source"};
21 | private JCheckBox localDataSourceCheckBox;
22 |
23 |
24 | public DsnTableModel() {
25 | dsnConfigList.add(new DsnConfig(false, "MySQL", "test","jdbc:mysql://localhost:3306/mydatabase?user=username&password=password&useSSL=false"));
26 | dsnConfigList.add(new DsnConfig(true, "Redis", "test","jdbc:mysql://localhost:3306/mydatabase?user=username&password=password&useSSL=false"));
27 | }
28 |
29 | public DsnTableModel(List dsnConfigList,JCheckBox localDataSourceCheckBox) {
30 | this.localDataSourceCheckBox = localDataSourceCheckBox;
31 | if (!CollectionUtils.isEmpty(dsnConfigList)) {
32 | this.dsnConfigList = dsnConfigList;
33 | }
34 | }
35 |
36 | @Override
37 | public String getColumnName(int column) {
38 | return columnNames[column];
39 | }
40 |
41 | @Override
42 | public boolean isCellEditable(int rowIndex, int columnIndex) {
43 | return columnIndex == 0;
44 | }
45 |
46 | public void addRow(DsnConfig rowData) {
47 | dsnConfigList.add(rowData);
48 | fireTableRowsInserted(dsnConfigList.size() - 1, dsnConfigList.size() - 1);
49 | }
50 |
51 | public void removeRow(int rowIndex) {
52 | if (rowIndex >= 0 && rowIndex < dsnConfigList.size()) {
53 | dsnConfigList.remove(rowIndex);
54 | fireTableRowsDeleted(rowIndex, rowIndex);
55 | }
56 | }
57 |
58 | public DsnConfig getLocationAt(final int index) {
59 | return dsnConfigList.get(index);
60 | }
61 |
62 | @Override
63 | public int getRowCount() {
64 | return dsnConfigList.size();
65 | }
66 |
67 | @Override
68 | public int getColumnCount() {
69 | return columnNames.length;
70 | }
71 |
72 | @Override
73 | public Object getValueAt(int rowIndex, int columnIndex) {
74 | DsnConfig data = dsnConfigList.get(rowIndex);
75 | switch (columnIndex) {
76 | case 1:
77 | return data.getType();
78 | case 2:
79 | return data.getTableName();
80 | case 3:
81 | return data.getDsn();
82 | case 0:
83 | return data.getSelect();
84 | default:
85 | return null;
86 | }
87 | }
88 |
89 | @Override
90 | public void setValueAt(final Object aValue, final int rowIndex, final int columnIndex) {
91 | DsnConfig dsnConfig = dsnConfigList.get(rowIndex);
92 | if (columnIndex == 0 && localDataSourceCheckBox.isSelected()) {
93 | dsnConfig.setSelect(!dsnConfig.getSelect());
94 | fireTableCellUpdated(rowIndex, 0);
95 | }
96 | }
97 |
98 | public void removeLocationAt(int selectedIndex) {
99 | removeRow(selectedIndex);
100 | }
101 |
102 | @Override
103 | public Class> getColumnClass(int columnIndex) {
104 | if (columnIndex == 0) {
105 | return Boolean.class;
106 | } else {
107 | return String.class;
108 | }
109 | }
110 |
111 | public void updateLocation(DsnConfig dsnConfig, DsnConfig newDsnConfig) {
112 | if (dsnConfig != null && newDsnConfig != null) {
113 | final int index = dsnConfigList.indexOf(dsnConfig);
114 | if (index != -1) {
115 | dsnConfigList.remove(index);
116 | dsnConfigList.add(index, newDsnConfig);
117 | fireTableRowsUpdated(index, index);
118 | }
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/window/GuidePanel.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.window;
2 |
3 | import cn.xmirror.sca.ui.NotificationUtils;
4 | import com.intellij.icons.AllIcons;
5 | import com.intellij.notification.NotificationType;
6 | import com.intellij.openapi.project.Project;
7 | import com.intellij.ui.JBColor;
8 | import com.intellij.ui.treeStructure.Tree;
9 | import com.intellij.uiDesigner.core.GridConstraints;
10 | import com.intellij.uiDesigner.core.Spacer;
11 | import icons.Icons;
12 |
13 | import javax.swing.*;
14 | import javax.swing.tree.DefaultMutableTreeNode;
15 | import javax.swing.tree.DefaultTreeModel;
16 | import javax.swing.tree.TreePath;
17 | import java.awt.*;
18 |
19 | public class GuidePanel extends JPanel {
20 | private Project project;
21 | private OverviewPanel overviewPanel;
22 |
23 | public GuidePanel(Project project, OverviewPanel overviewPanel) {
24 | super(PaintUtils.defaultMarginGridLayoutManager(10,1));
25 | this.project = project;
26 | this.overviewPanel = overviewPanel;
27 | this.add(getTitlePanel(), PaintUtils.panelGridConstraints(0));
28 | this.add(getSettingPanel(), PaintUtils.panelGridConstraints(1));
29 | this.add(getRunPanel(), PaintUtils.panelGridConstraints(2));
30 | this.add(getStopPanel(), PaintUtils.panelGridConstraints(3));
31 | this.add(getCleanPanel(), PaintUtils.panelGridConstraints(4));
32 | this.add(new Spacer(), PaintUtils.spacerGridConstraints(9));
33 | }
34 |
35 | private JPanel getTitlePanel() {
36 | JPanel panel = PaintUtils.defaultGridPanel(2,1);
37 |
38 | Icon titleIcon = Icons.XMIRROR_LOGO_24;
39 | String titleText = "OpenSCA Xcheck组件漏洞检测工具";
40 | Font titleFont = PaintUtils.primaryTitleFont();
41 | JLabel titleLabel = new JLabel();
42 | titleLabel.setFont(titleFont);
43 | titleLabel.setText(titleText);
44 | titleLabel.setIcon(titleIcon);
45 | titleLabel.setForeground(JBColor.BLUE);
46 |
47 | panel.add(titleLabel, MyGridConstraints.gridBuilder(0).build());
48 | panel.add(titleDescription(),
49 | MyGridConstraints
50 | .gridBuilder(1)
51 | .setFill(GridConstraints.FILL_HORIZONTAL)
52 | .setHSizePolicy(GridConstraints.SIZEPOLICY_CAN_GROW)
53 | .build());
54 | return panel;
55 | }
56 |
57 | private JPanel titleDescription() {
58 | JPanel panel = PaintUtils.defaultGridPanel(1, 2);
59 | String descriptionText = "本工具实时检测项目的组件及漏洞信息,开始使用前请先配置检测平台地址。";
60 | JLabel descriptionLabel = new JLabel();
61 | descriptionLabel.setText(descriptionText);
62 | JButton checkDescriptionButton = new JButton("检测概览");
63 | checkDescriptionButton.addActionListener(e -> triggerRootNode());
64 | panel.add(descriptionLabel, MyGridConstraints.gridBuilder(0).build());
65 | panel.add(checkDescriptionButton,
66 | MyGridConstraints
67 | .gridBuilder(0)
68 | .setColumn(1)
69 | .setAnchor(GridConstraints.ANCHOR_EAST)
70 | .setHSizePolicy(GridConstraints.SIZEPOLICY_CAN_GROW)
71 | .build());
72 | return panel;
73 | }
74 |
75 | /**
76 | * 触发树中的根节点
77 | */
78 | public void triggerRootNode() {
79 | DefaultMutableTreeNode rootNode = overviewPanel.getRootNode();
80 | Tree tree = overviewPanel.getTree();
81 | tree.getSelectionModel().clearSelection();
82 | if (rootNode.getChildCount() != 0) {
83 | TreePath treePath = new TreePath(((DefaultTreeModel) tree.getModel()).getPathToRoot(rootNode.getFirstChild()));
84 | tree.setSelectionPath(treePath);
85 | }else {
86 | NotificationUtils.balloonNotify("请检测完成后,查看检测结果", NotificationType.WARNING);
87 | }
88 | }
89 |
90 | private JPanel getSettingPanel() {
91 | Icon titleIcon = AllIcons.General.Settings;
92 | String titleText = "连接配置";
93 | String descriptionText = "点击工具栏的“Setting”按钮,点击“Quick Authentication”按钮,跳转平台授权后,自动填充Url,Token!";
94 | return getDefaultPanel(titleIcon, titleText, descriptionText);
95 | }
96 |
97 | private JPanel getRunPanel() {
98 | Icon titleIcon = AllIcons.Actions.Execute;
99 | String titleText = "开始检测";
100 | String descriptionText = "点击工具栏的“Run”,开始检测当前项目内的组件漏洞风险情况。";
101 | return getDefaultPanel(titleIcon, titleText, descriptionText);
102 | }
103 |
104 | private JPanel getStopPanel() {
105 | Icon titleIcon = AllIcons.Actions.Suspend;
106 | String titleText = "停止检测";
107 | String descriptionText = "点击工具栏的“Stop”,停止检测当前项目内的组件漏洞风险情况。";
108 | return getDefaultPanel(titleIcon, titleText, descriptionText);
109 | }
110 |
111 | private JPanel getCleanPanel() {
112 | Icon titleIcon = AllIcons.Actions.GC;
113 | String titleText = "清除检测结果";
114 | String descriptionText = "点击工具栏的“Clean”,清除当前项目的检测结果。";
115 | return getDefaultPanel(titleIcon, titleText, descriptionText);
116 | }
117 |
118 | private JPanel getDefaultPanel(Icon titleIcon, String titleText, String descriptionText) {
119 | Font defaultTitleFont = PaintUtils.secondaryTitleFont();
120 | return getDefaultPanel(titleIcon, titleText, defaultTitleFont, descriptionText);
121 | }
122 |
123 | private JPanel getDefaultPanel(Icon titleIcon, String titleText, Font titleFont, String descriptionText) {
124 | JPanel panel = PaintUtils.defaultGridPanel(2,1);
125 |
126 | JLabel titleLabel = new JLabel();
127 | titleLabel.setFont(titleFont);
128 | titleLabel.setText(titleText);
129 | titleLabel.setIcon(titleIcon);
130 |
131 | JLabel descriptionLabel = new JLabel();
132 | descriptionLabel.setText(" " + descriptionText);
133 |
134 | panel.add(titleLabel, MyGridConstraints.gridBuilder(0).build());
135 | panel.add(descriptionLabel, MyGridConstraints.gridBuilder(1).build());
136 | return panel;
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/window/MyGridConstraints.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.window;
2 |
3 | import com.intellij.uiDesigner.core.GridConstraints;
4 |
5 | public class MyGridConstraints {
6 | private final GridConstraints gridConstraints;
7 |
8 | public MyGridConstraints(int row) {
9 | gridConstraints = new GridConstraints(
10 | row,
11 | 0,
12 | 1,
13 | 1,
14 | GridConstraints.ANCHOR_WEST,
15 | GridConstraints.FILL_NONE,
16 | GridConstraints.SIZEPOLICY_FIXED,
17 | GridConstraints.SIZEPOLICY_FIXED,
18 | null,
19 | null,
20 | null,
21 | 1,
22 | false);
23 | }
24 |
25 | public static MyGridConstraints gridBuilder(int row) {
26 | return new MyGridConstraints(row);
27 | }
28 |
29 | public GridConstraints build() {
30 | return gridConstraints;
31 | }
32 |
33 | public MyGridConstraints setRow(int row) {
34 | gridConstraints.setRow(row);
35 | return this;
36 | }
37 |
38 | public MyGridConstraints setColumn(int column) {
39 | gridConstraints.setColumn(column);
40 | return this;
41 | }
42 |
43 | public MyGridConstraints setRowSpan(int rowSpan) {
44 | gridConstraints.setRowSpan(rowSpan);
45 | return this;
46 | }
47 |
48 | public MyGridConstraints setColSpan(int colSpan) {
49 | gridConstraints.setColSpan(colSpan);
50 | return this;
51 | }
52 |
53 | public MyGridConstraints setAnchor(int anchor) {
54 | gridConstraints.setAnchor(anchor);
55 | return this;
56 | }
57 |
58 | public MyGridConstraints setFill(int fill) {
59 | gridConstraints.setFill(fill);
60 | return this;
61 | }
62 |
63 | /**
64 | * 内部组件水平方向间距
65 | * @param HSizePolicy 间距
66 | */
67 | public MyGridConstraints setHSizePolicy(int HSizePolicy) {
68 | gridConstraints.setHSizePolicy(HSizePolicy);
69 | return this;
70 | }
71 |
72 | /**
73 | * 内部组件垂直方向间距
74 | * @param VSizePolicy 间距
75 | */
76 | public MyGridConstraints setVSizePolicy(int VSizePolicy) {
77 | gridConstraints.setVSizePolicy(VSizePolicy);
78 | return this;
79 | }
80 |
81 | /**
82 | * 首行缩进
83 | * @param indent 0:不缩进 1:缩进
84 | */
85 | public MyGridConstraints setIndent(int indent) {
86 | gridConstraints.setIndent(indent);
87 | return this;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/window/PaintUtils.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.window;
2 |
3 | import cn.xmirror.sca.common.OpenSCASettingState;
4 | import cn.xmirror.sca.ui.NotificationUtils;
5 | import com.intellij.icons.AllIcons;
6 | import com.intellij.ide.BrowserUtil;
7 | import com.intellij.notification.NotificationType;
8 | import com.intellij.openapi.util.text.StringUtil;
9 | import com.intellij.ui.ScrollPaneFactory;
10 | import com.intellij.ui.components.ActionLink;
11 | import com.intellij.uiDesigner.core.GridConstraints;
12 | import com.intellij.uiDesigner.core.GridLayoutManager;
13 | import com.intellij.util.ui.JBUI;
14 |
15 | import javax.swing.*;
16 | import java.awt.*;
17 | import java.awt.event.MouseAdapter;
18 | import java.awt.event.MouseEvent;
19 | import java.awt.event.MouseListener;
20 | import java.util.Collections;
21 | import java.util.List;
22 | import java.util.function.UnaryOperator;
23 |
24 | public class PaintUtils {
25 | private PaintUtils() {
26 | }
27 |
28 | // ---------------------------------- JPanel ------------------------------------
29 | public static int PANEL_V_GAP = 5;
30 | public static int PANEL_H_GAP = 5;
31 |
32 | public static JPanel defaultGridPanel(int rowCount, int columnCount) {
33 | return defaultGridPanel(rowCount, columnCount, PANEL_H_GAP, PANEL_V_GAP);
34 | }
35 |
36 | public static JPanel defaultGridPanel(int rowCount, int columnCount, int hGap, int vGap) {
37 | return new JPanel(new GridLayoutManager(rowCount, columnCount, JBUI.emptyInsets(), hGap, vGap));
38 | }
39 |
40 |
41 | // ------------------------------- LayoutManager --------------------------------
42 |
43 | /**
44 | * 布局管理器
45 | *
46 | * @param rowCount 行数
47 | * @param columnCount 列数
48 | * @return 布局管理器
49 | */
50 | public static GridLayoutManager defaultMarginGridLayoutManager(int rowCount, int columnCount) {
51 | return new GridLayoutManager(rowCount, columnCount, JBUI.insets(10, 10, 10, 10), PANEL_H_GAP, PANEL_V_GAP);
52 | }
53 |
54 | // ------------------------------ GridConstraints --------------------------------
55 | public static GridConstraints spacerGridConstraints(int row) {
56 | return new MyGridConstraints(row)
57 | .setAnchor(GridConstraints.ANCHOR_CENTER)
58 | .setFill(GridConstraints.FILL_VERTICAL)
59 | .setHSizePolicy(GridConstraints.SIZEPOLICY_CAN_SHRINK)
60 | .setVSizePolicy(GridConstraints.SIZEPOLICY_CAN_GROW)
61 | .setIndent(0).build();
62 | }
63 |
64 | public static GridConstraints panelGridConstraints(int row) {
65 | return new MyGridConstraints(row)
66 | .setAnchor(GridConstraints.ANCHOR_CENTER)
67 | .setFill(GridConstraints.FILL_BOTH)
68 | .setHSizePolicy(GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW)
69 | .setVSizePolicy(GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK)
70 | .setIndent(0).build();
71 | }
72 |
73 | // ------------------------------ Separator Label --------------------------------
74 | public static int addRowOfItemsToPanel(
75 | JPanel panel,
76 | int startingColumn,
77 | String item,
78 | UnaryOperator buildUrl) {
79 | return addRowOfItemsToPanel(panel, startingColumn, item, true, buildUrl);
80 | }
81 |
82 | public static int addRowOfItemsToPanel(
83 | JPanel panel,
84 | int startingColumn,
85 | String item,
86 | boolean firstSeparator,
87 | UnaryOperator buildUrl) {
88 | List items = StringUtil.isNotEmpty(item) ? Collections.singletonList(item) : Collections.emptyList();
89 | return addRowOfItemsToPanel(panel, startingColumn, items, firstSeparator, buildUrl);
90 | }
91 |
92 | public static int addRowOfItemsToPanel(
93 | JPanel panel,
94 | int startingColumn,
95 | List items,
96 | UnaryOperator buildUrl) {
97 | return addRowOfItemsToPanel(panel, startingColumn, items, true, buildUrl);
98 | }
99 |
100 | public static int addRowOfItemsToPanel(
101 | JPanel panel,
102 | int startingColumn,
103 | List items,
104 | boolean firstSeparator,
105 | UnaryOperator buildUrl) {
106 | return addRowOfItemsToPanel(panel, startingColumn, 0, items, " | ", firstSeparator, true, buildUrl);
107 | }
108 |
109 | public static int addRowOfItemsToPanel(
110 | JPanel panel,
111 | int startingColumn,
112 | int row,
113 | List items,
114 | String separator,
115 | boolean firstSeparator,
116 | boolean opaqueSeparator,
117 | UnaryOperator buildUrl) {
118 | int currentColumn = startingColumn;
119 | for (String item : items) {
120 | final String text = item.trim();
121 | if (StringUtil.isNotEmpty(item)) {
122 | ActionLink positionPanel;
123 | if (buildUrl != null) {
124 | String url = buildUrl.apply(text);
125 | positionPanel = new ActionLink(item);
126 | positionPanel.addActionListener(e->BrowserUtil.open(url));
127 | } else {
128 | positionPanel = new ActionLink(text);
129 | }
130 | if (currentColumn != startingColumn || (firstSeparator && currentColumn != -1)) {
131 | currentColumn++;
132 | JLabel label = new JLabel(separator);
133 | if (opaqueSeparator) makeOpaque(label, 50);
134 | panel.add(label, MyGridConstraints.gridBuilder(row).setColumn(currentColumn).setIndent(0).build());
135 | }
136 | currentColumn++;
137 | panel.add(positionPanel, MyGridConstraints.gridBuilder(row).setColumn(currentColumn).setIndent(0).build());
138 | }
139 | }
140 | return currentColumn;
141 | }
142 |
143 | public static int addRowOfItemsToPanel(
144 | JPanel panel,
145 | int startingColumn,
146 | int row,
147 | JLabel itemLabel,
148 | String separator,
149 | boolean firstSeparator,
150 | boolean opaqueSeparator) {
151 | if (itemLabel == null) return startingColumn;
152 | if (firstSeparator) {
153 | startingColumn++;
154 | JLabel label = new JLabel(separator);
155 | if (opaqueSeparator) makeOpaque(label, 50);
156 | panel.add(label, MyGridConstraints.gridBuilder(row).setColumn(startingColumn).setIndent(0).build());
157 | }
158 | startingColumn++;
159 | panel.add(itemLabel, MyGridConstraints.gridBuilder(row).setColumn(startingColumn).setIndent(0).build());
160 | return startingColumn;
161 | }
162 |
163 | private static void makeOpaque(JComponent component, int alpha) {
164 | component.setForeground(new Color(
165 | component.getForeground().getRed(),
166 | component.getForeground().getGreen(),
167 | component.getForeground().getBlue(),
168 | alpha));
169 | }
170 |
171 | // ------------------------------ More Information --------------------------------
172 | public static JPanel moreInfoPanel() {
173 | JPanel panel = defaultGridPanel(1, 1);
174 | String url = OpenSCASettingState.getInstance().getOpenSCASetting().getServerAddress();
175 | JLabel linkLabel = new JLabel("更多信息请登录平台查看");
176 | linkLabel.addMouseListener(linkLabelMouseListener(linkLabel, url));
177 | linkLabel.setIcon(AllIcons.General.Information);
178 | panel.add(linkLabel, MyGridConstraints.gridBuilder(0).build());
179 | return panel;
180 | }
181 |
182 | private static MouseListener linkLabelMouseListener(JLabel linkLabel, String url) {
183 | return new MouseAdapter() {
184 | @Override
185 | public void mouseClicked(MouseEvent e) {
186 | if (StringUtil.isNotEmpty(url)) {
187 | BrowserUtil.open(url);
188 | } else {
189 | NotificationUtils.balloonNotify("请先配置服务器地址", NotificationType.WARNING);
190 | }
191 | }
192 |
193 | @Override
194 | public void mouseEntered(MouseEvent e) {
195 | linkLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
196 | }
197 |
198 | @Override
199 | public void mouseExited(MouseEvent e) {
200 | linkLabel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
201 | }
202 | };
203 | }
204 |
205 | // --------------------------------- Font ----------------------------------------
206 | public static Font primaryTitleFont() {
207 | return titleFont(20);
208 | }
209 |
210 | public static Font secondaryTitleFont() {
211 | return titleFont(16);
212 | }
213 |
214 | public static Font titleFont(int size) {
215 | return new Font(Font.DIALOG, Font.BOLD, size);
216 | }
217 |
218 | // ----------------------------- JScrollPane -------------------------------------
219 | public static JScrollPane wrapWithScrollPanel(Component component) {
220 | JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(
221 | component,
222 | ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
223 | ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
224 | scrollPane.setBorder(null);
225 | return scrollPane;
226 | }
227 | }
228 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/window/ToolWindowContentPanel.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.window;
2 |
3 | import com.intellij.openapi.Disposable;
4 | import com.intellij.openapi.project.Project;
5 | import com.intellij.ui.OnePixelSplitter;
6 |
7 | import javax.swing.*;
8 | import java.awt.*;
9 |
10 | /**
11 | * 主面板中的内容面板
12 | */
13 | public class ToolWindowContentPanel extends JPanel implements Disposable {
14 |
15 | private static final String TOOL_WINDOW_SPLITTER_PROPORTION_KEY = "XMIRROR_TOOL_WINDOW_SPLITTER_PROPORTION";
16 |
17 | // 概览面板
18 | private OverviewPanel overviewPanel;
19 |
20 | // 描述面板
21 | private DescriptionPanel descriptionPanel;
22 |
23 | public ToolWindowContentPanel(Project project) {
24 | super(new BorderLayout());
25 | overviewPanel = new OverviewPanel(project, this);
26 | descriptionPanel = new DescriptionPanel(project, this);
27 | OnePixelSplitter vulnerabilitiesSplitter = new OnePixelSplitter(TOOL_WINDOW_SPLITTER_PROPORTION_KEY, 0.4f);
28 | vulnerabilitiesSplitter.setFirstComponent(overviewPanel);
29 | vulnerabilitiesSplitter.setSecondComponent(descriptionPanel);
30 | add(vulnerabilitiesSplitter, BorderLayout.CENTER);
31 | }
32 |
33 | public DescriptionPanel getDescriptionPanel() {
34 | return descriptionPanel;
35 | }
36 |
37 | public OverviewPanel getOverviewPanel() {
38 | return overviewPanel;
39 | }
40 |
41 | @Override
42 | public void dispose() {
43 |
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/window/ToolWindowFactory.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.window;
2 |
3 | import cn.xmirror.sca.ui.ToolWindowManager;
4 | import com.intellij.openapi.project.DumbAware;
5 | import com.intellij.openapi.project.Project;
6 | import com.intellij.openapi.util.Disposer;
7 | import com.intellij.openapi.wm.ToolWindow;
8 | import com.intellij.ui.content.Content;
9 | import com.intellij.ui.content.ContentManager;
10 | import org.jetbrains.annotations.NotNull;
11 |
12 | public class ToolWindowFactory implements com.intellij.openapi.wm.ToolWindowFactory, DumbAware {
13 | @Override
14 | public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {
15 | ToolWindowMainPanel mainPanel = new ToolWindowMainPanel(project);
16 | ContentManager contentManager = toolWindow.getContentManager();
17 | Content content = contentManager.getFactory().createContent(mainPanel, "Xcheck", false);
18 | contentManager.addContent(content);
19 |
20 | Disposer.register(project, mainPanel);
21 | ToolWindowManager.addMainWindow(project, mainPanel);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/window/ToolWindowMainPanel.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.window;
2 |
3 | import cn.xmirror.sca.common.CheckListener;
4 | import cn.xmirror.sca.ui.NotificationUtils;
5 | import com.intellij.notification.NotificationType;
6 | import com.intellij.openapi.Disposable;
7 | import com.intellij.openapi.actionSystem.ActionGroup;
8 | import com.intellij.openapi.actionSystem.ActionManager;
9 | import com.intellij.openapi.actionSystem.ActionToolbar;
10 | import com.intellij.openapi.project.Project;
11 | import com.intellij.openapi.ui.SimpleToolWindowPanel;
12 |
13 | import javax.swing.*;
14 | import javax.swing.tree.MutableTreeNode;
15 | import java.awt.*;
16 |
17 | /**
18 | * 主窗口。组合 窗口工具栏 和 窗口内容面板
19 | *
20 | * @author Yuan Shengjun
21 | */
22 | public class ToolWindowMainPanel extends JPanel implements Disposable, CheckListener {
23 |
24 | private JProgressBar progressBar;
25 | private ToolWindowContentPanel contentPanel;
26 | private final Project project;
27 |
28 | public ToolWindowMainPanel(Project project) {
29 | super(new BorderLayout());
30 | this.project = project;
31 | initPanel();
32 | }
33 |
34 | public ToolWindowContentPanel getContentPanel() {
35 | return contentPanel;
36 | }
37 |
38 | private void initPanel() {
39 | // 窗口内容面板
40 | contentPanel = new ToolWindowContentPanel(project);
41 | // 窗口工具栏
42 | ActionManager actionManager = ActionManager.getInstance();
43 | ActionGroup actionGroup = (ActionGroup) actionManager.getAction("cn.xmirror.sca.ActionBar");
44 | ActionToolbar actionToolbar = actionManager.createActionToolbar("Xmirror Toolbar", actionGroup, false);
45 | actionToolbar.setTargetComponent(contentPanel);
46 | initializeToolbar();
47 |
48 | SimpleToolWindowPanel mainPanel = new SimpleToolWindowPanel(false, true);
49 | mainPanel.setToolbar(actionToolbar.getComponent());
50 | mainPanel.setContent(contentPanel);
51 |
52 | add(mainPanel, BorderLayout.CENTER);
53 | }
54 |
55 | private void initializeToolbar() {
56 | // project.getMessageBus().connect(this).subscribe(Topic.create("Xmirror Scan",));
57 | }
58 |
59 | @Override
60 | public void clean() {
61 | contentPanel.getOverviewPanel().render(null);
62 | contentPanel.getDescriptionPanel().getGuidePanel().setVisible(true);
63 | }
64 |
65 | @Override
66 | public void progress(boolean running) {
67 | if (running) {
68 | if (progressBar == null) {
69 | progressBar = new JProgressBar();
70 | progressBar.setIndeterminate(true);
71 | }
72 | add(progressBar, BorderLayout.NORTH);
73 | } else {
74 | remove(progressBar);
75 | }
76 | updateUI();
77 | }
78 |
79 | @Override
80 | public void onSuccess(MutableTreeNode resultTree) {
81 | contentPanel.getOverviewPanel().render(resultTree);
82 | }
83 |
84 | @Override
85 | public void onError(Exception e) {
86 | NotificationUtils.balloonNotify(e.getMessage(), NotificationType.ERROR);
87 | }
88 |
89 | @Override
90 | public void dispose() {
91 |
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/window/TreeCellRenderer.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.window;
2 |
3 | import cn.xmirror.sca.common.dto.Component;
4 | import cn.xmirror.sca.common.dto.Overview;
5 | import cn.xmirror.sca.common.dto.Vulnerability;
6 | import cn.xmirror.sca.ui.window.tree.ComponentTreeNode;
7 | import cn.xmirror.sca.ui.window.tree.RootTreeNode;
8 | import cn.xmirror.sca.ui.window.tree.VulnerabilityTreeNode;
9 | import com.intellij.ui.ColoredTreeCellRenderer;
10 | import com.intellij.ui.SimpleTextAttributes;
11 | import com.intellij.util.ui.UIUtil;
12 | import icons.Icons;
13 | import org.apache.commons.lang3.StringUtils;
14 | import org.jetbrains.annotations.NotNull;
15 |
16 | import javax.swing.*;
17 | import javax.swing.tree.DefaultMutableTreeNode;
18 |
19 | public class TreeCellRenderer extends ColoredTreeCellRenderer {
20 | @Override
21 | public void customizeCellRenderer(
22 | @NotNull JTree tree,
23 | Object value,
24 | boolean selected,
25 | boolean expanded,
26 | boolean leaf,
27 | int row,
28 | boolean hasFocus) {
29 |
30 | Icon nodeIcon = null;
31 | String text = null;
32 | SimpleTextAttributes attributes = SimpleTextAttributes.REGULAR_ATTRIBUTES;
33 | if (value instanceof ComponentTreeNode) {
34 | ComponentTreeNode node = (ComponentTreeNode) value;
35 | Component component = (Component) node.getUserObject();
36 | nodeIcon = Icons.getIconFromResources(component.getSecurityLevelId());
37 | text = component.getName() + ":" + component.getVersion();
38 | } else if (value instanceof VulnerabilityTreeNode) {
39 | VulnerabilityTreeNode node = (VulnerabilityTreeNode) value;
40 | Vulnerability vulnerability = (Vulnerability) node.getUserObject();
41 | nodeIcon = Icons.getIconFromResources(vulnerability.getSecurityLevelId());
42 | text = vulnerability.getName();
43 | } else if (value instanceof RootTreeNode) {
44 | RootTreeNode node = (RootTreeNode) value;
45 | Object userObject = node.getUserObject();
46 | Overview rootUserObject = (Overview) userObject;
47 | nodeIcon = Icons.OPEN_SCA_LOGO;
48 | text = "OpenSCA";
49 | attributes = SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES;
50 | } else {
51 | DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
52 | // 结果树的顶层根节点为空
53 | if (StringUtils.isEmpty(value.toString())) {
54 | return;
55 | }else {
56 | text = node.getUserObject().toString();
57 | }
58 | }
59 |
60 | setIcon(nodeIcon);
61 | setFont(UIUtil.getTreeFont());
62 | if (text != null) {
63 | append(text, attributes);
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/window/tree/ComponentTreeNode.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.window.tree;
2 |
3 |
4 | import cn.xmirror.sca.common.dto.Component;
5 |
6 | import javax.swing.tree.DefaultMutableTreeNode;
7 |
8 | public class ComponentTreeNode extends DefaultMutableTreeNode {
9 | public ComponentTreeNode(Component o) {
10 | super(o);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/window/tree/FilePathTreeNode.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.window.tree;
2 |
3 | import cn.xmirror.sca.common.dto.FilePath;
4 | import com.intellij.openapi.project.Project;
5 |
6 | import javax.swing.tree.DefaultMutableTreeNode;
7 |
8 | /**
9 | * @author xingluheng
10 | * @date 2023/07/18 16:21
11 | **/
12 | public class FilePathTreeNode extends DefaultMutableTreeNode {
13 | public FilePathTreeNode(FilePath filePath){
14 | super(filePath);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/window/tree/RootTreeNode.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.window.tree;
2 |
3 |
4 | import cn.xmirror.sca.common.dto.Overview;
5 |
6 | import javax.swing.tree.DefaultMutableTreeNode;
7 |
8 | public class RootTreeNode extends DefaultMutableTreeNode {
9 | public RootTreeNode(Overview o) {
10 | super(o);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/cn/xmirror/sca/ui/window/tree/VulnerabilityTreeNode.java:
--------------------------------------------------------------------------------
1 | package cn.xmirror.sca.ui.window.tree;
2 |
3 |
4 | import cn.xmirror.sca.common.dto.Vulnerability;
5 |
6 | import javax.swing.tree.DefaultMutableTreeNode;
7 |
8 | public class VulnerabilityTreeNode extends DefaultMutableTreeNode {
9 | public VulnerabilityTreeNode(Vulnerability o) {
10 | super(o);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/icons/Icons.java:
--------------------------------------------------------------------------------
1 | package icons;
2 |
3 | import cn.xmirror.sca.common.constant.SecurityLevelEnum;
4 | import com.intellij.openapi.util.IconLoader;
5 |
6 | import javax.swing.*;
7 |
8 | public class Icons {
9 | /**
10 | * 主题
11 | */
12 | public static final Icon XMIRROR_LOGO = IconLoader.getIcon("/icons/xmirror_logo.png",Icons.class);
13 | public static final Icon XMIRROR_LOGO_24 = IconLoader.getIcon("/icons/xmirror_logo_24.png",Icons.class);
14 | public static final Icon OPEN_SCA_LOGO = IconLoader.getIcon("/icons/open_sca.svg",Icons.class);
15 |
16 | /**
17 | * 展示面板
18 | */
19 | public static final Icon USE_SUGGESTION = IconLoader.getIcon("/icons/use_suggestion_20.svg",Icons.class);
20 | public static final Icon LICENSE = IconLoader.getIcon("/icons/license_20.svg",Icons.class);
21 | public static final Icon VUL_DESCRIPTION = IconLoader.getIcon("/icons/vul_description_20.svg",Icons.class);
22 | public static final Icon FIX_SUGGESTION = IconLoader.getIcon("/icons/fix_suggestion_20.svg",Icons.class);
23 | public static final Icon COMPONENT_STATISTICS = IconLoader.getIcon("/icons/fix_suggestion_20.svg",Icons.class);
24 | public static final Icon VUL_STATISTICS = IconLoader.getIcon("/icons/vul_statistics_20.svg",Icons.class);
25 | public static final Icon LICENSE_STATISTICS = IconLoader.getIcon("/icons/license_statistics_20.svg",Icons.class);
26 |
27 | public static final Icon UPLOAD_SAAS = IconLoader.getIcon("/actions/upload.png",Icons.class);
28 |
29 |
30 | /**
31 | * 危险级别
32 | */
33 | public static final Icon CRITICAL = IconLoader.getIcon("/icons/severity_critical.svg",Icons.class);
34 | public static final Icon CRITICAL_24 = IconLoader.getIcon("/icons/severity_critical_24.svg",Icons.class);
35 | public static final Icon HIGH = IconLoader.getIcon("/icons/severity_high.svg",Icons.class);
36 | public static final Icon HIGH_24 = IconLoader.getIcon("/icons/severity_high_24.svg",Icons.class);
37 | public static final Icon LOW = IconLoader.getIcon("/icons/severity_low.svg",Icons.class);
38 | public static final Icon LOW_24 = IconLoader.getIcon("/icons/severity_low_24.svg",Icons.class);
39 | public static final Icon MEDIUM = IconLoader.getIcon("/icons/severity_medium.svg",Icons.class);
40 | public static final Icon MEDIUM_24 = IconLoader.getIcon("/icons/severity_medium_24.svg",Icons.class);
41 | public static final Icon SEC = IconLoader.getIcon("/icons/sec.svg",Icons.class);
42 | private static final Icon NO_RATING = IconLoader.getIcon("/icons/severity_no_rating.svg",Icons.class);
43 | private static final Icon NO_RATING_24 = IconLoader.getIcon("/icons/severity_no_rating_24.svg",Icons.class);
44 |
45 | /**
46 | * 黑名单
47 | */
48 | public static final Icon BLACKLIST = IconLoader.getIcon("/icons/severity_blacklist.svg",Icons.class);
49 | public static final Icon BLACKLIST_24 = IconLoader.getIcon("/icons/severity_blacklist_24.svg",Icons.class);
50 |
51 | /**
52 | * 检测相关
53 | */
54 | public static final Icon SUCCEEDED = IconLoader.getIcon("/icons/succeeded.svg",Icons.class);
55 | public static final Icon FAILED = IconLoader.getIcon("/icons/failed.svg",Icons.class);
56 | public static final Icon WARNING = IconLoader.getIcon("/icons/warning.svg",Icons.class);
57 | public static final Icon REFRESH = IconLoader.getIcon("/icons/refresh.svg",Icons.class);
58 |
59 | public static Icon getIconFromResources(int securityLevel) {
60 | return getIconFromResources(securityLevel, IconSize.SIZE16);
61 | }
62 |
63 | public static Icon getIconFromResources(int securityLevel, IconSize iconSize) {
64 | Icon icon = null;
65 | if (securityLevel == SecurityLevelEnum.CRITICAL.getLevel()) {
66 | if (iconSize == IconSize.SIZE16) icon = CRITICAL;
67 | if (iconSize == IconSize.SIZE24) icon = CRITICAL_24;
68 | } else if (securityLevel == SecurityLevelEnum.HIGH.getLevel()) {
69 | if (iconSize == IconSize.SIZE16) icon = HIGH;
70 | if (iconSize == IconSize.SIZE24) icon = HIGH_24;
71 | } else if (securityLevel == SecurityLevelEnum.MEDIUM.getLevel()) {
72 | if (iconSize == IconSize.SIZE16) icon = MEDIUM;
73 | if (iconSize == IconSize.SIZE24) icon = MEDIUM_24;
74 | } else if (securityLevel == SecurityLevelEnum.LOW.getLevel()) {
75 | if (iconSize == IconSize.SIZE16) icon = LOW;
76 | if (iconSize == IconSize.SIZE24) icon = LOW_24;
77 | }else if (securityLevel == SecurityLevelEnum.SEC.getLevel()) {
78 | icon = SEC;
79 | }
80 | return icon;
81 | }
82 |
83 | public enum IconSize {
84 | SIZE16, SIZE24
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 | cn.xmirror.sca.xcheck
3 | OpenSCA Xcheck
4 | Xmirror
5 |
6 |
7 |
10 | Usage:
11 |
12 | - Click "Setting" in OpenSCA Toolbar to set information of server
13 | - Click "Run" in OpenSCA Toolbar to detect and analyze code
14 | - Click "Stop" in OpenSCA Toolbar to stop detect
15 | - Click "Clean" in OpenSCA Toolbar to clean all
16 |
17 | ]]>
18 |
19 |
20 | com.intellij.modules.platform
21 | com.intellij.modules.lang
22 |
23 |
24 |
28 |
30 |
32 |
33 |
34 |
35 |
36 |
37 |
42 |
47 |
52 |
57 |
58 |
59 |
62 |
65 |
68 |
71 |
72 |
77 |
82 |
83 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/src/main/resources/icons/component_statistics_20.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/main/resources/icons/component_statistics_20_dark.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/main/resources/icons/failed.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/main/resources/icons/failed_dark.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/main/resources/icons/fix_suggestion_20.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/icons/fix_suggestion_20_dark.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/icons/license_20.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/icons/license_20_dark.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/icons/license_statistics_20.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/icons/license_statistics_20_dark.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/icons/refresh.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/icons/sec.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/main/resources/icons/severity_blacklist.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/src/main/resources/icons/severity_blacklist_24.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/main/resources/icons/severity_critical.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/main/resources/icons/severity_critical_24.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/src/main/resources/icons/severity_high.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/main/resources/icons/severity_high_24.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/main/resources/icons/severity_low.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/main/resources/icons/severity_low_24.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/main/resources/icons/severity_medium.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/main/resources/icons/severity_medium_24.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/main/resources/icons/severity_no_rating.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/main/resources/icons/severity_no_rating_24.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/main/resources/icons/succeeded.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/main/resources/icons/succeeded_dark.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/main/resources/icons/use_suggestion_20.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/main/resources/icons/use_suggestion_20_dark.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/main/resources/icons/vul_description_20.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/icons/vul_description_20_dark.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/icons/vul_statistics_20.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/icons/vul_statistics_20_dark.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/icons/warning.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/src/main/resources/icons/xmirror_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XmirrorSecurity/OpenSCA-intellij-plugin/5dfeca6fb0bcbefb3976ad38f6b01d92ca85798b/src/main/resources/icons/xmirror_logo.png
--------------------------------------------------------------------------------
/src/main/resources/icons/xmirror_logo_24.svg:
--------------------------------------------------------------------------------
1 |
84 |
--------------------------------------------------------------------------------