# 如何评估源代码可用的安全软件

能读到代码仓库，并不代表团队拥有开源软件所赋予的权利和独立运维能力。采用源代码可用的安全软件之前，应把许可证、发布流程和商业功能边界都当作安全架构的一部分。概念验证阶段看似无关紧要的一条限制，日后可能让你无法部署紧急修复、为客户提供服务，或在厂商改变方向后继续运行系统。

我见过有的团队花几周审查密码学设计，却只用半小时阅读保护密钥的软件许可证。这个顺序完全反了。审查必须回答一个具体问题：如果厂商、代码仓库和商业关系同时发生变化，组织还能合法并且实际地继续做什么？

这是一项需要法律人员参与的工程审查，不是让工程师充当律师。工程师必须说明软件如何实际运行、谁会接触它，以及故障恢复需要哪些操作。法律人员据此评估真实部署，而不是一句含糊的“源代码可见”。

## 许可证如何界定这款软件？

首先要准确判断许可证类别，因为“源代码可用”和“开源”授予的权利不同。公开代码只是交付方式。开源则是一个许可证层面的主张，它包含使用、修改和再分发软件的权利，并且不得歧视个人、群体或应用领域。

开放源代码促进会的《开放源代码定义》让这一区别变得明确。其标准要求提供源代码，也要求允许衍生作品、自由再分发，而且不得限制应用领域。即使某个许可证公开了每一行代码，只要它禁止生产使用、竞争性服务或某类商业活动，就不符合这一定义。

不要把“开源”当成“我们能查看仓库”的友好说法。记录许可证的准确名称、版本和标识符，再记录开放源代码促进会是否批准了它。SPDX 许可证列表有助于一致地识别许可证文本，但出现在列表中本身不代表获得批准。该列表单独标注 OSI 批准状态，也收录 BUSL-1.1 和 Elastic-2.0 这类未标为 OSI 批准的许可证。

这一区别不只是措辞问题。依赖扫描器可能会自动放行 Apache-2.0，却把自定义的源代码可用许可证送交人工审查。采购政策可能只有在再分发权明确时才允许修改。事件响应人员也可能以为团队能修补并部署一份可见的代码库，实际上许可证却禁止这种生产使用。

把结论写成一句任何人都无法事后弱化的话：“代码以[准确许可证]提供，该许可证[已/未]获 OSI 批准，我们计划的用途依赖[具体授权或限制]。”如果团队填不完这句话，第一轮审查就没有完成。

## 许可证是否允许我们的真实部署方式？

应对照部署图阅读具有约束力的许可证正文，不要只看厂商的摘要页面。“生产”“托管服务”“竞争性产品”“内部业务用途”和“授权用户”等术语，只有对应到具体进程、账户、客户和数据流之后才有意义。

把所有法律实体以及将使用软件或接收其功能的人都画进边界。母公司、子公司、承包商、托管服务提供商和客户在同一条款下可能处于不同位置。如果代理代表客户发起 API 调用，要问客户得到的是源代码可用产品本身的服务，还是仅得到你方产品的结果。不要用销售聊天中的一句回复来结束讨论。

Business Source License 1.1 说明了为什么必须查看已经填写的参数。其标准文本授予复制、修改、创作衍生作品、再分发和非生产使用的权利。许可方可以增加有限的生产授权，随后每个版本会在指定的 Change Date 或许可证规定的最迟期限转换为某个具名的开源许可证。因此，实际权限有一部分位于对应产品和版本的许可证头部。只阅读通用 BSL 说明，而不查看 Additional Use Grant 和 Change License，就漏掉了最重要的条款。

Elastic License 2.0 采用另一种结构。Elastic 自己的常见问题说明，该许可证允许使用、修改、创作衍生作品和再分发，但限制将产品作为托管服务提供、绕过许可证密钥功能以及删除声明。这并不能直接证明你的具体托管架构是否合规，只说明哪个边界必须得到书面答复。

至少用文字测试这些具体状态：内部评估、供员工使用的生产环境、支持付费客户的生产环境、承包商访问、随设备再分发、由另一法律实体执行灾难恢复，以及带本地改动的分支版本。每种状态都应记录“允许”“禁止”或“未解决”，并附上控制该结论的条款。“大多数用途免费”不算结论。

模糊措辞本身就是部署风险。请厂商针对你的部署图提供书面解释，再由法律人员判断这个答复是否足够。如果厂商愿意给予例外，应写入已经签署的协议，并注明涵盖哪些版本。论坛回复可以消失，你的义务不会消失。

要把著作权许可与交易中的其他部分分开。仓库许可证可能允许某种用途，而订阅协议、服务条款、商标政策、专利条款、出口条款或支持合同可能施加其他条件。收集所有被引用并纳入的文件，并确定文件冲突时以哪一份为准。账户负责人接受的点击协议应与 LICENSE 文件一起进入审查。

安全基础设施经常涉及身份验证、加密、网络和设备管理，因此专利条款值得关注。询问许可证是否明确授予专利许可、提出专利主张后该许可是否终止，以及贡献者是否有权授予这类许可。源代码可见本身不授予专利权。当产品用于分布式服务，或计划中的分支会改变软件工作方式时，应让法律人员评估这一点。

主仓库的许可证和依赖项要分别梳理。宽松的顶层许可证无法修复不兼容的库、禁止再分发的模型或规则集、带有打包限制的字体，或生产中必需的纯二进制辅助程序。根据将要发布的版本生成依赖许可证清单，再调查标记为 unknown、custom 或 NOASSERTION 的项目。如果部署必须使用某组件，“可选”这个标签没有帮助。

不要把合同承诺混进许可证一栏。支持响应目标、安全通知义务、源代码交付日期和价格保护也许会使采用决定可以接受，但它们通常只在限定期限内约束特定主体。许可证权利则可能随每份副本存在得更久。记录必须说明每项保护来自哪份文件，以及合同到期后会发生什么。

这种区分会揭穿一种常见的错误建议：先接受许可证，之后再让采购部门谈例外。它受欢迎，因为概念验证可以迅速推进。但一旦真实数据、客户或自动化依赖该软件，这个办法就错了，因为此时厂商已经知道你的切换成本。在技术集成造成这种压力之前，就应解决必需的权利。

## 我们能否检查、构建、修补并发布同一套软件？

代码可见有助于检查，但真正承担安全责任需要一条更长的权利和能力链。团队需要确认能否获得完整源代码、复现相关制品、修改代码、测试改动、部署修改后的构建，并在恢复需要的任何地方分发它。

厂商经常公开一个有用的核心，却不公开构建基础设施、签名步骤、生成文件、高级模块，或正式版本使用的打包流程。这样的仓库仍可帮助研究人员理解解析器或检查密码学调用，但如果已发布的二进制依赖未公开部分，它就无法支撑紧急分支。

在一台没有任何开发者私有缓存的机器上执行干净构建。固定提交或标签，保存命令，并把生成的包与厂商版本比较。如果项目支持，逐字节复现当然最好；有文档解释的差异也可以接受。对于受信任来处理凭据或审计证据的组件，如果二进制中含有对应源代码里不存在且无法解释的文件，就不能接受。

不要只用一张构建成功的截图，应保存一份精简的证据记录：

```text
release: 4.2.1
source_ref: refs/tags/v4.2.1
source_commit: 8f2c...91a
build_command: ./scripts/build-release
artifact: dist/tool-4.2.1.pkg
artifact_sha256: 1c71...0be
vendor_sha256: 93a4...82d
comparison: differs
explained_differences: signing envelope, build timestamp
unexplained_files: none
patch_deploy_allowed_by: License section 2, counsel ticket LEG-184
```

这份记录会迫使团队得出两个独立结论。“我们成功构建了它”属于技术结论。“我们可以运行并再分发自己的构建”属于法律结论。团队常把两者混为一谈，直到事件发生时才发现少了一半。

还要检查安全更新路径。你能否对组织获准运行的最后一个版本应用一行修复？能否在自有设备群中签名或以其他方式授权这个修补制品？插件、代理或服务器会不会拒绝非厂商构建？如果软件保护密钥，而分支无法从周边系统接收凭据，它就不是真正的退出路径。

最后检查贡献者条款。贡献者许可协议可能允许厂商重新许可贡献内容，却不允许外部贡献者使用未来的商业代码。这种安排可能合理，但它会改变项目分裂后谁能继续维护。记录贡献采用的是 Developer Certificate of Origin、著作权转让、广泛的 CLA，还是没有公开流程。

## 正式版本与公开仓库是否一致？

安全审查针对的是具体制品，不是抽象的代码仓库。必须在所安装版本、源代码提交、许可证文本、依赖集合和该版本安全公告之间建立可靠映射。

先看发布历史。检查签名标签或其他经过认证的发布机制、能够识别安全改动的变更日志、仍受维护的发布分支，以及二进制发布与源代码公开之间是否存在稳定延迟。一次源代码迟发可能只是失误。长期存在的差距说明公开仓库并不是生产版本的真实来源。

要求维护者在持久文档中说明发布计划和支持周期。“频繁更新”毫无信息量。你需要知道哪些分支会收到安全修复、旧版本支持多久、修复会在客户二进制之前还是之后进入源代码，以及披露禁运是否会让自行构建者暴露在风险中。不要根据过去的活动自行推断服务承诺。

不要只检查最新标签，应比较三个近期版本。每个版本都回答四个问题：

1. 标签是否指向用于生成已发布制品的源代码？
2. 该标签中是否包含构建说明和依赖锁定文件？
3. 许可证或商业功能边界是否变化？
4. 全新环境能否生成可运行的软件包？

把结果保存在工程决策记录里，在续约和重大升级前重新比较。当厂商把打包移到私有系统，或把某项功能移入商业仓库时，源代码可用性可能悄然倒退。

软件物料清单有帮助，但不能把它当作源代码一致性的证据。SBOM 描述制品中的组件，却不能证明公开仓库包含复现该制品所需的代码、构建逻辑或权利。两者都要用：SBOM 用于依赖和漏洞工作，源代码到制品的映射用于判断独立性。

发布节奏也显示团队可能继承多少维护工作。每月发布功能却只修复最新分支的项目，可能迫使团队快速升级。更新较慢但有明确回移政策的项目，反而更容易运行。计算厂商给团队增加的工作，不要计算厂商宣传的发布次数。

## 当前和未来的商业功能边界在哪里？

如果计划中的商业功能位于安全路径上，即使它们尚未存在，也必须纳入考虑。请厂商把产品划分为当前开源或源代码可用代码、当前付费代码和计划中的付费代码，然后把每一部分与所需控制对应起来。

不要问模糊的“核心会一直免费吗？”即使答案为是，安全团队使用所需的功能仍可能被移到别处。应询问身份集成、集中撤销、策略管理、审计导出、保留期、高可用性、设备群管理、事件支持和迁移工具。具体集合取决于产品，但方法不变：把每项运维要求对应到已经发布的组件及其许可证。

路线图不是合同，未出现在路线图上也不代表承诺。把计划功能记录为规划信号，注明负责人、预计交付窗口、预期许可证和备用方案。如果采用决定依赖未来的企业控制，就现在估算并批准商业路径，否则把该控制视为不可用。不能因为幻灯片说缺失的保护措施即将推出，就部署更弱的设计。

还要留意可见代码中的许可证密钥检查或远程授权。确定许可服务不可用、订阅结束、厂商关闭，或组织运行修补分支时会发生什么。合理要求可能是继续只读访问、导出数据并安全关停，而不是无限期保留付费功能。无论要求是什么，都要测试。

还要问清协议和数据格式归谁控制。如果代理能使用有文档记录的协议，并能以稳定格式导出完整记录，商业控制台就比较容易替换。如果可见核心保存不透明状态，或必须由付费服务签发启动凭据，更换就困难得多。围绕私有控制平面的公开代码，可能几乎不提供运维独立性。

Sallyport 是一个清楚的对照：当前 macOS 应用完全以 Apache-2.0 开源，商业 Enterprise 组件仍在计划中。这句话并不能说明未来团队功能的价格或内容，因此采用团队应按当前许可证评估已经发布的应用，在边界正式公布前把计划组件视为未知。

## 项目能否在采用后改变条件？

如果许可方控制相关著作权，通常可以用不同条款发布未来版本。它一般不能抹掉你对已经持有版本获得的许可，但停留在该版本上可能意味着失去修复、兼容性工作或新协议支持。

所以，“他们拿不走代码”只是一种很弱的安慰。实际选择可能是接受新条款、冻结在有漏洞的分支，或出资维护分支。应在采用前评估这些成本，因为此时拒绝软件仍然便宜。

检查仓库治理和著作权集中度。谁能合并代码？谁发布版本？外部维护者能否发布兼容发行版？一家公司是否通过雇佣关系和贡献协议掌握几乎全部著作权？公司主导的项目可以维护得很好，但控制高度集中会让未来更容易更换许可证，也让社区更难接手。

查看项目历史中的许可证变化、模块迁移、标签删除、源代码延迟发布，以及功能在不同仓库之间的移动。不要自动把任何变化视作不当行为。应判断这种模式是否符合你的承受范围，以及以往用户是否得到通知、过渡期和可用的最后版本。

随后监控可能变化的输入：

- 对每份已接受的许可证文本和产品专用参数文件计算哈希并保留副本。
- 当依赖元数据出现新的许可证表达式时发出告警。
- 审查发布说明中的仓库或授权变化。
- 生产部署重大版本前重新批准。
- 在自己的受控存储中保留最后批准的源代码和构建说明。

这些控制让工程团队能够观察许可证承诺，也能阻止日常依赖更新在未经审查时带入新义务。

不要依赖厂商关于许可证永不改变的公开承诺，除非风险决策明确接受这种没有约束力的承诺。如果稳定权利不可缺少，应优先选择所需代码采用标准 OSI 批准许可证的产品，或谈判出在商业关系结束后仍有效的条款。善意不能替代持久的许可。

## 是否存在真正可行的退出路径？

真正可行的退出路径应允许你在不违反许可证、也不依赖可能消失的服务的情况下，让安全功能继续运行到完成迁移。公开仓库只是其中一个条件。

把退出路径当作事件演练来测试。假设厂商在同一天停止发布版本、关闭许可端点，并不再回复支持请求。团队应找出最后获准使用的版本，恢复源代码和依赖，构建制品，载入现有配置，恢复或迁移受保护数据，并通过自己的签名和部署流程运行它。

对于安全软件，演练必须包含密钥和审计记录。能否用有文档说明的格式导出它们？导出是否要求付费服务或仍有效的授权？本地构建能否使用组织控制的密钥解密现有状态？旧审计记录能否在没有厂商的情况下验证？某种设计即使能防止厂商接触数据，也可能把数据困在专有格式里。

维护分支还需要人。指明负责代码的团队，估算所需语言和平台知识，并找出不能再分发的依赖。如果没有人能接手，就写“只能迁移”，不要假装仓库就是后备方案。

商标应在计划中单列一项。软件许可证通常授予代码权利，却不授予品牌权利。分支可能需要新名称、软件包标识、签名身份、更新渠道和文档。提前计划时这很容易处理，仓促发布时才发现就会造成干扰。

延迟转换为开源可以改善长期处境，但必须逐版本检查。在 BSL 1.1 下，每个版本都有自己的 Change Date，许可证也分别适用于每个版本。已经转换的旧版本可能没有较新受限版本中的安全修复。“最终开源”不代表在你需要时，仍受维护的版本就是开源的。

设置一个可以测试的退出目标，例如：“十个工作日内，我们能重新构建最后批准的版本、部署本地补丁、导出全部组织数据，并在不使用厂商基础设施的情况下开始迁移。”时间应与风险相匹配，然后实际演练。如果测试失败，就为缺失能力投入资源，或把厂商依赖记录为已接受风险。

## 源代码可见时，谁负责安全响应？

源代码可见并不会自动分配漏洞分类、披露、修补或客户沟通责任。询问谁接收漏洞报告、哪些版本会得到修复、禁运问题如何通知获许可用户，以及团队是否可以制作并分发紧急补丁。

阅读仓库中的安全政策，并与真实发布做法比较。有用的政策应注明受支持版本、私密报告渠道、预计确认方式和披露流程。如果政策只写“提交 issue”，敏感报告可能在修复前公开。如果它承诺响应时间，要确定这是目标还是合同承诺。

弄清厂商对自行构建者的态度。有些厂商即使允许修改，也只支持自己的签名发行版。这可能合理，但运行手册必须说明本地修补后厂商支持在哪里结束，以及如何回到受支持构建。否则一次紧急修复会留下一个无人负责的长期私有分支。

安全主张应能追溯到代码和测试。对于凭据网关，要检查明文出现在哪里、哪个进程执行外部操作、批准状态如何绑定调用方，以及审计日志究竟证明什么。源代码访问让这些问题可以回答，却不保证答案有利。应在进程边界测试行为，不要只看加密某个值的函数。

询问威胁模型、架构说明、依赖更新做法和外部评估报告是否存在。不要因为年轻项目没有精美报告就直接拒绝，也不要因为一个徽章就给予信任而不看范围。记录团队验证过哪些主张、哪些依赖厂商，以及哪些尚未测试。

生产前指定内部负责人。此人负责关注安全公告、许可证变化、发布偏差和商业边界。没有负责人时，公开源代码只会让每个人都觉得有人可以检查，而每个人都假设是别人去做。

## 采用记录应包含什么？

最终决定应写入一份工程、安全、采购和法律人员都能质疑的记录。冗长的聊天线程不是采用记录，因为它会丢失版本范围、假设和未解决的问题。

在批准会议上使用这份精简清单：

- 身份：准确产品版本、源代码提交、制品摘要、许可证文本摘要、SPDX 表达式和 OSI 状态。
- 权利：评估、内部生产、面向客户的生产、修改、再分发、承包商和关联公司的允许与禁止用途。
- 可运维性：干净构建结果、源代码与制品差异、签名路径、数据导出、依赖存档和补丁部署测试。
- 厂商路径：受支持分支、发布与披露做法、当前付费边界、计划中的商业依赖和书面解释。
- 退出与责任：迁移目标、分支或迁移负责人、已接受剩余风险、复审触发条件和批准有效期。

为每个未解决事项指定负责人和期限，明确标出假设。如果法律人员仍在等待关于托管服务用途的答复，决定应标为有条件，而不是已批准。如果计划中的商业功能控制审计保留，就把缺失功能写入安全例外，不要藏在路线图备注中。

当许可证明确禁止计划用途、已发布制品无法对应到可用源代码，或必需数据无法离开厂商控制的基础设施时，应拒绝采用。重要条款含糊时应暂停。只有当组织理解厂商依赖的成本并主动选择它时才接受，不要因为仓库看起来让人安心就接受。

在重大版本、许可证文本变化、所有权变化、新增商业依赖、重大架构变化或退出测试失败时重新打开记录。即使这些事件都没发生，也要设定复审日期。安全软件常在不知不觉中变成基础设施，评估阶段容易替换的产品，可能在代理、凭据和审计工作流围绕它增长后变成难以摆脱的依赖。

最好的结果不一定是采用许可证最宽松的产品。条款明确、维护及时并且迁移计划已有预算的受限产品，可能比无人维护的开源项目更适合组织。当团队能准确说明自己拥有哪些权利、依赖哪些能力，以及两者变化时将采取什么行动，这个决定才算可靠。
