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
16 changes: 8 additions & 8 deletions security/browser-headers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ The candidate is disabled and both approval flags remain false.
## Reviewed input and source prerequisite

`rust-policy.json` binds the accepted Rust 4.12.0 public crate and the
[protected documentation run](https://github.com/durable-workflow/sdk-rust/actions/runs/41939116087),
artifact 13375154247. Its ZIP SHA-256 is recorded with its tooling revision.
[protected documentation run](https://github.com/durable-workflow/sdk-rust/actions/runs/41954374074),
artifact 13382143373. Its ZIP SHA-256 is recorded with its tooling revision.
The full input contains 484 regular files and 372 HTML pages.

The earlier artifact's `help.html` and `settings.html` contained executable
links and inline history-back handlers. The generator refuses them. The merged
[SDK source correction](https://github.com/durable-workflow/sdk-rust/pull/207)
provides a safe crate-index fallback and an external history listener. The sole
automatic protected build and deployment for the current proposal are successful
at tooling revision `1ea3bfc3965423534b5bbbed700371f126835fc3`. Public package/source
at tooling revision `2b45fd7b12a6e9460666045a3c7f644860caea3b`. Public package/source
identities remain unchanged. Eight live identity, navigation, help/settings and
API files matched this artifact on November 5, 2026. The
[current staging proposal](rust-stage-proposal.md) records their hashes and the
Expand All @@ -27,11 +27,11 @@ separate acceptance that remains pending.
The current inventory describes that accepted published artifact.
`source_preparation` is null. `history/` retains the earlier local derivative and
the accepted SDK #207 artifact's browser result as historical evidence only.
The derivative was not a public artifact. The current artifact changes the build
identity and rustdoc's current-crate attribute on help/settings. Its executable
script hashes and generated rule are unchanged. That static comparison does not
carry forward the earlier browser pass. Current compatibility is unrun and the
rule remains disabled.
The derivative was not a public artifact. SDK #212 retains every page and asset
byte from the prior SDK #209 artifact.
Only the build identity changes. The executable script hashes and generated
rule are unchanged. That static comparison does not carry forward the earlier
browser pass. Current compatibility is unrun and the rule remains disabled.

## Policy and inventory

Expand Down
12 changes: 6 additions & 6 deletions security/browser-headers/rust-browser-validation.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
"scope": "Current artifact source proposal only; browser, telemetry, TLS/HSTS and provider acceptance pending",
"input_artifact": {
"repository": "durable-workflow/sdk-rust",
"run_id": 41939116087,
"artifact_id": 13375154247,
"tooling_revision": "1ea3bfc3965423534b5bbbed700371f126835fc3",
"zip_bytes": 4071555,
"zip_sha256": "ff902191bdb90f7f1d047ecc5d27e9b8c5215a0239e4f11fcbf574f7ddab83d1"
"run_id": 41954374074,
"artifact_id": 13382143373,
"tooling_revision": "2b45fd7b12a6e9460666045a3c7f644860caea3b",
"zip_bytes": 4071521,
"zip_sha256": "15f1e2745a0b268e1093820b3486c4910e8d1bf7b63062e562200586d3beef95"
},
"source_preparation": null,
"compatibility_accepted": false,
"historical_result": "history/rust-sdk207-browser-validation.json",
"next_action": "Security reviews the exact current-artifact proposal; General separately admits bounded compatibility and any later provider stage.",
"inventory_sha256": "8c4bc2eb1ef3ffeeb9788a9d0b44d7b718a2a56caff55b3d4531716a25b270d7",
"inventory_sha256": "01e0872a2d03726d52092362f7654285a81635f92f2f5bd30ecace505d14c71d",
"csp_sha256": "b002ffc9f95abc8145319d8a045aef9f0184d323d81ff9aec5a9de54d2f2d710"
}
14 changes: 7 additions & 7 deletions security/browser-headers/rust-inventory.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
"origin": "https://rust.durable-workflow.com",
"input_artifact": {
"repository": "durable-workflow/sdk-rust",
"run_id": 41939116087,
"artifact_id": 13375154247,
"tooling_revision": "1ea3bfc3965423534b5bbbed700371f126835fc3",
"zip_bytes": 4071555,
"zip_sha256": "ff902191bdb90f7f1d047ecc5d27e9b8c5215a0239e4f11fcbf574f7ddab83d1"
"run_id": 41954374074,
"artifact_id": 13382143373,
"tooling_revision": "2b45fd7b12a6e9460666045a3c7f644860caea3b",
"zip_bytes": 4071521,
"zip_sha256": "15f1e2745a0b268e1093820b3486c4910e8d1bf7b63062e562200586d3beef95"
},
"source_preparation": null,
"build_identity": {
Expand All @@ -16,7 +16,7 @@
"version": "4.12.0",
"crate_sha256": "620a8287af73c363dac61f0a91f20971a39f3b8cdc9513fbea85cdbd95a74ba4",
"source_revision": "4b2440a5618ccc1c98960ea2e1e62127042c3590",
"tooling_revision": "1ea3bfc3965423534b5bbbed700371f126835fc3"
"tooling_revision": "2b45fd7b12a6e9460666045a3c7f644860caea3b"
},
"files": [
{
Expand All @@ -27,7 +27,7 @@
{
"path": "__build.json",
"bytes": 332,
"sha256": "622b90e304c9ea1c2e89efeb4fb9145d64f86ca50f3bcedaf2f5f94fa773ce0d"
"sha256": "6338f8427799ac2cf9b0c2a1c08fcf1889b8887761f42c2d748dc0e0cfad8a8a"
},
{
"path": "activity_options/all.html",
Expand Down
16 changes: 8 additions & 8 deletions security/browser-headers/rust-local-browser-packet.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
"run_status": "not-run",
"input_artifact": {
"repository": "durable-workflow/sdk-rust",
"run_id": 41939116087,
"artifact_id": 13375154247,
"tooling_revision": "1ea3bfc3965423534b5bbbed700371f126835fc3",
"zip_bytes": 4071555,
"zip_sha256": "ff902191bdb90f7f1d047ecc5d27e9b8c5215a0239e4f11fcbf574f7ddab83d1"
"run_id": 41954374074,
"artifact_id": 13382143373,
"tooling_revision": "2b45fd7b12a6e9460666045a3c7f644860caea3b",
"zip_bytes": 4071521,
"zip_sha256": "15f1e2745a0b268e1093820b3486c4910e8d1bf7b63062e562200586d3beef95"
},
"playwright_version": "1.64.0",
"expected_chromium_version": "156.0.8078.4",
Expand All @@ -27,9 +27,9 @@
"rust-browser.test.cjs": "0b2f731db13e4f095230673bfda3d97a36f71c5ef62b8f3702a7978740d17d61",
"rust-browser-inputs.cjs": "43bb050185f05187e7d94a40d73b4fae965276696fe7356e6609b319c6b120ee",
"rust-browser-source.test.cjs": "deafc5ce08806c148b189a48f11012cc0c03600122dd8fe34eb01e223f381952",
"rust-policy.json": "a357bcc5ea8ad96db5e601e4e1c1cb3c8c7eb3441caa9b331d0c2a906ffe84d8",
"rust-inventory.json": "8c4bc2eb1ef3ffeeb9788a9d0b44d7b718a2a56caff55b3d4531716a25b270d7",
"rust-rule.candidate.json": "bce0c66db4454bc16941a8f5dadb04706315e673d5e2c33f677e7728bafd0135",
"rust-policy.json": "c2441f7b0bf7aebc0c4ee491cda0470170231bc8baf14b4c2163552b8d2fd79c",
"rust-inventory.json": "01e0872a2d03726d52092362f7654285a81635f92f2f5bd30ecace505d14c71d",
"rust-rule.candidate.json": "90b37562aabf6ed8619fbb488ad764137731bec976d8e2ff5e7bfa382fba5634",
"runtime/package.json": "a2cd697a8ed17ab196e4eac1bcb15ceec6da243a600a8f2584c8aabdadfc3c9d",
"runtime/package-lock.json": "959580e91aae43c61d0bbfcf11c21eb52ecafcfadb088f166b322fd3988a47ae"
}
Expand Down
100 changes: 92 additions & 8 deletions security/browser-headers/rust-local-browser-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ production operation is proposed here.
## Inputs and provenance

`rust-local-browser-packet.json` binds the executable/data closure and exact
runtime. Its artifact tuple is the accepted SDK #209 input already recorded in
`rust-policy.json`: artifact 13375154247, ZIP SHA-256
`ff902191bdb90f7f1d047ecc5d27e9b8c5215a0239e4f11fcbf574f7ddab83d1`,
484 files / 372 HTML pages / 49 inline-script hashes. Public Rust 4.12.0, crate
source and tooling identities are preserved. `rust-rule.candidate.json` and all
policy/approval/browser-result files remain unchanged and disabled/unrun.
runtime. Its artifact tuple is the accepted SDK #212 input already recorded in
`rust-policy.json`: artifact 13382143373, ZIP SHA-256
`15f1e2745a0b268e1093820b3486c4910e8d1bf7b63062e562200586d3beef95`,
484 files / 372 HTML pages / 49 inline-script hashes. Public Rust 4.12.0 crate
and source identities are preserved; tooling matches the accepted SDK #212
build. The parsed rule, policy sources, approval flags and unrun status remain
unchanged. Input provenance fields now bind this artifact.

The proposed runtime is the existing Playwright 1.64.0 generation, pinned to the
official **linux/amd64** platform manifest in the packet. Registry metadata was
Expand All @@ -39,7 +40,7 @@ membership, recheck the accepted protected-run/deployment identity and compare
live `__build.json` with the exact artifact. Those are finite read-only checks.
Stop if the live input has changed, the artifact has expired, or a required input
is unavailable. Do not dispatch a replacement build or silently retarget the
packet. The input API expires this ZIP on November 6, 2026 at 00:09:56 UTC.
packet. The input API expires this ZIP on November 6, 2026 at 02:19:51 UTC.

## What the proposed check exercises

Expand Down Expand Up @@ -72,6 +73,88 @@ Normal CI runs only `rust-browser-source.test.cjs` with fake routes and temporar
input files, plus the existing header-policy controls. It never imports
Playwright, launches a browser or runs the optional browser entrypoint.

## Required custody before execution

[Security's source review](https://github.com/durable-workflow/durable-workflow.github.io/pull/477#issuecomment-6647872853)
requires verification outside Node before any packet code is loaded. The
in-process check cannot authenticate code that is already executing. The exact
current packet SHA-256 is
`2060309bf48d3e179abf55c0f2e1ffbc4908fcd7227b0cc4eb11089afde71f2a`.

Before any separate preparation/run admission, General and Security must bind
an approved write-preventing mechanism and exclusive owner for all three public
staging trees. They must remain unchanged through the attempt. Host writers can
still change a read-only bind mount's source; bind flags and a prior hash alone
do not establish that custody. Ordinary mutable worktrees and downloaded caches
are not execution seals. An unavailable seal is a blocker, not authority to add
privileges, a helper, a writable mount or a new storage service.

After that separate admission, use the following literal reviewed command with
the host's trusted isolated Python interpreter. Copy it from the reviewed
record, not from an unverified packet file. Do not load any packet Python/Node
module to perform this check. Bind the interpreter identity in the execution
record. The packet and runtime staging paths must be outside home/credential
trees, absolute, regular, free of symlink components and exclusively owned.
Copy only the eight manifest members and the manifest into the packet tree.
Exclude repository metadata, extra modules/configuration and `node_modules`
from this tree so they cannot shadow the separately sealed runtime dependency.

```sh
/usr/bin/python3 -I - "$packet_directory" <<'PY'
import hashlib, json, stat, sys
from pathlib import Path, PurePosixPath

root = Path(sys.argv[1])
assert root.is_absolute() and root.resolve() == root
assert root.is_dir() and not root.is_symlink()
assert not root.is_relative_to(Path.home())
manifest_path = root / 'rust-local-browser-packet.json'
assert stat.S_ISREG(manifest_path.lstat().st_mode)
manifest_bytes = manifest_path.read_bytes()
expected = '2060309bf48d3e179abf55c0f2e1ffbc4908fcd7227b0cc4eb11089afde71f2a'
assert hashlib.sha256(manifest_bytes).hexdigest() == expected
manifest = json.loads(manifest_bytes)
assert manifest['run_status'] == 'not-run' and len(manifest['files']) == 8
expected_files = set(manifest['files']) | {'rust-local-browser-packet.json'}
actual_files = set()
for member in root.rglob('*'):
mode = member.lstat().st_mode
assert stat.S_ISREG(mode) or stat.S_ISDIR(mode)
relative = member.relative_to(root).as_posix()
if stat.S_ISDIR(mode):
assert relative == 'runtime'
else:
actual_files.add(relative)
assert actual_files == expected_files
for name, expected_hash in manifest['files'].items():
relative = PurePosixPath(name)
assert not relative.is_absolute() and '..' not in relative.parts
member = root
for part in relative.parts:
member = member / part
assert not member.is_symlink()
assert stat.S_ISREG(member.lstat().st_mode)
assert hashlib.sha256(member.read_bytes()).hexdigest() == expected_hash
print('External packet and eight-member hash check passed; no packet code loaded.')
PY
```

This check is an integrity prerequisite. It does not create a seal or approve an
attempt. Record the separate write-prevention/custody proof and recheck the
mounts and cached image immediately before creating the admitted cell.

Dependency preparation is separately held. Its future admitted environment
must have a container-only empty home, distinct empty user/global npm config
files, a task-private cache, a public registry and an explicit environment
allowlist. No host home/profile, `.npmrc`, token, credential directory or secret
proxy setting may be inherited. Set `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1` and
`--ignore-scripts`; use the exact lock and verify both archive integrity values
and library version. Do not import Playwright during preparation. Enumerate and
hash the resulting dependency tree, reject external links, then establish its
approved immutable/exclusive custody before any browser code resolves it.
No such environment, dependency tree or seal has been created by this source
retarget. Missing preparation/image/custody proof leaves execution held.

## Proposed execution and cleanup

Documentation owns one attempt and cleanup. General names the admitted executor
Expand All @@ -83,7 +166,8 @@ There is no automatic retry, host IPC, privileged mode, extra capability or
published port. The writable tmpfs and private shared memory count within the
container's memory cap. This is a proposed budget, not a reservation.

The following is the exact proposed launch shape after admission. Assign the
The following is the exact proposed launch shape after admission, the external
pre-load hash check and accepted custody proof above. Assign the
three absolute input paths from the reviewed public-input preparation; reject
commas/newlines in those paths. `packet_directory` contains only this public
packet. `runtime_modules` is the prepared public dependency tree. Do not point
Expand Down
12 changes: 6 additions & 6 deletions security/browser-headers/rust-policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
"version": "4.12.0",
"source_revision": "4b2440a5618ccc1c98960ea2e1e62127042c3590",
"crate_sha256": "620a8287af73c363dac61f0a91f20971a39f3b8cdc9513fbea85cdbd95a74ba4",
"tooling_revision": "1ea3bfc3965423534b5bbbed700371f126835fc3"
"tooling_revision": "2b45fd7b12a6e9460666045a3c7f644860caea3b"
},
"input_artifact": {
"repository": "durable-workflow/sdk-rust",
"run_id": 41939116087,
"artifact_id": 13375154247,
"tooling_revision": "1ea3bfc3965423534b5bbbed700371f126835fc3",
"zip_bytes": 4071555,
"zip_sha256": "ff902191bdb90f7f1d047ecc5d27e9b8c5215a0239e4f11fcbf574f7ddab83d1"
"run_id": 41954374074,
"artifact_id": 13382143373,
"tooling_revision": "2b45fd7b12a6e9460666045a3c7f644860caea3b",
"zip_bytes": 4071521,
"zip_sha256": "15f1e2745a0b268e1093820b3486c4910e8d1bf7b63062e562200586d3beef95"
},
"sources": {
"script": [
Expand Down
12 changes: 6 additions & 6 deletions security/browser-headers/rust-rule.candidate.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
"schema": "durable-workflow.browser-security-rule-candidate/v1",
"rollout_approved": false,
"phase": "http_response_headers_transform",
"inventory_sha256": "8c4bc2eb1ef3ffeeb9788a9d0b44d7b718a2a56caff55b3d4531716a25b270d7",
"inventory_sha256": "01e0872a2d03726d52092362f7654285a81635f92f2f5bd30ecace505d14c71d",
"compatibility_accepted": false,
"input_artifact": {
"repository": "durable-workflow/sdk-rust",
"run_id": 41939116087,
"artifact_id": 13375154247,
"tooling_revision": "1ea3bfc3965423534b5bbbed700371f126835fc3",
"zip_bytes": 4071555,
"zip_sha256": "ff902191bdb90f7f1d047ecc5d27e9b8c5215a0239e4f11fcbf574f7ddab83d1"
"run_id": 41954374074,
"artifact_id": 13382143373,
"tooling_revision": "2b45fd7b12a6e9460666045a3c7f644860caea3b",
"zip_bytes": 4071521,
"zip_sha256": "15f1e2745a0b268e1093820b3486c4910e8d1bf7b63062e562200586d3beef95"
},
"source_preparation": null,
"rule": {
Expand Down
28 changes: 15 additions & 13 deletions security/browser-headers/rust-stage-proposal.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,40 +15,42 @@ credentials or API were used. A source merge does not clear those gates.

## Exact input

The [Rust owner receipt](https://github.com/durable-workflow/sdk-rust/pull/209#issuecomment-6645951490)
The [Rust owner receipt](https://github.com/durable-workflow/sdk-rust/pull/212#issuecomment-6648011712)
binds the following accepted automatic protected build:

| Input | Value |
| --- | --- |
| Package | Published `durable-workflow` 4.12.0 |
| Public source | `4b2440a5618ccc1c98960ea2e1e62127042c3590` |
| Crate SHA-256 | `620a8287af73c363dac61f0a91f20971a39f3b8cdc9513fbea85cdbd95a74ba4` |
| Tooling | `1ea3bfc3965423534b5bbbed700371f126835fc3` |
| Protected run | [41939116087](https://github.com/durable-workflow/sdk-rust/actions/runs/41939116087), push, attempt 1 |
| Successful deployment | `7561374287` |
| Pages artifact | `13375154247`, 4,071,555-byte ZIP |
| ZIP SHA-256 | `ff902191bdb90f7f1d047ecc5d27e9b8c5215a0239e4f11fcbf574f7ddab83d1` |
| Tooling | `2b45fd7b12a6e9460666045a3c7f644860caea3b` |
| Protected run | [41954374074](https://github.com/durable-workflow/sdk-rust/actions/runs/41954374074), push, attempt 1 |
| Successful deployment | `7563384998` |
| Pages artifact | `13382143373`, 4,071,521-byte ZIP |
| ZIP SHA-256 | `15f1e2745a0b268e1093820b3486c4910e8d1bf7b63062e562200586d3beef95` |
| Extracted inventory | 484 regular files, 372 HTML pages, 49 executable inline-script hashes |
| Inventory file SHA-256 | `8c4bc2eb1ef3ffeeb9788a9d0b44d7b718a2a56caff55b3d4531716a25b270d7` |
| Policy file SHA-256 | `a357bcc5ea8ad96db5e601e4e1c1cb3c8c7eb3441caa9b331d0c2a906ffe84d8` |
| Candidate file SHA-256 | `bce0c66db4454bc16941a8f5dadb04706315e673d5e2c33f677e7728bafd0135` |
| Inventory file SHA-256 | `01e0872a2d03726d52092362f7654285a81635f92f2f5bd30ecace505d14c71d` |
| Policy file SHA-256 | `c2441f7b0bf7aebc0c4ee491cda0470170231bc8baf14b4c2163552b8d2fd79c` |
| Candidate file SHA-256 | `90b37562aabf6ed8619fbb488ad764137731bec976d8e2ff5e7bfa382fba5634` |

The downloaded ZIP matched its GitHub API digest. Extraction rejected absolute
paths, parent traversal and non-regular members. Compared with #472's immutable
SDK #207 subject, only `__build.json`, `help.html` and `settings.html` differ.
The two HTML changes replace `data-current-crate="activity_options"` with
`data-current-crate="workflow_testing"`. All other files and every inventoried
script/style/handler/origin record are identical. Public package/source identity
is preserved. The historical browser pass remains in
`history/rust-sdk207-browser-validation.json`; it does not qualify this artifact.
is preserved. SDK #212 retains every SDK #209 page and asset byte. Only its
`__build.json` changes from the accepted #476/#477 subject. The historical browser
pass remains in `history/rust-sdk207-browser-validation.json`; it does not qualify
this artifact.

At 01:02 UTC on November 5, 2026, these actual HTTPS responses returned 200 and
On November 5, 2026, these actual HTTPS responses returned 200 and
matched the artifact byte for byte. All six proposed security headers were
absent. This is a finite identity sample, not a live site-wide compatibility run.

| Path at `https://rust.durable-workflow.com/` | SHA-256 |
| --- | --- |
| `__build.json` | `622b90e304c9ea1c2e89efeb4fb9145d64f86ca50f3bcedaf2f5f94fa773ce0d` |
| `__build.json` | `6338f8427799ac2cf9b0c2a1c08fcf1889b8887761f42c2d748dc0e0cfad8a8a` |
| `index.html` | `b2e918903002c3d863aab6eb77c44a01af5ece95d26bb433120d396cae055fae` |
| `durable_workflow/index.html` | `8747b578d0749a534ae67416aeb79bca02b7e50c9c8194f85aae883cc99f0e20` |
| `durable_workflow/struct.Client.html` | `1c4a15c7383e28a3af199e55ee217a157165f45e131dd6e708ef5788661606b1` |
Expand Down
Loading