fix(channel): carry config_secrets into the generated DRIVER header - #107
Conversation
FTW core parses only the generated header tools/ftw_repository.py prepends, never the source's own DRIVER block further down. That header never copied config_secrets, so myuplink, nibe_local, sonnen and tibber all published catalog entries with nothing declared to mask -- confirmed live: GET /api/config on a box running the channel build returned myuplink's client_secret and refresh_token in clear text. Fixes #106. Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBX7xb1rd9ndEhCYUmZMjp
The config_secrets fix changes these four drivers' published artifact bytes -- the signed channel now emits their config_secrets list in the generated header -- so the channel's own version rule (checked in CI as "signed channel accepts this tree") requires a new version before it will publish the changed bytes. No Lua source changed beyond the version field. Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBX7xb1rd9ndEhCYUmZMjp
|
The Pushed Generated by Claude Code |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |


Summary
tools/ftw_repository.pyprepends a generatedDRIVER = { … }header ahead of a driver's own source block. FTW core parses only the firstDRIVERblock it finds (extractDriverBlock), so that generated header is the one that actually reaches a running box. It already copiesid,version,capabilities,tested_modelsand a few other fields — but neverconfig_secrets, the field that tells a host which config keys to mask in API responses and logs.Confirmed live on
myuplink(drivers-stable,driver-myuplink-v1.2.0-45cc4ad15760e4e8.lua): the generated header omitsconfig_secrets, andGET /api/drivers/catalog/GET /api/configon a box running that channel build returnedclient_secretandrefresh_tokenin clear text, even though the source's ownDRIVERblock further down (line 112 of that artifact) still correctly declaresconfig_secrets = { "client_secret", "refresh_token" }.The fix: read
config_secretsfrom the driver's ownDRIVERtable (via the existing_string_list_fieldhelper, same astested_models) and carry it into the generated header when present. Four drivers declare the field today —myuplink,nibe_local,sonnen,tibber— and all four now get it in their published artifact.Scope note: the issue (#106) also mentions
write_capabilitiesandcontrolsas possibly-missing fields.controlsis already carried through the signed identity via thepreserve_controlsmechanism (see #85), and no driver declareswrite_capabilitiesyet, so there's nothing there to fix today — this PR stays scoped to the confirmed, currently-liveconfig_secretsgap.Fixes #106.
Driver evidence
Not applicable — this is a channel-build tooling fix (
tools/ftw_repository.py), not a driver change. No Lua source, register map or manifest is touched.Safety
client_secret,refresh_token, etc. — are already public in the drivers' own source; no secret values appear anywhere in this diff).Package or promotion evidence
Not applicable — no package version, target or control contract changes.
Checks
Signed-off-by.uv run --frozen --extra package --extra dev python -m pytest tests/test_ftw_repository.py -q— 44 passed, 3 skipped (new regression testtest_config_secrets_reach_the_generated_headerincluded).uv run --frozen --extra package --extra dev pytest -q drivers/tests tests— 4035 passed, 1309 skipped. (One pre-existing, unrelated failure —test_history_never_rewrites_a_published_version— reproduces identically onmainwith no changes in this session's shallow-clone sandbox; it needs full git history that a shallow clone doesn't have, and is not caused by this diff.)make check— same shallow-clone-only failure as above (generate_history.py --check); every other step in the target (manifest sync/validate, index/devices/support-status generation, FTW-baseline check, host-API check, sandbox check) passes clean.🤖 Generated with Claude Code
https://claude.ai/code/session_01UBX7xb1rd9ndEhCYUmZMjp
Generated by Claude Code
Note
High Risk
This closes a confirmed credential disclosure path (secrets exposed via config API on channel builds). The fix is narrow tooling/metadata, but the blast radius for affected cloud/credential drivers was security-critical until published.
Overview
Fixes a channel-build gap where OAuth/API credentials could appear unmasked on the box. The signed channel prepends a generated
DRIVERblock that FTW core reads first; that header never copiedconfig_secrets, so hosts had no list of config keys to redact inGET /api/configeven when the Lua source declared them correctly below.tools/ftw_repository.pynow parsesconfig_secretsfrom the sourceDRIVERtable (same path astested_models) and emits it in the generated header when present. Regression coverage istest_config_secrets_reach_the_generated_headeron myuplink.Because the published artifact bytes change, myuplink 1.2.2, nibe_local 1.1.4, sonnen 2.0.2, and tibber 1.1.1 are patch-bumped with refreshed catalog manifests/index hashes; driver logic is unchanged aside from version strings.
Reviewed by Cursor Bugbot for commit 3aa2151. Bugbot is set up for automated code reviews on this repo. Configure here.