chore(security): resolve all open CodeQL python alerts#133
Merged
Conversation
Sweeps the full backlog of CodeQL `python` alerts on `main` (alerts 396, 401,
402, 408, 411-439). All fixes are surgical and behaviour-preserving — they
make CodeQL's taint and dataflow tracker recognise existing invariants rather
than changing runtime semantics.
py/log-injection (4 alerts)
- `services/api/app/api/v1/endpoints/alerts.py` (#396, #401, #402):
apply existing `_log_safe` helper to `idempotency_key` and
`payload.connector_type` before they hit `logger.info` / `logger.exception`.
- `services/api/app/api/v1/endpoints/waitlist.py` (#413): introduce a local
`_log_safe` and sanitise `previous` + `payload.status` on the
`waitlist_status_transition` log line. Pydantic already gates these against
`ALLOWED_WAITLIST_STATUSES`, so this is a no-op for valid input.
- `services/api/app/services/effective_permissions/service.py` (#412):
scrub CR/LF out of `provider` before the
"no production snapshot loader wired" warning.
py/uninitialized-local-variable (2 alerts)
- `services/agents/tests/test_graph_freshness.py` (#414, #415):
add `return` after `pytest.skip()` in two `except ImportError` blocks so
CodeQL sees every branch terminating.
py/mixed-returns (1 alert)
- `services/agents/tests/test_graph_freshness.py` (#438):
add explicit `return None` after `pytest.skip()` in `_import_or_skip` so
every control-flow path ends in an explicit return.
py/side-effect-in-assert (1 alert)
- `services/agents/tests/test_fidelity_harness.py` (#435):
hoist file open + line count out of the `assert` expression so the
side-effect doesn't disappear under `python -O`.
py/incomplete-url-substring-sanitization (1 alert)
- `services/teams-bot/tests/test_cards.py` (#411):
replace `startswith("http://adaptivecards.io/")` with an exact equality
check against the full canonical `$schema` URL.
py/ineffectual-statement (12 alerts)
- Replace `...` placeholders inside `Protocol` method bodies with explicit
docstrings so CodeQL doesn't flag the ellipsis as a no-op statement:
- `services/teams-bot/app/services/aisoc_clients.py` (#418, #419, #420, #421)
- `services/teams-bot/app/callbacks.py` (#426, #427, #428)
- `services/slack-bot/app/services/approval_audit.py` (#422)
- `services/api/app/services/attack_chain.py` (#424, #425)
- `services/api/app/services/email_approval.py` (#429)
- Bind bare `await task` results to `_` so the value is consumed
semantically, not discarded:
- `services/slack-bot/app/services/approval_timeout.py` (#423)
- `services/slack-bot/tests/test_approval_timeout.py` (#430-#434)
py/unnecessary-lambda (1 alert)
- `services/slack-bot/app/services/approval_audit.py` (#439):
use `default_factory=time.time` directly instead of wrapping it in a
lambda.
py/unused-global-variable (3 alerts)
- `services/agents/app/context/bundle.py` (#408) +
`services/agents/tests/test_context_bundle.py`:
rename `_LLM_SAFE_KEYS` -> `LLM_SAFE_KEYS` (already used by tests, so the
leading underscore was misleading) and update the importer.
- `services/api/app/scripts/seed_demo.py` (#417): drop the unused
`_quick_rng` module-level RNG; deterministic seeding still flows through
`_rng_for_tenant`. Replaced with a comment explaining the removal so
future work doesn't accidentally re-add an unused source of randomness.
- `services/connectors/app/connectors/datadog.py` (#416): remove the unused
`_EVENTS_PER_PAGE` constant; the Datadog Events v1 API is windowed by
`start`/`end` epoch seconds, not by a page-size knob, so the constant was
dead. Added a comment so anyone wiring pagination in later does it inside
`_fetch_events` instead of reintroducing the global.
py/import-and-import-from (1 alert)
- `services/connectors/tests/connectors/test_confluence_audit.py` (#437):
collapse the inline `import app.connectors.confluence_audit as mod` into
a single module-level `import ... as confluence_audit_module` alias and
reuse it for the `_PAGE_SIZE` monkey-patch.
py/unused-import (1 alert)
- `services/api/app/services/tenant_provision/provisioner.py` (#436):
unquote `Any` in `DemoSeederCallable = Callable[[AsyncSession, Tenant], Any]`
so the existing `from typing import Any` is recognised as used. The
module already has `from __future__ import annotations`, so the string
form was unnecessary — and it was hiding the only runtime reference.
Verification
- `python3 -m py_compile` clean on all 19 files.
- `ruff format` is a no-op (files already match style).
- `ruff check` passes with no new lint findings.
No behaviour change, no public API change. All edits are sanitisation,
docstring, or static-analysis hint changes.
4 tasks
beenuar
added a commit
that referenced
this pull request
May 14, 2026
Address the two CodeQL alerts that persisted after PR #133 because the fixes there were not recognised by CodeQL's static analysis: - #441 py/log-injection in services/api/app/api/v1/endpoints/waitlist.py: the `_log_safe` helper was not detected as a sanitiser when the value came from a database row. Inline the `.replace("\\r","").replace("\\n"," ")` chain with a 32-char cap at the `logger.info` call site so the taint tracker sees the cleansing directly. The now-unused `_log_safe` helper has been removed. - #440 py/import-and-import-from in services/connectors/tests/connectors/test_confluence_audit.py: the previous module alias still tripped the rule. Drop the alias entirely and switch the `_PAGE_SIZE` override to `pytest.MonkeyPatch.setattr`, giving a single import style and automatic restoration at teardown. Both values in waitlist are also Pydantic-validated against ALLOWED_WAITLIST_STATUSES, so this is defence in depth, not a behaviour change. Co-authored-by: Beenu Arora <[email protected]>
3 tasks
beenuar
added a commit
that referenced
this pull request
May 14, 2026
Brings every cross-cutting doc surface in line with the 21 PRs that landed on `main` on 2026-05-14, anchored by the v8.0 architectural foundation (PR #125) and the security + correctness wave that followed it. - `CHANGELOG.md` — new `[Unreleased]` block covering the v8.0 architectural foundation (graph at ingest, four-agent rebrand, `/hunt`, sixteen connectors, automation maturity, public scoreboard), the eight-PR security hardening wave (PRs #116-#128), the three-PR CodeQL alert sweep to zero (#133, #136, #137), the UEBA env-var alignment (PR #135, first community contribution, closes #134), the security-smoke + UX cleanup pair (PR #132, closes #131 + #130), and the playbook engine correctness pass (PR #129). - `README.md` — new `v8.0 wave-1 (on main, not yet tagged)` entry in the version-history section; `Next` block rewritten as `v8.0 wave-2` with the still-`[~]` items from `AISOC_V8_PROGRESS.md`. Version badge intentionally not bumped (still 7.3.1) because wave-1 is on `main` but not tagged. - `AGENTS.md` — new `v8.0 wave-1` block under "Learned Workspace Facts" documenting the four-agent topology, `/hunt` surface, connector inventory, automation maturity ladder, security wave outcomes, CodeQL hygiene patterns (inline `replace`-chain sanitisation for `py/log-injection`, single import style for `py/import-and-import-from`), and the UEBA env-var dual-alias convention. - `AISOC_V8_PROGRESS.md` — `Status` block refreshed to record that PR #125 shipped at `b854010e` on 2026-05-14, list the 12 post-merge PRs that landed on `main` after it, and clarify that wave-2 is the still-tracked `[~]` work. - `apps/docs/docs/deployment/env-vars.md` — UEBA section rewritten around the dual-alias rule (unprefixed wins over `UEBA_`-prefixed, matches every other Python service and the `docker-compose.yml` exports); table now lists canonical + legacy names side by side. - `apps/docs/docs/operations/security.md` — new `Static analysis (CodeQL)` section: zero alerts on `main` as a CI gate, plus the two patterns that came up repeatedly during the sweep (inline-at-call-site sanitisation for `py/log-injection`, single import style for `py/import-and-import-from`). No code changes; pure documentation sync. Co-authored-by: Beenu Arora <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Sweeps the full backlog of open CodeQL
pythonalerts onmain— 26 alertsacross 11 distinct rules. Every change is behaviour-preserving; they help
the static analyzer recognise existing invariants (validated inputs, terminated
control flow, runtime-resolved names) rather than altering runtime semantics.
Closes the following alerts:
py/log-injectionpy/uninitialized-local-variablepy/mixed-returnspy/side-effect-in-assertpy/incomplete-url-substring-sanitizationpy/ineffectual-statementpy/unnecessary-lambdapy/unused-global-variablepy/import-and-import-frompy/unused-importChanges by category
py/log-injection— sanitise CR/LF before loggingUser-controlled strings that flow into
logger.*calls now pass through asmall
_log_safe(value)helper that strips\rand replaces\nwith aspace. Pydantic validators already prevent newlines for most fields, but
CodeQL's taint tracker doesn't model the validator as a sanitiser, so the
helper makes the cleansing explicit and is a no-op for valid input.
services/api/app/api/v1/endpoints/alerts.py— apply existing_log_safeto
idempotency_keyandpayload.connector_type.services/api/app/api/v1/endpoints/waitlist.py— introduce_log_safe,apply to
previousandpayload.statusonwaitlist_status_transition.services/api/app/services/effective_permissions/service.py— scrubproviderintosafe_providerbefore the no-loader warning.py/uninitialized-local-variable+py/mixed-returns— explicit control flowservices/agents/tests/test_graph_freshness.py—pytest.skip()raisesSkipped, but CodeQL doesn't model it asNoReturn. Added explicitreturn/return Noneafter every skip so every branch terminates.py/side-effect-in-assertservices/agents/tests/test_fidelity_harness.py— hoist file open +line count out of the
assertexpression. Underpython -Othe sideeffect would otherwise disappear silently.
py/incomplete-url-substring-sanitizationservices/teams-bot/tests/test_cards.py— replacestartswith("http://adaptivecards.io/")with an exact equality checkagainst the full canonical
$schemaURL.py/ineffectual-statement(17 alerts)Two patterns:
Protocolmethod bodies with...— replaced the ellipsis with anexplicit docstring so CodeQL doesn't flag the placeholder as a no-op.
Affects
aisoc_clients.py,callbacks.py,approval_audit.py,attack_chain.py,email_approval.py.await task— bound to_so the awaited value is consumedsemantically. Affects
approval_timeout.py(1 prod, 5 tests).py/unnecessary-lambdaservices/slack-bot/app/services/approval_audit.py— usedefault_factory=time.timedirectly instead oflambda: time.time().py/unused-global-variableservices/agents/app/context/bundle.py— rename_LLM_SAFE_KEYS→LLM_SAFE_KEYS(the test already imports it, so the leading underscorewas misleading); update the importer in
test_context_bundle.py.services/api/app/scripts/seed_demo.py— drop unused_quick_rng;deterministic seeding still flows through
_rng_for_tenant.services/connectors/app/connectors/datadog.py— drop unused_EVENTS_PER_PAGE; the Datadog Events v1 API windows onstart/endepoch seconds, not a page-size knob.py/import-and-import-fromservices/connectors/tests/connectors/test_confluence_audit.py— collapseinline
import app.connectors.confluence_audit as modinto a singlemodule-level alias and reuse it for the
_PAGE_SIZEmonkey-patch.py/unused-importservices/api/app/services/tenant_provision/provisioner.py— unquoteAnyinDemoSeederCallable = Callable[[AsyncSession, Tenant], Any]. Themodule already has
from __future__ import annotations, so the stringform was unnecessary and hid the only runtime reference to
typing.Any.Verification
python3 -m py_compileclean on all 19 modified filesruff formatis a no-op (files already match repo style)ruff checkpasses with no new findingsspecific CodeQL rule patterns
Test plan
Python — Lint & Type-checkgreenPython — TestsgreenVerify docs/openapi.yaml is up to dategreen (no schema changes,so this should be a no-op)
mainafter merge shows all 26 alerts dismissed/v1/alerts/ingeststill logsidempotency_key(now sanitised)PATCH /v1/waitlist/{id}still logswaitlist_status_transitionNotes
Made with Cursor