mirror of
https://github.com/usestrix/strix.git
synced 2026-10-15 04:18:10 +00:00
fix(api_spec): exclude specification extensions and non-operation fields from server extraction
- Restrict operation server extraction in _collect_path_item_servers strictly to valid HTTP methods (_OPENAPI_OPERATION_METHODS). - Skip vendor extension properties (x-*) at the paths and webhooks dictionary level. - Ensure non-operation dictionary structures (e.g. x-deployment, parameters) never authorize unintended hosts into scan scope. - Add regression test test_openapi_ignores_vendor_extensions_in_paths_and_operations.
This commit is contained in:
parent
5346bc53e4
commit
c73b018f1d
2 changed files with 39 additions and 2 deletions
|
|
@ -123,13 +123,21 @@ def _resolve_server_url(url: str, variables: Any) -> str:
|
|||
return _SERVER_VAR_PATTERN.sub(lambda m: defaults.get(m.group(1), m.group(0)), url)
|
||||
|
||||
|
||||
_OPENAPI_OPERATION_METHODS = frozenset(
|
||||
{"get", "put", "post", "delete", "options", "head", "patch", "trace"}
|
||||
)
|
||||
|
||||
|
||||
def _collect_path_item_servers(path_item: Any, server_objs: list[dict[str, Any]]) -> None:
|
||||
if not isinstance(path_item, dict):
|
||||
return
|
||||
path_servers = path_item.get("servers")
|
||||
if isinstance(path_servers, list):
|
||||
server_objs.extend(s for s in path_servers if isinstance(s, dict))
|
||||
for op in path_item.values():
|
||||
# Inspect only standard HTTP method operations; skip metadata, parameters,
|
||||
# and vendor extensions (x-*).
|
||||
for method in _OPENAPI_OPERATION_METHODS:
|
||||
op = path_item.get(method)
|
||||
if isinstance(op, dict):
|
||||
op_servers = op.get("servers")
|
||||
if isinstance(op_servers, list):
|
||||
|
|
@ -148,7 +156,9 @@ def _openapi_base_urls(raw: dict[str, Any]) -> list[str]:
|
|||
for section_key in ("paths", "webhooks"):
|
||||
section = raw.get(section_key)
|
||||
if isinstance(section, dict):
|
||||
for path_item in section.values():
|
||||
for path_key, path_item in section.items():
|
||||
if str(path_key).startswith("x-"):
|
||||
continue
|
||||
_collect_path_item_servers(path_item, server_objs)
|
||||
|
||||
return _absolute_urls(
|
||||
|
|
|
|||
|
|
@ -208,6 +208,33 @@ def test_openapi_extracts_webhook_servers() -> None:
|
|||
assert spec_base_urls(raw) == ["https://webhook-receiver.test"]
|
||||
|
||||
|
||||
def test_openapi_ignores_vendor_extensions_in_paths_and_operations() -> None:
|
||||
raw = {
|
||||
"openapi": "3.0.0",
|
||||
"info": {"title": "Extensions Test"},
|
||||
"paths": {
|
||||
"x-internal-path": {
|
||||
"servers": [{"url": "https://leaked-internal.test"}],
|
||||
"get": {"servers": [{"url": "https://leaked-internal-op.test"}]},
|
||||
},
|
||||
"/api/valid": {
|
||||
"servers": [{"url": "https://valid.test"}],
|
||||
"get": {
|
||||
"servers": [{"url": "https://valid-get.test"}],
|
||||
"summary": "Valid GET operation",
|
||||
},
|
||||
"x-deployment": {
|
||||
"servers": [{"url": "https://admin-extension.test"}],
|
||||
},
|
||||
"parameters": [
|
||||
{"name": "id", "in": "query", "servers": [{"url": "https://param.test"}]}
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
assert spec_base_urls(raw) == ["https://valid.test", "https://valid-get.test"]
|
||||
|
||||
|
||||
def test_swagger_base_urls_built_from_host() -> None:
|
||||
assert spec_base_urls(SWAGGER_JSON) == ["https://legacy.test/api"]
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue