Skip to content

Commit 1acaa21

Browse files
Vamsi-kluclaude
andcommitted
Validate update_mask fields in PATCH endpoints against Pydantic models
When update_mask is provided, Pydantic validation was skipped entirely, allowing invalid field values to bypass validation and go straight to setattr() on ORM models. Now validation runs regardless of whether update_mask is provided. Affected endpoints: variables, connections, dags, pools. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 7d29a78 commit 1acaa21

4 files changed

Lines changed: 21 additions & 22 deletions

File tree

airflow/api_fastapi/core_api/routes/public/connections.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -199,11 +199,11 @@ def patch_connection(
199199

200200
if update_mask:
201201
fields_to_update = fields_to_update.intersection(update_mask)
202-
else:
203-
try:
204-
ConnectionBody(**patch_body.model_dump())
205-
except ValidationError as e:
206-
raise RequestValidationError(errors=e.errors())
202+
203+
try:
204+
ConnectionBody(**patch_body.model_dump(include=fields_to_update))
205+
except ValidationError as e:
206+
raise RequestValidationError(errors=e.errors())
207207

208208
data = patch_body.model_dump(include=fields_to_update - non_update_fields, by_alias=True)
209209

airflow/api_fastapi/core_api/routes/public/dags.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -227,11 +227,11 @@ def patch_dag(
227227
status.HTTP_400_BAD_REQUEST, "Only `is_paused` field can be updated through the REST API"
228228
)
229229
fields_to_update = fields_to_update.intersection(update_mask)
230-
else:
231-
try:
232-
DAGPatchBody(**patch_body.model_dump())
233-
except ValidationError as e:
234-
raise RequestValidationError(errors=e.errors())
230+
231+
try:
232+
DAGPatchBody(**patch_body.model_dump(include=fields_to_update))
233+
except ValidationError as e:
234+
raise RequestValidationError(errors=e.errors())
235235

236236
data = patch_body.model_dump(include=fields_to_update, by_alias=True)
237237

airflow/api_fastapi/core_api/routes/public/pools.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,12 @@ def patch_pool(
162162
fields_to_update = patch_body.model_fields_set
163163
if update_mask:
164164
fields_to_update = fields_to_update.intersection(update_mask)
165-
data = patch_body.model_dump(include=fields_to_update, by_alias=True)
166-
else:
167-
data = patch_body.model_dump(include=fields_to_update, by_alias=True)
168-
try:
169-
BasePool.model_validate(data)
170-
except ValidationError as e:
171-
raise RequestValidationError(errors=e.errors())
165+
166+
data = patch_body.model_dump(include=fields_to_update, by_alias=True)
167+
try:
168+
BasePool.model_validate(data)
169+
except ValidationError as e:
170+
raise RequestValidationError(errors=e.errors())
172171

173172
for key, value in data.items():
174173
setattr(pool, key, value)

airflow/api_fastapi/core_api/routes/public/variables.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,11 @@ def patch_variable(
151151
fields_to_update = patch_body.model_fields_set
152152
if update_mask:
153153
fields_to_update = fields_to_update.intersection(update_mask)
154-
else:
155-
try:
156-
VariableBody(**patch_body.model_dump())
157-
except ValidationError as e:
158-
raise RequestValidationError(errors=e.errors())
154+
155+
try:
156+
VariableBody(**patch_body.model_dump(include=fields_to_update))
157+
except ValidationError as e:
158+
raise RequestValidationError(errors=e.errors())
159159

160160
data = patch_body.model_dump(include=fields_to_update - non_update_fields, by_alias=True)
161161

0 commit comments

Comments
 (0)