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, 合规映射, 多模态安全, 自动化攻击, 配置检查