Skip to content

security(homecore-migrate): redact secret value from malformed secrets.yaml error#1089

Merged
ruvnet merged 2 commits into
mainfrom
feat/v2-beyond-sota-sweep
Jun 15, 2026
Merged

security(homecore-migrate): redact secret value from malformed secrets.yaml error#1089
ruvnet merged 2 commits into
mainfrom
feat/v2-beyond-sota-sweep

Conversation

@ruvnet

@ruvnet ruvnet commented Jun 15, 2026

Copy link
Copy Markdown
Owner

homecore-migrate security review (ADR-165) — completes the HOMECORE / HA stack sweep

Final crate in the Home-Assistant compatibility stack review (core · api · automation · recorder · assist · migrate).

One real finding — secret-leak (Grade A)

A malformed secrets.yaml whose offending scalar fails a typed-tag coercion (e.g. port: !!int <value>) produced a serde_yaml error that embeds the scalar verbatiminvalid value: string "<the-secret-value>". Wrapped into MigrateError::YamlParse, it propagated out of read_secrets, was ?-returned by the InspectSecrets CLI path, and printed to stderr by anyhow — leaking a secret value despite the CLI's deliberate <redacted> design (which only ever prints keys).

Fix: secrets.yaml parse failures map to a dedicated redacting variant MigrateError::SecretsParse { path, line, column } — file path + coarse serde_yaml::Error::location() only, never the scalar. Other (non-secret) YAML keeps YamlParse.

Pinned by secrets::tests::malformed_secrets_error_never_contains_secret_value — asserts the rendered error and its full #[source] chain never contain the value and that it is still the structured SecretsParse (fail-closed). Observed leak on old code: ... invalid value: string "s3cr3t_TOKEN_VALUE" ...; passes on the fix. Plus malformed_secrets_error_reports_location (still locatable).

Dimensions confirmed clean (with evidence)

  • Secret leakage elsewhere — only secret sink is the value map; main.rs prints keys as <key> = <redacted>; MissingField/Io paths surface the path, never content.
  • Source mutation / data-lossstructurally impossible: no fs::write/fs::remove/fs::create/File::create/OpenOptions anywhere in the crate; P1 reads source and writes nothing. Re-runs idempotent; HA source never touched.
  • Path traversal — fixed filenames joined onto a user dir; no user-controlled path component, no ../absolute escape.
  • Panic-on-input — duplicate-key, bad-indent, control-char, multi-doc, non-mapping-root, unterminated-flow, !input blueprint tags, deep nesting, anchors: every malformed input errors, never panics. All unwrap/expect are #[cfg(test)].
  • Fail-closed versioning — unknown storage minor_version hard-errors.
  • Injection — no SQL/shell/path interpolation; diagnostics only, persists nothing in P1.

Validation

  • cargo test -p homecore-migrate --no-default-features21 passed, 0 failed (+2 pins, 19→21)
  • cargo test --workspace --no-default-featuresGREEN (exit 0, no failures)
  • python archive/v1/data/proof/verify.pyVERDICT: PASS, hash f8e76f21…46f7a unchanged (off the signal proof path)

🤖 Generated with claude-flow

ruvnet added 2 commits June 14, 2026 22:48
…l error (secret-leak)

`read_secrets` wrapped serde_yaml's parse error into `MigrateError::YamlParse {
source }`. serde_yaml's message for a typed-tag coercion failure embeds the
offending scalar verbatim, e.g. `invalid value: string "<the-secret-value>"`.
That error propagates out of `read_secrets`, is `?`-returned by the
`InspectSecrets` CLI path in main.rs, and printed to stderr by anyhow — leaking
a secret value despite the CLI's deliberate `<redacted>` design.

Fix: secrets.yaml parse failures now map to a new redacting variant
`MigrateError::SecretsParse { path, line, column }` that carries only the file
path and a coarse location (from `serde_yaml::Error::location()`), never the
scalar content. Other (non-secret) YAML files keep `YamlParse`.

Pinned by `secrets::tests::malformed_secrets_error_never_contains_secret_value`
(asserts the rendered error AND its full #[source] chain never contain the
secret value; fails on the old `YamlParse` path) plus
`malformed_secrets_error_reports_location` (still fail-closed + locatable).

ADR-165 secret-handling rule: a secret value must never appear in output.

Co-Authored-By: claude-flow <ruv@ruv.net>
Note the secrets.yaml error-redaction fix and the review's clean dimensions
(read-only source / no traversal / no panic / fail-closed versioning / no
injection) in ADR-165 §2.4, bump the test-evidence count 19→21 in §2.6, and add
an [Unreleased] Security entry to CHANGELOG.

Co-Authored-By: claude-flow <ruv@ruv.net>
@ruvnet ruvnet merged commit 5287497 into main Jun 15, 2026
19 checks passed
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.

1 participant