Skip to content

Clear the 19 py/log-injection CodeQL alerts on V2 - #1597

Merged
Paul Lizer (paullizer) merged 4 commits into
microsoft:paullizer-react-v2-uifrom
paullizer:paullizer-log-injection-codeql-alerts
Oct 1, 2026
Merged

Paul Lizer (paullizer) merged 4 commits into
microsoft:paullizer-react-v2-uifrom
paullizer:paullizer-log-injection-codeql-alerts

Conversation

@paullizer

@paullizer Paul Lizer (paullizer) commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Clears the 19 open py/log-injection CodeQL alerts on paullizer-react-v2-ui. They all sit on code that predates the Chat Orchestration Workflows roadmap, and reviews of that roadmap's PRs kept hitting them.
  • functions_appinsights.py (2551–2554, false positives). sanitize_log_message already removed every CR and LF with LOG_CONTROL_CHAR_RE.sub(" ", ...), but CodeQL credits only .replace("\r\n" | "\n", ...) as a line-break sanitizer. It now also calls .replace("\r\n", " ").replace("\n", " ").replace("\r", " ") straight after the regex and before both returns. At run time these calls change nothing, so its output is byte-identical.
  • utils_cache.py (7) and route_backend_workflows.py (7). The flagged log lines pass request-derived IDs, scopes, partition keys and exception text through sanitize_log_message. Ordinary values log exactly as before. A value containing \n or \r\n now logs on one line. logging.exception keeps its level, format string, argument order and traceback.
    • In invalidate_group_search_cache (3411), group_id is rebound through sanitize_log_message on the line before the log call, so the log line keeps its text. See Decision 9 and the CodeQL section.
  • route_backend_plugins.py (1105). POST /api/admin/plugins/settings no longer logs the request body. The body was logged before validation, so a field the route then rejects, such as api_key, was written to the log as sent. The route now logs [PLUGINS] Received plugin settings update request. through log_event, with a field_count property.

Linked issue

Refs #1543

Release Notes & Latest Features

  • New Feature
  • Bug Fix
  • UI Enhancement
  • Breaking Change
  • Internal only

Is this visible to end users?

  • Yes
  • No

Is this admin-facing (Admin Settings, governance, deployment, config)?

  • Yes
  • No

Should this become a Latest Feature card?

  • Yes
  • No
  • Already added

Screenshot needed for the card?

  • Yes
  • No
  • Attached

Version bump

Testing / validation

Every command below ran with the pinned venv Python (Flask 3.1.3) and PYTHONPATH=application/single_app;functional_tests, one process at a time. No live Azure resources were used. The results in this section are for the PR's own commits, ending at 2a38aea0a, against base 70c255508, unless a bullet says otherwise. The reruns on the V2 merge head are under After the V2 merges.

  • python -u functional_tests/test_log_injection_codeql_alerts_fix.py: passed, 44 tests.

  • python -O -u functional_tests/test_log_injection_codeql_alerts_fix.py: passed, 44 tests.

    • Output unchanged. A frozen copy of the 0.261.213 sanitize_log_message is the reference over a 33-item corpus: "a\n\nb" → "a b", CRLF runs, a lone CR, tabs, mixed \t\r\n, secret masking, Bearer/Basic values, the exact ... [truncated] suffix around the 8,192-character limit, and non-string values (an exception, int, float, None, bool, dict, list, bytes) and unicode.

    • AST check. The .replace("\r\n", ...) and .replace("\n", ...) chain must come after the LOG_CONTROL_CHAR_RE.sub line and before every return. At run time those calls are no-ops, so a refactor that drops them would otherwise silently reopen 2551–2554.

    • Call sites. These run from source and capture the log records:

      • Each flagged log call logs an ordinary ID byte-identically and logs \n or \r\n IDs on one line. That covers the 7 in utils_cache.py and the 5 in the 2 cancellation handlers, which carry alerts 2120–2126.
      • logging.exception keeps ERROR, exc_info and the traceback, and the response is unchanged.
      • The plugin route never logs a fake API key, whether it is sent as a rejected field or as a non-boolean value. A valid request still saves and still requests the kernel reload.
    • Fresh-process imports. A subprocess with sockets blocked runs normally and under -O. It imports these, in this order:

      • functions_appinsights and utils_cache, both orders
      • config → utils_cache
      • functions_authentication → route_backend_workflows
      • route_backend_plugins
      • app

      It checks that each edited module resolves its functions_appinsights import (sanitize_log_message, or log_event for the plugins module), and that nothing tried to open a socket.

  • Mutation checks: 50/50 caught. 25 source mutations each ran normally and under -O, and every run failed at least one test:

    • unwrapping any one of the 20 sanitized values, including undoing the group_id rebinding
    • moving the replaces before the regex (3 tests fail)
    • dropping the replaces (the AST check fails)
    • restoring the payload log (3 tests fail)
    • removing either new import

    Sources were restored byte-identical afterwards.

  • Local CodeQL. CodeQL 2.27.1 with codeql/python-queries 1.8.11 (the versions in V2's base analysis) ran Security/CWE-117/LogInjection.ql and Security/CWE-312/CleartextLogging.ql. The input was the 4 modules that carry the alerts, the 2 app-settings modules that functions_appinsights imports, and a synthetic Flask driver.

    • The driver registers the workflow routes on a Blueprint, as app.py does. It feeds request arguments to debug_print, log_event, the cache helpers, and a stand-in for _get_trusted_group_upload_scope_ids().
    • Log injection, base: 23 results. These are the 19 alerts, 3 unflagged utils_cache.py siblings, and a driver line that logs sanitize_log_message(value) directly.
    • Log injection, this branch: 3 results. These are the same 3 siblings, with unchanged fingerprints. All 19 alerts clear, and so does the driver line.
    • Clear-text logging: 3 results on both base and this branch, with identical fingerprints. At base lines, they are functions_appinsights.py L375 and utils_cache.py L100 and L711. See CodeQL.
    • The 3 remaining siblings are the personal fingerprint error and the expired and miss debug lines. The driver reaches them only because it feeds request values where the app passes the session user ID or a hashed key. GitHub doesn't flag them (see Follow-ups).
  • Alert fingerprints. GitHub matches alerts between analyses by a hash of the text from the alert's line onward (primaryLocationLineHash). A changed line can therefore show an existing alert as new.

    • A script that reimplements the hash reproduces all 1,096 fingerprints in V2's base Python analysis.
    • The edited files had 163 base results across all rules. 149 keep their fingerprints.
    • The 14 that change are all log-injection alerts on lines this PR rewrites, and the local run reports nothing on those lines.
    • No other rule's result changes fingerprint.
  • Existing suites. 48 files covering these modules ran one process at a time: script mode when the file has a __main__, otherwise python -m pytest -q -p no:cacheprovider.

    • 39 files pass, including both harnesses this PR edits:
      • test_group_document_collaboration.py: 321 passed
      • test_group_workflow_fixture_parity.py: 60 passed
    • 9 files fail with an identical tail on a clean 70c255508 worktree, so the failures were already there. None is on the known-failures list. Details are below.
    • This sweep ran on f583455fd. The second commit changes only invalidate_group_search_cache and the fix doc. test_group_document_collaboration.py is the only suite that runs the real function, and it was rerun on 2a38aea0a with python -m pytest: 321 passed. Two other suites that execute real utils_cache.py functions also pass there: test_content_screening_read_boundaries.py (20) and test_ai_connection_embedding_consumers.py (8).
  • Docs. python functional_tests/test_docs_site_quality.py passes 6/6. test_docs_link_integrity (2/5) and test_docs_release_notes_integrity (0/1) match base: the generated release-notes pages are stale on V2 and were not regenerated.

  • V2 guardrails. run_v2_guardrails.py <worktree> 70c255508675bb0de7b7940c0ec90d5980f8b529 HEAD loginj, rerun on 2a38aea0a, reports 11 changed files:

    • [PASS] broken-access-control: 5 files
    • [PASS] xss-sinks: 5 files
    • [PASS] swagger-routes: 2 changed route files
    • [PASS] python-syntax: 488 top-level files compile
    • [PASS] malicious-pr-review: no blockers. It raised 229 Important and 14 Moderate heuristic keyword matches, mostly in the new test and fix doc: words like secret and sanitize, and the standard exec(compile(...)) and importlib harness patterns.
    • [PASS] v2-ts-sinks: no TS/TSX files changed
Existing-suite results (48 files)
File Mode Result
test_group_document_collaboration.py (edited) script PASS, 321 passed
test_group_workflow_fixture_parity.py (edited) pytest PASS, 60 passed
test_group_document_publication.py script PASS, 127 passed
test_group_workflow_round_trip_preservation.py pytest PASS, 5 passed
test_orchestration_workflow_run_adapter.py script PASS
test_group_workflow_hint_seam.py pytest PASS, 25 passed
test_mcp_action_manifest_workflow.py script PASS
test_privacy_logging_telemetry_audit.py script PASS, 5/5
test_chat_fact_memory_admin_placement.py script PASS, 8/8
test_plugin_error_response_sanitization.py script PASS, 3/3
test_mcp_outbound_logging.py script PASS
test_ai_connection_embedding_consumers.py script PASS
test_content_screening_read_boundaries.py script PASS
test_log_event_call_contract.py script PASS
test_codeql_import_cycle_lazy_imports.py script PASS, 2/2
test_app_settings_import_boundaries.py pytest PASS, 7 passed
test_appinsights_logging_recursion_guard.py script PASS
test_flask_exception_console_logging.py script PASS, 4 passed
test_log_credential_key_redaction.py script PASS
test_workflow_alert_error_redaction.py script PASS, 25 passed
test_workflow_loop_inputs.py script PASS
route_tests/test_route_blueprint_policy_inventory.py script PASS, 10/10
route_tests/test_route_unauthenticated_policy_contract.py script PASS, 4/4
route_tests/test_route_policy_test_coverage.py script PASS, 2/2
test_group_directory_apis.py script PASS, 148 passed
test_public_settings_apis.py script PASS, 210 passed
test_group_file_source_apis.py script PASS, 60 passed
test_public_file_source_apis.py script PASS, 60 passed
test_group_identity_apis.py script PASS, 72 passed
test_group_settings_apis.py pytest PASS, 203 passed
test_public_directory_apis.py script PASS, 35 passed
test_public_identity_apis.py script PASS, 72 passed
test_group_action_apis.py script PASS, 63 passed
test_group_agent_apis.py script PASS, 92 passed
test_group_bulk_endpoint_save_writer.py pytest PASS, 16 passed
test_group_document_read_apis.py script PASS, 208 passed
test_public_document_publication.py script PASS, 63 passed
test_public_document_management.py pytest PASS, 205 passed
test_analysis_artifact_publication.py pytest PASS, 53 passed
test_workflow_cancellation.py pytest FAIL, same on base: 3 runner tests hit NameError: capture_execution_identity in the harness namespace; the 3 route and contract tests pass
test_workspace_authoring_backend.py script FAIL, same on base: 1 of 138, 'azure.core' is not a package
test_agents_plugins_blueprint_fix.py script FAIL, same on base: imports the real app, and config.py needs Cosmos credentials
test_plugin_tool_agent_security_audit.py script FAIL, same on base: pins VERSION == 0.242.072
test_logged_core_plugins.py script FAIL, same on base: no thought events recorded
test_unified_logging_entrypoint.py script FAIL, same on base: 3/4 pass; the debug-trace forwarding check fails
test_route_backend_models_safe_logging.py script FAIL, same on base: a source contract finds route_backend_models.py returning raw exception text
test_logging_tag_standardization.py script FAIL, same on base: nonstandard Workflows/WorkflowDrafts/WorkflowAssist tags in files this PR doesn't touch. The [PLUGINS] tag used here is standard and already documented, and the tag-doc sets match base.
test_agent_delegation_action.py script FAIL, same on base: 14 of 86

Documentation

  • Release notes updated, or not needed
    • New ### **(v0.261.216)** section at the top of docs/explanation/release_notes.md; every existing section is unchanged.
  • Feature documentation updated, or not needed
    • Not needed.
  • Fix documentation updated, or not needed
    • docs/explanation/fixes/LOG_INJECTION_CODEQL_ALERTS_FIX.md.

Security checklist

  • New Flask routes include @swagger_route(security=get_auth_security())
    • No new routes.
  • Settings sent to non-admin frontends use sanitize_settings_for_user()
    • No settings are sent to frontends.
  • Browser JavaScript is served from local SimpleChat static assets only; no CDN-hosted JS
    • No browser assets changed.
  • No secrets, keys, connection strings, or local-only artifacts are included
    • The test's API key is the obvious fake sk-FAKE-log-injection-….

After the V2 merges

V2 moved twice while this PR was open. Each move was merged in with git -c rerere.enabled=false merge; nothing was rebased or force-pushed.

Conflicts. Both merges conflicted only in config.py (VERSION) and release_notes.md (the top section). Each was resolved by hand, and rerere replayed nothing: VERSION stays 0.261.216, and the .216 section sits directly above V2's newest section. Every other file merged without conflicts.

The PR's diff hasn't changed.

Results on 7f90c5d7d, with the same venv, one process at a time:

  • python -u functional_tests/test_log_injection_codeql_alerts_fix.py: passed, 44 tests.
  • python -O -u functional_tests/test_log_injection_codeql_alerts_fix.py: passed, 44 tests.
  • Docs. test_docs_site_quality.py passes 6/6 and test_docs_app_surface_coverage.py 7/7. test_docs_link_integrity (2/5) and test_docs_release_notes_integrity (0/1) fail exactly as on a clean 86f3f3629 worktree: the same 11 broken links, plus the stale generated release-notes pages. The only differences are 2 more links checked, both valid ones from the fix doc, and .216 as one more release missing from the stale pages.
  • Existing suites. 53 files ran, through the same runner as before. They are the 48 above plus 5 suites that Phase 6a: workflow results reader and Follow up in chat #1592 and Gate personal workflow instruction drafting on the AI workflow assistant setting (N9) #1596 added: test_workflow_result_privacy_import_cycle.py (11), test_workflow_result_chat_routes.py (12), test_workflow_result_review_paths.py (2), route_tests/test_workflow_result_context_policy.py (38) and test_workflow_draft_instructions_assistant_gate.py (12/12).
    • 44 files pass, including all 5 new suites, test_group_document_collaboration.py (321 passed), test_group_workflow_fixture_parity.py (60 passed), test_content_screening_read_boundaries.py (20, OK), test_ai_connection_embedding_consumers.py (8, OK) and the 3 route-policy suites (10/10, 5/5, 2/2).
    • The 9 files listed under Testing / validation still fail. A clean 86f3f3629 worktree gives an identical tail for each of them; the only differences are wall-clock times and the version test_plugin_tool_agent_security_audit.py reports (.216 here, .215 there).
  • Mutation checks: 50/50 caught. The same 25 mutations each ran normally and under -O, and every run failed at least one test. The 7 workflow unwraps now target L1936–L2372. The sources were restored byte-identical, and git status was clean afterwards.
  • V2 guardrails. run_v2_guardrails.py <worktree> 86f3f3629731427f13c08648478cc92ddb781651 HEAD loginj reports the same 11 changed files, and all six checks pass:
    • [PASS] broken-access-control: 5 files
    • [PASS] xss-sinks: 5 files
    • [PASS] swagger-routes: 2 changed route files
    • [PASS] python-syntax: 491 top-level files compile
    • [PASS] malicious-pr-review: no blockers, with the same 229 Important and 14 Moderate keyword matches as before
    • [PASS] v2-ts-sinks: no TS/TSX files changed
  • CI. All 11 checks pass on 7f90c5d7d. The CodeQL check reports "No new alerts in code changed by this pull request", with 0 annotations. The merge-ref alert listing is under CodeQL.

A one-off failure. On the first merge head, a83340d7e, one normal-mode run of my test failed a single case, the [app-optimized] cold-import probe. That run took 405s, about three times as long as usual, and its output wasn't kept, so the cause isn't confirmed. Since then the probe has passed every time:

  • in an isolated rerun on a83340d7e
  • in both full runs on 7f90c5d7d, normal and -O
  • in 5 repeat runs of both [app-*] probes on 7f90c5d7d, 10 of 10

The probe goes through the shared test_support/offline_bootstrap.offline_app_imports, which 25 other functional suites also use and this PR doesn't change. If it fails again, its assertion message includes the last 8000 characters of the subprocess's stdout and stderr, so the cause will be visible.

Decisions made autonomously

  1. The plugin settings log records only field_count. It logs no values and no key names, through log_event with the [PLUGINS] tag the module already uses. This deliberately reduces what that log line shows: it used to print the whole request body. Key names were left out because a valid request always sends the same eight keys, plus up to two deprecated ones, and before validation they are whatever the client sent.
  2. Workflow cancellation user_id is not wrapped. It comes from the signed-in session and is not flagged. It is listed under Follow-ups.
  3. cache_key is wrapped on the two flagged cache lines. GitHub's reported taint there comes from doc_scope and the partition key, but wrapping every value on those lines costs nothing; for the hex hash it is a no-op.
  4. _build_logger_extra, _normalize_extra_key and the debug category are unchanged. All 16 SARIF flows for 2551–2554 pass through sanitize_log_message and reach extra as sc_message, so no path needed those.
  5. Closed-namespace harnesses get the real sanitizer. test_group_document_collaboration.py and test_group_workflow_fixture_parity.py now receive sanitize_log_message through a new helper, test_support/log_sanitizer.py, which runs it from functions_appinsights.py source. A stub would hide its real behavior.
  6. The import probe imports functions_authentication before route_backend_workflows, as app.py does. A cold import route_backend_workflows on its own already fails on base with NameError: enabled_required, from an existing functions_settings ↔ functions_authentication cycle. That cycle is listed under Follow-ups.
  7. Strict harness stubs are unchanged. The functions_appinsights stubs that provide only log_event, in test_support/app_stubs.py and several group and public harnesses, were left alone. None of them loads utils_cache or route_backend_workflows, and the suite run confirms it.
  8. No header bumps or regeneration. Existing test headers keep their versions, and the generated release-notes pages were not regenerated; they are already stale on base.
  9. Group cache invalidation rebinds group_id instead of wrapping it inside the log call.
    • What happened. The first push wrapped it inline, which changed the text of utils_cache.py's Invalidated {count} cache entries for group {group_id} line. That line also carries a pre-existing py/clear-text-logging-sensitive-data alert. Its source is _get_trusted_group_upload_scope_ids() in route_frontend_chats.py, and the query treats the "trusted" name as sensitive.
    • Why it mattered. GitHub fingerprints alerts by line text, so the PR's CodeQL check reported that existing alert as 1 new high-severity alert.
    • The change. The second commit binds group_id = sanitize_log_message(group_id) on the line before the call. The log line keeps its base text, and the alert keeps its identity. The logged text is the same either way.
    • Recommendation. A group ID isn't a secret, so the clear-text alert is a false positive. It should be triaged on its own; this PR doesn't dismiss it.

CodeQL

Pull request check

  • First analysis, f583455fd. The CodeQL check failed with "1 new alert including 1 high severity security vulnerability". That alert, #3936, was py/clear-text-logging-sensitive-data at utils_cache.py:725, and it wasn't really new: it duplicated V2's existing alert #3405 from L711. That commit had wrapped group_id inside the line, which changed its fingerprint (Decision 9). No new py/log-injection alert was reported.
  • Second analysis, 2a38aea0a. The CodeQL check passed: "No new alerts in code changed by this pull request", with 0 annotations. This analysis also marked #3936 fixed on the PR ref. All 11 checks on the head pass:
    • CodeQL: Analyze (python), Analyze (javascript-typescript), Analyze (actions)
    • repository checks: broken-access-control-check, xss-sink-check, swagger-route-check, syntax-check, malicious-pr-security-review, enforce-branch-flow
    • license/cla
  • Third analysis, 7f90c5d7d, the V2 merge head, analyzed as merge commit f75d75bec. The CodeQL check passed again: "No new alerts in code changed by this pull request", with 0 annotations, and the same 11 checks all pass. While only the Actions analysis had uploaded, the check briefly showed neutral with "2 configurations not found". It passed once the Python and JS/TS analyses had uploaded.

The 19 alerts on refs/pull/1597/merge

Code-scanning reads work from this session again; for a while they returned 403, and I didn't change auth. Everything below came from the API after the third analysis.

What the merge ref shows.

  • alerts?ref=refs/pull/1597/merge lists one alert of any rule: #3936, which is fixed. No py/log-injection alert appears there, in any state.
  • alerts/<n>/instances?ref=refs/pull/1597/merge returns no instances for any of the 19.
  • As controls, the same call returns 2551's open instance on V2's ref, and #3936's fixed instance on the merge ref.

Why none of the 19 can read fixed there.

  • PR analyses in this repository report results only on the lines the PR changes. The newest Python analysis on the merge ref (1876341526) runs CodeQL 2.27.1, the CLI version of V2's analyses, and codeql/python-queries 1.8.11, the pack in V2's base analysis. It also loads the codeql-action/pr-diff-range extension, which keeps only results located on changed lines.
  • That analysis has 0 results. V2's full analysis at 86f3f3629 (1875782707) has 2469.
  • So none of the 19 base alerts has an instance on the PR ref, in any state. They will read fixed only on paullizer-react-v2-ui, once the branch analysis runs after the merge.

What the merge-ref analysis does prove.

  • 14 alerts have their sink on a line this PR rewrites, which puts them inside the diff range: 1105, 1110, 1111, 1113, 1114, 1115, 3412 and 2120–2126. The analysis reports nothing on those lines, so CodeQL no longer flags them.
  • 5 alerts keep their sink lines unchanged: 2551–2554 and 3411. They fall outside the diff range, so the PR analysis doesn't evaluate them. Their evidence is the local run with the same versions, which reports none of them on this branch (see Testing / validation).

The locations below are V2's current ones at 86f3f3629, read from the alerts API. All 19 are open there, and V2 has no other open py/log-injection alert. #1592 and #1596 moved 2120–2126; the other 12 are where they were at 70c255508. The merge head's changes sit on these same lines.

Alert Location on V2 Change Merge-ref analysis Expected on V2 after merge
2551 functions_appinsights.py:419 .replace chain after the regex in sanitize_log_message not evaluated (line outside diff) fixed
2552 functions_appinsights.py:515 same not evaluated (line outside diff) fixed
2553 functions_appinsights.py:531 same not evaluated (line outside diff) fixed
2554 functions_appinsights.py:553 same not evaluated (line outside diff) fixed
1110 utils_cache.py:215 wrapped no result (line in diff) fixed
1111 utils_cache.py:265 wrapped no result (line in diff) fixed
1113 utils_cache.py:501 wrapped no result (line in diff) fixed
1114 utils_cache.py:571 wrapped no result (line in diff) fixed
1115 utils_cache.py:635 wrapped no result (line in diff) fixed
3411 utils_cache.py:711 group_id rebound before the call not evaluated (line outside diff) fixed
3412 utils_cache.py:776 wrapped no result (line in diff) fixed
2120 route_backend_workflows.py:1936 wrapped no result (line in diff) fixed
2121 route_backend_workflows.py:1937 wrapped no result (line in diff) fixed
2122 route_backend_workflows.py:1944 wrapped no result (line in diff) fixed
2123 route_backend_workflows.py:1945 wrapped no result (line in diff) fixed
2124 route_backend_workflows.py:2358 wrapped no result (line in diff) fixed
2125 route_backend_workflows.py:2365 wrapped no result (line in diff) fixed
2126 route_backend_workflows.py:2372 wrapped no result (line in diff) fixed
1105 route_backend_plugins.py:2214 request body no longer logged no result (line in diff) fixed

Why the last column should hold:

  • Same versions. The local run used CodeQL 2.27.1 and codeql/python-queries 1.8.11, the same as the merge-ref analysis and V2's base analysis, and reports none of the 19 on this branch (see Testing / validation).
  • 14 alerts on rewritten lines. Their fingerprints change, so GitHub can't carry them forward. Neither the merge-ref analysis nor the local run reports anything on those lines.
  • 5 alerts that keep their fingerprints (2551–2554 and 3411). They close only if CodeQL stops reporting them, which the local run shows it does.
  • No new log-injection results. The merge-ref analysis has no log-injection result, and the PR check found no new alert in the code this PR changes. The local run found no log-injection result on this branch that isn't also on V2.

Other alerts

  • The only alert of any rule on the merge ref is #3936, the clear-text duplicate the first analysis created (Decision 9). It's fixed there. Its V2 original, #3405 at utils_cache.py:711, is still open on V2, with its fingerprint unchanged.
  • The PR check reports no new alert of any rule in the code this PR changes.
  • Nothing was dismissed.

Follow-ups

  • Same-shape log lines that CodeQL doesn't flag on base, left alone to keep this PR to the 19 alerts:
    • In utils_cache.py: the personal fingerprint error (user_id), the cache-expired and cache-miss debug lines (cache_key), the cache read and write error lines, and the _debug_print calls.
    • In route_backend_workflows.py: the workflow cancellation user_id.
    • In route_backend_plugins.py: logging.info("Validated plugin settings: %s", updates), which logs only the validated booleans.
  • A pre-existing import cycle. A cold import route_backend_workflows fails with NameError: enabled_required, through functions_settings ↔ functions_authentication.
  • Pre-existing clear-text results next to lines this PR touches. Their fingerprints are unchanged, so this PR neither opens nor closes them.
    • utils_cache.py: 2 results. One is the group-invalidation log line (Decision 9), the other the _debug_print stdout line. Both trace back to _get_trusted_group_upload_scope_ids() in route_frontend_chats.py, so the logged value is a group ID that the query flags because of the "trusted" name. They look like false positives and are worth triaging on their own.
    • functions_appinsights.py: 9 results in its logging calls, at base L375–L553. Their sources are Key Vault secret names and references and file-sync strings. This PR didn't analyze them.
  • Pre-existing failures that aren't on the known-failures list. Each is identical on a clean 70c255508 and on a clean V2 tip, 86f3f3629:
    • test_workflow_cancellation.py (3 runner tests)
    • test_workspace_authoring_backend.py (1)
    • test_agents_plugins_blueprint_fix.py
    • test_plugin_tool_agent_security_audit.py
    • test_logged_core_plugins.py
    • test_unified_logging_entrypoint.py
    • test_route_backend_models_safe_logging.py
    • test_logging_tag_standardization.py
    • test_agent_delegation_action.py (14)

- sanitize_log_message: add line-break replace calls after the
  LOG_CONTROL_CHAR_RE regex. They are no-ops at run time, so its output
  is unchanged, but they let CodeQL credit it as a sanitizer (alerts
  2551-2554).
- utils_cache.py and route_backend_workflows.py: pass request-derived
  IDs, scopes, keys and exception text through sanitize_log_message in
  the 14 flagged log calls. Ordinary values log exactly as before, and
  logging.exception keeps its traceback.
- route_backend_plugins.py: stop logging the plugin settings request
  body, which can carry secrets; log its top-level field count through
  log_event instead (alert 1105).
- Add functional tests, fix documentation and release notes, and set
  VERSION to 0.261.216.

Refs microsoft#1543

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Comment thread application/single_app/utils_cache.py Fixed
Paul Lizer (paullizer) and others added 3 commits October 1, 2026 11:34
The first version wrapped group_id inside the invalidate_group_search_cache
log call. That line already carries a py/clear-text-logging-sensitive-data
alert (a false positive from the _get_trusted_group_upload_scope_ids name),
and editing its text changed the fingerprint GitHub matches alerts by, so the
PR's CodeQL check reported the existing alert as a new high-severity one.

Rebind group_id through sanitize_log_message on the line before the call so
the log line keeps its 0.261.213 text. Local CodeQL still clears the
log-injection result there, and the clear-text result keeps its fingerprint.

Update the fix doc with the GitHub and fingerprint results.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Resolves the two conflicts with 0.261.214:
- config.py keeps VERSION 0.261.216.
- release_notes.md puts the v0.261.216 section above the new v0.261.214 section. Every V2 section is unchanged.

route_backend_workflows.py merged without conflicts. Its sanitize_log_message wraps and the extended functions_appinsights import are unchanged.

N9 (microsoft#1596, 0.261.215) has not landed on V2 yet. When it does, its release-notes section will need one more merge.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Resolves the two conflicts with 0.261.215:
- config.py keeps VERSION 0.261.216.
- release_notes.md puts the v0.261.216 section above the new v0.261.215 section. Every V2 section is unchanged.

route_backend_workflows.py merged without conflicts. N9's import and draft-instructions gate sit above this branch's hunks, so the seven sanitize_log_message wraps moved down 10 lines unchanged, and the extended functions_appinsights import is unchanged.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@paullizer
Paul Lizer (paullizer) marked this pull request as ready for review October 1, 2026 19:09
@paullizer
Paul Lizer (paullizer) merged commit b617296 into microsoft:paullizer-react-v2-ui Oct 1, 2026
11 checks passed
Paul Lizer (paullizer) added a commit to paullizer/simplechat that referenced this pull request Oct 1, 2026
Docs-only status update to the chat orchestration workflows roadmap at
paullizer-react-v2-ui 0.261.216:

- 6a done in microsoft#1592 (v0.261.214), with where it differs from the plan
- workflow_results capability and 6b (6b-1 server, 6b-2 V2) in progress;
  Retry uses the durable runtime/resume, not /resume-failed; run deep
  link reuses P's query form through one scope-aware helper
- N9 follow-up microsoft#1596 (v0.261.215) and microsoft#1597 log injection fix (v0.261.216)
- new follow-ups: microsoft#1598 in review, non_group_invitee product bug, stale
  base assertions, and the verified cold-import cycle
- section 10: branch from paullizer-react-v2-ui, not only base on it

Refs microsoft#1543 microsoft#1546

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Paul Lizer (paullizer) added a commit to paullizer/simplechat that referenced this pull request Oct 1, 2026
Brings in the log-injection CodeQL alerts fix (sanitized log arguments
in utils_cache and the workflow cancellation routes, and a field-count
log line in the admin plugin settings route) so the harness fixes on
this branch are rerun against it.

Refs microsoft#1543

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Paul Lizer (paullizer) added a commit that referenced this pull request Oct 1, 2026
…lows-capability

Roadmap status: 6a done, 6b split, #1597 fixed
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