fix: prevent asset dedup key from collapsing distinct IPs under the same domain - #243
Open
keyblues wants to merge 2 commits into
Open
fix: prevent asset dedup key from collapsing distinct IPs under the same domain#243keyblues wants to merge 2 commits into
keyblues wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes incorrect deduplication during bulk asset import where the previous domain > ip > host single-identifier key could collapse distinct IPs under the same (registrable root) domain. It updates both frontend preview deduplication and backend persistence semantics, and adds an automatic migration to recompute legacy dedup_key values.
Changes:
- Update asset dedup key semantics to
host(fallback only) | ip | domain | port | protocol, preventing distinct IPs under the same domain from being merged. - Add startup migration to recompute existing
assets.dedup_keyvalues to the new format. - Improve UNIQUE-conflict UX by translating
dedup_keycollisions into a readable validation error; update docs/OpenAPI/MCP tool description and add regression tests.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| web/static/js/assets.js | Align frontend import preview dedup key with backend assetDedupKey (ip+domain, host fallback). |
| internal/handler/openapi.go | Update OpenAPI description to reflect new deduplication semantics. |
| internal/database/database.go | Add migration logic to recompute legacy dedup_key values. |
| internal/database/asset.go | Change assetDedupKey format; translate dedup UNIQUE conflicts for Update/Merge. |
| internal/database/asset_test.go | Add regression tests for distinct IPs under same domain, migration recompute, and conflict translation. |
| internal/app/asset_tools.go | Update MCP create_asset tool description to match new dedup semantics. |
| docs/zh-CN/asset-management.md | Update deduplication explanation for bulk import behavior (CN). |
| docs/zh-CN/api-reference.md | Update API deduplication rules wording (CN). |
| docs/zh-CN/api-recipes.md | Update recipe notes to new dedup semantics (CN). |
| docs/en-US/asset-management.md | Update deduplication explanation for bulk import behavior (EN). |
| docs/en-US/api-reference.md | Update API deduplication rules wording (EN). |
| docs/en-US/api-recipes.md | Update recipe notes to new dedup semantics (EN). |
Suppressed comments (1)
internal/database/asset.go:902
- UpdateAsset 中
if key == "|||0|" { ... }判断同样不可达:validateAsset 已保证 host/ip/domain 至少其一非空,因此 dedup_key 不会是全空形式。建议移除该判断,避免维护时误解这里可能返回非校验错误的 fmt.Errorf。
key := assetDedupKey(a)
if key == "|||0|" {
return fmt.Errorf("资产目标不能为空")
}
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
keyblues
force-pushed
the
fix/asset-import-dedup-key
branch
from
August 8, 2026 15:56
94727f2 to
1061316
Compare
…ame domain 批量导入资产时,去重键采用 domain > ip > host 优先级的单标识符设计。 FOFA 等测绘平台导出的数据中 domain 列通常是注册根域名,真实资产标识 在 host 列(子域名)和 ip 列。domain 存在时 ip/host 被完全忽略, 导致同根域名下的不同资产全部坍缩为同一个键,被大面积误判为重复行。 修复: - 去重键改为 ip + domain 联合标识(host 仅在 ip 与 domain 均缺失时 兜底,纯 URL 类资产),前后端规则保持一致;空资产哨兵同步更新。 - migrateAssetsTable 新增启动迁移:在 Go 侧调用 assetDedupKey 幂等 重算存量 dedup_key(单事务批量写回),覆盖 upgrade.sh 保留 data/ 的升级场景。新键区分度严格细于旧键,可证明不会撞 UNIQUE 约束。 - UpdateAsset/MergeAssets 中新语义引入的 dedup_key UNIQUE 冲突路径 转译为可读的 AssetValidationError,不再透出裸 SQLite 错误。 - 同步更新 MCP create_asset 工具描述、OpenAPI 描述及中英文文档。 - 新增 3 个回归测试:同域名不同 IP 保持独立、存量旧键迁移重算、 UNIQUE 冲突转译。
keyblues
force-pushed
the
fix/asset-import-dedup-key
branch
from
August 8, 2026 16:02
1061316 to
11401b9
Compare
- migrateAssetsTable: add a lightweight EXISTS probe comparing stored keys against the new-format expression, and skip the full-table recompute when all rows already match. Post-migration startups now pay only one cheap SQL scan instead of scanning every row into Go. The probe compares values rather than counting '|' separators because host is free-form text and may itself contain '|'. The probe cannot miss stale keys (normalizeAsset keeps ip/domain/protocol lowercase at write time); in the extreme case of host-fallback keys with non-ASCII uppercase it may only produce a write-free extra pass. - translateAssetDedupConflict: match the UNIQUE constraint error case-insensitively, consistent with other err.Error() substring checks in this repository. - Remove the unreachable "|||0|" empty-key guards in UpsertAssets and UpdateAsset: normalizeAsset + validateAsset already guarantee at least one of host/ip/domain is non-empty before the dedup key is computed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
问题描述
批量导入资产(XLSX/CSV)的去重逻辑对测绘平台导出格式(如 FOFA)的数据会产生大面积误判:文件中的 domain 列为注册根域名、真实资产标识为 host 列的子域名与 ip 列的 IP 时,大量行被错误标记为「与第 X 行重复」,仅有少量行判定有效;手动删除文件中的 domain 列后重新导入则基本全部有效。
根因
去重键采用
domain > ip > host优先级的单标识符设计(assetDedupKey,前后端各实现一份):domain 存在时 ip 和 host 被完全忽略。而测绘平台导出的数据中,
domain列通常是注册根域名,真实资产标识在host列(子域名)和ip列。于是同根域名下不同资产的键全部坍缩为根域名|端口|协议,被误判为重复。修复方案
去重键改为 ip + domain 联合标识:
host兜底|ip|domain|port|protocol,其中 host 仅在 ip 与 domain 均缺失时参与(纯 URL 类资产兜底),避免自由文本 host 带来的格式敏感。该设计:存量数据迁移
migrateAssetsTable新增启动迁移:在 Go 侧调用assetDedupKey本体幂等重算存量dedup_key(单事务批量写回),覆盖upgrade.sh保留data/的升级场景,无需改动升级脚本。安全性:新键区分度严格细于旧键(新键相同 ⇒ host/ip/domain/port/protocol 全等 ⇒ 旧键相同),而旧键受 UNIQUE 约束,故存量记录重算后不可能撞 UNIQUE 约束。Go 侧计算而非 SQL 表达式,避免了 SQLite 内置
lower()仅处理 ASCII 与 Go Unicode 小写化的偏差。附带修复
UpdateAsset/MergeAssets:新语义允许同 domain+port+protocol 不同 IP 的资产共存后,编辑标识字段使其与已共存资产完全一致会触发 UNIQUE 冲突(此前不可达)。现转译为可读的AssetValidationError,不再透出裸 SQLite 错误(HTTP 状态码不变)。create_asset工具描述、OpenAPI/api/assets/import描述及中英文文档共 8 处。测试
新增 3 个回归测试(数据库层测试需 CGO,请 CI 验证):
TestAssetUpsertKeepsDistinctIPsUnderSameDomain:同域名不同 IP 保持独立,完全相同行仍合并更新;TestAssetDedupKeyMigrationRecomputesLegacyKeys:旧格式键在重新打开数据库时被自动重算;TestUpdateAssetRejectsDedupKeyConflict:UNIQUE 冲突被转换为校验错误。本地已通过
go build ./...与go vet;迁移逻辑已用 SQLite 实测验证幂等性与正确性。