Skip to content

[security] Fix ISEC-V2026-03/04/05 authorization findings and move to Python 3.11 only - #4384

Merged
Harshg999 merged 6 commits into
masterfrom
fix/configuration-api-authorization
Oct 28, 2026
Merged

Harshg999 merged 6 commits into
masterfrom
fix/configuration-api-authorization

Conversation

@Harshg999

@Harshg999 Harshg999 commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Fixes three authorization findings and drops support for Python versions
below 3.11 (including GitHub Actions fixes).

ISEC-V2026-03: Improper authorization (IDOR) in the configuration API (Medium)

  • /desktop/api/configurations/, /user/ and /delete/ did not check ownership or admin role.
    A client-supplied user_id or configuration id was trusted, so any authenticated user could
    read, overwrite or delete another user's editor configuration and change the cluster-wide
    default.
  • New desktop/configuration/authorization.py:
    • GET and POST /desktop/api/configurations/ (global and group configuration) require a
      Hue admin.
    • /desktop/api/configurations/user/: non-admins can only use their own user_id. Admins can
      use any valid id.
    • /desktop/api/configurations/delete/: default and group rows require an admin. User rows
      require the owner or an admin.
  • CONFIG_SAVE_API in the frontend pointed at a route that does not exist
    (/configurations/save/). It now uses /desktop/api/configurations/user/.

ISEC-V2026-04: Server configuration disclosure via the configuration-check API (Low)

  • GET /api/v1/check_config was available to any authenticated user. It returns the Hue
    configuration directory and the validation results, which include internal service endpoints.
  • The endpoint now requires a Hue admin and returns HTTP 403 otherwise, with the same is_admin
    check as the legacy /desktop/debug/check_config view.
  • available_app_examples and install_app_examples use the same shared helper
    (deny_unless_hue_admin). Their behavior is unchanged.

ISEC-V2026-05: Improper authorization (IDOR) in the indexer save API (Medium)

  • POST /indexer/api/importer/save (save_pipeline) had its permission decorator commented out,
    and _save_pipeline loaded the target with Document2.objects.get(id=...) and no write check.
    Any authenticated user could overwrite another user's saved document.
  • New notebook/saved_document_auth.py with get_writable_document_for_update(). It resolves the
    document by parentSavedQueryUuid (write permission required) or by id
    (can_write_or_exception), and returns None for a new document.
  • @check_document_modify_permission() is re-enabled on save_pipeline. The decorator,
    _save_pipeline and _save_notebook all use the helper, so authorization still holds if a
    view decorator is ever missing.
  • A missing document and a denied write return the same message, so document ids cannot be probed.
    Admins keep write access to other users' documents, as before.

Python 3.11 only

  • CI (commitflow-py3.yml) runs a single Python 3.11 job, which also fixes the Ubuntu runner
    failure where setup-python could not find CPython 3.9.
  • Removed multi-version support from Makefile.vars, generate_requirements.py, the
    tools/cloudera and tools/container build scripts, the Dockerfiles, python_helper.sh and
    runruff. generate_requirements.py now fails clearly on an unsupported Python version.
  • Docs updated. Ruff target-version is now py311.

Behavior changes

  • A read-only shared document can no longer be saved over. Previously only read access was checked
    for parentSavedQueryUuid.
  • check_document_modify_permission also checks parentSavedQueryUuid when no id is sent.
  • Non-admins now get an error from the global /desktop/api/configurations/ endpoint.

Other changes in this PR

  • indexer/api3.py: the fetch_handle lambda is now a nested function (ruff E306).
  • Small ruff cleanups in notebook/api.py, notebook/decorators.py and api_public_tests.py
    (unused imports and bindings, and an undefined SkipTest replaced with pytest.skip).
  • Removed test_save_notebook_with_connector_on. It only ran with connectors enabled and used
    an undefined variable. test_save_notebook_with_connector_off remains.

Test plan

Automated:

  • New: TestConfigurationAuthorization, TestCheckConfigPublicApi, TestCheckConfigAPI
    (admin and non-admin), TestSavedDocumentAuth (including admin access and the same
    missing-vs-denied message), TestSavePipeline (model and HTTP, cross-user denied, owner
    allowed) and test_save_notebook_denies_cross_user_update.
  • hue runruff check passes.
  • Local run of the notebook, indexer, configuration and desktop API tests: no failure is
    specific to this branch. The failures that remain also occur on origin/master (HDFS and Hive
    are not configured locally).

Manual (live server, real sessions for an admin and two normal users, 39 of 40 checks pass):

  • Configuration API: a non-admin cannot read, save or delete another user's or global
    configuration. Own-user and admin flows work.
  • GET /api/v1/check_config: anonymous is rejected, a non-admin gets 403 with no server paths,
    and an admin gets 200.
  • Save endpoints: another user cannot overwrite a notebook or pipeline by id or
    parentSavedQueryUuid (database unchanged). Owner save and save-as work. A read-only share
    cannot be saved over and a write share can. An admin can save another user's private query.

@Harshg999
Harshg999 requested review from JohanAhlen and agl29 October 26, 2026 17:09
@Harshg999 Harshg999 self-assigned this Oct 26, 2026
@github-actions

Copy link
Copy Markdown

✅ Test files were modified. Ensure that the tests cover all relevant changes. ✅

@Harshg999
Harshg999 force-pushed the fix/configuration-api-authorization branch from 319cd13 to 5af8587 Compare October 26, 2026 17:10
@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

UI Code Coverage Report

Lines Statements Branches Functions
Coverage: 33%
40.15% (31632/78765) 32.06% (14985/46740) 26.4% (2414/9142)

@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Coverage

Warning

Your comment is too long (maximum is 65536 characters), so the coverage report was not added. See the job log for how to reduce it.

@Harshg999
Harshg999 force-pushed the fix/configuration-api-authorization branch 2 times, most recently from 9bde03b to b810fc7 Compare October 28, 2026 08:08
The DefaultConfiguration REST endpoints trusted a client-supplied
user_id and configuration id without checking ownership or admin role.
Any authenticated user could read or change another user's effective
settings, modify cluster-wide default/group configuration, or delete
arbitrary configuration rows.

- Add configuration/authorization.py with helpers for the global admin
  check, per-user target resolution and delete authorization.
- Require a Hue admin for GET/POST /desktop/api/configurations/.
- Resolve and authorize user_id on GET/POST
  /desktop/api/configurations/user/. Non-admins can only use their own
  id; admins can use any valid id.
- Authorize before deleting on /desktop/api/configurations/delete/:
  default and group rows need an admin, user rows need the owner or an
  admin.
- Point CONFIG_SAVE_API at /desktop/api/configurations/user/. The
  previous /configurations/save/ route does not exist.
- Add TestConfigurationAuthorization regression tests covering the
  cross-user, non-admin and admin cases.
GitHub-hosted ubuntu-latest now resolves to Ubuntu 26.04, where
actions/setup-python no longer publishes prebuilt CPython 3.9. Python 3
CI failed at the setup-python step with "version 3.9 was not found for
Ubuntu 26.04". Python 3.9 and 3.10 are also end of life.

Align the supported, tested and documented Python version on 3.11 only,
following the same direction as #4376 (drop EOL interpreters from the
matrix).

- commitflow-py3.yml: remove the 3.9/3.10/3.11 matrix and run a single
  job on Python 3.11. Always use build/env for pytest, ruff and
  artifacts.
- Makefile.vars: PYTHON_VERSIONS is python3.11 only, require minor
  version >= 11 and simplify the pip/virtualenv version settings.
- generate_requirements.py: drop the Python 3.9-specific pins on x86_64
  and aarch64 (3.11 entries unchanged).
- generate_requirements_test.py: update the tests for the 3.11 paths.
- Docs: installation dependencies, developer guide, SDK and the ci-cd
  design note now state Python 3.11 as the required version.

Follow-up (separate commit): tools/cloudera and the legacy container
Dockerfiles still reference older Python versions.
CI runs `hue runruff check` with PYTHONWARNINGS=always. The
configuration authorization changes introduced import order, unused
exception bindings and style issues in api.py and tests.py.

Apply the ruff fixes (I001, F401, F841, E303, E711, E712) so the Python
3 and ARM64 CI lint steps pass. No behavior change.
Follow-up to the CI/Makefile minimum of Python 3.11: remove the
remaining multi-Python and EOL interpreter paths across the Cloudera
builds, containers, scripts and requirement generation, so the
supported, tested and documented versions line up.

Cloudera packaging (tools/cloudera):
- build_hue_cloudera.sh: single python3.11 build/release. All supported
  OS targets use PYTHON311_OSES and always build into build/env.
- build_hue_common.sh: drop install_python39 and the Python 3.9 install
  hooks from every OS installer. install_sqlite_python only bootstraps
  3.11.12.

Runtime and developer tooling:
- python_helper.sh and hue.sh: resolve build/env/bin only and reject
  other HUE_PYTHON_VERSION values.
- Makefile.vars: always use build/env and inst/env (no build/venvs
  layout).
- runruff.py: always use build/env/bin/ruff.
- relocatable.sh: document and gate on Python 3.11.
- pyproject.toml: ruff target-version is py311 and pytest
  norecursedirs drops the removed 3.8-3.10 directories.

Requirements:
- generate_requirements.py: one 3.11 arch-specific pin set. Fail on an
  unsupported Python minor version instead of falling back to legacy
  "default" pins. Refresh the stale version comments.

Containers:
- UBI compile/base/hue Dockerfiles: python3.11 packages and pip paths.
  Install psycopg2-binary with pip instead of a cp38 wheel.

Tests:
- generate_requirements_test.py updated.

Not changed: historical gethue posts, vendored third-party CI configs
and tools/docker/hue/Dockerfile.py2 (legacy Python 2 image).
GET /api/v1/check_config was available to any authenticated user. It
returns the Hue configuration directory path and the configuration
validation errors, which expose server paths and internal service
topology. The legacy /desktop/debug/check_config view is already
admin-only.

- Gate check_config with deny_unless_hue_admin and return HTTP 403 with
  the shared "You must be a Hue admin" message for non-admins. The
  check uses the same is_admin helper as the legacy view.
- Switch available_app_examples and install_app_examples to the same
  helper. Their behavior and message are unchanged.
- Add TestCheckConfigPublicApi (anonymous, non-admin, admin), admin and
  non-admin unit tests for check_config, and unit tests for
  deny_unless_hue_admin.
- Point the existing api2 admin tests at the shared is_admin patch
  target. Fix the unused imports and the undefined SkipTest in
  api_public_tests.py that ruff flagged once the file was touched.
POST /indexer/api/importer/save (save_pipeline) had its permission
decorator commented out, and _save_pipeline loaded the target with a
raw Document2.objects.get(id=...) and no write check. Any authenticated
user could overwrite another user's saved document by id.

- Add notebook/saved_document_auth.py with
  get_writable_document_for_update(). It resolves the existing document
  by parentSavedQueryUuid (write permission required) or by id
  (can_write_or_exception), and returns None when a new document should
  be created. A missing document and a denied write raise the same
  message so document ids cannot be probed. Admins keep write access to
  other users' documents, as before.
- Re-enable @check_document_modify_permission() on save_pipeline.
- Make check_document_modify_permission, _save_pipeline and
  _save_notebook use the helper, so authorization still holds if a
  view decorator is missing or incomplete.
- check_document_modify_permission now also checks
  parentSavedQueryUuid when no id is sent, and a read-only share can no
  longer be saved over.
- Add tests: TestSavedDocumentAuth, TestSavePipeline (model and HTTP,
  cross-user denied, owner allowed) and
  test_save_notebook_denies_cross_user_update.

Housekeeping needed to pass ruff on the touched files: replace the
fetch_handle lambda in indexer api3.py with a nested function, drop
unused exception bindings in notebook/decorators.py and api.py, and
remove test_save_notebook_with_connector_on, which was skipped unless
connectors were enabled and referenced an undefined variable.
@Harshg999
Harshg999 force-pushed the fix/configuration-api-authorization branch from b810fc7 to 003aa8c Compare October 28, 2026 08:48
@Harshg999 Harshg999 changed the title [desktop] Enforce authorization on DefaultConfiguration API (ISEC-V2026-03) [security] Fix ISEC-V2026-03/04/05 authorization findings and move to Python 3.11 only Oct 28, 2026
@Harshg999
Harshg999 requested a review from tabraiz12 October 28, 2026 09:05
@Harshg999
Harshg999 enabled auto-merge (rebase) October 28, 2026 09:06

@agl29 agl29 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@Harshg999
Harshg999 merged commit 311189c into master Oct 28, 2026
7 checks passed
@Harshg999
Harshg999 deleted the fix/configuration-api-authorization branch October 28, 2026 09:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants