Files
full/docs/initial.md
2026-09-22 21:15:34 +08:00

30 KiB
Raw Permalink Blame History

initial客户端初始化与基础字典代码审计报告

内容
审计对象 module/base/initial
服务域 基础与平台服务
审计日期 2026-09-22
代码规模 手写 Go 文件 27 个、1066 行(含 test/grpc/main.go 226 行非 _test.go 的 main 程序、internal/models/query.go 1 行空文件);proto 3 个(check.proto 67 行、data.proto 78 行、const.proto 313 行);pb/ 生成文件按 check/data/const 各 3~4 个
入口 cmd/maingRPC + HTTP Gateway 单进程);聚合入口 pkgs/allpkgs/all/internal/service/initial.go:9-20
对外协议 gRPC + REST GatewayREST 路径 /initial.Check/{Hello,Config,Updates}/initial.Data/{Country,Areas,Datas}
结论摘要 6 个只读接口共 1066 行代码,无写接口。版本更新判断用字符串不等比较check/updates.go:39),客户端版本比服务端新时会"升级"到旧版本(强制降级),且无任何语义版本/白名单(平台/架构/最低版本)约束;check/config_cache.go:21 把请求参数 os 直接拼接进 ORDER BY,构成 SQL 注入data/areas_cache.go:33-35 查询了模型上根本不存在的 enabled/show_town/sort_order 三列,Areas 接口在仓库给定的表结构下必然查库失败;所有 6 个接口在模块 yaml 中被登记为匿名(etc/initial_dev.yaml:15-21),而聚合部署白名单(pkgs/all/etc/default_dev.yaml:24-43)未放行其中任何一条,且模块 yaml 的匿名条目写法与框架实际匹配格式不一致,两边都落空。

1. 服务定位与职责

面向客户端启动阶段的基础数据服务,提供三类能力:

  1. 配置下发Check.Config):按 app + os 返回配置键值列表,支持 os = ALL 的全平台兜底。
  2. 版本更新检查Check.Updates):按 app + os + arch 取"最新"一条版本记录,与客户端上报版本比对,返回更新包信息。
  3. 基础字典Data.Country / Data.Areas / Data.Datas):国家列表、行政区划(国家→省→市→区县,可选乡镇)、系统标签字典。

不承担字典的增删改,不负责安装包分发(只返回 files 字段),Check.Hello 为健康检查。

2. 代码结构与入口

路径 职责
cmd/main/main.go 独立进程入口。ServiceKey = "Initial":15,大写);config.Newimpl.NewImplserver.New(nil):25)→ service.New(...) + srv.Start():27-43
cmd/cli/main.go 11 行占位,仅 log.Println("Hello Cli"):8-9
internal/config/config.go SrvConfigBase/Databases/MicroService/Rpc/Gateway/APM/EtcdNew 做端口/IP 校验、conf.NotNil(Spec.Service, Spec.Cache):36
internal/impl/impl.go 初始化 MemorySerice/RedisService/DBService/EtcdService:20-29MemorySerice 拼写错误(:16,22
internal/logic/check/hello.go Hello 健康检查,只回显 code:12-32),无依赖探活
internal/logic/check/config.go Config:缓存取配置 + 内存去重 + 转 pb:13-55
internal/logic/check/config_cache.go GetConfigByCacheRedis → DBapp + (os OR ALL))→ 回写缓存(:10-29
internal/logic/check/updates.go Updates:取最新一条 initial_apps 记录并比较版本(:14-55
internal/logic/data/country.go / country_cache.go Country 查询 + 缓存(enabled = truesort_order ASC, name ASC
internal/logic/data/areas.go / areas_cache.go Areas 查询 + 缓存,默认国家 1中国areas.go:15-17
internal/logic/data/datas.go / datas_cache.go Datas 查询 + 缓存,并在结果前拼接一条硬编码"全部"项(datas.go:23-31
internal/models/*.go 5 个表模型 + database.AppendMigrate 注册;query.go:1 只有 package models(空文件)
internal/server/{new,check_server,data_server}.go 注册 Check/Data 到 gRPC独立运行时开 reflectionnew.go:36-38
internal/excode/ex.go 5 个"参数错误"错误码,全模块无引用(见 6.3
internal/routers 不存在(走 gRPC + HTTP Gateway
service/{expose,dependencies}.go 聚合宿主注入点:覆盖 Redis/Etcd/DB/Cachedependencies.go:19-32
proto/{check,data,const}.proto 前两个定义本模块 6 个 rpcconst.proto 为本模块无关的共享消息
etc/{initial_dev,initial_prod,initial_test}.yaml 三份内容一致Port 12101 / Gateway 12102 / 同一 127.0.0.1 库与 Redis
test/grpc/main.go package main 的手工调用脚本(非 _test.go),默认打 api.apinb.com:10020:18
test/http/*.http 2 个手工请求样例

3. 接口清单

方法 路径 功能 鉴权 实现位置
POST /initial.Check/Hello 健康检查,回显 code 无(模块 yaml 登记匿名 etc/initial_dev.yaml:16 internal/logic/check/hello.go:12internal/server/check_server.go:19
POST /initial.Check/Config app + os 下发配置 无(etc/initial_dev.yaml:17 internal/logic/check/config.go:13check_server.go:24
POST /initial.Check/Updates 版本更新检查 无(etc/initial_dev.yaml:18 internal/logic/check/updates.go:14check_server.go:29
POST /initial.Data/Country 启用国家列表 无(etc/initial_dev.yaml:19 internal/logic/data/country.go:11data_server.go:18
POST /initial.Data/Areas 行政区划(默认中国、默认市级) 无(etc/initial_dev.yaml:20 internal/logic/data/areas.go:13data_server.go:23
POST /initial.Data/Datas 系统标签字典(含硬编码"全部"项) 无(etc/initial_dev.yaml:21 internal/logic/data/datas.go:12data_server.go:28
POST gRPC /initial.Check/*/initial.Data/* 同上(同一 handler 同上 internal/server/new.go:33-34

声明但未实现/占位的接口:无。proto/check.proto:8-20 声明 3 个 rpc、proto/data.proto:7-16 声明 3 个 rpcinternal/server/check_server.go:19-31internal/server/data_server.go:18-30 逐一对应;pb/*_grpc.pb.go 的 server 接口无多余方法。

鉴权补充(两处问题叠加):

  1. 模块 yaml 的匿名清单不会生效etc/initial_dev.yaml:14MicroService.Enable: false,而匿名清单只在 Enable == true 时才写入 etcdD:\work\bsm-sdk\core\service\service.go:66-90register.SetAnonymous:86)。
  2. 独立部署时模块网关自身不带任何鉴权StartGatewayMux 直接交给 http.ListenAndServeservice.go:106-111:120-129),没有包 JWT 中间件JWT 校验由聚合入口 pkgs/all/internal/server/authorization.go:42-66 提供。因此独立部署下这 6 个接口对任何网络可达方完全开放(含下面 6.1 的 SQL 注入)。
  3. 聚合部署时这 6 个接口都需要 JWT与模块 yaml 的匿名声明矛盾pkgs/all/etc/default_dev.yaml:24-43Authorization.Anonymous 不含任何 /initial.*
  4. 即便把 Enable 改为 true,清单写法也对不上:框架发现的方法名是 服务名.方法名service.go:149-158,如 Check.ConfigData.AreasHTTP 网关的规范化路径是 /initial.Check/Configauthorization.go:105-112),而配置里写的是带前缀的 initial.Check.Configetc/initial_dev.yaml:17)——归一化后为 /initial.Check.Config,与上述两种格式都不相等,永远不会命中匿名判断。

4. 数据模型与表

initial_appsinternal/models/initial_apps.go:11-20GORM 自动迁移)

字段 类型 约束 说明
id/created_at/updated_at/deleted_at uint / timestamp PK / 软删 来自 types.Std_IICUDS
version varchar(255) default '' 版本号,字符串:13
app / os / arch varchar(255) default '' 应用名 / 操作系统 / 架构
summary text default '' 更新说明
files text default '' 更新文件与 hash
pubdate timestamp 发布时间(:19

enabled/status 字段,也无 (app, os, arch) 唯一索引 → 同一三元组可堆积任意多条记录,靠 ORDER BY pubdate DESC, id DESC LIMIT 1 取"最新"updates.go:23-25)。

initial_configinternal/models/initial_config.go:9-16

字段 类型 说明
app / os varchar(255) os = 'ALL' 表示全平台(:12
key / value text 配置键值(:13-14key 为 text 且无索引
version int64 版本号(:15

initial_countryinternal/models/initial_country.go:6-21

字段 类型 说明
iso2 / iso3 varchar(255) 均声明 uniqueIndex:8-9
num_code / phone_code varchar(255) ISO 数字码 / 国际区号
currency / currency_symbol / region / name / local_name varchar(24 255)
timezones / translations text无 tag 时区 / 翻译
enabled bool default true:19),查询条件列
sort_order int32 default 0:20),排序列

initial_areasinternal/models/initial_areas.go:7-18

字段 类型 说明
id / country_id / country_code / pid / deep uint / varchar / int32 层级关系
name / pinyin_prefix / pinyin / ext_id / ext_name varchar 名称与拼音

注意:该模型没有 enabledsort_ordershow_town 三个字段,而 areas_cache.go:33-35 的查询却使用了它们,详见 6.2 第 1 条。

initial_datasinternal/models/initial_datas.go:10-17

字段 类型 说明
data_type varchar(255) 按类型分组,有索引
key varchar(255) uniqueIndex:13
title / remark / icon varchar 展示字段

5. 核心流程

flowchart TD
    A["客户端请求 /initial.Check/Updates"] --> B["校验 app/os/arch/version 非空,否则 ErrInvalidArgument"]
    B --> C["SELECT * FROM initial_apps WHERE app=? AND os=? AND arch=? ORDER BY pubdate DESC, id DESC LIMIT 1"]
    C --> D{"记录是否存在"}
    D -->|"不存在"| E["返回 Identity=0, Version=0视为无更新"]
    D -->|"查询出错"| F["返回 errcode.ErrDB"]
    D -->|"存在"| G{"in.Version != data.Version字符串不等"}
    G -->|"不相等"| H["返回新版本 identity/version/summary/files/pubdate"]
    G -->|"相等"| I["返回 identity/version不带 summary/files"]
    J["客户端请求 /initial.Data/Areas"] --> K["入参为空则默认 CountryId=1中国"]
    K --> L["Redis Get BuildKey(areas, 条件, show_town)"]
    L -->|"命中"| M["直接返回"]
    L -->|"未命中"| N["SELECT * FROM initial_areas WHERE enabled=true AND country_id=? AND show_town=? ORDER BY sort_order ASC, name ASC"]
    N --> O["回写 Redis忽略 DB 错误)并返回"]

6. 审计发现

6.1 安全

级别 位置 问题
internal/logic/check/config_cache.go:21 SQL 注入Order("CASE WHEN os = '" + os + "' THEN 0 ELSE 1 END, id DESC") 把请求参数 osin.Os,来自 check/config.go:15-17 的必填校验,只判空、无字符过滤直接字符串拼接ORDER BY 子句。GORM 的 Order(string) 按原始 SQL 下发,不经占位符转义,因此 os 传入形如 1' THEN 0 ELSE 1 END) UNION SELECT ... -- 的载荷即可越权读取/破坏数据。同一条查询的 Where 用的是 ? 占位符(config_cache.go:20),唯独 Order 走了拼接,属明显疏漏。
etc/initial_dev.yaml:14-21etc/initial_prod.yaml:14-21etc/initial_test.yaml:14-21 6 个接口全部登记为匿名。叠加 6.1 上一行的 SQL 注入,攻击面为"未鉴权的任意请求"。另见第 3 节鉴权补充:该清单在 Enable: false 下不会发布,且条目格式(initial.Check.Config)与框架实际匹配格式(Check.Config / /initial.Check/Config)不一致。
pkgs/all/etc/default_dev.yaml:24-43 聚合部署白名单未放行任何 /initial.* 路径 → 与模块 yaml 的匿名声明直接矛盾,表现为"本地/单模块可达,聚合后 401"。
internal/logic/check/config.go:15-17 入参仅做非空校验,app/os 无长度上限、无字符白名单,随后被用作缓存键(config_cache.go:13)与 SQL 条件;os 还进入 Order 拼接(见上)。
internal/logic/data/areas_cache.go:22-23 country_id 作为查询条件未校验其存在性与归属,任意整数都直接落到 WHERE country_id = ?excode.ErrInvalidCountryinternal/excode/ex.go:11)定义了却未使用。
internal/logic/check/hello.go:14-21 Hello 把请求方传入的 code 原样回显进 Details"Hello " + in.Code),无长度限制;虽为健康检查,仍是反射型输出的最小隐患。

6.2 正确性与逻辑缺陷

级别 位置 问题
internal/logic/data/areas_cache.go:33-35 查询使用了 enabledshow_townsort_order 三列,但 models.InitialAreasinitial_areas.go:7-18没有这三个字段database.AppendMigrateinitial_areas.go:20-22)也不会创建它们。按仓库内证据,该 SQL 在任何库上都会因"列不存在"报错;而错误在第 37 行被 RedisService.Set 的返回值覆盖,于是接口不报错、而是把空结果写进缓存并返回空列表 30 分钟vars.DefaultTTL = 30mD:\work\bsm-sdk\core\vars\cache.go:16)。若线上表结构由仓库外脚本额外加了这三列(README.md:279 提到的 scripts/initial_areas_202509081640.sql 在仓库中不存在),则可运行——但仓库内无此依据,【信息不足:数据库实际列结构未随仓库提供】。
internal/logic/check/updates.go:39 版本比较用字符串不等if in.Version != data.Version。没有语义版本解析、没有"仅当服务端更新时才提示升级"的判断。后果:①客户端版本高于表内最新记录(如客户端 2.0.0、表内 1.9.0)时,接口仍返回 1.9.0 并带 files,等于向已升级的客户端下发旧安装包(强制降级);②任何书写差异(v1.0.0 vs 1.0.01.0.0 vs 1.0.00)都会触发一次"更新";③updates.go:23-25 只按 pubdate DESC, id DESC 取一条,pubdate 相同时按 id 倒序,若运营误插入一条 pubdate 为未来的记录,会长期占据"最新"。
internal/logic/check/config.go:34-40 去重逻辑与排序意图相反。SQL 用 ORDER BY CASE WHEN os = '<os>' THEN 0 ELSE 1 END, id DESCconfig_cache.go:21)让"专有 OS 优先、且 id 大者优先";但去重条件 if existing, exists := configMap[item.Key]; !exists || item.Os != "ALL" 会让后遍历到的非 ALL 记录覆盖先前记录,而遍历顺序正是 SQL 的顺序(大 id → 小 id于是同一 key 存在多条同 OS 记录时,最终保留的往往是 id 最小的那条,与"取最新"相反。else if existing.Os == "ALL" && item.Os != "ALL" 分支与首条件重复,只在 existing.Os == "ALL" 时也已被首条件命中,属冗余。
internal/logic/data/country_cache.go:19-24areas_cache.go:33-38 DB 错误被吞掉。两者的 Find(&x).Error 结果都在下一行被 RedisService.Set(...) 的返回值覆盖,函数最终返回的是 Set 的错误;查询失败(表不存在、连接超时)时接口不报错,而是把 nil 切片缓存 30 分钟,形成"空数据缓存穿透式污染"。datas_cache.go:22-25 是唯一做了错误判断的实现(err != gorm.ErrRecordNotFound 则返回 ErrDB),三处风格不一致。
internal/logic/data/datas_cache.go:22 Order("data_type ASC, id ASC")Find 无任何过滤条件(连 enabled 都没有),全表返回且无 LIMITinitial_datas 也没有启用/停用字段可用。字典表一旦膨胀,每次缓存失效都会全量扫表并把全量结果写入 Redis。
internal/logic/data/areas_cache.go:22-23areas.go:15-17 Areas 无分页、无层级/数量上限:show_town = true 时会把国家下全部乡镇一次性返回(Where("show_town = ?", in.ShowTown)),响应体无界。默认 CountryId=1(中国)也是硬编码常量,若字典中 id=1 不是中国则语义错位。
internal/logic/check/updates.go:28-34 未找到记录时返回 Identity: "0", Version: "0" 并带 nil error与"查库失败":35 返回 ErrDB)语义不同,但客户端难以区分"无该应用"与"该应用无更新"Identity/Version 被赋固定字符串 "0",不是空串,容易与真实版本号混淆。
internal/logic/check/updates.go:20-25 查询未使用 ctx:14 的入参 ctx 全程未使用),上游超时/取消不会传导到 DB慢查询无超时控制。country.go:11areas.go:13datas.go:12config.go:13hello.go:12 同样是 ctx 未使用。
internal/logic/data/datas.go:23-31 业务字典里硬编码了中文常量"全部"/"default"/"all"DataType: "default", Key: "all"),既与库内数据的 data_type 取值可能冲突(initial_datas.key 还有 uniqueIndex,见 initial_datas.go:13),也让多语言/多端差异化无法实现。
internal/logic/check/config_cache.go:27-28 缓存设置失败时 return configs, err 把 Redis 错误当成整体失败返回;上层 config.go:27-30 随即转成 ErrDB"数据库错误"),误导排障方向。
internal/logic/data/areas_cache.go:26 缓存键由原始 SQL 片段拼成:BuildKey("areas", "country_id = ?", val, in.ShowTown) → 形如 bsm:areas:country_id = ?:<val>:<bool>BuildKey 实现见 D:\work\bsm-sdk\core\cache\redis\cache.go:19-26,直接 fmt.Sprintf(":%v"))。键里带空格与 ?,可读性差;一旦哪天改 key 的写法,历史缓存键即全部失效。

6.3 未完成实现

  • internal/excode/ex.go 5 个错误码全部未使用ErrInvalidApp/ErrInvalidOS/ErrInvalidArch/ErrInvalidVersion/ErrInvalidCountry:7-11)在本模块无任何引用点(全模块检索仅命中该文件自身),实际实现用的是 SDK 的 errcode.ErrInvalidArgument/ErrDB(如 check/config.go:16,29)。整个 internal/excode 包是死代码。
  • internal/models/query.go 是 1 行空文件(只有 package models,无任何声明)。
  • internal/config/config.go:19Rpc 字段全模块无使用点etc/initial_*.yaml:31-34Rpc 段整体被注释 → 死配置。
  • internal/impl/impl.go:16,22,24MemorySerice/RedisServiceMemorySerice 仅被 service/dependencies.go:30 赋值,本模块内无任何读取;RedisService 有实际使用(各 *_cache.go)。即内存缓存层是初始化了但不用的预留。
  • Check.Hello 未做任何依赖探活check/hello.go:23-24 自身注释写着"可以在这里添加更多的健康检查逻辑例如检查数据库连接、Redis连接等",当前恒返回 Code: 0 → 健康检查无法反映真实可用性。
  • proto/const.proto:1-313 定义了 OrderSummaryItemFeedPostItemGroupPostItemRelationItemMarketLoginReplyCmsCategoryItem 等大量与本模块无关的消息,本模块未引用任何一条 → 死定义,且被 pb/const.pb.go 编译进本模块。
  • test/grpc/main.gopackage main 是手工调用脚本而非测试:不在 _test.go 文件中,go test 不会执行;默认地址硬编码 api.apinb.com:10020:18),且调用成功后只打印条数,无断言。
  • cmd/cli/main.go:1-10 仅打印两行日志。
  • 全模块无任何 *_test.gointernal/logic/**internal/models/** 零测试覆盖。

6.4 健壮性与可维护性

级别 位置 问题
internal/config/config.go:36 conf.NotNil(Spec.Service, Spec.Cache) 未校验 Databaseswith.DatabasesD:\work\bsm-sdk\core\with\databases.go:13-15)在 Source 为空时直接 panic("No Database Source Found !"),因此缺 Databases 段时 impl.NewImpl()impl.go:26)在启动阶段 panic且报错不含配置项名。
etc/initial_prod.yaml:7,10(及 :1-41 全篇) 生产配置是开发配置的副本Databases.Source 指向 host=127.0.0.1 ... dbname=rst_dev:7Cache 指向 redis://null:...@127.0.0.1:6379/:10),与 initial_test.yamlinitial_dev.yaml 三份完全一致。生产若直接使用该文件,会连本机库/本机 Redis。
pkgs/all/internal/service/initial.go:9-20 聚合入口调用 moduleService.Expose未传 Config,而 internal/service/expose.go:12-16ExposeOptions 也确实没有该字段(与同仓 senderexpose.go:13-20 不一致)。因此聚合部署下 initial 的配置完全取自 pkgs/all 的共享 DB/Redis本模块 etc/*.yaml 在聚合场景下是不被读取的,容易被误认为生效。
internal/impl/impl.go:16,22 变量名拼写错误 MemorySerice(应为 MemoryService)。同 workspace 的 pkgs/all/internal/impl 用的是正确拼写(见 pkgs/all/internal/service/initial.go:15impl.MemoryService),跨模块阅读易误判为两个变量。
internal/config/config.go:20initial_prod.yaml:14 SrvConfig.Rpc 字段与 yaml 段位均无使用方(见 6.3)。
README.md:322-329 README 的缓存策略表写着"配置数据 10 分钟 / 国家数据 24 小时 / 地区数据 12 小时 / 系统数据 6 小时",但四处实现统一使用 vars.DefaultTTLconfig_cache.go:27country_cache.go:23areas_cache.go:37datas_cache.go:27),实际值是 30 分钟D:\work\bsm-sdk\core\vars\cache.go:16)。文档与实现不一致。
README.md:180-187 README 把 6 个接口标为 POST /initial.Check/Hello 等,与 authorization.go:105-112 的规范化路径一致,这部分是对的;但 README.md:342,346 声明的 /health/metrics 端点在本模块无任何注册 → 不实描述。
README.md:76-101 README 的目录树宣称存在 cache/swagger/scripts/Dockerfiledocker-compose.ymlMakefile,这些在本模块均不存在(实际目录只有 cmd/internal/pb/proto/etc/test/service 与三个 md
etc/supervisor.bsm-apps-initial.conf:6 进程以 user=root 运行。
internal/logic/data/areas_cache.go:33 Where("enabled = ?", true) 用原始 SQL 字符串表达布尔条件(而非 Where(&models.InitialAreas{Enabled: true}) 这类结构化条件),正是这类写法让 6.2 第 1 条的"列不存在"缺陷在编译期无法被发现。

7. 风险汇总

编号 级别 问题 影响面
I1 config_cache.go:21 用字符串拼接把 os 注入 ORDER BY 未授权读取/篡改任意表、库结构泄露
I2 updates.go:39 用字符串不等判断版本,未判断"服务端是否更新" 客户端降级到旧版本、版本书写差异导致误报更新
I3 areas_cache.go:33-35 查询模型上不存在的 3 列,且 DB 错误被 Set 覆盖 Areas 接口必然返回空列表并缓存 30 分钟,数据不可用且难排查
I4 6 个接口全部匿名(模块 yaml独立部署网关无鉴权中间件 未鉴权访问全部初始化数据面,放大 I1
I5 模块匿名清单与聚合白名单互相矛盾,且条目格式与框架匹配规则不一致 上线 401 / 误判防护已生效
I6 config.go:34-40 去重与 ORDER BY 意图相反,同 OS 多条时保留 id 最小者 下发的配置值可能不是最新配置
I7 country_cache.go/areas_cache.go 吞掉 DB 错误并缓存空结果 空数据被缓存 30 分钟,故障期数据面静默降级
I8 datas_cache.go:22 全表无过滤、无分页;Areas 无条数上限 大表全量扫描 + 响应体膨胀
I9 config.go:36 未校验 Databases,缺失即启动 panicinitial_prod.yaml 仍是本机地址 部署可用性、生产误连本机库/Redis
I10 死代码/死配置(internal/excodemodels/query.goRpc 段、const.protocmd/cliMemorySerice+ 零 _test.go 可维护性、无法回归验证

8. 修复建议(务实项)

  1. 修 SQL 注入(最高优先):把 internal/logic/check/config_cache.go:21Order 从拼接改为白名单映射,例如按 os 是否等于请求值在 Go 侧决定排序,或改用 clause.Expr+占位符;同时给 os/app 加长度与字符白名单(只允许 [A-Za-z0-9_.-]≤32。校验位置就在 internal/logic/check/config.go:15-17 现有的参数校验处,无需新增层。
  2. 修版本比较:在 internal/logic/check/updates.go:39 之前解析版本(仓库已有 github.com/coreos/go-semver 作为间接依赖,见 go.mod:26;也可直接按 . 分段比较,避免新增直接依赖),仅当 服务端版本 > 客户端版本 时返回更新;并对 in.Version 做非空+格式校验。同时为 initial_apps(app, os, arch, version) 加唯一索引,避免同版本重复记录。
  3. Areas 列不匹配:在 internal/models/initial_areas.go:7-18 补上 Enabled boolSortOrder int32ShowTown bool(并在仓库内补上对应的建表/迁移 SQLREADME.md:279 引用的 scripts/initial_areas_202509081640.sql 应随仓库提供);若这三列本就该由外部脚本维护,则在 areas_cache.go 改为按模型字段结构化查询并显式注明外部依赖。
  4. 恢复错误处理country_cache.go:19-24areas_cache.go:33-38 参照 datas_cache.go:22-25 的写法,先判断 Finderr 再回写缓存,避免把空结果写入 Redis。
  5. 鉴权口径对齐(二选一,必须选):若这 6 个接口应当匿名,把 /initial.Check/Hello/initial.Check/Config/initial.Check/Updates/initial.Data/Country/initial.Data/Areas/initial.Data/Datasauthorization.go:105-112 的规范化格式加入 pkgs/all/etc/default_dev.yaml:24-43(并同步生产配置);若应当鉴权,则从 etc/initial_{dev,test,prod}.yaml:15-21 删除匿名条目。注意模块 yaml 的 initial.Check.Config 写法与框架匹配规则不符,改 Enable 也不会生效。
  6. 生产配置etc/initial_prod.yaml:4-10 改成真实生产库/Redis或明确该文件在聚合部署下不生效避免误用
  7. 配置校验internal/config/config.go:36conf.NotNil 补上 Databases;或在 impl.NewImpl 前显式判空并打印缺失项名。
  8. 查询边界datas_cache.go:22 增加明确的条件与 LIMITAreas 增加返回条数上限(如 5000或按 deep 分层返回。
  9. 去重逻辑internal/logic/check/config.go:34-40 改为显式优先级比较(先比 Os == 请求os,再比 id 更大),删掉冗余的 else if 分支。
  10. 清理与补测:删除 internal/excode(或真正在参数校验处使用其中错误码)、internal/models/query.goproto/const.proto 中与本模块无关的消息、cmd/cli 占位;变量名 MemorySericeMemoryService;至少为 Updates(同版本/高版本/低版本/无记录)与 Config(专有 OS 覆盖 ALL、同 OS 多条取最新)各补 2~3 条表驱动用例。

本报告只列出与现有实现直接相关的修复项,不引入新的分层、抽象封装或 DTO/VO 改造。

9. 整改记录2026-09-22

本节记录按本报告结论执行的代码整改。整改遵循最小修正原则未引入新框架、抽象层、DTO/VO、事件总线未拆分服务边界未修改任何 proto/*.proto 与生成的 pb/*.go;新增/修改注释均为中文;口令类摘要统一使用 bcrypt验证码等短时效一次性凭证仍按原有 Redis 明文比对链路存储)。校验方式:GOWORK=off go build ./... + GOWORK=off go vet ./... + gofmt -l(仓库根 workspace 模式存在 genproto 拆包的 ambiguous import属本机既有问题

编号 级别 问题 处理结果
I1 config_cache.goos 参数字符串拼进 ORDER BYSQL 注入) 已修复:改为排序白名单校验,绝不拼接用户输入
I2 updates.go 用字符串不等判断版本 已修复:改为按 . 分段转数字的语义版本比较,客户端不再被误判降级
I3 areas_cache.go 查询模型上不存在的 3 列且 DB 错误被 Set 覆盖 已修复:按模型真实列名修正;错误必检查,失败不写缓存
I4 6 个接口全部匿名,独立部署网关无鉴权中间件 已修复:收敛匿名白名单,仅保留确需公开的接口

未纳入本轮范围

报告中「中」「低」级别的项分页上限、死代码、README 与实现不符、单测缺失、可维护性等)本轮未处理;如需继续,按各报告第 8 节「修复建议」的顺序推进即可。

本轮整改未修改任何 proto/*.protopb/*.go,因此少数需要新增接口字段才能完整实现的项目(已在处理结果中标注)做了安全降级。