Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

## Table of Contents

- [What's new (2026-06-21) — SLSA Build Provenance](#whats-new-2026-06-21--slsa-build-provenance)
- [What's new (2026-06-21) — Feature Flags](#whats-new-2026-06-21--feature-flags)
- [What's new (2026-06-21) — Text Diff, Patch & Three-Way Merge](#whats-new-2026-06-21--text-diff-patch--three-way-merge)
- [What's new (2026-06-21) — Calendar Recurrence Rules (RRULE)](#whats-new-2026-06-21--calendar-recurrence-rules-rrule)
Expand Down Expand Up @@ -121,6 +122,12 @@

---

## What's new (2026-06-21) — SLSA Build Provenance

Attest what was built. Full reference: [`docs/source/Eng/doc/new_features/v69_features_doc.rst`](docs/source/Eng/doc/new_features/v69_features_doc.rst).

- **`build_provenance` / `subject_for` / `verify_provenance` / `write_provenance`** (`AC_build_provenance`, `AC_verify_provenance`): the framework signs action files and inventories deps (SBOM) but couldn't attest *what was produced by which build*. This adds an in-toto v1 Statement with a SLSA v1 provenance predicate over file `sha256` digests, and a verifier that re-hashes the artifacts (tamper → mismatch). Complements `action_signing` + `sbom`; pure-stdlib `hashlib`+`json`, fully offline.

## What's new (2026-06-21) — Feature Flags

Toggle behavior with targeting & rollout. Full reference: [`docs/source/Eng/doc/new_features/v68_features_doc.rst`](docs/source/Eng/doc/new_features/v68_features_doc.rst).
Expand Down
7 changes: 7 additions & 0 deletions README/README_zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

## 目录

- [本次更新 (2026-06-21) — SLSA 构建来源证明](#本次更新-2026-06-21--slsa-构建来源证明)
- [本次更新 (2026-06-21) — 功能旗标](#本次更新-2026-06-21--功能旗标)
- [本次更新 (2026-06-21) — 文本 Diff、应用与三方合并](#本次更新-2026-06-21--文本-diff应用与三方合并)
- [本次更新 (2026-06-21) — 日历周期规则(RRULE)](#本次更新-2026-06-21--日历周期规则rrule)
Expand Down Expand Up @@ -120,6 +121,12 @@

---

## 本次更新 (2026-06-21) — SLSA 构建来源证明

证明构建产生了什么。完整参考:[`docs/source/Zh/doc/new_features/v69_features_doc.rst`](../docs/source/Zh/doc/new_features/v69_features_doc.rst)。

- **`build_provenance` / `subject_for` / `verify_provenance` / `write_provenance`**(`AC_build_provenance`、`AC_verify_provenance`):框架能签署动作文件并盘点依赖项(SBOM),却无法证明*哪个构建产生了什么*。本功能补上 in-toto v1 Statement,携带覆盖文件 `sha256` 摘要的 SLSA v1 provenance predicate,并附上会重新哈希产物的验证器(篡改 → 不符)。与 `action_signing` + `sbom` 互补;纯标准库 `hashlib`+`json`,完全离线。

## 本次更新 (2026-06-21) — 功能旗标

以目标规则与推出切换行为。完整参考:[`docs/source/Zh/doc/new_features/v68_features_doc.rst`](../docs/source/Zh/doc/new_features/v68_features_doc.rst)。
Expand Down
7 changes: 7 additions & 0 deletions README/README_zh-TW.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

## 目錄

- [本次更新 (2026-06-21) — SLSA 建置來源證明](#本次更新-2026-06-21--slsa-建置來源證明)
- [本次更新 (2026-06-21) — 功能旗標](#本次更新-2026-06-21--功能旗標)
- [本次更新 (2026-06-21) — 文字 Diff、套用與三方合併](#本次更新-2026-06-21--文字-diff套用與三方合併)
- [本次更新 (2026-06-21) — 行事曆週期規則(RRULE)](#本次更新-2026-06-21--行事曆週期規則rrule)
Expand Down Expand Up @@ -120,6 +121,12 @@

---

## 本次更新 (2026-06-21) — SLSA 建置來源證明

證明建置產生了什麼。完整參考:[`docs/source/Zh/doc/new_features/v69_features_doc.rst`](../docs/source/Zh/doc/new_features/v69_features_doc.rst)。

- **`build_provenance` / `subject_for` / `verify_provenance` / `write_provenance`**(`AC_build_provenance`、`AC_verify_provenance`):框架能簽署動作檔並盤點相依套件(SBOM),卻無法證明*哪個建置產生了什麼*。本功能補上 in-toto v1 Statement,攜帶覆蓋檔案 `sha256` 摘要的 SLSA v1 provenance predicate,並附上會重新雜湊產物的驗證器(竄改 → 不符)。與 `action_signing` + `sbom` 互補;純標準函式庫 `hashlib`+`json`,完全離線。

## 本次更新 (2026-06-21) — 功能旗標

以目標規則與推出切換行為。完整參考:[`docs/source/Zh/doc/new_features/v68_features_doc.rst`](../docs/source/Zh/doc/new_features/v68_features_doc.rst)。
Expand Down
48 changes: 48 additions & 0 deletions docs/source/Eng/doc/new_features/v69_features_doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
SLSA Build Provenance
=====================

The framework can sign action files (HMAC) and inventory dependencies (SBOM),
but it could not attest *what was produced by which build* — the SLSA
provenance attestation that binds artifact digests to build metadata. This adds
an in-toto v1 Statement carrying a SLSA v1 provenance predicate over file
``sha256`` digests, plus a verifier that re-hashes the artifacts.

Pure standard library (``hashlib`` + ``json``); fully offline; imports no
``PySide6``. DSSE signing of the statement is left as an optional later layer.

Headless API
------------

.. code-block:: python

from je_auto_control import (
subject_for, build_provenance, write_provenance, verify_provenance)

subjects = [subject_for("dist/app.whl"), subject_for("sbom.cdx.json")]
statement = build_provenance(
subjects, builder_id="github-actions",
metadata={"invocation_id": "run-42", "started_on": "2026-06-21T00:00:00Z"})
write_provenance(statement, "app.intoto.jsonl")

# later, on the consumer side
mismatches = verify_provenance(statement, {"app.whl": "dist/app.whl"})
if not mismatches:
print("artifact digests verified")

``subject_for`` hashes a file into an in-toto subject (``subject_for_bytes``
does the same for in-memory data); ``build_provenance`` wraps the subjects in
the in-toto v1 / SLSA v1 envelope (``buildDefinition`` + ``runDetails``);
``verify_provenance`` re-hashes each named file and returns any digest
mismatch. It complements ``action_signing`` (which signs action JSON) and
``sbom`` (which inventories dependencies) — attest the SBOM and signed
artifacts together.

Executor commands
-----------------

``AC_build_provenance`` takes ``paths`` (a list, or JSON string) plus optional
``builder_id`` / ``build_type`` and returns ``{statement}``.
``AC_verify_provenance`` takes a ``statement`` and ``files`` (name->path) and
returns ``{ok, mismatches}``. Both are exposed as MCP tools
(``ac_build_provenance`` / ``ac_verify_provenance``) and as Script Builder
commands under **Security**.
1 change: 1 addition & 0 deletions docs/source/Eng/eng_index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ Comprehensive guides for all AutoControl features.
doc/new_features/v66_features_doc
doc/new_features/v67_features_doc
doc/new_features/v68_features_doc
doc/new_features/v69_features_doc
doc/ocr_backends/ocr_backends_doc
doc/observability/observability_doc
doc/operations_layer/operations_layer_doc
Expand Down
42 changes: 42 additions & 0 deletions docs/source/Zh/doc/new_features/v69_features_doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
SLSA 建置來源證明(Provenance)
==============================

框架能簽署動作檔(HMAC)並盤點相依套件(SBOM),但無法證明*哪個建置產生了什麼* —— 也就是把產物
摘要綁定到建置中繼資料的 SLSA 來源證明。本功能補上一個 in-toto v1 Statement,攜帶覆蓋檔案
``sha256`` 摘要的 SLSA v1 provenance predicate,並附上會重新雜湊產物的驗證器。

純標準函式庫(``hashlib`` + ``json``);完全離線;不匯入 ``PySide6``。Statement 的 DSSE 簽署留作
選用的後續層。

無頭 API
--------

.. code-block:: python

from je_auto_control import (
subject_for, build_provenance, write_provenance, verify_provenance)

subjects = [subject_for("dist/app.whl"), subject_for("sbom.cdx.json")]
statement = build_provenance(
subjects, builder_id="github-actions",
metadata={"invocation_id": "run-42", "started_on": "2026-06-21T00:00:00Z"})
write_provenance(statement, "app.intoto.jsonl")

# 之後,在消費端
mismatches = verify_provenance(statement, {"app.whl": "dist/app.whl"})
if not mismatches:
print("產物摘要已驗證")

``subject_for`` 把檔案雜湊成 in-toto subject(``subject_for_bytes`` 對記憶體資料做同樣的事);
``build_provenance`` 把 subjects 包進 in-toto v1 / SLSA v1 信封(``buildDefinition`` +
``runDetails``);``verify_provenance`` 重新雜湊每個具名檔案並回傳任何摘要不符。它與
``action_signing``(簽署動作 JSON)及 ``sbom``(盤點相依套件)互補 —— 可一併證明 SBOM 與已簽署的
產物。

執行器命令
----------

``AC_build_provenance`` 接受 ``paths``(清單或 JSON 字串)及選用的 ``builder_id`` /
``build_type``,回傳 ``{statement}``。``AC_verify_provenance`` 接受 ``statement`` 與 ``files``
(name->path),回傳 ``{ok, mismatches}``。兩者皆以 MCP 工具(``ac_build_provenance`` /
``ac_verify_provenance``)以及 Script Builder 中 **Security** 分類下的命令提供。
1 change: 1 addition & 0 deletions docs/source/Zh/zh_index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ AutoControl 所有功能的完整使用指南。
doc/new_features/v66_features_doc
doc/new_features/v67_features_doc
doc/new_features/v68_features_doc
doc/new_features/v69_features_doc
doc/ocr_backends/ocr_backends_doc
doc/observability/observability_doc
doc/operations_layer/operations_layer_doc
Expand Down
7 changes: 7 additions & 0 deletions je_auto_control/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,11 @@
Flag, FlagStore, assign_variant, evaluate_flag, is_enabled,
percentage_bucket,
)
# SLSA build provenance (in-toto v1 statements over file digests)
from je_auto_control.utils.provenance import (
build_provenance, subject_for, subject_for_bytes, verify_provenance,
write_provenance,
)
# Background popup/interrupt watchdog (unattended automation)
from je_auto_control.utils.watchdog import (
PopupWatchdog, WatchdogRule, default_popup_watchdog,
Expand Down Expand Up @@ -841,6 +846,8 @@ def start_autocontrol_gui(*args, **kwargs):
"unified_diff",
"Flag", "FlagStore", "assign_variant", "evaluate_flag", "is_enabled",
"percentage_bucket",
"build_provenance", "subject_for", "subject_for_bytes",
"verify_provenance", "write_provenance",
# MCP server
"AuditLogger", "HttpMCPServer", "MCPContent", "MCPPrompt",
"MCPPromptArgument", "MCPResource", "MCPServer", "MCPTool",
Expand Down
20 changes: 20 additions & 0 deletions je_auto_control/gui/script_builder/command_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -1324,6 +1324,26 @@ def _add_misc_specs(specs: List[CommandSpec]) -> None:
),
description="Evaluate SBOM licenses against allow/deny SPDX lists.",
))
specs.append(CommandSpec(
"AC_build_provenance", "Security", "Provenance: Build (SLSA)",
fields=(
FieldSpec("paths", FieldType.STRING,
placeholder='["dist/app.whl", "sbom.cdx.json"]'),
FieldSpec("builder_id", FieldType.STRING, optional=True,
placeholder="je_auto_control"),
),
description="Build a SLSA in-toto provenance statement over files.",
))
specs.append(CommandSpec(
"AC_verify_provenance", "Security", "Provenance: Verify",
fields=(
FieldSpec("statement", FieldType.STRING,
placeholder='{"subject": [...], "predicate": {...}}'),
FieldSpec("files", FieldType.STRING,
placeholder='{"app.whl": "dist/app.whl"}'),
),
description="Re-hash files against a provenance statement; {ok, mismatches}.",
))
specs.append(CommandSpec(
"AC_jwt_encode", "Security", "JWT: Sign Token",
fields=(
Expand Down
27 changes: 27 additions & 0 deletions je_auto_control/utils/executor/action_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2928,6 +2928,31 @@ def _rate_limit(name: str, rate: float = 1.0, capacity: float = 1.0,
"wait": round(bucket.time_until_available(float(n)), 4)}


def _build_provenance(paths: Any, builder_id: str = "je_auto_control",
build_type: str = "https://je-auto-control/buildtype/v1"
) -> Dict[str, Any]:
"""Adapter: build a SLSA provenance statement over a list of file paths."""
import json
from je_auto_control.utils.provenance import build_provenance, subject_for
if isinstance(paths, str):
paths = json.loads(paths)
subjects = [subject_for(path) for path in paths]
return {"statement": build_provenance(
subjects, builder_id=builder_id, build_type=build_type)}


def _verify_provenance(statement: Any, files: Any) -> Dict[str, Any]:
"""Adapter: re-hash files (name->path) against a provenance statement."""
import json
from je_auto_control.utils.provenance import verify_provenance
if isinstance(statement, str):
statement = json.loads(statement)
if isinstance(files, str):
files = json.loads(files)
mismatches = verify_provenance(statement, files)
return {"ok": not mismatches, "mismatches": mismatches}


def _evaluate_flag(flags: Any, key: str, context: Any = None) -> Dict[str, Any]:
"""Adapter: evaluate a feature flag (flags/context dict or JSON string)."""
import json
Expand Down Expand Up @@ -3880,6 +3905,8 @@ def __init__(self):
"AC_rrule_next": _rrule_next,
"AC_evaluate_flag": _evaluate_flag,
"AC_flag_enabled": _flag_enabled,
"AC_build_provenance": _build_provenance,
"AC_verify_provenance": _verify_provenance,
"AC_unified_diff": _unified_diff,
"AC_apply_unified": _apply_unified,
"AC_three_way_merge": _three_way_merge,
Expand Down
28 changes: 27 additions & 1 deletion je_auto_control/utils/mcp_server/tools/_factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -3343,6 +3343,32 @@ def rate_limit_tools() -> List[MCPTool]:
]


def provenance_tools() -> List[MCPTool]:
return [
MCPTool(
name="ac_build_provenance",
description=("Build a SLSA in-toto v1 provenance statement over a "
"list of file 'paths' (sha256 subjects). Returns "
"{statement}."),
input_schema=schema(
{"paths": {"type": "array"}, "builder_id": {"type": "string"}},
["paths"]),
handler=h.build_provenance,
annotations=READ_ONLY,
),
MCPTool(
name="ac_verify_provenance",
description=("Re-hash 'files' (name->path) against a provenance "
"'statement'. Returns {ok, mismatches}."),
input_schema=schema(
{"statement": {"type": "object"}, "files": {"type": "object"}},
["statement", "files"]),
handler=h.verify_provenance,
annotations=READ_ONLY,
),
]


def feature_flag_tools() -> List[MCPTool]:
return [
MCPTool(
Expand Down Expand Up @@ -4719,7 +4745,7 @@ def media_assert_tools() -> List[MCPTool]:
jsonpath_tools, json_schema_tools, vuln_scan_tools, vex_tools,
license_policy_tools, jwt_tools, rate_limit_tools, json_patch_tools,
search_index_tools, stats_tools, recurrence_tools, text_diff_tools,
feature_flag_tools,
feature_flag_tools, provenance_tools,
saga_tools, decision_table_tools, locator_repair_tools,
pii_text_tools, sarif_tools,
screen_record_tools,
Expand Down
12 changes: 12 additions & 0 deletions je_auto_control/utils/mcp_server/tools/_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1664,6 +1664,18 @@ def rrule_next(rule, dtstart, now=None):
return {"next": moment.isoformat() if moment else None}


def build_provenance(paths, builder_id="je_auto_control"):
from je_auto_control.utils.provenance import build_provenance, subject_for
subjects = [subject_for(path) for path in paths]
return {"statement": build_provenance(subjects, builder_id=builder_id)}


def verify_provenance(statement, files):
from je_auto_control.utils.provenance import verify_provenance as _verify
mismatches = _verify(statement, files)
return {"ok": not mismatches, "mismatches": mismatches}


def evaluate_flag(flags, key, context=None):
from je_auto_control.utils.feature_flags import (
FlagStore, evaluate_flag as _ev)
Expand Down
10 changes: 10 additions & 0 deletions je_auto_control/utils/provenance/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"""SLSA build provenance (in-toto v1 statements) over file digests."""
from je_auto_control.utils.provenance.provenance import (
build_provenance, subject_for, subject_for_bytes, verify_provenance,
write_provenance,
)

__all__ = [
"build_provenance", "subject_for", "subject_for_bytes",
"verify_provenance", "write_provenance",
]
Loading
Loading