Skip to content

fix(inference): validate local TTS output paths - #406

Merged
senamakel merged 8 commits into
epic/8092-inference-outfrom
epic-8092-245-tts-validation
Oct 18, 2026
Merged

senamakel merged 8 commits into
epic/8092-inference-outfrom
epic-8092-245-tts-validation

Conversation

@senamakel

@senamakel senamakel commented Oct 18, 2026 •

Copy link
Copy Markdown
Owner

What changed

The local AI TTS host adapter now rejects empty or whitespace-only text before resolving Piper and validates requested output paths before creating directories or starting Piper. Outputs are confined to the workspace or the configured local-AI models directory; traversal, protected paths, and symlink escapes (including a symlink at the final output filename) are rejected.

The path check uses OpenHuman's security policy and preserves its always-forbidden path floor. Tests cover the allowed roots with autonomy enabled, outside-root paths, traversal, parent and final-component symlinks, and /etc remaining forbidden even when configured as an allowed workspace root.

Refs #245
Part of tinyhumansai#8092

Validation

  • cargo test --manifest-path Cargo.toml -p openhuman --lib inference::host_runtime::service::speech::tests — passed (7 tests) at 758186ea681f8b5ec2fa0b19a5660f04642e0e0f.
  • cargo fmt --manifest-path Cargo.toml -p openhuman --check — passed.
  • git diff --check — passed.
  • Before the fix, both regressions reached Piper lookup and failed with piper binary not found instead of rejecting blank text or an outside-root output path.
  • pnpm rust:layout — reports a pre-existing stale audited allowance for tokio::fs::write(&target, body) in crates/openhuman-core/src/agent/harness/artifact_offload/mod.rs; the speech changes do not touch that code.
  • Root/app Cargo checks, clippy, and pnpm debug rust inference are pending serialized execution due to host memory pressure.

The speech tests now build LocalAiService from the local runtime config derived from the app config, matching the updated constructor signature.

Auto-committed-on: dragonfly
…/service/speech.rs,crates/openh

Auto-committed-on: dragonfly
Rustfmt-style line wrapping was applied to the TTS path validation error
construction and to several test invocations. No behaviour changed; the
reformatting only adjusts how long expressions are broken across lines.

Auto-committed-on: dragonfly
The TTS output path validator now rejects a final path component that is a
symlink, closing a gap where a symlink inside an allowed root could redirect
writes outside it. Root resolution was reworked to reuse the security policy's
path validation instead of a bespoke canonicalization helper.

Auto-committed-on: dragonfly
The TTS output path validator probed each allowed root by validating a
synthetic child file, which failed when the root directory did not exist
yet. Roots are now resolved by canonicalizing the nearest existing ancestor
and re-appending the missing suffix, and the workspace directory is
registered as a trusted root so workspace-only configurations accept
outputs written there.

Auto-committed-on: dragonfly
The validator now rejects paths outside the workspace or local-ai models
directory before resolving them, and refuses roots that canonicalize into
protected locations. Canonicalization also distinguishes missing paths from
other filesystem errors instead of treating every failure as a missing suffix.

Auto-committed-on: dragonfly
The test now builds a Config with a workspace directory under /etc instead of relying on the enabled_config helper, so the always-forbidden path assertion is exercised against a workspace that actually overlaps the blocked prefix.

Auto-committed-on: dragonfly
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