Skip to content

[AAP-86051] Wire DAB validation bypass observability for Controller - #16672

Draft
vidyanambiar wants to merge 5 commits into
ansible:develfrom
vidyanambiar:AAP-86051
Draft

vidyanambiar wants to merge 5 commits into
ansible:develfrom
vidyanambiar:AAP-86051

Conversation

@vidyanambiar

@vidyanambiar vidyanambiar commented Sep 23, 2026 •

Copy link
Copy Markdown
Member
SUMMARY

Wire django-ansible-base#1147 validation bypass observability into Controller: log Tier 1/Tier 2 violations when registered models are written outside CleanTextMixin serializers (warnings only; ORM writes are not blocked).

Depends on: DAB #1147 (register_validation_signals, audit_bulk_model_instances, caller extend_* APIs). Pin DAB branch AAP-86051 (Controller does not import queryset helpers; EDA and other services may need 4d68525+ for audited_queryset_update).

DAB (after serializers load): post_save on registered models for .save() / .create().

Controller hooks (post_save does not run for bulk):

Surface Location Audited
Bulk host API BulkHostCreateSerializer.create → audit_bulk_model_instances before Host.objects.bulk_create Host Char/Text on instances
Bulk workflow launch BulkJobLaunchSerializer.create → audit_workflow_job_nodes_for_bulk_create before WorkflowJobNode.objects.bulk_create ORM text + char_prompts pseudo-fields (limit, job_tags, skip_tags, scm_branch)
Shared bulk update bulk_update_sorted_by_id in awx/main/utils/db.py Columns in fields= that are registered Char/Text
Scheduler batch task_manager.py before UnifiedJob.objects.bulk_update(..., ['job_explanation']) job_explanation on Job

awx/main/utils/validation_bypass_observability.py — AWX-specific wiring on top of DAB (keeps bulk audit call sites thin):

  • configure_validation_bypass_observability() (from MainConfig.ready()): calls DAB register_validation_signals(); extends caller allowlist (awx.api.views, awx.api.serializers, awx.main.tasks, awx.main.management, awx.main.utils) and internal denylist (awx.main.models, awx.main.signals, awx.main.dispatch) so [caller: module:line] in logs points at product code, not Django ORM plumbing. MainConfig.ready() also imports awx.api.serializers so CleanTextMixin serializers populate DAB _protected_models.
  • audit_workflow_job_nodes_for_bulk_create() — used from BulkJobLaunchSerializer.create before WorkflowJobNode.objects.bulk_create. Delegates to DAB audit_bulk_model_instances for real Char/Text columns, then audits char_prompts pseudo-fields (limit, job_tags, skip_tags, scm_branch) via getattr (not visible to _get_text_fields). Caller for prompt violations is resolved at this helper (_get_caller_info()); logs may show validation_bypass_observability.audit_workflow_job_nodes_for_bulk_create:… because awx.main.utils is allowlisted before awx.api.serializers on the stack.
  • audit_bulk_update_instances() — field-scoped bulk-update audit used from bulk_update_sorted_by_id and task_manager.py; only columns in fields= that are registered Char/Text are checked (avoids noise when bulk-updating JSON such as Host ansible_facts).

Intentionally not hooked: Host ansible_facts bulk-only updates (JSON); production QuerySet.update() on registered user text (none triaged); job-event/metrics bulk on non-registry models.

API enforcement unchanged: invalid serializer input still returns 400 when ENHANCED_INPUT_VALIDATION_ENABLED is on.

related AAP-86051

ISSUE TYPE
  • New or Enhanced Feature
COMPONENT NAME
  • API
  • Other
STEPS TO REPRODUCE AND EXTRA INFO

Prerequisites: Install DAB from #1147 (AAP-86051). Deploy or run AWX from this branch.

Verify:

  1. ORM-direct save — awx-manage shell: create Organization with bad description → ORM bypass: in logs (ansible_base.lib.utils.validation_signals); row still saved.
  2. Bulk host (bypass path) — internal call to BulkHostCreateSerializer.create() without serializer validation → ORM bypass (bulk_create): for bad host description.
  3. Bulk workflow (bypass path) — BulkJobLaunchSerializer.create() with bad limit on a node → ORM bypass (bulk_create): for limit / WorkflowJobNode (via audit_workflow_job_nodes_for_bulk_create; [caller: …] often names that helper).
  4. Bulk update — bulk_update_sorted_by_id(Job, [job], fields=['job_explanation']) with bad job_explanation → ORM bypass (bulk_update):.
  5. API enforcement (negative) — POST /api/v2/bulk/host_create/ (or gateway /api/controller/v2/bulk/host_create/) with bad description and enforcement on → 400; no ORM bypass.
  6. Serializer path (negative) — normal API update through CleanTextMixin serializer → no ORM bypass (DAB context var).

Regression / new tests:

  • awx/main/tests/functional/test_bulk.py — bulk host and bulk job launch bypass logging (enforcement mocked off).
  • awx/main/tests/unit/utils/test_validation_bypass_observability.py — workflow pseudo-fields, audit_bulk_update_instances, hook order in db.py / task_manager.py.

Optional demo: kind-devel walkthrough in controller-validation-bypass-demo.md (local doc).

# Example: functional tests
pytest awx/main/tests/functional/test_bulk.py -k bypass
pytest awx/main/tests/unit/utils/test_validation_bypass_observability.py

Register ORM bypass signals and AWX caller prefixes at startup, and audit
bulk host create before bulk_create.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Sep 23, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Comment @coderabbitai help to get the list of available commands.

vidyanambiar and others added 3 commits September 23, 2026 10:51
Log Tier 1/2 bypass violations for WorkflowJobNode prompt fields stored in
char_prompts, which post_save and bulk CharField scans do not cover alone.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add field-scoped audit_bulk_update_instances for bulk_update_sorted_by_id
and scheduler job_explanation batches. Expand functional and unit tests for
bulk host/workflow create and bulk update bypass logging.

Co-authored-by: Cursor <cursoragent@cursor.com>
@redhat-chai-bot

Copy link
Copy Markdown

Two P2 findings from reviewing the AWX integration against the companion DAB PR:

  1. Caller attribution is captured by broad utility prefixes. DAB resolves caller frames inner-to-outer and returns the first allowlist match before applying its denylist. Because AWX allowlists awx.main.utils, workflow pseudo-field and scheduler/shared-helper logs resolve to validation_bypass_observability rather than the serializer, scheduler, or task caller. Please replace that broad prefix with awx.main.scheduler and add only awx.main.utils.validation_bypass_observability plus awx.main.utils.db to the internal prefixes. Bulk host create is already correctly attributed to the serializer.

  2. The AWX bulk-update helper duplicates DAB's public audit API and performs unnecessary stack walks. audit_bulk_update_instances() duplicates DAB PR This document does not contains docker and docker compose, AWX tower installation steps #1147's public audit_bulk_model_instances(..., operation="bulk_update", update_fields=fields). The AWX helper calls _get_caller_info() before determining whether any requested field is audit-eligible, so fact-cache ansible_facts updates pay for a stack walk despite being non-text. Please route ordinary bulk updates through DAB's public helper; retain only a narrow workflow pseudo-field adapter, using public log_orm_bypass_violation (or a future DAB public custom-field API) instead of _log_bulk_violation.


AI-generated. Review for accuracy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants