ashb commented on code in PR #72499:
URL: https://github.com/apache/airflow/pull/72499#discussion_r4379281407
##########
airflow-core/src/airflow/api_fastapi/core_api/routes/public/dag_run.py:
##########
@@ -149,7 +184,10 @@ def get_dag_run(dag_id: str, dag_run_id: str, session:
SessionDep) -> DAGRunResp
status.HTTP_404_NOT_FOUND,
f"The DagRun with dag_id: `{dag_id}` and run_id: `{dag_run_id}`
was not found",
)
- return dag_run
+ response = DAGRunResponse.model_validate(dag_run)
+ dag = get_dag_for_run(dag_bag, dag_run, session=session)
+ response.conf = _mask_password_conf(dag, response.conf)
Review Comment:
Is there any way we could have it so that DagRunResponse applies this
masking _inside_ the response model, so that it's not missed or skipped?
##########
airflow-core/docs/core-concepts/params.rst:
##########
@@ -453,6 +457,50 @@ Finally the fourth section shows advanced form elements.
``description_html`` is now super-seeded with the attribute
``description_md``. ``description_html`` is not supported anymore.
Custom form elements using the attribute ``custom_html_form`` was
deprecated in version 2.8.0 and support was removed in 3.0.0.
+.. _params-masking:
+
+Masking Sensitive Param Values
+-------------------------------
+
+.. versionadded:: 3.4.0
+
+If a ``string``-typed :class:`~airflow.sdk.definitions.param.Param` is
declared with ``format="password"``,
+its value is masked wherever Airflow already masks sensitive values:
+
+.. code-block::
+
+ with DAG(
+ "the_dag",
+ params={
+ "api_token": Param(
+ default="",
+ type="string",
+ format="password",
+ ),
+ },
+ ):
+
+This is masked in:
+
+- The Trigger UI Form, as a password-style input with a show/hide toggle.
Review Comment:
```suggestion
- The Trigger Dag UI form, as a password-style input with a show/hide toggle.
```
##########
airflow-core/src/airflow/api_fastapi/core_api/routes/public/dag_run.py:
##########
@@ -129,6 +129,41 @@
dag_run_at_dag_router = AirflowRouter(tags=["DagRun"], prefix="/dags/{dag_id}")
+def _mask_password_conf(dag, conf: dict | None) -> dict | None:
+ """Redact conf values for keys whose declared Param uses schema
format="password"."""
+ if not conf or dag is None:
+ return conf
+ params = getattr(dag, "params", None)
+ if not params:
+ return conf
+ masked = dict(conf)
+ for key in masked:
+ if key not in params:
+ continue
+ schema = params.get_param(key).schema
+ if schema.get("type") == "string" and schema.get("format") ==
"password":
+ masked[key] = "***"
+ return masked
+
+
+def _build_masked_dag_run_responses(dag_runs, dag_bag, session) ->
list[DAGRunResponse]:
+ """
+ Convert ORM DagRuns to DAGRunResponse, redacting password-format conf keys
per-run.
+
+ Deliberately does not cache DAGs by dag_id: two DagRuns can share a dag_id
but resolve to
+ different Dag versions (get_dag_for_run keys off
dag_run.created_dag_version_id), and a param's
+ format="password" declaration can differ between versions. dag_bag itself
already caches by
+ dag_version_id internally, so this stays cheap without an extra, incorrect
layer on top.
Review Comment:
This comment doesn't feel useful to keep?
--
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]