chunk-invariance

io.github.ninadphalak由 GitHub 账号 ninadphalak 发布v0.2.0更新于 Oct 26, 2026

Checks whether your streaming redactor leaks a secret split across chunks; writes a pytest for it.

已验证STDIO仅桌面开发者工具安全与监控

概览

AI 生成的概览

检查流式脱敏器或护栏是否会在分块边界泄露被切开的敏感值,并生成对应的 pytest 回归测试。

可以做什么
  • 检查流式过滤器:从项目中导入函数,对样本输入尝试每一种两段切分,并指出最小的失败切分。
  • 生成基于 assert_chunk_invariant 的 pytest 文件,让检查留在 CI 中。
  • 用一段话解释逐块过滤为何会泄露被切分的值。
  • 使用默认的假样本(邮箱、SSN、卡号、几种 API 密钥格式),也可传入自编样本。
适用场景
适合维护流式脱敏器、护栏或改写器时使用,用来确认流式输出与整体输入过滤结果在任意分块下一致,并把该检查作为回归测试留在仓库中。
运行要求
本地 stdio 进程,从 PyPI 安装并带 mcp 附加项(Python 3.10 或更高版本),通过 chunk-invariance-mcp 命令启动。目标函数必须是 project_dir 下源文件中的 Python 函数;若项目有 .venv 则使用它。未声明需要账号、密钥或网络访问。
安装前请注意
检查函数会在本机以独立进程运行你的代码,且环境变量被精简,因此只应对自己的项目批准调用。切勿把真实密钥或个人数据作为样本:样本、泄露值、异常消息和生成的测试文件都会返回给助手,并随对话发送给其模型提供商。服务器本身不联网、不写文件,但它运行的代码可以。

安装

在 SourceWeft 中

  1. 打开 控制台中的 chunk-invariance,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。

其他 MCP 客户端

参照 仓库 中的启动说明。

README

chunk-invariance

A streaming filter (a redactor, a guardrail, a rewriter) is correct only if, for every way of splitting an input into chunks, the concatenated streamed output equals the output of filtering the whole input at once. This package turns that rule into one assertion.

python
from chunk_invariance import assert_chunk_invariant
def redact(text):                 # your filter on the whole input    return text.replace("sk-AbCd1234", "[KEY]")
def redact_stream(chunks):        # your filter on a stream: takes chunks, yields chunks    for chunk in chunks:        yield redact(chunk)       # redacts each chunk alone
def test_streaming_redaction():    assert_chunk_invariant(redact_stream, redact, "use sk-AbCd1234 now")

That test fails, and names the smallest split that breaks it:

chunk_invariance.core.ChunkInvarianceError: stream filter is not chunk-invariant  input:    'use sk-AbCd1234 now'  split:    ['use s', 'k-AbCd1234 now'] (cut at 5)  whole:    'use [KEY] now'  streamed: 'use sk-AbCd1234 now'  split #6 in the order tried; generated splits run fewest parts first
bash
pip install chunk-invariancepip install "chunk-invariance[hypothesis]"   # optional Hypothesis strategiespip install "chunk-invariance[mcp]"          # optional MCP server for AI assistants

What it catches

  • A filter that evaluates each chunk alone, so a value cut across two chunks matches neither half.
  • A filter that prepends a fixed carryover from the previous chunk. The match fires, but the start of the value was already emitted with the previous chunk.
  • A hold-back that is shorter than the pattern, a missing end-of-stream flush, and output that is corrupted rather than leaked (any difference from the whole-input output fails).

chunk_invariance.examples has one correct filter (hold_back) and the two wrong designs (design_a, design_b) for the same pattern, as a worked comparison.

What it does not catch

It tests a filter you can call in-process. It does not test a running gateway, where the chunks are decided by an upstream model, a network and an SSE parser. For that, use pii-leak-benchmark, which sends fragmented responses through the gateway and checks what reaches the client.

It checks the splits you ask for. The default is the whole input plus every single cut, which catches a value cut in two. splits="all" tries every split of a short input (up to 16 characters); splits=3 tries every split into at most three parts; an explicit list of chunk lists runs exactly those. It cannot prove a filter correct for inputs you did not give it.

Use it from your AI assistant

Do you use Claude Code, Cursor or Claude Desktop? The package has an optional MCP server. With it you can ask the assistant to run the every-split check on your own streaming function, and then to add the test to your repo.

bash
pip install "chunk-invariance[mcp]"      # Python 3.10 or laterclaude mcp add chunk-invariance -- chunk-invariance-mcp

For Claude Desktop, add this to claude_desktop_config.json (give the full path to the command if Claude Desktop cannot find it on your PATH):

json
{  "mcpServers": {    "chunk-invariance": { "command": "chunk-invariance-mcp" }  }}

Then ask, for example: "Check whether redact_stream in myapp/filters.py leaks values split across chunks, and add a regression test for it."

There are three tools. check_stream_filter imports your function (myapp.filters:redact_stream), streams each sample through it in every two-part split and tells you which split broke it, for example that 'sk-AbCd1234' came out unredacted when the input was cut into 'use s' and 'k-AbCd1234 now'. write_regression_test returns a pytest file built on assert_chunk_invariant, so the check stays in your CI. explain is one paragraph on why per-chunk filtering leaks.

The default samples (an email, an SSN, a card number and a few API-key shapes) are all fake. Pass your own made-up samples in the formats your filter redacts, such as a key you typed yourself in your provider's format. A sample the filter does not change tests nothing, and the tool says so.

Never pass a real secret or real personal data as a sample. The samples, and any value your filter leaks, come back in the tool's result, and your assistant sends that result to its model provider like the rest of the conversation. So does the message of any exception your code raises, but nothing your code prints. The same samples also end up in the generated test file.

Checking a function runs your code. The server imports your module on your own machine, in a separate process that is stopped after a timeout (60 seconds by default). If your project has a .venv (or venv), that is the Python it uses; the python argument accepts only that interpreter or the server's own. The target, flush_target and whole_target must be Python functions defined in source files under project_dir: built-ins, the standard library and installed packages (anything in the interpreter's site-packages, including your .venv's) are refused, and so is a project_dir that is or contains the interpreter's own library. The check process gets a minimal environment (PATH, HOME, temp and locale variables), not the server's, so a filter that reads its configuration from other environment variables will not see them. project_dir is chosen by the assistant's tool call, so whatever code lives under it is what can run: approve calls only for your own project. The server itself makes no network calls, sends no telemetry and writes no files, though the code it runs can do whatever that code does. Saving the test file is up to you and your assistant.

API

NameDoes
assert_chunk_invariant(stream_filter, whole_filter, text, *, splits="all-two-part")Raises ChunkInvarianceError (an AssertionError) on the first failing split, fewest parts first. Returns the number of splits checked. Works on str and bytes.
assert_chunk_invariant_async(...)The same, for an async generator filter, inside an async test.
two_part_splits(text)Every split into two non-empty parts.
all_splits(text, max_parts=None)Every split into at most max_parts non-empty parts, the whole input first.
per_chunk(process, *, state=dict, flush=None)Adapts process(chunk, state) -> output and flush(state) -> output, with fresh state per stream.
from_async(async_stream_filter)Adapts an async generator filter for the synchronous assertion.
strategies.splits_of(text, max_parts=None)Hypothesis strategy: random splits of text, shrinking toward fewer cuts.
strategies.text_and_splits(texts, max_parts=None)Hypothesis strategy: (text, chunks) pairs. texts is itself a strategy, such as st.sampled_from([...]), not a list.

stream_filter is called once per split, so it must start from fresh state each time.

Background

The invariant and the two wrong designs are described in Phalak, N. (2026), Split-Boundary Leaks in Streaming Guardrails, Zenodo, 10.5281/zenodo.22909585.

Standard library only (the mcp extra adds the MCP SDK, for the server alone). Apache-2.0. A TypeScript version with the same API is published on npm as chunk-invariance.

来源:chunk-invariance/README.md,提交 f95ba91

工具

0
SourceWeft 尚未在沙箱中启动过该软件包,因此其工具未知。

版本历史

1
  1. v0.2.0最新Oct 19, 2026

评分

已满足适用检查项的 50%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通25 分

    请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。

  • 已声明认证方式10 分

    请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。

  • 已列出工具15 分

    请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。

  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分