全网最详细IDE插件开发之代码审计
字数 2316 2025-08-15 21:33:46
IntelliJ IDEA 插件开发与代码审计工具实现教程
一、背景与概述
本教程将详细介绍如何开发一个IntelliJ IDEA插件,特别是针对代码审计功能的插件开发。教程基于陌陌安全团队开源的momo-code-sec-inspector-java项目,从环境搭建到实际插件开发,逐步讲解整个过程。
二、开发环境搭建
2.1 准备工作
| 序号 | 软件名 | 备注 |
|---|---|---|
| 1 | IntelliJ IDEA 2020.1.4 Community | 下载页面 |
| 2 | JDK 1.8 | 百度云链接(密码:9k19) |
| 3 | momo开源代码 | git clone地址 |
2.2 JDK安装
- 下载JDK 1.8安装包
- 使用"下一步"方式完成安装
- 确保环境变量配置正确
2.3 IDEA安装与项目创建
- 安装IntelliJ IDEA Community版,使用默认设置
- 创建新项目:
- 选择Gradle作为项目构建工具
- 勾选"IntelliJ Platform Plugin"选项
- 确保Project SDK自动选择为JDK 1.8
- 填写项目基本信息:
- GroupId: com.security.guy
- ArtifactId: myfirstPlugin
- Version: 1.0-SNAPSHOT
- 等待Gradle构建项目框架(可能需要10-30分钟下载依赖)
2.4 项目结构详解
项目根目录/
├── build.gradle # Gradle配置文件
├── gradlew # Linux下Gradle执行文件
├── gradlew.bat # Windows下Gradle执行文件
├── settings.gradle # Gradle设置文件
├── .gradle/ # Gradle缓存目录
│ ├── 6.1.1/ # Gradle版本目录
│ │ ├── fileChanges/ # 文件变更记录
│ │ ├── fileHashes/ # 文件哈希值
│ │ └── ... # 其他Gradle缓存文件
├── .idea/ # IDEA项目配置
│ ├── compiler.xml # 编译器配置
│ ├── gradle.xml # Gradle配置
│ └── ... # 其他IDEA配置文件
├── gradle/ # Gradle包装器
│ ├── wrapper/
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
└── src/ # 源代码目录
├── main/ # 主代码
│ ├── java/ # Java源代码
│ └── resources/ # 资源文件
│ ├── META-INF/ # 插件元信息
│ │ └── plugin.xml # 插件配置文件
└── test/ # 测试代码
├── java/ # 测试Java代码
└── resources/ # 测试资源
三、开发第一个插件
3.1 组件类型介绍
| 组件类型 | 描述 | 对应plugin.xml配置元素 |
|---|---|---|
| ApplicationComponent | 在IDEA启动时初始化,整个IDEA只有一个实例 | <application-components> |
| ProjectComponent | IDEA会为每一个Project实例创建对应级别的Component | <project-components> |
| ModuleComponent | IDEA会为每一个已加载Project中的每一个模块(module)创建对应级别的Component | <module-components> |
3.2 创建启动弹窗插件
1. 创建ApplicationComponent
在src/main/java下创建包com.tolly.security.action,然后创建MyChickenSoul.java:
package com.tolly.security.action;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.components.ApplicationComponent;
public class MyChickenSoul implements ApplicationComponent {
@Override
public void initComponent() {
// 创建并显示对话框
DialogChickenSoul dialogChickenSoul = new DialogChickenSoul();
dialogChickenSoul.show();
}
}
2. 创建对话框类
在相同包下创建DialogChickenSoul.java:
package com.tolly.security.action;
import com.intellij.openapi.ui.DialogWrapper;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
public class DialogChickenSoul extends DialogWrapper {
protected DialogChickenSoul() {
super(true);
setTitle("哈哈"); // 设置对话框标题
init(); // 必须调用初始化方法
}
@Override
protected @Nullable JComponent createCenterPanel() {
JPanel jPanel = new JPanel();
HttpRequest hrequest = new HttpRequest();
String content = hrequest.httpclient(); // 获取网络内容
JLabel jLabel = new JLabel(content);
jPanel.add(jLabel);
return jPanel;
}
}
3. 创建HTTP请求工具类
创建HttpRequest.java用于获取网络内容:
package com.tolly.security.action;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
public class HttpRequest {
public String httpclient() {
String url = "https://data.zhai78.com/openOneGood.php";
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(url);
try (CloseableHttpResponse httpResponse = httpClient.execute(httpGet)) {
HttpEntity httpEntity = httpResponse.getEntity();
String response = EntityUtils.toString(httpEntity);
JSONObject result = JSON.parseObject(response);
return result.getString("txt");
} catch (ClientProtocolException e) {
e.printStackTrace();
return "请求失败";
} catch (IOException e) {
e.printStackTrace();
return "IO异常";
}
}
}
4. 添加依赖
修改build.gradle文件,添加必要的依赖:
plugins {
id 'java'
id 'org.jetbrains.intellij' version '0.6.5'
}
group 'com.security.guy'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
// 添加HTTPClient和FastJSON依赖
compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.13'
compile group: 'com.alibaba', name: 'fastjson', version: '1.2.75'
}
intellij {
version '2020.1.4'
}
patchPluginXml {
changeNotes """
Add change notes here.<br>
<em>most HTML tags may be used</em>
"""
}
// 解决中文乱码问题
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
5. 配置plugin.xml
修改src/main/resources/META-INF/plugin.xml文件:
<idea-plugin>
<id>com.security.guy.myfirstPlugin</id>
<name>Plugin display name here</name>
<vendor email="support@yourcompany.com" url="http://www.yourcompany.com">YourCompany</vendor>
<description><![CDATA[
Enter short description for your plugin here.<br>
<em>most HTML tags may be used</em>
]]></description>
<depends>com.intellij.modules.platform</depends>
<extensions defaultExtensionNs="com.intellij">
<!-- 可以在此添加扩展点 -->
</extensions>
<actions>
<!-- 可以在此添加动作 -->
</actions>
<application-components>
<component>
<implementation-class>com.tolly.security.action.MyChickenSoul</implementation-class>
</component>
</application-components>
</idea-plugin>
6. 运行插件
- 点击Gradle工具栏中的
runIde任务 - 等待IDEA沙箱启动
- 观察启动时是否弹出包含网络内容的对话框
四、代码审计插件开发进阶
4.1 理解SAST原理
静态应用程序安全测试(SAST)通过分析源代码而不执行程序来发现潜在安全问题。在IDE插件中实现SAST可以:
- 实时分析代码
- 标记潜在安全问题
- 提供修复建议
- 与开发流程无缝集成
4.2 实现代码审计功能
1. 创建代码检查器
public class SecurityInspector extends LocalInspectionTool {
@Nullable
@Override
public ProblemDescriptor[] checkFile(@NotNull PsiFile file,
@NotNull InspectionManager manager,
boolean isOnTheFly) {
// 实现安全检查逻辑
List<ProblemDescriptor> problems = new ArrayList<>();
// 示例:检查SQL注入
checkSqlInjection(file, manager, problems);
return problems.toArray(new ProblemDescriptor[0]);
}
private void checkSqlInjection(PsiFile file,
InspectionManager manager,
List<ProblemDescriptor> problems) {
// 查找所有字符串拼接的SQL语句
file.accept(new JavaRecursiveElementVisitor() {
@Override
public void visitMethodCallExpression(PsiMethodCallExpression expression) {
super.visitMethodCallExpression(expression);
if (isSqlExecutionMethod(expression)) {
PsiExpression[] args = expression.getArgumentList().getExpressions();
if (args.length > 0 && isStringConcatenation(args[0])) {
// 创建问题描述
ProblemDescriptor descriptor = manager.createProblemDescriptor(
expression,
"Potential SQL injection vulnerability",
(LocalQuickFix)null,
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
isOnTheFly
);
problems.add(descriptor);
}
}
}
});
}
}
2. 注册检查器
在plugin.xml中添加:
<extensions defaultExtensionNs="com.intellij">
<localInspection
language="JAVA"
displayName="Security Inspector"
groupPath="Java"
enabledByDefault="true"
level="ERROR"
implementationClass="com.tolly.security.inspector.SecurityInspector"/>
</extensions>
3. 实现复杂规则
public class XssInspector extends LocalInspectionTool {
@Nullable
@Override
public ProblemDescriptor[] checkFile(@NotNull PsiFile file,
@NotNull InspectionManager manager,
boolean isOnTheFly) {
List<ProblemDescriptor> problems = new ArrayList<>();
file.accept(new JavaRecursiveElementVisitor() {
@Override
public void visitMethodCallExpression(PsiMethodCallExpression expression) {
super.visitMethodCallExpression(expression);
// 检查未转义的输出到响应的方法
if (isResponseWriteMethod(expression)) {
PsiExpression[] args = expression.getArgumentList().getExpressions();
if (args.length > 0 && isUnescapedOutput(args[0])) {
problems.add(createProblem(manager, expression,
"Potential XSS vulnerability: unescaped output to response"));
}
}
}
});
return problems.toArray(new ProblemDescriptor[0]);
}
}
五、插件打包与发布
5.1 打包插件
- 在Gradle面板中运行
buildPlugin任务 - 生成的插件包位于
build/distributions目录 - 文件格式为
.zip或.jar
5.2 发布到JetBrains插件市场
- 注册JetBrains账号
- 申请成为插件开发者
- 通过JetBrains Plugin Repository上传插件
- 填写插件元数据:
- 描述
- 截图
- 变更日志
- 兼容性信息
六、调试与优化
6.1 调试技巧
- 使用IDEA内置调试器
- 设置断点在关键检查逻辑
- 观察PSI树结构
- 使用
PsiViewer插件查看PSI结构
6.2 性能优化
- 避免全文件扫描,针对特定元素检查
- 缓存常用查询结果
- 使用轻量级检查器
- 考虑延迟加载规则
七、总结
本教程详细介绍了从零开始开发IntelliJ IDEA插件的过程,特别是针对代码审计功能的实现。关键点包括:
- 正确搭建开发环境
- 理解IDEA插件架构
- 实现各种类型的组件
- 开发自定义代码检查器
- 打包和发布插件
通过本教程,您应该能够开发出自己的代码审计插件,或基于陌陌安全开源的SAST规则进行二次开发。