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, 代码审查, 无后门, 逆向工具, 错误基检测, 静态代码分析