fix: reject dot project ids

This commit is contained in:
nac
2026-07-13 02:24:58 +00:00
parent 87a26196dd
commit b51be25e2e
4 changed files with 40 additions and 5 deletions
+10 -3
View File
@@ -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)
+9 -2
View File
@@ -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:
+11
View File
@@ -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")
+10
View File
@@ -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}])