初始化提交

CyberMatrix 量子安全分析引擎
This commit is contained in:
savior-only 2025-02-22 02:25:06 +08:00 committed by GitHub
commit a4260b3d42
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 1286 additions and 0 deletions

110
README.md Normal file
View File

@ -0,0 +1,110 @@
# CyberMatrix 量子安全分析引擎
CyberMatrix 是一个基于 AI 的代码安全分析工具,专注于自动化检测和分析代码中的潜在安全漏洞。采用赛博朋克风格的现代化界面,提供直观的安全分析体验。
![image-20250222011558648](./assets/image-20250222011558648.png)
## 核心功能
### 1. 深度安全分析
- 自动识别代码中的安全风险点
- 支持检测 SQL 注入、XSS、CSRF、文件上传漏洞等常见安全问题
- 基于 CVSS 评分标准评估风险等级
- 提供详细的漏洞位置和描述
![image-20250222014422442](./assets/image-20250222014422442.png)
### 2. Webshell 检测
- 智能识别 PHP/JSP/ASP 等 WebShell 特征
- 检测内存马和无文件落地木马
- 分析可疑的代码执行和文件操作
- 识别混淆编码和加密规避技术
![image-20250222012127096](./assets/image-20250222012127096.png)
### 3. 智能分析引擎
- 基于大语言模型的智能代码理解
- 支持多种编程语言的代码分析
- 极低的误报率和高准确度
- 持续学习和优化的检测能力
## 界面特点
- 赛博朋克风格界面设计
- 实时进度展示和分析反馈
- 直观的文件树浏览
- 智能的颜色标记系统
- 流畅的动画效果
<div align="center">
<img src="assets/image-20250222012315255.png" width="45%" alt="常态界面"/>
<img src="assets/image-20250222012232207.png" width="45%" alt="扫描状态界面"/>
</div>
## 技术栈
- JavaFX + JFoenix现代化 UI 框架
- Ollama本地化 AI 模型
- AnimateFX界面动画效果
- JacksonJSON 处理
- OkHttp网络请求
## 使用方法
1. 点击"初始化量子扫描目标"选择要分析的代码目录
2. 选择分析模式(深度安全分析/Webshell检测
3. 可选择是否包含静态资源分析
4. 点击"启动量子安全分析"开始扫描
5. 实时查看分析结果和进度
<div align="center">
<table>
<tr>
<td><img src="assets/image-20250222012445385.png" alt="选择目标"/></td>
<td><img src="assets/image-20250222012838474.png" alt="选择模式"/></td>
</tr>
<tr>
<td>1. 选择扫描目标</td>
<td>2. 选择分析模式</td>
</tr>
<tr>
<td><img src="assets/image-20250222012916552.png" alt="开始扫描"/></td>
<td><img src="assets/image-20250222012232207.png" alt="查看结果"/></td>
</tr>
<tr>
<td>3. 启动扫描</td>
<td>4. 查看分析结果</td>
</tr>
</table>
</div>
## 配置要求
- Java 8 或更高版本
- Ollama 本地服务
- 建议系统内存 8GB 以上
## 安装说明
1. 确保已安装 Java 8 运行环境
2. 下载并安装 Ollama
3. 配置 config.yaml 文件
4. 运行启动脚本
## 开发环境搭建
1. Clone 项目代码
2. 使用 Maven 导入依赖
3. 配置 JDK 8
4. 运行 CyberScannerApp 主类
## 参考项目
本项目受以下开源项目启发:
- [DeepSeekSelfTool](https://github.com/ChinaRan0/DeepSeekSelfTool) - 基于 DeepSeek 的代码安全分析工具
## 许可证
MIT License

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 598 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 KiB

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.cyberscanner</groupId>
<artifactId>cyber-scanner</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer>
<mainClass>com.cyberscanner.CyberScannerApp</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext7-core</artifactId>
<version>7.2.5</version>
<type>pom</type>
<scope>compile</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<javafx.version>8.0.202</javafx.version>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
</project>

109
pom.xml Normal file
View File

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.cyberscanner</groupId>
<artifactId>cyber-scanner</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.jfoenix</groupId>
<artifactId>jfoenix</artifactId>
<version>8.0.10</version>
</dependency>
<!-- YAML 处理 -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.33</version>
</dependency>
<!-- HTTP 客户端 -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.14.9</version>
</dependency>
<!-- JSON 处理 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.7.1</version>
</dependency>
<!-- 动画效果库 -->
<dependency>
<groupId>io.github.typhon0</groupId>
<artifactId>AnimateFX</artifactId>
<version>1.2.1</version>
</dependency>
<!-- 日志框架 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.9</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
<exclude>config.yaml</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.cyberscanner.CyberScannerApp</mainClass>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/services/javax.annotation.processing.Processor</resource>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,7 @@
package com.cyberscanner;
import javafx.scene.paint.Color;
public interface ColoredMessageCallback {
void updateMessage(String message, Color color);
}

View File

@ -0,0 +1,348 @@
package com.cyberscanner;
import animatefx.animation.Bounce;
import animatefx.animation.FadeIn;
import animatefx.animation.Pulse;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.scene.effect.DropShadow;
import javafx.scene.effect.Glow;
import javafx.util.Duration;
import com.jfoenix.controls.*;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.text.Text;
import javafx.scene.paint.Color;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
public class CyberScannerApp extends Application {
private ScanTask currentTask;
private JFXTextArea resultDisplay;
private JFXButton scanButton;
private JFXButton selectButton;
private Label pathLabel;
private TreeView<File> fileTree;
private JFXRadioButton auditRadio;
private JFXRadioButton webshellRadio;
private JFXCheckBox auditJsCheckbox;
private StatusBar statusBar;
private JFXProgressBar progressBar;
@Override
public void start(Stage primaryStage) {
primaryStage.setOnCloseRequest(event -> {
if (currentTask != null) {
currentTask.stop();
}
});
primaryStage.setTitle("CyberMatrix - 量子安全分析引擎");
primaryStage.setMinWidth(1280);
primaryStage.setMinHeight(720);
// 创建主布局
BorderPane mainLayout = new BorderPane();
mainLayout.getStyleClass().add("main-layout");
mainLayout.setPrefSize(1280, 720);
// 左侧面板
VBox leftPanel = createLeftPanel();
leftPanel.setMinWidth(300);
leftPanel.setPrefWidth(300);
leftPanel.setMaxWidth(400);
VBox.setVgrow(fileTree, Priority.ALWAYS);
mainLayout.setLeft(leftPanel);
// 右侧结果显示区
resultDisplay = new JFXTextArea();
resultDisplay.getStyleClass().add("result-display");
resultDisplay.setEditable(false);
resultDisplay.setWrapText(true);
BorderPane.setMargin(resultDisplay, new Insets(10));
VBox.setVgrow(resultDisplay, Priority.ALWAYS);
mainLayout.setCenter(resultDisplay);
// 进度条
progressBar = new JFXProgressBar();
progressBar.setProgress(0);
progressBar.getStyleClass().add("progress-bar");
progressBar.setPrefWidth(Double.MAX_VALUE);
// 状态栏
statusBar = new StatusBar();
statusBar.getStyleClass().add("status-bar");
// 将进度条和状态栏放在底部
VBox bottomBox = new VBox(5);
bottomBox.setPadding(new Insets(5));
bottomBox.getChildren().addAll(progressBar, statusBar);
mainLayout.setBottom(bottomBox);
// 场景和样式
Scene scene = new Scene(mainLayout);
scene.getStylesheets().add(getClass().getResource("/styles/cyber-theme.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
// 添加启动动画序列
new FadeIn(mainLayout).play();
new Pulse(selectButton).play();
// 为pathLabel添加发光效果动画
Glow glow = new Glow(0);
pathLabel.setEffect(glow);
Timeline glowTimeline = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(glow.levelProperty(), 0)),
new KeyFrame(Duration.seconds(1), new KeyValue(glow.levelProperty(), 0.8)),
new KeyFrame(Duration.seconds(2), new KeyValue(glow.levelProperty(), 0))
);
glowTimeline.setCycleCount(Timeline.INDEFINITE);
glowTimeline.play();
// 添加周期性动画效果
Timeline pulseTimeline = new Timeline(
new KeyFrame(Duration.seconds(2),
new KeyValue(leftPanel.effectProperty(),
new DropShadow(10, Color.valueOf("#4d4dff"))))
);
pulseTimeline.setAutoReverse(true);
pulseTimeline.setCycleCount(Timeline.INDEFINITE);
pulseTimeline.play();
}
private VBox createLeftPanel() {
VBox leftPanel = new VBox(10);
leftPanel.getStyleClass().add("left-panel");
leftPanel.setPadding(new Insets(10));
// 目录选择按钮
selectButton = new JFXButton("🌐 初始化量子扫描目标");
selectButton.getStyleClass().add("cyber-button");
selectButton.setOnAction(e -> selectDirectory());
// 路径显示标签
pathLabel = new Label("等待目标初始化...");
pathLabel.getStyleClass().add("path-label");
// 模式选择组
VBox modeBox = createModeSelectionBox();
// 文件树
fileTree = new TreeView<>();
fileTree.getStyleClass().add("file-tree");
VBox.setVgrow(fileTree, Priority.ALWAYS);
// 扫描按钮
scanButton = new JFXButton("⚡ 启动量子安全分析");
scanButton.getStyleClass().add("scan-button");
scanButton.setDisable(true);
scanButton.setOnAction(e -> startScan());
leftPanel.getChildren().addAll(
selectButton,
pathLabel,
modeBox,
auditJsCheckbox,
fileTree,
scanButton
);
return leftPanel;
}
private VBox createModeSelectionBox() {
VBox modeBox = new VBox(5);
modeBox.getStyleClass().add("mode-box");
Label modeLabel = new Label("🔧 分析模式");
modeLabel.getStyleClass().add("mode-label");
ToggleGroup modeGroup = new ToggleGroup();
auditRadio = new JFXRadioButton("深度安全分析");
webshellRadio = new JFXRadioButton("Webshell检测");
auditRadio.setToggleGroup(modeGroup);
webshellRadio.setToggleGroup(modeGroup);
auditRadio.setSelected(true);
auditJsCheckbox = new JFXCheckBox("包含静态资源分析");
auditJsCheckbox.setSelected(true);
modeBox.getChildren().addAll(modeLabel, auditRadio, webshellRadio);
return modeBox;
}
private void selectDirectory() {
DirectoryChooser directoryChooser = new DirectoryChooser();
directoryChooser.setTitle("选择代码矩阵接入点");
File selectedDirectory = directoryChooser.showDialog(null);
if (selectedDirectory != null) {
pathLabel.setText("📂 目标:" + selectedDirectory.getName());
updateFileTree(selectedDirectory);
scanButton.setDisable(false);
statusBar.setStatus("✅ 目标初始化完成");
// 添加动画效果
new Pulse(scanButton).play();
}
}
private int totalFileCount = 0;
private void updateFileTree(File root) {
TreeItem<File> rootItem = new TreeItem<>(root);
rootItem.setExpanded(true);
fileTree.setRoot(rootItem);
totalFileCount = 0;
populateFileTree(rootItem);
// 设置单元格工厂来自定义显示
fileTree.setCellFactory(tv -> new TreeCell<File>() {
@Override
protected void updateItem(File item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
} else {
// 只显示文件或目录名而不是完整路径
setText(item.getName());
}
}
});
}
private void populateFileTree(TreeItem<File> item) {
File[] files = item.getValue().listFiles();
if (files != null) {
Arrays.sort(files, (f1, f2) -> {
// 目录优先然后按名称排序
if (f1.isDirectory() && !f2.isDirectory()) {
return -1;
} else if (!f1.isDirectory() && f2.isDirectory()) {
return 1;
} else {
return f1.getName().compareToIgnoreCase(f2.getName());
}
});
for (File file : files) {
TreeItem<File> fileItem = new TreeItem<>(file);
item.getChildren().add(fileItem);
if (!file.isDirectory()) {
totalFileCount++;
}
if (file.isDirectory()) {
populateFileTree(fileItem);
}
}
}
}
private void startScan() {
if (currentTask != null) {
currentTask.stop();
}
if (fileTree.getRoot() == null) {
showAlert("警告", "请先选择代码目录!");
return;
}
scanButton.setDisable(true);
String initMsg = auditRadio.isSelected() ?
"🚀 正在启动深度安全分析引擎..." :
"🕵️ 正在启动Webshell检测引擎...";
resultDisplay.setText(initMsg + "\n" + String.join("", Collections.nCopies(50, "")) + "\n");
// 创建并启动扫描任务
currentTask = new ScanTask(
fileTree.getRoot().getValue(),
auditRadio.isSelected(),
auditJsCheckbox.isSelected(),
this::updateProgress,
this::updateStatus,
this::showResults
);
new Thread(currentTask).start();
}
private void updateProgress(double progress) {
javafx.application.Platform.runLater(() -> {
progressBar.setProgress(progress);
});
}
private void updateStatus(String message, javafx.scene.paint.Color color) {
javafx.application.Platform.runLater(() -> {
statusBar.setStatus(message);
String style = String.format("-fx-text-fill: %s;", color.toString().replace("0x", "#"));
Text text = new Text("" + message + "\n");
text.setStyle(style);
resultDisplay.appendText(text.getText());
resultDisplay.setStyle(style);
});
}
private void showResults(String report) {
javafx.application.Platform.runLater(() -> {
scanButton.setDisable(false);
if (auditRadio.isSelected()) {
String header = "\n📊 深度安全分析完成!统计信息:\n";
// 解析报告并生成统计信息
String[] lines = report.split("\n");
int highRiskIssues = 0;
int mediumRiskIssues = 0;
for (String line : lines) {
if (line.contains("[高危]")) highRiskIssues++;
if (line.contains("[中危]")) mediumRiskIssues++;
}
int totalIssues = highRiskIssues + mediumRiskIssues;
StringBuilder statisticsBuilder = new StringBuilder();
statisticsBuilder.append(String.format(
"总扫描文件数:%d\n" +
"高危问题数:%d\n" +
"中危问题数:%d\n" +
"问题总计:%d\n",
totalFileCount, highRiskIssues, mediumRiskIssues, totalIssues
));
resultDisplay.appendText(header + statisticsBuilder.toString());
} else {
resultDisplay.appendText("\n🎯 Webshell检测完成");
}
statusBar.setStatus("✅ 扫描完成");
// 添加完成动画
new Bounce(resultDisplay).play();
});
}
private void showAlert(String title, String content) {
Alert alert = new Alert(Alert.AlertType.WARNING);
alert.setTitle(title);
alert.setHeaderText(null);
alert.setContentText(content);
alert.showAndWait();
}
public static void main(String[] args) {
launch(args);
}
}

View File

@ -0,0 +1,11 @@
package com.cyberscanner;
@FunctionalInterface
public interface ProgressCallback {
void updateProgress(double progress);
}
@FunctionalInterface
interface MessageCallback {
void updateMessage(String message);
}

View File

@ -0,0 +1,301 @@
package com.cyberscanner;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.*;
import org.yaml.snakeyaml.Yaml;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.Consumer;
public class ScanTask implements Runnable {
private volatile boolean isRunning = true;
private final File rootDirectory;
private final boolean isAuditMode;
private final boolean includeJsFiles;
private final ProgressCallback progressCallback;
private final ColoredMessageCallback messageCallback;
private final Consumer<String> resultCallback;
private final OkHttpClient httpClient;
private final ObjectMapper objectMapper;
private String ollamaHost;
private String ollamaModel;
public ScanTask(File rootDirectory, boolean isAuditMode, boolean includeJsFiles,
ProgressCallback progressCallback, ColoredMessageCallback messageCallback, Consumer<String> resultCallback) {
this.rootDirectory = rootDirectory;
this.isAuditMode = isAuditMode;
this.includeJsFiles = includeJsFiles;
this.progressCallback = progressCallback;
this.messageCallback = messageCallback;
this.resultCallback = resultCallback;
this.httpClient = new OkHttpClient.Builder()
.connectTimeout(30, java.util.concurrent.TimeUnit.SECONDS)
.writeTimeout(30, java.util.concurrent.TimeUnit.SECONDS)
.readTimeout(60, java.util.concurrent.TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build();
this.objectMapper = new ObjectMapper();
loadConfig();
}
private void loadConfig() {
try {
// 首先尝试从jar包同级目录读取config.yaml
File externalConfig = new File(new File(getClass().getProtectionDomain()
.getCodeSource().getLocation().toURI()).getParent(), "config.yaml");
InputStream inputStream;
if (externalConfig.exists()) {
inputStream = new FileInputStream(externalConfig);
} else {
// 如果外部配置不存在尝试从jar包内部读取
inputStream = getClass().getResourceAsStream("/config.yaml");
if (inputStream == null) {
// 如果两个位置都没有配置文件显示错误对话框
javafx.application.Platform.runLater(() -> {
javafx.scene.control.Alert alert = new javafx.scene.control.Alert(javafx.scene.control.Alert.AlertType.ERROR);
alert.setTitle("配置错误");
alert.setHeaderText("找不到配置文件");
alert.setContentText(String.format("请确保在程序目录 %s 下存在 config.yaml 配置文件。\n\n" +
"配置文件示例内容:\n" +
"api:\n" +
" ollama:\n" +
" url: http://localhost:11434/api\n" +
" model: deepseek-coder",
externalConfig.getParent()));
alert.showAndWait();
System.exit(1);
});
return;
}
}
Yaml yaml = new Yaml();
Map<String, Object> config = yaml.load(inputStream);
Map<String, Object> api = (Map<String, Object>) config.get("api");
Map<String, Object> ollama = (Map<String, Object>) api.get("ollama");
String ollamaUrl = (String) ollama.get("url");
this.ollamaHost = ollamaUrl.split("/api")[0];
this.ollamaModel = (String) ollama.get("model");
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
// 显示错误对话框
javafx.application.Platform.runLater(() -> {
javafx.scene.control.Alert alert = new javafx.scene.control.Alert(javafx.scene.control.Alert.AlertType.ERROR);
alert.setTitle("配置错误");
alert.setHeaderText("配置文件读取失败");
alert.setContentText("请检查config.yaml文件格式是否正确。\n\n错误信息" + e.getMessage());
alert.showAndWait();
System.exit(1);
});
}
}
public void stop() {
isRunning = false;
}
@Override
public void run() {
Map<String, String> filesContent = scanCodeFiles(rootDirectory);
List<String> results = new ArrayList<>();
int totalFiles = filesContent.size(); // 使用实际扫描的文件数量
int processedFiles = 0;
int detectedFiles = 0; // 新增检测到问题的文件数量
// 初始化进度条
javafx.application.Platform.runLater(() -> {
progressCallback.updateProgress(0.0);
});
for (Map.Entry<String, String> entry : filesContent.entrySet()) {
if (!isRunning) {
messageCallback.updateMessage("⚠️ 扫描任务已中断", javafx.scene.paint.Color.web("#FFB86C"));
return;
}
String filepath = entry.getKey();
String content = entry.getValue();
try {
String filename = new File(filepath).getName();
messageCallback.updateMessage(
isAuditMode ?
String.format("🔍 分析 %s... (%d/%d)", filename, processedFiles + 1, totalFiles) :
String.format("🕵️ 扫描 %s... (%d/%d)", filename, processedFiles + 1, totalFiles),
javafx.scene.paint.Color.DODGERBLUE);
String prompt = createPrompt(content);
String response = callOllamaAPI(prompt);
String result = processResponse(response);
// 如果检测到问题包含[高危]标记增加检测文件计数
if (!isAuditMode && result.contains("[高危]") && !results.stream().anyMatch(r -> r.contains(filepath))) {
detectedFiles++;
}
// 根据扫描结果中的风险等级设置不同的颜色
String formattedResult = String.format("%s %s\n%s\n%s",
isAuditMode ? "📄" : "📁",
filepath,
result,
String.join("", Collections.nCopies(50, "")));
// 根据结果内容设置不同的颜色
javafx.scene.paint.Color messageColor;
if (result.contains("[高危]")) {
messageColor = javafx.scene.paint.Color.web("#FF4444");
} else if (result.contains("[中危]")) {
messageColor = javafx.scene.paint.Color.web("#FFB86C");
} else if (result.contains("[低危]")) {
messageColor = javafx.scene.paint.Color.web("#50FA7B");
} else {
messageColor = javafx.scene.paint.Color.web("#8BE9FD");
}
messageCallback.updateMessage(formattedResult, messageColor);
results.add(formattedResult);
// 更新进度
processedFiles++;
double progress = (double) processedFiles / totalFiles;
javafx.application.Platform.runLater(() -> {
progressCallback.updateProgress(progress);
});
} catch (Exception e) {
String errorMessage = String.format("❌ 错误:%s\n%s", filepath, e.getMessage());
messageCallback.updateMessage(errorMessage, javafx.scene.paint.Color.web("#6272A4"));
results.add(errorMessage);
}
}
// Webshell检测模式下不在这里添加统计信息统计信息由UI层处理
resultCallback.accept(String.join("\n", results));
}
private Map<String, String> scanCodeFiles(File directory) {
Map<String, String> codeFiles = new HashMap<>();
List<String> allowedExt = new ArrayList<>(Arrays.asList(
".php", ".jsp", ".jspx", ".asp", ".aspx", ".js", ".html", ".py", ".java"
));
if (!includeJsFiles) {
allowedExt.remove(".js");
allowedExt.remove(".html");
}
scanDirectory(directory, codeFiles, allowedExt);
return codeFiles;
}
private void scanDirectory(File directory, Map<String, String> codeFiles, List<String> allowedExt) {
File[] files = directory.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
scanDirectory(file, codeFiles, allowedExt);
} else {
String extension = getFileExtension(file);
if (allowedExt.contains(extension.toLowerCase())) {
try {
String content = readFile(file);
// 使用相对路径存储文件
String relativePath = rootDirectory.toPath().relativize(file.toPath()).toString();
codeFiles.put(relativePath, content);
} catch (IOException e) {
String relativePath = rootDirectory.toPath().relativize(file.toPath()).toString();
codeFiles.put(relativePath, "无法读取文件内容");
}
}
}
}
}
}
private String getFileExtension(File file) {
String name = file.getName();
int lastIndexOf = name.lastIndexOf(".");
return lastIndexOf == -1 ? "" : name.substring(lastIndexOf);
}
private String readFile(File file) throws IOException {
StringBuilder content = new StringBuilder();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
content.append(line).append("\n");
}
}
return content.toString();
}
private String createPrompt(String content) {
if (isAuditMode) {
return String.format("【强制指令】你是一个专业的安全审计AI请按以下要求分析代码\n\n" +
"1. 漏洞分析流程:\n" +
" 1.1 识别潜在风险点SQL操作、文件操作、用户输入点、文件上传漏洞、CSRF、SSRF、XSS、RCE、OWASP top10等漏洞\n" +
" 1.2 验证漏洞可利用性\n" +
" 1.3 按CVSS评分标准评估风险等级\n\n" +
"2. 输出规则:\n" +
" - 仅输出确认存在的高危/中危漏洞\n" +
" - 使用严格格式:[风险等级] 类型 - 位置:行号 - 50字内描述\n" +
" - 禁止解释漏洞原理\n" +
" - 禁止给出修复建议\n" +
" - 如果有可能给出POCHTTP请求数据包\n\n" +
"3. 输出示例(除此外不要有任何输出):\n" +
" [高危] SQL注入 - user_login.php:32 - 未过滤的$_GET参数直接拼接SQL查询\n" +
" [POC]\nPOST /login.php HTTP/1.1\n" +
" Host: example.com\n" +
" Content-Type: application/x-www-form-urlencoded\n" +
" [中危] XSS - comment.jsp:15 - 未转义的userInput输出到HTML\n" +
" [POC]\nPOST /login.php HTTP/1.1\n" +
" Host: example.com\n" +
" Content-Type: application/x-www-form-urlencoded\n\n" +
"4. 当前代码(仅限分析):\n%s", content.substring(0, Math.min(content.length(), 3000)));
} else {
return String.format("【Webshell检测指令】请严格按以下步骤分析代码\n\n" +
"1. 检测要求: \n" +
" 请分析以下文件内容是否为WebShell或内存马。要求\n" +
" 1. 检查PHP/JSP/ASP等WebShell特征如加密函数、执行系统命令、文件操作\n" +
" 2. 识别内存马特征(如无文件落地、进程注入、异常网络连接)\n" +
" 3. 分析代码中的可疑功能(如命令执行、文件上传、信息收集)\n" +
" 4. 检查混淆编码、加密手段等规避技术\n\n" +
"2. 判断规则:\n" +
" - 仅当确认恶意性时报告\n" +
" - 输出格式:🔴 [高危] Webshell - 文件名:行号 - 检测到[特征1+特征2+...]\n\n" +
"3. 输出示例(严格按照此格式输出,不要有任何的补充,如果未检测到危险,则不输出,除此之外,不要有任何输出):\n" +
" 🔴 [高危] Webshell - malicious.php:8 - 检测到[system执行+base64解码+错误抑制]\n\n" +
"4. 待分析代码:\n%s", content.substring(0, Math.min(content.length(), 3000)));
}
}
private String callOllamaAPI(String prompt) throws IOException {
MediaType JSON = MediaType.get("application/json; charset=utf-8");
Map<String, Object> requestMap = new HashMap<>();
requestMap.put("model", ollamaModel);
requestMap.put("prompt", prompt);
requestMap.put("stream", false);
RequestBody body = RequestBody.create(JSON, objectMapper.writeValueAsString(requestMap));
Request request = new Request.Builder()
.url(ollamaHost + "/api/generate")
.post(body)
.build();
try (Response response = httpClient.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("请求失败: " + response);
JsonNode jsonResponse = objectMapper.readTree(response.body().string());
return jsonResponse.get("response").asText();
}
}
private String processResponse(String response) {
return response.replaceAll("<think>.*?</think>", "");
}
}

View File

@ -0,0 +1,97 @@
package com.cyberscanner;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.geometry.Insets;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.effect.Glow;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.util.Duration;
public class StatusBar extends HBox {
private final Label statusLabel;
private final Label progressLabel;
private final ProgressBar progressBar;
private Timeline progressTimeline;
public StatusBar() {
setSpacing(10);
setPadding(new Insets(5));
getStyleClass().add("status-bar");
// 创建状态文本标签
statusLabel = new Label("就绪");
statusLabel.setFont(Font.font("System", 14));
statusLabel.setTextFill(Color.valueOf("#4d4dff"));
statusLabel.getStyleClass().add("status-label");
statusLabel.setWrapText(true);
statusLabel.setMaxWidth(Double.MAX_VALUE);
// 添加发光效果
Glow glow = new Glow(0.6);
statusLabel.setEffect(glow);
// 创建进度条
progressBar = new ProgressBar();
progressBar.setProgress(0);
progressBar.setPrefWidth(200);
progressBar.getStyleClass().add("status-progress");
progressBar.setVisible(false);
// 创建进度百分比标签
progressLabel = new Label("0%");
progressLabel.setFont(Font.font("System", 12));
progressLabel.setTextFill(Color.valueOf("#4d4dff"));
progressLabel.setVisible(false);
// 设置布局
getChildren().addAll(statusLabel, progressBar, progressLabel);
HBox.setHgrow(statusLabel, Priority.ALWAYS);
}
public void setStatus(String text) {
statusLabel.setText(text);
}
public void setProgress(double progress) {
// 确保进度条和百分比标签可见
if (!progressBar.isVisible()) {
progressBar.setVisible(true);
progressLabel.setVisible(true);
}
// 取消之前的动画如果存在
if (progressTimeline != null) {
progressTimeline.stop();
}
// 创建平滑动画
progressTimeline = new Timeline(
new KeyFrame(Duration.ZERO,
new KeyValue(progressBar.progressProperty(), progressBar.getProgress())),
new KeyFrame(Duration.millis(500),
new KeyValue(progressBar.progressProperty(), progress))
);
progressTimeline.setOnFinished(event -> {
// 更新百分比标签
int percentage = (int) (progress * 100);
progressLabel.setText(percentage + "%");
});
progressTimeline.play();
}
public void reset() {
statusLabel.setText("就绪");
progressBar.setProgress(0);
progressLabel.setText("0%");
progressBar.setVisible(false);
progressLabel.setVisible(false);
}
}

View File

@ -0,0 +1,42 @@
# API配置
api:
type: ollama # 可选值: "deepseek" 或 "ollama"
# DeepSeek API配置
deepseek:
# 官方默认API地址: "https://api.deepseek.com/v1/chat/completions"
# 硅基流动https://api.siliconflow.cn/v1/chat/completions
url: ""
api_key: ""
# DeepSeek模型名称官方默认模型: "deepseek-chat"
# 硅基流动deepseek-ai/DeepSeek-V3
model: ""
# Ollama API配置
ollama:
url: "http://x.x.x.x/api/chat" # Ollama API地址
model: "qwen2.5:7b" # Ollama模型名称
# 主题配色方案
themes:
dark:
main_bg: "#1a1a2e"
secondary_bg: "#16213e"
text_color: "#e4e4e4"
accent_color: "#4d4dff"
border_color: "#7b2cbf"
button_hover: "#00b4d8"
button_pressed: "#0096c7"
gradient_start: "#2b2b4b"
gradient_end: "#1a1a2e"
neon_glow: "#4d4dff"
highlight: "#7b2cbf"
light:
main_bg: "#f5f5f5"
secondary_bg: "#ffffff"
text_color: "#333333"
accent_color: "#2196f3"
border_color: "#e0e0e0"
button_hover: "#1976d2"
button_pressed: "#1565c0"

View File

@ -0,0 +1,208 @@
.main-layout {
-fx-background-color: #282a36;
-fx-text-fill: #f8f8f2;
}
.left-panel {
-fx-background-color: #44475a;
-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.3), 10, 0, 0, 0);
-fx-min-width: 300;
-fx-pref-width: 300;
}
.cyber-button, .scan-button {
-fx-background-color: #6272a4;
-fx-text-fill: #f8f8f2;
-fx-font-size: 14px;
-fx-padding: 10px 20px;
-fx-background-radius: 5px;
}
.cyber-button:hover, .scan-button:hover {
-fx-background-color: #50fa7b;
-fx-text-fill: #282a36;
}
.cyber-button:hover {
-fx-background-color: #1a1a2e;
-fx-border-color: #00ffff;
-fx-effect: dropshadow(gaussian, #00ffff88, 20, 0, 0, 0);
}
.path-label {
-fx-text-fill: #cc66ff;
-fx-font-size: 10pt;
-fx-font-family: 'Consolas';
-fx-padding: 5;
}
.mode-box {
-fx-spacing: 5;
-fx-padding: 10;
-fx-border-color: #ff33ff;
-fx-border-width: 1;
-fx-border-radius: 5;
-fx-effect: dropshadow(gaussian, #ff33ff44, 10, 0, 0, 0);
}
.mode-label {
-fx-text-fill: #cc66ff;
-fx-font-size: 12pt;
-fx-font-family: 'Consolas';
-fx-font-weight: bold;
}
.jfx-radio-button {
-fx-text-fill: #cc66ff;
-fx-padding: 8;
}
.jfx-radio-button .radio {
-fx-background-color: #2b0052;
-fx-border-color: #ff33ff;
-fx-border-width: 2;
}
.jfx-radio-button:selected .radio .dot {
-fx-background-color: #ff33ff;
}
.jfx-check-box {
-fx-text-fill: #cc66ff;
-fx-padding: 8;
}
.jfx-check-box .box {
-fx-background-color: #2b0052;
-fx-border-color: #ff33ff;
-fx-border-width: 2;
}
.jfx-check-box:selected .box .mark {
-fx-background-color: #ff33ff;
}
.file-tree {
-fx-background-color: #44475a;
}
.file-tree .tree-cell {
-fx-background-color: #44475a;
-fx-text-fill: #f8f8f2;
}
.file-tree .tree-cell:selected {
-fx-background-color: #6272a4;
}
.file-tree .tree-cell {
-fx-background-color: transparent;
-fx-text-fill: #cc66ff;
-fx-padding: 5;
}
.file-tree .tree-cell:hover {
-fx-background-color: #2b0052;
}
.scan-button {
-fx-background-color: #4d0099;
-fx-text-fill: #ff33ff;
-fx-border-color: #ff33ff;
-fx-border-width: 2;
-fx-padding: 15;
-fx-font-size: 16pt;
-fx-font-family: 'Consolas';
-fx-font-weight: bold;
-fx-border-radius: 5;
-fx-background-radius: 5;
-fx-cursor: hand;
-fx-effect: dropshadow(gaussian, #ff33ff44, 20, 0, 0, 0);
}
.scan-button:hover {
-fx-background-color: #1a1a2e;
-fx-effect: dropshadow(gaussian, #00ffff88, 25, 0, 0, 0);
-fx-text-fill: #00ffff;
}
.scan-button:disabled {
-fx-background-color: #2b0052;
-fx-text-fill: #9933ff;
-fx-border-color: #4d0099;
-fx-effect: none;
}
.result-display {
-fx-font-family: "JetBrains Mono", "Consolas", monospace;
-fx-font-size: 14px;
-fx-background-color: #282a36;
-fx-text-fill: #f8f8f2;
-fx-padding: 10px;
}
.result-display .content {
-fx-background-color: #282a36;
}
.status-bar {
-fx-padding: 5px;
-fx-background-color: #44475a;
-fx-text-fill: #f8f8f2;
}
.progress-bar {
-fx-accent: #50fa7b;
}
.progress-bar .track {
-fx-background-color: #44475a;
}
.progress-bar .track {
-fx-background-color: #2b0052;
}
.progress-bar .bar {
-fx-background-insets: 0;
-fx-background-radius: 0;
-fx-effect: dropshadow(gaussian, #ff33ff44, 10, 0, 0, 0);
}
/* 滚动条样式 */
.scroll-bar:vertical,
.scroll-bar:horizontal {
-fx-background-color: #1a0033;
}
.scroll-bar:vertical .thumb,
.scroll-bar:horizontal .thumb {
-fx-background-color: #4d0099;
-fx-background-radius: 0;
}
.scroll-bar:vertical .thumb:hover,
.scroll-bar:horizontal .thumb:hover {
-fx-background-color: #6600cc;
}
.scroll-bar .increment-button,
.scroll-bar .decrement-button {
-fx-background-color: #2b0052;
-fx-border-color: #4d0099;
}
.scroll-bar .increment-button:hover,
.scroll-bar .decrement-button:hover {
-fx-background-color: #4d0099;
}
/* 动画效果 */
.cyber-glow {
-fx-effect: dropshadow(gaussian, #ff33ff44, 20, 0, 0, 0);
-fx-transition: all 0.3s ease;
}
.cyber-glow:hover {
-fx-effect: dropshadow(gaussian, #ff33ff88, 25, 0, 0, 0);
}