Commit Graph
6 Commits
Author SHA1 Message Date
Adam SmithandClaude Opus 5 9d8d1970f7 fix: drop the URL from the base_url description for hassfest
The LAN-address scrub replaced 10.10.70.133 with litellm.local in
strings.json, which broke the hassfest job that had been green:

  [ERROR] [TRANSLATIONS] Invalid strings.json: the string should not
  contain URLs, please use description placeholders instead at
  'config.step.user.data_description.base_url'

The rule is not new -- the previous text was equally a URL. It slipped
through because script/hassfest/translations.py's RE_URL requires an
alphabetic TLD ([a-z]{2,5}), so "http://10.10.70.133:4000/v1" never
matched (".133" is not alphabetic) while "http://litellm.local:4000/v1"
does. Verified by running core's actual regex against both strings, so
the scrub did not introduce a violation so much as stop hiding one.

Rather than wire up a description placeholder for what is only an
example, the description now describes the field without any URL:
concrete endpoint examples live in the README, which has no such
constraint. The reauth_confirm step picks this up automatically -- it
references the user step's strings via [%key:...%] rather than copying
them.

Also re-ran core's RE_URL over every string in strings.json, not just
the one hassfest named: 0 remaining violations, so this should not
bounce again on a second string.

Local: 138 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NxJYWgGJtCw13qUHZtG8ki
2026-09-17 22:09:29 -04:00
Adam SmithandClaude Opus 5 99003c0cca chore: replace a real LAN address with documentation examples
Pre-publication audit of the full history (all 14 commits, every blob)
found no API keys, tokens, JWTs, private keys or credentials -- the
`user:secret@` strings in the tests are synthetic literals. It did find
one piece of personal infrastructure: 192.0.2.10:4000, a real
RFC1918 host, in 8 places.

Most of those are test fixtures, but one was shipping to users:
strings.json's base_url data_description used it as *the* example in the
config flow, so every install would have displayed a stranger's
internal LiteLLM address as the suggested endpoint. That is a bug
independent of whether this repo is public.

- strings.json: example is now http://litellm.local:4000/v1
- tests: 192.0.2.10 (RFC 5737 TEST-NET-1, reserved for documentation,
  so it can never collide with a real host)

Not addressed here: the address remains in the 14 commits of git
history, which publishing would expose. Scrubbing that needs a history
rewrite, which is a separate decision. The commit author email is
likewise in every commit and would become public.

Local: 137 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NxJYWgGJtCw13qUHZtG8ki
2026-09-17 21:54:55 -04:00
Adam SmithandClaude Sonnet 5 b96b46495a fix: reauth must preserve base_url; reject credentials embedded in base_url
Critical (review finding 1): async_step_reauth_confirm showed
STEP_USER_DATA_SCHEMA directly instead of prefilling it via
add_suggested_values_to_schema (unlike async_step_user, which does),
so the reauth form displayed DEFAULT_BASE_URL rather than the entry's
configured endpoint. A user rotating their API key would have their
integration silently repointed from e.g. LiteLLM back to
api.openai.com on save. Fixed by prefilling from
self._get_reauth_entry().data, same as the user step. strings.json's
reauth_confirm block now documents the base_url field too.
Regression test: test_reauth_preserves_base_url (asserts both the
shown form's suggested_value and the final stored base_url).

Important (review finding 2): base_url is user-suppliable and could
carry embedded user:pass@ credentials that later surface through
vendored exception logging (e.g. entity.py's "Error talking to
OpenAI: %s") this fork does not and should not edit. Rather than
touch vendored logging, reject credentials at the boundary:
normalize_base_url now raises the new CredentialsInBaseURL (a
subclass of InvalidBaseURL) when the URL has a username or password,
mapped to a distinct "credentials_in_url" form error telling the user
to use the API key field instead. Tests added in test_url_util.py and
test_config_flow.py.

Important (review finding 3): documented on sanitize_url_for_logging
that it is not a general-purpose credential scrubber -- it only masks
what urlparse recognizes as a netloc's user:pass@ authority component,
so a scheme-less/relative string with embedded credentials passes
through unmasked. Safe today because every caller normalizes first
(which now also rejects embedded credentials outright), but it is
exported for a later task's use and must not be assumed bulletproof
on an arbitrary unvalidated string.

Not fixed (reviewer's Minor 4, by direction): the default-config setup
call reaching api.openai.com with Authorization: Bearer not-required
when both base_url and api_key are left at defaults -- cosmetic, and
correctly surfaces as invalid_auth.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01739Seac5eMAfNwFeiwV5r4
2026-09-17 18:08:20 -04:00
Adam SmithandClaude Sonnet 5 914c89dc6b feat: normalize base_url and strip credentials from logs
New url_util.py (deliberately separate from the vendored files, so a
future upstream re-sync never conflicts with it): normalize_base_url
strips whitespace/trailing slashes and rejects non-http(s) or hostless
URLs; sanitize_url_for_logging masks embedded basic-auth credentials
for Task 7's discovery logging.

Wires normalization into config_flow.validate_input before the client
is built, with a new invalid_base_url form error.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01739Seac5eMAfNwFeiwV5r4
2026-09-17 17:53:10 -04:00
Adam SmithandClaude Sonnet 5 564c52159f feat: configurable base_url threaded to all three client sites
Adds CONF_BASE_URL/DEFAULT_BASE_URL and threads base_url through every
AsyncOpenAI construction site: config_flow.validate_input,
__init__.async_setup_entry, and config_flow's web-search geolocation
helper (_get_location_data) — the easy one to miss, since it would
otherwise silently talk to api.openai.com when the user configured a
different endpoint.

Fixes upstream-ported fixtures (conftest.mock_config_entry,
test_config_flow's inline MockConfigEntry/data-schema assertions) to
supply base_url now that the config-flow schema requires it.

No translations/en.json exists in this repo (only strings.json), so
only strings.json got the new "base_url" UI strings.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01739Seac5eMAfNwFeiwV5r4
2026-09-17 17:50:38 -04:00
Adam SmithandClaude Sonnet 5 9d8c36f793 feat: vendor openai_conversation from core 2026.9.2, rename domain
Verbatim copy from home-assistant/core at tag 2026.9.2, with only the
domain constant and manifest changed. Apache-2.0, attribution in NOTICE.

Also fixed self-references the domain rename left dangling: the
config_entry selector's `integration:` field in services.yaml, the
`[%key:component::openai_conversation::...%]` translation
cross-references in strings.json, the deprecated-action descriptions
naming the old domain, and config_flow.py's instructions_url (now
pointing at this repo instead of the core docs page).

Local pytest run skipped per owner decision (see prior commit); files
validated with py_compile / json.load / yaml.safe_load instead. CI
(added in the prior commit) is the real gate.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01739Seac5eMAfNwFeiwV5r4
2026-09-17 17:23:37 -04:00