borborich/pgextassure

GitHub: borborich/pgextassure

PgExtAssure 是一款在 PostgreSQL 扩展安装前执行静态分析的安全准入工具,用于在跨越信任边界前提供确定性的安全审查和可验证证据。

Stars: 0 | Forks: 0

# PgExtAssure **为 PostgreSQL 扩展提供静态准入前的安全保证。** PgExtAssure 会在平台团队将扩展加入白名单、构建或安装之前,检查扩展的源码树。它会读取 PostgreSQL 扩展的元数据和源文件,报告与安全相关的模式,并可在达到特定严重程度时中断 CI 流程。它**不会**构建、加载、安装或执行其扫描的扩展。 官方仓库: 命令行可执行文件和 Python 模块均命名为 `pgextassure`。 ## 问题背景 安装 PostgreSQL 扩展可能会跨越异常强大的信任边界。安装和升级脚本可能以提升的数据库权限运行;原生模块可能在 PostgreSQL 服务器进程内执行;而 `trusted` 或已加入白名单的扩展可能会将这些能力暴露给非超级用户。 因此,准入决策很难得到一致性的审查: - 源码包混合了控制元数据、安装 SQL、升级 SQL 和原生代码; - 审查者在花费时间进行手动分析之前,需要一份可重复的清单; - 传统的数据库 linter 会在扩展已经越过准入边界之后,才检查现有的数据库; - 白名单说明了*允许安装什么*,但没有解释为什么某个条目足够安全可以被允许。 PgExtAssure 在该决策之前设置了一个确定性的、可审查的静态关卡。 ## 两分钟快速开始 在克隆此仓库后: ``` git clone https://github.com/borborich/pgextassure.git cd pgextassure python -m venv .venv source .venv/bin/activate python -m pip install -e . pgextassure scan /path/to/postgres-extension \ --format text \ --fail-on high ``` 等效的模块调用: ``` python -m pgextassure scan /path/to/postgres-extension ``` 生成机器可读的报告而不中断当前运行: ``` pgextassure scan /path/to/postgres-extension \ --format json \ --output pgextassure.json \ --fail-on none ``` 生成紧凑的审查队列,在仅对具有明确共享例程身份的发现进行分组的同时,保留每一个源位置: ``` pgextassure scan /path/to/postgres-extension \ --format grouped-json \ --output pgextassure-grouped.json \ --fail-on none ``` `grouped-json` 是一种单独的报告类型。其摘要同时报告原始发现数量和根本原因数量。没有明确语义身份的规则将保持位置作用域限制,且绝不会被启发式地合并。 为安全工程师或 AI 编程智能体创建一个确定性的工作队列: ``` pgextassure scan /path/to/postgres-extension \ --format review-json \ --output pgextassure-review.json \ --fail-on none ``` Agent Review Pack `1.0` 将每个任务绑定到确切的分组报告和源码清单,携带一套封闭的处理结果词汇表,不包含源文件载荷,且明确表示无法授予准入许可。请参阅 [Agent Review Pack](docs/agent-review-pack.md)。 创建并验证由智能体编写的独立 Decision Ledger: ``` pgextassure review template pgextassure-review.json \ --output pgextassure-decisions.json pgextassure review verify \ pgextassure-review.json \ pgextassure-decisions.json ``` 验证要求完全覆盖任务,并为每个已解决的处理结果提供引用。结构上有效的账本依然不具备准入授权。 如果审查过的构建元数据生成了源码树中缺失的安装 SQL 或控制文件,请提供一个固定的、非执行性的生成计划: ``` pgextassure scan /path/to/postgres-extension \ --generation-plan /path/to/generation-plan.json \ --format grouped-json \ --output pgextassure-grouped.json ``` PgExtAssure 会验证每个声明的输入 SHA-256,并可能在内存中应用有限的字面量模板替换。它从不运行构建。有关架构和信任边界,请参阅[生成计划](docs/generation-plans.md)。 对于包含已知生成别名或超大测试夹具的单体仓库或源码树,请使用经过审查且绑定摘要的范围计划: ``` pgextassure scan /path/to/postgres-extension \ --scope-plan /path/to/scope-plan.json \ --format grouped-json \ --output pgextassure-grouped.json ``` 范围计划声明互不重叠的相对扫描根目录。每个被排除的常规文件都被精确定位到其确切的字节,每个被排除的软链接都被精确定位到其确切的目标文本。缺失、更改或未使用的排除项将导致失败关闭。请参阅[范围计划](docs/scope-plans.md)。 要在包含现有发现的仓库中引入一个关卡,请创建并审查根本原因基线: ``` pgextassure baseline /path/to/postgres-extension \ --created-on 2026-07-29 \ --output pgextassure-baseline.json pgextassure scan /path/to/postgres-extension \ --baseline pgextassure-baseline.json \ --format grouped-json \ --fail-on high ``` 新的根本原因仍会阻断。基线化的发现依然可见。临时豁免需要确切的根本原因 ID、所有者、原因和过期日期;过期的豁免会再次阻断。请参阅[基线与抑制项](docs/admission-state.md)。 对于集中审查的关卡,请提供严格的组织策略: ``` pgextassure policy-template adoption \ --output pgextassure-policy.json pgextassure scan /path/to/postgres-extension \ --policy pgextassure-policy.json \ --format grouped-json \ --output pgextassure-grouped.json ``` 策略拥有该关卡的控制权,可以阻断特定的能力或规则,并控制是否允许使用基线/抑制机制。请参阅[组织策略](docs/organization-policy.md)。 创建一个确定性的、可独立验证的试运行产物: ``` pgextassure evidence create /path/to/postgres-extension \ --policy pgextassure-policy.json \ --created-on 2026-07-29 \ --component-name example-extension \ --output pgextassure-evidence.zip pgextassure evidence verify pgextassure-evidence.zip ``` Evidence Bundle 1.0 将报告、已分析的源码清单、覆盖率、有限的 SPDX 2.3 清单以及精确的控制输入字节绑定在一起。它不包含源文件载荷。它可以使用 GitHub/Sigstore 证明进行签名,也可以使用离线的公司 RSA 密钥进行签名: ``` pgextassure evidence sign pgextassure-evidence.zip \ --private-key corporate-release-key.pem \ --signer-id acme-security/postgresql-admission-key-01 \ --statement-output pgextassure-signature.json \ --signature-output pgextassure-signature.bin \ --public-key-output pgextassure-public-key.pem pgextassure evidence verify-signature pgextassure-evidence.zip \ --statement pgextassure-signature.json \ --signature pgextassure-signature.bin \ --public-key pgextassure-public-key.pem \ --expected-key-sha256 'sha256:TRUSTED_64_HEX_DIGEST' ``` 请参阅 [Evidence bundles](docs/evidence-bundles.md)、 [Corporate Evidence Signature Profile 1.0](docs/corporate-signatures.md) 以及 [企业试点](docs/enterprise-pilot.md)。 退出行为由 `--fail-on` 控制: ``` critical | high | medium | low | none ``` 当至少有一个发现达到或超过所选阈值时,进程将以非零状态退出。扫描器错误也会导致非零状态退出。 ## 发现示例 确切的呈现方式可能会演变,但每种格式都带有相同的核心证据:规则标识符、严重程度、位置、解释和修复建议。 ``` CRITICAL sql.security-definer-search-path sql/example--1.0.sql:42 SECURITY DEFINER function does not establish a constrained search_path. An attacker may be able to redirect an unqualified object reference. Evidence: CREATE FUNCTION example.refresh_cache() ... SECURITY DEFINER; Remediation: Set a safe search_path and schema-qualify referenced objects. Review the function against PostgreSQL's SECURITY DEFINER guidance. ``` 摘要示例: ``` PgExtAssure 0.1.0-alpha.8 Manifest: sha256:93c7a1aa82da96c290155124b31fcfaa15e369d105cef327c38c17e1b82d8128 Coverage: sha256:7cd80d20a4cdb7b1b88828e3d769f36a3353e6c955e07b65f717efa0d9c62a51 | Skipped: 0 Files: 6 | Findings: 0 (critical 0, high 0, medium 0, low 0) ``` 发现问题指明了审查工作;它们并不能证明漏洞的可利用性。 ## GitHub Action 该仓库包含一个复合 Action,它会安装本地 PgExtAssure 包并扫描请求的路径。目标扩展永远不会被执行。 ``` name: PgExtAssure on: pull_request: push: branches: [main] permissions: contents: read security-events: write jobs: scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: borborich/pgextassure@v0.1.0-alpha.8 with: path: . format: sarif output: pgextassure.sarif annotations: active max-annotations: "25" fail-on: high - name: Upload SARIF if: ${{ always() && github.event_name != 'pull_request' }} uses: github/codeql-action/upload-sarif@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3 with: sarif_file: pgextassure.sarif ``` Action 输入: | 输入项 | 默认值 | 含义 | | --- | --- | --- | | `path` | `.` | 扩展源代码目录或支持的输入文件 | | `format` | `sarif` | `text`、`json`、`grouped-json`、`review-json` 或 `sarif` | | `output` | `pgextassure.sarif` | 报告文件;设置为空字符串以输出到 stdout | | `generation-plan` | 空 | 可选的经过审查、固定的生成产物声明 | | `scope-plan` | 空 | 可选的经过审查、绑定摘要的根目录和确切排除项 | | `baseline` | 空 | 可选的经过审查的根本原因基线 | | `suppressions` | 空 | 可选的归属于所有者的过期抑制项 | | `evaluated-on` | 当前 UTC 日期 | 显式指定抑制项的 `YYYY-MM-DD` 评估日期 | | `policy` | 空 | 可选的拥有该关卡的组织策略 | | `annotations` | `none` | `active`、`all` 或 `none` 根本原因注释 | | `max-annotations` | `25` | 最大注释行数,从 2 到 50 | | `evidence-output` | 空 | 启用证据模式并写入 Bundle 1.0 | | `evidence-predicate-output` | `pgextassure-evidence-predicate.json` | 验证过的自定义证明谓词 | | `evidence-sbom-output` | `pgextassure-sbom.spdx.json` | 验证过的 SPDX 清单 | | `evidence-created-on` | 当前 UTC 日期 | 可选的显式打包日期 | | `component-name` | `postgresql-extension` | 非机密的 SPDX 组件名称 | | `component-version` | 空 | 可选的 SPDX 组件版本 | | `fail-on` | `none` | 最低的阻断严重级别,或 `none` | | `python-version` | `3.11` | 用于运行 PgExtAssure 的 Python 版本 | 注释按根本原因分组,省略匹配的源证据,并受 `max-annotations` 限制。`active` 包含活动和已过期的决策;`all` 还会将已接受的基线和抑制决策作为通知发出。由于工作流命令使用 stdout,因此必须提供报告输出。请参阅 [GitHub 注释](docs/github-annotations.md)。 当设置了 `evidence-output` 时,Action 会创建并验证一个打包文件,而不是生成独立报告。GitHub 注释在此模式下被禁用。Action 将打包文件、谓词和 SPDX 路径作为 `evidence-bundle`、`evidence-predicate` 和 `evidence-sbom` 输出公开。签名仍然需要调用方显式拥有的 `actions/attest` 步骤和 OIDC 权限。 在更高保证级别的工作流中,请固定使用已发布的提交 SHA。上传 SARIF 会将生成的报告发送给 GitHub;在启用该步骤之前,请审查您的仓库可见性、保留期限和访问设置。该示例仅在非 PR 事件时上传,因为 fork 的 pull request 会收到只读的 token。SARIF 上传适用于公共仓库,以及启用了 GitHub Code Security 且符合条件的私有/内部仓库。 当目标位于该工作空间内时,SARIF 的 artifact URI 将设置为相对于 `GITHUB_WORKSPACE` 的路径,并且特殊路径字符会进行 URI 编码。内置的 `actions/setup-python` v7 运行时需要 GitHub Actions Runner 2.327.1 或更高版本;GitHub 托管的运行器已满足此要求。 该 Action 默认仅用于审计,因为某些高严重级别的记录属于特权能力清单,而非已证实的缺陷。只有在审查了基线并选择了适合目标平台的策略之后,才应显式设置 `fail-on`。 ## 支持的输入 静态 MVP 仅扫描本地文件。它适用于已解压的 PostgreSQL 扩展源码树,包含以下部分或全部内容: - 主文件和特定版本的 `*.control` 文件; - 生成的控制模板 (`*.control.in`); - 匹配的安装和升级 `*.sql` / `*.sql.in` 产物,包括它们的版本图; - C/C 头文件源码 (`*.c`, `*.h`); - Rust 源码 (`*.rs`) 和 `Cargo.toml`。 它不会拉取仓库、下载发布版本、解压不受信任的压缩包、连接到 PostgreSQL 或解析包依赖。请扫描一个已经检出的、有界限的目录。如果未找到支持的源文件、支持的源文件不是常规文件,或者源码树包含可能逃逸出审查边界的软链接目录/源文件时,目录扫描将以失败关闭。它还对总条目数、目录数、路径深度、路径长度、源文件数、字节数和发现数量强制执行限制。 每份报告都会公开确切的静态边界:已分析的文件会在清单中进行内容哈希处理,而不支持的条目将出现在有限的仅限元数据的跳过文件清单中。请参阅 [报告架构与覆盖率](docs/report-schemas.md)。 有关已实施的检查和输入覆盖率,请参阅[规则参考](docs/rules.md)。 ## MVP 会检查什么 规则围绕准入前的问题进行分组: - 控制元数据是否声明了特权安装、敏感要求或递归包含的配置? - 安装或升级脚本是否包含 `COPY ... PROGRAM`、服务器端文件访问、不受信任的过程语言或公共执行授权? - 特权函数是否缺少防御性配置,例如受限的 `search_path`? - C 或 Rust 源码是否表明存在需要手动审查的文件系统、进程、网络、后台工作线程或不安全代码能力? - 安装/升级图能否到达声明的默认版本,并且每个 artifact 是否都能与一个明确的控制文件作用域相关联? PgExtAssure 倾向于提供证据和修复建议,而不是单一的晦涩评分。一个发现可能是真正的安全缺陷、故意的特权能力,或者是需要在将来版本中抑制的误报。 ## 明确的限制 静态 MVP: - 不会执行 SQL 或原生代码; - 不能证明没有恶意或易受攻击的行为; - 不能完全对 PL/pgSQL、动态 SQL、C/Rust 语义、PostgreSQL 规划器行为或运行时配置进行建模; - 不能确定扫描的源码树与分发的二进制文件匹配; - 不会检查传递性的构建依赖项或编译器行为; - 不会验证安装/升级的等效性; - 不能替代来源验证、签名检查、沙盒动态分析、人工审查或生产环境控制; - 可能会出现误报和漏报。 为了防止恶意输入造成无限制的报告,每个文件针对每条规则最多发出 32 个发现;当省略了其他匹配项时,第一个保留的发现会记录下来。如果超过了全局发现限制,扫描将失败,而不是返回部分报告。 不要仅凭干净的 PgExtAssure 结果作为授予超级用户、文件系统、进程、网络或预加载权限的理由。 ## PgExtAssure 如何与其他工具配合使用 这些工具解决的是不同的问题,可以与 PgExtAssure 结合使用。 - [pgspot](https://github.com/timescale/pgspot) 是一款成熟的、基于 AST 的 PostgreSQL SQL 和 PL/pgSQL 扩展安全专家级工具。PgExtAssure 内置的 SQL 检查只是一个微小的离线基线,并非旨在取代 pgspot。生产环境的保证流程应该接入 pgspot 的发现,而不是重复其更深层次的 SQL 分析。 - [phostile](https://github.com/Aiven-Open/pghostile) 创建对抗性数据库对象并执行扩展测试,以暴露提权漏洞。它是一种极具价值的动态测试,必然会在 PostgreSQL 中执行 SQL。PgExtAssure 的默认扫描保持非执行状态,涵盖了包元数据、原生能力、更新拓扑、证据标准化和 CI 策略。 - [CMU ExtAnalyzer](https://github.com/cmu-db/ext-analyzer) 静态统计扩展 API 的使用情况,并动态测量安装和跨扩展的兼容性。PgExtAssure范围仅限于安全准入证据,而不是生态系统兼容性研究。 - [Splinter](https://github.com/supabase/splinter) 针对 PostgreSQL/Supabase 项目现有的 schema 运行 SQL lints。PgExtAssure 在扩展包被准入或安装之前对其进行检查。 - [pgextwlist](https://github.com/dimitri/pgextwlist) 强制执行运行时白名单,并可以以提升的权限安装已批准的扩展。PgExtAssure 为决定哪些内容应进入该白名单提供可重复的证据;它不强制执行安装策略。 - [pg_validate_extupgrade](https://github.com/rjuju/pg_validate_extupgrade) 在 PostgreSQL 中安装并升级扩展,然后比较生成的对象。它验证的是升级路径的等效性,而不是源码安全性,并且必然会执行它所测试的扩展路径。 ## 威胁模型 PgExtAssure 假定被扫描的源码树可能是恶意的或已被篡改。MVP 将该源码树保持在边界的数据端:它只读取已识别的文件,不会调用其构建系统、SQL、钩子或二进制文件。 主要受保护的资产是: - PostgreSQL 超级用户和数据库所有者权限; - 数据库的机密性、完整性和可用性; - PostgreSQL 服务器进程和主机; - 扩展准入决策及其证据的完整性。 主要威胁来自恶意的扩展作者、被破坏的上游发布版本,或者是引入了不安全构造的原本无害的维护者。 仅限运行时的行为、未知的解析器规避、传递性依赖攻击以及从源码到二进制文件的替换,仍然不在静态 MVP 的保证范围之内。 在将 PgExtAssure 用作 CI 关卡之前,请阅读完整的[威胁模型](docs/threat-model.md)。 ## 路线图:隔离的动态保证 下一层的保证特意与静态扫描器分开: 1. 重现并证明从源码到 artifact 的构建; 2. 创建一个临时的、非特权的 PostgreSQL 环境,该环境没有生产凭据,具有默认拒绝的网络访问权限、资源限制和一次性文件系统; 3. 执行安装、升级、回滚和代表性的调用; 4. 捕获目录差异、文件系统/进程/网络尝试、崩溃以及其他行为证据; 5. 销毁环境并将报告链接到确切的源码和 artifact 摘要。 动态分析仍然只是证据,而非认证。详细信息请参阅[路线图](docs/roadmap.md)。 ## 公共语料库循环 PgExtAssure 旨在创建一个有用的公共证据语料库,而无需默认上传源码: 1. 维护者在本地或 CI 中运行扫描器; 2. 他们可以选择发布包含规则 ID、严重程度、扫描器/规则集版本、扩展版本和加密摘要的标准化报告——而不是私有源码; 3. 接受的修复将发现与修复 diff 联系起来; 4. 该语料库可改进回归测试夹具、规则精度和版本化的兼容性/准入徽章; 5. 更好的规则会生成更有用的本地报告并吸引更多贡献者。 静态 MVP 中不存在语料库上传或遥测功能。未来的任何贡献流程都必须是显式的主动选择,预览确切的载荷,并支持删除。 ## 隐私 - 扫描器本身在本地文件上运行,在 MVP 阶段不会故意发出网络请求。 - PgExtAssure 不会故意将源码、发现结果或遥测数据发送到托管服务。CI 设置和包安装步骤可能会独立于扫描下载 Python 或构建工具。 - 报告可能包含文件名、行号和源码摘录。请将它们视为潜在的敏感信息。 - `--output` 仅写入您选择的路径。 - 第三方 CI 系统、artifact 上传步骤和 SARIF 服务有其自己的数据处理策略;PgExtAssure 无法控制它们。 ## 项目文档 - [安全策略](SECURITY.md) - [贡献指南](CONTRIBUTING.md) - [支持](SUPPORT.md) - [更新日志](CHANGELOG.md) - [行为准则](CODE_OF_CONDUCT.md) - [规则参考](docs/rules.md) - [威胁模型](docs/threat-model.md) - [生成计划](docs/generation-plans.md) - [范围计划](docs/scope-plans.md) - [基线与抑制项](docs/admission-state.md) - [组织策略](docs/organization-policy.md) - [GitHub 注释](docs/github-annotations.md) - [Evidence bundles](docs/evidence-bundles.md) - [Agent Review Pack](docs/agent-review-pack.md) - [企业试点](docs/enterprise-pilot.md) - [路线图](docs/roadmap.md) - [公共语料库试点](benchmarks/public-corpus/README.md) ## 开发 ``` python -m pip install -e . python -m unittest discover -s tests -v ``` CI 工作流会运行相同的单元测试命令。 ## 许可证 Apache License 2.0。请参阅 [LICENSE](LICENSE)。
标签:APT组织, PostgreSQL扩展, Python, 代码审查, 无后门, 逆向工具, 错误基检测, 静态代码分析