feat(data-warehouse): implement qualys_vmdr import source - #71163
Conversation
|
Hey @Gilbert09! 👋 It looks like your git author email on this PR isn't your
You can fix it for this repo with: git config user.email "you@posthog.com"Or set it globally with |
🤖 CI report
|
| File | Patch | Uncovered changed lines |
|---|---|---|
products/warehouse_sources/backend/temporal/data_imports/sources/qualys_vmdr/qualys_vmdr.py |
93.4% | 79, 83, 122, 129–131, 145–148, 196–197 |
products/warehouse_sources/backend/temporal/data_imports/sources/qualys_vmdr/source.py |
94.1% | 98, 102 |
🤖 Agents: add a test covering the lines above, or note why under "How did you test this code?". Machine-readable gap list: the patch-coverage artifact on this run (gh run download 29497320496 -n patch-coverage), or the coverage-data block at the end of this comment.
Per-product line coverage (touched products)
| Product | Coverage | Lines |
|---|---|---|
demo |
███████████░░░░░░░░░ 56.2% |
1,497 / 2,663 |
tasks |
█████████████░░░░░░░ 67.4% |
25,805 / 38,278 |
signals |
████████████████░░░░ 79.1% |
19,037 / 24,073 |
data_modeling |
████████████████░░░░ 80.0% |
4,834 / 6,045 |
cdp |
████████████████░░░░ 80.7% |
3,118 / 3,864 |
notebooks |
█████████████████░░░ 84.3% |
6,343 / 7,520 |
agent_platform |
█████████████████░░░ 84.7% |
3,273 / 3,862 |
cohorts |
█████████████████░░░ 86.1% |
4,022 / 4,671 |
actions |
█████████████████░░░ 86.6% |
717 / 828 |
product_tours |
█████████████████░░░ 87.5% |
1,266 / 1,447 |
exports |
██████████████████░░ 88.3% |
6,891 / 7,800 |
conversations |
██████████████████░░ 88.9% |
16,129 / 18,133 |
dashboards |
██████████████████░░ 89.1% |
5,719 / 6,418 |
mcp_analytics |
██████████████████░░ 89.1% |
2,502 / 2,807 |
error_tracking |
██████████████████░░ 89.6% |
9,790 / 10,925 |
alerts |
██████████████████░░ 89.9% |
3,638 / 4,046 |
engineering_analytics |
██████████████████░░ 90.1% |
5,105 / 5,665 |
streamlit_apps |
██████████████████░░ 90.4% |
2,499 / 2,764 |
slack_app |
██████████████████░░ 90.6% |
9,511 / 10,503 |
marketing_analytics |
██████████████████░░ 90.8% |
11,514 / 12,684 |
product_analytics |
██████████████████░░ 91.2% |
5,663 / 6,209 |
data_warehouse |
██████████████████░░ 92.1% |
18,133 / 19,683 |
workflows |
██████████████████░░ 92.4% |
5,148 / 5,574 |
web_analytics |
███████████████████░ 92.7% |
13,624 / 14,691 |
ai_observability |
███████████████████░ 92.8% |
14,868 / 16,019 |
surveys |
███████████████████░ 92.9% |
5,687 / 6,120 |
posthog_ai |
███████████████████░ 93.2% |
1,322 / 1,418 |
approvals |
███████████████████░ 93.3% |
3,395 / 3,640 |
reminders |
███████████████████░ 93.4% |
468 / 501 |
early_access_features |
███████████████████░ 93.8% |
848 / 904 |
endpoints |
███████████████████░ 94.1% |
8,606 / 9,143 |
skills |
███████████████████░ 94.4% |
2,827 / 2,995 |
revenue_analytics |
███████████████████░ 94.5% |
3,598 / 3,809 |
review_hog |
███████████████████░ 94.5% |
6,557 / 6,935 |
logs |
███████████████████░ 95.3% |
9,528 / 9,994 |
experiments |
███████████████████░ 95.6% |
24,234 / 25,351 |
replay_vision |
███████████████████░ 95.7% |
13,354 / 13,952 |
annotations |
███████████████████░ 96.2% |
732 / 761 |
feature_flags |
███████████████████░ 96.3% |
16,118 / 16,741 |
warehouse_sources |
███████████████████░ 96.3% |
240,055 / 249,249 |
user_interviews |
███████████████████░ 96.4% |
2,242 / 2,325 |
data_catalog |
███████████████████░ 97.1% |
2,035 / 2,095 |
customer_analytics |
███████████████████░ 97.2% |
7,480 / 7,698 |
Report-only. Patch coverage = changed backend lines covered vs origin/master. Sorted lowest first.
Known gaps: lines covered only by Temporal tests show as uncovered; core line numbers may drift if master changed the same file.
✅ Django migration risk — no migrations to analyze
No Django migrations need risk analysis.
ℹ️ Docs preview — preview build triggered
Docs from this PR will be published at posthog.com.
| Project | Preview | Updated (UTC) |
|---|---|---|
| posthog.com | Open preview | Jul 15, 2026, 9:00 PM |
The preview should be ready in about 10 minutes. Open the preview at /handbook/engineering/.
PR overviewAll previously flagged issues have been addressed. No open security concerns remain on this pull request. Security reviewNo open security issues remain on this pull request. Fixed/addressed: 2 · PR risk: 0/10 |
Implements the Qualys VMDR warehouse source: XML transport with truncation-URL pagination, resumable state, server-side incremental filters for hosts, host_list_detection, scans, and knowledge_base, plus canonical table descriptions and tests. Generated-By: PostHog Code Task-Id: 6f8fb7c3-3615-40d9-a6c3-2bb5d428ef0f
Parse Qualys XML with defusedxml (XXE/entity-expansion protection, flagged by semgrep), and fix mypy errors in the transport tests (unwrapped tenacity call and fake resumable manager typing, following the established sibling-test idioms). Generated-By: PostHog Code Task-Id: 6f8fb7c3-3615-40d9-a6c3-2bb5d428ef0f
Validate the user-supplied API server with is_url_allowed at credential validation and on every sync (defense-in-depth alongside the Smokescreen egress proxy), and disable redirects on the tracked session so the basic-auth credential stays on the validated host. Follows the established active_campaign pattern. Credential validation now returns a specific error message. Generated-By: PostHog Code Task-Id: 6f8fb7c3-3615-40d9-a6c3-2bb5d428ef0f
…mport The use-defused-xml semgrep rule also flags the type-only `from xml.etree.ElementTree import Element` import. Only parsing carries XXE risk (and all parsing goes through defusedxml), so suppress with the same justified pragma used in posthog/udf_versioner.py. Generated-By: PostHog Code Task-Id: 6f8fb7c3-3615-40d9-a6c3-2bb5d428ef0f
afa3130 to
2e634f2
Compare
…time The Qualys API server is user-supplied, so a malicious or misconfigured host could stream an unbounded or slow-dripped body and exhaust a shared worker. Read sync responses with stream=True under byte and wall-clock caps, and open the credential probe with stream=True, closing it without ever reading the body. Generated-By: PostHog Code Task-Id: ae6686e3-e27a-4ffe-bfba-e799b38b1c65
Generated-By: PostHog Code Task-Id: ae6686e3-e27a-4ffe-bfba-e799b38b1c65
Problem
Qualys VMDR is a widely deployed enterprise vulnerability management platform. Data teams want its host-detection history in the warehouse to track remediation velocity and join vulnerability data with the rest of their analytics. The source was scaffolded (hidden, empty fields) in the batch scaffold PR; this fills in the implementation.
Why: completes the scaffolded
qualys_vmdrconnector so users can sync Qualys VMDR vulnerability data into the PostHog Data warehouse.Changes
Implements the source end-to-end on the
ResumableSourcebase, following thesource.py/settings.py/qualys_vmdr.pyarchitecture split:settings.py):hosts(asset inventory),host_list_detection(per-host vulnerability detections, the core VMDR feed, flattened to one row per detection withhost_*-prefixed host fields),scans(VM scan history), andknowledge_base(QID definitions, off by default since it needs a subscription add-on). All four use genuine server-side timestamp filters for incremental sync (vm_scan_since,detection_updated_since,launched_after_datetime,last_modified_after).qualys_vmdr.py): the classic FO API returns XML, so this is a bespoke transport onmake_tracked_session()rather than the generic JSON REST client. Handles the mandatoryX-Requested-Withheader, HTTP Basic auth against the user-configured regional API server, keep-alive whitespace before the XML declaration, truncation-URL pagination (theWARNINGblock's next-batch URL, re-rooted onto the configured server so credentials never follow a response-supplied host), and 409 rate limits honoringX-RateLimit-ToWait-Sec.sort_mode=\"desc\"because batches are ordered by record id, not by the incremental field, so the watermark only persists at successful job end.api_serveris declared as aconnection_host_fieldsentry so retargeting it forces credential re-entry.QualysVmdrSourceConfig, released visible withreleaseStatus=ReleaseStatus.ALPHA(removedunreleasedSource=True).Note
I could not verify endpoint behavior against a live Qualys subscription (no credentials available), so parameter names and truncation semantics follow the current Qualys API v2 user guide plus existing Airbyte/Fivetran connector behavior, and the riskier assumptions are marked with comments. In particular
launched_after_datetimeon the scan list and the absence of truncation onscans/knowledge_baseare doc-based. The source ships as alpha accordingly.How did you test this code?
Ran the new targeted test suites (38 tests) plus the registry-wide source tests (
sources/tests/, 1727 passed; the 2test_stripe_configfailures pre-exist on the base branch),ruff check/ruff format, andhogli ci:preflight --fix. No live-API testing was performed.tests/test_qualys_vmdr.py(transport): catches regressions in truncation pagination (dropping the WARNING URL would silently truncate syncs), resume-state ordering (saving before yield would skip a batch on crash), incremental filter construction (droppingvm_scan_sincewould silently full-refresh), detection flattening (losingunique_vuln_idor thehost_prefix would corrupt the primary key), next-URL re-rooting (following a response-supplied host would leak credentials), rate-limit wait parsing, and credential-validation status mapping (401 vs 403/409).tests/test_qualys_vmdr_source.py(source class): catches re-hiding the source (unreleasedSource), losing theapi_servercredential-retarget guard, wrong primary/partition keys or sort mode per endpoint, and passing a stale watermark when incremental is disabled.Automatic notifications
Docs update
No posthog.com checkout was available in this environment, so the user-facing doc below needs to land in the posthog.com repo at
contents/docs/cdp/sources/qualys-vmdr.md(matching the source'sdocsUrl);audit_source_docsshould be run once it does. The source setslists_tables_without_credentials = Trueso<SourceTables />renders the table catalog.Doc content for posthog.com
🤖 Agent context
Autonomy: Fully autonomous
Implemented by PostHog Code (Claude) as part of the batch source-implementation effort stacked on the scaffold PR. Skills invoked:
/implementing-warehouse-sources,/documenting-warehouse-sources,/writing-tests. Key decisions: bespoke XML transport instead ofrest_source.RESTClient(XML responses, truncation pagination);sort_mode=\"desc\"since batches are id-ordered rather than time-ordered; nested XML serialized to JSON strings for Arrow type stability;knowledge_baseoff by default (subscription add-on); create-time credential probe accepts 403/409 so a missing asset-list scope doesn't block source creation.Created with PostHog Code