Skip to content

fix(channel): carry config_secrets into the generated DRIVER header - #107

Merged
frahlg merged 2 commits into
mainfrom
claude/hopeful-fermi-vsbatx
Sep 5, 2026
Merged

fix(channel): carry config_secrets into the generated DRIVER header#107
frahlg merged 2 commits into
mainfrom
claude/hopeful-fermi-vsbatx

Conversation

@frahlg

@frahlg frahlg commented Sep 4, 2026

Copy link
Copy Markdown
Member

Summary

tools/ftw_repository.py prepends a generated DRIVER = { … } header ahead of a driver's own source block. FTW core parses only the first DRIVER block it finds (extractDriverBlock), so that generated header is the one that actually reaches a running box. It already copies id, version, capabilities, tested_models and a few other fields — but never config_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 omits config_secrets, and GET /api/drivers/catalog / GET /api/config on a box running that channel build returned client_secret and refresh_token in clear text, even though the source's own DRIVER block further down (line 112 of that artifact) still correctly declares config_secrets = { "client_secret", "refresh_token" }.

The fix: read config_secrets from the driver's own DRIVER table (via the existing _string_list_field helper, same as tested_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_capabilities and controls as possibly-missing fields. controls is already carried through the signed identity via the preserve_controls mechanism (see #85), and no driver declares write_capabilities yet, so there's nothing there to fix today — this PR stays scoped to the confirmed, currently-live config_secrets gap.

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

  • Not a driver change; no read/write behavior on any device is affected.
  • No credentials, serial numbers, private addresses or site data are included (the affected secret namesclient_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

  • Commits include 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 test test_config_secrets_reach_the_generated_header included).
  • 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 on main with 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 DRIVER block that FTW core reads first; that header never copied config_secrets, so hosts had no list of config keys to redact in GET /api/config even when the Lua source declared them correctly below.

tools/ftw_repository.py now parses config_secrets from the source DRIVER table (same path as tested_models) and emits it in the generated header when present. Regression coverage is test_config_secrets_reach_the_generated_header on 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.

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

frahlg commented Sep 4, 2026

Copy link
Copy Markdown
Member Author

The signed channel accepts this tree check failed because this was expected, once I read the rule closely: config_secrets reaching the generated header is exactly the kind of metadata change _load_channel copies into the signed artifact, so the four drivers that declare it (myuplink, nibe_local, sonnen, tibber) all get changed published bytes — and the channel's own version rule refuses to republish changed bytes under an already-published version.

Pushed 3aa2151: patch bump for all four (myuplink 1.2.2, nibe_local 1.1.4, sonnen 2.0.2, tibber 1.1.1), CHANGELOG entry added, no Lua source touched beyond the version field. Reproduced the exact CI command locally against the published drivers-beta manifest — tools/ftw_repository.py check-versions now exits 0 and reports only those four as changed, which is what this fix is supposed to do. Full test suite green (4035 passed; the one unrelated failure is this sandbox's shallow git clone hitting generate_history.py --check, reproduces identically on unmodified main).


Generated by Claude Code

@frahlg
frahlg marked this pull request as ready for review September 5, 2026 07:04
@frahlg
frahlg merged commit b46c6f2 into main Sep 5, 2026
8 checks passed
@frahlg
frahlg deleted the claude/hopeful-fermi-vsbatx branch September 5, 2026 07:04
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 5, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-05T07:05:43.299420Z 3aa2151 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved: Cursor Bugbot completed successfully with no findings that need human review. No reviewers were assigned.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Router and Approver

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Channel build prepends a DRIVER header that drops config_secrets, so the box masks nothing

2 participants