Skip to content

Commit 8de9c51

Browse files
fix(#256): handle nul character in string parameters
1 parent 89e101f commit 8de9c51

File tree

3 files changed

+53
-0
lines changed

3 files changed

+53
-0
lines changed

tests/test_collections.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,3 +692,12 @@ def test_collections_cql_filter(filter_expr, filter_lang, app):
692692
assert len(resp) == 1
693693
assert list(resp[0]) == ["id", "bbox", "assets", "collection"]
694694
assert resp[0]["id"] == "20200307aC0853000w361030"
695+
696+
697+
def test_nul_byte_handling(app) -> None:
698+
"""Ensure nul bytes do not generate 500 errors"""
699+
response = app.get("/collections/missing_collection%00id/tiles")
700+
assert response.status_code == 404
701+
702+
response = app.get("/collections/missing_collection_id/tiles?param1=bad%00value")
703+
assert response.status_code == 404

titiler/pgstac/main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
add_search_list_route,
5151
add_search_register_route,
5252
)
53+
from titiler.pgstac.middleware import StripNulMiddleware
5354
from titiler.pgstac.reader import PgSTACReader
5455
from titiler.pgstac.settings import ApiSettings
5556

@@ -122,6 +123,7 @@ async def lifespan(app: FastAPI):
122123
cachecontrol=settings.cachecontrol,
123124
exclude_path=settings.cachecontrol_exclude_paths,
124125
)
126+
app.add_middleware(StripNulMiddleware)
125127

126128
optional_headers = []
127129
if settings.debug:

titiler/pgstac/middleware.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""titiler-pgstac Middleware."""
2+
3+
import re
4+
from typing import Final, cast
5+
from urllib.parse import quote
6+
7+
from starlette.types import ASGIApp, Receive, Scope, Send
8+
9+
_encoding: Final[str] = "utf-8"
10+
_nul: Final[str] = "\x00"
11+
12+
13+
class StripNulMiddleware:
14+
r"""
15+
Postgres cannot handle NUL bytes. If left unhandled \x00 will first cause problems
16+
when JSON is passed to SQL, as JSON escapes \x00 to \u0000 but does not escape the
17+
\ escape character. If this character _is_ escaped then Postgres will report
18+
"PostgreSQL text fields cannot contain NUL (0x00) bytes".
19+
This class presumes that no legitimate API client will intentionally submit NUL bytes
20+
and simply strips them from all parameters.
21+
"""
22+
23+
def __init__(
24+
self,
25+
app: ASGIApp,
26+
) -> None:
27+
"""Strip NUL Middleware."""
28+
self.app = app
29+
30+
async def __call__(self, scope: Scope, receive: Receive, send: Send):
31+
"""Remove NUL bytes from path and query string."""
32+
if scope["type"] != "http":
33+
await self.app(scope, receive, send)
34+
return
35+
36+
scope["path"] = re.sub(_nul, "", scope["path"])
37+
for property in ["query_string", "raw_path"]:
38+
scope[property] = re.sub(
39+
quote(_nul), "", cast(bytes, scope[property]).decode(_encoding)
40+
).encode(_encoding)
41+
42+
await self.app(scope, receive, send)

0 commit comments

Comments
 (0)