kaxil commented on code in PR #72640:
URL: https://github.com/apache/airflow/pull/72640#discussion_r3951113534


##########
airflow-core/src/airflow/api_fastapi/core_api/routes/ui/partitioned_dag_runs.py:
##########
@@ -283,7 +283,14 @@ def get_partitioned_dag_runs(
 
     if not (rows := session.execute(query).all()):
         if dag_id.value is not None and total_entries == 0:
-            dag_exists = 
session.scalar(select(DagModel.dag_id).where(DagModel.dag_id == dag_id.value))
+            # Scope the existence probe to Dags the caller may read: without 
this, an
+            # unreadable-but-existing Dag returns 200-empty while a 
nonexistent Dag
+            # returns 404, letting the caller distinguish the two and learn 
which Dags
+            # exist outside their permitted set.
+            dag_exists_query = select(DagModel.dag_id).where(DagModel.dag_id 
== dag_id.value)

Review Comment:
   3.3.0 and 3.3.1 ship this same unscoped probe (the readable-dags row filter 
landed in #65344, which is in 3.3.0), so the leak is in released versions, not 
just main. Does this want a v3-3 backport?



##########
airflow-core/src/airflow/api_fastapi/core_api/routes/ui/partitioned_dag_runs.py:
##########
@@ -283,7 +283,14 @@ def get_partitioned_dag_runs(
 
     if not (rows := session.execute(query).all()):
         if dag_id.value is not None and total_entries == 0:
-            dag_exists = 
session.scalar(select(DagModel.dag_id).where(DagModel.dag_id == dag_id.value))
+            # Scope the existence probe to Dags the caller may read: without 
this, an
+            # unreadable-but-existing Dag returns 200-empty while a 
nonexistent Dag
+            # returns 404, letting the caller distinguish the two and learn 
which Dags
+            # exist outside their permitted set.
+            dag_exists_query = select(DagModel.dag_id).where(DagModel.dag_id 
== dag_id.value)
+            if readable_dag_ids is not None:
+                dag_exists_query = 
dag_exists_query.where(DagModel.dag_id.in_(readable_dag_ids))

Review Comment:
   `readable_dag_ids` already comes out of `DagModel` in both in-tree auth 
managers (`BaseAuthManager.get_authorized_dag_ids` selects `DagModel.dag_id` 
joined to `DagBundleModel`, and FAB's override selects from `DagModel` in both 
branches), so this is equivalent to a plain `dag_id.value in readable_dag_ids` 
and would skip the query plus a potentially large IN list. Is the `DagModel` 
round-trip deliberate cover for a third-party auth manager that returns ids not 
in `DagModel`?



##########
airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_partitioned_dag_runs.py:
##########
@@ -301,6 +301,29 @@ def 
test_partitioned_dag_runs_filters_unreadable_dags(self, _, test_client, dag_
         dag_ids = {r["dag_id"] for r in body["partitioned_dag_runs"]}
         assert "restricted_dag" not in dag_ids
 
+    @mock.patch(
+        
"airflow.api_fastapi.auth.managers.base_auth_manager.BaseAuthManager.get_authorized_dag_ids",
+        return_value={"other_dag"},
+    )
+    def test_dag_id_filter_does_not_disclose_unreadable_dag_existence(
+        self, _, test_client, dag_maker, session
+    ):
+        """
+        An unreadable-but-existing Dag must not be distinguishable from a 
nonexistent
+        Dag via the ``dag_id`` filter: both return 404. Without the scoped 
existence
+        probe, the existing Dag returned 200-empty while the nonexistent one 
returned
+        404, giving the caller an oracle for Dag ids outside their permitted 
set.
+        """
+        schedule = PartitionedAssetTimetable(assets=Asset(uri="s3://bucket/a", 
name="a"))
+        with dag_maker(dag_id="restricted_dag", schedule=schedule, 
serialized=True):
+            EmptyOperator(task_id="t")
+        dag_maker.sync_dagbag_to_db()
+        session.commit()

Review Comment:
   Worth adding an `AssetPartitionDagRun` for `restricted_dag` here, the way 
`test_partitioned_dag_runs_filters_unreadable_dags` above does. With no rows at 
all the 404 comes from the existence probe alone, so this passes even if the 
readable filter on the main query were removed, and that filter dropping the 
rows is the case the docstring describes.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to