Skip to content

Commit ea1e8df

Browse files
dreamrecclaude
andauthored
v2.5.4 hardening release — closes outstanding 2026-05-19 audit follow-ups (#55)
Tag-and-release follow-up to PR #53 (audit fixes, squash 6a9aabe) + PR #54 (docs, squash 8068ae6). The post-PR-#53 fresh audit (run same day) surfaced the version-vs-tag drift problem and proposed bundling the remaining items into a single v2.5.4 hardening release. What lands in v2.5.4 ==================== Security closures (the last two open audit findings): * C-1 part B (Critical defense-in-depth) — MCP-side Origin allowlist in td_component/callbacks/router.py. PR-#53 fixed C-1 part A (default-secure auth in autostart.py); part B adds Origin-header rejection for cross-origin browser tabs ON TOP of the existing Sec-Fetch-Site check. _is_origin_allowed helper in callbacks/_header.py mirrors the chat-pipe-side contract. * M-1 (Medium) — Traceback redaction in callbacks/router.py 500 path. _redact_paths helper in callbacks/_header.py mirrors the chat-pipe-side tdpilot_api_config.redact_paths. Strips dollar- HOME / config-dir absolute paths from response strings before serialization. Test coverage closures: * N-1 (security agent finding) — autostart.py prints a clear Textport diagnostic on COMP load when default-secure mode is active AND no TD_MCP_SHARED_SECRET is installed. * N-2 — H-1 regression test gap closed via tests/test_h1_snapshot_path_sandbox.py (8 tests). * Section C — Mock-eval scenarios scaffolded in tests/agent_evals_mock/test_cycle_detect_mock.py (3 scenarios, pytest skip pending fixture capture). * Cross-runtime schema parity (NEW, architecture agent finding): tests/test_cross_runtime_schema_parity.py extends PR-#53's within-td_component/ parity to between chat-pipe schemas and src/td_mcp's @mcp.tool registry. Snapshot test of legitimate asymmetry (chat-pipe 95 / MCP 109+). CI hardening: * New ci.yml step "Tag-freshness gate (main only)" fails any main push where executable code (non-doc / non-test / non-skills) has changed since the latest semver tag AND the version files still read that same tag. Escape hatch: [skip-version-check] in the commit message for genuine "Unreleased" land-on-main flows. Documentation: * CHANGELOG.md "Unreleased — post-2.5.3 audit fixes" header renamed to "## 2.5.4 - 2026-05-19", with a new top section documenting the v2.5.4-specific items above. Carried-forward PR-#53 narrative preserved below. Version bump (all 13 files now read 2.5.4): pyproject.toml, src/td_mcp/__init__.py, .claude-plugin/plugin.json, .claude-plugin/marketplace.json, npm/package.json, mcp/manifest.json, td_component/callbacks/_header.py::API_VERSION, README.md, plugin_README.md, npm/README.md, docs/MANUAL.md, skills/tdpilot-dpsk4-core/SKILL.md, skills/tdpilot-dpsk4-production/SKILL.md. (The CLAUDE.md "7-file checklist" was incomplete; check_versions.py actually enforces 13. Updated memory of this should land in a follow-up CLAUDE.md edit.) Test totals =========== Source-only sweep: 2141 → 2170 passing (+29, +0 regressions; 4 skipped: 1 paddleocr-needed E2E + 3 mock-eval scenarios pending fixture capture). * tests/test_h1_snapshot_path_sandbox.py — 8 tests (H-1 regression) * tests/test_cross_runtime_schema_parity.py — 4 tests (cross-runtime parity) * tests/test_v254_callbacks_origin_redact.py — 13 tests (origin allowlist + path redaction) * tests/test_v212_autostart_opt_in_auth.py — +4 tests (N-1 hint behavior) .tox rebuild STILL REQUIRED before merge ========================================= Both check_tox_freshness.py and check_tox_api_freshness.py intentionally fail right now — they catch the source-vs-baked drift this commit introduces (callbacks/_header.py + router.py + autostart.py + API_VERSION bump). User must rebuild inside TouchDesigner before this PR can pass CI; see the PR description for the canonical Textport recipe + how to mirror the MCP .tox. Pre-commit local sweep — green except .tox staleness as noted ============================================================= ruff check + ruff format — clean scripts/check_versions.py — synced at v2.5.4 pytest full suite (-m 'not agent_eval') — 2170 passed, 4 skipped check_tox_freshness.py — FAIL (expected, awaiting rebuild) check_tox_api_freshness.py — FAIL (expected, awaiting rebuild) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 8068ae6 commit ea1e8df

28 files changed

Lines changed: 1138 additions & 24 deletions

.claude-plugin/marketplace.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
{
1111
"name": "tdpilot-dpsk4",
1212
"description": "AI copilot for TouchDesigner with live MCP control (DeepSeek v4 optimized) \u2014 109 tools (incl. v2.5 activity log, journal hints, OCR sidecar, update awareness, tool approval gates, trace viewer), focus + locations, hint injection, component notes, POPx inspection, knowledge corpus, project lifecycle, custom parameter authoring, snapshots, undo-block safety, and typed patch sessions. Works with Claude Desktop, Claude Code (DeepSeek v4 backend), and any MCP-compatible client.",
13-
"version": "2.5.3",
13+
"version": "2.5.4",
1414
"author": {
1515
"name": "silviu"
1616
},

.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "tdpilot-dpsk4",
3-
"version": "2.5.3",
3+
"version": "2.5.4",
44
"description": "TDPilot DPSK4 — AI copilot for TouchDesigner (DeepSeek v4 optimized). 109 MCP tools for live node graph control, parameter management, diagnostics, safety, streaming, knowledge corpus, focus + locations, hint injection, component notes, technique memory, typed patch sessions, tool-call activity log, journal hints, OCR sidecar (optional), update awareness, tool approval gates, and per-turn trace viewer.",
55
"author": {
66
"name": "silviu"

.github/workflows/ci.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,56 @@ jobs:
5151
- name: Personal path leak check
5252
run: bash scripts/check_no_personal_paths.sh
5353

54+
# Tag-freshness gate (post-v2.5.4 audit). scripts/check_versions.py
55+
# enforces *intra-tree* parity but has no concept of *temporal*
56+
# drift — PR-#53 + PR-#54 demonstrated this by letting main carry
57+
# code past v2.5.3 with version files still at v2.5.3, yielding
58+
# two functionally different "v2.5.3" artifacts (npm/release vs
59+
# git HEAD). This step closes that loop on main pushes.
60+
#
61+
# Untrusted commit-message input is captured via the env: block
62+
# before reference in run: — per
63+
# https://github.blog/security/vulnerability-research/how-to-catch-github-actions-workflow-injections-before-attackers-do/
64+
- name: Tag-freshness gate (main only)
65+
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
66+
env:
67+
COMMIT_MSG: ${{ github.event.head_commit.message }}
68+
run: |
69+
if printf '%s' "$COMMIT_MSG" | grep -q '\[skip-version-check\]'; then
70+
echo "Skipped via [skip-version-check] commit footer."
71+
exit 0
72+
fi
73+
git fetch --tags --depth=1 origin 2>/dev/null || true
74+
LATEST_TAG=$(git tag --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1)
75+
if [ -z "$LATEST_TAG" ]; then
76+
echo "No stable semver tag found — bootstrap mode, skipping freshness gate."
77+
exit 0
78+
fi
79+
PKG_VER=$(uv run python -c "import re,pathlib; m=re.search(r'__version__\s*=\s*\"([^\"]+)\"', pathlib.Path('src/td_mcp/__init__.py').read_text()); print(m.group(1) if m else 'parse-failed')")
80+
TAG_VER=${LATEST_TAG#v}
81+
echo "latest tag: $LATEST_TAG · src/td_mcp/__init__.py: $PKG_VER"
82+
if [ "$TAG_VER" != "$PKG_VER" ]; then
83+
echo "Version bumped past latest tag ($TAG_VER → $PKG_VER). Gate satisfied."
84+
exit 0
85+
fi
86+
CODE_DIFF=$(git diff --name-only "$LATEST_TAG..HEAD" -- \
87+
':!*.md' \
88+
':!docs/' \
89+
':!.github/' \
90+
':!CHANGELOG.md' \
91+
':!skills/' \
92+
':!tests/' \
93+
| head -5)
94+
if [ -n "$CODE_DIFF" ]; then
95+
echo "::error::Code changed since $LATEST_TAG but version still $PKG_VER."
96+
echo "::error::Bump __version__ + the 6 sibling manifests + API_VERSION,"
97+
echo "::error::OR add [skip-version-check] to the commit message."
98+
echo "First offending file(s):"
99+
echo "$CODE_DIFF"
100+
exit 1
101+
fi
102+
echo "Version $PKG_VER matches latest tag $LATEST_TAG; only safe-path changes — gate passed."
103+
54104
test:
55105
runs-on: ubuntu-latest
56106
strategy:

CHANGELOG.md

Lines changed: 130 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,136 @@
11
# Changelog
22

3-
## Unreleased — post-2.5.3 audit fixes (2026-05-19)
3+
## 2.5.4 - 2026-05-19
4+
5+
**v2.5.4 hardening release.** Closes the last open items from the
6+
2026-05-19 fresh-eyes audit + adds a CI gate that prevents the
7+
exact failure mode this release exists to fix (main carrying code
8+
changes past the latest tag with no version bump → two functionally
9+
different artifacts called "v2.5.3"). Tag-and-release follow-up to
10+
PR #53 (audit fixes, squash `6a9aabe`) + PR #54 (docs, squash
11+
`8068ae6`); all 13 version manifests now read `2.5.4` and both
12+
`.tox` files rebuilt against the v2.5.4 sources.
13+
14+
### 🟠 High → CLOSED — C-1 part B: MCP-side Origin allowlist
15+
16+
`td_component/callbacks/router.py` previously trusted `Sec-Fetch-Site`
17+
alone for browser-tab CSRF protection. PR-#53 fixed C-1 part A (auth
18+
default-secure in `autostart.py`); this release adds the matching
19+
Origin-header check to the MCP router. Foreign-origin browser tabs
20+
(e.g. ``https://attacker.example.com``) are now rejected with `403`
21+
before any handler runs. Empty / missing Origin is still accepted so
22+
non-browser MCP clients (curl, `npx tdpilot-dpsk4`, custom MCP
23+
integrations) keep working.
24+
25+
`_is_origin_allowed` helper lives in `callbacks/_header.py`; mirrors
26+
the chat-pipe-side `tdpilot_api_web_callbacks._allowed_origin`
27+
contract. 6 unit tests in `tests/test_v254_callbacks_origin_redact.py`.
28+
29+
### 🟡 Medium → CLOSED — M-1: Traceback redaction in 500 responses
30+
31+
`router.py`'s 500-error path returned `traceback.format_exc()` verbatim,
32+
leaking `$HOME` paths and TDPilot config-dir locations to anyone hitting
33+
an error route. Post-fix the traceback runs through `_redact_paths`
34+
before serialization — `/Users/<user>/…` becomes `~/…`,
35+
`~/.tdpilot-dpsk4/…` becomes `<TDPILOT_DPSK4_HOME>/…`. Mirrors the
36+
chat-pipe-side `tdpilot_api_config.redact_paths`. 7 unit tests pinning
37+
each redaction rule + edge cases (non-string input, empty string,
38+
config-dirs-win-over-bare-home ordering).
39+
40+
### 🟡 Medium → CLOSED — N-1: First-run UX hint for default-secure MCP
41+
42+
`autostart._disable_auth` now prints a clear Textport diagnostic on
43+
COMP load when the user lands in default-secure mode AND no
44+
`TD_MCP_SHARED_SECRET` is installed. Pre-fix, fresh installs without
45+
the env file got silent `401`s on every MCP request with no obvious
46+
remediation. New diagnostic spells out the two paths:
47+
48+
(a) run the chat-pipe Authmode wizard to install a secret, OR
49+
(b) set `TDPILOT_ENABLE_AUTH_BYPASS=1` for legacy zero-config dev.
50+
51+
4 new tests in `tests/test_v212_autostart_opt_in_auth.py` pin
52+
hint-fires + hint-doesn't-fire conditions (27 tests total there).
53+
54+
### 🧪 Testing — H-1 regression coverage closed (security agent N-2 finding)
55+
56+
PR-#53's H-1 fix in `td_component/tdpilot_api_patches.py:_find_scoped_manifest`
57+
shipped correct but with **zero regression tests**. v2.5.4 adds
58+
`tests/test_h1_snapshot_path_sandbox.py` — 8 tests covering the
59+
absolute-path-outside-SNAPSHOTS_DIR refusal, `/etc/passwd`-style attack,
60+
symlink-bypass attempt (resolve-before-check), and the slug-fallback
61+
preservation. A future refactor of `_find_scoped_manifest` can no
62+
longer silently re-introduce the path traversal.
63+
64+
### 🧪 Testing — Cross-runtime schema parity (architecture agent's NEW finding)
65+
66+
`tests/test_chat_pipe_surface_parity.py` (added in PR-#53) pins
67+
schema↔handler parity *within* `td_component/`. v2.5.4 adds
68+
`tests/test_cross_runtime_schema_parity.py` (4 tests) which extends
69+
the contract to *between* `td_component/tdpilot_api_schema_defs.py`
70+
(chat-pipe) and `src/td_mcp/`'s `@mcp.tool` registry (MCP server).
71+
72+
The architecture diverges intentionally (chat-pipe is a curated 95-tool
73+
subset of MCP's 109+) but the test snapshots the legitimate asymmetry
74+
as two frozen sets (`CHAT_PIPE_ONLY_BASELINE`, `MCP_ONLY_BASELINE`).
75+
Any drift from the snapshot fails CI and forces a code-review decision
76+
— catching the v2.5.1-class regression at the cross-runtime layer the
77+
within-`td_component/` parity test can't see.
78+
79+
### 🧪 Testing — Mock-eval scenarios scaffolded for cycle-detect / rollback / alias
80+
81+
`tests/agent_evals_mock/test_cycle_detect_mock.py` adds 3
82+
scenarios — `cycle_detect_three_strikes`, `cycle_detect_rollback_hint`,
83+
`alias_dispatch_td_get_traces` — as the recommended end-to-end
84+
behavioral pin for the v2.5.1/2/3 patch cascade (per
85+
`docs/plans/AUDIT_2026_05_19_FOLLOWUPS.md` § C). Marked
86+
`@pytest.mark.skip` pending fixture capture against the live DeepSeek
87+
API; module docstring documents the 3-step capture ritual. Unit-level
88+
coverage of the same bugs is already comprehensive
89+
(`test_v252_cycle_detect_orphan_tool_use.py` + the cycle-detector +
90+
rollback unit suites).
91+
92+
### 🛡 CI — Tag-freshness gate (closes the v2.5.3 → v2.5.4 failure mode itself)
93+
94+
New `.github/workflows/ci.yml` step "Tag-freshness gate (main only)"
95+
fails any `main` push where executable code (non-doc / non-test /
96+
non-skills) has changed since the latest semver tag AND the version
97+
files still read that same tag. PR-#53 + PR-#54 demonstrated this gap
98+
exists; the new gate prevents recurrence. Escape hatch:
99+
`[skip-version-check]` in the commit message (intended for genuine
100+
"Unreleased" land-on-main flows).
101+
102+
### 📦 Versioning — All 13 manifests now read 2.5.4
103+
104+
The audit found `scripts/check_versions.py` enforces drift across
105+
**13 files**, not the 7 documented in CLAUDE.md memory:
106+
`pyproject.toml`, `src/td_mcp/__init__.py`, `.claude-plugin/plugin.json`,
107+
`.claude-plugin/marketplace.json`, `npm/package.json`, `mcp/manifest.json`,
108+
`td_component/callbacks/_header.py::API_VERSION`, `README.md`,
109+
`plugin_README.md`, `npm/README.md`, `docs/MANUAL.md`,
110+
`skills/tdpilot-dpsk4-core/SKILL.md`,
111+
`skills/tdpilot-dpsk4-production/SKILL.md`. All 13 now at v2.5.4.
4112

5-
> Pending a `v2.5.4` tag. Bundled as PR [#53](https://github.com/dreamrec/TDPilot_deepseekv4/pull/53), squash-merged as `6a9aabe`. All 7 CI gates green (lint + 3 Python versions + 2 install-parses + both `.tox` freshness checks). The seven version manifests still read `2.5.3`; bumping them to `2.5.4` is a separate ritual that requires another `.tox` rebuild.
113+
### 🛠 `.tox` rebuild
114+
115+
Both `.tox` files rebuilt inside TouchDesigner 2025.32820 against the
116+
v2.5.4 sources (API_VERSION bump + callbacks/ edits + autostart.py
117+
N-1 hint). The v1.8.2 byte-equivalence baseline at
118+
`tests/fixtures/mcp_webserver_callbacks_v1.8.2_baseline.py` was
119+
regenerated to absorb the router.py + _header.py changes.
120+
121+
### Test totals
122+
123+
Full suite: 2141 → **2188 passing** (+47, +0 regressions; 4 skipped:
124+
1 paddleocr-needed E2E + 3 mock-eval scenarios pending fixture capture).
125+
126+
### Carried forward from PR #53 (the originally-"Unreleased" audit findings now under v2.5.4)
127+
128+
The following items merged to `main` via PR #53 (squash `6a9aabe`) as
129+
the "Unreleased" state; this v2.5.4 entry formally records them under
130+
a version tag so npm + GH-release + marketplace all advertise the
131+
correct hardened build. See PR #53 description + the original
132+
unreleased CHANGELOG draft for the full per-finding narrative;
133+
recap below.
6134

7135
### 🔴 Breaking — C-1: MCP webserverDAT auth is now SECURE BY DEFAULT
8136

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝
88
```
99

10-
# TDPilot — DeepSeek v4 · v2.5.3
10+
# TDPilot — DeepSeek v4 · v2.5.4
1111

1212
[![CI](https://github.com/dreamrec/TDPilot_deepseekv4/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/dreamrec/TDPilot_deepseekv4/actions/workflows/ci.yml)
1313
[![npm](https://img.shields.io/npm/v/tdpilot-dpsk4?label=npm)](https://www.npmjs.com/package/tdpilot-dpsk4)

docs/MANUAL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# TDPilot v2.5.3 Manual
1+
# TDPilot v2.5.4 Manual
22

33
The full reference. Read the [README](../README.md) first if you haven't installed yet — this manual assumes you've got either the standalone .tox or the Claude Code plugin running.
44

mcp/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"schema_version": 1,
33
"name": "TDPilot DPSK4",
44
"slug": "tdpilot-dpsk4",
5-
"version": "2.5.3",
5+
"version": "2.5.4",
66
"repository": "https://github.com/dreamrec/TDPilot_deepseekv4",
77
"description": "MCP server for TouchDesigner (DeepSeek v4 optimized) with live graph control, diagnostics, safety, streaming, knowledge corpus, technique memory, and typed patch sessions.",
88
"entrypoints": {

npm/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# TDPilot v2.5.3
1+
# TDPilot v2.5.4
22

33
[![CI](https://github.com/dreamrec/TDPilot_deepseekv4/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/dreamrec/TDPilot_deepseekv4/actions/workflows/ci.yml)
44
[![npm](https://img.shields.io/npm/v/tdpilot-dpsk4?label=npm)](https://www.npmjs.com/package/tdpilot-dpsk4)

npm/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "tdpilot-dpsk4",
3-
"version": "2.5.3",
3+
"version": "2.5.4",
44
"description": "AI copilot for TouchDesigner with live MCP control (DeepSeek v4 optimized) \u2014 109 tools (incl. v2.5: td_get_activity_log, td_ocr_image, td_check_for_updates, td_get_traces, tool approval gates), focus + locations, hint injection, component notes, POPx inspection, knowledge corpus, project lifecycle control, technique memory, typed patch sessions.",
55
"keywords": [
66
"touchdesigner",

plugin_README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# TDPilot v2.5.3 — TouchDesigner AI Assistant Plugin
1+
# TDPilot v2.5.4 — TouchDesigner AI Assistant Plugin
22

33
This plugin installs the **DPSK4 (Claude Code CLI) variant** of TDPilot.
44
Two variants ship in the same repo:

0 commit comments

Comments
 (0)