cclabsnz/sf-audit-plugin
GitHub: cclabsnz/sf-audit-plugin
一款 Salesforce 安全审计 sf CLI 插件,通过 88 项只读检查对 org 进行 A–F 风险评级,并生成带攻击链关联和合规映射的技术报告与高管报告。
Stars: 11 | Forks: 0
# @cclabsnz/sf-audit
一个 Salesforce CLI (`sf`) 插件,可对任何 Salesforce org 运行完整、**只读** 的安全审计,进行 A-F 等级的风险评分,并将结果转化为您的安全团队(或您客户的团队)可以采取行动的报告。
- 针对 identity、access、data、code、integrations、monitoring 以及 Agentforce / GenAI 的 **88 项只读检查**
- **攻击链关联:** 将单个发现链接为命名的、多步骤的攻击场景
- **合规性映射:** 将每个发现映射到 8 个框架(OWASP、OWASP LLM Top 10、SOC 2、ISO/IEC 27001:2022、Security Benchmark for Salesforce、NZ Privacy Act、HISO 10029、NZISM)中**经来源验证**的控制措施
- **输出内容:** 技术性的 `html` / `md` / `json` 报告,或者是带有品牌标识、可直接交付客户的**高管报告**(打印为 PDF),其中包含优先事项、修复路线图和合规覆盖矩阵
- **历史记录与差异对比:** 归档每次运行结果,并展示安全态势随时间的变化
- **免费事件基准:** `sf audit events pull` 会在 1 天的保留期丢弃数据之前,将 org 免费的每日 `EventLogFile` 日志捕获到本地磁盘——无需 Event Monitoring / Shield 附加组件
- **Connected-app 最小权限:** `sf audit apps` 会读取 `RestApi` EventLogFile 以查看每个 connected app 实际使用了哪些对象,将其与其运行身份用户被授予的权限进行比较,并报告每个对象及读/写位的过度授权——此外还会提供推荐的最小权限 permission set
- 严格只读(SOQL/Tooling/REST GET + Metadata API 读取);请参阅 [PERMISSIONS.md](PERMISSIONS.md) 了解其所需的最小权限
## 安装
```
sf plugins install @cclabsnz/sf-audit
```
或者,用于本地开发:
```
git clone https://github.com/cclabsnz/sf-audit-plugin.git
cd sf-audit-plugin
npm install
npm run build
sf plugins link .
```
## 用法
```
sf audit security --target-org
```
这会针对目标 org 运行所有 88 项安全检查,并将报告写入当前目录。
列出所有可用的检查 ID(即您传递给 `--checks` 的值):
```
sf audit list
```
### 选项
| 标志 | 默认值 | 描述 |
|------|---------|-------------|
| `--target-org` | *(必填)* | 要审计的 Org 别名或用户名 |
| `--format` / `-f` | `html` | 输出格式,以逗号分隔:`html`、`md`、`json`、`executive` |
| `--output` / `-o` | `.` | 写入报告文件的目录 |
| `--fail-on` | (无) | 如果任何发现的严重程度等于或高于此级别,则退出代码为 1:`CRITICAL`、`HIGH`、`MEDIUM`、`LOW` |
| `--checks` | *(全部)* | 要运行的检查 ID(以逗号分隔),而不是全部 88 项(例如 `hardcoded-credentials,apex-sharing`) |
| `--scoring-config` | (无) | 自定义评分配置 JSON 文件的路径,用于覆盖权重和评级阈值 |
| `--prepared-for` | (无) | 高管报告封面上的客户名称 |
| `--branding` | (无) | `report-branding.json` 的路径,用于覆盖 CloudCounsel 默认设置(executive 格式) |
| `--top` | `5` | 要突出显示的高管优先事项数量(executive 格式) |
| `--frameworks` | `universal` | 合规矩阵范围(executive 格式):`universal` (OWASP/OWASP LLM/SOC 2/ISO 27001)、`nz` (ISO/HISO/Privacy Act/NZISM)、`all` 或逗号分隔列表(例如 `owasp,owasp-llm,iso,nzism`) |
| `--resolve-domains` | `false` | 从**本机发起出站 DNS 查询**,以验证 CSP 信任域名是否仍然可解析(将无法解析/已停用的域名标记为数据渗出通道)。默认关闭;默认运行仅联系**目标 org**,绝不联系任何其他主机。 |
### 示例
```
# HTML 报告(默认)
sf audit security --target-org myOrg
# 同时输出多种格式
sf audit security --target-org myOrg --format html,md,json
# 将报告写入特定目录
sf audit security --target-org myOrg --output ./reports
# 发现 HIGH 或 CRITICAL 问题则中断 CI pipeline
sf audit security --target-org myOrg --fail-on HIGH
# 仅运行特定检查
sf audit security --target-org myOrg --checks hardcoded-credentials,apex-sharing,guest-user-access
# Guest / Experience Cloud 暴露扫描 — 未经身份验证的数据泄露面
# (通过 UI API 批量读取、Guest 拥有的记录绕过 Private OWD、文件访问、自行注册、威胁检测)
sf audit security --target-org myOrg --checks guest-user-access,guest-object-exposure,guest-site-options,guest-executable-apex,experience-cloud-site,threat-detection
# 使用自定义 scoring config(例如:为您的 org 设置更严格的权重)
sf audit security --target-org myOrg --scoring-config ./my-scoring.json
```
### 高管报告
`--format executive` 会生成一个带有 CloudCounsel 品牌标识、可打印为 PDF 的 HTML 客户报告:
包含评级和高管摘要、带有滥用/影响叙述的最高优先级事项、攻击场景、
风险×工作量 修复路线图,以及一个将发现映射到框架控制措施的**合规覆盖矩阵**。
它是完全自包含的(内嵌字体);打开它并**另存为 PDF** 即可。
```
# 为客户定制的品牌化高管报告(通用 compliance matrix)
sf audit security --target-org myOrg --format executive --prepared-for "Acme Health" --top 5
# NZ 健康/政府业务:NZ framework matrix
sf audit security --target-org myOrg --format executive --frameworks nz
# 通过 overrides 实现 White-label / co-brand
sf audit security --target-org myOrg --format executive --branding ./report-branding.json
```
合规控制措施是从权威的、固定版本的来源映射而来的(OWASP Top 10:2021、
OWASP Top 10 for LLM Applications 2025、AICPA TSC、ISO/IEC 27001:2022、
Security Benchmark for Salesforce、NZ Privacy Act、HISO 10029、NZISM)。仅渲染经过来源验证的控制措施。
“未检测到发现”**并不**代表合规(请参阅报告的范围与责任部分)。
报告文件以 `sf-audit--.` 格式写入输出目录(例如 `sf-audit-00D000000000001-1711234567890.html`)。
## 检查内容
审计共运行 **88 项只读检查**。每个发现都经过风险评级(CRITICAL → INFO),并映射到各个合规框架的控制措施中(请参阅[合规框架](#compliance-frameworks))。这些检查分为以下十个领域。
### Org 健康状况与配置
| 检查项 | 查找目标 |
|-------|------------------|
| 安全健康检查 | Salesforce Health Check 分数以及个别高风险设置 |
| 增强域名 | 是否启用了 Enhanced Domains:防止跨 org 的 cookie 泄露并强制执行 URL 隔离 |
| 待处理的发布更新 | Salesforce 待激活的发布更新,尤其是那些已过自动激活期的更新 |
| 旧版 API 版本 | 基于旧版 API 编译的 Apex 以及基于 SOAP 的远程站点集成 |
| API 与资源限制 | API 请求消耗与每日及并发限制的对比情况 |
### 身份与认证
| 检查项 | 查找目标 |
|-------|------------------|
| SSO 强制执行 | 用户名-密码登录行为,表明 SSO 未在 org 范围内强制执行 |
| My Domain 登录策略 | 是否配置了 My Domain 并且阻止了来自 login.salesforce.com 的登录(防止 SSO 绕过) |
| 内部用户 MFA | 活跃内部标准用户的 MFA 强制执行情况 |
| 外部用户 MFA | 对具有数据访问权限的外部/门户用户强制执行 MFA |
| MFA 方法注册 | 没有注册 MFA 方法的活跃标准用户 |
| MFA 方法强度 | 根据强度对已注册的 MFA 方法进行分类(防钓鱼 / TOTP / 弱) |
| 高保障会话 | 需要短超时或高保障 MFA 会话的具备管理员权限的 connected apps |
| 信任 IP 范围 | 绕过 MFA 的信任 IP 范围,包括过于宽泛的范围 |
| 登录 IP 限制 | 缺少 IP 范围的管理员配置文件;具有宽松 IP 策略的 connected apps |
| 密码与会话策略 | 密码复杂度、会话超时和 MFA 漏洞(来自 Health Check) |
| 证书过期 | 即将过期的已安装证书(30 / 90 / 180 天阈值) |
| 认证提供程序与外部 IdP | 外部 Auth Providers 和 SAML SSO 配置;标记可以联合登录或自动配置用户的社会化/JIT 提供程序 |
| 会话与点击劫持强化 | 直接从 SecuritySettings 读取的点击劫持、CSRF、XSS 和内容嗅探设置(Health Check 缓存回退),并提供针对每个设置的修复建议 |
### 用户、权限与特权
| 检查项 | 查找目标 |
|-------|------------------|
| 用户与管理员 | 具有系统级权限(ModifyAllData、ViewAllData、AuthorApex、CustomizeApplication)的用户 |
| 权限 | 未分配的 permission set 和扩大攻击面的过多 profile 数量 |
| 标准配置文件使用情况 | 被分配给开箱即用标准 profile 的活跃用户 |
| 使用任何 API 客户端 | 具有绕过 API Access Control 权限的用户 |
| 权限提升权限 | 拥有横向移动 / 持久化权限集群的用户 |
| 特权访问与影子管理员 | 每个用户的有效高风险权限(profile + permission set + groups);不在 System Administrator profile 上的等效管理员用户 |
| 职责分离 | 单个用户持有的有害权限组合(例如 Manage Users + Assign Permission Sets,Author Apex + Modify All Data) |
| 集成 / 服务账号 | 非人类身份清单及过度特权 |
| 非活跃用户 | 超过 90 天未登录的活跃许可用户 |
| 登录身份与委托管理 | 委托管理员组(SOQL)以及从 SecuritySettings 读取的“以任何用户身份登录”策略——用户模拟和范围扩大的提权路径 |
| 批量数据导出访问 | 具有 Weekly Data Export 的 profile/permission set,或 API 访问权限结合 View/Modify All Data(批量数据渗出能力) |
### 数据访问与共享
| 检查项 | 查找目标 |
|-------|------------------|
| OWD 共享模型 | Account、Contact、Opportunity、Case、Lead 的组织范围默认值(内部 + 外部) |
| 字段级安全 | 暴露给广泛权限集的敏感字段(SSN、信用卡、税务 ID) |
| 公共组共享 | 向 All Internal Users 授予访问权限的共享规则 |
| 报告文件夹公共访问 | 任何经过身份验证的用户都可以查看的报告文件夹 |
| 字段历史跟踪 | 敏感标准对象上启用的历史跟踪 |
| 数据分类与加密 | 字段数据分类使用情况以及 Shield Platform Encryption |
| 敏感字段的加密覆盖范围 | 被归类为 PII/PHI (ComplianceGroup) 但在静态时未使用 Shield 加密的字段 |
| 沙盒数据掩码 | 在沙盒中,已填充的 PII 字段(可能是未掩码的生产数据);建议运行 Data Mask |
| 无共享的 Flows | 在没有共享强制执行的系统上下文中运行的活跃 flows |
| 内容分发链接 | 缺少过期时间或密码的公共文件链接,以及陈旧记录 |
| 公共静态资源与文档 | 标记为外部可用的文档(匿名 URL 访问)和公开缓存的静态资源 |
### 访客与外部访问
| 检查项 | 查找目标 |
|-------|------------------|
| 访客用户访问 | 授予未经身份验证访客的对象权限和共享规则 |
| 访客对象暴露(通过 UI API 批量读取) | 自动发现访客可读的对象(包括尽管 OWD 为 Private,但因访客所有权而暴露的记录),然后根据实际的 UI-API 可达性对其进行分级:UI API 建模的对象评为 CRITICAL(可通过 GraphQL 拉取),仅在共享模型中可读但未被 UI-API 建模的对象(Calendar、AuthSession 等)分离出来评为 MEDIUM,并以 UserRecordAccess 作为真实读取确认 |
| 访客可执行 Apex | 访客 profile 可以运行的 Apex,特别标记 `without sharing` 类 |
| Experience Cloud 站点 | 启用了自注册并且存在访客用户的活跃站点 |
| Experience Cloud 访客站点选项 | Experience Cloud 站点上的访客文件访问和访客成员可见性 |
| 安全访客用户记录访问 | 验证是否激活了“安全访客用户记录访问”强制执行——这是阻止访客拥有的记录破坏 Private OWD 的护栏(补充了访客对象暴露检查) |
| 访客 API 与批量访问 | 被授予 API Enabled 或 Bulk API Hard Delete 的访客用户——为未经身份验证的访问者提供程序化批量读取/删除权限 |
| 经典 Force.com Sites | 活跃的经典 (Visualforce) Sites 及其访客用户——独立于 Experience Cloud 的未经身份验证的攻击面 |
| Experience Cloud CSP 与 Lightning Web Security | 建议验证活跃站点上的 Strict CSP 和 Lightning Web Security(无法通过 API 可靠读取) |
| CORS 允许列表 | 通配符或过于宽泛的 CORS 允许列表来源 |
| CSP 信任站点 | 具有不安全 HTTP(混合内容)端点的内容安全策略信任站点 |
### Apex 与代码安全
| 检查项 | 查找目标 |
|-------|------------------|
| Apex 共享声明 | 根据共享声明分类的类(with / without / inherited / omitted) |
| Apex CRUD/FLS 强执行 | 在没有 CRUD/FLS 权限检查的情况下执行 DML 或 SOQL |
| Apex REST 端点 | 运行 `without sharing` 的 `@RestResource` 类 |
| Visualforce XSS | `escape="false"` 以及 Visualforce 标记中未编码的合并字段 |
| 硬编码凭证 | Apex 中的 Bearer token、Basic auth、API key 和原始 callout URL |
| 代码安全与覆盖率 | org 范围内的 Apex 测试覆盖率、类/触发器数量以及 SOQL 注入模式 |
| 计划与批处理 Apex | 活跃的计划和批处理 Apex 作业 |
| 匿名 Apex 审计 | 过去 90 天内执行的匿名 Apex(通过 SetupAuditTrail) |
| Apex 日志框架 | 持久化日志的使用情况以及 Apex 日志中暴露的敏感数据 |
### 集成、Connected Apps 与部署
| 检查项 | 查找目标 |
|-------|------------------|
| Connected Apps | 未限制为仅限管理员批准的用户的 Apps |
| Connected App OAuth 范围 | 完全的 OAuth scope 授权和无期限的 refresh-token 策略 |
| 非活跃 Connected Apps | 过去 90 天内没有 OAuth 登录的 Apps |
| Named Credentials | Named credential 清单;Apex 中未引用的凭证 |
| 外部凭证认证 | 使用无身份验证或自定义(非标准)方案的 External Credentials |
| 远程站点设置 | 禁用了协议安全的远程站点 |
| 出站消息 | 包含 session ID 或发送到明文 (http://) 端点的工作流出站消息 |
| 电子邮件安全与欺骗 | 接受来自任何发件人的入站电子邮件服务 / 未经身份验证运行的 Apex,以及向所有 profile 开放的 org 范围内代发地址 |
| 已安装的包 | 托管/非托管包清单;生产环境中的非托管或 beta 包 |
| 部署身份 | 指定的部署身份和不受控的部署活动 |
### 机密与凭证存储
| 检查项 | 查找目标 |
|-------|------------------|
| 自定义设置与凭证 | 名称类似凭证、可能存储机密的自定义设置 |
| 自定义标签凭证暴露 | 全局可读的 Custom Labels 中的 API key 和 token |
### 监控与威胁检测
| 检查项 | 查找目标 |
|-------|------------------|
| 审计跟踪 | 设置审计跟踪中的权限更改和 Login-As 事件 |
| 登录会话 | 登录失败趋势、Login-As 事件以及来自不同 IP 的访问 |
| 登录失败检测 | 暴力破解和撞库模式(过去 7 天) |
| 事务安全策略 | 已配置的自动化威胁检测和响应策略 |
| 活跃的调试日志跟踪 | 捕获日志的活跃 TraceFlag 记录,包括高细节跟踪 |
| 事件监控 | 启用了 Event Monitoring,且日志保留时间超过 30 天 |
| 威胁检测事件存储 | 启用了 Guest User Anomaly / Threat Detection 事件存储并保留事件 |
| 访客流量异常 | 扫描最近的 EventLogFile 访客请求,查找匿名器/托管源 IP、单 IP 突发流量以及 GraphQL `totalCount` 侦察扫描 |
| 异常的成功登录 | 成功登录且来自异常大量不同源 IP 的账户(凭证共享/泄露的特征) |
| SIEM 集成信号 | 存在 SIEM 或外部监控集成的证据 |
### AI 与 Agents (Agentforce / GenAI)
| 检查项 | 查找目标 |
|-------|------------------|
| Agent 清单 | 每个 Agentforce agent、其活跃版本及其运行身份用户(清单发现);标记运行身份处于非活跃或冻结状态的活跃 agent |
| Agent 用户特权 | 具有 Modify All Data 或 View All Data、广泛对象写入权限,或对归类为敏感的对象具有读取权限的 Agent / 运行身份用户——即 prompt injection 继承的数据 |
| Agent 动作面 | 具备写入能力的 agent 动作(创建、更新或删除的 Apex/Flow)以及动作面异常庞大的 agents |
| Agentforce 渠道暴露 | 将活跃 agents 与能够触达它们的渠道(Experience Cloud 站点、嵌入式部署、消息渠道)相关联;标记访客可触达的暴露面 |
| Agentforce 监控覆盖范围 | 在没有 Event Monitoring 捕获且没有 Transaction Security 策略的情况下运行的活跃 agents(ForcedLeak 模式中的监控盲区);指向 `sf audit events pull` |
| 信任 URL 卫生 | 检查 CSP 信任站点允许列表中可能被重新用作数据渗出渠道的非 Salesforce 域名;使用 `--resolve-domains` 时,会对每个条目进行 DNS 检查,以查找无法解析或已停用的条目 |
两个命名的攻击链将这些发现关联起来:**Prompt injection 爆炸半径**(访客可触达的渠道 + 特权过高的 agent 用户 + 具备写入能力的动作)和 **ForcedLeak 模式**(活跃 agents + 陈旧/无法解析的信任 URL + 无事件捕获)。
在未启用 Agentforce 的 org 中,这五项特定于 agent 的检查将保持静默(GenAI 对象不存在,因此清单记录为 `not-enabled`,并且相关检查不返回任何内容)。信任 URL 卫生会在所有地方运行,因为无论是否启用 Agentforce,CSP 允许列表都是一个 org 范围的数据渗出面。
### 已知限制(仅供建议的检查)
少量检查会发出**建议** 而不是通过/失败判定,因为底层设置未公开给此插件使用的读取 API(SOQL/Tooling/REST/Metadata 读取):
- **Experience Cloud CSP 与 Lightning Web Security** —— 每个站点的 LWR 内容安全策略级别和 Lightning Web Security 开关位于站点的 `ExperienceBundle` 内部,而不是 `metadata.read` 可读取的类型中。检查会列出活跃的 Experience 站点并要求手动验证。真正的检测需要检索并解析 `ExperienceBundle`(retrieve → unzip → read `config/*.json`),这超出了当前读取客户端的能力范围;这被列为未来的增强功能。
- **“管理员可以以任何用户身份登录”** 在 SecuritySettings 可读时是真正的检测;如果 Metadata 客户端不可用,它将降级为手动验证建议。
建议发现会显示为 `INFO`,绝不会导致健康分数虚高。
## 合规框架
发现结果被映射到八个安全和隐私框架的控制措施中。该映射建立在**带有来源的控制目录**之上(每个控制措施都包含其框架、**固定版本**、官方标题和引用),因此发现的合规性参考会关联到精确、可辩护的要求,而不仅仅是一个简单的标签。
| 框架 | 版本 | 备注 |
|-----------|---------|-------|
| OWASP Top 10 | 2021 | Web 应用程序风险类别 |
| OWASP LLM Top 10 | 2025 | LLM/GenAI 应用程序风险(LLM01 Prompt Injection、LLM02 Sensitive Information Disclosure、LLM05 Improper Output Handling、LLM06 Excessive Agency);由 AI & Agents 检查映射 |
| SOC 2 | AICPA TSC 2017 | 通用标准 (CC6–CC9) |
| ISO/IEC 27001 | 2022 | Annex A 控制措施 |
| Security Benchmark for Salesforce (SBS) | 当前版本 | Salesforce 原生基准:[docs.securitybenchmark.org](https://docs.securitybenchmark.org) |
| NZ Privacy Act | 2020 | 信息隐私原则 (IPP 5/9/12) |
| HISO 10029 | 2022 | 新西兰健康信息安全框架 |
| NZISM | v3.8 | 新西兰信息安全手册 |
**来源关卡。** 每个编目的控制措施只有在根据权威来源确认其标题/参考后才被标记为 `verified`。**未验证的控制措施不会**在合规矩阵中渲染。绝不会在未确认数据的情况下以“符合条款”的形式输出。当前的验证状态记录在 [`docs/compliance/verification-worksheet.md`](docs/compliance/verification-worksheet.md) 中。
**框架包。** 高管报告的合规矩阵通过 `--frameworks` 设定范围:
- `universal` *(默认)*:OWASP、OWASP LLM Top 10、SOC 2、ISO 27001
- `nz`:ISO 27001、HISO 10029、NZ Privacy Act、NZISM(用于新西兰健康/政府项目)
- `all`:所有框架
- 以逗号分隔的别名列表,例如 `owasp,owasp-llm,iso,nzism`(`owasp-llm` / `llm` 选择 OWASP LLM Top 10)
**延伸阅读:** [将 Salesforce 安全性映射到 NZISM、NZ Privacy Act 和 ISO 27001](https://www.softwareinsights.dev/posts/salesforce-security-nzism-nz-privacy-act) 以及 [为什么 Salesforce Health Cloud 需要自己的安全审查](https://www.softwareinsights.dev/posts/salesforce-health-cloud-security-review)。
## 范围与责任
**本工具是什么。** 一项只读的、基于时间点的配置审查。每项检查仅使用标准的 Salesforce SOQL、Tooling 和 REST **GET** 查询:该工具不执行 DML,不进行 metadata 部署,也绝不修改目标 org 或其数据。它在经过身份验证的 `sf` 用户的权限下运行;用户无法访问的检查将被报告为*不确定*,而不是静默通过。
**本工具不是什么。** 它**不是**渗透测试、动态/运行时安全测试,或对托管包内部源代码的审计。它不利用漏洞、不尝试提权,也不保证能检测出每一个配置错误。健康分数和 A-F 评级是**优先级排序辅助工具**,而非认证,也不代表符合或获得任何标准的认可(OWASP、SOC 2、ISO 27001、HIPAA、GDPR 等)。合规框架标签仅表示与控制领域的*相关性*。
**基于时间点。** 结果反映了审计**运行那一刻**的 org 配置。配置漂移、新的自定义和平台更改随时可能使发现失效。请定期重新运行(请参阅[历史记录与差异对比](#history--diff))。
**授权。** 仅针对您拥有或获得**明确书面授权**进行评估的 org 运行此工具。您有责任获得必要的许可,并根据您组织的数据处理和保密义务处理生成的报告(其中可能包含敏感的安全配置)。
**无担保。** 本软件按“原样”提供,不提供任何形式的明示或暗示的保证。在法律允许的最大范围内,对于因使用本工具或依赖其输出而产生的任何损失、损害或索赔,作者和 CloudCounsel Limited 概不负责。发现结果仅供参考,在采取任何修复行动之前,应由合格的 Salesforce 安全从业者进行验证。
## 评分
每个发现都被分配一个风险等级和相应的权重:
| 风险等级 | 默认权重 |
|------------|---------------|
| CRITICAL | 10 |
| HIGH | 7 |
| MEDIUM | 4 |
| LOW | 1 |
| INFO | 0 |
健康分数计算为 `100 - (总权重 / 最大可能权重) * 100`,最低为 0。
审计会生成一个**健康分数**(0–100)和一个**评级**(A–F):
| 评级 | 标准 |
|-------|---------|
| A | 分数 ≥ 85,无 HIGH 发现 |
| B | 分数 ≥ 70,≤ 1 个 HIGH 发现 |
| C | 分数 ≥ 55,≤ 3 个 HIGH 发现 |
| D | 分数 ≥ 40,无 CRITICAL 发现 |
| F | 分数 < 40 或有任何 CRITICAL 发现 |
### 自定义评分配置
所有权重和评级阈值均可配置:无需重新编译。这在您的 org 具有不同的风险偏好时非常有用(例如,您希望更严厉地惩罚硬编码凭证,或者设置更严格的评级阈值)。
**第 1 步:** 复制示例配置作为起点:
```
cp config/scoring.sample.json my-scoring.json
```
**第 2 步:** 编辑这些值。所有三个部分(`riskScores`、`checkWeights`、`gradeThresholds`)都是可选的:省略任何部分即可保留默认值。
```
{
"riskScores": {
"CRITICAL": 10,
"HIGH": 7,
"MEDIUM": 4,
"LOW": 1,
"INFO": 0
},
"checkWeights": {
"hardcoded-credentials": 10,
"guest-user-access": 10,
"users-and-admins": 10,
"apex-sharing": 7
},
"gradeThresholds": {
"A": { "minScore": 90, "maxHigh": 0 },
"B": { "minScore": 75, "maxHigh": 1 },
"C": { "minScore": 60, "maxHigh": 3 },
"D": { "minScore": 40, "maxCritical": 0 },
"F": {}
}
}
```
完整的有效 `checkWeights` 键列表(每个检查对应一个)位于 [`config/scoring.sample.json`](config/scoring.sample.json) 中。
**第 3 步:** 在运行审计时将其传递进去:
```
sf audit security --target-org myOrg --scoring-config ./my-scoring.json
```
您的配置将与默认值进行深度合并,因此您只需包含要更改的值。
## 历史记录与差异对比
每次 `sf audit security` 运行都会自动将报告的 JSON 副本归档到:
```
~/.sf/audit-history/{orgId}/sf-audit-{orgId}-{timestamp}.json
```
无需配置:归档在每次运行后静默进行。
### 查看审计历史
显示您的 org 的安全态势在多次运行中是如何变化的:
```
sf audit history --target-org myOrg
```
打印带有分数趋势的终端表格,并将 HTML 时间轴写入当前目录。
**标志:**
| 标志 | 描述 | 默认值 |
|------|-------------|---------|
| `--target-org` | Org 别名或用户名 | 必填 |
| `reports-dir` | 包含已归档报告的自定义目录 | `~/.sf/audit-history/{orgId}` |
| `--output` | 写入 HTML 时间轴的目录 | `.` (当前工作目录) |
| `--limit` | 要显示的最近运行的最大次数 | 全部 |
**示例输出:**
```
Audit History: My Org (00D000000000001)
────────────────────────────────────────────────────────────────────────────────
# Date Score Grade CRIT HIGH MED LOW Δ Score
────────────────────────────────────────────────────────────────────────────────
1 2026-03-23 15:10 64 D 1 5 8 3 —
2 2026-04-09 11:22 81 B 0 2 5 3 +17
────────────────────────────────────────────────────────────────────────────────
Trend: ▲ +17 over 2 audits Best: 81 (2026-04-09 11:22) Worst: 64 (2026-03-23 15:10)
```
### 对比两份报告
比较任何两个审计 JSON 文件,以确切了解发生了什么变化:
```
sf audit diff baseline.json current.json
```
将 HTML 和 JSON 差异报告写入当前目录。
**标志:**
| 标志 | 描述 | 默认值 |
|------|-------------|---------|
| `--output` | 写入差异报告的目录 | `.` (当前工作目录) |
| `--format` | 逗号分隔的格式:`html`、`json` | `html,json` |
**示例输出:**
```
Diff report written: ./sf-audit-diff-00D000000000001-...-vs-....html
Diff report written: ./sf-audit-diff-00D000000000001-...-vs-....json
─────────────────────────────
Diff Summary
─────────────────────────────
Score delta +17
Grade D → B
New 0
Resolved 1
─────────────────────────────
```
## 免费事件基准
Salesforce 的免费层在 Enterprise/Unlimited/Performance 版本和 Developer Edition 上公开了**每日间隔的 `EventLogFile` 日志**(登录、API 和错误活动)——*无需*付费的
Event Monitoring / Shield 附加组件。问题在于:在免费层上,这些日志仅保留
**约 1 天**。错过一天,当天的活动就丢失了。
`sf audit events pull` 会在它们过期之前将它们捕获到本地磁盘,因此每日运行会建立起一个您拥有的滚动本地基准:
```
sf audit events pull --target-org myOrg
```
它查询 org 实际公开的任何每日事件类型,下载每个日志的 CSV 正文,并
将其保存到 `~/.sf/event-baseline/{orgId}/{EventType}/{LogDate}-{Id}.csv`,以及每次运行的清单。
它是**只读**(仅 GET)且**幂等**的:磁盘上已有的任何日志都会被跳过,因此可以安全地
重复运行。每天从 cron 或预定的 GitHub Action 运行一次,您就能以不断增长的归档
击败 1 天的保留期——无需附加组件。
```
# 每日 cron 条目(07:15)— 捕获昨日的日志
15 7 * * * sf audit events pull --target-org myOrg >> ~/.sf/event-baseline/pull.log 2>&1
```
**标志:**
| 标志 | 描述 | 默认值 |
|------|-------------|---------|
| `--target-org` | Org 别名或用户名 | 必填 |
| `--since` | 要请求的 `LogDate` 天数(`LAST_N_DAYS` 窗口) | `1` |
| `--types` | 限制为特定的 EventTypes,以逗号分隔(例如 `Login,ApiTotalUsage`) | *(所有可用项)* |
| `--output` / `-o` | 用于存储日志的基础目录 | `~/.sf/event-baseline` |
```
# 回填过去 3 天的数据(每项仍受 org 保留策略的限制)
sf audit events pull --target-org myOrg --since 3
# 仅拉取 login 和 API-usage 日志
sf audit events pull --target-org myOrg --types Login,ApiTotalUsage
# 存储在项目本地目录而不是 ~/.sf
sf audit events pull --target-org myOrg --output ./event-baseline
```
**示例输出:**
```
Pulling free EventLogFile logs for org: My Org (00D000000000001)
─────────────────────────────
Event Baseline Pull
─────────────────────────────
Found 7
Downloaded 7
Skipped 0 (already saved)
Total bytes 48213
─────────────────────────────
Saved to: ~/.sf/event-baseline/00D000000000001
Manifest: ~/.sf/event-baseline/00D000000000001/_manifests/manifest-...-....json
```
### 分析捕获的日志
`events pull` 是收集部分。要对那些 `EventLogFile` CSV 进行漏洞利用和
滥用模式的分类排查,请使用配套的 CLI **[sfelf-triage](https://github.com/cclabsnz/sfelf-triage)**。
它读取下载的 EventLogFile CSV,并发出针对每个 IP 的判定
(`BENIGN_SCANNER | SUSPICIOUS | LIKELY_ABUSE`),回答*“这个访客/社区 IP 是
漏洞扫描器还是真正的威胁?”*——在**零网络出口**且不连接 org 的情况下。
sfelf-triage 直接读取此插件的 `~/.sf/event-baseline/` 布局,因此这两个
工具无需任何粘合代码即可串联使用:
```
sf audit events pull --target-org myOrg # capture (this plugin)
sfelf-triage analyze ~/.sf/event-baseline/ # triage (companion)
```
请参阅 [sfelf-triage README](https://github.com/cclabsnz/sfelf-triage#readme) 了解安装和用法。
## Connected-app 最小权限
Connected-app 过度授权是 2025-2026 年通过 OAuth 窃取 Salesforce 数据浪潮的核心:
应用程序被授权了超出其使用范围的 scope,而集成用户拥有的对象访问权限远远超出了
应用程序的涉及范围。静态检查(`connected-apps`、`connected-app-scope`、
`connected-app-inactivity`)告诉您*授予*了什么。`sf audit apps` 告诉您实际*使用*了什么,
因此它可以指出需要移除的特定访问权限。
```
sf audit apps --target-org myOrg --since 7
```
它读取 `RestApi` `EventLogFile` 以查看每个 connected app 涉及了哪些对象,将其
与其运行身份用户可以访问的对象进行比较,并报告每个对象和读/写位的过度授权——
此外还会生成一个最小权限的 permission set,精确授予所观察到的权限。App ID 会被
解析为人类可读的名称(`AppMenuItem` / `ConnectedApplication` / 内置的标准应用程序
目录 / `LoginHistory` 关联),并且任何未解析的内容都会被显眼地标记出来,而不是被隐藏。
它是**只读**的:建议的 permission set 仅作为数据输出,绝不进行部署。
**诚实的边界。** `RestApi` 将大约一半的 API 流量归因于 connected app(其余是
UI-API / 会话流量),因此*使用*情况是一个下限。发现结果包含观察窗口和
归因率,并且对于在 soak 窗口以下以及被许多交互式用户使用的应用程序,会抑制撤销建议。
读取 `EventLogFile` 需要 **View Event Log Files** 权限(与
`events pull` 使用的权限相同)。
**标志:**
| 标志 | 描述 | 默认值 |
|------|-------------|---------|
| `--target-org` | Org 别名或用户名 | 必填 |
| `--since` | 要分析的 `RestApi` 日志天数 | `7` |
| `--from` | 从本地 `events pull` 基准目录读取 `RestApi` CSV,而不是下载 | *(下载)* |
| `--soak` | 断言撤销建议前的最小窗口期(天) | `7` |
| `--format` | `table` / `json` / `md` | `table` |
```
# 复用现有的 events-pull baseline 而不是重新下载
sf audit apps --target-org myOrg --from ~/.sf/event-baseline/00Dxxx
# 机器可读输出
sf audit apps --target-org myOrg --format json
```
## 要求
- Node.js 18+
- Salesforce CLI (`sf`) v2+
- 最小权限、**只读**的 org 用户。审计不执行任何写入操作,**不**需要 `View All Data`。请参阅 **[PERMISSIONS.md](PERMISSIONS.md)** 了解确切的最小权限集、每个权限的用途、该工具*不需要*什么,以及一个可随时部署的 `SF Audit (Read-Only)` permission set([`docs/permissionset/`](docs/permissionset/SF_Audit_ReadOnly.permissionset-meta.xml))。
## 开发
```
npm run build # compile TypeScript
npm test # run all tests
npm run test:unit # unit tests only
npm run clean # remove compiled output
```
标签:CLI插件, LNA, MITM代理, Salesforce, 合规映射, 多模态安全, 自动化攻击, 配置检查