harden #5511: active provider owns its OWN providers: allowlist (complete ownership model)
Codex 5th CORE: the ownership set only covered static _PROVIDER_MODELS, not the active provider's own config.yaml providers.<active>.models allowlist. So an active provider defined purely via providers: (no static catalog) could be hijacked by another slug's overlapping entry earlier in config order. Fix folds the active provider's own providers: allowlist (list + dict shapes, copilot excluded) into _provider_models_set — completing the ownership model so the active provider owns its declared models from EITHER the static catalog OR its own providers: entry. Regression test (fail-without-fix verified). 40 model_resolver tests green. Co-authored-by: akay64 <akay64@users.noreply.github.com>
This commit is contained in:
parent
f2604a22bd
commit
d27ef5d196
2 changed files with 47 additions and 0 deletions
|
|
@ -2541,6 +2541,31 @@ def resolve_model_provider(model_id: str) -> tuple:
|
|||
m.get('id', '') for m in _PROVIDER_MODELS[_canon_config_provider]
|
||||
if isinstance(m, dict) and isinstance(m.get('id'), str)
|
||||
}
|
||||
# The active provider may be defined ENTIRELY via config.yaml `providers:`
|
||||
# (no static _PROVIDER_MODELS entry) with its own `models:` allowlist. Fold
|
||||
# that allowlist into the ownership set too, so the active provider owns its
|
||||
# own declared models and can't be hijacked by another providers.<slug>
|
||||
# entry that happens to list the same bare id earlier in config order (#5511).
|
||||
if _canon_config_provider:
|
||||
_providers_cfg_own = cfg.get('providers', {})
|
||||
if isinstance(_providers_cfg_own, dict):
|
||||
for _slug, _pdef in _providers_cfg_own.items():
|
||||
if not isinstance(_pdef, dict):
|
||||
continue
|
||||
if _canonicalise_provider_id(_slug) != _canon_config_provider:
|
||||
continue
|
||||
if _canon_config_provider == "copilot":
|
||||
continue # copilot.models is a settings map, not an allowlist
|
||||
_own_models = _pdef.get('models')
|
||||
if isinstance(_own_models, list):
|
||||
for _m in _own_models:
|
||||
_mid = str(_m.get('id') or '') if isinstance(_m, dict) else str(_m or '')
|
||||
if _mid.strip():
|
||||
_provider_models_set.add(_mid.strip())
|
||||
elif isinstance(_own_models, dict):
|
||||
_provider_models_set.update(
|
||||
str(k).strip() for k in _own_models if isinstance(k, str) and str(k).strip()
|
||||
)
|
||||
_skip_custom_providers = (
|
||||
_is_explicit_non_custom_provider
|
||||
and (
|
||||
|
|
|
|||
|
|
@ -897,3 +897,25 @@ def test_providers_scan_ownership_guard_canonicalises_gemini_alias_5511():
|
|||
"aliased active Gemini provider that owns the model must not be hijacked "
|
||||
f"by providers.openai.models; got {provider!r}"
|
||||
)
|
||||
|
||||
|
||||
def test_providers_scan_active_own_providers_entry_owns_over_other_slug_5511():
|
||||
"""An active provider defined purely via config.yaml `providers:` (no static
|
||||
catalog entry) owns the models in its OWN providers.<active>.models allowlist,
|
||||
so another provider's entry listing the same bare id (even earlier in config
|
||||
order) must NOT hijack it (#5511 gate finding 5)."""
|
||||
# 'openai' entry lists gpt-x first, but the ACTIVE provider (myprov) also
|
||||
# declares gpt-x in its own providers.myprov.models — active must win.
|
||||
model, provider, base_url = _resolve_with_providers(
|
||||
'gpt-x',
|
||||
{
|
||||
'openai': {'models': ['gpt-x']},
|
||||
'myprov': {'base_url': 'https://my.example/v1', 'models': ['gpt-x']},
|
||||
},
|
||||
provider='myprov',
|
||||
)
|
||||
assert provider == 'myprov', (
|
||||
"active provider's own providers: allowlist must own its model over "
|
||||
f"another slug's overlapping entry; got {provider!r}"
|
||||
)
|
||||
assert base_url == 'https://my.example/v1'
|
||||
|
|
|
|||
Loading…
Reference in a new issue