fix: reject dot project ids
This commit is contained in:
@@ -86,8 +86,15 @@ class NocoBaseHttpClient:
|
||||
|
||||
|
||||
def _project_id_segment(project_id: Any) -> str:
|
||||
return quote(validate_project_id(project_id), safe="")
|
||||
|
||||
|
||||
def validate_project_id(project_id: Any) -> str:
|
||||
if isinstance(project_id, bool) or not isinstance(project_id, (str, int)):
|
||||
raise ValueError("project_id must be a scalar identifier")
|
||||
if isinstance(project_id, str) and not project_id.strip():
|
||||
raise ValueError("project_id must not be blank")
|
||||
return quote(str(project_id), safe="")
|
||||
if isinstance(project_id, str):
|
||||
if not project_id.strip():
|
||||
raise ValueError("project_id must not be blank")
|
||||
if project_id in {".", ".."}:
|
||||
raise ValueError("project_id must not be a dot path segment")
|
||||
return str(project_id)
|
||||
|
||||
@@ -5,7 +5,7 @@ from datetime import date
|
||||
from typing import Any
|
||||
|
||||
from .config import ProjectConfig
|
||||
from .nocobase_http import NocoBaseHttpError
|
||||
from .nocobase_http import NocoBaseHttpError, validate_project_id
|
||||
|
||||
|
||||
def _configured_scalar_fields(config: ProjectConfig) -> tuple[str, ...]:
|
||||
@@ -97,7 +97,7 @@ class ProjectService:
|
||||
remove_attachment_ids: Sequence[Any] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
try:
|
||||
_validate_identifier(project_id, "project_id")
|
||||
_validate_project_id(project_id)
|
||||
values = self._project_values(
|
||||
name=name,
|
||||
customer_id=customer_id,
|
||||
@@ -223,6 +223,13 @@ def _validate_identifier(value: Any, name: str) -> None:
|
||||
raise _InvalidProjectInput(f"{name} must not be blank")
|
||||
|
||||
|
||||
def _validate_project_id(project_id: Any) -> None:
|
||||
try:
|
||||
validate_project_id(project_id)
|
||||
except ValueError as error:
|
||||
raise _InvalidProjectInput(str(error)) from error
|
||||
|
||||
|
||||
def _attachment_error(project_id: Any, error: Exception) -> dict[str, Any]:
|
||||
if isinstance(error, NocoBaseHttpError):
|
||||
if error.status_code == 404:
|
||||
|
||||
@@ -92,6 +92,17 @@ class NocoBaseHttpClientTests(unittest.TestCase):
|
||||
with self.assertRaisesRegex(ValueError, "project_id"):
|
||||
client.add_attachment_ids({"id": 7}, [8])
|
||||
|
||||
def test_attachment_association_rejects_dot_path_project_ids_before_request(self) -> None:
|
||||
client = NocoBaseHttpClient(
|
||||
"https://nb.example/api",
|
||||
"jwt",
|
||||
transport=httpx.MockTransport(lambda request: self.fail("request must not be sent")),
|
||||
)
|
||||
|
||||
for project_id in (".", ".."):
|
||||
with self.subTest(project_id=project_id), self.assertRaisesRegex(ValueError, "project_id"):
|
||||
client.add_attachment_ids(project_id, [8])
|
||||
|
||||
def test_rejects_blank_upload_credentials(self) -> None:
|
||||
with self.assertRaises(ValueError):
|
||||
NocoBaseHttpClient(" ", "jwt")
|
||||
|
||||
@@ -145,6 +145,16 @@ class ProjectServiceTests(unittest.TestCase):
|
||||
self.assertEqual(self.nb.update_calls, [])
|
||||
self.assertEqual(self.http.add_calls, [])
|
||||
|
||||
def test_update_rejects_dot_path_project_id_before_any_client_call(self) -> None:
|
||||
for project_id in (".", ".."):
|
||||
with self.subTest(project_id=project_id):
|
||||
self.setUp()
|
||||
result = self.service.update_project(project_id, name="x", add_attachment_ids=[8])
|
||||
|
||||
self.assertEqual(result["error"]["code"], "invalid_project_input")
|
||||
self.assertEqual(self.nb.update_calls, [])
|
||||
self.assertEqual(self.http.add_calls, [])
|
||||
|
||||
def test_create_rejects_nested_attachment_id_before_any_client_call(self) -> None:
|
||||
result = self.service.create_project(name="x", attachment_ids=[{"id": 8}])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user